cssprovider: Add parsing support for @keyframes
This commit is contained in:
parent
3ee837564e
commit
c4cdb33b32
@ -445,6 +445,7 @@ gtk_private_h_sources = \
|
|||||||
gtkcssimagewin32private.h \
|
gtkcssimagewin32private.h \
|
||||||
gtkcssinheritvalueprivate.h \
|
gtkcssinheritvalueprivate.h \
|
||||||
gtkcssinitialvalueprivate.h \
|
gtkcssinitialvalueprivate.h \
|
||||||
|
gtkcsskeyframesprivate.h \
|
||||||
gtkcsslookupprivate.h \
|
gtkcsslookupprivate.h \
|
||||||
gtkcssmatcherprivate.h \
|
gtkcssmatcherprivate.h \
|
||||||
gtkcssnumbervalueprivate.h \
|
gtkcssnumbervalueprivate.h \
|
||||||
@ -665,6 +666,7 @@ gtk_base_c_sources = \
|
|||||||
gtkcssimagewin32.c \
|
gtkcssimagewin32.c \
|
||||||
gtkcssinheritvalue.c \
|
gtkcssinheritvalue.c \
|
||||||
gtkcssinitialvalue.c \
|
gtkcssinitialvalue.c \
|
||||||
|
gtkcsskeyframes.c \
|
||||||
gtkcsslookup.c \
|
gtkcsslookup.c \
|
||||||
gtkcssmatcher.c \
|
gtkcssmatcher.c \
|
||||||
gtkcssnumbervalue.c \
|
gtkcssnumbervalue.c \
|
||||||
|
521
gtk/gtkcsskeyframes.c
Normal file
521
gtk/gtkcsskeyframes.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
52
gtk/gtkcsskeyframesprivate.h
Normal file
52
gtk/gtkcsskeyframesprivate.h
Normal file
@ -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__ */
|
@ -27,11 +27,12 @@
|
|||||||
|
|
||||||
#include "gtkbitmaskprivate.h"
|
#include "gtkbitmaskprivate.h"
|
||||||
#include "gtkcssarrayvalueprivate.h"
|
#include "gtkcssarrayvalueprivate.h"
|
||||||
#include "gtkcssstylefuncsprivate.h"
|
#include "gtkcsskeyframesprivate.h"
|
||||||
#include "gtkcssparserprivate.h"
|
#include "gtkcssparserprivate.h"
|
||||||
#include "gtkcsssectionprivate.h"
|
#include "gtkcsssectionprivate.h"
|
||||||
#include "gtkcssselectorprivate.h"
|
#include "gtkcssselectorprivate.h"
|
||||||
#include "gtkcssshorthandpropertyprivate.h"
|
#include "gtkcssshorthandpropertyprivate.h"
|
||||||
|
#include "gtkcssstylefuncsprivate.h"
|
||||||
#include "gtksymboliccolor.h"
|
#include "gtksymboliccolor.h"
|
||||||
#include "gtkstyleprovider.h"
|
#include "gtkstyleprovider.h"
|
||||||
#include "gtkstylecontextprivate.h"
|
#include "gtkstylecontextprivate.h"
|
||||||
@ -1007,6 +1008,7 @@ struct _GtkCssProviderPrivate
|
|||||||
GScanner *scanner;
|
GScanner *scanner;
|
||||||
|
|
||||||
GHashTable *symbolic_colors;
|
GHashTable *symbolic_colors;
|
||||||
|
GHashTable *keyframes;
|
||||||
|
|
||||||
GArray *rulesets;
|
GArray *rulesets;
|
||||||
GResource *resource;
|
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,
|
priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||||
(GDestroyNotify) g_free,
|
(GDestroyNotify) g_free,
|
||||||
(GDestroyNotify) gtk_symbolic_color_unref);
|
(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
|
static void
|
||||||
@ -1666,8 +1671,8 @@ gtk_css_provider_finalize (GObject *object)
|
|||||||
|
|
||||||
g_array_free (priv->rulesets, TRUE);
|
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)
|
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->symbolic_colors);
|
||||||
|
g_hash_table_remove_all (priv->keyframes);
|
||||||
|
|
||||||
for (i = 0; i < priv->rulesets->len; i++)
|
for (i = 0; i < priv->rulesets->len; i++)
|
||||||
gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));
|
gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));
|
||||||
@ -2083,6 +2089,71 @@ skip_semicolon:
|
|||||||
return TRUE;
|
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
|
static void
|
||||||
parse_at_keyword (GtkCssScanner *scanner)
|
parse_at_keyword (GtkCssScanner *scanner)
|
||||||
{
|
{
|
||||||
@ -2092,6 +2163,8 @@ parse_at_keyword (GtkCssScanner *scanner)
|
|||||||
return;
|
return;
|
||||||
if (parse_binding_set (scanner))
|
if (parse_binding_set (scanner))
|
||||||
return;
|
return;
|
||||||
|
if (parse_keyframes (scanner))
|
||||||
|
return;
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -2964,6 +3037,33 @@ gtk_css_provider_print_colors (GHashTable *colors,
|
|||||||
g_list_free (keys);
|
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:
|
* gtk_css_provider_to_string:
|
||||||
* @provider: the provider to write to a string
|
* @provider: the provider to write to a string
|
||||||
@ -2994,6 +3094,7 @@ gtk_css_provider_to_string (GtkCssProvider *provider)
|
|||||||
str = g_string_new ("");
|
str = g_string_new ("");
|
||||||
|
|
||||||
gtk_css_provider_print_colors (priv->symbolic_colors, str);
|
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++)
|
for (i = 0; i < priv->rulesets->len; i++)
|
||||||
{
|
{
|
||||||
|
@ -40,6 +40,9 @@ G_BEGIN_DECLS
|
|||||||
* @GTK_CSS_SECTION_DECLARATION: The section defines the declaration of
|
* @GTK_CSS_SECTION_DECLARATION: The section defines the declaration of
|
||||||
* a CSS variable.
|
* a CSS variable.
|
||||||
* @GTK_CSS_SECTION_VALUE: The section defines the value of a CSS declaration.
|
* @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
|
* 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
|
* 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_RULESET,
|
||||||
GTK_CSS_SECTION_SELECTOR,
|
GTK_CSS_SECTION_SELECTOR,
|
||||||
GTK_CSS_SECTION_DECLARATION,
|
GTK_CSS_SECTION_DECLARATION,
|
||||||
GTK_CSS_SECTION_VALUE
|
GTK_CSS_SECTION_VALUE,
|
||||||
|
GTK_CSS_SECTION_KEYFRAMES
|
||||||
} GtkCssSectionType;
|
} GtkCssSectionType;
|
||||||
|
|
||||||
typedef struct _GtkCssSection GtkCssSection;
|
typedef struct _GtkCssSection GtkCssSection;
|
||||||
|
Loading…
Reference in New Issue
Block a user