From 09f91ff6eab3d617be88e11bd17c226fa4d23aed Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 8 Feb 2010 15:45:43 +0000 Subject: [PATCH 1/4] script: Use a node when resolving an animation mode Instead of taking a string and duplicating the "is it a string or an integer" check in both Alpha and Animation, the function in ClutterScript that resolves the animation mode values should take a JsonNode and do all the checks it needs. --- clutter/clutter-alpha.c | 21 ++++---------- clutter/clutter-animation.c | 25 ++++------------ clutter/clutter-script-parser.c | 49 ++++++++++++++++++++------------ clutter/clutter-script-private.h | 2 +- 4 files changed, 42 insertions(+), 55 deletions(-) diff --git a/clutter/clutter-alpha.c b/clutter/clutter-alpha.c index 0a144c476..9a41de6aa 100644 --- a/clutter/clutter-alpha.c +++ b/clutter/clutter-alpha.c @@ -302,25 +302,14 @@ clutter_alpha_parse_custom_node (ClutterScriptable *scriptable, */ if (strncmp (name, "mode", 4) == 0) { - if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE) - return FALSE; + gulong mode; + + mode = clutter_script_resolve_animation_mode (node); g_value_init (value, G_TYPE_ULONG); + g_value_set_ulong (value, mode); - if (json_node_get_value_type (node) == G_TYPE_INT64) - { - g_value_set_ulong (value, json_node_get_int (node)); - return TRUE; - } - else if (json_node_get_value_type (node) == G_TYPE_STRING) - { - const gchar *str = json_node_get_string (node); - gulong mode; - - mode = clutter_script_resolve_animation_mode (str); - g_value_set_ulong (value, mode); - return TRUE; - } + return TRUE; } return FALSE; diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c index 7139e77e6..40590acb9 100644 --- a/clutter/clutter-animation.c +++ b/clutter/clutter-animation.c @@ -403,29 +403,14 @@ clutter_animation_parse_custom_node (ClutterScriptable *scriptable, { if (strncmp (name, "mode", 4) == 0) { - if (json_node_get_node_type (node) != JSON_NODE_VALUE) - return FALSE; + gulong mode; + + mode = clutter_script_resolve_animation_mode (node); g_value_init (value, G_TYPE_ULONG); + g_value_set_ulong (value, mode); - if (json_node_get_value_type (node) == G_TYPE_INT64) - { - g_value_set_ulong (value, json_node_get_int (node)); - return TRUE; - } - else if (json_node_get_value_type (node) == G_TYPE_STRING) - { - const gchar *str = json_node_get_string (node); - gulong mode = CLUTTER_LINEAR; - - mode = clutter_script_resolve_animation_mode (str); - g_value_set_ulong (value, mode); - - return TRUE; - } - else - g_warning ("Expected an integer id or a string id for " - "the ClutterAnimation mode property"); + return TRUE; } return FALSE; diff --git a/clutter/clutter-script-parser.c b/clutter/clutter-script-parser.c index a33702c7e..37585fecd 100644 --- a/clutter/clutter-script-parser.c +++ b/clutter/clutter-script-parser.c @@ -769,27 +769,40 @@ static const struct static const gint n_animation_modes = G_N_ELEMENTS (animation_modes); gulong -clutter_script_resolve_animation_mode (const gchar *name) +clutter_script_resolve_animation_mode (JsonNode *node) { - gint i, res = 0; + gint i, res = CLUTTER_CUSTOM_MODE; - /* XXX - we might be able to optimize by changing the ordering - * of the animation_modes array, e.g. - * - special casing linear - * - tokenizing ('ease', 'In', 'Sine') and matching on token - * - binary searching? - */ - for (i = 0; i < n_animation_modes; i++) + if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE) + return CLUTTER_CUSTOM_MODE; + + if (json_node_get_value_type (node) == G_TYPE_INT64) + return json_node_get_int (node); + + if (json_node_get_value_type (node) == G_TYPE_STRING) { - if (strcmp (animation_modes[i].name, name) == 0) - return animation_modes[i].mode; + const gchar *name = json_node_get_string (node); + + /* XXX - we might be able to optimize by changing the ordering + * of the animation_modes array, e.g. + * - special casing linear + * - tokenizing ('ease', 'In', 'Sine') and matching on token + * - binary searching? + */ + for (i = 0; i < n_animation_modes; i++) + { + if (strcmp (animation_modes[i].name, name) == 0) + return animation_modes[i].mode; + } + + if (clutter_script_enum_from_string (CLUTTER_TYPE_ANIMATION_MODE, + name, + &res)) + return res; + + g_warning ("Unable to find the animation mode '%s'", name); } - if (clutter_script_enum_from_string (CLUTTER_TYPE_ANIMATION_MODE, name, &res)) - return res; - - g_warning ("Unable to find the animation mode '%s'", name); - return CLUTTER_CUSTOM_MODE; } @@ -850,8 +863,8 @@ _clutter_script_parse_alpha (ClutterScript *script, } val = json_object_get_member (object, "mode"); - if (val && json_node_get_string (val) != NULL) - mode = clutter_script_resolve_animation_mode (json_node_get_string (val)); + if (val) + mode = clutter_script_resolve_animation_mode (val); if (mode == CLUTTER_CUSTOM_MODE) { diff --git a/clutter/clutter-script-private.h b/clutter/clutter-script-private.h index 601a93b73..3dc73786a 100644 --- a/clutter/clutter-script-private.h +++ b/clutter/clutter-script-private.h @@ -103,7 +103,7 @@ gboolean clutter_script_parse_node (ClutterScript *script, GType clutter_script_get_type_from_symbol (const gchar *symbol); GType clutter_script_get_type_from_class (const gchar *name); -gulong clutter_script_resolve_animation_mode (const gchar *namer); +gulong clutter_script_resolve_animation_mode (JsonNode *node); gboolean clutter_script_enum_from_string (GType gtype, const gchar *string, From 790a13c0d9d2249747cfb81dcb13f83c6cc2cec1 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 8 Feb 2010 15:47:46 +0000 Subject: [PATCH 2/4] animator: Allow retrieving type property type from a key When asking a key for its target value we also ask the developer to pass in an initialized GValue - but we don't make it easy to know the type of the GValue. A developer has to ask the GObject class for the GParamSpec and then initialize the GValue, instead. Since we know the type of the GValue we should provide a getter for it. We should also allow developers to throw at us GValue with compatible and transformable types. Finally, all the accessors should be constified. --- clutter/clutter-animator.c | 93 ++++++++++++++++++++++++++++---------- clutter/clutter-animator.h | 13 +++--- 2 files changed, 75 insertions(+), 31 deletions(-) diff --git a/clutter/clutter-animator.c b/clutter/clutter-animator.c index f9fd309de..ae5a775da 100644 --- a/clutter/clutter-animator.c +++ b/clutter/clutter-animator.c @@ -1340,24 +1340,25 @@ clutter_animator_key_get_type (void) /** * clutter_animator_key_get_object: - * @animator_key: a #ClutterAnimatorKey + * @key: a #ClutterAnimatorKey * * Retrieves the object a key applies to. * - * Return value: the object an animator_key exist for. + * Return value: (transfer none): the object an animator_key exist for. * * Since: 1.2 */ GObject * -clutter_animator_key_get_object (ClutterAnimatorKey *animator_key) +clutter_animator_key_get_object (const ClutterAnimatorKey *key) { - g_return_val_if_fail (animator_key, NULL); - return animator_key->object; + g_return_val_if_fail (key != NULL, NULL); + + return key->object; } /** * clutter_animator_key_get_property_name: - * @animator_key: a #ClutterAnimatorKey + * @key: a #ClutterAnimatorKey * * Retrieves the name of the property a key applies to. * @@ -1366,16 +1367,37 @@ clutter_animator_key_get_object (ClutterAnimatorKey *animator_key) * Since: 1.2 */ G_CONST_RETURN gchar * -clutter_animator_key_get_property_name (ClutterAnimatorKey *animator_key) +clutter_animator_key_get_property_name (const ClutterAnimatorKey *key) { - g_return_val_if_fail (animator_key != NULL, NULL); + g_return_val_if_fail (key != NULL, NULL); - return animator_key->property_name; + return key->property_name; +} + +/** + * clutter_animator_key_get_property_type: + * @key: a #ClutterAnimatorKey + * + * Retrieves the #GType of the property a key applies to + * + * You can use this type to initialize the #GValue to pass to + * clutter_animator_key_get_value() + * + * Return value: the #GType of the property + * + * Since: 1.2 + */ +GType +clutter_animator_key_get_property_type (const ClutterAnimatorKey *key) +{ + g_return_val_if_fail (key != NULL, G_TYPE_INVALID); + + return G_VALUE_TYPE (&key->value); } /** * clutter_animator_key_get_mode: - * @animator_key: a #ClutterAnimatorKey + * @key: a #ClutterAnimatorKey * * Retrieves the mode of a #ClutterAnimator key, for the first key of a * property for an object this represents the whether the animation is @@ -1387,16 +1409,16 @@ clutter_animator_key_get_property_name (ClutterAnimatorKey *animator_key) * Since: 1.2 */ gulong -clutter_animator_key_get_mode (ClutterAnimatorKey *animator_key) +clutter_animator_key_get_mode (const ClutterAnimatorKey *key) { - g_return_val_if_fail (animator_key != NULL, 0); + g_return_val_if_fail (key != NULL, 0); - return animator_key->mode; + return key->mode; } /** * clutter_animator_key_get_progress: - * @animator_key: a #ClutterAnimatorKey + * @key: a #ClutterAnimatorKey * * Retrieves the progress of an clutter_animator_key * @@ -1405,31 +1427,52 @@ clutter_animator_key_get_mode (ClutterAnimatorKey *animator_key) * Since: 1.2 */ gdouble -clutter_animator_key_get_progress (ClutterAnimatorKey *animator_key) +clutter_animator_key_get_progress (const ClutterAnimatorKey *key) { - g_return_val_if_fail (animator_key != NULL, 0.0); + g_return_val_if_fail (key != NULL, 0.0); - return animator_key->progress; + return key->progress; } /** * clutter_animator_key_get_value: - * @animator_key: a #ClutterAnimatorKey + * @key: a #ClutterAnimatorKey * @value: a #GValue initialized with the correct type for the animator key * * Retrieves a copy of the value for a #ClutterAnimatorKey. * - * The passed in GValue needs to be already initialized for the value type. + * The passed in #GValue needs to be already initialized for the value + * type of the key or to a type that allow transformation from the value + * type of the key. * - * Use g_value_unset() when done + * Use g_value_unset() when done. + * + * Return value: %TRUE if the passed #GValue was successfully set, and + * %FALSE otherwise * * Since: 1.2 */ -void -clutter_animator_key_get_value (ClutterAnimatorKey *animator_key, - GValue *value) +gboolean +clutter_animator_key_get_value (const ClutterAnimatorKey *key, + GValue *value) { - g_return_if_fail (animator_key != NULL); + GType gtype; - g_value_copy (&animator_key->value, value); + g_return_val_if_fail (key != NULL, FALSE); + + gtype = G_VALUE_TYPE (&key->value); + + if (g_value_type_compatible (gtype, G_VALUE_TYPE (value))) + { + g_value_copy (&key->value, value); + return TRUE; + } + + if (g_value_type_transformable (gtype, G_VALUE_TYPE (value))) + { + if (g_value_transform (&key->value, value)) + return TRUE; + } + + return FALSE; } diff --git a/clutter/clutter-animator.h b/clutter/clutter-animator.h index d530c8432..3d4e86d02 100644 --- a/clutter/clutter-animator.h +++ b/clutter/clutter-animator.h @@ -154,12 +154,13 @@ void clutter_animator_property_set_interpolation (ClutterAnimato ClutterInterpolation interpolation); GType clutter_animator_key_get_type (void) G_GNUC_CONST; -GObject * clutter_animator_key_get_object (ClutterAnimatorKey *animator_key); -G_CONST_RETURN gchar *clutter_animator_key_get_property_name (ClutterAnimatorKey *animator_key); -gulong clutter_animator_key_get_mode (ClutterAnimatorKey *animator_key); -gdouble clutter_animator_key_get_progress (ClutterAnimatorKey *animator_key); -void clutter_animator_key_get_value (ClutterAnimatorKey *animator_key, - GValue *value); +GObject * clutter_animator_key_get_object (const ClutterAnimatorKey *animator_key); +G_CONST_RETURN gchar *clutter_animator_key_get_property_name (const ClutterAnimatorKey *animator_key); +GType clutter_animator_key_get_property_type (const ClutterAnimatorKey *animator_key); +gulong clutter_animator_key_get_mode (const ClutterAnimatorKey *animator_key); +gdouble clutter_animator_key_get_progress (const ClutterAnimatorKey *animator_key); +gboolean clutter_animator_key_get_value (const ClutterAnimatorKey *animator_key, + GValue *value); G_END_DECLS From 4dd11d6915f75ba62b8292c3138d4cc11a28fb21 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 8 Feb 2010 15:52:18 +0000 Subject: [PATCH 3/4] animator: Provide a ClutterScript parser The whole point of having the Animator class is that the developer can describe a complex animation using ClutterScript. Hence, ClutterAnimator should hook into the Script machinery and parse a specific description format for its keys. --- .gitignore | 2 + clutter/clutter-animator.c | 303 +++++++++++++++++++++++++++--- tests/conform/Makefile.am | 1 + tests/conform/test-animator.c | 88 +++++++++ tests/conform/test-conform-main.c | 2 + tests/data/Makefile.am | 2 + tests/data/test-animator-1.json | 5 + tests/data/test-animator-2.json | 29 +++ 8 files changed, 409 insertions(+), 23 deletions(-) create mode 100644 tests/conform/test-animator.c create mode 100644 tests/data/test-animator-1.json create mode 100644 tests/data/test-animator-2.json diff --git a/.gitignore b/.gitignore index a1d164951..3b1a150f8 100644 --- a/.gitignore +++ b/.gitignore @@ -245,6 +245,8 @@ TAGS /tests/conform/test-behaviours /tests/conform/test-cogl-sub-texture /tests/conform/test-cogl-multitexture +/tests/conform/test-animator-base +/tests/conform/test-animator-properties /tests/micro-bench/test-text-perf /tests/micro-bench/test-text /tests/micro-bench/test-picking diff --git a/clutter/clutter-animator.c b/clutter/clutter-animator.c index ae5a775da..86f6ebd6d 100644 --- a/clutter/clutter-animator.c +++ b/clutter/clutter-animator.c @@ -54,8 +54,8 @@ #include "clutter-enum-types.h" #include "clutter-interval.h" #include "clutter-private.h" - -G_DEFINE_TYPE (ClutterAnimator, clutter_animator, G_TYPE_OBJECT); +#include "clutter-script-private.h" +#include "clutter-scriptable.h" #define CLUTTER_ANIMATOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ANIMATOR, ClutterAnimatorPrivate)) @@ -104,6 +104,13 @@ enum PROP_DURATION }; +static void clutter_scriptable_init (ClutterScriptableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ClutterAnimator, + clutter_animator, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, + clutter_scriptable_init)); /** * clutter_animator_new: * @@ -270,10 +277,10 @@ object_disappeared (gpointer data, static ClutterAnimatorKey * clutter_animator_key_new (ClutterAnimator *animator, - gdouble progress, GObject *object, - guint mode, - const gchar *property_name) + const gchar *property_name, + gdouble progress, + guint mode) { ClutterAnimatorKey *animator_key; @@ -771,10 +778,16 @@ clutter_animator_get_timeline (ClutterAnimator *animator) ClutterTimeline * clutter_animator_run (ClutterAnimator *animator) { + ClutterAnimatorPrivate *priv; + g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL); - clutter_timeline_rewind (animator->priv->timeline); - clutter_timeline_start (animator->priv->timeline); - return animator->priv->timeline; + + priv = animator->priv; + + clutter_timeline_rewind (priv->timeline); + clutter_timeline_start (priv->timeline); + + return priv->timeline; } /** @@ -915,18 +928,47 @@ clutter_animator_set (ClutterAnimator *animator, va_end (args); } +static inline void +clutter_animator_set_key_internal (ClutterAnimator *animator, + ClutterAnimatorKey *key) +{ + ClutterAnimatorPrivate *priv = animator->priv; + GList *old_item; + + old_item = g_list_find_custom (priv->score, key, + sort_actor_prop_progress_func); + + /* replace the key if we already have a similar one */ + if (old_item != NULL) + { + ClutterAnimatorKey *old_key = old_item->data; + + clutter_animator_key_free (old_key); + + priv->score = g_list_remove (priv->score, old_key); + } + + priv->score = g_list_insert_sorted (priv->score, key, + sort_actor_prop_progress_func); +} + /** * clutter_animator_set_key: * @animator: a #ClutterAnimator * @object: a #GObject * @property_name: the property to specify a key for * @mode: the id of the alpha function to use - * @progress: at which stage of the animation this value applies (range 0.0-1.0) + * @progress: the normalized range at which stage of the animation this + * value applies * @value: the value property_name should have at progress. * - * As clutter_animator_set but only for a single key. + * Sets a single key in the #ClutterAnimator for the @property_name of + * @object at @progress. + * + * See also: clutter_animator_set() + * + * Return value: (transfer none): The animator instance * - * Return value: (transfer none): The animator itself. * Since: 1.2 */ ClutterAnimator * @@ -939,7 +981,6 @@ clutter_animator_set_key (ClutterAnimator *animator, { ClutterAnimatorPrivate *priv; ClutterAnimatorKey *animator_key; - GList *old_item; g_return_val_if_fail (CLUTTER_IS_ANIMATOR (animator), NULL); g_return_val_if_fail (G_IS_OBJECT (object), NULL); @@ -949,22 +990,16 @@ clutter_animator_set_key (ClutterAnimator *animator, priv = animator->priv; property_name = g_intern_string (property_name); - animator_key = clutter_animator_key_new (animator, progress, object, mode, - property_name); + animator_key = clutter_animator_key_new (animator, + object, property_name, + progress, + mode); g_value_init (&animator_key->value, G_VALUE_TYPE (value)); g_value_copy (value, &animator_key->value); - if ((old_item = g_list_find_custom (priv->score, animator_key, - sort_actor_prop_progress_func))) - { - ClutterAnimatorKey *old_key = old_item->data; - clutter_animator_key_free (old_key); - animator->priv->score = g_list_remove (animator->priv->score, old_key); - } + clutter_animator_set_key_internal (animator, animator_key); - priv->score = g_list_insert_sorted (priv->score, animator_key, - sort_actor_prop_progress_func); return animator; } @@ -1077,6 +1112,228 @@ again: } } +typedef struct _ParseClosure { + ClutterAnimator *animator; + ClutterScript *script; + + GValue *value; + + gboolean result; +} ParseClosure; + +static ClutterInterpolation +resolve_interpolation (JsonNode *node) +{ + if ((JSON_NODE_TYPE (node) != JSON_NODE_VALUE)) + return CLUTTER_INTERPOLATION_LINEAR; + + if (json_node_get_value_type (node) == G_TYPE_INT64) + { + return json_node_get_int (node); + } + else if (json_node_get_value_type (node) == G_TYPE_STRING) + { + const gchar *str = json_node_get_string (node); + gboolean res; + gint enum_value; + + res = clutter_script_enum_from_string (CLUTTER_TYPE_INTERPOLATION, + str, + &enum_value); + if (res) + return enum_value; + } + + return CLUTTER_INTERPOLATION_LINEAR; +} + +static void +parse_animator_property (JsonArray *array, + guint index_, + JsonNode *element, + gpointer data) +{ + ParseClosure *clos = data; + JsonObject *object; + JsonArray *keys; + GObject *gobject; + const gchar *id, *pname; + GObjectClass *klass; + GParamSpec *pspec; + GSList *valid_keys = NULL; + GList *k; + ClutterInterpolation interpolation = CLUTTER_INTERPOLATION_LINEAR; + gboolean ease_in = FALSE; + + if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT) + { + g_warning ("The 'properties' member of a ClutterAnimator description " + "should be an array of objects, but the element %d of the " + "array is of type '%s'. The element will be ignored.", + index_, + json_node_type_name (element)); + return; + } + + object = json_node_get_object (element); + + if (!json_object_has_member (object, "object") || + !json_object_has_member (object, "name") || + !json_object_has_member (object, "keys")) + { + g_warning ("The property description at index %d is missing one of " + "the mandatory fields: object, name and keys", + index_); + return; + } + + id = json_object_get_string_member (object, "object"); + gobject = clutter_script_get_object (clos->script, id); + if (gobject == NULL) + { + g_warning ("No object with id '%s' has been defined.", id); + return; + } + + pname = json_object_get_string_member (object, "name"); + klass = G_OBJECT_GET_CLASS (gobject); + pspec = g_object_class_find_property (klass, pname); + if (pspec == NULL) + { + g_warning ("The object of type '%s' and name '%s' has no " + "property named '%s'", + G_OBJECT_TYPE_NAME (gobject), + id, + pname); + return; + } + + if (json_object_has_member (object, "ease-in")) + ease_in = json_object_get_boolean_member (object, "ease-in"); + + if (json_object_has_member (object, "interpolation")) + { + JsonNode *node = json_object_get_member (object, "interpolation"); + + interpolation = resolve_interpolation (node); + } + + keys = json_object_get_array_member (object, "keys"); + if (keys == NULL) + { + g_warning ("The property description at index %d has an invalid " + "key field of type '%s' when an array was expected.", + index_, + json_node_type_name (json_object_get_member (object, "keys"))); + return; + } + + valid_keys = NULL; + for (k = json_array_get_elements (keys); + k != NULL; + k = k->next) + { + JsonNode *node = k->data; + JsonArray *key = json_node_get_array (node); + ClutterAnimatorKey *animator_key; + gdouble progress; + gulong mode; + GValue *value; + gboolean res; + + progress = json_array_get_double_element (key, 0); + mode = clutter_script_resolve_animation_mode (json_array_get_element (key, 1)); + + animator_key = clutter_animator_key_new (clos->animator, + gobject, + pname, + progress, + mode); + value = &animator_key->value; + res = clutter_script_parse_node (clos->script, + value, + pname, + json_array_get_element (key, 2), + pspec); + if (!res) + { + g_warning ("Unable to parse the key value for the " + "property '%s' (progress: %.2f) at index %d", + pname, + progress, + index_); + continue; + } + + animator_key->ease_in = ease_in; + animator_key->interpolation = interpolation; + + valid_keys = g_slist_prepend (valid_keys, animator_key); + } + + g_value_init (clos->value, G_TYPE_POINTER); + g_value_set_pointer (clos->value, g_slist_reverse (valid_keys)); + + clos->result = TRUE; +} + +static gboolean +clutter_animator_parse_custom_node (ClutterScriptable *scriptable, + ClutterScript *script, + GValue *value, + const gchar *name, + JsonNode *node) +{ + ParseClosure parse_closure; + + if (strcmp (name, "properties") != 0) + return FALSE; + + if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY) + return FALSE; + + parse_closure.animator = CLUTTER_ANIMATOR (scriptable); + parse_closure.script = script; + parse_closure.value = value; + parse_closure.result = FALSE; + + json_array_foreach_element (json_node_get_array (node), + parse_animator_property, + &parse_closure); + + /* we return TRUE if we had at least one key parsed */ + + return parse_closure.result; +} + +static void +clutter_animator_set_custom_property (ClutterScriptable *scriptable, + ClutterScript *script, + const gchar *name, + const GValue *value) +{ + if (strcmp (name, "properties") == 0) + { + ClutterAnimator *animator = CLUTTER_ANIMATOR (scriptable); + GSList *keys = g_value_get_pointer (value); + GSList *k; + + for (k = keys; k != NULL; k = k->next) + clutter_animator_set_key_internal (animator, k->data); + + g_slist_free (keys); + } + else + g_object_set_property (G_OBJECT (scriptable), name, value); +} + +static void +clutter_scriptable_init (ClutterScriptableIface *iface) +{ + iface->parse_custom_node = clutter_animator_parse_custom_node; + iface->set_custom_property = clutter_animator_set_custom_property; +} + static void clutter_animator_set_property (GObject *gobject, guint prop_id, diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 7b35d27c4..9c7c57fb8 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -45,6 +45,7 @@ test_conformance_SOURCES = \ test-script-parser.c \ test-actor-destroy.c \ test-behaviours.c \ + test-animator.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-animator.c b/tests/conform/test-animator.c new file mode 100644 index 000000000..da17e31d0 --- /dev/null +++ b/tests/conform/test-animator.c @@ -0,0 +1,88 @@ +#include + +#include "test-conform-common.h" + +void +test_animator_properties (TestConformSimpleFixture *fixture, + gconstpointer dummy) +{ + ClutterScript *script = clutter_script_new (); + GObject *animator = NULL; + GError *error = NULL; + gchar *test_file; + GList *keys; + + test_file = clutter_test_get_data_file ("test-animator-2.json"); + clutter_script_load_from_file (script, test_file, &error); + if (g_test_verbose () && error) + g_print ("Error: %s", error->message); + g_assert (error == NULL); + + animator = clutter_script_get_object (script, "animator"); + g_assert (CLUTTER_IS_ANIMATOR (animator)); + + /* get all the keys */ + keys = clutter_animator_get_keys (CLUTTER_ANIMATOR (animator), + NULL, NULL, -1.0); + g_assert_cmpint (g_list_length (keys), ==, 3); + + { + const ClutterAnimatorKey *key = g_list_nth_data (keys, 1); + GValue value = { 0, }; + + g_assert (key != NULL); + + if (g_test_verbose ()) + { + g_print ("keys[1] = \n" + ".object = %s\n" + ".progress = %.2f\n" + ".name = '%s'\n" + ".type = '%s'\n", + clutter_get_script_id (clutter_animator_key_get_object (key)), + clutter_animator_key_get_progress (key), + clutter_animator_key_get_property_name (key), + g_type_name (clutter_animator_key_get_property_type (key))); + } + + g_assert (clutter_animator_key_get_object (key) != NULL); + g_assert_cmpfloat (clutter_animator_key_get_progress (key), ==, 0.2); + g_assert_cmpstr (clutter_animator_key_get_property_name (key), ==, "x"); + + g_assert (clutter_animator_key_get_property_type (key) == G_TYPE_FLOAT); + + g_value_init (&value, G_TYPE_FLOAT); + g_assert (clutter_animator_key_get_value (key, &value)); + g_assert_cmpfloat (g_value_get_float (&value), ==, 150.0); + } + + g_list_free (keys); + g_object_unref (script); + g_free (test_file); +} + +void +test_animator_base (TestConformSimpleFixture *fixture, + gconstpointer dummy) +{ + ClutterScript *script = clutter_script_new (); + GObject *animator = NULL; + GError *error = NULL; + guint duration = 0; + gchar *test_file; + + test_file = clutter_test_get_data_file ("test-animator-1.json"); + clutter_script_load_from_file (script, test_file, &error); + if (g_test_verbose () && error) + g_print ("Error: %s", error->message); + g_assert (error == NULL); + + animator = clutter_script_get_object (script, "animator"); + g_assert (CLUTTER_IS_ANIMATOR (animator)); + + duration = clutter_animator_get_duration (CLUTTER_ANIMATOR (animator)); + g_assert_cmpint (duration, ==, 1000); + + g_object_unref (script); + g_free (test_file); +} diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 836cbffed..07791732b 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -177,6 +177,8 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/script", test_script_object_property); TEST_CONFORM_SIMPLE ("/script", test_script_animation); TEST_CONFORM_SIMPLE ("/script", test_script_named_object); + TEST_CONFORM_SIMPLE ("/script", test_animator_base); + TEST_CONFORM_SIMPLE ("/script", test_animator_properties); TEST_CONFORM_SIMPLE ("/behaviours", test_behaviours); diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 335f8f39d..3236b261f 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -8,6 +8,8 @@ json_files = \ test-script-named-object.json \ test-script-object-property.json \ test-script-single.json \ + test-animator-1.json \ + test-animator-2.json \ $(NULL) png_files = \ diff --git a/tests/data/test-animator-1.json b/tests/data/test-animator-1.json new file mode 100644 index 000000000..2d6aab908 --- /dev/null +++ b/tests/data/test-animator-1.json @@ -0,0 +1,5 @@ +{ + "type" : "ClutterAnimator", + "id" : "animator", + "duration" : 1000 +} diff --git a/tests/data/test-animator-2.json b/tests/data/test-animator-2.json new file mode 100644 index 000000000..9059f57ed --- /dev/null +++ b/tests/data/test-animator-2.json @@ -0,0 +1,29 @@ +[ + { + "type" : "ClutterRectangle", + "id" : "foo", + "x" : 0, + "y" : 0, + "width" : 100, + "height" : 100 + }, + { + "type" : "ClutterAnimator", + "id" : "animator", + "duration" : 1000, + + "properties" : [ + { + "object" : "foo", + "name" : "x", + "ease-in" : true, + "interpolation" : "linear", + "keys" : [ + [ 0.0, "easeInCubic", 100.0 ], + [ 0.2, "easeOutCubic", 150.0 ], + [ 0.8, "linear", 200.0 ] + ] + } + ] + } +] From 6ab90899badf7820ffc4f40f23bf18c979762577 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 8 Feb 2010 16:50:29 +0000 Subject: [PATCH 4/4] docs: Describe the Animation definition syntax The ClutterAnimator documentation needs a section on the syntax of its ClutterScript definition, possibly with an example. --- clutter/clutter-animator.c | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/clutter/clutter-animator.c b/clutter/clutter-animator.c index 86f6ebd6d..ffabf9c24 100644 --- a/clutter/clutter-animator.c +++ b/clutter/clutter-animator.c @@ -37,6 +37,65 @@ * through the #ClutterScript definition format, but it comes with a * convenience C API. * + * + * ClutterAnimator description for #ClutterScript + * #ClutterAnimator defines a custom "properties" property + * which allows describing the key frames for objects. + * The "properties" property has the following syntax: + * + * + * { + * "properties" : [ + * { + * "object" : <id of an object>, + * "name" : <name of the property>, + * "ease-in" : <boolean>, + * "interpolation" : <#ClutterInterpolation value>, + * "keys" : [ + * [ <progress>, <easing mode>, <final value> ] + * ] + * ] + * } + * + * + * + * ClutterAnimator definition<title> + * <para>The following JSON fragment defines a #ClutterAnimator + * with the duration of 1 second and operating on the x and y + * properties of a #ClutterActor named "rect-01", with two frames + * for each property. The first frame will linearly move the actor + * from its current position to the 100, 100 position in 20 percent + * of the duration of the animation; the second will using a cubic + * easing to move the actor to the 200, 200 coordinates.</para> + * <programlisting> + * { + * "type" : "ClutterAnimator", + * "duration" : 1000, + * "properties" : [ + * { + * "object" : "rect-01", + * "name" : "x", + * "ease-in" : true, + * "keys" : [ + * [ 0.2, "linear", 100.0 ], + * [ 1.0, "easeOutCubic", 200.0 ] + * ] + * }, + * { + * "object" : "rect-01", + * "name" : "y", + * "ease-in" : true, + * "keys" : [ + * [ 0.2, "linear", 100.0 ], + * [ 1.0, "easeOutCubic", 200.0 ] + * ] + * } + * ] + * } + * </programlisting> + * </example> + * </refsect2> + * * #ClutterAnimator is available since Clutter 1.2 */