This patch simplifies ClutterAnimation by automating the management of the
internal Alpha and Timeline objects.  ClutterAnimation is made to be a wrapper
around a single, driving Alpha object which has its own Timeline.  Utility
functions for setting properties of the Alpha and Timeline objects (duration,
loop, and mode) are available, but these functions now set the corresponding
properties on the underlying objects directly.

Using underlying objects directly allows the Animation to behave correctly
even when these objects are manipulated outside of the animation context.  The
user remains free to set arbitrary Timeline and Alpha objects on the Animation,
but this is optional and no explicit action needs otherwise be taken in order
to set up the Animation's underlying objects.

With these changes, using an Animation becomes simpler as per the following
example:

anim = clutter_animation_new()
clutter_animation_set_duration(anim, ...)
clutter_animation_set_mode(anim, ...)
clutter_animation_set_object(anim, ...)
clutter_animation_bind...(anim, ...)
timeline = clutter_animation_get_timeline()
clutter_timeline_start(timeline)

Note that explicit calls to clutter_animation_set_timeline and
clutter_animation_set_alpha are no longer needed.

Signed-off-by: Jonas Bonn <[email protected]>
---
 clutter/clutter-animation.c |  304 ++++++++++++++++++++++---------------------
 1 files changed, 156 insertions(+), 148 deletions(-)

diff --git a/clutter/clutter-animation.c b/clutter/clutter-animation.c
index 6eb7d85..43aa547 100644
--- a/clutter/clutter-animation.c
+++ b/clutter/clutter-animation.c
@@ -85,12 +85,6 @@ struct _ClutterAnimationPrivate
 
   GHashTable *properties;
 
-  gulong mode;
-
-  guint loop : 1;
-  guint duration;
-
-  ClutterTimeline *timeline;
   guint timeline_started_id;
   guint timeline_completed_id;
 
