iconGrid: Use IconGridLayout
Replace the current grid code with IconGridLayout. https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1271
This commit is contained in:
parent
3555550d5e
commit
4e05bcd3b6
@ -1,17 +1,13 @@
|
|||||||
/* App Grid */
|
/* App Grid */
|
||||||
|
|
||||||
$app_icon_size: 96px;
|
$app_icon_size: 96px;
|
||||||
$app_icon_padding: 24px;
|
|
||||||
|
|
||||||
// app icons
|
// app icons
|
||||||
.icon-grid {
|
.icon-grid {
|
||||||
-shell-grid-horizontal-item-size: $app_icon_size + $app_icon_padding * 2;
|
row-spacing: $base_spacing * 6;
|
||||||
-shell-grid-vertical-item-size: $app_icon_size + $app_icon_padding * 2;
|
column-spacing: $base_spacing * 6;
|
||||||
spacing: $base_spacing * 6;
|
max-row-spacing: $base_spacing * 12;
|
||||||
|
max-column-spacing: $base_spacing * 12;
|
||||||
.overview-icon {
|
|
||||||
icon-size: $app_icon_size;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* App Icons */
|
/* App Icons */
|
||||||
|
@ -20,16 +20,12 @@ const Util = imports.misc.util;
|
|||||||
const SystemActions = imports.misc.systemActions;
|
const SystemActions = imports.misc.systemActions;
|
||||||
|
|
||||||
var MENU_POPUP_TIMEOUT = 600;
|
var MENU_POPUP_TIMEOUT = 600;
|
||||||
var MAX_COLUMNS = 6;
|
|
||||||
var MIN_COLUMNS = 4;
|
|
||||||
var MIN_ROWS = 4;
|
|
||||||
|
|
||||||
var FOLDER_SUBICON_FRACTION = .4;
|
var FOLDER_SUBICON_FRACTION = .4;
|
||||||
|
|
||||||
var VIEWS_SWITCH_TIME = 400;
|
var VIEWS_SWITCH_TIME = 400;
|
||||||
var VIEWS_SWITCH_ANIMATION_DELAY = 100;
|
var VIEWS_SWITCH_ANIMATION_DELAY = 100;
|
||||||
|
|
||||||
var PAGE_SWITCH_TIME = 250;
|
|
||||||
var SCROLL_TIMEOUT_TIME = 150;
|
var SCROLL_TIMEOUT_TIME = 150;
|
||||||
|
|
||||||
var APP_ICON_SCALE_IN_TIME = 500;
|
var APP_ICON_SCALE_IN_TIME = 500;
|
||||||
@ -121,17 +117,10 @@ var BaseAppView = GObject.registerClass({
|
|||||||
'view-loaded': {},
|
'view-loaded': {},
|
||||||
},
|
},
|
||||||
}, class BaseAppView extends St.Widget {
|
}, class BaseAppView extends St.Widget {
|
||||||
_init(params = {}, gridParams) {
|
_init(params = {}) {
|
||||||
super._init(params);
|
super._init(params);
|
||||||
|
|
||||||
gridParams = Params.parse(gridParams, {
|
this._grid = new IconGrid.IconGrid();
|
||||||
columnLimit: MAX_COLUMNS,
|
|
||||||
minRows: MIN_ROWS,
|
|
||||||
minColumns: MIN_COLUMNS,
|
|
||||||
padWithSpacing: true,
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
this._grid = new IconGrid.IconGrid(gridParams);
|
|
||||||
this._grid.connect('child-focused', (grid, actor) => {
|
this._grid.connect('child-focused', (grid, actor) => {
|
||||||
this._childFocused(actor);
|
this._childFocused(actor);
|
||||||
});
|
});
|
||||||
@ -181,7 +170,7 @@ var BaseAppView = GObject.registerClass({
|
|||||||
let iconIndex = newApps.indexOf(icon);
|
let iconIndex = newApps.indexOf(icon);
|
||||||
|
|
||||||
this._orderedItems.splice(iconIndex, 0, icon);
|
this._orderedItems.splice(iconIndex, 0, icon);
|
||||||
this._grid.addItem(icon, iconIndex);
|
this._grid.addItem(icon);
|
||||||
this._items.set(icon.id, icon);
|
this._items.set(icon.id, icon);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -331,19 +320,13 @@ class AppDisplay extends BaseAppView {
|
|||||||
});
|
});
|
||||||
this.add_actor(this._stack);
|
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({
|
this._scrollView = new St.ScrollView({
|
||||||
style_class: 'all-apps',
|
style_class: 'all-apps',
|
||||||
x_expand: true,
|
x_expand: true,
|
||||||
y_expand: true,
|
y_expand: true,
|
||||||
reactive: true,
|
reactive: true,
|
||||||
});
|
});
|
||||||
this._scrollView.add_actor(box);
|
this._scrollView.add_actor(this._grid);
|
||||||
this._stack.add_actor(this._scrollView);
|
this._stack.add_actor(this._scrollView);
|
||||||
|
|
||||||
this._scrollView.set_policy(St.PolicyType.NEVER,
|
this._scrollView.set_policy(St.PolicyType.NEVER,
|
||||||
@ -365,8 +348,6 @@ class AppDisplay extends BaseAppView {
|
|||||||
|
|
||||||
this._folderIcons = [];
|
this._folderIcons = [];
|
||||||
|
|
||||||
this._grid.currentPage = 0;
|
|
||||||
|
|
||||||
this._scrollView.connect('scroll-event', this._onScroll.bind(this));
|
this._scrollView.connect('scroll-event', this._onScroll.bind(this));
|
||||||
|
|
||||||
this._swipeTracker = new SwipeTracker.SwipeTracker(
|
this._swipeTracker = new SwipeTracker.SwipeTracker(
|
||||||
@ -472,10 +453,20 @@ class AppDisplay extends BaseAppView {
|
|||||||
let newIdx = Util.insertSorted(this._orderedItems, item, this._compareItems);
|
let newIdx = Util.insertSorted(this._orderedItems, item, this._compareItems);
|
||||||
|
|
||||||
this._grid.removeItem(item);
|
this._grid.removeItem(item);
|
||||||
this._grid.addItem(item, newIdx);
|
this._grid.addItem(item, -1, newIdx);
|
||||||
this.selectApp(item.id);
|
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() {
|
_refilterApps() {
|
||||||
let filteredApps = this._orderedItems.filter(icon => !icon.visible);
|
let filteredApps = this._orderedItems.filter(icon => !icon.visible);
|
||||||
|
|
||||||
@ -551,6 +542,7 @@ class AppDisplay extends BaseAppView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
icon.visible = !this._isItemInFolder(appId);
|
||||||
appIcons.push(icon);
|
appIcons.push(icon);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -596,7 +588,7 @@ class AppDisplay extends BaseAppView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
goToPage(pageNumber, animate = true) {
|
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 &&
|
if (this._grid.currentPage === pageNumber &&
|
||||||
this._displayingDialog &&
|
this._displayingDialog &&
|
||||||
@ -605,23 +597,7 @@ class AppDisplay extends BaseAppView {
|
|||||||
if (this._displayingDialog && this._currentDialog)
|
if (this._displayingDialog && this._currentDialog)
|
||||||
this._currentDialog.popdown();
|
this._currentDialog.popdown();
|
||||||
|
|
||||||
if (!this.mapped) {
|
this._grid.goToPage(pageNumber, animate);
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onScroll(actor, event) {
|
_onScroll(actor, event) {
|
||||||
@ -661,7 +637,7 @@ class AppDisplay extends BaseAppView {
|
|||||||
adjustment.remove_transition('value');
|
adjustment.remove_transition('value');
|
||||||
|
|
||||||
let progress = adjustment.value / adjustment.page_size;
|
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,
|
tracker.confirmSwipe(this._scrollView.height,
|
||||||
points, progress, Math.round(progress));
|
points, progress, Math.round(progress));
|
||||||
@ -738,21 +714,15 @@ class AppDisplay extends BaseAppView {
|
|||||||
box = this._grid.get_theme_node().get_content_box(box);
|
box = this._grid.get_theme_node().get_content_box(box);
|
||||||
let availWidth = box.x2 - box.x1;
|
let availWidth = box.x2 - box.x1;
|
||||||
let availHeight = box.y2 - box.y1;
|
let availHeight = box.y2 - box.y1;
|
||||||
let oldNPages = this._grid.nPages();
|
let oldNPages = this._grid.nPages;
|
||||||
|
|
||||||
this._grid.adaptToSize(availWidth, availHeight);
|
this._grid.adaptToSize(availWidth, availHeight);
|
||||||
|
|
||||||
let fadeOffset = Math.min(this._grid.topPadding,
|
if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages != this._grid.nPages) {
|
||||||
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()) {
|
|
||||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
||||||
this._adjustment.value = 0;
|
this._adjustment.value = 0;
|
||||||
this._grid.currentPage = 0;
|
this._grid.currentPage = 0;
|
||||||
this._pageIndicators.setNPages(this._grid.nPages());
|
this._pageIndicators.setNPages(this._grid.nPages);
|
||||||
this._pageIndicators.setCurrentPosition(0);
|
this._pageIndicators.setCurrentPosition(0);
|
||||||
return GLib.SOURCE_REMOVE;
|
return GLib.SOURCE_REMOVE;
|
||||||
});
|
});
|
||||||
@ -1006,8 +976,6 @@ class FolderView extends BaseAppView {
|
|||||||
layout_manager: new Clutter.BinLayout(),
|
layout_manager: new Clutter.BinLayout(),
|
||||||
x_expand: true,
|
x_expand: true,
|
||||||
y_expand: true,
|
y_expand: true,
|
||||||
}, {
|
|
||||||
minRows: 3,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// If it not expand, the parent doesn't take into account its preferred_width when allocating
|
// 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._parentAvailableHeight = height;
|
||||||
|
|
||||||
this._grid.adaptToSize(width, 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() {
|
_loadApps() {
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- 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 Params = imports.misc.params;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
var ICON_SIZE = 96;
|
var ICON_SIZE = 96;
|
||||||
var MIN_ICON_SIZE = 16;
|
|
||||||
|
|
||||||
var ANIMATION_TIME_IN = 350;
|
var ANIMATION_TIME_IN = 350;
|
||||||
var ANIMATION_TIME_OUT = 1 / 2 * ANIMATION_TIME_IN;
|
var ANIMATION_TIME_OUT = 1 / 2 * ANIMATION_TIME_IN;
|
||||||
var ANIMATION_MAX_DELAY_FOR_ITEM = 2 / 3 * 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_MAX_DELAY_OUT_FOR_ITEM = 2 / 3 * ANIMATION_TIME_OUT;
|
||||||
var ANIMATION_FADE_IN_TIME_FOR_ITEM = 1 / 4 * ANIMATION_TIME_IN;
|
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 = {
|
var AnimationDirection = {
|
||||||
IN: 0,
|
IN: 0,
|
||||||
@ -24,7 +22,6 @@ var AnimationDirection = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var IconSize = {
|
var IconSize = {
|
||||||
HUGE: 128,
|
|
||||||
LARGE: 96,
|
LARGE: 96,
|
||||||
MEDIUM: 64,
|
MEDIUM: 64,
|
||||||
SMALL: 32,
|
SMALL: 32,
|
||||||
@ -1124,72 +1121,64 @@ var IconGridLayout = GObject.registerClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
var IconGrid = GObject.registerClass({
|
var IconGrid = GObject.registerClass({
|
||||||
Signals: { 'animation-done': {},
|
Signals: {
|
||||||
'child-focused': { param_types: [Clutter.Actor.$gtype] } },
|
'pages-changed': {},
|
||||||
}, class IconGrid extends St.Widget {
|
'animation-done': {},
|
||||||
_init(params) {
|
'child-focused': { param_types: [Clutter.Actor.$gtype] },
|
||||||
super._init({ style_class: 'icon-grid',
|
},
|
||||||
y_align: Clutter.ActorAlign.START });
|
}, 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'));
|
||||||
|
|
||||||
params = Params.parse(params, { rowLimit: null,
|
super._init({
|
||||||
columnLimit: null,
|
style_class: 'icon-grid',
|
||||||
minRows: 1,
|
layoutManager,
|
||||||
minColumns: 1,
|
x_expand: true,
|
||||||
xAlign: St.Align.MIDDLE,
|
y_expand: true,
|
||||||
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._currentPage = 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;
|
|
||||||
|
|
||||||
this._updateIconSizesLaterId = 0;
|
|
||||||
|
|
||||||
this._items = [];
|
|
||||||
this._clonesAnimating = [];
|
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-added', this._childAdded.bind(this));
|
||||||
this.connect('actor-removed', this._childRemoved.bind(this));
|
this.connect('actor-removed', this._childRemoved.bind(this));
|
||||||
this.connect('destroy', this._onDestroy.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vfunc_unmap() {
|
_getChildrenToAnimate() {
|
||||||
// Cancel animations when hiding the overview, to avoid icons
|
const layoutManager = this.layout_manager;
|
||||||
// swarming into the void ...
|
const children = layoutManager.getItemsAtPage(this._currentPage);
|
||||||
|
|
||||||
|
return children.filter(c => c.visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetAnimationActors() {
|
||||||
|
this._clonesAnimating.forEach(clone => {
|
||||||
|
clone.source.reactive = true;
|
||||||
|
clone.source.opacity = 255;
|
||||||
|
clone.destroy();
|
||||||
|
});
|
||||||
|
this._clonesAnimating = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_animationDone() {
|
||||||
this._resetAnimationActors();
|
this._resetAnimationActors();
|
||||||
super.vfunc_unmap();
|
this.emit('animation-done');
|
||||||
}
|
|
||||||
|
|
||||||
_onDestroy() {
|
|
||||||
if (this._updateIconSizesLaterId) {
|
|
||||||
Meta.later_remove(this._updateIconSizesLaterId);
|
|
||||||
this._updateIconSizesLaterId = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_keyFocusIn(actor) {
|
|
||||||
this.emit('child-focused', actor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_childAdded(grid, child) {
|
_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._paintVisible = child.opacity > 0;
|
||||||
child._opacityChangedId = child.connect('notify::opacity', () => {
|
child._opacityChangedId = child.connect('notify::opacity', () => {
|
||||||
@ -1209,201 +1198,180 @@ var IconGrid = GObject.registerClass({
|
|||||||
delete child._paintVisible;
|
delete child._paintVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
vfunc_get_preferred_width(_forHeight) {
|
vfunc_unmap() {
|
||||||
let nChildren = this.get_n_children();
|
// Cancel animations when hiding the overview, to avoid icons
|
||||||
let nColumns = this._colLimit
|
// swarming into the void ...
|
||||||
? Math.min(this._colLimit, nChildren)
|
this._resetAnimationActors();
|
||||||
: nChildren;
|
super.vfunc_unmap();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getVisibleChildren() {
|
vfunc_style_changed() {
|
||||||
return this.get_children().filter(actor => actor.visible);
|
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
|
||||||
vfunc_get_preferred_height() {
|
* @param {int} index: position in the page
|
||||||
const height = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
|
*
|
||||||
return [height, height];
|
* Adds @item to the grid. @item must not be part of the grid.
|
||||||
}
|
*
|
||||||
|
* If @index exceeds the number of items per page, @item will
|
||||||
vfunc_allocate(box) {
|
* be added to the next page.
|
||||||
if (this._childrenPerPage === 0)
|
*
|
||||||
log('computePages() must be called before allocate(); pagination will not work.');
|
* @page must be a number between 0 and the number of pages.
|
||||||
|
* Adding to the page after next will create a new page.
|
||||||
this.set_allocation(box);
|
|
||||||
|
|
||||||
let children = this._getVisibleChildren();
|
|
||||||
let availWidth = box.x2 - box.x1;
|
|
||||||
let spacing = this._getSpacing();
|
|
||||||
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
|
||||||
|
|
||||||
let leftEmptySpace;
|
|
||||||
switch (this._xAlign) {
|
|
||||||
case St.Align.START:
|
|
||||||
leftEmptySpace = 0;
|
|
||||||
break;
|
|
||||||
case St.Align.MIDDLE:
|
|
||||||
leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
|
|
||||||
break;
|
|
||||||
case St.Align.END:
|
|
||||||
leftEmptySpace = availWidth - usedWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
let x = box.x1 + leftEmptySpace + this.leftPadding;
|
|
||||||
let y = box.y1 + this.topPadding;
|
|
||||||
let columnIndex = 0;
|
|
||||||
|
|
||||||
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 (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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
let childVolume = child.get_transformed_paint_volume(this);
|
|
||||||
if (!childVolume)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
paintVolume.union(childVolume);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Intended to be override by subclasses if they need a different
|
|
||||||
* set of items to be animated.
|
|
||||||
*/
|
*/
|
||||||
_getChildrenToAnimate() {
|
addItem(item, page = -1, index = -1) {
|
||||||
const children = this._getVisibleChildren().filter(child => child.opacity > 0);
|
if (!(item.icon instanceof BaseIcon))
|
||||||
const firstIndex = this._childrenPerPage * this.currentPage;
|
throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
|
||||||
const lastIndex = firstIndex + this._childrenPerPage;
|
|
||||||
|
|
||||||
return children.slice(firstIndex, lastIndex);
|
this.layout_manager.addItem(item, page, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
_resetAnimationActors() {
|
/**
|
||||||
this._clonesAnimating.forEach(clone => {
|
* appendItem:
|
||||||
clone.source.reactive = true;
|
* @param {Clutter.Actor} item: item to append to the grid
|
||||||
clone.source.opacity = 255;
|
*
|
||||||
clone.destroy();
|
* Appends @item to the grid. @item must not be part of the grid.
|
||||||
});
|
*/
|
||||||
this._clonesAnimating = [];
|
appendItem(item) {
|
||||||
|
this.layout_manager.appendItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
_animationDone() {
|
/**
|
||||||
this._resetAnimationActors();
|
* removeItem:
|
||||||
this.emit('animation-done');
|
* @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.layout_manager.removeItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
animatePulse(animationDirection) {
|
/**
|
||||||
if (animationDirection != AnimationDirection.IN) {
|
* goToPage:
|
||||||
throw new GObject.NotImplementedError("Pulse animation only implements " +
|
* @param {int} pageIndex: page index
|
||||||
"'in' animation direction");
|
* @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 newValue;
|
||||||
|
let adjustment;
|
||||||
|
switch (this.layout_manager.orientation) {
|
||||||
|
case Clutter.Orientation.VERTICAL:
|
||||||
|
adjustment = this.vadjustment;
|
||||||
|
newValue = pageIndex * this.layout_manager.pageHeight;
|
||||||
|
break;
|
||||||
|
case Clutter.Orientation.HORIZONTAL:
|
||||||
|
adjustment = this.hadjustment;
|
||||||
|
newValue = pageIndex * this.layout_manager.pageWidth;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._resetAnimationActors();
|
this._currentPage = pageIndex;
|
||||||
|
|
||||||
let actors = this._getChildrenToAnimate();
|
if (!this.mapped)
|
||||||
if (actors.length == 0) {
|
animate = false;
|
||||||
this._animationDone();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For few items the animation can be slow, so use a smaller
|
adjustment.ease(newValue, {
|
||||||
// delay when there are less than 4 items
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
||||||
// (ANIMATION_BASE_DELAY_FOR_ITEM = 1/4 *
|
duration: animate ? PAGE_SWITCH_TIME : 0,
|
||||||
// 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;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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];
|
||||||
|
|
||||||
|
const layoutManager = this.layout_manager;
|
||||||
|
return layoutManager.getItemPosition(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getItemsAtPage:
|
||||||
|
* @param {int} page: the page index
|
||||||
|
*
|
||||||
|
* Retrieves the children at page @page, including invisible children.
|
||||||
|
*
|
||||||
|
* @returns {Array} an array of {Clutter.Actor}s
|
||||||
|
*/
|
||||||
|
getItemsAtPage(page) {
|
||||||
|
if (page < 0 || page > this.nPages)
|
||||||
|
throw new Error(`Page ${page} does not exist at IconGrid`);
|
||||||
|
|
||||||
|
const layoutManager = this.layout_manager;
|
||||||
|
return layoutManager.getItemsAtPage(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentPage() {
|
||||||
|
return this._currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
set currentPage(v) {
|
||||||
|
this.goToPage(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
get nPages() {
|
||||||
|
return this.layout_manager.nPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
adaptToSize(width, height) {
|
||||||
|
this.layout_manager.adaptToSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
animateSpring(animationDirection, sourceActor) {
|
animateSpring(animationDirection, sourceActor) {
|
||||||
@ -1450,11 +1418,11 @@ var IconGrid = GObject.registerClass({
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
this._clonesAnimating.forEach(actorClone => {
|
this._clonesAnimating.forEach(actorClone => {
|
||||||
let actor = actorClone.source;
|
const actor = actorClone.source;
|
||||||
actor.opacity = 0;
|
actor.opacity = 0;
|
||||||
actor.reactive = false;
|
actor.reactive = false;
|
||||||
|
|
||||||
let [width, height] = this._getAllocatedChildSizeAndSpacing(actor);
|
let [width, height] = actor.get_size();
|
||||||
actorClone.set_size(width, height);
|
actorClone.set_size(width, height);
|
||||||
let scaleX = sourceScaledWidth / width;
|
let scaleX = sourceScaledWidth / width;
|
||||||
let scaleY = sourceScaledHeight / height;
|
let scaleY = sourceScaledHeight / height;
|
||||||
@ -1523,238 +1491,8 @@ var IconGrid = GObject.registerClass({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getAllocatedChildSizeAndSpacing(child) {
|
get itemsPerPage() {
|
||||||
let [,, natWidth, natHeight] = child.get_preferred_size();
|
const layoutManager = this.layout_manager;
|
||||||
let width = Math.min(this._getHItemSize(), natWidth);
|
return layoutManager.rows_per_page * layoutManager.columns_per_page;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user