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



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

    New collection implementation, contained inside parent view
    
    testing

 js/ui/appDisplay.js |  219 +++++++++++++++++++++++++++++++--------------------
 js/ui/iconGrid.js   |  173 ++++++++++++++++++++++++----------------
 2 files changed, 238 insertions(+), 154 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 5128b44..f113c03 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;
@@ -199,6 +201,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 +213,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 +221,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)
@@ -256,7 +261,6 @@ const AppPages = new Lang.Class({
         }
         this._panViewForFolderView(rowsUp, rowsDown, panViewUpNRows, panViewDownNRows, iconActor);
         this.updateIconOpacities(true);
-        global.log("#### END makeSpaceForPopUp ####");
     },
     
     returnSpaceToOriginalPosition: function() {
@@ -286,18 +290,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 +306,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 +314,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 +413,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 +525,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 +571,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 +850,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 +911,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 +1100,21 @@ 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;
 
         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 +1130,9 @@ const FolderView = new Lang.Class({
     },
 
     addApp: function(app) {
+        //FIXME
+        if(this._allItems.length > 1)
+            return;
         this._addItem(app);
     },
 
@@ -1179,12 +1188,27 @@ 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 in side the view
+        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._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() {
@@ -1200,14 +1224,12 @@ const FolderView = new Lang.Class({
         let box = this._containerBox();
         let availWidthPerPage = box.x2 - box.x1;
         let maxUsedWidth = this._grid.usedWidth(availWidthPerPage);
-        global.log("maxUsedWidth " + 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 +1246,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 +1279,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 +1318,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 +1411,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 +1427,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 +1435,21 @@ 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;
-
+            let usedHeight = this._popUpGridHeight();
+            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 +1457,7 @@ const FolderIcon = new Lang.Class({
         this._displayWidth = width;
         this._displayHeight = height;
         this.view.onUpdatedDisplaySize(width, height);
+        this.invalidatePopUp = true;
     },
 
 });
@@ -1493,7 +1537,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 +1547,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..a7c6e27 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,13 +353,13 @@ 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;
             }
@@ -372,14 +368,16 @@ const IconGrid = new Lang.Class({
     },
     
     _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 +394,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 +405,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 +436,7 @@ const IconGrid = new Lang.Class({
     getRowLimit: function() {
         return this._rowLimit;
     },
-    
+
     nUsedRows: function(forWidth) {
         let children = this._getVisibleChildren();
         let nColumns;
@@ -423,7 +445,6 @@ const IconGrid = new Lang.Class({
         } else {
             [nColumns, ] = this._computeLayout(forWidth);
         }
-        
         let nRows;
         if (nColumns > 0)
             nRows = Math.ceil(children.length / nColumns);
@@ -433,8 +454,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 +464,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 +514,51 @@ 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 {
+            //We don't want spacing if there's only one row, it will be touching the boundary.
+            if(minRows == 1) {
+                let maxSpacingForRows = 0;
+                let maxSpacingForColumns = 0;
+            } 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]