timeline: Add a new "stopped" signal

The ::stopped signal is emitted when the timeline has been completely
exhausted or when the timeline has been programmatically stopped by
using clutter_timeline_stop(); the notification at the end of the
timeline run allows to write handlers without having to check whether
the current repeat is the last one, like we are forced to do when using
the ::completed signal.

Based on the patch by: Jasper St. Pierre <jstpierre@mecheye.net>

https://bugzilla.gnome.org/show_bug.cgi?id=676854
This commit is contained in:
Emmanuele Bassi 2012-05-25 19:41:14 -04:00
parent 4634dde613
commit de4d70af69
6 changed files with 90 additions and 56 deletions

View File

@ -17272,39 +17272,29 @@ transition_closure_free (gpointer data)
} }
static void static void
on_transition_completed (ClutterTransition *transition, on_transition_stopped (ClutterTransition *transition,
TransitionClosure *clos) gboolean is_finished,
TransitionClosure *clos)
{ {
ClutterTimeline *timeline = CLUTTER_TIMELINE (transition);
ClutterActor *actor = clos->actor; ClutterActor *actor = clos->actor;
ClutterAnimationInfo *info; ClutterAnimationInfo *info;
gint n_repeats, cur_repeat;
info = _clutter_actor_get_animation_info (actor);
/* reset the caches used by animations */ /* reset the caches used by animations */
clutter_actor_store_content_box (actor, NULL); clutter_actor_store_content_box (actor, NULL);
/* ensure that we remove the transition only at the end if (!is_finished)
* of its run; we emit ::completed for every repeat return;
*/
n_repeats = clutter_timeline_get_repeat_count (timeline);
cur_repeat = clutter_timeline_get_current_repeat (timeline);
if (cur_repeat == n_repeats) info = _clutter_actor_get_animation_info (actor);
{
if (clutter_transition_get_remove_on_complete (transition)) /* we take a reference here because removing the closure
{ * will release the reference on the transition, and we
/* we take a reference here because removing the closure * want the transition to survive the signal emission;
* will release the reference on the transition, and we * the master clock will release the last reference at
* want the transition to survive the signal emission; * the end of the frame processing.
* the master clock will release the last reference at */
* the end of the frame processing. g_object_ref (transition);
*/ g_hash_table_remove (info->transitions, clos->name);
g_object_ref (transition);
g_hash_table_remove (info->transitions, clos->name);
}
}
/* if it's the last transition then we clean up */ /* if it's the last transition then we clean up */
if (g_hash_table_size (info->transitions) == 0) if (g_hash_table_size (info->transitions) == 0)
@ -17585,8 +17575,8 @@ clutter_actor_add_transition (ClutterActor *self,
clos->actor = self; clos->actor = self;
clos->transition = g_object_ref (transition); clos->transition = g_object_ref (transition);
clos->name = g_strdup (name); clos->name = g_strdup (name);
clos->completed_id = g_signal_connect (timeline, "completed", clos->completed_id = g_signal_connect (timeline, "stopped",
G_CALLBACK (on_transition_completed), G_CALLBACK (on_transition_stopped),
clos); clos);
CLUTTER_NOTE (ANIMATION, CLUTTER_NOTE (ANIMATION,
@ -17842,13 +17832,13 @@ clutter_actor_get_easing_delay (ClutterActor *self)
* clutter_actor_set_rotation (actor, CLUTTER_Y_AXIS, 360.0, x, y, z); * clutter_actor_set_rotation (actor, CLUTTER_Y_AXIS, 360.0, x, y, z);
* *
* transition = clutter_actor_get_transition (actor, "rotation-angle-y"); * transition = clutter_actor_get_transition (actor, "rotation-angle-y");
* g_signal_connect (transition, "completed", * g_signal_connect (transition, "stopped",
* G_CALLBACK (on_transition_complete), * G_CALLBACK (on_transition_stopped),
* actor); * actor);
* ]| * ]|
* *
* will call the <function>on_transition_complete</function> callback when * will call the <function>on_transition_stopped</function> callback when
* the transition is complete. * the transition is finished.
* *
* Return value: (transfer none): a #ClutterTransition, or %NULL is none * Return value: (transfer none): a #ClutterTransition, or %NULL is none
* was found to match the passed name; the returned instance is owned * was found to match the passed name; the returned instance is owned

View File

@ -7,6 +7,7 @@ BOOLEAN:OBJECT,FLOAT,FLOAT
BOXED:UINT,UINT BOXED:UINT,UINT
DOUBLE:VOID DOUBLE:VOID
UINT:VOID UINT:VOID
VOID:BOOLEAN
VOID:BOXED VOID:BOXED
VOID:BOXED,FLAGS VOID:BOXED,FLAGS
VOID:INT VOID:INT

View File

@ -187,6 +187,7 @@ enum
PAUSED, PAUSED,
COMPLETED, COMPLETED,
MARKER_REACHED, MARKER_REACHED,
STOPPED,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -684,6 +685,10 @@ clutter_timeline_class_init (ClutterTimelineClass *klass)
* *
* This signal will be emitted even if the #ClutterTimeline is set to be * This signal will be emitted even if the #ClutterTimeline is set to be
* repeating. * repeating.
*
* If you want to get notification on whether the #ClutterTimeline has
* been stopped or has finished its run, including its eventual repeats,
* you should use the #ClutterTimeline::stopped signal instead.
*/ */
timeline_signals[COMPLETED] = timeline_signals[COMPLETED] =
g_signal_new (I_("completed"), g_signal_new (I_("completed"),
@ -766,6 +771,33 @@ clutter_timeline_class_init (ClutterTimelineClass *klass)
G_TYPE_NONE, 2, G_TYPE_NONE, 2,
G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_INT); G_TYPE_INT);
/**
* ClutterTimeline::stopped:
* @timeline: the #ClutterTimeline that emitted the signal
* @is_finished: %TRUE if the signal was emitted at the end of the
* timeline.
*
* The #ClutterTimeline::stopped signal is emitted when the timeline
* has been stopped, either because clutter_timeline_stop() has been
* called, or because it has been exhausted.
*
* This is different from the #ClutterTimeline::completed signal,
* which gets emitted after every repeat finishes.
*
* If the #ClutterTimeline has is marked as infinitely repeating,
* this signal will never be emitted.
*
* Since: 1.12
*/
timeline_signals[STOPPED] =
g_signal_new (I_("stopped"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterTimelineClass, completed),
NULL, NULL,
_clutter_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1,
G_TYPE_BOOLEAN);
} }
static void static void
@ -897,12 +929,13 @@ set_is_playing (ClutterTimeline *timeline,
ClutterTimelinePrivate *priv = timeline->priv; ClutterTimelinePrivate *priv = timeline->priv;
ClutterMasterClock *master_clock; ClutterMasterClock *master_clock;
is_playing = is_playing != FALSE; is_playing = !!is_playing;
if (is_playing == priv->is_playing) if (is_playing == priv->is_playing)
return; return;
priv->is_playing = is_playing; priv->is_playing = is_playing;
master_clock = _clutter_master_clock_get_default (); master_clock = _clutter_master_clock_get_default ();
if (priv->is_playing) if (priv->is_playing)
{ {
@ -911,9 +944,7 @@ set_is_playing (ClutterTimeline *timeline,
priv->current_repeat = 0; priv->current_repeat = 0;
} }
else else
{ _clutter_master_clock_remove_timeline (master_clock, timeline);
_clutter_master_clock_remove_timeline (master_clock, timeline);
}
} }
static gboolean static gboolean
@ -1002,7 +1033,8 @@ clutter_timeline_do_frame (ClutterTimeline *timeline)
* priv->repeat_count. Are we limiting the things that could be * priv->repeat_count. Are we limiting the things that could be
* done in the above new-frame signal handler? * done in the above new-frame signal handler?
*/ */
set_is_playing (timeline, FALSE); set_is_playing (timeline, FALSE);
g_signal_emit (timeline, timeline_signals[STOPPED], 0, TRUE);
} }
g_signal_emit (timeline, timeline_signals[COMPLETED], 0); g_signal_emit (timeline, timeline_signals[COMPLETED], 0);
@ -1156,8 +1188,22 @@ clutter_timeline_pause (ClutterTimeline *timeline)
void void
clutter_timeline_stop (ClutterTimeline *timeline) clutter_timeline_stop (ClutterTimeline *timeline)
{ {
gboolean was_playing;
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
/* we check the is_playing here because pause() will return immediately
* if the timeline wasn't playing, so we don't know if it was actually
* stopped, and yet we still don't want to emit a ::stopped signal if
* the timeline was not playing in the first place.
*/
was_playing = timeline->priv->is_playing;
clutter_timeline_pause (timeline); clutter_timeline_pause (timeline);
clutter_timeline_rewind (timeline); clutter_timeline_rewind (timeline);
if (was_playing)
g_signal_emit (timeline, timeline_signals[STOPPED], 0, FALSE);
} }
/** /**

View File

@ -104,13 +104,14 @@ struct _ClutterTimelineClass
void (*marker_reached) (ClutterTimeline *timeline, void (*marker_reached) (ClutterTimeline *timeline,
const gchar *marker_name, const gchar *marker_name,
gint msecs); gint msecs);
void (*stopped) (ClutterTimeline *timeline,
gboolean is_finished);
/*< private >*/ /*< private >*/
void (*_clutter_timeline_1) (void); void (*_clutter_timeline_1) (void);
void (*_clutter_timeline_2) (void); void (*_clutter_timeline_2) (void);
void (*_clutter_timeline_3) (void); void (*_clutter_timeline_3) (void);
void (*_clutter_timeline_4) (void); void (*_clutter_timeline_4) (void);
void (*_clutter_timeline_5) (void);
}; };
GType clutter_timeline_get_type (void) G_GNUC_CONST; GType clutter_timeline_get_type (void) G_GNUC_CONST;