@@ -122,6 +116,7 @@ static void
 clutter_animation_dispose (GObject *gobject)
 {
   ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv;
+  ClutterTimeline* timeline = NULL;
 
   if (priv->object)
     {
@@ -133,24 +128,24 @@ clutter_animation_dispose (GObject *gobject)
       priv->object = NULL;
     }
 
-  if (priv->timeline)
+  if (priv->alpha)
+    timeline = clutter_alpha_get_timeline(priv->alpha);
+
+  if (timeline)
     {
       if (priv->timeline_started_id)
         {
-          g_signal_handler_disconnect (priv->timeline,
+          g_signal_handler_disconnect (timeline,
                                        priv->timeline_started_id);
           priv->timeline_started_id = 0;
         }
 
       if (priv->timeline_completed_id)
         {
-          g_signal_handler_disconnect (priv->timeline,
+          g_signal_handler_disconnect (timeline,
                                        priv->timeline_completed_id);
           priv->timeline_completed_id = 0;
         }
-
-      g_object_unref (priv->timeline);
-      priv->timeline = NULL;
     }
 
   if (priv->alpha)
@@ -214,6 +209,7 @@ clutter_animation_get_property (GObject    *gobject,
                                 GValue     *value,
                                 GParamSpec *pspec)
 {
+  ClutterAnimation* animation = CLUTTER_ANIMATION (gobject);
   ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv;
 
   switch (prop_id)
@@ -223,19 +219,19 @@ clutter_animation_get_property (GObject    *gobject,
       break;
 
     case PROP_MODE:
-      g_value_set_ulong (value, priv->mode);
+      g_value_set_ulong (value, 
clutter_alpha_get_mode(clutter_animation_get_alpha(animation)));
       break;
 
     case PROP_DURATION:
-      g_value_set_uint (value, priv->duration);
+      g_value_set_uint (value, 
clutter_timeline_get_duration(clutter_animation_get_timeline(animation)));
       break;
 
     case PROP_LOOP:
-      g_value_set_boolean (value, priv->loop);
+      g_value_set_boolean (value, 
clutter_timeline_get_loop(clutter_animation_get_timeline(animation)));
       break;
 
     case PROP_TIMELINE:
-      g_value_set_object (value, priv->timeline);
+      g_value_set_object (value, clutter_animation_get_timeline(animation));
       break;
 
     case PROP_ALPHA:
@@ -396,7 +392,6 @@ clutter_animation_init (ClutterAnimation *self)
 {
   self->priv = CLUTTER_ANIMATION_GET_PRIVATE (self);
 
-  self->priv->mode = CLUTTER_LINEAR;
   self->priv->properties =
     g_hash_table_new_full (g_str_hash, g_str_equal,
                            (GDestroyNotify) g_free,
@@ -750,7 +745,7 @@ on_timeline_completed (ClutterTimeline  *timeline,
 {
   CLUTTER_NOTE (ANIMATION, "Timeline [%p] complete", timeline);
 
-  if (!animation->priv->loop)
+  if (!clutter_timeline_get_loop(timeline))
     g_signal_emit (animation, animation_signals[COMPLETED], 0);
 }
 
@@ -924,16 +919,6 @@ clutter_animation_get_object (ClutterAnimation *animation)
   return animation->priv->object;
 }
 
-static inline void
-clutter_animation_set_mode_internal (ClutterAnimation *animation,
-                                     ClutterAlpha     *alpha)
-{
-  ClutterAnimationPrivate *priv = animation->priv;
-
-  if (alpha)
-    clutter_alpha_set_mode (alpha, priv->mode);
-}
-
 /**
  * clutter_animation_set_mode:
  * @animation: a #ClutterAnimation
@@ -943,20 +928,18 @@ clutter_animation_set_mode_internal (ClutterAnimation 
*animation,
  * a logical id, either coming from the #ClutterAnimationMode enumeration
  * or the return value of clutter_alpha_register_func().
  *
+ * If the @animation does not yet have a #ClutterAlpha, one will be created
+ * automatically for it.
+ *
  * Since: 1.0
  */
 void
 clutter_animation_set_mode (ClutterAnimation *animation,
                             gulong            mode)
 {
-  ClutterAnimationPrivate *priv;
-
   g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
 
-  priv = animation->priv;
-
-  priv->mode = mode;
-  clutter_animation_set_mode_internal (animation, priv->alpha);
+  clutter_alpha_set_mode(clutter_animation_get_alpha(animation), mode);
 
   g_object_notify (G_OBJECT (animation), "mode");
 }
@@ -977,7 +960,7 @@ clutter_animation_get_mode (ClutterAnimation *animation)
 {
   g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), CLUTTER_LINEAR);
 
-  return animation->priv->mode;
+  return clutter_alpha_get_mode(clutter_animation_get_alpha(animation));
 }
 
 /**
@@ -987,33 +970,30 @@ clutter_animation_get_mode (ClutterAnimation *animation)
  *
  * Sets the duration of @animation in milliseconds.
  *
+ * If the @animation does not yet have a #ClutterTimeline, one will be created
+ * automatically for it.
+ *
  * Since: 1.0
  */
 void
 clutter_animation_set_duration (ClutterAnimation *animation,
                                 gint              msecs)
 {
-  ClutterAnimationPrivate *priv;
+  ClutterTimeline *timeline;
+  gboolean was_playing;
 
   g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
 
-  priv = animation->priv;
-
-  priv->duration = msecs;
-
-  if (priv->timeline)
-    {
-      gboolean was_playing;
+  timeline = clutter_animation_get_timeline(animation);
 
-      was_playing = clutter_timeline_is_playing (priv->timeline);
-      if (was_playing)
-        clutter_timeline_stop (priv->timeline);
+  was_playing = clutter_timeline_is_playing (timeline);
+  if (was_playing)
+    clutter_timeline_stop (timeline);
 
-      clutter_timeline_set_duration (priv->timeline, msecs);
+  clutter_timeline_set_duration (timeline, msecs);
 
-      if (was_playing)
-        clutter_timeline_start (priv->timeline);
-    }
+  if (was_playing)
+    clutter_timeline_start (timeline);
 
   g_object_notify (G_OBJECT (animation), "duration");
 }
@@ -1028,6 +1008,9 @@ clutter_animation_set_duration (ClutterAnimation 
*animation,
  * A looping #ClutterAnimation will not emit the #ClutterAnimation::completed
  * signal when finished.
  *
+ * If the @animation does not yet have a #ClutterTimeline, one will be created
+ * automatically for it.
+ *
  * Since: 1.0
  */
 void
@@ -1035,17 +1018,17 @@ clutter_animation_set_loop (ClutterAnimation *animation,
                             gboolean          loop)
 {
   ClutterAnimationPrivate *priv;
+  ClutterTimeline *timeline;
 
   g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
 
   priv = animation->priv;
 
-  if (priv->loop != loop)
-    {
-      priv->loop = loop;
+  timeline = clutter_animation_get_timeline(animation);
 
-      if (priv->timeline)
-        clutter_timeline_set_loop (priv->timeline, priv->loop);
+  if (clutter_timeline_get_loop(timeline) != loop)
+    {
+      clutter_timeline_set_loop (timeline, loop);
 
       g_object_notify (G_OBJECT (animation), "loop");
     }
@@ -1057,6 +1040,9 @@ clutter_animation_set_loop (ClutterAnimation *animation,
  *
  * Retrieves whether @animation is looping.
  *
+ * If the @animation does not yet have a #ClutterTimeline, one will be created
+ * automatically for it.
+ *
  * Return value: %TRUE if the animation is looping
  *
  * Since: 1.0
@@ -1066,7 +1052,7 @@ clutter_animation_get_loop (ClutterAnimation *animation)
 {
   g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), FALSE);
 
-  return animation->priv->loop;
+  return clutter_timeline_get_loop(clutter_animation_get_timeline(animation));
 }
 
 /**
@@ -1075,6 +1061,9 @@ clutter_animation_get_loop (ClutterAnimation *animation)
  *
  * Retrieves the duration of @animation, in milliseconds.
  *
+ * If the @animation does not yet have a #ClutterTimeline, one will be created
+ * automatically for it.
+ *
  * Return value: the duration of the animation
  *
  * Since: 1.0
@@ -1084,87 +1073,85 @@ clutter_animation_get_duration (ClutterAnimation 
*animation)
 {
   g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), 0);
 
-  return animation->priv->duration;
+  return 
clutter_timeline_get_duration(clutter_animation_get_timeline(animation));
 }
 
-/**
- * clutter_animation_set_timeline:
- * @animation: a #ClutterAnimation
- * @timeline: a #ClutterTimeline or %NULL
- *
- * Sets the #ClutterTimeline used by @animation.
- *
- * The #ClutterAnimation:duration and #ClutterAnimation:loop properties
- * will be set using the corresponding #ClutterTimeline properties as a
- * side effect.
- *
- * If @timeline is %NULL a new #ClutterTimeline will be constructed
- * using the current values of the #ClutterAnimation:duration and
- * #ClutterAnimation:loop properties.
- *
- * Since: 1.0
- */
-void
-clutter_animation_set_timeline (ClutterAnimation *animation,
-                                ClutterTimeline  *timeline)
+static void clutter_animation_set_timeline_internal(
+                   ClutterAnimation *animation,
+                   ClutterTimeline  *timeline)
 {
   ClutterAnimationPrivate *priv;
-
-  g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
-  g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline));
+  ClutterTimeline *old_timeline = NULL;
 
   priv = animation->priv;
 
-  if (timeline != NULL && priv->timeline == timeline)
-    return;
+  if (priv->alpha)
+    {
+      old_timeline = clutter_alpha_get_timeline(priv->alpha);
+    }
 
-  g_object_freeze_notify (G_OBJECT (animation));
+  if (timeline != NULL && old_timeline == timeline)
+    return;
 
-  if (priv->timeline != NULL)
+  if (old_timeline != NULL)
     {
       if (priv->timeline_started_id)
-        g_signal_handler_disconnect (priv->timeline,
+        g_signal_handler_disconnect (old_timeline,
                                      priv->timeline_started_id);
 
       if (priv->timeline_completed_id)
-        g_signal_handler_disconnect (priv->timeline,
+        g_signal_handler_disconnect (old_timeline,
                                      priv->timeline_completed_id);
 
-      g_object_unref (priv->timeline);
       priv->timeline_started_id = 0;
       priv->timeline_completed_id = 0;
-      priv->timeline = 0;
     }
 
-  if (timeline == NULL)
+  if (timeline != NULL) 
     {
-      timeline = g_object_new (CLUTTER_TYPE_TIMELINE,
-                               "duration", priv->duration,
-                               "loop", priv->loop,
-                               NULL);
+      priv->timeline_started_id =
+        g_signal_connect (timeline, "started",
+                          G_CALLBACK (on_timeline_started),
+                          animation);
+      priv->timeline_completed_id =
+        g_signal_connect (timeline, "completed",
+                          G_CALLBACK (on_timeline_completed),
+                          animation);
     }
-  else
-    {
-      g_object_ref (timeline);
 
-      priv->duration = clutter_timeline_get_duration (timeline);
-      g_object_notify (G_OBJECT (animation), "duration");
+  g_object_notify (G_OBJECT (animation), "timeline");
+  g_object_notify (G_OBJECT (animation), "duration");
+  g_object_notify (G_OBJECT (animation), "loop");
+}
 
-      priv->loop = clutter_timeline_get_loop (timeline);
-      g_object_notify (G_OBJECT (animation), "loop");
-    }
+/**
+ * clutter_animation_set_timeline:
+ * @animation: a #ClutterAnimation
+ * @timeline: a #ClutterTimeline or %NULL
+ *
+ * Sets the #ClutterTimeline used by @animation.  Note that this sets the 
+ * timeline on the @animation's alpha, too, if it has one.
+ *
+ * The duration and loop properties of @animation are updated from the
+ * properties of the timeline.
+ *
+ * Set @timeline to %NULL to clear the @animation's current timeline.
+ *
+ * Since: 1.0
+ */
+void
+clutter_animation_set_timeline (ClutterAnimation *animation,
+                                ClutterTimeline  *timeline)
+{
+  g_return_if_fail (CLUTTER_IS_ANIMATION (animation));
+  g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline));
 
-  priv->timeline = timeline;
-  g_object_notify (G_OBJECT (animation), "timeline");
+  g_object_freeze_notify (G_OBJECT (animation));
 
-  priv->timeline_started_id =
-    g_signal_connect (timeline, "started",
-                      G_CALLBACK (on_timeline_started),
-                      animation);
-  priv->timeline_completed_id =
-    g_signal_connect (timeline, "completed",
-                      G_CALLBACK (on_timeline_completed),
-                      animation);
+  clutter_animation_set_timeline_internal(animation, timeline);
+
+  if (timeline || animation->priv->alpha)
+    clutter_alpha_set_timeline(clutter_animation_get_alpha(animation), 
timeline);
 
   g_object_thaw_notify (G_OBJECT (animation));
 }
@@ -1173,7 +1160,9 @@ clutter_animation_set_timeline (ClutterAnimation 
*animation,
  * clutter_animation_get_timeline:
  * @animation: a #ClutterAnimation
  *
- * Retrieves the #ClutterTimeline used by @animation
+ * Retrieves the #ClutterTimeline used by @animation.  If no timeline is
+ * set on the @animation, then a new one is automatically created with
+ * duration=0 and loop=FALSE.
  *
  * Return value: (transfer none): the timeline used by the animation
  *
@@ -1182,9 +1171,25 @@ clutter_animation_set_timeline (ClutterAnimation 
*animation,
 ClutterTimeline *
 clutter_animation_get_timeline (ClutterAnimation *animation)
 {
+  ClutterAlpha* alpha;
+  ClutterTimeline* timeline;
+
   g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
 
-  return animation->priv->timeline;
+  alpha = clutter_animation_get_alpha(animation);
+
+  timeline = clutter_alpha_get_timeline(alpha);
+
+  if (timeline == NULL)
+  {
+    timeline = g_object_new(CLUTTER_TYPE_TIMELINE,
+                            "duration", 0,
+                            "loop", 0,
+                            NULL);
+    clutter_animation_set_timeline(animation, timeline);
+  }
+
+  return timeline;
 }
 
 /**
@@ -1192,11 +1197,13 @@ clutter_animation_get_timeline (ClutterAnimation 
*animation)
  * @animation: a #ClutterAnimation
  * @alpha: a #ClutterAlpha, or %NULL
  *
- * Sets @alpha as the #ClutterAlpha used by @animation.
+ * Sets @alpha as the #ClutterAlpha used by @animation.  The timeline
+ * from @alpha becomes the timeline for the @animation as well.
+ *
+ * The @animation's mode is set to the mode of @alpha, and the duration
+ * and loop are updated from @alpha's timeline.
  *
- * If @alpha is %NULL, a new #ClutterAlpha will be constructed from
- * the current values of the #ClutterAnimation:mode and
- * #ClutterAnimation:timeline properties.
+ * Set @alpha to %NULL to disconnect the animation from its current alpha.
  *
  * Since: 1.0
  */
@@ -1211,18 +1218,13 @@ clutter_animation_set_alpha (ClutterAnimation 
*animation,
 
   priv = animation->priv;
 
-  if (!alpha)
-    {
-      ClutterTimeline *timeline;
-
-      timeline = clutter_animation_get_timeline (animation);
+  if (alpha && priv->alpha == alpha)
+    return;
 
-      alpha = clutter_alpha_new ();
-      clutter_alpha_set_timeline (alpha, timeline);
-      clutter_animation_set_mode_internal (animation, alpha);
-    }
+  g_object_freeze_notify (G_OBJECT (animation));
 
-  g_object_ref_sink (alpha);
+  /* This must be done before priv->alpha is modified */
+  clutter_animation_set_timeline_internal(animation, 
clutter_alpha_get_timeline(alpha));
 
   if (priv->alpha)
     {
@@ -1234,19 +1236,28 @@ clutter_animation_set_alpha (ClutterAnimation 
*animation,
       priv->alpha = NULL;
     }
 
-  priv->alpha = alpha;
+  if (alpha)
+    {
+      g_object_ref_sink (alpha);
+      priv->alpha = alpha;
+
+      priv->alpha_notify_id =
+        g_signal_connect (alpha, "notify::alpha",
+                          G_CALLBACK (on_alpha_notify),
+                          animation);
+    }
+
+  g_object_notify (G_OBJECT (animation), "alpha");
 
-  priv->alpha_notify_id =
-    g_signal_connect (alpha, "notify::alpha",
-                      G_CALLBACK (on_alpha_notify),
-                      animation);
+  g_object_thaw_notify (G_OBJECT (animation));
 }
 
 /**
  * clutter_animation_get_alpha:
  * @animation: a #ClutterAnimation
  *
- * Retrieves the #ClutterAlpha used by @animation.
+ * Retrieves the #ClutterAlpha used by @animation.  If the animation does not
+ * yet have an alpha, a new one is created and returned.
  *
  * Return value: (transfer none): the alpha object used by the animation
  *
@@ -1257,6 +1268,13 @@ clutter_animation_get_alpha (ClutterAnimation *animation)
 {
   g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL);
 
+  if (G_UNLIKELY (animation->priv->alpha == NULL)) {
+    clutter_animation_set_alpha (animation,
+                                 g_object_new(CLUTTER_TYPE_ALPHA,
+                                              "mode", CLUTTER_LINEAR,
+                                              NULL));
+  }
+
   return animation->priv->alpha;
 }
 
@@ -1298,13 +1316,11 @@ on_animation_completed (ClutterAnimation *animation)
 static void
 clutter_animation_start (ClutterAnimation *animation)
 {
-  if (G_LIKELY (animation->priv->timeline))
-    clutter_timeline_start (animation->priv->timeline);
-  else
-    {
-      /* sanity check */
-      g_warning (G_STRLOC ": no timeline found, unable to start the 
animation");
-    }
+  ClutterTimeline *timeline;
+
+  timeline = clutter_animation_get_timeline(animation);
+
+  clutter_timeline_start (timeline);
 }
 
 static void
@@ -1566,7 +1582,6 @@ clutter_actor_animate_with_alpha (ClutterActor *actor,
   else
     CLUTTER_NOTE (ANIMATION, "Reusing Animation [%p]", animation);
 
-  clutter_animation_set_timeline (animation, timeline);
   clutter_animation_set_alpha (animation, alpha);
   clutter_animation_set_object (animation, G_OBJECT (actor));
 
@@ -1632,7 +1647,6 @@ clutter_actor_animate_with_timeline (ClutterActor    
*actor,
     CLUTTER_NOTE (ANIMATION, "Reusing Animation [%p]", animation);
 
   clutter_animation_set_timeline (animation, timeline);
-  clutter_animation_set_alpha (animation, NULL);
   clutter_animation_set_mode (animation, mode);
   clutter_animation_set_object (animation, G_OBJECT (actor));
 
@@ -1816,8 +1830,6 @@ clutter_actor_animate (ClutterActor *actor,
        * current values for duration, mode and loop
        */
       animation = clutter_animation_new ();
-      clutter_animation_set_timeline (animation, NULL);
-      clutter_animation_set_alpha (animation, NULL);
       clutter_animation_set_object (animation, G_OBJECT (actor));
 
       g_signal_connect (animation, "completed",
@@ -1895,8 +1907,6 @@ clutter_actor_animatev (ClutterActor        *actor,
        * current values for duration, mode and loop
        */
       animation = clutter_animation_new ();
-      clutter_animation_set_timeline (animation, NULL);
-      clutter_animation_set_alpha (animation, NULL);
       clutter_animation_set_object (animation, G_OBJECT (actor));
 
       g_signal_connect (animation, "completed",
@@ -1981,7 +1991,6 @@ clutter_actor_animate_with_timelinev (ClutterActor        
*actor,
     CLUTTER_NOTE (ANIMATION, "Reusing Animation [%p]", animation);
 
   clutter_animation_set_timeline (animation, timeline);
-  clutter_animation_set_alpha (animation, NULL);
   clutter_animation_set_mode (animation, mode);
   clutter_animation_set_object (animation, G_OBJECT (actor));
   clutter_animation_setupv (animation, n_properties, properties, values);
@@ -2057,7 +2066,6 @@ clutter_actor_animate_with_alphav (ClutterActor        
*actor,
   else
     CLUTTER_NOTE (ANIMATION, "Reusing Animation [%p]", animation);
 
-  clutter_animation_set_timeline (animation, timeline);
   clutter_animation_set_alpha (animation, alpha);
   clutter_animation_set_object (animation, G_OBJECT (actor));
   clutter_animation_setupv (animation, n_properties, properties, values);
-- 
1.6.0.4


-- 
To unsubscribe send a mail to [email protected]

Reply via email to