summaryrefslogtreecommitdiffstats
path: root/extensions/vertical-workspaces/lib/iconGrid.js
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/vertical-workspaces/lib/iconGrid.js')
-rw-r--r--extensions/vertical-workspaces/lib/iconGrid.js314
1 files changed, 314 insertions, 0 deletions
diff --git a/extensions/vertical-workspaces/lib/iconGrid.js b/extensions/vertical-workspaces/lib/iconGrid.js
new file mode 100644
index 0000000..1aa980e
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/iconGrid.js
@@ -0,0 +1,314 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * iconGrid.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+const { GLib, St, Meta } = imports.gi;
+const IconGrid = imports.ui.iconGrid;
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const _Util = Me.imports.lib.util;
+const shellVersion = _Util.shellVersion;
+
+// added sizes for better scaling
+const IconSize = {
+ LARGEST: 256,
+ 224: 224,
+ 208: 208,
+ 192: 192,
+ 176: 176,
+ 160: 160,
+ 144: 144,
+ 128: 128,
+ 112: 112,
+ LARGE: 96,
+ 80: 80,
+ 64: 64,
+ 48: 48,
+ TINY: 32,
+};
+
+const PAGE_WIDTH_CORRECTION = 100;
+
+let opt;
+let _overrides;
+let _firstRun = true;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('appDisplayModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+
+ if (_overrides)
+ _overrides.removeAll();
+
+
+ if (reset) {
+ _overrides = null;
+ opt = null;
+ return;
+ }
+
+ _overrides = new _Util.Overrides();
+
+ if (shellVersion < 43 && IconGridCommon._findBestModeForSize) {
+ IconGridCommon['findBestModeForSize'] = IconGridCommon._findBestModeForSize;
+ IconGridCommon['_findBestModeForSize'] = undefined;
+ }
+ _overrides.addOverride('IconGrid', IconGrid.IconGrid.prototype, IconGridCommon);
+ _overrides.addOverride('IconGridLayout', IconGrid.IconGridLayout.prototype, IconGridLayoutCommon);
+}
+// workaround - silence page -2 error on gnome 43 while cleaning app grid
+
+const IconGridCommon = {
+ getItemsAtPage(page) {
+ if (page < 0 || page >= this.nPages)
+ return [];
+ // throw new Error(`Page ${page} does not exist at IconGrid`);
+
+ const layoutManager = this.layout_manager;
+ return layoutManager.getItemsAtPage(page);
+ },
+
+ _findBestModeForSize(width, height) {
+ // this function is for main grid only, folder grid calculation is in appDisplay.AppFolderDialog class
+ if (this._currentMode > -1 || this.layoutManager._isFolder)
+ return;
+ const { pagePadding } = this.layout_manager;
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ const iconPadding = 53 * scaleFactor;
+ // provided width is usually about 100px wider in horizontal orientation with prev/next page indicators
+ const pageIndicatorCompensation = opt.ORIENTATION ? 0 : PAGE_WIDTH_CORRECTION;
+
+ width -= pagePadding.left + pagePadding.right + pageIndicatorCompensation;
+ width *= opt.APP_GRID_PAGE_WIDTH_SCALE;
+ height -= pagePadding.top + pagePadding.bottom;
+
+ // store grid max dimensions for icon size algorithm
+ this.layoutManager._gridWidth = width;
+ this.layoutManager._gridHeight = height;
+
+ const spacing = opt.APP_GRID_SPACING;
+ const iconSize = (opt.APP_GRID_ICON_SIZE > 0 ? opt.APP_GRID_ICON_SIZE : opt.APP_GRID_ICON_SIZE_DEFAULT) * scaleFactor;
+ // if this._gridModes.length === 1, custom grid should be used
+ // if (iconSize > 0 && this._gridModes.length > 1) {
+ let columns = opt.APP_GRID_COLUMNS;
+ let rows = opt.APP_GRID_ROWS;
+ // 0 means adaptive size
+ let unusedSpaceH = -1;
+ let unusedSpaceV = -1;
+ if (!columns) {
+ columns = Math.floor(width / (iconSize + iconPadding)) + 1;
+ while (unusedSpaceH < 0) {
+ columns -= 1;
+ unusedSpaceH = width - columns * (iconSize + iconPadding) - (columns - 1) * spacing;
+ }
+ }
+ if (!rows) {
+ rows = Math.floor(height / (iconSize + iconPadding)) + 1;
+ while (unusedSpaceV < 0) {
+ rows -= 1;
+ unusedSpaceV = height - rows * (iconSize + iconPadding) - (rows - 1) * spacing;
+ }
+ }
+
+ this._gridModes = [{ columns, rows }];
+ // }
+
+ this._setGridMode(0);
+ },
+};
+
+const IconGridLayoutCommon = {
+ _findBestIconSize() {
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ const nColumns = this.columnsPerPage;
+ const nRows = this.rowsPerPage;
+ const columnSpacingPerPage = opt.APP_GRID_SPACING * (nColumns - 1);
+ const rowSpacingPerPage = opt.APP_GRID_SPACING * (nRows - 1);
+ const iconPadding = 53 * scaleFactor;
+
+ const paddingH = this._isFolder ? this.pagePadding.left + this.pagePadding.right : 0;
+ const paddingV = this._isFolder ? this.pagePadding.top + this.pagePadding.bottom : 0;
+
+ const width = this._gridWidth ? this._gridWidth : this._pageWidth;
+ const height = this._gridHeight ? this._gridHeight : this._pageHeight;
+ if (!width || !height)
+ return opt.APP_GRID_ICON_SIZE_DEFAULT;
+
+ const [firstItem] = this._container;
+
+ if (this.fixedIconSize !== -1)
+ return this.fixedIconSize;
+
+ /* if (opt.APP_GRID_ADAPTIVE && !this._isFolder)
+ return opt.APP_GRID_ICON_SIZE_DEFAULT;*/
+
+ let iconSizes = Object.values(IconSize).sort((a, b) => b - a);
+
+ // limit max icon size for folders, the whole range is for the main grid with active folders
+ if (this._isFolder)
+ iconSizes = iconSizes.slice(iconSizes.indexOf(IconSize.LARGE), -1);
+
+ let sizeInvalid = false;
+ for (const size of iconSizes) {
+ let usedWidth, usedHeight;
+
+ if (firstItem) {
+ firstItem.icon.setIconSize(size);
+ const [firstItemWidth, firstItemHeight] =
+ firstItem.get_preferred_size();
+
+ const itemSize = Math.max(firstItemWidth, firstItemHeight);
+ if (itemSize < size)
+ sizeInvalid = true;
+
+ usedWidth = itemSize * nColumns;
+ usedHeight = itemSize * nRows;
+ }
+
+ if (!firstItem || sizeInvalid) {
+ usedWidth = (size + iconPadding) * nColumns;
+ usedHeight = (size + iconPadding) * nRows;
+ }
+ const emptyHSpace =
+ width - usedWidth - columnSpacingPerPage - paddingH;
+ // this.pagePadding.left - this.pagePadding.right;
+ const emptyVSpace =
+ height - usedHeight - rowSpacingPerPage - paddingV;
+ // this.pagePadding.top - this.pagePadding.bottom;
+
+ if (emptyHSpace >= 0 && emptyVSpace >= 0) {
+ return size;
+ }
+ }
+
+ return IconSize.TINY;
+ },
+
+ removeItem(item) {
+ if (!this._items.has(item)) {
+ log(`Item ${item} is not part of the IconGridLayout`);
+ return;
+ // throw new Error(`Item ${item} is not part of the IconGridLayout`);
+ }
+
+ if (!this._container)
+ return;
+
+ this._shouldEaseItems = true;
+
+ this._container.remove_child(item);
+ this._removeItemData(item);
+ },
+
+ addItem(item, page = -1, index = -1) {
+ if (this._items.has(item)) {
+ log(`iconGrid: Item ${item} already added to IconGridLayout`);
+ return;
+ // throw new Error(`Item ${item} already added to IconGridLayout`);
+ }
+
+ if (page > this._pages.length) {
+ log(`iconGrid: Cannot add ${item} to page ${page}`);
+ page = -1;
+ index = -1;
+ // throw new Error(`Cannot add ${item} to page ${page}`);
+ }
+
+ if (!this._container)
+ return;
+
+ if (page !== -1 && index === -1)
+ page = this._findBestPageToAppend(page);
+
+ this._shouldEaseItems = true;
+ this._container.add_child(item);
+ this._addItemToPage(item, page, index);
+ },
+
+ moveItem(item, newPage, newPosition) {
+ if (!this._items.has(item)) {
+ log(`iconGrid: Item ${item} is not part of the IconGridLayout`);
+ return;
+ // throw new Error(`Item ${item} is not part of the IconGridLayout`);
+ }
+
+ this._shouldEaseItems = true;
+
+ this._removeItemData(item);
+
+ if (newPage !== -1 && newPosition === -1)
+ newPage = this._findBestPageToAppend(newPage);
+ this._addItemToPage(item, newPage, newPosition);
+ },
+
+ _addItemToPage(item, pageIndex, index) {
+ // Ensure we have at least one page
+ if (this._pages.length === 0)
+ this._appendPage();
+
+ // Append a new page if necessary
+ if (pageIndex === this._pages.length)
+ this._appendPage();
+
+ if (pageIndex >= this._pages.length) {
+ pageIndex = -1;
+ index = -1;
+ }
+
+ if (pageIndex === -1)
+ pageIndex = this._pages.length - 1;
+
+ if (index === -1)
+ index = this._pages[pageIndex].children.length;
+
+ this._items.set(item, {
+ actor: item,
+ pageIndex,
+ destroyId: item.connect('destroy', () => this._removeItemData(item)),
+ visibleId: item.connect('notify::visible', () => {
+ const itemData = this._items.get(item);
+
+ this._updateVisibleChildrenForPage(itemData.pageIndex);
+
+ if (item.visible)
+ this._relocateSurplusItems(itemData.pageIndex);
+ else if (!this.allowIncompletePages)
+ this._fillItemVacancies(itemData.pageIndex);
+ }),
+ queueRelayoutId: item.connect('queue-relayout', () => {
+ this._childrenMaxSize = -1;
+ }),
+ });
+
+ item.icon.setIconSize(this._iconSize);
+ this._pages[pageIndex].children.splice(index, 0, item);
+ this._updateVisibleChildrenForPage(pageIndex);
+ this._relocateSurplusItems(pageIndex);
+ },
+
+ _findBestPageToAppend(startPage) {
+ const itemsPerPage = this.columnsPerPage * this.rowsPerPage;
+
+ for (let i = startPage; i < this._pages.length; i++) {
+ const visibleItems = this._pages[i].visibleChildren;
+
+ if (visibleItems.length < itemsPerPage)
+ return i;
+ }
+
+ return this._pages.length;
+ },
+};