[gnome-shell/wip/paging-release: 2/85] Paggination



commit 61f314b3246a8a95593cd805b3c53abbd8db51b1
Author: Carlos Soriano <csoriano src gnome org>
Date:   Fri Jun 14 11:58:50 2013 +0200

    Paggination
    
    First attempt of pagination with scrollActor.
    Work done:
    -Separate applications in pages
    -Scroll changes between pages
    
    First attempt of pagination with St.ScrollView
    work done:
    -Applications divided into pages
    -Scroll working to change between pages
    
    Some clean-up
    
    Make iconGrid with pagination mode

 js/ui/appDisplay.js |  239 ++++++++++++++++++++++++++++++++-------------------
 js/ui/iconGrid.js   |  134 ++++++++++++++++++++++------
 2 files changed, 256 insertions(+), 117 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 41ecddc..6c519eb 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -36,7 +36,7 @@ const FOLDER_SUBICON_FRACTION = .4;
 
 
 // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
-function _loadCategory(dir, view) {
+function _loadCategory(dir, list) {
     let iter = dir.iter();
     let appSystem = Shell.AppSystem.get_default();
     let nextType;
@@ -45,11 +45,11 @@ function _loadCategory(dir, view) {
             let entry = iter.get_entry();
             let app = appSystem.lookup_app_by_tree_entry(entry);
             if (!entry.get_app_info().get_nodisplay())
-                view.addApp(app);
+                list.addApp(app);
         } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
             let itemDir = iter.get_directory();
             if (!itemDir.get_is_nodisplay())
-                _loadCategory(itemDir, view);
+                _loadCategory(itemDir, list);
         }
     }
 };
@@ -60,6 +60,7 @@ const AlphabeticalView = new Lang.Class({
 
     _init: function() {
         this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
+                                             usePagination: true,
                                              columnLimit: MAX_COLUMNS });
 
         // Standard hack for ClutterBinLayout
@@ -156,80 +157,13 @@ const FolderView = new Lang.Class({
     }
 });
 
