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



commit a9644b9f643cce7949ddd4b51087470935dbb079
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 |  165 +++++++++++++++++++++++++++++++++++++++++++++++++-
 js/ui/iconGrid.js   |   24 ++++++++
 2 files changed, 185 insertions(+), 4 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index d22bda4..a9fd91d 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 MAX_APPS_PAGES = 20;
@@ -50,6 +50,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();
@@ -311,6 +313,9 @@ const AllView = new Lang.Class({
         }));
         this._eventBlocker.add_action(this._clickAction);
 
+        this._popupExpansionNeeded = true;
+        this.displayingPopup = false;
+
         this._availWidth = 0;
         this._availHeight = 0;
 
@@ -343,6 +348,10 @@ const AllView = new Lang.Class({
     },
 
     goToPage: function(pageNumber, updateIndicators, animated) {
+        if (this._currentPage != pageNumber && this.displayingPopup && this._currentPopup)
+            this._currentPopup.popdown();
+        else if (this.displayingPopup && this._currentPopup)
+                return;
         this.viewGoToPage(pageNumber, animated);
         if (updateIndicators)
             this.indicatorsGoToPage(pageNumber);
@@ -405,7 +414,132 @@ 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 folderNVisibleRowsAtOnce 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, folderNVisibleRowsAtOnce) {
+        let rowsUp = [];
+        let rowsDown = [];
+        let mainIconYPosition = iconActor.actor.y;
+        let mainIconRowReached = false;
+        let isMainIconRow = false;
+        let rows = this._grid.pageRows(this._currentPage);
+        this._translatedRows = rows;
+        for (let rowIndex in rows) {
+            isMainIconRow = mainIconYPosition == rows[rowIndex][0].y;
+            if (isMainIconRow)
+                mainIconRowReached = true;
+            if (!mainIconRowReached) {
+                rowsUp.push(rows[rowIndex]);
+            } else {
+                if (isMainIconRow) {
+                    if (side == St.Side.BOTTOM)
+                        rowsDown.push(rows[rowIndex]);
+                    else
+                        rowsUp.push(rows[rowIndex]);
+                } else {
+                    rowsDown.push(rows[rowIndex]);
+                }
+            }
+        }
+        //The last page can have space without rows
+        let emptyRows = this._grid.rowsPerPage() - rows.length ;
+        let panViewUpNRows = 0;
+        let panViewDownNRows = 0;
+        if(side == St.Side.BOTTOM) {
+            // There's not need to pan view down
+            if (rowsUp.length >= folderNVisibleRowsAtOnce) {
+                panViewUpNRows = folderNVisibleRowsAtOnce;
+            } else {
+                panViewUpNRows = rowsUp.length;
+                panViewDownNRows = folderNVisibleRowsAtOnce - rowsUp.length;
+            }
+        } else {
+            // There's not need to pan view up
+            if (rowsDown.length + emptyRows >= folderNVisibleRowsAtOnce) {
+                panViewDownNRows = folderNVisibleRowsAtOnce;
+            } else {
+                panViewDownNRows = rowsDown.length + emptyRows;
+                panViewUpNRows = folderNVisibleRowsAtOnce - rowsDown.length - emptyRows;
+            }
+        }
+        this._updateIconOpacities(true);
+        // 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 (panViewDownNRows > 0 && rowsDown.length == 0 && rowsUp.length == 0) {
+            this.displayingPopup = true;
+            this._popupExpansionNeeded = false;
+            iconActor.onCompleteMakeSpaceForPopUp();
+        } else {
+            this._popupExpansionNeeded = true;
+            this._panViewForFolderView(rowsUp, rowsDown, panViewUpNRows, panViewDownNRows, iconActor);
+        }
+    },
+
+    returnSpaceToOriginalPosition: function() {
+        this._updateIconOpacities(false);
+        if (!this._popupExpansionNeeded) {
+            this.displayingPopup = false;
+            return;
+        }
+        if (this._translatedRows) {
+            for (let rowId in this._translatedRows) {
+                for (let childrenId in this._translatedRows[rowId]) {
+                    if (this._translatedRows[rowId][childrenId]._translateY) {
+                        let tweenerParams = { _translateY: 0,
+                                              time: POPUP_FOLDER_VIEW_ANIMATION,
+                                              onUpdate: function() {this.queue_relayout();},
+                                              transition: 'easeInOutQuad',
+                                              onComplete: Lang.bind(this, function(){ this.displayingPopup = 
false; }) };
+                        Tweener.addTween(this._translatedRows[rowId][childrenId], tweenerParams);
+                    }
+                }
+            }
+        }
+    },
+
+    _panViewForFolderView: function(rowsUp, rowsDown, panViewUpNRows, panViewDownNRows, iconActor) {
+        let rowHeight = this._grid.rowHeight();
+        if (panViewUpNRows > 0) {
+            this.displayingPopup = true;
+            let height = rowHeight * panViewUpNRows;
+            for (let rowId in rowsUp) {
+                for (let childrenId in rowsUp[rowId]) {
+                    rowsUp[rowId][childrenId]._translateY = 0;
+                    let tweenerParams = { _translateY: - height,
+                                          time: POPUP_FOLDER_VIEW_ANIMATION,
+                                          onUpdate: function() { this.queue_relayout(); },
+                                          transition: 'easeInOutQuad' };
+                    if ((rowId == rowsUp.length - 1) && (childrenId == rowsUp[rowId].length - 1))
+                            tweenerParams['onComplete'] = Lang.bind(iconActor, 
iconActor.onCompleteMakeSpaceForPopUp);
+                    Tweener.addTween(rowsUp[rowId][childrenId], tweenerParams);
+                }
+            }
+        }
+        if (panViewDownNRows > 0) {
+            this.displayingPopup = true;
+            let height = rowHeight * panViewDownNRows;
+            for (let rowId in rowsDown) {
+                for (let childrenId in rowsDown[rowId]) {
+                    rowsDown[rowId][childrenId]._translateY = 0;
+                    let tweenerParams = { _translateY: height,
+                                          time: POPUP_FOLDER_VIEW_ANIMATION,
+                                          onUpdate: function() { this.queue_relayout(); } };
+                    if ((rowId == rowsDown.length - 1) && (childrenId == rowsDown[rowId].length - 1))
+                        tweenerParams['onComplete'] = Lang.bind(iconActor, 
iconActor.onCompleteMakeSpaceForPopUp);
+                    Tweener.addTween(rowsDown[rowId][childrenId], tweenerParams);
+                }
+            }
+        }
+    },
+
     _onScroll: function(actor, event) {
+         if(this.displayingPopup)
+            return;
         let direction = event.get_scroll_direction();
         if (direction == Clutter.ScrollDirection.UP) {
             if (this._currentPage > 0)
@@ -419,6 +553,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);
@@ -427,6 +563,8 @@ const AllView = new Lang.Class({
     },
 
     _onPanEnd: function(action) {
+         if (this.displayingPopup)
+            return false;
         let diffCurrentPage = this._diffToPage(this._currentPage);
         if (diffCurrentPage > this._pagesView.height * PAGE_SWITCH_TRESHOLD) {
             if (action.get_velocity(0)[2] > 0 && this._currentPage > 0)
@@ -991,7 +1129,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,
@@ -999,6 +1136,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) {
@@ -1010,6 +1150,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);
@@ -1045,16 +1197,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);
@@ -1062,6 +1218,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]