diff options
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/gtkcsskeyframes.c | 521 | ||||
-rw-r--r-- | gtk/gtkcsskeyframesprivate.h | 52 | ||||
-rw-r--r-- | gtk/gtkcssprovider.c | 107 | ||||
-rw-r--r-- | gtk/gtkcsssection.h | 6 |
5 files changed, 684 insertions, 4 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index afd36c7693..1524b4b503 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -445,6 +445,7 @@ gtk_private_h_sources = \ gtkcssimagewin32private.h \ gtkcssinheritvalueprivate.h \ gtkcssinitialvalueprivate.h \ + gtkcsskeyframesprivate.h \ gtkcsslookupprivate.h \ gtkcssmatcherprivate.h \ gtkcssnumbervalueprivate.h \ @@ -665,6 +666,7 @@ gtk_base_c_sources = \ gtkcssimagewin32.c \ gtkcssinheritvalue.c \ gtkcssinitialvalue.c \ + gtkcsskeyframes.c \ gtkcsslookup.c \ gtkcssmatcher.c \ gtkcssnumbervalue.c \ diff --git a/gtk/gtkcsskeyframes.c b/gtk/gtkcsskeyframes.c new file mode 100644 index 0000000000..f25ccef3f2 --- /dev/null +++ b/gtk/gtkcsskeyframes.c @@ -0,0 +1,521 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2011 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 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, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "gtkcsskeyframesprivate.h" + +#include "gtkcssarrayvalueprivate.h" +#include "gtkcssshorthandpropertyprivate.h" +#include "gtkcssstylepropertyprivate.h" +#include "gtkstylepropertyprivate.h" + +#include <stdlib.h> +#include <string.h> + +struct _GtkCssKeyframes { + int ref_count; /* ref count */ + int n_keyframes; /* number of keyframes (at least 2 for 0% and 100% */ + double *keyframe_progress; /* ordered array of n_keyframes of [0..1] */ + int n_properties; /* number of properties used by keyframes */ + guint *property_ids; /* ordered array of n_properties property ids */ + GtkCssValue **values; /* 2D array: n_keyframes * n_properties of (value or NULL) for all the keyframes */ +}; + +GtkCssKeyframes * +_gtk_css_keyframes_ref (GtkCssKeyframes *keyframes) +{ + g_return_val_if_fail (keyframes != NULL, NULL); + + keyframes->ref_count++; + + return keyframes; +} + +void +_gtk_css_keyframes_unref (GtkCssKeyframes *keyframes) +{ + g_return_if_fail (keyframes != NULL); + + keyframes->ref_count--; + if (keyframes->ref_count > 0) + return; + + g_free (keyframes->keyframe_progress); + g_free (keyframes->property_ids); + g_free (keyframes->values); + + g_slice_free (GtkCssKeyframes, keyframes); +} + +#define KEYFRAMES_VALUE(keyframes, k, p) ((keyframes)->values[(k) * (keyframes)->n_properties + (p)]) + +static guint +gtk_css_keyframes_add_keyframe (GtkCssKeyframes *keyframes, + double progress) +{ + guint k, p; + + for (k = 0; k < keyframes->n_keyframes; k++) + { + if (keyframes->keyframe_progress[k] == progress) + { + for (p = 0; p < keyframes->n_properties; p++) + { + if (KEYFRAMES_VALUE (keyframes, k, p) == NULL) + continue; + + _gtk_css_value_unref (KEYFRAMES_VALUE (keyframes, k, p)); + KEYFRAMES_VALUE (keyframes, k, p) = NULL; + + /* XXX: GC properties that are now unset + * in all keyframes? */ + } + return k; + } + else if (keyframes->keyframe_progress[k] > progress) + break; + } + + keyframes->n_keyframes++; + keyframes->keyframe_progress = g_realloc (keyframes->keyframe_progress, sizeof (double) * keyframes->n_keyframes); + memmove (keyframes->keyframe_progress + k + 1, keyframes->keyframe_progress + k, sizeof (double) * (keyframes->n_keyframes - k - 1)); + keyframes->keyframe_progress[k] = progress; + + if (keyframes->n_properties) + { + gsize size = sizeof (GtkCssValue *) * keyframes->n_properties; + + keyframes->values = g_realloc (keyframes->values, sizeof (GtkCssValue *) * keyframes->n_keyframes * keyframes->n_properties); + memmove (&KEYFRAMES_VALUE (keyframes, k + 1, 0), &KEYFRAMES_VALUE (keyframes, k, 0), size * (keyframes->n_keyframes - k - 1)); + memset (&KEYFRAMES_VALUE (keyframes, k, 0), 0, size); + } + + return k; +} + +static guint +gtk_css_keyframes_lookup_property (GtkCssKeyframes *keyframes, + guint property_id) +{ + guint p; + + for (p = 0; p < keyframes->n_properties; p++) + { + if (keyframes->property_ids[p] == property_id) + return p; + else if (keyframes->property_ids[p] > property_id) + break; + } + + keyframes->n_properties++; + keyframes->property_ids = g_realloc (keyframes->property_ids, sizeof (guint) * keyframes->n_properties); + memmove (keyframes->property_ids + p + 1, keyframes->property_ids + p, sizeof (guint) * (keyframes->n_properties - p - 1)); + keyframes->property_ids[p] = property_id; + + if (keyframes->n_properties > 1) + { + guint old_n_properties = keyframes->n_properties - 1; + int k; + + keyframes->values = g_realloc (keyframes->values, sizeof (GtkCssValue *) * keyframes->n_keyframes * keyframes->n_properties); + + if (p + 1 < keyframes->n_properties) + { + memmove (&KEYFRAMES_VALUE (keyframes, keyframes->n_keyframes - 1, p + 1), + &keyframes->values[(keyframes->n_keyframes - 1) * old_n_properties + p], + sizeof (GtkCssValue *) * (keyframes->n_properties - p - 1)); + } + KEYFRAMES_VALUE (keyframes, keyframes->n_keyframes - 1, p) = NULL; + + for (k = keyframes->n_keyframes - 2; k >= 0; k--) + { + memmove (&KEYFRAMES_VALUE (keyframes, k, p + 1), + &keyframes->values[k * old_n_properties + p], + sizeof (GtkCssValue *) * old_n_properties); + KEYFRAMES_VALUE (keyframes, k, p) = NULL; + } + } + else + { + keyframes->values = g_new0 (GtkCssValue *, keyframes->n_keyframes); + } + + return p; +} + +static GtkCssKeyframes * +gtk_css_keyframes_new (void) +{ + GtkCssKeyframes *keyframes; + + keyframes = g_slice_new0 (GtkCssKeyframes); + keyframes->ref_count = 1; + + gtk_css_keyframes_add_keyframe (keyframes, 0); + gtk_css_keyframes_add_keyframe (keyframes, 1); + + return keyframes; +} + +static gboolean +keyframes_set_value (GtkCssKeyframes *keyframes, + guint k, + GtkCssStyleProperty *property, + GtkCssValue *value) +{ + guint p; + + if (!_gtk_css_style_property_is_animated (property)) + return FALSE; + + p = gtk_css_keyframes_lookup_property (keyframes, _gtk_css_style_property_get_id (property)); + + if (KEYFRAMES_VALUE (keyframes, k, p)) + _gtk_css_value_unref (KEYFRAMES_VALUE (keyframes, k, p)); + + KEYFRAMES_VALUE (keyframes, k, p) = _gtk_css_value_ref (value); + + return TRUE; +} + +static gboolean +parse_declaration (GtkCssKeyframes *keyframes, + guint k, + GtkCssParser *parser) +{ + GtkStyleProperty *property; + GtkCssValue *value; + char *name; + + while (_gtk_css_parser_try (parser, ";", TRUE)) + { + /* SKIP ALL THE THINGS! */ + } + + name = _gtk_css_parser_try_ident (parser, TRUE); + if (name == NULL) + { + _gtk_css_parser_error (parser, "No property name given"); + return FALSE; + } + + property = _gtk_style_property_lookup (name); + if (property == NULL) + { + /* should be GTK_CSS_PROVIDER_ERROR_NAME */ + _gtk_css_parser_error (parser, "No property named '%s'", name); + g_free (name); + return FALSE; + } + + g_free (name); + + if (!_gtk_css_parser_try (parser, ":", TRUE)) + { + _gtk_css_parser_error (parser, "Expected a ':'"); + return FALSE; + } + + value = _gtk_style_property_parse_value (property, parser); + if (value == NULL) + return FALSE; + + if (!_gtk_css_parser_try (parser, ";", TRUE) && + !_gtk_css_parser_begins_with (parser, '}')) + { + _gtk_css_parser_error (parser, "Junk at end of value"); + _gtk_css_value_unref (value); + return FALSE; + } + + if (GTK_IS_CSS_SHORTHAND_PROPERTY (property)) + { + GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (property); + gboolean animatable = FALSE; + guint i; + + for (i = 0; i < _gtk_css_shorthand_property_get_n_subproperties (shorthand); i++) + { + GtkCssStyleProperty *child = _gtk_css_shorthand_property_get_subproperty (shorthand, i); + GtkCssValue *sub = _gtk_css_array_value_get_nth (value, i); + + animatable |= keyframes_set_value (keyframes, k, child, sub); + } + + if (!animatable) + _gtk_css_parser_error (parser, "shorthand '%s' cannot be animated", _gtk_style_property_get_name (property)); + } + else if (GTK_IS_CSS_STYLE_PROPERTY (property)) + { + if (!keyframes_set_value (keyframes, k, GTK_CSS_STYLE_PROPERTY (property), value)) + _gtk_css_parser_error (parser, "Cannot animate property '%s'", _gtk_style_property_get_name (property)); + } + else + { + g_assert_not_reached (); + } + + _gtk_css_value_unref (value); + + return TRUE; +} + +static gboolean +parse_block (GtkCssKeyframes *keyframes, + guint k, + GtkCssParser *parser) +{ + if (!_gtk_css_parser_try (parser, "{", TRUE)) + { + _gtk_css_parser_error (parser, "Expected closing bracket after keyframes block"); + return FALSE; + } + + while (!_gtk_css_parser_try (parser, "}", TRUE)) + { + if (!parse_declaration (keyframes, k, parser)) + _gtk_css_parser_resync (parser, TRUE, '}'); + + if (_gtk_css_parser_is_eof (parser)) + { + _gtk_css_parser_error (parser, "Expected closing '}' after keyframes block"); + return FALSE; + } + } + + return TRUE; +} + +GtkCssKeyframes * +_gtk_css_keyframes_parse (GtkCssParser *parser) +{ + GtkCssKeyframes *keyframes; + double progress; + guint k; + + g_return_val_if_fail (parser != NULL, NULL); + + keyframes = gtk_css_keyframes_new (); + + while (!_gtk_css_parser_begins_with (parser, '}')) + { + if (_gtk_css_parser_try (parser, "from", TRUE)) + progress = 0; + else if (_gtk_css_parser_try (parser, "to", TRUE)) + progress = 1; + else if (_gtk_css_parser_try_double (parser, &progress) && + _gtk_css_parser_try (parser, "%", TRUE)) + { + if (progress < 0 || progress > 100) + { + /* XXX: should we skip over the block here? */ + _gtk_css_parser_error (parser, "percentages must be between 0%% and 100%%"); + _gtk_css_keyframes_unref (keyframes); + return NULL; + } + progress /= 100; + } + else + { + _gtk_css_parser_error (parser, "expected a percentage"); + _gtk_css_keyframes_unref (keyframes); + return NULL; + } + + k = gtk_css_keyframes_add_keyframe (keyframes, progress); + + if (!parse_block (keyframes, k, parser)) + { + _gtk_css_keyframes_unref (keyframes); + return NULL; + } + } + + return keyframes; +} + +static int +compare_property_by_name (gconstpointer a, + gconstpointer b, + gpointer data) +{ + GtkCssKeyframes *keyframes = data; + + return strcmp (_gtk_style_property_get_name (GTK_STYLE_PROPERTY ( + _gtk_css_style_property_lookup_by_id (keyframes->property_ids[*(const guint *) a]))), + _gtk_style_property_get_name (GTK_STYLE_PROPERTY ( + _gtk_css_style_property_lookup_by_id (keyframes->property_ids[*(const guint *) b])))); +} + +void +_gtk_css_keyframes_print (GtkCssKeyframes *keyframes, + GString *string) +{ + guint k, p; + guint *sorted; + + g_return_if_fail (keyframes != NULL); + g_return_if_fail (string != NULL); + + sorted = g_new (guint, keyframes->n_properties); + for (p = 0; p < keyframes->n_properties; p++) + sorted[p] = p; + g_qsort_with_data (sorted, keyframes->n_properties, sizeof (guint), compare_property_by_name, keyframes); + + for (k = 0; k < keyframes->n_keyframes; k++) + { + /* useful for 0% and 100% which might be empty */ + gboolean opened = FALSE; + + for (p = 0; p < keyframes->n_properties; p++) + { + if (KEYFRAMES_VALUE (keyframes, k, sorted[p]) == NULL) + continue; + + if (!opened) + { + if (keyframes->keyframe_progress[k] == 0.0) + g_string_append (string, " from {\n"); + else if (keyframes->keyframe_progress[k] == 1.0) + g_string_append (string, " to {\n"); + else + g_string_append_printf (string, " %g%% {\n", keyframes->keyframe_progress[k] * 100); + opened = TRUE; + } + + g_string_append_printf (string, " %s: ", _gtk_style_property_get_name ( + GTK_STYLE_PROPERTY ( + _gtk_css_style_property_lookup_by_id ( + keyframes->property_ids[sorted[p]])))); + _gtk_css_value_print (KEYFRAMES_VALUE (keyframes, k, sorted[p]), string); + g_string_append (string, ";\n"); + } + + if (opened) + g_string_append (string, " }\n"); + } + + g_free (sorted); +} + +GtkCssKeyframes * +_gtk_css_keyframes_compute (GtkCssKeyframes *keyframes, + GtkStyleContext *context) +{ + GtkCssKeyframes *resolved; + guint k, p; + + g_return_val_if_fail (keyframes != NULL, NULL); + g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL); + + resolved = gtk_css_keyframes_new (); + resolved->n_keyframes = keyframes->n_keyframes; + resolved->keyframe_progress = g_memdup (keyframes->keyframe_progress, keyframes->n_keyframes * sizeof (double)); + resolved->n_properties = keyframes->n_properties; + resolved->property_ids = g_memdup (keyframes->property_ids, keyframes->n_properties * sizeof (guint)); + resolved->values = g_new0 (GtkCssValue *, resolved->n_keyframes * resolved->n_properties); + + for (p = 0; p < resolved->n_properties; p++) + { + for (k = 0; k < resolved->n_keyframes; k++) + { + if (KEYFRAMES_VALUE (keyframes, k, p) == NULL) + continue; + + KEYFRAMES_VALUE (resolved, k, p) = _gtk_css_value_compute (KEYFRAMES_VALUE (keyframes, k, p), + resolved->property_ids[p], + context, + NULL); + } + } + + return resolved; +} + +guint +_gtk_css_keyframes_get_n_properties (GtkCssKeyframes *keyframes) +{ + g_return_val_if_fail (keyframes != NULL, 0); + + return keyframes->n_properties; +} + +guint +_gtk_css_keyframes_get_property_id (GtkCssKeyframes *keyframes, + guint id) +{ + g_return_val_if_fail (keyframes != NULL, 0); + g_return_val_if_fail (id < keyframes->n_properties, 0); + + return keyframes->property_ids[id]; +} + +GtkCssValue * +_gtk_css_keyframes_get_value (GtkCssKeyframes *keyframes, + guint id, + double progress, + GtkCssValue *default_value) +{ + GtkCssValue *start_value, *end_value, *result; + double start_progress, end_progress; + guint k; + + g_return_val_if_fail (keyframes != NULL, 0); + g_return_val_if_fail (id < keyframes->n_properties, 0); + + start_value = default_value; + start_progress = 0.0; + end_value = default_value; + end_progress = 1.0; + + for (k = 0; k < keyframes->n_keyframes; k++) + { + if (KEYFRAMES_VALUE (keyframes, k, id) == NULL) + continue; + + if (keyframes->keyframe_progress[k] == progress) + { + return _gtk_css_value_ref (KEYFRAMES_VALUE (keyframes, k, id)); + } + else if (keyframes->keyframe_progress[k] < progress) + { + start_value = KEYFRAMES_VALUE (keyframes, k, id); + start_progress = keyframes->keyframe_progress[k]; + } + else + { + end_value = KEYFRAMES_VALUE (keyframes, k, id); + end_progress = keyframes->keyframe_progress[k]; + break; + } + } + + progress = (progress - start_progress) / (end_progress - start_progress); + + result = _gtk_css_value_transition (start_value, + end_value, + keyframes->property_ids[id], + progress); + + /* XXX: Dear spec, what's the correct thing to do here? */ + if (result == NULL) + return _gtk_css_value_ref (start_value); + + return result; +} + diff --git a/gtk/gtkcsskeyframesprivate.h b/gtk/gtkcsskeyframesprivate.h new file mode 100644 index 0000000000..b08ce09083 --- /dev/null +++ b/gtk/gtkcsskeyframesprivate.h @@ -0,0 +1,52 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + * + * Authors: Alexander Larsson <alexl@gnome.org> + */ + +#ifndef __GTK_CSS_KEYFRAMES_PRIVATE_H__ +#define __GTK_CSS_KEYFRAMES_PRIVATE_H__ + +#include "gtkcssparserprivate.h" +#include "gtkcssvalueprivate.h" +#include "gtktypes.h" + +G_BEGIN_DECLS + +typedef struct _GtkCssKeyframes GtkCssKeyframes; + +GtkCssKeyframes * _gtk_css_keyframes_parse (GtkCssParser *parser); + +GtkCssKeyframes * _gtk_css_keyframes_ref (GtkCssKeyframes *keyframes); +void _gtk_css_keyframes_unref (GtkCssKeyframes *keyframes); + +void _gtk_css_keyframes_print (GtkCssKeyframes *keyframes, + GString *string); + +GtkCssKeyframes * _gtk_css_keyframes_compute (GtkCssKeyframes *keyframes, + GtkStyleContext *context); + +guint _gtk_css_keyframes_get_n_properties (GtkCssKeyframes *keyframes); +guint _gtk_css_keyframes_get_property_id (GtkCssKeyframes *keyframes, + guint id); +GtkCssValue * _gtk_css_keyframes_get_value (GtkCssKeyframes *keyframes, + guint id, + double progress, + GtkCssValue *default_value); + +G_END_DECLS + +#endif /* __GTK_CSS_KEYFRAMES_PRIVATE_H__ */ diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index f0f4eadab6..5526167248 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -27,11 +27,12 @@ #include "gtkbitmaskprivate.h" #include "gtkcssarrayvalueprivate.h" -#include "gtkcssstylefuncsprivate.h" +#include "gtkcsskeyframesprivate.h" #include "gtkcssparserprivate.h" #include "gtkcsssectionprivate.h" #include "gtkcssselectorprivate.h" #include "gtkcssshorthandpropertyprivate.h" +#include "gtkcssstylefuncsprivate.h" #include "gtksymboliccolor.h" #include "gtkstyleprovider.h" #include "gtkstylecontextprivate.h" @@ -1007,6 +1008,7 @@ struct _GtkCssProviderPrivate GScanner *scanner; GHashTable *symbolic_colors; + GHashTable *keyframes; GArray *rulesets; GResource *resource; @@ -1423,6 +1425,9 @@ gtk_css_provider_init (GtkCssProvider *css_provider) priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) gtk_symbolic_color_unref); + priv->keyframes = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) _gtk_css_value_unref); } static void @@ -1666,8 +1671,8 @@ gtk_css_provider_finalize (GObject *object) g_array_free (priv->rulesets, TRUE); - if (priv->symbolic_colors) - g_hash_table_destroy (priv->symbolic_colors); + g_hash_table_destroy (priv->symbolic_colors); + g_hash_table_destroy (priv->keyframes); if (priv->resource) { @@ -1797,6 +1802,7 @@ gtk_css_provider_reset (GtkCssProvider *css_provider) } g_hash_table_remove_all (priv->symbolic_colors); + g_hash_table_remove_all (priv->keyframes); for (i = 0; i < priv->rulesets->len; i++) gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i)); @@ -2083,6 +2089,71 @@ skip_semicolon: return TRUE; } +static gboolean +parse_keyframes (GtkCssScanner *scanner) +{ + GtkCssKeyframes *keyframes; + char *name; + + gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + + if (!_gtk_css_parser_try (scanner->parser, "@keyframes", TRUE)) + { + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + return FALSE; + } + + name = _gtk_css_parser_try_ident (scanner->parser, TRUE); + if (name == NULL) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected name for keyframes"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + goto exit; + } + + if (!_gtk_css_parser_try (scanner->parser, "{", TRUE)) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "Expected '{' for keyframes"); + _gtk_css_parser_resync (scanner->parser, TRUE, 0); + g_free (name); + goto exit; + } + + keyframes = _gtk_css_keyframes_parse (scanner->parser); + if (keyframes == NULL) + { + _gtk_css_parser_resync (scanner->parser, TRUE, '}'); + g_free (name); + goto exit; + } + + g_hash_table_insert (scanner->provider->priv->keyframes, name, keyframes); + + if (!_gtk_css_parser_try (scanner->parser, "}", TRUE)) + { + gtk_css_provider_error_literal (scanner->provider, + scanner, + GTK_CSS_PROVIDER_ERROR, + GTK_CSS_PROVIDER_ERROR_SYNTAX, + "expected '}' after declarations"); + if (!_gtk_css_parser_is_eof (scanner->parser)) + _gtk_css_parser_resync (scanner->parser, FALSE, 0); + } + +exit: + gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_KEYFRAMES); + + return TRUE; +} + static void parse_at_keyword (GtkCssScanner *scanner) { @@ -2092,6 +2163,8 @@ parse_at_keyword (GtkCssScanner *scanner) return; if (parse_binding_set (scanner)) return; + if (parse_keyframes (scanner)) + return; else { @@ -2964,6 +3037,33 @@ gtk_css_provider_print_colors (GHashTable *colors, g_list_free (keys); } +static void +gtk_css_provider_print_keyframes (GHashTable *keyframes, + GString *str) +{ + GList *keys, *walk; + + keys = g_hash_table_get_keys (keyframes); + /* so the output is identical for identical styles */ + keys = g_list_sort (keys, (GCompareFunc) strcmp); + + for (walk = keys; walk; walk = walk->next) + { + const char *name = walk->data; + GtkCssKeyframes *keyframe = g_hash_table_lookup (keyframes, (gpointer) name); + + if (str->len > 0) + g_string_append (str, "\n"); + g_string_append (str, "@keyframes "); + g_string_append (str, name); + g_string_append (str, " {\n"); + _gtk_css_keyframes_print (keyframe, str); + g_string_append (str, "}\n"); + } + + g_list_free (keys); +} + /** * gtk_css_provider_to_string: * @provider: the provider to write to a string @@ -2994,6 +3094,7 @@ gtk_css_provider_to_string (GtkCssProvider *provider) str = g_string_new (""); gtk_css_provider_print_colors (priv->symbolic_colors, str); + gtk_css_provider_print_keyframes (priv->keyframes, str); for (i = 0; i < priv->rulesets->len; i++) { diff --git a/gtk/gtkcsssection.h b/gtk/gtkcsssection.h index 7901a43d03..b26c587371 100644 --- a/gtk/gtkcsssection.h +++ b/gtk/gtkcsssection.h @@ -40,6 +40,9 @@ G_BEGIN_DECLS * @GTK_CSS_SECTION_DECLARATION: The section defines the declaration of * a CSS variable. * @GTK_CSS_SECTION_VALUE: The section defines the value of a CSS declaration. + * @GTK_CSS_SECTION_KEYFRAMES: The section defines keyframes. See <ulink + * url="http://dev.w3.org/csswg/css3-animations/#keyframes">CSS + * animations</ulink> for details. Since 3.6 * * The different types of sections indicate parts of a CSS document as * parsed by GTK's CSS parser. They are oriented towards the CSS grammar @@ -60,7 +63,8 @@ typedef enum GTK_CSS_SECTION_RULESET, GTK_CSS_SECTION_SELECTOR, GTK_CSS_SECTION_DECLARATION, - GTK_CSS_SECTION_VALUE + GTK_CSS_SECTION_VALUE, + GTK_CSS_SECTION_KEYFRAMES } GtkCssSectionType; typedef struct _GtkCssSection GtkCssSection; |