[gnome-shell] panel: programmatic anim. control of AnimatedIcon



commit c48a246ccbee618956eefdb496770cc8d2694ec6
Author: StÃphane DÃmurget <stephane demurget free fr>
Date:   Mon Nov 5 23:11:27 2012 +0100

    panel: programmatic anim. control of AnimatedIcon
    
    The AnimatedIcon does not have an API for controlling the animation but
    relies on the :visible property changes to start and stop a timeout used
    to update the frame.
    
    This has the inconvenient of having a side effect when visible is set to
    true multiple times, and is not really the API expected from such
    component. Also, there is a race if it is displayed before the images
    are loaded: there is no child yet and thus we get this._frame = NaN
    which leads to a crash.
    
    Switch to a play/stop API instead, and add a load event callback to the
    TextureCache.load_slice_image to exactly know when we can start using
    the images.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=687583

 js/ui/panel.js            |   39 +++++++++++++++++++++++++++++----------
 src/st/st-texture-cache.c |   20 ++++++++++++++++----
 src/st/st-texture-cache.h |   10 ++++++----
 3 files changed, 51 insertions(+), 18 deletions(-)
---
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 9f36963..15a5db2 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -82,26 +82,34 @@ const AnimatedIcon = new Lang.Class({
     _init: function(name, size) {
         this.actor = new St.Bin({ visible: false });
         this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
-        this.actor.connect('notify::visible', Lang.bind(this, this._onVisibleNotify));
 
+        this._isLoaded = false;
+        this._isPlaying = false;
         this._timeoutId = 0;
         this._frame = 0;
-        this._animations = St.TextureCache.get_default().load_sliced_image (global.datadir + '/theme/' + name, size, size);
+        this._animations = St.TextureCache.get_default().load_sliced_image (global.datadir + '/theme/' + name, size, size,
+                                                                            Lang.bind(this, this._animationsLoaded));
         this.actor.set_child(this._animations);
     },
 
-    _disconnectTimeout: function() {
+    play: function() {
+        if (this._isLoaded && this._timeoutId == 0) {
+            if (this._frame == 0)
+                this._showFrame(0);
+
+            this._timeoutId = Mainloop.timeout_add(ANIMATED_ICON_UPDATE_TIMEOUT, Lang.bind(this, this._update));
+        }
+
+        this._isPlaying = true;
+    },
+
+    stop: function() {
         if (this._timeoutId > 0) {
             Mainloop.source_remove(this._timeoutId);
             this._timeoutId = 0;
         }
-    },
 
-    _onVisibleNotify: function() {
-        if (this.actor.visible)
-            this._timeoutId = Mainloop.timeout_add(ANIMATED_ICON_UPDATE_TIMEOUT, Lang.bind(this, this._update));
-        else
-            this._disconnectTimeout();
+        this._isPlaying = false;
     },
 
     _showFrame: function(frame) {
@@ -121,8 +129,17 @@ const AnimatedIcon = new Lang.Class({
         return true;
     },
 
+    _animationsLoaded: function() {
+        this._isLoaded = true;
+
+        if (this._isPlaying) {
+            this._showFrame(0);
+            this._timeoutId = Mainloop.timeout_add(ANIMATED_ICON_UPDATE_TIMEOUT, Lang.bind(this, this._update));
+        }
+    },
+
     _onDestroy: function() {
-        this._disconnectTimeout();
+        this.stop();
     }
 });
 
@@ -367,6 +384,7 @@ const AppMenuButton = new Lang.Class({
                            transition: "easeOutQuad",
                            onCompleteScope: this,
                            onComplete: function() {
+                               this._spinner.stop();
                                this._spinner.actor.opacity = 255;
                                this._spinner.actor.hide();
                            }
@@ -376,6 +394,7 @@ const AppMenuButton = new Lang.Class({
     startAnimation: function() {
         this._stop = false;
         this.actor.reactive = false;
+        this._spinner.play();
         this._spinner.actor.show();
     },
 
diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c
index d178490..c562a77 100644
--- a/src/st/st-texture-cache.c
+++ b/src/st/st-texture-cache.c
@@ -1073,6 +1073,8 @@ typedef struct {
   gchar *path;
   gint   grid_width, grid_height;
   ClutterActor *actor;
+  GFunc load_callback;
+  gpointer load_callback_data;
 } AsyncImageData;
 
 static void
@@ -1089,6 +1091,7 @@ on_sliced_image_loaded (GObject *source_object,
                         GAsyncResult *res,
                         gpointer user_data)
 {
+  GObject *cache = source_object;
   AsyncImageData *data = (AsyncImageData *)user_data;
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
   GList *list;
@@ -1102,6 +1105,9 @@ on_sliced_image_loaded (GObject *source_object,
       clutter_actor_hide (actor);
       clutter_actor_add_child (data->actor, actor);
     }
+
+  if (data->load_callback != NULL)
+    data->load_callback (cache, data->load_callback_data);
 }
 
 static void
@@ -1156,6 +1162,8 @@ load_sliced_image (GSimpleAsyncResult *result,
  * @path: Path to a filename
  * @grid_width: Width in pixels
  * @grid_height: Height in pixels
+ * @load_callback: (scope async) (allow-none): Function called when the image is loaded, or %NULL
+ * @user_data: Data to pass to the load callback
  *
  * This function reads a single image file which contains multiple images internally.
  * The image file will be divided using @grid_width and @grid_height;
@@ -1165,10 +1173,12 @@ load_sliced_image (GSimpleAsyncResult *result,
  * Returns: (transfer none): A new #ClutterActor
  */
 ClutterActor *
-st_texture_cache_load_sliced_image (StTextureCache    *cache,
-                                    const gchar       *path,
-                                    gint               grid_width,
-                                    gint               grid_height)
+st_texture_cache_load_sliced_image (StTextureCache *cache,
+                                    const gchar    *path,
+                                    gint            grid_width,
+                                    gint            grid_height,
+                                    GFunc           load_callback,
+                                    gpointer        user_data)
 {
   AsyncImageData *data;
   GSimpleAsyncResult *result;
@@ -1179,6 +1189,8 @@ st_texture_cache_load_sliced_image (StTextureCache    *cache,
   data->grid_height = grid_height;
   data->path = g_strdup (path);
   data->actor = actor;
+  data->load_callback = load_callback;
+  data->load_callback_data = user_data;
   g_object_ref (G_OBJECT (actor));
 
   result = g_simple_async_result_new (G_OBJECT (cache), on_sliced_image_loaded, data, st_texture_cache_load_sliced_image);
diff --git a/src/st/st-texture-cache.h b/src/st/st-texture-cache.h
index 8db438b..c913b39 100644
--- a/src/st/st-texture-cache.h
+++ b/src/st/st-texture-cache.h
@@ -69,10 +69,12 @@ GType st_texture_cache_get_type (void) G_GNUC_CONST;
 StTextureCache* st_texture_cache_get_default (void);
 
 ClutterActor *
-st_texture_cache_load_sliced_image (StTextureCache    *cache,
-                                    const gchar       *path,
-                                    gint               grid_width,
-                                    gint               grid_height);
+st_texture_cache_load_sliced_image (StTextureCache *cache,
+                                    const gchar    *path,
+                                    gint            grid_width,
+                                    gint            grid_height,
+                                    GFunc           load_callback,
+                                    gpointer        user_data);
 
 ClutterActor *st_texture_cache_bind_pixbuf_property (StTextureCache    *cache,
                                                      GObject           *object,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]