style cascade: Allow cascades with more ancestors

Previously a style cascade's parent could not have a parent itself. That
represented the two levels at which you could add a style provider: at
the screen level, with gtk_style_context_add_provider_for_screen(), and
at the style context level, with gtk_style_context_add_provider().

This commit changes no functionality, but this change will be necessary
for adding style providers in the future that apply to a subtree of the
widget tree. It relaxes the requirement that a style cascade's parent
must not have a parent, since in the future a style context may be
affected by any number of parent widgets' style contexts.

https://bugzilla.gnome.org/show_bug.cgi?id=751409
This commit is contained in:
Philip Chimento 2016-06-15 23:02:11 -04:00 committed by Matthias Clasen
parent 1c6b878e09
commit 76bacfde6e
2 changed files with 288 additions and 41 deletions

View File

@ -27,8 +27,9 @@ typedef struct _GtkStyleCascadeIter GtkStyleCascadeIter;
typedef struct _GtkStyleProviderData GtkStyleProviderData;
struct _GtkStyleCascadeIter {
int parent_index; /* pointing at last index that was returned, not next one that should be returned */
int index; /* pointing at last index that was returned, not next one that should be returned */
int n_cascades;
int *cascade_index; /* each one points at last index that was returned, */
/* not next one that should be returned */
};
struct _GtkStyleProviderData
@ -42,56 +43,57 @@ static GtkStyleProvider *
gtk_style_cascade_iter_next (GtkStyleCascade *cascade,
GtkStyleCascadeIter *iter)
{
if (iter->parent_index > 0)
GtkStyleCascade *cas;
int ix, highest_priority_index = 0;
GtkStyleProviderData *highest_priority_data = NULL;
for (cas = cascade, ix = 0; ix < iter->n_cascades; cas = cas->parent, ix++)
{
if (iter->index > 0)
{
GtkStyleProviderData *data, *parent_data;
if (iter->cascade_index[ix] <= 0)
continue;
data = &g_array_index (cascade->providers, GtkStyleProviderData, iter->index - 1);
parent_data = &g_array_index (cascade->parent->providers, GtkStyleProviderData, iter->parent_index - 1);
if (data->priority >= parent_data->priority)
{
iter->index--;
return data->provider;
}
else
{
iter->parent_index--;
return parent_data->provider;
}
}
else
GtkStyleProviderData *data = &g_array_index (cas->providers,
GtkStyleProviderData,
iter->cascade_index[ix] - 1);
if (highest_priority_data == NULL || data->priority > highest_priority_data->priority)
{
iter->parent_index--;
return g_array_index (cascade->parent->providers, GtkStyleProviderData, iter->parent_index).provider;
highest_priority_index = ix;
highest_priority_data = data;
}
}
else
if (highest_priority_data != NULL)
{
if (iter->index > 0)
{
iter->index--;
return g_array_index (cascade->providers, GtkStyleProviderData, iter->index).provider;
}
else
{
return NULL;
}
iter->cascade_index[highest_priority_index]--;
return highest_priority_data->provider;
}
return NULL;
}
static GtkStyleProvider *
gtk_style_cascade_iter_init (GtkStyleCascade *cascade,
GtkStyleCascadeIter *iter)
{
iter->parent_index = cascade->parent ? cascade->parent->providers->len : 0;
iter->index = cascade->providers->len;
GtkStyleCascade *cas = cascade;
int ix;
iter->n_cascades = 1;
while ((cas = cas->parent) != NULL)
iter->n_cascades++;
iter->cascade_index = g_new (int, iter->n_cascades);
for (cas = cascade, ix = 0; ix < iter->n_cascades; cas = cas->parent, ix++)
iter->cascade_index[ix] = cas->providers->len;
return gtk_style_cascade_iter_next (cascade, iter);
}
static void
gtk_style_cascade_iter_clear (GtkStyleCascadeIter *iter)
{
g_free (iter->cascade_index);
}
static gboolean
gtk_style_cascade_get_style_property (GtkStyleProvider *provider,
GtkWidgetPath *path,
@ -112,9 +114,13 @@ gtk_style_cascade_get_style_property (GtkStyleProvider *provider,
state,
pspec,
value))
return TRUE;
{
gtk_style_cascade_iter_clear (&iter);
return TRUE;
}
}
gtk_style_cascade_iter_clear (&iter);
return FALSE;
}
@ -141,9 +147,13 @@ gtk_style_cascade_get_settings (GtkStyleProviderPrivate *provider)
settings = _gtk_style_provider_private_get_settings (GTK_STYLE_PROVIDER_PRIVATE (item));
if (settings)
return settings;
{
gtk_style_cascade_iter_clear (&iter);
return settings;
}
}
gtk_style_cascade_iter_clear (&iter);
return NULL;
}
@ -164,7 +174,10 @@ gtk_style_cascade_get_color (GtkStyleProviderPrivate *provider,
{
color = _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (item), name);
if (color)
return color;
{
gtk_style_cascade_iter_clear (&iter);
return color;
}
}
else
{
@ -172,6 +185,7 @@ gtk_style_cascade_get_color (GtkStyleProviderPrivate *provider,
}
}
gtk_style_cascade_iter_clear (&iter);
return NULL;
}
@ -201,9 +215,13 @@ gtk_style_cascade_get_keyframes (GtkStyleProviderPrivate *provider,
keyframes = _gtk_style_provider_private_get_keyframes (GTK_STYLE_PROVIDER_PRIVATE (item), name);
if (keyframes)
return keyframes;
{
gtk_style_cascade_iter_clear (&iter);
return keyframes;
}
}
gtk_style_cascade_iter_clear (&iter);
return NULL;
}
@ -236,6 +254,7 @@ gtk_style_cascade_lookup (GtkStyleProviderPrivate *provider,
g_warn_if_reached ();
}
}
gtk_style_cascade_iter_clear (&iter);
}
static void
@ -303,8 +322,6 @@ _gtk_style_cascade_set_parent (GtkStyleCascade *cascade,
{
gtk_internal_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
gtk_internal_return_if_fail (parent == NULL || GTK_IS_STYLE_CASCADE (parent));
if (parent)
gtk_internal_return_if_fail (parent->parent == NULL);
if (cascade->parent == parent)
return;

View File

@ -1,5 +1,12 @@
#include <gtk/gtk.h>
typedef struct {
GtkStyleContext *context;
GtkCssProvider *blue_provider;
GtkCssProvider *red_provider;
GtkCssProvider *green_provider;
} PrioritiesFixture;
static void
test_parse_selectors (void)
{
@ -362,6 +369,213 @@ test_style_classes (void)
g_object_unref (context);
}
static void
test_style_priorities_setup (PrioritiesFixture *f,
gconstpointer unused)
{
GError *error = NULL;
f->blue_provider = gtk_css_provider_new ();
f->red_provider = gtk_css_provider_new ();
f->green_provider = gtk_css_provider_new ();
f->context = gtk_style_context_new ();
GtkWidgetPath *path = gtk_widget_path_new ();
gtk_css_provider_load_from_data (f->blue_provider, "* { color: blue; }", -1, &error);
g_assert_no_error (error);
gtk_css_provider_load_from_data (f->red_provider, "* { color: red; }", -1, &error);
g_assert_no_error (error);
gtk_css_provider_load_from_data (f->green_provider, "* { color: green; }", -1, &error);
g_assert_no_error (error);
gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
gtk_style_context_set_path (f->context, path);
gtk_widget_path_free (path);
}
static void
test_style_priorities_teardown (PrioritiesFixture *f,
gconstpointer unused)
{
g_object_unref (f->blue_provider);
g_object_unref (f->red_provider);
g_object_unref (f->green_provider);
g_object_unref (f->context);
}
static void
test_style_priorities_equal (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->blue_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
/* When style providers are added to the screen as well as the style context
the one specific to the style context should take priority */
gdk_rgba_parse (&ref_color, "red");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
static void
test_style_priorities_screen_only (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->blue_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gdk_rgba_parse (&ref_color, "blue");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
static void
test_style_priorities_context_only (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gdk_rgba_parse (&ref_color, "red");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
static void
test_style_priorities_screen_higher (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->blue_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER + 1);
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gdk_rgba_parse (&ref_color, "blue");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
static void
test_style_priorities_context_higher (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->blue_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER + 1);
gdk_rgba_parse (&ref_color, "red");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
static void
test_style_priorities_two_screen (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->blue_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->red_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER + 1);
gdk_rgba_parse (&ref_color, "red");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
static void
test_style_priorities_two_context (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->blue_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER + 1);
gdk_rgba_parse (&ref_color, "red");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
static void
test_style_priorities_three_screen_higher (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->blue_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->green_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER + 1);
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gdk_rgba_parse (&ref_color, "green");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
static void
test_style_priorities_three_context_higher (PrioritiesFixture *f,
gconstpointer unused)
{
GdkRGBA color, ref_color;
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (f->blue_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->red_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_style_context_add_provider (f->context, GTK_STYLE_PROVIDER (f->green_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER + 1);
gdk_rgba_parse (&ref_color, "green");
gtk_style_context_get_color (f->context, gtk_style_context_get_state (f->context),
&color);
g_assert_true (gdk_rgba_equal (&ref_color, &color));
}
int
main (int argc, char *argv[])
{
@ -376,5 +590,21 @@ main (int argc, char *argv[])
g_test_add_func ("/style/widget-path-parent", test_widget_path_parent);
g_test_add_func ("/style/classes", test_style_classes);
#define ADD_PRIORITIES_TEST(path, func) \
g_test_add ("/style/priorities/" path, PrioritiesFixture, NULL, test_style_priorities_setup, \
(func), test_style_priorities_teardown)
ADD_PRIORITIES_TEST ("equal", test_style_priorities_equal);
ADD_PRIORITIES_TEST ("screen-only", test_style_priorities_screen_only);
ADD_PRIORITIES_TEST ("context-only", test_style_priorities_context_only);
ADD_PRIORITIES_TEST ("screen-higher", test_style_priorities_screen_higher);
ADD_PRIORITIES_TEST ("context-higher", test_style_priorities_context_higher);
ADD_PRIORITIES_TEST ("two-screen", test_style_priorities_two_screen);
ADD_PRIORITIES_TEST ("two-context", test_style_priorities_two_context);
ADD_PRIORITIES_TEST ("three-screen-higher", test_style_priorities_three_screen_higher);
ADD_PRIORITIES_TEST ("three-context-higher", test_style_priorities_three_context_higher);
#undef ADD_PRIORITIES_TEST
return g_test_run ();
}