[gnome-shell/wip/paging-release2: 5/12] AppDisplay: Add pages indicators



commit e9c28a13636808bc6afaf29545e53d8c757d2b43
Author: Carlos Soriano <carlos soriano89 gmail com>
Date:   Mon Aug 12 19:09:43 2013 +0200

    AppDisplay: Add pages indicators
    
    Add indicators to the AllView pagination, which display
    how many pages of apps we have and allow the user to
    navigate through them.

 data/Makefile.am                       |    2 +
 data/theme/gnome-shell.css             |   16 +++-
 data/theme/page-indicator-active.svg   |   67 +++++++++++++++
 data/theme/page-indicator-inactive.svg |   67 +++++++++++++++
 js/ui/appDisplay.js                    |  146 ++++++++++++++++++++++++++++++-
 5 files changed, 292 insertions(+), 6 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index ece7925..3126e27 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -41,6 +41,8 @@ dist_theme_DATA =                             \
        theme/message-tray-background.png       \
        theme/more-results.svg                  \
        theme/noise-texture.png                 \
+       theme/page-indicator-active.svg \
+       theme/page-indicator-inactive.svg       \
        theme/panel-button-border.svg           \
        theme/panel-button-highlight-narrow.svg \
        theme/panel-button-highlight-wide.svg   \
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 2342579..ce7d445 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -899,11 +899,25 @@ StScrollBar StButton#vhandle:active {
 
 .search-display > StBoxLayout,
 .all-apps,
-.frequent-apps > StBoxLayout {
+.frequent-apps > StBoxLayout{
     /* horizontal padding to make sure scrollbars or dash don't overlap content */
     padding: 0px 88px 10px 88px;
 }
 
+.pages-icon-indicator {
+       background-image: url(page-indicator-inactive.svg);
+}
+
+.pages-icon-indicator:hover,
+.pages-icon-indicator:checked{
+       background-image: url(page-indicator-active.svg);
+}
+
+.pages-indicator {
+       spacing: 35px;
+       padding: 0px, 20px 0px, 0px;
+}
+
 .app-folder-icon {
     padding: 5px;
 }
diff --git a/data/theme/page-indicator-active.svg b/data/theme/page-indicator-active.svg
new file mode 100644
index 0000000..38b720f
--- /dev/null
+++ b/data/theme/page-indicator-active.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="18"
+   height="18"
+   id="svg4703"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="page-indicator-active.svg">
+  <defs
+     id="defs4705" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.197802"
+     inkscape:cx="2.1522887"
+     inkscape:cy="16.782904"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1021"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata4708">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     transform="translate(0,2)">
+    <path
+       transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
+       d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 
-10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
+       sodipodi:ry="9.5964489"
+       sodipodi:rx="10.985409"
+       sodipodi:cy="638.83099"
+       sodipodi:cx="2088.9954"
+       id="path4711"
+       style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
+       sodipodi:type="arc" />
+  </g>
+</svg>
diff --git a/data/theme/page-indicator-inactive.svg b/data/theme/page-indicator-inactive.svg
new file mode 100644
index 0000000..3048f56
--- /dev/null
+++ b/data/theme/page-indicator-inactive.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="18"
+   height="18"
+   id="svg5266"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="page-indicator-inactive.svg">
+  <defs
+     id="defs5268" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.313709"
+     inkscape:cx="13.381365"
+     inkscape:cy="17.859535"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1021"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata5271">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     transform="translate(0,2)">
+    <path
+       sodipodi:type="arc"
+       
style="fill:#ffffff;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276;stroke-miterlimit:4;stroke-opacity:0.39215686;stroke-dasharray:none"
+       id="path5274"
+       sodipodi:cx="2088.9954"
+       sodipodi:cy="638.83099"
+       sodipodi:rx="10.985409"
+       sodipodi:ry="9.5964489"
+       d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 
-10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
+       transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
+  </g>
+</svg>
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 498df15..f5d444d 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -37,6 +37,8 @@ const INACTIVE_GRID_OPACITY = 77;
 const INACTIVE_GRID_OPACITY_ANIMATION_TIME = 0.15;
 const FOLDER_SUBICON_FRACTION = .4;
 
+const MAX_APPS_PAGES = 20;
+
 const PAGE_SWITCH_TIME = 0.25;
 
 // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
@@ -169,6 +171,99 @@ const PaginationScrollView = new Lang.Class({
     }
 });
 
