From c48a246ccbee618956eefdb496770cc8d2694ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20D=C3=A9murget?= Date: Mon, 5 Nov 2012 23:11:27 +0100 Subject: [PATCH] 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 9f3696346..15a5db2f3 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 d178490f4..c562a777b 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 8db438b4f..c913b39cd 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,