[gnome-shell/wip/paging-release2: 8/23] appDisplay: Use new IconGrid implementation in AllView and FrequentView
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/paging-release2: 8/23] appDisplay: Use new IconGrid implementation in AllView and FrequentView
- Date: Mon, 2 Sep 2013 14:31:27 +0000 (UTC)
commit d2920da816d050a5f9ecaf12144816fa9ec81b96
Author: Carlos Soriano <carlos soriano89 gmail com>
Date: Tue Aug 27 21:22:16 2013 +0200
appDisplay: Use new IconGrid implementation in AllView and FrequentView
Add "usePagination" parameter to Alphabetical view to allow AllView
use the new PaginatedIconGrid.
FrequentView adapts the new IconGrid implementation. Create a
PagesView class to allow us control the allocation of the IconGrid
child and the scroll adjustment. Hook up the allocation/size comunication
between the FrequentView, AllView and FolderView with AppDisplay with
a ViewStackLayout that updates all views.
https://bugzilla.gnome.org/show_bug.cgi?id=706081
data/theme/gnome-shell.css | 3 +-
js/ui/appDisplay.js | 222 +++++++++++++++++++++++++++++---------------
js/ui/iconGrid.js | 15 +++-
3 files changed, 164 insertions(+), 76 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index afb1f48..8992fc7 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -934,9 +934,8 @@ StScrollBar StButton#vhandle:active {
padding: 3px 31px;
}
-
.search-display > StBoxLayout,
-.all-apps > StBoxLayout,
+.all-apps,
.frequent-apps > StBoxLayout {
/* horizontal padding to make sure scrollbars or dash don't overlap content */
padding: 0px 88px;
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index b68a330..30d033c 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -35,6 +35,7 @@ const INACTIVE_GRID_OPACITY = 77;
const INACTIVE_GRID_OPACITY_ANIMATION_TIME = 0.15;
const FOLDER_SUBICON_FRACTION = .4;
+const PAGE_SWITCH_TIME = 0.3;
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
function _loadCategory(dir, view) {
@@ -59,9 +60,16 @@ const BaseAppView = new Lang.Class({
Name: 'BaseAppView',
Abstract: true,
- _init: function() {
- this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
- columnLimit: MAX_COLUMNS });
+ _init: function(params, gridParams) {
+ gridParams = Params.parse(gridParams, { xAlign: St.Align.MIDDLE,
+ columnLimit: MAX_COLUMNS,
+ fillParent: false });
+ params = Params.parse(params, { usePagination: false });
+
+ if(params.usePagination)
+ this._grid = new IconGrid.PaginatedIconGrid(gridParams);
+ else
+ this._grid = new IconGrid.IconGrid(gridParams);
// Standard hack for ClutterBinLayout
this._grid.actor.x_expand = true;
@@ -142,27 +150,18 @@ const BaseAppView = new Lang.Class({
});
Signals.addSignalMethods(BaseAppView.prototype);
-const AllViewLayout = new Lang.Class({
- Name: 'AllViewLayout',
- Extends: Clutter.BinLayout,
- vfunc_get_preferred_height: function(container, forWidth) {
- let minBottom = 0;
- let naturalBottom = 0;
+// Ignore child size requests to use the available size from the parent
+const PagesBin = new Lang.Class({
+ Name: 'PagesBin',
+ Extends: St.Bin,
- 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;
+ vfunc_get_preferred_height: function (forWidth) {
+ return [0, 0];
+ },
- if (childNatural + childY > naturalBottom)
- naturalBottom = childNatural + childY;
- }
- return [minBottom, naturalBottom];
+ vfunc_get_preferred_width: function(forHeight) {
+ return [0, 0];
}
});
@@ -171,30 +170,32 @@ const AllView = new Lang.Class({
Extends: BaseAppView,
_init: function() {
- this.parent();
-
- this._grid.actor.y_align = Clutter.ActorAlign.START;
- this._grid.actor.y_expand = true;
+ this.parent({ usePagination: true }, null);
+ this._pagesBin = new PagesBin({ style_class: 'all-apps',
+ x_expand: true,
+ y_expand: true,
+ x_fill: true,
+ y_fill: false,
+ reactive: true,
+ y_align: St.Align.START });
+ this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
+ x_expand:true, y_expand:true });
+ this.actor.add_actor(this._pagesBin);
+ this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
let box = new St.BoxLayout({ vertical: true });
- this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
+ this._verticalAdjustment = new St.Adjustment();
+ box.set_adjustments(new St.Adjustment() /* unused */, this._verticalAdjustment);
+
+ this._currentPage = 0;
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);
+
+ box.add_actor(this._stack);
+ this._pagesBin.add_actor(box);
+
+ this._pagesBin.connect('scroll-event', Lang.bind(this, this._onScroll));
this._clickAction = new Clutter.ClickAction();
this._clickAction.connect('clicked', Lang.bind(this, function() {
@@ -207,15 +208,30 @@ const AllView = new Lang.Class({
this._currentPopup.popdown();
}));
this._eventBlocker.add_action(this._clickAction);
+
+ this._availWidth = 0;
+ this._availHeight = 0;
},
- _onPan: function(action) {
- this._clickAction.release();
+ goToPage: function(pageNumber) {
+ this._currentPage = pageNumber;
+ Tweener.addTween(this._verticalAdjustment,
+ { value: this._grid.getPageY(this._currentPage),
+ time: PAGE_SWITCH_TIME,
+ transition: 'easeOutQuad' });
+ },
- 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;
+ _onScroll: function(actor, event) {
+ let direction = event.get_scroll_direction();
+ if (direction == Clutter.ScrollDirection.UP) {
+ if (this._currentPage > 0)
+ this.goToPage(this._currentPage - 1);
+ } else {
+ if (direction == Clutter.ScrollDirection.DOWN) {
+ if (this._currentPage < (this._grid.nPages() - 1))
+ this.goToPage(this._currentPage + 1);
+ }
+ }
},
_getItemId: function(item) {
@@ -265,17 +281,17 @@ const AllView = new Lang.Class({
this._eventBlocker.reactive = isOpen;
this._currentPopup = isOpen ? popup : null;
this._updateIconOpacities(isOpen);
- if (isOpen) {
- this._ensureIconVisible(popup.actor);
- this._grid.actor.y = popup.parentOffset;
- } else {
- this._grid.actor.y = 0;
- }
}));
},
_ensureIconVisible: function(icon) {
- Util.ensureActorVisibleInScrollView(this.actor, icon);
+ let itemPage = this._grid.getItemPage(icon);
+ this.goToPage(itemPage);
+ },
+
+ _updateAdjustment: function(availHeight) {
+ this._verticalAdjustment.page_size = availHeight;
+ this._verticalAdjustment.upper = this._stack.height;
},
_updateIconOpacities: function(folderOpen) {
@@ -290,16 +306,41 @@ const AllView = new Lang.Class({
transition: 'easeOutQuad' };
Tweener.addTween(this._items[id].actor, params);
}
+ },
+
+ // Called before allocation to calculate dynamic spacing
+ adaptToSize: function(width, height) {
+ let box = new Clutter.ActorBox();
+ box.x1 = 0;
+ box.x2 = width;
+ box.y1 = 0;
+ box.y2 = height;
+ box = this.actor.get_theme_node().get_content_box(box);
+ box = this._pagesBin.get_theme_node().get_content_box(box);
+ box = this._grid.actor.get_theme_node().get_content_box(box);
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+ let oldNPages = this._grid.nPages();
+
+ this._updateAdjustment(availHeight);
+ this._grid.updateSpacingForSize(availWidth, availHeight);
+ this._grid.computePages(availWidth, availHeight);
+ // Make sure the view doesn't have a bad adjustment value after screen size changes
+ // and therefore the pages computation.
+ if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages !=
this._grid.nPages())
+ this._verticalAdjustment.value = 0;
+
+ this._availWidth = availWidth;
+ this._availHeight = availHeight;
}
});
const FrequentView = new Lang.Class({
Name: 'FrequentView',
+ Extends: BaseAppView,
_init: function() {
- this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
- fillParent: true,
- columnLimit: MAX_COLUMNS });
+ this.parent(null, { fillParent: true });
this.actor = new St.Widget({ style_class: 'frequent-apps',
x_expand: true, y_expand: true });
this.actor.add_actor(this._grid.actor);
@@ -307,10 +348,6 @@ const FrequentView = new Lang.Class({
this._usage = Shell.AppUsage.get_default();
},
- removeAll: function() {
- this._grid.removeAll();
- },
-
loadApps: function() {
let mostUsed = this._usage.get_most_used ("");
for (let i = 0; i < mostUsed.length; i++) {
@@ -319,6 +356,19 @@ const FrequentView = new Lang.Class({
let appIcon = new AppIcon(mostUsed[i]);
this._grid.addItem(appIcon.actor, -1);
}
+ },
+
+ // Called before allocation to calculate dynamic spacing
+ adaptToSize: function(width, height) {
+ let box = new Clutter.ActorBox();
+ box.x1 = box.y1 = 0;
+ box.x2 = width;
+ box.y2 = height;
+ box = this.actor.get_theme_node().get_content_box(box);
+ box = this._grid.actor.get_theme_node().get_content_box(box);
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+ this._grid.updateSpacingForSize(availWidth, availHeight);
}
});
@@ -352,6 +402,21 @@ const ControlsBoxLayout = Lang.Class({
}
});
+const ViewStackLayout = new Lang.Class({
+ Name: 'ViewStackLayout',
+ Extends: Clutter.BinLayout,
+
+ 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 upcoming allocation, calculate all
+ // the needed values to adapt available size
+ this.emit('allocated-size-changed', availWidth, availHeight);
+ this.parent(actor, box, flags);
+ }
+});
+Signals.addSignalMethods(ViewStackLayout.prototype);
+
const AppDisplay = new Lang.Class({
Name: 'AppDisplay',
@@ -387,20 +452,19 @@ const AppDisplay = new Lang.Class({
x_expand: true });
this._views[Views.ALL] = { 'view': view, 'control': button };
- this.actor = new St.BoxLayout({ style_class: 'app-display',
- vertical: true,
- x_expand: true, y_expand: true });
-
- this._viewStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
- x_expand: true, y_expand: true });
- this.actor.add(this._viewStack, { expand: true });
-
+ this.actor = new St.BoxLayout ({ style_class: 'app-display',
+ x_expand: true, y_expand: true,
+ vertical: true });
+ this._viewStackLayout = new ViewStackLayout();
+ this._viewStack = new St.Widget({ x_expand: true, y_expand: true,
+ layout_manager: this._viewStackLayout });
+ this._viewStackLayout.connect('allocated-size-changed', Lang.bind(this,
this._onAllocatedSizeChanged));
+ this.actor.add_actor(this._viewStack, { expand: true });
let layout = new ControlsBoxLayout({ homogeneous: true });
this._controls = new St.Widget({ style_class: 'app-view-controls',
layout_manager: layout });
layout.hookup_style(this._controls);
- this.actor.add(new St.Bin({ child: this._controls }));
-
+ this.actor.add_actor(new St.Bin({ child: this._controls }));
for (let i = 0; i < this._views.length; i++) {
this._viewStack.add_actor(this._views[i].view.actor);
@@ -506,7 +570,19 @@ const AppDisplay = new Lang.Class({
this._showView(Views.ALL);
this._views[Views.ALL].view.selectApp(id);
},
-});
+
+ _onAllocatedSizeChanged: function(actor, width, height) {
+ let box = new Clutter.ActorBox();
+ box.x1 = box.y1 =0;
+ box.x2 = width;
+ box.y2 = height;
+ box = this._viewStack.get_theme_node().get_content_box(box);
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+ for (let i = 0; i < this._views.length; i++)
+ this._views[i].view.adaptToSize(availWidth, availHeight);
+ }
+})
const AppSearchProvider = new Lang.Class({
Name: 'AppSearchProvider',
@@ -569,7 +645,7 @@ const FolderView = new Lang.Class({
Extends: BaseAppView,
_init: function() {
- this.parent();
+ this.parent(null, null);
this.actor = this._grid.actor;
},
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index d408b1b..59c6360 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -261,7 +261,6 @@ const IconGrid = new Lang.Class({
let children = this._getVisibleChildren();
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
- this.updateSpacingForSize(availWidth);
let spacing = this._getSpacing();
let [nColumns, usedWidth] = this._computeLayout(availWidth);
@@ -390,6 +389,10 @@ const IconGrid = new Lang.Class({
return this._fixedSpacing ? this._fixedSpacing : this._spacing;
},
+ /**
+ * This function must to be called before iconGrid allocation,
+ * to know how much spacing can the grid has
+ */
updateSpacingForSize: function(availWidth) {
let spacing = this._spacing;
@@ -504,5 +507,15 @@ const PaginatedIconGrid = new Lang.Class({
let firstPageItem = pageNumber * this._childrenPerPage
let childBox = this._getVisibleChildren()[firstPageItem].get_allocation_box();
return childBox.y1;
+ },
+
+ getItemPage: function(item) {
+ let children = this._getVisibleChildren();
+ let index = children.indexOf(item);
+ if (index == -1) {
+ throw new Error('Item not found.');
+ return 0;
+ }
+ return Math.floor(index / this._childrenPerPage);
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]