[gtk+/wip/css: 1/37] cssprovider: Add parsing support for @keyframes
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/css: 1/37] cssprovider: Add parsing support for @keyframes
- Date: Mon, 17 Sep 2012 15:08:25 +0000 (UTC)
commit 3dcfd1ec9aada13294924d2e43510d98103ade5a
Author: Benjamin Otte <otte redhat com>
Date: Thu Apr 19 03:41:41 2012 +0200
cssprovider: Add parsing support for @keyframes
gtk/Makefile.am | 2 +
gtk/gtkcsskeyframes.c | 521 ++++++++++++++++++++++++++++++++++++++++++
gtk/gtkcsskeyframesprivate.h | 52 +++++
gtk/gtkcssprovider.c | 107 +++++++++-
gtk/gtkcsssection.h | 6 +-
5 files changed, 684 insertions(+), 4 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index afd36c7..1524b4b 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 0000000..f25ccef
--- /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 0000000..b08ce09
--- /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 d4a3196..a50e176 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
{
@@ -2900,6 +2973,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
@@ -2930,6 +3030,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 7901a43..b26c587 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;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]