[gnome-shell/wip/swarm: 1/2] Prototyping swarm animation



commit a74912d0f8af8e9ede9f8eba1d1aadd7637d57eb
Author: Carlos Soriano <carlos soriano89 gmail com>
Date:   Sun Mar 16 23:21:28 2014 +0100

    Prototyping swarm animation

 js/ui/appDisplay.js   |   40 +++++++++++
 js/ui/iconGrid.js     |  180 ++++++++++++++++++++++++++++++++++++++++++++++---
 js/ui/viewSelector.js |    3 +
 3 files changed, 212 insertions(+), 11 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index e43a679..dac04b6 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -623,6 +623,10 @@ const AllView = new Lang.Class({
         // Update folder views
         for (let i = 0; i < this.folderIcons.length; i++)
             this.folderIcons[i].adaptToSize(availWidth, availHeight);
+    },
+
+    swarmAnimation: function() {
+        //this._grid.swarmAnimation(0);
     }
 });
 Signals.addSignalMethods(AllView.prototype);
@@ -688,6 +692,10 @@ const FrequentView = new Lang.Class({
         let availWidth = box.x2 - box.x1;
         let availHeight = box.y2 - box.y1;
         this._grid.adaptToSize(availWidth, availHeight);
+    },
+    
+    swarmAnimation: function() {
+        //this._grid.swarmAnimation();
     }
 });
 
