[gnome-shell/gbsneto/pagination: 2/14] iconGrid: Use IconGridLayout
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/gbsneto/pagination: 2/14] iconGrid: Use IconGridLayout
- Date: Wed, 17 Jun 2020 19:23:28 +0000 (UTC)
commit f93ed440c41f91b1de658628fa63d2fab44232d5
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Tue May 19 10:43:10 2020 -0300
iconGrid: Use IconGridLayout
Replace the current grid code with IconGridLayout.
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1271
data/theme/gnome-shell-sass/widgets/_app-grid.scss | 12 +-
js/ui/appDisplay.js | 90 +--
js/ui/iconGrid.js | 646 ++++++---------------
3 files changed, 218 insertions(+), 530 deletions(-)
---
diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss
b/data/theme/gnome-shell-sass/widgets/_app-grid.scss
index c2b8f7e848..f52a91663f 100644
--- a/data/theme/gnome-shell-sass/widgets/_app-grid.scss
+++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss
@@ -1,17 +1,13 @@
/* App Grid */
$app_icon_size: 96px;
-$app_icon_padding: 24px;
// app icons
.icon-grid {
- -shell-grid-horizontal-item-size: $app_icon_size + $app_icon_padding * 2;
- -shell-grid-vertical-item-size: $app_icon_size + $app_icon_padding * 2;
- spacing: $base_spacing * 6;
-
- .overview-icon {
- icon-size: $app_icon_size;
- }
+ row-spacing: $base_spacing * 6;
+ column-spacing: $base_spacing * 6;
+ max-row-spacing: $base_spacing * 12;
+ max-column-spacing: $base_spacing * 12;
}
/* App Icons */
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index c2c258f6ea..6bd3b61451 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -20,16 +20,12 @@ const Util = imports.misc.util;
const SystemActions = imports.misc.systemActions;
var MENU_POPUP_TIMEOUT = 600;
-var MAX_COLUMNS = 6;
-var MIN_COLUMNS = 4;
-var MIN_ROWS = 4;
var FOLDER_SUBICON_FRACTION = .4;
var VIEWS_SWITCH_TIME = 400;
var VIEWS_SWITCH_ANIMATION_DELAY = 100;
-var PAGE_SWITCH_TIME = 250;
var SCROLL_TIMEOUT_TIME = 150;
var APP_ICON_SCALE_IN_TIME = 500;
@@ -121,17 +117,10 @@ var BaseAppView = GObject.registerClass({
'view-loaded': {},
},
}, class BaseAppView extends St.Widget {
- _init(params = {}, gridParams) {
+ _init(params = {}) {
super._init(params);
- gridParams = Params.parse(gridParams, {
- columnLimit: MAX_COLUMNS,
- minRows: MIN_ROWS,
- minColumns: MIN_COLUMNS,
- padWithSpacing: true,
- }, true);
-
- this._grid = new IconGrid.IconGrid(gridParams);
+ this._grid = new IconGrid.IconGrid();
this._grid.connect('child-focused', (grid, actor) => {
this._childFocused(actor);
});
@@ -181,7 +170,7 @@ var BaseAppView = GObject.registerClass({
let iconIndex = newApps.indexOf(icon);
this._orderedItems.splice(iconIndex, 0, icon);
- this._grid.addItem(icon, iconIndex);
+ this._grid.addItem(icon);
this._items.set(icon.id, icon);
});
@@ -331,19 +320,13 @@ class AppDisplay extends BaseAppView {
});
this.add_actor(this._stack);
- let box = new St.BoxLayout({
- vertical: true,
- y_align: Clutter.ActorAlign.START,
- });
- box.add_child(this._grid);
-
this._scrollView = new St.ScrollView({
style_class: 'all-apps',
x_expand: true,
y_expand: true,
reactive: true,
});
- this._scrollView.add_actor(box);
+ this._scrollView.add_actor(this._grid);
this._stack.add_actor(this._scrollView);
this._scrollView.set_policy(St.PolicyType.NEVER,
@@ -365,8 +348,6 @@ class AppDisplay extends BaseAppView {
this._folderIcons = [];
- this._grid.currentPage = 0;
-
this._scrollView.connect('scroll-event', this._onScroll.bind(this));
this._swipeTracker = new SwipeTracker.SwipeTracker(
@@ -472,10 +453,20 @@ class AppDisplay extends BaseAppView {
let newIdx = Util.insertSorted(this._orderedItems, item, this._compareItems);
this._grid.removeItem(item);
- this._grid.addItem(item, newIdx);
+ this._grid.addItem(item, -1, newIdx);
this.selectApp(item.id);
}
+ _isItemInFolder(itemId) {
+ for (const folder of this._folderIcons) {
+ const folderApps = folder.getAppIds();
+ if (folderApps.some(appId => appId === itemId))
+ return true;
+ }
+
+ return false;
+ }
+
_refilterApps() {
let filteredApps = this._orderedItems.filter(icon => !icon.visible);
@@ -551,6 +542,7 @@ class AppDisplay extends BaseAppView {
});
}
+ icon.visible = !this._isItemInFolder(appId);
appIcons.push(icon);
});
@@ -596,7 +588,7 @@ class AppDisplay extends BaseAppView {
}
goToPage(pageNumber, animate = true) {
- pageNumber = Math.clamp(pageNumber, 0, this._grid.nPages() - 1);
+ pageNumber = Math.clamp(pageNumber, 0, this._grid.nPages - 1);
if (this._grid.currentPage === pageNumber &&
this._displayingDialog &&
@@ -605,23 +597,7 @@ class AppDisplay extends BaseAppView {
if (this._displayingDialog && this._currentDialog)
this._currentDialog.popdown();
- if (!this.mapped) {
- this._adjustment.value = this._grid.getPageY(pageNumber);
- this._pageIndicators.setCurrentPosition(pageNumber);
- this._grid.currentPage = pageNumber;
- return;
- }
-
- if (this._grid.currentPage === pageNumber)
- return;
-
- this._grid.currentPage = pageNumber;
-
- // Animate the change between pages.
- this._adjustment.ease(this._grid.getPageY(this._grid.currentPage), {
- mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
- duration: animate ? PAGE_SWITCH_TIME : 0,
- });
+ this._grid.goToPage(pageNumber, animate);
}
_onScroll(actor, event) {
@@ -661,7 +637,7 @@ class AppDisplay extends BaseAppView {
adjustment.remove_transition('value');
let progress = adjustment.value / adjustment.page_size;
- let points = Array.from({ length: this._grid.nPages() }, (v, i) => i);
+ let points = Array.from({ length: this._grid.nPages }, (v, i) => i);
tracker.confirmSwipe(this._scrollView.height,
points, progress, Math.round(progress));
@@ -738,21 +714,15 @@ class AppDisplay extends BaseAppView {
box = this._grid.get_theme_node().get_content_box(box);
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
- let oldNPages = this._grid.nPages();
+ let oldNPages = this._grid.nPages;
this._grid.adaptToSize(availWidth, availHeight);
- let fadeOffset = Math.min(this._grid.topPadding,
- this._grid.bottomPadding);
- this._scrollView.update_fade_effect(fadeOffset, 0);
- if (fadeOffset > 0)
- this._scrollView.get_effect('fade').fade_edges = true;
-
- if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages !=
this._grid.nPages()) {
+ if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages !=
this._grid.nPages) {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
this._adjustment.value = 0;
this._grid.currentPage = 0;
- this._pageIndicators.setNPages(this._grid.nPages());
+ this._pageIndicators.setNPages(this._grid.nPages);
this._pageIndicators.setCurrentPosition(0);
return GLib.SOURCE_REMOVE;
});
@@ -1006,8 +976,6 @@ class FolderView extends BaseAppView {
layout_manager: new Clutter.BinLayout(),
x_expand: true,
y_expand: true,
- }, {
- minRows: 3,
});
// If it not expand, the parent doesn't take into account its preferred_width when allocating
@@ -1098,20 +1066,6 @@ class FolderView extends BaseAppView {
this._parentAvailableHeight = height;
this._grid.adaptToSize(width, height);
-
- // To avoid the fade effect being applied to the unscrolled grid,
- // the offset would need to be applied after adjusting the padding;
- // however the final padding is expected to be too small for the
- // effect to look good, so use the unadjusted padding
- let fadeOffset = Math.min(this._grid.topPadding,
- this._grid.bottomPadding);
- this._scrollView.update_fade_effect(fadeOffset, 0);
-
- // Set extra padding to avoid popup or close button being cut off
- this._grid.topPadding = Math.max(this._grid.topPadding, 0);
- this._grid.bottomPadding = Math.max(this._grid.bottomPadding, 0);
- this._grid.leftPadding = Math.max(this._grid.leftPadding, 0);
- this._grid.rightPadding = Math.max(this._grid.rightPadding, 0);
}
_loadApps() {
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index a746850cd8..6a5b1dbbb9 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -1,22 +1,20 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported BaseIcon, IconGrid */
+/* exported BaseIcon, IconGrid, IconGridLayout */
-const { Clutter, GLib, GObject, Graphene, Meta, St } = imports.gi;
+const { Clutter, GLib, GObject, Meta, St } = imports.gi;
const Params = imports.misc.params;
const Main = imports.ui.main;
var ICON_SIZE = 96;
-var MIN_ICON_SIZE = 16;
var ANIMATION_TIME_IN = 350;
var ANIMATION_TIME_OUT = 1 / 2 * ANIMATION_TIME_IN;
var ANIMATION_MAX_DELAY_FOR_ITEM = 2 / 3 * ANIMATION_TIME_IN;
-var ANIMATION_BASE_DELAY_FOR_ITEM = 1 / 4 * ANIMATION_MAX_DELAY_FOR_ITEM;
var ANIMATION_MAX_DELAY_OUT_FOR_ITEM = 2 / 3 * ANIMATION_TIME_OUT;
var ANIMATION_FADE_IN_TIME_FOR_ITEM = 1 / 4 * ANIMATION_TIME_IN;
-var ANIMATION_BOUNCE_ICON_SCALE = 1.1;
+var PAGE_SWITCH_TIME = 300;
var AnimationDirection = {
IN: 0,
@@ -24,7 +22,6 @@ var AnimationDirection = {
};
var IconSize = {
- HUGE: 128,
LARGE: 96,
MEDIUM: 64,
SMALL: 32,
@@ -1124,72 +1121,64 @@ var IconGridLayout = GObject.registerClass({
});
var IconGrid = GObject.registerClass({
- Signals: { 'animation-done': {},
- 'child-focused': { param_types: [Clutter.Actor.$gtype] } },
-}, class IconGrid extends St.Widget {
- _init(params) {
- super._init({ style_class: 'icon-grid',
- y_align: Clutter.ActorAlign.START });
-
- params = Params.parse(params, { rowLimit: null,
- columnLimit: null,
- minRows: 1,
- minColumns: 1,
- xAlign: St.Align.MIDDLE,
- padWithSpacing: false });
- this._rowLimit = params.rowLimit;
- this._colLimit = params.columnLimit;
- this._minRows = params.minRows;
- this._minColumns = params.minColumns;
- this._xAlign = params.xAlign;
- this._padWithSpacing = params.padWithSpacing;
-
- this.topPadding = 0;
- this.bottomPadding = 0;
- this.rightPadding = 0;
- this.leftPadding = 0;
-
- this._nPages = 0;
- this.currentPage = 0;
- this._rowsPerPage = 0;
- this._spaceBetweenPages = 0;
- this._childrenPerPage = 0;
+ Signals: {
+ 'pages-changed': {},
+ 'animation-done': {},
+ 'child-focused': { param_types: [Clutter.Actor.$gtype] },
+ },
+}, class IconGrid extends St.Viewport {
+ _init(layoutParams = {}) {
+ layoutParams = Params.parse(layoutParams, {
+ allow_incomplete_pages: false,
+ orientation: Clutter.Orientation.VERTICAL,
+ columns_per_page: 6,
+ rows_per_page: 4,
+ page_halign: Clutter.ActorAlign.CENTER,
+ page_valign: Clutter.ActorAlign.CENTER,
+ last_row_align: Clutter.ActorAlign.START,
+ column_spacing: 0,
+ row_spacing: 0,
+ });
+ const layoutManager = new IconGridLayout(layoutParams);
+ layoutManager.connect('pages-changed', () => this.emit('pages-changed'));
- this._updateIconSizesLaterId = 0;
+ super._init({
+ style_class: 'icon-grid',
+ layoutManager,
+ x_expand: true,
+ y_expand: true,
+ });
- this._items = [];
+ this._currentPage = 0;
this._clonesAnimating = [];
- // Pulled from CSS, but hardcode some defaults here
- this._spacing = 0;
- this._hItemSize = this._vItemSize = ICON_SIZE;
- this._fixedHItemSize = this._fixedVItemSize = undefined;
- this.connect('style-changed', this._onStyleChanged.bind(this));
this.connect('actor-added', this._childAdded.bind(this));
this.connect('actor-removed', this._childRemoved.bind(this));
- this.connect('destroy', this._onDestroy.bind(this));
}
- vfunc_unmap() {
- // Cancel animations when hiding the overview, to avoid icons
- // swarming into the void ...
- this._resetAnimationActors();
- super.vfunc_unmap();
+ _getChildrenToAnimate() {
+ const layoutManager = this.layout_manager;
+ const children = layoutManager.getItemsAtPage(this._currentPage);
+
+ return children.filter(c => c.visible);
}
- _onDestroy() {
- if (this._updateIconSizesLaterId) {
- Meta.later_remove(this._updateIconSizesLaterId);
- this._updateIconSizesLaterId = 0;
- }
+ _resetAnimationActors() {
+ this._clonesAnimating.forEach(clone => {
+ clone.source.reactive = true;
+ clone.source.opacity = 255;
+ clone.destroy();
+ });
+ this._clonesAnimating = [];
}
- _keyFocusIn(actor) {
- this.emit('child-focused', actor);
+ _animationDone() {
+ this._resetAnimationActors();
+ this.emit('animation-done');
}
_childAdded(grid, child) {
- child._iconGridKeyFocusInId = child.connect('key-focus-in', this._keyFocusIn.bind(this));
+ child._iconGridKeyFocusInId = child.connect('key-focus-in', actor => this.emit('child-focused',
actor));
child._paintVisible = child.opacity > 0;
child._opacityChangedId = child.connect('notify::opacity', () => {
@@ -1209,201 +1198,180 @@ var IconGrid = GObject.registerClass({
delete child._paintVisible;
}
- vfunc_get_preferred_width(_forHeight) {
- let nChildren = this.get_n_children();
- let nColumns = this._colLimit
- ? Math.min(this._colLimit, nChildren)
- : 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
- let minSize = this._getHItemSize() + this.leftPadding + this.rightPadding;
- let natSize = nColumns * this._getHItemSize() + totalSpacing + this.leftPadding + this.rightPadding;
-
- return this.get_theme_node().adjust_preferred_width(minSize, natSize);
+ vfunc_unmap() {
+ // Cancel animations when hiding the overview, to avoid icons
+ // swarming into the void ...
+ this._resetAnimationActors();
+ super.vfunc_unmap();
}
- _getVisibleChildren() {
- return this.get_children().filter(actor => actor.visible);
+ vfunc_style_changed() {
+ super.vfunc_style_changed();
+
+ const node = this.get_theme_node();
+ this.layout_manager.column_spacing = node.get_length('column-spacing');
+ this.layout_manager.row_spacing = node.get_length('row-spacing');
+
+ let [found, value] = node.lookup_length('max-column-spacing', false);
+ this.layout_manager.max_column_spacing = found ? value : -1;
+
+ [found, value] = node.lookup_length('max-row-spacing', false);
+ this.layout_manager.max_row_spacing = found ? value : -1;
}
- _availableHeightPerPageForItems() {
- return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding);
+ /**
+ * addItem:
+ * @param {Clutter.Actor} item: item to append to the grid
+ * @param {int} page: page number
+ * @param {int} index: position in the page
+ *
+ * Adds @item to the grid. @item must not be part of the grid.
+ *
+ * If @index exceeds the number of items per page, @item will
+ * be added to the next page.
+ *
+ * @page must be a number between 0 and the number of pages.
+ * Adding to the page after next will create a new page.
+ */
+ addItem(item, page = -1, index = -1) {
+ if (!(item.icon instanceof BaseIcon))
+ throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
+
+ this.layout_manager.addItem(item, page, index);
}
- vfunc_get_preferred_height() {
- const height = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) *
this._nPages + this._spaceBetweenPages * this._nPages;
- return [height, height];
+ /**
+ * appendItem:
+ * @param {Clutter.Actor} item: item to append to the grid
+ *
+ * Appends @item to the grid. @item must not be part of the grid.
+ */
+ appendItem(item) {
+ this.layout_manager.appendItem(item);
}
- vfunc_allocate(box) {
- if (this._childrenPerPage === 0)
- log('computePages() must be called before allocate(); pagination will not work.');
+ /**
+ * removeItem:
+ * @param {Clutter.Actor} item: item to remove from the grid
+ *
+ * Removes @item to the grid. @item must be part of the grid.
+ */
+ removeItem(item) {
+ if (!this.contains(item))
+ throw new Error(`Item ${item} is not part of the IconGrid`);
- this.set_allocation(box);
+ this.layout_manager.removeItem(item);
+ }
- let children = this._getVisibleChildren();
- let availWidth = box.x2 - box.x1;
- let spacing = this._getSpacing();
- let [nColumns, usedWidth] = this._computeLayout(availWidth);
+ /**
+ * goToPage:
+ * @param {int} pageIndex: page index
+ * @param {boolean} animate: animate the page transition
+ *
+ * Moves the current page to @pageIndex. @pageIndex must be a valid page
+ * number.
+ */
+ goToPage(pageIndex, animate = true) {
+ if (pageIndex >= this.nPages)
+ throw new Error(`IconGrid does not have page ${pageIndex}`);
- let leftEmptySpace;
- switch (this._xAlign) {
- case St.Align.START:
- leftEmptySpace = 0;
+ let newValue;
+ let adjustment;
+ switch (this.layout_manager.orientation) {
+ case Clutter.Orientation.VERTICAL:
+ adjustment = this.vadjustment;
+ newValue = pageIndex * this.layout_manager.pageHeight;
break;
- case St.Align.MIDDLE:
- leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
+ case Clutter.Orientation.HORIZONTAL:
+ adjustment = this.hadjustment;
+ newValue = pageIndex * this.layout_manager.pageWidth;
break;
- case St.Align.END:
- leftEmptySpace = availWidth - usedWidth;
}
- let x = box.x1 + leftEmptySpace + this.leftPadding;
- let y = box.y1 + this.topPadding;
- let columnIndex = 0;
+ this._currentPage = pageIndex;
- let nChangedIcons = 0;
- for (let i = 0; i < children.length; i++) {
- let childBox = this._calculateChildBox(children[i], x, y, box);
-
- if (animateIconPosition(children[i], childBox, nChangedIcons))
- nChangedIcons++;
-
- children[i].show();
-
- columnIndex++;
- if (columnIndex === nColumns)
- columnIndex = 0;
+ if (!this.mapped)
+ animate = false;
- if (columnIndex == 0) {
- y += this._getVItemSize() + spacing;
- if ((i + 1) % this._childrenPerPage === 0)
- y += this._spaceBetweenPages - spacing + this.bottomPadding + this.topPadding;
- x = box.x1 + leftEmptySpace + this.leftPadding;
- } else {
- x += this._getHItemSize() + spacing;
- }
- }
+ adjustment.ease(newValue, {
+ mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+ duration: animate ? PAGE_SWITCH_TIME : 0,
+ });
}
- vfunc_get_paint_volume(paintVolume) {
- // Setting the paint volume does not make sense when we don't have
- // any allocation
- if (!this.has_allocation())
- return false;
-
- let themeNode = this.get_theme_node();
- let allocationBox = this.get_allocation_box();
- let paintBox = themeNode.get_paint_box(allocationBox);
-
- let origin = new Graphene.Point3D();
- origin.x = paintBox.x1 - allocationBox.x1;
- origin.y = paintBox.y1 - allocationBox.y1;
- origin.z = 0.0;
-
- paintVolume.set_origin(origin);
- paintVolume.set_width(paintBox.x2 - paintBox.x1);
- paintVolume.set_height(paintBox.y2 - paintBox.y1);
-
- if (this.get_clip_to_allocation())
- return true;
-
- for (let child = this.get_first_child();
- child != null;
- child = child.get_next_sibling()) {
-
- if (!child.visible || !child.opacity)
- continue;
+ /**
+ * getItemPage:
+ * @param {BaseIcon} item: the item
+ *
+ * Retrieves the page @item is in, or -1 if @item is not part of the grid.
+ *
+ * @returns {int} the page where @item is in
+ */
+ getItemPage(item) {
+ return this.layout_manager.getItemPage(item);
+ }
- let childVolume = child.get_transformed_paint_volume(this);
- if (!childVolume)
- return false;
+ /**
+ * getItemPosition:
+ * @param {BaseIcon} item: the item
+ *
+ * Retrieves the position of @item is its page, or -1 if @item is not
+ * part of the grid.
+ *
+ * @returns {[int, int]} the page and position of @item
+ */
+ getItemPosition(item) {
+ if (!this.contains(item))
+ return [-1, -1];
- paintVolume.union(childVolume);
- }
+ const layoutManager = this.layout_manager;
+ return layoutManager.getItemPosition(item);
+ }
- return true;
+ /**
+ * getItemAt:
+ * @param {int} page: the page
+ * @param {int} position: the position in page
+ *
+ * Retrieves the item at @page and @position.
+ *
+ * @returns {BaseItem} the item at @page and @position, or null
+ */
+ getItemAt(page, position) {
+ const layoutManager = this.layout_manager;
+ return layoutManager.getItemAt(page, position);
}
- /*
- * Intended to be override by subclasses if they need a different
- * set of items to be animated.
+ /**
+ * getItemsAtPage:
+ * @param {int} page: the page index
+ *
+ * Retrieves the children at page @page, including invisible children.
+ *
+ * @returns {Array} an array of {Clutter.Actor}s
*/
- _getChildrenToAnimate() {
- const children = this._getVisibleChildren().filter(child => child.opacity > 0);
- const firstIndex = this._childrenPerPage * this.currentPage;
- const lastIndex = firstIndex + this._childrenPerPage;
+ getItemsAtPage(page) {
+ if (page < 0 || page > this.nPages)
+ throw new Error(`Page ${page} does not exist at IconGrid`);
- return children.slice(firstIndex, lastIndex);
+ const layoutManager = this.layout_manager;
+ return layoutManager.getItemsAtPage(page);
}
- _resetAnimationActors() {
- this._clonesAnimating.forEach(clone => {
- clone.source.reactive = true;
- clone.source.opacity = 255;
- clone.destroy();
- });
- this._clonesAnimating = [];
+ get currentPage() {
+ return this._currentPage;
}
- _animationDone() {
- this._resetAnimationActors();
- this.emit('animation-done');
+ set currentPage(v) {
+ this.goToPage(v);
}
- animatePulse(animationDirection) {
- if (animationDirection != AnimationDirection.IN) {
- throw new GObject.NotImplementedError("Pulse animation only implements " +
- "'in' animation direction");
- }
-
- this._resetAnimationActors();
-
- let actors = this._getChildrenToAnimate();
- if (actors.length == 0) {
- this._animationDone();
- return;
- }
+ get nPages() {
+ return this.layout_manager.nPages;
+ }
- // For few items the animation can be slow, so use a smaller
- // delay when there are less than 4 items
- // (ANIMATION_BASE_DELAY_FOR_ITEM = 1/4 *
- // ANIMATION_MAX_DELAY_FOR_ITEM)
- let maxDelay = Math.min(ANIMATION_BASE_DELAY_FOR_ITEM * actors.length,
- ANIMATION_MAX_DELAY_FOR_ITEM);
-
- for (let index = 0; index < actors.length; index++) {
- let actor = actors[index];
- actor.set_scale(0, 0);
- actor.set_pivot_point(0.5, 0.5);
-
- let delay = index / actors.length * maxDelay;
- let bounceUpTime = ANIMATION_TIME_IN / 4;
- let isLastItem = index == actors.length - 1;
- actor.ease({
- scale_x: ANIMATION_BOUNCE_ICON_SCALE,
- scale_y: ANIMATION_BOUNCE_ICON_SCALE,
- duration: bounceUpTime,
- mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
- delay,
- onComplete: () => {
- let duration = ANIMATION_TIME_IN - bounceUpTime;
- actor.ease({
- scale_x: 1,
- scale_y: 1,
- duration,
- mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
- onComplete: () => {
- if (isLastItem)
- this._animationDone();
- actor.reactive = true;
- },
- });
- },
- });
- }
+ adaptToSize(width, height) {
+ this.layout_manager.adaptToSize(width, height);
}
animateSpring(animationDirection, sourceActor) {
@@ -1450,11 +1418,11 @@ var IconGrid = GObject.registerClass({
*/
this._clonesAnimating.forEach(actorClone => {
- let actor = actorClone.source;
+ const actor = actorClone.source;
actor.opacity = 0;
actor.reactive = false;
- let [width, height] = this._getAllocatedChildSizeAndSpacing(actor);
+ let [width, height] = actor.get_size();
actorClone.set_size(width, height);
let scaleX = sourceScaledWidth / width;
let scaleY = sourceScaledHeight / height;
@@ -1523,238 +1491,8 @@ var IconGrid = GObject.registerClass({
});
}
- _getAllocatedChildSizeAndSpacing(child) {
- let [,, natWidth, natHeight] = child.get_preferred_size();
- let width = Math.min(this._getHItemSize(), natWidth);
- let xSpacing = Math.max(0, width - natWidth) / 2;
- let height = Math.min(this._getVItemSize(), natHeight);
- let ySpacing = Math.max(0, height - natHeight) / 2;
- return [width, height, xSpacing, ySpacing];
- }
-
- _calculateChildBox(child, x, y, box) {
- /* Center the item in its allocation horizontally */
- let [width, height, childXSpacing, childYSpacing] =
- this._getAllocatedChildSizeAndSpacing(child);
-
- 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;
- }
-
- columnsForWidth(rowWidth) {
- return this._computeLayout(rowWidth)[0];
- }
-
- getRowLimit() {
- return this._rowLimit;
- }
-
- _computeLayout(forWidth) {
- this.ensure_style();
-
- let nColumns = 0;
- let usedWidth = this.leftPadding + this.rightPadding;
- let spacing = this._getSpacing();
-
- while ((this._colLimit == null || nColumns < this._colLimit) &&
- (usedWidth + this._getHItemSize() <= forWidth)) {
- usedWidth += this._getHItemSize() + spacing;
- nColumns += 1;
- }
-
- if (nColumns > 0)
- usedWidth -= spacing;
-
- return [nColumns, usedWidth];
- }
-
- _onStyleChanged() {
- let themeNode = this.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.queue_relayout();
- }
-
- nRows(forWidth) {
- let children = this._getVisibleChildren();
- let nColumns = forWidth < 0 ? children.length : this._computeLayout(forWidth)[0];
- let nRows = nColumns > 0 ? Math.ceil(children.length / nColumns) : 0;
- if (this._rowLimit)
- nRows = Math.min(nRows, this._rowLimit);
- return nRows;
- }
-
- rowsForHeight(forHeight) {
- return Math.floor((forHeight - (this.topPadding + this.bottomPadding) + this._getSpacing()) /
(this._getVItemSize() + this._getSpacing()));
- }
-
- usedHeightForNRows(nRows) {
- return (this._getVItemSize() + this._getSpacing()) * nRows - this._getSpacing() + this.topPadding +
this.bottomPadding;
- }
-
- usedWidth(forWidth) {
- return this.usedWidthForNColumns(this.columnsForWidth(forWidth));
- }
-
- usedWidthForNColumns(columns) {
- let usedWidth = columns * (this._getHItemSize() + this._getSpacing());
- usedWidth -= this._getSpacing();
- return usedWidth + this.leftPadding + this.rightPadding;
- }
-
- addItem(item, index) {
- if (!(item.icon instanceof BaseIcon))
- throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
-
- this._items.push(item);
- if (index !== undefined)
- this.insert_child_at_index(item, index);
- else
- this.add_actor(item);
- }
-
- removeItem(item) {
- this.remove_child(item);
- }
-
- setSpacing(spacing) {
- this._fixedSpacing = spacing;
- }
-
- _getSpacing() {
- return this._fixedSpacing ? this._fixedSpacing : this._spacing;
- }
-
- _getHItemSize() {
- return this._fixedHItemSize ? this._fixedHItemSize : this._hItemSize;
- }
-
- _getVItemSize() {
- return this._fixedVItemSize ? this._fixedVItemSize : this._vItemSize;
- }
-
- _updateSpacingForSize(availWidth, availHeight) {
- let maxEmptyVArea = availHeight - this._minRows * this._getVItemSize();
- let maxEmptyHArea = availWidth - this._minColumns * this._getHItemSize();
- let maxHSpacing, maxVSpacing;
-
- if (this._padWithSpacing) {
- // 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
- maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows + 1));
- maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns + 1));
- } else {
- if (this._minRows <= 1)
- maxVSpacing = maxEmptyVArea;
- else
- maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows - 1));
-
- if (this._minColumns <= 1)
- maxHSpacing = maxEmptyHArea;
- else
- maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns - 1));
- }
-
- let maxSpacing = Math.min(maxHSpacing, maxVSpacing);
- // Limit spacing to the item size
- maxSpacing = Math.min(maxSpacing, Math.min(this._getVItemSize(), this._getHItemSize()));
- // The minimum spacing, regardless of whether it satisfies the row/columng minima,
- // is the spacing we get from CSS.
- let spacing = Math.max(this._spacing, maxSpacing);
- this.setSpacing(spacing);
- if (this._padWithSpacing)
- this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing;
- }
-
- _computePages(availWidthPerPage, availHeightPerPage) {
- const [nColumns] = this._computeLayout(availWidthPerPage);
- const children = this._getVisibleChildren();
- let nRows;
- if (nColumns > 0)
- nRows = Math.ceil(children.length / nColumns);
- else
- nRows = 0;
- if (this._rowLimit)
- nRows = Math.min(nRows, this._rowLimit);
-
- // We want to contain the grid inside the parent box with padding
- this._rowsPerPage = this.rowsForHeight(availHeightPerPage);
- this._nPages = Math.ceil(nRows / this._rowsPerPage);
- this._spaceBetweenPages = availHeightPerPage - (this.topPadding + this.bottomPadding) -
this._availableHeightPerPageForItems();
- this._childrenPerPage = nColumns * this._rowsPerPage;
- }
-
- /*
- * This function must to be called before iconGrid allocation,
- * to know how much spacing can the grid has
- */
- adaptToSize(availWidth, availHeight) {
- this._fixedHItemSize = this._hItemSize;
- this._fixedVItemSize = this._vItemSize;
- this._updateSpacingForSize(availWidth, availHeight);
-
- if (this.columnsForWidth(availWidth) < this._minColumns || this.rowsForHeight(availHeight) <
this._minRows) {
- let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth;
- let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight;
-
- let neededSpacePerItem = neededWidth > neededHeight
- ? Math.ceil(neededWidth / this._minColumns)
- : Math.ceil(neededHeight / this._minRows);
- this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE);
- this._fixedVItemSize = Math.max(this._vItemSize - neededSpacePerItem, MIN_ICON_SIZE);
-
- this._updateSpacingForSize(availWidth, availHeight);
- }
- if (!this._updateIconSizesLaterId) {
- this._updateIconSizesLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
- this._updateIconSizes.bind(this));
- }
- this._computePages(availWidth, availHeight);
- }
-
- // Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
- _updateIconSizes() {
- this._updateIconSizesLaterId = 0;
- let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize,
this._vItemSize);
- let newIconSize = Math.floor(ICON_SIZE * scale);
- for (let i in this._items)
- this._items[i].icon.setIconSize(newIconSize);
-
- return GLib.SOURCE_REMOVE;
- }
-
- nPages() {
- return this._nPages;
- }
-
- getPageHeight() {
- return this._availableHeightPerPageForItems();
- }
-
- getPageY(pageNumber) {
- if (!this._nPages)
- return 0;
-
- let firstPageItem = pageNumber * this._childrenPerPage;
- let childBox = this._getVisibleChildren()[firstPageItem].get_allocation_box();
- return childBox.y1 - this.topPadding;
- }
-
- getItemPage(item) {
- let children = this._getVisibleChildren();
- let index = children.indexOf(item);
- if (index == -1)
- throw new Error('Item not found.');
- return Math.floor(index / this._childrenPerPage);
+ get itemsPerPage() {
+ const layoutManager = this.layout_manager;
+ return layoutManager.rows_per_page * layoutManager.columns_per_page;
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]