-const AllViewLayout = new Lang.Class({
-    Name: 'AllViewLayout',
-    Extends: Clutter.BinLayout,
-
-    vfunc_get_preferred_height: function(container, forWidth) {
-        let minBottom = 0;
-        let naturalBottom = 0;
-
-        for (let child = container.get_first_child();
-             child;
-             child = child.get_next_sibling()) {
-            let childY = child.y;
-            let [childMin, childNatural] = child.get_preferred_height(forWidth);
-
-            if (childMin + childY > minBottom)
-                minBottom = childMin + childY;
-
-            if (childNatural + childY > naturalBottom)
-                naturalBottom = childNatural + childY;
-        }
-        return [minBottom, naturalBottom];
-    }
-});
-
-const AllView = new Lang.Class({
-    Name: 'AllView',
+const AppPages = new Lang.Class({
+    Name: 'AppPages',
     Extends: AlphabeticalView,
-
+   
     _init: function() {
         this.parent();
-
-        this._grid.actor.y_align = Clutter.ActorAlign.START;
-        this._grid.actor.y_expand = true;
-
-        let box = new St.BoxLayout({ vertical: true });
-        this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
-        this._stack.add_actor(this._grid.actor);
-        this._eventBlocker = new St.Widget({ x_expand: true, y_expand: true });
-        this._stack.add_actor(this._eventBlocker);
-        box.add(this._stack, { y_align: St.Align.START, expand: true });
-
-        this.actor = new St.ScrollView({ x_fill: true,
-                                         y_fill: false,
-                                         y_align: St.Align.START,
-                                         x_expand: true,
-                                         y_expand: true,
-                                         overlay_scrollbars: true,
-                                         style_class: 'all-apps vfade' });
-        this.actor.add_actor(box);
-        this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
-        let action = new Clutter.PanAction({ interpolate: true });
-        action.connect('pan', Lang.bind(this, this._onPan));
-        this.actor.add_action(action);
-
-        this._clickAction = new Clutter.ClickAction();
-        this._clickAction.connect('clicked', Lang.bind(this, function() {
-            if (!this._currentPopup)
-                return;
-
-            let [x, y] = this._clickAction.get_coords();
-            let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
-            if (!this._currentPopup.actor.contains(actor))
-                this._currentPopup.popdown();
-        }));
-        this._eventBlocker.add_action(this._clickAction);
-    },
-
-    _onPan: function(action) {
-        this._clickAction.release();
-
-        let [dist, dx, dy] = action.get_motion_delta(0);
-        let adjustment = this.actor.vscroll.adjustment;
-        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
-        return false;
+        this.actor = this._grid.actor;
     },
 
     _getItemId: function(item) {
@@ -240,7 +174,7 @@ const AllView = new Lang.Class({
         else
             return null;
     },
-
+   
     _createItemIcon: function(item) {
         if (item instanceof Shell.App)
             return new AppIcon(item);
@@ -257,22 +191,147 @@ const AllView = new Lang.Class({
         let nameB = GLib.utf8_collate_key(itemB.get_name(), -1);
         return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0);
     },
+   
+    addItem: function(item) {
+        this._addItem(item);
+    },
+    
+    nPages: function() {
+        return this._grid.nPages();
+    },
+    
+    goToPreviousPage: function() {
+        this._grid.goToPreviousPage();
+    },
+    
+    goToNextPage: function() {
+        this._grid.goToNextPage();
+    },
+    
+    goToFirstPage: function() {
+        this._grid.goToFirstPage();
+    },
+    
+    currentPagePosition: function() {
+        return this._grid.currentPagePosition();
+    },
+    
+    setGridParentSize: function(size) {
+        this._grid._parentSize = size;
+    }
+});
+const PaginationScrollActor = new Lang.Class({
+    Name: 'PaginationScrollActor',
+    Extends: St.ScrollView,
+    
+    _init: function() {
+        this.parent();
+        this._box = new St.BoxLayout({vertical: true});
+        this._pages = new AppPages();
+        this._box.add_actor(this._pages.actor);
+        this.add_actor(this._box);
+
+        this.connect('scroll-event', Lang.bind(this, this._onScroll));
+    },
+    
+    vfunc_get_preferred_height: function (container, forWidht) {
+        return [0, 0];
+    },
+
+    vfunc_get_preferred_width: function(container, forHeight) {
+        return [0, 0];
+    },
+    
+    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;
+        
+        let childBox = new Clutter.ActorBox();
+        //Get the  boxLayout inside scrollView
+        let child = this.get_children()[2];
+        let childWidth = child.get_preferred_width(availHeight)[1];
+
+        childBox.x1 = 0;
+        childBox.y1 = 0;
+        childBox.x2 = availWidth;
+        childBox.y2 = availHeight;   
+        
+        this._pages.setGridParentSize([availWidth, availHeight]);
+
+        child.allocate(childBox, flags);
+        
+        if(this._pages.nPages > 0) {
+            this._pages.goToFirstPage();
+            this.vscroll.adjustment.set_value(this._pages.currentPagePosition()[1]);
+       }
+    },
+    
+    goToNextPage: function() {
+        this._pages.goToNextPage();
+        this.vscroll.adjustment.set_value(this._pages.currentPagePosition()[1]);
+    },
+    goToPreviousPage: function() {
+        this._pages.goToPreviousPage();
+        this.vscroll.adjustment.set_value(this._pages.currentPagePosition()[1]);
+    },
+    
+    _onScroll: function(actor, event) {
+        let direction = event.get_scroll_direction();
+        if (direction == Clutter.ScrollDirection.UP)
+            this.goToPreviousPage();
+        if (direction == Clutter.ScrollDirection.DOWN)
+            this.goToNextPage();
+    }
+});
+
+const AllView = new Lang.Class({
+    Name: 'AllView',
+   
+    _init: function() {      
+        this.actor = new PaginationScrollActor(); 
+        this._pageControl = new St.Widget();   
+    },
+    
+    _onKeyRelease: function(actor, event) {
+        if (event.get_key_symbol() == Clutter.KEY_Up) {
+            this.actor.goToNextPage();
+            return true;
+        } else if(event.get_key_symbol() == Clutter.KEY_Down) {
+            this.actor.goToPreviousPage();
+            return true
+        }
+
+        return false;
+    },
+   
+    _onPan: function(action) {
+        /*
+        this._clickAction.release();
+
+        let [dist, dx, dy] = action.get_motion_delta(0);
+        let adjustment = this.actor.vscroll.adjustment;
+        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;*/
+        return false;
+    },
 
     addApp: function(app) {
-        let appIcon = this._addItem(app);
-        if (appIcon)
+       let appIcon = this.actor._pages.addItem(app);
+        /*if (appIcon)
             appIcon.actor.connect('key-focus-in',
-                                  Lang.bind(this, this._ensureIconVisible));
+                                  Lang.bind(this, this._ensureIconVisible));*/
     },
 
     addFolder: function(dir) {
-        let folderIcon = this._addItem(dir);
-        if (folderIcon)
+        let folderIcon = this.actor._pages.addItem(dir);
+        /*if (folderIcon)
             folderIcon.actor.connect('key-focus-in',
-                                     Lang.bind(this, this._ensureIconVisible));
+                                     Lang.bind(this, this._ensureIconVisible));*/
     },
 
     addFolderPopup: function(popup) {
+        /*
         this._stack.add_actor(popup.actor);
         popup.connect('open-state-changed', Lang.bind(this,
             function(popup, isOpen) {
@@ -285,11 +344,7 @@ const AllView = new Lang.Class({
                 } else {
                     this._grid.actor.y = 0;
                 }
-            }));
-    },
-
-    _ensureIconVisible: function(icon) {
-        Util.ensureActorVisibleInScrollView(this.actor, icon);
+            }));*/
     },
 
     _updateIconOpacities: function(folderOpen) {
@@ -299,6 +354,14 @@ const AllView = new Lang.Class({
             else
                 this._items[id].actor.opacity = 255;
         }
+    },
+   
+    removeAll: function() {
+        this.actor._pages.removeAll();
+    },
+
+    loadGrid: function() {
+        this.actor._pages.loadGrid();
     }
 });
 
@@ -327,8 +390,7 @@ const FrequentView = new Lang.Class({
                 continue;
             let appIcon = new AppIcon(mostUsed[i]);
             this._grid.addItem(appIcon.actor, -1);
-        }
-    }
+        }    }
 });
 
 const Views = {
@@ -1027,3 +1089,4 @@ const AppIconMenu = new Lang.Class({
     }
 });
 Signals.addSignalMethods(AppIconMenu.prototype);
+
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index 416e659..5936a0c 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -177,12 +177,21 @@ const IconGrid = new Lang.Class({
         params = Params.parse(params, { rowLimit: null,
                                         columnLimit: null,
                                         fillParent: false,
-                                        xAlign: St.Align.MIDDLE });
+                                        xAlign: St.Align.MIDDLE,
+                                        usePagination: false});
         this._rowLimit = params.rowLimit;
         this._colLimit = params.columnLimit;
         this._xAlign = params.xAlign;
         this._fillParent = params.fillParent;
-
+        this._usePagination = params.usePagination;
+        
+        if(this._usePagination) {
+            this._nPages = 0;
+            //Set this variable properly before getPreferredHeight function is called
+            this._parentSize = [0, 0];
+            this._currentPage = 0;
+            this._firstPagesItems = [];
+        }
         this.actor = new St.BoxLayout({ style_class: 'icon-grid',
                                         vertical: true });
 
@@ -196,6 +205,7 @@ 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) {
@@ -248,12 +258,25 @@ const IconGrid = new Lang.Class({
             nRows = Math.min(nRows, this._rowLimit);
         let totalSpacing = Math.max(0, nRows - 1) * spacing;
         let height = nRows * this._vItemSize + totalSpacing;
+        
+        if(this._usePagination) {
+            
+            this._spacePerRow = this._vItemSize + spacing;
+            this._rowsPerPage = Math.floor(this._parentSize[1] / this._spacePerRow);            
+            this._nPages = Math.ceil(nRows / this._rowsPerPage);
+            this._spaceBetweenPages = this._parentSize[1] - (this._rowsPerPage * (this._vItemSize + 
spacing));
+            let spaceBetweenPagesTotal = this._spaceBetweenPages * (this._nPages);
+            this._childrenPerPage = nColumns * this._rowsPerPage;
+            alloc.min_size = this._rowsPerPage * this._spacePerRow * this._nPages + spaceBetweenPagesTotal;
+            alloc.natural_size = this._rowsPerPage * this._spacePerRow * this._nPages + 
spaceBetweenPagesTotal;
+            return;
+        }
         alloc.min_size = height;
         alloc.natural_size = height;
     },
 
     _allocate: function (grid, box, flags) {
-        if (this._fillParent) {
+        if(this._fillParent) {
             // Reset the passed in box to fill the parent
             let parentBox = this.actor.get_parent().allocation;
             let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
@@ -263,8 +286,11 @@ const IconGrid = new Lang.Class({
         let children = this._getVisibleChildren();
         let availWidth = box.x2 - box.x1;
         let availHeight = box.y2 - box.y1;
-
         let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
+        if(this._usePagination) {
+            //Recalculate the space between pages with the new spacing
+            this._spaceBetweenPages = this._parentSize[1] - (this._rowsPerPage * (this._vItemSize + 
spacing));
+        }
 
         let leftPadding;
         switch(this._xAlign) {
@@ -282,35 +308,25 @@ const IconGrid = new Lang.Class({
         let y = box.y1;
         let columnIndex = 0;
         let rowIndex = 0;
+        
+        if(children.length > 0) {
+            this._firstPagesItems = [children[0]];
+        }
         for (let i = 0; i < children.length; i++) {
-            let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
-                = children[i].get_preferred_size();
-
-            /* Center the item in its allocation horizontally */
-            let width = Math.min(this._hItemSize, childNaturalWidth);
-            let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
-            let height = Math.min(this._vItemSize, childNaturalHeight);
-            let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
-
-            let childBox = new Clutter.ActorBox();
-            if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
-                let _x = box.x2 - (x + width);
-                childBox.x1 = Math.floor(_x - childXSpacing);
-            } else {
-                childBox.x1 = Math.floor(x + childXSpacing);
-            }
-            childBox.y1 = Math.floor(y + childYSpacing);
-            childBox.x2 = childBox.x1 + width;
-            childBox.y2 = childBox.y1 + height;
-
-            if (this._rowLimit && rowIndex >= this._rowLimit ||
-                this._fillParent && childBox.y2 > availHeight) {
-                this._grid.set_skip_paint(children[i], true);
+            let childBox = this._calculateChildrenBox(children[i], x, y);
+            if(!this._usePagination) {
+                if (this._rowLimit && rowIndex >= this._rowLimit ||
+                        this._fillParent && childBox.y2 > availHeight) {
+                    this._grid.set_skip_paint(children[i], true);
+                } else {
+                    children[i].allocate(childBox, flags);
+                    this._grid.set_skip_paint(children[i], false);
+                }
             } else {
                 children[i].allocate(childBox, flags);
                 this._grid.set_skip_paint(children[i], false);
             }
-
+            
             columnIndex++;
             if (columnIndex == nColumns) {
                 columnIndex = 0;
@@ -319,6 +335,14 @@ const IconGrid = new Lang.Class({
 
             if (columnIndex == 0) {
                 y += this._vItemSize + spacing;
+                if(this._usePagination) {
+                    if((i + 1) % this._childrenPerPage == 0) {
+                        y+= this._spaceBetweenPages;
+                        if(i < children.length) {
+                            this._firstPagesItems.push(children[i+1]);
+                        }
+                    }
+                }
                 x = box.x1 + leftPadding;
             } else {
                 x += this._hItemSize + spacing;
@@ -326,8 +350,31 @@ const IconGrid = new Lang.Class({
         }
     },
 
+    _calculateChildrenBox: function(child, x, y) {
+        let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
+        = child.get_preferred_size();
+
+        /* Center the item in its allocation horizontally */
+        let width = Math.min(this._hItemSize, childNaturalWidth);
+        let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
+        let height = Math.min(this._vItemSize, childNaturalHeight);
+        let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
+    
+        let childBox = new Clutter.ActorBox();
+        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
+            let _x = box.x2 - (x + width);
+            childBox.x1 = Math.floor(_x - childXSpacing);
+        } else {
+            childBox.x1 = Math.floor(x + childXSpacing);
+        }
+        childBox.y1 = Math.floor(y + childYSpacing);
+        childBox.x2 = childBox.x1 + width;
+        childBox.y2 = childBox.y1 + height;
+        return childBox;
+    },
+    
     childrenInRow: function(rowWidth) {
-        return this._computeLayout(rowWidth)[0];
+        return this._computeLayout(rowWidth)[0]
     },
 
     getRowLimit: function() {
@@ -383,5 +430,34 @@ const IconGrid = new Lang.Class({
 
     visibleItemsCount: function() {
         return this._grid.get_n_children() - this._grid.get_n_skip_paint();
+    },
+    
+    nPages: function() {
+        return this._nPages;
+    },
+
+    currentPagePosition: function() {
+        let childBox = this._firstPagesItems[this._currentPage].get_allocation_box();
+        return [childBox.x1, childBox.y1];
+    },
+    
+    goToNextPage: function() {
+        if(!this._nPages)
+            return;
+        if(this._currentPage + 1 < this._firstPagesItems.length)
+            this._currentPage++;
+    },
+    
+    goToPreviousPage: function() {
+        if(!this._nPages)
+            return;
+        if(this._currentPage > 0)
+            this._currentPage--;
+    },
+    
+    goToFirstPage: function() {
+        if(!this._nPages)
+            return;
+        this._currentPage = 0;
     }
 });


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