+const PaginationIconIndicator = new Lang.Class({
+    Name: 'PaginationIconIndicator',
+    _init: function(parent, index) {
+
+        this.actor = new St.Button({ style_class: 'pages-icon-indicator',
+                                     button_mask: St.ButtonMask.ONE || St.ButtonMask.TWO,
+                                     toggle_mode: true,
+                                     can_focus: true });
+        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
+        this.actor._delegate = this;
+        //Background image is 18x18 but it is a svg, so we have to put a size
+        this.actor.set_size(18, 18);
+        this._parent = parent;
+        this.actor._index = index;
+    },
+
+    _onClicked: function(actor, button) {
+        this._parent.goToPage(this.actor._index, true);
+        return false;
+    },
+
+    setChecked: function (checked) {
+        this.actor.set_checked(checked);
+    }
+});
+
+const PaginationIndicator = new Lang.Class({
+    Name:'PaginationIndicator',
+
+    _init: function(params) {
+        params['y_expand'] = true;
+        this.actor = new Shell.GenericContainer(params);
+        this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
+        this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
+        this.actor.connect('allocate', Lang.bind(this, this._allocate));
+        this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
+        this._spacing = 0;
+    },
+
+    _getPreferredHeight: function(actor, forWidth, alloc) {
+        let [minHeight, natHeight] = this.actor.get_children()[0].get_preferred_height(forWidth);
+        if (this._nPages) {
+            let natHeightPerChild = natHeight + this._spacing;
+            let totalUsedHeight =  this._nPages * natHeightPerChild - this._spacing;
+            let minHeightPerChild = minHeight + this._spacing;
+            minHeight = this._nPages * minHeightPerChild - this._spacing;
+            natHeight = this._nPages * natHeightPerChild - this._spacing;
+        } else
+            minHeight = natHeight = 0;
+        alloc.min_size = 0;
+        alloc.natural_size = natHeight;
+    },
+
+    _getPreferredWidth: function(actor, forHeight, alloc) {
+        let [minWidth, natWidth] = this.actor.get_children()[0].get_preferred_width(forHeight);
+        let totalWidth = natWidth + this._spacing;
+        alloc.min_size = totalWidth;
+        alloc.natural_size = totalWidth;
+    },
+
+    _allocate: function(actor, box, flags) {
+        let children = this.actor.get_children();
+        for (let i in children)
+            this.actor.set_skip_paint(children[i], true);
+        if (children.length < 1)
+            return;
+        let availHeight = box.y2 - box.y1;
+        let availWidth = box.x2 - box.x1;
+        let [minHeight, natHeight] = children[0].get_preferred_height(availWidth);
+        let heightPerChild = natHeight + this._spacing;
+        let totalUsedHeight =  this._nPages * heightPerChild - this._spacing;
+        let [minWidth, natWidth] = children[0].get_preferred_width(natHeight);
+        let widthPerChild = natWidth + this._spacing * 2;
+        let firstPosition = [this._spacing, availHeight / 2 - totalUsedHeight / 2];
+        for (let i = 0; i < this._nPages; i++) {
+            let childBox = new Clutter.ActorBox();
+            childBox.x1 =  0;
+            childBox.x2 = availWidth;
+            childBox.y1 = firstPosition[1] + i * heightPerChild;
+            childBox.y2 = childBox.y1 + heightPerChild;
+            if (childBox.y2 > availHeight)
+                break;
+            children[i].allocate(childBox, flags);
+            this.actor.set_skip_paint(children[i], false);
+        }
+    },
+
+    _styleChanged: function() {
+        this._spacing = this.actor.get_theme_node().get_length('spacing');
+        this.actor.queue_relayout();
+    }
+});
+
 const AllView = new Lang.Class({
     Name: 'AllView',
     Extends: PaginatedAlphabeticalView,
@@ -176,10 +271,22 @@ const AllView = new Lang.Class({
     _init: function() {
         this.parent();
         this._paginationView = new PaginationScrollView(this, {style_class: 'all-apps'});
+        this._paginationIndicator = new PaginationIndicator({ style_class: 'pages-indicator' });
         let layout = new Clutter.BinLayout();
         this.actor = new St.Widget({ layout_manager: layout, 
                                      x_expand:true, y_expand:true });
         layout.add(this._paginationView, Clutter.ActorAlign.CENTER, Clutter.ActorAlign.CENTER);
+        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+            layout.add(this._paginationIndicator.actor, Clutter.ActorAlign.START, Clutter.ActorAlign.CENTER);
+        else
+            layout.add(this._paginationIndicator.actor, Clutter.ActorAlign.END, Clutter.ActorAlign.CENTER);
+        for (let i = 0; i < MAX_APPS_PAGES; i++) {
+            let indicatorIcon = new PaginationIconIndicator(this, i);
+            if (i == 0)
+                indicatorIcon.setChecked(true);
+            this._paginationIndicator.actor.add_actor(indicatorIcon.actor);
+        }
+
         this._grid.connect('n-pages-changed', Lang.bind(this, this._updatedNPages));
 
         this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
@@ -218,10 +325,33 @@ const AllView = new Lang.Class({
     _updatedNPages: function(iconGrid, nPages) {
         // We don't need a relayout because we already done it at iconGrid
         // when pages are calculated (and then the signal is emitted before that)");
+        this._paginationIndicator._nPages = nPages;
         this.invalidatePagination = true;
     },
 
-    goToPage: function(pageNumber) {
+    goToPage: function(pageNumber, updateIndicators, action) {
+        this.viewGoToPage(pageNumber, action);
+        if (updateIndicators)
+            this.indicatorsGoToPage(pageNumber);
+    },
+
+    indicatorsGoToPage: function(pageNumber) {
+        // Since it can happens after a relayout, we have to ensure that all is unchecked
+        let indicators = this._paginationIndicator.actor.get_children();
+        if (this._grid.nPages() > 1) {
+            for (let index in indicators)
+                indicators[index].set_checked(false);
+            this._paginationIndicator.actor.get_child_at_index(this._currentPage).set_checked(true);
+        }
+    },
+
+    viewGoToPage: function(pageNumber, action) {
+        // Since we call this function also from shown signal of the overview, we can't assure the 
pagination is already calculated
+        // so we first ask pagination if it has some page, if not, we return and that's it. (nevermind, 
because when creating for first time
+        // pagination, that is when it can happens, paginations starts at page 0 already, so no problem here)
+        if (!this._grid.nPages())
+            return;
+
         if (pageNumber < this._grid.nPages() && pageNumber >= 0) {
             this._currentPage = pageNumber;
             let params = { value: this._grid.getPagePosition(this._currentPage)[1],
@@ -237,12 +367,12 @@ const AllView = new Lang.Class({
         if (direction == Clutter.ScrollDirection.UP)
             if (this._currentPage > 0) {
                 nextPage = this._currentPage - 1;
-                this.goToPage(nextPage);
+                this.goToPage(nextPage, true);
             }
         if (direction == Clutter.ScrollDirection.DOWN)
             if(this._currentPage < (this._grid.nPages() - 1)) {
                 nextPage = this._currentPage + 1;
-                this.goToPage(nextPage);
+                this.goToPage(nextPage, true);
             }
     },
 
@@ -304,8 +434,14 @@ const AllView = new Lang.Class({
     updateAdjustment: function(availHeight) {
         this._verticalAdjustment.page_size = availHeight;
         this._verticalAdjustment.upper = this._stack.height;
-        if (this.invalidatePagination)
-            this.goToPage(0);
+        if (this.invalidatePagination) {
+            // We can modify the adjustment, so we do that to show the first page, but we can't modify the 
indicators,
+            // so we modify it before redraw (we won't see too much flickering at all)
+            if (this._grid.nPages() > 1) {
+                this.goToPage(0, false);
+                Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this.goToPage(0, 
true); }));
+            }
+        }
         this.invalidatePagination = false;
     },
 


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