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
This commit is contained in:
Emmanuele Bassi 2010-11-17 15:27:42 +00:00
parent 1ce47bb562
commit 71a838815f
4 changed files with 144 additions and 38 deletions

View File

@ -66,10 +66,14 @@ struct _ClutterTimelinePrivate
/* Time we last advanced the elapsed time and showed a frame */ /* Time we last advanced the elapsed time and showed a frame */
gint64 last_frame_time; gint64 last_frame_time;
guint loop : 1; guint loop : 1;
guint is_playing : 1; guint is_playing : 1;
/* If we've just started playing and haven't yet gotten a tick from the master clock */
/* If we've just started playing and haven't yet gotten
* a tick from the master clock
*/
guint waiting_first_tick : 1; guint waiting_first_tick : 1;
guint reverse : 1;
}; };
typedef struct { typedef struct {
@ -86,11 +90,12 @@ enum
PROP_DELAY, PROP_DELAY,
PROP_DURATION, PROP_DURATION,
PROP_DIRECTION, PROP_DIRECTION,
PROP_REVERSE,
PROP_LAST PROP_LAST
}; };
static GParamSpec *obj_props[PROP_LAST]; static GParamSpec *obj_props[PROP_LAST] = { NULL, };
enum enum
{ {
@ -138,20 +143,16 @@ clutter_timeline_set_property (GObject *object,
const GValue *value, const GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
ClutterTimeline *timeline; ClutterTimeline *timeline = CLUTTER_TIMELINE (object);
ClutterTimelinePrivate *priv;
timeline = CLUTTER_TIMELINE(object);
priv = timeline->priv;
switch (prop_id) switch (prop_id)
{ {
case PROP_LOOP: case PROP_LOOP:
priv->loop = g_value_get_boolean (value); clutter_timeline_set_loop (timeline, g_value_get_boolean (value));
break; break;
case PROP_DELAY: case PROP_DELAY:
priv->delay = g_value_get_uint (value); clutter_timeline_set_delay (timeline, g_value_get_uint (value));
break; break;
case PROP_DURATION: case PROP_DURATION:
@ -162,6 +163,10 @@ clutter_timeline_set_property (GObject *object,
clutter_timeline_set_direction (timeline, g_value_get_enum (value)); clutter_timeline_set_direction (timeline, g_value_get_enum (value));
break; break;
case PROP_REVERSE:
clutter_timeline_set_reverse (timeline, g_value_get_boolean (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -174,11 +179,8 @@ clutter_timeline_get_property (GObject *object,
GValue *value, GValue *value,
GParamSpec *pspec) GParamSpec *pspec)
{ {
ClutterTimeline *timeline; ClutterTimeline *timeline = CLUTTER_TIMELINE (object);
ClutterTimelinePrivate *priv; ClutterTimelinePrivate *priv = timeline->priv;
timeline = CLUTTER_TIMELINE(object);
priv = timeline->priv;
switch (prop_id) switch (prop_id)
{ {
@ -198,6 +200,10 @@ clutter_timeline_get_property (GObject *object,
g_value_set_enum (value, priv->direction); g_value_set_enum (value, priv->direction);
break; break;
case PROP_REVERSE:
g_value_set_boolean (value, priv->reverse);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -307,6 +313,21 @@ clutter_timeline_class_init (ClutterTimelineClass *klass)
CLUTTER_TIMELINE_FORWARD, CLUTTER_TIMELINE_FORWARD,
CLUTTER_PARAM_READWRITE); 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->dispose = clutter_timeline_dispose;
object_class->finalize = clutter_timeline_finalize; object_class->finalize = clutter_timeline_finalize;
object_class->set_property = clutter_timeline_set_property; 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); 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 /* Again check to see if the user has manually played with
* the elapsed time, before we finally stop or loop the timeline */ * 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 time without emitting the new frame signal so we need to
check for markers again */ check for markers again */
check_markers (timeline, check_markers (timeline,
priv->direction == CLUTTER_TIMELINE_FORWARD ? priv->direction == CLUTTER_TIMELINE_FORWARD
priv->elapsed_time : ? priv->elapsed_time
priv->duration - priv->elapsed_time); : priv->duration - priv->elapsed_time);
g_object_unref (timeline); g_object_unref (timeline);
return TRUE; return TRUE;
@ -1501,3 +1536,86 @@ clutter_timeline_has_marker (ClutterTimeline *timeline,
return NULL != g_hash_table_lookup (timeline->priv->markers_by_name, return NULL != g_hash_table_lookup (timeline->priv->markers_by_name,
marker_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;
}

View File

@ -127,6 +127,9 @@ void clutter_timeline_stop (ClutterTimeline *timeli
void clutter_timeline_set_loop (ClutterTimeline *timeline, void clutter_timeline_set_loop (ClutterTimeline *timeline,
gboolean loop); gboolean loop);
gboolean clutter_timeline_get_loop (ClutterTimeline *timeline); 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_rewind (ClutterTimeline *timeline);
void clutter_timeline_skip (ClutterTimeline *timeline, void clutter_timeline_skip (ClutterTimeline *timeline,
guint msecs); guint msecs);

View File

@ -660,6 +660,8 @@ clutter_timeline_get_delay
ClutterTimelineDirection ClutterTimelineDirection
clutter_timeline_set_direction clutter_timeline_set_direction
clutter_timeline_get_direction clutter_timeline_get_direction
clutter_timeline_set_reverse
clutter_timeline_get_reverse
<SUBSECTION> <SUBSECTION>
clutter_timeline_start clutter_timeline_start

View File

@ -44,21 +44,6 @@ scroll_event_cb (ClutterStage *stage,
return FALSE; 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 { typedef enum {
PATH_POLY, PATH_POLY,
PATH_ELLIPSE, PATH_ELLIPSE,
@ -160,11 +145,9 @@ test_behave_main (int argc, char *argv[])
clutter_container_add (CLUTTER_CONTAINER (group), rect, hand, NULL); clutter_container_add (CLUTTER_CONTAINER (group), rect, hand, NULL);
/* Make a timeline */ /* Make a timeline */
timeline = clutter_timeline_new (4000); /* num frames, fps */ timeline = clutter_timeline_new (4000);
clutter_timeline_set_loop (timeline, TRUE); clutter_timeline_set_loop (timeline, TRUE);
g_signal_connect (timeline, clutter_timeline_set_reverse (timeline, TRUE);
"completed", G_CALLBACK (timeline_completed),
NULL);
/* Set an alpha func to power behaviour - ramp is constant rise */ /* Set an alpha func to power behaviour - ramp is constant rise */
alpha = clutter_alpha_new_full (timeline, CLUTTER_LINEAR); alpha = clutter_alpha_new_full (timeline, CLUTTER_LINEAR);