[gnome-shell/wip/paging-release2: 3/12] AppDisplay: Use new IconGrid implemetation in AllView and FrequentView
- From: Carlos Soriano <csoriano src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/paging-release2: 3/12] AppDisplay: Use new IconGrid implemetation in AllView and FrequentView
- Date: Tue, 20 Aug 2013 22:04:50 +0000 (UTC)
commit 06423fe21132e920e7193165ed40f0f748f9e46f
Author: Carlos Soriano <carlos soriano89 gmail com>
Date: Tue Aug 20 11:19:34 2013 +0200
AppDisplay: Use new IconGrid implemetation in AllView and FrequentView
Create a PaginatedAlphabeticalView to allow AllView to be a
paginatedView and adapt the implementation to that.
On the other hand FrequentView adapts the new IconGrid implementation.
Create a PaginationScrollView 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.
js/ui/appDisplay.js | 306 ++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 230 insertions(+), 76 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index b26c2b7..498df15 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -30,10 +30,14 @@ 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;
const FOLDER_SUBICON_FRACTION = .4;
+const PAGE_SWITCH_TIME = 0.25;
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
function _loadCategory(dir, view) {
@@ -58,10 +62,12 @@ const AlphabeticalView = new Lang.Class({
Name: 'AlphabeticalView',
Abstract: true,
- _init: function() {
- this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
- columnLimit: MAX_COLUMNS });
-
+ _init: function(gridParams) {
+ gridParams = Params.parse(gridParams, { xAlign: St.Align.MIDDLE,
+ columnLimit: MAX_COLUMNS,
+ minRows: MIN_ROWS,
+ minColumns: MIN_COLUMNS });
+ this._grid = new IconGrid.IconGrid(gridParams);
// Standard hack for ClutterBinLayout
this._grid.actor.x_expand = true;
@@ -111,59 +117,86 @@ const AlphabeticalView = new Lang.Class({
}
});
-const AllViewLayout = new Lang.Class({
- Name: 'AllViewLayout',
- Extends: Clutter.BinLayout,
+const PaginatedAlphabeticalView = new Lang.Class({
+ Name: 'PaginatedAlphabeticalView',
+ Extends: AlphabeticalView,
- vfunc_get_preferred_height: function(container, forWidth) {
- let minBottom = 0;
- let naturalBottom = 0;
+ _init: function(gridParams) {
+ gridParams = Params.parse(gridParams, { xAlign: St.Align.MIDDLE,
+ columnLimit: MAX_COLUMNS,
+ minRows: MIN_ROWS,
+ minColumns: MIN_COLUMNS });
+ this._grid = new IconGrid.PaginatedIconGrid(gridParams);
+ // Standard hack for ClutterBinLayout
+ this._grid.actor.x_expand = true;
+ this._items = {};
+ this._allItems = [];
+ }
+});
- 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);
+const PaginationScrollView = new Lang.Class({
+ Name: 'PaginationScrollView',
+ Extends: St.Bin,
- if (childMin + childY > minBottom)
- minBottom = childMin + childY;
+ _init: function(parent, params) {
+ params['reactive'] = true;
+ this.parent(params);
+ this._parent = parent;
+ },
- if (childNatural + childY > naturalBottom)
- naturalBottom = childNatural + childY;
- }
- return [minBottom, naturalBottom];
+ vfunc_get_preferred_height: function (forWidth) {
+ return [0, 0];
+ },
+
+ vfunc_get_preferred_width: function(forHeight) {
+ return [0, 0];
+ },
+
+ vfunc_allocate: function(box, flags) {
+ box = this.get_parent().allocation;
+ box = this.get_theme_node().get_content_box(box);
+ this.set_allocation(box, flags);
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+ let childBox = new Clutter.ActorBox();
+ childBox.x1 = 0;
+ childBox.y1 = 0;
+ childBox.x2 = availWidth;
+ childBox.y2 = availHeight;
+ let child = this.get_child();
+ child.allocate(childBox, flags);
+ this._parent.updateAdjustment(availHeight);
}
});
const AllView = new Lang.Class({
Name: 'AllView',
- Extends: AlphabeticalView,
+ Extends: PaginatedAlphabeticalView,
_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._paginationView = new PaginationScrollView(this, {style_class: 'all-apps'});
+ 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);
+ this._grid.connect('n-pages-changed', Lang.bind(this, this._updatedNPages));
+
+ this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
+ this._box = new St.BoxLayout({ vertical: true });
+ this._verticalAdjustment = new St.Adjustment();
+ this._horizontalAdjustment = new St.Adjustment();
+ this._box.set_adjustments(this._horizontalAdjustment, 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);
+ this._stack.add_actor(this._eventBlocker, {x_align:St.Align.MIDDLE});
+
+ this._box.add_actor(this._stack);
+ this._paginationView.add_actor(this._box);
+
+ this._paginationView.connect('scroll-event', Lang.bind(this, this._onScroll));
this._clickAction = new Clutter.ClickAction();
this._clickAction.connect('clicked', Lang.bind(this, function() {
@@ -176,14 +209,51 @@ const AllView = new Lang.Class({
this._currentPopup.popdown();
}));
this._eventBlocker.add_action(this._clickAction);
+ // When the number of pages change (i.e. when changing screen resolution or during clutter false
allocations)
+ // we have to tell pagination that the adjustment is not correct (since the allocated size of
pagination changed)
+ // For that problem we return to the first page of pagination.
+ this.invalidatePagination = false;
},
- _onPan: function(action) {
- this._clickAction.release();
+ _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.invalidatePagination = true;
+ },
+
+ goToPage: function(pageNumber) {
+ if (pageNumber < this._grid.nPages() && pageNumber >= 0) {
+ this._currentPage = pageNumber;
+ let params = { value: this._grid.getPagePosition(this._currentPage)[1],
+ time: PAGE_SWITCH_TIME,
+ transition: 'easeOutQuad' };
+ Tweener.addTween(this._verticalAdjustment, params);
+ }
+ },
- let [dist, dx, dy] = action.get_motion_delta(0);
- let adjustment = this.actor.vscroll.adjustment;
- adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
+ _onScroll: function(actor, event) {
+ let direction = event.get_scroll_direction();
+ let nextPage;
+ if (direction == Clutter.ScrollDirection.UP)
+ if (this._currentPage > 0) {
+ nextPage = this._currentPage - 1;
+ this.goToPage(nextPage);
+ }
+ if (direction == Clutter.ScrollDirection.DOWN)
+ if(this._currentPage < (this._grid.nPages() - 1)) {
+ nextPage = this._currentPage + 1;
+ this.goToPage(nextPage);
+ }
+ },
+
+ _onKeyRelease: function(actor, event) {
+ if (event.get_key_symbol() == Clutter.KEY_Up) {
+ this.goToNextPage();
+ return true;
+ } else if (event.get_key_symbol() == Clutter.KEY_Down) {
+ this.goToPreviousPage();
+ return true;
+ }
return false;
},
@@ -214,17 +284,11 @@ const AllView = new Lang.Class({
},
addApp: function(app) {
- let appIcon = this._addItem(app);
- if (appIcon)
- appIcon.actor.connect('key-focus-in',
- Lang.bind(this, this._ensureIconVisible));
+ this._addItem(app);
},
addFolder: function(dir) {
- let folderIcon = this._addItem(dir);
- if (folderIcon)
- folderIcon.actor.connect('key-focus-in',
- Lang.bind(this, this._ensureIconVisible));
+ this._addItem(dir);
},
addFolderPopup: function(popup) {
@@ -234,26 +298,55 @@ 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);
+ updateAdjustment: function(availHeight) {
+ this._verticalAdjustment.page_size = availHeight;
+ this._verticalAdjustment.upper = this._stack.height;
+ if (this.invalidatePagination)
+ this.goToPage(0);
+ this.invalidatePagination = false;
},
_updateIconOpacities: function(folderOpen) {
for (let id in this._items) {
- if (folderOpen && !this._items[id].actor.checked)
- this._items[id].actor.opacity = INACTIVE_GRID_OPACITY;
- else
- this._items[id].actor.opacity = 255;
+ if (folderOpen && !this._items[id].actor.checked) {
+ let params = { opacity: INACTIVE_GRID_OPACITY,
+ time: INACTIVE_GRID_OPACITY_ANIMATION_TIME,
+ transition: 'easeOutQuad' };
+ Tweener.addTween(this._items[id].actor, params);
+ }
+ else {
+ let params = { opacity: 255,
+ time: INACTIVE_GRID_OPACITY_ANIMATION_TIME,
+ transition: 'easeOutQuad' };
+ Tweener.addTween(this._items[id].actor, params);
+ }
}
+ },
+
+ onUpdatedDisplaySize: 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._paginationView.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;
+
+ // Update grid dinamyc spacing based on display width
+ let spacing = this._grid.maxSpacingForWidthHeight(availWidth, availHeight, MIN_COLUMNS, MIN_ROWS,
true);
+ // Calculate pagination values
+ this._grid.calculatePaginationValues(availWidth, availHeight);
+ this._grid.top_padding = spacing;
+ this._grid.bottom_padding = spacing;
+ this._grid.left_padding = spacing;
+ this._grid.right_padding = spacing;
+ this._grid.setSpacing(spacing);
}
});
@@ -263,6 +356,8 @@ const FrequentView = new Lang.Class({
_init: function() {
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
fillParent: true,
+ minRows: MIN_ROWS,
+ minColumns: MIN_COLUMNS,
columnLimit: MAX_COLUMNS });
this.actor = new St.Widget({ style_class: 'frequent-apps',
x_expand: true, y_expand: true });
@@ -283,6 +378,24 @@ const FrequentView = new Lang.Class({
let appIcon = new AppIcon(mostUsed[i]);
this._grid.addItem(appIcon.actor, -1);
}
+ },
+
+ onUpdatedDisplaySize: 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._grid.actor.get_theme_node().get_content_box(box);
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+ let spacing = this._grid.maxSpacingForWidthHeight(availWidth, availHeight, MIN_COLUMNS, MIN_ROWS,
true);
+ this._grid.top_padding = spacing;
+ this._grid.bottom_padding = spacing;
+ this._grid.left_padding = spacing;
+ this._grid.right_padding = spacing;
+ this._grid.setSpacing(spacing);
}
});
@@ -316,6 +429,32 @@ 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 upcomming allocation, calculate all
+ // the needed values in the responsive design we are trying to emulate
+ this.emit('allocated-size-changed', availWidth, availHeight);
+ this.parent(actor, box, flags);
+ },
+
+ vfunc_set_container: function(container) {
+ if (this._styleChangedId) {
+ this._container.disconnect(this._styleChangedId);
+ this._styleChangedId = 0;
+ }
+ if (container != null)
+ this._styleChangedId = container.connect('style-changed', Lang.bind(this,
+ function() { this.spacing = this._container.get_theme_node().get_length('spacing'); }));
+ this._container = container;
+ }
+});
+Signals.addSignalMethods(ViewStackLayout.prototype);
+
const AppDisplay = new Lang.Class({
Name: 'AppDisplay',
@@ -351,20 +490,21 @@ 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.actor = new St.Widget({ style_class: 'app-display',
+ x_expand: true, y_expand: true });
+ this._viewStackLayout = new ViewStackLayout();
+ 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(),
- x_expand: true, y_expand: true });
- this.actor.add(this._viewStack, { expand: true });
+ this._viewStack = new St.Widget({ x_expand: true, y_expand: true });
+ this._viewStack.set_layout_manager(this._viewStackLayout);
+ 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);
@@ -464,6 +604,20 @@ const AppDisplay = new Lang.Class({
if (focused)
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
+ },
+
+ _onUpdatedDisplaySize: function(actor, width, height) {
+ let box = new Clutter.ActorBox();
+ box.x1 = 0;
+ box.x2 = width;
+ box.y1 = 0;
+ 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.onUpdatedDisplaySize(availWidth, availHeight);
+ }
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]