[gnome-shell/wip/paging-release2: 19/24] appDisplay: Make space on grid to fit collection when opening



commit 246135dc02b95614efef3d5c7da918ec9516b78c
Author: Carlos Soriano <carlos soriano89 gmail com>
Date:   Tue Jul 9 15:11:03 2013 +0200

    appDisplay: Make space on grid to fit collection when opening
    
    Add properly functions in AllView and FolderView to animate
    the FolderView popup opening and give enough space on the parent
    view to fit the popup of the folder view
    
    https://bugzilla.gnome.org/show_bug.cgi?id=706081

 js/ui/appDisplay.js |  137 +++++++++++++++++++++++++++++++++++++++++++++++++--
 js/ui/iconGrid.js   |   24 +++++++++
 2 files changed, 157 insertions(+), 4 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 9513432..fb5b7f0 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -34,7 +34,7 @@ const MIN_COLUMNS = 4;
 const MIN_ROWS = 4;
 
 const INACTIVE_GRID_OPACITY = 77;
-const INACTIVE_GRID_OPACITY_ANIMATION_TIME = 0.15;
+const INACTIVE_GRID_OPACITY_ANIMATION_TIME = 0.40;
 const FOLDER_SUBICON_FRACTION = .4;
 
 const INDICATORS_ANIMATION_TIME = 0.6;
@@ -48,6 +48,8 @@ const INDICATOR_MOVE_OFFSET = 60;
 const PAGE_SWITCH_TRESHOLD = 0.2;
 const PAGE_SWITCH_TIME = 0.3;
 
+const POPUP_FOLDER_VIEW_ANIMATION = 0.25;
+
 // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
 function _loadCategory(dir, view) {
     let iter = dir.iter();
@@ -287,6 +289,8 @@ const AllView = new Lang.Class({
         }));
         this._eventBlocker.add_action(this._clickAction);
 
+        this._displayingPopup = false;
+
         this._availWidth = 0;
         this._availHeight = 0;
 
