From 71a838815f4d3ea1d9e2054bbbaaf0449eae357e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 17 Nov 2010 15:27:42 +0000 Subject: [PATCH] timeline: Add :reverse property The :reverse property removes the pattern of connecting to the ::completed signal of a Timeline to change the direction. http://bugzilla.clutter-project.org/show_bug.cgi?id=2408 --- clutter/clutter-timeline.c | 156 +++++++++++++++++++++++++---- clutter/clutter-timeline.h | 3 + doc/reference/clutter/clutter-sections.txt | 2 + tests/interactive/test-behave.c | 21 +--- 4 files changed, 144 insertions(+), 38 deletions(-) diff --git a/clutter/clutter-timeline.c b/clutter/clutter-timeline.c index e8ec31b..a06b114 100644 --- a/clutter/clutter-timeline.c +++ b/clutter/clutter-timeline.c @@ -66,10 +66,14 @@ struct _ClutterTimelinePrivate /* Time we last advanced the elapsed time and showed a frame */ gint64 last_frame_time; - guint loop : 1; - guint is_playing : 1; - /* If we've just started playing and haven't yet gotten a tick from the master clock */ + guint loop : 1; + guint is_playing : 1; + + /* If we've just started playing and haven't yet gotten + * a tick from the master clock + */ guint waiting_first_tick : 1; + guint reverse : 1; }; typedef struct { @@ -86,11 +90,12 @@ enum PROP_DELAY, PROP_DURATION, PROP_DIRECTION, + PROP_REVERSE, PROP_LAST }; -static GParamSpec *obj_props[PROP_LAST]; +static GParamSpec *obj_props[PROP_LAST] = { NULL, }; enum { @@ -138,20 +143,16 @@ clutter_timeline_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - ClutterTimeline *timeline; - ClutterTimelinePrivate *priv; - - timeline = CLUTTER_TIMELINE(object); - priv = timeline->priv; + ClutterTimeline *timeline = CLUTTER_TIMELINE (object); switch (prop_id) { case PROP_LOOP: - priv->loop = g_value_get_boolean (value); + clutter_timeline_set_loop (timeline, g_value_get_boolean (value)); break; case PROP_DELAY: - priv->delay = g_value_get_uint (value); + clutter_timeline_set_delay (timeline, g_value_get_uint (value)); break; case PROP_DURATION: @@ -162,6 +163,10 @@ clutter_timeline_set_property (GObject *object, clutter_timeline_set_direction (timeline, g_value_get_enum (value)); break; + case PROP_REVERSE: + clutter_timeline_set_reverse (timeline, g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -174,11 +179,8 @@ clutter_timeline_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - ClutterTimeline *timeline; - ClutterTimelinePrivate *priv; - - timeline = CLUTTER_TIMELINE(object); - priv = timeline->priv; + ClutterTimeline *timeline = CLUTTER_TIMELINE (object); + ClutterTimelinePrivate *priv = timeline->priv; switch (prop_id) { @@ -198,6 +200,10 @@ clutter_timeline_get_property (GObject *object, g_value_set_enum (value, priv->direction); break; + case PROP_REVERSE: + g_value_set_boolean (value, priv->reverse); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -307,6 +313,21 @@ clutter_timeline_class_init (ClutterTimelineClass *klass) CLUTTER_TIMELINE_FORWARD, CLUTTER_PARAM_READWRITE); + /** + * ClutterTimeline:reverse: + * + * Whether the direction of a looping timeline should be reversed + * when emitting the #ClutterTimeline::completed signal. + * + * Since: 1.6 + */ + obj_props[PROP_REVERSE] = + g_param_spec_boolean ("reverse", + P_("Reverse"), + P_("Whether the direction should be reversed when looping"), + FALSE, + CLUTTER_PARAM_READWRITE); + object_class->dispose = clutter_timeline_dispose; object_class->finalize = clutter_timeline_finalize; object_class->set_property = clutter_timeline_set_property; @@ -662,6 +683,20 @@ clutter_timeline_do_frame (ClutterTimeline *timeline) g_signal_emit (timeline, timeline_signals[COMPLETED], 0); + /* reverse the direction of the timeline if :loop and + * :reverse are set to TRUE + */ + if (priv->reverse) + { + if (priv->direction == CLUTTER_TIMELINE_FORWARD) + priv->direction = CLUTTER_TIMELINE_BACKWARD; + else + priv->direction = CLUTTER_TIMELINE_FORWARD; + + _clutter_notify_by_pspec (G_OBJECT (timeline), + obj_props[PROP_DIRECTION]); + } + /* Again check to see if the user has manually played with * the elapsed time, before we finally stop or loop the timeline */ @@ -692,9 +727,9 @@ clutter_timeline_do_frame (ClutterTimeline *timeline) time without emitting the new frame signal so we need to check for markers again */ check_markers (timeline, - priv->direction == CLUTTER_TIMELINE_FORWARD ? - priv->elapsed_time : - priv->duration - priv->elapsed_time); + priv->direction == CLUTTER_TIMELINE_FORWARD + ? priv->elapsed_time + : priv->duration - priv->elapsed_time); g_object_unref (timeline); return TRUE; @@ -1501,3 +1536,86 @@ clutter_timeline_has_marker (ClutterTimeline *timeline, return NULL != g_hash_table_lookup (timeline->priv->markers_by_name, marker_name); } + +/** + * clutter_timeline_set_reverse: + * @timeline: a #ClutterTimeline + * @reverse: %TRUE if the @timeline should reverse the direction + * + * Sets whether @timeline should reverse the direction after the + * emission of the #ClutterTimeline::completed signal. + * + * Setting the #ClutterTimeline:reverse property to %TRUE is the + * equivalent of connecting a callback to the #ClutterTimeline::completed + * signal and changing the direction of the timeline from that callback; + * for instance, this code: + * + * |[ + * static void + * reverse_timeline (ClutterTimeline *timeline) + * { + * ClutterTimelineDirection dir = clutter_timeline_get_direction (timeline); + * + * if (dir == CLUTTER_TIMELINE_FORWARD) + * dir = CLUTTER_TIMELINE_BACKWARD; + * else + * dir = CLUTTER_TIMELINE_FORWARD; + * + * clutter_timeline_set_direction (timeline, dir); + * } + * ... + * timeline = clutter_timeline_new (1000); + * clutter_timeline_set_loop (timeline); + * g_signal_connect (timeline, "completed", + * G_CALLBACK (reverse_timeline), + * NULL); + * ]| + * + * can be effectively replaced by: + * + * |[ + * timeline = clutter_timeline_new (1000); + * clutter_timeline_set_loop (timeline); + * clutter_timeline_set_reverse (timeline); + * ]| + * + * Since: 1.6 + */ +void +clutter_timeline_set_reverse (ClutterTimeline *timeline, + gboolean reverse) +{ + ClutterTimelinePrivate *priv; + + g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); + + reverse = !!reverse; + + priv = timeline->priv; + + if (priv->reverse != reverse) + { + priv->reverse = reverse; + + _clutter_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_REVERSE]); + } +} + +/** + * clutter_timeline_get_reverse: + * @timeline: a #ClutterTimeline + * + * Retrieves the value set by clutter_timeline_set_reverse(). + * + * Return value: %TRUE if the timeline should reverse when looping, and + * %FALSE otherwise + * + * Since: 1.6 + */ +gboolean +clutter_timeline_get_reverse (ClutterTimeline *timeline) +{ + g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE); + + return timeline->priv->reverse; +} diff --git a/clutter/clutter-timeline.h b/clutter/clutter-timeline.h index 056d79a..a5f5feb 100644 --- a/clutter/clutter-timeline.h +++ b/clutter/clutter-timeline.h @@ -127,6 +127,9 @@ void clutter_timeline_stop (ClutterTimeline *timeli void clutter_timeline_set_loop (ClutterTimeline *timeline, gboolean loop); gboolean clutter_timeline_get_loop (ClutterTimeline *timeline); +void clutter_timeline_set_reverse (ClutterTimeline *timeline, + gboolean reverse); +gboolean clutter_timeline_get_reverse (ClutterTimeline *timeline); void clutter_timeline_rewind (ClutterTimeline *timeline); void clutter_timeline_skip (ClutterTimeline *timeline, guint msecs); diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 68b3ae7..c4c232c 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -660,6 +660,8 @@ clutter_timeline_get_delay ClutterTimelineDirection clutter_timeline_set_direction clutter_timeline_get_direction +clutter_timeline_set_reverse +clutter_timeline_get_reverse clutter_timeline_start diff --git a/tests/interactive/test-behave.c b/tests/interactive/test-behave.c index 5e424d5..d39eb0c 100644 --- a/tests/interactive/test-behave.c +++ b/tests/interactive/test-behave.c @@ -44,21 +44,6 @@ scroll_event_cb (ClutterStage *stage, return FALSE; } -static void -timeline_completed (ClutterTimeline *timeline) -{ - ClutterTimelineDirection direction; - - direction = clutter_timeline_get_direction (timeline); - - if (direction == CLUTTER_TIMELINE_FORWARD) - direction = CLUTTER_TIMELINE_BACKWARD; - else - direction = CLUTTER_TIMELINE_FORWARD; - - clutter_timeline_set_direction (timeline, direction); -} - typedef enum { PATH_POLY, PATH_ELLIPSE, @@ -160,11 +145,9 @@ test_behave_main (int argc, char *argv[]) clutter_container_add (CLUTTER_CONTAINER (group), rect, hand, NULL); /* Make a timeline */ - timeline = clutter_timeline_new (4000); /* num frames, fps */ + timeline = clutter_timeline_new (4000); clutter_timeline_set_loop (timeline, TRUE); - g_signal_connect (timeline, - "completed", G_CALLBACK (timeline_completed), - NULL); + clutter_timeline_set_reverse (timeline, TRUE); /* Set an alpha func to power behaviour - ramp is constant rise */ alpha = clutter_alpha_new_full (timeline, CLUTTER_LINEAR); -- 2.7.4