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



commit 20081eeae96dae909a773158bf97248c595c3140
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 |  167 +++++++++++++++++++++++++++++++++++++++++++++++++--
 js/ui/iconGrid.js   |   24 +++++++
 2 files changed, 186 insertions(+), 5 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index d19e46d..5006a20 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;
@@ -44,6 +44,8 @@ const MAX_APPS_PAGES = 20;
 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();
@@ -325,6 +327,8 @@ const AllView = new Lang.Class({
         // we have to tell pagination that the adjustment is not correct (since the allocated size of 
pagination changed)
         // For that problem we return to the first page of pagination.
         this._paginationInvalidated = false;
+        this._popupExpansionNeeded = true;
+        this._displayingPopup = false;
 
         Main.overview.connect('hidden', Lang.bind(this, function() {this.goToPage(0, true);}));
     },
@@ -335,6 +339,10 @@ const AllView = new Lang.Class({
     },
 
     goToPage: function(pageNumber, updateIndicators) {
+        if (this._currentPage != pageNumber && this._displayingPopup && this._currentPopup)
+            this._currentPopup.popdown();
+        else if(this._displayingPopup && this._currentPopup)
+                return;
         this.viewGoToPage(pageNumber);
         if (updateIndicators)
             this.indicatorsGoToPage(pageNumber);
@@ -390,7 +398,132 @@ const AllView = new Lang.Class({
         return Math.abs(currentScrollPosition - this._grid.getPageYPosition(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();
         let nextPage;
         if (direction == Clutter.ScrollDirection.UP) {
@@ -408,6 +541,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);
@@ -417,6 +552,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)
@@ -427,6 +564,7 @@ const AllView = new Lang.Class({
             this.goToPage(this._currentPage, action);
         }
         this._panning = false;
+        return false;
     },
 
     _getItemId: function(item) {
@@ -1016,7 +1154,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,
@@ -1024,6 +1161,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) {
@@ -1045,6 +1185,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 absoluteActorYPosition = this.actor.get_transformed_position()[1];
         let spaceTop = absoluteActorYPosition;
@@ -1130,23 +1282,28 @@ 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.updateBoxPointer(this._boxPointerArrowside);
         }
         this._updatePopUpSize();
         this._updatePopupPosition();
-        this._popupInvalidated = false; 
+        this._popupInvalidated = false;
+        this.makeSpaceForPopUp();
     },
 
     adaptToSize: function(width, height) {
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index 01ea9ba..cbdb9ac 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -338,6 +338,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;
     },
 
@@ -578,6 +582,26 @@ const PaginatedIconGrid = new Lang.Class({
         }
     },
 
+    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._rowsPerPage * this.rowHeight() - this._getSpacing() + this.top_padding + 
this.bottom_padding;
     },


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