[gnome-shell/wip/paging-release: 13/38] New collection implementation, contained inside parent view



commit cff8042800dc41f19b39f426fe304268a0a1a8fe
Author: Carlos Soriano <carlos soriano89 gmail com>
Date:   Tue Aug 13 13:20:12 2013 +0200

    New collection implementation, contained inside parent view
    
    testing
    
    Fixed folder view not expading when we change screen resolution from a
    small resolution to a bigger resolution
    
    Fixed special case when we are at last page and only one row is shown
    and we have a folder. We have to not animate here and directly show the
    collection view.
    
    Fixed bad collection view width on small resolutions due to the padding
    and close button of the popup
    
    Fix bad boxpointer width on small resolutiosn due to the boxpointer paddings

 data/theme/gnome-shell.css |    2 +-
 js/ui/appDisplay.js        |  251 ++++++++++++++++++++++++++++---------------
 js/ui/iconGrid.js          |  175 ++++++++++++++++++-------------
 3 files changed, 268 insertions(+), 160 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 7b65207..1edb68b 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -905,7 +905,7 @@ StScrollBar StButton#vhandle:active {
 }
 
 .pages-indicator {
-       spacing: 30px;
+       spacing: 50px;
 }
 
 .app-folder-icon {
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 5128b44..7e3a2de 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -30,6 +30,8 @@ const Util = imports.misc.util;
 const MAX_APPLICATION_WORK_MILLIS = 75;
 const MENU_POPUP_TIMEOUT = 600;
 const MAX_COLUMNS = 6;
+const MIN_COLUMNS = 4;
+const MIN_ROWS = 4;
 
 const INACTIVE_GRID_OPACITY = 77;
 const INACTIVE_GRID_OPACITY_ANIMATION_TIME = 0.15;
@@ -131,6 +133,7 @@ const AppPages = new Lang.Class({
         this._parent = parent;
         this._folderIcons = [];
         this.doingTransitions = false;
+        this._popupExpansionNeeded = true;
     },
 
     _getItemId: function(item) {
@@ -199,6 +202,11 @@ const AppPages = new Lang.Class({
     addFolderPopup: function(popup) {
         this._parent.addFolderPopup(popup);
     },
+    
+    removeFolderPopUp: function(popup) {
+        this._parent.removeFolderPopUp(popup);
+    },
+    
     /**
      * Pan view with items to make space for the folder view.
      * @param folderNVisibleRowsAtOnce this parameter tell how many rows the folder view has, but,
@@ -206,7 +214,6 @@ const AppPages = new Lang.Class({
      * enough space to show the folder view.
      */
     makeSpaceForPopUp: function(iconActor, side, folderNVisibleRowsAtOnce) {
-        global.log("#### makeSpaceForPopUp ####");
         let rowsUp = [];
         let rowsDown = [];
         let mainIconYPosition = iconActor.actor.y;
@@ -215,7 +222,6 @@ const AppPages = new Lang.Class({
         let isMainIconRow = false;
         let rows = this._grid.pageRows(currentPage);
         this._translatedRows = rows;
-        //global.log(" ROWS " + rows);
         for(let rowIndex in rows) {
             isMainIconRow = mainIconYPosition == rows[rowIndex][0].y;
             if(isMainIconRow)
@@ -254,12 +260,26 @@ const AppPages = new Lang.Class({
                 panViewUpNRows = folderNVisibleRowsAtOnce - rowsDown.length - emptyRows;
             }
         }
-        this._panViewForFolderView(rowsUp, rowsDown, panViewUpNRows, panViewDownNRows, iconActor);
+        // Especial case, last page and only one row, no rows down neither rows up, we call directly the
+        // popup
         this.updateIconOpacities(true);
-        global.log("#### END makeSpaceForPopUp ####");
+        if(panViewDownNRows > 0 && rowsDown.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]) {
@@ -275,7 +295,6 @@ const AppPages = new Lang.Class({
                 }
             }
         }
-        this.updateIconOpacities(false);
     },
     
     _panViewForFolderView: function(rowsUp, rowsDown, panViewUpNRows, panViewDownNRows, iconActor) {
@@ -286,18 +305,14 @@ const AppPages = new Lang.Class({
             for(let rowId in rowsUp) {
                 for(let childrenId in rowsUp[rowId]) {
                     rowsUp[rowId][childrenId].translate_y = 0;
-                    //global.log("children up y " + rowsUp[rowId][childrenId].translate_y);
                     let tweenerParams = { translate_y: - 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);
+                        tweenerParams['onComplete'] = Lang.bind(iconActor, 
iconActor.onCompleteMakeSpaceForPopUp);
                     }
                     Tweener.addTween(rowsUp[rowId][childrenId], tweenerParams);
-                    //rowsUp[rowId][childrenId].translate_y = - height;
-                    //rowsUp[rowId][childrenId].queue_relayout();
-                    //global.log("after children up y " + rowsUp[rowId][childrenId].translate_y);
                 }
             }
         }
@@ -306,7 +321,6 @@ const AppPages = new Lang.Class({
             let height = rowHeight * panViewDownNRows;
             for(let rowId in rowsDown) {
                 for(let childrenId in rowsDown[rowId]) {
-                    //global.log("children down y " + rowsDown[rowId][childrenId].translate_y);
                     rowsDown[rowId][childrenId].translate_y = 0;
                     let tweenerParams = { translate_y: height,
                                           time: POPUP_FOLDER_VIEW_ANIMATION,
@@ -315,9 +329,6 @@ const AppPages = new Lang.Class({
                         tweenerParams['onComplete'] = Lang.bind(iconActor, 
iconActor.onCompleteMakeSpaceForPopUp);
                     }
                     Tweener.addTween(rowsDown[rowId][childrenId], tweenerParams);
-                    //rowsDown[rowId][childrenId].translate_y = height;
-                    //rowsDown[rowId][childrenId].queue_relayout();
-                    //global.log("after children down y " + rowsDown[rowId][childrenId].translate_y);
                 }
             }
         }
@@ -417,10 +428,12 @@ const PaginationScrollView = new Lang.Class({
     },
 
     vfunc_allocate: function(box, flags) {
+        
         box = this.get_parent().allocation;
         this.set_allocation(box, flags);        
         let availWidth = box.x2 - box.x1;
         let availHeight = box.y2 - box.y1;
+        //FIXME
         let childBox = new Clutter.ActorBox();
         childBox.x1 = 0;
         childBox.y1 = 0;
@@ -527,13 +540,18 @@ const PaginationScrollView = new Lang.Class({
     
     addFolderPopup: function(popup) {
         this._stack.add_actor(popup.actor);
-        popup.connect('open-state-changed', Lang.bind(this,
+        popup.openStateId = popup.connect('open-state-changed', Lang.bind(this,
                 function(popup, isOpen) {
                     this._eventBlocker.reactive = isOpen;
                     this._currentPopup = isOpen ? popup : null;
                 }));
     },
     
+    removeFolderPopUp: function(popup) {
+        popup.disconnect(popup.openStateId);
+        this._stack.remove_child(popup.actor);     
+    },
+    
     _onPan: function(action) {
         this._clickAction.release();
         if(this._pages.displayingPopup)
@@ -568,6 +586,7 @@ const PaginationScrollView = new Lang.Class({
         let availWidth = box.x2 - box.x1;
         let availHeight = box.y2 - box.y1;
         this._pages.onUpdatedDisplaySize(availWidth, availHeight);
+        //this.invalidatePagination = true;
     }
     
 });
@@ -846,9 +865,9 @@ const ControlsBoxLayout = Lang.Class({
 
 const AppDisplayActor = new Lang.Class({
     Name: 'AppDisplayActor',
-    Extends: Clutter.BoxLayout,
+    Extends: Clutter.BinLayout,
     
-    vfunc_allocate: function (actor, box, flags) {
+    vfunc_allocate: function (actor, box, flags) {        
         let availWidth = box.x2 - box.x1;
         let availHeight = box.y2 - box.y1;
         // Prepare children of all views for the upcomming allocation, calculate all
@@ -907,12 +926,13 @@ const AppDisplay = new Lang.Class({
 
         this.actor = new St.Widget({ style_class: 'app-display',
                                         x_expand: true, y_expand: true });
-        this._actorLayout = new AppDisplayActor({vertical: true});
-        this.actor.set_layout_manager(this._actorLayout);
-        this._actorLayout.connect('allocated-size-changed', Lang.bind(this, this._onUpdatedDisplaySize));
+        this._viewStackLayout = new AppDisplayActor();
+        this.actor.set_layout_manager(new Clutter.BoxLayout({vertical: true}));
+        this._viewStackLayout.connect('allocated-size-changed', Lang.bind(this, this._onUpdatedDisplaySize));
 
-        this._viewStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
+        this._viewStack = new St.Widget({ 
                                           x_expand: true, y_expand: true });
+        this._viewStack.set_layout_manager(this._viewStackLayout);
         //FIXME
         this.actor.add_actor(this._viewStack, { expand: true });
         let layout = new ControlsBoxLayout({ homogeneous: true });
@@ -1095,20 +1115,24 @@ const AppSearchProvider = new Lang.Class({
 const FolderView = new Lang.Class({
     Name: 'FolderView',
 
-    _init: function(parentView) {
+    _init: function() {
         this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
             columnLimit: MAX_COLUMNS });
-        this._parentView = parentView;
-
+        // If it not expand, the parent doesn't take into account its preferred_width when allocating
+        // the second time it allocates
+        this._grid.actor.x_expand = true;
+        
         this.actor = new St.ScrollView({overlay_scrollbars: true});
         this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
-        this._box = new St.BoxLayout({vertical:true, reactive: true});
+        this._box = new St.BoxLayout({vertical: true, reactive: true});
         this._widget = new St.Widget({layout_manager: new Clutter.BinLayout()});
         this._widget.add_child(this._grid.actor);
         this._box.add_actor(this._widget);
         this.actor.add_actor(this._box);
         this._items = {};
         this._allItems = [];
+        
+        this._boxPointerOffsets = {};
     },
 
     _getItemId: function(item) {
@@ -1124,6 +1148,9 @@ const FolderView = new Lang.Class({
     },
 
     addApp: function(app) {
+        //FIXME
+        if(this._allItems.length > 1)
+            return;
         this._addItem(app);
     },
 
@@ -1179,12 +1206,30 @@ const FolderView = new Lang.Class({
         this._appDisplayWidth = width;
         this._appDisplayHeight = height;
         // Update grid dinamyc spacing based on display width
-        let itemWidth = this._grid._hItemSize * MAX_COLUMNS;
-        let emptyArea = width - itemWidth;
-        let spacing;
-        spacing = Math.max(this._grid._spacing, emptyArea / ( 2 *  MAX_COLUMNS));
-        spacing = Math.round(spacing);
+        let spacing = this._grid.maxSpacingForWidthHeight(width, height, MIN_COLUMNS, MIN_ROWS, true);
         this._grid.setSpacing(spacing);
+        if(!Object.keys(this._boxPointerOffsets).length)
+            return;
+        //WE put the normal padding as spacing as we have in the main grid to do well the calculations for 
used rows, used columns etc, since
+        // it is the behaviour we want to emulate. After that we will put the correct padding 
fromcalculations of the boxpointer offsets, to ensure
+        //the boxpointer will be contained inside the view
+        this._parentSpacing = spacing;
+
+        this._grid.top_padding = spacing;
+        this._grid.bottom_padding = spacing;
+        this._grid.left_padding = spacing;
+        this._grid.right_padding = spacing;
+        let boxPointerTotalOffset;
+        if(this.nRowsDisplayedAtOnce() == this.maxRowsDisplayedAtOnce())
+            boxPointerTotalOffset = this._boxPointerOffsets['arrowHeight'] + 
this._boxPointerOffsets['padding'] * 2 + this._boxPointerOffsets['closeButtonOverlap'];
+        else
+            boxPointerTotalOffset = this._boxPointerOffsets['arrowHeight'] + 
this._boxPointerOffsets['padding'] * 2;  
+        let offsetForEachSide = Math.ceil(boxPointerTotalOffset / 2);
+        this._offsetForEachSide = offsetForEachSide;
+        this._grid.top_padding = spacing - offsetForEachSide;
+        this._grid.bottom_padding = spacing - offsetForEachSide;
+        this._grid.left_padding = spacing - offsetForEachSide;
+        this._grid.right_padding = spacing - offsetForEachSide;
     },
     
     _containerBox: function() {
@@ -1199,15 +1244,19 @@ const FolderView = new Lang.Class({
     usedWidth: function() {
         let box = this._containerBox();
         let availWidthPerPage = box.x2 - box.x1;
+        // Since we want to do the calculation of the real width of the grid
+        // taking into account the parent behaviour, we have to substract from the
+        // avail width the padding we subsctratc before to the folder view
+        // in its surrounding spacings
+        availWidthPerPage -= 2 * this._offsetForEachSide;
         let maxUsedWidth = this._grid.usedWidth(availWidthPerPage);
-        global.log("maxUsedWidth " + maxUsedWidth);
+        global.log("CALCULATED USED WIDTH " + maxUsedWidth);
         return maxUsedWidth;
     },
     
     usedHeight: function() {
         // Then calculate the real maxUsedHeight
-        global.log("this.nRowsDisplayedAtOnce() " + this.nRowsDisplayedAtOnce());
-        return this._grid.usedHeightForNRows(this.nRowsDisplayedAtOnce());
+        return this._grid.usedHeightForNRows(this.nRowsDisplayedAtOnce()) + this._grid.top_padding + 
this._grid.bottom_padding;
     },   
     
     nRowsDisplayedAtOnce: function() {
@@ -1224,10 +1273,22 @@ const FolderView = new Lang.Class({
         let box = this._containerBox();
         let availHeightPerPage = box.y2 - box.y1;
         let availWidthPerPage = box.x2 - box.x1;
+        //FIXME
         let maxRowsPerPage = this._grid.rowsForHeight(availHeightPerPage);
         //Then, we can only show that rows least one.
         maxRowsPerPage -= 1;
         return maxRowsPerPage;
+    },
+    
+    updateBoxPointerOffsets: function(arrowHeight, padding, closeButtonOverlap) {
+        // We have to ensure the close button doesn't go outside the allocation, so
+        // if we are using all the possible rows, means the boxpointer goes until
+        // the boundary of allocation, and then we have to take into account the close button
+        // overlap and least it from padding
+        this._boxPointerOffsets['arrowHeight'] = arrowHeight;
+        this._boxPointerOffsets['padding'] = padding;
+        this._boxPointerOffsets['closeButtonOverlap'] = closeButtonOverlap;
+        
     }
 });
 
@@ -1245,7 +1306,13 @@ const FolderIcon = new Lang.Class({
                                      y_fill: true });
         this.actor._delegate = this;
         this._parentView = parentView;
-
+        // when changing screen resolution or during clutter false allocations
+        // we have to tell folder view that the calculated values of boxpointer arrow side, position, etc 
+        // are not correct (since the allocated size of pagination changed)
+        // For that problem we calculate everything again and apply it maintaining current popup.
+        this.invalidatePopUp = false;
+        this._boxPointerOffsets = {};
+        
         let label = this._dir.get_name();
         this.icon = new IconGrid.BaseIcon(label,
                                           { createIcon: Lang.bind(this, this._createIcon) });
@@ -1278,76 +1345,71 @@ const FolderIcon = new Lang.Class({
     _updatePopupPosition: function() {
         if(this._popup) {
             // Position the popup above or below the source icon
-            if (this._side == St.Side.BOTTOM) {
-                global.log("Bottom " + this.actor.y);
+            if (this._boxPointerArrowside == St.Side.BOTTOM) {
                 let closeButtonOffset = -this._popup.closeButton.translation_y;
-                let y = this.actor.y - this._popup.actor.fixed_height;
-                global.log("Bottom " + this._popup.actor.fixed_height);
+                // FLORIAN REVIEW
+                // We have to use this function, since this._popup.actor.height not always return a good 
value (32 px??)
+                // and then all this calculation of position fails. To solve this in this function we 
calculate the used height with the grid
+                // since we knoe all of the properties of grid. Then we add the padding, arrowheigth etc of 
boxpointer, and we have the
+                // used height of the popup
+                let y = this.actor.y - this._popUpHeight();
                 let yWithButton = y - closeButtonOffset;
                 this._popup.parentOffset = yWithButton < 0 ? -yWithButton : 0;
                 this._popup.actor.y = Math.max(y, closeButtonOffset);
+                this._popup.actor.y = y
             } else {
                 this._popup.actor.y = this.actor.y + this.actor.height;
             }
         }
     },
     
-    _popUpWidth: function() {
+    _popUpGridWidth: function() {
         return this.view.usedWidth();
     },
     
-    _popUpHeight: function() {
-        /*
-         * To maintain the grid of the collection aligned to the main grid, we have to
-         * make the same spacing to each element of the collection as the main grid has, except
-         * for the last row which has to take less space, since the grid of collection is inside a view with 
padding (the popup)
-         * and, the arrow of the popup is rising some pixels the collection, we have to calculate how much 
real spacing
-         * we have to let under/above the last/first arrow to make let the collection grid aligned with the 
main grid
-         */
-        let arrowHeight = this._popup._boxPointer.actor.get_theme_node().get_length('-arrow-rise');
-        let popupPadding = this._popup._boxPointer.bin.get_theme_node().get_length('padding');
-        //It will be negative value, so we have to rest it, instead of plust it.
-        let closeButtonOverlap = 
this._popup.closeButton.get_theme_node().get_length('-shell-close-overlap-y');
-        let closeButtonHeight = this._popup.closeButton.height;
-        global.log("BUTTON OFFSET " + closeButtonOverlap);
+    _popUpGridHeight: function() {
         let usedHeight = this.view.usedHeight();
-        // If we want it corrected aligned with the main grid the calculation will be: usedHeight - 
popupPadding - arrowHeight
-        // but, if we do that and the popup needs all the height, the popup will remain outside the 
allocation and then clipped. so:
-        if(this.view.nRowsDisplayedAtOnce() == this.view.maxRowsDisplayedAtOnce())
-            usedHeight = usedHeight - popupPadding * 2  - arrowHeight + closeButtonOverlap;
-        else
-            usedHeight =  usedHeight - popupPadding - arrowHeight;
-        return usedHeight;
-        
+        return usedHeight;   
+    },
+    
+    _popUpHeight: function() {
+        let usedHeight = this.view.usedHeight() + this._boxPointerOffsets['arrowHeight'] + 
this._boxPointerOffsets['padding'] * 2;
+        return usedHeight;   
     },
 
     makeSpaceForPopUp: function() {
-        this._parentView.makeSpaceForPopUp(this, this._side, this.view.nRowsDisplayedAtOnce());
+        this._parentView.makeSpaceForPopUp(this, this._boxPointerArrowside, 
this.view.nRowsDisplayedAtOnce());
     },
     
     returnSpaceToOriginalPosition: function() {
-        global.log("Original position");
         this._parentView.returnSpaceToOriginalPosition();
     },
     
     onCompleteMakeSpaceForPopUp: function() {
-        //Mainloop.timeout_add(0.1, Lang.bind(this, function() {
         this._popup.popup();
     },
     
+    _calculateBoxPointerArrowSide: function() {
+        let absoluteActorYPosition = this.actor.get_transformed_position()[1];
+        let spaceTop = absoluteActorYPosition;
+        let spaceBottom = this.actor.get_stage().height - (absoluteActorYPosition + this.actor.height);
+        return spaceTop > spaceBottom ? St.Side.BOTTOM : St.Side.TOP;
+    },
+    
     _ensurePopup: function() {
-        if(this._popup){
+        if(this._popup && !this.invalidatePopUp){
             this.makeSpaceForPopUp();
             return;
         } else {
-            let absoluteActorYPosition = this.actor.get_transformed_position()[1];
-            let spaceTop = absoluteActorYPosition;
-            let spaceBottom = this.actor.get_stage().height - (absoluteActorYPosition + this.actor.height);
-            global.log("absoluteActorYPosition " + absoluteActorYPosition);
-            this._side = spaceTop > spaceBottom ? St.Side.BOTTOM : St.Side.TOP;
-            global.log("this._side " + this._side);
-            this._popup = new AppFolderPopup(this, this._side);
-            this._parentView.addFolderPopup(this._popup);
+            
+            this._boxPointerArrowside = this._calculateBoxPointerArrowSide();
+            let firstCreationPopup = this._popup ? false : true;
+            if(!this._popup) {
+                this._popup = new AppFolderPopup(this, this._boxPointerArrowside);
+                this._parentView.addFolderPopup(this._popup);
+            } else
+                this._popup.updateBoxPointer(this._boxPointerArrowside);
+            // FLORIAN REVIEW
             /**
              * Why we need that: AppDiplay update width for the spacing for all
              * views Allview and frequent view and folder views calcualte spacing
@@ -1376,7 +1438,14 @@ const FolderIcon = new Lang.Class({
              * Solution: ensure style of the grid just after we add it to the parent
              * and before the calculation of the position.
              */
+
             this.view._grid.actor.ensure_style();
+            this._boxPointerOffsets['arrowHeight'] = 
this._popup._boxPointer.actor.get_theme_node().get_length('-arrow-rise');
+            this._boxPointerOffsets['padding'] = 
this._popup._boxPointer.bin.get_theme_node().get_length('padding');
+            //It will be negative value, so we have to substract it, instead of plust it.
+            this._boxPointerOffsets['closeButtonOverlap'] = - 
this._popup.closeButton.get_theme_node().get_length('-shell-close-overlap-y');
+
+            this.view.updateBoxPointerOffsets(this._boxPointerOffsets['arrowHeight'], 
this._boxPointerOffsets['padding'], this._boxPointerOffsets['closeButtonOverlap']);
             this.view.onUpdatedDisplaySize(this._displayWidth, this._displayHeight);
 
             /*
@@ -1385,7 +1454,7 @@ const FolderIcon = new Lang.Class({
              * icons than necesary to full the row. In that manner the popup will be
              * more eye pleasant, fulling the parent view
              */
-            this.view.actor.set_width(this._popUpWidth());
+            this.view.actor.set_width(this._popUpGridWidth());
 
             /*
              * A folder view can only be, at a maximum, one row less than the parent
@@ -1393,20 +1462,20 @@ const FolderIcon = new Lang.Class({
              * then calculate the maxUsedHeigth and the current Used height, if it
              * is more, strech to the maxUsedHeight
              */
-            let usedHeight = this._popUpHeight();
-            global.log("Used height " + usedHeight);
-            this.view.actor.set_height(this._popUpHeight());
-            this._popup.actor.fixed_height = this._popup.actor.height;
-
+            this.view.actor.set_height(this._popUpGridHeight());
             
+            this._updatePopupPosition();
             this.makeSpaceForPopUp();
-            this._popup.connect('open-state-changed', Lang.bind(this,
-                    function(popup, isOpen) {
-                if (!isOpen) {
-                    this.actor.checked = false;
-                    this.returnSpaceToOriginalPosition();
-                }
-            }));
+            if(firstCreationPopup) {
+                this._popup.connect('open-state-changed', Lang.bind(this,
+                        function(popup, isOpen) {
+                    if (!isOpen) {
+                        this.actor.checked = false;
+                        this.returnSpaceToOriginalPosition();
+                    }
+                }));
+            }
+            this.invalidatePopUp = false;            
         }
     },
 
@@ -1414,6 +1483,7 @@ const FolderIcon = new Lang.Class({
         this._displayWidth = width;
         this._displayHeight = height;
         this.view.onUpdatedDisplaySize(width, height);
+        this.invalidatePopUp = true;
     },
 
 });
@@ -1493,7 +1563,7 @@ const AppFolderPopup = new Lang.Class({
         this.actor.show();
         this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
         this._boxPointer.setArrowActor(this._source.actor);
-        this._boxPointer.show(BoxPointer.PopupAnimation.FADE);
+        this._boxPointer.show(BoxPointer.PopupAnimation.FADE | BoxPointer.PopupAnimation.SLIDE);
 
         this._isOpen = true;
         this.emit('open-state-changed', true);
@@ -1503,9 +1573,14 @@ const AppFolderPopup = new Lang.Class({
         if (!this._isOpen)
             return;
 
-        this._boxPointer.hide(BoxPointer.PopupAnimation.FADE);
+        this._boxPointer.hide(BoxPointer.PopupAnimation.FADE | BoxPointer.PopupAnimation.SLIDE);
         this._isOpen = false;
         this.emit('open-state-changed', false);
+    },
+    
+    updateBoxPointer: function (side) {
+        this._arrowSide = side;
+        this._boxPointer._arrowSide = side;
     }
 });
 Signals.addSignalMethods(AppFolderPopup.prototype);
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index 3060019..1ff04a3 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -187,9 +187,15 @@ const IconGrid = new Lang.Class({
         this._fillParent = params.fillParent;
         this._usePagination = params.usePagination;
         
+        this.top_padding = 0;
+        this.bottom_padding = 0;
+        this.right_padding = 0;
+        this.left_padding = 0;
+        
         if(this._usePagination) {
             this._nPages = 0;
-            //Set this variable properly before allocate function is called
+            // Set this variable properly pointing to the scrollView containing the grid
+            // before allocate function is called
             this._viewForPageSize = null;
             this._firstPagesItems = [];
         }
@@ -206,7 +212,6 @@ const IconGrid = new Lang.Class({
         this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
         this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
         this._grid.connect('allocate', Lang.bind(this, this._allocate));
-        
     },
 
     _getPreferredWidth: function (grid, forHeight, alloc) {
@@ -218,14 +223,13 @@ const IconGrid = new Lang.Class({
         let nChildren = this._grid.get_n_children();
         let nColumns = this._colLimit ? Math.min(this._colLimit,
                                                  nChildren)
-                                      : nChildren;
-        let spacing = this._fixedSpacing ? this._fixedSpacing : this._spacing;
-        let totalSpacing = Math.max(0, nColumns - 1) * spacing;
+                                                         : nChildren;
+        let totalSpacing = Math.max(0, nColumns - 1) * this.getSpacing();
         // Kind of a lie, but not really an issue right now.  If
         // we wanted to support some sort of hidden/overflow that would
         // need higher level design
-        alloc.min_size = this._hItemSize;
-        alloc.natural_size = nColumns * this._hItemSize + totalSpacing;
+        alloc.min_size = this._hItemSize + this.left_padding + this.right_padding;
+        alloc.natural_size = nColumns * this._hItemSize + totalSpacing + this.left_padding + 
this.right_padding;
     },
 
     _getVisibleChildren: function() {
@@ -249,8 +253,7 @@ const IconGrid = new Lang.Class({
         } else {
             [nColumns, ] = this._computeLayout(forWidth);
         }
-        
-        let spacing = this._fixedSpacing ? this._fixedSpacing : this._spacing;
+        let spacing = this.getSpacing();
         
         let nRows;
         if (nColumns > 0)
@@ -260,11 +263,10 @@ const IconGrid = new Lang.Class({
         if (this._rowLimit)
             nRows = Math.min(nRows, this._rowLimit);
         let totalSpacing = Math.max(0, nRows - 1) * spacing;
-        let height = nRows * this._vItemSize + totalSpacing;
-        
+        let height = nRows * this._vItemSize + totalSpacing + this.top_padding + this.bottom_padding;     
         if(this._usePagination && this._nPages) {
-            alloc.min_size = this._rowsPerPage * this._spacePerRow * this._nPages + 
this._spaceBetweenPagesTotal;
-            alloc.natural_size = this._rowsPerPage * this._spacePerRow * this._nPages + 
this._spaceBetweenPagesTotal;
+            alloc.min_size =  this.usedHeightPerPage() * this._nPages + this._spaceBetweenPagesTotal;
+            alloc.natural_size = this.usedHeightPerPage() * this._nPages + this._spaceBetweenPagesTotal;
             return;
         }
         alloc.min_size = height;
@@ -282,14 +284,13 @@ const IconGrid = new Lang.Class({
         let children = this._getVisibleChildren();
         let availWidth = box.x2 - box.x1;
         let availHeight = box.y2 - box.y1;
-        let spacing = this._fixedSpacing ? this._fixedSpacing : this._spacing;
-        let [nColumns, usedWidth] = this._computeLayout(availWidth);        
+        let spacing = this.getSpacing();
+        let [nColumns, usedWidth] = this._computeLayout(availWidth);
         if(this._usePagination) {
-            // ScrollView height
+            // Calculate icongrid box inside the scrollView
             let parentBox = this._viewForPageSize.allocation;
             let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
             let customBox = this._grid.get_theme_node().get_content_box(gridBox);
-            let availWidth = customBox.x2 - customBox.x1;
             let availHeightPerPage = customBox.y2 - customBox.y1;
             let nRows;
             if (nColumns > 0)
@@ -302,10 +303,6 @@ const IconGrid = new Lang.Class({
             this._calculatePaginationValues(availHeightPerPage, nColumns, nRows);
             if(oldNPages != this._nPages) {
                 this.emit('n-pages-changed', this._nPages);
-                /*Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
-                    this._grid.queue_relayout();
-                    return false;
-                }));*/
             }
         }
         let leftPadding;
@@ -319,13 +316,12 @@ const IconGrid = new Lang.Class({
             case St.Align.END:
                 leftPadding = availWidth - usedWidth;
         }
-
-        let x = box.x1 + leftPadding;
-        let y = box.y1;
+        
+        let x = box.x1 + leftPadding + this.left_padding;
+        let y = box.y1 + this.top_padding;
         let columnIndex = 0;
         let rowIndex = 0;
-        
-        if(children.length > 0) {
+        if(this._usePagination && children.length > 0) {
             this._firstPagesItems = [children[0]];
         }
         for (let i = 0; i < children.length; i++) {
@@ -336,7 +332,7 @@ const IconGrid = new Lang.Class({
             }
             if(!this._usePagination) {
                 if (this._rowLimit && rowIndex >= this._rowLimit ||
-                        this._fillParent && childBox.y2 >= availHeight) {
+                        this._fillParent && childBox.y2 > availHeight - this.bottom_padding) {
                     this._grid.set_skip_paint(children[i], true);
                 } else {
                     children[i].allocate(childBox, flags);
@@ -357,29 +353,30 @@ const IconGrid = new Lang.Class({
                 y += this._vItemSize + spacing;
                 if(this._usePagination) {
                     if((i + 1) % this._childrenPerPage == 0) {
-                        y+= this._spaceBetweenPages;
+                        y+= this._spaceBetweenPages + this.top_padding;
                         if(i < children.length) {
                             this._firstPagesItems.push(children[i+1]);
                         }
                     }
                 }
-                x = box.x1 + leftPadding;
+                x = box.x1 + leftPadding + this.left_padding;
             } else {
                 x += this._hItemSize + spacing;
             }
-        }
-        
+        }       
     },
     
     _calculatePaginationValues: function (availHeightPerPage, nColumns, nRows) {
-        let spacing = this._fixedSpacing ? this._fixedSpacing : this._spacing;
+        let spacing = this.getSpacing();
         this._spacePerRow = this._vItemSize + spacing;
+        // We want to contain the grid inside the parent box with padding
+        availHeightPerPage -= this.top_padding + this.bottom_padding;
         this._rowsPerPage = Math.floor(availHeightPerPage / this._spacePerRow);
         // Check if deleting spacing from bottom there's enough space for another row
         let spaceWithOneMoreRow = (this._rowsPerPage + 1) * this._spacePerRow - spacing;
         this._rowsPerPage = spaceWithOneMoreRow <= availHeightPerPage? this._rowsPerPage + 1 : 
this._rowsPerPage;
         this._nPages = Math.ceil(nRows / this._rowsPerPage);
-        this._spaceBetweenPages = availHeightPerPage - (this._rowsPerPage * (this._vItemSize + spacing));
+        this._spaceBetweenPages = availHeightPerPage - (this._rowsPerPage * (this._vItemSize + spacing) - 
spacing);
         this._spaceBetweenPagesTotal = this._spaceBetweenPages * (this._nPages);
         this._childrenPerPage = nColumns * this._rowsPerPage;
     },
@@ -396,6 +393,7 @@ const IconGrid = new Lang.Class({
     
         let childBox = new Clutter.ActorBox();
         if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
+          //FIXME not defined box????????
             let _x = box.x2 - (x + width);
             childBox.x1 = Math.floor(_x - childXSpacing);
         } else {
@@ -406,7 +404,30 @@ const IconGrid = new Lang.Class({
         childBox.y2 = childBox.y1 + height;
         return childBox;
     },
-    
+
+    _computeLayout: function (forWidth) {
+        let nColumns = 0;
+        let usedWidth = this.left_padding + this.right_padding;
+        let spacing = this.getSpacing();
+        while ((this._colLimit == null || nColumns < this._colLimit) &&
+               (usedWidth + this._hItemSize <= forWidth)) {
+            usedWidth += this._hItemSize + spacing;
+            nColumns += 1;
+        }
+
+        if (nColumns > 0)
+            usedWidth -= spacing;
+        return [nColumns, usedWidth];
+    },
+
+    _onStyleChanged: function() {
+        let themeNode = this.actor.get_theme_node();
+        this._spacing = themeNode.get_length('spacing');
+        this._hItemSize = themeNode.get_length('-shell-grid-horizontal-item-size') || ICON_SIZE;
+        this._vItemSize = themeNode.get_length('-shell-grid-vertical-item-size') || ICON_SIZE;
+        this._grid.queue_relayout();
+    },
+
     childrenInRow: function(rowWidth) {
         return this._computeLayout(rowWidth)[0];
     },
@@ -414,7 +435,7 @@ const IconGrid = new Lang.Class({
     getRowLimit: function() {
         return this._rowLimit;
     },
-    
+
     nUsedRows: function(forWidth) {
         let children = this._getVisibleChildren();
         let nColumns;
@@ -423,7 +444,6 @@ const IconGrid = new Lang.Class({
         } else {
             [nColumns, ] = this._computeLayout(forWidth);
         }
-        
         let nRows;
         if (nColumns > 0)
             nRows = Math.ceil(children.length / nColumns);
@@ -433,8 +453,9 @@ const IconGrid = new Lang.Class({
             nRows = Math.min(nRows, this._rowLimit);
         return nRows;
     },
-    
+
     rowsForHeight: function(forHeight) {
+        forHeight -= this.top_padding + this.bottom_padding;
         let spacePerRow = this._vItemSize + this.getSpacing();
         let rowsPerPage = Math.floor(forHeight / spacePerRow);
         // Check if deleting spacing from bottom there's enough space for another row
@@ -442,42 +463,24 @@ const IconGrid = new Lang.Class({
         rowsPerPage = spaceWithOneMoreRow <= forHeight? rowsPerPage + 1 : rowsPerPage;
         return rowsPerPage;
     },
-    
+
+    /**
+     * Don't take into account paddings
+     */
     usedHeightForNRows: function(nRows) {
         let spacePerRow = this.rowHeight();
-        return spacePerRow * nRows;
+        return spacePerRow * nRows - this.getSpacing();
     },
-    
+
+    usedHeightPerPage: function() {
+        return this._rowsPerPage * this._spacePerRow - this.getSpacing() + this.top_padding + 
this.bottom_padding;
+    },
+
     usedWidth: function(forWidth) {
         let childrenInRow = this.childrenInRow(forWidth);
         let usedWidth = childrenInRow  * (this._hItemSize + this.getSpacing());
         usedWidth -= this.getSpacing();
-        return usedWidth;
-    },
-    
-    _computeLayout: function (forWidth) {
-        let nColumns = 0;
-        let usedWidth = 0;
-        let spacing = this._fixedSpacing ? this._fixedSpacing : this._spacing;
-
-        while ((this._colLimit == null || nColumns < this._colLimit) &&
-               (usedWidth + this._hItemSize <= forWidth)) {
-            usedWidth += this._hItemSize + spacing;
-            nColumns += 1;
-        }
-
-        if (nColumns > 0)
-            usedWidth -= spacing;
-
-        return [nColumns, usedWidth];
-    },
-
-    _onStyleChanged: function() {
-        let themeNode = this.actor.get_theme_node();
-        this._spacing = themeNode.get_length('spacing');
-        this._hItemSize = themeNode.get_length('-shell-grid-horizontal-item-size') || ICON_SIZE;
-        this._vItemSize = themeNode.get_length('-shell-grid-vertical-item-size') || ICON_SIZE;
-        this._grid.queue_relayout();
+        return usedWidth + this.left_padding + this.right_padding;
     },
 
     removeAll: function() {
@@ -510,20 +513,50 @@ const IconGrid = new Lang.Class({
             throw new Error('Invalid page number ' + pageNumber);
         }
         let childBox = this._firstPagesItems[pageNumber].get_allocation_box();
-        return [childBox.x1, childBox.y1];
+        return [childBox.x1 - this.top_padding, childBox.y1 - this.top_padding];
     },
     
     setSpacing: function(spacing) {
             this._fixedSpacing = spacing;
-            /*Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
-                this._grid.queue_relayout();
-                return false;
-            }));*/
     },
     
     getSpacing: function() {
         return this._fixedSpacing ? this._fixedSpacing : this._spacing;
     },
+    /**
+     * This functions is intended to use before iconGrid allocation, to know how much spacing can we have at 
the grid
+     * but also to set manually the top/bottom rigth/left padding accordnly to the spacing calculated here.
+     * To take into account the spacing also for before the first row and for after the last row mark 
usingSurroundingSpacing true
+     * This function doesn't take into account the dynamic padding rigth now, since in fact we want to 
calculate also that.
+     */
+    maxSpacingForWidthHeight: function(availWidth, availHeight, minColumns, minRows, 
usingSurroundingSpacing) {
+        // Maximum spacing will be the icon item size. It doesn't make any sense to have more spacing than 
items.
+        let maxSpacing = Math.floor(Math.min(this._vItemSize, this._hItemSize));
+        let minEmptyVerticalArea = (availHeight - minRows * this._vItemSize);
+        let minEmptyHorizontalArea = (availWidth - minColumns * this._hItemSize);
+        let spacing;
+        if(usingSurroundingSpacing) {
+            // minRows + 1 because we want to put spacing before the first row, so it is like we have one 
more row
+            // to divide the empty space
+            let maxSpacingForRows = Math.floor(minEmptyVerticalArea / (minRows +1));
+            let maxSpacingForColumns = Math.floor(minEmptyHorizontalArea / (minColumns +1));
+            let spacingToEnsureMinimums = Math.min(maxSpacingForRows, maxSpacingForColumns);
+            let spacingNotTooBig = Math.min(spacingToEnsureMinimums, maxSpacing);
+            spacing = Math.max(this._spacing, spacingNotTooBig);
+        } else {
+            if(minRows == 1) {
+                let maxSpacingForRows = Math.floor(minEmptyVerticalArea / minRows);
+                let maxSpacingForColumns = Math.floor(minEmptyHorizontalArea / minColumns);
+            } else {
+                let maxSpacingForRows = Math.floor(minEmptyVerticalArea / (minRows - 1));
+                let maxSpacingForColumns = Math.floor(minEmptyHorizontalArea / (minColumns - 1));
+            }
+            let spacingToEnsureMinimums = Math.min(maxSpacingForRows, maxSpacingForColumns);
+            let spacingNotTooBig = Math.min(spacingToEnsureMinimums, maxSpacing);
+            spacing = Math.max(this._spacing, spacingNotTooBig); 
+        }
+        return spacing;
+    },
     
     pageRows: function(pageNumber) {
         let pagePosition = this.getPagePosition(pageNumber);



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