summaryrefslogtreecommitdiffstats
path: root/extensions/vertical-workspaces/lib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--extensions/vertical-workspaces/lib/appDisplay.js1474
-rw-r--r--extensions/vertical-workspaces/lib/appFavorites.js61
-rw-r--r--extensions/vertical-workspaces/lib/dash.js (renamed from extensions/vertical-workspaces/dash.js)565
-rw-r--r--extensions/vertical-workspaces/lib/iconGrid.js314
-rw-r--r--extensions/vertical-workspaces/lib/layout.js380
-rw-r--r--extensions/vertical-workspaces/lib/messageTray.js67
-rw-r--r--extensions/vertical-workspaces/lib/optionsFactory.js (renamed from extensions/vertical-workspaces/optionsFactory.js)233
-rw-r--r--extensions/vertical-workspaces/lib/osdWindow.js93
-rw-r--r--extensions/vertical-workspaces/lib/overlayKey.js108
-rw-r--r--extensions/vertical-workspaces/lib/overview.js (renamed from extensions/vertical-workspaces/overview.js)33
-rw-r--r--extensions/vertical-workspaces/lib/overviewControls.js (renamed from extensions/vertical-workspaces/overviewControls.js)732
-rw-r--r--extensions/vertical-workspaces/lib/panel.js (renamed from extensions/vertical-workspaces/panel.js)183
-rw-r--r--extensions/vertical-workspaces/lib/recentFilesSearchProvider.js (renamed from extensions/vertical-workspaces/recentFilesSearchProvider.js)206
-rw-r--r--extensions/vertical-workspaces/lib/search.js206
-rw-r--r--extensions/vertical-workspaces/lib/settings.js (renamed from extensions/vertical-workspaces/settings.js)279
-rw-r--r--extensions/vertical-workspaces/lib/swipeTracker.js (renamed from extensions/vertical-workspaces/swipeTracker.js)28
-rw-r--r--extensions/vertical-workspaces/lib/util.js364
-rw-r--r--extensions/vertical-workspaces/lib/windowAttentionHandler.js90
-rw-r--r--extensions/vertical-workspaces/lib/windowManager.js (renamed from extensions/vertical-workspaces/windowManager.js)161
-rw-r--r--extensions/vertical-workspaces/lib/windowPreview.js (renamed from extensions/vertical-workspaces/windowPreview.js)220
-rw-r--r--extensions/vertical-workspaces/lib/windowSearchProvider.js (renamed from extensions/vertical-workspaces/windowSearchProvider.js)209
-rw-r--r--extensions/vertical-workspaces/lib/workspace.js (renamed from extensions/vertical-workspaces/workspace.js)108
-rw-r--r--extensions/vertical-workspaces/lib/workspaceAnimation.js (renamed from extensions/vertical-workspaces/workspaceAnimation.js)123
-rw-r--r--extensions/vertical-workspaces/lib/workspaceSwitcherPopup.js90
-rw-r--r--extensions/vertical-workspaces/lib/workspaceThumbnail.js (renamed from extensions/vertical-workspaces/workspaceThumbnail.js)810
-rw-r--r--extensions/vertical-workspaces/lib/workspacesView.js (renamed from extensions/vertical-workspaces/workspacesView.js)530
26 files changed, 5973 insertions, 1694 deletions
diff --git a/extensions/vertical-workspaces/lib/appDisplay.js b/extensions/vertical-workspaces/lib/appDisplay.js
new file mode 100644
index 0000000..2ac70b1
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/appDisplay.js
@@ -0,0 +1,1474 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * appDisplay.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+
+const { Clutter, GLib, GObject, Meta, Shell, St, Graphene, Pango } = imports.gi;
+
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+const AppDisplay = imports.ui.appDisplay;
+const IconGrid = imports.ui.iconGrid;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const IconGridOverride = Me.imports.lib.iconGrid;
+const _Util = Me.imports.lib.util;
+
+const DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x00000022);
+const DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000000);
+
+// gettext
+const _ = Me.imports.lib.settings._;
+
+let _overrides;
+
+let _appGridLayoutSettings;
+let _appDisplayScrollConId;
+let _appSystemStateConId;
+let _appGridLayoutConId;
+let _origAppViewItemAcceptDrop;
+let _updateFolderIcons;
+
+let opt;
+let shellVersion = _Util.shellVersion;
+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) {
+ _setAppDisplayOrientation(false);
+ _updateAppGridProperties(reset);
+ _updateAppGridDND(reset);
+ _restoreOverviewGroup();
+ _overrides = null;
+ opt = null;
+ return;
+ }
+
+ _overrides = new _Util.Overrides();
+
+ if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) {
+ _overrides.addOverride('AppDisplayVertical', AppDisplay.AppDisplay.prototype, AppDisplayVertical);
+ _overrides.addOverride('BaseAppViewVertical', AppDisplay.BaseAppView.prototype, BaseAppViewVertical);
+ }
+
+ // Custom App Grid
+ _overrides.addOverride('AppFolderDialog', AppDisplay.AppFolderDialog.prototype, AppFolderDialog);
+ if (shellVersion >= 43) {
+ // const defined class needs to be touched before real access
+ AppDisplay.BaseAppViewGridLayout;
+ _overrides.addOverride('BaseAppViewGridLayout', AppDisplay.BaseAppViewGridLayout.prototype, BaseAppViewGridLayout);
+ }
+ _overrides.addOverride('FolderView', AppDisplay.FolderView.prototype, FolderView);
+ _overrides.addOverride('FolderIcon', AppDisplay.FolderIcon.prototype, FolderIcon);
+ _overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIcon);
+ _overrides.addOverride('AppDisplay', AppDisplay.AppDisplay.prototype, AppDisplayCommon);
+ _overrides.addOverride('AppViewItem', AppDisplay.AppViewItem.prototype, AppViewItemCommon);
+ _overrides.addOverride('BaseAppViewCommon', AppDisplay.BaseAppView.prototype, BaseAppViewCommon);
+
+ _setAppDisplayOrientation(opt.ORIENTATION === Clutter.Orientation.VERTICAL);
+ _updateAppGridProperties();
+ _updateAppGridDND();
+ opt._appGridNeedsRedisplay = true;
+}
+
+function _setAppDisplayOrientation(vertical = false) {
+ const CLUTTER_ORIENTATION = vertical ? Clutter.Orientation.VERTICAL : Clutter.Orientation.HORIZONTAL;
+ const scroll = vertical ? 'vscroll' : 'hscroll';
+ // app display to vertical has issues - page indicator not working
+ // global appDisplay orientation switch is not built-in
+ let appDisplay = Main.overview._overview._controls._appDisplay;
+ // following line itself only changes in which axis will operate overshoot detection which switches appDisplay pages while dragging app icon to vertical
+ appDisplay._orientation = CLUTTER_ORIENTATION;
+ appDisplay._grid.layoutManager._orientation = CLUTTER_ORIENTATION;
+ appDisplay._swipeTracker.orientation = CLUTTER_ORIENTATION;
+ appDisplay._swipeTracker._reset();
+ if (vertical) {
+ appDisplay._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL);
+
+ // move and change orientation of page indicators
+ // needs corrections in appgrid page calculations, e.g. appDisplay.adaptToSize() fnc,
+ // which complicates use of super call inside the function
+ const pageIndicators = appDisplay._pageIndicators;
+ pageIndicators.vertical = true;
+ appDisplay._box.vertical = false;
+ pageIndicators.x_expand = false;
+ pageIndicators.y_align = Clutter.ActorAlign.CENTER;
+ pageIndicators.x_align = Clutter.ActorAlign.START;
+
+ const scrollContainer = appDisplay._scrollView.get_parent();
+ if (shellVersion < 43) {
+ // remove touch friendly side navigation bars / arrows
+ if (appDisplay._hintContainer && appDisplay._hintContainer.get_parent())
+ scrollContainer.remove_child(appDisplay._hintContainer);
+ } else {
+ // moving these bars needs more patching of the appDisplay's code
+ // for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page
+ appDisplay._nextPageIndicator.add_style_class_name('nextPageIndicator');
+ appDisplay._prevPageIndicator.add_style_class_name('prevPageIndicator');
+ }
+
+ // setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them
+ appDisplay._nextPageArrow.scale_x = 0;
+ appDisplay._prevPageArrow.scale_x = 0;
+ } else {
+ appDisplay._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER);
+ if (_appDisplayScrollConId) {
+ appDisplay._adjustment.disconnect(_appDisplayScrollConId);
+ _appDisplayScrollConId = 0;
+ }
+
+ // restore original page indicators
+ const pageIndicators = appDisplay._pageIndicators;
+ pageIndicators.vertical = false;
+ appDisplay._box.vertical = true;
+ pageIndicators.x_expand = true;
+ pageIndicators.y_align = Clutter.ActorAlign.END;
+ pageIndicators.x_align = Clutter.ActorAlign.CENTER;
+
+ // put back touch friendly navigation bars/buttons
+ const scrollContainer = appDisplay._scrollView.get_parent();
+ if (appDisplay._hintContainer && !appDisplay._hintContainer.get_parent()) {
+ scrollContainer.add_child(appDisplay._hintContainer);
+ // the hit container covers the entire app grid and added at the top of the stack blocks DND drops
+ // so it needs to be pushed below
+ scrollContainer.set_child_below_sibling(appDisplay._hintContainer, null);
+ }
+
+ appDisplay._nextPageArrow.scale_x = 1;
+ appDisplay._prevPageArrow.scale_x = 1;
+
+ appDisplay._nextPageIndicator.remove_style_class_name('nextPageIndicator');
+ appDisplay._prevPageIndicator.remove_style_class_name('prevPageIndicator');
+ }
+
+ // value for page indicator is calculated from scroll adjustment, horizontal needs to be replaced by vertical
+ appDisplay._adjustment = appDisplay._scrollView[scroll].adjustment;
+
+ // no need to connect already connected signal (wasn't removed the original one before)
+ if (!vertical) {
+ // reset used appDisplay properties
+ Main.overview._overview._controls._appDisplay.scale_y = 1;
+ Main.overview._overview._controls._appDisplay.scale_x = 1;
+ Main.overview._overview._controls._appDisplay.opacity = 255;
+ return;
+ }
+
+ // update appGrid dot pages indicators
+ _appDisplayScrollConId = appDisplay._adjustment.connect('notify::value', adj => {
+ const value = adj.value / adj.page_size;
+ appDisplay._pageIndicators.setCurrentPosition(value);
+ });
+}
+
+// Set App Grid columns, rows, icon size, incomplete pages
+function _updateAppGridProperties(reset = false) {
+ opt._appGridNeedsRedisplay = false;
+ // columns, rows, icon size
+ const appDisplay = Main.overview._overview._controls._appDisplay;
+ appDisplay.visible = true;
+
+ if (reset) {
+ appDisplay._grid.layoutManager.fixedIconSize = -1;
+ appDisplay._grid.layoutManager.allow_incomplete_pages = true;
+ appDisplay._grid.setGridModes();
+ if (_appGridLayoutSettings) {
+ _appGridLayoutSettings.disconnect(_appGridLayoutConId);
+ _appGridLayoutConId = 0;
+ _appGridLayoutSettings = null;
+ }
+ appDisplay._redisplay();
+
+ appDisplay._grid.set_style('');
+ _resetAppGrid();
+ } else {
+ // update grid on layout reset
+ if (!_appGridLayoutSettings) {
+ _appGridLayoutSettings = ExtensionUtils.getSettings('org.gnome.shell');
+ _appGridLayoutConId = _appGridLayoutSettings.connect('changed::app-picker-layout', _resetAppGrid);
+ }
+
+ appDisplay._grid.layoutManager.allow_incomplete_pages = opt.APP_GRID_ALLOW_INCOMPLETE_PAGES;
+ appDisplay._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`);
+
+ // force redisplay
+ appDisplay._grid._currentMode = -1;
+ appDisplay._grid.setGridModes();
+ appDisplay._grid.layoutManager.fixedIconSize = opt.APP_GRID_ICON_SIZE;
+ // appDisplay._folderIcons.forEach(folder => folder._dialog?._updateFolderSize());
+ _resetAppGrid();
+ }
+}
+
+function _updateAppGridDND(reset) {
+ if (!reset) {
+ if (!_appSystemStateConId && opt.APP_GRID_INCLUDE_DASH >= 3) {
+ _appSystemStateConId = Shell.AppSystem.get_default().connect(
+ 'app-state-changed',
+ () => {
+ _updateFolderIcons = true;
+ Main.overview._overview._controls._appDisplay._redisplay();
+ }
+ );
+ }
+ } else if (_appSystemStateConId) {
+ Shell.AppSystem.get_default().disconnect(_appSystemStateConId);
+ _appSystemStateConId = 0;
+ }
+ if (opt.APP_GRID_ORDER && !reset) {
+ if (!_origAppViewItemAcceptDrop)
+ _origAppViewItemAcceptDrop = AppDisplay.AppViewItem.prototype.acceptDrop;
+ AppDisplay.AppViewItem.prototype.acceptDrop = () => false;
+ } else if (_origAppViewItemAcceptDrop) {
+ AppDisplay.AppViewItem.prototype.acceptDrop = _origAppViewItemAcceptDrop;
+ }
+}
+
+function _restoreOverviewGroup() {
+ Main.overview.dash.showAppsButton.checked = false;
+ Main.layoutManager.overviewGroup.opacity = 255;
+ Main.layoutManager.overviewGroup.scale_x = 1;
+ Main.layoutManager.overviewGroup.hide();
+}
+
+const AppDisplayVertical = {
+ // correction of the appGrid size when page indicators were moved from the bottom to the right
+ adaptToSize(width, height) {
+ const [, indicatorWidth] = this._pageIndicators.get_preferred_width(-1);
+ width -= indicatorWidth;
+
+ this._grid.findBestModeForSize(width, height);
+
+ const adaptToSize = AppDisplay.BaseAppView.prototype.adaptToSize.bind(this);
+ adaptToSize(width, height);
+ },
+};
+
+const AppDisplayCommon = {
+ _ensureDefaultFolders() {
+ // disable creation of default folders if user deleted them
+ },
+
+ _redisplay() {
+ this._folderIcons.forEach(icon => {
+ icon.view._redisplay();
+ });
+
+ BaseAppViewCommon._redisplay.bind(this)();
+ },
+
+ // 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_ORDER) {
+ 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 (_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
+ _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_ORDER && 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;
+ },
+
+ // support active preview icons
+ _onDragBegin(overview, source) {
+ if (source._sourceItem)
+ source = source._sourceItem;
+
+ this._dragMonitor = {
+ dragMotion: this._onDragMotion.bind(this),
+ };
+ DND.addDragMonitor(this._dragMonitor);
+ if (shellVersion < 43)
+ this._slideSidePages(AppDisplay.SidePages.PREVIOUS | AppDisplay.SidePages.NEXT | AppDisplay.SidePages.DND);
+ else
+ 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 preview
+ acceptDrop(source) {
+ if (opt.APP_GRID_ORDER)
+ return false;
+ if (source._sourceItem)
+ source = source._sourceItem;
+
+ let dropTarget = null;
+ if (shellVersion >= 43) {
+ dropTarget = this._dropTarget;
+ delete this._dropTarget;
+ }
+
+ if (!this._canAccept(source))
+ return false;
+
+ if ((shellVersion < 43 && this._dropPage) ||
+ (shellVersion >= 43 && (dropTarget === this._prevPageIndicator ||
+ dropTarget === this._nextPageIndicator))) {
+ let increment;
+
+ if (shellVersion < 43)
+ increment = this._dropPage === AppDisplay.SidePages.NEXT ? 1 : -1;
+ else
+ 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) {
+ log(`Warning:${e}`);
+ }
+ this._removeDelayedMove();
+ }
+
+ this._savePages();
+
+ let 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;
+ },
+};
+
+const BaseAppViewVertical = {
+ after__init() {
+ this._grid.layoutManager._orientation = Clutter.Orientation.VERTICAL;
+ this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL);
+ this._orientation = Clutter.Orientation.VERTICAL;
+ this._swipeTracker.orientation = Clutter.Orientation.VERTICAL;
+ this._swipeTracker._reset();
+ this._pageIndicators.vertical = true;
+ this._box.vertical = false;
+ this._pageIndicators.x_expand = false;
+ this._pageIndicators.y_align = Clutter.ActorAlign.CENTER;
+ this._pageIndicators.x_align = Clutter.ActorAlign.START;
+ this._pageIndicators.set_style('margin-right: 10px;');
+ const scrollContainer = this._scrollView.get_parent();
+ if (shellVersion < 43) {
+ // remove touch friendly side navigation bars / arrows
+ if (this._hintContainer && this._hintContainer.get_parent())
+ scrollContainer.remove_child(this._hintContainer);
+ } else {
+ // moving these bars needs more patching of the this's code
+ // for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page
+ this._nextPageIndicator.add_style_class_name('nextPageIndicator');
+ this._prevPageIndicator.add_style_class_name('prevPageIndicator');
+ }
+
+ // setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them
+ this._nextPageArrow.scale_x = 0;
+ this._prevPageArrow.scale_x = 0;
+
+ this._adjustment = this._scrollView.vscroll.adjustment;
+
+ this._adjustment.connect('notify::value', adj => {
+ const value = adj.value / adj.page_size;
+ this._pageIndicators.setCurrentPosition(value);
+ });
+ },
+ // <= 42 only, this fixes dnd from appDisplay to the workspace thumbnail on the left if appDisplay is on page 1 because of appgrid left overshoot
+ _pageForCoords() {
+ return AppDisplay.SidePages.NONE;
+ },
+};
+
+const BaseAppViewCommon = {
+ _sortOrderedItemsAlphabetically(icons = null) {
+ if (!icons)
+ icons = this._orderedItems;
+ icons.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
+ },
+
+ _setLinearPositions(icons) {
+ const { itemsPerPage } = this._grid;
+ icons.forEach((icon, i) => {
+ const page = Math.floor(i / itemsPerPage);
+ const position = i % itemsPerPage;
+ try {
+ this._moveItem(icon, page, position);
+ } catch (e) {
+ log(`Warning:${e}`);
+ }
+ });
+ },
+
+ // adds sorting options and option to add favorites and running apps
+ _redisplay() {
+ let oldApps = this._orderedItems.slice();
+ let oldAppIds = oldApps.map(icon => icon.id);
+
+ let newApps = this._loadApps().sort(this._compareItems.bind(this));
+ let newAppIds = newApps.map(icon => icon.id);
+
+ let addedApps = newApps.filter(icon => !oldAppIds.includes(icon.id));
+ let removedApps = oldApps.filter(icon => !newAppIds.includes(icon.id));
+
+ // Remove old app icons
+ removedApps.forEach(icon => {
+ this._removeItem(icon);
+ icon.destroy();
+ });
+
+ // Add new app icons, or move existing ones
+ newApps.forEach(icon => {
+ const [page, position] = this._getItemPosition(icon);
+ if (addedApps.includes(icon)) {
+ this._addItem(icon, page, position);
+ } else if (page !== -1 && position !== -1) {
+ this._moveItem(icon, page, position);
+ } else {
+ // App is part of a folder
+ }
+ });
+
+ // sort all alphabetically
+ if (opt.APP_GRID_ORDER > 0) {
+ // const { itemsPerPage } = this._grid;
+ let appIcons = this._orderedItems;
+ this._sortOrderedItemsAlphabetically(appIcons);
+ // appIcons.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
+ // then sort used apps by usage
+ if (opt.APP_GRID_ORDER === 2)
+ appIcons.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id));
+
+ // sort favorites first
+ if (opt.APP_GRID_DASH_FIRST) {
+ const fav = Object.keys(this._appFavorites._favorites);
+ appIcons.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_DASH_FIRST)
+ appIcons.sort((a, b) => a.app.get_state() !== Shell.AppState.RUNNING && b.app.get_state() === Shell.AppState.RUNNING);
+
+ this._setLinearPositions(appIcons);
+
+ this._orderedItems = appIcons;
+ }
+
+ this.emit('view-loaded');
+ if (!opt.APP_GRID_ALLOW_INCOMPLETE_PAGES) {
+ for (let i = 0; i < this._grid.nPages; i++)
+ this._grid.layoutManager._fillItemVacancies(i);
+ }
+ },
+
+ _canAccept(source) {
+ return opt.APP_GRID_ORDER ? false : source instanceof AppDisplay.AppViewItem;
+ },
+
+ // support active preview icons
+ acceptDrop(source) {
+ if (!this._canAccept(source))
+ return false;
+
+ if (source._sourceItem)
+ source = source._sourceItem;
+
+
+ if (this._dropPage) {
+ const increment = this._dropPage === AppDisplay.SidePages.NEXT ? 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;
+
+ this._moveItem(source, page, position);
+ 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 (shellVersion < 43) {
+ this._dropPage = this._pageForCoords(dragEvent.x, dragEvent.y);
+ if (this._dropPage &&
+ this._dropPage === AppDisplay.SidePages.PREVIOUS &&
+ this._grid.currentPage === 0) {
+ delete this._dropPage;
+ return DND.DragMotionResult.NO_DROP;
+ }
+ }
+
+ if (appIcon instanceof AppDisplay.AppViewItem) {
+ if (shellVersion < 44) {
+ // Handle the drag overshoot. When dragging to above the
+ // icon grid, move to the page above; when dragging below,
+ // move to the page below.
+ this._handleDragOvershoot(dragEvent);
+ } else 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();
+ }
+ }
+
+ this._maybeMoveItem(dragEvent);
+
+ return DND.DragMotionResult.CONTINUE;
+ },
+
+ // adjustable page width for GS <= 42
+ adaptToSize(width, height, isFolder = false) {
+ let box = new Clutter.ActorBox({
+ x2: width,
+ y2: height,
+ });
+ box = this.get_theme_node().get_content_box(box);
+ box = this._scrollView.get_theme_node().get_content_box(box);
+ box = this._grid.get_theme_node().get_content_box(box);
+
+ const availWidth = box.get_width();
+ const availHeight = box.get_height();
+
+ let pageWidth, pageHeight;
+
+ pageHeight = availHeight;
+ pageWidth = Math.ceil(availWidth * (isFolder ? 1 : opt.APP_GRID_PAGE_WIDTH_SCALE));
+ // subtract space for navigation arrows in horizontal mode
+ pageWidth -= opt.ORIENTATION ? 0 : 128;
+
+ this._grid.layout_manager.pagePadding.left =
+ Math.floor(availWidth * 0.02);
+ this._grid.layout_manager.pagePadding.right =
+ Math.ceil(availWidth * 0.02);
+
+ this._grid.adaptToSize(pageWidth, pageHeight);
+
+ const leftPadding = Math.floor(
+ (availWidth - this._grid.layout_manager.pageWidth) / 2);
+ const rightPadding = Math.ceil(
+ (availWidth - this._grid.layout_manager.pageWidth) / 2);
+ const topPadding = Math.floor(
+ (availHeight - this._grid.layout_manager.pageHeight) / 2);
+ const bottomPadding = Math.ceil(
+ (availHeight - this._grid.layout_manager.pageHeight) / 2);
+
+ this._scrollView.content_padding = new Clutter.Margin({
+ left: leftPadding,
+ right: rightPadding,
+ top: topPadding,
+ bottom: bottomPadding,
+ });
+
+ this._availWidth = availWidth;
+ this._availHeight = availHeight;
+
+ this._pageIndicatorOffset = leftPadding;
+ this._pageArrowOffset = Math.max(
+ leftPadding - 80, 0); // 80 is AppDisplay.PAGE_PREVIEW_MAX_ARROW_OFFSET
+ },
+};
+
+const BaseAppViewGridLayout = {
+ _getIndicatorsWidth(box) {
+ const [width, height] = box.get_size();
+ const arrows = [
+ this._nextPageArrow,
+ this._previousPageArrow,
+ ];
+
+ const minArrowsWidth = arrows.reduce(
+ (previousWidth, accessory) => {
+ const [min] = accessory.get_preferred_width(height);
+ return Math.max(previousWidth, min);
+ }, 0);
+
+ const idealIndicatorWidth = (width * 0.1/* PAGE_PREVIEW_RATIO*/) / 2;
+
+ return Math.max(idealIndicatorWidth, minArrowsWidth);
+ },
+};
+
+const FolderIcon = {
+ after__init() {
+ /* // If folder preview icons are clickable,
+ // disable opening the folder with primary mouse button and enable the secondary one
+ const buttonMask = opt.APP_GRID_ACTIVE_PREVIEW
+ ? St.ButtonMask.TWO | St.ButtonMask.THREE
+ : St.ButtonMask.ONE | St.ButtonMask.TWO;
+ this.button_mask = buttonMask;*/
+ this.button_mask = St.ButtonMask.ONE | St.ButtonMask.TWO;
+
+ // build the folders now to avoid node errors when dragging active folder preview icons
+ if (this.visible && opt.APP_GRID_ACTIVE_PREVIEW)
+ this._ensureFolderDialog();
+ },
+
+ open() {
+ this._ensureFolderDialog();
+ if (this._dialog._designCapacity !== this.view._orderedItems.length)
+ this._dialog._updateFolderSize();
+
+ this.view._scrollView.vscroll.adjustment.value = 0;
+ this._dialog.popup();
+ },
+};
+
+const FolderView = {
+ _createGrid() {
+ let grid;
+ if (shellVersion < 43)
+ grid = new FolderGrid();
+ else
+ grid = new FolderGrid43();
+
+ // IconGrid algorithm for adaptive icon size
+ // counts with different default(max) size for folders
+ grid.layoutManager._isFolder = true;
+ 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 ActiveFolderIcon(app);
+ child._sourceItem = this._orderedItems[i];
+ child._sourceFolder = this;
+ child.icon.style_class = '';
+ child.icon.set_style('margin: 0; padding: 0;');
+ child.icon.setIconSize(subSize);
+
+ bin.child = child;
+
+ bin.connect('enter-event', () => {
+ bin.ease({
+ duration: 100,
+ scale_x: 1.14,
+ scale_y: 1.14,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ });
+ bin.connect('leave-event', () => {
+ bin.ease({
+ duration: 100,
+ scale_x: 1,
+ scale_y: 1,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ });
+ }
+ }
+
+ layout.attach(bin, rtl ? (i + 1) % gridSize : i % gridSize, Math.floor(i / gridSize), 1, 1);
+ }
+
+ // if folder content changed, update folder size
+ if (this._dialog && this._dialog._designCapacity !== this._orderedItems.length)
+ this._dialog._updateFolderSize();
+
+ return icon;
+ },
+
+ // this just overrides _redisplay() for GS < 44
+ _redisplay() {
+ // super._redisplay(); - super doesn't work in my overrides
+ AppDisplay.BaseAppView.prototype._redisplay.bind(this)();
+ },
+
+ _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 (!AppDisplay._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);
+ });
+ this._appIds = this._apps.map(app => app.get_id());
+ return items;
+ },
+
+ // 42 only - don't apply appGrid scale on folders
+ adaptToSize(width, height) {
+ if (!opt.ORIENTATION) {
+ const [, indicatorHeight] = this._pageIndicators.get_preferred_height(-1);
+ height -= indicatorHeight;
+ }
+ BaseAppViewCommon.adaptToSize.bind(this)(width, height, true);
+ },
+};
+
+// folder columns and rows
+const FolderGrid = GObject.registerClass(
+class FolderGrid extends IconGrid.IconGrid {
+ _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
+ columns_per_page: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 20,
+ rows_per_page: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 20,
+ page_halign: Clutter.ActorAlign.CENTER,
+ page_valign: Clutter.ActorAlign.CENTER,
+ });
+
+ // if (!opt.APP_GRID_FOLDER_DEFAULT)
+ const spacing = opt.APP_GRID_SPACING;
+ this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`);
+ this.layout_manager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE;
+ }
+
+ adaptToSize(width, height) {
+ this.layout_manager.adaptToSize(width, height);
+ }
+});
+
+
+let FolderGrid43;
+// first reference to constant defined using const in other module returns undefined, the AppGrid const will remain empty and unused
+const AppGrid = AppDisplay.AppGrid;
+if (AppDisplay.AppGrid) {
+ FolderGrid43 = GObject.registerClass(
+ class FolderGrid43 extends AppDisplay.AppGrid {
+ _init() {
+ super._init({
+ allow_incomplete_pages: false,
+ columns_per_page: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 20,
+ rows_per_page: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 20,
+ page_halign: Clutter.ActorAlign.CENTER,
+ page_valign: Clutter.ActorAlign.CENTER,
+ });
+
+ const spacing = opt.APP_GRID_SPACING;
+ this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`);
+ this.layout_manager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE;
+
+ this.setGridModes([
+ {
+ columns: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 3,
+ rows: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 3,
+ },
+ ]);
+ }
+
+ adaptToSize(width, height) {
+ this.layout_manager.adaptToSize(width, height);
+ }
+ });
+}
+
+const FOLDER_DIALOG_ANIMATION_TIME = 200; // AppDisplay.FOLDER_DIALOG_ANIMATION_TIME
+const AppFolderDialog = {
+ // injection to _init()
+ after__init() {
+ // 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);
+ },
+
+ popup() {
+ if (this._isOpen)
+ return;
+
+ /* if (!this._correctSize) {
+ // update folder with the precise app item size when the dialog is realized
+ GLib.idle_add(0, () => this._updateFolderSize(true));
+ this._correctSize = true;
+ }*/
+
+ this._isOpen = this._grabHelper.grab({
+ actor: this,
+ onUngrab: () => this.popdown(),
+ });
+
+ if (!this._isOpen)
+ return;
+
+ this.get_parent().set_child_above_sibling(this, null);
+
+ this._needsZoomAndFade = true;
+ this.show();
+
+ this.emit('open-state-changed', true);
+ },
+
+ _updateFolderSize() {
+ // adapt folder size according to the settings and number of icons
+ const view = this._view;
+ view._grid.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE;
+ view._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`);
+
+ const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
+ const dialogMargin = 30;
+ const nItems = view._orderedItems.length;
+ let columns = opt.APP_GRID_FOLDER_COLUMNS;
+ let rows = opt.APP_GRID_FOLDER_ROWS;
+ let spacing = opt.APP_GRID_SPACING;
+ const monitor = global.display.get_monitor_geometry(global.display.get_primary_monitor());
+
+ if (!columns && !rows) {
+ 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) {
+ columns = Math.ceil(nItems / rows);
+ } else if (columns && !rows) {
+ rows = Math.ceil(nItems / columns);
+ }
+
+ const iconSize = opt.APP_GRID_FOLDER_ICON_SIZE < 0 ? opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT : opt.APP_GRID_FOLDER_ICON_SIZE;
+ let itemSize = iconSize + 53; // icon padding
+ // 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._notFirstRun) {
+ const [firstItem] = view._grid.layoutManager._container;
+ firstItem.icon.setIconSize(iconSize);
+ const [firstItemWidth] = firstItem.get_preferred_size();
+ const realSize = firstItemWidth / scaleFactor;
+ if (realSize > iconSize)
+ itemSize = realSize;
+ } else {
+ this._needsUpdateSize = true;
+ this._notFirstRun = true;
+ }
+
+
+ let width = columns * (itemSize + spacing) + /* padding for nav arrows*/64;
+ width = Math.round(width + (opt.ORIENTATION || !opt.APP_GRID_FOLDER_COLUMNS ? 100 : 160/* space for navigation arrows*/));
+ let height = rows * (itemSize + spacing) + /* header*/75 + /* padding*/100;
+
+ // folder must fit the primary monitor
+ // reduce columns/rows if needed and count with the scaled values
+ while (width * scaleFactor > monitor.width - 2 * dialogMargin) {
+ width -= itemSize + spacing;
+ columns -= 1;
+ }
+ while (height * scaleFactor > monitor.height - 2 * dialogMargin) {
+ height -= itemSize + spacing;
+ rows -= 1;
+ }
+ width = Math.max(540, width);
+
+ const layoutManager = view._grid.layoutManager;
+ layoutManager.rows_per_page = rows;
+ layoutManager.columns_per_page = columns;
+
+ // this line is required by GS 43
+ view._grid.setGridModes([{ columns, rows }]);
+
+ this.child.set_style(`
+ width: ${width}px;
+ height: ${height}px;
+ padding: 30px;
+ `);
+
+ view._redisplay();
+
+ // store original item count
+ this._designCapacity = nItems;
+ },
+
+ _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;
+ if (!opt.APP_GRID_FOLDER_CENTER) {
+ const appDisplay = this._source._parentView;
+
+ dialogTargetX = Math.round(sourceCenterX - this.child.width / 2);
+ dialogTargetY = Math.round(sourceCenterY - this.child.height / 2);
+
+ // keep the dialog in appDisplay area if possible
+ dialogTargetX = Math.clamp(
+ dialogTargetX,
+ this.x + appDisplay.x,
+ this.x + appDisplay.x + appDisplay.width - this.child.width
+ );
+
+ dialogTargetY = Math.clamp(
+ dialogTargetY,
+ this.y + appDisplay.y,
+ this.y + appDisplay.y + appDisplay.height - this.child.height
+ );
+ // or at least in the monitor area
+ const monitor = global.display.get_monitor_geometry(global.display.get_primary_monitor());
+ dialogTargetX = Math.clamp(
+ dialogTargetX,
+ this.x + monitor.x,
+ this.x + monitor.x + monitor.width - this.child.width
+ );
+
+ dialogTargetY = Math.clamp(
+ dialogTargetY,
+ this.y + monitor.y,
+ this.y + monitor.y + monitor.height - this.child.height
+ );
+ }
+ const dialogOffsetX = -dialogX + dialogTargetX;
+ const dialogOffsetY = -dialogY + dialogTargetY;
+
+ 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.ease({
+ background_color: DIALOG_SHADE_NORMAL,
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ 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,
+ onComplete: () => {
+ // if the folder grid was build with the estimated icon item size because the real size wasn't available
+ // rebuild it with the real size now, after the folder was realized
+ if (this._needsUpdateSize) {
+ this._updateFolderSize();
+ this._view._redisplay();
+ this._needsUpdateSize = false;
+ }
+ },
+ });
+
+ 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;
+ }
+
+ let [sourceX, sourceY] =
+ this._source.get_transformed_position();
+ let [dialogX, dialogY] =
+ this.child.get_transformed_position();
+
+ this.ease({
+ background_color: Clutter.Color.from_pixel(0x00000000),
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ 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_OUT_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 = [];
+ },
+ });
+
+ this._needsZoomAndFade = false;
+ },
+
+ _setLighterBackground(lighter) {
+ const backgroundColor = lighter
+ ? DIALOG_SHADE_HIGHLIGHT
+ : DIALOG_SHADE_NORMAL;
+
+ this.ease({
+ backgroundColor,
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ },
+};
+
+// just make app grid to update all invalid positions that may be result of grid/icon size change
+function _updateIconPositions() {
+ const appDisplay = Main.overview._overview._controls._appDisplay;
+ const icons = [...appDisplay._orderedItems];
+ for (let i = 0; i < icons.length; i++)
+ appDisplay._moveItem(icons[i], -1, -1);
+}
+
+function _removeIcons() {
+ const appDisplay = Main.overview._overview._controls._appDisplay;
+ const icons = [...appDisplay._orderedItems];
+ for (let i = 0; i < icons.length; i++) {
+ const icon = icons[i];
+ if (icon._dialog)
+ Main.layoutManager.overviewGroup.remove_child(icon._dialog);
+ appDisplay._removeItem(icon);
+ icon.destroy();
+ }
+ appDisplay._folderIcons = [];
+}
+
+function _resetAppGrid(settings) {
+ const appDisplay = Main.overview._overview._controls._appDisplay;
+ // reset the grid only if called directly without args or if all folders where removed by using reset button in Settings window
+ // otherwise this function is called every time a user moves icon to another position as a settings callback
+ if (settings) {
+ const currentValue = JSON.stringify(global.settings.get_value('app-picker-layout').deep_unpack());
+ const emptyValue = JSON.stringify([]);
+ const customLayout = currentValue !== emptyValue;
+ // appDisplay._customLayout = customLayout;
+ if (customLayout)
+ return;
+ else
+ opt._appGridNeedsRedisplay = true;
+ }
+
+ // force update icon size using adaptToSize(). the page size cannot be the same as the current one
+ appDisplay._grid.layoutManager._pageWidth += 1;
+ appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight);
+ _removeIcons();
+ appDisplay._redisplay();
+ // force appDisplay to move all icons to proper positions and update all properties
+ GLib.idle_add(0, () => {
+ _updateIconPositions();
+ if (appDisplay._sortOrderedItemsAlphabetically) {
+ appDisplay._sortOrderedItemsAlphabetically();
+ appDisplay._grid.layoutManager._pageWidth += 1;
+ appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight);
+ appDisplay._setLinearPositions(appDisplay._orderedItems);
+ } else {
+ appDisplay._removeItem(appDisplay._orderedItems[0]);
+ appDisplay._redisplay();
+ }
+
+ appDisplay._redisplay();
+ });
+}
+
+function _getWindowApp(metaWin) {
+ const tracker = Shell.WindowTracker.get_default();
+ return tracker.get_window_app(metaWin);
+}
+
+function _getAppLastUsedWindow(app) {
+ let recentWin;
+ global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => {
+ const winApp = _getWindowApp(metaWin);
+ if (!recentWin && winApp === app)
+ recentWin = metaWin;
+ });
+ return recentWin;
+}
+
+function _getAppRecentWorkspace(app) {
+ const recentWin = _getAppLastUsedWindow(app);
+ if (recentWin)
+ return recentWin.get_workspace();
+
+ return null;
+}
+
+const AppIcon = {
+ after__init() {
+ // update the app label behavior
+ this._updateMultiline();
+ },
+
+ // avoid accepting by placeholder when dragging active preview
+ // and also by icon if alphabet or usage sorting are used
+ _canAccept(source) {
+ if (source._sourceItem)
+ source = source._sourceItem;
+ let view = AppDisplay._getViewFromIcon(source);
+
+ return source !== this &&
+ (source instanceof this.constructor) &&
+ (view instanceof AppDisplay.AppDisplay &&
+ !opt.APP_GRID_ORDER);
+ },
+};
+
+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
+ ? AppDisplay.APP_ICON_TITLE_EXPAND_TIME
+ : AppDisplay.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_ORDER)
+ 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
+ if (source._sourceItem) {
+ const app = source._sourceItem.app;
+ source._sourceFolder.removeApp(app);
+ }
+
+ return true;
+ },
+
+};
+
+const ActiveFolderIcon = GObject.registerClass(
+class ActiveFolderIcon extends AppDisplay.AppIcon {
+ _init(app) {
+ super._init(app, {
+ setSizeManually: true,
+ showLabel: false,
+ });
+ }
+
+ handleDragOver() {
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ acceptDrop() {
+ return false;
+ }
+
+ _onDragEnd() {
+ this._dragging = false;
+ this.undoScaleAndFade();
+ Main.overview.endItemDrag(this._sourceItem.icon);
+ }
+});
diff --git a/extensions/vertical-workspaces/lib/appFavorites.js b/extensions/vertical-workspaces/lib/appFavorites.js
new file mode 100644
index 0000000..50ebce9
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/appFavorites.js
@@ -0,0 +1,61 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * appFavorites.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+
+const { Shell } = imports.gi;
+const AppFavorites = imports.ui.appFavorites;
+const Main = imports.ui.main;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const _Util = Me.imports.lib.util;
+
+let opt;
+let _overrides;
+let _firstRun = true;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('appFavoritesModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+
+ if (_overrides)
+ _overrides.removeAll();
+
+
+ // if notifications are enabled no override is needed
+ if (reset || opt.SHOW_FAV_NOTIFICATION) {
+ _overrides = null;
+ opt = null;
+ return;
+ }
+
+ _overrides = new _Util.Overrides();
+
+ // AppFavorites.AppFavorites is const, first access returns undefined
+ const dummy = AppFavorites.AppFavorites;
+ _overrides.addOverride('AppFavorites', AppFavorites.AppFavorites.prototype, AppFavoritesCommon);
+}
+
+const AppFavoritesCommon = {
+ addFavoriteAtPos(appId, pos) {
+ this._addFavorite(appId, pos);
+ },
+
+ removeFavorite(appId) {
+ this._removeFavorite(appId);
+ },
+};
diff --git a/extensions/vertical-workspaces/dash.js b/extensions/vertical-workspaces/lib/dash.js
index c4e3101..bf832bd 100644
--- a/extensions/vertical-workspaces/dash.js
+++ b/extensions/vertical-workspaces/lib/dash.js
@@ -1,5 +1,5 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* dash.js
*
* @author GdH <G-dH@github.com>
@@ -8,7 +8,7 @@
* modified dash module of https://github.com/RensAlthuis/vertical-overview extension
*/
-const { Clutter, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi;
+const { Clutter, GObject, St, Shell, Meta } = imports.gi;
const AppDisplay = imports.ui.appDisplay;
const AppFavorites = imports.ui.appFavorites;
const DND = imports.ui.dnd;
@@ -16,42 +16,54 @@ const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Dash = imports.ui.dash;
-const { DashIcon, DashItemContainer, getAppFromSource, DragPlaceholderItem } = imports.ui.dash;
+const PopupMenu = imports.ui.popupMenu;
+const { AppMenu } = imports.ui.appMenu;
+const BoxPointer = imports.ui.boxpointer;
+const AltTab = imports.ui.altTab;
const Me = imports.misc.extensionUtils.getCurrentExtension();
-const Util = Me.imports.util;
-const _ = Me.imports.settings._;
+const Util = Me.imports.lib.util;
+const _ = Me.imports.lib.settings._;
-let verticalOverrides = {};
+const shellVersion = Util.shellVersion;
let _origWorkId;
let _newWorkId;
let _showAppsIconBtnPressId;
-// added values to achieve better ability to scale down according the available space
+// added values to achieve a better ability to scale down according to available space
var BaseIconSizes = [16, 24, 32, 40, 44, 48, 56, 64, 72, 80, 96, 112, 128];
-const RecentFilesSearchProviderPrefix = Me.imports.recentFilesSearchProvider.prefix;
-const WindowSearchProviderPrefix = Me.imports.windowSearchProvider.prefix;
+const RecentFilesSearchProviderPrefix = Me.imports.lib.recentFilesSearchProvider.prefix;
+const WindowSearchProviderPrefix = Me.imports.lib.windowSearchProvider.prefix;
let _overrides;
+let opt;
+let _firstRun = true;
const DASH_ITEM_LABEL_SHOW_TIME = 150;
-let opt;
-
function update(reset = false) {
- if (_overrides) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('dashModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+
+ if (_overrides)
_overrides.removeAll();
- }
-
- opt = Me.imports.settings.opt;
+
+
const dash = Main.overview._overview._controls.layoutManager._dash;
setToHorizontal();
- dash.remove_style_class_name("vertical-overview");
- dash.remove_style_class_name("vertical-overview-left");
- dash.remove_style_class_name("vertical-overview-right");
+ dash.remove_style_class_name('vertical');
+ dash.remove_style_class_name('vertical-left');
+ dash.remove_style_class_name('vertical-right');
if (reset) {
_moveDashAppGridIcon(reset);
@@ -60,6 +72,7 @@ function update(reset = false) {
_updateRecentFilesIcon(false);
dash.visible = true;
dash._background.opacity = 255;
+ dash._background.remove_style_class_name('v-shell-dash-background');
_overrides = null;
opt = null;
return;
@@ -67,13 +80,15 @@ function update(reset = false) {
_overrides = new Util.Overrides();
- _overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerOverride);
- _overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommonOverride);
+ _overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerCommon);
+ _overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommon);
+ _overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIconCommon);
+ _overrides.addOverride('DashIcon', Dash.DashIcon.prototype, DashIconCommon);
if (opt.DASH_VERTICAL) {
_overrides.addOverride('Dash', Dash.Dash.prototype, DashOverride);
setToVertical();
- dash.add_style_class_name("vertical-overview");
+ dash.add_style_class_name('vertical');
if (!_newWorkId) {
_origWorkId = dash._workId;
@@ -93,14 +108,15 @@ function update(reset = false) {
_moveDashAppGridIcon();
_connectShowAppsIcon();
- if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) {
+ if (dash._showWindowsIcon && !dash._showWindowsIconClickedId)
dash._showWindowsIconClickedId = dash._showWindowsIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(WindowSearchProviderPrefix));
- }
- if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) {
+
+ if (dash._recentFilesIcon && !dash._recentFilesIconClickedId)
dash._recentFilesIconClickedId = dash._recentFilesIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(RecentFilesSearchProviderPrefix));
- }
- Main.overview.dash._redisplay();
- Main.overview._overview._controls.layoutManager._dash.visible = opt.DASH_VISIBLE;
+
+ dash.visible = opt.DASH_VISIBLE;
+ dash._background.add_style_class_name('v-shell-dash-background');
+ dash._redisplay();
}
function setToVertical() {
@@ -128,13 +144,13 @@ function setToVertical() {
dash._queueRedisplay();
dash._adjustIconSize();
- dash.add_style_class_name(opt.DASH_LEFT ? 'vertical-overview-left' : 'vertical-overview-right');
+ dash.add_style_class_name(opt.DASH_LEFT ? 'vertical-left' : 'vertical-right');
}
function setToHorizontal() {
let dash = Main.overview._overview._controls.layoutManager._dash;
if (_origWorkId)
- dash._workId = _origWorkId; //pretty sure this is a leak, but there no provided way to disconnect these...
+ dash._workId = _origWorkId; // pretty sure this is a leak, but there no provided way to disconnect these...
dash._box.layout_manager.orientation = Clutter.Orientation.HORIZONTAL;
dash._dashContainer.layout_manager.orientation = Clutter.Orientation.HORIZONTAL;
dash._dashContainer.y_expand = true;
@@ -173,7 +189,7 @@ function _moveDashAppGridIcon(reset = false) {
const index = dash._dashContainer.get_children().length - 1;
dash._dashContainer.set_child_at_index(dash._showAppsIcon, index);
}
- if (!reset && appIconPosition === 2) {// 2 - hide
+ if (!reset && appIconPosition === 2) { // 2 - hide
const style = opt.DASH_VERTICAL ? 'show-apps-icon-vertical-hide' : 'show-apps-icon-horizontal-hide';
dash._showAppsIcon.add_style_class_name(style);
// for some reason even if the icon height in vertical mode should be set to 0 by the style, it stays visible in full size returning height 1px
@@ -191,29 +207,27 @@ function _connectShowAppsIcon(reset = false) {
Main.overview.dash._showAppsIcon.reactive = true;
_showAppsIconBtnPressId = Main.overview.dash._showAppsIcon.connect('button-press-event', (actor, event) => {
const button = event.get_button();
- if (button === Clutter.BUTTON_MIDDLE) {
+ if (button === Clutter.BUTTON_MIDDLE)
Util.openPreferences();
- } else if (button === Clutter.BUTTON_SECONDARY) {
+ else if (button === Clutter.BUTTON_SECONDARY)
Util.activateSearchProvider(WindowSearchProviderPrefix);
- } else {
+ else
return Clutter.EVENT_PROPAGATE;
- }
+ return Clutter.EVENT_STOP;
});
- } else {
- if (_showAppsIconBtnPressId) {
- Main.overview.dash._showAppsIcon.disconnect(_showAppsIconBtnPressId);
- _showAppsIconBtnPressId = 0;
- Main.overview.dash._showAppsIcon.reactive = false;
- }
+ } else if (_showAppsIconBtnPressId) {
+ Main.overview.dash._showAppsIcon.disconnect(_showAppsIconBtnPressId);
+ _showAppsIconBtnPressId = 0;
+ Main.overview.dash._showAppsIcon.reactive = false;
}
}
-var DashOverride = {
- handleDragOver: function (source, actor, _x, y, _time) {
- let app = getAppFromSource(source);
+const DashOverride = {
+ handleDragOver(source, actor, _x, y, _time) {
+ let app = Dash.getAppFromSource(source);
// Don't allow favoriting of transient apps
- if (app == null || app.is_window_backed())
+ if (app === null || app.is_window_backed())
return DND.DragMotionResult.NO_DROP;
if (!global.settings.is_writable('favorite-apps'))
@@ -257,7 +271,7 @@ var DashOverride = {
this._dragPlaceholderPos = pos;
// Don't allow positioning before or after self
- if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
+ if (favPos !== -1 && (pos === favPos || pos === favPos + 1)) {
this._clearDragPlaceholder();
return DND.DragMotionResult.CONTINUE;
}
@@ -273,7 +287,7 @@ var DashOverride = {
fadeIn = true;
}
- this._dragPlaceholder = new DragPlaceholderItem();
+ this._dragPlaceholder = new Dash.DragPlaceholderItem();
this._dragPlaceholder.child.set_width(this.iconSize / 2);
this._dragPlaceholder.child.set_height(this.iconSize);
this._box.insert_child_at_index(this._dragPlaceholder,
@@ -284,7 +298,7 @@ var DashOverride = {
if (!this._dragPlaceholder)
return DND.DragMotionResult.NO_DROP;
- let srcIsFavorite = favPos != -1;
+ let srcIsFavorite = favPos !== -1;
if (srcIsFavorite)
return DND.DragMotionResult.MOVE_DROP;
@@ -292,7 +306,7 @@ var DashOverride = {
return DND.DragMotionResult.COPY_DROP;
},
- _redisplay: function () {
+ _redisplay() {
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let running = this._appSystem.get_running();
@@ -343,7 +357,7 @@ var DashOverride = {
let newApp = newApps.length > newIndex ? newApps[newIndex] : null;
// No change at oldIndex/newIndex
- if (oldApp == newApp) {
+ if (oldApp === newApp) {
oldIndex++;
newIndex++;
continue;
@@ -361,7 +375,7 @@ var DashOverride = {
addedItems.push({
app: newApp,
item: this._createAppItem(newApp),
- pos: newIndex
+ pos: newIndex,
});
newIndex++;
continue;
@@ -370,10 +384,10 @@ var DashOverride = {
// App moved
let nextApp = newApps.length > newIndex + 1
? newApps[newIndex + 1] : null;
- let insertHere = nextApp && nextApp == oldApp;
+ let insertHere = nextApp && nextApp === oldApp;
let alreadyRemoved = removedActors.reduce((result, actor) => {
let removedApp = actor.child._delegate.app;
- return result || removedApp == newApp;
+ return result || removedApp === newApp;
}, false);
if (insertHere || alreadyRemoved) {
@@ -381,7 +395,7 @@ var DashOverride = {
addedItems.push({
app: newApp,
item: newItem,
- pos: newIndex + removedActors.length
+ pos: newIndex + removedActors.length,
});
newIndex++;
} else {
@@ -439,10 +453,10 @@ var DashOverride = {
width: this.iconSize,
height: 1,
});
- this._box.add_child(this._separator)
+ this._box.add_child(this._separator);
}
- //FIXME: separator placement is broken (also in original dash)
+ // FIXME: separator placement is broken (also in original dash)
let pos = nFavorites;
if (this._dragPlaceholder)
pos++;
@@ -456,8 +470,8 @@ var DashOverride = {
this._box.queue_relayout();
},
- _createAppItem: function (app) {
- let appIcon = new DashIcon(app);
+ _createAppItem(app) {
+ let appIcon = new Dash.DashIcon(app);
let indicator = appIcon._dot;
indicator.x_align = opt.DASH_LEFT ? Clutter.ActorAlign.START : Clutter.ActorAlign.END;
@@ -468,7 +482,7 @@ var DashOverride = {
this._itemMenuStateChanged(item, opened);
});
- let item = new DashItemContainer();
+ let item = new Dash.DashItemContainer();
item.setChild(appIcon);
// Override default AppIcon label_actor, now the
@@ -480,12 +494,12 @@ var DashOverride = {
this._hookUpLabel(item, appIcon);
return item;
- }
-}
+ },
+};
-var DashItemContainerOverride = {
+const DashItemContainerCommon = {
// move labels according dash position
- showLabel: function() {
+ showLabel() {
if (!this._labelText)
return;
@@ -500,7 +514,7 @@ var DashItemContainerOverride = {
const labelWidth = this.label.get_width();
const labelHeight = this.label.get_height();
- const xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ let xOffset = Math.floor((itemWidth - labelWidth) / 2);
let x = Math.clamp(stageX + xOffset, 0, global.stage.width - labelWidth);
let node = this.label.get_theme_node();
@@ -509,23 +523,18 @@ var DashItemContainerOverride = {
if (opt.DASH_TOP) {
const yOffset = itemHeight - labelHeight + 3 * node.get_length('-y-offset');
y = stageY + yOffset;
-
} else if (opt.DASH_BOTTOM) {
const yOffset = node.get_length('-y-offset');
y = stageY - this.label.height - yOffset;
-
} else if (opt.DASH_RIGHT) {
const yOffset = Math.floor((itemHeight - labelHeight) / 2);
-
- const xOffset = 4;
+ xOffset = 4;
x = stageX - xOffset - this.label.width;
y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight);
-
- } if (opt.DASH_LEFT) {
+ } else if (opt.DASH_LEFT) {
const yOffset = Math.floor((itemHeight - labelHeight) / 2);
-
- const xOffset = 4;
+ xOffset = 4;
x = stageX + this.width + xOffset;
y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight);
@@ -544,11 +553,17 @@ var DashItemContainerOverride = {
duration: DASH_ITEM_LABEL_SHOW_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
- }
-}
+ },
+};
+
+const DashCommon = {
+ // use custom BaseIconSizes and add support for custom icons
+ _adjustIconSize() {
+ // if a user launches multiple apps at once, this function may be called again before the previous call has finished
+ // as a result, new icons will not reach their full size, or will be missing, if adding a new icon and changing the dash size due to lack of space at the same time
+ if (this._adjustingInProgress)
+ return;
-var DashCommonOverride = {
- _adjustIconSize: function () {
// For the icon size, we only consider children which are "proper"
// icons (i.e. ignoring drag placeholders) and which are not
// animating out (which means they will be destroyed at the end of
@@ -560,18 +575,19 @@ var DashCommonOverride = {
!actor.animatingOut;
});
- // add new custom icons into the calculation
- if (this._showAppsIcon.visible) {
+ // add new custom icons to the list
+ if (this._showAppsIcon.visible)
iconChildren.push(this._showAppsIcon);
- }
- if (this._showWindowsIcon) {
+
+ if (this._showWindowsIcon)
iconChildren.push(this._showWindowsIcon);
- }
- if (this._recentFilesIcon) {
+
+ if (this._recentFilesIcon)
iconChildren.push(this._recentFilesIcon);
- }
- if (!iconChildren.length) return;
+
+ if (!iconChildren.length)
+ return;
if (this._maxWidth === -1 || this._maxHeight === -1)
return;
@@ -593,12 +609,14 @@ var DashCommonOverride = {
let firstButton = iconChildren[0].child;
let firstIcon = firstButton._delegate.icon;
- if (!firstIcon.icon) return;
+ if (!firstIcon.icon)
+ return;
// Enforce valid spacings during the size request
firstIcon.icon.ensure_style();
const [, , iconWidth, iconHeight] = firstIcon.icon.get_preferred_size();
const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size();
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let availWidth, availHeight, maxIconSize;
if (dashHorizontal) {
@@ -614,7 +632,7 @@ var DashCommonOverride = {
availHeight -= themeNode.get_vertical_padding();
availHeight -= buttonHeight - iconHeight;
- maxIconSize = Math.min(availWidth / iconChildren.length, availHeight, opt.MAX_ICON_SIZE);
+ maxIconSize = Math.min(availWidth / iconChildren.length, availHeight, opt.MAX_ICON_SIZE * scaleFactor);
} else {
availWidth = this._maxWidth;
availWidth -= this._background.get_theme_node().get_horizontal_padding();
@@ -626,10 +644,9 @@ var DashCommonOverride = {
(iconChildren.length - 1) * spacing +
2 * this._background.get_theme_node().get_vertical_padding();
- maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE);
+ maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE * scaleFactor);
}
- let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let iconSizes = BaseIconSizes.map(s => s * scaleFactor);
let newIconSize = BaseIconSizes[0];
@@ -638,8 +655,11 @@ var DashCommonOverride = {
newIconSize = BaseIconSizes[i];
}
- /*if (newIconSize == this.iconSize)
- return;*/
+ if (newIconSize === this.iconSize)
+ return;
+
+ // set the in-progress state here after all the possible cancels
+ this._adjustingInProgress = true;
let oldIconSize = this.iconSize;
this.iconSize = newIconSize;
@@ -665,7 +685,7 @@ var DashCommonOverride = {
// Scale the icon's texture to the previous size and
// tween to the new size
icon.icon.set_size(icon.icon.width * scale,
- icon.icon.height * scale);
+ icon.icon.height * scale);
icon.icon.ease({
width: targetWidth,
@@ -683,23 +703,351 @@ var DashCommonOverride = {
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
+
+ this._adjustingInProgress = false;
+ },
+};
+
+const DashIconCommon = {
+ after__init() {
+ if (opt.DASH_ICON_SCROLL) {
+ this._scrollConId = this.connect('scroll-event', _onScrollEvent.bind(this));
+ this._leaveConId = this.connect('leave-event', _onLeaveEvent.bind(this));
+ }
},
+};
+
+function _onScrollEvent(source, event) {
+ if ((this.app && !opt.DASH_ICON_SCROLL) || (this._isSearchWindowsIcon && !opt.SEARCH_WINDOWS_ICON_SCROLL)) {
+ if (this._scrollConId)
+ this.disconnect(this._scrollConId);
+ if (this._leaveConId)
+ this.disconnect(this._leaveConId);
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ let direction = Util.getScrollDirection(event);
+ if (direction === Clutter.ScrollDirection.UP)
+ direction = 1;
+ else if (direction === Clutter.ScrollDirection.DOWN)
+ direction = -1;
+ else
+ return Clutter.EVENT_STOP;
+
+ // avoid uncontrollable switching if smooth scroll wheel or trackpad is used
+ if (this._lastScroll && Date.now() - this._lastScroll < 160)
+ return Clutter.EVENT_STOP;
+
+ this._lastScroll = Date.now();
+
+ _switchWindow.bind(this)(direction);
+ return Clutter.EVENT_STOP;
}
-function _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON) {
+function _onLeaveEvent() {
+ if (!this._selectedMetaWin || this.has_pointer || this.toggleButton?.has_pointer)
+ return;
+
+ this._selectedPreview._activateSelected = false;
+ this._selectedMetaWin = null;
+ this._scrolledWindows = null;
+ _showWindowPreview.bind(this)(null);
+}
+
+function _switchWindow(direction) {
+ if (!this._scrolledWindows) {
+ // source is app icon
+ if (this.app) {
+ this._scrolledWindows = this.app.get_windows();
+ const wsList = [];
+ this._scrolledWindows.forEach(w => {
+ const ws = w.get_workspace();
+ if (!wsList.includes(ws))
+ wsList.push(ws);
+ });
+ // sort windows by workspaces in MRU order
+ this._scrolledWindows.sort((a, b) => wsList.indexOf(a.get_workspace()) > wsList.indexOf(b.get_workspace()));
+ // source is Search Windows icon
+ } else if (this._isSearchWindowsIcon) {
+ if (opt.SEARCH_WINDOWS_ICON_SCROLL === 1) // all windows
+ this._scrolledWindows = AltTab.getWindows(null);
+ else
+ this._scrolledWindows = AltTab.getWindows(global.workspace_manager.get_active_workspace());
+ }
+ }
+ let windows = this._scrolledWindows;
+
+ if (!windows.length)
+ return;
+
+ // if window selection is in the process, the previewed window must be the current one
+ let currentWin = this._selectedMetaWin ? this._selectedMetaWin : windows[0];
+
+ const currentIdx = windows.indexOf(currentWin);
+ let targetIdx = currentIdx + direction;
+
+ if (targetIdx > windows.length - 1)
+ targetIdx = 0;
+ else if (targetIdx < 0)
+ targetIdx = windows.length - 1;
+
+ const metaWin = windows[targetIdx];
+ _showWindowPreview.bind(this)(metaWin);
+ this._selectedMetaWin = metaWin;
+}
+
+function _showWindowPreview(metaWin) {
+ const views = Main.overview._overview.controls._workspacesDisplay._workspacesViews;
+ const viewsIter = [views[0]];
+ // secondary monitors use different structure
+ views.forEach(v => {
+ if (v._workspacesView)
+ viewsIter.push(v._workspacesView);
+ });
+
+ viewsIter.forEach(view => {
+ // if workspaces are on primary monitor only
+ if (!view || !view._workspaces)
+ return;
+
+ view._workspaces.forEach(ws => {
+ ws._windows.forEach(windowPreview => {
+ // metaWin === null resets opacity
+ let opacity = metaWin ? 50 : 255;
+ windowPreview._activateSelected = false;
+
+ // minimized windows are invisible if windows are not exposed (WORKSPACE_MODE === 0)
+ if (!windowPreview.opacity)
+ windowPreview.opacity = 255;
+
+ // app windows set to lower opacity, so they can be recognized
+ if (this._scrolledWindows && this._scrolledWindows.includes(windowPreview.metaWindow)) {
+ if (opt.DASH_ICON_SCROLL === 2)
+ opacity = 254;
+ }
+ if (windowPreview.metaWindow === metaWin) {
+ if (metaWin && metaWin.get_workspace() !== global.workspace_manager.get_active_workspace())
+ Main.wm.actionMoveWorkspace(metaWin.get_workspace());
+
+ windowPreview.get_parent().set_child_above_sibling(windowPreview, null);
+
+ opacity = 255;
+ this._selectedPreview = windowPreview;
+ windowPreview._activateSelected = true;
+ }
+
+ // if windows are exposed, highlight selected using opacity
+ if ((opt.OVERVIEW_MODE && opt.WORKSPACE_MODE) || !opt.OVERVIEW_MODE) {
+ if (metaWin && opacity === 255)
+ windowPreview.showOverlay(true);
+ else
+ windowPreview.hideOverlay(true);
+ windowPreview.ease({
+ duration: 200,
+ opacity,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ });
+ });
+ });
+}
+
+const AppIconCommon = {
+ activate(button) {
+ const event = Clutter.get_current_event();
+ const state = event ? event.get_state() : 0;
+ const isMiddleButton = button && button === Clutter.BUTTON_MIDDLE;
+ const isCtrlPressed = Util.isCtrlPressed(state);
+ const isShiftPressed = Util.isShiftPressed(state);
+ const openNewWindow = (this.app.can_open_new_window() &&
+ this.app.state === Shell.AppState.RUNNING &&
+ (isCtrlPressed || isMiddleButton) && !opt.DASH_CLICK_ACTION === 2) ||
+ (opt.DASH_CLICK_ACTION === 2 && !this._selectedMetaWin && !isMiddleButton);
+
+ const currentWS = global.workspace_manager.get_active_workspace();
+ const appRecentWorkspace = _getAppRecentWorkspace(this.app);
+ // this feature shouldn't affect search results, dash icons don't have labels, so we use them as a condition
+ const showWidowsBeforeActivation = opt.DASH_CLICK_ACTION === 1 && !this.icon.label;
+
+ let targetWindowOnCurrentWs = false;
+ if (opt.DASH_FOLLOW_RECENT_WIN) {
+ targetWindowOnCurrentWs = appRecentWorkspace === currentWS;
+ } else {
+ this.app.get_windows().forEach(
+ w => {
+ targetWindowOnCurrentWs = targetWindowOnCurrentWs || (w.get_workspace() === currentWS);
+ }
+ );
+ }
+
+ if ((this.app.state === Shell.AppState.STOPPED || openNewWindow) && !isShiftPressed)
+ this.animateLaunch();
+
+ if (openNewWindow) {
+ this.app.open_new_window(-1);
+ // if DASH_CLICK_ACTION == "SHOW_WINS_BEFORE", the app has more than one window and has no window on the current workspace,
+ // don't activate the app immediately, only move the overview to the workspace with the app's recent window
+ } else if (showWidowsBeforeActivation && !isShiftPressed && this.app.get_n_windows() > 1 && !targetWindowOnCurrentWs/* && !(opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE)*/) {
+ // this._scroll = true;
+ // this._scrollTime = Date.now();
+ Main.wm.actionMoveWorkspace(appRecentWorkspace);
+ Main.overview.dash.showAppsButton.checked = false;
+ return;
+ } else if (this._selectedMetaWin) {
+ this._selectedMetaWin.activate(global.get_current_time());
+ } else if (showWidowsBeforeActivation && opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE && !isShiftPressed && this.app.get_n_windows() > 1) {
+ // expose windows
+ Main.overview._overview._controls._thumbnailsBox._activateThumbnailAtPoint(0, 0, global.get_current_time(), true);
+ return;
+ } else if (opt.DASH_SHIFT_CLICK_MV && isShiftPressed && this.app.get_windows().length) {
+ this._moveAppToCurrentWorkspace();
+ return;
+ } else if (isShiftPressed) {
+ return;
+ } else {
+ this.app.activate();
+ }
+
+ Main.overview.hide();
+ },
+
+ _moveAppToCurrentWorkspace() {
+ this.app.get_windows().forEach(w => w.change_workspace(global.workspace_manager.get_active_workspace()));
+ },
+
+ popupMenu(side = St.Side.LEFT) {
+ if (shellVersion >= 42)
+ this.setForcedHighlight(true);
+ this._removeMenuTimeout();
+ this.fake_release();
+
+ if (!this._getWindowsOnCurrentWs) {
+ this._getWindowsOnCurrentWs = function () {
+ const winList = [];
+ this.app.get_windows().forEach(w => {
+ if (w.get_workspace() === global.workspace_manager.get_active_workspace())
+ winList.push(w);
+ });
+ return winList;
+ };
+
+ this._windowsOnOtherWs = function () {
+ return (this.app.get_windows().length - this._getWindowsOnCurrentWs().length) > 0;
+ };
+ }
+
+ if (!this._menu) {
+ this._menu = new AppMenu(this, side, {
+ favoritesSection: true,
+ showSingleWindows: true,
+ });
+
+ this._menu.setApp(this.app);
+ this._openSigId = this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ });
+ // Main.overview.connectObject('hiding',
+ this._hidingSigId = Main.overview.connect('hiding',
+ () => this._menu.close(), this);
+
+ Main.uiGroup.add_actor(this._menu.actor);
+ this._menuManager.addMenu(this._menu);
+ }
+
+ // once the menu is created, it stays unchanged and we need to modify our items based on current situation
+ if (this._addedMenuItems && this._addedMenuItems.length)
+ this._addedMenuItems.forEach(i => i.destroy());
+
+
+ const popupItems = [];
+
+ const separator = new PopupMenu.PopupSeparatorMenuItem();
+ this._menu.addMenuItem(separator);
+
+ if (this.app.get_n_windows()) {
+ // if (/* opt.APP_MENU_FORCE_QUIT*/true) {}
+ popupItems.push([_('Force Quit'), () => {
+ this.app.get_windows()[0].kill();
+ }]);
+
+ // if (opt.APP_MENU_CLOSE_WS) {}
+ const nWin = this._getWindowsOnCurrentWs().length;
+ if (nWin) {
+ popupItems.push([_(`Close ${nWin} Windows on Current Workspace`), () => {
+ const windows = this._getWindowsOnCurrentWs();
+ let time = global.get_current_time();
+ for (let win of windows) {
+ // increase time by 1 ms for each window to avoid errors from GS
+ win.delete(time++);
+ }
+ }]);
+ }
+
+ if (/* opt.APP_MENU_MV_TO_WS && */this._windowsOnOtherWs())
+ popupItems.push([_('Move App to Current Workspace ( Shift + Click )'), this._moveAppToCurrentWorkspace]);
+ }
+
+ this._addedMenuItems = [];
+ this._addedMenuItems.push(separator);
+ popupItems.forEach(i => {
+ let item = new PopupMenu.PopupMenuItem(i[0]);
+ this._menu.addMenuItem(item);
+ item.connect('activate', i[1].bind(this));
+ this._addedMenuItems.push(item);
+ });
+
+ this.emit('menu-state-changed', true);
+
+ this._menu.open(BoxPointer.PopupAnimation.FULL);
+ this._menuManager.ignoreRelease();
+ this.emit('sync-tooltip');
+
+ return false;
+ },
+};
+
+function _getWindowApp(metaWin) {
+ const tracker = Shell.WindowTracker.get_default();
+ return tracker.get_window_app(metaWin);
+}
+
+function _getAppLastUsedWindow(app) {
+ let recentWin;
+ global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => {
+ const winApp = _getWindowApp(metaWin);
+ if (!recentWin && winApp === app)
+ recentWin = metaWin;
+ });
+ return recentWin;
+}
+
+function _getAppRecentWorkspace(app) {
+ const recentWin = _getAppLastUsedWindow(app);
+ if (recentWin)
+ return recentWin.get_workspace();
+
+ return null;
+}
+
+function _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON) {
const dash = Main.overview._overview._controls.layoutManager._dash;
const dashContainer = dash._dashContainer;
if (dash._showWindowsIcon) {
dashContainer.remove_child(dash._showWindowsIcon);
- dash._showWindowsIconClickedId && dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId);
+ if (dash._showWindowsIconClickedId)
+ dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId);
dash._showWindowsIconClickedId = undefined;
- dash._showWindowsIcon && dash._showWindowsIcon.destroy();
+ if (dash._showWindowsIcon)
+ dash._showWindowsIcon.destroy();
dash._showWindowsIcon = undefined;
}
- if (!show || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) return;
+ if (!show || !opt.WINDOW_SEARCH_PROVIDER_ENABLED)
+ return;
if (!dash._showWindowsIcon) {
dash._showWindowsIcon = new ShowWindowsIcon();
@@ -708,22 +1056,23 @@ function _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON) {
dash._hookUpLabel(dash._showWindowsIcon);
}
- dash._showWindowsIcon.icon.setIconSize(opt.MAX_ICON_SIZE);
+ dash._showWindowsIcon.icon.setIconSize(dash.iconSize);
if (opt.SHOW_WINDOWS_ICON === 1) {
dashContainer.set_child_at_index(dash._showWindowsIcon, 0);
} else if (opt.SHOW_WINDOWS_ICON === 2) {
- index = dashContainer.get_children().length - 1;
+ const index = dashContainer.get_children().length - 1;
dashContainer.set_child_at_index(dash._showWindowsIcon, index);
}
Main.overview._overview._controls.layoutManager._dash._adjustIconSize();
}
-var ShowWindowsIcon = GObject.registerClass(
+const ShowWindowsIcon = GObject.registerClass(
class ShowWindowsIcon extends Dash.DashItemContainer {
_init() {
super._init();
+ this._isSearchWindowsIcon = true;
this._labelText = _('Search Open Windows (Hotkey: Space)');
this.toggleButton = new St.Button({
style_class: 'show-apps',
@@ -744,6 +1093,12 @@ class ShowWindowsIcon extends Dash.DashItemContainer {
this.toggleButton._delegate = this;
this.setChild(this.toggleButton);
+
+ if (opt.SEARCH_WINDOWS_ICON_SCROLL) {
+ this.reactive = true;
+ this._scrollConId = this.connect('scroll-event', _onScrollEvent.bind(this));
+ this._leaveConId = this.connect('leave-event', _onLeaveEvent.bind(this));
+ }
}
_createIcon(size) {
@@ -758,19 +1113,21 @@ class ShowWindowsIcon extends Dash.DashItemContainer {
});
function _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON) {
-
const dash = Main.overview._overview._controls.layoutManager._dash;
const dashContainer = dash._dashContainer;
if (dash._recentFilesIcon) {
dashContainer.remove_child(dash._recentFilesIcon);
- dash._recentFilesIconClickedId && dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId);
+ if (dash._recentFilesIconClickedId)
+ dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId);
dash._recentFilesIconClickedId = undefined;
- dash._recentFilesIcon && dash._recentFilesIcon.destroy();
+ if (dash._recentFilesIcon)
+ dash._recentFilesIcon.destroy();
dash._recentFilesIcon = undefined;
}
- if (!show || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED) return;
+ if (!show || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED)
+ return;
if (!dash._recentFilesIcon) {
dash._recentFilesIcon = new ShowRecentFilesIcon();
@@ -779,18 +1136,18 @@ function _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON) {
dash._hookUpLabel(dash._recentFilesIcon);
}
- dash._recentFilesIcon.icon.setIconSize(opt.MAX_ICON_SIZE);
+ dash._recentFilesIcon.icon.setIconSize(dash.iconSize);
if (opt.SHOW_RECENT_FILES_ICON === 1) {
dashContainer.set_child_at_index(dash._recentFilesIcon, 0);
} else if (opt.SHOW_RECENT_FILES_ICON === 2) {
- index = dashContainer.get_children().length - 1;
+ const index = dashContainer.get_children().length - 1;
dashContainer.set_child_at_index(dash._recentFilesIcon, index);
}
Main.overview._overview._controls.layoutManager._dash._adjustIconSize();
}
-var ShowRecentFilesIcon = GObject.registerClass(
+const ShowRecentFilesIcon = GObject.registerClass(
class ShowRecentFilesIcon extends Dash.DashItemContainer {
_init() {
super._init();
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;
+ },
+};
diff --git a/extensions/vertical-workspaces/lib/layout.js b/extensions/vertical-workspaces/lib/layout.js
new file mode 100644
index 0000000..f6562fd
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/layout.js
@@ -0,0 +1,380 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * layout.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+
+const { Meta, GLib, Shell, Clutter, GObject } = imports.gi;
+
+const Main = imports.ui.main;
+const Layout = imports.ui.layout;
+const Ripples = imports.ui.ripples;
+const DND = imports.ui.dnd;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const _Util = Me.imports.lib.util;
+
+let _overrides;
+let _timeouts;
+let opt;
+let _firstRun = true;
+let _originalUpdateHotCorners;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('layoutModule', true);
+ const conflict = _Util.getEnabledExtensions('custom-hot-corners').length ||
+ _Util.getEnabledExtensions('dash-to-panel').length;
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled or in conflict
+ if (_firstRun && (reset || conflict))
+ return;
+
+ _firstRun = false;
+
+ if (!_originalUpdateHotCorners)
+ _originalUpdateHotCorners = Layout.LayoutManager.prototype._updateHotCorners;
+
+ if (_overrides)
+ _overrides.removeAll();
+
+ if (_timeouts) {
+ Object.values(_timeouts).forEach(t => {
+ if (t)
+ GLib.source_remove(t);
+ });
+ }
+
+ if (reset) {
+ _overrides = null;
+ opt = null;
+ _timeouts = null;
+ Main.layoutManager._updateHotCorners = _originalUpdateHotCorners;
+ Main.layoutManager._updateHotCorners();
+ return;
+ }
+
+ _timeouts = {};
+
+ _overrides = new _Util.Overrides();
+ _overrides.addOverride('LayoutManager', Layout.LayoutManager.prototype, LayoutManagerCommon);
+
+ Main.layoutManager._updateHotCorners = LayoutManagerCommon._updateHotCorners.bind(Main.layoutManager);
+
+ Main.layoutManager._updatePanelBarrier();
+ Main.layoutManager._updateHotCorners();
+}
+
+const LayoutManagerCommon = {
+ _updatePanelBarrier() {
+ if (this._rightPanelBarrier) {
+ this._rightPanelBarrier.destroy();
+ this._rightPanelBarrier = null;
+ }
+
+ if (this._leftPanelBarrier) {
+ this._leftPanelBarrier.destroy();
+ this._leftPanelBarrier = null;
+ }
+
+ if (!this.primaryMonitor || !opt)
+ return;
+
+ if (this.panelBox.height) {
+ let primary = this.primaryMonitor;
+ if ([0, 1, 3].includes(opt.HOT_CORNER_POSITION)) {
+ this._rightPanelBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: primary.x + primary.width, y1: this.panelBox.allocation.y1,
+ x2: primary.x + primary.width, y2: this.panelBox.allocation.y2,
+ directions: Meta.BarrierDirection.NEGATIVE_X,
+ });
+ }
+
+ if ([2, 4].includes(opt.HOT_CORNER_POSITION)) {
+ this._leftPanelBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: primary.x, y1: this.panelBox.allocation.y1,
+ x2: primary.x, y2: this.panelBox.allocation.y2,
+ directions: Meta.BarrierDirection.POSITIVE_X,
+ });
+ }
+ }
+ },
+
+ _updateHotCorners() {
+ // avoid errors if called from foreign override
+ if (!opt)
+ return;
+ // destroy old hot corners
+ this.hotCorners.forEach(corner => corner?.destroy());
+ this.hotCorners = [];
+
+ if (!this._interfaceSettings.get_boolean('enable-hot-corners')) {
+ this.emit('hot-corners-changed');
+ return;
+ }
+
+ let size = this.panelBox.height;
+
+ // position 0 - default, 1-TL, 2-TR, 3-BL, 4-BR
+ const position = opt.HOT_CORNER_POSITION;
+
+ // build new hot corners
+ for (let i = 0; i < this.monitors.length; i++) {
+ let monitor = this.monitors[i];
+ let cornerX, cornerY;
+
+ if (position === 0) {
+ cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
+ cornerY = monitor.y;
+ } else if (position === 1) {
+ cornerX = monitor.x;
+ cornerY = monitor.y;
+ } else if (position === 2) {
+ cornerX = monitor.x + monitor.width;
+ cornerY = monitor.y;
+ } else if (position === 3) {
+ cornerX = monitor.x;
+ cornerY = monitor.y + monitor.height;
+ } else {
+ cornerX = monitor.x + monitor.width;
+ cornerY = monitor.y + monitor.height;
+ }
+
+ let haveCorner = true;
+
+ if (i !== this.primaryIndex) {
+ // Check if we have a top left (right for RTL) corner.
+ // I.e. if there is no monitor directly above or to the left(right)
+ let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
+ let besideY = cornerY;
+ let aboveX = cornerX;
+ let aboveY = cornerY - 1;
+
+ for (let j = 0; j < this.monitors.length; j++) {
+ if (i === j)
+ continue;
+ let otherMonitor = this.monitors[j];
+ if (besideX >= otherMonitor.x &&
+ besideX < otherMonitor.x + otherMonitor.width &&
+ besideY >= otherMonitor.y &&
+ besideY < otherMonitor.y + otherMonitor.height) {
+ haveCorner = false;
+ break;
+ }
+ if (aboveX >= otherMonitor.x &&
+ aboveX < otherMonitor.x + otherMonitor.width &&
+ aboveY >= otherMonitor.y &&
+ aboveY < otherMonitor.y + otherMonitor.height) {
+ haveCorner = false;
+ break;
+ }
+ }
+ }
+
+ if (haveCorner) {
+ let corner = new HotCorner(this, monitor, cornerX, cornerY);
+ corner.setBarrierSize(size);
+ this.hotCorners.push(corner);
+ } else {
+ this.hotCorners.push(null);
+ }
+ }
+
+ this.emit('hot-corners-changed');
+ },
+};
+
+var HotCorner = GObject.registerClass(
+class HotCorner extends Layout.HotCorner {
+ _init(layoutManager, monitor, x, y) {
+ super._init(layoutManager, monitor, x, y);
+
+ let angle = 0;
+ switch (opt.HOT_CORNER_POSITION) {
+ case 2:
+ angle = 90;
+ break;
+ case 3:
+ angle = 270;
+ break;
+ case 4:
+ angle = 180;
+ break;
+ }
+
+ this._ripples._ripple1.rotation_angle_z = angle;
+ this._ripples._ripple2.rotation_angle_z = angle;
+ this._ripples._ripple3.rotation_angle_z = angle;
+ }
+
+ setBarrierSize(size) {
+ if (this._verticalBarrier) {
+ this._pressureBarrier.removeBarrier(this._verticalBarrier);
+ this._verticalBarrier.destroy();
+ this._verticalBarrier = null;
+ }
+
+ if (this._horizontalBarrier) {
+ this._pressureBarrier.removeBarrier(this._horizontalBarrier);
+ this._horizontalBarrier.destroy();
+ this._horizontalBarrier = null;
+ }
+
+ if (size > 0) {
+ const primaryMonitor = global.display.get_primary_monitor();
+ const monitor = this._monitor;
+ const extendV = opt && opt.HOT_CORNER_EDGE && opt.DASH_VERTICAL && monitor.index === primaryMonitor;
+ const extendH = opt && opt.HOT_CORNER_EDGE && !opt.DASH_VERTICAL && monitor.index === primaryMonitor;
+
+ if (opt.HOT_CORNER_POSITION <= 1) {
+ this._verticalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x, y1: this._y, y2: this._y + (extendV ? monitor.height : size),
+ directions: Meta.BarrierDirection.POSITIVE_X,
+ });
+ this._horizontalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x + (extendH ? monitor.width : size), y1: this._y, y2: this._y,
+ directions: Meta.BarrierDirection.POSITIVE_Y,
+ });
+ } else if (opt.HOT_CORNER_POSITION === 2) {
+ this._verticalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x, y1: this._y, y2: this._y + (extendV ? monitor.height : size),
+ directions: Meta.BarrierDirection.NEGATIVE_X,
+ });
+ this._horizontalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x - size, x2: this._x, y1: this._y, y2: this._y,
+ directions: Meta.BarrierDirection.POSITIVE_Y,
+ });
+ } else if (opt.HOT_CORNER_POSITION === 3) {
+ this._verticalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x, y1: this._y, y2: this._y - size,
+ directions: Meta.BarrierDirection.POSITIVE_X,
+ });
+ this._horizontalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x + (extendH ? monitor.width : size), y1: this._y, y2: this._y,
+ directions: Meta.BarrierDirection.NEGATIVE_Y,
+ });
+ } else if (opt.HOT_CORNER_POSITION === 4) {
+ this._verticalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x, y1: this._y, y2: this._y - size,
+ directions: Meta.BarrierDirection.NEGATIVE_X,
+ });
+ this._horizontalBarrier = new Meta.Barrier({
+ display: global.display,
+ x1: this._x, x2: this._x - size, y1: this._y, y2: this._y,
+ directions: Meta.BarrierDirection.NEGATIVE_Y,
+ });
+ }
+
+ this._pressureBarrier.addBarrier(this._verticalBarrier);
+ this._pressureBarrier.addBarrier(this._horizontalBarrier);
+ }
+ }
+
+ _toggleOverview() {
+ if (!opt.HOT_CORNER_ACTION || (!opt.HOT_CORNER_FULLSCREEN && this._monitor.inFullscreen && !Main.overview.visible))
+ return;
+
+ if (Main.overview.shouldToggleByCornerOrButton()) {
+ if ((opt.HOT_CORNER_ACTION === 1 && !_Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 2 && _Util.isCtrlPressed()))
+ this._toggleWindowPicker(true);
+ else if ((opt.HOT_CORNER_ACTION === 2 && !_Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 1 && _Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 3 && _Util.isCtrlPressed()))
+ this._toggleApplications(true);
+ else if (opt.HOT_CORNER_ACTION === 3 && !_Util.isCtrlPressed())
+ this._toggleWindowSearchProvider();
+ if (opt.HOT_CORNER_RIPPLES && Main.overview.animationInProgress)
+ this._ripples.playAnimation(this._x, this._y);
+ }
+ }
+
+ _toggleWindowPicker(leaveOverview = false) {
+ if (Main.overview._shown && (leaveOverview || !Main.overview.dash.showAppsButton.checked)) {
+ Main.overview.hide();
+ } else if (Main.overview.dash.showAppsButton.checked) {
+ Main.overview.dash.showAppsButton.checked = false;
+ } else {
+ const focusWindow = global.display.get_focus_window();
+ // at least GS 42 is unable to show overview in X11 session if VirtualBox Machine window grabbed keyboard
+ if (!Meta.is_wayland_compositor() && focusWindow && focusWindow.wm_class.includes('VirtualBox Machine')) {
+ // following should help when windowed VBox Machine has focus.
+ global.stage.set_key_focus(Main.panel);
+ // key focus doesn't take the effect immediately, we must wait for it
+ // still looking for better solution!
+ _timeouts.releaseKeyboardTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ // delay cannot be too short
+ 200,
+ () => {
+ Main.overview.show();
+
+ _timeouts.releaseKeyboardTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+ );
+ } else {
+ Main.overview.show();
+ }
+ }
+ }
+
+ _toggleApplications(leaveOverview = false) {
+ if ((leaveOverview && Main.overview._shown) || Main.overview.dash.showAppsButton.checked) {
+ Main.overview.hide();
+ } else {
+ const focusWindow = global.display.get_focus_window();
+ // at least GS 42 is unable to show overview in X11 session if VirtualBox Machine window grabbed keyboard
+ if (!Meta.is_wayland_compositor() && focusWindow && focusWindow.wm_class.includes('VirtualBox Machine')) {
+ // following should help when windowed VBox Machine has focus.
+ global.stage.set_key_focus(Main.panel);
+ // key focus doesn't take the effect immediately, we must wait for it
+ // still looking for better solution!
+ _timeouts.releaseKeyboardTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ // delay cannot be too short
+ 200,
+ () => {
+ Main.overview.show(2);
+
+ _timeouts.releaseKeyboardTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+ );
+ } else if (Main.overview._shown) {
+ Main.overview.dash.showAppsButton.checked = true;
+ } else {
+ Main.overview.show(2); // 2 for App Grid
+ }
+ }
+ }
+
+ _toggleWindowSearchProvider() {
+ if (!Main.overview._overview._controls._searchController._searchActive) {
+ this._toggleWindowPicker();
+ const prefix = 'wq// ';
+ const position = prefix.length;
+ const searchEntry = Main.overview.searchEntry;
+ searchEntry.set_text(prefix);
+ // searchEntry.grab_key_focus();
+ searchEntry.get_first_child().set_cursor_position(position);
+ searchEntry.get_first_child().set_selection(position, position);
+ } else {
+ // Main.overview.searchEntry.text = '';
+ Main.overview.hide();
+ }
+ }
+});
diff --git a/extensions/vertical-workspaces/lib/messageTray.js b/extensions/vertical-workspaces/lib/messageTray.js
new file mode 100644
index 0000000..b35541a
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/messageTray.js
@@ -0,0 +1,67 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * messageTray.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+
+const { Clutter } = imports.gi;
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const Main = imports.ui.main;
+
+let opt;
+let _firstRun = true;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('messageTrayModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+
+ if (reset) {
+ opt = null;
+ setNotificationPosition(1);
+ return;
+ }
+
+ setNotificationPosition(opt.NOTIFICATION_POSITION);
+}
+
+function setNotificationPosition(position) {
+ switch (position) {
+ case 0:
+ Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.START;
+ Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START;
+ break;
+ case 1:
+ Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.CENTER;
+ Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START;
+ break;
+ case 2:
+ Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.END;
+ Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START;
+ break;
+ case 3:
+ Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.START;
+ Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END;
+ break;
+ case 4:
+ Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.CENTER;
+ Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END;
+ break;
+ case 5:
+ Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.END;
+ Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END;
+ break;
+ }
+}
diff --git a/extensions/vertical-workspaces/optionsFactory.js b/extensions/vertical-workspaces/lib/optionsFactory.js
index bfaaec6..da62dd1 100644
--- a/extensions/vertical-workspaces/optionsFactory.js
+++ b/extensions/vertical-workspaces/lib/optionsFactory.js
@@ -1,5 +1,5 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* optionsFactory.js
*
* @author GdH <G-dH@github.com>
@@ -13,16 +13,25 @@ const { Gtk, Gio, GObject } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
-const Settings = Me.imports.settings;
+const Settings = Me.imports.lib.settings;
const shellVersion = Settings.shellVersion;
// gettext
const _ = Settings._;
+const ProfileNames = [
+ _('GNOME 3'),
+ _('GNOME 40+ - Bottom Hot Edge'),
+ _('Hot Corner Centric - Top Left Hot Corner'),
+ _('Dock Overview - Bottom Hot Edge'),
+];
+
// libadwaita is available starting with GNOME Shell 42.
let Adw = null;
-try { Adw = imports.gi.Adw; } catch (e) {}
+try {
+ Adw = imports.gi.Adw;
+} catch (e) {}
function _newImageFromIconName(name) {
return Gtk.Image.new_from_icon_name(name);
@@ -35,7 +44,6 @@ var ItemFactory = class ItemFactory {
}
getRowWidget(text, caption, widget, variable, options = []) {
-
let item = [];
let label;
if (widget) {
@@ -55,9 +63,9 @@ var ItemFactory = class ItemFactory {
const captionLabel = new Gtk.Label({
halign: Gtk.Align.START,
wrap: true,
- /*width_chars: 80,*/
- xalign: 0
- })
+ /* width_chars: 80, */
+ xalign: 0,
+ });
const context = captionLabel.get_style_context();
context.add_class('dim-label');
context.add_class('caption');
@@ -79,25 +87,24 @@ var ItemFactory = class ItemFactory {
}
if (widget) {
- if (widget._is_switch) {
+ if (widget._isSwitch)
this._connectSwitch(widget, key, variable);
- } else if (widget._is_spinbutton || widget._is_scale) {
+ else if (widget._isSpinButton || widget._isScale)
this._connectSpinButton(widget, key, variable);
- } else if (widget._is_combo_box) {
+ else if (widget._isComboBox)
this._connectComboBox(widget, key, variable, options);
- } else if (widget._is_drop_down) {
+ else if (widget._isDropDown)
this._connectDropDown(widget, key, variable, options);
- }
}
return item;
}
- _connectSwitch(widget, key, variable) {
+ _connectSwitch(widget, key /* , variable */) {
this._settings.bind(key, widget, 'active', Gio.SettingsBindFlags.DEFAULT);
}
- _connectSpinButton(widget, key, variable) {
+ _connectSpinButton(widget, key /* , variable */) {
this._settings.bind(key, widget.adjustment, 'value', Gio.SettingsBindFlags.DEFAULT);
}
@@ -108,9 +115,9 @@ var ItemFactory = class ItemFactory {
for (const [label, value] of options) {
let iter;
model.set(iter = model.append(), [0, 1], [label, value]);
- if (value === currentValue) {
+ if (value === currentValue)
widget.set_active_iter(iter);
- }
+
widget._comboMap[value] = iter;
}
this._gOptions.connect(`changed::${key}`, () => {
@@ -119,7 +126,8 @@ var ItemFactory = class ItemFactory {
widget.connect('changed', () => {
const [success, iter] = widget.get_active_iter();
- if (!success) return;
+ if (!success)
+ return;
this._gOptions.set(variable, model.get_value(iter, 1));
});
@@ -132,23 +140,22 @@ var ItemFactory = class ItemFactory {
const text = options[i][0];
const id = options[i][1];
model.append(new DropDownItem({ text, id }));
- if (id === currentValue) {
+ if (id === currentValue)
widget.set_selected(i);
- }
}
const factory = new Gtk.SignalListItemFactory();
- factory.connect("setup", (factory, list_item) => {
- const label = new Gtk.Label({xalign: 0});
- list_item.set_child(label);
+ factory.connect('setup', (fact, listItem) => {
+ const label = new Gtk.Label({ xalign: 0 });
+ listItem.set_child(label);
});
- factory.connect("bind", (factory, list_item) => {
- const label = list_item.get_child();
- const item = list_item.get_item();
+ factory.connect('bind', (fact, listItem) => {
+ const label = listItem.get_child();
+ const item = listItem.get_item();
label.set_text(item.text);
});
- widget.connect("notify::selected-item", (dropDown) => {
+ widget.connect('notify::selected-item', dropDown => {
const item = dropDown.get_selected_item();
this._gOptions.set(variable, item.id);
});
@@ -157,9 +164,8 @@ var ItemFactory = class ItemFactory {
const newId = this._gOptions.get(variable, true);
for (let i = 0; i < options.length; i++) {
const id = options[i][1];
- if (id === newId) {
+ if (id === newId)
widget.set_selected(i);
- }
}
});
@@ -172,7 +178,7 @@ var ItemFactory = class ItemFactory {
valign: Gtk.Align.CENTER,
hexpand: true,
});
- sw._is_switch = true;
+ sw._isSwitch = true;
return sw;
}
@@ -185,7 +191,7 @@ var ItemFactory = class ItemFactory {
xalign: 0.5,
});
spinButton.set_adjustment(adjustment);
- spinButton._is_spinbutton = true;
+ spinButton._isSpinButton = true;
return spinButton;
}
@@ -201,20 +207,20 @@ var ItemFactory = class ItemFactory {
const renderer = new Gtk.CellRendererText();
comboBox.pack_start(renderer, true);
comboBox.add_attribute(renderer, 'text', 0);
- comboBox._is_combo_box = true;
+ comboBox._isComboBox = true;
return comboBox;
}
newDropDown() {
const dropDown = new Gtk.DropDown({
model: new Gio.ListStore({
- item_type: DropDownItem
+ item_type: DropDownItem,
}),
halign: Gtk.Align.END,
valign: Gtk.Align.CENTER,
hexpand: true,
});
- dropDown._is_drop_down = true;
+ dropDown._isDropDown = true;
return dropDown;
}
@@ -232,7 +238,7 @@ var ItemFactory = class ItemFactory {
});
scale.set_size_request(300, -1);
scale.set_adjustment(adjustment);
- scale._is_scale = true;
+ scale._isScale = true;
return scale;
}
@@ -258,13 +264,95 @@ var ItemFactory = class ItemFactory {
return linkBtn;
}
- newResetButton(callback) {
+ newButton() {
const btn = new Gtk.Button({
halign: Gtk.Align.END,
valign: Gtk.Align.CENTER,
hexpand: true,
+ });
+
+ btn._activatable = true;
+ return btn;
+ }
+
+ newPresetButton(opt, profileIndex) {
+ const load = opt.loadProfile.bind(opt);
+ const save = opt.storeProfile.bind(opt);
+ const reset = opt.resetProfile.bind(opt);
+
+ const box = new Gtk.Box({
+ halign: Gtk.Align.END,
+ valign: Gtk.Align.CENTER,
+ hexpand: true,
+ spacing: 8,
+ });
+ box.is_profile_box = true;
+
+ const entry = new Gtk.Entry({
+ width_chars: 40,
+ halign: Gtk.Align.END,
+ valign: Gtk.Align.CENTER,
+ hexpand: true,
+ xalign: 0,
+ });
+ entry.set_text(opt.get(`profileName${profileIndex}`));
+ entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, 'edit-clear-symbolic');
+ entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, true);
+ entry.connect('icon-press', e => e.set_text(''));
+ entry.connect('changed', e => opt.set(`profileName${profileIndex}`, e.get_text()));
+
+ const resetProfile = this.newButton();
+ resetProfile.set({
+ tooltip_text: _('Reset profile to defaults'),
+ icon_name: 'edit-delete-symbolic',
+ hexpand: false,
css_classes: ['destructive-action'],
- icon_name: 'view-refresh-symbolic'
+ });
+
+ function setName() {
+ let name = opt.get(`profileName${profileIndex}`, true);
+ if (!name)
+ name = ProfileNames[profileIndex - 1];
+ entry.set_text(name);
+ }
+
+ setName();
+ resetProfile.connect('clicked', () => {
+ reset(profileIndex);
+ setName();
+ });
+ resetProfile._activatable = false;
+
+ const loadProfile = this.newButton();
+ loadProfile.set({
+ tooltip_text: _('Load profile'),
+ icon_name: 'view-refresh-symbolic',
+ hexpand: false,
+ });
+ loadProfile.connect('clicked', () => load(profileIndex));
+ loadProfile._activatable = false;
+
+ const saveProfile = this.newButton();
+ saveProfile.set({
+ tooltip_text: _('Save current settings into this profile'),
+ icon_name: 'document-save-symbolic',
+ hexpand: false,
+ });
+ saveProfile.connect('clicked', () => save(profileIndex));
+ saveProfile._activatable = false;
+
+ box.append(resetProfile);
+ box.append(entry);
+ box.append(saveProfile);
+ box.append(loadProfile);
+ return box;
+ }
+
+ newResetButton(callback) {
+ const btn = this.newButton();
+ btn.set({
+ css_classes: ['destructive-action'],
+ icon_name: 'edit-delete-symbolic',
});
btn.connect('clicked', callback);
@@ -278,7 +366,7 @@ var ItemFactory = class ItemFactory {
valign: Gtk.Align.CENTER,
hexpand: true,
css_classes: ['destructive-action'],
- icon_name: 'view-refresh-symbolic'
+ icon_name: 'edit-delete-symbolic',
});
btn.connect('clicked', () => {
@@ -290,7 +378,7 @@ var ItemFactory = class ItemFactory {
btn._activatable = false;
return btn;
}
-}
+};
var AdwPrefs = class {
constructor(gOptions) {
@@ -300,13 +388,13 @@ var AdwPrefs = class {
getFilledWindow(window, pages) {
for (let page of pages) {
const title = page.title;
- const icon_name = page.iconName;
+ const iconName = page.iconName;
const optionList = page.optionList;
window.add(
this._getAdwPage(optionList, {
title,
- icon_name
+ icon_name: iconName,
})
);
}
@@ -326,13 +414,13 @@ var AdwPrefs = class {
const option = item[0];
const widget = item[1];
if (!widget) {
- if (group) {
+ if (group)
page.add(group);
- }
+
group = new Adw.PreferencesGroup({
title: option,
hexpand: true,
- width_request: 700
+ width_request: 700,
});
continue;
}
@@ -349,25 +437,25 @@ var AdwPrefs = class {
margin_top: 8,
margin_bottom: 8,
hexpand: true,
- })
- /*for (let i of item) {
+ });
+ /* for (let i of item) {
box.append(i);*/
grid.attach(option, 0, 0, 1, 1);
- if (widget) {
+ if (widget)
grid.attach(widget, 1, 0, 1, 1);
- }
+
row.set_child(grid);
- if (widget._activatable === false) {
+ if (widget._activatable === false)
row.activatable = false;
- } else {
+ else
row.activatable_widget = widget;
- }
+
group.add(row);
}
page.add(group);
return page;
}
-}
+};
var LegacyPrefs = class {
constructor(gOptions) {
@@ -376,14 +464,14 @@ var LegacyPrefs = class {
getPrefsWidget(pages) {
const prefsWidget = new Gtk.Box({
- orientation: Gtk.Orientation.VERTICAL
+ orientation: Gtk.Orientation.VERTICAL,
});
const stack = new Gtk.Stack({
- hexpand: true
+ hexpand: true,
});
const stackSwitcher = new Gtk.StackSwitcher({
halign: Gtk.Align.CENTER,
- hexpand: true
+ hexpand: true,
});
const context = stackSwitcher.get_style_context();
@@ -398,7 +486,7 @@ var LegacyPrefs = class {
vscrollbar_policy: Gtk.PolicyType.AUTOMATIC,
vexpand: true,
hexpand: true,
- visible: true
+ visible: true,
};
const pagesBtns = [];
@@ -411,13 +499,13 @@ var LegacyPrefs = class {
stack.add_named(this._getLegacyPage(optionList, pageProperties), name);
pagesBtns.push(
- [new Gtk.Label({ label: title}), _newImageFromIconName(iconName, Gtk.IconSize.BUTTON)]
+ [new Gtk.Label({ label: title }), _newImageFromIconName(iconName, Gtk.IconSize.BUTTON)]
);
}
let stBtn = stackSwitcher.get_first_child ? stackSwitcher.get_first_child() : null;
for (let i = 0; i < pagesBtns.length; i++) {
- const box = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL, spacing: 6, visible: true});
+ const box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, spacing: 6, visible: true });
const icon = pagesBtns[i][1];
icon.margin_start = 30;
icon.margin_end = 30;
@@ -433,12 +521,15 @@ var LegacyPrefs = class {
}
}
- stack.show_all && stack.show_all();
- stackSwitcher.show_all && stackSwitcher.show_all();
+ if (stack.show_all)
+ stack.show_all();
+ if (stackSwitcher.show_all)
+ stackSwitcher.show_all();
prefsWidget.append(stack);
- prefsWidget.show_all && prefsWidget.show_all();
+ if (prefsWidget.show_all)
+ prefsWidget.show_all();
prefsWidget._stackSwitcher = stackSwitcher;
@@ -457,7 +548,7 @@ var LegacyPrefs = class {
margin_bottom: 12,
});
- const context = page.get_style_context();
+ let context = page.get_style_context();
context.add_class('background');
let frame;
@@ -472,20 +563,20 @@ var LegacyPrefs = class {
const lbl = new Gtk.Label({
label: option,
xalign: 0,
- margin_bottom: 4
+ margin_bottom: 4,
});
- const context = lbl.get_style_context();
+ context = lbl.get_style_context();
context.add_class('heading');
mainBox.append(lbl);
frame = new Gtk.Frame({
- margin_bottom: 16
+ margin_bottom: 16,
});
frameBox = new Gtk.ListBox({
- selection_mode: null
+ selection_mode: null,
});
mainBox.append(frame);
@@ -500,21 +591,21 @@ var LegacyPrefs = class {
margin_end: 8,
margin_top: 8,
margin_bottom: 8,
- hexpand: true
- })
+ hexpand: true,
+ });
grid.attach(option, 0, 0, 5, 1);
- if (widget) {
+ if (widget)
grid.attach(widget, 5, 0, 2, 1);
- }
+
frameBox.append(grid);
}
page.set_child(mainBox);
return page;
}
-}
+};
const DropDownItem = GObject.registerClass({
GTypeName: 'DropdownItem',
@@ -538,6 +629,7 @@ const DropDownItem = GObject.registerClass({
get text() {
return this._text;
}
+
set text(text) {
this._text = text;
}
@@ -545,6 +637,7 @@ const DropDownItem = GObject.registerClass({
get id() {
return this._id;
}
+
set id(id) {
this._id = id;
}
diff --git a/extensions/vertical-workspaces/lib/osdWindow.js b/extensions/vertical-workspaces/lib/osdWindow.js
new file mode 100644
index 0000000..a010558
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/osdWindow.js
@@ -0,0 +1,93 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * osdWindow.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+
+const { Clutter } = imports.gi;
+const Main = imports.ui.main;
+const OsdWindow = imports.ui.osdWindow;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const _Util = Me.imports.lib.util;
+
+const OsdPositions = {
+ 1: {
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.START,
+ },
+ 2: {
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.START,
+ },
+ 3: {
+ x_align: Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.START,
+ },
+ 4: {
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ },
+ 5: {
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.END,
+ },
+ 6: {
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.END,
+ },
+ 7: {
+ x_align: Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.END,
+ },
+};
+
+let _overrides;
+let opt;
+let _firstRun = true;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('osdWindowModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+
+ if (_overrides)
+ _overrides.removeAll();
+
+ if (reset || !moduleEnabled) {
+ updateExistingOsdWindows(6);
+ _overrides = null;
+ opt = null;
+ return;
+ }
+
+ _overrides = new _Util.Overrides();
+ _overrides.addOverride('osdWindow', OsdWindow.OsdWindow.prototype, OsdWindowCommon);
+}
+
+function updateExistingOsdWindows(position) {
+ position = position ? position : opt.OSD_POSITION;
+ Main.osdWindowManager._osdWindows.forEach(osd => {
+ osd.set(OsdPositions[position]);
+ });
+}
+
+const OsdWindowCommon = {
+ after_show() {
+ if (!opt.OSD_POSITION)
+ this.opacity = 0;
+ this.set(OsdPositions[opt.OSD_POSITION]);
+ },
+};
diff --git a/extensions/vertical-workspaces/lib/overlayKey.js b/extensions/vertical-workspaces/lib/overlayKey.js
new file mode 100644
index 0000000..e0fc11d
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/overlayKey.js
@@ -0,0 +1,108 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * overlayKey.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+const { GObject, Gio, GLib, Meta, St } = imports.gi;
+
+const Main = imports.ui.main;
+const Overview = imports.ui.overview;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const _Util = Me.imports.lib.util;
+
+const _ = Me.imports.lib.settings._;
+const shellVersion = _Util.shellVersion;
+const WIN_SEARCH_PREFIX = Me.imports.lib.windowSearchProvider.prefix;
+const RECENT_FILES_PREFIX = Me.imports.lib.recentFilesSearchProvider.prefix;
+const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
+
+let opt;
+let _firstRun = true;
+
+let _originalOverlayKeyHandlerId;
+let _overlayKeyHandlerId;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('overlayKeyModule', true);
+ reset = reset || (!_firstRun && !moduleEnabled);
+
+ // don't even touch this module if disabled
+ if (_firstRun && !moduleEnabled)
+ return;
+
+ _firstRun = false;
+
+ if (reset) {
+ _updateOverlayKey(reset);
+ opt = null;
+ return;
+ }
+
+ _updateOverlayKey();
+}
+
+function _updateOverlayKey(reset = false) {
+ if (reset) {
+ _restoreOverlayKeyHandler();
+ } else if (!_originalOverlayKeyHandlerId) {
+ _originalOverlayKeyHandlerId = GObject.signal_handler_find(global.display, { signalId: 'overlay-key' });
+ if (_originalOverlayKeyHandlerId !== null)
+ global.display.block_signal_handler(_originalOverlayKeyHandlerId);
+ _connectOverlayKey.bind(Main.overview._overview.controls)();
+ }
+}
+
+function _restoreOverlayKeyHandler() {
+ // Disconnect modified overlay key handler
+ if (_overlayKeyHandlerId !== null) {
+ global.display.disconnect(_overlayKeyHandlerId);
+ _overlayKeyHandlerId = null;
+ }
+
+ // Unblock original overlay key handler
+ if (_originalOverlayKeyHandlerId !== null) {
+ global.display.unblock_signal_handler(_originalOverlayKeyHandlerId);
+ _originalOverlayKeyHandlerId = null;
+ }
+}
+
+function _connectOverlayKey() {
+ this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
+
+ this._lastOverlayKeyTime = 0;
+ _overlayKeyHandlerId = global.display.connect('overlay-key', () => {
+ if (this._a11ySettings.get_boolean('stickykeys-enable'))
+ return;
+
+ const { initialState, finalState, transitioning } =
+ this._stateAdjustment.getStateTransitionParams();
+
+ const time = GLib.get_monotonic_time() / 1000;
+ const timeDiff = time - this._lastOverlayKeyTime;
+ this._lastOverlayKeyTime = time;
+
+ const shouldShift = St.Settings.get().enable_animations
+ ? transitioning && finalState > initialState
+ : Main.overview.visible && timeDiff < Overview.ANIMATION_TIME;
+
+ const mode = opt.OVERLAY_KEY_SECONDARY;
+ if (shouldShift) {
+ if (mode === 1)
+ this._shiftState(Meta.MotionDirection.UP);
+ else if (mode === 2)
+ _Util.activateSearchProvider(WIN_SEARCH_PREFIX);
+ else if (mode === 3)
+ _Util.activateSearchProvider(RECENT_FILES_PREFIX);
+ } else {
+ Main.overview.toggle();
+ }
+ });
+}
diff --git a/extensions/vertical-workspaces/overview.js b/extensions/vertical-workspaces/lib/overview.js
index db267b6..2f23d05 100644
--- a/extensions/vertical-workspaces/overview.js
+++ b/extensions/vertical-workspaces/lib/overview.js
@@ -1,9 +1,6 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* overview.js
- *
- * panel barrier should follow panel position
- * or disable it to not collide with Custom Hot Corners barriers
*
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
@@ -13,21 +10,18 @@
'use strict';
-const { Meta, Clutter } = imports.gi;
-
-const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Me = imports.misc.extensionUtils.getCurrentExtension();
-const _Util = Me.imports.util;
+const _Util = Me.imports.lib.util;
let _overrides;
let opt;
function update(reset = false) {
- if (_overrides) {
+ if (_overrides)
_overrides.removeAll();
- }
+
if (reset) {
_overrides = null;
@@ -35,34 +29,31 @@ function update(reset = false) {
return;
}
- opt = Me.imports.settings.opt;
-
+ opt = Me.imports.lib.settings.opt;
_overrides = new _Util.Overrides();
_overrides.addOverride('Overview', Overview.Overview.prototype, OverviewCommon);
}
-
const OverviewCommon = {
- _showDone: function() {
+ _showDone() {
this._animationInProgress = false;
this._coverPane.hide();
-
+
this.emit('shown');
// Handle any calls to hide* while we were showing
if (!this._shown)
this._animateNotVisible();
-
+
this._syncGrab();
// if user activates overview during startup animation, transition needs to be shifted to the state 2 here
const controls = this._overview._controls;
if (controls._searchController._searchActive && controls._stateAdjustment.value === 1) {
- if (opt.SEARCH_VIEW_ANIMATION) {
+ if (opt.SEARCH_VIEW_ANIMATION)
controls._onSearchChanged();
- } else {
+ else if (!opt.OVERVIEW_MODE2)
controls._stateAdjustment.value = 2;
- }
}
- }
-}
+ },
+};
diff --git a/extensions/vertical-workspaces/overviewControls.js b/extensions/vertical-workspaces/lib/overviewControls.js
index ffe0f89..4959b83 100644
--- a/extensions/vertical-workspaces/overviewControls.js
+++ b/extensions/vertical-workspaces/lib/overviewControls.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* overviewControls.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -10,7 +10,7 @@
'use strict';
-const { Clutter, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi;
+const { Clutter, GLib, GObject, St } = imports.gi;
const Main = imports.ui.main;
const Util = imports.misc.util;
const OverviewControls = imports.ui.overviewControls;
@@ -22,55 +22,50 @@ const FitMode = imports.ui.workspacesView.FitMode;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
-const _Util = Me.imports.util;
+const _Util = Me.imports.lib.util;
+
let _overrides;
+let opt;
const ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME;
-const DASH_MAX_SIZE_RATIO = 0.15;
+const DASH_MAX_SIZE_RATIO = 0.25;
let _originalSearchControllerSigId;
let _searchControllerSigId;
-let _startupAnimTimeoutId1;
-let _startupAnimTimeoutId2;
-let _updateAppGridTimeoutId;
+let _timeouts;
let _startupInitComplete = false;
-let opt;
-
-
function update(reset = false) {
-
- if (_overrides) {
+ if (_overrides)
_overrides.removeAll();
+
+ if (_timeouts) {
+ Object.values(_timeouts).forEach(id => {
+ if (id)
+ GLib.source_remove(id);
+ });
}
-
+
_replaceOnSearchChanged(reset);
-
+
if (reset) {
_overrides = null;
opt = null;
+ _timeouts = null;
return;
}
-
- opt = Me.imports.settings.opt;
+ _timeouts = {};
+
+ opt = Me.imports.lib.settings.opt;
_overrides = new _Util.Overrides();
_overrides.addOverride('ControlsManager', OverviewControls.ControlsManager.prototype, ControlsManager);
-
- if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) {
+
+ if (opt.ORIENTATION === Clutter.Orientation.VERTICAL)
_overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutVertical);
- } else {
+ else
_overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutHorizontal);
- }
-}
-
-function _dashNotDefault() {
- return Main.overview.dash !== Main.overview._overview._controls.layoutManager._dash;
-}
-
-function _dashIsDashToDock() {
- return Main.overview.dash._isHorizontal !== undefined;
}
function _replaceOnSearchChanged(reset = false) {
@@ -84,29 +79,32 @@ function _replaceOnSearchChanged(reset = false) {
searchController.unblock_signal_handler(_originalSearchControllerSigId);
_originalSearchControllerSigId = 0;
}
+ Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_x = 0;
+ Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_y = 0;
+ Main.overview.searchEntry.visible = true;
+ Main.overview.searchEntry.opacity = 255;
} else {
// reconnect signal to use custom function (callbacks cannot be overridden in class prototype, they are already in memory as a copy for the given callback)
_originalSearchControllerSigId = GObject.signal_handler_find(searchController, { signalId: 'notify', detail: 'search-active' });
- if (_originalSearchControllerSigId) {
+ if (_originalSearchControllerSigId)
searchController.block_signal_handler(_originalSearchControllerSigId);
- }
+
_searchControllerSigId = searchController.connect('notify::search-active', ControlsManager._onSearchChanged.bind(Main.overview._overview.controls));
}
-
}
-var ControlsManager = {
+const ControlsManager = {
// this function is used as a callback by a signal handler, needs to be reconnected after modification as the original callback uses a copy of the original function
- /*_update: function() {
+ /* _update: function() {
...
}*/
// this function has duplicate in WorkspaceView so we use one function for both to avoid issues with syncing them
- _getFitModeForState: function(state) {
+ _getFitModeForState(state) {
return _getFitModeForState(state);
},
- _updateThumbnailsBox: function() {
+ _updateThumbnailsBox() {
const { shouldShow } = this._thumbnailsBox;
const thumbnailsBoxVisible = shouldShow;
this._thumbnailsBox.visible = thumbnailsBoxVisible;
@@ -116,13 +114,12 @@ var ControlsManager = {
},
// this function is pure addition to the original code and handles wsDisp transition to APP_GRID view
- _updateWorkspacesDisplay: function() {
+ _updateWorkspacesDisplay() {
this._workspacesDisplay.translation_x = 0;
this._workspacesDisplay.translation_y = 0;
this._workspacesDisplay.scale_x = 1;
this._workspacesDisplay.scale_y = 1;
const { initialState, finalState, progress, currentState } = this._stateAdjustment.getStateTransitionParams();
- const { searchActive } = this._searchController;
const paramsForState = s => {
let opacity;
@@ -146,23 +143,26 @@ var ControlsManager = {
let opacity = Math.round(Util.lerp(initialParams.opacity, finalParams.opacity, progress));
- let workspacesDisplayVisible = (opacity != 0)/* && !(searchActive)*/;
+ let workspacesDisplayVisible = opacity !== 0/* && !(searchActive)*/;
// improve transition from search results to desktop
- if (finalState === 0 && this._searchController._searchResults.visible) {
+ if (finalState === 0 && this._searchController._searchResults.visible)
this._searchController.hide();
- }
// reset Static Workspace window picker mode
- if (currentState === 0/*finalState === 0 && progress === 1*/ && opt.OVERVIEW_MODE && opt.WORKSPACE_MODE) {
+ if (currentState === 0/* finalState === 0 && progress === 1*/ && opt.OVERVIEW_MODE && opt.WORKSPACE_MODE)
opt.WORKSPACE_MODE = 0;
- }
+
+ if (currentState < 2 && currentState > 1)
+ WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 0;
+ else
+ WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 200;
if (!opt.WS_ANIMATION || !opt.SHOW_WS_TMB) {
this._workspacesDisplay.opacity = opacity;
} else if (!opt.SHOW_WS_TMB_BG) {
// fade out ws wallpaper during transition to ws switcher if ws switcher background disabled
- const ws = this._workspacesDisplay._workspacesViews[global.display.get_primary_monitor()]._workspaces[this._workspaceAdjustment.value];
+ const ws = this._workspacesDisplay._workspacesViews[global.display.get_primary_monitor()]?._workspaces[this._workspaceAdjustment.value];
if (ws)
ws._background.opacity = opacity;
}
@@ -172,19 +172,19 @@ var ControlsManager = {
const dash = this.dash;
const searchEntryBin = this._searchEntryBin;
// this dash transition collides with startup animation and freezes GS for good, needs to be delayed (first Main.overview 'hiding' event enables it)
- const skipDash = _dashNotDefault();
+ const skipDash = _Util.dashNotDefault();
// OVERVIEW_MODE 2 should animate dash and wsTmbBox only if WORKSPACE_MODE === 0 (windows not spread)
const animateOverviewMode2 = opt.OVERVIEW_MODE2 && !(finalState === 1 && opt.WORKSPACE_MODE);
- if (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !(opt.OVERVIEW_MODE2)) || animateOverviewMode2)) {
+ if (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2) || animateOverviewMode2)) {
if (!tmbBox._translationOriginal || Math.abs(tmbBox._translationOriginal[0]) > 500) { // swipe gesture can call this calculation before tmbBox is finalized, giving nonsense width
- const [tmbTranslation_x, tmbTranslation_y, dashTranslation_x, dashTranslation_y, searchTranslation_y] = _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin);
- tmbBox._translationOriginal = [tmbTranslation_x, tmbTranslation_y];
- dash._translationOriginal = [dashTranslation_x, dashTranslation_y];
- searchEntryBin._translationOriginal = searchTranslation_y;
+ const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin);
+ tmbBox._translationOriginal = [tmbTranslationX, tmbTranslationY];
+ dash._translationOriginal = [dashTranslationX, dashTranslationY];
+ searchEntryBin._translationOriginal = searchTranslationY;
}
if (finalState === 0 || initialState === 0) {
- const prg = Math.abs((finalState == 0 ? 0 : 1) - progress);
+ const prg = Math.abs((finalState === 0 ? 0 : 1) - progress);
tmbBox.translation_x = Math.round(prg * tmbBox._translationOriginal[0]);
tmbBox.translation_y = Math.round(prg * tmbBox._translationOriginal[1]);
if (!skipDash) {
@@ -195,9 +195,9 @@ var ControlsManager = {
}
if (progress === 1) {
tmbBox._translationOriginal = 0;
- if (!skipDash) {
+ if (!skipDash)
dash._translationOriginal = 0;
- }
+
searchEntryBin._translationOriginal = 0;
}
} else if (!Main.layoutManager._startingUp && (tmbBox.translation_x || tmbBox.translation_y)) {
@@ -211,11 +211,10 @@ var ControlsManager = {
}
if (!Main.layoutManager._startingUp) {
- if (initialState === ControlsState.HIDDEN && finalState === ControlsState.APP_GRID) {
+ if (initialState === ControlsState.HIDDEN && finalState === ControlsState.APP_GRID)
this._appDisplay.opacity = Math.round(progress * 255);
- } else {
+ else
this._appDisplay.opacity = 255 - opacity;
- }
}
if (currentState === ControlsState.APP_GRID) {
@@ -230,22 +229,19 @@ var ControlsManager = {
// set searchEntry above appDisplay
this.set_child_above_sibling(this._searchEntryBin, null);
// move dash above wsTmb for case that dash and wsTmb animate from the same side
- this.set_child_above_sibling(dash, null);
+ if (!_Util.dashNotDefault())
+ this.set_child_above_sibling(dash, null);
this.set_child_below_sibling(this._thumbnailsBox, null);
this.set_child_below_sibling(this._workspacesDisplay, null);
this.set_child_below_sibling(this._appDisplay, null);
} else if (!this.dash._isAbove && progress === 1 && finalState > ControlsState.HIDDEN) {
// set dash above workspace in the overview
- if (!_dashNotDefault()) {
- this.set_child_above_sibling(this._thumbnailsBox, null);
- this.set_child_above_sibling(this._searchEntryBin, null);
+ this.set_child_above_sibling(this._thumbnailsBox, null);
+ this.set_child_above_sibling(this._searchEntryBin, null);
+ if (!_Util.dashNotDefault())
this.set_child_above_sibling(this.dash, null);
- this.dash._isAbove = true;
- }
-
- // update max tmb scale in case some other extension changed it
- WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = opt.MAX_THUMBNAIL_SCALE;
+ this.dash._isAbove = true;
} else if (this.dash._isAbove && progress < 1) {
// keep dash below for ws transition between the overview and hidden state
this.set_child_above_sibling(this._workspacesDisplay, null);
@@ -254,7 +250,7 @@ var ControlsManager = {
},
// fix for upstream bug - appGrid.visible after transition from APP_GRID to HIDDEN
- _updateAppDisplayVisibility: function(stateTransitionParams = null) {
+ _updateAppDisplayVisibility(stateTransitionParams = null) {
if (!stateTransitionParams)
stateTransitionParams = this._stateAdjustment.getStateTransitionParams();
@@ -262,6 +258,12 @@ var ControlsManager = {
if (this.dash.showAppsButton.checked)
this._searchTransition = false;
+ // update App Grid after settings changed
+ // only if the App Grid is currently visible on the screen, the paging updates correctly
+ if (currentState === ControlsState.APP_GRID && this._appDisplay.visible && opt._appGridNeedsRedisplay) {
+ Me.imports.lib.appDisplay._updateAppGridProperties();
+ opt._appGridNeedsRedisplay = false;
+ }
// if !APP_GRID_ANIMATION, appGrid needs to be hidden in WINDOW_PICKER mode (1)
// but needs to be visible for transition from HIDDEN (0) to APP_GRID (2)
this._appDisplay.visible =
@@ -271,150 +273,156 @@ var ControlsManager = {
!this._searchTransition;
},
- _onSearchChanged: function() {
- // if user start typing or activated search provider during overview animation, this switcher will be called again after animation ends
- if (opt.SEARCH_VIEW_ANIMATION && Main.overview._animationInProgress) return;
-
+ _onSearchChanged() {
const { finalState, currentState } = this._stateAdjustment.getStateTransitionParams();
+
const { searchActive } = this._searchController;
const SIDE_CONTROLS_ANIMATION_TIME = 250; // OverviewControls.SIDE_CONTROLS_ANIMATION_TIME = Overview.ANIMATION_TIME = 250
+ const entry = this._searchEntry;
+ if (opt.SHOW_SEARCH_ENTRY) {
+ entry.visible = true;
+ entry.opacity = 255;
+ } else if (!(searchActive && entry.visible)) {
+ entry.visible = true;
+ entry.opacity = searchActive ? 0 : 255;
+ // show search entry only if the user starts typing, and hide it when leaving the search mode
+ entry.ease({
+ opacity: searchActive ? 255 : 0,
+ duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ entry.visible = searchActive;
+ },
+ });
+ }
+
+ // if user start typing or activated search provider during overview animation, this switcher will be called again after animation ends
+ if (opt.SEARCH_VIEW_ANIMATION && Main.overview._animationInProgress && finalState !== ControlsState.HIDDEN)
+ return;
+
if (!searchActive) {
this._workspacesDisplay.reactive = true;
this._workspacesDisplay.setPrimaryWorkspaceVisible(true);
} else {
this._searchController.show();
+ entry.visible = true;
+ entry.opacity = 255;
}
this._searchTransition = true;
this._searchController._searchResults.translation_x = 0;
this._searchController._searchResults.translation_y = 0;
+ this._searchController.opacity = 255;
this._searchController.visible = true;
- if (opt.SEARCH_VIEW_ANIMATION && !this.dash.showAppsButton.checked && ![4, 8].includes(opt.WS_TMB_POSITION) /*&& !opt.OVERVIEW_MODE2*/) {
+ if (opt.SEARCH_VIEW_ANIMATION && !this.dash.showAppsButton.checked && ![4, 8].includes(opt.WS_TMB_POSITION) /* && !opt.OVERVIEW_MODE2*/) {
this._updateAppDisplayVisibility();
this._searchController.opacity = searchActive ? 255 : 0;
- let translation_x = 0;
- let translation_y = 0;
+ let translationX = 0;
+ let translationY = 0;
const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor());
-
if (currentState < ControlsState.APP_GRID) {
switch (opt.SEARCH_VIEW_ANIMATION) {
- case 0:
- translation_x = 0;
- translation_y = 0;
- break;
- case 1:
- // make it longer to cover the delay before results appears
- translation_x = geometry.x + geometry.width - this._searchController.x + this._workspacesDisplay.width;
- translation_y = 0;
- break;
- case 2:
- translation_x = - this._searchController.x - 2 * this._workspacesDisplay.width;
- translation_y = 0;
- break;
- case 3:
- translation_x = 0;
- translation_y = geometry.y + geometry.height + this._searchController.y + this._workspacesDisplay.height;
- break;
- case 5:
- translation_x = 0;
- translation_y = - this._searchController.y - 2 * this._workspacesDisplay.height;
- break;
+ case 1:
+ // make it longer to cover the delay before results appears
+ translationX = geometry.width;
+ translationY = 0;
+ break;
+ case 2:
+ translationX = -geometry.width;
+ translationY = 0;
+ break;
+ case 3:
+ translationX = 0;
+ translationY = geometry.height;
+ break;
+ case 5:
+ translationX = 0;
+ translationY = -geometry.height;
+ break;
}
}
if (searchActive) {
- this._searchController._searchResults.translation_x = translation_x;
- this._searchController._searchResults.translation_y = translation_y;
+ this._searchController._searchResults.translation_x = translationX;
+ this._searchController._searchResults.translation_y = translationY;
} else {
this._searchController._searchResults.translation_x = 0;
this._searchController._searchResults.translation_y = 0;
}
this._searchController._searchResults.ease({
- //opacity: searchActive ? 255 : 0,
- translation_x: searchActive ? 0 : translation_x,
- translation_y: searchActive ? 0 : translation_y,
+ translation_x: searchActive ? 0 : translationX,
+ translation_y: searchActive ? 0 : translationY,
duration: SIDE_CONTROLS_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._searchController.visible = searchActive;
this._searchTransition = false;
- }
+ },
});
this._workspacesDisplay.opacity = 255;
} else {
this._appDisplay.ease({
- opacity: (searchActive || currentState < 2) ? 0 : 255,
+ opacity: searchActive || currentState < 2 ? 0 : 255,
duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._updateAppDisplayVisibility(),
});
- //this._updateAppDisplayVisibility();
+ // this._updateAppDisplayVisibility();
this._workspacesDisplay.setPrimaryWorkspaceVisible(true);
- this._workspacesDisplay.ease({
+ /* this._workspacesDisplay.ease({
opacity: searchActive ? 0 : 255,
- duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
+ duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME / 2 : 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._workspacesDisplay.reactive = !searchActive;
this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive);
},
- });
+ });*/
+ this._searchController.opacity = searchActive ? 0 : 255;
this._searchController.ease({
opacity: searchActive ? 255 : 0,
- duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
+ duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME * 2 : 0,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => (this._searchController.visible = searchActive),
});
}
// reuse already tuned overview transition, just replace APP_GRID with the search view
- if (!Main.overview._animationInProgress && finalState !== ControlsState.HIDDEN && !this.dash.showAppsButton.checked) {
+ if (!(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) && !Main.overview._animationInProgress && finalState !== ControlsState.HIDDEN && !this.dash.showAppsButton.checked) {
+ Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-om2');
+ Main.overview.searchEntry.remove_style_class_name('search-entry-om2');
this._stateAdjustment.ease(searchActive ? ControlsState.APP_GRID : ControlsState.WINDOW_PICKER, {
// shorter animation time when entering search view can avoid stuttering in transition
// collecting search results take some time and the problematic part is the realization of the object on the screen
// if the ws animation ends before this event, the whole transition is smoother
// removing the ws transition (duration: 0) seems like the best solution here
- duration: searchActive ? 0 : SIDE_CONTROLS_ANIMATION_TIME,
+ duration: searchActive || (opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE) ? 80 : SIDE_CONTROLS_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive);
- }
- });
- }
-
- const entry = this._searchEntry;
- if (opt.SHOW_SEARCH_ENTRY) {
- entry.visible = true;
- entry.opacity = 255;
- } else {
- entry.visible = true;
- entry.opacity = searchActive ? 0 : 255;
- // show search entry only if the user starts typing, and hide it when leaving the search mode
- entry.ease({
- opacity: searchActive ? 255 : 0,
- duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- onComplete: () => {
- entry.visible = searchActive;
},
});
+ } else if (opt.OVERVIEW_MODE2 && !(opt.WORKSPACE_MODE || this.dash.showAppsButton.checked)) {
+ // add background to search results and make searchEntry border thicker for better visibility
+ Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-om2');
+ Main.overview.searchEntry.add_style_class_name('search-entry-om2');
+ } else {
+ Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-om2');
+ Main.overview.searchEntry.remove_style_class_name('search-entry-om2');
}
},
- runStartupAnimation: async function(callback) {
- // fix for upstream bug - overview always shows workspace 1 instead of the active one after restart
- this._workspaceAdjustment.set_value(global.workspace_manager.get_active_workspace_index());
+ async runStartupAnimation(callback) {
this._ignoreShowAppsButtonToggle = true;
-
this._searchController.prepareToEnterOverview();
this._workspacesDisplay.prepareToEnterOverview();
@@ -443,7 +451,7 @@ var ControlsManager = {
onComplete: () => {
// part of the workaround for stuttering first app grid animation
this._appDisplay.visible = true;
- }
+ },
});
const dash = this.dash;
@@ -457,10 +465,10 @@ var ControlsManager = {
}
const searchEntryBin = this._searchEntryBin;
- const [tmbTranslation_x, tmbTranslation_y, dashTranslation_x, dashTranslation_y, searchTranslation_y] =
+ const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] =
_Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin);
- const onComplete = function() {
+ const onComplete = function () {
// running init callback again causes issues (multiple connections)
if (!_startupInitComplete)
callback();
@@ -470,16 +478,24 @@ var ControlsManager = {
this._appDisplay.opacity = 1;
const [x, y] = this._appDisplay.get_position();
- const translation_x = - x;
- const translation_y = - y;
- this._appDisplay.translation_x = translation_x;
- this._appDisplay.translation_y = translation_y;
+ const translationX = -x;
+ const translationY = -y;
+ this._appDisplay.translation_x = translationX;
+ this._appDisplay.translation_y = translationY;
+ GLib.idle_add(0, () => {
+ this._appDisplay._removeItem(this._appDisplay._orderedItems[0]);
+ this._appDisplay._redisplay();
+ });
// let the main loop realize previous changes before continuing
- _startupAnimTimeoutId1 = GLib.timeout_add(
+ _timeouts.startupAnim1 = GLib.timeout_add(
GLib.PRIORITY_DEFAULT,
10,
() => {
+ GLib.idle_add(0, () => {
+ this._appDisplay._removeItem(this._appDisplay._orderedItems[0]);
+ this._appDisplay._redisplay();
+ });
this._appDisplay.translation_x = 0;
this._appDisplay.translation_y = 0;
this._appDisplay.visible = false;
@@ -489,15 +505,15 @@ var ControlsManager = {
this._appDisplay.opacity = 255;
this.dash.showAppsButton.checked = true;
}
- _startupAnimTimeoutId1 = 0;
+ _timeouts.startupAnim1 = 0;
return GLib.SOURCE_REMOVE;
}
);
}.bind(this);
- if (dash.visible && !_dashNotDefault()) {
- dash.translation_x = dashTranslation_x;
- dash.translation_y = dashTranslation_y;
+ if (dash.visible && !_Util.dashNotDefault()) {
+ dash.translation_x = dashTranslationX;
+ dash.translation_y = dashTranslationY;
dash.opacity = 255;
dash.ease({
translation_x: 0,
@@ -513,20 +529,20 @@ var ControlsManager = {
// set dash opacity to make it visible if user enable it later
dash.opacity = 255;
// if dash is hidden, substitute the ease timeout with GLib.timeout
- _startupAnimTimeoutId2 = GLib.timeout_add(
+ _timeouts.startupAnim2 = GLib.timeout_add(
GLib.PRIORITY_DEFAULT,
// delay + animation time
- STARTUP_ANIMATION_TIME * 2 * opt.ANIMATION_TIME_FACTOR,
+ STARTUP_ANIMATION_TIME * 2 * St.Settings.get().slow_down_factor,
() => {
onComplete();
- _startupAnimTimeoutId2 = 0;
+ _timeouts.startupAnim2 = 0;
return GLib.SOURCE_REMOVE;
}
);
}
if (searchEntryBin.visible) {
- searchEntryBin.translation_y = searchTranslation_y;
+ searchEntryBin.translation_y = searchTranslationY;
searchEntryBin.ease({
translation_y: 0,
delay: STARTUP_ANIMATION_TIME / 2,
@@ -536,8 +552,8 @@ var ControlsManager = {
}
if (tmbBox.visible) {
- tmbBox.translation_x = tmbTranslation_x;
- tmbBox.translation_y = tmbTranslation_y;
+ tmbBox.translation_x = tmbTranslationX;
+ tmbBox.translation_y = tmbTranslationY;
tmbBox.ease({
translation_x: 0,
translation_y: 0,
@@ -553,21 +569,21 @@ var ControlsManager = {
if (workspacesViews.length > 1) {
for (const view of workspacesViews) {
if (view._monitorIndex !== global.display.get_primary_monitor() && view._thumbnails.visible) {
- const tmbBox = view._thumbnails;
-
- _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin);
- if (opt.SEC_WS_TMB_LEFT) {
- tmbBox.translation_x = - (tmbBox.width + 12); // compensate for padding
- } else if (opt.SEC_WS_TMB_RIGHT) {
- tmbBox.translation_x = (tmbBox.width + 12);
- } else if (opt.SEC_WS_TMB_TOP) {
- tmbBox.translation_y = - (tmbBox.height + 12);
- } else if (opt.SEC_WS_TMB_BOTTOM) {
- tmbBox.translation_y = (tmbBox.height + 12);
- }
- tmbBox.opacity = 255;
+ const secTmbBox = view._thumbnails;
+
+ _Util.getOverviewTranslations(opt, dash, secTmbBox, searchEntryBin);
+ if (opt.SEC_WS_TMB_LEFT)
+ secTmbBox.translation_x = -(secTmbBox.width + 12); // compensate for padding
+ else if (opt.SEC_WS_TMB_RIGHT)
+ secTmbBox.translation_x = secTmbBox.width + 12;
+ else if (opt.SEC_WS_TMB_TOP)
+ secTmbBox.translation_y = -(secTmbBox.height + 12);
+ else if (opt.SEC_WS_TMB_BOTTOM)
+ secTmbBox.translation_y = secTmbBox.height + 12;
- tmbBox.ease({
+ secTmbBox.opacity = 255;
+
+ secTmbBox.ease({
translation_y: 0,
delay: STARTUP_ANIMATION_TIME / 2,
duration: STARTUP_ANIMATION_TIME,
@@ -578,10 +594,7 @@ var ControlsManager = {
}
},
- animateToOverview: function(state, callback) {
- // don't enter overview during updating appDisplay properties
- if (_updateAppGridTimeoutId)
- Main.overview.hide();
+ animateToOverview(state, callback) {
this._ignoreShowAppsButtonToggle = true;
this._searchTransition = false;
@@ -595,10 +608,13 @@ var ControlsManager = {
// in which case the the animation is greatly delayed, stuttering, or even skipped
// for user it is more acceptable to watch delayed smooth animation,
// even if it takes little more time, than jumping frames
- const delay = global.display.get_tab_list(0, global.workspace_manager.get_active_workspace()).length * 3;
+ let delay = 0;
+ if (opt.DELAY_OVERVIEW_ANIMATION)
+ delay = global.display.get_tab_list(0, global.workspace_manager.get_active_workspace()).length * 3;
+
this._stateAdjustment.ease(state, {
delay,
- duration: 250, //Overview.ANIMATION_TIME,
+ duration: 250, // Overview.ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onStopped: () => {
if (callback)
@@ -611,29 +627,28 @@ var ControlsManager = {
this._ignoreShowAppsButtonToggle = false;
},
-}
+};
-var ControlsManagerLayoutVertical = {
- _computeWorkspacesBoxForState: function(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, searchHeight, startY) {
+const ControlsManagerLayoutVertical = {
+ _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, searchHeight, startY) {
const workspaceBox = box.copy();
let [width, height] = workspaceBox.get_size();
- const { x1: startX,/* y1: startY*/ } = workAreaBox;
+ // const { x1: startX/* y1: startY*/ } = workAreaBox;
const { spacing } = this;
- //const { expandFraction } = this._workspacesThumbnails;
+ // const { expandFraction } = this._workspacesThumbnails;
const dash = Main.overview.dash;
// including Dash to Dock and clones properties for compatibility
- if (_dashIsDashToDock()) {
+ if (_Util.dashIsDashToDock()) {
// Dash to Dock also always affects workAreaBox
- Main.layoutManager._trackedActors.forEach((actor) => {
+ Main.layoutManager._trackedActors.forEach(actor => {
if (actor.affectsStruts && actor.actor.width === dash.width) {
if (dash._isHorizontal) {
// disabled inteli-hide don't needs compensation
// startY needs to be corrected in allocate()
- if (dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled) {
+ if (dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled)
height += dash.height;
- }
} else {
width += dash.width;
}
@@ -647,9 +662,9 @@ var ControlsManagerLayoutVertical = {
switch (state) {
case ControlsState.HIDDEN:
- // if PANEL_MODE == 2 (overview only) the affectStruts property stays on false to avoid stuttering
+ // if PANEL_OVERVIEW_ONLY, the affectStruts property is set to false to avoid stuttering
// therefore we added panel height to startY for the overview allocation,
- // but here we need to remove the correction since the panel will be in the hidden state
+ // but here we need to remove the correction because the panel will be in the hidden state
if (opt.START_Y_OFFSET) {
let [x, y] = workAreaBox.get_origin();
y -= opt.START_Y_OFFSET;
@@ -674,27 +689,27 @@ var ControlsManagerLayoutVertical = {
}
workspaceBox.set_size(...workAreaBox.get_size());
} else {
- // in PANEL_MODE 2 panel don't affects workArea height (affectStruts === false), it needs to be compensated
+ // if PANEL_OVERVIEW_ONLY, panel doesn't affect workArea height (affectStruts === false), it is necessary to compensate
height = opt.PANEL_POSITION_TOP ? height : height - Main.panel.height;
searchHeight = opt.SHOW_SEARCH_ENTRY ? searchHeight : 0;
- wWidth = width
- - (opt.DASH_VERTICAL ? dash.width : 0)
- - (thumbnailsWidth)
- - 4 * spacing
- wHeight = height
- - (opt.DASH_VERTICAL ? 0 : dashHeight)
- - searchHeight
- - 4 * spacing;
+ wWidth = width -
+ (opt.DASH_VERTICAL ? dash.width : 0) -
+ thumbnailsWidth -
+ 4 * spacing;
+ wHeight = height -
+ (opt.DASH_VERTICAL ? 0 : dashHeight) -
+ searchHeight -
+ 4 * spacing;
const ratio = width / height;
let wRatio = wWidth / wHeight;
let scale = ratio / wRatio;
if (scale > 1) {
- wHeight = wHeight / scale;
+ wHeight /= scale;
wWidth = wHeight * ratio;
} else {
- wWidth = wWidth * scale;
+ wWidth *= scale;
wHeight = wWidth / ratio;
}
@@ -706,7 +721,7 @@ var ControlsManagerLayoutVertical = {
let yOffset = 0;
const yOffsetT = (opt.DASH_TOP ? dashHeight : 0) + searchHeight;
- const yOffsetB = (opt.DASH_BOTTOM ? dashHeight : 0);
+ const yOffsetB = opt.DASH_BOTTOM ? dashHeight : 0;
const yAvailableSpace = (height - yOffsetT - wHeight - yOffsetB) / 2;
yOffset = yOffsetT + yAvailableSpace;
@@ -723,7 +738,7 @@ var ControlsManagerLayoutVertical = {
this._xAlignCenter = true;
}
- const wsBoxX = /*startX + */xOffset;
+ const wsBoxX = /* startX + */xOffset;
wsBoxY = Math.round(startY + yOffset);
workspaceBox.set_origin(Math.round(wsBoxX), Math.round(wsBoxY));
workspaceBox.set_size(Math.round(wWidth), Math.round(wHeight));
@@ -733,13 +748,11 @@ var ControlsManagerLayoutVertical = {
return workspaceBox;
},
- _getAppDisplayBoxForState: function(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsWidth, startY) {
+ _getAppDisplayBoxForState(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsWidth, startY) {
const [width] = box.get_size();
const { x1: startX } = workAreaBox;
- //const { y1: startY } = workAreaBox;
+ // const { y1: startY } = workAreaBox;
let height = workAreaBox.get_height();
- // in PANEL_MODE 2 panel don't affects workArea height (affectStruts === false), it needs to be compensated
- height = opt.PANEL_MODE === 2 ? height - Main.panel.height : height;
const appDisplayBox = new Clutter.ActorBox();
const { spacing } = this;
@@ -748,11 +761,11 @@ var ControlsManagerLayoutVertical = {
const xOffsetL = (opt.WS_TMB_LEFT ? thumbnailsWidth : 0) + (opt.DASH_LEFT ? dashWidth : 0);
const xOffsetR = (opt.WS_TMB_RIGHT ? thumbnailsWidth : 0) + (opt.DASH_RIGHT ? dashWidth : 0);
const yOffsetT = (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0);
- const yOffsetB = (opt.DASH_BOTTOM ? dashHeight : 0);
- const adWidth = opt.CENTER_APP_GRID ? (width - 2 * Math.max (xOffsetL, xOffsetR) - 4 * spacing) : (width - xOffsetL - xOffsetR - 4 * spacing);
+ const yOffsetB = opt.DASH_BOTTOM ? dashHeight : 0;
+ const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 4 * spacing : width - xOffsetL - xOffsetR - 4 * spacing;
const adHeight = height - yOffsetT - yOffsetB - 4 * spacing;
- const appDisplayX = opt.CENTER_APP_GRID ? ((width - adWidth) / 2) : (xOffsetL + 2 * spacing);
+ const appDisplayX = opt.CENTER_APP_GRID ? (width - adWidth) / 2 : xOffsetL + 2 * spacing;
const appDisplayY = startY + yOffsetT + 2 * spacing;
switch (state) {
@@ -786,7 +799,7 @@ var ControlsManagerLayoutVertical = {
return appDisplayBox;
},
- vfunc_allocate: function(container, box) {
+ vfunc_allocate(container, box) {
const childBox = new Clutter.ActorBox();
const { spacing } = this;
@@ -794,7 +807,7 @@ var ControlsManagerLayoutVertical = {
const monitor = Main.layoutManager.findMonitorForActor(this._container);
const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
const startX = workArea.x - monitor.x;
- // if PANEL_MODE == 2 (overview only) the affectStruts property stays on false to avoid stuttering
+ // if PANEL_OVERVIEW_ONLY, the affectStruts property is set to false to avoid stuttering
// therefore we need to add panel height to startY
let startY = workArea.y - monitor.y + opt.START_Y_OFFSET;
@@ -817,7 +830,7 @@ var ControlsManagerLayoutVertical = {
// dash cloud be overridden by the Dash to Dock clone
const dash = Main.overview.dash;
- if (_dashIsDashToDock()) {
+ if (_Util.dashIsDashToDock()) {
// if Dash to Dock replaced the default dash and its inteli-hide id disabled we need to compensate for affected startY
if (!Main.overview.dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled) {
if (Main.panel.y === monitor.y)
@@ -835,9 +848,8 @@ var ControlsManagerLayoutVertical = {
[, dashHeight] = this._dash.get_preferred_height(dashWidth);
dashWidth = Math.min(dashWidth, maxDashWidth);
dashHeight = Math.min(dashHeight, height);
-
} else if (!opt.WS_TMB_FULL) {
- this._dash.setMaxSize(width, maxDashHeight);
+ this._dash.setMaxSize(width, maxDashHeight);
[, dashHeight] = this._dash.get_preferred_height(width);
[, dashWidth] = this._dash.get_preferred_width(dashHeight);
dashHeight = Math.min(dashHeight, maxDashHeight);
@@ -845,46 +857,48 @@ var ControlsManagerLayoutVertical = {
}
}
+ const transitionParams = this._stateAdjustment.getStateTransitionParams();
+
// Workspace Thumbnails
let wsTmbWidth = 0;
let wsTmbHeight = 0;
if (this._workspacesThumbnails.visible) {
- //const REDUCE_WS_TMB_IF_NEEDED = (this._searchController._searchActive && opt.CENTER_SEARCH_VIEW) || opt.CENTER_APP_GRID;
-
- const { expandFraction } = this._workspacesThumbnails;
- const dashHeightReservation = (!opt.WS_TMB_FULL && !opt.DASH_VERTICAL) ? dashHeight : 0;
- wsTmbHeight = opt.WS_TMB_FULL
- ? height - spacing
- : height - (opt.DASH_VERTICAL ? 0 : dashHeightReservation) - spacing;
-
- wsTmbWidth = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight)[0];
- wsTmbWidth = Math.round(Math.min(
- wsTmbWidth * expandFraction,
- width * opt.MAX_THUMBNAIL_SCALE
- ));
+ // const { expandFraction } = this._workspacesThumbnails;
+ const dashHeightReservation = !opt.WS_TMB_FULL && !opt.DASH_VERTICAL ? dashHeight : 0;
+
+ let maxScale = opt.MAX_THUMBNAIL_SCALE;
+ if (!opt.MAX_THUMBNAIL_SCALE_STABLE) {
+ const initState = transitionParams.initialState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE;
+ const finalState = transitionParams.finalState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE;
+ maxScale = Util.lerp(initState, finalState, transitionParams.progress);
+ }
- /*if (opt.REDUCE_WS_TMB_IF_NEEDED) {
- const searchAllocation = this._searchController._searchResults._content.allocation;
- const searchWidth = searchAllocation.x2 - searchAllocation.x1;
- wsTmbWidth = Math.clamp((width - searchWidth) / 2 - spacing, width * 0.05, wsTmbWidth);
- }*/
+ wsTmbWidth = width * maxScale;
+ let totalTmbSpacing;
+ [totalTmbSpacing, wsTmbHeight] = this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth);
+ wsTmbHeight += totalTmbSpacing;
- wsTmbHeight = Math.round(Math.min(this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth)[1], wsTmbHeight));
+ const wsTmbHeightMax = height - dashHeightReservation;
+
+ if (wsTmbHeight > wsTmbHeightMax) {
+ wsTmbHeight = wsTmbHeightMax;
+ wsTmbWidth = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight)[1];
+ }
let wsTmbX;
- if (opt.WS_TMB_RIGHT) {
+ if (opt.WS_TMB_RIGHT)
wsTmbX = Math.round(startX + width - (opt.DASH_RIGHT ? dashWidth : 0) - wsTmbWidth - spacing / 2);
- } else {
+ else
wsTmbX = Math.round((opt.DASH_LEFT ? dashWidth : 0) + spacing / 2);
- }
+
let wstOffset = (height - wsTmbHeight - (opt.DASH_VERTICAL ? 0 : dashHeightReservation)) / 2;
- wstOffset = wstOffset - opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2);
- let wsTmbY = Math.round(startY + ((dashHeightReservation && opt.DASH_TOP) ? dashHeight : 0) + wstOffset);
+ wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2);
+ let wsTmbY = Math.round(startY + (dashHeightReservation && opt.DASH_TOP ? dashHeight : 0) + wstOffset);
childBox.set_origin(wsTmbX, wsTmbY);
- childBox.set_size(wsTmbWidth, wsTmbHeight);
+ childBox.set_size(Math.round(wsTmbWidth), Math.round(wsTmbHeight));
this._workspacesThumbnails.allocate(childBox);
}
@@ -903,23 +917,21 @@ var ControlsManagerLayoutVertical = {
let dashX, dashY, offset;
if (opt.DASH_RIGHT)
dashX = width - dashWidth;
- else if (opt.DASH_LEFT) {
+ else if (opt.DASH_LEFT)
dashX = 0;
- }
+
else if (opt.DASH_TOP)
dashY = startY;
else
dashY = startY + height - dashHeight;
if (!opt.DASH_VERTICAL) {
- offset = (width - (((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter) ? wsTmbWidth : 0) - dashWidth) / 2;
- offset = offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2);
+ offset = (width - ((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter ? wsTmbWidth : 0) - dashWidth) / 2;
+ offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2);
dashX = offset;
if ((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter) {
- if (opt.WS_TMB_RIGHT) {
- //dashX = Math.min(dashX, width - dashWidth - (wsTmbWidth ? wsTmbWidth : 0));
- } else {
+ if (!opt.WS_TMB_RIGHT) {
dashX = (wsTmbWidth ? wsTmbWidth : 0) + offset;
dashX = Math.max(dashX, wsTmbWidth ? wsTmbWidth + spacing : 0);
dashX = Math.min(dashX, width - dashWidth - spacing);
@@ -927,11 +939,11 @@ var ControlsManagerLayoutVertical = {
}
if (opt.WS_TMB_FULL && !opt.CENTER_DASH_WS) {
dashX = opt.WS_TMB_RIGHT
- ? Math.min(width - wsTmbWidth - dashWidth, dashX + (wsTmbWidth) / 2 * (1 - Math.abs(opt.DASH_POSITION_ADJUSTMENT)))
- : Math.max(wsTmbWidth, dashX - (wsTmbWidth) / 2 * (1 - Math.abs(opt.DASH_POSITION_ADJUSTMENT)));
+ ? Math.min(width - wsTmbWidth - dashWidth, dashX + wsTmbWidth / 2 * (1 - Math.abs(opt.DASH_POSITION_ADJUSTMENT)))
+ : Math.max(wsTmbWidth, dashX - wsTmbWidth / 2 * (1 - Math.abs(opt.DASH_POSITION_ADJUSTMENT)));
}
} else {
- const offset = (height - dashHeight) / 2;
+ offset = (height - dashHeight) / 2;
dashY = startY + (offset - opt.DASH_POSITION_ADJUSTMENT * offset);
}
@@ -940,13 +952,12 @@ var ControlsManagerLayoutVertical = {
this._dash.allocate(childBox);
}
- availableHeight -= (opt.DASH_VERTICAL ? 0 : dashHeight + spacing);
+ availableHeight -= opt.DASH_VERTICAL ? 0 : dashHeight + spacing;
let [searchHeight] = this._searchEntry.get_preferred_height(width - wsTmbWidth);
// Workspaces
let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, searchHeight, startY];
- const transitionParams = this._stateAdjustment.getStateTransitionParams();
// Update cached boxes
for (const state of Object.values(ControlsState)) {
@@ -955,9 +966,9 @@ var ControlsManagerLayoutVertical = {
}
let workspacesBox;
- if (!transitionParams.transitioning) {
+ if (!transitionParams.transitioning)
workspacesBox = this._cachedWorkspaceBoxes.get(transitionParams.currentState);
- }
+
if (!workspacesBox) {
const initialBox = this._cachedWorkspaceBoxes.get(transitionParams.initialState);
const finalBox = this._cachedWorkspaceBoxes.get(transitionParams.finalState);
@@ -968,17 +979,14 @@ var ControlsManagerLayoutVertical = {
// Search entry
const searchXoffset = (opt.DASH_LEFT ? dashWidth : 0) + spacing + (opt.WS_TMB_RIGHT ? 0 : wsTmbWidth + spacing);
- //let [searchHeight] = this._searchEntry.get_preferred_height(width - wsTmbWidth);
// Y position under top Dash
let searchEntryX, searchEntryY;
- /*if (opt.OVERVIEW_MODE2 && !opt.DASH_TOP && !opt.WS_TMB_TOP) {
- searchEntryY = 7;
- } else*/ if (opt.DASH_TOP) {
+ if (opt.DASH_TOP)
searchEntryY = startY + dashHeight - spacing;
- } else {
+ else
searchEntryY = startY;
- }
+
searchEntryX = searchXoffset;
let searchWidth = width - 2 * spacing - wsTmbWidth - (opt.DASH_VERTICAL ? dashWidth : 0); // xAlignCenter is given by wsBox
@@ -996,24 +1004,21 @@ var ControlsManagerLayoutVertical = {
availableHeight -= searchHeight + spacing;
- // AppDisplay - state, box, workAreaBox, searchHeight, dashHeight, appGridBox, wsTmbWidth
- //if (this._appDisplay.visible) {
-
- params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbWidth, startY]; // send startY, can be compensated
- let appDisplayBox;
- if (!transitionParams.transitioning) {
- appDisplayBox =
+ // if (this._appDisplay.visible)... ? Can cause problems
+ params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbWidth, startY]; // send startY, can be corrected
+ let appDisplayBox;
+ if (!transitionParams.transitioning) {
+ appDisplayBox =
this._getAppDisplayBoxForState(transitionParams.currentState, ...params);
- } else {
- const initialBox =
+ } else {
+ const initialBox =
this._getAppDisplayBoxForState(transitionParams.initialState, ...params);
- const finalBox =
+ const finalBox =
this._getAppDisplayBoxForState(transitionParams.finalState, ...params);
- appDisplayBox = initialBox.interpolate(finalBox, transitionParams.progress);
- }
- this._appDisplay.allocate(appDisplayBox);
- //}
+ appDisplayBox = initialBox.interpolate(finalBox, transitionParams.progress);
+ }
+ this._appDisplay.allocate(appDisplayBox);
// Search
if (opt.CENTER_SEARCH_VIEW) {
@@ -1028,31 +1033,30 @@ var ControlsManagerLayoutVertical = {
this._searchController.allocate(childBox);
this._runPostAllocation();
- }
-}
+ },
+};
-var ControlsManagerLayoutHorizontal = {
- _computeWorkspacesBoxForState: function(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsHeight, searchHeight, startY) {
+const ControlsManagerLayoutHorizontal = {
+ _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsHeight, searchHeight, startY) {
const workspaceBox = box.copy();
let [width, height] = workspaceBox.get_size();
- let { x1: startX/*, y1: startY*/ } = workAreaBox;
+ // let { x1: startX/* , y1: startY*/ } = workAreaBox;
const { spacing } = this;
- //const { expandFraction } = this._workspacesThumbnails;
+ // const { expandFraction } = this._workspacesThumbnails;
const dash = Main.overview.dash;
// including Dash to Dock and clones properties for compatibility
- if (_dashIsDashToDock()) {
+ if (_Util.dashIsDashToDock()) {
// Dash to Dock always affects workAreaBox
- Main.layoutManager._trackedActors.forEach((actor) => {
+ Main.layoutManager._trackedActors.forEach(actor => {
if (actor.affectsStruts && actor.actor.width === dash.width) {
if (dash._isHorizontal) {
// disabled inteli-hide don't need compensation
// startY needs to be corrected in allocate()
- if (dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled) {
+ if (dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled)
height += dash.height;
- } else if (opt.DASH_TOP) {
+ else if (opt.DASH_TOP)
height += dash.height;
- }
} else {
width += dash.width;
}
@@ -1064,7 +1068,7 @@ var ControlsManagerLayoutHorizontal = {
switch (state) {
case ControlsState.HIDDEN:
- // if PANEL_MODE == 2 (overview only) the affectStruts property stays on false to avoid stuttering
+ // if PANEL_OVERVIEW_ONLY, the affectStruts property is set to false to avoid stuttering
// therefore we added panel height to startY for the overview allocation,
// but here we need to remove the correction since the panel will be in the hidden state
if (opt.START_Y_OFFSET) {
@@ -1091,28 +1095,28 @@ var ControlsManagerLayoutHorizontal = {
}
workspaceBox.set_size(...workAreaBox.get_size());
} else {
- // in PANEL_MODE 2 panel don't affects workArea height (affectStruts === false), it needs to be compensated
+ // if PANEL_OVERVIEW_ONLY, panel doesn't affect workArea height (affectStruts === false), it is necessary to compensate
height = opt.PANEL_POSITION_TOP ? height : height - Main.panel.height;
searchHeight = opt.SHOW_SEARCH_ENTRY ? searchHeight : 0;
- wWidth = width
- - spacing
- - (opt.DASH_VERTICAL ? dashWidth : 0)
- - 4 * spacing;
- wHeight = height
- - (opt.DASH_VERTICAL ? spacing : (dashHeight ? dashHeight : 0))
- - (thumbnailsHeight ? thumbnailsHeight : 0)
- - searchHeight
- - 4 * spacing;
+ wWidth = width -
+ spacing -
+ (opt.DASH_VERTICAL ? dashWidth : 0) -
+ 4 * spacing;
+ wHeight = height -
+ (opt.DASH_VERTICAL ? spacing : dashHeight) -
+ thumbnailsHeight -
+ searchHeight -
+ 4 * spacing;
const ratio = width / height;
let wRatio = wWidth / wHeight;
let scale = ratio / wRatio;
if (scale > 1) {
- wHeight = wHeight / scale;
+ wHeight /= scale;
wWidth = wHeight * ratio;
} else {
- wWidth = wWidth * scale;
+ wWidth *= scale;
wHeight = wWidth / ratio;
}
@@ -1141,8 +1145,8 @@ var ControlsManagerLayoutHorizontal = {
this._xAlignCenter = true;
}
- wsBoxX = /*startX + */xOffset;
- wsBoxY = Math.round(startY + yOffset)
+ wsBoxX = /* startX + */xOffset;
+ wsBoxY = Math.round(startY + yOffset);
workspaceBox.set_origin(Math.round(wsBoxX), Math.round(wsBoxY));
workspaceBox.set_size(Math.round(wWidth), Math.round(wHeight));
}
@@ -1151,25 +1155,24 @@ var ControlsManagerLayoutHorizontal = {
return workspaceBox;
},
- _getAppDisplayBoxForState: function(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsHeight, startY) {
+ _getAppDisplayBoxForState(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsHeight, startY) {
const [width] = box.get_size();
const { x1: startX } = workAreaBox;
- //const { y1: startY } = workAreaBox;
+ // const { y1: startY } = workAreaBox;
let height = workAreaBox.get_height();
- // in PANEL_MODE 2 panel don't affects workArea height (affectStruts === false), it needs to be compensated
- height = opt.PANEL_MODE === 2 ? height - Main.panel.height : height;
const appDisplayBox = new Clutter.ActorBox();
const { spacing } = this;
- const yOffsetT = (opt.WS_TMB_TOP ? thumbnailsHeight : 0) + (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0);
+ const yOffsetT = (opt.WS_TMB_TOP ? thumbnailsHeight : 0) + (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0) + 2 * spacing;
const yOffsetB = (opt.WS_TMB_BOTTOM ? thumbnailsHeight : 0) + (opt.DASH_BOTTOM ? dashHeight : 0);
- const xOffsetL = (opt.DASH_LEFT ? dashWidth : 0);
- const xOffsetR = (opt.DASH_RIGHT ? dashWidth : 0);
- const adWidth = opt.CENTER_APP_GRID ? (width - 2 * Math.max (xOffsetL, xOffsetR) - 4 * spacing) : (width - xOffsetL - xOffsetR - 4 * spacing);
+ const xOffsetL = opt.DASH_LEFT ? dashWidth : 0;
+ const xOffsetR = opt.DASH_RIGHT ? dashWidth : 0;
+ const hSpacing = xOffsetL + xOffsetR ? 2 * spacing : 0;
+ const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 2 * hSpacing : width - xOffsetL - xOffsetR - 2 * hSpacing;
const adHeight = height - yOffsetT - yOffsetB - 4 * spacing;
- const appDisplayX = opt.CENTER_APP_GRID ? ((width - adWidth) / 2) : (xOffsetL + 2 * spacing);
- const appDisplayY = startY + yOffsetT + 2 * spacing;
+ const appDisplayX = opt.CENTER_APP_GRID ? (width - adWidth) / 2 : xOffsetL + hSpacing;
+ const appDisplayY = startY + yOffsetT + hSpacing;
switch (state) {
case ControlsState.HIDDEN:
@@ -1202,7 +1205,7 @@ var ControlsManagerLayoutHorizontal = {
return appDisplayBox;
},
- vfunc_allocate: function(container, box) {
+ vfunc_allocate(container, box) {
const childBox = new Clutter.ActorBox();
const { spacing } = this;
@@ -1210,7 +1213,7 @@ var ControlsManagerLayoutHorizontal = {
const monitor = Main.layoutManager.findMonitorForActor(this._container);
const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
const startX = workArea.x - monitor.x;
- // if PANEL_MODE == 2 (overview only) the affectStruts property stays on false to avoid stuttering
+ // if PANEL_OVERVIEW_ONLY, the affectStruts property is set to false to avoid stuttering
// therefore we need to add panel height to startY
let startY = workArea.y - monitor.y + opt.START_Y_OFFSET;
const workAreaBox = new Clutter.ActorBox();
@@ -1220,7 +1223,7 @@ var ControlsManagerLayoutHorizontal = {
box.x1 += startX;
let [width, height] = box.get_size();
// if panel is at bottom position,
- // compensate the height of the available box (the box size is calculated for top panel)
+ // compensate for the height of the available box (the box size is calculated for top panel)
height = opt.PANEL_POSITION_TOP ? height : height - Main.panel.height;
let availableHeight = height;
@@ -1232,11 +1235,11 @@ var ControlsManagerLayoutHorizontal = {
// dash cloud be overridden by the Dash to Dock clone
const dash = Main.overview.dash;
- if (_dashIsDashToDock()) {
+ if (_Util.dashIsDashToDock()) {
// if Dash to Dock replaced the default dash and its inteli-hide is disabled we need to compensate for affected startY
if (!Main.overview.dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled) {
- if (Main.panel.y === monitor.y)
- startY = Main.panel.height + spacing;
+ // if (Main.panel.y === monitor.y)
+ // startY = Main.panel.height + spacing;
}
dashHeight = dash.height;
dashWidth = dash.width;
@@ -1251,7 +1254,6 @@ var ControlsManagerLayoutHorizontal = {
[, dashWidth] = this._dash.get_preferred_width(dashHeight);
dashHeight = Math.min(dashHeight, maxDashHeight);
dashWidth = Math.min(dashWidth, width - spacing);
-
} else if (!opt.WS_TMB_FULL) {
this._dash.setMaxSize(maxDashWidth, height);
[, dashWidth] = this._dash.get_preferred_width(height);
@@ -1263,44 +1265,53 @@ var ControlsManagerLayoutHorizontal = {
let [searchHeight] = this._searchEntry.get_preferred_height(width);
+ const transitionParams = this._stateAdjustment.getStateTransitionParams();
+
+ // Workspace Thumbnails
let wsTmbWidth = 0;
let wsTmbHeight = 0;
if (this._workspacesThumbnails.visible) {
- const { expandFraction } = this._workspacesThumbnails;
- const dashWidthReservation = (!opt.WS_TMB_FULL && opt.DASH_VERTICAL) ? dashWidth : 0;
+ // const { expandFraction } = this._workspacesThumbnails;
+ const dashWidthReservation = !opt.WS_TMB_FULL && opt.DASH_VERTICAL ? dashWidth : 0;
+
+ let maxScale = opt.MAX_THUMBNAIL_SCALE;
+ if (!opt.MAX_THUMBNAIL_SCALE_STABLE) {
+ const initState = transitionParams.initialState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE;
+ const finalState = transitionParams.finalState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE;
+ maxScale = Util.lerp(initState, finalState, transitionParams.progress);
+ }
- wsTmbWidth = opt.WS_TMB_FULL
- ? width
- : width - (opt.DASH_VERTICAL ? 0 : dashWidthReservation);
+ wsTmbHeight = height * maxScale;
+ let totalTmbSpacing;
+ [totalTmbSpacing, wsTmbWidth] = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight);
+ wsTmbWidth += totalTmbSpacing;
- wsTmbHeight = this._workspacesThumbnails.get_preferred_height(wsTmbWidth)[0];
- wsTmbHeight = Math.round(Math.min(
- wsTmbHeight * expandFraction,
- height * opt.MAX_THUMBNAIL_SCALE
- ));
+ const wsTmbWidthMax = opt.WS_TMB_FULL
+ ? width
+ : width - (opt.DASH_VERTICAL ? 0 : dashWidthReservation);
- wsTmbWidth = Math.round(Math.min(this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight)[1], wsTmbWidth));
+ if (wsTmbWidth > wsTmbWidthMax) {
+ wsTmbWidth = wsTmbWidthMax;
+ wsTmbHeight = this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth)[1];
+ }
let wsTmbY;
- if (opt.WS_TMB_TOP) {
- wsTmbY = Math.round(startY + /*searchHeight + */(opt.DASH_TOP ? dashHeight : spacing / 2));
- } else {
- //const boxY = workArea.y - monitor.y; // startY might be compensated
- //wsTmbY = Math.round(boxY + height - (DASH_BOTTOM ? dashHeight : 0) - wsTmbHeight);
+ if (opt.WS_TMB_TOP)
+ wsTmbY = Math.round(startY + /* searchHeight + */(opt.DASH_TOP ? dashHeight : spacing / 2));
+ else
wsTmbY = Math.round(startY + height - (opt.DASH_BOTTOM ? dashHeight : 0) - wsTmbHeight);
- }
let wstOffset = (width - wsTmbWidth) / 2;
- wstOffset = wstOffset - opt.WS_TMB_POSITION_ADJUSTMENT * wstOffset;
+ wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2);
let wsTmbX = Math.round(Math.clamp(
startX + wstOffset,
- startX + (opt.DASH_LEFT ? dashWidthReservation + spacing / 2 : spacing / 2),
- width - wsTmbWidth - startX - (opt.DASH_RIGHT ? dashWidthReservation + spacing / 2 : spacing / 2)
+ startX + (opt.DASH_LEFT ? dashWidthReservation : 0),
+ width - wsTmbWidth - startX - (opt.DASH_RIGHT ? dashWidthReservation : 0)
));
childBox.set_origin(wsTmbX, wsTmbY);
- childBox.set_size(wsTmbWidth, wsTmbHeight);
+ childBox.set_size(Math.round(wsTmbWidth), Math.round(wsTmbHeight));
this._workspacesThumbnails.allocate(childBox);
@@ -1309,7 +1320,6 @@ var ControlsManagerLayoutHorizontal = {
if (this._dash.visible) {
- //const wMaxHeight = height - spacing - wsTmbHeight - 2 * spacing - (DASH_VERTICAL ? 0 : dashHeight + spacing);
if (opt.WS_TMB_FULL && opt.DASH_VERTICAL) {
const wMaxHeight = height - spacing - wsTmbHeight;
this._dash.setMaxSize(maxDashWidth, wMaxHeight);
@@ -1320,31 +1330,29 @@ var ControlsManagerLayoutHorizontal = {
}
let dashX, dashY, offset;
- if (opt.DASH_RIGHT) {
+ if (opt.DASH_RIGHT)
dashX = width - dashWidth;
- } else if (opt.DASH_LEFT) {
+ else if (opt.DASH_LEFT)
dashX = 0;
- } else if (opt.DASH_TOP) {
+ else if (opt.DASH_TOP)
dashY = startY;
- } else {
+ else
dashY = startY + height - dashHeight;
- }
+
if (opt.DASH_VERTICAL) {
if (opt.WS_TMB_FULL) {
offset = (height - dashHeight - wsTmbHeight) / 2;
if (opt.WS_TMB_TOP) {
- offset = offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2);
+ offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2);
dashY = startY + offset + wsTmbHeight;
- //dashY = Math.max(dashY, startY + wsTmbHeight);
} else {
- offset = offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2);
+ offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2);
dashY = startY + offset;
- //dashY = Math.max(dashY, height - wsTmbHeight - dashHeight - 3 * spacing);
}
} else {
offset = (height - dashHeight) / 2;
- offset = offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2);
+ offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2);
dashY = startY + offset;
}
} else {
@@ -1357,13 +1365,10 @@ var ControlsManagerLayoutHorizontal = {
this._dash.allocate(childBox);
}
- availableHeight -= (opt.DASH_VERTICAL ? 0 : dashHeight);
-
- /*let [searchHeight] = this._searchEntry.get_preferred_height(width);*/
+ availableHeight -= opt.DASH_VERTICAL ? 0 : dashHeight;
// Workspaces
let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbHeight, searchHeight, startY];
- const transitionParams = this._stateAdjustment.getStateTransitionParams();
// Update cached boxes
for (const state of Object.values(ControlsState)) {
@@ -1372,9 +1377,9 @@ var ControlsManagerLayoutHorizontal = {
}
let workspacesBox;
- if (!transitionParams.transitioning) {
+ if (!transitionParams.transitioning)
workspacesBox = this._cachedWorkspaceBoxes.get(transitionParams.currentState);
- }
+
if (!workspacesBox) {
const initialBox = this._cachedWorkspaceBoxes.get(transitionParams.initialState);
const finalBox = this._cachedWorkspaceBoxes.get(transitionParams.finalState);
@@ -1385,17 +1390,14 @@ var ControlsManagerLayoutHorizontal = {
// Search entry
const searchXoffset = (opt.DASH_LEFT ? dashWidth : 0) + spacing;
- //let [searchHeight] = this._searchEntry.get_preferred_height(width - wsTmbWidth);
// Y position under top Dash
let searchEntryX, searchEntryY;
- /*if (opt.OVERVIEW_MODE2 && !opt.DASH_TOP && !opt.WS_TMB_TOP) {
- searchEntryY = 7;
- } else */if (opt.DASH_TOP) {
+ if (opt.DASH_TOP)
searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight : 0) + dashHeight - spacing;
- } else {
+ else
searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0);
- }
+
searchEntryX = searchXoffset;
let searchWidth = width - 2 * spacing - (opt.DASH_VERTICAL ? dashWidth : 0); // xAlignCenter is given by wsBox
@@ -1413,23 +1415,21 @@ var ControlsManagerLayoutHorizontal = {
availableHeight -= searchHeight + spacing;
- // AppDisplay - state, box, workAreaBox, searchHeight, dashHeight, appGridBox, wsTmbWidth
- //if (this._appDisplay.visible) {
- params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbHeight, startY];
- let appDisplayBox;
- if (!transitionParams.transitioning) {
- appDisplayBox =
+ // if (this._appDisplay.visible)... ? Can cause problems
+ params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbHeight, startY];
+ let appDisplayBox;
+ if (!transitionParams.transitioning) {
+ appDisplayBox =
this._getAppDisplayBoxForState(transitionParams.currentState, ...params);
- } else {
- const initialBox =
+ } else {
+ const initialBox =
this._getAppDisplayBoxForState(transitionParams.initialState, ...params);
- const finalBox =
+ const finalBox =
this._getAppDisplayBoxForState(transitionParams.finalState, ...params);
- appDisplayBox = initialBox.interpolate(finalBox, transitionParams.progress);
- }
- this._appDisplay.allocate(appDisplayBox);
- //}
+ appDisplayBox = initialBox.interpolate(finalBox, transitionParams.progress);
+ }
+ this._appDisplay.allocate(appDisplayBox);
// Search
if (opt.CENTER_SEARCH_VIEW) {
@@ -1444,8 +1444,8 @@ var ControlsManagerLayoutHorizontal = {
this._searchController.allocate(childBox);
this._runPostAllocation();
- }
-}
+ },
+};
// same copy of this function should be available in OverviewControls and WorkspacesView
function _getFitModeForState(state) {
diff --git a/extensions/vertical-workspaces/panel.js b/extensions/vertical-workspaces/lib/panel.js
index 2f7143d..3f44ae7 100644
--- a/extensions/vertical-workspaces/panel.js
+++ b/extensions/vertical-workspaces/lib/panel.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* panel.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -10,92 +10,139 @@
'use strict';
+const { GLib } = imports.gi;
const Main = imports.ui.main;
const Me = imports.misc.extensionUtils.getCurrentExtension();
+const _Util = Me.imports.lib.util;
const ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME;
let opt;
+let _firstRun = true;
+
let _showingOverviewConId;
let _hidingOverviewConId;
let _styleChangedConId;
function update(reset = false) {
- opt = Me.imports.settings.opt;
- const panelBox = Main.layoutManager.panelBox;
- const panelHeight = Main.panel.height; // panelBox height can be 0 after shell start
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('panelModule', true);
+ // Avoid conflict with other extensions
+ const conflict = _Util.getEnabledExtensions('dash-to-panel').length ||
+ _Util.getEnabledExtensions('hidetopbar').length;
+ reset = reset || (!_firstRun && !moduleEnabled);
- const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor());
- if (reset || opt.PANEL_POSITION_TOP) {
- panelBox.set_position(geometry.x, geometry.y);
- } else {
- panelBox.set_position(geometry.x, geometry.y + geometry.height - panelHeight);
- }
+ // don't even touch this module if disabled or in potential conflict
+ if (_firstRun && (reset || conflict))
+ return;
- if (!_styleChangedConId) {
- Main.panel.connect('style-changed', ()=> Main.panel.remove_style_pseudo_class('overview'));
- }
+ _firstRun = false;
- if (reset || opt.PANEL_MODE === 0) {
- //_disconnectPanel();
- _disconnectOverview()
+ const panelBox = Main.layoutManager.panelBox;
+ if (reset || !moduleEnabled) {
+ // _disconnectPanel();
+ reset = true;
+ _setPanelPosition(reset);
+ _updateOverviewConnection(reset);
_reparentPanel(false);
- _showPanel();
- if (_styleChangedConId) {
- Main.panel.disconnect(_styleChangedConId);
- _styleChangedConId = 0;
- }
+ _updateStyleChangedConnection(reset);
panelBox.translation_y = 0;
- panelBox.opacity = 255;
+ Main.panel.opacity = 255;
+ _setPanelStructs(true);
+ return;
+ }
+
+ _setPanelPosition();
+ _updateStyleChangedConnection();
+
+ if (opt.PANEL_MODE === 0) {
+ _updateOverviewConnection(true);
+ _reparentPanel(false);
+ panelBox.translation_y = 0;
+ Main.panel.opacity = 255;
+ _setPanelStructs(true);
} else if (opt.PANEL_MODE === 1) {
if (opt.SHOW_WS_PREVIEW_BG) {
_reparentPanel(true);
if (opt.OVERVIEW_MODE2) {
// in OM2 if the panel has been moved to the overviewGroup move panel above all
Main.layoutManager.overviewGroup.set_child_above_sibling(panelBox, null);
+ _updateOverviewConnection();
} else {
// otherwise move the panel below overviewGroup so it can get below workspacesDisplay
Main.layoutManager.overviewGroup.set_child_below_sibling(panelBox, Main.overview._overview);
+ _updateOverviewConnection(true);
}
_showPanel(true);
} else {
// if ws preview bg is disabled, panel can stay in uiGroup
_reparentPanel(false);
_showPanel(false);
- if (!_hidingOverviewConId)
- _hidingOverviewConId = Main.overview.connect('hiding', () => {
- if ((!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2)) {
- _showPanel(false);
- }
- });
- if (!_showingOverviewConId)
- _showingOverviewConId = Main.overview.connect('showing', () => {
- if ((!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2)) {
- _showPanel(true);
- }
- });
+ _updateOverviewConnection();
}
-
- _connectPanel();
+ // _connectPanel();
} else if (opt.PANEL_MODE === 2) {
- _disconnectOverview();
+ _updateOverviewConnection(true);
_reparentPanel(false);
_showPanel(false);
- _connectPanel();
+ // _connectPanel();
}
- _setPanelStructs(reset || opt.PANEL_MODE === 0);
+ _setPanelStructs(opt.PANEL_MODE === 0);
+ Main.layoutManager._updateHotCorners();
+}
+
+function _setPanelPosition(reset = false) {
+ const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor());
+ const panelBox = Main.layoutManager.panelBox;
+ const panelHeight = Main.panel.height; // panelBox height can be 0 after shell start
+
+ if (opt.PANEL_POSITION_TOP || reset)
+ panelBox.set_position(geometry.x, geometry.y);
+ else
+ panelBox.set_position(geometry.x, geometry.y + geometry.height - panelHeight);
}
-function _disconnectOverview() {
- if (_hidingOverviewConId) {
- Main.overview.disconnect(_hidingOverviewConId);
- _hidingOverviewConId = 0;
+function _updateStyleChangedConnection(reset = false) {
+ if (reset) {
+ if (_styleChangedConId) {
+ Main.panel.disconnect(_styleChangedConId);
+ _styleChangedConId = 0;
+ }
+ } else if (!_styleChangedConId) {
+ Main.panel.connect('style-changed', () => {
+ if (opt.PANEL_MODE === 1)
+ Main.panel.add_style_pseudo_class('overview');
+ else if (opt.OVERVIEW_MODE2)
+ Main.panel.remove_style_pseudo_class('overview');
+ });
}
- if (_showingOverviewConId) {
- Main.overview.disconnect(_showingOverviewConId);
- _showingOverviewConId = 0;
+}
+
+function _updateOverviewConnection(reset = false) {
+ if (reset) {
+ if (_hidingOverviewConId) {
+ Main.overview.disconnect(_hidingOverviewConId);
+ _hidingOverviewConId = 0;
+ }
+ if (_showingOverviewConId) {
+ Main.overview.disconnect(_showingOverviewConId);
+ _showingOverviewConId = 0;
+ }
+ } else {
+ if (!_hidingOverviewConId) {
+ _hidingOverviewConId = Main.overview.connect('hiding', () => {
+ if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2)
+ _showPanel(false);
+ });
+ }
+ if (!_showingOverviewConId) {
+ _showingOverviewConId = Main.overview.connect('showing', () => {
+ if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2 || Main.layoutManager.panelBox.translation_y)
+ _showPanel(true);
+ });
+ }
}
}
@@ -106,7 +153,7 @@ function _reparentPanel(reparent = false) {
Main.layoutManager.overviewGroup.add_child(panel);
} else if (!reparent && panel.get_parent() === Main.layoutManager.overviewGroup) {
Main.layoutManager.overviewGroup.remove_child(panel);
- // return the panel at default position, pane shouldn't cover objects that should be above
+ // return the panel at default position, panel shouldn't cover objects that should be above
Main.layoutManager.uiGroup.insert_child_at_index(panel, 4);
}
}
@@ -120,10 +167,10 @@ function _setPanelStructs(state) {
// workaround to force maximized windows to resize after removing affectsStruts
// simulation of minimal swipe gesture to the opposite direction
// todo - needs better solution!!!!!!!!!!!
- /*const direction = _getAppGridAnimationDirection() === 2 ? 1 : -1;
- Main.overview._swipeTracker._beginTouchSwipe(null, global.get_current_time(), 1, 1);
- Main.overview._swipeTracker._updateGesture(null, global.get_current_time(), direction, 1);
- GLib.timeout_add(0, 50, () => Main.overview._swipeTracker._endGesture(global.get_current_time(), 1, true));*/
+ // const direction = _getAppGridAnimationDirection() === 2 ? 1 : -1;
+ // Main.overview._swipeTracker._beginTouchSwipe(null, global.get_current_time(), 1, 1);
+ // Main.overview._swipeTracker._updateGesture(null, global.get_current_time(), direction, 1);
+ // GLib.timeout_add(0, 50, () => Main.overview._swipeTracker._endGesture(global.get_current_time(), 1, true));*/
}
function _showPanel(show = true) {
@@ -134,9 +181,8 @@ function _showPanel(show = true) {
translation_y: 0,
onComplete: () => {
_setPanelStructs(opt.PANEL_MODE === 0);
- }
+ },
});
-
} else {
const panelHeight = Main.panel.height;
Main.layoutManager.panelBox.ease({
@@ -145,34 +191,7 @@ function _showPanel(show = true) {
onComplete: () => {
Main.panel.opacity = 0;
_setPanelStructs(opt.PANEL_MODE === 0);
- }
- });
- }
-}
-
-function _connectPanel() {
- // not reliable, disabled for now
- /*if (!_panelEnterSigId) {
- _panelEnterSigId = Main.panel.connect('enter-event', () => {
- if (!Main.overview._shown)
- _showPanel(true);
+ },
});
}
- if (!_panelLeaveSigId) {
- _panelLeaveSigId = Main.panel.connect('leave-event', () => {
- if (!Main.overview._shown)
- _showPanel(false);
- });
- }*/
-}
-
-function _disconnectPanel() {
- /*if (_panelEnterSigId) {
- Main.panel.disconnect(_panelEnterSigId);
- _panelEnterSigId = 0;
- }
- if (_panelLeaveSigId) {
- Main.panel.disconnect(_panelLeaveSigId);
- _panelLeaveSigId = 0;
- }*/
}
diff --git a/extensions/vertical-workspaces/recentFilesSearchProvider.js b/extensions/vertical-workspaces/lib/recentFilesSearchProvider.js
index b94d696..86e38f4 100644
--- a/extensions/vertical-workspaces/recentFilesSearchProvider.js
+++ b/extensions/vertical-workspaces/lib/recentFilesSearchProvider.js
@@ -9,13 +9,16 @@
'use strict';
-const { GLib, GObject, Gio, Gtk, Meta, St, Shell } = imports.gi;
+const { GLib, Gio, Meta, St, Shell, Gtk } = imports.gi;
const Main = imports.ui.main;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
-const Settings = Me.imports.settings;
-const _ = Me.imports.settings._;
+const Settings = Me.imports.lib.settings;
+const _Util = Me.imports.lib.util;
+
+// gettext
+const _ = Settings._;
const shellVersion = Settings.shellVersion;
@@ -31,24 +34,13 @@ var prefix = 'fq//';
var opt;
-const Action = {
- NONE: 0,
- CLOSE: 1,
- CLOSE_ALL: 2,
- MOVE_TO_WS: 3,
- MOVE_ALL_TO_WS: 4
-}
-
-function init() {
-}
-
function getOverviewSearchResult() {
- return Main.overview._overview.controls._searchController._searchResults;
+ return Main.overview._overview.controls._searchController._searchResults;
}
function update(reset = false) {
- opt = Me.imports.settings.opt;
+ opt = Me.imports.lib.settings.opt;
if (!reset && opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED && !recentFilesSearchProvider) {
enable();
} else if (reset || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED) {
@@ -63,7 +55,7 @@ function enable() {
GLib.PRIORITY_DEFAULT,
2000,
() => {
- if (recentFilesSearchProvider == null) {
+ if (!recentFilesSearchProvider) {
recentFilesSearchProvider = new RecentFilesSearchProvider(opt);
getOverviewSearchResult()._registerProvider(recentFilesSearchProvider);
}
@@ -84,58 +76,6 @@ function disable() {
}
}
-function fuzzyMatch(term, text) {
- let pos = -1;
- const matches = [];
- // convert all accented chars to their basic form and to lower case
- const _text = text;//.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
- const _term = term.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
-
- // if term matches the substring exactly, gains the highest weight
- if (_text.includes(_term)) {
- return 0;
- }
-
- for (let i = 0; i < _term.length; i++) {
- let c = _term[i];
- let p;
- if (pos > 0)
- p = _term[i - 1];
- while (true) {
- pos += 1;
- if (pos >= _text.length) {
- return -1;
- }
- if (_text[pos] == c) {
- matches.push(pos);
- break;
- } else if (_text[pos] == p) {
- matches.pop();
- matches.push(pos);
- }
- }
- }
-
- // add all position to get a weight of the result
- // results closer to the beginning of the text and term characters closer to each other will gain more weight.
- return matches.reduce((r, p) => r + p) - matches.length * matches[0] + matches[0];
-}
-
-function strictMatch(term, text) {
- // remove diacritics and accents from letters
- let s = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
- let p = term.toLowerCase();
- let ps = p.split(/ +/);
-
- // allows to use multiple exact patterns separated by a space in arbitrary order
- for (let w of ps) { // escape regex control chars
- if (!s.match(w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))) {
- return -1;
- }
- }
- return 0;
-}
-
function makeResult(window, i) {
const app = Shell.WindowTracker.get_default().get_window_app(window);
const appName = app ? app.get_name() : 'Unknown';
@@ -143,13 +83,13 @@ function makeResult(window, i) {
const wsIndex = window.get_workspace().index();
return {
- 'id': i,
- // convert all accented chars to their basic form and lower case for search
- 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(),
- 'appName': appName,
- 'windowTitle': windowTitle,
- 'window': window
- }
+ 'id': i,
+ // convert all accented chars to their basic form and lower case for search
+ 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(),
+ appName,
+ windowTitle,
+ window,
+ };
}
const closeSelectedRegex = /^\/x!$/;
@@ -157,22 +97,22 @@ const closeAllResultsRegex = /^\/xa!$/;
const moveToWsRegex = /^\/m[0-9]+$/;
const moveAllToWsRegex = /^\/ma[0-9]+$/;
-var RecentFilesSearchProvider = class RecentFilesSearchProvider {
- constructor(gOptions) {
- this._gOptions = gOptions;
+const RecentFilesSearchProvider = class RecentFilesSearchProvider {
+ constructor() {
+ this.id = 'org.gnome.Nautilus.desktop';
this.appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -ws recent:///', 'Recent Files', null);
- //this.appInfo = Shell.AppSystem.get_default().lookup_app('org.gnome.Nautilus.desktop').appInfo;
+ // this.appInfo = Shell.AppSystem.get_default().lookup_app('org.gnome.Nautilus.desktop').appInfo;
this.appInfo.get_description = () => _('Search recent files');
this.appInfo.get_name = () => _('Recent Files');
- this.appInfo.get_id = () => 'org.gnome.Nautilus.desktop';
+ this.appInfo.get_id = () => this.id;
this.appInfo.get_icon = () => Gio.icon_new_for_string('document-open-recent-symbolic');
this.appInfo.should_show = () => true;
- this.title = _('Recent Files Search Provider'),
+
this.canLaunchSearch = true;
this.isRemoteProvider = false;
}
- _getResultSet (terms) {
+ _getResultSet(terms) {
if (!terms[0].startsWith(prefix))
return [];
// do not modify original terms
@@ -182,60 +122,59 @@ var RecentFilesSearchProvider = class RecentFilesSearchProvider {
const candidates = this.files;
const _terms = [].concat(termsCopy);
- let match;
+ // let match;
const term = _terms.join(' ');
- match = (s) => {
+ /* match = s => {
return fuzzyMatch(term, s);
- }
+ }; */
const results = [];
let m;
for (let id in candidates) {
const file = this.files[id];
- const name = `${file.get_age()}d: ${file.get_display_name()} ${file.get_uri_display().replace(`\/${file.get_display_name()}`, '')}`;
- if (this._gOptions.get('searchFuzzy')) {
- m = fuzzyMatch(term, name);
- } else {
- m = strictMatch(term, name);
- }
- if (m !== -1) {
- results.push({ weight: m, id: id});
- }
+ const name = `${file.get_age()}d: ${file.get_display_name()} ${file.get_uri_display().replace(`/${file.get_display_name()}`, '')}`;
+ if (opt.SEARCH_FUZZY)
+ m = _Util.fuzzyMatch(term, name);
+ else
+ m = _Util.strictMatch(term, name);
+
+ if (m !== -1)
+ results.push({ weight: m, id });
}
results.sort((a, b) => this.files[a.id].get_visited() < this.files[b.id].get_visited());
- this.resultIds = results.map((item) => item.id);
+ this.resultIds = results.map(item => item.id);
return this.resultIds;
}
- getResultMetas (resultIds, callback = null) {
- const metas = resultIds.map((id) => this.getResultMeta(id));
- if (shellVersion >= 43) {
+ getResultMetas(resultIds, callback = null) {
+ const metas = resultIds.map(id => this.getResultMeta(id));
+ if (shellVersion >= 43)
return new Promise(resolve => resolve(metas));
- } else {
+ else if (callback)
callback(metas);
- }
+ return null;
}
- getResultMeta (resultId) {
+ getResultMeta(resultId) {
const result = this.files[resultId];
return {
'id': resultId,
'name': `${result.get_age()}: ${result.get_display_name()}`,
- 'description': `${result.get_uri_display().replace(`\/${result.get_display_name()}`, '')}`,
- 'createIcon': (size) => {
+ 'description': `${result.get_uri_display().replace(`/${result.get_display_name()}`, '')}`,
+ 'createIcon': size => {
let icon = this.getIcon(result, size);
return icon;
},
- }
+ };
}
getIcon(result, size) {
let file = Gio.File.new_for_uri(result.get_uri());
let info = file.query_info(Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH,
- Gio.FileQueryInfoFlags.NONE, null);
+ Gio.FileQueryInfoFlags.NONE, null);
let path = info.get_attribute_byte_string(
Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH);
@@ -249,17 +188,17 @@ var RecentFilesSearchProvider = class RecentFilesSearchProvider {
gicon = appInfo.get_icon();
}
- if (gicon) {
+ if (gicon)
icon = new St.Icon({ gicon, icon_size: size });
- } else {
+ else
icon = new St.Icon({ icon_name: 'icon-missing', icon_size: size });
- }
+
return icon;
}
- launchSearch(terms, timeStamp) {
- this._openNautilus('recent:///')
+ launchSearch(/* terms, timeStamp */) {
+ this._openNautilus('recent:///');
}
_openNautilus(uri) {
@@ -270,55 +209,52 @@ var RecentFilesSearchProvider = class RecentFilesSearchProvider {
}
}
- activateResult (resultId, terms, timeStamp) {
+ activateResult(resultId /* , terms, timeStamp */) {
const file = this.files[resultId];
- const [,,state] = global.get_pointer();
- //const isCtrlPressed = (state & ModifierType.CONTROL_MASK) != 0;
- const isShiftPressed = (state & ModifierType.SHIFT_MASK) != 0;
-
- if (isShiftPressed) {
+ if (_Util.isShiftPressed()) {
Main.overview.toggle();
this._openNautilus(file.get_uri());
} else {
const appInfo = Gio.AppInfo.get_default_for_type(file.get_mime_type(), false);
- if (!(appInfo && appInfo.launch_uris([file.get_uri()], null))) {
+ if (!(appInfo && appInfo.launch_uris([file.get_uri()], null)))
this._openNautilus(file.get_uri());
- }
}
}
- getInitialResultSet (terms, callback, cancellable = null) {
- if (shellVersion >=43) {
- cancellable = callback;
- }
+ getInitialResultSet(terms, callback /* , cancellable = null*/) {
+ // In GS 43 callback arg has been removed
+ /* if (shellVersion >= 43)
+ cancellable = callback; */
const filesDict = {};
- const files = Gtk.RecentManager.get_default().get_items().filter((f)=> f.exists());
+ const files = Gtk.RecentManager.get_default().get_items().filter(f => f.exists());
- for (let file of files) {
+ for (let file of files)
filesDict[file.get_uri()] = file;
- }
+
this.files = filesDict;
- if (shellVersion >= 43) {
+ if (shellVersion >= 43)
return new Promise(resolve => resolve(this._getResultSet(terms)));
- } else {
+ else
callback(this._getResultSet(terms));
- }
+
+ return null;
}
- filterResults (results, maxResults) {
- return results.slice(0, maxResults);
+ filterResults(results, maxResults) {
+ return results.slice(0, 20);
+ // return results.slice(0, maxResults);
}
- getSubsearchResultSet (previousResults, terms, callback, cancellable) {
+ getSubsearchResultSet(previousResults, terms, callback /* , cancellable*/) {
// if we return previous results, quick typers get non-actual results
callback(this._getResultSet(terms));
}
- createResultOjbect(resultMeta) {
+ /* createResultObject(resultMeta) {
return this.files[resultMeta.id];
- }
-}
+ }*/
+};
diff --git a/extensions/vertical-workspaces/lib/search.js b/extensions/vertical-workspaces/lib/search.js
new file mode 100644
index 0000000..8540626
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/search.js
@@ -0,0 +1,206 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * search.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+const { Shell, Gio, St, Clutter } = imports.gi;
+const Main = imports.ui.main;
+
+const AppDisplay = imports.ui.appDisplay;
+const Search = imports.ui.search;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const _Util = Me.imports.lib.util;
+
+const _ = Me.imports.lib.settings._;
+const shellVersion = _Util.shellVersion;
+
+let opt;
+let _overrides;
+let _firstRun = true;
+
+let SEARCH_MAX_WIDTH;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('searchModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+
+ if (_overrides)
+ _overrides.removeAll();
+
+ _updateSearchViewWidth(reset);
+
+ if (reset) {
+ Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.FILL;
+ opt = null;
+ _overrides = null;
+ return;
+ }
+
+ _overrides = new _Util.Overrides();
+
+ _overrides.addOverride('AppSearchProvider', AppDisplay.AppSearchProvider.prototype, AppSearchProvider);
+ _overrides.addOverride('SearchResult', Search.SearchResult.prototype, SearchResult);
+ _overrides.addOverride('SearchResultsView', Search.SearchResultsView.prototype, SearchResultsView);
+
+ // Don't expand the search view vertically and align it to the top
+ // this is important in the static workspace mode when the search view bg is not transparent
+ // also the "Searching..." and "No Results" notifications will be closer to the search entry, with the distance given by margin-top in the stylesheet
+ Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.START;
+}
+
+function _updateSearchViewWidth(reset = false) {
+ const searchContent = Main.overview._overview._controls.layoutManager._searchController._searchResults._content;
+ if (!SEARCH_MAX_WIDTH) { // just store original value;
+ const themeNode = searchContent.get_theme_node();
+ const width = themeNode.get_max_width();
+ SEARCH_MAX_WIDTH = width;
+ }
+
+ if (reset) {
+ searchContent.set_style('');
+ } else {
+ let width = Math.round(SEARCH_MAX_WIDTH * opt.SEARCH_VIEW_SCALE);
+ searchContent.set_style(`max-width: ${width}px;`);
+ }
+}
+
+// AppDisplay.AppSearchProvider
+const AppSearchProvider = {
+ getInitialResultSet(terms, callback, _cancellable) {
+ // Defer until the parental controls manager is initialized, so the
+ // results can be filtered correctly.
+ if (!this._parentalControlsManager.initialized) {
+ let initializedId = this._parentalControlsManager.connect('app-filter-changed', () => {
+ if (this._parentalControlsManager.initialized) {
+ this._parentalControlsManager.disconnect(initializedId);
+ this.getInitialResultSet(terms, callback, _cancellable);
+ }
+ });
+ return;
+ }
+
+
+ const pattern = terms.join(' ');
+ let appInfoList = Shell.AppSystem.get_default().get_installed();
+
+ let weightList = {};
+ appInfoList = appInfoList.filter(appInfo => {
+ try {
+ appInfo.get_id(); // catch invalid file encodings
+ } catch (e) {
+ return false;
+ }
+
+ let string = '';
+ let name;
+ let shouldShow = false;
+ if (appInfo.get_display_name) {
+ // show only launchers that should be visible in this DE
+ shouldShow = appInfo.should_show() && this._parentalControlsManager.shouldShowApp(appInfo);
+
+ if (shouldShow) {
+ let dispName = appInfo.get_display_name() || '';
+ let gName = appInfo.get_generic_name() || '';
+ let description = appInfo.get_description() || '';
+ let categories = appInfo.get_string('Categories') || '';
+ let keywords = appInfo.get_string('Keywords') || '';
+ name = dispName;
+ string = `${dispName} ${gName} ${description} ${categories} ${keywords}`;
+ }
+ }
+
+ let m = -1;
+ if (shouldShow && opt.SEARCH_FUZZY) {
+ m = _Util.fuzzyMatch(pattern, name);
+ m = (m + _Util.strictMatch(pattern, string)) / 2;
+ } else if (shouldShow) {
+ m = _Util.strictMatch(pattern, string);
+ }
+
+ if (m !== -1)
+ weightList[appInfo.get_id()] = m;
+
+ return shouldShow && (m !== -1);
+ });
+
+ appInfoList.sort((a, b) => weightList[a.get_id()] > weightList[b.get_id()]);
+
+ const usage = Shell.AppUsage.get_default();
+ // sort apps by usage list
+ appInfoList.sort((a, b) => usage.compare(a.get_id(), b.get_id()));
+ // prefer apps where any word in their name starts with the pattern
+ appInfoList.sort((a, b) => _Util.isMoreRelevant(a.get_display_name(), b.get_display_name(), pattern));
+
+ let results = appInfoList.map(app => app.get_id());
+
+ results = results.concat(this._systemActions.getMatchingActions(terms));
+
+ if (shellVersion < 43)
+ callback(results);
+ else
+ return new Promise(resolve => resolve(results));
+ },
+
+ // App search result size
+ createResultObject(resultMeta) {
+ if (resultMeta.id.endsWith('.desktop')) {
+ const icon = new AppDisplay.AppIcon(this._appSys.lookup_app(resultMeta['id']), {
+ expandTitleOnHover: false,
+ });
+ icon.icon.setIconSize(opt.SEARCH_ICON_SIZE);
+ return icon;
+ } else {
+ const icon = new AppDisplay.SystemActionIcon(this, resultMeta);
+ icon.icon._setSizeManually = true;
+ icon.icon.setIconSize(opt.SEARCH_ICON_SIZE);
+ return icon;
+ }
+ },
+};
+
+const SearchResult = {
+ activate() {
+ this.provider.activateResult(this.metaInfo.id, this._resultsView.terms);
+
+ if (this.metaInfo.clipboardText) {
+ St.Clipboard.get_default().set_text(
+ St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText);
+ }
+ // don't close overview if Shift key is pressed - Shift moves windows to the workspace
+ if (!_Util.isShiftPressed())
+ Main.overview.toggle();
+ },
+};
+
+const SearchResultsView = {
+ _updateSearchProgress() {
+ let haveResults = this._providers.some(provider => {
+ let display = provider.display;
+ return display.getFirstResult() !== null;
+ });
+
+ this._scrollView.visible = haveResults;
+ this._statusBin.visible = !haveResults;
+
+ if (!haveResults) {
+ if (this.searchInProgress)
+ this._statusText.set_text(_('Searching…'));
+ else
+ this._statusText.set_text(_('No results.'));
+ }
+ },
+};
diff --git a/extensions/vertical-workspaces/settings.js b/extensions/vertical-workspaces/lib/settings.js
index feb285e..66f3a45 100644
--- a/extensions/vertical-workspaces/settings.js
+++ b/extensions/vertical-workspaces/lib/settings.js
@@ -1,5 +1,5 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* settings.js
*
* @author GdH <G-dH@github.com>
@@ -11,10 +11,11 @@
const { GLib } = imports.gi;
+const Config = imports.misc.config;
+
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
-const Config = imports.misc.config;
var shellVersion = parseFloat(Config.PACKAGE_VERSION);
const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
@@ -24,7 +25,6 @@ const _schema = Me.metadata['settings-schema'];
// common instance of Options accessible from all modules
var opt;
-
var Options = class Options {
constructor() {
this._gsettings = ExtensionUtils.getSettings(_schema);
@@ -37,7 +37,7 @@ var Options = class Options {
this._writeTimeoutId = GLib.timeout_add(
GLib.PRIORITY_DEFAULT,
- 100,
+ 400,
() => {
this._gsettings.apply();
this._updateCachedSettings();
@@ -50,20 +50,25 @@ var Options = class Options {
workspaceThumbnailsPosition: ['int', 'ws-thumbnails-position'],
wsMaxSpacing: ['int', 'ws-max-spacing'],
wsPreviewScale: ['int', 'ws-preview-scale'],
- WsThumbnailsFull: ['bool', 'ws-thumbnails-full'],
- secondaryWsThumbnailsPosition: ['int', 'secondary-ws-thumbnails-position'],
+ secWsPreviewScale: ['int', 'secondary-ws-preview-scale'],
+ secWsPreviewShift: ['bool', 'secondary-ws-preview-shift'],
+ wsThumbnailsFull: ['bool', 'ws-thumbnails-full'],
+ secWsThumbnailsPosition: ['int', 'secondary-ws-thumbnails-position'],
dashPosition: ['int', 'dash-position'],
dashPositionAdjust: ['int', 'dash-position-adjust'],
wsTmbPositionAdjust: ['int', 'wst-position-adjust'],
showWsTmbLabels: ['int', 'show-wst-labels'],
showWsTmbLabelsOnHover: ['boolean', 'show-wst-labels-on-hover'],
- SecWsTmbPositionAdjust: ['int', 'sec-wst-position-adjust'],
+ closeWsButtonMode: ['int', 'close-ws-button-mode'],
+ secWsTmbPositionAdjust: ['int', 'sec-wst-position-adjust'],
dashMaxIconSize: ['int', 'dash-max-icon-size'],
dashShowWindowsIcon: ['int', 'dash-show-windows-icon'],
dashShowRecentFilesIcon: ['int', 'dash-show-recent-files-icon'],
centerDashToWs: ['boolean', 'center-dash-to-ws'],
showAppsIconPosition: ['int', 'show-app-icon-position'],
wsThumbnailScale: ['int', 'ws-thumbnail-scale'],
+ wsThumbnailScaleAppGrid: ['int', 'ws-thumbnail-scale-appgrid'],
+ secWsThumbnailScale: ['int', 'secondary-ws-thumbnail-scale'],
showSearchEntry: ['boolean', 'show-search-entry'],
centerSearch: ['boolean', 'center-search'],
centerAppGrid: ['boolean', 'center-app-grid'],
@@ -72,7 +77,9 @@ var Options = class Options {
enablePageShortcuts: ['boolean', 'enable-page-shortcuts'],
showWsSwitcherBg: ['boolean', 'show-ws-switcher-bg'],
showWsPreviewBg: ['boolean', 'show-ws-preview-bg'],
+ wsPreviewBgRadius: ['int', 'ws-preview-bg-radius'],
showBgInOverview: ['boolean', 'show-bg-in-overview'],
+ overviewBgBrightness: ['int', 'overview-bg-brightness'],
overviewBgBlurSigma: ['int', 'overview-bg-blur-sigma'],
appGridBgBlurSigma: ['int', 'app-grid-bg-blur-sigma'],
smoothBlurTransitions: ['boolean', 'smooth-blur-transitions'],
@@ -82,33 +89,78 @@ var Options = class Options {
animationSpeedFactor: ['int', 'animation-speed-factor'],
fixUbuntuDock: ['boolean', 'fix-ubuntu-dock'],
winPreviewIconSize: ['int', 'win-preview-icon-size'],
- alwaysShowWinTitles: ['int', 'always-show-win-titles'],
+ alwaysShowWinTitles: ['boolean', 'always-show-win-titles'],
startupState: ['int', 'startup-state'],
overviewMode: ['int', 'overview-mode'],
workspaceSwitcherAnimation: ['int', 'workspace-switcher-animation'],
searchIconSize: ['int', 'search-icon-size'],
searchViewScale: ['int', 'search-width-scale'],
- appGridAllowCustom: ['int', 'app-grid-allow-custom'],
appGridIconSize: ['int', 'app-grid-icon-size'],
appGridColumns: ['int', 'app-grid-columns'],
appGridRows: ['int', 'app-grid-rows'],
appGridFolderIconSize: ['int', 'app-grid-folder-icon-size'],
appGridFolderColumns: ['int', 'app-grid-folder-columns'],
appGridFolderRows: ['int', 'app-grid-folder-rows'],
- appGridIncludeDash: ['int', 'app-grid-include-dash'],
+ appGridFolderIconGrid: ['int', 'app-grid-folder-icon-grid'],
+ appGridContent: ['int', 'app-grid-content'],
appGridIncompletePages: ['boolean', 'app-grid-incomplete-pages'],
appGridOrder: ['int', 'app-grid-order'],
+ appGridNamesMode: ['int', 'app-grid-names'],
+ appGridActivePreview: ['boolean', 'app-grid-active-preview'],
+ appGridFolderCenter: ['boolean', 'app-grid-folder-center'],
+ appGridPageWidthScale: ['int', 'app-grid-page-width-scale'],
+ appGridSpacing: ['int', 'app-grid-spacing'],
searchWindowsEnable: ['boolean', 'search-windows-enable'],
searchRecentFilesEnable: ['boolean', 'search-recent-files-enable'],
searchFuzzy: ['boolean', 'search-fuzzy'],
searchMaxResultsRows: ['int', 'search-max-results-rows'],
dashShowWindowsBeforeActivation: ['int', 'dash-show-windows-before-activation'],
+ dashIconScroll: ['int', 'dash-icon-scroll'],
+ searchWindowsIconScroll: ['int', 'search-windows-icon-scroll'],
panelVisibility: ['int', 'panel-visibility'],
panelPosition: ['int', 'panel-position'],
- }
+ windowAttentionMode: ['int', 'window-attention-mode'],
+ wsSwPopupHPosition: ['int', 'ws-sw-popup-h-position'],
+ wsSwPopupVPosition: ['int', 'ws-sw-popup-v-position'],
+ wsSwPopupMode: ['int', 'ws-sw-popup-mode'],
+ favoritesNotify: ['int', 'favorites-notify'],
+ notificationPosition: ['int', 'notification-position'],
+ osdPosition: ['int', 'osd-position'],
+ hotCornerAction: ['int', 'hot-corner-action'],
+ hotCornerPosition: ['int', 'hot-corner-position'],
+ hotCornerFullscreen: ['boolean', 'hot-corner-fullscreen'],
+ hotCornerRipples: ['boolean', 'hot-corner-ripples'],
+ alwaysActivateSelectedWindow: ['boolean', 'always-activate-selected-window'],
+ windowIconClickSearch: ['boolean', 'window-icon-click-search'],
+ overlayKeySecondary: ['int', 'overlay-key-secondary'],
+
+ workspaceThumbnailsModule: ['boolean', 'workspace-thumbnails-module'],
+ workspaceSwitcherPopupModule: ['boolean', 'workspace-switcher-popup-module'],
+ workspaceAnimationModule: ['boolean', 'workspace-animation-module'],
+ workspaceModule: ['boolean', 'workspace-module'],
+ windowManagerModule: ['boolean', 'window-manager-module'],
+ windowPreviewModule: ['boolean', 'window-preview-module'],
+ winAttentionHandlerModule: ['boolean', 'win-attention-handler-module'],
+ swipeTrackerModule: ['boolean', 'swipe-tracker-module'],
+ searchModule: ['boolean', 'search-module'],
+ panelModule: ['boolean', 'panel-module'],
+ overlayKeyModule: ['boolean', 'overlay-key-module'],
+ osdWindowModule: ['boolean', 'osd-window-module'],
+ messageTrayModule: ['boolean', 'message-tray-module'],
+ layoutModule: ['boolean', 'layout-module'],
+ dashModule: ['boolean', 'dash-module'],
+ appFavoritesModule: ['boolean', 'app-favorites-module'],
+ appDisplayModule: ['boolean', 'app-display-module'],
+
+ profileName1: ['string', 'profile-name-1'],
+ profileName2: ['string', 'profile-name-2'],
+ profileName3: ['string', 'profile-name-3'],
+ profileName4: ['string', 'profile-name-4'],
+ };
this.cachedOptions = {};
this.shellVersion = shellVersion;
+ // this.storeProfile(0);
}
connect(name, callback) {
@@ -125,24 +177,24 @@ var Options = class Options {
}
}
- _updateCachedSettings(settings, key) {
+ _updateCachedSettings() {
Object.keys(this.options).forEach(v => this.get(v, true));
}
get(option, updateCache = false) {
if (!this.options[option]) {
log(`[${Me.metadata.name}] Error: Option ${option} is undefined.`);
- return;
+ return null;
}
if (updateCache || this.cachedOptions[option] === undefined) {
- const [format, key, settings] = this.options[option];
+ const [, key, settings] = this.options[option];
let gSettings;
- if (settings !== undefined) {
+ if (settings !== undefined)
gSettings = settings();
- } else {
+ else
gSettings = this._gsettings;
- }
+
this.cachedOptions[option] = gSettings.get_value(key).deep_unpack();
}
@@ -155,39 +207,74 @@ var Options = class Options {
let gSettings = this._gsettings;
- if (settings !== undefined) {
+ if (settings !== undefined)
gSettings = settings();
- }
+
switch (format) {
- case 'boolean':
- gSettings.set_boolean(key, value);
- break;
- case 'int':
- gSettings.set_int(key, value);
- break;
- case 'string':
- gSettings.set_string(key, value);
- break;
- case 'strv':
- gSettings.set_strv(key, value);
- break;
+ case 'boolean':
+ gSettings.set_boolean(key, value);
+ break;
+ case 'int':
+ gSettings.set_int(key, value);
+ break;
+ case 'string':
+ gSettings.set_string(key, value);
+ break;
+ case 'strv':
+ gSettings.set_strv(key, value);
+ break;
}
}
getDefault(option) {
- const [format, key, settings] = this.options[option];
+ const [, key, settings] = this.options[option];
let gSettings = this._gsettings;
- if (settings !== undefined) {
+ if (settings !== undefined)
gSettings = settings();
- }
+
return gSettings.get_default_value(key).deep_unpack();
}
- _updateSettings(settings, key) {
+ storeProfile(index) {
+ const profile = {};
+ Object.keys(this.options).forEach(v => {
+ profile[v] = this.get(v).toString();
+ });
+
+ this._gsettings.set_value(`profile-data-${index}`, new GLib.Variant('a{ss}', profile));
+ }
+
+ loadProfile(index) {
+ const options = this._gsettings.get_value(`profile-data-${index}`).deep_unpack();
+ this._gsettings.set_boolean('aaa-loading-profile', !this._gsettings.get_boolean('aaa-loading-profile'));
+ for (let o of Object.keys(options)) {
+ const [type] = this.options[o];
+ let value = options[o];
+ switch (type) {
+ case 'string':
+ break;
+ case 'boolean':
+ value = value === 'true';
+ break;
+ case 'int':
+ value = parseInt(value);
+ break;
+ }
+
+ this.set(o, value);
+ }
+ }
+
+ resetProfile(index) {
+ this._gsettings.reset(`profile-data-${index}`);
+ this._gsettings.reset(`profile-name-${index}`);
+ }
+
+ _updateSettings() {
this.DASH_POSITION = this.get('dashPosition', true);
this.DASH_TOP = this.DASH_POSITION === 0;
this.DASH_RIGHT = this.DASH_POSITION === 1;
@@ -197,6 +284,12 @@ var Options = class Options {
this.DASH_VISIBLE = this.DASH_POSITION !== 4; // 4 - disable
this.DASH_FOLLOW_RECENT_WIN = false;
+ this.DASH_CLICK_ACTION = this.get('dashShowWindowsBeforeActivation', true);
+ this.DASH_ICON_SCROLL = this.get('dashIconScroll', true);
+ this.DASH_SHIFT_CLICK_MV = true;
+
+ this.SEARCH_WINDOWS_ICON_SCROLL = this.get('searchWindowsIconScroll', true);
+
this.DASH_POSITION_ADJUSTMENT = this.get('dashPositionAdjust', true);
this.DASH_POSITION_ADJUSTMENT = this.DASH_POSITION_ADJUSTMENT * -1 / 100; // range 1 to -1
this.CENTER_DASH_WS = this.get('centerDashToWs', true);
@@ -208,34 +301,44 @@ var Options = class Options {
this.WS_TMB_POSITION = this.get('workspaceThumbnailsPosition', true);
this.ORIENTATION = this.WS_TMB_POSITION > 4 ? 0 : 1;
this.WORKSPACE_MAX_SPACING = this.get('wsMaxSpacing', true);
- //ORIENTATION || DASH_LEFT || DASH_RIGHT ? 350 : 80;
+ // ORIENTATION || DASH_LEFT || DASH_RIGHT ? 350 : 80;
this.SHOW_WS_TMB = ![4, 9].includes(this.WS_TMB_POSITION); // 4, 9 - disable
- this.WS_TMB_FULL = this.get('WsThumbnailsFull', true);
+ this.WS_TMB_FULL = this.get('wsThumbnailsFull', true);
// translate ws tmb position to 0 top, 1 right, 2 bottom, 3 left
- //0L 1R, 2LF, 3RF, 4DV, 5T, 6B, 7TF, 8BF, 9DH
+ // 0L 1R, 2LF, 3RF, 4DV, 5T, 6B, 7TF, 8BF, 9DH
this.WS_TMB_POSITION = [3, 1, 3, 1, 4, 0, 2, 0, 2, 8][this.WS_TMB_POSITION];
this.WS_TMB_TOP = this.WS_TMB_POSITION === 0;
this.WS_TMB_RIGHT = this.WS_TMB_POSITION === 1;
this.WS_TMB_BOTTOM = this.WS_TMB_POSITION === 2;
this.WS_TMB_LEFT = this.WS_TMB_POSITION === 3;
this.WS_TMB_POSITION_ADJUSTMENT = this.get('wsTmbPositionAdjust', true) * -1 / 100; // range 1 to -1
- this.SEC_WS_TMB_POSITION = this.get('secondaryWsThumbnailsPosition', true);
+ this.SEC_WS_TMB_POSITION = this.get('secWsThumbnailsPosition', true);
+ this.SHOW_SEC_WS_TMB = this.SEC_WS_TMB_POSITION !== 3 && this.SHOW_WS_TMB;
this.SEC_WS_TMB_TOP = (this.SEC_WS_TMB_POSITION === 0 && !this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_TOP);
this.SEC_WS_TMB_RIGHT = (this.SEC_WS_TMB_POSITION === 1 && this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_RIGHT);
this.SEC_WS_TMB_BOTTOM = (this.SEC_WS_TMB_POSITION === 1 && !this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_BOTTOM);
this.SEC_WS_TMB_LEFT = (this.SEC_WS_TMB_POSITION === 0 && this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_LEFT);
- this.SEC_WS_TMB_POSITION_ADJUSTMENT = this.get('SecWsTmbPositionAdjust', true) * -1 / 100; // range 1 to -1
+ this.SEC_WS_TMB_POSITION_ADJUSTMENT = this.get('secWsTmbPositionAdjust', true) * -1 / 100; // range 1 to -1
+ this.SEC_WS_PREVIEW_SHIFT = this.get('secWsPreviewShift', true);
this.SHOW_WST_LABELS = this.get('showWsTmbLabels', true);
this.SHOW_WST_LABELS_ON_HOVER = this.get('showWsTmbLabelsOnHover', true);
+ this.CLOSE_WS_BUTTON_MODE = this.get('closeWsButtonMode', true);
this.MAX_THUMBNAIL_SCALE = this.get('wsThumbnailScale', true) / 100;
+ this.MAX_THUMBNAIL_SCALE_APPGRID = this.get('wsThumbnailScaleAppGrid', true) / 100;
+ if (this.MAX_THUMBNAIL_SCALE_APPGRID === 0)
+ this.MAX_THUMBNAIL_SCALE_APPGRID = this.MAX_THUMBNAIL_SCALE;
+ this.MAX_THUMBNAIL_SCALE_STABLE = this.MAX_THUMBNAIL_SCALE === this.MAX_THUMBNAIL_SCALE_APPGRID;
+ this.SEC_MAX_THUMBNAIL_SCALE = this.get('secWsThumbnailScale', true) / 100;
this.WS_PREVIEW_SCALE = this.get('wsPreviewScale', true) / 100;
+ this.SEC_WS_PREVIEW_SCALE = this.get('secWsPreviewScale', true) / 100;
// calculate number of possibly visible neighbor previews according to ws scale
- this.NUMBER_OF_VISIBLE_NEIGHBORS = Math.round(1 + (100 - this.WS_PREVIEW_SCALE) / 40);
+ this.NUMBER_OF_VISIBLE_NEIGHBORS = Math.round(1 + (1 - this.WS_PREVIEW_SCALE) / 4);
this.SHOW_WS_TMB_BG = this.get('showWsSwitcherBg', true) && this.SHOW_WS_TMB;
+ this.WS_PREVIEW_BG_RADIUS = this.get('wsPreviewBgRadius', true);
this.SHOW_WS_PREVIEW_BG = this.get('showWsPreviewBg', true);
this.CENTER_APP_GRID = this.get('centerAppGrid', true);
@@ -243,13 +346,13 @@ var Options = class Options {
this.SHOW_SEARCH_ENTRY = this.get('showSearchEntry', true);
this.CENTER_SEARCH_VIEW = this.get('centerSearch', true);
this.APP_GRID_ANIMATION = this.get('appGridAnimation', true);
- if (this.APP_GRID_ANIMATION === 4) {
+ if (this.APP_GRID_ANIMATION === 4)
this.APP_GRID_ANIMATION = this._getAnimationDirection();
- }
+
this.SEARCH_VIEW_ANIMATION = this.get('searchViewAnimation', true);
- if (this.SEARCH_VIEW_ANIMATION === 4) {
+ if (this.SEARCH_VIEW_ANIMATION === 4)
this.SEARCH_VIEW_ANIMATION = 3;
- }
+
this.WS_ANIMATION = this.get('workspaceAnimation', true);
this.WIN_PREVIEW_ICON_SIZE = [64, 48, 32, 22, 8][this.get('winPreviewIconSize', true)];
@@ -257,6 +360,7 @@ var Options = class Options {
this.STARTUP_STATE = this.get('startupState', true);
this.SHOW_BG_IN_OVERVIEW = this.get('showBgInOverview', true);
+ this.OVERVIEW_BG_BRIGHTNESS = this.get('overviewBgBrightness', true) / 100;
this.OVERVIEW_BG_BLUR_SIGMA = this.get('overviewBgBlurSigma', true);
this.APP_GRID_BG_BLUR_SIGMA = this.get('appGridBgBlurSigma', true);
this.SMOOTH_BLUR_TRANSITIONS = this.get('smoothBlurTransitions', true);
@@ -264,47 +368,102 @@ var Options = class Options {
this.OVERVIEW_MODE = this.get('overviewMode', true);
this.OVERVIEW_MODE2 = this.OVERVIEW_MODE === 2;
this.WORKSPACE_MODE = this.OVERVIEW_MODE ? 0 : 1;
- //Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95;
this.STATIC_WS_SWITCHER_BG = this.get('workspaceSwitcherAnimation', true);
this.ANIMATION_TIME_FACTOR = this.get('animationSpeedFactor', true) / 100;
- //St.Settings.get().slow_down_factor = this.ANIMATION_TIME_FACTOR;
this.SEARCH_ICON_SIZE = this.get('searchIconSize', true);
this.SEARCH_VIEW_SCALE = this.get('searchViewScale', true) / 100;
this.SEARCH_MAX_ROWS = this.get('searchMaxResultsRows', true);
- //imports.ui.search.MAX_LIST_SEARCH_RESULTS_ROWS = this.SEARCH_MAX_ROWS;
+ this.SEARCH_FUZZY = this.get('searchFuzzy', true);
- this.APP_GRID_ALLOW_INCOMPLETE_PAGES = false;
- this.APP_GRID_ALLOW_CUSTOM = this.get('appGridAllowCustom', true);
+ this.APP_GRID_ALLOW_INCOMPLETE_PAGES = this.get('appGridIncompletePages', true);
this.APP_GRID_ICON_SIZE = this.get('appGridIconSize', true);
this.APP_GRID_COLUMNS = this.get('appGridColumns', true);
this.APP_GRID_ROWS = this.get('appGridRows', true);
+ this.APP_GRID_ADAPTIVE = !this.APP_GRID_COLUMNS && !this.APP_GRID_ROWS;
this.APP_GRID_ORDER = this.get('appGridOrder', true);
- this.APP_GRID_INCLUDE_DASH = this.get('appGridIncludeDash', true);
-
+
+ this.APP_GRID_INCLUDE_DASH = this.get('appGridContent', true);
+ /* APP_GRID_INCLUDE_DASH
+ 0 - Include All
+ 1 - Include All - Favorites and Runnings First
+ 2 - Exclude Favorites (Default)
+ 3 - Exclude Running
+ 4 - Exclude Favorites and Running
+ */
+ this.APP_GRID_EXCLUDE_FAVORITES = this.APP_GRID_INCLUDE_DASH === 2 || this.APP_GRID_INCLUDE_DASH === 4;
+ this.APP_GRID_EXCLUDE_RUNNING = this.APP_GRID_INCLUDE_DASH === 3 || this.APP_GRID_INCLUDE_DASH === 4;
+ this.APP_GRID_DASH_FIRST = this.APP_GRID_INCLUDE_DASH === 1;
+
+ this.APP_GRID_NAMES_MODE = this.get('appGridNamesMode', true);
+
this.APP_GRID_FOLDER_ICON_SIZE = this.get('appGridFolderIconSize', true);
+ this.APP_GRID_FOLDER_ICON_GRID = this.get('appGridFolderIconGrid', true);
this.APP_GRID_FOLDER_COLUMNS = this.get('appGridFolderColumns', true);
this.APP_GRID_FOLDER_ROWS = this.get('appGridFolderRows', true);
+ this.APP_GRID_SPACING = this.get('appGridSpacing', true);
+ this.APP_GRID_FOLDER_DEFAULT = this.APP_GRID_FOLDER_ROWS === 3 && this.APP_GRID_FOLDER_COLUMNS === 3;
+ this.APP_GRID_ACTIVE_PREVIEW = this.get('appGridActivePreview', true);
+ this.APP_GRID_FOLDER_CENTER = this.get('appGridFolderCenter', true);
+ this.APP_GRID_PAGE_WIDTH_SCALE = this.get('appGridPageWidthScale', true) / 100;
- this.DASH_SHOW_WINS_BEFORE = this.get('dashShowWindowsBeforeActivation', true);
- this.DASH_SHIFT_CLICK_MV = true;
+ this.APP_GRID_ICON_SIZE_DEFAULT = this.APP_GRID_ACTIVE_PREVIEW && !this.APP_GRID_ORDER ? 176 : 96;
+ this.APP_GRID_FOLDER_ICON_SIZE_DEFAULT = 96;
this.WINDOW_SEARCH_PROVIDER_ENABLED = this.get('searchWindowsEnable', true);
this.RECENT_FILES_SEARCH_PROVIDER_ENABLED = this.get('searchRecentFilesEnable', true);
this.PANEL_POSITION_TOP = this.get('panelPosition', true) === 0;
this.PANEL_MODE = this.get('panelVisibility', true);
+ this.PANEL_DISABLED = this.PANEL_MODE === 2;
+ this.PANEL_OVERVIEW_ONLY = this.PANEL_MODE === 1;
this.START_Y_OFFSET = 0; // set from main module
+ this.FIX_UBUNTU_DOCK = this.get('fixUbuntuDock', true);
+
+ this.WINDOW_ATTENTION_MODE = this.get('windowAttentionMode', true);
+ this.WINDOW_ATTENTION_DISABLE_NOTIFICATIONS = this.WINDOW_ATTENTION_MODE === 1;
+ this.WINDOW_ATTENTION_FOCUS_IMMEDIATELY = this.WINDOW_ATTENTION_MODE === 2;
+
+ this.WS_SW_POPUP_H_POSITION = this.get('wsSwPopupHPosition', true) / 100;
+ this.WS_SW_POPUP_V_POSITION = this.get('wsSwPopupVPosition', true) / 100;
+ this.WS_SW_POPUP_MODE = this.get('wsSwPopupMode', true);
+
+ this.SHOW_FAV_NOTIFICATION = this.get('favoritesNotify', true);
+ this.NOTIFICATION_POSITION = this.get('notificationPosition', true);
+
+ this.OSD_POSITION = this.get('osdPosition', true);
+
+ this.HOT_CORNER_ACTION = this.get('hotCornerAction', true);
+ this.HOT_CORNER_POSITION = this.get('hotCornerPosition', true);
+ if (this.HOT_CORNER_POSITION === 6 && this.DASH_VISIBLE)
+ this.HOT_CORNER_EDGE = true;
+ else
+ this.HOT_CORNER_EDGE = false;
+ if ([5, 6].includes(this.HOT_CORNER_POSITION)) {
+ if (this.DASH_TOP || this.DASH_LEFT)
+ this.HOT_CORNER_POSITION = 1;
+ else if (this.DASH_RIGHT)
+ this.HOT_CORNER_POSITION = 2;
+ else if (this.DASH_BOTTOM)
+ this.HOT_CORNER_POSITION = 3;
+ else
+ this.HOT_CORNER_POSITION = 0;
+ }
+ this.HOT_CORNER_FULLSCREEN = this.get('hotCornerFullscreen', true);
+ this.HOT_CORNER_RIPPLES = this.get('hotCornerRipples', true);
+
+ this.ALWAYS_ACTIVATE_SELECTED_WINDOW = this.get('alwaysActivateSelectedWindow', true);
+ this.WINDOW_ICON_CLICK_SEARCH = this.get('windowIconClickSearch', true);
+
+ this.OVERLAY_KEY_SECONDARY = this.get('overlayKeySecondary', true);
}
_getAnimationDirection() {
- if (this.ORIENTATION) {
- return (this.WS_TMB_LEFT || !this.SHOW_WS_TMB) ? 1 : 2; // 1 right, 2 left
- } else {
- return (this.WS_TMB_TOP || !this.SHOW_WS_TMB) ? 3 : 5; // 3 bottom, 5 top
- }
+ if (this.ORIENTATION)
+ return this.WS_TMB_LEFT || !this.SHOW_WS_TMB ? 1 : 2; // 1 right, 2 left
+ else
+ return this.WS_TMB_TOP || !this.SHOW_WS_TMB ? 3 : 5; // 3 bottom, 5 top
}
-
};
diff --git a/extensions/vertical-workspaces/swipeTracker.js b/extensions/vertical-workspaces/lib/swipeTracker.js
index d5c52da..d9c3407 100644
--- a/extensions/vertical-workspaces/swipeTracker.js
+++ b/extensions/vertical-workspaces/lib/swipeTracker.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* swipeTracker.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -15,13 +15,23 @@ const Main = imports.ui.main;
const SwipeTracker = imports.ui.swipeTracker;
const Me = imports.misc.extensionUtils.getCurrentExtension();
+
let opt;
+let _firstRun = true;
let _vwGestureUpdateId;
let _originalGestureUpdateId;
function update(reset = false) {
- opt = Me.imports.settings.opt;
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('swipeTrackerModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
if (reset || !opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL
// original swipeTrackers' orientation and updateGesture function
@@ -56,11 +66,9 @@ function update(reset = false) {
}
}
-//---- SwipeTracker -----------------------------------------------------------------------------------
-// switch overview's state gesture direction
-var SwipeTrackerVertical = {
- _updateGesture: function(gesture, time, delta, distance) {
- if (this._state !== 1) //State.SCROLLING)
+const SwipeTrackerVertical = {
+ _updateGesture(gesture, time, delta, distance) {
+ if (this._state !== 1) // State.SCROLLING)
return;
if ((this._allowedModes & Main.actionMode) === 0 || !this.enabled) {
@@ -75,5 +83,5 @@ var SwipeTrackerVertical = {
this._progress = Math.clamp(this._progress, ...this._getBounds(this._initialProgress));
this.emit('update', this._progress);
- }
-}
+ },
+};
diff --git a/extensions/vertical-workspaces/lib/util.js b/extensions/vertical-workspaces/lib/util.js
new file mode 100644
index 0000000..5f5c069
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/util.js
@@ -0,0 +1,364 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * util.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+
+const Gi = imports._gi;
+const { Shell, Meta, Clutter } = imports.gi;
+
+const Config = imports.misc.config;
+const Main = imports.ui.main;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+
+var shellVersion = parseFloat(Config.PACKAGE_VERSION);
+
+var Overrides = class {
+ constructor() {
+ this._overrides = {};
+ }
+
+ addOverride(name, prototype, overrideList) {
+ this._overrides[name] = {
+ originals: this.overrideProto(prototype, overrideList),
+ prototype,
+ };
+ }
+
+ removeOverride(name) {
+ const override = this._overrides[name];
+ if (!override)
+ return false;
+
+ this.overrideProto(override.prototype, override.originals);
+ this._overrides[name] = undefined;
+ return true;
+ }
+
+ removeAll() {
+ for (let name in this._overrides) {
+ this.removeOverride(name);
+ this._overrides[name] = undefined;
+ }
+ }
+
+ hookVfunc(proto, symbol, func) {
+ proto[Gi.hook_up_vfunc_symbol](symbol, func);
+ }
+
+ overrideProto(proto, overrides) {
+ const backup = {};
+
+ for (let symbol in overrides) {
+ if (symbol.startsWith('after_')) {
+ const actualSymbol = symbol.slice('after_'.length);
+ const fn = proto[actualSymbol];
+ const afterFn = overrides[symbol];
+ proto[actualSymbol] = function (...args) {
+ args = Array.prototype.slice.call(args);
+ const res = fn.apply(this, args);
+ afterFn.apply(this, args);
+ return res;
+ };
+ backup[actualSymbol] = fn;
+ } else {
+ backup[symbol] = proto[symbol];
+ if (symbol.startsWith('vfunc')) {
+ if (shellVersion < 42)
+ this.hookVfunc(proto, symbol.slice(6), overrides[symbol]);
+ else
+ this.hookVfunc(proto[Gi.gobject_prototype_symbol], symbol.slice(6), overrides[symbol]);
+ } else {
+ proto[symbol] = overrides[symbol];
+ }
+ }
+ }
+ return backup;
+ }
+};
+
+function getOverviewTranslations(opt, dash, tmbBox, searchEntryBin) {
+ // const tmbBox = Main.overview._overview._controls._thumbnailsBox;
+ let searchTranslationY = 0;
+ if (searchEntryBin.visible) {
+ const offset = (dash.visible && (!opt.DASH_VERTICAL ? dash.height + 12 : 0)) +
+ (opt.WS_TMB_TOP ? tmbBox.height + 12 : 0);
+ searchTranslationY = -searchEntryBin.height - offset - 30;
+ }
+
+ let tmbTranslationX = 0;
+ let tmbTranslationY = 0;
+ let offset;
+ if (tmbBox.visible) {
+ switch (opt.WS_TMB_POSITION) {
+ case 3: // left
+ offset = 10 + (dash?.visible && opt.DASH_LEFT ? dash.width : 0);
+ tmbTranslationX = -tmbBox.width - offset;
+ tmbTranslationY = 0;
+ break;
+ case 1: // right
+ offset = 10 + (dash?.visible && opt.DASH_RIGHT ? dash.width : 0);
+ tmbTranslationX = tmbBox.width + offset;
+ tmbTranslationY = 0;
+ break;
+ case 0: // top
+ offset = 10 + (dash?.visible && opt.DASH_TOP ? dash.height : 0) + Main.panel.height;
+ tmbTranslationX = 0;
+ tmbTranslationY = -tmbBox.height - offset;
+ break;
+ case 2: // bottom
+ offset = 10 + (dash?.visible && opt.DASH_BOTTOM ? dash.height : 0) + Main.panel.height; // just for case the panel is at bottom
+ tmbTranslationX = 0;
+ tmbTranslationY = tmbBox.height + offset;
+ break;
+ }
+ }
+
+ let dashTranslationX = 0;
+ let dashTranslationY = 0;
+ let position = opt.DASH_POSITION;
+ // if DtD replaced the original Dash, read its position
+ if (dashIsDashToDock())
+ position = dash._position;
+
+ if (dash?.visible) {
+ switch (position) {
+ case 0: // top
+ dashTranslationX = 0;
+ dashTranslationY = -dash.height - dash.margin_bottom - Main.panel.height;
+ break;
+ case 1: // right
+ dashTranslationX = dash.width;
+ dashTranslationY = 0;
+ break;
+ case 2: // bottom
+ dashTranslationX = 0;
+ dashTranslationY = dash.height + dash.margin_bottom + Main.panel.height;
+ break;
+ case 3: // left
+ dashTranslationX = -dash.width;
+ dashTranslationY = 0;
+ break;
+ }
+ }
+
+ return [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY];
+}
+
+function openPreferences() {
+ const windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null);
+ let tracker = Shell.WindowTracker.get_default();
+ let metaWin, isVW = null;
+
+ for (let win of windows) {
+ const app = tracker.get_window_app(win);
+ if (win.get_title().includes(Me.metadata.name) && app.get_name() === 'Extensions') {
+ // this is our existing window
+ metaWin = win;
+ isVW = true;
+ break;
+ } else if (win.wm_class.includes('org.gnome.Shell.Extensions')) {
+ // this is prefs window of another extension
+ metaWin = win;
+ isVW = false;
+ }
+ }
+
+ if (metaWin && !isVW) {
+ // other prefs window blocks opening another prefs window, so close it
+ metaWin.delete(global.get_current_time());
+ } else if (metaWin && isVW) {
+ // if prefs window already exist, move it to the current WS and activate it
+ metaWin.change_workspace(global.workspace_manager.get_active_workspace());
+ metaWin.activate(global.get_current_time());
+ }
+
+ if (!metaWin || (metaWin && !isVW)) {
+ try {
+ Main.extensionManager.openExtensionPrefs(Me.metadata.uuid, '', {});
+ } catch (e) {
+ log(e);
+ }
+ }
+}
+
+function activateSearchProvider(prefix = '') {
+ const searchEntry = Main.overview.searchEntry;
+ if (!searchEntry.get_text() || !searchEntry.get_text().startsWith(prefix)) {
+ prefix = `${prefix} `;
+ const position = prefix.length;
+ searchEntry.set_text(prefix);
+ searchEntry.get_first_child().set_cursor_position(position);
+ searchEntry.get_first_child().set_selection(position, position);
+ } else {
+ searchEntry.set_text('');
+ }
+}
+
+function dashNotDefault() {
+ return Main.overview.dash !== Main.overview._overview._controls.layoutManager._dash;
+}
+
+function dashIsDashToDock() {
+ return Main.overview.dash._isHorizontal !== undefined;
+}
+
+// Reorder Workspaces - callback for Dash and workspacesDisplay
+function reorderWorkspace(direction = 0) {
+ let activeWs = global.workspace_manager.get_active_workspace();
+ let activeWsIdx = activeWs.index();
+ let targetIdx = activeWsIdx + direction;
+ if (targetIdx > -1 && targetIdx < global.workspace_manager.get_n_workspaces())
+ global.workspace_manager.reorder_workspace(activeWs, targetIdx);
+}
+
+function exposeWindows(adjustment, activateKeyboard) {
+ // expose windows for static overview modes
+ if (!adjustment.value && !Main.overview._animationInProgress) {
+ if (adjustment.value === 0) {
+ adjustment.value = 0;
+ adjustment.ease(1, {
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ if (activateKeyboard) {
+ Main.ctrlAltTabManager._items.forEach(i => {
+ if (i.sortGroup === 1 && i.name === 'Windows')
+ Main.ctrlAltTabManager.focusGroup(i);
+ });
+ }
+ },
+ });
+ }
+ }
+}
+
+function isShiftPressed(state = null) {
+ if (state === null)
+ [,, state] = global.get_pointer();
+ return (state & Clutter.ModifierType.SHIFT_MASK) !== 0;
+}
+
+function isCtrlPressed(state = null) {
+ if (state === null)
+ [,, state] = global.get_pointer();
+ return (state & Clutter.ModifierType.CONTROL_MASK) !== 0;
+}
+
+function isAltPressed(state = null) {
+ if (state === null)
+ [,, state] = global.get_pointer();
+ return (state & Clutter.ModifierType.MOD1_MASK) !== 0;
+}
+
+function fuzzyMatch(term, text) {
+ let pos = -1;
+ const matches = [];
+ // convert all accented chars to their basic form and to lower case
+ const _text = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
+ const _term = term.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
+
+ // if term matches the substring exactly, gains the highest weight
+ if (_text.includes(_term))
+ return 0;
+
+ for (let i = 0; i < _term.length; i++) {
+ let c = _term[i];
+ let p;
+ if (pos > 0)
+ p = _term[i - 1];
+ while (true) {
+ pos += 1;
+ if (pos >= _text.length)
+ return -1;
+
+ if (_text[pos] === c) {
+ matches.push(pos);
+ break;
+ } else if (_text[pos] === p) {
+ matches.pop();
+ matches.push(pos);
+ }
+ }
+ }
+
+ // add all position to get a weight of the result
+ // results closer to the beginning of the text and term characters closer to each other will gain more weight.
+ return matches.reduce((r, p) => r + p) - matches.length * matches[0] + matches[0];
+}
+
+function strictMatch(term, text) {
+ // remove diacritics and accents from letters
+ let s = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
+ let p = term.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
+ let ps = p.split(/ +/);
+
+ // allows to use multiple exact patterns separated by a space in arbitrary order
+ for (let w of ps) { // escape regex control chars
+ if (!s.match(w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
+ return -1;
+ }
+ return 0;
+}
+
+function isMoreRelevant(stringA, stringB, pattern) {
+ let regex = /[^a-zA-Z\d]/;
+ let strSplitA = stringA.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().split(regex);
+ let strSplitB = stringB.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().split(regex);
+ let aAny = false;
+ strSplitA.forEach(w => {
+ aAny = aAny || w.startsWith(pattern);
+ });
+ let bAny = false;
+ strSplitB.forEach(w => {
+ bAny = bAny || w.startsWith(pattern);
+ });
+
+ // if both strings contain a word that starts with the pattern
+ // prefer the one whose first word starts with the pattern
+ if (aAny && bAny)
+ return !strSplitA[0].startsWith(pattern) && strSplitB[0].startsWith(pattern);
+ else
+ return !aAny && bAny;
+}
+
+function getEnabledExtensions(uuid = '') {
+ let extensions = [];
+ Main.extensionManager._extensions.forEach(e => {
+ if (e.state === 1 && e.uuid.includes(uuid))
+ extensions.push(e);
+ });
+ return extensions;
+}
+
+function getScrollDirection(event) {
+ // scroll wheel provides two types of direction information:
+ // 1. Clutter.ScrollDirection.DOWN / Clutter.ScrollDirection.UP
+ // 2. Clutter.ScrollDirection.SMOOTH + event.get_scroll_delta()
+ // first SMOOTH event returns 0 delta,
+ // so we need to always read event.direction
+ // since mouse without smooth scrolling provides exactly one SMOOTH event on one wheel rotation click
+ // on the other hand, under X11, one wheel rotation click sometimes doesn't send direction event, only several SMOOTH events
+ // so we also need to convert the delta to direction
+ let direction = event.get_scroll_direction();
+
+ if (direction !== Clutter.ScrollDirection.SMOOTH)
+ return direction;
+
+ let [, delta] = event.get_scroll_delta();
+
+ if (!delta)
+ return null;
+
+ direction = delta > 0 ? Clutter.ScrollDirection.DOWN : Clutter.ScrollDirection.UP;
+
+ return direction;
+}
diff --git a/extensions/vertical-workspaces/lib/windowAttentionHandler.js b/extensions/vertical-workspaces/lib/windowAttentionHandler.js
new file mode 100644
index 0000000..10703c2
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/windowAttentionHandler.js
@@ -0,0 +1,90 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * windowAttentionHandler.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+
+const Main = imports.ui.main;
+const WindowAttentionHandler = imports.ui.windowAttentionHandler;
+const MessageTray = imports.ui.messageTray;
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+
+const _Util = Me.imports.lib.util;
+
+let opt;
+let _firstRun = false;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('winAttentionHandlerModule', true);
+ reset = reset || !moduleEnabled;
+
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+ if (reset) {
+ reset = true;
+ _updateConnections(reset);
+ opt = null;
+ return;
+ }
+
+ _updateConnections();
+}
+
+function _updateConnections(reset) {
+ global.display.disconnectObject(Main.windowAttentionHandler);
+
+ const handlerFnc = reset
+ ? Main.windowAttentionHandler._onWindowDemandsAttention
+ : WindowAttentionHandlerCommon._onWindowDemandsAttention;
+
+ global.display.connectObject(
+ 'window-demands-attention', handlerFnc.bind(Main.windowAttentionHandler),
+ 'window-marked-urgent', handlerFnc.bind(Main.windowAttentionHandler),
+ Main.windowAttentionHandler);
+}
+
+const WindowAttentionHandlerCommon = {
+ _onWindowDemandsAttention(display, window) {
+ // Deny attention notifications if the App Grid is open, to avoid notification spree when opening a folder
+ if (Main.overview._shown && Main.overview.dash.showAppsButton.checked) {
+ return;
+ } else if (opt.WINDOW_ATTENTION_FOCUS_IMMEDIATELY) {
+ if (!Main.overview._shown)
+ Main.activateWindow(window);
+ return;
+ }
+
+ const app = this._tracker.get_window_app(window);
+ const source = new WindowAttentionHandler.WindowAttentionSource(app, window);
+ Main.messageTray.add(source);
+
+ let [title, banner] = this._getTitleAndBanner(app, window);
+
+ const notification = new MessageTray.Notification(source, title, banner);
+ notification.connect('activated', () => {
+ source.open();
+ });
+ notification.setForFeedback(true);
+
+ if (opt.WINDOW_ATTENTION_DISABLE_NOTIFICATIONS)
+ // just push the notification to the message tray without showing notification
+ source.pushNotification(notification);
+ else
+ source.showNotification(notification);
+
+ window.connectObject('notify::title', () => {
+ [title, banner] = this._getTitleAndBanner(app, window);
+ notification.update(title, banner);
+ }, source);
+ },
+};
diff --git a/extensions/vertical-workspaces/windowManager.js b/extensions/vertical-workspaces/lib/windowManager.js
index e931a67..2d46b0b 100644
--- a/extensions/vertical-workspaces/windowManager.js
+++ b/extensions/vertical-workspaces/lib/windowManager.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* windowManager.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -10,40 +10,50 @@
'use strict';
-const { GObject, Clutter, Meta, Shell } = imports.gi;
+const { GObject, Clutter, Meta } = imports.gi;
const Main = imports.ui.main;
const WindowManager = imports.ui.windowManager;
const Me = imports.misc.extensionUtils.getCurrentExtension();
-const _Util = Me.imports.util;
+const _Util = Me.imports.lib.util;
let _overrides;
const MINIMIZE_WINDOW_ANIMATION_TIME = WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME;
const MINIMIZE_WINDOW_ANIMATION_MODE = WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE;
let opt;
+let _firstRun = true;
function update(reset = false) {
- if (_overrides) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('windowManagerModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+
+ if (_overrides)
_overrides.removeAll();
- }
+
_replaceMinimizeFunction(reset);
+
if (reset) {
_overrides = null;
opt = null;
return;
}
- opt = Me.imports.settings.opt;
-
_overrides = new _Util.Overrides();
_overrides.addOverride('WindowManager', WindowManager.WindowManager.prototype, WindowManagerCommon);
}
-//------------- Fix and adapt minimize/unminimize animations --------------------------------------
+// ------------- Fix and adapt minimize/unminimize animations --------------------------------------
let _originalMinimizeSigId;
let _minimizeSigId;
@@ -61,7 +71,6 @@ function _replaceMinimizeFunction(reset = false) {
_unminimizeSigId = 0;
Main.wm._shellwm.unblock_signal_handler(_originalUnminimizeSigId);
_originalUnminimizeSigId = 0;
-
} else if (!_minimizeSigId) {
_originalMinimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'minimize' });
if (_originalMinimizeSigId) {
@@ -82,7 +91,7 @@ function _replaceMinimizeFunction(reset = false) {
// anyway, animation is better, even if the Activities button is not visible...
// and also add support for bottom position of the panel
const WindowManagerCommon = {
- _minimizeWindow: function(shellwm, actor) {
+ _minimizeWindow(shellwm, actor) {
const types = [
Meta.WindowType.NORMAL,
Meta.WindowType.MODAL_DIALOG,
@@ -97,48 +106,48 @@ const WindowManagerCommon = {
this._minimizing.add(actor);
- if (false/*actor.meta_window.is_monitor_sized()*/) {
+ /* if (actor.meta_window.is_monitor_sized()) {
actor.get_first_child().ease({
opacity: 0,
duration: MINIMIZE_WINDOW_ANIMATION_TIME,
mode: MINIMIZE_WINDOW_ANIMATION_MODE,
onStopped: () => this._minimizeWindowDone(shellwm, actor),
});
+ } else { */
+ let xDest, yDest, xScale, yScale;
+ let [success, geom] = actor.meta_window.get_icon_geometry();
+ if (success) {
+ xDest = geom.x;
+ yDest = geom.y;
+ xScale = geom.width / actor.width;
+ yScale = geom.height / actor.height;
} else {
- let xDest, yDest, xScale, yScale;
- let [success, geom] = actor.meta_window.get_icon_geometry();
- if (success) {
- xDest = geom.x;
- yDest = geom.y;
- xScale = geom.width / actor.width;
- yScale = geom.height / actor.height;
- } else {
- let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
- if (!monitor) {
- this._minimizeWindowDone();
- return;
- }
- xDest = monitor.x;
- yDest = opt.PANEL_POSITION_TOP ? monitor.y : monitor.y + monitor.height;
- if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
- xDest += monitor.width;
- xScale = 0;
- yScale = 0;
+ let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
+ if (!monitor) {
+ this._minimizeWindowDone();
+ return;
}
-
- actor.ease({
- scale_x: xScale,
- scale_y: yScale,
- x: xDest,
- y: yDest,
- duration: MINIMIZE_WINDOW_ANIMATION_TIME,
- mode: MINIMIZE_WINDOW_ANIMATION_MODE,
- onStopped: () => this._minimizeWindowDone(shellwm, actor),
- });
+ xDest = monitor.x;
+ yDest = opt.PANEL_POSITION_TOP ? monitor.y : monitor.y + monitor.height;
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
+ xDest += monitor.width;
+ xScale = 0;
+ yScale = 0;
}
+
+ actor.ease({
+ scale_x: xScale,
+ scale_y: yScale,
+ x: xDest,
+ y: yDest,
+ duration: MINIMIZE_WINDOW_ANIMATION_TIME,
+ mode: MINIMIZE_WINDOW_ANIMATION_MODE,
+ onStopped: () => this._minimizeWindowDone(shellwm, actor),
+ });
+ // }
},
- _minimizeWindowDone: function(shellwm, actor) {
+ _minimizeWindowDone(shellwm, actor) {
if (this._minimizing.delete(actor)) {
actor.remove_all_transitions();
actor.set_scale(1.0, 1.0);
@@ -149,7 +158,7 @@ const WindowManagerCommon = {
}
},
- _unminimizeWindow: function(shellwm, actor) {
+ _unminimizeWindow(shellwm, actor) {
const types = [
Meta.WindowType.NORMAL,
Meta.WindowType.MODAL_DIALOG,
@@ -162,7 +171,7 @@ const WindowManagerCommon = {
this._unminimizing.add(actor);
- if (false/*actor.meta_window.is_monitor_sized()*/) {
+ /* if (false/* actor.meta_window.is_monitor_sized()) {
actor.opacity = 0;
actor.set_scale(1.0, 1.0);
actor.ease({
@@ -171,38 +180,38 @@ const WindowManagerCommon = {
mode: MINIMIZE_WINDOW_ANIMATION_MODE,
onStopped: () => this._unminimizeWindowDone(shellwm, actor),
});
+ } else { */
+ let [success, geom] = actor.meta_window.get_icon_geometry();
+ if (success) {
+ actor.set_position(geom.x, geom.y);
+ actor.set_scale(geom.width / actor.width,
+ geom.height / actor.height);
} else {
- let [success, geom] = actor.meta_window.get_icon_geometry();
- if (success) {
- actor.set_position(geom.x, geom.y);
- actor.set_scale(geom.width / actor.width,
- geom.height / actor.height);
- } else {
- let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
- if (!monitor) {
- actor.show();
- this._unminimizeWindowDone();
- return;
- }
- actor.set_position(monitor.x, opt.PANEL_POSITION_TOP ? monitor.y : monitor.y + monitor.height);
- if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
- actor.x += monitor.width;
- actor.set_scale(0, 0);
+ let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
+ if (!monitor) {
+ actor.show();
+ this._unminimizeWindowDone();
+ return;
}
-
- let rect = actor.meta_window.get_buffer_rect();
- let [xDest, yDest] = [rect.x, rect.y];
-
- actor.show();
- actor.ease({
- scale_x: 1,
- scale_y: 1,
- x: xDest,
- y: yDest,
- duration: MINIMIZE_WINDOW_ANIMATION_TIME,
- mode: MINIMIZE_WINDOW_ANIMATION_MODE,
- onStopped: () => this._unminimizeWindowDone(shellwm, actor),
- });
+ actor.set_position(monitor.x, opt.PANEL_POSITION_TOP ? monitor.y : monitor.y + monitor.height);
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
+ actor.x += monitor.width;
+ actor.set_scale(0, 0);
}
- }
-}
+
+ let rect = actor.meta_window.get_buffer_rect();
+ let [xDest, yDest] = [rect.x, rect.y];
+
+ actor.show();
+ actor.ease({
+ scale_x: 1,
+ scale_y: 1,
+ x: xDest,
+ y: yDest,
+ duration: MINIMIZE_WINDOW_ANIMATION_TIME,
+ mode: MINIMIZE_WINDOW_ANIMATION_MODE,
+ onStopped: () => this._unminimizeWindowDone(shellwm, actor),
+ });
+ // }
+ },
+};
diff --git a/extensions/vertical-workspaces/windowPreview.js b/extensions/vertical-workspaces/lib/windowPreview.js
index 013f48c..5d2bd61 100644
--- a/extensions/vertical-workspaces/windowPreview.js
+++ b/extensions/vertical-workspaces/lib/windowPreview.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* windowPreview.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -10,7 +10,7 @@
'use strict';
-const { Clutter, GLib, Graphene, Meta, Shell, St } = imports.gi;
+const { Clutter, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi;
const Main = imports.ui.main;
const WindowPreview = imports.ui.windowPreview;
@@ -18,40 +18,54 @@ const WindowPreview = imports.ui.windowPreview;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
-const _Util = Me.imports.util;
+const _Util = Me.imports.lib.util;
+const shellVersion = _Util.shellVersion;
+
let _overrides;
const WINDOW_SCALE_TIME = imports.ui.windowPreview.WINDOW_SCALE_TIME;
const WINDOW_ACTIVE_SIZE_INC = imports.ui.windowPreview.WINDOW_ACTIVE_SIZE_INC;
const WINDOW_OVERLAY_FADE_TIME = imports.ui.windowPreview.WINDOW_OVERLAY_FADE_TIME;
-const SEARCH_WINDOWS_PREFIX = Me.imports.windowSearchProvider.prefix;
+const SEARCH_WINDOWS_PREFIX = Me.imports.lib.windowSearchProvider.prefix;
const ControlsState = imports.ui.overviewControls.ControlsState;
-var opt = null;
+let opt;
+let _firstRun = true;
function update(reset = false) {
- if (_overrides) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('windowPreviewModule', 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;
+ WindowPreview.WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 750;
return;
}
- opt = Me.imports.settings.opt;
_overrides = new _Util.Overrides();
_overrides.addOverride('WindowPreview', WindowPreview.WindowPreview.prototype, WindowPreviewCommon);
- // move titles into window previews
- _overrides.addInjection('WindowPreview', WindowPreview.WindowPreview.prototype, WindowPreviewInjections);
+ // A shorter timeout allows user to quickly cancel the selection by leaving the preview with the mouse pointer
+ if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW)
+ WindowPreview.WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 150;
}
-//----- WindowPreview ------------------------------------------------------------------
-var WindowPreviewInjections = {
- _init: function() {
+const WindowPreviewCommon = {
+ // injection to _init()
+ after__init() {
const ICON_OVERLAP = 0.7;
if (opt.WIN_PREVIEW_ICON_SIZE < 64) {
@@ -91,7 +105,7 @@ var WindowPreviewInjections = {
const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
const iconOverlap = opt.WIN_PREVIEW_ICON_SIZE * ICON_OVERLAP;
// we cannot get proper title height before it gets to the stage, so 35 is estimated height + spacing
- this._title.get_constraints()[1].offset = scaleFactor * (- iconOverlap - 35);
+ this._title.get_constraints()[1].offset = scaleFactor * (-iconOverlap - 35);
this.set_child_above_sibling(this._title, null);
// if window is created while the overview is shown, icon and title should be visible immediately
if (Main.overview._overview._controls._stateAdjustment.value < 1) {
@@ -100,34 +114,23 @@ var WindowPreviewInjections = {
this._title.opacity = 0;
}
- if (opt.ALWAYS_SHOW_WIN_TITLES) {
+ if (opt.ALWAYS_SHOW_WIN_TITLES)
this._title.show();
- this._stateConId = this._workspace._background._stateAdjustment.connect('notify::value', (adj) => {
- this._title.opacity = Math.floor(adj.value) * 255;
- });
-
- }
if (opt.OVERVIEW_MODE === 1) {
// spread windows on hover
this._wsStateConId = this.connect('enter-event', () => {
// don't spread windows if user don't use pointer device at this moment
- if (global.get_pointer()[0] === opt.showingPointerX)
+ if (global.get_pointer()[0] === opt.showingPointerX || Main.overview._overview._controls._stateAdjustment.value < 1)
return;
const adjustment = this._workspace._background._stateAdjustment;
- if (!adjustment.value && !Main.overview._animationInProgress) {
- opt.WORKSPACE_MODE = 1;
- if (adjustment.value === 0) {
- adjustment.value = 0;
- adjustment.ease(1, {
- duration: 200,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD
- });
- }
- }
+ opt.WORKSPACE_MODE = 1;
+ _Util.exposeWindows(adjustment, false);
+ this.disconnect(this._wsStateConId);
});
}
+
if (opt.OVERVIEW_MODE) {
// show window icon and title on ws windows spread
this._stateAdjustmentSigId = this._workspace.stateAdjustment.connect('notify::value', this._updateIconScale.bind(this));
@@ -135,37 +138,52 @@ var WindowPreviewInjections = {
// replace click action with custom one
const action = this.get_actions()[0];
- this.remove_action(action);
- const clickAction = new Clutter.ClickAction();
- clickAction.connect('clicked', (action) => {
- const button = action.get_button();
+ const handlerId = GObject.signal_handler_find(action, { signalId: 'clicked' });
+ if (handlerId)
+ action.disconnect(handlerId);
+
+ action.connect('clicked', act => {
+ const button = act.get_button();
if (button === Clutter.BUTTON_PRIMARY) {
this._activate();
+ return Clutter.EVENT_STOP;
} else if (button === Clutter.BUTTON_SECONDARY) {
// this action cancels long-press event and the 'long-press-cancel' event is used by the Shell to actually initiate DnD
// so the dnd initiation needs to be removed
if (this._longPressLater) {
- Meta.later_remove(this._longPressLater);
- delete this._longPressLater;
+ if (shellVersion >= 44) {
+ const laters = global.compositor.get_laters();
+ laters.remove(this._longPressLater);
+ } else {
+ Meta.later_remove(this._longPressLater);
+ delete this._longPressLater;
+ }
}
const tracker = Shell.WindowTracker.get_default();
const appName = tracker.get_window_app(this.metaWindow).get_name();
_Util.activateSearchProvider(`${SEARCH_WINDOWS_PREFIX} ${appName}`);
return Clutter.EVENT_STOP;
}
+ return Clutter.EVENT_PROPAGATE;
});
- clickAction.connect('long-press', this._onLongPress.bind(this));
- this.add_action(clickAction);
-
- this.connect('destroy', () => this._workspace.stateAdjustment.disconnect(this._stateConId));
- }
-}
+ if (opt.WINDOW_ICON_CLICK_SEARCH) {
+ const iconClickAction = new Clutter.ClickAction();
+ iconClickAction.connect('clicked', act => {
+ if (act.get_button() === Clutter.BUTTON_PRIMARY) {
+ const tracker = Shell.WindowTracker.get_default();
+ const appName = tracker.get_window_app(this.metaWindow).get_name();
+ _Util.activateSearchProvider(`${SEARCH_WINDOWS_PREFIX} ${appName}`);
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
+ });
+ this._icon.add_action(iconClickAction);
+ }
+ },
-// WindowPreview
-var WindowPreviewCommon = {
- _updateIconScale: function() {
+ _updateIconScale() {
let { currentState, initialState, finalState } =
this._overviewAdjustment.getStateTransitionParams();
@@ -173,46 +191,60 @@ var WindowPreviewCommon = {
const primaryMonitor = this.metaWindow.get_monitor() === global.display.get_primary_monitor();
const visible =
- (initialState > ControlsState.HIDDEN || finalState > ControlsState.HIDDEN)
- && !(finalState === ControlsState.APP_GRID && primaryMonitor);
+ (initialState > ControlsState.HIDDEN || finalState > ControlsState.HIDDEN) &&
+ !(finalState === ControlsState.APP_GRID && primaryMonitor);
- let scale = visible
- ? (currentState >= 1 ? 1 : currentState % 1) : 0;
- if (!primaryMonitor &&
+ let scale = 0;
+ if (visible)
+ scale = currentState >= 1 ? 1 : currentState % 1;
+
+ if (!primaryMonitor && opt.WORKSPACE_MODE &&
((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) ||
(initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER))
- ) {
+ )
scale = 1;
- /*} else if (primaryMonitor && ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) ||
+ else if (!primaryMonitor && opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE)
+ scale = 0;
+ /* } else if (primaryMonitor && ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) ||
initialState === ControlsState.APP_GRID && finalState === ControlsState.HIDDEN)) {*/
- } else if (primaryMonitor && currentState > ControlsState.WINDOW_PICKER) {
+ else if (primaryMonitor && currentState > ControlsState.WINDOW_PICKER)
scale = 0;
- }
- // in static workspace mode show icon and title on ws windows spread
+
+ // in static workspace mode show icon and title on windows expose
if (opt.OVERVIEW_MODE) {
- const windowsSpread = this._workspace.stateAdjustment.value;
- if (currentState === 1) {
- scale = windowsSpread;
- } else if (finalState === 1 || (finalState === 0 && !windowsSpread)) {
+ if (currentState === 1)
+ scale = opt.WORKSPACE_MODE;
+ else if (finalState === 1 || (finalState === 0 && !opt.WORKSPACE_MODE))
return;
- }
}
- this._icon.set({
- scale_x: scale,
- scale_y: scale,
- });
+ if (scale === 1) {
+ this._icon.ease({
+ duration: 50,
+ scale_x: scale,
+ scale_y: scale,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ this._title.ease({
+ duration: 100,
+ opacity: 255,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ } else if (this._icon.scale_x !== 0) {
+ this._icon.set({
+ scale_x: 0,
+ scale_y: 0,
+ });
+ this._title.opacity = 0;
+ }
// if titles are in 'always show' mode, we need to add transition between visible/invisible state
// but the transition is quite expensive,
// showing the titles at the end of the transition is good enough and workspace preview transition is much smoother
- this._title.set({
- opacity: Math.floor(scale) * 255,
- });
},
- showOverlay: function(animate) {
+ showOverlay(animate) {
if (!this._overlayEnabled)
return;
@@ -220,7 +252,8 @@ var WindowPreviewCommon = {
return;
this._overlayShown = true;
- //this._restack();
+ if (!opt.ALWAYS_ACTIVATE_SELECTED_WINDOW)
+ this._restack();
// If we're supposed to animate and an animation in our direction
// is already happening, let that one continue
@@ -234,9 +267,9 @@ var WindowPreviewCommon = {
? [this._closeButton]
: [];
- if (!opt.ALWAYS_SHOW_WIN_TITLES) {
+ if (!opt.ALWAYS_SHOW_WIN_TITLES)
toShow.push(this._title);
- }
+
toShow.forEach(a => {
a.opacity = 0;
@@ -264,11 +297,17 @@ var WindowPreviewCommon = {
this.emit('show-chrome');
},
- hideOverlay: function(animate) {
+ hideOverlay(animate) {
if (!this._overlayShown)
return;
this._overlayShown = false;
- //this._restack();
+ if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW && Main.overview._overview.controls._stateAdjustment.value < 1) {
+ this.get_parent()?.set_child_above_sibling(this, null);
+ this._activateSelected = true;
+ }
+
+ if (!opt.ALWAYS_ACTIVATE_SELECTED_WINDOW)
+ this._restack();
// If we're supposed to animate and an animation in our direction
// is already happening, let that one continue
@@ -280,9 +319,9 @@ var WindowPreviewCommon = {
const toHide = [this._closeButton];
- if (!opt.ALWAYS_SHOW_WIN_TITLES) {
+ if (!opt.ALWAYS_SHOW_WIN_TITLES)
toHide.push(this._title);
- }
+
toHide.forEach(a => {
a.opacity = 255;
a.ease({
@@ -304,18 +343,24 @@ var WindowPreviewCommon = {
},
_onDestroy() {
- // fix for upstream bug - hideOverlay is called after windowPreview is destroyed, from the leave event callback
- // but it still throws:
- // clutter_actor_get_preferred_width: assertion 'CLUTTER_IS_ACTOR (self)' failed
- // clutter_actor_get_preferred_height: assertion 'CLUTTER_IS_ACTOR (self)' failed
- this.hideOverlay(false);
+ // workaround for upstream bug - hideOverlay is called after windowPreview is destroyed, from the leave event callback
+ // hiding the preview now avoids firing the post-mortem leave event
+ this.hide();
+ if (this._activateSelected)
+ this._activate();
this.metaWindow._delegate = null;
this._delegate = null;
if (this._longPressLater) {
- Meta.later_remove(this._longPressLater);
- delete this._longPressLater;
+ if (shellVersion >= 44) {
+ const laters = global.compositor.get_laters();
+ laters.remove(this._longPressLater);
+ delete this._longPressLater;
+ } else {
+ Meta.later_remove(this._longPressLater);
+ delete this._longPressLater;
+ }
}
if (this._idleHideOverlayId > 0) {
@@ -328,8 +373,7 @@ var WindowPreviewCommon = {
this.inDrag = false;
}
- if (this._stateAdjustmentSigId) {
+ if (this._stateAdjustmentSigId)
this._workspace.stateAdjustment.disconnect(this._stateAdjustmentSigId);
- }
- }
-}
+ },
+};
diff --git a/extensions/vertical-workspaces/windowSearchProvider.js b/extensions/vertical-workspaces/lib/windowSearchProvider.js
index 3256742..5f90784 100644
--- a/extensions/vertical-workspaces/windowSearchProvider.js
+++ b/extensions/vertical-workspaces/lib/windowSearchProvider.js
@@ -1,5 +1,5 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* windowSearchProvider.js
*
* @author GdH <G-dH@github.com>
@@ -9,13 +9,16 @@
'use strict';
-const { GLib, GObject, Gio, Gtk, Meta, St, Shell } = imports.gi;
+const { GLib, Gio, Meta, St, Shell } = imports.gi;
const Main = imports.ui.main;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
-const Settings = Me.imports.settings;
-const _ = Me.imports.settings._;
+const Settings = Me.imports.lib.settings;
+const _Util = Me.imports.lib.util;
+
+// gettext
+const _ = Settings._;
const shellVersion = Settings.shellVersion;
@@ -36,18 +39,15 @@ const Action = {
CLOSE: 1,
CLOSE_ALL: 2,
MOVE_TO_WS: 3,
- MOVE_ALL_TO_WS: 4
-}
-
-function init() {
-}
+ MOVE_ALL_TO_WS: 4,
+};
function getOverviewSearchResult() {
- return Main.overview._overview.controls._searchController._searchResults;
+ return Main.overview._overview.controls._searchController._searchResults;
}
function update(reset = false) {
- opt = Me.imports.settings.opt;
+ opt = Me.imports.lib.settings.opt;
if (!reset && opt.WINDOW_SEARCH_PROVIDER_ENABLED && !windowSearchProvider) {
enable();
} else if (reset || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) {
@@ -62,7 +62,7 @@ function enable() {
GLib.PRIORITY_DEFAULT,
2000,
() => {
- if (windowSearchProvider == null) {
+ if (!windowSearchProvider) {
windowSearchProvider = new WindowSearchProvider(opt);
getOverviewSearchResult()._registerProvider(
windowSearchProvider
@@ -87,58 +87,6 @@ function disable() {
}
}
-function fuzzyMatch(term, text) {
- let pos = -1;
- const matches = [];
- // convert all accented chars to their basic form and to lower case
- const _text = text;//.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
- const _term = term.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
-
- // if term matches the substring exactly, gains the highest weight
- if (_text.includes(_term)) {
- return 0;
- }
-
- for (let i = 0; i < _term.length; i++) {
- let c = _term[i];
- let p;
- if (pos > 0)
- p = _term[i - 1];
- while (true) {
- pos += 1;
- if (pos >= _text.length) {
- return -1;
- }
- if (_text[pos] == c) {
- matches.push(pos);
- break;
- } else if (_text[pos] == p) {
- matches.pop();
- matches.push(pos);
- }
- }
- }
-
- // add all position to get a weight of the result
- // results closer to the beginning of the text and term characters closer to each other will gain more weight.
- return matches.reduce((r, p) => r + p) - matches.length * matches[0] + matches[0];
-}
-
-function strictMatch(term, text) {
- // remove diacritics and accents from letters
- let s = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
- let p = term.toLowerCase();
- let ps = p.split(/ +/);
-
- // allows to use multiple exact patterns separated by a space in arbitrary order
- for (let w of ps) { // escape regex control chars
- if (!s.match(w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))) {
- return -1;
- }
- }
- return 0;
-}
-
function makeResult(window, i) {
const app = Shell.WindowTracker.get_default().get_window_app(window);
const appName = app ? app.get_name() : 'Unknown';
@@ -146,13 +94,13 @@ function makeResult(window, i) {
const wsIndex = window.get_workspace().index();
return {
- 'id': i,
- // convert all accented chars to their basic form and lower case for search
- 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(),
- 'appName': appName,
- 'windowTitle': windowTitle,
- 'window': window
- }
+ 'id': i,
+ // convert all accented chars to their basic form and lower case for search
+ 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(),
+ appName,
+ windowTitle,
+ window,
+ };
}
const closeSelectedRegex = /^\/x!$/;
@@ -160,29 +108,29 @@ const closeAllResultsRegex = /^\/xa!$/;
const moveToWsRegex = /^\/m[0-9]+$/;
const moveAllToWsRegex = /^\/ma[0-9]+$/;
-var WindowSearchProvider = class WindowSearchProvider {
- constructor(gOptions) {
- this._gOptions = gOptions;
- this.appInfo = Gio.AppInfo.create_from_commandline('true', 'Open Windows', null);
- this.appInfo.get_description = () => 'List of open windows';
- this.appInfo.get_name = () => 'Open Windows';
- this.appInfo.get_id = () => `${Me.metadata.uuid} ${this.title}`;
+const WindowSearchProvider = class WindowSearchProvider {
+ constructor() {
+ this.id = `open-windows@${Me.metadata.uuid}`;
+ this.appInfo = Gio.AppInfo.create_from_commandline('true', _('Open Windows'), null);
+ this.appInfo.get_description = () => _('List of open windows');
+ this.appInfo.get_name = () => _('Open Windows');
+ this.appInfo.get_id = () => this.id;
this.appInfo.get_icon = () => Gio.icon_new_for_string('focus-windows-symbolic');
this.appInfo.should_show = () => true;
- this.title = 'Window Search Provider',
+
this.canLaunchSearch = true;
this.isRemoteProvider = false;
this.action = 0;
}
- _getResultSet (terms) {
+ _getResultSet(terms) {
// do not modify original terms
let termsCopy = [...terms];
// search for terms without prefix
termsCopy[0] = termsCopy[0].replace(prefix, '');
- /*if (gOptions.get('searchWindowsCommands')) {
+ /* if (gOptions.get('searchWindowsCommands')) {
this.action = 0;
this.targetWs = 0;
@@ -208,46 +156,45 @@ var WindowSearchProvider = class WindowSearchProvider {
const candidates = this.windows;
const _terms = [].concat(termsCopy);
- let match;
+ // let match;
const term = _terms.join(' ');
- match = (s) => {
+ /* match = s => {
return fuzzyMatch(term, s);
- }
+ }; */
const results = [];
let m;
for (let key in candidates) {
- if (this._gOptions.get('searchFuzzy')) {
- m = fuzzyMatch(term, candidates[key].name);
- } else {
- m = strictMatch(term, candidates[key].name);
- }
- if (m !== -1) {
+ if (opt.SEARCH_FUZZY)
+ m = _Util.fuzzyMatch(term, candidates[key].name);
+ else
+ m = _Util.strictMatch(term, candidates[key].name);
+
+ if (m !== -1)
results.push({ weight: m, id: key });
- }
}
results.sort((a, b) => a.weight > b.weight);
const currentWs = global.workspace_manager.get_active_workspace_index();
// prefer current workspace
results.sort((a, b) => (this.windows[a.id].window.get_workspace().index() !== currentWs) && (this.windows[b.id].window.get_workspace().index() === currentWs));
- results.sort((a, b) => ((_terms != ' ') && (a.weight > 0 && b.weight === 0)));
+ results.sort((a, b) => (_terms !== ' ') && (a.weight > 0 && b.weight === 0));
- this.resultIds = results.map((item) => item.id);
+ this.resultIds = results.map(item => item.id);
return this.resultIds;
}
- getResultMetas (resultIds, callback = null) {
- const metas = resultIds.map((id) => this.getResultMeta(id));
- if (shellVersion >= 43) {
+ getResultMetas(resultIds, callback = null) {
+ const metas = resultIds.map(id => this.getResultMeta(id));
+ if (shellVersion >= 43)
return new Promise(resolve => resolve(metas));
- } else {
+ else
callback(metas);
- }
+ return null;
}
- getResultMeta (resultId) {
+ getResultMeta(resultId) {
const result = this.windows[resultId];
const wsIndex = result.window.get_workspace().index();
const app = Shell.WindowTracker.get_default().get_window_app(result.window);
@@ -255,32 +202,30 @@ var WindowSearchProvider = class WindowSearchProvider {
'id': resultId,
'name': `${wsIndex + 1}: ${result.windowTitle}`,
'description': result.appName,
- 'createIcon': (size) => {
+ 'createIcon': size => {
return app
? app.create_icon_texture(size)
: new St.Icon({ icon_name: 'icon-missing', icon_size: size });
- }
- }
+ },
+ };
}
- launchSearch(terms, timeStamp) {
+ launchSearch(/* terms, timeStamp*/) {
}
- activateResult (resultId, terms, timeStamp) {
- const [,,state] = global.get_pointer();
-
- const isCtrlPressed = (state & ModifierType.CONTROL_MASK) != 0;
- const isShiftPressed = (state & ModifierType.SHIFT_MASK) != 0;
+ activateResult(resultId/* , terms, timeStamp*/) {
+ const isCtrlPressed = _Util.isCtrlPressed();
+ const isShiftPressed = _Util.isShiftPressed();
this.action = 0;
this.targetWs = 0;
this.targetWs = global.workspaceManager.get_active_workspace().index() + 1;
- if (isShiftPressed && !isCtrlPressed) {
+ if (isShiftPressed && !isCtrlPressed)
this.action = Action.MOVE_TO_WS;
- } else if (isShiftPressed && isCtrlPressed) {
+ else if (isShiftPressed && isCtrlPressed)
this.action = Action.MOVE_ALL_TO_WS;
- }
+
if (!this.action) {
const result = this.windows[resultId];
@@ -306,53 +251,55 @@ var WindowSearchProvider = class WindowSearchProvider {
_closeWindows(ids) {
let time = global.get_current_time();
- for (let i = 0; i < ids.length; i++) {
+ for (let i = 0; i < ids.length; i++)
this.windows[ids[i]].window.delete(time + i);
- }
+
Main.notify('Window Search Provider', `Closed ${ids.length} windows.`);
}
_moveWindowsToWs(selectedId, resultIds) {
const workspace = global.workspaceManager.get_active_workspace();
- for (let i = 0; i < resultIds.length; i++) {
+ for (let i = 0; i < resultIds.length; i++)
this.windows[resultIds[i]].window.change_workspace(workspace);
- }
+
const selectedWin = this.windows[selectedId].window;
selectedWin.activate_with_workspace(global.get_current_time(), workspace);
}
- getInitialResultSet (terms, callback, cancellable = null) {
- if (shellVersion >=43) {
- cancellable = callback;
- }
+ getInitialResultSet(terms, callback/* , cancellable = null*/) {
+ // In GS 43 callback arg has been removed
+ /* if (shellVersion >= 43)
+ cancellable = callback;*/
+
let windows;
this.windows = windows = {};
global.display.get_tab_list(Meta.TabList.NORMAL, null).filter(w => w.get_workspace() !== null).map(
- (v, i) => windows[`${i}-${v.get_id()}`] = makeResult(v, `${i}-${v.get_id()}`)
+ (v, i) => {
+ windows[`${i}-${v.get_id()}`] = makeResult(v, `${i}-${v.get_id()}`);
+ return windows[`${i}-${v.get_id()}`];
+ }
);
-
-
- if (shellVersion >= 43) {
+ if (shellVersion >= 43)
return new Promise(resolve => resolve(this._getResultSet(terms)));
- } else {
+ else
callback(this._getResultSet(terms));
- }
+ return null;
}
- filterResults (results, maxResults) {
- //return results.slice(0, maxResults);
+ filterResults(results /* , maxResults*/) {
+ // return results.slice(0, maxResults);
return results;
}
- getSubsearchResultSet (previousResults, terms, callback, cancellable) {
+ getSubsearchResultSet(previousResults, terms, callback/* , cancellable*/) {
// if we return previous results, quick typers get non-actual results
callback(this._getResultSet(terms));
}
- createResultOjbect(resultMeta) {
+ /* createResultObject(resultMeta) {
const app = Shell.WindowTracker.get_default().get_window_app(resultMeta.id);
return new AppIcon(app);
- }
-}
+ }*/
+};
diff --git a/extensions/vertical-workspaces/workspace.js b/extensions/vertical-workspaces/lib/workspace.js
index 84ca696..3b61a6d 100644
--- a/extensions/vertical-workspaces/workspace.js
+++ b/extensions/vertical-workspaces/lib/workspace.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* workspace.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -10,7 +10,7 @@
'use strict';
-const { Clutter, St, Graphene } = imports.gi;
+const { St, Graphene } = imports.gi;
const Main = imports.ui.main;
const Util = imports.misc.util;
@@ -19,33 +19,37 @@ const Workspace = imports.ui.workspace;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
-const _Util = Me.imports.util;
-let _overrides;
+const _Util = Me.imports.lib.util;
+let _overrides;
let opt;
+let _firstRun = true;
-const BACKGROUND_CORNER_RADIUS_PIXELS = 40;
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('workspaceModule', true);
+ reset = reset || !moduleEnabled;
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
-function update(reset = false) {
- if (_overrides) {
+ _firstRun = false;
+
+ if (_overrides)
_overrides.removeAll();
- }
+
if (reset) {
- imports.ui.workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95;
+ Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95;
_overrides = null;
opt = null;
return;
}
- opt = Me.imports.settings.opt;
-
_overrides = new _Util.Overrides();
_overrides.addOverride('WorkspaceBackground', Workspace.WorkspaceBackground.prototype, WorkspaceBackground);
- // fix window scaling in workspace state 0
- _overrides.addInjection('WorkspaceLayout', Workspace.WorkspaceLayout.prototype, WorkspaceLayoutInjections);
// fix overlay base for Vertical Workspaces
_overrides.addOverride('WorkspaceLayout', Workspace.WorkspaceLayout.prototype, WorkspaceLayout);
@@ -54,32 +58,35 @@ function update(reset = false) {
// workaround for upstream bug (that is not that invisible in default shell)
// smaller window cannot be scaled below 0.95 (WINDOW_PREVIEW_MAXIMUM_SCALE)
-// when its target scale for spread windows view (workspace state 1) is bigger than the scale needed for ws state 0.
+// when its target scale for exposed windows view (workspace state 1) is bigger than the scale needed for ws state 0.
// in workspace state 0 where windows are not spread and window scale should follow workspace scale,
// this window follows proper top left corner position, but doesn't scale with the workspace
// so it looks bad and the window can exceed border of the workspace
// extremely annoying in OVERVIEW_MODE 1 with single smaller window on the workspace, also affects appGrid transition animation
-var WorkspaceLayoutInjections = {
- _init: function() {
- this._stateAdjustment.connect('notify::value', () => {
- if (opt.OVERVIEW_MODE !== 1) return;
- // scale 0.1 for window state 0 just needs to be smaller then possible scale of any window in spread view
- const scale = this._stateAdjustment.value ? 0.95 : 0.1;
- if (scale !== Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE || this._stateAdjustment.value === 1) {
- // when transition to ws state 1 begins, replace the constant with the original one
- // disadvantage - the value changes for all workspaces, so one affects others
- // that can be visible in certain situations but not a big deal.
- Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = scale;
- // and force recalculation of the target layout, so the transition will be smooth
- this._needsLayout = true;
- }
- });
- }
-}
-var WorkspaceLayout = {
+// disadvantage of following workaround - the WINDOW_PREVIEW_MAXIMUM_SCALE value is common for every workspace,
+// on multi-monitor system can be visible unwanted scaling of windows on workspace in WORKSPACE_MODE 0 (windows not spread)
+// when leaving overview while any other workspace is in the WORKSPACE_MODE 1.
+const WorkspaceLayout = {
+ // injection to _init()
+ after__init() {
+ if (opt.OVERVIEW_MODE === 1) {
+ this._stateAdjustment.connect('notify::value', () => {
+ // scale 0.1 for window state 0 just needs to be smaller then possible scale of any window in spread view
+ const scale = this._stateAdjustment.value ? 0.95 : 0.1;
+ if (scale !== this.WINDOW_PREVIEW_MAXIMUM_SCALE) {
+ this.WINDOW_PREVIEW_MAXIMUM_SCALE = scale;
+ // when transition to ws state 1 (WINDOW_PICKER) begins, replace the constant with the original one
+ Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = scale;
+ // and force recalculation of the target layout, so the transition will be smooth
+ this._needsLayout = true;
+ }
+ });
+ }
+ },
+
// this fixes wrong size and position calculation of window clones while moving overview to the next (+1) workspace if vertical ws orientation is enabled in GS
- _adjustSpacingAndPadding: function(rowSpacing, colSpacing, containerBox) {
+ _adjustSpacingAndPadding(rowSpacing, colSpacing, containerBox) {
if (this._sortedWindows.length === 0)
return [rowSpacing, colSpacing, containerBox];
@@ -103,11 +110,11 @@ var WorkspaceLayout = {
const monitor = Main.layoutManager.monitors[this._monitorIndex];
const bottomPoint = new Graphene.Point3D();
- if (vertical) {
+ if (vertical)
bottomPoint.x = containerBox.x2;
- } else {
+ else
bottomPoint.y = containerBox.y2;
- }
+
const transformedBottomPoint =
this._container.apply_transform_to_point(bottomPoint);
@@ -117,30 +124,29 @@ var WorkspaceLayout = {
const [, bottomOverlap] = window.overlapHeights();
- if ((bottomOverlap + oversize) > bottomFreeSpace && !vertical) {
+ if ((bottomOverlap + oversize) > bottomFreeSpace && !vertical)
containerBox.y2 -= (bottomOverlap + oversize) - bottomFreeSpace;
- }
}
return [rowSpacing, colSpacing, containerBox];
- }
-}
+ },
+};
-var WorkspaceBackground = {
- _updateBorderRadius: function(value = false) {
+const WorkspaceBackground = {
+ _updateBorderRadius(value = false) {
// don't round already rounded corners during exposing windows
- if (value === false && opt.OVERVIEW_MODE === 1) {
+ if (value === false && opt.OVERVIEW_MODE === 1)
return;
- }
+
const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
- const cornerRadius = scaleFactor * BACKGROUND_CORNER_RADIUS_PIXELS;
+ const cornerRadius = scaleFactor * opt.WS_PREVIEW_BG_RADIUS;
const backgroundContent = this._bgManager.backgroundActor.content;
- value = (value !==false)
- ? value
- : this._stateAdjustment.value;
+ value = value !== false
+ ? value
+ : this._stateAdjustment.value;
backgroundContent.rounded_clip_radius =
Util.lerp(0, cornerRadius, value);
- }
-}
+ },
+};
diff --git a/extensions/vertical-workspaces/workspaceAnimation.js b/extensions/vertical-workspaces/lib/workspaceAnimation.js
index 40f61b8..07008c6 100644
--- a/extensions/vertical-workspaces/workspaceAnimation.js
+++ b/extensions/vertical-workspaces/lib/workspaceAnimation.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* workspacesAnimation.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -9,31 +9,40 @@
*/
'use strict';
-
-const { St } = imports.gi;
-
const Main = imports.ui.main;
const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
const WorkspaceAnimation = imports.ui.workspaceAnimation;
const Me = imports.misc.extensionUtils.getCurrentExtension();
-const _Util = Me.imports.util;
+const _Util = Me.imports.lib.util;
-// touching module properties defined by const/let for the first time returns undefined in GS 42, so we touch it here before we use it
-WorkspaceAnimation.MonitorGroup;
+// first reference to constant defined using const in other module returns undefined, the MonitorGroup const will remain empty and unused
+let MonitorGroupDummy = WorkspaceAnimation.MonitorGroup;
+MonitorGroupDummy = null;
let _origBaseDistance;
let _wsAnimationSwipeBeginId;
+let _wsAnimationSwipeUpdateId;
let _wsAnimationSwipeEndId;
+
let _overrides;
let opt;
+let _firstRun = true;
function update(reset = false) {
- if (_overrides) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('workspaceAnimationModule', true);
+ reset = reset || !moduleEnabled;
+
+ // don't even touch this module if disabled
+ if (_firstRun && reset)
+ return;
+
+ _firstRun = false;
+
+ if (_overrides)
_overrides.removeAll();
- }
- opt = Me.imports.settings.opt;
- if (reset || !opt.STATIC_WS_SWITCHER_BG) {
+ if (reset || !moduleEnabled) {
_connectWsAnimationSwipeTracker(true);
_overrideMonitorGroupProperty(true);
_overrides = null;
@@ -41,17 +50,19 @@ function update(reset = false) {
return;
}
- _overrides = new _Util.Overrides();
- _connectWsAnimationSwipeTracker();
- _overrideMonitorGroupProperty();
+ if (opt.STATIC_WS_SWITCHER_BG) {
+ _overrides = new _Util.Overrides();
+ _overrideMonitorGroupProperty();
+ _overrides.addOverride('WorkspaceAnimationMonitorGroup', WorkspaceAnimation.MonitorGroup.prototype, MonitorGroup);
+ }
- _overrides.addInjection('WorkspaceAnimationMonitorGroup', WorkspaceAnimation.MonitorGroup.prototype, MonitorGroupInjections);
+ _connectWsAnimationSwipeTracker();
}
// remove spacing between workspaces during transition to remove flashing wallpaper between workspaces with maximized windows
function _overrideMonitorGroupProperty(reset = false) {
if (!_origBaseDistance)
- _origBaseDistance = Object.getOwnPropertyDescriptor(WorkspaceAnimation.MonitorGroup.prototype,'baseDistance').get;
+ _origBaseDistance = Object.getOwnPropertyDescriptor(WorkspaceAnimation.MonitorGroup.prototype, 'baseDistance').get;
let getter;
if (reset) {
@@ -59,28 +70,29 @@ function _overrideMonitorGroupProperty(reset = false) {
getter = { get: _origBaseDistance };
} else {
getter = {
- get: function () {
- //const spacing = 100 * St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ get() {
+ // const spacing = 100 * imports.gi.St.ThemeContext.get_for_stage(global.stage).scale_factor;
const spacing = 0;
if (global.workspace_manager.layout_rows === -1)
return this._monitor.height + spacing + (opt.PANEL_MODE ? Main.panel.height : 0); // compensation for hidden panel
else
return this._monitor.width + spacing;
- }
- }
+ },
+ };
}
if (getter)
- Object.defineProperty(WorkspaceAnimation.MonitorGroup.prototype, "baseDistance", getter);
+ Object.defineProperty(WorkspaceAnimation.MonitorGroup.prototype, 'baseDistance', getter);
}
-const MonitorGroupInjections = {
- _init: function() {
+const MonitorGroup = {
+ // injection to _init()
+ after__init() {
// we have two options to implement static bg feature
// one is adding background to monitorGroup
// but this one has disadvantage - sticky windows will be always on top of animated windows
// which is bad for conky, for example, that window should be always below
- /*this._bgManager = new Background.BackgroundManager({
+ /* this._bgManager = new Background.BackgroundManager({
container: this,
monitorIndex: this._monitor.index,
controlPosition: false,
@@ -92,21 +104,22 @@ const MonitorGroupInjections = {
this.set_style('background-color: transparent;');
// stickyGroup holds the Always on Visible Workspace windows to keep them static and above other windows during animation
const stickyGroup = this.get_children()[1];
- stickyGroup._windowRecords.forEach((r, index) => {
+ stickyGroup._windowRecords.forEach(r => {
const metaWin = r.windowActor.metaWindow;
// conky is sticky but should never get above other windows during ws animation
// so we hide it from the overlay group, we will see the original if not covered by other windows
- if (metaWin.wm_class == 'conky') {
+ if (metaWin.wm_class === 'conky')
r.clone.opacity = 0;
- }
- })
+ });
this._hiddenWindows = [];
// remove (hide) background wallpaper from the animation, we will see the original one
- this._workspaceGroups.forEach(w => w._background.opacity = 0);
+ this._workspaceGroups.forEach(w => {
+ w._background.opacity = 0;
+ });
// hide (scale to 0) all non-sticky windows, their clones will be animated
global.get_window_actors().forEach(actor => {
const metaWin = actor.metaWindow;
- if (metaWin?.get_monitor() === this._monitor.index && !(metaWin?.wm_class == 'conky' && metaWin?.is_on_all_workspaces())) { //* && !w.is_on_all_workspaces()*/) {
+ if (metaWin?.get_monitor() === this._monitor.index && !(metaWin?.wm_class === 'conky' && metaWin?.is_on_all_workspaces())) { //* && !w.is_on_all_workspaces()*/) {
// hide original window. we cannot use opacity since it also affects clones.
// scaling them to 0 works well
actor.scale_x = 0;
@@ -116,16 +129,13 @@ const MonitorGroupInjections = {
// restore all hidden windows at the end of animation
// todo - actors removed during transition need to be removed from the list to avoid access to destroyed actor
- this.connect('destroy', () =>{
+ this.connect('destroy', () => {
this._hiddenWindows.forEach(actor => {
actor.scale_x = 1;
});
});
- }
-}
-
-
-// ------ connect Ws Animation Swipe Tracker --------------
+ },
+};
function _connectWsAnimationSwipeTracker(reset = false) {
if (reset) {
@@ -139,35 +149,30 @@ function _connectWsAnimationSwipeTracker(reset = false) {
}
} else if (!_wsAnimationSwipeBeginId) {
// display ws switcher popup when gesture begins and connect progress
- _wsAnimationSwipeBeginId = Main.wm._workspaceAnimation._swipeTracker.connect('begin', _connectWsAnimationProgress);
+ _wsAnimationSwipeBeginId = Main.wm._workspaceAnimation._swipeTracker.connect('begin', () => _connectWsAnimationProgress(true));
// we want to be sure that popup with the final ws index show up when gesture ends
- _wsAnimationSwipeEndId = Main.wm._workspaceAnimation._swipeTracker.connect('end', () => _showWsSwitcherPopup(0));
+ _wsAnimationSwipeEndId = Main.wm._workspaceAnimation._swipeTracker.connect('end', (tracker, duration, endProgress) => _connectWsAnimationProgress(false, endProgress));
}
}
-function _connectWsAnimationProgress() {
- if (Main.overview.visible) return;
-
- // progress is being updated only when user finished gesture and the animation continues on "autopilot"
- Main.wm._workspaceAnimation._switchData.monitors[0].connect('notify::progress',(actor) => {
- const progress = actor.progress % 1;
- let direction = 0;
- if (!actor._prg) actor._prg = progress;
- else if (!progress) return;
- else if (progress < actor._prg) direction = -1;
- else if (progress > actor._prg) direction = 1;
- if (progress < 0.6 && progress > 0.4)
- _showWsSwitcherPopup(direction);
- });
-
- // display popup when gesture begins
- _showWsSwitcherPopup(0);
+function _connectWsAnimationProgress(connect, endProgress = null) {
+ if (Main.overview.visible)
+ return;
+
+ if (connect && !_wsAnimationSwipeUpdateId) {
+ _wsAnimationSwipeUpdateId = Main.wm._workspaceAnimation._swipeTracker.connect('update', (tracker, progress) => _showWsSwitcherPopup(progress));
+ } else if (!connect && _wsAnimationSwipeUpdateId) {
+ Main.wm._workspaceAnimation._swipeTracker.disconnect(_wsAnimationSwipeUpdateId);
+ _wsAnimationSwipeUpdateId = 0;
+ _showWsSwitcherPopup(Math.round(endProgress));
+ }
}
-function _showWsSwitcherPopup(direction) {
- if (Main.overview.visible) return;
+function _showWsSwitcherPopup(progress) {
+ if (Main.overview.visible)
+ return;
- const wsIndex = global.workspaceManager.get_active_workspace_index() + direction;
+ const wsIndex = Math.round(progress);
if (Main.wm._workspaceSwitcherPopup === null) {
Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
Main.wm._workspaceSwitcherPopup.connect('destroy', () => {
diff --git a/extensions/vertical-workspaces/lib/workspaceSwitcherPopup.js b/extensions/vertical-workspaces/lib/workspaceSwitcherPopup.js
new file mode 100644
index 0000000..972f35e
--- /dev/null
+++ b/extensions/vertical-workspaces/lib/workspaceSwitcherPopup.js
@@ -0,0 +1,90 @@
+/**
+ * V-Shell (Vertical Workspaces)
+ * workspacesSwitcherPopup.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022 - 2023
+ * @license GPL-3.0
+ *
+ */
+
+'use strict';
+
+const Main = imports.ui.main;
+const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+
+const _Util = Me.imports.lib.util;
+let _overrides;
+
+let opt;
+let _firstRun = true;
+
+function update(reset = false) {
+ opt = Me.imports.lib.settings.opt;
+ const moduleEnabled = opt.get('workspaceSwitcherPopupModule', 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();
+
+ const enabled = global.settings.get_strv('enabled-extensions');
+ const allowWsPopupInjection = !(enabled.includes('workspace-switcher-manager@G-dH.github.com') || enabled.includes('WsSwitcherPopupManager@G-dH.github.com-dev'));
+ if (allowWsPopupInjection) { // 1-VERTICAL, 0-HORIZONTAL
+ _overrides.addOverride('WorkspaceSwitcherPopup', WorkspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype, WorkspaceSwitcherPopupOverride);
+ }
+}
+
+const WorkspaceSwitcherPopupOverride = {
+ // injection to _init()
+ after__init() {
+ if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL
+ this._list.vertical = true;
+ }
+ this._list.set_style('margin: 0;');
+ this.remove_constraint(this.get_constraints()[0]);
+ },
+
+ // injection to display()
+ after_display() {
+ if (opt.WS_SW_POPUP_MODE)
+ this._setPopupPosition();
+ else
+ this.opacity = 0;
+ },
+
+ _setPopupPosition() {
+ let workArea;
+ if (opt.WS_SW_POPUP_MODE === 1) {
+ // workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);*/
+ workArea = global.display.get_monitor_geometry(Main.layoutManager.primaryIndex);
+ } else {
+ // workArea = Main.layoutManager.getWorkAreaForMonitor(global.display.get_current_monitor());
+ workArea = global.display.get_monitor_geometry(global.display.get_current_monitor());
+ }
+
+ let [, natHeight] = this.get_preferred_height(global.screen_width);
+ let [, natWidth] = this.get_preferred_width(natHeight);
+ let h = opt.WS_SW_POPUP_H_POSITION;
+ let v = opt.WS_SW_POPUP_V_POSITION;
+ this.x = workArea.x + Math.floor((workArea.width - natWidth) * h);
+ this.y = workArea.y + Math.floor((workArea.height - natHeight) * v);
+ this.set_position(this.x, this.y);
+ },
+};
diff --git a/extensions/vertical-workspaces/workspaceThumbnail.js b/extensions/vertical-workspaces/lib/workspaceThumbnail.js
index bbf8900..d0bc206 100644
--- a/extensions/vertical-workspaces/workspaceThumbnail.js
+++ b/extensions/vertical-workspaces/lib/workspaceThumbnail.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* workspaceThumbnail.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -10,7 +10,7 @@
'use strict';
-const { Clutter, Graphene, Meta, Shell, St } = imports.gi;
+const { GLib, Clutter, Graphene, Meta, Shell, St } = imports.gi;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Background = imports.ui.background;
@@ -22,70 +22,65 @@ const ControlsState = imports.ui.overviewControls.ControlsState;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
-const _Util = Me.imports.util;
+// gettext
+const _ = Me.imports.lib.settings._;
+
+const _Util = Me.imports.lib.util;
+const shellVersion = _Util.shellVersion;
+
let _overrides;
const WORKSPACE_CUT_SIZE = 10;
-const original_MAX_THUMBNAIL_SCALE = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE;
-let MAX_THUMBNAIL_SCALE;
-
-var opt = null;
+const _originalMaxThumbnailScale = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE;
+let opt = null;
function update(reset = false) {
- if (_overrides) {
+ if (_overrides)
_overrides.removeAll();
- }
+
if (reset) {
- if (original_MAX_THUMBNAIL_SCALE)
- WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = original_MAX_THUMBNAIL_SCALE;
+ if (_originalMaxThumbnailScale)
+ WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = _originalMaxThumbnailScale;
_overrides = null;
opt = null;
return;
}
- opt = Me.imports.settings.opt;
-
- MAX_THUMBNAIL_SCALE = opt.MAX_THUMBNAIL_SCALE;
- WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = MAX_THUMBNAIL_SCALE;
-
+ opt = Me.imports.lib.settings.opt;
_overrides = new _Util.Overrides();
+ // don't limit max thumbnail scale for other clients than overview, for example AATWS.
+ WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = 1;
+
_overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon);
+ _overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon);
- if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) {
- _overrides.addOverride('ThumbnailsBox', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxVertical);
- } else {
- _overrides.addOverride('ThumbnailsBox', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxHorizontal);
- }
+ // replacing opt.ORIENTATION local constant with boxOrientation internal variable allows external customers such as the AATWS extension to control the box orientation.
+ Main.overview._overview.controls._thumbnailsBox._boxOrientation = opt.ORIENTATION;
}
-
-var WorkspaceThumbnailCommon = {
- after__init: function () {
-
- //radius of ws thumbnail background
- this.add_style_class_name('ws-tmb');
+const WorkspaceThumbnailCommon = {
+ // injection to _init()
+ after__init() {
+ // layout manager allows aligning widget children
+ this.layout_manager = new Clutter.BinLayout();
+ // adding layout manager to tmb widget breaks wallpaper background aligning and rounded corners
+ // unless border is removed
+ if (opt.SHOW_WS_TMB_BG)
+ this.add_style_class_name('ws-tmb-labeled');
// add workspace thumbnails labels if enabled
if (opt.SHOW_WST_LABELS) { // 0 - disable
- // layout manager allows aligning widget children
- this.layout_manager = new Clutter.BinLayout();
- // adding layout manager to tmb widget breaks wallpaper background aligning and rounded corners
- // unless border is removed
- if (opt.SHOW_WS_TMB_BG)
- this.add_style_class_name('ws-tmb-labeled');
-
- const getLabel = function() {
+ const getLabel = function () {
const wsIndex = this.metaWorkspace.index();
let label = `${wsIndex + 1}`;
if (opt.SHOW_WST_LABELS === 2) { // 2 - index + workspace name
const settings = ExtensionUtils.getSettings('org.gnome.desktop.wm.preferences');
const wsLabels = settings.get_strv('workspace-names');
- if (wsLabels.length > wsIndex && wsLabels[wsIndex]) {
+ if (wsLabels.length > wsIndex && wsLabels[wsIndex])
label += `: ${wsLabels[wsIndex]}`;
- }
} else if (opt.SHOW_WST_LABELS === 3) { // 3- index + app name
// global.display.get_tab_list offers workspace filtering using the second argument, but...
// ... it sometimes includes windows from other workspaces, like minimized VBox machines, after Shell restarts
@@ -93,16 +88,16 @@ var WorkspaceThumbnailCommon = {
w => w.get_monitor() === this.monitorIndex && w.get_workspace().index() === wsIndex)[0];
if (metaWin) {
- let tracker = Shell.WindowTracker.get_default();
- label += `: ${tracker.get_window_app(metaWin).get_name()}`;
+ const tracker = Shell.WindowTracker.get_default();
+ const app = tracker.get_window_app(metaWin);
+ label += `: ${app ? app.get_name() : ''}`;
}
} else if (opt.SHOW_WST_LABELS === 4) {
const metaWin = global.display.get_tab_list(0, null).filter(
w => w.get_monitor() === this.monitorIndex && w.get_workspace().index() === wsIndex)[0];
- if (metaWin) {
+ if (metaWin)
label += `: ${metaWin.title}`;
- }
}
return label;
}.bind(this);
@@ -124,28 +119,96 @@ var WorkspaceThumbnailCommon = {
this.add_child(this._wsLabel);
this.set_child_above_sibling(this._wsLabel, null);
- this._wsIndexSigId = this.metaWorkspace.connect('notify::workspace-index', () => {
+ this._wsIndexConId = this.metaWorkspace.connect('notify::workspace-index', () => {
const newLabel = getLabel();
this._wsLabel.text = newLabel;
+ // avoid possibility of accessing non existing ws
+ if (this._updateLabelTimeout) {
+ GLib.source_remove(this._updateLabelTimeout);
+ this._updateLabelTimeout = 0;
+ }
+ });
+ this._nWindowsConId = this.metaWorkspace.connect('notify::n-windows', () => {
+ // wait for new information
+ this._updateLabelTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => {
+ const newLabel = getLabel();
+ this._wsLabel.text = newLabel;
+ this._updateLabelTimeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
});
+ }
+
+ if (opt.CLOSE_WS_BUTTON_MODE) {
+ const closeButton = new St.Icon({
+ style_class: 'workspace-close-button',
+ icon_name: 'window-close-symbolic',
+ x_align: Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.START,
+ x_expand: true,
+ y_expand: true,
+ reactive: true,
+ opacity: 0,
+ });
+
+ closeButton.connect('button-release-event', () => {
+ if (opt.CLOSE_WS_BUTTON_MODE) {
+ this._closeWorkspace();
+ return Clutter.EVENT_STOP;
+ } else {
+ return Clutter.EVENT_PROPAGATE;
+ }
+ });
+
+ closeButton.connect('button-press-event', () => {
+ return Clutter.EVENT_STOP;
+ });
+
+ closeButton.connect('enter-event', () => {
+ closeButton.opacity = 255;
+ if (!Meta.prefs_get_dynamic_workspaces() || (Meta.prefs_get_dynamic_workspaces() && global.workspace_manager.get_n_workspaces() - 1 !== this.metaWorkspace.index())) {
+ // color the button red if ready to react on clicks
+ if (opt.CLOSE_WS_BUTTON_MODE < 3 || (opt.CLOSE_WS_BUTTON_MODE === 3 && _Util.isCtrlPressed()))
+ closeButton.add_style_class_name('workspace-close-button-hover');
+ }
+ });
+
+ closeButton.connect('leave-event', () => {
+ closeButton.remove_style_class_name('workspace-close-button-hover');
+ });
+
+ this.add_child(closeButton);
+ this._closeButton = closeButton;
+
+ this.reactive = true;
+ this._lastCloseClickTime = 0;
+ }
- this.connect('destroy', () => this.metaWorkspace.disconnect(this._wsIndexSigId));
+ if (opt.SHOW_WST_LABELS_ON_HOVER)
+ this._wsLabel.opacity = 0;
+ this.connect('enter-event', () => {
+ if (opt.CLOSE_WS_BUTTON_MODE && (!Meta.prefs_get_dynamic_workspaces() || (Meta.prefs_get_dynamic_workspaces() && global.workspace_manager.get_n_workspaces() - 1 !== this.metaWorkspace.index())))
+ this._closeButton.opacity = 200;
if (opt.SHOW_WST_LABELS_ON_HOVER) {
- this._wsLabel.opacity = 0;
- this.reactive = true;
- this.connect('enter-event', ()=> this._wsLabel.ease({
+ this._wsLabel.ease({
duration: 100,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- opacity: this._wsLabel._maxOpacity
- }));
- this.connect('leave-event', ()=> this._wsLabel.ease({
+ opacity: this._wsLabel._maxOpacity,
+ });
+ }
+ });
+
+ this.connect('leave-event', () => {
+ this._closeButton.opacity = 0;
+ if (opt.SHOW_WST_LABELS_ON_HOVER) {
+ this._wsLabel.ease({
duration: 100,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- opacity: 0
- }));
+ opacity: 0,
+ });
}
- }
+ });
if (opt.SHOW_WS_TMB_BG) {
this._bgManager = new Background.BackgroundManager({
@@ -157,32 +220,58 @@ var WorkspaceThumbnailCommon = {
this._viewport.set_child_below_sibling(this._bgManager.backgroundActor, null);
- this.connect('destroy', function () {
+ this.connect('destroy', () => {
if (this._bgManager)
this._bgManager.destroy();
this._bgManager = null;
- }.bind(this));
+ });
+ // full brightness of the thumbnail bg draws unnecessary attention
+ // there is a grey bg under the wallpaper
this._bgManager.backgroundActor.opacity = 220;
+ }
- // this all is just for the small border radius...
- /*const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
- const cornerRadius = scaleFactor * BACKGROUND_CORNER_RADIUS_PIXELS;
- const backgroundContent = this._bgManager.backgroundActor.content;
- backgroundContent.rounded_clip_radius = cornerRadius;
+ this.connect('destroy', () => {
+ if (this._wsIndexConId)
+ this.metaWorkspace.disconnect(this._wsIndexConId);
+
+ if (this._nWindowsConId)
+ this.metaWorkspace.disconnect(this._nWindowsConId);
+
+ if (this._updateLabelTimeout)
+ GLib.source_remove(this._updateLabelTimeout);
+
+ if (this._bgManager)
+ this._bgManager.destroy();
+ });
+ },
+
+ _closeWorkspace() {
+ // CLOSE_WS_BUTTON_MODE 1: single click, 2: double-click, 3: Ctrl
+
+ if (opt.CLOSE_WS_BUTTON_MODE === 2) {
+ const doubleClickTime = Clutter.Settings.get_default().double_click_time;
+ const clickDelay = Date.now() - this._lastCloseClickTime;
+ if (clickDelay > doubleClickTime) {
+ this._lastCloseClickTime = Date.now();
+ return;
+ }
+ } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !_Util.isCtrlPressed()) {
+ return;
+ }
- // the original clip has some addition at the bottom
- const rect = new Graphene.Rect();
- rect.origin.x = this._viewport.x;
- rect.origin.y = this._viewport.y;
- rect.size.width = this._viewport.width;
- rect.size.height = this._viewport.height;
+ // close windows on this monitor
+ const windows = global.display.get_tab_list(0, null).filter(
+ w => w.get_monitor() === this.monitorIndex && w.get_workspace() === this.metaWorkspace
+ );
- this._bgManager.backgroundActor.content.set_rounded_clip_bounds(rect);*/
+ for (let i = 0; i < windows.length; i++) {
+ if (!windows[i].is_on_all_workspaces())
+ windows[i].delete(global.get_current_time() + i);
}
},
- activate: function(time) {
+ activate(time) {
if (this.state > ThumbnailState.NORMAL)
return;
@@ -199,112 +288,214 @@ var WorkspaceThumbnailCommon = {
} else {
this.metaWorkspace.activate(time);
}
- } else {
- if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && wsIndex < lastWsIndex) {
- if (stateAdjustment.value > 1) {
- stateAdjustment.value = 1;
- }
+ } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && wsIndex < lastWsIndex) {
+ if (stateAdjustment.value > 1)
+ stateAdjustment.value = 1;
- // spread windows
- // in OVERVIEW MODE 2 windows are not spread and workspace is not scaled
- // we need to repeat transition to the overview state 1 (window picker), but with spreading windows animation
- if (this.metaWorkspace.active) {
- opt.WORKSPACE_MODE = 1;
- const stateAdjustment = Main.overview._overview.controls._stateAdjustment
- // setting value to 0 would reset WORKSPACE_MODE
- stateAdjustment.value = 0.01;
- stateAdjustment.ease(1, {
- duration: 200,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
- /*const adjustment = Main.overview._overview.controls._workspacesDisplay._workspacesViews[0]._workspaces[wsIndex].stateAdjustment;
- adjustment.value = 0;
- adjustment.ease(1, {
- duration: 200,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });*/
- } else {
- // switch ws
- this.metaWorkspace.activate(time);
- }
- // a click on the current workspace should go back to the main view
- } else if (this.metaWorkspace.active) {
- Main.overview.hide();
+ // spread windows
+ // in OVERVIEW MODE 2 windows are not spread and workspace is not scaled
+ // we need to repeat transition to the overview state 1 (window picker), but with spreading windows animation
+ if (this.metaWorkspace.active) {
+ Main.overview._overview.controls._searchController._setSearchActive(false);
+ opt.WORKSPACE_MODE = 1;
+ // setting value to 0 would reset WORKSPACE_MODE
+ stateAdjustment.value = 0.01;
+ stateAdjustment.ease(1, {
+ duration: 200,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
} else {
+ // switch ws
this.metaWorkspace.activate(time);
}
+ // a click on the current workspace should go back to the main view
+ } else if (this.metaWorkspace.active) {
+ Main.overview.hide();
+ } else {
+ this.metaWorkspace.activate(time);
}
- }
-}
+ },
-// ThumbnailsBox Vertical
+ // Draggable target interface used only by ThumbnailsBox
+ handleDragOverInternal(source, actor, time) {
+ if (source === Main.xdndHandler) {
+ this.metaWorkspace.activate(time);
+ return DND.DragMotionResult.CONTINUE;
+ }
-var ThumbnailsBoxVertical = {
- _activateThumbnailAtPoint: function(stageX, stageY, time) {
- const [r_, x, y] = this.transform_stage_point(stageX, stageY);
+ if (this.state > ThumbnailState.NORMAL)
+ return DND.DragMotionResult.CONTINUE;
- const thumbnail = this._thumbnails.find(t => y >= t.y && y <= t.y + t.height);
- if (thumbnail) {
- thumbnail.activate(time);
- }
- },
+ if (source.metaWindow &&
+ !this._isMyWindow(source.metaWindow.get_compositor_private()))
+ return DND.DragMotionResult.MOVE_DROP;
+ if (source.app && source.app.can_open_new_window())
+ return DND.DragMotionResult.COPY_DROP;
+ if (!source.app && source.shellWorkspaceLaunch)
+ return DND.DragMotionResult.COPY_DROP;
- _getPlaceholderTarget: function(index, spacing, rtl) {
- const workspace = this._thumbnails[index];
+ if (source instanceof imports.ui.appDisplay.FolderIcon)
+ return DND.DragMotionResult.COPY_DROP;
- let targetY1;
- let targetY2;
- if (rtl) {
- const baseY = workspace.y + workspace.height;
- targetY1 = baseY - WORKSPACE_CUT_SIZE;
- targetY2 = baseY + spacing + WORKSPACE_CUT_SIZE;
- } else {
- targetY1 = workspace.y - spacing - WORKSPACE_CUT_SIZE;
- targetY2 = workspace.y + WORKSPACE_CUT_SIZE;
- }
+ return DND.DragMotionResult.CONTINUE;
+ },
- if (index === 0) {
- if (rtl)
- targetY2 -= spacing + WORKSPACE_CUT_SIZE;
- else
- targetY1 += spacing + WORKSPACE_CUT_SIZE;
+ acceptDropInternal(source, actor, time) {
+ if (this.state > ThumbnailState.NORMAL)
+ return false;
+
+ if (source.metaWindow) {
+ let win = source.metaWindow.get_compositor_private();
+ if (this._isMyWindow(win))
+ return false;
+
+ let metaWindow = win.get_meta_window();
+ Main.moveWindowToMonitorAndWorkspace(metaWindow,
+ this.monitorIndex, this.metaWorkspace.index());
+ return true;
+ } else if (source.app && source.app.can_open_new_window()) {
+ if (source.animateLaunchAtPos)
+ source.animateLaunchAtPos(actor.x, actor.y);
+
+ source.app.open_new_window(this.metaWorkspace.index());
+ return true;
+ } else if (!source.app && source.shellWorkspaceLaunch) {
+ // While unused in our own drag sources, shellWorkspaceLaunch allows
+ // extensions to define custom actions for their drag sources.
+ source.shellWorkspaceLaunch({
+ workspace: this.metaWorkspace.index(),
+ timestamp: time,
+ });
+ return true;
+ } else if (source instanceof imports.ui.appDisplay.FolderIcon) {
+ if (shellVersion >= 44) {
+ for (let app of source.view._apps) {
+ // const app = Shell.AppSystem.get_default().lookup_app(id);
+ app.open_new_window(this.metaWorkspace.index());
+ }
+ } else {
+ for (let id of source.view._appIds) {
+ const app = Shell.AppSystem.get_default().lookup_app(id);
+ app.open_new_window(this.metaWorkspace.index());
+ }
+ }
}
- if (index === this._dropPlaceholderPos) {
- const placeholderHeight = this._dropPlaceholder.get_height() + spacing;
- if (rtl)
- targetY2 += placeholderHeight;
- else
- targetY1 -= placeholderHeight;
+ return false;
+ },
+};
+
+const ThumbnailsBoxCommon = {
+ after__init(scrollAdjustment, monitorIndex, orientation = opt.ORIENTATION) {
+ this._boxOrientation = orientation;
+ },
+
+ _activateThumbnailAtPoint(stageX, stageY, time, activateCurrent = false) {
+ if (activateCurrent) {
+ const thumbnail = this._thumbnails.find(t => t.metaWorkspace.active);
+ if (thumbnail)
+ thumbnail.activate(time);
+ return;
}
+ const [r_, x, y] = this.transform_stage_point(stageX, stageY);
- return [targetY1, targetY2];
+ let thumbnail;
+
+ if (this._boxOrientation)
+ thumbnail = this._thumbnails.find(t => y >= t.y && y <= t.y + t.height);
+ else
+ thumbnail = this._thumbnails.find(t => x >= t.x && x <= t.x + t.width);
+
+ if (thumbnail)
+ thumbnail.activate(time);
},
- _withinWorkspace: function(y, index, rtl) {
- const length = this._thumbnails.length;
- const workspace = this._thumbnails[index];
+ acceptDrop(source, actor, x, y, time) {
+ if (this._dropWorkspace !== -1) {
+ return this._thumbnails[this._dropWorkspace].acceptDropInternal(source, actor, time);
+ } else if (this._dropPlaceholderPos !== -1) {
+ if (!source.metaWindow &&
+ (!source.app || !source.app.can_open_new_window()) &&
+ (source.app || !source.shellWorkspaceLaunch) &&
+ !(source instanceof imports.ui.appDisplay.FolderIcon))
+ return false;
+
+
+ let isWindow = !!source.metaWindow;
+
+ let newWorkspaceIndex;
+ [newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1];
+ this._spliceIndex = newWorkspaceIndex;
+
+ Main.wm.insertWorkspace(newWorkspaceIndex);
+
+ if (isWindow) {
+ // Move the window to our monitor first if necessary.
+ let thumbMonitor = this._thumbnails[newWorkspaceIndex].monitorIndex;
+ Main.moveWindowToMonitorAndWorkspace(source.metaWindow,
+ thumbMonitor, newWorkspaceIndex, true);
+ } else if (source.app && source.app.can_open_new_window()) {
+ if (source.animateLaunchAtPos)
+ source.animateLaunchAtPos(actor.x, actor.y);
+
+ source.app.open_new_window(newWorkspaceIndex);
+ } else if (!source.app && source.shellWorkspaceLaunch) {
+ // While unused in our own drag sources, shellWorkspaceLaunch allows
+ // extensions to define custom actions for their drag sources.
+ source.shellWorkspaceLaunch({
+ workspace: newWorkspaceIndex,
+ timestamp: time,
+ });
+ } else if (source instanceof imports.ui.appDisplay.FolderIcon) {
+ if (shellVersion >= 44) {
+ for (let app of source.view._apps) {
+ // const app = Shell.AppSystem.get_default().lookup_app(id);
+ app.open_new_window(newWorkspaceIndex);
+ }
+ } else {
+ for (let id of source.view._appIds) {
+ const app = Shell.AppSystem.get_default().lookup_app(id);
+ app.open_new_window(newWorkspaceIndex);
+ }
+ }
+ }
- let workspaceY1 = workspace.y + WORKSPACE_CUT_SIZE;
- let workspaceY2 = workspace.y + workspace.height - WORKSPACE_CUT_SIZE;
+ if (source.app || (!source.app && source.shellWorkspaceLaunch)) {
+ // This new workspace will be automatically removed if the application fails
+ // to open its first window within some time, as tracked by Shell.WindowTracker.
+ // Here, we only add a very brief timeout to avoid the _immediate_ removal of the
+ // workspace while we wait for the startup sequence to load.
+ let workspaceManager = global.workspace_manager;
+ Main.wm.keepWorkspaceAlive(workspaceManager.get_workspace_by_index(newWorkspaceIndex),
+ WorkspaceThumbnail.WORKSPACE_KEEP_ALIVE_TIME);
+ }
- if (index === length - 1) {
- if (rtl)
- workspaceY1 -= WORKSPACE_CUT_SIZE;
- else
- workspaceY2 += WORKSPACE_CUT_SIZE;
- }
+ // Start the animation on the workspace (which is actually
+ // an old one which just became empty)
+ let thumbnail = this._thumbnails[newWorkspaceIndex];
+ this._setThumbnailState(thumbnail, ThumbnailState.NEW);
+ thumbnail.slide_position = 1;
+ thumbnail.collapse_fraction = 1;
- return y > workspaceY1 && y <= workspaceY2;
+ this._queueUpdateStates();
+
+ return true;
+ } else {
+ return false;
+ }
},
- handleDragOver: function(source, actor, x, y, time) {
+ handleDragOver(source, actor, x, y, time) {
+ // switch axis for vertical orientation
+ if (this._boxOrientation)
+ x = y;
+
if (!source.metaWindow &&
(!source.app || !source.app.can_open_new_window()) &&
(source.app || !source.shellWorkspaceLaunch) &&
- source != Main.xdndHandler)
+ source !== Main.xdndHandler && !(source instanceof imports.ui.appDisplay.FolderIcon))
return DND.DragMotionResult.CONTINUE;
const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
@@ -321,34 +512,132 @@ var ThumbnailsBoxVertical = {
const [targetStart, targetEnd] =
this._getPlaceholderTarget(index, spacing, rtl);
- if (y > targetStart && y <= targetEnd) {
+ if (x > targetStart && x <= targetEnd) {
placeholderPos = index;
break;
}
}
- if (this._withinWorkspace(y, index, rtl)) {
+ if (this._withinWorkspace(x, index, rtl)) {
this._dropWorkspace = index;
break;
}
}
- if (this._dropPlaceholderPos != placeholderPos) {
+ if (this._dropPlaceholderPos !== placeholderPos) {
this._dropPlaceholderPos = placeholderPos;
this.queue_relayout();
}
- if (this._dropWorkspace != -1)
+ if (this._dropWorkspace !== -1)
return this._thumbnails[this._dropWorkspace].handleDragOverInternal(source, actor, time);
- else if (this._dropPlaceholderPos != -1)
+ else if (this._dropPlaceholderPos !== -1)
return source.metaWindow ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.COPY_DROP;
else
return DND.DragMotionResult.CONTINUE;
},
- //vfunc_get_preferred_width: function(forHeight) {
+ _getPlaceholderTarget(...args) {
+ if (this._boxOrientation)
+ return ThumbnailsBoxVertical._getPlaceholderTarget.bind(this)(...args);
+ else
+ return ThumbnailsBoxHorizontal._getPlaceholderTarget.bind(this)(...args);
+ },
+
+ _withinWorkspace(...args) {
+ if (this._boxOrientation)
+ return ThumbnailsBoxVertical._withinWorkspace.bind(this)(...args);
+ else
+ return ThumbnailsBoxHorizontal._withinWorkspace.bind(this)(...args);
+ },
+
+ get_preferred_custom_width(...args) {
+ if (this._boxOrientation)
+ return ThumbnailsBoxVertical.get_preferred_custom_width.bind(this)(...args);
+ else
+ return ThumbnailsBoxHorizontal.get_preferred_custom_width.bind(this)(...args);
+ },
+
+ get_preferred_custom_height(...args) {
+ if (this._boxOrientation)
+ return ThumbnailsBoxVertical.get_preferred_custom_height.bind(this)(...args);
+ else
+ return ThumbnailsBoxHorizontal.get_preferred_custom_height.bind(this)(...args);
+ },
+
+ vfunc_allocate(...args) {
+ if (this._boxOrientation)
+ return ThumbnailsBoxVertical.vfunc_allocate.bind(this)(...args);
+ else
+ return ThumbnailsBoxHorizontal.vfunc_allocate.bind(this)(...args);
+ },
+
+ _updateShouldShow(...args) {
+ if (this._boxOrientation)
+ return ThumbnailsBoxVertical._updateShouldShow.bind(this)(...args);
+ else
+ return ThumbnailsBoxHorizontal._updateShouldShow.bind(this)(...args);
+ },
+};
+
+const ThumbnailsBoxVertical = {
+ _getPlaceholderTarget(index, spacing, rtl) {
+ this._dropPlaceholder.add_style_class_name('placeholder-vertical');
+ const workspace = this._thumbnails[index];
+
+ let targetY1;
+ let targetY2;
+
+ if (rtl) {
+ const baseY = workspace.y + workspace.height;
+ targetY1 = baseY - WORKSPACE_CUT_SIZE;
+ targetY2 = baseY + spacing + WORKSPACE_CUT_SIZE;
+ } else {
+ targetY1 = workspace.y - spacing - WORKSPACE_CUT_SIZE;
+ targetY2 = workspace.y + WORKSPACE_CUT_SIZE;
+ }
+
+ if (index === 0) {
+ if (rtl)
+ targetY2 -= spacing + WORKSPACE_CUT_SIZE;
+ else
+ targetY1 += spacing + WORKSPACE_CUT_SIZE;
+ }
+
+ if (index === this._dropPlaceholderPos) {
+ const placeholderHeight = this._dropPlaceholder.get_height() + spacing;
+ if (rtl)
+ targetY2 += placeholderHeight;
+ else
+ targetY1 -= placeholderHeight;
+ }
+
+ return [targetY1, targetY2];
+ },
+
+ _withinWorkspace(y, index, rtl) {
+ const length = this._thumbnails.length;
+ const workspace = this._thumbnails[index];
+
+ let workspaceY1 = workspace.y + WORKSPACE_CUT_SIZE;
+ let workspaceY2 = workspace.y + workspace.height - WORKSPACE_CUT_SIZE;
+
+ if (index === length - 1) {
+ if (rtl)
+ workspaceY1 -= WORKSPACE_CUT_SIZE;
+ else
+ workspaceY2 += WORKSPACE_CUT_SIZE;
+ }
+
+ return y > workspaceY1 && y <= workspaceY2;
+ },
+
+ // vfunc_get_preferred_width: function(forHeight) {
// override of this vfunc doesn't work for some reason (tested on Ubuntu and Fedora), it's not reachable
- get_preferred_custom_width: function(forHeight) {
+ get_preferred_custom_width(forHeight) {
+ if (!this.visible)
+ return [0, 0];
+
if (forHeight === -1)
return this.get_preferred_custom_height(forHeight);
@@ -363,14 +652,16 @@ var ThumbnailsBoxVertical = {
const avail = forHeight - totalSpacing;
let scale = (avail / nWorkspaces) / this._porthole.height;
- scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
+ // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE);
const width = Math.round(this._porthole.width * scale);
-
- return themeNode.adjust_preferred_width(width, width);
+ return themeNode.adjust_preferred_height(width, width);
},
- get_preferred_custom_height: function(_forWidth) {
+ get_preferred_custom_height(_forWidth) {
+ if (!this.visible)
+ return [0, 0];
+
// Note that for getPreferredHeight/Width we cheat a bit and skip propagating
// the size request to our children because we know how big they are and know
// that the actors aren't depending on the virtual functions being called.
@@ -379,34 +670,29 @@ var ThumbnailsBoxVertical = {
let spacing = themeNode.get_length('spacing');
let nWorkspaces = this._thumbnails.length;
- let totalSpacing = (nWorkspaces - 1) * spacing;
+ // remove also top/bottom box padding
+ let totalSpacing = (nWorkspaces - 3) * spacing;
const ratio = this._porthole.width / this._porthole.height;
- const tmbHeight = _forWidth / ratio;
-
- const naturalheight = this._thumbnails.reduce((accumulator, thumbnail, index) => {
- //let workspaceSpacing = 0;
+ const tmbHeight = themeNode.adjust_for_width(_forWidth) / ratio;
+ const naturalheight = this._thumbnails.reduce((accumulator, thumbnail/* , index*/) => {
const progress = 1 - thumbnail.collapse_fraction;
- //const height = (this._porthole.height * MAX_THUMBNAIL_SCALE + workspaceSpacing) * progress;
- const height = (tmbHeight) * progress;
+ const height = tmbHeight * progress;
return accumulator + height;
}, 0);
- //return themeNode.adjust_preferred_height(totalSpacing, naturalheight);
- // we need to calculate the height precisely as it need to align with the workspacesDisplay because of transition animation
- // This works perfectly for fullHD monitor, for some reason 5:4 aspect ratio monitor adds unnecessary pixels to the final height of the thumbnailsBox
- return [totalSpacing, naturalheight];
+ return themeNode.adjust_preferred_width(totalSpacing, naturalheight);
},
// removes extra space (extraWidth in the original function), we need the box as accurate as possible
// for precise app grid transition animation
- vfunc_allocate: function(box) {
+ vfunc_allocate(box) {
this.set_allocation(box);
- let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
+ let rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
- if (this._thumbnails.length == 0) // not visible
+ if (this._thumbnails.length === 0) // not visible
return;
let themeNode = this.get_theme_node();
@@ -416,16 +702,19 @@ var ThumbnailsBoxVertical = {
const portholeHeight = this._porthole.height;
const spacing = themeNode.get_length('spacing');
- const nWorkspaces = this._thumbnails.length;
+ /* const nWorkspaces = this._thumbnails.length;*/
// Compute the scale we'll need once everything is updated,
// unless we are currently transitioning
if (this._expandFraction === 1) {
- const totalSpacing = (nWorkspaces - 1) * spacing;
- const availableHeight = (box.get_height() - totalSpacing) / nWorkspaces;
+ // remove size "breathing" during adding/removing workspaces
+
+ /* const totalSpacing = (nWorkspaces - 1) * spacing;
+ const availableHeight = (box.get_height() - totalSpacing) / nWorkspaces; */
const hScale = box.get_width() / portholeWidth;
- const vScale = availableHeight / portholeHeight;
+ /* const vScale = availableHeight / portholeHeight;*/
+ const vScale = box.get_height() / portholeHeight;
const newScale = Math.min(hScale, vScale);
if (newScale !== this._targetScale) {
@@ -466,13 +755,20 @@ var ThumbnailsBoxVertical = {
let y = box.y1;
- if (this._dropPlaceholderPos == -1) {
+ if (this._dropPlaceholderPos === -1) {
this._dropPlaceholder.allocate_preferred_size(
...this._dropPlaceholder.get_position());
- Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
- this._dropPlaceholder.hide();
- });
+ if (shellVersion >= 44) {
+ const laters = global.compositor.get_laters();
+ laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.hide();
+ });
+ } else {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.hide();
+ });
+ }
}
let childBox = new Clutter.ActorBox();
@@ -486,7 +782,7 @@ var ThumbnailsBoxVertical = {
const x2 = x1 + thumbnailWidth;
if (i === this._dropPlaceholderPos) {
- let [, placeholderHeight] = this._dropPlaceholder.get_preferred_height(-1);
+ let [, placeholderHeight] = this._dropPlaceholder.get_preferred_width(-1);
childBox.x1 = x1;
childBox.x2 = x2;
@@ -500,9 +796,16 @@ var ThumbnailsBoxVertical = {
this._dropPlaceholder.allocate(childBox);
- Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
- this._dropPlaceholder.show();
- });
+ if (shellVersion >= 44) {
+ const laters = global.compositor.get_laters();
+ laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.show();
+ });
+ } else {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.show();
+ });
+ }
y += placeholderHeight + spacing;
}
@@ -559,51 +862,119 @@ var ThumbnailsBoxVertical = {
this._indicator.allocate(childBox);
},
- _updateShouldShow: function() {
- // set current workspace indicator border radius
- // here just 'cause it's easier than adding to init
- this._indicator.add_style_class_name('ws-tmb');
-
+ _updateShouldShow() {
const shouldShow = opt.SHOW_WS_TMB;
if (this._shouldShow === shouldShow)
return;
this._shouldShow = shouldShow;
this.notify('should-show');
- }
-}
+ },
+};
// ThumbnailsBox Horizontal
-var ThumbnailsBoxHorizontal = {
- get_preferred_custom_width: function(_forHeight) {
+const ThumbnailsBoxHorizontal = {
+ _getPlaceholderTarget(index, spacing, rtl) {
+ const workspace = this._thumbnails[index];
+
+ let targetX1;
+ let targetX2;
+
+ if (rtl) {
+ const baseX = workspace.x + workspace.width;
+ targetX1 = baseX - WORKSPACE_CUT_SIZE;
+ targetX2 = baseX + spacing + WORKSPACE_CUT_SIZE;
+ } else {
+ targetX1 = workspace.x - spacing - WORKSPACE_CUT_SIZE;
+ targetX2 = workspace.x + WORKSPACE_CUT_SIZE;
+ }
+
+ if (index === 0) {
+ if (rtl)
+ targetX2 -= spacing + WORKSPACE_CUT_SIZE;
+ else
+ targetX1 += spacing + WORKSPACE_CUT_SIZE;
+ }
+
+ if (index === this._dropPlaceholderPos) {
+ const placeholderWidth = this._dropPlaceholder.get_width() + spacing;
+ if (rtl)
+ targetX2 += placeholderWidth;
+ else
+ targetX1 -= placeholderWidth;
+ }
+
+ return [targetX1, targetX2];
+ },
+
+ _withinWorkspace(x, index, rtl) {
+ const length = this._thumbnails.length;
+ const workspace = this._thumbnails[index];
+
+ let workspaceX1 = workspace.x + WORKSPACE_CUT_SIZE;
+ let workspaceX2 = workspace.x + workspace.width - WORKSPACE_CUT_SIZE;
+
+ if (index === length - 1) {
+ if (rtl)
+ workspaceX1 -= WORKSPACE_CUT_SIZE;
+ else
+ workspaceX2 += WORKSPACE_CUT_SIZE;
+ }
+
+ return x > workspaceX1 && x <= workspaceX2;
+ },
+
+ get_preferred_custom_height(forWidth) {
+ let themeNode = this.get_theme_node();
+
+ forWidth = themeNode.adjust_for_width(forWidth);
+
+ let spacing = themeNode.get_length('spacing');
+ let nWorkspaces = this._thumbnails.length;
+ let totalSpacing = (nWorkspaces - 1) * spacing;
+
+ const avail = forWidth - totalSpacing;
+
+ let scale = (avail / nWorkspaces) / this._porthole.width;
+ // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE);
+
+ const height = Math.round(this._porthole.height * scale);
+ return themeNode.adjust_preferred_height(height, height);
+ },
+
+ get_preferred_custom_width(_forHeight) {
// Note that for getPreferredHeight/Width we cheat a bit and skip propagating
// the size request to our children because we know how big they are and know
// that the actors aren't depending on the virtual functions being called.
+ if (!this.visible)
+ return [0, 0];
+
let themeNode = this.get_theme_node();
let spacing = themeNode.get_length('spacing');
let nWorkspaces = this._thumbnails.length;
- let totalSpacing = (nWorkspaces - 1) * spacing;
+ // remove also left/right box padding from the total spacing
+ let totalSpacing = (nWorkspaces - 3) * spacing;
const ratio = this._porthole.height / this._porthole.width;
- const tmbWidth = (_forHeight - 2 * spacing) / ratio;
- const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail, index) => {
+ const tmbWidth = themeNode.adjust_for_height(_forHeight) / ratio;
+
+ const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail) => {
const progress = 1 - thumbnail.collapse_fraction;
const width = tmbWidth * progress;
return accumulator + width;
}, 0);
-
return themeNode.adjust_preferred_width(totalSpacing, naturalWidth);
},
vfunc_allocate(box) {
this.set_allocation(box);
- let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
+ let rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
- if (this._thumbnails.length == 0) // not visible
+ if (this._thumbnails.length === 0) // not visible
return;
let themeNode = this.get_theme_node();
@@ -613,15 +984,18 @@ var ThumbnailsBoxHorizontal = {
const portholeHeight = this._porthole.height;
const spacing = themeNode.get_length('spacing');
- const nWorkspaces = this._thumbnails.length;
+ /* const nWorkspaces = this._thumbnails.length; */
// Compute the scale we'll need once everything is updated,
// unless we are currently transitioning
if (this._expandFraction === 1) {
- const totalSpacing = (nWorkspaces - 1) * spacing;
+ // remove size "breathing" during adding/removing workspaces
+
+ /* const totalSpacing = (nWorkspaces - 1) * spacing;
const availableWidth = (box.get_width() - totalSpacing) / nWorkspaces;
- const hScale = availableWidth / portholeWidth;
+ const hScale = availableWidth / portholeWidth; */
+ const hScale = box.get_width() / portholeWidth;
const vScale = box.get_height() / portholeHeight;
const newScale = Math.min(hScale, vScale);
@@ -663,13 +1037,20 @@ var ThumbnailsBoxHorizontal = {
let x = box.x1;
- if (this._dropPlaceholderPos == -1) {
+ if (this._dropPlaceholderPos === -1) {
this._dropPlaceholder.allocate_preferred_size(
...this._dropPlaceholder.get_position());
- Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
- this._dropPlaceholder.hide();
- });
+ if (shellVersion >= 44) {
+ const laters = global.compositor.get_laters();
+ laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.hide();
+ });
+ } else {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.hide();
+ });
+ }
}
let childBox = new Clutter.ActorBox();
@@ -697,9 +1078,16 @@ var ThumbnailsBoxHorizontal = {
this._dropPlaceholder.allocate(childBox);
- Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
- this._dropPlaceholder.show();
- });
+ if (shellVersion >= 44) {
+ const laters = global.compositor.get_laters();
+ laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.show();
+ });
+ } else {
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._dropPlaceholder.show();
+ });
+ }
x += placeholderWidth + spacing;
}
@@ -756,5 +1144,5 @@ var ThumbnailsBoxHorizontal = {
this._indicator.allocate(childBox);
},
- _updateShouldShow: ThumbnailsBoxVertical._updateShouldShow
-}
+ _updateShouldShow: ThumbnailsBoxVertical._updateShouldShow,
+};
diff --git a/extensions/vertical-workspaces/workspacesView.js b/extensions/vertical-workspaces/lib/workspacesView.js
index 820ea6b..e3575f1 100644
--- a/extensions/vertical-workspaces/workspacesView.js
+++ b/extensions/vertical-workspaces/lib/workspacesView.js
@@ -1,7 +1,7 @@
/**
- * Vertical Workspaces
+ * V-Shell (Vertical Workspaces)
* workspacesView.js
- *
+ *
* @author GdH <G-dH@github.com>
* @copyright 2022 - 2023
* @license GPL-3.0
@@ -15,27 +15,30 @@ const { GObject, Clutter, Meta, St } = imports.gi;
const Main = imports.ui.main;
const Util = imports.misc.util;
const WorkspacesView = imports.ui.workspacesView;
-//const SecondaryMonitorDisplay = WorkspacesView.SecondaryMonitorDisplay;
-// first call of item defined using const in other module returns undefined
-WorkspacesView.SecondaryMonitorDisplay;
+// first reference to constant defined using const in other module returns undefined, the SecondaryMonitorDisplay const will remain empty and unused
+const SecondaryMonitorDisplay = WorkspacesView.SecondaryMonitorDisplay;
const ControlsState = imports.ui.overviewControls.ControlsState;
const FitMode = imports.ui.workspacesView.FitMode;
const SIDE_CONTROLS_ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME;
const Me = imports.misc.extensionUtils.getCurrentExtension();
-const SEARCH_WINDOWS_PREFIX = Me.imports.windowSearchProvider.prefix;
-const SEARCH_RECENT_FILES_PREFIX = Me.imports.recentFilesSearchProvider.prefix;
+const SEARCH_WINDOWS_PREFIX = Me.imports.lib.windowSearchProvider.prefix;
+const SEARCH_RECENT_FILES_PREFIX = Me.imports.lib.recentFilesSearchProvider.prefix;
-const _Util = Me.imports.util;
+const _Util = Me.imports.lib.util;
let _overrides;
let opt;
-
function update(reset = false) {
- if (_overrides) {
+ opt = Me.imports.lib.settings.opt;
+ opt.DESKTOP_CUBE_ENABLED = Main.extensionManager._enabledExtensions.includes('desktop-cube@schneegans.github.com');
+ const cubeSupported = opt.DESKTOP_CUBE_ENABLED && !opt.ORIENTATION && !opt.OVERVIEW_MODE;
+
+ // if desktop cube extension is enabled while V-Shell is loaded, removeAll() would override its code
+ if (_overrides && !cubeSupported) {
_overrides.removeAll();
global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1);
}
@@ -46,12 +49,14 @@ function update(reset = false) {
return;
}
- opt = Me.imports.settings.opt;
_overrides = new _Util.Overrides();
- _overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon);
+ if (!cubeSupported)
+ _overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon);
+
_overrides.addOverride('WorkspacesDisplay', WorkspacesView.WorkspacesDisplay.prototype, WorkspacesDisplay);
+ _overrides.addOverride('ExtraWorkspaceView', WorkspacesView.ExtraWorkspaceView.prototype, ExtraWorkspaceView);
if (opt.ORIENTATION) {
// switch internal workspace orientation in GS
@@ -62,8 +67,8 @@ function update(reset = false) {
}
}
-var WorkspacesViewCommon = {
- _getFirstFitSingleWorkspaceBox: function(box, spacing, vertical) {
+const WorkspacesViewCommon = {
+ _getFirstFitSingleWorkspaceBox(box, spacing, vertical) {
let [width, height] = box.get_size();
const [workspace] = this._workspaces;
@@ -74,8 +79,8 @@ var WorkspacesViewCommon = {
// Single fit mode implies centered too
let [x1, y1] = box.get_origin();
- const [, workspaceWidth] = workspace ? workspace.get_preferred_width(Math.floor(height)) : [,width];
- const [, workspaceHeight] = workspace ? workspace.get_preferred_height(workspaceWidth) : [,height];
+ const [, workspaceWidth] = workspace ? workspace.get_preferred_width(Math.floor(height)) : [0, width];
+ const [, workspaceHeight] = workspace ? workspace.get_preferred_height(workspaceWidth) : [0, height];
if (vertical) {
x1 += (width - workspaceWidth) / 2;
@@ -85,7 +90,7 @@ var WorkspacesViewCommon = {
x1 -= currentWorkspace * (workspaceWidth + spacing);
}
- const fitSingleBox = new Clutter.ActorBox({x1, y1});
+ const fitSingleBox = new Clutter.ActorBox({ x1, y1 });
fitSingleBox.set_size(workspaceWidth, workspaceHeight);
@@ -93,11 +98,12 @@ var WorkspacesViewCommon = {
},
// set spacing between ws previews
- _getSpacing: function(box, fitMode, vertical) {
+ _getSpacing(box, fitMode, vertical) {
const [width, height] = box.get_size();
const [workspace] = this._workspaces;
- if (!workspace) return;
+ if (!workspace)
+ return 0;
let availableSpace;
let workspaceSize;
@@ -117,12 +123,12 @@ var WorkspacesViewCommon = {
},
// this function has duplicate in OverviewControls so we use one function for both to avoid issues with syncing them
- _getFitModeForState: function(state) {
+ _getFitModeForState(state) {
return _getFitModeForState(state);
},
// normal view 0, spread windows 1
- _getWorkspaceModeForOverviewState: function(state) {
+ _getWorkspaceModeForOverviewState(state) {
switch (state) {
case ControlsState.HIDDEN:
@@ -130,14 +136,15 @@ var WorkspacesViewCommon = {
case ControlsState.WINDOW_PICKER:
return opt.WORKSPACE_MODE;
case ControlsState.APP_GRID:
- return ((this._monitorIndex !== global.display.get_primary_monitor() || !opt.WS_ANIMATION) && !opt.OVERVIEW_MODE) ? 1 : 0;
+ return (this._monitorIndex !== global.display.get_primary_monitor() || !opt.WS_ANIMATION) && !opt.OVERVIEW_MODE ? 1 : 0;
}
return 0;
},
- _updateVisibility: function() {
- let workspaceManager = global.workspace_manager;
+ _updateVisibility() {
+ // replaced in _updateWorkspacesState
+ /* let workspaceManager = global.workspace_manager;
let active = workspaceManager.get_active_workspace_index();
const fitMode = this._fitModeAdjustment.value;
@@ -146,17 +153,15 @@ var WorkspacesViewCommon = {
for (let w = 0; w < this._workspaces.length; w++) {
let workspace = this._workspaces[w];
- if (this._animating || this._gestureActive || !singleFitMode) {
- //workspace.show();
- } else {
+ if (this._animating || this._gestureActive || !singleFitMode)
+ workspace.show();
+ else
workspace.visible = Math.abs(w - active) <= opt.NUMBER_OF_VISIBLE_NEIGHBORS;
- }
-
- }
+ }*/
},
// disable scaling and hide inactive workspaces
- _updateWorkspacesState: function() {
+ _updateWorkspacesState() {
const adj = this._scrollAdjustment;
const fitMode = this._fitModeAdjustment.value;
@@ -168,12 +173,11 @@ var WorkspacesViewCommon = {
this._getWorkspaceModeForOverviewState(finalState),
progress);
-
- const currentMonitor = Main.layoutManager.primaryMonitor.index;
+ const primaryMonitor = Main.layoutManager.primaryMonitor.index;
// define the transition values here to save time in each ws
let scaleX, scaleY;
- if (opt.ORIENTATION) { //vertical 1 / horizontal 0
+ if (opt.ORIENTATION) { // vertical 1 / horizontal 0
scaleX = 1;
scaleY = 0.1;
} else {
@@ -181,134 +185,143 @@ var WorkspacesViewCommon = {
scaleY = 1;
}
+ const wsScrollProgress = adj.value % 1;
const secondaryMonitor = this._monitorIndex !== global.display.get_primary_monitor();
- const blockSecondaryAppGrid = opt.OVERVIEW_MODE && currentState >= 1;
+ const blockSecondaryAppGrid = opt.OVERVIEW_MODE && currentState > 1;
// Hide inactive workspaces
this._workspaces.forEach((w, index) => {
if (!(blockSecondaryAppGrid && secondaryMonitor))
w.stateAdjustment.value = workspaceMode;
- //w.stateAdjustment.value = workspaceMode;
const distanceToCurrentWorkspace = Math.abs(adj.value - index);
const scaleProgress = 1 - Math.clamp(distanceToCurrentWorkspace, 0, 1);
// if we disable workspaces that we can't or don't need to see, transition animations will be noticeably smoother
-
// only the current ws needs to be visible during overview transition animations
// and only current and adjacent ws when switching ws
- if (opt.WORKSPACE_MAX_SPACING > 340) { // large spacing - only one workspace needs to be visible at once in the overview
- w.visible = scaleProgress || ((currentState % 1) && !distanceToCurrentWorkspace);
-
- // horizontal orientation - 2 adjacent workspaces can be visible on the screen with the current one
- // in order to keep animations as smooth as possible, hide all ws that cannot/shouldn't be visible at the given time
- } else {
- //
- w.visible = w.monitorIndex !== currentMonitor || scaleProgress || (!opt.WS_ANIMATION && distanceToCurrentWorkspace < opt.NUMBER_OF_VISIBLE_NEIGHBORS)
- || (distanceToCurrentWorkspace < opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState <= ControlsState.WINDOW_PICKER
- && ((initialState < ControlsState.APP_GRID && finalState < ControlsState.APP_GRID))
- );
-
- // after transition from APP_GRID to WINDOW_PICKER state,
- // adjacent workspaces are hidden and we need them to show up
- // make them visible during animation can impact smoothness of the animation
- // so we show them after the animation finished, scaling animation will make impression that they move in from outside the monitor
- if (!w.visible && distanceToCurrentWorkspace <= opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState === ControlsState.WINDOW_PICKER) {
- w.scale_x = scaleX;
- w.scale_y = scaleY;
- w.visible = true;
- w.ease({
- duration: 100,
- scale_x: 1,
- scale_y: 1,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
- }
+ w.visible = (this._animating && wsScrollProgress && distanceToCurrentWorkspace <= (opt.NUMBER_OF_VISIBLE_NEIGHBORS + 1)) || scaleProgress === 1 ||
+ (opt.WORKSPACE_MAX_SPACING > 340 && distanceToCurrentWorkspace <= opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState === ControlsState.WINDOW_PICKER) ||
+ (this._monitorIndex !== primaryMonitor && distanceToCurrentWorkspace <= opt.NUMBER_OF_VISIBLE_NEIGHBORS) || (!opt.WS_ANIMATION && distanceToCurrentWorkspace < opt.NUMBER_OF_VISIBLE_NEIGHBORS) ||
+ (opt.WORKSPACE_MAX_SPACING < 340 && distanceToCurrentWorkspace <= opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState <= ControlsState.WINDOW_PICKER &&
+ ((initialState < ControlsState.APP_GRID && finalState < ControlsState.APP_GRID))
+ );
+
+ // after transition from APP_GRID to WINDOW_PICKER state,
+ // adjacent workspaces are hidden and we need them to show up
+ // make them visible during animation can impact smoothness of the animation
+ // so we show them after the animation finished, scaling animation will make impression that they move in from outside the monitor
+ if (!w.visible && distanceToCurrentWorkspace === 1 && initialState === ControlsState.APP_GRID && currentState === ControlsState.WINDOW_PICKER) {
+ w.scale_x = scaleX;
+ w.scale_y = scaleY;
+ w.visible = true;
+ w.ease({
+ duration: 100,
+ scale_x: 1,
+ scale_y: 1,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ } else if (!w.visible && distanceToCurrentWorkspace <= opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState === ControlsState.WINDOW_PICKER) {
+ w.set({
+ scale_x: 1,
+ scale_y: 1,
+ });
}
// force ws preview bg corner radiuses where GS doesn't do it
- if (opt.SHOW_WS_PREVIEW_BG && opt.OVERVIEW_MODE === 1 && distanceToCurrentWorkspace < 2) {
+ if (opt.SHOW_WS_PREVIEW_BG && opt.OVERVIEW_MODE === 1 && distanceToCurrentWorkspace < 2)
w._background._updateBorderRadius(Math.min(1, w._overviewAdjustment.value));
- }
+
// hide workspace background
- if (!opt.SHOW_WS_PREVIEW_BG && w._background.opacity) {
+ if (!opt.SHOW_WS_PREVIEW_BG && w._background.opacity)
w._background.opacity = 0;
- }
});
- }
-}
+ },
+};
// SecondaryMonitorDisplay Vertical
-var SecondaryMonitorDisplayVertical = {
- _getThumbnailParamsForState: function(state) {
+const SecondaryMonitorDisplayVertical = {
+ _getThumbnailParamsForState(state) {
- let opacity, scale, translation_x;
+ let opacity, scale, translationX;
switch (state) {
case ControlsState.HIDDEN:
opacity = 255;
scale = 1;
- translation_x = 0;
- if (!Main.layoutManager._startingUp && (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2)) {
- translation_x = this._thumbnails.width * (opt.SEC_WS_TMB_LEFT ? -1 : 1);
- }
+ translationX = 0;
+ if (!Main.layoutManager._startingUp && (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2))
+ translationX = this._thumbnails.width * (opt.SEC_WS_TMB_LEFT ? -1 : 1);
+
break;
case ControlsState.WINDOW_PICKER:
case ControlsState.APP_GRID:
opacity = 255;
scale = 1;
- translation_x = 0;
+ translationX = 0;
break;
default:
opacity = 255;
scale = 1;
- translation_x = 0;
+ translationX = 0;
break;
}
- return { opacity, scale, translation_x };
+ return { opacity, scale, translationX };
},
- _getThumbnailsWidth: function(box, spacing) {
- if (!this._thumbnails.visible)
+ _getThumbnailsWidth(box, spacing) {
+ if (opt.SEC_WS_TMB_HIDDEN)
return 0;
const [width, height] = box.get_size();
const { expandFraction } = this._thumbnails;
const [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(height - 2 * spacing);
+ let scaledWidth;
+ if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED)
+ scaledWidth = ((height - Main.panel.height) * opt.SEC_MAX_THUMBNAIL_SCALE) * (width / height);
+ else
+ scaledWidth = width * opt.SEC_MAX_THUMBNAIL_SCALE;
+
return Math.min(
thumbnailsWidth * expandFraction,
- width * opt.MAX_THUMBNAIL_SCALE);
+ Math.round(scaledWidth));
},
- _getWorkspacesBoxForState: function(state, box, padding, thumbnailsWidth, spacing) {
- //const { ControlsState } = OverviewControls;
+ _getWorkspacesBoxForState(state, box, padding, thumbnailsWidth, spacing) {
+ // const { ControlsState } = OverviewControls;
const workspaceBox = box.copy();
const [width, height] = workspaceBox.get_size();
+ let wWidth, wHeight, wsbX, wsbY, offset, yShift;
switch (state) {
case ControlsState.HIDDEN:
break;
case ControlsState.WINDOW_PICKER:
case ControlsState.APP_GRID:
- if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) {
+ if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)
break;
+
+ yShift = 0;
+ if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED) {
+ if (opt.PANEL_POSITION_TOP)
+ yShift = Main.panel.height;
+ else
+ yShift = -Main.panel.height;
}
- let wWidth = Math.round(width - thumbnailsWidth - 5 * spacing);
- let wHeight = Math.round(Math.min(wWidth / (width / height), height - 1.7 * padding));
- wWidth *= opt.WS_PREVIEW_SCALE;
- wHeight *= opt.WS_PREVIEW_SCALE;
+ wWidth = width - thumbnailsWidth - 5 * spacing;
+ wHeight = Math.min(wWidth / (width / height) - Math.abs(yShift), height - 4 * spacing);
+ wWidth = Math.round(wWidth * opt.SEC_WS_PREVIEW_SCALE);
+ wHeight = Math.round(wHeight * opt.SEC_WS_PREVIEW_SCALE);
- let wsbX;
- let offset = Math.round(width - thumbnailsWidth - wWidth) / 2;
- if (opt.SEC_WS_TMB_LEFT) {
+ offset = Math.round(width - thumbnailsWidth - wWidth) / 2;
+ if (opt.SEC_WS_TMB_LEFT)
wsbX = thumbnailsWidth + offset;
- } else {
+ else
wsbX = offset;
- }
- const wsbY = Math.round((height - wHeight) / 2);
+ wsbY = Math.round((height - wHeight - Math.abs(yShift)) / 2 + yShift);
workspaceBox.set_origin(wsbX, wsbY);
workspaceBox.set_size(wWidth, wHeight);
@@ -318,7 +331,7 @@ var SecondaryMonitorDisplayVertical = {
return workspaceBox;
},
- vfunc_allocate: function(box) {
+ vfunc_allocate(box) {
this.set_allocation(box);
const themeNode = this.get_theme_node();
@@ -332,7 +345,7 @@ var SecondaryMonitorDisplayVertical = {
let [, thumbnailsHeight] = this._thumbnails.get_preferred_custom_height(thumbnailsWidth);
thumbnailsHeight = Math.min(thumbnailsHeight, height - 2 * spacing);
- this._thumbnails.visible = opt.SHOW_WS_TMB;
+ this._thumbnails.visible = !opt.SEC_WS_TMB_HIDDEN;
if (this._thumbnails.visible) {
let wsTmbX;
if (opt.SEC_WS_TMB_LEFT) { // left
@@ -373,13 +386,12 @@ var SecondaryMonitorDisplayVertical = {
this._workspacesView.allocate(workspacesBox);
},
- _updateThumbnailVisibility: function() {
- if (opt.OVERVIEW_MODE2) {
+ _updateThumbnailVisibility() {
+ if (opt.OVERVIEW_MODE2)
this.set_child_above_sibling(this._thumbnails, null);
- }
- const visible = !(this._settings.get_boolean('workspaces-only-on-primary') ||
- opt.SEC_WS_TMB_POSITION === 3); // 3 - disabled
+
+ const visible = !opt.SEC_WS_TMB_HIDDEN;
if (this._thumbnails.visible === visible)
return;
@@ -396,15 +408,15 @@ var SecondaryMonitorDisplayVertical = {
});
},
- _updateThumbnailParams: function() {
+ _updateThumbnailParams() {
+ if (opt.SEC_WS_TMB_HIDDEN)
+ return;
+
// workaround for upstream bug - secondary thumbnails boxes don't catch 'showing' signal on the shell startup and don't populate the box with thumbnails
// the tmbBox contents is also destroyed when overview state adjustment gets above 1 when swiping gesture from window picker to app grid
- if (!this._thumbnails._thumbnails.length) {
+ if (!this._thumbnails._thumbnails.length)
this._thumbnails._createThumbnails();
- }
- if (!this._thumbnails.visible)
- return;
const { initialState, finalState, progress } =
this._overviewAdjustment.getStateTransitionParams();
@@ -412,113 +424,122 @@ var SecondaryMonitorDisplayVertical = {
const initialParams = this._getThumbnailParamsForState(initialState);
const finalParams = this._getThumbnailParamsForState(finalState);
- /*const opacity =
+ /* const opacity =
Util.lerp(initialParams.opacity, finalParams.opacity, progress);
const scale =
Util.lerp(initialParams.scale, finalParams.scale, progress);*/
// OVERVIEW_MODE 2 should animate dash and wsTmbBox only if WORKSPACE_MODE === 0 (windows not spread)
const animateOverviewMode2 = opt.OVERVIEW_MODE2 && !(finalState === 1 && opt.WORKSPACE_MODE);
- const translation_x = (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !(opt.OVERVIEW_MODE2)) || animateOverviewMode2))
- ? Util.lerp(initialParams.translation_x, finalParams.translation_x, progress)
+ const translationX = !Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2) || animateOverviewMode2)
+ ? Util.lerp(initialParams.translationX, finalParams.translationX, progress)
: 0;
this._thumbnails.set({
opacity: 255,
- //scale_x: scale,
- //scale_y: scale,
- translation_x,
+ // scale_x: scale,
+ // scale_y: scale,
+ translation_x: translationX,
});
},
- _updateWorkspacesView: function() {
+ _updateWorkspacesView() {
if (this._workspacesView)
this._workspacesView.destroy();
if (this._settings.get_boolean('workspaces-only-on-primary')) {
+ opt.SEC_WS_TMB_HIDDEN = true;
this._workspacesView = new WorkspacesView.ExtraWorkspaceView(
this._monitorIndex,
this._overviewAdjustment);
} else {
+ opt.SEC_WS_TMB_HIDDEN = !opt.SHOW_SEC_WS_TMB;
this._workspacesView = new WorkspacesView.WorkspacesView(
this._monitorIndex,
this._controls,
this._scrollAdjustment,
// Secondary monitors don't need FitMode.ALL since there is workspace switcher always visible
- //this._fitModeAdjustment,
+ // this._fitModeAdjustment,
new St.Adjustment({
actor: this,
- value: 0,//FitMode.SINGLE,
- lower: 0,//FitMode.SINGLE,
- upper: 0,//FitMode.SINGLE,
+ value: 0, // FitMode.SINGLE,
+ lower: 0, // FitMode.SINGLE,
+ upper: 0, // FitMode.SINGLE,
}),
- //secondaryOverviewAdjustment);
+ // secondaryOverviewAdjustment);
this._overviewAdjustment);
}
this.add_child(this._workspacesView);
this._thumbnails.opacity = 0;
- }
-}
+ },
+};
// SecondaryMonitorDisplay Horizontal
-var SecondaryMonitorDisplayHorizontal = {
- _getThumbnailParamsForState: function(state) {
- //const { ControlsState } = OverviewControls;
+const SecondaryMonitorDisplayHorizontal = {
+ _getThumbnailParamsForState(state) {
+ // const { ControlsState } = OverviewControls;
- let opacity, scale, translation_y;
+ let opacity, scale, translationY;
switch (state) {
case ControlsState.HIDDEN:
opacity = 255;
scale = 1;
- translation_y = 0;
- if (!Main.layoutManager._startingUp && (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2)) {
- translation_y = this._thumbnails.height * (opt.SEC_WS_TMB_TOP ? -1 : 1);
- }
+ translationY = 0;
+ if (!Main.layoutManager._startingUp && (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2))
+ translationY = this._thumbnails.height * (opt.SEC_WS_TMB_TOP ? -1 : 1);
+
break;
case ControlsState.WINDOW_PICKER:
case ControlsState.APP_GRID:
opacity = 255;
scale = 1;
- translation_y = 0;
+ translationY = 0;
break;
default:
opacity = 255;
scale = 1;
- translation_y = 0;
+ translationY = 0;
break;
}
- return { opacity, scale, translation_y };
+ return { opacity, scale, translationY };
},
- _getWorkspacesBoxForState: function(state, box, padding, thumbnailsHeight, spacing) {
- //const { ControlsState } = OverviewControls;
+ _getWorkspacesBoxForState(state, box, padding, thumbnailsHeight, spacing) {
+ // const { ControlsState } = OverviewControls;
const workspaceBox = box.copy();
const [width, height] = workspaceBox.get_size();
+ let wWidth, wHeight, wsbX, wsbY, offset, yShift;
switch (state) {
case ControlsState.HIDDEN:
break;
case ControlsState.WINDOW_PICKER:
case ControlsState.APP_GRID:
- if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) {
+ if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)
break;
+
+ yShift = 0;
+ if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED) {
+ if (opt.PANEL_POSITION_TOP)
+ yShift = Main.panel.height;
+ else
+ yShift = -Main.panel.height;
}
- let wHeight = Math.round(Math.min(height - thumbnailsHeight - 5 * spacing));
- let wWidth = Math.round(Math.min(wHeight * (width / height), width - 1.7 * padding));
- wWidth *= opt.WS_PREVIEW_SCALE;
- wHeight *= opt.WS_PREVIEW_SCALE;
+ wHeight = height - Math.abs(yShift) - (thumbnailsHeight ? thumbnailsHeight + 4 * spacing : padding);
+ wWidth = Math.min(wHeight * (width / height), width - 5 * spacing);
+ wWidth = Math.round(wWidth * opt.SEC_WS_PREVIEW_SCALE);
+ wHeight = Math.round(wHeight * opt.SEC_WS_PREVIEW_SCALE);
- let wsbY;
- let offset = Math.round((height - thumbnailsHeight - wHeight) / 2);
- if (opt.WS_TMB_TOP) {
+ offset = Math.round((height - thumbnailsHeight - wHeight - Math.abs(yShift)) / 2);
+ if (opt.SEC_WS_TMB_TOP)
wsbY = thumbnailsHeight + offset;
- } else {
+ else
wsbY = offset;
- }
- const wsbX = Math.round((width - wWidth) / 2);
+ wsbY += yShift;
+ wsbX = Math.round((width - wWidth) / 2);
workspaceBox.set_origin(wsbX, wsbY);
workspaceBox.set_size(wWidth, wHeight);
@@ -528,8 +549,8 @@ var SecondaryMonitorDisplayHorizontal = {
return workspaceBox;
},
- _getThumbnailsHeight: function(box) {
- if (!this._thumbnails.visible)
+ _getThumbnailsHeight(box) {
+ if (opt.SEC_WS_TMB_HIDDEN)
return 0;
const [width, height] = box.get_size();
@@ -537,10 +558,10 @@ var SecondaryMonitorDisplayHorizontal = {
const [thumbnailsHeight] = this._thumbnails.get_preferred_height(width);
return Math.min(
thumbnailsHeight * expandFraction,
- height * opt.MAX_THUMBNAIL_SCALE);
+ height * opt.SEC_MAX_THUMBNAIL_SCALE);
},
- vfunc_allocate: function(box) {
+ vfunc_allocate(box) {
this.set_allocation(box);
const themeNode = this.get_theme_node();
@@ -554,14 +575,14 @@ var SecondaryMonitorDisplayHorizontal = {
let [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(thumbnailsHeight);
thumbnailsWidth = Math.min(thumbnailsWidth, width - 2 * spacing);
- this._thumbnails.visible = opt.SHOW_WS_TMB;
+ this._thumbnails.visible = !opt.SEC_WS_TMB_HIDDEN;
if (this._thumbnails.visible) {
let wsTmbY;
- if (opt.SEC_WS_TMB_TOP) {
+ if (opt.SEC_WS_TMB_TOP)
wsTmbY = Math.round(spacing / 4);
- } else {
+ else
wsTmbY = Math.round(height - spacing / 4 - thumbnailsHeight);
- }
+
const childBox = new Clutter.ActorBox();
const availSpace = width - thumbnailsWidth - 2 * spacing;
@@ -595,15 +616,15 @@ var SecondaryMonitorDisplayHorizontal = {
_updateThumbnailVisibility: SecondaryMonitorDisplayVertical._updateThumbnailVisibility,
- _updateThumbnailParams: function() {
+ _updateThumbnailParams() {
+ if (opt.SEC_WS_TMB_HIDDEN)
+ return;
+
// workaround for upstream bug - secondary thumbnails boxes don't catch 'showing' signal on the shell startup and don't populate the box with thumbnails
// the tmbBox contents is also destroyed when overview state adjustment gets above 1 when swiping gesture from window picker to app grid
- if (!this._thumbnails._thumbnails.length) {
+ if (!this._thumbnails._thumbnails.length)
this._thumbnails._createThumbnails();
- }
- if (!this._thumbnails.visible)
- return;
const { initialState, finalState, progress } =
this._overviewAdjustment.getStateTransitionParams();
@@ -611,56 +632,79 @@ var SecondaryMonitorDisplayHorizontal = {
const initialParams = this._getThumbnailParamsForState(initialState);
const finalParams = this._getThumbnailParamsForState(finalState);
- /*const opacity =
+ /* const opacity =
Util.lerp(initialParams.opacity, finalParams.opacity, progress);
const scale =
Util.lerp(initialParams.scale, finalParams.scale, progress);*/
// OVERVIEW_MODE 2 should animate dash and wsTmbBox only if WORKSPACE_MODE === 0 (windows not spread)
const animateOverviewMode2 = opt.OVERVIEW_MODE2 && !(finalState === 1 && opt.WORKSPACE_MODE);
- const translation_y = (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !(opt.OVERVIEW_MODE2)) || animateOverviewMode2))
- ? Util.lerp(initialParams.translation_y, finalParams.translation_y, progress)
+ const translationY = !Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2) || animateOverviewMode2)
+ ? Util.lerp(initialParams.translationY, finalParams.translationY, progress)
: 0;
this._thumbnails.set({
opacity: 255,
- //scale_x: scale,
- //scale_y: scale,
- translation_y,
+ // scale_x: scale,
+ // scale_y: scale,
+ translation_y: translationY,
});
},
- _updateWorkspacesView: function() {
+ _updateWorkspacesView() {
if (this._workspacesView)
this._workspacesView.destroy();
if (this._settings.get_boolean('workspaces-only-on-primary')) {
+ opt.SEC_WS_TMB_HIDDEN = true;
this._workspacesView = new WorkspacesView.ExtraWorkspaceView(
this._monitorIndex,
this._overviewAdjustment);
} else {
+ opt.SEC_WS_TMB_HIDDEN = !opt.SHOW_SEC_WS_TMB;
this._workspacesView = new WorkspacesView.WorkspacesView(
this._monitorIndex,
this._controls,
this._scrollAdjustment,
// Secondary monitors don't need FitMode.ALL since there is workspace switcher always visible
- //this._fitModeAdjustment,
+ // this._fitModeAdjustment,
new St.Adjustment({
actor: this,
- value: 0,//FitMode.SINGLE,
- lower: 0,//FitMode.SINGLE,
- upper: 0,//FitMode.SINGLE,
+ value: 0, // FitMode.SINGLE,
+ lower: 0, // FitMode.SINGLE,
+ upper: 0, // FitMode.SINGLE,
}),
- //secondaryOverviewAdjustment);
+ // secondaryOverviewAdjustment);
this._overviewAdjustment);
}
this.add_child(this._workspacesView);
this._thumbnails.opacity = 0;
- }
-}
+ },
+};
+
+const ExtraWorkspaceView = {
+ _updateWorkspaceMode() {
+ const overviewState = this._overviewAdjustment.value;
-var WorkspacesDisplay = {
- _updateWorkspacesViews: function() {
+ const progress = Math.clamp(overviewState,
+ ControlsState.HIDDEN,
+ opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE ? ControlsState.HIDDEN : ControlsState.WINDOW_PICKER);
+
+ this._workspace.stateAdjustment.value = progress;
+
+ // force ws preview bg corner radiuses where GS doesn't do it
+ if (opt.SHOW_WS_PREVIEW_BG && opt.OVERVIEW_MODE === 1)
+ this._workspace._background._updateBorderRadius(Math.min(1, this._workspace._overviewAdjustment.value));
+
+
+ // hide workspace background
+ if (!opt.SHOW_WS_PREVIEW_BG && this._workspace._background.opacity)
+ this._workspace._background.opacity = 0;
+ },
+};
+
+const WorkspacesDisplay = {
+ _updateWorkspacesViews() {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
@@ -684,12 +728,12 @@ var WorkspacesDisplay = {
this._controls,
this._scrollAdjustment,
// Secondary monitors don't need FitMode.ALL since there is workspace switcher always visible
- //this._fitModeAdjustment,
+ // this._fitModeAdjustment,
new St.Adjustment({
actor: this,
- value: 0,//FitMode.SINGLE,
- lower: 0,//FitMode.SINGLE,
- upper: 0,//FitMode.SINGLE,
+ value: 0, // FitMode.SINGLE,
+ lower: 0, // FitMode.SINGLE,
+ upper: 0, // FitMode.SINGLE,
}),
this._overviewAdjustment);
Main.layoutManager.overviewGroup.add_actor(view);
@@ -699,7 +743,7 @@ var WorkspacesDisplay = {
}
},
- _onScrollEvent: function(actor, event) {
+ _onScrollEvent(actor, event) {
if (this._swipeTracker.canHandleScrollEvent(event))
return Clutter.EVENT_PROPAGATE;
@@ -707,35 +751,37 @@ var WorkspacesDisplay = {
return Clutter.EVENT_PROPAGATE;
if (this._workspacesOnlyOnPrimary &&
- this._getMonitorIndexForEvent(event) != this._primaryIndex)
+ this._getMonitorIndexForEvent(event) !== this._primaryIndex)
return Clutter.EVENT_PROPAGATE;
- const isShiftPressed = (event.get_state() & Clutter.ModifierType.SHIFT_MASK) != 0;
- /*const isCtrlPressed = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
- const isAltPressed = (event.get_state() & Clutter.ModifierType.MOD1_MASK) != 0;
- const noModifiersPressed = !(isCtrlPressed && isShiftPressed && isAltPressed);
-
- if (OVERVIEW_MODE2 && noModifiersPressed) {
- Main.overview.hide();
- return Clutter.EVENT_STOP;
- }*/
+ if (opt.PANEL_MODE === 1) {
+ const panelBox = Main.layoutManager.panelBox;
+ const [, y] = global.get_pointer();
+ if (y > panelBox.allocation.y1 && y < panelBox.allocation.y2)
+ return Clutter.EVENT_STOP;
+ }
- let direction = event.get_scroll_direction();
+ if (_Util.isShiftPressed()) {
+ let direction = _Util.getScrollDirection(event);
+ if (direction === null || (Date.now() - this._lastScrollTime) < 150)
+ return Clutter.EVENT_STOP;
+ this._lastScrollTime = Date.now();
- if (/*SHIFT_REORDERS_WS && */isShiftPressed) {
- if (direction === Clutter.ScrollDirection.UP) {
+ if (direction === Clutter.ScrollDirection.UP)
direction = -1;
- }
- else if (direction === Clutter.ScrollDirection.DOWN) {
+
+ else if (direction === Clutter.ScrollDirection.DOWN)
direction = 1;
- } else {
+ else
direction = 0;
- }
+
if (direction) {
- _reorderWorkspace(direction);
+ _Util.reorderWorkspace(direction);
// make all workspaces on primary monitor visible for case the new position is hidden
- Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => w.visible = true);
+ Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => {
+ w.visible = true;
+ });
return Clutter.EVENT_STOP;
}
}
@@ -743,24 +789,30 @@ var WorkspacesDisplay = {
return Main.wm.handleWorkspaceScroll(event);
},
- _onKeyPressEvent: function(actor, event) {
+ _onKeyPressEvent(actor, event) {
const symbol = event.get_key_symbol();
- /*const { ControlsState } = OverviewControls;
+ /* const { ControlsState } = OverviewControls;
if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER && symbol !== Clutter.KEY_space)
return Clutter.EVENT_PROPAGATE;*/
- /*if (!this.reactive)
- return Clutter.EVENT_PROPAGATE;**/
- const isCtrlPressed = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
- const isShiftPressed = (event.get_state() & Clutter.ModifierType.SHIFT_MASK) != 0;
- const isAltPressed = (event.get_state() & Clutter.ModifierType.MOD1_MASK) != 0;
+ /* if (!this.reactive)
+ return Clutter.EVENT_PROPAGATE; */
const { workspaceManager } = global;
const vertical = workspaceManager.layout_rows === -1;
const rtl = this.get_text_direction() === Clutter.TextDirection.RTL;
+ const state = this._overviewAdjustment.value;
let which;
switch (symbol) {
- /*case Clutter.KEY_Return:*/
+ case Clutter.KEY_Return:
+ case Clutter.KEY_KP_Enter:
+ if (_Util.isCtrlPressed()) {
+ Main.ctrlAltTabManager._items.forEach(i => {
+ if (i.sortGroup === 1 && i.name === 'Dash')
+ Main.ctrlAltTabManager.focusGroup(i);
+ });
+ }
+ return Clutter.EVENT_STOP;
case Clutter.KEY_Page_Up:
if (vertical)
which = Meta.MotionDirection.UP;
@@ -784,30 +836,57 @@ var WorkspacesDisplay = {
which = workspaceManager.n_workspaces - 1;
break;
case Clutter.KEY_space:
- if (isCtrlPressed && isShiftPressed) {
+ if (_Util.isCtrlPressed() && _Util.isShiftPressed()) {
_Util.openPreferences();
- } else if (isAltPressed) {
- Main.ctrlAltTabManager._items.forEach(i => {if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i)});
- } else if (opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED && isCtrlPressed) {
+ } else if (_Util.isAltPressed()) {
+ Main.ctrlAltTabManager._items.forEach(i => {
+ if (i.sortGroup === 1 && i.name === 'Dash')
+ Main.ctrlAltTabManager.focusGroup(i);
+ });
+ } else if (opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED && _Util.isCtrlPressed()) {
_Util.activateSearchProvider(SEARCH_RECENT_FILES_PREFIX);
- } else if (opt.WINDOW_SEARCH_PROVIDER_ENABLED/* && SEARCH_WINDOWS_SPACE*/) {
+ } else if (opt.WINDOW_SEARCH_PROVIDER_ENABLED) {
_Util.activateSearchProvider(SEARCH_WINDOWS_PREFIX);
}
+
return Clutter.EVENT_STOP;
case Clutter.KEY_Down:
case Clutter.KEY_Left:
case Clutter.KEY_Right:
case Clutter.KEY_Up:
+ case Clutter.KEY_Tab:
if (Main.overview._overview._controls._searchController.searchActive) {
Main.overview.searchEntry.grab_key_focus();
- } else /*if (OVERVIEW_MODE && !WORKSPACE_MODE)*/ {
- Main.ctrlAltTabManager._items.forEach(i => {if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i)});
+ } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && state === 1) {
+ // expose windows by "clicking" on ws thumbnail
+ // in this case overview stateAdjustment will be used for transition
+ Main.overview._overview.controls._thumbnailsBox._activateThumbnailAtPoint(0, 0, global.get_current_time(), true);
+ Main.ctrlAltTabManager._items.forEach(i => {
+ if (i.sortGroup === 1 && i.name === 'Windows')
+ Main.ctrlAltTabManager.focusGroup(i);
+ });
+ } else if (opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE && state === 1) {
+ // expose windows for OVERVIEW_MODE 1
+ const adjustment = this._workspacesViews[0]._workspaces[global.workspace_manager.get_active_workspace().index()]._background._stateAdjustment;
+ opt.WORKSPACE_MODE = 1;
+ _Util.exposeWindows(adjustment, true);
+ } else {
+ if (state === 2)
+ return Clutter.EVENT_PROPAGATE;
+ Main.ctrlAltTabManager._items.forEach(i => {
+ if (i.sortGroup === 1 && i.name === 'Windows')
+ Main.ctrlAltTabManager.focusGroup(i);
+ });
}
+
return Clutter.EVENT_STOP;
default:
return Clutter.EVENT_PROPAGATE;
}
+ if (state === 2)
+ return Clutter.EVENT_PROPAGATE;
+
let ws;
if (which < 0)
// Negative workspace numbers are directions
@@ -816,17 +895,19 @@ var WorkspacesDisplay = {
// Otherwise it is a workspace index
ws = workspaceManager.get_workspace_by_index(which);
- if (/*SHIFT_REORDERS_WS && */isShiftPressed) {
+ if (_Util.isShiftPressed()) {
let direction;
if (which === Meta.MotionDirection.UP || which === Meta.MotionDirection.LEFT)
direction = -1;
else if (which === Meta.MotionDirection.DOWN || which === Meta.MotionDirection.RIGHT)
direction = 1;
if (direction)
- _reorderWorkspace(direction);
+ _Util.reorderWorkspace(direction);
// make all workspaces on primary monitor visible for case the new position is hidden
- Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => w.visible = true);
- return Clutter.EVENT_STOP;
+ Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => {
+ w.visible = true;
+ });
+ return Clutter.EVENT_STOP;
}
if (ws)
@@ -834,7 +915,7 @@ var WorkspacesDisplay = {
return Clutter.EVENT_STOP;
},
-}
+};
// same copy of this function should be available in OverviewControls and WorkspacesView
function _getFitModeForState(state) {
@@ -851,14 +932,3 @@ function _getFitModeForState(state) {
return FitMode.SINGLE;
}
}
-
-// ------------------ Reorder Workspaces - callback for Dash and workspacesDisplay -----------------------------------
-
-function _reorderWorkspace(direction = 0) {
- let activeWs = global.workspace_manager.get_active_workspace();
- let activeWsIdx = activeWs.index();
- let targetIdx = activeWsIdx + direction;
- if (targetIdx > -1 && targetIdx < (global.workspace_manager.get_n_workspaces())) {
- global.workspace_manager.reorder_workspace(activeWs, targetIdx);
- }
-}