From 9510cbbad290cc8edd3b602991e57f98989a86ce Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 26 May 2010 13:43:42 +0100 Subject: [PATCH] state: Minor fixes and documentation additions Clean up the code a bit, and move common code paths into separate function. Also, document clutter_state_set() with examples. --- clutter/clutter-state.c | 296 +++++++++++++++++++++++++--------------- 1 file changed, 188 insertions(+), 108 deletions(-) diff --git a/clutter/clutter-state.c b/clutter/clutter-state.c index d2f4bb62b..6a8b91b99 100644 --- a/clutter/clutter-state.c +++ b/clutter/clutter-state.c @@ -178,6 +178,7 @@ clutter_state_key_new (State *state, GParamSpec *pspec, guint mode) { + ClutterStatePrivate *priv = state->clutter_state->priv; ClutterStateKey *state_key; GValue value = { 0, }; @@ -191,8 +192,7 @@ clutter_state_key_new (State *state, state_key->alpha = clutter_alpha_new (); g_object_ref_sink (state_key->alpha); clutter_alpha_set_mode (state_key->alpha, mode); - clutter_alpha_set_timeline (state_key->alpha, - state->clutter_state->priv->slave_timeline); + clutter_alpha_set_timeline (state_key->alpha, priv->slave_timeline); state_key->interval = g_object_new (CLUTTER_TYPE_INTERVAL, @@ -225,8 +225,12 @@ clutter_state_key_free (gpointer clutter_state_key) return; if (!key->is_inert) - g_object_weak_unref (key->object, object_disappeared, - key->target_state->clutter_state); + { + g_object_weak_unref (key->object, + object_disappeared, + key->target_state->clutter_state); + } + g_object_unref (key->alpha); g_object_unref (key->interval); @@ -503,7 +507,8 @@ clutter_state_change (ClutterState *state, priv->target_state = new_state; if (animator != NULL) - { /* we've got an animator overriding the tweened animation */ + { + /* we've got an animator overriding the tweened animation */ priv->current_animator = animator; clutter_animator_set_timeline (animator, priv->timeline); } @@ -514,8 +519,7 @@ clutter_state_change (ClutterState *state, ClutterStateKey *key = k->data; GValue initial = { 0, }; - g_value_init (&initial, clutter_interval_get_value_type ( - key->interval)); + g_value_init (&initial, clutter_interval_get_value_type (key->interval)); g_object_get_property (key->object, key->property_name, &initial); if (clutter_alpha_get_mode (key->alpha) != key->mode) @@ -536,7 +540,8 @@ clutter_state_change (ClutterState *state, /* emit signals, to change properties, and indicate that the * state change is complete */ g_signal_emit_by_name (priv->timeline, "new-frame", - GINT_TO_POINTER(duration), NULL); + GINT_TO_POINTER (duration), + NULL); g_signal_emit_by_name (priv->timeline, "completed", NULL); } else @@ -549,6 +554,52 @@ clutter_state_change (ClutterState *state, return priv->timeline; } +static GParamSpec * +get_property_from_object (GObject *gobject, + const gchar *property_name) +{ + GObjectClass *klass = G_OBJECT_GET_CLASS (gobject); + GParamSpec *pspec; + + pspec = g_object_class_find_property (klass, property_name); + if (pspec == NULL) + { + g_warning ("Cannot bind property '%s': objects of type '%s' " + "do not have this property", + property_name, + G_OBJECT_TYPE_NAME (gobject)); + return NULL; + } + + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_warning ("Cannot bind property '%s' of object of type '%s': " + "the property is not writable", + property_name, + G_OBJECT_TYPE_NAME (gobject)); + return NULL; + } + + if (!(pspec->flags & G_PARAM_READABLE)) + { + g_warning ("Cannot bind property '%s' of object of type '%s': " + "the property is not readable", + property_name, + G_OBJECT_TYPE_NAME (gobject)); + return NULL; + } + + if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) + { + g_warning ("Cannot bind property '%s' of object of type '%s': " + "the property is set as constructor-only", + property_name, + G_OBJECT_TYPE_NAME (gobject)); + return NULL; + } + + return pspec; +} /** * clutter_state_set: @@ -558,7 +609,7 @@ clutter_state_change (ClutterState *state, * @first_object: a #GObject * @first_property_name: a property of @first_object to specify a key for * @first_mode: the id of the alpha function to use - * @Varargs: the value @first_property_name should have in @state_name, + * @Varargs: the value @first_property_name should have in @target_state_name, * followed by object, property name, mode, value tuples, terminated * by %NULL * @@ -569,13 +620,46 @@ clutter_state_change (ClutterState *state, * The mode specified is the easing mode used when going to from the previous * key to the specified key. * - * If a given object, state_name, property tuple already exist then the mode - * and value will be replaced with the new specified values. + * For instance, the code below: * - * If a the property_name is prefixed with "delayed::" two additional + * |[ + * clutter_state_set (state, "default", "hover", + * button, "opacity", 255, CLUTTER_LINEAR, + * button, "scale-x", 1.2, CLUTTER_EASE_OUT_CUBIC, + * button, "scale-y", 1.2, CLUTTER_EASE_OUT_CUBIC, + * NULL); + * ]| + * + * will create a transition between the "default" and "hover" state; the + * button object will have the #ClutterActor:opacity + * property animated to a value of 255 using %CLUTTER_LINEAR as the animation + * mode, and the #ClutterActor:scale-x and #ClutterActor:scale-y properties + * animated to a value of 1.2 using %CLUTTER_EASE_OUT_CUBIC as the animation + * mode. To change the state (and start the transition) you can use the + * clutter_state_change() function: + * + * |[ + * clutter_state_change (state, "hover", TRUE); + * ]| + * + * If a given object, state_name, property tuple already exist in the + * #ClutterState instance, then the mode and value will be replaced with + * the new specified values. + * + * If a property name is prefixed with "delayed::" two additional * arguments per key are expected: a value relative to the full state time * to pause before transitioning and a similar value to pause after - * transitioning. + * transitioning, e.g.: + * + * |[ + * clutter_state_set (state, "hover", "toggled", + * button, "delayed::scale-x", 1.0, 0.2, 0.2, + * button, "delayed::scale-y", 1.0, 0.2, 0.2, + * NULL); + * ]| + * + * will pause for 20% of the duration of the transition before animating, + * and 20% of the duration after animating. * * Since: 1.4 */ @@ -588,11 +672,12 @@ clutter_state_set (ClutterState *state, gulong first_mode, ...) { - GObjectClass *klass; - gpointer object; - const gchar *property_name; - gulong mode; - va_list args; + gpointer object; + const gchar *property_name; + gulong mode; + va_list args; + + g_return_if_fail (CLUTTER_IS_STATE (state)); object = first_object; @@ -610,20 +695,12 @@ clutter_state_set (ClutterState *state, gchar *error = NULL; const gchar *real_property_name = property_name; - klass = G_OBJECT_GET_CLASS (object); - if (g_str_has_prefix (property_name, "delayed::")) real_property_name = strstr (property_name, "::") + 2; - pspec = g_object_class_find_property (klass, real_property_name); + pspec = get_property_from_object (object, real_property_name); if (pspec == NULL) - { - g_warning ("Cannot bind property '%s': objects of type '%s' " - "do not have this property", - real_property_name, - G_OBJECT_TYPE_NAME (object)); - break; - } + break; #if GLIB_CHECK_VERSION (2, 23, 2) G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), @@ -702,6 +779,50 @@ clutter_state_set_key_internal (ClutterState *state, sort_props_func); } +/* + * clutter_state_get_state: + * @state: a #ClutterState + * @state_name: the name of the state to be retrieved + * @force_creation: %TRUE if the state should be forcibly created + * if not found + * + * Retrieves the #State structure for @state_name inside the given + * #ClutterState instance + * + * If @state_name is %NULL and @force_creation is %TRUE then the + * #State for the default state name will be returned; if @force_creation + * is %FALSE then %NULL will be returned + * + * Return value: a #State structure for the given name, or %NULL + */ +static State * +clutter_state_get_state (ClutterState *state, + const gchar *state_name, + gboolean force_creation) +{ + ClutterStatePrivate *priv = state->priv; + State *retval; + + if (state_name == NULL) + { + if (force_creation) + state_name = g_intern_static_string ("default"); + else + return NULL; + } + else + state_name = g_intern_string (state_name); + + retval = g_hash_table_lookup (priv->states, state_name); + if (retval == NULL) + { + retval = state_new (state, state_name); + g_hash_table_insert (priv->states, (gpointer) state_name, retval); + } + + return retval; +} + /** * clutter_state_set_key: * @state: a #ClutterState instance. @@ -748,46 +869,14 @@ clutter_state_set_key (ClutterState *state, priv = state->priv; - if (target_state_name == NULL) - target_state_name = "default"; + pspec = get_property_from_object (object, property_name); + if (pspec == NULL) + return state; - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), - property_name); - if (pspec == NULL || !(pspec->flags & G_PARAM_WRITABLE)) - { - g_warning ("No writable property '%s' for object type '%s' found", - property_name, - G_OBJECT_TYPE_NAME (object)); - return NULL; - } + source_state = clutter_state_get_state (state, source_state_name, FALSE); + target_state = clutter_state_get_state (state, target_state_name, TRUE); - source_state_name = g_intern_string (source_state_name); - target_state_name = g_intern_string (target_state_name); property_name = g_intern_string (property_name); - - target_state = g_hash_table_lookup (priv->states, target_state_name); - - if (!target_state) - { - target_state = state_new (state, target_state_name); - g_hash_table_insert (priv->states, - (gpointer) target_state_name, - target_state); - } - - if (source_state_name) - { - source_state = g_hash_table_lookup (priv->states, source_state_name); - - if (!source_state) - { - source_state = state_new (state, source_state_name); - g_hash_table_insert (priv->states, - (gpointer) source_state_name, - source_state); - } - } - state_key = clutter_state_key_new (target_state, object, property_name, pspec, mode); @@ -875,12 +964,12 @@ clutter_state_get_keys (ClutterState *state, source_state = g_hash_table_lookup (state->priv->states, source_state_name); - for (s = state_list; s; s=s->next) + for (s = state_list; s != NULL; s = s->next) { State *target_state; - target_state = g_hash_table_lookup (state->priv->states, s->data); - if (target_state) + target_state = g_hash_table_lookup (state->priv->states, s->data); + if (target_state != NULL) { GList *k; @@ -888,12 +977,11 @@ clutter_state_get_keys (ClutterState *state, { ClutterStateKey *key = k->data; - if ( (object == NULL || (object == key->object)) - && (source_state_name == NULL - ||source_state == key->source_state) - && (property_name == NULL - ||(property_name == key->property_name)) - ) + if ((object == NULL || (object == key->object)) && + (source_state_name == NULL || + source_state == key->source_state) && + (property_name == NULL || + (property_name == key->property_name))) { targets = g_list_prepend (targets, key); } @@ -1397,14 +1485,19 @@ clutter_state_key_get_target_state_name (const ClutterStateKey *state_key) /** * clutter_state_set_duration: * @state: a #ClutterState - * @source_state_name: the name of the source state to set duration for or NULL - * @target_state_name: the name of the source state to set duration for or NULL - * @duration: milliseconds for transition between source_state and target_state. + * @source_state_name: (allow-none): the name of the source state, or %NULL + * @target_state_name: (allow-none): the name of the target state, or %NULL + * @duration: the duration of the transition, in milliseconds * - * If both state names are NULL the default duration for ClutterState is set, - * if only target_state_name is specified this becomes the default duration - * for transitions to this state. When both are specified the change only - * applies to this transition. + * Sets the duration of a transition. + * + * If both state names are %NULL the default duration for @state is set. + * + * If only @target_state_name is specified, the passed @duration becomes + * the default duration for transitions to the target state. + * + * If both states names are specified, the passed @duration only applies + * to the specified transition. * * Since: 1.4 */ @@ -1547,7 +1640,6 @@ parse_state_transition (JsonArray *array, gpointer data) { ParseClosure *clos = data; - ClutterStatePrivate *priv = clos->state->priv; JsonObject *object; const gchar *source_name, *target_name; State *source_state, *target_state; @@ -1577,9 +1669,6 @@ parse_state_transition (JsonArray *array, return; } - source_name = json_object_get_string_member (object, "source"); - target_name = json_object_get_string_member (object, "target"); - keys = json_object_get_array_member (object, "keys"); if (keys == NULL) { @@ -1590,29 +1679,18 @@ parse_state_transition (JsonArray *array, return; } - source_name = g_intern_string (source_name); - source_state = g_hash_table_lookup (priv->states, source_name); - if (source_state == NULL) - { - source_state = state_new (clos->state, source_name); - g_hash_table_insert (priv->states, (gpointer) source_name, source_state); - } + source_name = json_object_get_string_member (object, "source"); + source_state = clutter_state_get_state (clos->state, source_name, FALSE); - target_name = g_intern_string (target_name); - target_state = g_hash_table_lookup (priv->states, target_name); - if (target_state == NULL) - { - target_state = state_new (clos->state, target_name); - g_hash_table_insert (priv->states, (gpointer) target_name, target_state); - } + target_name = json_object_get_string_member (object, "target"); + target_state = clutter_state_get_state (clos->state, target_name, TRUE); if (json_object_has_member (object, "duration")) { guint duration = json_object_get_int_member (object, "duration"); clutter_state_set_duration (clos->state, - source_name, - target_name, + source_name, target_name, duration); } @@ -1696,20 +1774,22 @@ parse_state_transition (JsonArray *array, continue; } - if (json_array_get_length (key) == 5) + switch (json_array_get_length (key)) { + case 5: state_key->pre_delay = json_array_get_double_element (key, 4); state_key->post_delay = 0.0; - } - else if (json_array_get_length (key) == 6) - { + break; + + case 6: state_key->pre_delay = json_array_get_double_element (key, 4); state_key->post_delay = json_array_get_double_element (key, 5); - } - else - { + break; + + default: state_key->pre_delay = 0.0; state_key->post_delay = 0.0; + break; } state_key->source_state = source_state;