@@ -793,6 +801,28 @@ const AppDisplay = new Lang.Class({
             initialView = Views.ALL;
         this._showView(initialView);
         this._updateFrequentVisibility();
+        
+        // ViewSelector null in Main.overview, so we can't connect to page-changed
+        // signal. Manage all from view selector for now calling swamAnimation
+        /*
+        log(Main);
+        log(Main.overview);
+        log(Main.overview.viewSelector);
+        Main.overview.viewSelector.connect('page-changed', Lang.bind(this,
+            function() {
+                if (Main.overview.viewSelector.getActivePage() == ViewSelector.ViewPage.APPS) {
+                    this._views[global.settings.get_uint('app-picker-view')].swarmAnimation();
+                }
+            }));
+            */
+    },
+    
+    swarmAnimation: function() {
+        log("appDisplay");
+        log(this._views);
+        let view = this._views[global.settings.get_uint('app-picker-view')].view;
+        log(view);
+        view.swarmAnimation();
     },
 
     _showView: function(activeIndex) {
@@ -1034,6 +1064,7 @@ const FolderIcon = new Lang.Class({
 
     _init: function(id, path, parentView) {
         this.id = id;
+        this._path = path;
         this._parentView = parentView;
 
         this._folder = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder',
@@ -1199,6 +1230,10 @@ const FolderIcon = new Lang.Class({
             this.view.adaptToSize(width, height);
         this._popupInvalidated = true;
     },
+    
+    clone: function() {
+        return new FolderIcon(this.id, this._path, this._parentView);
+    }
 });
 Signals.addSignalMethods(FolderIcon.prototype);
 
@@ -1335,6 +1370,7 @@ const AppIcon = new Lang.Class({
 
         iconParams['createIcon'] = Lang.bind(this, this._createIcon);
         iconParams['setSizeManually'] = true;
+        this._iconParams = iconParams;
         this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
         this.actor.set_child(this.icon.actor);
 
@@ -1510,6 +1546,10 @@ const AppIcon = new Lang.Class({
     shouldShowTooltip: function() {
         return this.actor.hover && (!this._menu || !this._menu.isOpen);
     },
+    
+    clone: function() {
+        return new AppIcon(this.app, this._iconParams);
+    }
 });
 Signals.addSignalMethods(AppIcon.prototype);
 
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index 16c58e9..477892f 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -10,12 +10,19 @@ 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 SWARM_ANIMATION_TIME = 0.5;
+const SWARM_ANIMATION_MAX_DELAY_FOR_ITEM = 0.3;
+const SWARM_ANIMATION_FADE_IN_TIME_FOR_ITEM = 0.0;
+const SWARM_TYPE_RANDOM = 0;
+const SWARM_TYPE_FAR_FIRST = 1;
+
 const BaseIcon = new Lang.Class({
     Name: 'BaseIcon',
 
@@ -23,7 +30,9 @@ const BaseIcon = new Lang.Class({
         params = Params.parse(params, { createIcon: null,
                                         setSizeManually: false,
                                         showLabel: true });
-
+        this.labelForClone = label;
+        this.paramsForClone = params;
+        
         let styleClass = 'overview-icon';
         if (params.showLabel)
             styleClass += ' overview-icon-with-label';
@@ -216,6 +225,15 @@ const IconGrid = new Lang.Class({
         this._grid.connect('allocate', Lang.bind(this, this._allocate));
         this._grid.connect('actor-added', Lang.bind(this, this._childAdded));
         this._grid.connect('actor-removed', Lang.bind(this, this._childRemoved));
+        
+        this._paintedItems = [];
+        this._swarmType = SWARM_TYPE_FAR_FIRST;
+        this._animating = false; 
+        this._mappedId = this.actor.connect("notify::allocation", Lang.bind(this, 
+                function() {
+                    if (this.actor.mapped)
+                        this.swarmAnimation(this._paintedItems);
+                }));
     },
 
     _keyFocusIn: function(actor) {
@@ -256,6 +274,17 @@ const IconGrid = new Lang.Class({
         return children;
     },
 
+    _getVisibleItems: function() {;
+        items = this._items.filter(function(item) {
+            return item.actor.visible;
+        });
+        return items;
+    },
+    
+    _getPaintedItems: function() {;
+        return this._paintedItems;
+    },
+
     _getPreferredHeight: function (grid, forWidth, alloc) {
         if (this._fillParent)
             // Ignore all size requests of children and request a size of 0;
@@ -290,7 +319,7 @@ const IconGrid = new Lang.Class({
             box = this._grid.get_theme_node().get_content_box(gridBox);
         }
 
-        let children = this._getVisibleChildren();
+        let items = this._getVisibleItems();
         let availWidth = box.x2 - box.x1;
         let availHeight = box.y2 - box.y1;
         let spacing = this._getSpacing();
@@ -312,15 +341,18 @@ const IconGrid = new Lang.Class({
         let y = box.y1 + this.topPadding;
         let columnIndex = 0;
         let rowIndex = 0;
-        for (let i = 0; i < children.length; i++) {
-            let childBox = this._calculateChildBox(children[i], x, y, box);
+        this._paintedItems = [];
+
+        for (let i = 0; i < items.length; i++) {
+            let childBox = this._calculateChildBox(items[i].actor, x, y, box);
 
             if (this._rowLimit && rowIndex >= this._rowLimit ||
                 this._fillParent && childBox.y2 > availHeight - this.bottomPadding) {
-                this._grid.set_skip_paint(children[i], true);
+                this._grid.set_skip_paint(items[i].actor, true);
             } else {
-                children[i].allocate(childBox, flags);
-                this._grid.set_skip_paint(children[i], false);
+                items[i].actor.allocate(childBox, flags);
+                this._grid.set_skip_paint(items[i].actor, false);
+                this._paintedItems.push(items[i]);
             }
 
             columnIndex++;
@@ -338,6 +370,79 @@ const IconGrid = new Lang.Class({
         }
     },
 
+    swarmAnimation: function(items) {
+        if (this._animating)
+            return;
+
+        this._animating = true;
+        
+        let [startX, startY] = Main.overview._dash._showAppsIcon.get_transformed_position();
+        let distances = items.map(Lang.bind(this, function(item) {
+            return this._distance(item.actor.get_transformed_position(), [startX, startY]);
+        }));
+        
+        let delay;
+        let normalization = Math.max.apply(Math, distances);
+        
+        for (let index = 0; index < items.length; index++) {
+            items[index].actor.opacity = 0;
+            let [finalX, finalY] = items[index].actor.get_transformed_position();
+            if (this._swarmType == SWARM_TYPE_FAR_FIRST) {
+                let distance = this._distance([startX, startY], [finalX, finalY]);
+                log("dist, norm");
+                log(distance);
+                log(normalization)
+                delay = (1 - this._distance([startX, startY], [finalX, finalY]) / normalization) * 
SWARM_ANIMATION_MAX_DELAY_FOR_ITEM;
+            } else {
+                delay = Math.random() * SWARM_ANIMATION_MAX_DELAY_FOR_ITEM;
+            }
+            log(delay);
+            //delay = 0;
+            this._animateItem(items[index], [startX, startY], [finalX, finalY], delay);
+        }
+    },
+    
+    _distance: function(point1, point2) {
+        let x = point1[0] - point2[0];
+        let y = point1[1] - point2[1];
+        return Math.sqrt(x * x + y * y);
+    },
+
+    _animateItem: function(item, origin, target, delay) {
+        let itemClone = item.clone();
+        itemClone.actor.reactive = false;
+        let [width, height] = item.actor.get_transformed_size();
+        itemClone.actor.set_size(width, height);
+
+        itemClone.actor.set_position(origin[0], origin[1]);
+        itemClone.actor.opacity = 0;
+        
+        Tweener.addTween(itemClone.actor,
+                        { x: target[0],
+                          y: target[1],
+                          time: SWARM_ANIMATION_TIME,
+                          transition: 'easeInOutQuad',
+                          delay: delay,
+                          onStart: function() {
+                              Tweener.addTween(itemClone.actor,
+                                               { opacity : 255,
+                                                 transition: 'easeInOutQuad',
+                                                 time: SWARM_ANIMATION_FADE_IN_TIME_FOR_ITEM });
+                            },
+                          onComplete: Lang.bind(this, function() {
+                                log("opacity 255");
+                                item.actor.opacity = 255;
+                                itemClone.actor.destroy();
+                                // Assume the random value of delay is not important,
+                                // and setting animating to true in the first finished
+                                // animation is fine.
+                                this._animating = false;
+                            })
+                        });
+
+        Main.uiGroup.add_actor(itemClone.actor);
+    },
+
     _calculateChildBox: function(child, x, y, box) {
         let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] =
              child.get_preferred_size();
@@ -553,6 +658,13 @@ const PaginatedIconGrid = new Lang.Class({
         this._rowsPerPage = 0;
         this._spaceBetweenPages = 0;
         this._childrenPerPage = 0;
+        
+        this._mappedId = 0;
+                    this._mappedId = this.actor.connect("notify::allocation", Lang.bind(this, 
+                function() {
+                    if (this.actor.mapped)
+                        this.swarmAnimation();
+                }));
     },
 
     _getPreferredHeight: function (grid, forWidth, alloc) {
@@ -593,10 +705,16 @@ const PaginatedIconGrid = new Lang.Class({
         let columnIndex = 0;
         let rowIndex = 0;
 
-        for (let i = 0; i < children.length; i++) {
-            let childBox = this._calculateChildBox(children[i], x, y, box);
-            children[i].allocate(childBox, flags);
-            this._grid.set_skip_paint(children[i], false);
+/*
+        let dimmedItems = this._getItemsInPage(0);
+        for (let index = 0; index < dimmedItems.length; index++)
+            dimmedItems[index].actor.opacity = 0;*/
+
+        let visibleItems = this._getVisibleItems();
+        for (let i = 0; i < visibleItems.length; i++) {
+            let childBox = this._calculateChildBox(visibleItems[i].actor, x, y, box);
+            visibleItems[i].actor.allocate(childBox, flags);
+            this._grid.set_skip_paint(visibleItems[i].actor, false);
 
             columnIndex++;
             if (columnIndex == nColumns) {
@@ -613,6 +731,32 @@ const PaginatedIconGrid = new Lang.Class({
         }
     },
 
+    swarmAnimation: function() {
+        log("paginated swarm");
+        /*this.actor.disconnect(this._mappedId);
+        this._mappedId = 0;
+
+        log("animating" + items);
+        if (items.length == 0) {
+            this._mappedId = this.actor.connect("notify::allocation", Lang.bind(this, 
+                function() {
+                    if (this.actor.mapped)
+                        this.swarmAnimation(0);
+                }));
+        }*/
+        let items = this._getItemsInPage(0);
+        // TODO: Fix this
+        if (items.length == 0)
+            return;
+        this.parent(items);
+        /*
+        this._animating = true;
+        for (let index = 0; index < items.length; index++) {
+            items[index].actor.opacity = 0;
+            this._animateItem(items[index]);
+        }*/
+    },
+
     _computePages: function (availWidthPerPage, availHeightPerPage) {
         let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage);
         let nRows;
@@ -668,6 +812,20 @@ const PaginatedIconGrid = new Lang.Class({
         return Math.floor(index / this._childrenPerPage);
     },
 
+    _getItemsInPage: function(pageNumber) {
+        let items = this._getVisibleItems();
+        let firstIndex = this._childrenPerPage * pageNumber;
+        let indexOffset = 0;
+        let itemsInPage = []
+
+        while (indexOffset < this._childrenPerPage && firstIndex + indexOffset < items.length) {
+               itemsInPage.push(items[firstIndex + indexOffset]);
+               indexOffset++;
+        }
+
+        return itemsInPage;
+    },
+
     /**
     * openExtraSpace:
     * @sourceItem: the item for which to create extra space
diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js
index 7dacc47..a9a0a0d 100644
--- a/js/ui/viewSelector.js
+++ b/js/ui/viewSelector.js
@@ -240,6 +240,9 @@ const ViewSelector = new Lang.Class({
                              });
         else
             this._fadePageIn(oldPage);
+
+        if (this._activePage == this._appsPage)
+            this.appDisplay.swarmAnimation();
     },
 
     _a11yFocusPage: function(page) {


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