From 6962b49a99eadc7f4266d13f66fca8611802fe09 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 11 Jan 2012 03:59:17 +0100 Subject: [PATCH] css: Introduce GtkCssComputedValues To be used for storing computed values. Is the replacement for GtkStyleProperties, which is now legacy code. --- gtk/Makefile.am | 2 + gtk/gtkcsscomputedvalues.c | 233 ++++++++++++++++++++++++++++++ gtk/gtkcsscomputedvaluesprivate.h | 78 ++++++++++ gtk/gtkcsslookup.c | 102 ++----------- gtk/gtkcsslookupprivate.h | 4 +- gtk/gtkstylecontext.c | 117 ++++++++------- 6 files changed, 390 insertions(+), 146 deletions(-) create mode 100644 gtk/gtkcsscomputedvalues.c create mode 100644 gtk/gtkcsscomputedvaluesprivate.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index d35c76d2de..b8007d857d 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -411,6 +411,7 @@ gtk_private_h_sources = \ gtkbuttonprivate.h \ gtkcellareaboxcontextprivate.h \ gtkcontainerprivate.h \ + gtkcsscomputedvaluesprivate.h \ gtkcsscustompropertyprivate.h \ gtkcssimagegradientprivate.h \ gtkcssimageprivate.h \ @@ -592,6 +593,7 @@ gtk_base_c_sources = \ gtkcombobox.c \ gtkcomboboxtext.c \ gtkcontainer.c \ + gtkcsscomputedvalues.c \ gtkcsscustomproperty.c \ gtkcssimage.c \ gtkcssimagegradient.c \ diff --git a/gtk/gtkcsscomputedvalues.c b/gtk/gtkcsscomputedvalues.c new file mode 100644 index 0000000000..8423be729e --- /dev/null +++ b/gtk/gtkcsscomputedvalues.c @@ -0,0 +1,233 @@ +/* + * Copyright © 2012 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtkcsscomputedvaluesprivate.h" + +#include "gtkcssstylepropertyprivate.h" +#include "gtkcsstypesprivate.h" +#include "gtkprivatetypebuiltins.h" + +G_DEFINE_TYPE (GtkCssComputedValues, _gtk_css_computed_values, G_TYPE_OBJECT) + +static void +gtk_css_computed_values_dispose (GObject *object) +{ + GtkCssComputedValues *values = GTK_CSS_COMPUTED_VALUES (object); + + if (values->values) + { + g_value_array_free (values->values); + values->values = NULL; + } + if (values->sections) + { + g_ptr_array_unref (values->sections); + values->sections = NULL; + } + + G_OBJECT_CLASS (_gtk_css_computed_values_parent_class)->dispose (object); +} + +static void +_gtk_css_computed_values_class_init (GtkCssComputedValuesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gtk_css_computed_values_dispose; +} + +static void +_gtk_css_computed_values_init (GtkCssComputedValues *computed_values) +{ + +} + +GtkCssComputedValues * +_gtk_css_computed_values_new (void) +{ + return g_object_new (GTK_TYPE_CSS_COMPUTED_VALUES, NULL); +} + +static void +maybe_unref_section (gpointer section) +{ + if (section) + gtk_css_section_unref (section); +} + +void +_gtk_css_computed_values_compute_value (GtkCssComputedValues *values, + GtkStyleContext *context, + guint id, + const GValue *specified, + GtkCssSection *section) +{ + GtkCssStyleProperty *prop; + GtkStyleContext *parent; + + g_return_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values)); + g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); + g_return_if_fail (specified == NULL || G_IS_VALUE (specified)); + + prop = _gtk_css_style_property_lookup_by_id (id); + parent = gtk_style_context_get_parent (context); + + if (values->values == NULL) + values->values = g_value_array_new (id + 1); + while (values->values->n_values <= id) + g_value_array_append (values->values, NULL); + + /* http://www.w3.org/TR/css3-cascade/#cascade + * Then, for every element, the value for each property can be found + * by following this pseudo-algorithm: + * 1) Identify all declarations that apply to the element + */ + if (specified != NULL) + { + if (G_VALUE_HOLDS (specified, GTK_TYPE_CSS_SPECIAL_VALUE)) + { + switch (g_value_get_enum (specified)) + { + case GTK_CSS_INHERIT: + /* 3) if the value of the winning declaration is ‘inherit’, + * the inherited value (see below) becomes the specified value. + */ + specified = NULL; + break; + case GTK_CSS_INITIAL: + /* if the value of the winning declaration is ‘initial’, + * the initial value (see below) becomes the specified value. + */ + specified = _gtk_css_style_property_get_initial_value (prop); + break; + default: + /* This is part of (2) above */ + break; + } + } + + /* 2) If the cascading process (described below) yields a winning + * declaration and the value of the winning declaration is not + * ‘initial’ or ‘inherit’, the value of the winning declaration + * becomes the specified value. + */ + } + else + { + if (_gtk_css_style_property_is_inherit (prop)) + { + /* 4) if the property is inherited, the inherited value becomes + * the specified value. + */ + specified = NULL; + } + else + { + /* 5) Otherwise, the initial value becomes the specified value. + */ + specified = _gtk_css_style_property_get_initial_value (prop); + } + } + + if (specified == NULL && parent == NULL) + { + /* If the ‘inherit’ value is set on the root element, the property is + * assigned its initial value. */ + specified = _gtk_css_style_property_get_initial_value (prop); + } + + if (specified) + { + _gtk_css_style_property_compute_value (prop, + g_value_array_get_nth (values->values, id), + context, + specified); + } + else + { + const GValue *parent_value; + GValue *value = g_value_array_get_nth (values->values, id); + /* Set NULL here and do the inheritance upon lookup? */ + parent_value = _gtk_style_context_peek_property (parent, + _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop))); + g_value_init (value, G_VALUE_TYPE (parent_value)); + g_value_copy (parent_value, value); + } + + if (section) + { + if (values->sections == NULL) + values->sections = g_ptr_array_new_with_free_func (maybe_unref_section); + if (values->sections->len <= id) + g_ptr_array_set_size (values->sections, id + 1); + + g_ptr_array_index (values->sections, id) = gtk_css_section_ref (section); + } +} + +const GValue * +_gtk_css_computed_values_get_value (GtkCssComputedValues *values, + guint id) +{ + const GValue *v; + + g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL); + + if (values->values == NULL || + id >= values->values->n_values) + return NULL; + + v = g_value_array_get_nth (values->values, id); + if (!G_IS_VALUE (v)) + return NULL; + + return v; +} + +const GValue * +_gtk_css_computed_values_get_value_by_name (GtkCssComputedValues *values, + const char *name) +{ + GtkStyleProperty *prop; + + g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL); + g_return_val_if_fail (name != NULL, NULL); + + prop = _gtk_style_property_lookup (name); + g_assert (GTK_IS_CSS_STYLE_PROPERTY (prop)); + + return _gtk_css_computed_values_get_value (values, _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop))); +} + +GtkCssSection * +_gtk_css_computed_values_get_section (GtkCssComputedValues *values, + guint id) +{ + g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL); + + if (values->sections == NULL || + id >= values->sections->len) + return NULL; + + return g_ptr_array_index (values->sections, id); +} + diff --git a/gtk/gtkcsscomputedvaluesprivate.h b/gtk/gtkcsscomputedvaluesprivate.h new file mode 100644 index 0000000000..b382b25c2b --- /dev/null +++ b/gtk/gtkcsscomputedvaluesprivate.h @@ -0,0 +1,78 @@ +/* + * Copyright © 2012 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_CSS_COMPUTED_VALUES_PRIVATE_H__ +#define __GTK_CSS_COMPUTED_VALUES_PRIVATE_H__ + +#include + +#include "gtk/gtkcsssection.h" +#include "gtk/gtkstylecontext.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_CSS_COMPUTED_VALUES (_gtk_css_computed_values_get_type ()) +#define GTK_CSS_COMPUTED_VALUES(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GTK_TYPE_CSS_COMPUTED_VALUES, GtkCssComputedValues)) +#define GTK_CSS_COMPUTED_VALUES_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GTK_TYPE_CSS_COMPUTED_VALUES, GtkCssComputedValuesClass)) +#define GTK_IS_CSS_COMPUTED_VALUES(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GTK_TYPE_CSS_COMPUTED_VALUES)) +#define GTK_IS_CSS_COMPUTED_VALUES_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GTK_TYPE_CSS_COMPUTED_VALUES)) +#define GTK_CSS_COMPUTED_VALUES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CSS_COMPUTED_VALUES, GtkCssComputedValuesClass)) + +typedef struct _GtkCssComputedValues GtkCssComputedValues; +typedef struct _GtkCssComputedValuesClass GtkCssComputedValuesClass; + +struct _GtkCssComputedValues +{ + GObject parent; + + GValueArray *values; + GPtrArray *sections; +}; + +struct _GtkCssComputedValuesClass +{ + GObjectClass parent_class; +}; + +GType _gtk_css_computed_values_get_type (void) G_GNUC_CONST; + +GtkCssComputedValues * _gtk_css_computed_values_new (void); + +void _gtk_css_computed_values_compute_value (GtkCssComputedValues *values, + GtkStyleContext *context, + guint id, + const GValue *specified, + GtkCssSection *section); +void _gtk_css_computed_values_set_value (GtkCssComputedValues *values, + guint id, + const GValue *value, + GtkCssSection *section); + +const GValue * _gtk_css_computed_values_get_value (GtkCssComputedValues *values, + guint id); +const GValue * _gtk_css_computed_values_get_value_by_name (GtkCssComputedValues *values, + const char *name); +GtkCssSection * _gtk_css_computed_values_get_section (GtkCssComputedValues *values, + guint id); + + +G_END_DECLS + +#endif /* __GTK_CSS_COMPUTED_VALUES_PRIVATE_H__ */ diff --git a/gtk/gtkcsslookup.c b/gtk/gtkcsslookup.c index ef075d37d6..76bdb484d3 100644 --- a/gtk/gtkcsslookup.c +++ b/gtk/gtkcsslookup.c @@ -107,7 +107,7 @@ _gtk_css_lookup_set (GtkCssLookup *lookup, * _gtk_css_lookup_resolve: * @lookup: the lookup * @context: the context the values are resolved for - * @props: a new #GtkStyleProperties to be filled with the new properties + * @values: a new #GtkCssComputedValues to be filled with the new properties * * Resolves the current lookup into a styleproperties object. This is done * by converting from the "winning declaration" to the "computed value". @@ -116,106 +116,24 @@ _gtk_css_lookup_set (GtkCssLookup *lookup, * an issue, go fix it. **/ void -_gtk_css_lookup_resolve (GtkCssLookup *lookup, - GtkStyleContext *context, - GtkStyleProperties *props) +_gtk_css_lookup_resolve (GtkCssLookup *lookup, + GtkStyleContext *context, + GtkCssComputedValues *values) { - GtkStyleContext *parent; guint i, n; g_return_if_fail (lookup != NULL); g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); - g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props)); + g_return_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values)); - parent = gtk_style_context_get_parent (context); n = _gtk_css_style_property_get_n_properties (); for (i = 0; i < n; i++) { - GtkCssStyleProperty *prop = _gtk_css_style_property_lookup_by_id (i); - const GValue *result; - GValue value = { 0, }; - - /* http://www.w3.org/TR/css3-cascade/#cascade - * Then, for every element, the value for each property can be found - * by following this pseudo-algorithm: - * 1) Identify all declarations that apply to the element - */ - if (lookup->values[i].value != NULL) - { - /* 2) If the cascading process (described below) yields a winning - * declaration and the value of the winning declaration is not - * ‘initial’ or ‘inherit’, the value of the winning declaration - * becomes the specified value. - */ - if (!G_VALUE_HOLDS (lookup->values[i].value, GTK_TYPE_CSS_SPECIAL_VALUE)) - { - result = lookup->values[i].value; - } - else - { - switch (g_value_get_enum (lookup->values[i].value)) - { - case GTK_CSS_INHERIT: - /* 3) if the value of the winning declaration is ‘inherit’, - * the inherited value (see below) becomes the specified value. - */ - result = NULL; - break; - case GTK_CSS_INITIAL: - /* if the value of the winning declaration is ‘initial’, - * the initial value (see below) becomes the specified value. - */ - result = _gtk_css_style_property_get_initial_value (prop); - break; - default: - /* This is part of (2) above */ - result = lookup->values[i].value; - break; - } - } - } - else - { - if (_gtk_css_style_property_is_inherit (prop)) - { - /* 4) if the property is inherited, the inherited value becomes - * the specified value. - */ - result = NULL; - } - else - { - /* 5) Otherwise, the initial value becomes the specified value. - */ - result = _gtk_css_style_property_get_initial_value (prop); - } - } - - if (result == NULL && parent == NULL) - { - /* If the ‘inherit’ value is set on the root element, the property is - * assigned its initial value. */ - result = _gtk_css_style_property_get_initial_value (prop); - } - - if (result) - { - _gtk_css_style_property_compute_value (prop, &value, context, result); - } - else - { - /* Set NULL here and do the inheritance upon lookup? */ - result = _gtk_style_context_peek_property (parent, - _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop))); - g_value_init (&value, G_VALUE_TYPE (result)); - g_value_copy (result, &value); - } - - _gtk_style_properties_set_property_by_property (props, - prop, - 0, - &value); - g_value_unset (&value); + _gtk_css_computed_values_compute_value (values, + context, + i, + lookup->values[i].value, + lookup->values[i].section); } } diff --git a/gtk/gtkcsslookupprivate.h b/gtk/gtkcsslookupprivate.h index 90cf0cf6df..f966902595 100644 --- a/gtk/gtkcsslookupprivate.h +++ b/gtk/gtkcsslookupprivate.h @@ -22,9 +22,9 @@ #include #include "gtk/gtkbitmaskprivate.h" +#include "gtk/gtkcsscomputedvaluesprivate.h" #include "gtk/gtkcsssection.h" #include "gtk/gtkstylecontext.h" -#include "gtk/gtkstyleproperties.h" G_BEGIN_DECLS @@ -43,7 +43,7 @@ void _gtk_css_lookup_set (GtkCssLookup const GValue *value); void _gtk_css_lookup_resolve (GtkCssLookup *lookup, GtkStyleContext *context, - GtkStyleProperties *props); + GtkCssComputedValues *values); G_END_DECLS diff --git a/gtk/gtkstylecontext.c b/gtk/gtkstylecontext.c index 536ad37a19..2c1d8eb383 100644 --- a/gtk/gtkstylecontext.c +++ b/gtk/gtkstylecontext.c @@ -337,7 +337,7 @@ struct GtkStyleInfo struct StyleData { - GtkStyleProperties *store; + GtkCssComputedValues *store; GSList *icon_factories; GArray *property_cache; }; @@ -987,7 +987,7 @@ build_properties (GtkStyleContext *context, } } - style_data->store = gtk_style_properties_new (); + style_data->store = _gtk_css_computed_values_new (); _gtk_css_lookup_resolve (lookup, context, style_data->store); _gtk_css_lookup_free (lookup); } @@ -1073,6 +1073,7 @@ style_data_lookup (GtkStyleContext *context, GtkStyleContextPrivate *priv; StyleData *data; gboolean state_mismatch; + const GValue *v; priv = context->priv; state_mismatch = ((GtkStyleInfo *) priv->info_stack->data)->state_flags != state; @@ -1114,11 +1115,10 @@ style_data_lookup (GtkStyleContext *context, if (priv->theming_engine) g_object_unref (priv->theming_engine); - gtk_style_properties_get (priv->current_data->store, 0, - "engine", &priv->theming_engine, - NULL); - - if (!priv->theming_engine) + v = _gtk_css_computed_values_get_value_by_name (priv->current_data->store, "engine"); + if (v) + priv->theming_engine = g_value_dup_object (v); + else priv->theming_engine = g_object_ref (gtk_theming_engine_load (NULL)); if (G_UNLIKELY (state_mismatch)) @@ -1399,6 +1399,13 @@ gtk_style_context_remove_provider_for_screen (GdkScreen *screen, } } +static const GValue * +gtk_style_context_query_func (guint id, + gpointer values) +{ + return _gtk_css_computed_values_get_value (values, id); +} + /** * gtk_style_context_get_property: * @context: a #GtkStyleContext @@ -1420,6 +1427,7 @@ gtk_style_context_get_property (GtkStyleContext *context, GValue *value) { GtkStyleContextPrivate *priv; + GtkStyleProperty *prop; StyleData *data; g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); @@ -1427,11 +1435,22 @@ gtk_style_context_get_property (GtkStyleContext *context, g_return_if_fail (value != NULL); priv = context->priv; - g_return_if_fail (priv->widget_path != NULL); + prop = _gtk_style_property_lookup (property); + if (prop == NULL) + { + g_warning ("Style property \"%s\" is not registered", property); + return; + } + if (_gtk_style_property_get_value_type (prop) == G_TYPE_NONE) + { + g_warning ("Style property \"%s\" is not gettable", property); + return; + } + data = style_data_lookup (context, state); - gtk_style_properties_get_property (data->store, property, 0, value); + _gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store); } /** @@ -1449,16 +1468,34 @@ gtk_style_context_get_valist (GtkStyleContext *context, GtkStateFlags state, va_list args) { - GtkStyleContextPrivate *priv; - StyleData *data; + const gchar *property_name; g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); - priv = context->priv; - g_return_if_fail (priv->widget_path != NULL); + property_name = va_arg (args, const gchar *); - data = style_data_lookup (context, state); - gtk_style_properties_get_valist (data->store, 0, args); + while (property_name) + { + gchar *error = NULL; + GValue value = G_VALUE_INIT; + + gtk_style_context_get_property (context, + property_name, + state, + &value); + + G_VALUE_LCOPY (&value, args, 0, &error); + g_value_unset (&value); + + if (error) + { + g_warning ("Could not get style property \"%s\": %s", property_name, error); + g_free (error); + break; + } + + property_name = va_arg (args, const gchar *); + } } /** @@ -2288,21 +2325,9 @@ const GValue * _gtk_style_context_peek_property (GtkStyleContext *context, const char *property_name) { - GtkStyleProperty *property; - StyleData *data; + StyleData *data = style_data_lookup (context, gtk_style_context_get_state (context)); - property = _gtk_style_property_lookup (property_name); - if (!GTK_IS_CSS_STYLE_PROPERTY (property)) - { - g_warning ("Style property \"%s\" does not exist", property_name); - return NULL; - } - - data = style_data_lookup (context, gtk_style_context_get_state (context)); - - return _gtk_style_properties_peek_property (data->store, - GTK_CSS_STYLE_PROPERTY (property), - 0); + return _gtk_css_computed_values_get_value_by_name (data->store, property_name); } const GValue * @@ -2946,6 +2971,7 @@ gtk_style_context_notify_state_change (GtkStyleContext *context, GtkAnimationDescription *desc; AnimationInfo *info; GtkStateFlags flags; + const GValue *v; StyleData *data; g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); @@ -2987,10 +3013,10 @@ gtk_style_context_notify_state_change (GtkStyleContext *context, * state, it will fallback to the normal state as well if necessary. */ data = style_data_lookup (context, flags); - gtk_style_properties_get (data->store, 0, - "transition", &desc, - NULL); - + v = _gtk_css_computed_values_get_value_by_name (data->store, "transition"); + if (!v) + return; + desc = g_value_get_boxed (v); if (!desc) return; @@ -3630,7 +3656,6 @@ gtk_style_context_get_font (GtkStyleContext *context, { GtkStyleContextPrivate *priv; StyleData *data; - GHashTable *font_cache; PangoFontDescription *description; g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL); @@ -3642,27 +3667,15 @@ gtk_style_context_get_font (GtkStyleContext *context, /* Yuck, fonts are created on-demand but we don't return a ref. * Do bad things to achieve this requirement */ - font_cache = g_object_get_data (G_OBJECT (data->store), "font-cache-for-get_font"); - if (font_cache) - { - description = g_hash_table_lookup (font_cache, GUINT_TO_POINTER (state)); - } - else - { - font_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) pango_font_description_free); - g_object_set_data_full (G_OBJECT (data->store), - "font-cache-for-get_font", - font_cache, - (GDestroyNotify) g_hash_table_unref); - description = NULL; - } - + description = g_object_get_data (G_OBJECT (data->store), "font-cache-for-get_font"); if (description == NULL) { - gtk_style_properties_get (data->store, 0, "font", &description, NULL); - g_hash_table_insert (font_cache, GUINT_TO_POINTER (state), description); + gtk_style_context_get (context, state, "font", &description, NULL); + g_object_set_data_full (G_OBJECT (data->store), + "font-cache-for-get_font", + description, + (GDestroyNotify) pango_font_description_free); } - return description; }