summaryrefslogtreecommitdiffstats
path: root/extensions/46/vertical-workspaces/lib/appDisplay.js
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/46/vertical-workspaces/lib/appDisplay.js')
-rw-r--r--extensions/46/vertical-workspaces/lib/appDisplay.js2014
1 files changed, 0 insertions, 2014 deletions
diff --git a/extensions/46/vertical-workspaces/lib/appDisplay.js b/extensions/46/vertical-workspaces/lib/appDisplay.js
deleted file mode 100644
index d94f7df..0000000
--- a/extensions/46/vertical-workspaces/lib/appDisplay.js
+++ /dev/null
@@ -1,2014 +0,0 @@
-/**
- * V-Shell (Vertical Workspaces)
- * appDisplay.js
- *
- * @author GdH <G-dH@github.com>
- * @copyright 2022 - 2024
- * @license GPL-3.0
- *
- */
-
-'use strict';
-
-import Clutter from 'gi://Clutter';
-import Gio from 'gi://Gio';
-import GLib from 'gi://GLib';
-import GObject from 'gi://GObject';
-import Graphene from 'gi://Graphene';
-import Meta from 'gi://Meta';
-import Pango from 'gi://Pango';
-import Shell from 'gi://Shell';
-import St from 'gi://St';
-
-import * as Main from 'resource:///org/gnome/shell/ui/main.js';
-import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js';
-import * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
-import * as PageIndicators from 'resource:///org/gnome/shell/ui/pageIndicators.js';
-
-import { IconSize } from './iconGrid.js';
-
-let Me;
-let opt;
-// gettext
-let _;
-
-let _appDisplay;
-let _timeouts;
-
-const APP_ICON_TITLE_EXPAND_TIME = 200;
-const APP_ICON_TITLE_COLLAPSE_TIME = 100;
-
-const shellVersion46 = !Clutter.Container; // Container has been removed in 46
-
-function _getCategories(info) {
- let categoriesStr = info.get_categories();
- if (!categoriesStr)
- return [];
- return categoriesStr.split(';');
-}
-
-function _listsIntersect(a, b) {
- for (let itemA of a) {
- if (b.includes(itemA))
- return true;
- }
- return false;
-}
-
-export const AppDisplayModule = class {
- constructor(me) {
- Me = me;
- opt = Me.opt;
- _ = Me.gettext;
-
- _appDisplay = Main.overview._overview.controls._appDisplay;
-
- this._firstActivation = true;
- this.moduleEnabled = false;
- this._overrides = null;
-
- this._appSystemStateConId = 0;
- this._appGridLayoutConId = 0;
- this._origAppViewItemAcceptDrop = null;
- this._updateFolderIcons = 0;
- }
-
- cleanGlobals() {
- Me = null;
- opt = null;
- _ = null;
- _appDisplay = null;
- }
-
- update(reset) {
- this._removeTimeouts();
- this.moduleEnabled = opt.get('appDisplayModule');
- const conflict = false;
-
- reset = reset || !this.moduleEnabled || conflict;
-
- // don't touch the original code if module disabled
- if (reset && !this._firstActivation) {
- this._disableModule();
- this.moduleEnabled = false;
- } else if (!reset) {
- this._firstActivation = false;
- this._activateModule();
- }
- if (reset && this._firstActivation) {
- this.moduleEnabled = false;
- console.debug(' AppDisplayModule - Keeping untouched');
- }
- }
-
- _activateModule() {
- Me.Modules.iconGridModule.update();
-
- if (!this._overrides)
- this._overrides = new Me.Util.Overrides();
-
- _timeouts = {};
-
- this._applyOverrides();
- this._updateAppDisplay();
- _appDisplay.add_style_class_name('app-display-46');
-
- console.debug(' AppDisplayModule - Activated');
- }
-
- _disableModule() {
- Me.Modules.iconGridModule.update(true);
-
- if (this._overrides)
- this._overrides.removeAll();
- this._overrides = null;
-
- const reset = true;
- this._updateAppDisplay(reset);
- this._restoreOverviewGroup();
-
- _appDisplay.remove_style_class_name('app-display-46');
-
- console.debug(' AppDisplayModule - Disabled');
- }
-
- _removeTimeouts() {
- if (_timeouts) {
- Object.values(_timeouts).forEach(t => {
- if (t)
- GLib.source_remove(t);
- });
- _timeouts = null;
- }
- }
-
- _applyOverrides() {
- // Common/appDisplay
- // this._overrides.addOverride('BaseAppViewCommon', AppDisplay.BaseAppView.prototype, BaseAppViewCommon);
- // instead of overriding inaccessible BaseAppView class, we override its subclasses - AppDisplay and FolderView
- this._overrides.addOverride('BaseAppViewCommonApp', AppDisplay.AppDisplay.prototype, BaseAppViewCommon);
- this._overrides.addOverride('AppDisplay', AppDisplay.AppDisplay.prototype, AppDisplayCommon);
- this._overrides.addOverride('AppViewItem', AppDisplay.AppViewItem.prototype, AppViewItemCommon);
- this._overrides.addOverride('AppGridCommon', AppDisplay.AppGrid.prototype, AppGridCommon);
- this._overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIcon);
- if (opt.ORIENTATION) {
- this._overrides.removeOverride('AppGridLayoutHorizontal');
- this._overrides.addOverride('AppGridLayoutVertical', _appDisplay._appGridLayout, BaseAppViewGridLayoutVertical);
- } else {
- this._overrides.removeOverride('AppGridLayoutVertical');
- this._overrides.addOverride('AppGridLayoutHorizontal', _appDisplay._appGridLayout, BaseAppViewGridLayoutHorizontal);
- }
-
- // Custom folders
- this._overrides.addOverride('BaseAppViewCommonFolder', AppDisplay.FolderView.prototype, BaseAppViewCommon);
- this._overrides.addOverride('FolderView', AppDisplay.FolderView.prototype, FolderView);
- this._overrides.addOverride('AppFolderDialog', AppDisplay.AppFolderDialog.prototype, AppFolderDialog);
- this._overrides.addOverride('FolderIcon', AppDisplay.FolderIcon.prototype, FolderIcon);
-
- // Prevent changing grid page size when showing/hiding _pageIndicators
- this._overrides.addOverride('PageIndicators', PageIndicators.PageIndicators.prototype, PageIndicatorsCommon);
- }
-
- _updateAppDisplay(reset) {
- const orientation = reset ? Clutter.Orientation.HORIZONTAL : opt.ORIENTATION;
- BaseAppViewCommon._adaptForOrientation.bind(_appDisplay)(orientation);
-
- this._updateFavoritesConnection(reset);
-
- _appDisplay.visible = true;
- if (reset) {
- _appDisplay._grid.layoutManager.fixedIconSize = -1;
- _appDisplay._grid.layoutManager.allow_incomplete_pages = true;
- _appDisplay._grid._currentMode = -1;
- _appDisplay._grid.setGridModes();
- _appDisplay._grid.set_style('');
- _appDisplay._prevPageArrow.set_scale(1, 1);
- _appDisplay._nextPageArrow.set_scale(1, 1);
- if (this._appGridLayoutConId) {
- global.settings.disconnect(this._appGridLayoutConId);
- this._appGridLayoutConId = 0;
- }
- this._repopulateAppDisplay(reset);
- } else {
- _appDisplay._grid._currentMode = -1;
- // update grid on layout reset
- if (!this._appGridLayoutConId)
- this._appGridLayoutConId = global.settings.connect('changed::app-picker-layout', this._updateLayout.bind(this));
-
- // avoid resetting appDisplay before startup animation
- // x11 shell restart skips startup animation
- if (!Main.layoutManager._startingUp) {
- this._repopulateAppDisplay();
- } else if (Main.layoutManager._startingUp && Meta.is_restart()) {
- _timeouts.three = GLib.idle_add(GLib.PRIORITY_LOW, () => {
- this._repopulateAppDisplay();
- _timeouts.three = 0;
- return GLib.SOURCE_REMOVE;
- });
- }
- }
- }
-
- _updateFavoritesConnection(reset) {
- if (!reset) {
- if (!this._appSystemStateConId && opt.APP_GRID_INCLUDE_DASH >= 3) {
- this._appSystemStateConId = Shell.AppSystem.get_default().connect(
- 'app-state-changed',
- () => {
- this._updateFolderIcons = true;
- _appDisplay._redisplay();
- }
- );
- }
- } else if (this._appSystemStateConId) {
- Shell.AppSystem.get_default().disconnect(this._appSystemStateConId);
- this._appSystemStateConId = 0;
- }
- }
-
- _restoreOverviewGroup() {
- Main.overview.dash.showAppsButton.checked = false;
- Main.layoutManager.overviewGroup.opacity = 255;
- Main.layoutManager.overviewGroup.scale_x = 1;
- Main.layoutManager.overviewGroup.scale_y = 1;
- Main.layoutManager.overviewGroup.hide();
- _appDisplay.translation_x = 0;
- _appDisplay.translation_y = 0;
- _appDisplay.visible = true;
- _appDisplay.opacity = 255;
- }
-
- _updateLayout(settings, key) {
- // Reset the app grid only if the user layout has been completely removed
- if (!settings.get_value(key).deep_unpack().length) {
- this._repopulateAppDisplay();
- }
- }
-
- _repopulateAppDisplay(reset = false, callback) {
- // Remove all icons so they can be re-created with the current configuration
- // Updating appGrid content while rebasing extensions when session is locked makes no sense (relevant for GS version < 46)
- if (!Main.sessionMode.isLocked)
- AppDisplayCommon.removeAllItems.bind(_appDisplay)();
-
- // appDisplay disabled
- if (reset) {
- _appDisplay._redisplay();
- return;
- }
-
- _appDisplay._readyToRedisplay = true;
- _appDisplay._redisplay();
-
- // Setting OffscreenRedirect should improve performance when opacity transitions are used
- _appDisplay.offscreen_redirect = Clutter.OffscreenRedirect.ALWAYS;
-
- if (opt.APP_GRID_PERFORMANCE)
- this._realizeAppDisplay(callback);
- else if (callback)
- callback();
- }
-
- _realizeAppDisplay(callback) {
- // Workaround - silently realize appDisplay
- // The realization takes some time and affects animations during the first use
- // If we do it invisibly before the user needs the app grid, it can improve the user's experience
- _appDisplay.opacity = 1;
-
- this._exposeAppGrid();
- _appDisplay._redisplay();
- this._exposeAppFolders();
-
- // Let the main loop process our changes before we continue
- _timeouts.updateAppGrid = GLib.idle_add(GLib.PRIORITY_LOW, () => {
- this._restoreAppGrid();
- Me._resetInProgress = false;
-
- if (callback)
- callback();
-
- _timeouts.updateAppGrid = 0;
- return GLib.SOURCE_REMOVE;
- });
- }
-
- _exposeAppGrid() {
- const overviewGroup = Main.layoutManager.overviewGroup;
- if (!overviewGroup.visible) {
- // scale down the overviewGroup so it don't cover uiGroup
- overviewGroup.scale_y = 0.001;
- // make it invisible to the eye, but visible for the renderer
- overviewGroup.opacity = 1;
- // if overview is hidden, show it
- overviewGroup.visible = true;
- }
- }
-
- _restoreAppGrid() {
- if (opt.APP_GRID_PERFORMANCE)
- this._hideAppFolders();
-
- const overviewGroup = Main.layoutManager.overviewGroup;
- if (!Main.overview._shown)
- overviewGroup.hide();
- overviewGroup.scale_y = 1;
- overviewGroup.opacity = 255;
- _appDisplay.opacity = 0;
- _appDisplay.visible = false;
- }
-
- _exposeAppFolders() {
- _appDisplay._folderIcons.forEach(d => {
- d._ensureFolderDialog();
- d._dialog.scale_y = 0.0001;
- d._dialog.show();
- d._dialog._updateFolderSize();
- });
- }
-
- _hideAppFolders() {
- _appDisplay._folderIcons.forEach(d => {
- if (d._dialog) {
- d._dialog.hide();
- d._dialog.scale_y = 1;
- }
- });
- }
-};
-
-function _getViewFromIcon(icon) {
- icon = icon._sourceItem ? icon._sourceItem : icon;
- for (let parent = icon.get_parent(); parent; parent = parent.get_parent()) {
- if (parent instanceof AppDisplay.AppDisplay || parent instanceof AppDisplay.FolderView) {
- return parent;
- }
- }
- return null;
-}
-
-const AppDisplayCommon = {
- _ensureDefaultFolders() {
- // disable creation of default folders if user deleted them
- },
-
- removeAllItems() {
- this._orderedItems.slice().forEach(item => {
- if (item._dialog)
- Main.layoutManager.overviewGroup.remove_child(item._dialog);
-
- this._removeItem(item);
- item.destroy();
- });
- this._folderIcons = [];
- },
-
- // apps load adapted for custom sorting and including dash items
- _loadApps() {
- let appIcons = [];
- const runningApps = Shell.AppSystem.get_default().get_running().map(a => a.id);
-
- this._appInfoList = Shell.AppSystem.get_default().get_installed().filter(appInfo => {
- try {
- appInfo.get_id(); // catch invalid file encodings
- } catch (e) {
- return false;
- }
-
- const appIsRunning = runningApps.includes(appInfo.get_id());
- const appIsFavorite = this._appFavorites.isFavorite(appInfo.get_id());
- const excludeApp = (opt.APP_GRID_EXCLUDE_RUNNING && appIsRunning) || (opt.APP_GRID_EXCLUDE_FAVORITES && appIsFavorite);
-
- return this._parentalControlsManager.shouldShowApp(appInfo) && !excludeApp;
- });
-
- let apps = this._appInfoList.map(app => app.get_id());
-
- let appSys = Shell.AppSystem.get_default();
-
- const appsInsideFolders = new Set();
- this._folderIcons = [];
- if (!opt.APP_GRID_USAGE) {
- let folders = this._folderSettings.get_strv('folder-children');
- folders.forEach(id => {
- let path = `${this._folderSettings.path}folders/${id}/`;
- let icon = this._items.get(id);
- if (!icon) {
- icon = new AppDisplay.FolderIcon(id, path, this);
- icon.connect('apps-changed', () => {
- this._redisplay();
- this._savePages();
- });
- icon.connect('notify::pressed', () => {
- if (icon.pressed)
- this.updateDragFocus(icon);
- });
- } else if (this._updateFolderIcons && opt.APP_GRID_EXCLUDE_RUNNING) {
- // if any app changed its running state, update folder icon
- icon.icon.update();
- }
-
- // remove empty folder icons
- if (!icon.visible) {
- icon.destroy();
- return;
- }
-
- appIcons.push(icon);
- this._folderIcons.push(icon);
-
- icon.getAppIds().forEach(appId => appsInsideFolders.add(appId));
- });
- }
-
- // reset request to update active icon
- this._updateFolderIcons = false;
-
- // Allow dragging of the icon only if the Dash would accept a drop to
- // change favorite-apps. There are no other possible drop targets from
- // the app picker, so there's no other need for a drag to start,
- // at least on single-monitor setups.
- // This also disables drag-to-launch on multi-monitor setups,
- // but we hope that is not used much.
- const isDraggable =
- global.settings.is_writable('favorite-apps') ||
- global.settings.is_writable('app-picker-layout');
-
- apps.forEach(appId => {
- if (!opt.APP_GRID_USAGE && appsInsideFolders.has(appId))
- return;
-
- let icon = this._items.get(appId);
- if (!icon) {
- let app = appSys.lookup_app(appId);
- icon = new AppDisplay.AppIcon(app, { isDraggable });
- icon.connect('notify::pressed', () => {
- if (icon.pressed)
- this.updateDragFocus(icon);
- });
- }
-
- appIcons.push(icon);
- });
-
- // At last, if there's a placeholder available, add it
- if (this._placeholder)
- appIcons.push(this._placeholder);
-
- return appIcons;
- },
-
- _onDragBegin(overview, source) {
- // let sourceId;
- // support active preview icons
- if (source._sourceItem) {
- // sourceId = source._sourceFolder._id;
- source = source._sourceItem;
- } /* else {
- sourceId = source.id;
- }*/
- // Prevent switching page when an item on another page is selected
- // by removing the focus from all icons
- // This is an upstream bug
- // this.selectApp(sourceId);
- this.grab_key_focus();
-
- this._dragMonitor = {
- dragMotion: this._onDragMotion.bind(this),
- dragDrop: this._onDragDrop.bind(this),
- };
- DND.addDragMonitor(this._dragMonitor);
-
- this._appGridLayout.showPageIndicators();
- this._dragFocus = null;
- this._swipeTracker.enabled = false;
-
- // When dragging from a folder dialog, the dragged app icon doesn't
- // exist in AppDisplay. We work around that by adding a placeholder
- // icon that is either destroyed on cancel, or becomes the effective
- // new icon when dropped.
- if (/* AppDisplay.*/_getViewFromIcon(source) instanceof AppDisplay.FolderView ||
- (opt.APP_GRID_EXCLUDE_FAVORITES && this._appFavorites.isFavorite(source.id)))
- this._ensurePlaceholder(source);
- },
-
- _ensurePlaceholder(source) {
- if (this._placeholder)
- return;
-
- if (source._sourceItem)
- source = source._sourceItem;
-
- const appSys = Shell.AppSystem.get_default();
- const app = appSys.lookup_app(source.id);
-
- const isDraggable =
- global.settings.is_writable('favorite-apps') ||
- global.settings.is_writable('app-picker-layout');
-
- this._placeholder = new AppDisplay.AppIcon(app, { isDraggable });
- this._placeholder.connect('notify::pressed', () => {
- if (this._placeholder?.pressed)
- this.updateDragFocus(this._placeholder);
- });
- this._placeholder.scaleAndFade();
- this._redisplay();
- },
-
- // accept source from active folder preview
- acceptDrop(source) {
- if (opt.APP_GRID_USAGE)
- return false;
- if (source._sourceItem)
- source = source._sourceItem;
- if (!this._acceptDropCommon(source))
- return false;
-
- this._savePages();
-
- const view = /* AppDisplay.*/_getViewFromIcon(source);
- if (view instanceof AppDisplay.FolderView)
- view.removeApp(source.app);
-
- if (this._currentDialog)
- this._currentDialog.popdown();
-
- if (opt.APP_GRID_EXCLUDE_FAVORITES && this._appFavorites.isFavorite(source.id))
- this._appFavorites.removeFavorite(source.id);
- return true;
- },
-
- _savePages() {
- // Skip saving pages when search app grid mode is active
- // and the grid is showing search results
- if (Main.overview._overview.controls._origAppGridContent)
- return;
-
- const pages = [];
-
- for (let i = 0; i < this._grid.nPages; i++) {
- const pageItems =
- this._grid.getItemsAtPage(i).filter(c => c.visible);
- const pageData = {};
-
- pageItems.forEach((item, index) => {
- pageData[item.id] = {
- position: GLib.Variant.new_int32(index),
- };
- });
- pages.push(pageData);
- }
-
- this._pageManager.pages = pages;
- },
-};
-
-const BaseAppViewCommon = {
- after__init() {
- // Only folders can run this init
- this._isFolder = true;
-
- this._adaptForOrientation(opt.ORIENTATION, true);
-
- // Because the original class prototype is not exported, we need to inject every instance
- const overrides = new Me.Util.Overrides();
- if (opt.ORIENTATION) {
- overrides.addOverride('FolderGridLayoutVertical', this._appGridLayout, BaseAppViewGridLayoutVertical);
- this._pageIndicators.set_style('margin-right: 12px;');
- } else {
- overrides.addOverride('FolderGridLayoutHorizontal', this._appGridLayout, BaseAppViewGridLayoutHorizontal);
- this._pageIndicators.set_style('margin-bottom: 12px;');
- }
- },
-
- _adaptForOrientation(orientation, folder) {
- const vertical = !!orientation;
-
- this._grid.layoutManager.fixedIconSize = folder ? opt.APP_GRID_FOLDER_ICON_SIZE : opt.APP_GRID_ICON_SIZE;
- this._grid.layoutManager._orientation = orientation;
- this._orientation = orientation;
- this._swipeTracker.orientation = orientation;
- this._swipeTracker._reset();
-
- this._adjustment = vertical
- ? this._scrollView.get_vscroll_bar().adjustment
- : this._scrollView.get_hscroll_bar().adjustment;
-
- this._prevPageArrow.pivot_point = new Graphene.Point({ x: 0.5, y: 0.5 });
- this._prevPageArrow.rotation_angle_z = vertical ? 90 : 0;
-
- this._nextPageArrow.pivot_point = new Graphene.Point({ x: 0.5, y: 0.5 });
- this._nextPageArrow.rotation_angle_z = vertical ? 90 : 0;
-
- const pageIndicators = this._pageIndicators;
- pageIndicators.vertical = vertical;
- this._box.vertical = !vertical;
- pageIndicators.x_expand = !vertical;
- pageIndicators.y_align = vertical ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START;
- pageIndicators.x_align = vertical ? Clutter.ActorAlign.START : Clutter.ActorAlign.CENTER;
-
- this._grid.layoutManager.allow_incomplete_pages = folder ? false : opt.APP_GRID_ALLOW_INCOMPLETE_PAGES;
- const spacing = folder ? opt.APP_GRID_FOLDER_SPACING : opt.APP_GRID_SPACING;
- this._grid.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`);
-
- if (vertical) {
- this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL);
- if (!this._scrollConId) {
- this._scrollConId = this._adjustment.connect('notify::value', adj => {
- const value = adj.value / adj.page_size;
- this._pageIndicators.setCurrentPosition(value);
- });
- }
- pageIndicators.remove_style_class_name('page-indicators-horizontal');
- pageIndicators.add_style_class_name('page-indicators-vertical');
- this._prevPageIndicator.add_style_class_name('prev-page-indicator');
- this._nextPageIndicator.add_style_class_name('next-page-indicator');
- this._nextPageArrow.translationY = 0;
- this._prevPageArrow.translationY = 0;
- this._nextPageIndicator.translationX = 0;
- this._prevPageIndicator.translationX = 0;
- } else {
- this._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER);
- if (this._scrollConId) {
- this._adjustment.disconnect(this._scrollConId);
- this._scrollConId = 0;
- }
- pageIndicators.remove_style_class_name('page-indicators-vertical');
- pageIndicators.add_style_class_name('page-indicators-horizontal');
- this._prevPageIndicator.remove_style_class_name('prev-page-indicator');
- this._nextPageIndicator.remove_style_class_name('next-page-indicator');
- this._nextPageArrow.translationX = 0;
- this._prevPageArrow.translationX = 0;
- this._nextPageIndicator.translationY = 0;
- this._prevPageIndicator.translationY = 0;
- }
-
- const scale = opt.APP_GRID_SHOW_PAGE_ARROWS ? 1 : 0;
- this._prevPageArrow.set_scale(scale, scale);
- this._nextPageArrow.set_scale(scale, scale);
- },
-
- _sortItemsByName(items) {
- items.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
- },
-
- _updateItemPositions(icons, allowIncompletePages = false) {
- // Avoid recursion when relocating icons
- this._grid.layoutManager._skipRelocateSurplusItems = true;
-
- const { itemsPerPage } = this._grid;
-
- icons.slice().forEach((icon, index) => {
- const [currentPage, currentPosition] = this._grid.layoutManager.getItemPosition(icon);
-
- let page, position;
- if (allowIncompletePages) {
- [page, position] = this._getItemPosition(icon);
- } else {
- page = Math.floor(index / itemsPerPage);
- position = index % itemsPerPage;
- }
-
- if (currentPage !== page || currentPosition !== position) {
- this._moveItem(icon, page, position);
- }
- });
-
- this._grid.layoutManager._skipRelocateSurplusItems = false;
- // Disable animating the icons to their new positions
- // since it can cause glitches when the app grid search mode is active
- // and many icons are repositioning at once
- this._grid.layoutManager._shouldEaseItems = false;
- },
-
- // Adds sorting options
- _redisplay() {
- // different options for main app grid and app folders
- const thisIsFolder = this instanceof AppDisplay.FolderView;
- const thisIsAppDisplay = !thisIsFolder;
-
- // When an app was dragged from a folder and dropped to the main grid
- // folders (if exist) need to be redisplayed even if we temporary block it for the appDisplay
- this._folderIcons?.forEach(icon => {
- icon.view._redisplay();
- });
-
- // Avoid unwanted updates
- if (thisIsAppDisplay && !this._readyToRedisplay)
- return;
-
- const oldApps = this._orderedItems.slice();
- const oldAppIds = oldApps.map(icon => icon.id);
-
- const newApps = this._loadApps();
- const newAppIds = newApps.map(icon => icon.id);
-
- const addedApps = newApps.filter(icon => !oldAppIds.includes(icon.id));
- const removedApps = oldApps.filter(icon => !newAppIds.includes(icon.id));
-
- // Don't update folder without dialog if its content didn't change
- if (!addedApps.length && !removedApps.length && thisIsFolder && !this.get_parent())
- return;
-
- // Remove old app icons
- removedApps.forEach(icon => {
- this._removeItem(icon);
- icon.destroy();
- });
-
- // For the main app grid only
- let allowIncompletePages = thisIsAppDisplay && opt.APP_GRID_ALLOW_INCOMPLETE_PAGES;
-
- const customOrder = !((opt.APP_GRID_ORDER && thisIsAppDisplay) || (opt.APP_FOLDER_ORDER && thisIsFolder));
- if (!customOrder) {
- allowIncompletePages = false;
-
- // Sort by name
- this._sortItemsByName(newApps);
-
- // Sort by usage
- if ((opt.APP_GRID_USAGE && thisIsAppDisplay) ||
- (opt.APP_FOLDER_USAGE && thisIsFolder)) {
- newApps.sort((a, b) => Shell.AppUsage.get_default().compare(a.app?.id, b.app?.id));
- }
-
- // Sort favorites first
- if (!opt.APP_GRID_EXCLUDE_FAVORITES && opt.APP_GRID_DASH_FIRST) {
- const fav = Object.keys(this._appFavorites._favorites);
- newApps.sort((a, b) => {
- let aFav = fav.indexOf(a.id);
- if (aFav < 0)
- aFav = 999;
- let bFav = fav.indexOf(b.id);
- if (bFav < 0)
- bFav = 999;
- return bFav < aFav;
- });
- }
-
- // Sort running first
- if (!opt.APP_GRID_EXCLUDE_RUNNING && opt.APP_GRID_DASH_FIRST) {
- newApps.sort((a, b) => a.app?.get_state() !== Shell.AppState.RUNNING && b.app?.get_state() === Shell.AppState.RUNNING);
- }
-
- // Sort folders first
- if (thisIsAppDisplay && opt.APP_GRID_FOLDERS_FIRST)
- newApps.sort((a, b) => b._folder && !a._folder);
-
- // Sort folders last
- else if (thisIsAppDisplay && opt.APP_GRID_FOLDERS_LAST)
- newApps.sort((a, b) => a._folder && !b._folder);
- } else {
- // Sort items according to the custom order stored in pageManager
- newApps.sort(this._compareItems.bind(this));
- }
-
- // Add new app icons to the grid
- newApps.forEach(icon => {
- const [page, position] = this._grid.getItemPosition(icon);
- if (page === -1 && position === -1)
- this._addItem(icon, -1, -1);
- });
- // When a placeholder icon was added to the custom sorted grid during DND from a folder
- // update its initial position on the page
- if (customOrder)
- newApps.sort(this._compareItems.bind(this));
-
- this._orderedItems = newApps;
-
- // Update icon positions if needed
- this._updateItemPositions(this._orderedItems, allowIncompletePages);
-
- // Relocate items with invalid positions
- if (thisIsAppDisplay) {
- const nPages = this._grid.layoutManager.nPages;
- for (let pageIndex = 0; pageIndex < nPages; pageIndex++)
- this._grid.layoutManager._relocateSurplusItems(pageIndex);
- }
-
- this.emit('view-loaded');
- },
-
- _canAccept(source) {
- return source instanceof AppDisplay.AppViewItem;
- },
-
- // this method is replacing BaseAppVew.acceptDrop which can't be overridden directly
- _acceptDropCommon(source) {
- const dropTarget = this._dropTarget;
- delete this._dropTarget;
- if (!this._canAccept(source))
- return false;
-
- if (dropTarget === this._prevPageIndicator ||
- dropTarget === this._nextPageIndicator) {
- let increment;
- increment = dropTarget === this._prevPageIndicator ? -1 : 1;
- const { currentPage, nPages } = this._grid;
- const page = Math.min(currentPage + increment, nPages);
- const position = page < nPages ? -1 : 0;
-
- this._moveItem(source, page, position);
- this.goToPage(page);
- } else if (this._delayedMoveData) {
- // Dropped before the icon was moved
- const { page, position } = this._delayedMoveData;
- try {
- this._moveItem(source, page, position);
- } catch (e) {
- console.warn(`Warning:${e}`);
- }
- this._removeDelayedMove();
- }
-
- return true;
- },
-
- // support active preview icons
- _onDragMotion(dragEvent) {
- if (!(dragEvent.source instanceof AppDisplay.AppViewItem))
- return DND.DragMotionResult.CONTINUE;
-
- if (dragEvent.source._sourceItem)
- dragEvent.source = dragEvent.source._sourceItem;
-
- const appIcon = dragEvent.source;
-
- if (appIcon instanceof AppDisplay.AppViewItem) {
- if (!this._dragMaybeSwitchPageImmediately(dragEvent)) {
- // Two ways of switching pages during DND:
- // 1) When "bumping" the cursor against the monitor edge, we switch
- // page immediately.
- // 2) When hovering over the next-page indicator for a certain time,
- // we also switch page.
-
- const { targetActor } = dragEvent;
-
- if (targetActor === this._prevPageIndicator ||
- targetActor === this._nextPageIndicator)
- this._maybeSetupDragPageSwitchInitialTimeout(dragEvent);
- else
- this._resetDragPageSwitch();
- }
- }
-
- const thisIsFolder = this instanceof AppDisplay.FolderView;
- const thisIsAppDisplay = !thisIsFolder;
-
- // Prevent reorganizing the main app grid icons when an app folder is open and when sorting is not custom
- // For some reason in V-Shell the drag motion events propagate from folder to main grid, which is not a problem in default code - so test the open dialog
- if (!this._currentDialog && (!opt.APP_GRID_ORDER && thisIsAppDisplay) || (!opt.APP_FOLDER_ORDER && thisIsFolder))
- this._maybeMoveItem(dragEvent);
-
- return DND.DragMotionResult.CONTINUE;
- },
-};
-
-const BaseAppViewGridLayoutHorizontal = {
- _getIndicatorsWidth(box) {
- const [width, height] = box.get_size();
- const arrows = [
- this._nextPageArrow,
- this._previousPageArrow,
- ];
-
- let minArrowsWidth;
-
- minArrowsWidth = arrows.reduce(
- (previousWidth, accessory) => {
- const [min] = accessory.get_preferred_width(height);
- return Math.max(previousWidth, min);
- }, 0);
-
- minArrowsWidth = opt.APP_GRID_SHOW_PAGE_ARROWS ? minArrowsWidth : 0;
-
- const indicatorWidth = !this._grid._isFolder
- ? minArrowsWidth + ((width - minArrowsWidth) * (1 - opt.APP_GRID_PAGE_WIDTH_SCALE)) / 2
- : minArrowsWidth + 6;
-
- return Math.round(indicatorWidth);
- },
-
- vfunc_allocate(container, box) {
- const ltr = container.get_text_direction() !== Clutter.TextDirection.RTL;
- const indicatorsWidth = this._getIndicatorsWidth(box);
-
- const pageIndicatorsHeight = 20; // _appDisplay._pageIndicators.height is unstable, 20 is determined by the style
- const availHeight = box.get_height() - pageIndicatorsHeight;
- const vPadding = Math.round((availHeight - availHeight * opt.APP_GRID_PAGE_HEIGHT_SCALE) / 2);
- this._grid.indicatorsPadding = new Clutter.Margin({
- left: indicatorsWidth,
- right: indicatorsWidth,
- top: vPadding + pageIndicatorsHeight,
- bottom: vPadding,
- });
-
- this._scrollView.allocate(box);
-
- const leftBox = box.copy();
- leftBox.x2 = leftBox.x1 + indicatorsWidth;
-
- const rightBox = box.copy();
- rightBox.x1 = rightBox.x2 - indicatorsWidth;
-
- this._previousPageIndicator.allocate(ltr ? leftBox : rightBox);
- this._previousPageArrow.allocate_align_fill(ltr ? leftBox : rightBox,
- 0.5, 0.5, false, false);
- this._nextPageIndicator.allocate(ltr ? rightBox : leftBox);
- this._nextPageArrow.allocate_align_fill(ltr ? rightBox : leftBox,
- 0.5, 0.5, false, false);
-
- this._pageWidth = box.get_width();
-
- // Center page arrow buttons
- this._previousPageArrow.translationY = pageIndicatorsHeight / 2;
- this._nextPageArrow.translationY = pageIndicatorsHeight / 2;
- // Reset page indicators vertical position
- this._nextPageIndicator.translationY = 0;
- this._previousPageIndicator.translationY = 0;
- },
-};
-
-const BaseAppViewGridLayoutVertical = {
- _getIndicatorsHeight(box) {
- const [width, height] = box.get_size();
- const arrows = [
- this._nextPageArrow,
- this._previousPageArrow,
- ];
-
- let minArrowsHeight;
-
- minArrowsHeight = arrows.reduce(
- (previousHeight, accessory) => {
- const [min] = accessory.get_preferred_height(width);
- return Math.max(previousHeight, min);
- }, 0);
-
- minArrowsHeight = opt.APP_GRID_SHOW_PAGE_ARROWS ? minArrowsHeight : 0;
-
- const indicatorHeight = !this._grid._isFolder
- ? minArrowsHeight + ((height - minArrowsHeight) * (1 - opt.APP_GRID_PAGE_HEIGHT_SCALE)) / 2
- : minArrowsHeight + 6;
-
- return Math.round(indicatorHeight);
- },
-
- _syncPageIndicators() {
- if (!this._container)
- return;
-
- const { value } = this._pageIndicatorsAdjustment;
-
- const { top, bottom } = this._grid.indicatorsPadding;
- const topIndicatorOffset = -top * (1 - value);
- const bottomIndicatorOffset = bottom * (1 - value);
-
- this._previousPageIndicator.translationY =
- topIndicatorOffset;
- this._nextPageIndicator.translationY =
- bottomIndicatorOffset;
-
- const leftArrowOffset = -top * value;
- const rightArrowOffset = bottom * value;
-
- this._previousPageArrow.translationY =
- leftArrowOffset;
- this._nextPageArrow.translationY =
- rightArrowOffset;
-
- // Page icons
- this._translatePreviousPageIcons(value);
- this._translateNextPageIcons(value);
-
- if (this._grid.nPages > 0) {
- this._grid.getItemsAtPage(this._currentPage).forEach(icon => {
- icon.translationY = 0;
- });
- }
- },
-
- _translatePreviousPageIcons(value) {
- if (this._currentPage === 0)
- return;
-
- const pageHeight = this._grid.layoutManager._pageHeight;
- const previousPage = this._currentPage - 1;
- const icons = this._grid.getItemsAtPage(previousPage).filter(i => i.visible);
- if (icons.length === 0)
- return;
-
- const { top } = this._grid.indicatorsPadding;
- const { rowSpacing } = this._grid.layoutManager;
- const endIcon = icons[icons.length - 1];
- let iconOffset;
-
- const currentPageOffset = pageHeight * this._currentPage;
- iconOffset = currentPageOffset - endIcon.allocation.y1 - endIcon.width + top - rowSpacing;
-
- for (const icon of icons)
- icon.translationY = iconOffset * value;
- },
-
- _translateNextPageIcons(value) {
- if (this._currentPage >= this._grid.nPages - 1)
- return;
-
- const nextPage = this._currentPage + 1;
- const icons = this._grid.getItemsAtPage(nextPage).filter(i => i.visible);
- if (icons.length === 0)
- return;
-
- const { bottom } = this._grid.indicatorsPadding;
- const { rowSpacing } = this._grid.layoutManager;
- let iconOffset;
-
- const pageOffset = this._pageHeight * nextPage;
- iconOffset = pageOffset - icons[0].allocation.y1 - bottom + rowSpacing;
-
- for (const icon of icons)
- icon.translationY = iconOffset * value;
- },
-
- vfunc_allocate(container, box) {
- const indicatorsHeight = this._getIndicatorsHeight(box);
-
- const pageIndicatorsWidth = 20; // _appDisplay._pageIndicators.width is not stable, 20 is determined by the style
- const availWidth = box.get_width() - pageIndicatorsWidth;
- const hPadding = Math.round((availWidth - availWidth * opt.APP_GRID_PAGE_WIDTH_SCALE) / 2);
-
- this._grid.indicatorsPadding = new Clutter.Margin({
- top: indicatorsHeight,
- bottom: indicatorsHeight,
- left: hPadding + pageIndicatorsWidth,
- right: hPadding,
- });
-
- this._scrollView.allocate(box);
-
- const topBox = box.copy();
- topBox.y2 = topBox.y1 + indicatorsHeight;
-
- const bottomBox = box.copy();
- bottomBox.y1 = bottomBox.y2 - indicatorsHeight;
-
- this._previousPageIndicator.allocate(topBox);
- this._previousPageArrow.allocate_align_fill(topBox,
- 0.5, 0.5, false, false);
- this._nextPageIndicator.allocate(bottomBox);
- this._nextPageArrow.allocate_align_fill(bottomBox,
- 0.5, 0.5, false, false);
-
- this._pageHeight = box.get_height();
-
- // Center page arrow buttons
- this._previousPageArrow.translationX = pageIndicatorsWidth / 2;
- this._nextPageArrow.translationX = pageIndicatorsWidth / 2;
- // Reset page indicators vertical position
- this._nextPageIndicator.translationX = 0;
- this._previousPageIndicator.translationX = 0;
- },
-};
-
-const AppGridCommon = {
- _updatePadding() {
- const { rowSpacing, columnSpacing } = this.layoutManager;
-
- const padding = this._indicatorsPadding.copy();
-
- padding.left += rowSpacing;
- padding.right += rowSpacing;
- padding.top += columnSpacing;
- padding.bottom += columnSpacing;
-
- this.layoutManager.pagePadding = padding;
- },
-};
-
-const FolderIcon = {
- after__init() {
- this.button_mask = St.ButtonMask.ONE | St.ButtonMask.TWO;
- if (shellVersion46)
- this.add_style_class_name('app-folder-46');
- else
- this.add_style_class_name('app-folder-45');
- },
-
- open() {
- // Prevent switching page when an item on another page is selected
- GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
- // Select folder icon to prevent switching page to the one with currently selected icon
- this._parentView._selectAppInternal(this._id);
- // Remove key focus from the selected icon to prevent switching page after dropping the removed folder icon on another page of the main grid
- this._parentView.grab_key_focus();
- this._ensureFolderDialog();
- this._dialog.popup();
- });
- },
-
- vfunc_clicked() {
- this.open();
- },
-
- _canAccept(source) {
- if (!(source instanceof AppDisplay.AppIcon))
- return false;
-
- const view = _getViewFromIcon(source);
- if (!view /* || !(view instanceof AppDisplay.AppDisplay)*/)
- return false;
-
- // Disable this test to allow the user to cancel the current DND by dropping the icon on its original source
- /* if (this._folder.get_strv('apps').includes(source.id))
- return false;*/
-
- return true;
- },
-
- acceptDrop(source) {
- if (source._sourceItem)
- source = source._sourceItem;
-
- const accepted = AppViewItemCommon.acceptDrop.bind(this)(source);
-
- if (!accepted)
- return false;
-
- // If the icon is already in the folder (user dropped it back on the same folder), skip re-adding it
- if (this._folder.get_strv('apps').includes(source.id))
- return true;
-
- this._onDragEnd();
-
- this.view.addApp(source.app);
-
- return true;
- },
-};
-
-const FolderView = {
- _createGrid() {
- let grid = new FolderGrid();
- grid._view = this;
- return grid;
- },
-
- createFolderIcon(size) {
- const layout = new Clutter.GridLayout({
- row_homogeneous: true,
- column_homogeneous: true,
- });
-
- let icon = new St.Widget({
- layout_manager: layout,
- x_align: Clutter.ActorAlign.CENTER,
- style: `width: ${size}px; height: ${size}px;`,
- });
-
- const numItems = this._orderedItems.length;
- // decide what number of icons switch to 3x3 grid
- // APP_GRID_FOLDER_ICON_GRID: 3 -> more than 4
- // : 4 -> more than 8
- const threshold = opt.APP_GRID_FOLDER_ICON_GRID % 3 ? 8 : 4;
- const gridSize = opt.APP_GRID_FOLDER_ICON_GRID > 2 && numItems > threshold ? 3 : 2;
- const FOLDER_SUBICON_FRACTION = gridSize === 2 ? 0.4 : 0.27;
-
- let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
- let rtl = icon.get_text_direction() === Clutter.TextDirection.RTL;
- for (let i = 0; i < gridSize * gridSize; i++) {
- const style = `width: ${subSize}px; height: ${subSize}px;`;
- let bin = new St.Bin({ style, reactive: true });
- bin.pivot_point = new Graphene.Point({ x: 0.5, y: 0.5 });
- if (i < numItems) {
- if (!opt.APP_GRID_ACTIVE_PREVIEW) {
- bin.child = this._orderedItems[i].app.create_icon_texture(subSize);
- } else {
- const app = this._orderedItems[i].app;
- const child = new AppDisplay.AppIcon(app, {
- setSizeManually: true,
- showLabel: false,
- });
-
- child._sourceItem = this._orderedItems[i];
- child._sourceFolder = this;
- child.icon.style_class = '';
- child.set_style_class_name('');
- child.icon.set_style('margin: 0; padding: 0;');
- child._dot.set_style('margin-bottom: 1px;');
- child.icon.setIconSize(subSize);
- child._canAccept = () => false;
-
- bin.child = child;
-
- bin.connect('enter-event', () => {
- bin.ease({
- duration: 100,
- translation_y: -3,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
- });
- bin.connect('leave-event', () => {
- bin.ease({
- duration: 100,
- translation_y: 0,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
- });
- }
- }
-
- layout.attach(bin, rtl ? (i + 1) % gridSize : i % gridSize, Math.floor(i / gridSize), 1, 1);
- }
-
- return icon;
- },
-
- _loadApps() {
- this._apps = [];
- const excludedApps = this._folder.get_strv('excluded-apps');
- const appSys = Shell.AppSystem.get_default();
- const addAppId = appId => {
- if (excludedApps.includes(appId))
- return;
-
- if (opt.APP_GRID_EXCLUDE_FAVORITES && this._appFavorites.isFavorite(appId))
- return;
-
- const app = appSys.lookup_app(appId);
- if (!app)
- return;
-
- if (opt.APP_GRID_EXCLUDE_RUNNING) {
- const runningApps = Shell.AppSystem.get_default().get_running().map(a => a.id);
- if (runningApps.includes(appId))
- return;
- }
-
- if (!this._parentalControlsManager.shouldShowApp(app.get_app_info()))
- return;
-
- if (this._apps.indexOf(app) !== -1)
- return;
-
- this._apps.push(app);
- };
-
- const folderApps = this._folder.get_strv('apps');
- folderApps.forEach(addAppId);
-
- const folderCategories = this._folder.get_strv('categories');
- const appInfos = this._parentView.getAppInfos();
- appInfos.forEach(appInfo => {
- let appCategories = /* AppDisplay.*/_getCategories(appInfo);
- if (!_listsIntersect(folderCategories, appCategories))
- return;
-
- addAppId(appInfo.get_id());
- });
-
- let items = [];
- this._apps.forEach(app => {
- let icon = this._items.get(app.get_id());
- if (!icon)
- icon = new AppDisplay.AppIcon(app);
-
- items.push(icon);
- });
-
- return items;
- },
-
- acceptDrop(source) {
- /* if (!BaseAppViewCommon.acceptDrop.bind(this)(source))
- return false;*/
- if (opt.APP_FOLDER_ORDER)
- return false;
- if (source._sourceItem)
- source = source._sourceItem;
-
- if (!this._acceptDropCommon(source))
- return false;
-
- const folderApps = this._orderedItems.map(item => item.id);
- this._folder.set_strv('apps', folderApps);
-
- return true;
- },
-};
-
-const FolderGrid = GObject.registerClass({
- // Registered name should be unique
- GTypeName: `FolderGrid${Math.floor(Math.random() * 1000)}`,
-}, class FolderGrid extends AppDisplay.AppGrid {
- _init() {
- super._init({
- allow_incomplete_pages: false,
- // For adaptive size (0), set the numbers high enough to fit all the icons
- // to avoid splitting the icons to pages upon creating the grid
- columns_per_page: 20,
- rows_per_page: 20,
- page_halign: Clutter.ActorAlign.CENTER,
- page_valign: Clutter.ActorAlign.CENTER,
- });
- this.layoutManager._isFolder = true;
- this._isFolder = true;
- const spacing = opt.APP_GRID_FOLDER_SPACING;
- this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`);
- this.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE;
-
- this.setGridModes([
- {
- columns: 20,
- rows: 20,
- },
- ]);
- }
-
- _updatePadding() {
- const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
- const padding = this._indicatorsPadding.copy();
- const pageIndicatorSize = opt.ORIENTATION
- ? this._view._pageIndicators.get_preferred_width(1000)[1] / scaleFactor
- : this._view._pageIndicators.get_preferred_height(1000)[1] / scaleFactor;
- Math.round(Math.min(...this._view._pageIndicators.get_size()));// / scaleFactor);// ~28;
- padding.left = opt.ORIENTATION ? pageIndicatorSize : 0;
- padding.right = 0;
- padding.top = opt.ORIENTATION ? 0 : pageIndicatorSize;
- padding.bottom = 0;
- this.layoutManager.pagePadding = padding;
- }
-});
-
-
-const FOLDER_DIALOG_ANIMATION_TIME = 200; // AppDisplay.FOLDER_DIALOG_ANIMATION_TIME
-const AppFolderDialog = {
- // injection to _init()
- after__init() {
- // GS 46 changed the aligning to CENTER which restricts max folder dialog size
- this._viewBox.set({
- x_align: Clutter.ActorAlign.FILL,
- y_align: Clutter.ActorAlign.FILL,
- });
-
- // delegate this dialog to the FolderIcon._view
- // so its _createFolderIcon function can update the dialog if folder content changed
- this._view._dialog = this;
-
- // right click into the folder popup should close it
- this.child.reactive = true;
- const clickAction = new Clutter.ClickAction();
- clickAction.connect('clicked', act => {
- if (act.get_button() === Clutter.BUTTON_PRIMARY)
- return Clutter.EVENT_STOP;
- const [x, y] = clickAction.get_coords();
- const actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
- // if it's not entry for editing folder title
- if (actor !== this._entry)
- this.popdown();
- return Clutter.EVENT_STOP;
- });
-
- this.child.add_action(clickAction);
- },
-
- after__addFolderNameEntry() {
- // edit-folder-button class has been replaced with icon-button class which is not transparent in 46
- this._editButton.add_style_class_name('edit-folder-button');
-
- // Edit button
- this._removeButton = new St.Button({
- style_class: 'icon-button edit-folder-button',
- button_mask: St.ButtonMask.ONE,
- toggle_mode: false,
- reactive: true,
- can_focus: true,
- x_align: Clutter.ActorAlign.END,
- y_align: Clutter.ActorAlign.CENTER,
- child: new St.Icon({
- icon_name: 'user-trash-symbolic',
- icon_size: 16,
- }),
- });
-
- this._removeButton.connect('clicked', () => {
- if (Date.now() - this._removeButton._lastClick < Clutter.Settings.get_default().double_click_time) {
- // Close dialog to avoid crashes
- this._isOpen = false;
- this._grabHelper.ungrab({ actor: this });
- this.emit('open-state-changed', false);
- this.hide();
- this._popdownCallbacks.forEach(func => func());
- this._popdownCallbacks = [];
- _appDisplay.ease({
- opacity: 255,
- duration: FOLDER_DIALOG_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
-
- // Reset all keys to delete the relocatable schema
- this._view._deletingFolder = true; // Upstream property
- let keys = this._folder.settings_schema.list_keys();
- for (const key of keys)
- this._folder.reset(key);
-
- let settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' });
- let folders = settings.get_strv('folder-children');
- folders.splice(folders.indexOf(this._view._id), 1);
-
- // remove all abandoned folders (usually my own garbage and unwanted default folders...)
- /* const appFolders = _appDisplay._folderIcons.map(icon => icon._id);
- folders.forEach(folder => {
- if (!appFolders.includes(folder)) {
- folders.splice(folders.indexOf(folder._id), 1);
- }
- });*/
- settings.set_strv('folder-children', folders);
-
- this._view._deletingFolder = false;
- return;
- }
- this._removeButton._lastClick = Date.now();
- });
-
- this._entryBox.add_child(this._removeButton);
- this._entryBox.set_child_at_index(this._removeButton, 0);
-
- this._closeButton = new St.Button({
- style_class: 'icon-button edit-folder-button',
- button_mask: St.ButtonMask.ONE,
- toggle_mode: false,
- reactive: true,
- can_focus: true,
- x_align: Clutter.ActorAlign.END,
- y_align: Clutter.ActorAlign.CENTER,
- child: new St.Icon({
- icon_name: 'window-close-symbolic',
- icon_size: 16,
- }),
- });
-
- this._closeButton.connect('clicked', () => {
- this.popdown();
- });
-
- this._entryBox.add_child(this._closeButton);
- },
-
- popup() {
- if (this._isOpen)
- return;
-
- this._isOpen = this._grabHelper.grab({
- actor: this,
- focus: this._editButton,
- onUngrab: () => this.popdown(),
- });
-
- if (!this._isOpen)
- return;
-
- this.get_parent().set_child_above_sibling(this, null);
-
- // _zoomAndFadeIn() is called from the dialog's allocate()
- this._needsZoomAndFade = true;
-
- this.show();
- // force update folder size
- this._folderAreaBox = null;
- this._updateFolderSize();
-
- this.emit('open-state-changed', true);
- },
-
- _setupPopdownTimeout() {
- if (this._popdownTimeoutId > 0)
- return;
-
- // This timeout is handled in the original code and removed in _onDestroy()
- // All dialogs are destroyed on extension disable()
- this._popdownTimeoutId =
- GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
- this._popdownTimeoutId = 0;
- // Following line fixes upstream bug
- // https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6164
- this._view._onDragEnd();
- this.popdown();
- return GLib.SOURCE_REMOVE;
- });
- },
-
- vfunc_allocate(box) {
- this._updateFolderSize();
-
- // super.allocate(box)
- St.Bin.prototype.vfunc_allocate.bind(this)(box);
-
- // Override any attempt to resize the folder dialog, that happens when some child gets wild
- // Re-allocate the child only if necessary, because it terminates grid animations
- if (this._width && this._height && (this._width !== this.child.width || this._height !== this.child.height))
- this._allocateChild();
-
- // We can only start zooming after receiving an allocation
- if (this._needsZoomAndFade)
- this._zoomAndFadeIn();
- },
-
- _allocateChild() {
- const childBox = new Clutter.ActorBox();
- childBox.set_size(this._width, this._height);
- this.child.allocate(childBox);
- },
-
- // Note that the appDisplay may be off-screen so its coordinates may be shifted
- // However, for _updateFolderSize() it doesn't matter
- // and when _zoomAndFadeIn() is called, appDisplay is on the right place
- _getFolderAreaBox() {
- const appDisplay = this._source._parentView;
- const folderAreaBox = appDisplay.get_allocation_box().copy();
- const searchEntryHeight = opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0;
- folderAreaBox.y1 -= searchEntryHeight;
-
- // _zoomAndFadeIn() needs an absolute position within a multi-monitor workspace
- const monitorGeometry = global.display.get_monitor_geometry(global.display.get_primary_monitor());
- folderAreaBox.x1 += monitorGeometry.x;
- folderAreaBox.x2 += monitorGeometry.x;
- folderAreaBox.y1 += monitorGeometry.y;
- folderAreaBox.y2 += monitorGeometry.y;
-
- return folderAreaBox;
- },
-
- _updateFolderSize() {
- const view = this._view;
- const nItems = view._orderedItems.length;
- const [firstItem] = view._grid.layoutManager._container;
- if (!firstItem)
- return;
-
- const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
- const margin = 18; // see stylesheet .app-folder-dialog-container;
-
- const folderAreaBox = this._getFolderAreaBox();
-
- const maxDialogWidth = folderAreaBox.get_width() / scaleFactor;
- const maxDialogHeight = folderAreaBox.get_height() / scaleFactor;
-
- // We can't build folder if the available space is not available
- if (!isFinite(maxDialogWidth) || !isFinite(maxDialogHeight) || !maxDialogWidth || !maxDialogHeight)
- return;
-
- // We don't need to recalculate grid if nothing changed
- if (
- this._folderAreaBox?.get_width() === folderAreaBox.get_width() &&
- this._folderAreaBox?.get_height() === folderAreaBox.get_height() &&
- nItems === this._nItems
- )
- return;
-
- const layoutManager = view._grid.layoutManager;
- const spacing = opt.APP_GRID_FOLDER_SPACING;
- const padding = 40;
-
- const titleBoxHeight =
- Math.round(this._entryBox.get_preferred_height(-1)[1] / scaleFactor); // ~75
- const minDialogWidth = Math.max(640,
- Math.round(this._entryBox.get_preferred_width(-1)[1] / scaleFactor + 2 * margin));
- const navigationArrowsSize = // padding + one arrow width is sufficient for both arrows
- Math.round(view._nextPageArrow.get_preferred_width(-1)[1] / scaleFactor);
- const pageIndicatorSize =
- Math.round(Math.min(...view._pageIndicators.get_size()) / scaleFactor);// ~28;
- const horizontalNavigation = opt.ORIENTATION ? pageIndicatorSize : navigationArrowsSize; // either add padding or arrows
- const verticalNavigation = opt.ORIENTATION ? navigationArrowsSize : pageIndicatorSize;
-
- // Horizontal size
- const baseWidth = horizontalNavigation + 3 * padding + 2 * margin;
- const maxGridPageWidth = maxDialogWidth - baseWidth;
- // Vertical size
- const baseHeight = titleBoxHeight + verticalNavigation + 2 * padding + 2 * margin;
- const maxGridPageHeight = maxDialogHeight - baseHeight;
-
- // Will be updated to the actual value later
- let itemPadding = 55;
- const minItemSize = 48 + itemPadding;
-
- let columns = opt.APP_GRID_FOLDER_COLUMNS;
- let rows = opt.APP_GRID_FOLDER_ROWS;
- const maxColumns = columns ? columns : 100;
- const maxRows = rows ? rows : 100;
-
- // Find best icon size
- let iconSize = opt.APP_GRID_FOLDER_ICON_SIZE < 0 ? opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT : opt.APP_GRID_FOLDER_ICON_SIZE;
- if (opt.APP_GRID_FOLDER_ICON_SIZE === -1) {
- let maxIconSize;
- if (columns) {
- const maxItemWidth = (maxGridPageWidth - (columns - 1) * opt.APP_GRID_FOLDER_SPACING) / columns;
- maxIconSize = maxItemWidth - itemPadding;
- }
- if (rows) {
- const maxItemHeight = (maxGridPageHeight - (rows - 1) * spacing) / rows;
- maxIconSize = Math.min(maxItemHeight - itemPadding, maxIconSize);
- }
-
- if (maxIconSize) {
- // We only need sizes from the default to the smallest
- let iconSizes = Object.values(IconSize).sort((a, b) => b - a);
- iconSizes = iconSizes.slice(iconSizes.indexOf(iconSize));
- for (const size of iconSizes) {
- iconSize = size;
- if (iconSize <= maxIconSize)
- break;
- }
- }
- }
-
- if ((!columns && !rows) || opt.APP_GRID_FOLDER_ICON_SIZE !== -1) {
- columns = Math.ceil(Math.sqrt(nItems));
- rows = columns;
- if (columns * (columns - 1) >= nItems) {
- rows = columns - 1;
- } else if ((columns + 1) * (columns - 1) >= nItems) {
- rows = columns - 1;
- columns += 1;
- }
- } else if (columns && !rows) {
- rows = Math.ceil(nItems / columns);
- } else if (rows && !columns) {
- columns = Math.ceil(nItems / rows);
- }
-
- columns = Math.clamp(columns, 1, maxColumns);
- columns = Math.min(nItems, columns);
- rows = Math.clamp(rows, 1, maxRows);
-
- let itemSize = iconSize + itemPadding;
- // First run sets the grid before we can read the real icon size
- // so we estimate the size from default properties
- // and correct it in the second run
- if (this.realized) {
- firstItem.icon.setIconSize(iconSize);
- // Item height is inconsistent because it depends on its label height
- const [, firstItemWidth] = firstItem.get_preferred_width(-1);
- const realSize = firstItemWidth / scaleFactor;
- itemSize = realSize;
- itemPadding = realSize - iconSize;
- }
-
- const gridWidth = columns * (itemSize + spacing);
- let width = gridWidth + baseWidth;
- const gridHeight = rows * (itemSize + spacing);
- let height = gridHeight + baseHeight;
-
- // Folder must fit the appDisplay area plus searchEntryBin if visible
- // reduce columns/rows if needed
- while (height > maxDialogHeight && rows > 1) {
- height -= itemSize + spacing;
- rows -= 1;
- }
-
- while (width > maxDialogWidth && columns > 1) {
- width -= itemSize + spacing;
- columns -= 1;
- }
-
- // Try to compensate for the previous reduction if there is a space
- while ((nItems > columns * rows) && ((width + (itemSize + spacing)) <= maxDialogWidth) && (columns < maxColumns)) {
- width += itemSize + spacing;
- columns += 1;
- }
-
- // remove columns that cannot be displayed
- if (((columns * minItemSize + (columns - 1) * spacing)) > maxDialogWidth)
- columns = Math.floor(maxDialogWidth / (minItemSize + spacing));
-
- while ((nItems > columns * rows) && ((height + (itemSize + spacing)) <= maxDialogHeight) && (rows < maxRows)) {
- height += itemSize + spacing;
- rows += 1;
- }
- // remove rows that cannot be displayed
- if ((((rows * minItemSize + (rows - 1) * spacing))) > maxDialogHeight)
- rows = Math.floor(maxDialogWidth / (minItemSize + spacing));
-
- // remove size for rows that are empty
- const rowsNeeded = Math.ceil(nItems / columns);
- if (rows > rowsNeeded) {
- height -= (rows - rowsNeeded) * (itemSize + spacing);
- rows -= rows - rowsNeeded;
- }
-
- // Remove space reserved for page controls and indicator if not used
- if (rows * columns >= nItems) {
- width -= horizontalNavigation;
- height -= verticalNavigation;
- }
-
- width = Math.clamp(width, minDialogWidth, maxDialogWidth);
- height = Math.min(height, maxDialogHeight);
-
- layoutManager.columns_per_page = columns;
- layoutManager.rows_per_page = rows;
-
- layoutManager.fixedIconSize = iconSize;
-
-
- // Store data for further use
- this._width = width * scaleFactor;
- this._height = height * scaleFactor;
- this._folderAreaBox = folderAreaBox;
- this._nItems = nItems;
-
- // Set fixed dialog size to prevent size instability
- this.child.set_size(this._width, this._height);
- this._viewBox.set_style(`width: ${this._width - 2 * margin}px; height: ${this._height - 2 * margin}px;`);
- this._viewBox.set_size(this._width - 2 * margin, this._height - 2 * margin);
-
- view._redisplay();
- },
-
- _zoomAndFadeIn() {
- let [sourceX, sourceY] =
- this._source.get_transformed_position();
- let [dialogX, dialogY] =
- this.child.get_transformed_position();
-
- const sourceCenterX = sourceX + this._source.width / 2;
- const sourceCenterY = sourceY + this._source.height / 2;
-
- // this. covers the whole screen
- let dialogTargetX = dialogX;
- let dialogTargetY = dialogY;
-
- const appDisplay = this._source._parentView;
-
- const folderAreaBox = this._getFolderAreaBox();
-
- let folderAreaX = folderAreaBox.x1;
- let folderAreaY = folderAreaBox.y1;
- const folderAreaWidth = folderAreaBox.get_width();
- const folderAreaHeight = folderAreaBox.get_height();
- const folder = this.child;
-
- if (opt.APP_GRID_FOLDER_CENTER) {
- dialogTargetX = folderAreaX + folderAreaWidth / 2 - folder.width / 2;
- dialogTargetY = folderAreaY + (folderAreaHeight / 2 - folder.height / 2) / 2;
- } else {
- const { pagePadding } = appDisplay._grid.layoutManager;
- const hPadding = (pagePadding.left + pagePadding.right) / 2;
- const vPadding = (pagePadding.top + pagePadding.bottom) / 2;
- const minX = Math.min(folderAreaX + hPadding, folderAreaX + (folderAreaWidth - folder.width) / 2);
- const maxX = Math.max(folderAreaX + folderAreaWidth - hPadding - folder.width, folderAreaX + folderAreaWidth / 2 - folder.width / 2);
- const minY = Math.min(folderAreaY + vPadding, folderAreaY + (folderAreaHeight - folder.height) / 2);
- const maxY = Math.max(folderAreaY + folderAreaHeight - vPadding - folder.height, folderAreaY + folderAreaHeight / 2 - folder.height / 2);
-
- dialogTargetX = sourceCenterX - folder.width / 2;
- dialogTargetX = Math.clamp(dialogTargetX, minX, maxX);
- dialogTargetY = sourceCenterY - folder.height / 2;
- dialogTargetY = Math.clamp(dialogTargetY, minY, maxY);
-
- // keep the dialog in the appDisplay area
- dialogTargetX = Math.clamp(
- dialogTargetX,
- folderAreaX,
- folderAreaX + folderAreaWidth - folder.width
- );
-
- dialogTargetY = Math.clamp(
- dialogTargetY,
- folderAreaY,
- folderAreaY + folderAreaHeight - folder.height
- );
- }
-
- const dialogOffsetX = Math.round(dialogTargetX - dialogX);
- const dialogOffsetY = Math.round(dialogTargetY - dialogY);
-
- this.child.set({
- translation_x: sourceX - dialogX,
- translation_y: sourceY - dialogY,
- scale_x: this._source.width / this.child.width,
- scale_y: this._source.height / this.child.height,
- opacity: 0,
- });
-
- this.child.ease({
- translation_x: dialogOffsetX,
- translation_y: dialogOffsetY,
- scale_x: 1,
- scale_y: 1,
- opacity: 255,
- duration: FOLDER_DIALOG_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
-
- appDisplay.ease({
- opacity: 0,
- duration: FOLDER_DIALOG_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
-
- if (opt.SHOW_SEARCH_ENTRY) {
- Main.overview.searchEntry.ease({
- opacity: 0,
- duration: FOLDER_DIALOG_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
- }
-
- this._needsZoomAndFade = false;
-
- if (this._sourceMappedId === 0) {
- this._sourceMappedId = this._source.connect(
- 'notify::mapped', this._zoomAndFadeOut.bind(this));
- }
- },
-
- _zoomAndFadeOut() {
- if (!this._isOpen)
- return;
-
- if (!this._source.mapped) {
- this.hide();
- return;
- }
-
- // if the dialog was shown silently, skip animation
- if (this.scale_y < 1) {
- this._needsZoomAndFade = false;
- this.hide();
- this._popdownCallbacks.forEach(func => func());
- this._popdownCallbacks = [];
- return;
- }
-
- let [sourceX, sourceY] =
- this._source.get_transformed_position();
- let [dialogX, dialogY] =
- this.child.get_transformed_position();
-
- this.child.ease({
- translation_x: sourceX - dialogX + this.child.translation_x,
- translation_y: sourceY - dialogY + this.child.translation_y,
- scale_x: this._source.width / this.child.width,
- scale_y: this._source.height / this.child.height,
- opacity: 0,
- duration: FOLDER_DIALOG_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_IN_QUAD,
- onComplete: () => {
- this.child.set({
- translation_x: 0,
- translation_y: 0,
- scale_x: 1,
- scale_y: 1,
- opacity: 255,
- });
- this.hide();
-
- this._popdownCallbacks.forEach(func => func());
- this._popdownCallbacks = [];
- },
- });
-
- const appDisplay = this._source._parentView;
- appDisplay.ease({
- opacity: 255,
- duration: FOLDER_DIALOG_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_IN_QUAD,
- });
-
- if (opt.SHOW_SEARCH_ENTRY) {
- Main.overview.searchEntry.ease({
- opacity: 255,
- duration: FOLDER_DIALOG_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_IN_QUAD,
- });
- }
-
- this._needsZoomAndFade = false;
- },
-
- _setLighterBackground(lighter) {
- let opacity = 255;
- if (this._isOpen)
- opacity = lighter ? 20 : 0;
-
- _appDisplay.ease({
- opacity,
- duration: FOLDER_DIALOG_ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
- },
-
- vfunc_key_press_event(event) {
- if (global.focus_manager.navigate_from_event(event))
- return Clutter.EVENT_STOP;
- return Clutter.EVENT_PROPAGATE;
- },
-
- _showFolderLabel() {
- if (this._editButton.checked)
- this._editButton.checked = false;
-
- this._maybeUpdateFolderName();
- this._switchActor(this._entry, this._folderNameLabel);
- // This line has been added in 47 to fix focus after editing the folder name
- this.navigate_focus(this, St.DirectionType.TAB_FORWARD, false);
- },
-};
-
-const AppIcon = {
- after__init() {
- // update the app label behavior
- this._updateMultiline();
- },
-
- // avoid accepting by placeholder when dragging active preview
- // and also by icon if usage sorting is used
- _canAccept(source) {
- if (source._sourceItem)
- source = source._sourceItem;
-
- // Folders in folder are not supported
- if (!(_getViewFromIcon(this) instanceof AppDisplay.AppDisplay) || !this.opacity)
- return false;
-
- const view = /* AppDisplay.*/_getViewFromIcon(source);
- return source !== this &&
- (source instanceof this.constructor) &&
- // Include drops from folders
- // (view instanceof AppDisplay.AppDisplay &&
- (view &&
- !opt.APP_GRID_USAGE);
- },
-};
-
-const AppViewItemCommon = {
- _updateMultiline() {
- const { label } = this.icon;
- if (label)
- label.opacity = 255;
- if (!this._expandTitleOnHover || !this.icon.label)
- return;
-
- const { clutterText } = label;
-
- const isHighlighted = this.has_key_focus() || this.hover || this._forcedHighlight;
-
- if (opt.APP_GRID_NAMES_MODE === 2 && this._expandTitleOnHover) { // !_expandTitleOnHover indicates search result icon
- label.opacity = isHighlighted || !this.app ? 255 : 0;
- }
- if (isHighlighted)
- this.get_parent()?.set_child_above_sibling(this, null);
-
- if (!opt.APP_GRID_NAMES_MODE) {
- const layout = clutterText.get_layout();
- if (!layout.is_wrapped() && !layout.is_ellipsized())
- return;
- }
-
- label.remove_transition('allocation');
-
- const id = label.connect('notify::allocation', () => {
- label.restore_easing_state();
- label.disconnect(id);
- });
-
- const expand = opt.APP_GRID_NAMES_MODE === 1 || this._forcedHighlight || this.hover || this.has_key_focus();
-
- label.save_easing_state();
- label.set_easing_duration(expand
- ? APP_ICON_TITLE_EXPAND_TIME
- : APP_ICON_TITLE_COLLAPSE_TIME);
- clutterText.set({
- line_wrap: expand,
- line_wrap_mode: expand ? Pango.WrapMode.WORD_CHAR : Pango.WrapMode.NONE,
- ellipsize: expand ? Pango.EllipsizeMode.NONE : Pango.EllipsizeMode.END,
- });
- },
-
- // support active preview icons
- acceptDrop(source, _actor, x) {
- if (opt.APP_GRID_USAGE)
- return DND.DragMotionResult.NO_DROP;
-
- this._setHoveringByDnd(false);
-
- if (!this._canAccept(source))
- return false;
-
- if (this._withinLeeways(x))
- return false;
-
- // added - remove app from the source folder after dnd to other folder
- let view = /* AppDisplay.*/_getViewFromIcon(source);
- if (view instanceof AppDisplay.FolderView)
- view.removeApp(source.app);
-
- return true;
- },
-
-};
-
-const PageIndicatorsCommon = {
- after_setNPages() {
- this.visible = true;
- this.opacity = this._nPages > 1 ? 255 : 0;
- },
-};