[gnome-shell/wip/swarm: 114/126] appDisplay: Animate folder view items



commit 34a40fe39dfc0435a7cb39033ef32f1d0e7a11dd
Author: Carlos Soriano <carlos soriano89 gmail com>
Date:   Tue Jun 17 12:47:00 2014 +0200

    appDisplay: Animate folder view items

 js/ui/appDisplay.js |   23 ++++++++++++-
 js/ui/iconGrid.js   |   89 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+), 2 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index dcecae3..ca56d6c 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -966,6 +966,10 @@ const FolderView = new Lang.Class({
         Util.ensureActorVisibleInScrollView(this.actor, actor);
     },
 
+    animate: function(animationType, animationDirection, params) {
+        this._grid.animate(animationType, animationDirection, params);
+    },
+
     createFolderIcon: function(size) {
         let layout = new Clutter.GridLayout();
         let icon = new St.Widget({ layout_manager: layout,
@@ -1341,8 +1345,22 @@ const AppFolderPopup = new Lang.Class({
         this.actor.show();
 
         this._boxPointer.setArrowActor(this._source.actor);
+        // We need to hide the icons of the view until the boxpointer animation
+        // is completed so we can animate the icons after as we like withouth
+        // showing them while boxpointer is animating.
+        this._view.actor.opacity = 0;
         this._boxPointer.show(BoxPointer.PopupAnimation.FADE |
-                              BoxPointer.PopupAnimation.SLIDE);
+                              BoxPointer.PopupAnimation.SLIDE,
+                              Lang.bind(this,
+            function() {
+                // Restore the view opacity, so now we show the icons and animate
+                // them
+                this._view.actor.opacity = 255;
+                this._view.animate(IconGrid.AnimationType.BOUNCE,
+                                   IconGrid.AnimationDirection.IN,
+                                   { sourcePosition: this._source.actor.get_transformed_position(),
+                                     sourceSize: this._source.actor.get_transformed_size() });
+            }));
 
         this.emit('open-state-changed', true);
     },
@@ -1354,7 +1372,8 @@ const AppFolderPopup = new Lang.Class({
         this._grabHelper.ungrab({ actor: this.actor });
 
         this._boxPointer.hide(BoxPointer.PopupAnimation.FADE |
-                              BoxPointer.PopupAnimation.SLIDE);
+                              BoxPointer.PopupAnimation.SLIDE,
+                              this._view.animateOut);
         this._isOpen = false;
         this.emit('open-state-changed', false);
     },
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index 9cf9f2f..e29f3ba 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -10,12 +10,26 @@ const St = imports.gi.St;
 const Lang = imports.lang;
 const Params = imports.misc.params;
 const Tweener = imports.ui.tweener;
+const Main = imports.ui.main;
 
 const ICON_SIZE = 96;
 const MIN_ICON_SIZE = 16;
 
 const EXTRA_SPACE_ANIMATION_TIME = 0.25;
 
+const ANIMATION_TIME_IN = 0.350;
+const ANIMATION_MAX_DELAY_FOR_ITEM = 2/3 * ANIMATION_TIME_IN;
+
+const ANIMATION_BOUNCE_ICON_SCALE = 1.1;
+
+const AnimationType = {
+    BOUNCE: 0
+};
+
+const AnimationDirection = {
+    IN: 0
+};
+
 const BaseIcon = new Lang.Class({
     Name: 'BaseIcon',
 
@@ -338,6 +352,81 @@ const IconGrid = new Lang.Class({
         }
     },
 
+    /**
+     * Intended to be override by subclasses if they need a diferent
+     * set of items to be animated.
+     */
+    _getChildrenToAnimate: function() {
+        return this._getVisibleChildren();
+    },
+
+    animate: function(animationType, animationDirection, params) {
+        let actors = this._getChildrenToAnimate();
+        if (this._animating || actors.length == 0)
+            return;
+        this._animating = true;
+
+        for (let index = 0; index < actors.length; index++)
+            actors[index].opacity = 0;
+
+        let delayedAnimation = Lang.bind(this, function() {
+            switch (animationType) {
+                case AnimationType.BOUNCE:
+                    this._animateBounce(actors, animationDirection);
+                    break;
+                default:
+                    break;
+            }
+        });
+        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, delayedAnimation);
+    },
+
+    _animateBounce: function(actors, animationDirection) {
+        for (let index = 0; index < actors.length; index++) {
+            let delay = index / actors.length * ANIMATION_MAX_DELAY_FOR_ITEM;
+
+            let [originalX, originalY] = actors[index].get_transformed_position();
+            let [originalWidth, originalHeight] = actors[index].get_transformed_size();
+
+            let actorClone = new Clutter.Clone({ source: actors[index],
+                                                reactive: false });
+            Main.uiGroup.add_actor(actorClone);
+
+            actorClone.reactive = false;
+            actorClone.set_position(originalX, originalY);
+            actorClone.set_scale(0, 0);
+            actorClone.set_pivot_point(0.5, 0.5);
+            let [width, height] = actors[index].get_transformed_size();
+            actorClone.set_size(width, height);
+
+            // Defeat onComplete anonymous function closure
+            let actor= actors[index];
+            let isLastActor= index == actors.length - 1;
+            Tweener.addTween(actorClone,
+                            { time: ANIMATION_TIME_IN / 4,
+                              transition: 'easeInOutQuad',
+                              delay: delay,
+                              scale_x: ANIMATION_BOUNCE_ICON_SCALE,
+                              scale_y: ANIMATION_BOUNCE_ICON_SCALE,
+                              onComplete: Lang.bind(this, function() {
+                                  Tweener.addTween(actorClone,
+                                                   { time: ANIMATION_TIME_IN - ANIMATION_TIME_IN / 4,
+                                                     transition: 'easeInOutQuad',
+                                                     scale_x: 1,
+                                                     scale_y: 1,
+                                                     onComplete: Lang.bind(this, function() {
+                                                        if (isLastActor)
+                                                            this._animating = false;
+                                                        actor.opacity = 255;
+                                                        actorClone.destroy();
+                                                        this.emit('animation-done');
+                                                    })
+                                                   });
+                              })
+                            });
+        }
+    },
+
     _calculateChildBox: function(child, x, y, box) {
         let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] =
              child.get_preferred_size();


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