@@ -301,6 +305,11 @@ const AllView = new Lang.Class({
     },
 
     goToPage: function(pageNumber) {
+        if (this._currentPage != pageNumber && this._displayingPopup && this._currentPopup)
+            this._currentPopup.popdown();
+        else if (this._displayingPopup && this._currentPopup)
+                return;
+
         let velocity;
         if (!this._panning)
             velocity = 0;
@@ -342,7 +351,104 @@ const AllView = new Lang.Class({
         return Math.abs(currentScrollPosition - this._grid.getPageY(pageNumber));
     },
 
+    /**
+     * Pan view with items to make space for the folder view.
+     * @param nRows this parameter tell how many rows the folder view has, but,
+     * it is already constrained to be at maximum of main grid rows least one, to ensure we have
+     * enough space to show the folder view popup.
+     */
+    makeSpaceForPopUp: function(iconActor, side, nRows) {
+        let mainIconYPosition = iconActor.actor.y;
+        let rows = this._grid.pageRows(this._currentPage);
+        let rowsAbove = [];
+        let rowsBelow = [];
+        let rowsArray;
+        for (let rowIndex in rows) {
+            if (mainIconYPosition == rows[rowIndex][0].y)
+                rowsArray = (side == St.Side.BOTTOM) ? rowsBelow : rowsAbove;
+            else
+                rowsArray = (mainIconYPosition < rows[rowIndex][0].y) ? rowsBelow: rowsAbove;
+            rowsArray.push(rows[rowIndex]);
+        }
+        //The last page can have space without rows
+        let emptyRows = this._grid.rowsPerPage() - rows.length ;
+        let nRowsUp;
+        let nRowsDown;
+        if(side == St.Side.BOTTOM) {
+            nRowsUp = Math.min(rowsAbove.length, nRows);
+            nRowsDown = nRows - nRowsUp;
+        } else {
+            nRowsDown = Math.min(rowsBelow.length + emptyRows, nRows);
+            nRowsUp = nRows - nRowsDown;
+        }
+        this._updateIconOpacities(true);
+        let dontExpand = (nRowsDown > 0 && rowsBelow.length == 0 && rowsAbove.length == 0);
+        // Especial case: last page and no rows below the icon of the folder,  and
+        // there's not need to moave any rows down neither rows up.
+        // Call directly the popup without animating
+        if (dontExpand) {
+            this._displayingPopup = true;
+            this._translatedChildren = [];
+            iconActor.onCompleteMakeSpaceForPopUp();
+        } else {
+            let rowHeight = this._grid.rowHeight();
+
+            let childrenUp = [];
+            if (nRowsUp > 0) {
+                let translationY = -rowHeight * nRowsUp;
+                childrenUp = rowsAbove.reduce(function(prev, cur) { return prev.concat(cur); });
+                this._translateChildren(childrenUp, translationY, iconActor);
+            }
+
+            let childrenDown = [];
+            if (nRowsDown > 0) {
+                let translationY = rowHeight * nRowsDown;
+                childrenDown = rowsBelow.reduce(function(prev, cur) { return prev.concat(cur); });
+                this._translateChildren(childrenDown, translationY, iconActor);
+            }
+
+            this._displayingPopup = true;
+            this._translatedChildren = childrenUp.concat(childrenDown);
+        }
+    },
+
+    _translateChildren: function(children, translationY, iconActor) {
+        for (let i = 0; i < children.length; i++) {
+            children[i]._translateY = 0;
+            let params = { _translateY: translationY,
+                           time: POPUP_FOLDER_VIEW_ANIMATION,
+                           onUpdate: function() { this.queue_relayout(); },
+                           transition: 'easeInOutQuad'
+                         };
+            if (i == 0)
+                params.onComplete = Lang.bind(iconActor, iconActor.onCompleteMakeSpaceForPopUp);
+            Tweener.addTween(children[i], params);
+        }
+    },
+
+    returnSpaceToOriginalPosition: function() {
+        this._updateIconOpacities(false);
+        if (!this._translatedChildren || !this._translatedChildren.length) {
+            this._displayingPopup = false;
+            return;
+        }
+
+        for (let i = 0; i < this._translatedChildren.length; i++) {
+            if (!this._translatedChildren[i]._translateY)
+                continue;
+            Tweener.addTween(this._translatedChildren[i],
+                             { _translateY: 0,
+                               time: POPUP_FOLDER_VIEW_ANIMATION,
+                               onUpdate: function() { this.queue_relayout(); },
+                               transition: 'easeInOutQuad',
+                               onComplete: Lang.bind(this, function() { this._displayingPopup = false; })
+                             });
+        }
+    },
+
     _onScroll: function(actor, event) {
+         if(this._displayingPopup)
+            return;
         let direction = event.get_scroll_direction();
         if (direction == Clutter.ScrollDirection.UP) {
             if (this._currentPage > 0)
@@ -356,6 +462,8 @@ const AllView = new Lang.Class({
     },
 
     _onPan: function(action) {
+        if (this._displayingPopup)
+            return false;
         this._panning = true;
         this._clickAction.release();
         let [dist, dx, dy] = action.get_motion_delta(0);
@@ -365,6 +473,8 @@ const AllView = new Lang.Class({
     },
 
     _onPanEnd: function(action) {
+         if (this._displayingPopup)
+            return;
         let diffCurrentPage = this._diffToPage(this._currentPage);
         if (diffCurrentPage > this._pagesView.height * PAGE_SWITCH_TRESHOLD) {
             if (action.get_velocity(0)[2] > 0 && this._currentPage > 0)
@@ -929,7 +1039,6 @@ const FolderIcon = new Lang.Class({
         this.actor.connect('clicked', Lang.bind(this,
             function() {
                 this._ensurePopup();
-                this._popup.toggle();
                 this.view.actor.vscroll.adjustment.value = 0;
             }));
         this.actor.connect('notify::mapped', Lang.bind(this,
@@ -937,6 +1046,9 @@ const FolderIcon = new Lang.Class({
                 if (!this.actor.mapped && this._popup)
                     this._popup.popdown();
             }));
+        this.actor.connect('notify::allocation', Lang.bind(this, function() {
+            Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, this._updatePopupPosition));
+        }));
     },
 
     _createIcon: function(size) {
@@ -948,6 +1060,18 @@ const FolderIcon = new Lang.Class({
         return usedHeight;
     },
 
+    makeSpaceForPopUp: function() {
+        this._parentView.makeSpaceForPopUp(this, this._boxPointerArrowside, 
this.view.nRowsDisplayedAtOnce());
+    },
+
+    returnSpaceToOriginalPosition: function() {
+        this._parentView.returnSpaceToOriginalPosition();
+    },
+
+    onCompleteMakeSpaceForPopUp: function() {
+        this._popup.popup();
+    },
+
     _calculateBoxPointerArrowSide: function() {
         let spaceTop = this.actor.y - this._parentView.getCurrentPageY();
         let spaceBottom = this._parentView.actor.height - (spaceTop + this.actor.height);
@@ -983,16 +1107,20 @@ const FolderIcon = new Lang.Class({
     },
 
     _ensurePopup: function() {
-        if (this._popup && !this._popupInvalidated)
+        if (this._popup && !this._popupInvalidated) {
+            this.makeSpaceForPopUp();
             return;
+        }
         this._boxPointerArrowside = this._calculateBoxPointerArrowSide();
         if (!this._popup) {
             this._popup = new AppFolderPopup(this, this._boxPointerArrowside);
             this._parentView.addFolderPopup(this._popup);
             this._popup.connect('open-state-changed', Lang.bind(this,
                 function(popup, isOpen) {
-                    if (!isOpen)
+                    if (!isOpen) {
                         this.actor.checked = false;
+                        this.returnSpaceToOriginalPosition();
+                    }
                 }));
         } else {
             this._popup.updateArrowSide(this._boxPointerArrowside);
@@ -1000,6 +1128,7 @@ const FolderIcon = new Lang.Class({
         this._updatePopupSize();
         this._updatePopupPosition();
         this._popupInvalidated = false;
+        this.makeSpaceForPopUp();
     },
 
     adaptToSize: function(width, height) {
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index f76c2a5..803506f 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -337,6 +337,10 @@ const IconGrid = new Lang.Class({
         childBox.y1 = Math.floor(y + childYSpacing);
         childBox.x2 = childBox.x1 + width;
         childBox.y2 = childBox.y1 + height;
+        if(child._translateY) {
+            childBox.y1 += child._translateY;
+            childBox.y2 += child._translateY;
+        }
         return childBox;
     },
 
@@ -552,6 +556,26 @@ const PaginatedIconGrid = new Lang.Class({
         this._childrenPerPage = nColumns * this._rowsPerPage;
     },
 
+    rowsPerPage: function() {
+        return this._rowsPerPage;
+    },
+
+    pageRows: function(pageNumber) {
+        let rows = [];
+        let currentItem = this._getVisibleChildren()[pageNumber * this._childrenPerPage];
+        let children = this._grid.get_children();
+        let index = pageNumber * this._childrenPerPage;
+        for (let rowIndex = 0; rowIndex < this._rowsPerPage && index < children.length; rowIndex++) {
+            rows[rowIndex] = [];
+            while (index < children.length && children[index].y == currentItem.y) {
+                rows[rowIndex].push(children[index]);
+                index++;
+            }
+            currentItem = children[index];
+        }
+        return rows;
+    },
+
     _availableHeightPerPageForItems: function() {
         return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding);
     },


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