View File

@ -124,24 +124,19 @@ clutter_transition_new_frame (ClutterTimeline *timeline,
} }
static void static void
clutter_transition_completed (ClutterTimeline *timeline) clutter_transition_stopped (ClutterTimeline *timeline,
gboolean is_finished)
{ {
ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (timeline)->priv; ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (timeline)->priv;
if (priv->animatable != NULL && priv->remove_on_complete) if (is_finished &&
priv->animatable != NULL &&
priv->remove_on_complete)
{ {
int n_repeats, cur_repeat; clutter_transition_detach (CLUTTER_TRANSITION (timeline),
priv->animatable);
n_repeats = clutter_timeline_get_repeat_count (timeline); g_clear_object (&priv->animatable);
cur_repeat = clutter_timeline_get_current_repeat (timeline); g_object_unref (timeline);
if (n_repeats == 0 || cur_repeat == n_repeats)
{
clutter_transition_detach (CLUTTER_TRANSITION (timeline),
priv->animatable);
g_clear_object (&priv->animatable);
g_object_unref (timeline);
}
} }
} }
@ -232,7 +227,7 @@ clutter_transition_class_init (ClutterTransitionClass *klass)
klass->detached = clutter_transition_real_detached; klass->detached = clutter_transition_real_detached;
timeline_class->new_frame = clutter_transition_new_frame; timeline_class->new_frame = clutter_transition_new_frame;
timeline_class->completed = clutter_transition_completed; timeline_class->stopped = clutter_transition_stopped;
gobject_class->set_property = clutter_transition_set_property; gobject_class->set_property = clutter_transition_set_property;
gobject_class->get_property = clutter_transition_get_property; gobject_class->get_property = clutter_transition_get_property;

View File

@ -49,8 +49,9 @@ on_crossing (ClutterActor *actor,
} }
static void static void
on_transition_complete (ClutterTransition *transition, on_transition_stopped (ClutterTransition *transition,
ClutterActor *actor) gboolean is_finished,
ClutterActor *actor)
{ {
clutter_actor_save_easing_state (actor); clutter_actor_save_easing_state (actor);
clutter_actor_set_easing_duration (actor, 250); clutter_actor_set_easing_duration (actor, 250);
@ -74,8 +75,8 @@ animate_rotation (ClutterActor *actor,
SIZE / 2.0f, 0.f, 0.f); SIZE / 2.0f, 0.f, 0.f);
transition = clutter_actor_get_transition (actor, "rotation-angle-y"); transition = clutter_actor_get_transition (actor, "rotation-angle-y");
g_signal_connect (transition, "completed", g_signal_connect (transition, "stopped",
G_CALLBACK (on_transition_complete), G_CALLBACK (on_transition_stopped),
actor); actor);
clutter_actor_restore_easing_state (actor); clutter_actor_restore_easing_state (actor);