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.
This commit is contained in:
Emmanuele Bassi 2010-05-26 13:43:42 +01:00
parent 7a299e9b47
commit 9510cbbad2

View File

@ -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,
{
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
* <emphasis>button</emphasis> 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,12 +672,13 @@ clutter_state_set (ClutterState *state,
gulong first_mode,
...)
{
GObjectClass *klass;
gpointer object;
const gchar *property_name;
gulong mode;
va_list args;
g_return_if_fail (CLUTTER_IS_STATE (state));
object = first_object;
property_name = first_property_name;
@ -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;
}
#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;