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-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-animator.c b/clutter/clutter-animator.c
index f9fd309de..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
+ * 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.
+ *
+ * {
+ * "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 ]
+ * ]
+ * }
+ * ]
+ * }
+ *
+ *
+ *
+ *
* #ClutterAnimator is available since Clutter 1.2
*/
@@ -54,8 +113,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 +163,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 +336,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 +837,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 +987,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 +1040,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 +1049,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 +1171,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,
@@ -1340,24 +1656,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 +1683,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 +1725,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 +1743,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
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,
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 ]
+ ]
+ }
+ ]
+ }
+]