diff options
Diffstat (limited to 'extensions')
37 files changed, 9343 insertions, 4122 deletions
diff --git a/extensions/44/vertical-workspaces/CHANGELOG.md b/extensions/44/vertical-workspaces/CHANGELOG.md index 0beb20c..e8fd632 100644 --- a/extensions/44/vertical-workspaces/CHANGELOG.md +++ b/extensions/44/vertical-workspaces/CHANGELOG.md @@ -1,4 +1,100 @@ ## Changelog: +### v45.2 for GNOME 45.2+, v44.10 for GNOME 42-44 (2023-12-02) +**Added:** +- *Dash* option *Isolate Workspaces* on *Behavior* tab +- *Brightness for Search View* option allows adjusting background wallpaper brightness in overview search view +- *Extensions Search Provider* module allows to search for extensions from the overview, open their settings and enable or disable them + + +### v45.1 for GNOME 45.1+ (EGO 2023-11-25) v44.9 for GNOME 42-44 (not released) +**Added:** +- *Window Thumbnail - PIP* option in app icon menu and as an click actions for Window Preview +- Workspace switcher options *Wraparound* and *Ignore Last (empty)* +- *Overlay key (Super)* and *Hot Corner* actions offer different overview modes independent on global *Overview Mode* +- Workspace thumbnails background without wallpaper is now semi-transparent to match other overview elements +- App Grid sorting options allow alphabetical order with folders + +**Fixed:** +- App grid has less rows than it should +- Many minor fixes that reflect gnome-shell development and backports +- Centered app folder position on multi-monitor system +- Window can't be activated using touchscreen +- Setting background wallpaper too early on startup can crash Wayland session + +**Other changes:** +- Since GNOME 45 V-Shell has 2 versions - one for GS 45 and the second for GS 42-44. Versioning no longer depends on EGO's upload counter +- Dash light style background opacity is not limited +- Refactored Recent Files Search Provider and other modules +- Settings window - ComboBox replaced with (finally fixed) DropDown + + +### v37 (2023-07-01) +**Fixed:** +- False detections of conflicting extensions + + +### v36 (2023-07-01) (35/34/33/32/31 skipped due to issues with extensions.gnome.org) +**Added:** +- Option *Fix New Windows Not In Focus* on *Misc* tab + +**Fixed:** +- Improved compatibility with Dash to Dock extension - hidden dock in the overview, broken layout after startup +- Compatibility with Hide Top Bar extension +- App search provider ignores non-localized app names +- App folders grid dimensions wrong if set to *Adaptive* and folder icon is set to a fixed size +- Dash and active app folder icons running indicator position +- Blur/Brightness transitions in static overview mode +- Hot corner edge barrier can be active even if hot corner is disabled +- Disabling dash module does not reset dash position to default + +**Other changes:** +- Removed css class reducing Quick Settings buttons height in GNOME 44 + + +### v30 (2023-06-09) +**Added:** +- Dash option - Click Behavior: *Prefer Current Workspace* - opens a new window if app not present on the active workspace +- Window search provider sorting options +- Esc key behavior options +- Window preview - middle and secondary mouse button behavior options, close button can be hidden +- GNOME 3 vertical dash style is now optional +- Window preview title position option +- Light dash background option +- Remove app folder button in folder dialog +- *Updating V-Shell* banner appears during updating V-Shell settings when settings window is irresponsive +- Dutch translation by @Vistaus + +**Fixed:** +- Dash icon scroll action conflicts with Dash to Dock +- Open new window by middle click on app icon or Ctrl+Enter doesn't work +- Dash icon label can extend to the adjacent display +- WindowPreview module not updated when "always-activate-selected-window" changed +- App folder dialog position if secondary monitor connected +- App folder dialog sizing and positioning +- Background brightness in search view reduced independently to avoid unreadable text and consistent style +- Compatibility with Burn My Windows - freeze after screen unlocked, or extensions re-enabled +- Window and Recent files search providers modes not isolated well from results of other providers +- Recent file search provider results sorting +- App grid icons with multi-line label move on hover when label expands +- Search view animation skipped id triggered from app grid state +- DING desktop icons not visible during static background workspace animation + + +**Other changes:** +- Added `unlock-dialog` session mode to avoid unnecessary system load when using screen lock +- App Grid refactored, added transparent app folder dialogs on clean background +- Search view transparency and fixed background brightness in classic overview +- Search view in static workspace overview with full opacity and close to default style +- Settings window - Profiles tab moved at first position, Dash icons position options moved back to layout +- Updated default profiles + + +### v29 (2023-04-11) +**Fixed:** +- Window switcher/highlighter logic when scrolling over an dash icon +- Unhandled promise rejection warnings on GS 43+ + + ### v28 (2023-04-06) **Added:** - App Grid - vertical app folder orientation diff --git a/extensions/44/vertical-workspaces/README.md b/extensions/44/vertical-workspaces/README.md index ebff9df..449bafa 100644 --- a/extensions/44/vertical-workspaces/README.md +++ b/extensions/44/vertical-workspaces/README.md @@ -2,7 +2,7 @@ A GNOME Shell extension that lets you customize your GNOME Shell UX to suit your workflow, whether you like horizontally or vertically stacked workspaces. -Currently supported GNOME versions: 42, 43, 44 +Currently supported GNOME versions: 42 - 45 [<img alt="" height="100" src="https://raw.githubusercontent.com/andyholmes/gnome-shell-extensions-badge/master/get-it-on-ego.svg?sanitize=true">](https://extensions.gnome.org/extension/5177/vertical-workspaces/) @@ -17,6 +17,7 @@ Currently supported GNOME versions: 42, 43, 44 - Support for secondary monitors, workspace thumbnails can be placed on the opposite side than on the primary monitor - Wallpaper background with adjustable blur effect and brightness in the overview - Custom Dash icon size and on-click/scroll behavior +- Optional workspace isolated Dash - Dash background transparency and corner radius adjustments - Adjustable app grid icon size, number of columns and rows, content, optional active and draggable icons in folder preview in optional 3x3 grid - Custom search view width, app results icons size and number of result lists rows, improved app search @@ -26,12 +27,14 @@ Currently supported GNOME versions: 42, 43, 44 - Control over transition animations, including speed - Window search provider with *Space* hotkey allows quick window navigation - Recent files search provider with *Ctrl + Space* hotkey +- Extensions search provider with *Ctrl + Shift + Space* hotkey allows to search for extensions, open their settings and enable or disable them - Reorder workspaces in overview using *Shift + Scroll* or *Shift + Page Up/Down* - Adds *Force Quit*, *Close Windows on Current Workspace* and *Move Windows to Current Workspace* items to app icon menu. The latter action can be activated using *Shift + click* on app icon - Change notification banners and OSD popups position - Window attention handler options can activate the attention-demanding window immediately or silence its notification - Optional position of the hot corner that can follow the dash and expand to hot edge - Super key double-press options +- Window thumbnail (PIP) options allows you to create scaled down clone of the window by clicking on its preview in the overview (secondary mouse buttons or window preview icon) ## Changelog [CHANGELOG.md](CHANGELOG.md) @@ -51,12 +54,21 @@ Install the extension (`--force` switch needs to be used only if some version of gnome-extensions install --force vertical-workspaces@G-dH.github.com.zip ### Installation from GitHub repository -The most recent version in the repository is the one I'm currently using and developing on my own systems, problems may occur, but usually nothing serious. The repository version may change often and doesn't updates automatically on your system, but once the stable release shows up on extensions.gnome.org, it should be updated automatically from there. If you want to help me, use this latest version and report bugs. -You may need to install `git`, `gettext` and `glib2.0` for successful installation. +The most recent version in the repository is the one I'm currently using and developing on my own systems, problems may occur, but usually nothing serious. The repository version may change often and doesn't updates automatically on your system. If you want to help me, use this latest version and report bugs. +You may need to install `git`, `make`, `gettext` and `glib2.0` for successful installation. Navigate to the directory you want to download the source code and execute following commands in the terminal: +GNOME 45: + + git clone https://github.com/G-dH/vertical-workspaces.git + cd vertical-workspaces + make install + +GNOME 42 - 44: + git clone https://github.com/G-dH/vertical-workspaces.git cd vertical-workspaces + git checkout gnome-42-44 make install If you get `Can't recursively copy directory` error, take a look at issue #51. diff --git a/extensions/44/vertical-workspaces/extension.js b/extensions/44/vertical-workspaces/extension.js index 0a22994..911d7dd 100644 --- a/extensions/44/vertical-workspaces/extension.js +++ b/extensions/44/vertical-workspaces/extension.js @@ -10,655 +10,784 @@ 'use strict'; -const { GLib, Shell, St } = imports.gi; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const St = imports.gi.St; const Main = imports.ui.main; - -const Util = imports.misc.util; -const Background = imports.ui.background; +const Search = imports.ui.search; +const Workspace = imports.ui.workspace; const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; -const _Util = Me.imports.lib.util; - -const WindowSearchProvider = Me.imports.lib.windowSearchProvider; -const RecentFilesSearchProvider = Me.imports.lib.recentFilesSearchProvider; -const LayoutOverride = Me.imports.lib.layout; -const AppDisplayOverride = Me.imports.lib.appDisplay; -const WorkspaceThumbnailOverride = Me.imports.lib.workspaceThumbnail; -const WorkspaceOverride = Me.imports.lib.workspace; -const WorkspacesViewOverride = Me.imports.lib.workspacesView; -const WindowPreviewOverride = Me.imports.lib.windowPreview; -const IconGridOverride = Me.imports.lib.iconGrid; -const WorkspaceAnimationOverride = Me.imports.lib.workspaceAnimation; -const WindowManagerOverride = Me.imports.lib.windowManager; -const OverviewOverride = Me.imports.lib.overview; -const OverviewControlsOverride = Me.imports.lib.overviewControls; -const SwipeTrackerOverride = Me.imports.lib.swipeTracker; -const WorkspaceSwitcherPopupOverride = Me.imports.lib.workspaceSwitcherPopup; -const SearchOverride = Me.imports.lib.search; -const PanelOverride = Me.imports.lib.panel; -const DashOverride = Me.imports.lib.dash; -const WindowAttentionHandlerOverride = Me.imports.lib.windowAttentionHandler; -const AppFavoritesOverride = Me.imports.lib.appFavorites; -const MessageTrayOverride = Me.imports.lib.messageTray; -const OsdWindowOverride = Me.imports.lib.osdWindow; -const OverlayKey = Me.imports.lib.overlayKey; +const MyExtension = ExtensionUtils.getCurrentExtension(); +let Me; +// gettext +let _; let opt; -let _bgManagers; - -let _enabled; -let _resetExtensionIfEnabled; -let _prevDash; - -let _showingOverviewConId; -let _monitorsChangedSigId; -let _loadingProfileTimeoutId; -let _watchDockSigId; - -let _resetTimeoutId; - -let _enableTimeoutId = 0; -let _sessionLockActive = false; - - function init() { ExtensionUtils.initTranslations(); + return new Extension(); } -function enable() { - // globally readable flag for other extensions - global.verticalWorkspacesEnabled = true; - - _enableTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 400, - () => { - activateVShell(); - // unlock after modules update to avoid unnecessary appGrid rebuild - _sessionLockActive = Main.sessionMode.isLocked; - log(`${Me.metadata.name}: enabled`); - _enableTimeoutId = 0; - return GLib.SOURCE_REMOVE; - } - ); -} +class Extension { + _init() { + Me = {}; -function disable() { - _sessionLockActive = Main.sessionMode.isLocked; - if (_enableTimeoutId) { - GLib.source_remove(_enableTimeoutId); - _enableTimeoutId = 0; - } else { - resetVShell(); - } + Me.shellVersion = parseFloat(imports.misc.config.PACKAGE_VERSION); + Me.imports = MyExtension.imports; + Me.metadata = MyExtension.metadata; + Me.gSettings = ExtensionUtils.getSettings(Me.metadata['settings-schema']); + Me.Settings = MyExtension.imports.lib.settings; + Me.gettext = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; + Me.Util = MyExtension.imports.lib.util; - global.verticalWorkspacesEnabled = undefined; - log(`${Me.metadata.name}: ${_sessionLockActive ? 'suspended' : 'disabled'}`); -} + Me.Modules = this._importModules(); + Me.moduleList = this._getModuleList(); -// ------------------------------------------------------------------------------------------ + Me.WSP_PREFIX = Me.Modules.windowSearchProviderModule._PREFIX; + Me.RFSP_PREFIX = Me.Modules.recentFilesSearchProviderModule._PREFIX; + Me.ESP_PREFIX = Me.Modules.extensionsSearchProviderModule._PREFIX; -function activateVShell() { - _enabled = true; + Me.opt = new Me.Settings.Options(Me); + _ = Me.gettext; + opt = Me.opt; - _bgManagers = []; + Me.Util.init(Me); - Settings.opt = new Settings.Options(); - opt = Settings.opt; + Me.moduleList.forEach(module => { + Me.Modules[module] = new Me.Modules[module](Me); + }); - _updateSettings(); + Me.repairOverrides = this._repairOverrides; + } - opt.connect('changed', _updateSettings); + _importModules() { + return { + appDisplayModule: Me.imports.lib.appDisplay.AppDisplayModule, + appFavoritesModule: Me.imports.lib.appFavorites.AppFavoritesModule, + dashModule: Me.imports.lib.dash.DashModule, + iconGridModule: Me.imports.lib.iconGrid.IconGridModule, + layoutModule: Me.imports.lib.layout.LayoutModule, + messageTrayModule: Me.imports.lib.messageTray.MessageTrayModule, + osdWindowModule: Me.imports.lib.osdWindow.OsdWindowModule, + overviewModule: Me.imports.lib.overview.OverviewModule, + overlayKeyModule: Me.imports.lib.overlayKey.OverlayKeyModule, + overviewControlsModule: Me.imports.lib.overviewControls.OverviewControlsModule, + panelModule: Me.imports.lib.panel.PanelModule, + searchModule: Me.imports.lib.search.SearchModule, + searchControllerModule: Me.imports.lib.searchController.SearchControllerModule, + swipeTrackerModule: Me.imports.lib.swipeTracker.SwipeTrackerModule, + windowAttentionHandlerModule: Me.imports.lib.windowAttentionHandler.WindowAttentionHandlerModule, + windowManagerModule: Me.imports.lib.windowManager.WindowManagerModule, + windowPreviewModule: Me.imports.lib.windowPreview.WindowPreviewModule, + workspaceAnimationModule: Me.imports.lib.workspaceAnimation.WorkspaceAnimationModule, + workspaceModule: Me.imports.lib.workspace.WorkspaceModule, + workspaceSwitcherPopupModule: Me.imports.lib.workspaceSwitcherPopup.WorkspaceSwitcherPopupModule, + workspaceThumbnailModule: Me.imports.lib.workspaceThumbnail.WorkspaceThumbnailModule, + workspacesViewModule: Me.imports.lib.workspacesView.WorkspacesViewModule, + windowSearchProviderModule: Me.imports.lib.windowSearchProvider.WindowSearchProviderModule, + winTmbModule: Me.imports.lib.winTmb.WinTmbModule, + recentFilesSearchProviderModule: Me.imports.lib.recentFilesSearchProvider.RecentFilesSearchProviderModule, + extensionsSearchProviderModule: Me.imports.lib.extensionsSearchProvider.ExtensionsSearchProviderModule, + }; + } - _updateOverrides(); + _getModuleList() { + return Object.keys(Me.Modules); + } - _prevDash = {}; - const dash = Main.overview.dash; - _prevDash.dash = dash; - _prevDash.position = dash.position; + _cleanGlobals() { + Me = null; + opt = null; + _ = null; + } - _monitorsChangedSigId = Main.layoutManager.connect('monitors-changed', () => _resetExtension(2000)); + enable() { + this._init(); + // flag for Util.getEnabledExtensions() + Me.extensionsLoadIncomplete = Main.layoutManager._startingUp; - // static bg animations conflict with startup animation - // enable it on first hiding from the overview and disconnect the signal - _showingOverviewConId = Main.overview.connect('showing', _onShowingOverview); + this._activateVShell(); + Me.extensionsLoadIncomplete = false; + console.debug(`${Me.metadata.name}: enabled`); + } - // switch PageUp/PageDown workspace switcher shortcuts - _switchPageShortcuts(); - _setStaticBackground(); + // Reason for using "unlock-dialog" session mode: + // Updating the "appDisplay" content every time the screen is locked/unlocked takes quite a lot of time and affects the user experience. + disable() { + this._removeVShell(); + this._disposeModules(); - // fix for upstream bug - overview always shows workspace 1 instead of the active one after restart - Main.overview._overview.controls._workspaceAdjustment.set_value(global.workspace_manager.get_active_workspace_index()); + // If Dash to Dock is enabled, disabling V-Shell can end in broken overview + Main.overview.hide(); - // if Dash to Dock detected force enable "Fix for DtD" option - if (_Util.dashIsDashToDock()) { - opt.set('fixUbuntuDock', true); - _fixUbuntuDock(true); - } else { - _fixUbuntuDock(opt.get('fixUbuntuDock')); - } -} + console.debug(`${Me.metadata.name}: disabled`); -function resetVShell() { - _enabled = 0; + this._cleanGlobals(); + } - _fixUbuntuDock(false); + _disposeModules() { + Me.opt.destroy(); + Me.opt = null; - const reset = true; - _updateOverrides(reset); + for (let module of Me.moduleList) { + if (!Me.Modules[module].moduleEnabled) + Me.Modules[module].cleanGlobals(); + } - if (_monitorsChangedSigId) { - Main.layoutManager.disconnect(_monitorsChangedSigId); - _monitorsChangedSigId = 0; + Me.Util.cleanGlobals(); + Me.Modules = null; } - _prevDash = null; + _activateVShell() { + this._enabled = true; - // switch PageUp/PageDown workspace switcher shortcuts - _switchPageShortcuts(); + this._originalGetNeighbor = Meta.Workspace.prototype.get_neighbor; - _setStaticBackground(reset); + this._removeTimeouts(); + this._timeouts = {}; - // remove any position offsets from dash and ws thumbnails - if (!_Util.dashNotDefault()) { - Main.overview.dash.translation_x = 0; - Main.overview.dash.translation_y = 0; - } - Main.overview._overview._controls._thumbnailsBox.translation_x = 0; - Main.overview._overview._controls._thumbnailsBox.translation_y = 0; - Main.overview._overview._controls._searchEntryBin.translation_y = 0; + // load VShell configuration + this._updateSettings(); - Main.overview._overview._controls.set_child_above_sibling(Main.overview._overview._controls._workspacesDisplay, null); + // activate all enabled VShell modules + this._updateOverrides(); - if (_showingOverviewConId) { - Main.overview.disconnect(_showingOverviewConId); - _showingOverviewConId = 0; - } + // connect signals to help VShell adapt to changes in DE configuration + this._updateConnections(); - if (_loadingProfileTimeoutId) { - GLib.source_remove(_loadingProfileTimeoutId); - _loadingProfileTimeoutId = 0; - } + // switch PageUp/PageDown workspace switcher shortcuts + this._switchPageShortcuts(); - St.Settings.get().slow_down_factor = 1; + // if Dash to Dock detected force enable "Fix for DtD" option + this._updateFixDashToDockOption(); - Main.overview.dash._background.set_style(''); + // update overview background wallpaper if enabled, but don't set it too early on session startup + // because it crashes wayland + if (!Main.layoutManager._startingUp || Meta.is_restart()) + Main.overview._overview.controls._setBackground(); - opt.destroy(); - opt = null; -} + this._updateSettingsConnection(); -function _updateOverrides(reset = false) { - WorkspacesViewOverride.update(reset); - WorkspaceThumbnailOverride.update(reset); - OverviewOverride.update(reset); - OverviewControlsOverride.update(reset); + // store dash _workId so we will be able to detect replacement when entering overview + this._storeDashId(); - WorkspaceOverride.update(reset); - WindowPreviewOverride.update(reset); - WindowManagerOverride.update(reset); + // workaround for upstream bug - overview always shows workspace 1 instead of the active one after restart + this._setInitialWsIndex(); + } - LayoutOverride.update(reset); - DashOverride.update(reset); - PanelOverride.update(reset); + _removeVShell() { + this._enabled = false; - WorkspaceAnimationOverride.update(reset); - WorkspaceSwitcherPopupOverride.update(reset); + const reset = true; + this._removeTimeouts(); - SwipeTrackerOverride.update(reset); + this._removeConnections(); + Main.overview._overview.controls._setBackground(reset); - SearchOverride.update(reset); - WindowSearchProvider.update(reset); - RecentFilesSearchProvider.update(reset); + // remove changes mede by VShell modules + this._updateOverrides(reset); - // don't rebuild app grid on every screen lock - if (!_sessionLockActive) { - // IconGrid needs to be patched before AppDisplay - IconGridOverride.update(reset); - AppDisplayOverride.update(reset); - } + // switch PageUp/PageDown workspace switcher shortcuts + this._switchPageShortcuts(); - WindowAttentionHandlerOverride.update(reset); - AppFavoritesOverride.update(reset); - MessageTrayOverride.update(reset); - OsdWindowOverride.update(reset); - OverlayKey.update(reset); -} + // remove any position offsets from dash and ws thumbnails + if (!Me.Util.dashNotDefault()) { + Main.overview.dash.translation_x = 0; + Main.overview.dash.translation_y = 0; + } + Main.overview._overview._controls._thumbnailsBox.translation_x = 0; + Main.overview._overview._controls._thumbnailsBox.translation_y = 0; + Main.overview._overview._controls._searchEntryBin.translation_y = 0; + Main.overview._overview._controls.set_child_above_sibling(Main.overview._overview._controls._workspacesDisplay, null); + // restore default animation speed + St.Settings.get().slow_down_factor = 1; + + // restore default dash background style + Main.overview.dash._background.set_style(''); + // hide status message if shown + this._showStatusMessage(false); + this._prevDash = null; + + Meta.Workspace.prototype.get_neighbor = this._originalGetNeighbor; + } -function _onShowingOverview() { - // store pointer X coordinate for OVERVIEW_MODE 1 window spread - if mouse pointer is steady, don't spread - opt.showingPointerX = global.get_pointer()[0]; + _removeTimeouts() { + if (this._timeouts) { + Object.values(this._timeouts).forEach(id => { + if (id) + GLib.source_remove(id); + }); + } + this._timeouts = null; + } - if (opt.FIX_UBUNTU_DOCK) { - // workaround for Ubuntu Dock breaking overview allocations after changing position + _storeDashId() { const dash = Main.overview.dash; - if (_prevDash.dash !== dash || _prevDash.position !== dash._position) - _resetExtensionIfEnabled(0); + this._prevDash = dash._workId; } -} - -function _resetExtension(timeout = 200) { - if (_resetTimeoutId) - GLib.source_remove(_resetTimeoutId); - _resetTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - timeout, - () => { - if (!_enabled) - return GLib.SOURCE_REMOVE; - const dash = Main.overview.dash; - if (!timeout && _prevDash.dash && dash !== _prevDash.dash) { // !timeout means DtD workaround callback - _prevDash.dash = dash; - log(`[${Me.metadata.name}]: Dash has been replaced, resetting extension...`); - resetVShell(); - activateVShell(); - } else if (timeout) { - log(`[${Me.metadata.name}]: resetting extension...`); - resetVShell(); - activateVShell(); - } - _resetTimeoutId = 0; - return GLib.SOURCE_REMOVE; + _setInitialWsIndex() { + if (Main.layoutManager._startingUp) { + GLib.idle_add(GLib.PRIORITY_LOW, () => { + Main.overview._overview.controls._workspaceAdjustment.set_value(global.workspace_manager.get_active_workspace_index()); + }); } - ); -} - -function _fixUbuntuDock(activate = true) { - // Workaround for Ubuntu Dock breaking overview allocations after changing monitor configuration and deactivating dock - if (_watchDockSigId) { - global.settings.disconnect(_watchDockSigId); - _watchDockSigId = 0; } - if (_resetTimeoutId) { - GLib.source_remove(_resetTimeoutId); - _resetTimeoutId = 0; + _updateSettingsConnection() { + if (!opt._extensionUpdateId) + opt._extensionUpdateId = opt.connect('changed', this._updateSettings.bind(this)); } - _resetExtensionIfEnabled = () => {}; + _updateFixDashToDockOption() { + const dtdEnabled = !!(Me.Util.getEnabledExtensions('dash-to-dock').length || + Me.Util.getEnabledExtensions('ubuntu-dock').length); - if (!activate) - return; + // force enable Fix Dash to Dock option if DtD detected + opt._watchDashToDock = dtdEnabled; + // opt.set('fixUbuntuDock', dtdEnabled); + } - _watchDockSigId = global.settings.connect('changed::enabled-extensions', () => _resetExtension()); - _resetExtensionIfEnabled = _resetExtension; -} + _updateConnections() { + if (!this._monitorsChangedConId) + this._monitorsChangedConId = Main.layoutManager.connect('monitors-changed', () => this._updateVShell(2000)); + + + if (!this._showingOverviewConId) + this._showingOverviewConId = Main.overview.connect('showing', this._onShowingOverview.bind(this)); + + if (!this._sessionModeConId) { + // the panel must be visible when screen is locked + this._sessionModeConId = Main.sessionMode.connect('updated', session => { + if (session.currentMode === 'user' || session.parentMode === 'user') { + this._timeouts.unlock = GLib.idle_add(GLib.PRIORITY_LOW, + () => { + Me.Modules.panelModule.update(); + Me.Modules.overviewControlsModule.update(); + Me.Modules.winTmbModule.showThumbnails(); + + this._timeouts.unlock = 0; + return GLib.SOURCE_REMOVE; + } + ); + } else if (session.currentMode === 'unlock-dialog') { + Me.Modules.panelModule.update(true); + Me.Modules.winTmbModule.hideThumbnails(); + } + }); + } -function _updateSettings(settings, key) { - if (key?.includes('profile-data')) { - const index = key.replace('profile-data-', ''); - Main.notify(`${Me.metadata.name}`, `Profile ${index} has been saved`); - } - // avoid overload while loading profile - update only once - // delayed gsettings writes are processed alphabetically - if (key === 'aaa-loading-profile') { - Main.notify(`${Me.metadata.name}`, 'Profile has been loaded'); - if (_loadingProfileTimeoutId) - GLib.source_remove(_loadingProfileTimeoutId); - _loadingProfileTimeoutId = GLib.timeout_add(100, 0, () => { - _resetExtension(); - _loadingProfileTimeoutId = 0; - return GLib.SOURCE_REMOVE; - }); - } - if (_loadingProfileTimeoutId) - return; - - opt._updateSettings(); - - opt.WORKSPACE_MIN_SPACING = Main.overview._overview._controls._thumbnailsBox.get_theme_node().get_length('spacing'); - // update variables that cannot be processed within settings - const dash = Main.overview.dash; - if (_Util.dashIsDashToDock()) { - opt.DASH_POSITION = dash._position; - opt.DASH_TOP = opt.DASH_POSITION === 0; - opt.DASH_RIGHT = opt.DASH_POSITION === 1; - opt.DASH_BOTTOM = opt.DASH_POSITION === 2; - opt.DASH_LEFT = opt.DASH_POSITION === 3; - opt.DASH_VERTICAL = opt.DASH_LEFT || opt.DASH_RIGHT; + if (!this._watchDockSigId) { + this._watchDockSigId = Main.extensionManager.connect('extension-state-changed', + (source, extension) => { + const uuid = extension.uuid; + // ExtensionState = { + // ENABLED: 1, + // DISABLED: 2, + // ERROR: 3, + // OUT_OF_DATE: 4, + // DOWNLOADING: 5, + // INITIALIZED: 6, + // DISABLING: 7, + // ENABLING: 8, + // + // // Used as an error state for operations on unknown extensions, + // // should never be in a real extensionMeta object. + // UNINSTALLED: 99, + // }; + // no need to restart on disable/remove + // - if DtD was enabled before VShell, VShell will be rebased by extensionSystem + // - if DtD was enabled after VShell, the first _showingOverview detect replacement of the dash and repair VShell + const reset = [1, 2].includes(extension.state); + const dashReplacement = uuid.includes('dash-to-dock') || uuid.includes('ubuntu-dock') || uuid.includes('dash-to-panel'); + if (dashReplacement && reset) + opt._watchDashToDock = true; + if (!Main.layoutManager._startingUp && reset && dashReplacement) + this._updateVShell(1999); + } + ); + } } - opt.DASH_VISIBLE = opt.DASH_VISIBLE && !_Util.getEnabledExtensions('dash-to-panel@jderose9.github.com').length; + _removeConnections() { + if (this._monitorsChangedConId) { + Main.layoutManager.disconnect(this._monitorsChangedConId); + this._monitorsChangedConId = 0; + } + + if (this._showingOverviewConId) { + Main.overview.disconnect(this._showingOverviewConId); + this._showingOverviewConId = 0; + } - opt.MAX_ICON_SIZE = opt.get('dashMaxIconSize', true); - if (opt.MAX_ICON_SIZE < 16) { - opt.MAX_ICON_SIZE = 64; - opt.set('dashMaxIconSize', 64); - } + if (this._sessionModeConId) { + Main.sessionMode.disconnect(this._sessionModeConId); + this._sessionModeConId = 0; + } - const monitorWidth = global.display.get_monitor_geometry(global.display.get_primary_monitor()).width; - if (monitorWidth < 1600) { - opt.APP_GRID_ICON_SIZE_DEFAULT = opt.APP_GRID_ACTIVE_PREVIEW ? 128 : 64; - opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT = 64; + if (this._watchDockSigId) { + Main.extensionManager.disconnect(this._watchDockSigId); + this._watchDockSigId = 0; + } } - imports.ui.workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = opt.OVERVIEW_MODE === 1 ? 0.1 : 0.95; - - if (!_Util.dashIsDashToDock()) { // DtD has its own opacity control - Main.overview.dash._background.opacity = Math.round(opt.get('dashBgOpacity', true) * 2.5); // conversion % to 0-255 - const radius = opt.get('dashBgRadius', true); - if (radius) { - let style; - switch (opt.DASH_POSITION) { - case 1: - style = `border-radius: ${radius}px 0 0 ${radius}px;`; - break; - case 3: - style = `border-radius: 0 ${radius}px ${radius}px 0;`; - break; - default: - style = `border-radius: ${radius}px;`; - } - Main.overview.dash._background.set_style(style); - } else { - Main.overview.dash._background.set_style(''); + _updateOverrides(reset = false) { + Me.Modules.workspacesViewModule.update(reset); + Me.Modules.workspaceThumbnailModule.update(reset); + Me.Modules.overviewModule.update(reset); + Me.Modules.overviewControlsModule.update(reset); + + Me.Modules.workspaceModule.update(reset); + Me.Modules.windowPreviewModule.update(reset); + Me.Modules.windowManagerModule.update(reset); + + Me.Modules.layoutModule.update(reset); + Me.Modules.dashModule.update(reset); + // avoid enabling panel module when session is locked + if (reset || (!reset && !Main.sessionMode.isLocked)) + Me.Modules.panelModule.update(reset); + // the panel must be visible when screen is locked + // at startup time, panel will be updated from the startupAnimation after allocation + if (!reset && Main.sessionMode.isLocked && !Main.layoutManager._startingUp) + Me.Modules.panelModule._showPanel(true); + // PanelModule._showPanel(true); + // hide panel so it appears directly on the final place + /* else if (Main.layoutManager._startingUp && !Meta.is_restart()) + Main.panel.opacity = 0;*/ + + Me.Modules.workspaceAnimationModule.update(reset); + Me.Modules.workspaceSwitcherPopupModule.update(reset); + + Me.Modules.swipeTrackerModule.update(reset); + + Me.Modules.searchModule.update(reset); + + Me.Modules.windowSearchProviderModule.update(reset); + Me.Modules.recentFilesSearchProviderModule.update(reset); + Me.Modules.extensionsSearchProviderModule.update(reset); + + // don't rebuild app grid on any screen lock + // even if the extension includes unlock-screen session mode + // disable/enable is called at least once even on GS44 + // when screen lock is activated for the first time + // because every first disable of each extension rebases + // the entire extensions stack that was enabled later + if (Main.sessionMode.isLocked) + this._sessionLockActive = true; + + // This covers unnecessary enable/disable cycles during first screen lock when extensions are rebased, but is not allowed by the EGO rules + if (!this._sessionLockActive || !Main.extensionManager._getEnabledExtensions().includes(Me.metadata.uuid)) { + // iconGridModule will be updated from appDisplayModule + Me.Modules.appDisplayModule.update(reset); } - } - // adjust search entry style for OM2 - if (opt.OVERVIEW_MODE2) - Main.overview.searchEntry.add_style_class_name('search-entry-om2'); - else - Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); + if (!this._sessionLockActive && !Main.layoutManager._startingUp && opt.APP_GRID_PERFORMANCE) { + // Avoid showing status at startup, can cause freeze + this._showStatusMessage(); + } - Main.overview.searchEntry.visible = opt.SHOW_SEARCH_ENTRY; - St.Settings.get().slow_down_factor = opt.ANIMATION_TIME_FACTOR; - imports.ui.search.MAX_LIST_SEARCH_RESULTS_ROWS = opt.SEARCH_MAX_ROWS; + if (!Main.sessionMode.isLocked) + this._sessionLockActive = false; - opt.START_Y_OFFSET = (opt.get('panelModule', true) && opt.PANEL_OVERVIEW_ONLY && opt.PANEL_POSITION_TOP) || - // better to add unnecessary space than to have a panel overlapping other objects - _Util.getEnabledExtensions('hidetopbar@mathieu.bidon.ca').length - ? Main.panel.height - : 0; + // iconGridModule will be updated from appDisplayModule + // Me.Modules.appDisplayModule.update(reset); - if (settings) - _applySettings(key); -} + Me.Modules.windowAttentionHandlerModule.update(reset); + Me.Modules.appFavoritesModule.update(reset); + Me.Modules.messageTrayModule.update(reset); + Me.Modules.osdWindowModule.update(reset); + Me.Modules.overlayKeyModule.update(reset); + Me.Modules.searchControllerModule.update(reset); + Me.Modules.winTmbModule.update(reset); -function _applySettings(key) { - if (key?.endsWith('-module')) { - _updateOverrides(); - return; + if (!reset && !Main.layoutManager._startingUp) + Main.overview._overview.controls.setInitialTranslations(); } - _setStaticBackground(); - _updateOverviewTranslations(); - _switchPageShortcuts(); + _onShowingOverview() { + if (Main.layoutManager._startingUp) + return; + + Main.overview._overview.controls.opacity = 255; + + // store pointer X coordinate for OVERVIEW_MODE 1 window spread - if mouse pointer is steady, don't spread + opt.showingPointerX = global.get_pointer()[0]; + + if (!Main.overview._overview.controls._bgManagers && (opt.SHOW_BG_IN_OVERVIEW || opt.SHOW_WS_PREVIEW_BG)) + Main.overview._overview.controls._setBackground(); - if (key?.includes('app-grid')) { - AppDisplayOverride.update(); - return; + if (opt._watchDashToDock) { + // workaround for Dash to Dock (Ubuntu Dock) breaking overview allocations after enabled and changed position + // DtD replaces dock and its _workId on every position change + const dash = Main.overview.dash; + if (this._prevDash !== dash._workId) + this._updateVShell(0); + } } - if (key?.includes('panel')) - PanelOverride.update(); - - if (key?.includes('dash') || key?.includes('search') || key?.includes('icon')) - DashOverride.update(); - - if (key?.includes('hot-corner') || key?.includes('dash')) - LayoutOverride.update(); - - switch (key) { - case 'fix-ubuntu-dock': - _fixUbuntuDock(opt.get('fixUbuntuDock', true)); - break; - case 'ws-thumbnails-position': - _updateOverrides(); - break; - case 'workspace-switcher-animation': - WorkspaceAnimationOverride.update(); - break; - case 'search-width-scale': - SearchOverride.update(); - break; - case 'favorites-notify': - AppFavoritesOverride.update(); - break; - case 'window-attention-mode': - WindowAttentionHandlerOverride.update(); - break; - case 'show-ws-preview-bg': - PanelOverride.update(); - break; - case 'notification-position': - MessageTrayOverride.update(); - break; - case 'osd-position': - OsdWindowOverride.update(); - break; - case 'overlay-key': - OverlayKey.update(); + _updateVShell(timeout = 200) { + if (!this._enabled || Main.layoutManager._startingUp) + return; + + if (this._timeouts.reset) + GLib.source_remove(this._timeouts.reset); + this._timeouts.reset = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + timeout, + () => { + if (!this._enabled) + return GLib.SOURCE_REMOVE; + + const dash = Main.overview.dash; + if (timeout < 2000) { // timeout < 2000 for partial update + this._prevDash = dash._workId; + console.warn(`[${Me.metadata.name}]: Dash has been replaced, updating extension ...`); + Me._resetInProgress = true; + // update only necessary modules if dash has been replaced + this._repairOverrides(); + Me._resetInProgress = false; + } else { + console.warn(`[${Me.metadata.name}]: Updating extension ...`); + // for case the monitor configuration has been changed, update all + Me._resetInProgress = true; + this._activateVShell(); + Me._resetInProgress = false; + } + this._timeouts.reset = 0; + return GLib.SOURCE_REMOVE; + } + ); } -} -function _switchPageShortcuts() { - if (!opt.get('enablePageShortcuts', true)) - return; - - const vertical = global.workspaceManager.layout_rows === -1; - const schema = 'org.gnome.desktop.wm.keybindings'; - const settings = ExtensionUtils.getSettings(schema); - - const keyLeft = 'switch-to-workspace-left'; - const keyRight = 'switch-to-workspace-right'; - const keyUp = 'switch-to-workspace-up'; - const keyDown = 'switch-to-workspace-down'; - - const keyMoveLeft = 'move-to-workspace-left'; - const keyMoveRight = 'move-to-workspace-right'; - const keyMoveUp = 'move-to-workspace-up'; - const keyMoveDown = 'move-to-workspace-down'; - - const switchPrevSc = '<Super>Page_Up'; - const switchNextSc = '<Super>Page_Down'; - const movePrevSc = '<Super><Shift>Page_Up'; - const moveNextSc = '<Super><Shift>Page_Down'; - - let switchLeft = settings.get_strv(keyLeft); - let switchRight = settings.get_strv(keyRight); - let switchUp = settings.get_strv(keyUp); - let switchDown = settings.get_strv(keyDown); - - let moveLeft = settings.get_strv(keyMoveLeft); - let moveRight = settings.get_strv(keyMoveRight); - let moveUp = settings.get_strv(keyMoveUp); - let moveDown = settings.get_strv(keyMoveDown); - - if (vertical) { - if (switchLeft.includes(switchPrevSc)) - switchLeft.splice(switchLeft.indexOf(switchPrevSc), 1); - if (switchRight.includes(switchNextSc)) - switchRight.splice(switchRight.indexOf(switchNextSc), 1); - if (moveLeft.includes(movePrevSc)) - moveLeft.splice(moveLeft.indexOf(movePrevSc), 1); - if (moveRight.includes(moveNextSc)) - moveRight.splice(moveRight.indexOf(moveNextSc), 1); - - if (!switchUp.includes(switchPrevSc)) - switchUp.push(switchPrevSc); - if (!switchDown.includes(switchNextSc)) - switchDown.push(switchNextSc); - if (!moveUp.includes(movePrevSc)) - moveUp.push(movePrevSc); - if (!moveDown.includes(moveNextSc)) - moveDown.push(moveNextSc); - } else { - if (!switchLeft.includes(switchPrevSc)) - switchLeft.push(switchPrevSc); - if (!switchRight.includes(switchNextSc)) - switchRight.push(switchNextSc); - if (!moveLeft.includes(movePrevSc)) - moveLeft.push(movePrevSc); - if (!moveRight.includes(moveNextSc)) - moveRight.push(moveNextSc); - - if (switchUp.includes(switchPrevSc)) - switchUp.splice(switchUp.indexOf(switchPrevSc), 1); - if (switchDown.includes(switchNextSc)) - switchDown.splice(switchDown.indexOf(switchNextSc), 1); - if (moveUp.includes(movePrevSc)) - moveUp.splice(moveUp.indexOf(movePrevSc), 1); - if (moveDown.includes(moveNextSc)) - moveDown.splice(moveDown.indexOf(moveNextSc), 1); + // the key modules that can be affected by the supported incompatible extensions + _repairOverrides() { + Me.Modules.overviewModule.update(); + Me.Modules.overviewControlsModule.update(); + Me.Modules.workspacesViewModule.update(); + Me.Modules.windowPreviewModule.update(); + Me.Modules.panelModule.update(); + Me.Modules.dashModule.update(); } - settings.set_strv(keyLeft, switchLeft); - settings.set_strv(keyRight, switchRight); - settings.set_strv(keyUp, switchUp); - settings.set_strv(keyDown, switchDown); + _updateSettings(settings, key) { + // update settings cache and option variables + opt._updateSettings(); + + // avoid overload while loading profile - update only once + // delayed gsettings writes are processed alphabetically + if (key === 'aaa-loading-profile') { + this._showStatusMessage(); + if (this._timeouts.loadingProfile) + GLib.source_remove(this._timeouts.loadingProfile); + this._timeouts.loadingProfile = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 100, () => { + this._activateVShell(); + this._timeouts.loadingProfile = 0; + return GLib.SOURCE_REMOVE; + }); + } + if (this._timeouts.loadingProfile) + return; - settings.set_strv(keyMoveLeft, moveLeft); - settings.set_strv(keyMoveRight, moveRight); - settings.set_strv(keyMoveUp, moveUp); - settings.set_strv(keyMoveDown, moveDown); -} + if (key?.includes('profile-data')) { + const index = key.replace('profile-data-', ''); + Main.notify(`${Me.metadata.name}`, `Profile ${index} has been updated`); + } + opt.WORKSPACE_MIN_SPACING = Main.overview._overview._controls._thumbnailsBox.get_theme_node().get_length('spacing'); + // update variables that cannot be processed within settings + const dash = Main.overview.dash; + if (Me.Util.dashIsDashToDock()) { + opt.DASH_POSITION = dash._position; + opt.DASH_TOP = opt.DASH_POSITION === 0; + opt.DASH_RIGHT = opt.DASH_POSITION === 1; + opt.DASH_BOTTOM = opt.DASH_POSITION === 2; + opt.DASH_LEFT = opt.DASH_POSITION === 3; + opt.DASH_VERTICAL = opt.DASH_LEFT || opt.DASH_RIGHT; + } -function _shouldAnimateOverview() { - return !opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2; -} + opt.DASH_VISIBLE = opt.DASH_VISIBLE && !Me.Util.getEnabledExtensions('dash-to-panel@jderose9.github.com').length; -function _updateOverviewTranslations(dash = null, tmbBox = null, searchEntryBin = null) { - dash = dash ?? Main.overview.dash; - tmbBox = tmbBox ?? Main.overview._overview._controls._thumbnailsBox; - searchEntryBin = searchEntryBin ?? Main.overview._overview._controls._searchEntryBin; - - if (!_shouldAnimateOverview()) { - tmbBox.translation_x = 0; - tmbBox.translation_y = 0; - dash.translation_x = 0; - dash.translation_y = 0; - searchEntryBin.translation_x = 0; - searchEntryBin.translation_y = 0; - return; - } + const monitorWidth = global.display.get_monitor_geometry(global.display.get_primary_monitor()).width; + if (monitorWidth < 1600) { + opt.APP_GRID_ICON_SIZE_DEFAULT = opt.APP_GRID_ACTIVE_PREVIEW && !opt.APP_GRID_USAGE ? 128 : 64; + opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT = 64; + } - const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin); - tmbBox.translation_x = tmbTranslationX; - tmbBox.translation_y = tmbTranslationY; - if (!_Util.dashNotDefault()) { // only if dash is not dash to dock - dash.translation_x = dashTranslationX; - dash.translation_y = dashTranslationY; + Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = opt.OVERVIEW_MODE === 1 ? 0.1 : 0.95; + + // adjust search entry style for OM2 + if (opt.OVERVIEW_MODE2) + Main.overview.searchEntry.add_style_class_name('search-entry-om2'); + else + Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); + + Main.overview.searchEntry.visible = opt.SHOW_SEARCH_ENTRY; + Main.overview.searchEntry.opacity = 255; + St.Settings.get().slow_down_factor = opt.ANIMATION_TIME_FACTOR; + Search.MAX_LIST_SEARCH_RESULTS_ROWS = opt.SEARCH_MAX_ROWS; + + opt.START_Y_OFFSET = (opt.get('panelModule') && opt.PANEL_OVERVIEW_ONLY && opt.PANEL_POSITION_TOP) || + // better to add unnecessary space than to have a panel overlapping other objects + Me.Util.getEnabledExtensions('hidetopbar').length + ? Main.panel.height + : 0; + + // Options for workspace switcher, apply custom function only if needed + if (opt.WS_WRAPAROUND || opt.WS_IGNORE_LAST) + Meta.Workspace.prototype.get_neighbor = this._getNeighbor; + else + Meta.Workspace.prototype.get_neighbor = this._originalGetNeighbor; + + if (settings) + this._applySettings(key); } - searchEntryBin.translation_y = searchTranslationY; -} - -function _setStaticBackground(reset = false) { - _bgManagers.forEach(bg => { - Main.overview._overview._controls._stateAdjustment.disconnect(bg._fadeSignal); - bg.destroy(); - }); - - _bgManagers = []; - // if (!SHOW_BG_IN_OVERVIEW && !SHOW_WS_PREVIEW_BG) the background is used for static transition from wallpaper to empty bg in the overview - if (reset || (!opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG)) - return; - - for (const monitor of Main.layoutManager.monitors) { - const bgManager = new Background.BackgroundManager({ - monitorIndex: monitor.index, - container: Main.layoutManager.overviewGroup, - vignette: true, - }); - bgManager.backgroundActor.content.vignette_sharpness = 0; - bgManager.backgroundActor.content.brightness = 1; + _applySettings(key) { + if (key?.endsWith('-module')) { + for (let module of Me.moduleList) { + if (opt.options[module] && key === opt.options[module][1]) { + if (key === 'app-display-module') + this._showStatusMessage(); + Me.Modules[module].update(); + break; + } + } + return; + } + Main.overview._overview.controls._setBackground(); + this._switchPageShortcuts(); + + if (key?.includes('panel')) + Me.Modules.panelModule.update(); + + if (key?.includes('dash') || key?.includes('icon') || key?.includes('dot-style')) + Me.Modules.dashModule.update(); + + if (key?.includes('hot-corner') || key?.includes('dash')) + Me.Modules.layoutModule.update(); + + switch (key) { + case 'ws-thumbnails-position': + this._updateOverrides(); + break; + case 'workspace-switcher-animation': + Me.Modules.workspaceAnimationModule.update(); + break; + case 'search-width-scale': + Me.Modules.searchModule.update(); + break; + case 'favorites-notify': + Me.Modules.appFavoritesModule.update(); + break; + case 'window-attention-mode': + Me.Modules.windowAttentionHandlerModule.update(); + break; + case 'show-ws-preview-bg': + Me.Modules.panelModule.update(); + break; + case 'notification-position': + Me.Modules.messageTrayModule.update(); + break; + case 'osd-position': + Me.Modules.osdWindowModule.update(); + break; + case 'overlay-key': + Me.Modules.overlayKeyModule.update(); + break; + case 'always-activate-selected-window': + Me.Modules.windowPreviewModule.update(); + break; + } - bgManager._fadeSignal = Main.overview._overview._controls._stateAdjustment.connect('notify::value', v => { - _updateStaticBackground(bgManager, v.value, v); - }); + if (key?.includes('app-grid') || + key?.includes('app-folder') || + key?.includes('dot-style') || + key === 'show-search-entry' || + key === 'ws-thumbnail-scale' || + key === 'ws-thumbnail-scale-appgrid') { + this._showStatusMessage(); + Me.Modules.appDisplayModule.update(); + } + } - if (monitor.index === global.display.get_primary_monitor()) { - bgManager._primary = true; - _bgManagers.unshift(bgManager); // primary monitor first + _switchPageShortcuts() { + // ignore screen lock + if (!opt.get('enablePageShortcuts') || this._sessionLockActive) + return; + + const vertical = global.workspaceManager.layout_rows === -1; + const schema = 'org.gnome.desktop.wm.keybindings'; + const settings = new Gio.Settings({ schema_id: schema }); + + const keyLeft = 'switch-to-workspace-left'; + const keyRight = 'switch-to-workspace-right'; + const keyUp = 'switch-to-workspace-up'; + const keyDown = 'switch-to-workspace-down'; + + const keyMoveLeft = 'move-to-workspace-left'; + const keyMoveRight = 'move-to-workspace-right'; + const keyMoveUp = 'move-to-workspace-up'; + const keyMoveDown = 'move-to-workspace-down'; + + const switchPrevSc = '<Super>Page_Up'; + const switchNextSc = '<Super>Page_Down'; + const movePrevSc = '<Super><Shift>Page_Up'; + const moveNextSc = '<Super><Shift>Page_Down'; + + let switchLeft = settings.get_strv(keyLeft); + let switchRight = settings.get_strv(keyRight); + let switchUp = settings.get_strv(keyUp); + let switchDown = settings.get_strv(keyDown); + + let moveLeft = settings.get_strv(keyMoveLeft); + let moveRight = settings.get_strv(keyMoveRight); + let moveUp = settings.get_strv(keyMoveUp); + let moveDown = settings.get_strv(keyMoveDown); + + if (vertical) { + if (switchLeft.includes(switchPrevSc)) + switchLeft.splice(switchLeft.indexOf(switchPrevSc), 1); + if (switchRight.includes(switchNextSc)) + switchRight.splice(switchRight.indexOf(switchNextSc), 1); + if (moveLeft.includes(movePrevSc)) + moveLeft.splice(moveLeft.indexOf(movePrevSc), 1); + if (moveRight.includes(moveNextSc)) + moveRight.splice(moveRight.indexOf(moveNextSc), 1); + + if (!switchUp.includes(switchPrevSc)) + switchUp.push(switchPrevSc); + if (!switchDown.includes(switchNextSc)) + switchDown.push(switchNextSc); + if (!moveUp.includes(movePrevSc)) + moveUp.push(movePrevSc); + if (!moveDown.includes(moveNextSc)) + moveDown.push(moveNextSc); } else { - bgManager._primary = false; - _bgManagers.push(bgManager); + if (!switchLeft.includes(switchPrevSc)) + switchLeft.push(switchPrevSc); + if (!switchRight.includes(switchNextSc)) + switchRight.push(switchNextSc); + if (!moveLeft.includes(movePrevSc)) + moveLeft.push(movePrevSc); + if (!moveRight.includes(moveNextSc)) + moveRight.push(moveNextSc); + + if (switchUp.includes(switchPrevSc)) + switchUp.splice(switchUp.indexOf(switchPrevSc), 1); + if (switchDown.includes(switchNextSc)) + switchDown.splice(switchDown.indexOf(switchNextSc), 1); + if (moveUp.includes(movePrevSc)) + moveUp.splice(moveUp.indexOf(movePrevSc), 1); + if (moveDown.includes(moveNextSc)) + moveDown.splice(moveDown.indexOf(moveNextSc), 1); } + + settings.set_strv(keyLeft, switchLeft); + settings.set_strv(keyRight, switchRight); + settings.set_strv(keyUp, switchUp); + settings.set_strv(keyDown, switchDown); + + settings.set_strv(keyMoveLeft, moveLeft); + settings.set_strv(keyMoveRight, moveRight); + settings.set_strv(keyMoveUp, moveUp); + settings.set_strv(keyMoveDown, moveDown); } -} -function _updateStaticBackground(bgManager, stateValue, stateAdjustment = null) { - if (!opt.SHOW_BG_IN_OVERVIEW && !opt.SHOW_WS_PREVIEW_BG) { - // if no bg shown in the overview, fade out the wallpaper - if (!(opt.OVERVIEW_MODE2 && opt.WORKSPACE_MODE && stateAdjustment?.getStateTransitionParams().finalState === 1)) - bgManager.backgroundActor.opacity = Util.lerp(255, 0, Math.min(stateValue, 1)); - } else { - let VIGNETTE, BRIGHTNESS, bgValue; - if (opt.OVERVIEW_MODE2 && stateValue <= 1 && !opt.WORKSPACE_MODE) { - VIGNETTE = 0; - BRIGHTNESS = 1; - bgValue = stateValue; - } else { - VIGNETTE = 0.2; - BRIGHTNESS = opt.OVERVIEW_BG_BRIGHTNESS; - if (opt.OVERVIEW_MODE2 && stateValue > 1 && !opt.WORKSPACE_MODE) - bgValue = stateValue - 1; - else - bgValue = stateValue; + // Status dialog that appears during updating V-Shell configuration and blocks inputs + _showStatusMessage(show = true) { + if ((show && Me._resetInProgress) || Main.layoutManager._startingUp || !Main.overview._overview.controls._appDisplay._sortOrderedItemsAlphabetically) + return; + + if (Me._vShellMessageTimeoutId) { + GLib.source_remove(Me._vShellMessageTimeoutId); + Me._vShellMessageTimeoutId = 0; } - let blurEffect = bgManager.backgroundActor.get_effect('blur'); - if (!blurEffect) { - blurEffect = new Shell.BlurEffect({ - brightness: 1, - sigma: 0, - mode: Shell.BlurMode.ACTOR, - }); - bgManager.backgroundActor.add_effect_with_name('blur', blurEffect); + if (Me._vShellStatusMessage && !show) { + Me._vShellStatusMessage.close(); + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; } - bgManager.backgroundActor.content.vignette_sharpness = VIGNETTE; - bgManager.backgroundActor.content.brightness = BRIGHTNESS; + if (!show) + return; - let vignetteInit, brightnessInit;// , sigmaInit; - if (opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG) { - vignetteInit = VIGNETTE; - brightnessInit = BRIGHTNESS; - // sigmaInit = opt.OVERVIEW_BG_BLUR_SIGMA; - } else { - vignetteInit = 0; - brightnessInit = 1; - // sigmaInit = 0; + if (!Me._vShellStatusMessage) { + const sm = new Main.RestartMessage(_('Updating V-Shell...')); + sm.set_style('background-color: rgba(0,0,0,0.3);'); + sm.open(); + Me._vShellStatusMessage = sm; } - if (opt.OVERVIEW_MODE2) { - bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, bgValue); - bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, bgValue); - } else { - bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, Math.min(stateValue, 1)); - bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, Math.min(stateValue, 1)); - } + // just for case the message wasn't removed from appDisplay after App Grid realization + Me._vShellMessageTimeoutId = GLib.timeout_add_seconds( + GLib.PRIORITY_DEFAULT, + 5, + () => { + if (Me._vShellStatusMessage) { + Me._vShellStatusMessage.close(); + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + Me._resetInProgress = false; + } + + Me._vShellMessageTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } + ); + } - if (opt.OVERVIEW_BG_BLUR_SIGMA || opt.APP_GRID_BG_BLUR_SIGMA) { - // reduce number of steps of blur transition to improve performance - const step = opt.SMOOTH_BLUR_TRANSITIONS ? 0.05 : 0.2; - const searchActive = Main.overview._overview.controls._searchController.searchActive; - const progress = stateValue - (stateValue % step); - if (opt.SHOW_WS_PREVIEW_BG && stateValue < 1 && !searchActive) { // no need to animate transition, unless appGrid state is involved, static bg is covered by the ws preview bg - if (blurEffect.sigma !== opt.OVERVIEW_BG_BLUR_SIGMA) - blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; - } else if (stateValue < 1 && !searchActive) { - const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress)); - if (sigma !== blurEffect.sigma) - blurEffect.sigma = sigma; - } else if ((stateValue > 1 && bgManager._primary) || searchActive) { - const sigma = Math.round(Util.lerp(opt.OVERVIEW_BG_BLUR_SIGMA, opt.APP_GRID_BG_BLUR_SIGMA, progress % 1)); - if (sigma !== blurEffect.sigma) - blurEffect.sigma = sigma; - } else if (stateValue === 1) { - blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; - } else if (stateValue === 0) { - blurEffect.sigma = 0; + _getNeighbor(direction) { + // workspace matrix is supported + const activeIndex = this.index(); + const ignoreLast = opt.WS_IGNORE_LAST && !Main.overview._shown ? 1 : 0; + const wraparound = opt.WS_WRAPAROUND; + const nWorkspaces = global.workspace_manager.n_workspaces; + const lastIndex = nWorkspaces - 1 - ignoreLast; + const rows = global.workspace_manager.layout_rows > -1 ? global.workspace_manager.layout_rows : nWorkspaces; + const columns = global.workspace_manager.layout_columns > -1 ? global.workspace_manager.layout_columns : nWorkspaces; + + let index = activeIndex; + let neighborExists; + + if (direction === Meta.MotionDirection.LEFT) { + index -= 1; + const currentRow = Math.floor(activeIndex / columns); + const indexRow = Math.floor(index / columns); + neighborExists = index > -1 && indexRow === currentRow; + if (wraparound && !neighborExists) { + index = currentRow * columns + columns - 1; + const maxIndexOnLastRow = lastIndex % columns; + index = index < (lastIndex - ignoreLast) ? index : currentRow * columns + maxIndexOnLastRow; } + } else if (direction === Meta.MotionDirection.RIGHT) { + index += 1; + const currentRow = Math.floor(activeIndex / columns); + const indexRow = Math.floor(index / columns); + neighborExists = index <= lastIndex && indexRow === currentRow; + if (wraparound && !neighborExists) + index = currentRow * columns; + } else if (direction === Meta.MotionDirection.UP) { + index -= columns; + neighborExists = index > -1; + if (wraparound && !neighborExists) { + index = rows * columns + index; + index = index < nWorkspaces - ignoreLast ? index : index - columns; + } + } else if (direction === Meta.MotionDirection.DOWN) { + index += columns; + neighborExists = index <= lastIndex; + if (wraparound && !neighborExists) + index %= columns; } + + return global.workspace_manager.get_workspace_by_index(neighborExists || wraparound ? index : activeIndex); } } - diff --git a/extensions/44/vertical-workspaces/lib/appDisplay.js b/extensions/44/vertical-workspaces/lib/appDisplay.js index 2ac70b1..aeb2808 100644 --- a/extensions/44/vertical-workspaces/lib/appDisplay.js +++ b/extensions/44/vertical-workspaces/lib/appDisplay.js @@ -10,246 +10,501 @@ 'use strict'; -const { Clutter, GLib, GObject, Meta, Shell, St, Graphene, Pango } = imports.gi; +const Clutter = imports.gi.Clutter; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Graphene = imports.gi.Graphene; +const Meta = imports.gi.Meta; +const Pango = imports.gi.Pango; +const Shell = imports.gi.Shell; +const St = imports.gi.St; -const DND = imports.ui.dnd; -const Main = imports.ui.main; const AppDisplay = imports.ui.appDisplay; +const DND = imports.ui.dnd; const IconGrid = imports.ui.iconGrid; +const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const IconGridOverride = Me.imports.lib.iconGrid; -const _Util = Me.imports.lib.util; +let Me; +let opt; -const DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x00000022); -const DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000000); +let _timeouts; -// gettext -const _ = Me.imports.lib.settings._; +// DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x00000022); +// DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000000); -let _overrides; +var AppDisplayModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -let _appGridLayoutSettings; -let _appDisplayScrollConId; -let _appSystemStateConId; -let _appGridLayoutConId; -let _origAppViewItemAcceptDrop; -let _updateFolderIcons; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; -let opt; -let shellVersion = _Util.shellVersion; -let _firstRun = true; + this._appGridLayoutSettings = null; + this._appDisplayScrollConId = 0; + this._appSystemStateConId = 0; + this._appGridLayoutConId = 0; + this._origAppViewItemAcceptDrop = null; + this._updateFolderIcons = 0; + } -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('appDisplayModule', true); - reset = reset || !moduleEnabled; + cleanGlobals() { + Me = null; + opt = null; + } - // don't even touch this module if disabled - if (_firstRun && reset) - return; + update(reset) { + this._removeTimeouts(); + this.moduleEnabled = opt.get('appDisplayModule'); + const conflict = false; - _firstRun = false; + reset = reset || !this.moduleEnabled || conflict; - if (_overrides) - _overrides.removeAll(); + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + this.moduleEnabled = false; + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) { + this.moduleEnabled = false; + console.debug(' AppDisplayModule - Keeping untouched'); + } + } - if (reset) { - _setAppDisplayOrientation(false); - _updateAppGridProperties(reset); - _updateAppGridDND(reset); - _restoreOverviewGroup(); - _overrides = null; - opt = null; - return; + _activateModule() { + Me.Modules.iconGridModule.update(); + + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + // Common + this._overrides.addOverride('FolderView', AppDisplay.FolderView.prototype, FolderView); + this._overrides.addOverride('FolderIcon', AppDisplay.FolderIcon.prototype, FolderIcon); + if (opt.APP_GRID_ACTIVE_PREVIEW) + this._overrides.addOverride('ActiveFolderIcon', AppDisplay.FolderIcon, ActiveFolderIcon); + this._overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIcon); + this._overrides.addOverride('AppDisplay', AppDisplay.AppDisplay.prototype, AppDisplayCommon); + this._overrides.addOverride('AppViewItem', AppDisplay.AppViewItem.prototype, AppViewItemCommon); + this._overrides.addOverride('BaseAppViewCommon', AppDisplay.BaseAppView.prototype, BaseAppViewCommon); + + if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) { + this._overrides.addOverride('AppDisplayVertical', AppDisplay.AppDisplay.prototype, AppDisplayVertical); + this._overrides.addOverride('BaseAppViewVertical', AppDisplay.BaseAppView.prototype, BaseAppViewVertical); + } + + // Custom App Grid + this._overrides.addOverride('AppFolderDialog', AppDisplay.AppFolderDialog.prototype, AppFolderDialog); + if (Me.shellVersion >= 43) { + // const defined class needs to be touched before real access + this._dummy = AppDisplay.AppGrid; + delete this._dummy; + // BaseAppViewGridLayout is not exported, we can only access current instance + this._overrides.addOverride('BaseAppViewGridLayout', Main.overview._overview.controls._appDisplay._appGridLayout, BaseAppViewGridLayout); + this._overrides.addOverride('FolderGrid', AppDisplay.FolderGrid.prototype, FolderGrid); + } else { + this._overrides.addOverride('FolderGrid', AppDisplay.FolderGrid.prototype, FolderGridLegacy); + } + + this._setAppDisplayOrientation(opt.ORIENTATION === Clutter.Orientation.VERTICAL); + this._updateDND(); + if (!Main.sessionMode.isGreeter) + this._updateAppDisplayProperties(); + + console.debug(' AppDisplayModule - Activated'); } - _overrides = new _Util.Overrides(); + _disableModule() { + Me.Modules.iconGridModule.update(true); + + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; - if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) { - _overrides.addOverride('AppDisplayVertical', AppDisplay.AppDisplay.prototype, AppDisplayVertical); - _overrides.addOverride('BaseAppViewVertical', AppDisplay.BaseAppView.prototype, BaseAppViewVertical); + const reset = true; + this._setAppDisplayOrientation(false); + this._updateAppDisplayProperties(reset); + this._updateDND(reset); + this._restoreOverviewGroup(); + this._removeStatusMessage(); + + console.debug(' AppDisplayModule - Disabled'); } - // 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); + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } } - _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); + + _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 + 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 (Me.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 { - // 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'); - } + appDisplay._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); + if (this._appDisplayScrollConId) { + appDisplay._adjustment.disconnect(this._appDisplayScrollConId); + this._appDisplayScrollConId = 0; + } - // 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'); } - // 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); + // 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; } - appDisplay._nextPageArrow.scale_x = 1; - appDisplay._prevPageArrow.scale_x = 1; + // update appGrid dot pages indicators + this._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 + _updateAppDisplayProperties(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._currentMode = -1; + appDisplay._grid.setGridModes(); + if (this._appGridLayoutSettings) { + this._appGridLayoutSettings.disconnect(this._appGridLayoutConId); + this._appGridLayoutConId = 0; + this._appGridLayoutSettings = null; + } + appDisplay._redisplay(); - appDisplay._nextPageIndicator.remove_style_class_name('nextPageIndicator'); - appDisplay._prevPageIndicator.remove_style_class_name('prevPageIndicator'); + appDisplay._grid.set_style(''); + this._updateAppGrid(reset); + } else { + // update grid on layout reset + if (!this._appGridLayoutSettings) { + this._appGridLayoutSettings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); + this._appGridLayoutConId = this._appGridLayoutSettings.connect('changed::app-picker-layout', this._updateLayout); + } + + 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;`); + // APP_GRID_SPACING constant is used for grid dimensions calculation + // but sometimes the actual grid spacing properties affect/change the calculated size, therefore we set it lower to avoid this problem + // main app grid always use available space and the spacing is optimized for the grid dimensions + appDisplay._grid.set_style('column-spacing: 5px; row-spacing: 5px;'); + + // force redisplay + appDisplay._grid._currentMode = -1; + appDisplay._grid.setGridModes(); + appDisplay._grid.layoutManager.fixedIconSize = opt.APP_GRID_ICON_SIZE; + // avoid resetting appDisplay before startup animation + // x11 shell restart skips startup animation + if (!Main.layoutManager._startingUp) { + this._updateAppGrid(); + } else if (Main.layoutManager._startingUp && (Meta.is_restart() || Me.Util.dashIsDashToDock())) { + _timeouts.three = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._updateAppGrid(); + _timeouts.three = 0; + return GLib.SOURCE_REMOVE; + }); + } + } } - // value for page indicator is calculated from scroll adjustment, horizontal needs to be replaced by vertical - appDisplay._adjustment = appDisplay._scrollView[scroll].adjustment; + _updateDND(reset) { + if (!reset) { + if (!this._appSystemStateConId && opt.APP_GRID_INCLUDE_DASH >= 3) { + this._appSystemStateConId = Shell.AppSystem.get_default().connect( + 'app-state-changed', + () => { + this._updateFolderIcons = true; + Main.overview._overview.controls._appDisplay._redisplay(); + } + ); + } + } else if (this._appSystemStateConId) { + Shell.AppSystem.get_default().disconnect(this._appSystemStateConId); + this._appSystemStateConId = 0; + } + } - // 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; + _restoreOverviewGroup() { + Main.overview.dash.showAppsButton.checked = false; + Main.layoutManager.overviewGroup.opacity = 255; + Main.layoutManager.overviewGroup.scale_x = 1; + Main.layoutManager.overviewGroup.scale_y = 1; + Main.layoutManager.overviewGroup.hide(); + Main.overview._overview._controls._appDisplay.translation_x = 0; + Main.overview._overview._controls._appDisplay.translation_y = 0; + Main.overview._overview._controls._appDisplay.visible = true; 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; + // update all invalid positions that may be result of grid/icon size change + _updateIconPositions() { + const appDisplay = Main.overview._overview._controls._appDisplay; + const layout = JSON.stringify(global.settings.get_value('app-picker-layout').recursiveUnpack()); + // if app grid layout is empty, sort source alphabetically to avoid misplacing + if (layout === JSON.stringify([]) && appDisplay._sortOrderedItemsAlphabetically) + appDisplay._sortOrderedItemsAlphabetically(); + const icons = [...appDisplay._orderedItems]; + for (let i = 0; i < icons.length; i++) + appDisplay._moveItem(icons[i], -1, -1); + } + + _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._redisplay(); + appDisplay._folderIcons = []; + } + + _removeStatusMessage() { + if (Me._vShellStatusMessage) { + if (Me._vShellMessageTimeoutId) { + GLib.source_remove(Me._vShellMessageTimeoutId); + Me._vShellMessageTimeoutId = 0; + } + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + } + } - 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); + _updateLayout(settings, key) { + const currentValue = JSON.stringify(settings.get_value(key).deep_unpack()); + const emptyValue = JSON.stringify([]); + const customLayout = currentValue !== emptyValue; + if (!customLayout) { + this._updateAppGrid(); + } + } + + _updateAppGrid(reset = false, callback) { + 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 + + // 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); + + // don't delay the first screen lock on GS < 44, removing icons takes a time and with other 15 enabled extensions it can be multiplied by 15 + if (!Main.sessionMode.isLocked) + this._removeIcons(); + + appDisplay._redisplay(); + // don't realize appDisplay on disable, or at startup if disabled + // always realize appDisplay otherwise to avoid errors while opening folders (that I was unable to trace) + if (reset || (!opt.APP_GRID_PERFORMANCE && callback)) { + this._removeStatusMessage(); + if (callback) + callback(); + return; } - 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;`); + // workaround - silently realize appDisplay + // appDisplay and its content must be "visible" (opacity > 0) on the screen (within monitor geometry) + // to realize its objects + // this action takes some time and affects animations during the first use + // if we do it invisibly before user needs it, it can improve the user's experience + + this._exposeAppGrid(); + + // let the main loop process our changes before continuing + _timeouts.one = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._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); + } - // 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(); + appDisplay._redisplay(); + // realize also all app folders (by opening them) so the first popup is as smooth as the second one + // let the main loop process our changes before continuing + _timeouts.two = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._restoreAppGrid(); + Me._resetInProgress = false; + this._removeStatusMessage(); + + if (callback) + callback(); + + _timeouts.two = 0; + return GLib.SOURCE_REMOVE; + }); + _timeouts.one = 0; + return GLib.SOURCE_REMOVE; + }); } -} -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(); - } - ); + _exposeAppGrid() { + const overviewGroup = Main.layoutManager.overviewGroup; + if (!overviewGroup.visible) { + // scale down the overviewGroup so it don't cover uiGroup + overviewGroup.scale_y = 0.001; + // make it invisible to the eye, but visible for the renderer + overviewGroup.opacity = 1; + // if overview is hidden, show it + overviewGroup.visible = true; } - } else if (_appSystemStateConId) { - Shell.AppSystem.get_default().disconnect(_appSystemStateConId); - _appSystemStateConId = 0; + + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.opacity = 1; + + // find usable value, sometimes it's one, sometime the other... + let [x, y] = appDisplay.get_position(); + let { x1, y1 } = appDisplay.allocation; + x = x === Infinity ? 0 : x; + y = y === Infinity ? 0 : y; + x1 = x1 === Infinity ? 0 : x1; + y1 = y1 === Infinity ? 0 : y1; + appDisplay.translation_x = -(x ? x : x1); + appDisplay.translation_y = -(y ? y : y1); + this._exposeAppFolders(); + } + + _exposeAppFolders() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay._folderIcons.forEach(d => { + d._ensureFolderDialog(); + d._dialog._updateFolderSize(); + d._dialog.scale_y = 0.0001; + d._dialog.show(); + }); + } + + _restoreAppGrid() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.translation_x = 0; + appDisplay.translation_y = 0; + // appDisplay.opacity = 0; + this._hideAppFolders(); + + const overviewGroup = Main.layoutManager.overviewGroup; + if (!Main.overview._shown) + overviewGroup.hide(); + overviewGroup.scale_y = 1; + overviewGroup.opacity = 255; + + this._removeStatusMessage(); + } + + _hideAppFolders() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay._folderIcons.forEach(d => { + if (d._dialog) { + d._dialog._updateFolderSize(); + d._dialog.hide(); + d._dialog.scale_y = 1; + } + }); + } + + _getWindowApp(metaWin) { + const tracker = Shell.WindowTracker.get_default(); + return tracker.get_window_app(metaWin); } - 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; + + _getAppLastUsedWindow(app) { + let recentWin; + global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { + const winApp = this._getWindowApp(metaWin); + if (!recentWin && winApp === app) + recentWin = metaWin; + }); + return recentWin; } -} -function _restoreOverviewGroup() { - Main.overview.dash.showAppsButton.checked = false; - Main.layoutManager.overviewGroup.opacity = 255; - Main.layoutManager.overviewGroup.scale_x = 1; - Main.layoutManager.overviewGroup.hide(); -} + _getAppRecentWorkspace(app) { + const recentWin = this._getAppLastUsedWindow(app); + if (recentWin) + return recentWin.get_workspace(); + + return null; + } +}; const AppDisplayVertical = { // correction of the appGrid size when page indicators were moved from the bottom to the right @@ -302,7 +557,7 @@ const AppDisplayCommon = { const appsInsideFolders = new Set(); this._folderIcons = []; - if (!opt.APP_GRID_ORDER) { + if (!opt.APP_GRID_USAGE) { let folders = this._folderSettings.get_strv('folder-children'); folders.forEach(id => { let path = `${this._folderSettings.path}folders/${id}/`; @@ -317,8 +572,8 @@ const AppDisplayCommon = { if (icon.pressed) this.updateDragFocus(icon); }); - } else if (_updateFolderIcons && opt.APP_GRID_EXCLUDE_RUNNING) { - // if any app changed its running state, update folder icon + } else if (this._updateFolderIcons && opt.APP_GRID_EXCLUDE_RUNNING) { + // if any app changed its running state, update folder icon icon.icon.update(); } @@ -334,8 +589,9 @@ const AppDisplayCommon = { icon.getAppIds().forEach(appId => appsInsideFolders.add(appId)); }); } + // reset request to update active icon - _updateFolderIcons = false; + this._updateFolderIcons = false; // Allow dragging of the icon only if the Dash would accept a drop to // change favorite-apps. There are no other possible drop targets from @@ -348,7 +604,7 @@ const AppDisplayCommon = { global.settings.is_writable('app-picker-layout'); apps.forEach(appId => { - if (!opt.APP_GRID_ORDER && appsInsideFolders.has(appId)) + if (!opt.APP_GRID_USAGE && appsInsideFolders.has(appId)) return; let icon = this._items.get(appId); @@ -380,7 +636,7 @@ const AppDisplayCommon = { dragMotion: this._onDragMotion.bind(this), }; DND.addDragMonitor(this._dragMonitor); - if (shellVersion < 43) + if (Me.shellVersion < 43) this._slideSidePages(AppDisplay.SidePages.PREVIOUS | AppDisplay.SidePages.NEXT | AppDisplay.SidePages.DND); else this._appGridLayout.showPageIndicators(); @@ -419,50 +675,16 @@ const AppDisplayCommon = { this._redisplay(); }, - // accept source from active preview + // accept source from active folder preview acceptDrop(source) { - if (opt.APP_GRID_ORDER) + if (opt.APP_GRID_USAGE) return false; if (source._sourceItem) source = source._sourceItem; - let dropTarget = null; - if (shellVersion >= 43) { - dropTarget = this._dropTarget; - delete this._dropTarget; - } - - if (!this._canAccept(source)) + if (!BaseAppViewCommon.acceptDrop.bind(this)(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); @@ -493,7 +715,7 @@ const BaseAppViewVertical = { this._pageIndicators.x_align = Clutter.ActorAlign.START; this._pageIndicators.set_style('margin-right: 10px;'); const scrollContainer = this._scrollView.get_parent(); - if (shellVersion < 43) { + if (Me.shellVersion < 43) { // remove touch friendly side navigation bars / arrows if (this._hintContainer && this._hintContainer.get_parent()) scrollContainer.remove_child(this._hintContainer); @@ -536,7 +758,7 @@ const BaseAppViewCommon = { try { this._moveItem(icon, page, position); } catch (e) { - log(`Warning:${e}`); + console.warn(`Warning:${e}`); } }); }, @@ -570,14 +792,19 @@ const BaseAppViewCommon = { } }); - // sort all alphabetically - if (opt.APP_GRID_ORDER > 0) { + // different options for root app grid and app folders + const thisIsFolder = this instanceof AppDisplay.FolderView; + const thisIsAppDisplay = !thisIsFolder; + if ((opt.APP_GRID_ORDER && thisIsAppDisplay) || + (opt.APP_FOLDER_ORDER && thisIsFolder)) { // const { itemsPerPage } = this._grid; let appIcons = this._orderedItems; + // sort all alphabetically 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) + if ((opt.APP_GRID_USAGE && thisIsAppDisplay) || + (opt.APP_FOLDER_USAGE && thisIsFolder)) appIcons.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id)); // sort favorites first @@ -595,9 +822,14 @@ const BaseAppViewCommon = { } // sort running first - if (opt.APP_GRID_DASH_FIRST) + if (opt.APP_GRID_DASH_FIRST && thisIsAppDisplay) appIcons.sort((a, b) => a.app.get_state() !== Shell.AppState.RUNNING && b.app.get_state() === Shell.AppState.RUNNING); + if (opt.APP_GRID_FOLDERS_FIRST) + appIcons.sort((a, b) => b._folder && !a._folder); + else if (opt.APP_GRID_FOLDERS_LAST) + appIcons.sort((a, b) => a._folder && !b._folder); + this._setLinearPositions(appIcons); this._orderedItems = appIcons; @@ -611,7 +843,7 @@ const BaseAppViewCommon = { }, _canAccept(source) { - return opt.APP_GRID_ORDER ? false : source instanceof AppDisplay.AppViewItem; + return source instanceof AppDisplay.AppViewItem; }, // support active preview icons @@ -619,12 +851,25 @@ const BaseAppViewCommon = { if (!this._canAccept(source)) return false; - if (source._sourceItem) - source = source._sourceItem; + let dropTarget = null; + if (Me.shellVersion >= 43) { + dropTarget = this._dropTarget; + delete this._dropTarget; + } + + if (!this._canAccept(source)) + return false; + if ((Me.shellVersion < 43 && this._dropPage) || + (Me.shellVersion >= 43 && (dropTarget === this._prevPageIndicator || + dropTarget === this._nextPageIndicator))) { + let increment; + + if (Me.shellVersion < 43) + increment = this._dropPage === AppDisplay.SidePages.NEXT ? 1 : -1; + else + increment = dropTarget === this._prevPageIndicator ? -1 : 1; - 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; @@ -635,7 +880,11 @@ const BaseAppViewCommon = { // Dropped before the icon was moved const { page, position } = this._delayedMoveData; - this._moveItem(source, page, position); + try { + this._moveItem(source, page, position); + } catch (e) { + console.warn(`Warning:${e}`); + } this._removeDelayedMove(); } @@ -652,7 +901,7 @@ const BaseAppViewCommon = { const appIcon = dragEvent.source; - if (shellVersion < 43) { + if (Me.shellVersion < 43) { this._dropPage = this._pageForCoords(dragEvent.x, dragEvent.y); if (this._dropPage && this._dropPage === AppDisplay.SidePages.PREVIOUS && @@ -663,7 +912,7 @@ const BaseAppViewCommon = { } if (appIcon instanceof AppDisplay.AppViewItem) { - if (shellVersion < 44) { + if (Me.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. @@ -685,7 +934,10 @@ const BaseAppViewCommon = { } } - this._maybeMoveItem(dragEvent); + const thisIsFolder = this instanceof AppDisplay.FolderView; + const thisIsAppDisplay = !thisIsFolder; + if ((!opt.APP_GRID_ORDER && thisIsAppDisplay) || (!opt.APP_FOLDER_ORDER && thisIsFolder)) + this._maybeMoveItem(dragEvent); return DND.DragMotionResult.CONTINUE; }, @@ -771,16 +1023,12 @@ const FolderIcon = { : 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(); + // if (this._dialog._designCapacity !== this.view._orderedItems.length) + this._dialog._updateFolderSize(); this.view._scrollView.vscroll.adjustment.value = 0; this._dialog.popup(); @@ -789,15 +1037,7 @@ const FolderIcon = { 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; + let grid = new AppDisplay.FolderGrid(); return grid; }, @@ -832,11 +1072,15 @@ const FolderView = { bin.child = this._orderedItems[i].app.create_icon_texture(subSize); } else { const app = this._orderedItems[i].app; - const child = new ActiveFolderIcon(app); + const child = new AppDisplay.AppIcon(app, { + setSizeManually: true, + showLabel: false, + }); child._sourceItem = this._orderedItems[i]; child._sourceFolder = this; child.icon.style_class = ''; child.icon.set_style('margin: 0; padding: 0;'); + child._dot.set_style('margin-bottom: 1px;'); child.icon.setIconSize(subSize); bin.child = child; @@ -863,9 +1107,9 @@ const FolderView = { 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(); + // if folder content changed, update folder size, but not if it's empty + /* if (this._dialog && this._dialog._designCapacity !== this._orderedItems.length && this._orderedItems.length) + this._dialog._updateFolderSize();*/ return icon; }, @@ -927,6 +1171,13 @@ const FolderView = { items.push(icon); }); + + if (opt.APP_FOLDER_ORDER) + Main.overview._overview.controls._appDisplay._sortOrderedItemsAlphabetically(items); + + if (opt.APP_FOLDER_USAGE) + items.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id)); + this._appIds = this._apps.map(app => app.get_id()); return items; }, @@ -942,10 +1193,9 @@ const FolderView = { }; // folder columns and rows -const FolderGrid = GObject.registerClass( -class FolderGrid extends IconGrid.IconGrid { +const FolderGridLegacy = { _init() { - super._init({ + IconGrid.IconGrid.prototype._init.bind(this)({ 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 @@ -954,56 +1204,52 @@ class FolderGrid extends IconGrid.IconGrid { page_halign: Clutter.ActorAlign.CENTER, page_valign: Clutter.ActorAlign.CENTER, }); - + this.layout_manager._isFolder = true; // 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; - } + this.layoutManager.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 FolderGrid = { + _init() { + AppDisplay.AppGrid.prototype._init.bind(this)({ + 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, + }); + this.layout_manager._isFolder = true; + const spacing = opt.APP_GRID_SPACING; + this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`); + this.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; - 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, + }, + ]); + }, - 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); + }, +}; - 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() { + this._viewBox.add_style_class_name('app-folder-dialog-vshell'); + // delegate this dialog to the FolderIcon._view // so its _createFolderIcon function can update the dialog if folder content changed this._view._dialog = this; @@ -1023,18 +1269,65 @@ const AppFolderDialog = { }); this.child.add_action(clickAction); + + // Adjust empty actor to center the title + this._entryBox.get_first_child().width = 82; + }, + + after__addFolderNameEntry() { + // Edit button + this._removeButton = new St.Button({ + style_class: 'edit-folder-button', + button_mask: St.ButtonMask.ONE, + toggle_mode: false, + reactive: true, + can_focus: true, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + child: new St.Icon({ + icon_name: 'user-trash-symbolic', + icon_size: 16, + }), + }); + + this._removeButton.connect('clicked', () => { + if (Date.now() - this._removeButton._lastClick < Clutter.Settings.get_default().double_click_time) { + this._grabHelper.ungrab({ actor: this }); + // without hiding the dialog, Shell crashes (at least on X11) + this.hide(); + this._view._deletingFolder = true; + + // Resetting all keys deletes the relocatable schema + let keys = this._folder.settings_schema.list_keys(); + for (const key of keys) + this._folder.reset(key); + + let settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' }); + let folders = settings.get_strv('folder-children'); + folders.splice(folders.indexOf(this._view._id), 1); + + // remove all abandoned folders (usually my own garbage and unwanted default folders...) + /* const appFolders = this._appDisplay._folderIcons.map(icon => icon._id); + folders.forEach(folder => { + if (!appFolders.includes(folder)) { + folders.splice(folders.indexOf(folder._id), 1); + } + });*/ + settings.set_strv('folder-children', folders); + + this._view._deletingFolder = false; + return; + } + this._removeButton._lastClick = Date.now(); + }); + + this._entryBox.add_child(this._removeButton); }, 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(), @@ -1046,26 +1339,48 @@ const AppFolderDialog = { this.get_parent().set_child_above_sibling(this, null); this._needsZoomAndFade = true; - this.show(); + // the first folder dialog realization needs size correction + // so set the folder size, let it realize and then update the folder content + if (!this.realized) { + this._updateFolderSize(); + GLib.idle_add( + GLib.PRIORITY_DEFAULT, + () => { + this._updateFolderSize(); + } + ); + } + + this.show(); this.emit('open-state-changed', true); }, _updateFolderSize() { - // adapt folder size according to the settings and number of icons const view = this._view; + const [firstItem] = view._grid.layoutManager._container; + if (!firstItem) + return; + // adapt folder size according to the settings and number of icons + const appDisplay = this._source._parentView; + if (!appDisplay.width || appDisplay.allocation.x2 === Infinity || appDisplay.allocation.x2 === -Infinity) { + return; + } + 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 itemPadding = 55; // default icon item padding on Fedora 44 + // const dialogMargin = 30; const nItems = view._orderedItems.length; let columns = opt.APP_GRID_FOLDER_COLUMNS; let rows = opt.APP_GRID_FOLDER_ROWS; + const fullAdaptiveGrid = !columns && !rows; let spacing = opt.APP_GRID_SPACING; - const monitor = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + const minItemSize = 48 + itemPadding; - if (!columns && !rows) { + if (fullAdaptiveGrid) { columns = Math.ceil(Math.sqrt(nItems)); rows = columns; if (columns * (columns - 1) >= nItems) { @@ -1081,38 +1396,67 @@ const AppFolderDialog = { } 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 + view._grid.layoutManager.fixedIconSize = iconSize; + + let itemSize = iconSize + 55; // 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; + if (this.realized) { firstItem.icon.setIconSize(iconSize); const [firstItemWidth] = firstItem.get_preferred_size(); const realSize = firstItemWidth / scaleFactor; - if (realSize > iconSize) + // if the preferred item size is smaller than icon plus some padding, ignore it + // (icons that are not yet realized are returning sizes like 45 or 53) + if (realSize > (iconSize + 24)) 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; + width = Math.round(width + (opt.ORIENTATION ? 100 : 160/* space for navigation arrows*/)); + let height = rows * (itemSize + spacing) + /* header*/75 + /* padding*/ 2 * 30 + /* padding + ?page indicator*/(!opt.ORIENTATION || !opt.APP_GRID_FOLDER_COLUMNS ? 100 : 70); + + // allocation is more reliable than appDisplay width/height properties + const appDisplayWidth = appDisplay.allocation.x2 - appDisplay.allocation.x1; + const appDisplayHeight = appDisplay.allocation.y2 - appDisplay.allocation.y1 + (opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0); - // folder must fit the primary monitor + // folder must fit the appDisplay area // reduce columns/rows if needed and count with the scaled values - while (width * scaleFactor > monitor.width - 2 * dialogMargin) { - width -= itemSize + spacing; - columns -= 1; + if (!opt.APP_GRID_FOLDER_ROWS) { + while ((height * scaleFactor) > appDisplayHeight) { + height -= itemSize + spacing; + rows -= 1; + } + } + + if (!opt.APP_GRID_FOLDER_COLUMNS) { + while ((width * scaleFactor) > appDisplayWidth) { + width -= itemSize + spacing; + columns -= 1; + } } - while (height * scaleFactor > monitor.height - 2 * dialogMargin) { - height -= itemSize + spacing; - rows -= 1; + // try to compensate for the previous reduction if there is a space + if (!opt.APP_GRID_FOLDER_COLUMNS) { + while ((nItems > columns * rows) && ((width * scaleFactor + itemSize + spacing) <= appDisplayWidth)) { + width += itemSize + spacing; + columns += 1; + } + // remove columns that cannot be displayed + if ((columns * minItemSize + (columns - 1) * spacing) > appDisplayWidth) + columns = Math.floor(appDisplayWidth / (minItemSize + spacing)); } - width = Math.max(540, width); + if (!opt.APP_GRID_FOLDER_ROWS) { + while ((nItems > columns * rows) && ((height * scaleFactor + itemSize + spacing) <= appDisplayHeight)) { + height += itemSize + spacing; + rows += 1; + } + // remove rows that cannot be displayed + if ((rows * minItemSize + (rows - 1) * spacing) > appDisplayHeight) + rows = Math.floor(appDisplayWidth / (minItemSize + spacing)); + } + + width = Math.clamp(width, 640, appDisplayWidth); + height = Math.min(height, appDisplayHeight); const layoutManager = view._grid.layoutManager; layoutManager.rows_per_page = rows; @@ -1127,6 +1471,8 @@ const AppFolderDialog = { padding: 30px; `); + view._grid.layoutManager._pageWidth += 1; + view._grid.layoutManager.adaptToSize(view._grid.layoutManager._pageWidth - 1, view._grid.layoutManager._pageHeight); view._redisplay(); // store original item count @@ -1145,40 +1491,34 @@ const AppFolderDialog = { // 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); + const appDisplay = this._source._parentView; + const [appDisplayX, appDisplayY] = this._source._parentView.get_transformed_position(); - // 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 - ); + if (!opt.APP_GRID_FOLDER_CENTER) { + dialogTargetX = sourceCenterX - this.child.width / 2; + dialogTargetY = sourceCenterY - this.child.height / 2; - 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()); + // keep the dialog in appDisplay area if possible dialogTargetX = Math.clamp( dialogTargetX, - this.x + monitor.x, - this.x + monitor.x + monitor.width - this.child.width + appDisplayX, + appDisplayX + appDisplay.width - this.child.width ); dialogTargetY = Math.clamp( dialogTargetY, - this.y + monitor.y, - this.y + monitor.y + monitor.height - this.child.height + appDisplayY, + appDisplayY + appDisplay.height - this.child.height ); + } else { + const searchEntryHeight = opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0; + dialogTargetX = appDisplayX + appDisplay.width / 2 - this.child.width / 2; + dialogTargetY = appDisplayY - searchEntryHeight + ((appDisplay.height + searchEntryHeight) / 2 - this.child.height / 2) / 2; } - const dialogOffsetX = -dialogX + dialogTargetX; - const dialogOffsetY = -dialogY + dialogTargetY; + + const dialogOffsetX = Math.round(dialogTargetX - dialogX); + const dialogOffsetY = Math.round(dialogTargetY - dialogY); this.child.set({ translation_x: sourceX - dialogX, @@ -1188,12 +1528,6 @@ const AppFolderDialog = { 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, @@ -1202,17 +1536,22 @@ const AppFolderDialog = { 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; - } - }, }); + appDisplay.ease({ + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + if (opt.SHOW_SEARCH_ENTRY) { + Main.overview.searchEntry.ease({ + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + this._needsZoomAndFade = false; if (this._sourceMappedId === 0) { @@ -1230,17 +1569,20 @@ const AppFolderDialog = { return; } + // if the dialog was shown silently, skip animation + if (this.scale_y < 1) { + this._needsZoomAndFade = false; + this.hide(); + this._popdownCallbacks.forEach(func => func()); + this._popdownCallbacks = []; + return; + } + let [sourceX, sourceY] = this._source.get_transformed_position(); let [dialogX, dialogY] = this.child.get_transformed_position(); - this.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, @@ -1264,11 +1606,35 @@ const AppFolderDialog = { }, }); + const appDisplay = this._source._parentView; + appDisplay.ease({ + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + if (opt.SHOW_SEARCH_ENTRY) { + Main.overview.searchEntry.ease({ + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + this._needsZoomAndFade = false; }, _setLighterBackground(lighter) { - const backgroundColor = lighter + if (this._isOpen) { + const appDisplay = this._source._parentView; + appDisplay.ease({ + opacity: lighter ? 20 : 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + /* const backgroundColor = lighter ? DIALOG_SHADE_HIGHLIGHT : DIALOG_SHADE_NORMAL; @@ -1276,91 +1642,10 @@ const AppFolderDialog = { 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 @@ -1377,7 +1662,7 @@ const AppIcon = { return source !== this && (source instanceof this.constructor) && (view instanceof AppDisplay.AppDisplay && - !opt.APP_GRID_ORDER); + !opt.APP_GRID_USAGE); }, }; @@ -1427,7 +1712,7 @@ const AppViewItemCommon = { // support active preview icons acceptDrop(source, _actor, x) { - if (opt.APP_GRID_ORDER) + if (opt.APP_GRID_USAGE) return DND.DragMotionResult.NO_DROP; this._setHoveringByDnd(false); @@ -1449,26 +1734,18 @@ const AppViewItemCommon = { }; -const ActiveFolderIcon = GObject.registerClass( -class ActiveFolderIcon extends AppDisplay.AppIcon { - _init(app) { - super._init(app, { - setSizeManually: true, - showLabel: false, - }); - } - +const ActiveFolderIcon = { handleDragOver() { return DND.DragMotionResult.CONTINUE; - } + }, acceptDrop() { return false; - } + }, _onDragEnd() { this._dragging = false; this.undoScaleAndFade(); Main.overview.endItemDrag(this._sourceItem.icon); - } -}); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/appFavorites.js b/extensions/44/vertical-workspaces/lib/appFavorites.js index 50ebce9..3efb68c 100644 --- a/extensions/44/vertical-workspaces/lib/appFavorites.js +++ b/extensions/44/vertical-workspaces/lib/appFavorites.js @@ -10,45 +10,63 @@ '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 Me; 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; +var AppFavoritesModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // don't even touch this module if disabled - if (_firstRun && reset) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } - _firstRun = false; + cleanGlobals() { + Me = null; + opt = null; + } - if (_overrides) - _overrides.removeAll(); + update(reset) { + this.moduleEnabled = opt.get('appFavoritesModule'); + // if notifications are enabled no override is needed + reset = reset || !this.moduleEnabled || opt.SHOW_FAV_NOTIFICATION; - // if notifications are enabled no override is needed - if (reset || opt.SHOW_FAV_NOTIFICATION) { - _overrides = null; - opt = null; - return; + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) { + this.moduleEnabled = false; + console.debug(' AppFavoritesModule - Keeping untouched'); + } } - _overrides = new _Util.Overrides(); + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + // use actual instance instead of prototype + this._overrides.addOverride('AppFavorites', AppFavorites.getAppFavorites(), AppFavoritesCommon); - // AppFavorites.AppFavorites is const, first access returns undefined - const dummy = AppFavorites.AppFavorites; - _overrides.addOverride('AppFavorites', AppFavorites.AppFavorites.prototype, AppFavoritesCommon); -} + console.debug(' AppFavoritesModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' AppFavoritesModule - Deactivated'); + } +}; const AppFavoritesCommon = { addFavoriteAtPos(appId, pos) { diff --git a/extensions/44/vertical-workspaces/lib/dash.js b/extensions/44/vertical-workspaces/lib/dash.js index bf832bd..17d43ea 100644 --- a/extensions/44/vertical-workspaces/lib/dash.js +++ b/extensions/44/vertical-workspaces/lib/dash.js @@ -8,309 +8,523 @@ * modified dash module of https://github.com/RensAlthuis/vertical-overview extension */ -const { Clutter, GObject, St, Shell, Meta } = imports.gi; -const AppDisplay = imports.ui.appDisplay; +'use strict'; + +const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const AltTab = imports.ui.altTab; const AppFavorites = imports.ui.appFavorites; +const AppDisplay = imports.ui.appDisplay; +const AppMenu = imports.ui.appMenu; +const BoxPointer = imports.ui.boxpointer; +const Dash = imports.ui.dash; const DND = imports.ui.dnd; const IconGrid = imports.ui.iconGrid; const Main = imports.ui.main; -const Overview = imports.ui.overview; -const Dash = 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.lib.util; -const _ = Me.imports.lib.settings._; +let Me; +let opt; +// gettext +let _; -const shellVersion = Util.shellVersion; -let _origWorkId; -let _newWorkId; -let _showAppsIconBtnPressId; +let _moduleEnabled; +let _timeouts; // 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.lib.recentFilesSearchProvider.prefix; -const WindowSearchProviderPrefix = Me.imports.lib.windowSearchProvider.prefix; +const DASH_ITEM_LABEL_SHOW_TIME = 150; -let _overrides; -let opt; -let _firstRun = true; +var DashModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._originalWorkId = null; + this._customWorkId = null; + this._showAppsIconBtnPressId = 0; + } -const DASH_ITEM_LABEL_SHOW_TIME = 150; + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + this._removeTimeouts(); + + this.moduleEnabled = opt.get('dashModule'); + const conflict = !!(Me.Util.getEnabledExtensions('dash-to-dock').length || + Me.Util.getEnabledExtensions('ubuntu-dock').length || + Me.Util.getEnabledExtensions('dash-to-panel').length); -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('dashModule', true); - reset = reset || !moduleEnabled; + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Dash" module disabled due to potential conflict with another extension`); - // don't even touch this module if disabled - if (_firstRun && reset) - return; + reset = reset || !this.moduleEnabled || conflict; + this._conflict = conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' DashModule - Keeping untouched'); + } - _firstRun = false; + updateStyle(dash) { + if (opt.DASH_BG_LIGHT) + dash._background.add_style_class_name('dash-background-light'); + else + dash._background.remove_style_class_name('dash-background-light'); + + dash._background.opacity = opt.DASH_BG_OPACITY; + let radius = opt.DASH_BG_RADIUS; + if (radius) { + let style; + switch (opt.DASH_POSITION) { + case 1: + style = opt.DASH_BG_GS3_STYLE ? `border-radius: ${radius}px 0 0 ${radius}px;` : `border-radius: ${radius}px;`; + break; + case 3: + style = opt.DASH_BG_GS3_STYLE ? `border-radius: 0 ${radius}px ${radius}px 0;` : `border-radius: ${radius}px;`; + break; + default: + style = `border-radius: ${radius}px;`; + } + dash._background.set_style(style); + } else { + dash._background.set_style(''); + } + } - if (_overrides) - _overrides.removeAll(); + _activateModule() { + _moduleEnabled = true; + _timeouts = {}; + const dash = Main.overview._overview._controls.layoutManager._dash; + if (!this._originalWorkId) + this._originalWorkId = dash._workId; - const dash = Main.overview._overview._controls.layoutManager._dash; + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); - setToHorizontal(); + this._resetStyle(dash); + this.updateStyle(dash); - dash.remove_style_class_name('vertical'); - dash.remove_style_class_name('vertical-left'); - dash.remove_style_class_name('vertical-right'); + this._overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerCommon); + this._overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommon); + this._overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIconCommon); + this._overrides.addOverride('DashIcon', Dash.DashIcon.prototype, DashIconCommon); + this._overrides.addOverride('AppMenu', AppMenu.AppMenu.prototype, AppMenuCommon); - if (reset) { - _moveDashAppGridIcon(reset); - _connectShowAppsIcon(reset); - _updateSearchWindowsIcon(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; + if (opt.DASH_VERTICAL) { + dash.add_style_class_name('vertical'); + this._setOrientation(Clutter.Orientation.VERTICAL); + } else { + this._setOrientation(Clutter.Orientation.HORIZONTAL); + } + + if (!this._customWorkId) + this._customWorkId = Main.initializeDeferredWork(dash._box, dash._redisplay.bind(dash)); + dash._workId = this._customWorkId; + + this._updateSearchWindowsIcon(); + this._updateRecentFilesIcon(); + this._updateExtensionsIcon(); + this._moveDashAppGridIcon(); + this._connectShowAppsIcon(); + + dash.visible = opt.DASH_VISIBLE; + dash._background.add_style_class_name('dash-background-reduced'); + dash._queueRedisplay(); + + if (opt.DASH_ISOLATE_WS && !this._wmSwitchWsConId) { + this._wmSwitchWsConId = global.windowManager.connect('switch-workspace', () => dash._queueRedisplay()); + this._newWindowConId = global.display.connect_after('window-created', () => dash._queueRedisplay()); + } + + console.debug(' DashModule - Activated'); } - _overrides = new Util.Overrides(); + _disableModule() { + const dash = Main.overview._overview._controls.layoutManager._dash; + this._resetStyle(dash); - _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 (this._overrides) + this._overrides.removeAll(); + this._overrides = null; - if (opt.DASH_VERTICAL) { - _overrides.addOverride('Dash', Dash.Dash.prototype, DashOverride); - setToVertical(); - dash.add_style_class_name('vertical'); + dash._workId = this._originalWorkId; - if (!_newWorkId) { - _origWorkId = dash._workId; - dash._workId = Main.initializeDeferredWork(dash._box, dash._redisplay.bind(dash)); - _newWorkId = dash._workId; - } else { - dash._workId = _newWorkId; + if (this._wmSwitchWsConId) { + global.windowManager.disconnect(this._wmSwitchWsConId); + this._wmSwitchWsConId = 0; + } + if (this._newWindowConId) { + global.windowManager.disconnect(this._newWindowConId); + this._newWindowConId = 0; } - } else { - setToHorizontal(); - if (_origWorkId) - dash._workId = _origWorkId; + + const reset = true; + this._setOrientation(Clutter.Orientation.HORIZONTAL); + this._moveDashAppGridIcon(reset); + this._connectShowAppsIcon(reset); + this._updateSearchWindowsIcon(false); + this._updateRecentFilesIcon(false); + this._updateExtensionsIcon(false); + dash.visible = !this._conflict; + dash._background.opacity = 255; + + _moduleEnabled = false; + console.debug(' DashModule - Disabled'); } - _updateSearchWindowsIcon(); - _updateRecentFilesIcon(); - _moveDashAppGridIcon(); - _connectShowAppsIcon(); - - if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) - dash._showWindowsIconClickedId = dash._showWindowsIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(WindowSearchProviderPrefix)); - - if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) - dash._recentFilesIconClickedId = dash._recentFilesIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(RecentFilesSearchProviderPrefix)); - - dash.visible = opt.DASH_VISIBLE; - dash._background.add_style_class_name('v-shell-dash-background'); - dash._redisplay(); -} - -function setToVertical() { - let dash = Main.overview._overview._controls.layoutManager._dash; - - dash._box.layout_manager.orientation = Clutter.Orientation.VERTICAL; - dash._dashContainer.layout_manager.orientation = Clutter.Orientation.VERTICAL; - dash._dashContainer.y_expand = false; - dash._dashContainer.x_expand = true; - dash.x_align = Clutter.ActorAlign.START; - dash.y_align = Clutter.ActorAlign.CENTER; - - let sizerBox = dash._background.get_children()[0]; - sizerBox.clear_constraints(); - sizerBox.add_constraint(new Clutter.BindConstraint({ - source: dash._showAppsIcon.icon, - coordinate: Clutter.BindCoordinate.WIDTH, - })); - sizerBox.add_constraint(new Clutter.BindConstraint({ - source: dash._dashContainer, - coordinate: Clutter.BindCoordinate.HEIGHT, - })); - dash._box.remove_all_children(); - dash._separator = null; - dash._queueRedisplay(); - dash._adjustIconSize(); - - 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._box.layout_manager.orientation = Clutter.Orientation.HORIZONTAL; - dash._dashContainer.layout_manager.orientation = Clutter.Orientation.HORIZONTAL; - dash._dashContainer.y_expand = true; - dash._dashContainer.x_expand = false; - dash.x_align = Clutter.ActorAlign.CENTER; - dash.y_align = 0; - - let sizerBox = dash._background.get_children()[0]; - sizerBox.clear_constraints(); - sizerBox.add_constraint(new Clutter.BindConstraint({ - source: dash._showAppsIcon.icon, - coordinate: Clutter.BindCoordinate.HEIGHT, - })); - sizerBox.add_constraint(new Clutter.BindConstraint({ - source: dash._dashContainer, - coordinate: Clutter.BindCoordinate.WIDTH, - })); - - dash._box.remove_all_children(); - dash._separator = null; - dash._queueRedisplay(); - dash._adjustIconSize(); -} - -function _moveDashAppGridIcon(reset = false) { - // move dash app grid icon to the front - const dash = Main.overview._overview._controls.layoutManager._dash; - - const appIconPosition = opt.get('showAppsIconPosition', true); - dash._showAppsIcon.remove_style_class_name('show-apps-icon-vertical-hide'); - dash._showAppsIcon.remove_style_class_name('show-apps-icon-horizontal-hide'); - dash._showAppsIcon.opacity = 255; - if (!reset && appIconPosition === 0) // 0 - start - dash._dashContainer.set_child_at_index(dash._showAppsIcon, 0); - if (reset || appIconPosition === 1) { // 1 - end - const index = dash._dashContainer.get_children().length - 1; - dash._dashContainer.set_child_at_index(dash._showAppsIcon, index); + _resetStyle(dash) { + dash.remove_style_class_name('vertical'); + dash.remove_style_class_name('vertical-gs3-left'); + dash.remove_style_class_name('vertical-gs3-right'); + dash.remove_style_class_name('vertical-left'); + dash.remove_style_class_name('vertical-right'); + dash._background.remove_style_class_name('dash-background-light'); + dash._background.remove_style_class_name('dash-background-reduced'); } - 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 - dash._showAppsIcon.opacity = 0; + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } } -} -function _connectShowAppsIcon(reset = false) { - if (!reset) { - if (_showAppsIconBtnPressId || Util.dashIsDashToDock()) { - // button is already connected || dash is Dash to Dock - return; + _setOrientation(orientation, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + + dash._box.layout_manager.orientation = orientation; + dash._dashContainer.layout_manager.orientation = orientation; + dash._dashContainer.y_expand = !orientation; + dash._dashContainer.x_expand = !!orientation; + dash.x_align = orientation ? Clutter.ActorAlign.START : Clutter.ActorAlign.CENTER; + dash.y_align = orientation ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.FILL; + + let sizerBox = dash._background.get_children()[0]; + sizerBox.clear_constraints(); + sizerBox.add_constraint(new Clutter.BindConstraint({ + source: dash._showAppsIcon.icon, + coordinate: orientation ? Clutter.BindCoordinate.WIDTH : Clutter.BindCoordinate.HEIGHT, + })); + sizerBox.add_constraint(new Clutter.BindConstraint({ + source: dash._dashContainer, + coordinate: orientation ? Clutter.BindCoordinate.HEIGHT : Clutter.BindCoordinate.WIDTH, + })); + dash._box.remove_all_children(); + dash._separator = null; + dash._queueRedisplay(); + dash._adjustIconSize(); + + if (orientation && opt.DASH_BG_GS3_STYLE) { + if (opt.DASH_LEFT) + dash.add_style_class_name('vertical-gs3-left'); + else if (opt.DASH_RIGHT) + dash.add_style_class_name('vertical-gs3-right'); + } else { + dash.remove_style_class_name('vertical-gs3-left'); + dash.remove_style_class_name('vertical-gs3-right'); } + } - 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) - Util.openPreferences(); - else if (button === Clutter.BUTTON_SECONDARY) - Util.activateSearchProvider(WindowSearchProviderPrefix); - 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; + _moveDashAppGridIcon(reset = false, dash) { + // move dash app grid icon to the front + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + + const appIconPosition = opt.get('showAppsIconPosition'); + dash._showAppsIcon.remove_style_class_name('show-apps-icon-vertical-hide'); + dash._showAppsIcon.remove_style_class_name('show-apps-icon-horizontal-hide'); + dash._showAppsIcon.opacity = 255; + if (!reset && appIconPosition === 0) // 0 - start + dash._dashContainer.set_child_at_index(dash._showAppsIcon, 0); + if (reset || appIconPosition === 1) { // 1 - end + const index = dash._dashContainer.get_children().length - 1; + dash._dashContainer.set_child_at_index(dash._showAppsIcon, index); + } + 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 + dash._showAppsIcon.opacity = 0; + } } -} -const DashOverride = { - handleDragOver(source, actor, _x, y, _time) { - let app = Dash.getAppFromSource(source); + _connectShowAppsIcon(reset = false, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + if (!reset) { + if (this._showAppsIconBtnPressId || Me.Util.dashIsDashToDock()) { + // button is already connected || dash is Dash to Dock + return; + } + dash._showAppsIcon.reactive = true; + this._showAppsIconBtnPressId = dash._showAppsIcon.connect('button-press-event', (actor, event) => { + const button = event.get_button(); + if (button === Clutter.BUTTON_MIDDLE) + Me.Util.openPreferences(); + else if (button === Clutter.BUTTON_SECONDARY) + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + else + return Clutter.EVENT_PROPAGATE; + return Clutter.EVENT_STOP; + }); + } else if (this._showAppsIconBtnPressId) { + dash._showAppsIcon.disconnect(this._showAppsIconBtnPressId); + this._showAppsIconBtnPressId = 0; + dash._showAppsIcon.reactive = false; + } + } - // Don't allow favoriting of transient apps - if (app === null || app.is_window_backed()) - return DND.DragMotionResult.NO_DROP; + _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; - if (!global.settings.is_writable('favorite-apps')) - return DND.DragMotionResult.NO_DROP; + if (dash._showWindowsIcon) { + dashContainer.remove_child(dash._showWindowsIcon); + if (dash._showWindowsIconClickedId) { + dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId); + dash._showWindowsIconClickedId = 0; + } + delete dash._showWindowsIconClickedId; + if (dash._showWindowsIcon) + dash._showWindowsIcon.destroy(); + delete dash._showWindowsIcon; + } - let favorites = AppFavorites.getAppFavorites().getFavorites(); - let numFavorites = favorites.length; + if (!show || !opt.get('windowSearchProviderModule')) + return; - let favPos = favorites.indexOf(app); + if (!dash._showWindowsIcon) { + dash._showWindowsIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('showWindowsIcon', dash._showWindowsIcon, ShowWindowsIcon); + dash._showWindowsIcon._afterInit(); + dash._showWindowsIcon.show(false); + dashContainer.add_child(dash._showWindowsIcon); + dash._hookUpLabel(dash._showWindowsIcon); + } - let children = this._box.get_children(); - let numChildren = children.length; - let boxHeight = this._box.height; + 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) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._showWindowsIcon, index); + } - // Keep the placeholder out of the index calculation; assuming that - // the remove target has the same size as "normal" items, we don't - // need to do the same adjustment there. - if (this._dragPlaceholder) { - boxHeight -= this._dragPlaceholder.height; - numChildren--; + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); + + if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) { + dash._showWindowsIconClickedId = dash._showWindowsIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + }); } + } - // Same with the separator - if (this._separator) { - boxHeight -= this._separator.height; - numChildren--; + _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; + + if (dash._recentFilesIcon) { + dashContainer.remove_child(dash._recentFilesIcon); + if (dash._recentFilesIconClickedId) { + dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId); + dash._recentFilesIconClickedId = 0; + } + delete dash._recentFilesIconClickedId; + if (dash._recentFilesIcon) + dash._recentFilesIcon.destroy(); + delete dash._recentFilesIcon; } - let pos; - if (!this._emptyDropTarget) - pos = Math.floor(y * numChildren / boxHeight); - else - pos = 0; // always insert at the top when dash is empty + if (!show || !opt.get('recentFilesSearchProviderModule')) + return; - // Put the placeholder after the last favorite if we are not - // in the favorites zone - if (pos > numFavorites) - pos = numFavorites; + if (!dash._recentFilesIcon) { + dash._recentFilesIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('recentFilesIcon', dash._recentFilesIcon, ShowRecentFilesIcon); + dash._recentFilesIcon._afterInit(); + dash._recentFilesIcon.show(false); + dashContainer.add_child(dash._recentFilesIcon); + dash._hookUpLabel(dash._recentFilesIcon); + } - if (pos !== this._dragPlaceholderPos && this._animatingPlaceholdersCount === 0) { - this._dragPlaceholderPos = pos; + 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) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._recentFilesIcon, index); + } - // Don't allow positioning before or after self - if (favPos !== -1 && (pos === favPos || pos === favPos + 1)) { - this._clearDragPlaceholder(); - return DND.DragMotionResult.CONTINUE; - } + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); - // If the placeholder already exists, we just move - // it, but if we are adding it, expand its size in - // an animation - let fadeIn; - if (this._dragPlaceholder) { - this._dragPlaceholder.destroy(); - fadeIn = false; - } else { - fadeIn = true; + if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) { + dash._recentFilesIconClickedId = dash._recentFilesIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.RFSP_PREFIX); + }); + } + } + + _updateExtensionsIcon(show = opt.SHOW_EXTENSIONS_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; + + if (dash._extensionsIcon) { + dashContainer.remove_child(dash._extensionsIcon); + if (dash._extensionsIconClickedId) { + dash._extensionsIcon.toggleButton.disconnect(dash._extensionsIconClickedId); + dash._extensionsIconClickedId = 0; } + delete dash._extensionsIconClickedId; + if (dash._extensionsIcon) + dash._extensionsIcon.destroy(); + delete dash._extensionsIcon; + } - 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, - this._dragPlaceholderPos); - this._dragPlaceholder.show(fadeIn); + if (!show || !opt.get('extensionsSearchProviderModule')) + return; + + if (!dash._extensionsIcon) { + dash._extensionsIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('extensionsIcon', dash._extensionsIcon, ShowExtensionsIcon); + dash._extensionsIcon._afterInit(); + dash._extensionsIcon.show(false); + dashContainer.add_child(dash._extensionsIcon); + dash._hookUpLabel(dash._extensionsIcon); } - if (!this._dragPlaceholder) - return DND.DragMotionResult.NO_DROP; + dash._extensionsIcon.icon.setIconSize(dash.iconSize); + if (opt.SHOW_EXTENSIONS_ICON === 1) { + dashContainer.set_child_at_index(dash._extensionsIcon, 0); + } else if (opt.SHOW_EXTENSIONS_ICON === 2) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._extensionsIcon, index); + } - let srcIsFavorite = favPos !== -1; + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); - if (srcIsFavorite) - return DND.DragMotionResult.MOVE_DROP; + if (dash._extensionsIcon && !dash._extensionsIconClickedId) { + dash._extensionsIconClickedId = dash._extensionsIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.ESP_PREFIX); + }); + } + } +}; - return DND.DragMotionResult.COPY_DROP; +const DashItemContainerCommon = { + // move labels according dash position + showLabel() { + if (!this._labelText) + return; + + const windows = this.child.app?.get_windows(); + const recentWindowTitle = windows && windows.length ? windows[0].get_title() : ''; + const windowCount = this.child.app?.get_windows().length; + let labelSuffix = ''; + if (windowCount > 1) + labelSuffix = ` (${windowCount})`; + if (recentWindowTitle && recentWindowTitle !== this._labelText) + labelSuffix += `\n ${recentWindowTitle}`; + + + this.label.set_text(this._labelText + labelSuffix); + + this.label.opacity = 0; + this.label.show(); + + let [stageX, stageY] = this.get_transformed_position(); + + const itemWidth = this.allocation.get_width(); + const itemHeight = this.allocation.get_height(); + + const labelWidth = this.label.get_width(); + const labelHeight = this.label.get_height(); + let xOffset = Math.floor((itemWidth - labelWidth) / 2); + let x = Math.clamp(stageX + xOffset, 0, global.stage.width - labelWidth); + const primaryMonitor = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + x = Math.clamp(x, primaryMonitor.x, primaryMonitor.x + primaryMonitor.width - labelWidth); + + let node = this.label.get_theme_node(); + let y; + + if (opt.DASH_TOP) { + const yOffset = 0.75 * itemHeight + 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); + xOffset = 4; + + x = stageX - xOffset - this.label.width; + y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); + } else if (opt.DASH_LEFT) { + const yOffset = Math.floor((itemHeight - labelHeight) / 2); + xOffset = 4; + + x = stageX + this.width + xOffset; + y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); + } + + this.label.set_position(x, y); + this.label.ease({ + opacity: 255, + duration: DASH_ITEM_LABEL_SHOW_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + this.label.set_position(x, y); + this.label.ease({ + opacity: 255, + duration: DASH_ITEM_LABEL_SHOW_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); }, +}; +const DashCommon = { _redisplay() { + // After disabling V-Shell queueRedisplay() may call this function + // In that case redirect the call to the current _redisplay() + if (!_moduleEnabled) { + this._redisplay(); + return; + } + let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); let running = this._appSystem.get_running(); + if (opt.DASH_ISOLATE_WS) { + const currentWs = global.workspace_manager.get_active_workspace(); + running = running.filter(app => { + return app.get_windows().filter(w => w.get_workspace() === currentWs).length; + }); + this._box.get_children().forEach(a => a.child?._updateRunningStyle()); + } + let children = this._box.get_children().filter(actor => { return actor.child && actor.child._delegate && @@ -405,7 +619,8 @@ const DashOverride = { } for (let i = 0; i < addedItems.length; i++) { - this._box.insert_child_at_index(addedItems[i].item, + this._box.insert_child_at_index( + addedItems[i].item, addedItems[i].pos); } @@ -450,14 +665,14 @@ const DashOverride = { style_class: 'dash-separator', x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER, - width: this.iconSize, - height: 1, + width: opt.DASH_VERTICAL ? this.iconSize : 1, + height: opt.DASH_VERTICAL ? 1 : this.iconSize, }); this._box.add_child(this._separator); } // FIXME: separator placement is broken (also in original dash) - let pos = nFavorites; + let pos = nFavorites + this._animatingPlaceholdersCount; if (this._dragPlaceholder) pos++; this._box.set_child_at_index(this._separator, pos); @@ -474,8 +689,13 @@ const DashOverride = { let appIcon = new Dash.DashIcon(app); let indicator = appIcon._dot; - indicator.x_align = opt.DASH_LEFT ? Clutter.ActorAlign.START : Clutter.ActorAlign.END; - indicator.y_align = Clutter.ActorAlign.CENTER; + if (opt.DASH_VERTICAL) { + indicator.x_align = opt.DASH_LEFT ? Clutter.ActorAlign.START : Clutter.ActorAlign.END; + indicator.y_align = Clutter.ActorAlign.CENTER; + } else { + indicator.x_align = Clutter.ActorAlign.CENTER; + indicator.y_align = Clutter.ActorAlign.END; + } appIcon.connect('menu-state-changed', (o, opened) => { @@ -495,68 +715,7 @@ const DashOverride = { return item; }, -}; - -const DashItemContainerCommon = { - // move labels according dash position - showLabel() { - if (!this._labelText) - return; - - this.label.set_text(this._labelText); - this.label.opacity = 0; - this.label.show(); - - let [stageX, stageY] = this.get_transformed_position(); - - const itemWidth = this.allocation.get_width(); - const itemHeight = this.allocation.get_height(); - - const labelWidth = this.label.get_width(); - const labelHeight = this.label.get_height(); - 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(); - let y; - - 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); - xOffset = 4; - - x = stageX - xOffset - this.label.width; - y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); - } else if (opt.DASH_LEFT) { - const yOffset = Math.floor((itemHeight - labelHeight) / 2); - xOffset = 4; - - x = stageX + this.width + xOffset; - y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); - } - - this.label.set_position(x, y); - this.label.ease({ - opacity: 255, - duration: DASH_ITEM_LABEL_SHOW_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - - this.label.set_position(x, y); - this.label.ease({ - opacity: 255, - 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 @@ -585,6 +744,8 @@ const DashCommon = { if (this._recentFilesIcon) iconChildren.push(this._recentFilesIcon); + if (this._extensionsIcon) + iconChildren.push(this._extensionsIcon); if (!iconChildren.length) return; @@ -706,168 +867,313 @@ const DashCommon = { this._adjustingInProgress = false; }, + + 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()) + return DND.DragMotionResult.NO_DROP; + if (!global.settings.is_writable('favorite-apps')) + return DND.DragMotionResult.NO_DROP; + let favorites = AppFavorites.getAppFavorites().getFavorites(); + let numFavorites = favorites.length; + + let favPos = favorites.indexOf(app); + + let children = this._box.get_children(); + let numChildren = children.length; + let boxSize = opt.DASH_VERTICAL ? this._box.height : this._box.width; + + // Keep the placeholder out of the index calculation; assuming that + // the remove target has the same size as "normal" items, we don't + // need to do the same adjustment there. + if (this._dragPlaceholder) { + boxSize -= opt.DASH_VERTICAL ? this._dragPlaceholder.height : this._dragPlaceholder.width; + numChildren--; + } + + // Same with the separator + if (this._separator) { + boxSize -= opt.DASH_VERTICAL ? this._separator.height : this._separator.width; + numChildren--; + } + + let pos; + if (this._emptyDropTarget) + pos = 0; // always insert at the start when dash is empty + else if (this.text_direction === Clutter.TextDirection.RTL) + pos = numChildren - Math.floor((opt.DASH_VERTICAL ? y : x) * numChildren / boxSize); + else + pos = Math.floor((opt.DASH_VERTICAL ? y : x) * numChildren / boxSize); + + // Put the placeholder after the last favorite if we are not + // in the favorites zone + if (pos > numFavorites) + pos = numFavorites; + + if (pos !== this._dragPlaceholderPos && this._animatingPlaceholdersCount === 0) { + this._dragPlaceholderPos = pos; + + // Don't allow positioning before or after self + if (favPos !== -1 && (pos === favPos || pos === favPos + 1)) { + this._clearDragPlaceholder(); + return DND.DragMotionResult.CONTINUE; + } + + // If the placeholder already exists, we just move + // it, but if we are adding it, expand its size in + // an animation + let fadeIn; + if (this._dragPlaceholder) { + this._dragPlaceholder.destroy(); + fadeIn = false; + } else { + fadeIn = true; + } + + this._dragPlaceholder = new Dash.DragPlaceholderItem(); + this._dragPlaceholder.child.set_width(this.iconSize / (opt.DASH_VERTICAL ? 2 : 1)); + this._dragPlaceholder.child.set_height(this.iconSize / (opt.DASH_VERTICAL ? 1 : 2)); + this._box.insert_child_at_index( + this._dragPlaceholder, + this._dragPlaceholderPos); + this._dragPlaceholder.show(fadeIn); + } + + if (!this._dragPlaceholder) + return DND.DragMotionResult.NO_DROP; + + let srcIsFavorite = favPos !== -1; + + if (srcIsFavorite) + return DND.DragMotionResult.MOVE_DROP; + + return DND.DragMotionResult.COPY_DROP; + }, }; 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)); + if (opt.DASH_ICON_SCROLL && !Me.Util.dashNotDefault()) { + this._scrollConId = this.connect('scroll-event', DashExtensions.onScrollEvent.bind(this)); + this._leaveConId = this.connect('leave-event', DashExtensions.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; - } + popupMenu() { + const side = opt.DASH_VERTICAL ? St.Side.LEFT : St.Side.BOTTOM; + AppIconCommon.popupMenu.bind(this)(side); + }, - 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; + _updateRunningStyle() { + const currentWs = global.workspace_manager.get_active_workspace(); + const show = opt.DASH_ISOLATE_WS + ? this.app.get_windows().filter(w => w.get_workspace() === currentWs).length + : this.app.state !== Shell.AppState.STOPPED; - // avoid uncontrollable switching if smooth scroll wheel or trackpad is used - if (this._lastScroll && Date.now() - this._lastScroll < 160) - return Clutter.EVENT_STOP; + if (show) + this._dot.show(); + else + this._dot.hide(); + }, +}; - this._lastScroll = Date.now(); - - _switchWindow.bind(this)(direction); - return Clutter.EVENT_STOP; -} - -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()); +const DashExtensions = { + onScrollEvent(source, event) { + if ((this.app && !opt.DASH_ICON_SCROLL) || (this._isSearchWindowsIcon && !opt.SEARCH_WINDOWS_ICON_SCROLL)) { + if (this._scrollConId) { + this.disconnect(this._scrollConId); + this._scrollConId = 0; + } + if (this._leaveConId) { + this.disconnect(this._leaveConId); + this._leaveConId = 0; + } + return Clutter.EVENT_PROPAGATE; } - } - let windows = this._scrolledWindows; + if (Main.overview._overview.controls._stateAdjustment.value > 1) + return Clutter.EVENT_PROPAGATE; + + let direction = Me.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; - if (!windows.length) - return; + this._lastScroll = Date.now(); - // if window selection is in the process, the previewed window must be the current one - let currentWin = this._selectedMetaWin ? this._selectedMetaWin : windows[0]; + DashExtensions.switchWindow.bind(this)(direction); + return Clutter.EVENT_STOP; + }, - const currentIdx = windows.indexOf(currentWin); - let targetIdx = currentIdx + direction; + onLeaveEvent() { + if (!this._selectedMetaWin || this.has_pointer || this.toggleButton?.has_pointer) + return; - if (targetIdx > windows.length - 1) - targetIdx = 0; - else if (targetIdx < 0) - targetIdx = windows.length - 1; + this._selectedPreview._activateSelected = false; + this._selectedMetaWin = null; + this._scrolledWindows = null; + DashExtensions.showWindowPreview.bind(this)(null); + }, - 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); - }); + switchWindow(direction) { + if (!this._scrolledWindows) { + this._initialSelection = true; + // source is app icon + if (this.app) { + this._scrolledWindows = this.app.get_windows(); + if (opt.DASH_ISOLATE_WS) { + const currentWs = global.workspaceManager.get_active_workspace(); + this._scrolledWindows = this._scrolledWindows.filter(w => w.get_workspace() === currentWs); + } - viewsIter.forEach(view => { - // if workspaces are on primary monitor only - if (!view || !view._workspaces) + 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; - view._workspaces.forEach(ws => { - ws._windows.forEach(windowPreview => { - // metaWin === null resets opacity - let opacity = metaWin ? 50 : 255; - windowPreview._activateSelected = false; + // 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; + const focusWindow = AltTab.getWindows(null)[0]; + const appFocused = this._scrolledWindows[0] === focusWindow && this._scrolledWindows[0].get_workspace() === global.workspace_manager.get_active_workspace(); + // only if the app has focus, immediately switch to the previous window + // otherwise just set the current window above others + if (!this._initialSelection || appFocused) + targetIdx += direction; + else + this._initialSelection = false; - // minimized windows are invisible if windows are not exposed (WORKSPACE_MODE === 0) - if (!windowPreview.opacity) - windowPreview.opacity = 255; + if (targetIdx > windows.length - 1) + targetIdx = 0; + else if (targetIdx < 0) + targetIdx = windows.length - 1; - // 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()); + const metaWin = windows[targetIdx]; + DashExtensions.showWindowPreview.bind(this)(metaWin); + this._selectedMetaWin = metaWin; + }, - windowPreview.get_parent().set_child_above_sibling(windowPreview, null); + 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); + }); - opacity = 255; - this._selectedPreview = windowPreview; - windowPreview._activateSelected = true; - } + viewsIter.forEach(view => { + // if workspaces are on primary monitor only + if (!view || !view._workspaces) + return; - // 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, - }); - } + 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()); + if (_timeouts.wsSwitcherAnimation) + GLib.source_remove(_timeouts.wsSwitcherAnimation); + // setting window preview above siblings before workspace switcher animation has no effect + // we need to set the window above after the ws preview become visible on the screen + // the default switcher animation time is 250, 200 ms delay should be enough + _timeouts.wsSwitcherAnimation = GLib.timeout_add(0, 200 * St.Settings.get().slow_down_factor, () => { + windowPreview.get_parent().set_child_above_sibling(windowPreview, null); + _timeouts.wsSwitcherAnimation = 0; + return GLib.SOURCE_REMOVE; + }); + } else { + 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 = { + after__init() { + if (this._updateRunningDotStyle) + this._updateRunningDotStyle(); + }, + + _updateRunningDotStyle() { + if (opt.RUNNING_DOT_STYLE) + this._dot.add_style_class_name('app-well-app-running-dot-custom'); + else + this._dot.remove_style_class_name('app-well-app-running-dot-custom'); + }, + 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 isCtrlPressed = Me.Util.isCtrlPressed(state); + const isShiftPressed = Me.Util.isShiftPressed(state); const currentWS = global.workspace_manager.get_active_workspace(); - const appRecentWorkspace = _getAppRecentWorkspace(this.app); + const appRecentWorkspace = this._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; @@ -882,6 +1188,12 @@ const AppIconCommon = { ); } + const openNewWindow = this.app.can_open_new_window() && + this.app.state === Shell.AppState.RUNNING && + (((isCtrlPressed || isMiddleButton) && !opt.DASH_CLICK_OPEN_NEW_WIN) || + (opt.DASH_CLICK_OPEN_NEW_WIN && !this._selectedMetaWin && !isMiddleButton) || + ((opt.DASH_CLICK_PREFER_WORKSPACE || opt.DASH_ISOLATE_WS) && !targetWindowOnCurrentWs)); + if ((this.app.state === Shell.AppState.STOPPED || openNewWindow) && !isShiftPressed) this.animateLaunch(); @@ -890,19 +1202,23 @@ const AppIconCommon = { // 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) { + } else if (showWidowsBeforeActivation && opt.OVERVIEW_MODE2 && !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) { + } else if (((opt.DASH_SHIFT_CLICK_MV && isShiftPressed) || ((opt.DASH_CLICK_PREFER_WORKSPACE || opt.DASH_ISOLATE_WS) && !openNewWindow)) && this.app.get_windows().length) { this._moveAppToCurrentWorkspace(); + if (opt.DASH_ISOLATE_WS) { + this.app.activate(); + // hide the overview after the window is re-created + GLib.idle_add(GLib.PRIORITY_LOW, () => Main.overview.hide()); + } return; } else if (isShiftPressed) { return; @@ -918,8 +1234,7 @@ const AppIconCommon = { }, popupMenu(side = St.Side.LEFT) { - if (shellVersion >= 42) - this.setForcedHighlight(true); + this.setForcedHighlight(true); this._removeMenuTimeout(); this.fake_release(); @@ -939,7 +1254,7 @@ const AppIconCommon = { } if (!this._menu) { - this._menu = new AppMenu(this, side, { + this._menu = new AppMenu.AppMenu(this, side, { favoritesSection: true, showSingleWindows: true, }); @@ -986,8 +1301,12 @@ const AppIconCommon = { }]); } - if (/* opt.APP_MENU_MV_TO_WS && */this._windowsOnOtherWs()) - popupItems.push([_('Move App to Current Workspace ( Shift + Click )'), this._moveAppToCurrentWorkspace]); + popupItems.push([_('Move App to Current Workspace ( Shift + Click )'), this._moveAppToCurrentWorkspace]); + if (opt.WINDOW_THUMBNAIL_ENABLED) { + popupItems.push([_('Create Window Thumbnail - PIP'), () => { + Me.Modules.winTmbModule.createThumbnail(this.app.get_windows()[0]); + }]); + } } this._addedMenuItems = []; @@ -996,6 +1315,8 @@ const AppIconCommon = { let item = new PopupMenu.PopupMenuItem(i[0]); this._menu.addMenuItem(item); item.connect('activate', i[1].bind(this)); + if (i[1] === this._moveAppToCurrentWorkspace && !this._windowsOnOtherWs()) + item.setSensitive(false); this._addedMenuItems.push(item); }); @@ -1007,71 +1328,33 @@ const AppIconCommon = { 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); - if (dash._showWindowsIconClickedId) - dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId); - dash._showWindowsIconClickedId = undefined; - if (dash._showWindowsIcon) - dash._showWindowsIcon.destroy(); - dash._showWindowsIcon = undefined; - } - - if (!show || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) - return; - - if (!dash._showWindowsIcon) { - dash._showWindowsIcon = new ShowWindowsIcon(); - dash._showWindowsIcon.show(false); - dashContainer.add_child(dash._showWindowsIcon); - dash._hookUpLabel(dash._showWindowsIcon); - } + _getWindowApp(metaWin) { + const tracker = Shell.WindowTracker.get_default(); + return tracker.get_window_app(metaWin); + }, - 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) { - const index = dashContainer.get_children().length - 1; - dashContainer.set_child_at_index(dash._showWindowsIcon, index); - } + _getAppLastUsedWindow(app) { + let recentWin; + global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { + const winApp = this._getWindowApp(metaWin); + if (!recentWin && winApp === app) + recentWin = metaWin; + }); + return recentWin; + }, - Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); -} + _getAppRecentWorkspace(app) { + const recentWin = this._getAppLastUsedWindow(app); + if (recentWin) + return recentWin.get_workspace(); -const ShowWindowsIcon = GObject.registerClass( -class ShowWindowsIcon extends Dash.DashItemContainer { - _init() { - super._init(); + return null; + }, +}; +const ShowWindowsIcon = { + _afterInit() { this._isSearchWindowsIcon = true; this._labelText = _('Search Open Windows (Hotkey: Space)'); this.toggleButton = new St.Button({ @@ -1096,10 +1379,10 @@ class ShowWindowsIcon extends Dash.DashItemContainer { 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)); + this._scrollConId = this.connect('scroll-event', DashExtensions.onScrollEvent.bind(this)); + this._leaveConId = this.connect('leave-event', DashExtensions.onLeaveEvent.bind(this)); } - } + }, _createIcon(size) { this._iconActor = new St.Icon({ @@ -1109,50 +1392,47 @@ class ShowWindowsIcon extends Dash.DashItemContainer { track_hover: true, }); return this._iconActor; - } -}); - -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); - if (dash._recentFilesIconClickedId) - dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId); - dash._recentFilesIconClickedId = undefined; - if (dash._recentFilesIcon) - dash._recentFilesIcon.destroy(); - dash._recentFilesIcon = undefined; - } + }, +}; - if (!show || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED) - return; +const ShowRecentFilesIcon = { + _afterInit() { + this._labelText = _('Search Recent Files (Hotkey: Ctrl + Space)'); + this.toggleButton = new St.Button({ + style_class: 'show-apps', + track_hover: true, + can_focus: true, + toggle_mode: false, + }); - if (!dash._recentFilesIcon) { - dash._recentFilesIcon = new ShowRecentFilesIcon(); - dash._recentFilesIcon.show(false); - dashContainer.add_child(dash._recentFilesIcon); - dash._hookUpLabel(dash._recentFilesIcon); - } + this._iconActor = null; + this.icon = new IconGrid.BaseIcon(this.labelText, { + setSizeManually: true, + showLabel: false, + createIcon: this._createIcon.bind(this), + }); + this.icon.y_align = Clutter.ActorAlign.CENTER; - 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) { - const index = dashContainer.get_children().length - 1; - dashContainer.set_child_at_index(dash._recentFilesIcon, index); - } + this.toggleButton.add_actor(this.icon); + this.toggleButton._delegate = this; - Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); -} + this.setChild(this.toggleButton); + }, -const ShowRecentFilesIcon = GObject.registerClass( -class ShowRecentFilesIcon extends Dash.DashItemContainer { - _init() { - super._init(); + _createIcon(size) { + this._iconActor = new St.Icon({ + icon_name: 'document-open-recent-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }); + return this._iconActor; + }, +}; - this._labelText = _('Search Recent Files (Hotkey: Ctrl + Space)'); +const ShowExtensionsIcon = { + _afterInit() { + this._labelText = _('Search Extensions (Hotkey: Ctrl + Shift + Space)'); this.toggleButton = new St.Button({ style_class: 'show-apps', track_hover: true, @@ -1172,15 +1452,55 @@ class ShowRecentFilesIcon extends Dash.DashItemContainer { this.toggleButton._delegate = this; this.setChild(this.toggleButton); - } + }, _createIcon(size) { this._iconActor = new St.Icon({ - icon_name: 'document-open-recent-symbolic', + icon_name: 'application-x-addon-symbolic', icon_size: size, style_class: 'show-apps-icon', track_hover: true, }); return this._iconActor; - } -}); + }, +}; + +const AppMenuCommon = { + _updateWindowsSection() { + if (global.compositor) { + if (this._updateWindowsLaterId) { + const laters = global.compositor.get_laters(); + laters.remove(this._updateWindowsLaterId); + } + } else if (this._updateWindowsLaterId) { + Meta.later_remove(this._updateWindowsLaterId); + } + + this._updateWindowsLaterId = 0; + + this._windowSection.removeAll(); + this._openWindowsHeader.hide(); + + if (!this._app) + return; + + const minWindows = this._showSingleWindows ? 1 : 2; + const currentWs = global.workspaceManager.get_active_workspace(); + const isolateWs = opt.DASH_ISOLATE_WS && !Main.overview.dash.showAppsButton.checked; + const windows = this._app.get_windows().filter(w => !w.skip_taskbar && (isolateWs ? w.get_workspace() === currentWs : true)); + if (windows.length < minWindows) + return; + + this._openWindowsHeader.show(); + + windows.forEach(window => { + const title = window.title || this._app.get_name(); + const item = this._windowSection.addAction(title, event => { + Main.activateWindow(window, event.get_time()); + }); + window.connectObject('notify::title', () => { + item.label.text = window.title || this._app.get_name(); + }, item); + }); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/extensionsSearchProvider.js b/extensions/44/vertical-workspaces/lib/extensionsSearchProvider.js new file mode 100644 index 0000000..5d0f28a --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/extensionsSearchProvider.js @@ -0,0 +1,423 @@ +/** +* V-Shell (Vertical Workspaces) + * extensionsSearchProvider.js + * + * @author GdH <G-dH@github.com> + * @copyright 2022 - 2023 + * @license GPL-3.0 + */ + +'use strict'; + +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const St = imports.gi.St; +const Shell = imports.gi.Shell; +const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; + +const Main = imports.ui.main; + +const ExtensionState = { + 1: 'ENABLED', + 2: 'DISABLED', + 3: 'ERROR', + 4: 'INCOMPATIBLE', + 5: 'DOWNLOADING', + 6: 'INITIALIZED', + 7: 'DISABLING', + 8: 'ENABLING', +}; + +let Me; +let opt; +// gettext +let _; +let _toggleTimeout; + +// prefix helps to eliminate results from other search providers +// so it needs to be something less common +// needs to be accessible from vw module +const PREFIX = 'eq//'; + +var ExtensionsSearchProviderModule = class { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._extensionsSearchProvider = null; + this._enableTimeoutId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + if (_toggleTimeout) + GLib.source_remove(_toggleTimeout); + + this.moduleEnabled = opt.get('extensionsSearchProviderModule'); + + reset = reset || !this.moduleEnabled; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' ExtensionsSearchProviderModule - Keeping untouched'); + } + + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._extensionsSearchProvider) { + this._extensionsSearchProvider = new extensionsSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._extensionsSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } + ); + console.debug(' ExtensionsSearchProviderModule - Activated'); + } + + _disableModule() { + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } + + if (this._extensionsSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._extensionsSearchProvider); + this._extensionsSearchProvider = null; + } + + console.debug(' ExtensionsSearchProviderModule - Disabled'); + } + + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +}; + +class extensionsSearchProvider { + constructor() { + this.id = 'extensions'; + const appSystem = Shell.AppSystem.get_default(); + let appInfo = appSystem.lookup_app('com.matjakeman.ExtensionManager.desktop')?.get_app_info(); + if (!appInfo) + appInfo = appSystem.lookup_app('org.gnome.Extensions.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/gnome-extensions-app', 'Extensions', null); + appInfo.get_description = () => _('Search extensions'); + appInfo.get_name = () => _('Extensions'); + appInfo.get_id = () => 'org.gnome.Extensions.desktop'; + appInfo.get_icon = () => Gio.icon_new_for_string('application-x-addon'); + appInfo.should_show = () => true; + + this.appInfo = appInfo; + this.canLaunchSearch = true; + this.isRemoteProvider = false; + } + + getInitialResultSet(terms, callback /* , cancellable = null*/) { + // In GS 43 callback arg has been removed + /* if (Me.shellVersion >= 43) + cancellable = callback; */ + + const extensions = {}; + Main.extensionManager._extensions.forEach( + e => { + extensions[e.uuid] = e; + } + ); + this.extensions = extensions; + + if (Me.shellVersion >= 43) + return new Promise(resolve => resolve(this._getResultSet(terms))); + else + callback(this._getResultSet(terms)); + + return null; + } + + _getResultSet(terms) { + // do not modify original terms + let termsCopy = [...terms]; + // search for terms without prefix + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); + + const candidates = this.extensions; + const _terms = [].concat(termsCopy); + + const term = _terms.join(' '); + + const results = []; + let m; + for (let id in candidates) { + const extension = this.extensions[id]; + const text = extension.metadata.name + (extension.state === 1 ? 'enabled' : '') + ([6, 2].includes(extension.state) ? 'disabled' : ''); + if (opt.SEARCH_FUZZY) + m = Me.Util.fuzzyMatch(term, text); + else + m = Me.Util.strictMatch(term, text); + + if (m !== -1) + results.push({ weight: m, id }); + } + + // sort alphabetically + results.sort((a, b) => this.extensions[a.id].metadata.name.localeCompare(this.extensions[b.id].metadata.name)); + // enabled first + // results.sort((a, b) => this.extensions[a.id].state !== 1 && this.extensions[b.id].state === 1); + // incompatible last + results.sort((a, b) => this.extensions[a.id].state === 4 && this.extensions[b.id].state !== 4); + + this.resultIds = results.map(item => item.id); + return this.resultIds; + } + + getResultMetas(resultIds, callback = null) { + const metas = resultIds.map(id => this.getResultMeta(id)); + if (Me.shellVersion >= 43) + return new Promise(resolve => resolve(metas)); + else if (callback) + callback(metas); + return null; + } + + getResultMeta(resultId) { + const result = this.extensions[resultId]; + + const versionName = result.metadata['version-name'] ?? ''; + let version = result.metadata['version'] ?? ''; + version = versionName && version ? `/${version}` : version; + const versionStr = `${versionName}${version}`; + + return { + 'id': resultId, + 'name': `${result.metadata.name}`, + 'version': versionStr, + 'description': versionStr, // description will be updated in result object + 'createIcon': size => { + let icon = this.getIcon(result, size); + return icon; + }, + }; + } + + getIcon(extension, size) { + let opacity = 0; + let iconName = 'process-stop-symbolic'; + + switch (extension.state) { + case 1: + if (extension.hasUpdate) + iconName = 'software-update-available'; // 'software-update-available-symbolic'; + else + iconName = 'object-select';// 'object-select-symbolic'; + + opacity = 255; + break; + case 3: + if (Main.extensionManager._enabledExtensions.includes(extension.uuid)) + iconName = 'emblem-ok-symbolic'; + else + iconName = 'dialog-error'; + opacity = 100; + break; + case 4: + iconName = 'software-update-urgent'; // 'software-update-urgent-symbolic'; + opacity = 100; + break; + } + + if (extension.hasUpdate) { + iconName = 'software-update-available'; // 'software-update-available-symbolic'; + opacity = 100; + } + + const icon = new St.Icon({ icon_name: iconName, icon_size: size }); + icon.set({ + reactive: true, + opacity, + }); + + return icon; + } + + createResultObject(meta) { + return new ListSearchResult(this, meta, this.extensions[meta.id]); + } + + launchSearch(terms, timeStamp) { + this.appInfo.launch([], global.create_app_launch_context(timeStamp, -1), null); + } + + activateResult(resultId/* terms, timeStamp*/) { + const extension = this.extensions[resultId]; + if (Me.Util.isShiftPressed()) + this._toggleExtension(extension); + else if (extension.hasPrefs) + Me.Util.openPreferences(extension.metadata); + } + + filterResults(results /* , maxResults*/) { + // return results.slice(0, maxResults); + return results; + } + + getSubsearchResultSet(previousResults, terms, callback) { + if (Me.shellVersion < 43) { + this.getSubsearchResultSet42(terms, callback); + return null; + } + return this.getInitialResultSet(terms); + } + + getSubsearchResultSet42(terms, callback) { + callback(this._getResultSet(terms)); + } +} + +const ListSearchResult = GObject.registerClass( +class ListSearchResult extends St.Button { + _init(provider, metaInfo, extension) { + this.provider = provider; + this.metaInfo = metaInfo; + this.extension = extension; + + super._init({ + reactive: true, + can_focus: true, + track_hover: true, + }); + + this.style_class = 'list-search-result'; + + let content = new St.BoxLayout({ + style_class: 'list-search-result-content', + vertical: false, + x_align: Clutter.ActorAlign.START, + x_expand: true, + y_expand: true, + }); + this.set_child(content); + + let titleBox = new St.BoxLayout({ + style_class: 'list-search-result-title', + y_align: Clutter.ActorAlign.CENTER, + }); + + content.add_child(titleBox); + + // An icon for, or thumbnail of, content + let icon = this.metaInfo['createIcon'](this.ICON_SIZE); + let iconBox = new St.Button(); + iconBox.set_child(icon); + titleBox.add(iconBox); + iconBox.set_style('border: 1px solid rgba(200,200,200,0.2); padding: 2px; border-radius: 8px;'); + this._iconBox = iconBox; + this.icon = icon; + + iconBox.connect('clicked', () => { + this._toggleExtension(); + return Clutter.EVENT_STOP; + }); + + let title = new St.Label({ + text: this.metaInfo['name'], + y_align: Clutter.ActorAlign.CENTER, + opacity: extension.hasPrefs ? 255 : 150, + }); + titleBox.add_child(title); + + this.label_actor = title; + + this._descriptionLabel = new St.Label({ + style_class: 'list-search-result-description', + y_align: Clutter.ActorAlign.CENTER, + }); + content.add_child(this._descriptionLabel); + + this._highlightTerms(); + + this.connect('destroy', () => { + if (_toggleTimeout) { + GLib.source_remove(_toggleTimeout); + _toggleTimeout = 0; + } + }); + } + + _toggleExtension() { + const state = this.extension.state; + if (![1, 2, 6, 3].includes(state) || this.extension.metadata.uuid.includes('vertical-workspaces')) + return; + + if ([2, 6].includes(state)) + Main.extensionManager.enableExtension(this.extension.uuid); + else if ([1, 3].includes(state)) + Main.extensionManager.disableExtension(this.extension.uuid); + + if (_toggleTimeout) + GLib.source_remove(_toggleTimeout); + + _toggleTimeout = GLib.timeout_add(GLib.PRIORITY_LOW, 200, + () => { + if ([7, 8].includes(this.extension.state)) + return GLib.SOURCE_CONTINUE; + + this.icon?.destroy(); + this.icon = this.metaInfo['createIcon'](this.ICON_SIZE); + this._iconBox.set_child(this.icon); + this._highlightTerms(); + + _toggleTimeout = 0; + return GLib.SOURCE_REMOVE; + } + ); + } + + get ICON_SIZE() { + return 24; + } + + _highlightTerms() { + const extension = this.extension; + const state = extension.state === 4 ? ExtensionState[this.extension.state] : ''; + const error = extension.state === 3 ? ` ERROR: ${this.extension.error}` : ''; + const update = extension.hasUpdate ? ' | UPDATE PENDING' : ''; + const text = `${this.metaInfo.version} ${state}${error}${update}`; + let markup = text;// this.metaInfo['description'].split('\n')[0]; + this._descriptionLabel.clutter_text.set_markup(markup); + } + + vfunc_clicked() { + this.activate(); + } + + activate() { + this.provider.activateResult(this.metaInfo.id); + + if (this.metaInfo.clipboardText) { + St.Clipboard.get_default().set_text( + St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText); + } + Main.overview.toggle(); + } +}); diff --git a/extensions/44/vertical-workspaces/lib/iconGrid.js b/extensions/44/vertical-workspaces/lib/iconGrid.js index 1aa980e..1f7516b 100644 --- a/extensions/44/vertical-workspaces/lib/iconGrid.js +++ b/extensions/44/vertical-workspaces/lib/iconGrid.js @@ -9,11 +9,13 @@ */ 'use strict'; -const { GLib, St, Meta } = imports.gi; + +const St = imports.gi.St; + const IconGrid = imports.ui.iconGrid; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; -const shellVersion = _Util.shellVersion; + +let Me; +let opt; // added sizes for better scaling const IconSize = { @@ -29,47 +31,58 @@ const IconSize = { LARGE: 96, 80: 80, 64: 64, - 48: 48, - TINY: 32, + TINY: 48, }; 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; +var IconGridModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // don't even touch this module if disabled - if (_firstRun && reset) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } - _firstRun = false; + cleanGlobals() { + Me = null; + opt = null; + } - if (_overrides) - _overrides.removeAll(); + update(reset) { + this.moduleEnabled = opt.get('appDisplayModule'); + // if notifications are enabled no override is needed + reset = reset || !this.moduleEnabled; + + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + } + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); - if (reset) { - _overrides = null; - opt = null; - return; + if (Me.shellVersion < 43 && IconGridCommon._findBestModeForSize) { + IconGridCommon['findBestModeForSize'] = IconGridCommon._findBestModeForSize; + delete IconGridCommon['_findBestModeForSize']; + } + this._overrides.addOverride('IconGrid', IconGrid.IconGrid.prototype, IconGridCommon); + this._overrides.addOverride('IconGridLayout', IconGrid.IconGridLayout.prototype, IconGridLayoutCommon); } - _overrides = new _Util.Overrides(); - - if (shellVersion < 43 && IconGridCommon._findBestModeForSize) { - IconGridCommon['findBestModeForSize'] = IconGridCommon._findBestModeForSize; - IconGridCommon['_findBestModeForSize'] = undefined; + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; } - _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) { @@ -87,7 +100,7 @@ const IconGridCommon = { return; const { pagePadding } = this.layout_manager; const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const iconPadding = 53 * scaleFactor; + const iconPadding = 51 * scaleFactor; // provided width is usually about 100px wider in horizontal orientation with prev/next page indicators const pageIndicatorCompensation = opt.ORIENTATION ? 0 : PAGE_WIDTH_CORRECTION; @@ -99,8 +112,13 @@ const IconGridCommon = { this.layoutManager._gridWidth = width; this.layoutManager._gridHeight = height; + width -= 80; // compensation for default padding + height -= 80; + 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; + // set the icon size as fixed to avoid changes in size later + const iconSize = opt.APP_GRID_ICON_SIZE > 0 ? opt.APP_GRID_ICON_SIZE : opt.APP_GRID_ICON_SIZE_DEFAULT; + const itemSize = iconSize * scaleFactor + iconPadding; // if this._gridModes.length === 1, custom grid should be used // if (iconSize > 0 && this._gridModes.length > 1) { let columns = opt.APP_GRID_COLUMNS; @@ -109,17 +127,20 @@ const IconGridCommon = { let unusedSpaceH = -1; let unusedSpaceV = -1; if (!columns) { - columns = Math.floor(width / (iconSize + iconPadding)) + 1; + // calculate #columns + 1 without spacing + columns = Math.floor(width / itemSize) + 1; + // check if columns with spacing fits the available width + // and reduce the number until it fits while (unusedSpaceH < 0) { columns -= 1; - unusedSpaceH = width - columns * (iconSize + iconPadding) - (columns - 1) * spacing; + unusedSpaceH = width - columns * itemSize - (columns - 1) * spacing; } } if (!rows) { - rows = Math.floor(height / (iconSize + iconPadding)) + 1; + rows = Math.floor(height / itemSize) + 1; while (unusedSpaceV < 0) { rows -= 1; - unusedSpaceV = height - rows * (iconSize + iconPadding) - (rows - 1) * spacing; + unusedSpaceV = height - rows * itemSize - ((rows - 1) * spacing); } } @@ -135,9 +156,16 @@ const IconGridLayoutCommon = { const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); const nColumns = this.columnsPerPage; const nRows = this.rowsPerPage; + + // if grid is not defined return default icon size + if (nColumns < 1 && nRows < 1) { + return this._isFolder + ? opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT : opt.APP_GRID_ICON_SIZE_DEFAULT; + } + const columnSpacingPerPage = opt.APP_GRID_SPACING * (nColumns - 1); const rowSpacingPerPage = opt.APP_GRID_SPACING * (nRows - 1); - const iconPadding = 53 * scaleFactor; + const iconPadding = 55 * scaleFactor; const paddingH = this._isFolder ? this.pagePadding.left + this.pagePadding.right : 0; const paddingV = this._isFolder ? this.pagePadding.top + this.pagePadding.bottom : 0; @@ -156,10 +184,14 @@ const IconGridLayoutCommon = { 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) + // limit max icon size for folders and fully adaptive folder grids, the whole range is for the main grid with active folders + if (this._isFolder && opt.APP_GRID_FOLDER_ADAPTIVE && opt.APP_GRID_FOLDER_ICON_SIZE < 0) + iconSizes = iconSizes.slice(iconSizes.indexOf(opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT), -1); + else if (this._isFolder) iconSizes = iconSizes.slice(iconSizes.indexOf(IconSize.LARGE), -1); + else if (opt.APP_GRID_ADAPTIVE && opt.APP_GRID_ICON_SIZE < 0) + iconSizes = iconSizes.slice(iconSizes.indexOf(opt.APP_GRID_ICON_SIZE_DEFAULT), -1); + let sizeInvalid = false; for (const size of iconSizes) { @@ -167,10 +199,9 @@ const IconGridLayoutCommon = { if (firstItem) { firstItem.icon.setIconSize(size); - const [firstItemWidth, firstItemHeight] = - firstItem.get_preferred_size(); + const [firstItemWidth] = firstItem.get_preferred_size(); - const itemSize = Math.max(firstItemWidth, firstItemHeight); + const itemSize = firstItemWidth; if (itemSize < size) sizeInvalid = true; @@ -199,7 +230,7 @@ const IconGridLayoutCommon = { removeItem(item) { if (!this._items.has(item)) { - log(`Item ${item} is not part of the IconGridLayout`); + console.error(`Item ${item} is not part of the IconGridLayout`); return; // throw new Error(`Item ${item} is not part of the IconGridLayout`); } @@ -215,13 +246,13 @@ const IconGridLayoutCommon = { addItem(item, page = -1, index = -1) { if (this._items.has(item)) { - log(`iconGrid: Item ${item} already added to IconGridLayout`); + console.error(`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}`); + console.error(`iconGrid: Cannot add ${item} to page ${page}`); page = -1; index = -1; // throw new Error(`Cannot add ${item} to page ${page}`); @@ -240,7 +271,7 @@ const IconGridLayoutCommon = { moveItem(item, newPage, newPosition) { if (!this._items.has(item)) { - log(`iconGrid: Item ${item} is not part of the IconGridLayout`); + console.error(`iconGrid: Item ${item} is not part of the IconGridLayout`); return; // throw new Error(`Item ${item} is not part of the IconGridLayout`); } diff --git a/extensions/44/vertical-workspaces/lib/layout.js b/extensions/44/vertical-workspaces/lib/layout.js index f6562fd..6e72645 100644 --- a/extensions/44/vertical-workspaces/lib/layout.js +++ b/extensions/44/vertical-workspaces/lib/layout.js @@ -10,67 +10,107 @@ 'use strict'; -const { Meta, GLib, Shell, Clutter, GObject } = imports.gi; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const Gio = imports.gi.Gio; -const Main = imports.ui.main; const Layout = imports.ui.layout; -const Ripples = imports.ui.ripples; -const DND = imports.ui.dnd; +const Main = imports.ui.main; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; +let Me; +let opt; -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; +var LayoutModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _timeouts = {}; - // don't even touch this module if disabled or in conflict - if (_firstRun && (reset || conflict)) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._originalUpdateHotCorners = null; + } - _firstRun = false; + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this._removeTimeouts(); + + this.moduleEnabled = opt.get('layoutModule'); + const conflict = Me.Util.getEnabledExtensions('custom-hot-corners').length || + Me.Util.getEnabledExtensions('dash-to-panel').length; - if (!_originalUpdateHotCorners) - _originalUpdateHotCorners = Layout.LayoutManager.prototype._updateHotCorners; + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Layout" module disabled due to potential conflict with another extension`); - if (_overrides) - _overrides.removeAll(); + reset = reset || !this.moduleEnabled || conflict; - if (_timeouts) { - Object.values(_timeouts).forEach(t => { - if (t) - GLib.source_remove(t); - }); + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' LayoutModule - Keeping untouched'); } - if (reset) { - _overrides = null; - opt = null; - _timeouts = null; - Main.layoutManager._updateHotCorners = _originalUpdateHotCorners; + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + this._overrides.addOverride('LayoutManager', Main.layoutManager, LayoutManagerCommon); + this._overrides.addOverride('HotCorner', Layout.HotCorner.prototype, HotCornerCommon); + + Main.layoutManager._updatePanelBarrier(); Main.layoutManager._updateHotCorners(); - return; + + if (!this._hotCornersEnabledConId) { + this._interfaceSettings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.interface', + }); + this._hotCornersEnabledConId = this._interfaceSettings.connect('changed::enable-hot-corners', + () => Main.layoutManager._updateHotCorners()); + } + + console.debug(' LayoutModule - Activated'); } - _timeouts = {}; + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; - _overrides = new _Util.Overrides(); - _overrides.addOverride('LayoutManager', Layout.LayoutManager.prototype, LayoutManagerCommon); + Main.layoutManager._updateHotCorners(); - Main.layoutManager._updateHotCorners = LayoutManagerCommon._updateHotCorners.bind(Main.layoutManager); + if (this._hotCornersEnabledConId) { + this._interfaceSettings.disconnect(this._hotCornersEnabledConId); + this._hotCornersEnabledConId = 0; + this._interfaceSettings = null; + } - Main.layoutManager._updatePanelBarrier(); - Main.layoutManager._updateHotCorners(); -} + console.debug(' LayoutModule - Disabled'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } +}; const LayoutManagerCommon = { _updatePanelBarrier() { @@ -84,7 +124,7 @@ const LayoutManagerCommon = { this._leftPanelBarrier = null; } - if (!this.primaryMonitor || !opt) + if (!this.primaryMonitor || !opt || Me.Util.getEnabledExtensions('hidetopbar')) return; if (this.panelBox.height) { @@ -113,6 +153,7 @@ const LayoutManagerCommon = { // avoid errors if called from foreign override if (!opt) return; + // destroy old hot corners this.hotCorners.forEach(corner => corner?.destroy()); this.hotCorners = []; @@ -122,7 +163,7 @@ const LayoutManagerCommon = { return; } - let size = this.panelBox.height; + let size = this.panelBox.height ? this.panelBox.height : 27; // position 0 - default, 1-TL, 2-TR, 3-BL, 4-BR const position = opt.HOT_CORNER_POSITION; @@ -181,8 +222,8 @@ const LayoutManagerCommon = { } if (haveCorner) { - let corner = new HotCorner(this, monitor, cornerX, cornerY); - corner.setBarrierSize(size); + let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY); + corner.setBarrierSize(size, false); this.hotCorners.push(corner); } else { this.hotCorners.push(null); @@ -193,11 +234,8 @@ const LayoutManagerCommon = { }, }; -var HotCorner = GObject.registerClass( -class HotCorner extends Layout.HotCorner { - _init(layoutManager, monitor, x, y) { - super._init(layoutManager, monitor, x, y); - +const HotCornerCommon = { + after__init() { let angle = 0; switch (opt.HOT_CORNER_POSITION) { case 2: @@ -214,9 +252,14 @@ class HotCorner extends Layout.HotCorner { this._ripples._ripple1.rotation_angle_z = angle; this._ripples._ripple2.rotation_angle_z = angle; this._ripples._ripple3.rotation_angle_z = angle; - } + }, + + setBarrierSize(size, notMyCall = true) { + // ignore calls from the original _updateHotCorners() callback to avoid building barriers outside screen + if (notMyCall && size > 0) { + return; + } - setBarrierSize(size) { if (this._verticalBarrier) { this._pressureBarrier.removeBarrier(this._verticalBarrier); this._verticalBarrier.destroy(); @@ -232,8 +275,8 @@ class HotCorner extends Layout.HotCorner { 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; + const extendV = opt && opt.HOT_CORNER_ACTION && opt.HOT_CORNER_EDGE && opt.DASH_VERTICAL && monitor.index === primaryMonitor; + const extendH = opt && opt.HOT_CORNER_ACTION && opt.HOT_CORNER_EDGE && !opt.DASH_VERTICAL && monitor.index === primaryMonitor; if (opt.HOT_CORNER_POSITION <= 1) { this._verticalBarrier = new Meta.Barrier({ @@ -284,25 +327,52 @@ class HotCorner extends Layout.HotCorner { 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())) + if (Main.overview._shown) { 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())) + } else if ((opt.HOT_CORNER_ACTION === 2 && !Me.Util.isCtrlPressed()) || ([3, 4, 5, 6].includes(opt.HOT_CORNER_ACTION) && Me.Util.isCtrlPressed())) { + // Default overview + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 1) { + Main.overview.resetOverviewMode(); + this._toggleWindowPicker(true, true); + } else if ((opt.HOT_CORNER_ACTION === 3 && !Me.Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 2 && Me.Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 6 && Me.Util.isCtrlPressed())) { + // Applications this._toggleApplications(true); - else if (opt.HOT_CORNER_ACTION === 3 && !_Util.isCtrlPressed()) + } else if (opt.HOT_CORNER_ACTION === 4 && !Me.Util.isCtrlPressed()) { + // Overview - static ws preview + opt.OVERVIEW_MODE = 1; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 5 && !Me.Util.isCtrlPressed()) { + // Overview - static ws + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 6 && !Me.Util.isCtrlPressed()) { + // Window search provider + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; this._toggleWindowSearchProvider(); + } if (opt.HOT_CORNER_RIPPLES && Main.overview.animationInProgress) this._ripples.playAnimation(this._x, this._y); } - } + }, - _toggleWindowPicker(leaveOverview = false) { + _toggleWindowPicker(leaveOverview = false, customOverviewMode = false) { if (Main.overview._shown && (leaveOverview || !Main.overview.dash.showAppsButton.checked)) { Main.overview.hide(); } else if (Main.overview.dash.showAppsButton.checked) { @@ -320,17 +390,17 @@ class HotCorner extends Layout.HotCorner { // delay cannot be too short 200, () => { - Main.overview.show(); + Main.overview.show(1, customOverviewMode); _timeouts.releaseKeyboardTimeoutId = 0; return GLib.SOURCE_REMOVE; } ); } else { - Main.overview.show(); + Main.overview.show(1, customOverviewMode); } } - } + }, _toggleApplications(leaveOverview = false) { if ((leaveOverview && Main.overview._shown) || Main.overview.dash.showAppsButton.checked) { @@ -360,12 +430,15 @@ class HotCorner extends Layout.HotCorner { Main.overview.show(2); // 2 for App Grid } } - } + }, _toggleWindowSearchProvider() { if (!Main.overview._overview._controls._searchController._searchActive) { - this._toggleWindowPicker(); - const prefix = 'wq// '; + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(false, true); + const prefix = Me.WSP_PREFIX; const position = prefix.length; const searchEntry = Main.overview.searchEntry; searchEntry.set_text(prefix); @@ -376,5 +449,5 @@ class HotCorner extends Layout.HotCorner { // Main.overview.searchEntry.text = ''; Main.overview.hide(); } - } -}); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/messageTray.js b/extensions/44/vertical-workspaces/lib/messageTray.js index b35541a..ef7a51b 100644 --- a/extensions/44/vertical-workspaces/lib/messageTray.js +++ b/extensions/44/vertical-workspaces/lib/messageTray.js @@ -10,58 +10,82 @@ 'use strict'; -const { Clutter } = imports.gi; -const Me = imports.misc.extensionUtils.getCurrentExtension(); +const Clutter = imports.gi.Clutter; + const Main = imports.ui.main; +let Me; 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; +var MessageTrayModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - _firstRun = false; + this._firstActivation = true; + this.moduleEnabled = false; + } - if (reset) { + cleanGlobals() { + Me = null; opt = null; - setNotificationPosition(1); - return; } - setNotificationPosition(opt.NOTIFICATION_POSITION); -} + update(reset) { + this.moduleEnabled = opt.get('messageTrayModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' MessageTrayModule - Keeping untouched'); + } + + _activateModule() { + this._setNotificationPosition(opt.NOTIFICATION_POSITION); + + console.debug(' MessageTrayModule - Activated'); + } + + _disableModule() { + this._setNotificationPosition(1); + + console.debug(' MessageTrayModule - Disabled'); + } -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; + _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/44/vertical-workspaces/lib/optionsFactory.js b/extensions/44/vertical-workspaces/lib/optionsFactory.js index da62dd1..df6c970 100644 --- a/extensions/44/vertical-workspaces/lib/optionsFactory.js +++ b/extensions/44/vertical-workspaces/lib/optionsFactory.js @@ -9,41 +9,35 @@ 'use strict'; -const { Gtk, Gio, GObject } = imports.gi; +const Adw = imports.gi.Adw; +const Gtk = imports.gi.Gtk; +const Gio = imports.gi.Gio; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; - -const shellVersion = Settings.shellVersion; +let Me; // gettext -const _ = Settings._; +let _; // = Settings._; -const ProfileNames = [ - _('GNOME 3'), - _('GNOME 40+ - Bottom Hot Edge'), - _('Hot Corner Centric - Top Left Hot Corner'), - _('Dock Overview - Bottom Hot Edge'), -]; +function init(me) { + Me = me; + _ = Me.gettext; +} -// libadwaita is available starting with GNOME Shell 42. -let Adw = null; -try { - Adw = imports.gi.Adw; -} catch (e) {} +function cleanGlobals() { + Me = null; + _ = null; +} function _newImageFromIconName(name) { return Gtk.Image.new_from_icon_name(name); } var ItemFactory = class ItemFactory { - constructor(gOptions) { - this._gOptions = gOptions; - this._settings = this._gOptions._gsettings; + constructor() { + this._settings = Me.Opt._gsettings; } - getRowWidget(text, caption, widget, variable, options = []) { + getRowWidget(text, caption, widget, variable, options = [], dependsOn) { let item = []; let label; if (widget) { @@ -81,8 +75,8 @@ var ItemFactory = class ItemFactory { let key; - if (variable && this._gOptions.options[variable]) { - const opt = this._gOptions.options[variable]; + if (variable && Me.Opt.options[variable]) { + const opt = Me.Opt.options[variable]; key = opt[1]; } @@ -95,6 +89,11 @@ var ItemFactory = class ItemFactory { this._connectComboBox(widget, key, variable, options); else if (widget._isDropDown) this._connectDropDown(widget, key, variable, options); + + if (dependsOn) { + const dKey = Me.Opt.options[dependsOn][1]; + this._settings.bind(dKey, widget, 'sensitive', Gio.SettingsBindFlags.GET); + } } return item; @@ -111,7 +110,7 @@ var ItemFactory = class ItemFactory { _connectComboBox(widget, key, variable, options) { let model = widget.get_model(); widget._comboMap = {}; - const currentValue = this._gOptions.get(variable); + const currentValue = Me.Opt.get(variable); for (const [label, value] of options) { let iter; model.set(iter = model.append(), [0, 1], [label, value]); @@ -120,8 +119,8 @@ var ItemFactory = class ItemFactory { widget._comboMap[value] = iter; } - this._gOptions.connect(`changed::${key}`, () => { - widget.set_active_iter(widget._comboMap[this._gOptions.get(variable, true)]); + Me.Opt.connect(`changed::${key}`, () => { + widget.set_active_iter(widget._comboMap[Me.Opt.get(variable, true)]); }); widget.connect('changed', () => { const [success, iter] = widget.get_active_iter(); @@ -129,17 +128,17 @@ var ItemFactory = class ItemFactory { if (!success) return; - this._gOptions.set(variable, model.get_value(iter, 1)); + Me.Opt.set(variable, model.get_value(iter, 1)); }); } _connectDropDown(widget, key, variable, options) { const model = widget.get_model(); - const currentValue = this._gOptions.get(variable); + const currentValue = Me.Opt.get(variable); for (let i = 0; i < options.length; i++) { const text = options[i][0]; const id = options[i][1]; - model.append(new DropDownItem({ text, id })); + model.append(new DropDownItemVW({ text, id })); if (id === currentValue) widget.set_selected(i); } @@ -157,11 +156,11 @@ var ItemFactory = class ItemFactory { widget.connect('notify::selected-item', dropDown => { const item = dropDown.get_selected_item(); - this._gOptions.set(variable, item.id); + Me.Opt.set(variable, item.id); }); - this._gOptions.connect(`changed::${key}`, () => { - const newId = this._gOptions.get(variable, true); + Me.Opt.connect(`changed::${key}`, () => { + const newId = Me.Opt.get(variable, true); for (let i = 0; i < options.length; i++) { const id = options[i][1]; if (id === newId) @@ -214,7 +213,7 @@ var ItemFactory = class ItemFactory { newDropDown() { const dropDown = new Gtk.DropDown({ model: new Gio.ListStore({ - item_type: DropDownItem, + item_type: DropDownItemVW, }), halign: Gtk.Align.END, valign: Gtk.Align.CENTER, @@ -255,11 +254,11 @@ var ItemFactory = class ItemFactory { newLinkButton(uri) { const linkBtn = new Gtk.LinkButton({ - label: shellVersion < 42 ? 'Click Me!' : '', uri, halign: Gtk.Align.END, valign: Gtk.Align.CENTER, hexpand: true, + icon_name: 'emblem-symbolic-link', }); return linkBtn; } @@ -298,18 +297,23 @@ var ItemFactory = class ItemFactory { 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', + icon_name: 'document-revert-symbolic', hexpand: false, css_classes: ['destructive-action'], }); function setName() { + const ProfileNames = [ + _('GNOME 3'), + _('GNOME 40+ - Bottom Hot Edge'), + _('Hot Corner Centric - Top Left Hot Corner'), + _('Dock Overview - Bottom Hot Edge'), + ]; + let name = opt.get(`profileName${profileIndex}`, true); if (!name) name = ProfileNames[profileIndex - 1]; @@ -317,6 +321,10 @@ var ItemFactory = class ItemFactory { } setName(); + + entry.connect('icon-press', e => e.set_text('')); + entry.connect('changed', e => opt.set(`profileName${profileIndex}`, e.get_text())); + resetProfile.connect('clicked', () => { reset(profileIndex); setName(); @@ -366,7 +374,7 @@ var ItemFactory = class ItemFactory { valign: Gtk.Align.CENTER, hexpand: true, css_classes: ['destructive-action'], - icon_name: 'edit-delete-symbolic', + icon_name: 'document-revert-symbolic', }); btn.connect('clicked', () => { @@ -382,7 +390,7 @@ var ItemFactory = class ItemFactory { var AdwPrefs = class { constructor(gOptions) { - this._gOptions = gOptions; + Me.Opt = gOptions; } getFilledWindow(window, pages) { @@ -457,158 +465,9 @@ var AdwPrefs = class { } }; -var LegacyPrefs = class { - constructor(gOptions) { - this._gOptions = gOptions; - } - - getPrefsWidget(pages) { - const prefsWidget = new Gtk.Box({ - orientation: Gtk.Orientation.VERTICAL, - }); - const stack = new Gtk.Stack({ - hexpand: true, - }); - const stackSwitcher = new Gtk.StackSwitcher({ - halign: Gtk.Align.CENTER, - hexpand: true, - }); - - const context = stackSwitcher.get_style_context(); - context.add_class('caption'); - - stackSwitcher.set_stack(stack); - stack.set_transition_duration(300); - stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT); - - const pageProperties = { - hscrollbar_policy: Gtk.PolicyType.NEVER, - vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, - vexpand: true, - hexpand: true, - visible: true, - }; - - const pagesBtns = []; - - for (let page of pages) { - const name = page.name; - const title = page.title; - const iconName = page.iconName; - const optionList = page.optionList; - - stack.add_named(this._getLegacyPage(optionList, pageProperties), name); - pagesBtns.push( - [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 icon = pagesBtns[i][1]; - icon.margin_start = 30; - icon.margin_end = 30; - box.append(icon); - box.append(pagesBtns[i][0]); - if (stackSwitcher.get_children) { - stBtn = stackSwitcher.get_children()[i]; - stBtn.add(box); - } else { - stBtn.set_child(box); - stBtn.visible = true; - stBtn = stBtn.get_next_sibling(); - } - } - - if (stack.show_all) - stack.show_all(); - if (stackSwitcher.show_all) - stackSwitcher.show_all(); - - prefsWidget.append(stack); - - if (prefsWidget.show_all) - prefsWidget.show_all(); - - prefsWidget._stackSwitcher = stackSwitcher; - - return prefsWidget; - } - - _getLegacyPage(optionList, pageProperties) { - const page = new Gtk.ScrolledWindow(pageProperties); - const mainBox = new Gtk.Box({ - orientation: Gtk.Orientation.VERTICAL, - spacing: 5, - homogeneous: false, - margin_start: 30, - margin_end: 30, - margin_top: 12, - margin_bottom: 12, - }); - - let context = page.get_style_context(); - context.add_class('background'); - - let frame; - let frameBox; - for (let item of optionList) { - // label can be plain text for Section Title - // or GtkBox for Option - const option = item[0]; - const widget = item[1]; - - if (!widget) { - const lbl = new Gtk.Label({ - label: option, - xalign: 0, - margin_bottom: 4, - }); - - context = lbl.get_style_context(); - context.add_class('heading'); - - mainBox.append(lbl); - - frame = new Gtk.Frame({ - margin_bottom: 16, - }); - - frameBox = new Gtk.ListBox({ - selection_mode: null, - }); - - mainBox.append(frame); - frame.set_child(frameBox); - continue; - } - - const grid = new Gtk.Grid({ - column_homogeneous: false, - column_spacing: 20, - margin_start: 8, - margin_end: 8, - margin_top: 8, - margin_bottom: 8, - hexpand: true, - }); - - grid.attach(option, 0, 0, 5, 1); - - if (widget) - grid.attach(widget, 5, 0, 2, 1); - - frameBox.append(grid); - } - page.set_child(mainBox); - - return page; - } -}; - -const DropDownItem = GObject.registerClass({ - GTypeName: 'DropdownItem', +const { GObject } = imports.gi; +const DropDownItemVW = GObject.registerClass({ + GTypeName: 'DropDownItemVW', Properties: { 'text': GObject.ParamSpec.string( 'text', @@ -622,10 +481,11 @@ const DropDownItem = GObject.registerClass({ 'Id', 'Item id stored in settings', GObject.ParamFlags.READWRITE, - 0, 100, 0 + // min, max, default + -2147483648, 2147483647, 0 ), }, -}, class DropDownItem extends GObject.Object { +}, class DropDownItemVW extends GObject.Object { get text() { return this._text; } @@ -641,5 +501,4 @@ const DropDownItem = GObject.registerClass({ set id(id) { this._id = id; } -} -); +}); diff --git a/extensions/44/vertical-workspaces/lib/osdWindow.js b/extensions/44/vertical-workspaces/lib/osdWindow.js index a010558..4699ddf 100644 --- a/extensions/44/vertical-workspaces/lib/osdWindow.js +++ b/extensions/44/vertical-workspaces/lib/osdWindow.js @@ -10,79 +10,104 @@ 'use strict'; -const { Clutter } = imports.gi; +const Clutter = imports.gi.Clutter; + 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 Me; let opt; -let _firstRun = true; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('osdWindowModule', true); - reset = reset || !moduleEnabled; +let OsdPositions; - // don't even touch this module if disabled - if (_firstRun && reset) - return; +var OsdWindowModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - _firstRun = false; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; - if (_overrides) - _overrides.removeAll(); + 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, + }, + }; + } - if (reset || !moduleEnabled) { - updateExistingOsdWindows(6); - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; + OsdPositions = null; } - _overrides = new _Util.Overrides(); - _overrides.addOverride('osdWindow', OsdWindow.OsdWindow.prototype, OsdWindowCommon); -} + update(reset) { + this.moduleEnabled = opt.get('osdWindowModule'); + const conflict = false; -function updateExistingOsdWindows(position) { - position = position ? position : opt.OSD_POSITION; - Main.osdWindowManager._osdWindows.forEach(osd => { - osd.set(OsdPositions[position]); - }); -} + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OsdWindowModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('osdWindow', OsdWindow.OsdWindow.prototype, OsdWindowCommon); + console.debug(' OsdWindowModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + this._updateExistingOsdWindows(6); + + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); + } + + _updateExistingOsdWindows(position) { + position = position ? position : opt.OSD_POSITION; + Main.osdWindowManager._osdWindows.forEach(osd => { + osd.set(OsdPositions[position]); + }); + } +}; const OsdWindowCommon = { after_show() { diff --git a/extensions/44/vertical-workspaces/lib/overlayKey.js b/extensions/44/vertical-workspaces/lib/overlayKey.js index e0fc11d..815abaa 100644 --- a/extensions/44/vertical-workspaces/lib/overlayKey.js +++ b/extensions/44/vertical-workspaces/lib/overlayKey.js @@ -9,81 +9,94 @@ */ 'use strict'; -const { GObject, Gio, GLib, Meta, St } = imports.gi; + +const GLib = imports.gi.GLib; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const St = imports.gi.St; 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 Me; let opt; -let _firstRun = true; -let _originalOverlayKeyHandlerId; -let _overlayKeyHandlerId; +var OverlayKeyModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('overlayKeyModule', true); - reset = reset || (!_firstRun && !moduleEnabled); + this._firstActivation = true; + this.moduleEnabled = false; + this._originalOverlayKeyHandlerId = 0; + this._overlayKeyHandlerId = 0; + } - // don't even touch this module if disabled - if (_firstRun && !moduleEnabled) - return; + cleanGlobals() { + Me = null; + opt = null; + } - _firstRun = false; + update(reset) { + this.moduleEnabled = opt.get('overlayKeyModule'); + const conflict = false; - if (reset) { - _updateOverlayKey(reset); - opt = null; - return; + reset = reset || !this.moduleEnabled || conflict; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OverlayKeyModule - Keeping untouched'); } - _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)(); + _activateModule() { + if (!this._originalOverlayKeyHandlerId) { + this._originalOverlayKeyHandlerId = GObject.signal_handler_find(global.display, { signalId: 'overlay-key' }); + if (this._originalOverlayKeyHandlerId !== null) { + global.display.block_signal_handler(this._originalOverlayKeyHandlerId); + this._connectOverlayKey(); + } + } + console.debug(' OverlayKeyModule - Activated'); } -} -function _restoreOverlayKeyHandler() { - // Disconnect modified overlay key handler - if (_overlayKeyHandlerId !== null) { - global.display.disconnect(_overlayKeyHandlerId); - _overlayKeyHandlerId = null; + _disableModule() { + this._restoreOverlayKeyHandler(); + + console.debug(' OverlayKeyModule - Disabled'); } - // Unblock original overlay key handler - if (_originalOverlayKeyHandlerId !== null) { - global.display.unblock_signal_handler(_originalOverlayKeyHandlerId); - _originalOverlayKeyHandlerId = null; + _restoreOverlayKeyHandler() { + // Disconnect modified overlay key handler + if (this._overlayKeyHandlerId) { + global.display.disconnect(this._overlayKeyHandlerId); + this._overlayKeyHandlerId = 0; + } + + // Unblock original overlay key handler + if (this._originalOverlayKeyHandlerId) { + global.display.unblock_signal_handler(this._originalOverlayKeyHandlerId); + this._originalOverlayKeyHandlerId = 0; + } } -} -function _connectOverlayKey() { - this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA }); + _connectOverlayKey() { + if (this._overlayKeyHandlerId) + return; + + this._overlayKeyHandlerId = global.display.connect('overlay-key', this._onOverlayKeyPressed.bind(Main.overview._overview.controls)); + } - this._lastOverlayKeyTime = 0; - _overlayKeyHandlerId = global.display.connect('overlay-key', () => { + _onOverlayKeyPressed() { if (this._a11ySettings.get_boolean('stickykeys-enable')) return; const { initialState, finalState, transitioning } = - this._stateAdjustment.getStateTransitionParams(); + this._stateAdjustment.getStateTransitionParams(); const time = GLib.get_monotonic_time() / 1000; const timeDiff = time - this._lastOverlayKeyTime; @@ -95,14 +108,61 @@ function _connectOverlayKey() { const mode = opt.OVERLAY_KEY_SECONDARY; if (shouldShift) { - if (mode === 1) + Me.Util.activateSearchProvider(''); + 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 if (mode === 2) { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + } else if (mode === 3) { + // Changing the overview mode automatically changes the overview transition + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + } } else { - Main.overview.toggle(); + if (Main.overview._shown) { + Main.overview.hide(); + return; + } + switch (opt.OVERLAY_KEY_PRIMARY) { + case 0: // Disabled + return; + case 1: // Follow global overview mode + Main.overview.resetOverviewMode(); + break; + case 2: // Default overview + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + break; + case 3: // Default overview + if (Main.overview._shown) + Main.overview.hide(); + else + Main.overview.show(2); + return; + case 4: // Static WS preview + opt.OVERVIEW_MODE = 1; + opt.OVERVIEW_MODE2 = false; + if (!Main.overview._shown) + opt.WORKSPACE_MODE = 0; + break; + case 5: // Static WS + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + break; + case 6: // Window Search + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + if (!Main.overview._shown) + opt.WORKSPACE_MODE = 0; + break; + } + const customOverviewMode = !Main.overview._shown; + Main.overview.toggle(customOverviewMode); + if (opt.OVERLAY_KEY_PRIMARY === 6) + Me.Util.activateSearchProvider(Me.WSP_PREFIX); } - }); -} + } +}; diff --git a/extensions/44/vertical-workspaces/lib/overview.js b/extensions/44/vertical-workspaces/lib/overview.js index 2f23d05..833fc58 100644 --- a/extensions/44/vertical-workspaces/lib/overview.js +++ b/extensions/44/vertical-workspaces/lib/overview.js @@ -10,43 +10,113 @@ 'use strict'; +const Main = imports.ui.main; const Overview = imports.ui.overview; +const OverviewControls = imports.ui.overviewControls; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -let _overrides; +let Me; let opt; -function update(reset = false) { - if (_overrides) - _overrides.removeAll(); +var OverviewModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; } - opt = Me.imports.lib.settings.opt; - _overrides = new _Util.Overrides(); + update(reset) { + this.moduleEnabled = true; + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OverviewModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); - _overrides.addOverride('Overview', Overview.Overview.prototype, OverviewCommon); -} + this._overrides.addOverride('Overview', Overview.Overview.prototype, OverviewCommon); + console.debug(' OverviewModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' OverviewModule - Disabled'); + } +}; const OverviewCommon = { + show(state = OverviewControls.ControlsState.WINDOW_PICKER, customOverviewMode) { + if (!customOverviewMode) + this.resetOverviewMode(); + + if (state === OverviewControls.ControlsState.HIDDEN) + throw new Error('Invalid state, use hide() to hide'); + + if (this.isDummy) + return; + if (this._shown) + return; + this._shown = true; + + if (!this._syncGrab()) + return; + + Main.layoutManager.showOverview(); + this._animateVisible(state); + }, + + toggle(customOverviewMode) { + if (this.isDummy) + return; + + if (this._visible) + this.hide(); + else + this.show(OverviewControls.ControlsState.WINDOW_PICKER, customOverviewMode); + }, + + resetOverviewMode() { + // reset Overview Mode do default + opt.OVERVIEW_MODE = opt.get('overviewMode'); + opt.OVERVIEW_MODE2 = opt.OVERVIEW_MODE === 2; + opt.WORKSPACE_MODE = opt.OVERVIEW_MODE > 0 ? 0 : 1; + }, + _showDone() { this._animationInProgress = false; this._coverPane.hide(); - this.emit('shown'); + if (Me.shellVersion < 44) + this.emit('shown'); + else if (this._shownState !== 'SHOWN') + this._changeShownState('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) { @@ -55,5 +125,41 @@ const OverviewCommon = { else if (!opt.OVERVIEW_MODE2) controls._stateAdjustment.value = 2; } + + this._syncGrab(); + }, + + // Workaround - should probably be fixed elsewhere in the upstream code + // If a new window is opened from the overview + // and is realized before the overview animation is complete, + // the new window will not get focus + after__hideDone() { + if (!opt.FIX_NEW_WINDOW_FOCUS) + return; + + const workspace = global.workspace_manager.get_active_workspace(); + const recentDesktopWin = global.display.get_tab_list(1, workspace)[0]; + let recentNormalWin = null; + const tabList = global.display.get_tab_list(0, workspace); + + for (let i = 0; i < tabList.length; i++) { + if (tabList[i].minimized === false) { + recentNormalWin = tabList[i]; + break; + } + } + + let recentWin = recentNormalWin; + if (recentNormalWin && recentDesktopWin) { + recentWin = recentNormalWin.get_user_time() > recentDesktopWin.get_user_time() + ? recentNormalWin + : recentDesktopWin; + } + + const focusedWin = global.display.focus_window; + + if (recentWin && focusedWin !== recentWin) + recentWin.activate(global.get_current_time()); }, }; + diff --git a/extensions/44/vertical-workspaces/lib/overviewControls.js b/extensions/44/vertical-workspaces/lib/overviewControls.js index 4959b83..7528682 100644 --- a/extensions/44/vertical-workspaces/lib/overviewControls.js +++ b/extensions/44/vertical-workspaces/lib/overviewControls.js @@ -10,90 +10,148 @@ 'use strict'; -const { Clutter, GLib, GObject, St } = imports.gi; +const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const Background = imports.ui.background; +const Layout = imports.ui.layout; const Main = imports.ui.main; -const Util = imports.misc.util; +const Overview = imports.ui.overview; const OverviewControls = imports.ui.overviewControls; +const Workspace = imports.ui.workspace; const WorkspaceThumbnail = imports.ui.workspaceThumbnail; +const WorkspacesView = imports.ui.workspacesView; +const Util = imports.misc.util; -const ControlsState = imports.ui.overviewControls.ControlsState; -const FitMode = imports.ui.workspacesView.FitMode; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; - -let _overrides; +let Me; let opt; +// gettext +let _; + +const ControlsState = OverviewControls.ControlsState; +const FitMode = WorkspacesView.FitMode; -const ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME; +const ANIMATION_TIME = Overview.ANIMATION_TIME; const DASH_MAX_SIZE_RATIO = 0.25; let _originalSearchControllerSigId; let _searchControllerSigId; let _timeouts; -let _startupInitComplete = false; -function update(reset = false) { - if (_overrides) - _overrides.removeAll(); +var OverviewControlsModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; - if (_timeouts) { - Object.values(_timeouts).forEach(id => { - if (id) - GLib.source_remove(id); - }); + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - _replaceOnSearchChanged(reset); - - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - _timeouts = null; - return; + _ = null; } - _timeouts = {}; + update(reset) { + this._removeTimeouts(); + this.moduleEnabled = true; + const conflict = false; - opt = Me.imports.lib.settings.opt; - _overrides = new _Util.Overrides(); + reset = reset || !this.moduleEnabled || conflict; - _overrides.addOverride('ControlsManager', OverviewControls.ControlsManager.prototype, ControlsManager); + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OverviewControlsModule - Keeping untouched'); + } - if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) - _overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutVertical); - else - _overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutHorizontal); -} + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + this._replaceOnSearchChanged(); + + this._overrides.addOverride('ControlsManager', OverviewControls.ControlsManager.prototype, ControlsManagerCommon); + + if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) + this._overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutVertical); + else + this._overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutHorizontal); + + // if DtD is enabled, we need to replace _prepareStartupAnimation() to minimize the mess in the overview + // const dashToDockEnabled = Me.Util.getEnabledExtensions('dash-to-dock').length || + // Me.Util.getEnabledExtensions('ubuntu-dock').length; + // if (dashToDockEnabled) + this._overrides.addOverride('LayoutManagerDtD', Layout.LayoutManager.prototype, LayoutManager); + + console.debug(' OverviewControlsModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + const reset = true; + this._replaceOnSearchChanged(reset); + Main.overview._overview._controls._appDisplay.opacity = 255; -function _replaceOnSearchChanged(reset = false) { - const searchController = Main.overview._overview.controls._searchController; - if (reset) { - if (_searchControllerSigId) { - searchController.disconnect(_searchControllerSigId); - _searchControllerSigId = 0; + console.debug(' OverviewControlsModule - Disabled'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; } - if (_originalSearchControllerSigId) { - searchController.unblock_signal_handler(_originalSearchControllerSigId); - _originalSearchControllerSigId = 0; + } + + _replaceOnSearchChanged(reset) { + const searchController = Main.overview._overview.controls._searchController; + if (reset) { + if (_searchControllerSigId) { + searchController.disconnect(_searchControllerSigId); + _searchControllerSigId = 0; + } + if (_originalSearchControllerSigId) { + 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) + if (!_originalSearchControllerSigId) + _originalSearchControllerSigId = GObject.signal_handler_find(searchController, { signalId: 'notify', detail: 'search-active' }); + if (_originalSearchControllerSigId) + searchController.block_signal_handler(_originalSearchControllerSigId); + + if (!_searchControllerSigId) + _searchControllerSigId = searchController.connect('notify::search-active', ControlsManagerCommon._onSearchChanged.bind(Main.overview._overview.controls)); } - 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) - searchController.block_signal_handler(_originalSearchControllerSigId); - - _searchControllerSigId = searchController.connect('notify::search-active', ControlsManager._onSearchChanged.bind(Main.overview._overview.controls)); } -} +}; -const ControlsManager = { +const ControlsManagerCommon = { // 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() { ... @@ -105,16 +163,21 @@ const ControlsManager = { }, _updateThumbnailsBox() { + const { currentState } = this._stateAdjustment.getStateTransitionParams(); const { shouldShow } = this._thumbnailsBox; - const thumbnailsBoxVisible = shouldShow; + const thumbnailsBoxVisible = shouldShow && + ((currentState < ControlsState.APP_GRID && opt.SHOW_WS_TMB) || + (currentState > ControlsState.WINDOW_PICKER && opt.SHOW_WS_TMB_APPGRID) || + (currentState > ControlsState.WINDOW_PICKER && this._searchController.searchActive && opt.SHOW_WS_TMB) + ); this._thumbnailsBox.visible = thumbnailsBoxVisible; // this call should be directly in _update(), but it's used as a callback function and it would require to reconnect the signal - this._updateWorkspacesDisplay(); + this._updateOverview(); }, // this function is pure addition to the original code and handles wsDisp transition to APP_GRID view - _updateWorkspacesDisplay() { + _updateOverview() { this._workspacesDisplay.translation_x = 0; this._workspacesDisplay.translation_y = 0; this._workspacesDisplay.scale_x = 1; @@ -167,18 +230,21 @@ const ControlsManager = { ws._background.opacity = opacity; } + if (opt.WORKSPACE_MODE) + Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95; + // if ws preview background is disabled, animate tmb box and dash const tmbBox = this._thumbnailsBox; 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 = _Util.dashNotDefault(); + const skipDash = Me.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 (!tmbBox._translationOriginal || Math.abs(tmbBox._translationOriginal[0]) > 500) { // swipe gesture can call this calculation before tmbBox is finalized, giving nonsense width - const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin); + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = this._getOverviewTranslations(dash, tmbBox, searchEntryBin); tmbBox._translationOriginal = [tmbTranslationX, tmbTranslationY]; dash._translationOriginal = [dashTranslationX, dashTranslationY]; searchEntryBin._translationOriginal = searchTranslationY; @@ -229,7 +295,7 @@ const 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 - if (!_Util.dashNotDefault()) + if (!Me.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); @@ -238,7 +304,7 @@ const ControlsManager = { // set dash above workspace in the overview this.set_child_above_sibling(this._thumbnailsBox, null); this.set_child_above_sibling(this._searchEntryBin, null); - if (!_Util.dashNotDefault()) + if (!Me.Util.dashNotDefault()) this.set_child_above_sibling(this.dash, null); this.dash._isAbove = true; @@ -247,6 +313,7 @@ const ControlsManager = { this.set_child_above_sibling(this._workspacesDisplay, null); this.dash._isAbove = false; } + }, // fix for upstream bug - appGrid.visible after transition from APP_GRID to HIDDEN @@ -258,12 +325,6 @@ const 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 = @@ -305,11 +366,19 @@ const ControlsManager = { this._workspacesDisplay.reactive = true; this._workspacesDisplay.setPrimaryWorkspaceVisible(true); } else { + if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) + this._searchController._searchResults._statusText.add_style_class_name('search-statustext-om2'); + else + this._searchController._searchResults._statusText.remove_style_class_name('search-statustext-om2'); this._searchController.show(); entry.visible = true; entry.opacity = 255; + // avoid awkward ws scale animation during search activation + WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 0; } + if (opt.SHOW_BG_IN_OVERVIEW && this._bgManagers) + this._updateBackground(this._bgManagers[0]); this._searchTransition = true; this._searchController._searchResults.translation_x = 0; @@ -317,34 +386,33 @@ const ControlsManager = { 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 && ![4, 8].includes(opt.WS_TMB_POSITION) /* && !opt.OVERVIEW_MODE2*/) { this._updateAppDisplayVisibility(); + this.layoutManager._searchController._searchResults._statusBin.opacity = 1; this._searchController.opacity = searchActive ? 255 : 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 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; - } + switch (opt.SEARCH_VIEW_ANIMATION) { + 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) { @@ -363,6 +431,8 @@ const ControlsManager = { onComplete: () => { this._searchController.visible = searchActive; this._searchTransition = false; + this.layoutManager._searchController._searchResults._statusBin.opacity = 255; + WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 200; }, }); @@ -372,7 +442,10 @@ const ControlsManager = { opacity: searchActive || currentState < 2 ? 0 : 255, duration: SIDE_CONTROLS_ANIMATION_TIME / 2, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => this._updateAppDisplayVisibility(), + onComplete: () => { + this._updateAppDisplayVisibility(); + WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 200; + }, }); // this._updateAppDisplayVisibility(); @@ -390,7 +463,7 @@ const ControlsManager = { this._searchController.opacity = searchActive ? 0 : 255; this._searchController.ease({ opacity: searchActive ? 255 : 0, - duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME * 2 : 0, + duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME : 0, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => (this._searchController.visible = searchActive), }); @@ -398,14 +471,16 @@ const ControlsManager = { // reuse already tuned overview transition, just replace APP_GRID with the search view 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._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg'); Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); + const duration = opt.SEARCH_VIEW_ANIMATION ? 150 : 0; 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 || (opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE) ? 80 : SIDE_CONTROLS_ANIMATION_TIME, + duration: searchActive ? duration : SIDE_CONTROLS_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive); @@ -413,18 +488,20 @@ const ControlsManager = { }); } 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._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg-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._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg-om2'); Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); } }, async runStartupAnimation(callback) { this._ignoreShowAppsButtonToggle = true; - this._searchController.prepareToEnterOverview(); - this._workspacesDisplay.prepareToEnterOverview(); + + this.prepareToEnterOverview(); this._stateAdjustment.value = ControlsState.HIDDEN; this._stateAdjustment.ease(ControlsState.WINDOW_PICKER, { @@ -436,16 +513,20 @@ const ControlsManager = { this._ignoreShowAppsButtonToggle = false; // Set the opacity here to avoid a 1-frame flicker - this.opacity = 0; + this.opacity = 1; + this._appDisplay.opacity = 1; // We can't run the animation before the first allocation happens await this.layout_manager.ensureAllocation(); - const { STARTUP_ANIMATION_TIME } = imports.ui.layout; + this._setBackground(); + Main.panel.opacity = 255; + + const { STARTUP_ANIMATION_TIME } = Layout; // Opacity this.ease({ - opacity: 255, + opacity: opt.STARTUP_STATE === 1 ? 0 : 255, duration: STARTUP_ANIMATION_TIME, mode: Clutter.AnimationMode.LINEAR, onComplete: () => { @@ -465,53 +546,24 @@ const ControlsManager = { } const searchEntryBin = this._searchEntryBin; - const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = - _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin); + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = + this._getOverviewTranslations(dash, tmbBox, searchEntryBin); const onComplete = function () { // running init callback again causes issues (multiple connections) - if (!_startupInitComplete) + if (!Main.overview._startupInitComplete) callback(); - _startupInitComplete = true; - - // force app grid to build before the first visible animation to remove possible stuttering - this._appDisplay.opacity = 1; - - const [x, y] = this._appDisplay.get_position(); - 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 - _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; - if (opt.STARTUP_STATE === 1) { - Main.overview.hide(); - } else if (opt.STARTUP_STATE === 2) { - this._appDisplay.opacity = 255; - this.dash.showAppsButton.checked = true; - } - _timeouts.startupAnim1 = 0; - return GLib.SOURCE_REMOVE; - } - ); + const appDisplayModule = Me.Modules.appDisplayModule; + if (!appDisplayModule.moduleEnabled) + this._finishStartupSequence(); + else + this._realizeAppDisplayAndFinishSequence(); + + Main.overview._startupInitComplete = true; }.bind(this); - if (dash.visible && !_Util.dashNotDefault()) { + if (dash.visible && !Me.Util.dashNotDefault()) { dash.translation_x = dashTranslationX; dash.translation_y = dashTranslationY; dash.opacity = 255; @@ -521,9 +573,7 @@ const ControlsManager = { delay: STARTUP_ANIMATION_TIME / 2, duration: STARTUP_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - onComplete(); - }, + onComplete, }); } else { // set dash opacity to make it visible if user enable it later @@ -535,6 +585,7 @@ const ControlsManager = { STARTUP_ANIMATION_TIME * 2 * St.Settings.get().slow_down_factor, () => { onComplete(); + Main.overview._startupInitComplete = true; _timeouts.startupAnim2 = 0; return GLib.SOURCE_REMOVE; } @@ -571,7 +622,6 @@ const ControlsManager = { if (view._monitorIndex !== global.display.get_primary_monitor() && view._thumbnails.visible) { 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) @@ -594,13 +644,129 @@ const ControlsManager = { } }, + _realizeAppDisplayAndFinishSequence() { + const appDisplayModule = Me.Modules.appDisplayModule; + // realize app grid for smoother first animation + appDisplayModule._updateAppGrid(false, this._finishStartupSequence.bind(this)); + }, + + _finishStartupSequence(priority = GLib.PRIORITY_LOW) { + if (!this._bgManagers) + this._setBackground(); + + _timeouts.finishStartup = GLib.idle_add( + priority, () => { + this._appDisplay.opacity = 255; + if (opt.STARTUP_STATE === 1) { + Main.overview.hide(); + } else if (opt.STARTUP_STATE === 2) { + Main.overview.show(2); // just because of DtD, because we skipped startup animation + this.dash.showAppsButton.checked = true; + } else if (!opt.STARTUP_STATE && Me.Util.dashNotDefault()) { + Main.overview.show(); + } + + _timeouts.finishStartup = 0; + return GLib.SOURCE_REMOVE; + } + ); + }, + + setInitialTranslations() { + const dash = this.dash; + const tmbBox = this._thumbnailsBox; + const searchEntryBin = this._searchEntryBin; + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = + this._getOverviewTranslations(dash, tmbBox, searchEntryBin); + if (!Me.Util.dashNotDefault()) { + dash.translation_x = dashTranslationX; + dash.translation_y = dashTranslationY; + } + tmbBox.translation_x = tmbTranslationX; + tmbBox.translation_y = tmbTranslationY; + searchEntryBin.translation_y = searchTranslationY; + }, + + _getOverviewTranslations(dash, tmbBox, searchEntryBin) { + // const tmbBox = Main.overview._overview._controls._thumbnailsBox; + const animationsDisabled = !St.Settings.get().enable_animations || (opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2); + if (animationsDisabled) + return [0, 0, 0, 0, 0]; + + 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) { + const tmbWidth = tmbBox.width === Infinity ? 0 : tmbBox.width; + const tmbHeight = tmbBox.height === Infinity ? 0 : tmbBox.height; + switch (opt.WS_TMB_POSITION) { + case 3: // left + offset = 10 + (dash?.visible && opt.DASH_LEFT ? dash.width : 0); + tmbTranslationX = -tmbWidth - offset; + tmbTranslationY = 0; + break; + case 1: // right + offset = 10 + (dash?.visible && opt.DASH_RIGHT ? dash.width : 0); + tmbTranslationX = tmbWidth + offset; + tmbTranslationY = 0; + break; + case 0: // top + offset = 10 + (dash?.visible && opt.DASH_TOP ? dash.height : 0) + Main.panel.height; + tmbTranslationX = 0; + tmbTranslationY = -tmbHeight - 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 = tmbHeight + offset; + break; + } + } + + let dashTranslationX = 0; + let dashTranslationY = 0; + let position = opt.DASH_POSITION; + // if DtD replaced the original Dash, read its position + if (Me.Util.dashIsDashToDock()) + position = dash._position; + + if (dash?.visible) { + const dashWidth = dash.width === Infinity ? 0 : dash.width; + const dashHeight = dash.height === Infinity ? 0 : dash.height; + switch (position) { + case 0: // top + dashTranslationX = 0; + dashTranslationY = -dashHeight - dash.margin_bottom - Main.panel.height; + break; + case 1: // right + dashTranslationX = dashWidth; + dashTranslationY = 0; + break; + case 2: // bottom + dashTranslationX = 0; + dashTranslationY = dashHeight + dash.margin_bottom + Main.panel.height; + break; + case 3: // left + dashTranslationX = -dashWidth; + dashTranslationY = 0; + break; + } + } + + return [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY]; + }, + animateToOverview(state, callback) { this._ignoreShowAppsButtonToggle = true; this._searchTransition = false; - this._searchController.prepareToEnterOverview(); - this._workspacesDisplay.prepareToEnterOverview(); - this._stateAdjustment.value = ControlsState.HIDDEN; // building window thumbnails takes some time and with many windows on the workspace @@ -627,10 +793,151 @@ const ControlsManager = { this._ignoreShowAppsButtonToggle = false; }, + + _setBackground(reset = false) { + if (this._bgManagers) { + this._bgManagers.forEach(bg => { + Main.overview._overview._controls._stateAdjustment.disconnect(bg._fadeSignal); + bg.destroy(); + }); + } + + // if (!SHOW_BG_IN_OVERVIEW && !SHOW_WS_PREVIEW_BG) the background is used for static transition from wallpaper to empty bg in the overview + if (reset || (!opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG)) { + delete this._bgManagers; + return; + } + + this._bgManagers = []; + for (const monitor of Main.layoutManager.monitors) { + const bgManager = new Background.BackgroundManager({ + monitorIndex: monitor.index, + container: Main.layoutManager.overviewGroup, + vignette: true, + }); + + bgManager.backgroundActor.content.vignette_sharpness = 0; + bgManager.backgroundActor.content.brightness = 1; + + + bgManager._fadeSignal = Main.overview._overview._controls._stateAdjustment.connect('notify::value', v => { + this._updateBackground(bgManager, v.value, v); + }); + + if (monitor.index === global.display.get_primary_monitor()) { + bgManager._primary = true; + this._bgManagers.unshift(bgManager); // primary monitor first + } else { + bgManager._primary = false; + this._bgManagers.push(bgManager); + } + } + }, + + _updateBackground(bgManager, stateValue = 2, stateAdjustment = null) { + // Blur My Shell extension destroys all background actors in the overview and doesn't care about consequences + if (this._bgManagers[0] && !Main.layoutManager.overviewGroup.get_children().includes(this._bgManagers[0].backgroundActor)) { + Main.notifyError(`[${Me.metadata.name}]`, _('Overview background crashed!\nIf you are using Blur My Shell, disable overview blur in its settings and re-enable V-Shell Overview Background to avoid visual glitches.')); + // remove and disconnect our destroyed backgrounds to avoid more errors + this._setBackground(true); + return; + } + + const finalState = stateAdjustment?.getStateTransitionParams().finalState; + if (!opt.SHOW_BG_IN_OVERVIEW && !opt.SHOW_WS_PREVIEW_BG) { + // if no bg shown in the overview, fade out the wallpaper + if (!(opt.OVERVIEW_MODE2 && opt.WORKSPACE_MODE && finalState === 1)) + bgManager.backgroundActor.opacity = Util.lerp(255, 0, Math.min(stateValue, 1)); + } else { + let VIGNETTE, BRIGHTNESS, bgValue; + if (opt.OVERVIEW_MODE2 && stateValue <= 1 && !opt.WORKSPACE_MODE) { + VIGNETTE = 0; + BRIGHTNESS = 1; + bgValue = stateValue; + } else { + VIGNETTE = 0.2; + BRIGHTNESS = opt.OVERVIEW_BG_BRIGHTNESS; + if (opt.OVERVIEW_MODE2 && stateValue > 1 && !opt.WORKSPACE_MODE) + bgValue = stateValue - 1; + else + bgValue = stateValue; + } + + let blurEffect = bgManager.backgroundActor.get_effect('blur'); + if (!blurEffect) { + blurEffect = new Shell.BlurEffect({ + brightness: 1, + sigma: 0, + mode: Shell.BlurMode.ACTOR, + }); + bgManager.backgroundActor.add_effect_with_name('blur', blurEffect); + } + + const searchActive = Main.overview._overview.controls._searchController.searchActive; + if (searchActive) + BRIGHTNESS = opt.SEARCH_BG_BRIGHTNESS; + + bgManager.backgroundActor.content.vignette_sharpness = VIGNETTE; + bgManager.backgroundActor.content.brightness = BRIGHTNESS; + + let vignetteInit, brightnessInit;// , sigmaInit; + if (opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG) { + vignetteInit = VIGNETTE; + brightnessInit = BRIGHTNESS; + // sigmaInit = opt.OVERVIEW_BG_BLUR_SIGMA; + } else { + vignetteInit = 0; + brightnessInit = 1; + // sigmaInit = 0; + } + + if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { + bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, bgValue); + bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, bgValue); + } else { + bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, Math.min(stateValue, 1)); + bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, Math.min(stateValue, 1)); + } + + if (opt.OVERVIEW_BG_BLUR_SIGMA || opt.APP_GRID_BG_BLUR_SIGMA) { + // reduce number of steps of blur transition to improve performance + const step = opt.SMOOTH_BLUR_TRANSITIONS ? 0.05 : 0.2; + const progress = stateValue - (stateValue % step); + if (opt.SHOW_WS_PREVIEW_BG && stateValue < 1 && !searchActive) { // no need to animate transition, unless appGrid state is involved, static bg is covered by the ws preview bg + if (blurEffect.sigma !== opt.OVERVIEW_BG_BLUR_SIGMA) + blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; + } else if (stateValue < 1 && !searchActive && !(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue < 1 && !searchActive && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && blurEffect.sigma)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue > 1 && !searchActive && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && finalState === 1)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress % 1)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if ((stateValue > 1 && bgManager._primary) || searchActive) { + const sigma = Math.round(Util.lerp(opt.OVERVIEW_BG_BLUR_SIGMA, opt.APP_GRID_BG_BLUR_SIGMA, progress % 1)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue === 1 && !(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)) { + blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; + } else if (stateValue === 0 || (stateValue === 1 && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE))) { + blurEffect.sigma = 0; + } + } + } + }, }; const ControlsManagerLayoutVertical = { - _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, searchHeight, startY) { + _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, thumbnailsHeight, searchHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const workspaceBox = box.copy(); let [width, height] = workspaceBox.get_size(); // const { x1: startX/* y1: startY*/ } = workAreaBox; @@ -640,7 +947,7 @@ const ControlsManagerLayoutVertical = { const dash = Main.overview.dash; // including Dash to Dock and clones properties for compatibility - if (_Util.dashIsDashToDock()) { + if (Me.Util.dashIsDashToDock()) { // Dash to Dock also always affects workAreaBox Main.layoutManager._trackedActors.forEach(actor => { if (actor.affectsStruts && actor.actor.width === dash.width) { @@ -678,7 +985,7 @@ const ControlsManagerLayoutVertical = { case ControlsState.APP_GRID: if (opt.WS_ANIMATION && opt.SHOW_WS_TMB && state === ControlsState.APP_GRID) { workspaceBox.set_origin(...this._workspacesThumbnails.get_position()); - workspaceBox.set_size(...this._workspacesThumbnails.get_size()); + workspaceBox.set_size(thumbnailsWidth, thumbnailsHeight); } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { if (opt.START_Y_OFFSET) { let [x, y] = workAreaBox.get_origin(); @@ -693,7 +1000,7 @@ const ControlsManagerLayoutVertical = { 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) - + (opt.DASH_VERTICAL ? dashWidth : 0) - thumbnailsWidth - 4 * spacing; wHeight = height - @@ -739,7 +1046,7 @@ const ControlsManagerLayoutVertical = { } const wsBoxX = /* startX + */xOffset; - wsBoxY = Math.round(startY + yOffset); + wsBoxY = startY + yOffset; workspaceBox.set_origin(Math.round(wsBoxX), Math.round(wsBoxY)); workspaceBox.set_size(Math.round(wWidth), Math.round(wHeight)); } @@ -749,6 +1056,10 @@ const ControlsManagerLayoutVertical = { }, _getAppDisplayBoxForState(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsWidth, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const [width] = box.get_size(); const { x1: startX } = workAreaBox; // const { y1: startY } = workAreaBox; @@ -762,11 +1073,11 @@ const ControlsManagerLayoutVertical = { 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 adHeight = height - yOffsetT - yOffsetB - 4 * spacing; + const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 2 * spacing : width - xOffsetL - xOffsetR - 2 * spacing; + const adHeight = height - yOffsetT - yOffsetB; const appDisplayX = opt.CENTER_APP_GRID ? (width - adWidth) / 2 : xOffsetL + 2 * spacing; - const appDisplayY = startY + yOffsetT + 2 * spacing; + const appDisplayY = startY + yOffsetT; switch (state) { case ControlsState.HIDDEN: @@ -774,43 +1085,43 @@ const ControlsManagerLayoutVertical = { // 1 - left, 2 - right, 3 - bottom, 5 - top switch (opt.APP_GRID_ANIMATION) { case 0: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; case 1: - appDisplayBox.set_origin(startX + width, appDisplayY); + appDisplayBox.set_origin(Math.round(startX + width), Math.round(appDisplayY)); break; case 2: - appDisplayBox.set_origin(startX - adWidth, appDisplayY); + appDisplayBox.set_origin(Math.round(startX - adWidth), Math.round(appDisplayY)); break; case 3: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y2); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y2)); break; case 5: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y1 - adHeight); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y1 - adHeight)); break; } break; case ControlsState.APP_GRID: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; } - appDisplayBox.set_size(adWidth, adHeight); + appDisplayBox.set_size(Math.round(adWidth), Math.round(adHeight)); return appDisplayBox; }, vfunc_allocate(container, box) { - const childBox = new Clutter.ActorBox(); + const transitionParams = this._stateAdjustment.getStateTransitionParams(); + const childBox = new Clutter.ActorBox(); const { spacing } = this; - + const halfSpacing = spacing / 2; const monitor = Main.layoutManager.findMonitorForActor(this._container); const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index); const startX = workArea.x - monitor.x; // 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(); workAreaBox.set_origin(startX, startY); workAreaBox.set_size(workArea.width, workArea.height); @@ -818,7 +1129,7 @@ const ControlsManagerLayoutVertical = { 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; @@ -830,8 +1141,8 @@ const ControlsManagerLayoutVertical = { // dash cloud be overridden by the Dash to Dock clone const dash = Main.overview.dash; - 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 (Me.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; @@ -857,48 +1168,48 @@ const ControlsManagerLayoutVertical = { } } - const transitionParams = this._stateAdjustment.getStateTransitionParams(); - // Workspace Thumbnails let wsTmbWidth = 0; let wsTmbHeight = 0; - if (this._workspacesThumbnails.visible) { - // const { expandFraction } = this._workspacesThumbnails; + let maxWsTmbScale = opt.MAX_THUMBNAIL_SCALE; + if (opt.SHOW_WS_TMB) { const dashHeightReservation = !opt.WS_TMB_FULL && !opt.DASH_VERTICAL ? dashHeight : 0; - let maxScale = opt.MAX_THUMBNAIL_SCALE; - if (!opt.MAX_THUMBNAIL_SCALE_STABLE) { + const searchActive = this._searchController.searchActive; + if (!opt.MAX_THUMBNAIL_SCALE_STABLE && !searchActive) { 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); + maxWsTmbScale = Util.lerp(initState, finalState, transitionParams.progress); } - wsTmbWidth = width * maxScale; + wsTmbWidth = width * maxWsTmbScale; let totalTmbSpacing; - [totalTmbSpacing, wsTmbHeight] = this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth); + [totalTmbSpacing, wsTmbHeight] = this._workspacesThumbnails.get_preferred_height(wsTmbWidth); wsTmbHeight += totalTmbSpacing; - const wsTmbHeightMax = height - dashHeightReservation; + const wsTmbHeightMax = opt.WS_TMB_FULL + ? height - spacing + : height - dashHeightReservation - 2 * spacing; if (wsTmbHeight > wsTmbHeightMax) { wsTmbHeight = wsTmbHeightMax; - wsTmbWidth = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight)[1]; + wsTmbWidth = Math.round(this._workspacesThumbnails.get_preferred_width(wsTmbHeight)[1]); } let wsTmbX; if (opt.WS_TMB_RIGHT) - wsTmbX = Math.round(startX + width - (opt.DASH_RIGHT ? dashWidth : 0) - wsTmbWidth - spacing / 2); + wsTmbX = Math.round(startX + width - (opt.DASH_RIGHT ? dashWidth : 0) - wsTmbWidth /* - halfSpacing*/); // this halfSpacing is a part od dash style else - wsTmbX = Math.round((opt.DASH_LEFT ? dashWidth : 0) + spacing / 2); + wsTmbX = Math.round(opt.DASH_LEFT ? dashWidth : 0/* + halfSpacing*/); // this halfSpacing is a part od dash style let wstOffset = (height - wsTmbHeight - (opt.DASH_VERTICAL ? 0 : dashHeightReservation)) / 2; - wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2); + wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - halfSpacing); let wsTmbY = Math.round(startY + (dashHeightReservation && opt.DASH_TOP ? dashHeight : 0) + wstOffset); childBox.set_origin(wsTmbX, wsTmbY); - childBox.set_size(Math.round(wsTmbWidth), Math.round(wsTmbHeight)); + childBox.set_size(Math.max(wsTmbWidth, 1), Math.max(wsTmbHeight, 1)); this._workspacesThumbnails.allocate(childBox); } @@ -927,7 +1238,7 @@ const ControlsManagerLayoutVertical = { if (!opt.DASH_VERTICAL) { offset = (width - ((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter ? wsTmbWidth : 0) - dashWidth) / 2; - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashX = offset; if ((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter) { @@ -944,7 +1255,7 @@ const ControlsManagerLayoutVertical = { } } else { offset = (height - dashHeight) / 2; - dashY = startY + (offset - opt.DASH_POSITION_ADJUSTMENT * offset); + dashY = startY + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing)); } childBox.set_origin(Math.round(startX + dashX), Math.round(dashY)); @@ -957,7 +1268,7 @@ const ControlsManagerLayoutVertical = { let [searchHeight] = this._searchEntry.get_preferred_height(width - wsTmbWidth); // Workspaces - let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, searchHeight, startY]; + let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, wsTmbHeight, searchHeight, startY]; // Update cached boxes for (const state of Object.values(ControlsState)) { @@ -983,7 +1294,7 @@ const ControlsManagerLayoutVertical = { // Y position under top Dash let searchEntryX, searchEntryY; if (opt.DASH_TOP) - searchEntryY = startY + dashHeight - spacing; + searchEntryY = startY + dashHeight; else searchEntryY = startY; @@ -1005,7 +1316,11 @@ const ControlsManagerLayoutVertical = { availableHeight -= searchHeight + spacing; // if (this._appDisplay.visible)... ? Can cause problems - params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbWidth, startY]; // send startY, can be corrected + // Calculate appDisplay always for AppGrid state WsTmb scale + let wsTmbWidthAppGrid = opt.MAX_THUMBNAIL_SCALE_APPGRID > 0 + ? Math.round(wsTmbWidth / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE_APPGRID) + : Math.round(wsTmbWidth / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE); + params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbWidthAppGrid, startY]; // send startY, can be corrected let appDisplayBox; if (!transitionParams.transitioning) { appDisplayBox = @@ -1024,9 +1339,9 @@ const ControlsManagerLayoutVertical = { if (opt.CENTER_SEARCH_VIEW) { const dashW = (opt.DASH_VERTICAL ? dashWidth : 0) + spacing; searchWidth = width - 2 * wsTmbWidth - 2 * dashW; - childBox.set_origin(wsTmbWidth + dashW, startY + (opt.DASH_TOP ? dashHeight : spacing) + searchHeight); + childBox.set_origin(wsTmbWidth + dashW, startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + searchHeight); } else { - childBox.set_origin(this._xAlignCenter ? wsTmbWidth + spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight : spacing) + searchHeight); + childBox.set_origin(this._xAlignCenter ? wsTmbWidth + spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + searchHeight); } childBox.set_size(searchWidth, availableHeight); @@ -1037,7 +1352,11 @@ const ControlsManagerLayoutVertical = { }; const ControlsManagerLayoutHorizontal = { - _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsHeight, searchHeight, startY) { + _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, thumbnailsHeight, searchHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const workspaceBox = box.copy(); let [width, height] = workspaceBox.get_size(); // let { x1: startX/* , y1: startY*/ } = workAreaBox; @@ -1046,7 +1365,7 @@ const ControlsManagerLayoutHorizontal = { const dash = Main.overview.dash; // including Dash to Dock and clones properties for compatibility - if (_Util.dashIsDashToDock()) { + if (Me.Util.dashIsDashToDock()) { // Dash to Dock always affects workAreaBox Main.layoutManager._trackedActors.forEach(actor => { if (actor.affectsStruts && actor.actor.width === dash.width) { @@ -1084,7 +1403,7 @@ const ControlsManagerLayoutHorizontal = { case ControlsState.APP_GRID: if (opt.WS_ANIMATION && opt.SHOW_WS_TMB && state === ControlsState.APP_GRID) { workspaceBox.set_origin(...this._workspacesThumbnails.get_position()); - workspaceBox.set_size(...this._workspacesThumbnails.get_size()); + workspaceBox.set_size(thumbnailsWidth, thumbnailsHeight); } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { if (opt.START_Y_OFFSET) { let [x, y] = workAreaBox.get_origin(); @@ -1146,7 +1465,7 @@ const ControlsManagerLayoutHorizontal = { } wsBoxX = /* startX + */xOffset; - wsBoxY = Math.round(startY + yOffset); + wsBoxY = startY + yOffset; workspaceBox.set_origin(Math.round(wsBoxX), Math.round(wsBoxY)); workspaceBox.set_size(Math.round(wWidth), Math.round(wHeight)); } @@ -1156,6 +1475,10 @@ const ControlsManagerLayoutHorizontal = { }, _getAppDisplayBoxForState(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const [width] = box.get_size(); const { x1: startX } = workAreaBox; // const { y1: startY } = workAreaBox; @@ -1163,16 +1486,16 @@ const ControlsManagerLayoutHorizontal = { 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) + 2 * spacing; - const yOffsetB = (opt.WS_TMB_BOTTOM ? thumbnailsHeight : 0) + (opt.DASH_BOTTOM ? dashHeight : 0); + const yOffsetT = (opt.WS_TMB_TOP ? thumbnailsHeight + spacing : 0) + (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0); + const yOffsetB = (opt.WS_TMB_BOTTOM ? thumbnailsHeight + spacing : 0) + (opt.DASH_BOTTOM ? dashHeight : 0); const xOffsetL = opt.DASH_LEFT ? dashWidth : 0; const xOffsetR = opt.DASH_RIGHT ? dashWidth : 0; - const hSpacing = xOffsetL + xOffsetR ? 2 * spacing : 0; + const hSpacing = xOffsetL + xOffsetR ? 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 adHeight = height - yOffsetT - yOffsetB; const appDisplayX = opt.CENTER_APP_GRID ? (width - adWidth) / 2 : xOffsetL + hSpacing; - const appDisplayY = startY + yOffsetT + hSpacing; + const appDisplayY = startY + yOffsetT; switch (state) { case ControlsState.HIDDEN: @@ -1180,36 +1503,36 @@ const ControlsManagerLayoutHorizontal = { // 1 - left, 2 - right, 3 - bottom, 5 - top switch (opt.APP_GRID_ANIMATION) { case 0: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; case 1: - appDisplayBox.set_origin(startX + width, appDisplayY); + appDisplayBox.set_origin(Math.round(startX + width), Math.round(appDisplayY)); break; case 2: - appDisplayBox.set_origin(startX - adWidth, appDisplayY); + appDisplayBox.set_origin(Math.round(startX - adWidth), Math.round(appDisplayY)); break; case 3: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y2); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y2)); break; case 5: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y1 - adHeight); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y1 - adHeight)); break; } break; case ControlsState.APP_GRID: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; } - appDisplayBox.set_size(adWidth, adHeight); + appDisplayBox.set_size(Math.round(adWidth), Math.round(adHeight)); return appDisplayBox; }, vfunc_allocate(container, box) { + const transitionParams = this._stateAdjustment.getStateTransitionParams(); const childBox = new Clutter.ActorBox(); - const { spacing } = this; - + const halfSpacing = spacing / 2; const monitor = Main.layoutManager.findMonitorForActor(this._container); const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index); const startX = workArea.x - monitor.x; @@ -1235,7 +1558,7 @@ const ControlsManagerLayoutHorizontal = { // dash cloud be overridden by the Dash to Dock clone const dash = Main.overview.dash; - if (_Util.dashIsDashToDock()) { + if (Me.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) @@ -1265,60 +1588,53 @@ const 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; + let maxWsTmbScale = opt.MAX_THUMBNAIL_SCALE; + if (opt.SHOW_WS_TMB) { const dashWidthReservation = !opt.WS_TMB_FULL && opt.DASH_VERTICAL ? dashWidth : 0; - let maxScale = opt.MAX_THUMBNAIL_SCALE; - if (!opt.MAX_THUMBNAIL_SCALE_STABLE) { + const searchActive = this._searchController.searchActive; + if (!opt.MAX_THUMBNAIL_SCALE_STABLE && !searchActive) { 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); + maxWsTmbScale = Util.lerp(initState, finalState, transitionParams.progress); } - wsTmbHeight = height * maxScale; + wsTmbHeight = Math.round(height * maxWsTmbScale); let totalTmbSpacing; - [totalTmbSpacing, wsTmbWidth] = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight); + [totalTmbSpacing, wsTmbWidth] = this._workspacesThumbnails.get_preferred_width(wsTmbHeight); wsTmbWidth += totalTmbSpacing; const wsTmbWidthMax = opt.WS_TMB_FULL - ? width - : width - (opt.DASH_VERTICAL ? 0 : dashWidthReservation); + ? width - spacing + : width - dashWidthReservation - 2 * spacing; if (wsTmbWidth > wsTmbWidthMax) { wsTmbWidth = wsTmbWidthMax; - wsTmbHeight = this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth)[1]; + wsTmbHeight = Math.round(this._workspacesThumbnails.get_preferred_height(wsTmbWidth)[1]); } let wsTmbY; if (opt.WS_TMB_TOP) - wsTmbY = Math.round(startY + /* searchHeight + */(opt.DASH_TOP ? dashHeight : spacing / 2)); + wsTmbY = Math.round(startY + (opt.DASH_TOP ? dashHeight : halfSpacing)); else - wsTmbY = Math.round(startY + height - (opt.DASH_BOTTOM ? dashHeight : 0) - wsTmbHeight); + wsTmbY = Math.round(startY + height - (opt.DASH_BOTTOM ? dashHeight : halfSpacing) - wsTmbHeight); - let wstOffset = (width - wsTmbWidth) / 2; - wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2); - let wsTmbX = Math.round(Math.clamp( - startX + wstOffset, - startX + (opt.DASH_LEFT ? dashWidthReservation : 0), - width - wsTmbWidth - startX - (opt.DASH_RIGHT ? dashWidthReservation : 0) - )); + let wstOffset = (width - wsTmbWidth - dashWidthReservation) / 2; + wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * wstOffset; + let wsTmbX = Math.round(startX + (opt.DASH_LEFT ? dashWidthReservation : 0) + wstOffset); childBox.set_origin(wsTmbX, wsTmbY); - childBox.set_size(Math.round(wsTmbWidth), Math.round(wsTmbHeight)); + childBox.set_size(Math.max(wsTmbWidth, 1), Math.max(wsTmbHeight, 1)); this._workspacesThumbnails.allocate(childBox); availableHeight -= wsTmbHeight + spacing; } - if (this._dash.visible) { if (opt.WS_TMB_FULL && opt.DASH_VERTICAL) { const wMaxHeight = height - spacing - wsTmbHeight; @@ -1339,25 +1655,24 @@ const ControlsManagerLayoutHorizontal = { 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 -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset + wsTmbHeight; } else { - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset; } } else { offset = (height - dashHeight) / 2; - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset; } } else { offset = (width - dashWidth) / 2; - dashX = startX + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing)); + dashX = startX + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing)); } childBox.set_origin(Math.round(startX + dashX), Math.round(dashY)); @@ -1368,7 +1683,7 @@ const ControlsManagerLayoutHorizontal = { availableHeight -= opt.DASH_VERTICAL ? 0 : dashHeight; // Workspaces - let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbHeight, searchHeight, startY]; + let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, wsTmbHeight, searchHeight, startY]; // Update cached boxes for (const state of Object.values(ControlsState)) { @@ -1394,7 +1709,7 @@ const ControlsManagerLayoutHorizontal = { // Y position under top Dash let searchEntryX, searchEntryY; if (opt.DASH_TOP) - searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight : 0) + dashHeight - spacing; + searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight : 0) + dashHeight; else searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0); @@ -1416,7 +1731,11 @@ const ControlsManagerLayoutHorizontal = { availableHeight -= searchHeight + spacing; // if (this._appDisplay.visible)... ? Can cause problems - params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbHeight, startY]; + // Calculate appDisplay always for AppGrid state WsTmb scale + let wsTmbHeightAppGrid = opt.MAX_THUMBNAIL_SCALE_APPGRID > 0 + ? Math.round(wsTmbHeight / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE_APPGRID) + : Math.round(wsTmbHeight / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE); + params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbHeightAppGrid, startY]; let appDisplayBox; if (!transitionParams.transitioning) { appDisplayBox = @@ -1435,9 +1754,9 @@ const ControlsManagerLayoutHorizontal = { if (opt.CENTER_SEARCH_VIEW) { const dashW = (opt.DASH_VERTICAL ? dashWidth : 0) + spacing; searchWidth = width - 2 * dashW; - childBox.set_origin(dashW, startY + (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); + childBox.set_origin(dashW, startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); } else { - childBox.set_origin(this._xAlignCenter ? spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); + childBox.set_origin(this._xAlignCenter ? spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); } childBox.set_size(searchWidth, availableHeight); @@ -1462,3 +1781,21 @@ function _getFitModeForState(state) { return FitMode.SINGLE; } } + +const LayoutManager = { + _startupAnimation() { + if (Me.Util.dashIsDashToDock() && !Meta.is_restart()) { + // DtD breaks overview on startup + // Skip animation to hide the mess + this._startupAnimationComplete(); + const controlsManager = Main.overview._overview.controls; + controlsManager._finishStartupSequence.bind(controlsManager)(); + } else if (Meta.is_restart()) { + this._startupAnimationComplete(); + } else if (Main.sessionMode.isGreeter) { + this._startupAnimationGreeter(); + } else { + this._startupAnimationSession(); + } + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/panel.js b/extensions/44/vertical-workspaces/lib/panel.js index 3f44ae7..898407a 100644 --- a/extensions/44/vertical-workspaces/lib/panel.js +++ b/extensions/44/vertical-workspaces/lib/panel.js @@ -10,188 +10,247 @@ 'use strict'; -const { GLib } = imports.gi; -const Main = imports.ui.main; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; +const Clutter = imports.gi.Clutter; -const ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME; +const Main = imports.ui.main; +const Overview = imports.ui.overview; +const Panel = imports.ui.panel; +let Me; let opt; -let _firstRun = true; -let _showingOverviewConId; -let _hidingOverviewConId; -let _styleChangedConId; +const ANIMATION_TIME = Overview.ANIMATION_TIME; -function update(reset = false) { - 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); +var PanelModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // don't even touch this module if disabled or in potential conflict - if (_firstRun && (reset || conflict)) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; - _firstRun = false; + this._showingOverviewConId = 0; + this._hidingOverviewConId = 0; + this._styleChangedConId = 0; + } - const panelBox = Main.layoutManager.panelBox; - if (reset || !moduleEnabled) { - // _disconnectPanel(); - reset = true; - _setPanelPosition(reset); - _updateOverviewConnection(reset); - _reparentPanel(false); + cleanGlobals() { + Me = null; + opt = null; + } - _updateStyleChangedConnection(reset); + update(reset) { + this.moduleEnabled = opt.get('panelModule'); + const conflict = Me.Util.getEnabledExtensions('dash-to-panel').length || + Me.Util.getEnabledExtensions('hidetopbar').length; - panelBox.translation_y = 0; - Main.panel.opacity = 255; - _setPanelStructs(true); - return; + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Panel" module disabled due to potential conflict with another extension`); + + reset = reset || !this.moduleEnabled || conflict || Main.sessionMode.isLocked; + + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' PanelModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + const panelBox = Main.layoutManager.panelBox; + + this._setPanelPosition(); + this._updateStyleChangedConnection(); + + if (opt.PANEL_MODE === 0) { + this._updateOverviewConnection(true); + this._reparentPanel(false); + panelBox.translation_y = 0; + Main.panel.opacity = 255; + this._setPanelStructs(true); + } else if (opt.PANEL_MODE === 1) { + if (opt.SHOW_WS_PREVIEW_BG) { + this._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); + this._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); + this._updateOverviewConnection(true); + } + this._showPanel(true); + } else { + // if ws preview bg is disabled, panel can stay in uiGroup + this._reparentPanel(false); + this._showPanel(false); + this._updateOverviewConnection(); + } + // _connectPanel(); + } else if (opt.PANEL_MODE === 2) { + this._updateOverviewConnection(true); + this._reparentPanel(false); + this._showPanel(false); + // _connectPanel(); + } + this._setPanelStructs(opt.PANEL_MODE === 0); + Main.layoutManager._updateHotCorners(); + + this._overrides.addOverride('ActivitiesButton', Panel.ActivitiesButton.prototype, ActivitiesButton); + + console.debug(' PanelModule - Activated'); } - _setPanelPosition(); - _updateStyleChangedConnection(); + _disableModule() { + const reset = true; + this._setPanelPosition(reset); + this._updateOverviewConnection(reset); + this._reparentPanel(false); + + this._updateStyleChangedConnection(reset); - if (opt.PANEL_MODE === 0) { - _updateOverviewConnection(true); - _reparentPanel(false); + const panelBox = Main.layoutManager.panelBox; 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); + this._setPanelStructs(true); + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' PanelModule - Disabled'); + } + + _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); + } + + _updateStyleChangedConnection(reset = false) { + if (reset) { + if (this._styleChangedConId) { + Main.panel.disconnect(this._styleChangedConId); + this._styleChangedConId = 0; + } + } else if (!this._styleChangedConId) { + this._styleChangedConId = Main.panel.connect('style-changed', () => { + if (opt.PANEL_MODE === 1 && !opt.OVERVIEW_MODE2) + Main.panel.add_style_pseudo_class('overview'); + else if (opt.OVERVIEW_MODE2) + Main.panel.remove_style_pseudo_class('overview'); + }); + } + } + + _updateOverviewConnection(reset = false) { + if (reset) { + if (this._hidingOverviewConId) { + Main.overview.disconnect(this._hidingOverviewConId); + this._hidingOverviewConId = 0; + } + if (this._showingOverviewConId) { + Main.overview.disconnect(this._showingOverviewConId); + this._showingOverviewConId = 0; } - _showPanel(true); } else { - // if ws preview bg is disabled, panel can stay in uiGroup - _reparentPanel(false); - _showPanel(false); - _updateOverviewConnection(); + if (!this._hidingOverviewConId) { + this._hidingOverviewConId = Main.overview.connect('hiding', () => { + if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2) + this._showPanel(false); + }); + } + if (!this._showingOverviewConId) { + this._showingOverviewConId = Main.overview.connect('showing', () => { + if (Main.layoutManager._startingUp) + return; + if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2 || Main.layoutManager.panelBox.translation_y) + this._showPanel(true); + }); + } } - // _connectPanel(); - } else if (opt.PANEL_MODE === 2) { - _updateOverviewConnection(true); - _reparentPanel(false); - _showPanel(false); - // _connectPanel(); } - _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 _updateStyleChangedConnection(reset = false) { - if (reset) { - if (_styleChangedConId) { - Main.panel.disconnect(_styleChangedConId); - _styleChangedConId = 0; + + _reparentPanel(reparent = false) { + const panel = Main.layoutManager.panelBox; + if (reparent && panel.get_parent() === Main.layoutManager.uiGroup) { + Main.layoutManager.uiGroup.remove_child(panel); + 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, panel shouldn't cover objects that should be above + Main.layoutManager.uiGroup.insert_child_at_index(panel, 4); } - } 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'); + } + + _setPanelStructs(state) { + Main.layoutManager._trackedActors.forEach(a => { + if (a.actor === Main.layoutManager.panelBox) + a.affectsStruts = 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));*/ } -} -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); + _showPanel(show = true) { + if (show) { + Main.panel.opacity = 255; + Main.layoutManager.panelBox.ease({ + duration: ANIMATION_TIME, + translation_y: 0, + onComplete: () => { + this._setPanelStructs(opt.PANEL_MODE === 0); + }, }); - } - if (!_showingOverviewConId) { - _showingOverviewConId = Main.overview.connect('showing', () => { - if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2 || Main.layoutManager.panelBox.translation_y) - _showPanel(true); + } else { + const panelHeight = Main.panel.height; + Main.layoutManager.panelBox.ease({ + duration: ANIMATION_TIME, + translation_y: opt.PANEL_POSITION_TOP ? -panelHeight + 1 : panelHeight - 1, + onComplete: () => { + Main.panel.opacity = 0; + this._setPanelStructs(opt.PANEL_MODE === 0); + }, }); } } -} - -function _reparentPanel(reparent = false) { - const panel = Main.layoutManager.panelBox; - if (reparent && panel.get_parent() === Main.layoutManager.uiGroup) { - Main.layoutManager.uiGroup.remove_child(panel); - 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, panel shouldn't cover objects that should be above - Main.layoutManager.uiGroup.insert_child_at_index(panel, 4); - } -} - -function _setPanelStructs(state) { - Main.layoutManager._trackedActors.forEach(a => { - if (a.actor === Main.layoutManager.panelBox) - a.affectsStruts = 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));*/ -} - -function _showPanel(show = true) { - if (show) { - Main.panel.opacity = 255; - Main.layoutManager.panelBox.ease({ - duration: ANIMATION_TIME, - translation_y: 0, - onComplete: () => { - _setPanelStructs(opt.PANEL_MODE === 0); - }, - }); - } else { - const panelHeight = Main.panel.height; - Main.layoutManager.panelBox.ease({ - duration: ANIMATION_TIME, - translation_y: opt.PANEL_POSITION_TOP ? -panelHeight + 1 : panelHeight - 1, - onComplete: () => { - Main.panel.opacity = 0; - _setPanelStructs(opt.PANEL_MODE === 0); - }, - }); - } -} +}; + +const ActivitiesButton = { + vfunc_event(event) { + if (event.type() === Clutter.EventType.TOUCH_END || + event.type() === Clutter.EventType.BUTTON_RELEASE) { + if (Main.overview.shouldToggleByCornerOrButton()) { + if (event.get_button() === Clutter.BUTTON_SECONDARY && !Main.overview.dash.showAppsButton.checked) { + Main.overview.show(2); + Main.overview.dash.showAppsButton.checked = true; + } else { + Main.overview.toggle(); + } + } + } else if (event.type() === Clutter.EventType.SCROLL) { + Main.wm.handleWorkspaceScroll(event); + } + + return Clutter.EVENT_PROPAGATE; + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/recentFilesSearchProvider.js b/extensions/44/vertical-workspaces/lib/recentFilesSearchProvider.js index 86e38f4..b567ff2 100644 --- a/extensions/44/vertical-workspaces/lib/recentFilesSearchProvider.js +++ b/extensions/44/vertical-workspaces/lib/recentFilesSearchProvider.js @@ -1,5 +1,5 @@ /** - * Vertical Workspaces +* V-Shell (Vertical Workspaces) * recentFilesSearchProvider.js * * @author GdH <G-dH@github.com> @@ -9,116 +9,146 @@ 'use strict'; -const { GLib, Gio, Meta, St, Shell, Gtk } = imports.gi; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const RecentManager = imports.gi.Gtk.RecentManager; +const St = imports.gi.St; +const Shell = imports.gi.Shell; const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; -const _Util = Me.imports.lib.util; +let Me; +let opt; // gettext -const _ = Settings._; - -const shellVersion = Settings.shellVersion; - -const ModifierType = imports.gi.Clutter.ModifierType; - -let recentFilesSearchProvider; -let _enableTimeoutId = 0; +let _; // prefix helps to eliminate results from other search providers // so it needs to be something less common // needs to be accessible from vw module -var prefix = 'fq//'; +const PREFIX = 'fq//'; + +var RecentFilesSearchProviderModule = class { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._recentFilesSearchProvider = null; + this._enableTimeoutId = 0; + } -var opt; + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } -function getOverviewSearchResult() { - return Main.overview._overview.controls._searchController._searchResults; -} + update(reset) { + this.moduleEnabled = opt.get('recentFilesSearchProviderModule'); + reset = reset || !this.moduleEnabled; -function update(reset = false) { - 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) { - disable(); - opt = null; + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' RecentFilesSearchProviderModule - Keeping untouched'); } -} -function enable() { - // delay because Fedora had problem to register a new provider soon after Shell restarts - _enableTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 2000, - () => { - if (!recentFilesSearchProvider) { - recentFilesSearchProvider = new RecentFilesSearchProvider(opt); - getOverviewSearchResult()._registerProvider(recentFilesSearchProvider); + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._recentFilesSearchProvider) { + this._recentFilesSearchProvider = new RecentFilesSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._recentFilesSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; } - _enableTimeoutId = 0; - return GLib.SOURCE_REMOVE; - } - ); -} + ); -function disable() { - if (recentFilesSearchProvider) { - getOverviewSearchResult()._unregisterProvider(recentFilesSearchProvider); - recentFilesSearchProvider = null; - } - if (_enableTimeoutId) { - GLib.source_remove(_enableTimeoutId); - _enableTimeoutId = 0; + console.debug(' RecentFilesSearchProviderModule - Activated'); } -} -function makeResult(window, i) { - const app = Shell.WindowTracker.get_default().get_window_app(window); - const appName = app ? app.get_name() : 'Unknown'; - const windowTitle = window.get_title(); - 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, - windowTitle, - window, - }; -} + _disableModule() { + if (this._recentFilesSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._recentFilesSearchProvider); + this._recentFilesSearchProvider = null; + } + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } -const closeSelectedRegex = /^\/x!$/; -const closeAllResultsRegex = /^\/xa!$/; -const moveToWsRegex = /^\/m[0-9]+$/; -const moveAllToWsRegex = /^\/ma[0-9]+$/; + console.debug(' RecentFilesSearchProviderModule - Disabled'); + } -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.get_description = () => _('Search recent files'); - this.appInfo.get_name = () => _('Recent Files'); - this.appInfo.get_id = () => this.id; - this.appInfo.get_icon = () => Gio.icon_new_for_string('document-open-recent-symbolic'); - this.appInfo.should_show = () => true; + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +}; +class RecentFilesSearchProvider { + constructor() { + this.id = 'recent-files'; + const appSystem = Shell.AppSystem.get_default(); + let appInfo = appSystem.lookup_app('org.gnome.Nautilus.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -w', _('Recent Files'), null); + appInfo.get_description = () => _('Search recent files'); + appInfo.get_name = () => _('Recent Files'); + appInfo.get_id = () => 'org.gnome.Nautilus.desktop'; + appInfo.get_icon = () => Gio.icon_new_for_string('document-open-recent-symbolic'); + appInfo.should_show = () => true; + + this.appInfo = appInfo; this.canLaunchSearch = true; this.isRemoteProvider = false; } + getInitialResultSet(terms, callback /* , cancellable = null*/) { + // In GS 43 callback arg has been removed + /* if (Me.shellVersion >= 43) + cancellable = callback; */ + + const filesDict = {}; + let files = []; + if (terms[0].startsWith(PREFIX)) + files = RecentManager.get_default().get_items(); + + // Detect whether time stamps are in int, or in GLib.DateTime object + this._timeNeedsConversion = files[0]?.get_modified().to_unix; + + for (let file of files) + filesDict[file.get_uri()] = file; + + this.files = filesDict; + + if (Me.shellVersion >= 43) + return new Promise(resolve => resolve(this._getResultSet(terms))); + else + callback(this._getResultSet(terms)); + + return null; + } + _getResultSet(terms) { - if (!terms[0].startsWith(prefix)) + if (!terms[0].startsWith(PREFIX)) return []; // do not modify original terms let termsCopy = [...terms]; // search for terms without prefix - termsCopy[0] = termsCopy[0].replace(prefix, ''); + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); const candidates = this.files; const _terms = [].concat(termsCopy); @@ -135,15 +165,18 @@ const RecentFilesSearchProvider = class RecentFilesSearchProvider { 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 (opt.SEARCH_FUZZY) - m = _Util.fuzzyMatch(term, name); + m = Me.Util.fuzzyMatch(term, name); else - m = _Util.strictMatch(term, name); + m = Me.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()); + if (this._timeNeedsConversion) + results.sort((a, b) => this.files[a.id].get_modified().to_unix() < this.files[b.id].get_modified().to_unix()); + else + results.sort((a, b) => this.files[a.id].get_modified() < this.files[b.id].get_modified()); this.resultIds = results.map(item => item.id); return this.resultIds; @@ -151,7 +184,7 @@ const RecentFilesSearchProvider = class RecentFilesSearchProvider { getResultMetas(resultIds, callback = null) { const metas = resultIds.map(id => this.getResultMeta(id)); - if (shellVersion >= 43) + if (Me.shellVersion >= 43) return new Promise(resolve => resolve(metas)); else if (callback) callback(metas); @@ -172,89 +205,54 @@ const RecentFilesSearchProvider = class RecentFilesSearchProvider { } 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); - let path = info.get_attribute_byte_string( - Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH); - let icon, gicon; - if (path) { - gicon = Gio.FileIcon.new(Gio.File.new_for_path(path)); - } else { - const appInfo = Gio.AppInfo.get_default_for_type(result.get_mime_type(), false); - if (appInfo) - gicon = appInfo.get_icon(); - } + const appInfo = Gio.AppInfo.get_default_for_type(result.get_mime_type(), false); + if (appInfo) + gicon = appInfo.get_icon(); if (gicon) icon = new St.Icon({ gicon, icon_size: size }); else icon = new St.Icon({ icon_name: 'icon-missing', icon_size: size }); - return icon; } - launchSearch(/* terms, timeStamp */) { - this._openNautilus('recent:///'); + launchSearch(terms, timeStamp) { + const appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -w recent:///', 'Nautilus', null); + appInfo.launch([], global.create_app_launch_context(timeStamp, -1)); } - _openNautilus(uri) { - try { - GLib.spawn_command_line_async(`nautilus -ws ${uri}`); - } catch (e) { - log(e); - } - } - - activateResult(resultId /* , terms, timeStamp */) { - const file = this.files[resultId]; - - if (_Util.isShiftPressed()) { + activateResult(resultId, terms, timeStamp) { + const uri = resultId; + const context = global.create_app_launch_context(timeStamp, -1); + if (Me.Util.isShiftPressed()) { Main.overview.toggle(); - this._openNautilus(file.get_uri()); + this.appInfo.launch_uris([uri], context); + } else if (Gio.app_info_launch_default_for_uri(uri, context)) { + // update recent list after (hopefully) successful activation + const recentManager = RecentManager.get_default(); + recentManager.add_item(resultId); } else { - const appInfo = Gio.AppInfo.get_default_for_type(file.get_mime_type(), false); - if (!(appInfo && appInfo.launch_uris([file.get_uri()], null))) - this._openNautilus(file.get_uri()); + this.appInfo.launch_uris([uri], context); } } - 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()); - - for (let file of files) - filesDict[file.get_uri()] = file; - - - this.files = filesDict; - - if (shellVersion >= 43) - return new Promise(resolve => resolve(this._getResultSet(terms))); - else - callback(this._getResultSet(terms)); - - return null; + filterResults(results /* , maxResults*/) { + // return results.slice(0, maxResults); + return results.slice(0, 20); } - filterResults(results, maxResults) { - return results.slice(0, 20); - // return results.slice(0, maxResults); + getSubsearchResultSet(previousResults, terms, callback) { + if (Me.shellVersion < 43) { + this.getSubsearchResultSet42(terms, callback); + return null; + } + return this.getInitialResultSet(terms); } - getSubsearchResultSet(previousResults, terms, callback /* , cancellable*/) { - // if we return previous results, quick typers get non-actual results + getSubsearchResultSet42(terms, callback) { callback(this._getResultSet(terms)); } - - /* createResultObject(resultMeta) { - return this.files[resultMeta.id]; - }*/ -}; +} diff --git a/extensions/44/vertical-workspaces/lib/search.js b/extensions/44/vertical-workspaces/lib/search.js index 8540626..618c5ed 100644 --- a/extensions/44/vertical-workspaces/lib/search.js +++ b/extensions/44/vertical-workspaces/lib/search.js @@ -9,92 +9,137 @@ */ 'use strict'; -const { Shell, Gio, St, Clutter } = imports.gi; -const Main = imports.ui.main; + +const Clutter = imports.gi.Clutter; +const Shell = imports.gi.Shell; +const St = imports.gi.St; const AppDisplay = imports.ui.appDisplay; +const IconGrid = imports.ui.iconGrid; +const Main = imports.ui.main; 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 Me; +// gettext +let _; 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; +var SearchModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; - // don't even touch this module if disabled - if (_firstRun && reset) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } - _firstRun = false; + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } - if (_overrides) - _overrides.removeAll(); + update(reset) { + this.moduleEnabled = opt.get('searchModule'); + const conflict = false; - _updateSearchViewWidth(reset); + reset = reset || !this.moduleEnabled || conflict; - if (reset) { - Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.FILL; - opt = null; - _overrides = null; - return; + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' SearchModule - Keeping untouched'); + } + + _activateModule() { + this._updateSearchViewWidth(); + + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('AppSearchProvider', AppDisplay.AppSearchProvider.prototype, AppSearchProvider); + this._overrides.addOverride('SearchResult', Search.SearchResult.prototype, SearchResult); + this._overrides.addOverride('SearchResultsView', Search.SearchResultsView.prototype, SearchResultsView); + this._overrides.addOverride('ProviderInfo', Search.ProviderInfo.prototype, ProviderInfo); + + // 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; + console.debug(' SearchModule - Activated'); } - _overrides = new _Util.Overrides(); + _disableModule() { + const reset = true; + this._updateSearchViewWidth(reset); - _overrides.addOverride('AppSearchProvider', AppDisplay.AppSearchProvider.prototype, AppSearchProvider); - _overrides.addOverride('SearchResult', Search.SearchResult.prototype, SearchResult); - _overrides.addOverride('SearchResultsView', Search.SearchResultsView.prototype, SearchResultsView); + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.FILL; - // 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; + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); } - if (reset) { - searchContent.set_style(''); - } else { - let width = Math.round(SEARCH_MAX_WIDTH * opt.SEARCH_VIEW_SCALE); - searchContent.set_style(`max-width: ${width}px;`); + _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) { + 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; + if (Me.shellVersion < 43) { + let initializedId = this._parentalControlsManager.connect('app-filter-changed', () => { + if (this._parentalControlsManager.initialized) { + this._parentalControlsManager.disconnect(initializedId); + this.getInitialResultSet(terms, callback, cancellable); + } + }); + return null; + } else { + // callback has been removed in 43 + cancellable = callback; + return new Promise(resolve => { + let initializedId = this._parentalControlsManager.connect('app-filter-changed', async () => { + if (this._parentalControlsManager.initialized) { + this._parentalControlsManager.disconnect(initializedId); + resolve(await this.getInitialResultSet(terms, cancellable)); + } + }); + }); + } } - const pattern = terms.join(' '); + let appInfoList = Shell.AppSystem.get_default().get_installed(); let weightList = {}; @@ -113,22 +158,25 @@ const AppSearchProvider = { shouldShow = appInfo.should_show() && this._parentalControlsManager.shouldShowApp(appInfo); if (shouldShow) { + let id = appInfo.get_id().split('.'); + id = id[id.length - 2] || ''; + let baseName = appInfo.get_string('Name') || ''; 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}`; + name = `${dispName} ${id}`; + string = `${dispName} ${gName} ${baseName} ${description} ${categories} ${keywords} ${id}`; } } let m = -1; if (shouldShow && opt.SEARCH_FUZZY) { - m = _Util.fuzzyMatch(pattern, name); - m = (m + _Util.strictMatch(pattern, string)) / 2; + m = Me.Util.fuzzyMatch(pattern, name); + m = (m + Me.Util.strictMatch(pattern, string)) / 2; } else if (shouldShow) { - m = _Util.strictMatch(pattern, string); + m = Me.Util.strictMatch(pattern, string); } if (m !== -1) @@ -143,16 +191,18 @@ const AppSearchProvider = { // 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)); + appInfoList.sort((a, b) => Me.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) + if (Me.shellVersion < 43) { callback(results); - else + return null; + } else { return new Promise(resolve => resolve(results)); + } }, // App search result size @@ -181,12 +231,69 @@ const SearchResult = { St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText); } // don't close overview if Shift key is pressed - Shift moves windows to the workspace - if (!_Util.isShiftPressed()) + if (!Me.Util.isShiftPressed()) Main.overview.toggle(); }, }; const SearchResultsView = { + _doSearch() { + if (!this._doProviderSearch) { + this._doSearchLegacy(); + return; + } + this._startingSearch = false; + + let previousResults = this._results; + this._results = {}; + + this._providers.forEach(provider => { + const onlyVShellProviders = this._terms.includes('wq//') || this._terms.includes('fq//'); + if (!onlyVShellProviders || (onlyVShellProviders && (provider.id.includes('open-windows') || provider.id.includes('recent-files')))) { + let previousProviderResults = previousResults[provider.id]; + this._doProviderSearch(provider, previousProviderResults); + } + }); + + this._updateSearchProgress(); + + this._clearSearchTimeout(); + }, + + _doSearchLegacy() { + this._startingSearch = false; + + let previousResults = this._results; + this._results = {}; + + this._providers.forEach(provider => { + const onlyVShellProviders = this._terms.includes('wq//') || this._terms.includes('fq//'); + if (!onlyVShellProviders || (onlyVShellProviders && (provider.id.includes('open-windows') || provider.id.includes('recent-files')))) { + provider.searchInProgress = true; + + let previousProviderResults = previousResults[provider.id]; + if (this._isSubSearch && previousProviderResults) { + provider.getSubsearchResultSet(previousProviderResults, + this._terms, + results => { + this._gotResults(results, provider); + }, + this._cancellable); + } else { + provider.getInitialResultSet(this._terms, + results => { + this._gotResults(results, provider); + }, + this._cancellable); + } + } + }); + + this._updateSearchProgress(); + + this._clearSearchTimeout(); + }, + _updateSearchProgress() { let haveResults = this._providers.some(provider => { let display = provider.display; @@ -204,3 +311,13 @@ const SearchResultsView = { } }, }; + +// fixes app is null error if search provider id is not a desktop app id. +const ProviderInfo = { + animateLaunch() { + let appSys = Shell.AppSystem.get_default(); + let app = appSys.lookup_app(this.provider.appInfo.get_id()); + if (app && app.state === Shell.AppState.STOPPED) + IconGrid.zoomOutActor(this._content); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/searchController.js b/extensions/44/vertical-workspaces/lib/searchController.js new file mode 100644 index 0000000..76b65e8 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/searchController.js @@ -0,0 +1,94 @@ +/** + * V-Shell (Vertical Workspaces) + * searchController.js + * + * @author GdH <G-dH@github.com> + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; + +const Main = imports.ui.main; + +let Me; +let opt; + +var SearchControllerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._originalOnStageKeyPress = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('searchControllerModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' SearchControllerModule - Keeping untouched'); + } + + _activateModule() { + if (!this._originalOnStageKeyPress) + this._originalOnStageKeyPress = Main.overview._overview.controls._searchController._onStageKeyPress; + + Main.overview._overview.controls._searchController._onStageKeyPress = SearchControllerCommon._onStageKeyPress; + console.debug(' SearchControllerModule - Activated'); + } + + _disableModule() { + if (this._originalOnStageKeyPress) + Main.overview._overview.controls._searchController._onStageKeyPress = this._originalOnStageKeyPress; + this._originalOnStageKeyPress = null; + + console.debug(' SearchControlerModule - Disabled'); + } +}; + +// if opt.ESC_BEHAVIOR > 0 force close the overview +const SearchControllerCommon = { + _onStageKeyPress(actor, event) { + // Ignore events while anything but the overview has + // pushed a modal (system modals, looking glass, ...) + if (Main.modalCount > 1) + return Clutter.EVENT_PROPAGATE; + + let symbol = event.get_key_symbol(); + if (symbol === Clutter.KEY_Escape) { + if (this._searchActive && !opt.ESC_BEHAVIOR) { + this.reset(); + } else if (this._showAppsButton.checked && !opt.ESC_BEHAVIOR) { + this._showAppsButton.checked = false; + } else { + this.reset(); + Main.overview.hide(); + } + + return Clutter.EVENT_STOP; + } else if (this._shouldTriggerSearch(symbol)) { + this.startSearch(event); + } + return Clutter.EVENT_PROPAGATE; + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/settings.js b/extensions/44/vertical-workspaces/lib/settings.js index 66f3a45..cbb74f2 100644 --- a/extensions/44/vertical-workspaces/lib/settings.js +++ b/extensions/44/vertical-workspaces/lib/settings.js @@ -9,25 +9,15 @@ 'use strict'; -const { GLib } = imports.gi; +const GLib = imports.gi.GLib; -const Config = imports.misc.config; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -var shellVersion = parseFloat(Config.PACKAGE_VERSION); - -const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']); -var _ = Gettext.gettext; -const _schema = Me.metadata['settings-schema']; - -// common instance of Options accessible from all modules -var opt; +let Me; var Options = class Options { - constructor() { - this._gsettings = ExtensionUtils.getSettings(_schema); + constructor(me) { + Me = me; + + this._gsettings = Me.gSettings; this._connectionIds = []; this._writeTimeoutId = 0; this._gsettings.delay(); @@ -51,8 +41,8 @@ var Options = class Options { wsMaxSpacing: ['int', 'ws-max-spacing'], wsPreviewScale: ['int', 'ws-preview-scale'], secWsPreviewScale: ['int', 'secondary-ws-preview-scale'], - secWsPreviewShift: ['bool', 'secondary-ws-preview-shift'], - wsThumbnailsFull: ['bool', 'ws-thumbnails-full'], + secWsPreviewShift: ['boolean', 'secondary-ws-preview-shift'], + wsThumbnailsFull: ['boolean', 'ws-thumbnails-full'], secWsThumbnailsPosition: ['int', 'secondary-ws-thumbnails-position'], dashPosition: ['int', 'dash-position'], dashPositionAdjust: ['int', 'dash-position-adjust'], @@ -64,6 +54,7 @@ var Options = class Options { dashMaxIconSize: ['int', 'dash-max-icon-size'], dashShowWindowsIcon: ['int', 'dash-show-windows-icon'], dashShowRecentFilesIcon: ['int', 'dash-show-recent-files-icon'], + dashShowExtensionsIcon: ['int', 'dash-show-extensions-icon'], centerDashToWs: ['boolean', 'center-dash-to-ws'], showAppsIconPosition: ['int', 'show-app-icon-position'], wsThumbnailScale: ['int', 'ws-thumbnail-scale'], @@ -73,13 +64,17 @@ var Options = class Options { centerSearch: ['boolean', 'center-search'], centerAppGrid: ['boolean', 'center-app-grid'], dashBgOpacity: ['int', 'dash-bg-opacity'], + dashBgColor: ['int', 'dash-bg-color'], dashBgRadius: ['int', 'dash-bg-radius'], + dashBgGS3Style: ['boolean', 'dash-bg-gs3-style'], + runningDotStyle: ['int', 'running-dot-style'], 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'], + searchBgBrightness: ['int', 'search-bg-brightness'], overviewBgBlurSigma: ['int', 'overview-bg-blur-sigma'], appGridBgBlurSigma: ['int', 'app-grid-bg-blur-sigma'], smoothBlurTransitions: ['boolean', 'smooth-blur-transitions'], @@ -87,9 +82,8 @@ var Options = class Options { searchViewAnimation: ['int', 'search-view-animation'], workspaceAnimation: ['int', 'workspace-animation'], animationSpeedFactor: ['int', 'animation-speed-factor'], - fixUbuntuDock: ['boolean', 'fix-ubuntu-dock'], winPreviewIconSize: ['int', 'win-preview-icon-size'], - alwaysShowWinTitles: ['boolean', 'always-show-win-titles'], + winTitlePosition: ['int', 'win-title-position'], startupState: ['int', 'startup-state'], overviewMode: ['int', 'overview-mode'], workspaceSwitcherAnimation: ['int', 'workspace-switcher-animation'], @@ -105,17 +99,18 @@ var Options = class Options { appGridContent: ['int', 'app-grid-content'], appGridIncompletePages: ['boolean', 'app-grid-incomplete-pages'], appGridOrder: ['int', 'app-grid-order'], + appFolderOrder: ['int', 'app-folder-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'], + searchWindowsOrder: ['int', 'search-windows-order'], searchFuzzy: ['boolean', 'search-fuzzy'], searchMaxResultsRows: ['int', 'search-max-results-rows'], dashShowWindowsBeforeActivation: ['int', 'dash-show-windows-before-activation'], dashIconScroll: ['int', 'dash-icon-scroll'], + dashIsolateWorkspaces: ['boolean', 'dash-isolate-workspaces'], searchWindowsIconScroll: ['int', 'search-windows-icon-scroll'], panelVisibility: ['int', 'panel-visibility'], panelPosition: ['int', 'panel-position'], @@ -123,6 +118,8 @@ var Options = class Options { wsSwPopupHPosition: ['int', 'ws-sw-popup-h-position'], wsSwPopupVPosition: ['int', 'ws-sw-popup-v-position'], wsSwPopupMode: ['int', 'ws-sw-popup-mode'], + wsSwitcherWraparound: ['boolean', 'ws-switcher-wraparound'], + wsSwitcherIgnoreLast: ['boolean', 'ws-switcher-ignore-last'], favoritesNotify: ['int', 'favorites-notify'], notificationPosition: ['int', 'notification-position'], osdPosition: ['int', 'osd-position'], @@ -131,17 +128,26 @@ var Options = class Options { hotCornerFullscreen: ['boolean', 'hot-corner-fullscreen'], hotCornerRipples: ['boolean', 'hot-corner-ripples'], alwaysActivateSelectedWindow: ['boolean', 'always-activate-selected-window'], - windowIconClickSearch: ['boolean', 'window-icon-click-search'], + winPreviewSecBtnAction: ['int', 'win-preview-sec-mouse-btn-action'], + winPreviewMidBtnAction: ['int', 'win-preview-mid-mouse-btn-action'], + winPreviewShowCloseButton: ['boolean', 'win-preview-show-close-button'], + windowIconClickAction: ['int', 'window-icon-click-action'], + overlayKeyPrimary: ['int', 'overlay-key-primary'], overlayKeySecondary: ['int', 'overlay-key-secondary'], + overviewEscBehavior: ['int', 'overview-esc-behavior'], + newWindowFocusFix: ['boolean', 'new-window-focus-fix'], + appGridPerformance: ['boolean', 'app-grid-performance'], + windowThumbnailScale: ['int', 'window-thumbnail-scale'], - 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'], + windowAttentionHandlerModule: ['boolean', 'win-attention-handler-module'], + windowThumbnailModule: ['boolean', 'window-thumbnail-module'], swipeTrackerModule: ['boolean', 'swipe-tracker-module'], + searchControllerModule: ['boolean', 'search-controller-module'], searchModule: ['boolean', 'search-module'], panelModule: ['boolean', 'panel-module'], overlayKeyModule: ['boolean', 'overlay-key-module'], @@ -151,6 +157,9 @@ var Options = class Options { dashModule: ['boolean', 'dash-module'], appFavoritesModule: ['boolean', 'app-favorites-module'], appDisplayModule: ['boolean', 'app-display-module'], + windowSearchProviderModule: ['boolean', 'window-search-provider-module'], + recentFilesSearchProviderModule: ['boolean', 'recent-files-search-provider-module'], + extensionsSearchProviderModule: ['boolean', 'extensions-search-provider-module'], profileName1: ['string', 'profile-name-1'], profileName2: ['string', 'profile-name-2'], @@ -158,9 +167,10 @@ var Options = class Options { profileName4: ['string', 'profile-name-4'], }; this.cachedOptions = {}; + } - this.shellVersion = shellVersion; - // this.storeProfile(0); + cleanGlobals() { + Me = null; } connect(name, callback) { @@ -183,7 +193,7 @@ var Options = class Options { get(option, updateCache = false) { if (!this.options[option]) { - log(`[${Me.metadata.name}] Error: Option ${option} is undefined.`); + console.error(`[${Me.metadata.name}] Error: Option ${option} is undefined.`); return null; } @@ -195,7 +205,6 @@ var Options = class Options { else gSettings = this._gsettings; - this.cachedOptions[option] = gSettings.get_value(key).deep_unpack(); } @@ -242,7 +251,8 @@ var Options = class Options { storeProfile(index) { const profile = {}; Object.keys(this.options).forEach(v => { - profile[v] = this.get(v).toString(); + if (!v.startsWith('profileName')) + profile[v] = this.get(v).toString(); }); this._gsettings.set_value(`profile-data-${index}`, new GLib.Variant('a{ss}', profile)); @@ -250,8 +260,14 @@ var Options = class Options { loadProfile(index) { const options = this._gsettings.get_value(`profile-data-${index}`).deep_unpack(); + // set the aaa-loading-data so extension.js doesn't reset V-Shell after each profile item + // delayed gsettings writes are processed alphabetically, so this key will be processed first this._gsettings.set_boolean('aaa-loading-profile', !this._gsettings.get_boolean('aaa-loading-profile')); for (let o of Object.keys(options)) { + if (!this.options[o]) { + console.error(`[${Me.metadata.name}] Error: "${o}" is not a valid profile key -> Update your profile`); + continue; + } const [type] = this.options[o]; let value = options[o]; switch (type) { @@ -275,7 +291,14 @@ var Options = class Options { } _updateSettings() { - this.DASH_POSITION = this.get('dashPosition', true); + this._updateCachedSettings(); + this.DASH_BG_ALPHA = this.get('dashBgOpacity') / 100; + this.DASH_BG_OPACITY = this.get('dashBgOpacity') * 2.5; + this.DASH_BG_COLOR = this.get('dashBgColor'); + this.DASH_BG_RADIUS = this.get('dashBgRadius'); + this.DASH_BG_LIGHT = this.DASH_BG_COLOR === 1; + this.DASH_BG_GS3_STYLE = this.get('dashBgGS3Style'); + this.DASH_POSITION = this.get('dashModule') ? this.get('dashPosition') : 2; this.DASH_TOP = this.DASH_POSITION === 0; this.DASH_RIGHT = this.DASH_POSITION === 1; this.DASH_BOTTOM = this.DASH_POSITION === 2; @@ -284,26 +307,35 @@ 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_ISOLATE_WS = this.get('dashIsolateWorkspaces'); + + this.DASH_CLICK_ACTION = this.get('dashShowWindowsBeforeActivation'); + this.DASH_CLICK_SWITCH_BEFORE_ACTIVATION = this.DASH_CLICK_ACTION === 1; + this.DASH_CLICK_OPEN_NEW_WIN = this.DASH_CLICK_ACTION === 2; + this.DASH_CLICK_PREFER_WORKSPACE = this.DASH_CLICK_ACTION === 3; + + this.DASH_ICON_SCROLL = this.get('dashIconScroll'); this.DASH_SHIFT_CLICK_MV = true; - this.SEARCH_WINDOWS_ICON_SCROLL = this.get('searchWindowsIconScroll', true); + this.RUNNING_DOT_STYLE = this.get('runningDotStyle'); + + this.SEARCH_WINDOWS_ICON_SCROLL = this.get('searchWindowsIconScroll'); - this.DASH_POSITION_ADJUSTMENT = this.get('dashPositionAdjust', true); + this.DASH_POSITION_ADJUSTMENT = this.get('dashPositionAdjust'); this.DASH_POSITION_ADJUSTMENT = this.DASH_POSITION_ADJUSTMENT * -1 / 100; // range 1 to -1 - this.CENTER_DASH_WS = this.get('centerDashToWs', true); + this.CENTER_DASH_WS = this.get('centerDashToWs'); - this.MAX_ICON_SIZE = 64; // updates from main module - this.SHOW_WINDOWS_ICON = this.get('dashShowWindowsIcon', true); - this.SHOW_RECENT_FILES_ICON = this.get('dashShowRecentFilesIcon', true); + this.MAX_ICON_SIZE = this.get('dashMaxIconSize'); + this.SHOW_WINDOWS_ICON = this.get('dashShowWindowsIcon'); + this.SHOW_RECENT_FILES_ICON = this.get('dashShowRecentFilesIcon'); + this.SHOW_EXTENSIONS_ICON = this.get('dashShowExtensionsIcon'); - this.WS_TMB_POSITION = this.get('workspaceThumbnailsPosition', true); + this.WS_TMB_POSITION = this.get('workspaceThumbnailsPosition'); this.ORIENTATION = this.WS_TMB_POSITION > 4 ? 0 : 1; - this.WORKSPACE_MAX_SPACING = this.get('wsMaxSpacing', true); + this.WORKSPACE_MAX_SPACING = this.get('wsMaxSpacing'); // 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'); // translate ws tmb position to 0 top, 1 right, 2 bottom, 3 left // 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]; @@ -311,81 +343,104 @@ var Options = class Options { 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('secWsThumbnailsPosition', true); + this.WS_TMB_POSITION_ADJUSTMENT = this.get('wsTmbPositionAdjust') * -1 / 100; // range 1 to -1 + this.SEC_WS_TMB_POSITION = this.get('secWsThumbnailsPosition'); 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_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.SEC_WS_TMB_POSITION_ADJUSTMENT = this.get('secWsTmbPositionAdjust') * -1 / 100; // range 1 to -1 + this.SEC_WS_PREVIEW_SHIFT = this.get('secWsPreviewShift'); + this.SHOW_WST_LABELS = this.get('showWsTmbLabels'); + this.SHOW_WST_LABELS_ON_HOVER = this.get('showWsTmbLabelsOnHover'); + this.CLOSE_WS_BUTTON_MODE = this.get('closeWsButtonMode'); - 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 = this.get('wsThumbnailScale') / 100; + if (this.MAX_THUMBNAIL_SCALE === 0) { + this.MAX_THUMBNAIL_SCALE = 0.01; + this.SHOW_WS_TMB = false; + } + this.MAX_THUMBNAIL_SCALE_APPGRID = this.get('wsThumbnailScaleAppGrid') / 100; + this.SHOW_WS_TMB_APPGRID = true; + if (this.MAX_THUMBNAIL_SCALE_APPGRID === 0) { + this.MAX_THUMBNAIL_SCALE_APPGRID = 0.01; + this.SHOW_WS_TMB_APPGRID = false; + } 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; + this.SEC_MAX_THUMBNAIL_SCALE = this.get('secWsThumbnailScale') / 100; + if (this.SEC_MAX_THUMBNAIL_SCALE === 0) { + this.SEC_MAX_THUMBNAIL_SCALE = 0.01; + this.SHOW_SEC_WS_TMB = false; + } + + this.WS_PREVIEW_SCALE = this.get('wsPreviewScale') / 100; + this.SEC_WS_PREVIEW_SCALE = this.get('secWsPreviewScale') / 100; // calculate number of possibly visible neighbor previews according to ws scale 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.SHOW_WS_TMB_BG = this.get('showWsSwitcherBg') && this.SHOW_WS_TMB; + this.WS_PREVIEW_BG_RADIUS = this.get('wsPreviewBgRadius'); + this.SHOW_WS_PREVIEW_BG = this.get('showWsPreviewBg'); - this.CENTER_APP_GRID = this.get('centerAppGrid', true); + this.CENTER_APP_GRID = this.get('centerAppGrid'); - this.SHOW_SEARCH_ENTRY = this.get('showSearchEntry', true); - this.CENTER_SEARCH_VIEW = this.get('centerSearch', true); - this.APP_GRID_ANIMATION = this.get('appGridAnimation', true); + this.SHOW_SEARCH_ENTRY = this.get('showSearchEntry'); + this.CENTER_SEARCH_VIEW = this.get('centerSearch'); + this.APP_GRID_ANIMATION = this.get('appGridAnimation'); if (this.APP_GRID_ANIMATION === 4) this.APP_GRID_ANIMATION = this._getAnimationDirection(); - this.SEARCH_VIEW_ANIMATION = this.get('searchViewAnimation', true); + this.SEARCH_VIEW_ANIMATION = this.get('searchViewAnimation'); if (this.SEARCH_VIEW_ANIMATION === 4) this.SEARCH_VIEW_ANIMATION = 3; - this.WS_ANIMATION = this.get('workspaceAnimation', true); + this.WS_ANIMATION = this.get('workspaceAnimation'); - this.WIN_PREVIEW_ICON_SIZE = [64, 48, 32, 22, 8][this.get('winPreviewIconSize', true)]; - this.ALWAYS_SHOW_WIN_TITLES = this.get('alwaysShowWinTitles', true); + this.WIN_PREVIEW_ICON_SIZE = [64, 48, 32, 22, 8][this.get('winPreviewIconSize')]; + this.WIN_TITLES_POSITION = this.get('winTitlePosition'); + this.ALWAYS_SHOW_WIN_TITLES = this.WIN_TITLES_POSITION === 1; - 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); + this.STARTUP_STATE = this.get('startupState'); + this.SHOW_BG_IN_OVERVIEW = this.get('showBgInOverview'); + this.OVERVIEW_BG_BRIGHTNESS = this.get('overviewBgBrightness') / 100; + this.SEARCH_BG_BRIGHTNESS = this.get('searchBgBrightness') / 100; + this.OVERVIEW_BG_BLUR_SIGMA = this.get('overviewBgBlurSigma'); + this.APP_GRID_BG_BLUR_SIGMA = this.get('appGridBgBlurSigma'); + this.SMOOTH_BLUR_TRANSITIONS = this.get('smoothBlurTransitions'); - this.OVERVIEW_MODE = this.get('overviewMode', true); + this.OVERVIEW_MODE = this.get('overviewMode'); this.OVERVIEW_MODE2 = this.OVERVIEW_MODE === 2; this.WORKSPACE_MODE = this.OVERVIEW_MODE ? 0 : 1; - this.STATIC_WS_SWITCHER_BG = this.get('workspaceSwitcherAnimation', true); + this.STATIC_WS_SWITCHER_BG = this.get('workspaceSwitcherAnimation'); - this.ANIMATION_TIME_FACTOR = this.get('animationSpeedFactor', true) / 100; + this.ANIMATION_TIME_FACTOR = this.get('animationSpeedFactor') / 100; - 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); - this.SEARCH_FUZZY = this.get('searchFuzzy', true); + this.SEARCH_ICON_SIZE = this.get('searchIconSize'); + this.SEARCH_VIEW_SCALE = this.get('searchViewScale') / 100; + this.SEARCH_MAX_ROWS = this.get('searchMaxResultsRows'); + this.SEARCH_FUZZY = this.get('searchFuzzy'); - 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_ALLOW_INCOMPLETE_PAGES = this.get('appGridIncompletePages'); + this.APP_GRID_ICON_SIZE = this.get('appGridIconSize'); + this.APP_GRID_COLUMNS = this.get('appGridColumns'); + this.APP_GRID_ROWS = this.get('appGridRows'); 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('appGridContent', true); + this.APP_GRID_ORDER = this.get('appGridOrder'); + this.APP_GRID_ALPHABET = [1, 2].includes(this.APP_GRID_ORDER); + this.APP_GRID_FOLDERS_FIRST = this.APP_GRID_ORDER === 1; + this.APP_GRID_FOLDERS_LAST = this.APP_GRID_ORDER === 2; + this.APP_GRID_USAGE = this.APP_GRID_ORDER === 3; + + this.APP_FOLDER_ORDER = this.get('appFolderOrder'); + this.APP_FOLDER_ALPHABET = this.APP_FOLDER_ORDER === 1; + this.APP_FOLDER_USAGE = this.APP_FOLDER_ORDER === 2; + + this.APP_GRID_INCLUDE_DASH = this.get('appGridContent'); /* APP_GRID_INCLUDE_DASH 0 - Include All 1 - Include All - Favorites and Runnings First @@ -397,46 +452,50 @@ var Options = class Options { 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_NAMES_MODE = this.get('appGridNamesMode'); - 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_ICON_SIZE = this.get('appGridFolderIconSize'); + this.APP_GRID_FOLDER_ICON_GRID = this.get('appGridFolderIconGrid'); + this.APP_GRID_FOLDER_COLUMNS = this.get('appGridFolderColumns'); + this.APP_GRID_FOLDER_ROWS = this.get('appGridFolderRows'); + this.APP_GRID_SPACING = this.get('appGridSpacing'); 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.APP_GRID_FOLDER_ADAPTIVE = !this.APP_GRID_FOLDER_COLUMNS && !this.APP_GRID_FOLDER_ROWS; + this.APP_GRID_ACTIVE_PREVIEW = this.get('appGridActivePreview'); + this.APP_GRID_FOLDER_CENTER = this.get('appGridFolderCenter'); + this.APP_GRID_PAGE_WIDTH_SCALE = this.get('appGridPageWidthScale') / 100; - this.APP_GRID_ICON_SIZE_DEFAULT = this.APP_GRID_ACTIVE_PREVIEW && !this.APP_GRID_ORDER ? 176 : 96; + this.APP_GRID_ICON_SIZE_DEFAULT = this.APP_GRID_ACTIVE_PREVIEW && !this.APP_GRID_USAGE ? 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.APP_GRID_PERFORMANCE = this.get('appGridPerformance'); - this.PANEL_POSITION_TOP = this.get('panelPosition', true) === 0; - this.PANEL_MODE = this.get('panelVisibility', true); + this.WINDOW_SEARCH_ORDER = this.get('searchWindowsOrder'); + + this.PANEL_POSITION_TOP = this.get('panelPosition') === 0; + this.PANEL_MODE = this.get('panelVisibility'); 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_MODE = this.get('windowAttentionMode'); 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.WS_SW_POPUP_H_POSITION = this.get('wsSwPopupHPosition') / 100; + this.WS_SW_POPUP_V_POSITION = this.get('wsSwPopupVPosition') / 100; + this.WS_SW_POPUP_MODE = this.get('wsSwPopupMode'); + + this.WS_WRAPAROUND = this.get('wsSwitcherWraparound'); + this.WS_IGNORE_LAST = this.get('wsSwitcherIgnoreLast'); - this.SHOW_FAV_NOTIFICATION = this.get('favoritesNotify', true); - this.NOTIFICATION_POSITION = this.get('notificationPosition', true); + this.SHOW_FAV_NOTIFICATION = this.get('favoritesNotify'); + this.NOTIFICATION_POSITION = this.get('notificationPosition'); - this.OSD_POSITION = this.get('osdPosition', true); + this.OSD_POSITION = this.get('osdPosition'); - this.HOT_CORNER_ACTION = this.get('hotCornerAction', true); - this.HOT_CORNER_POSITION = this.get('hotCornerPosition', true); + this.HOT_CORNER_ACTION = this.get('hotCornerAction'); + this.HOT_CORNER_POSITION = this.get('hotCornerPosition'); if (this.HOT_CORNER_POSITION === 6 && this.DASH_VISIBLE) this.HOT_CORNER_EDGE = true; else @@ -451,13 +510,24 @@ var Options = class Options { else this.HOT_CORNER_POSITION = 0; } - this.HOT_CORNER_FULLSCREEN = this.get('hotCornerFullscreen', true); - this.HOT_CORNER_RIPPLES = this.get('hotCornerRipples', true); + this.HOT_CORNER_FULLSCREEN = this.get('hotCornerFullscreen'); + this.HOT_CORNER_RIPPLES = this.get('hotCornerRipples'); + + this.ALWAYS_ACTIVATE_SELECTED_WINDOW = this.get('alwaysActivateSelectedWindow'); + this.WIN_PREVIEW_SEC_BTN_ACTION = this.get('winPreviewSecBtnAction'); + this.WIN_PREVIEW_MID_BTN_ACTION = this.get('winPreviewMidBtnAction'); + this.SHOW_CLOSE_BUTTON = this.get('winPreviewShowCloseButton'); + this.WINDOW_ICON_CLICK_ACTION = this.get('windowIconClickAction'); + + this.OVERLAY_KEY_PRIMARY = this.get('overlayKeyPrimary'); + this.OVERLAY_KEY_SECONDARY = this.get('overlayKeySecondary'); + + this.ESC_BEHAVIOR = this.get('overviewEscBehavior'); - this.ALWAYS_ACTIVATE_SELECTED_WINDOW = this.get('alwaysActivateSelectedWindow', true); - this.WINDOW_ICON_CLICK_SEARCH = this.get('windowIconClickSearch', true); + this.WINDOW_THUMBNAIL_ENABLED = this.get('windowThumbnailModule'); + this.WINDOW_THUMBNAIL_SCALE = this.get('windowThumbnailScale') / 100; - this.OVERLAY_KEY_SECONDARY = this.get('overlayKeySecondary', true); + this.FIX_NEW_WINDOW_FOCUS = this.get('newWindowFocusFix'); } _getAnimationDirection() { diff --git a/extensions/44/vertical-workspaces/lib/swipeTracker.js b/extensions/44/vertical-workspaces/lib/swipeTracker.js index d9c3407..7122ead 100644 --- a/extensions/44/vertical-workspaces/lib/swipeTracker.js +++ b/extensions/44/vertical-workspaces/lib/swipeTracker.js @@ -10,61 +10,90 @@ 'use strict'; -const { Clutter, GObject } = imports.gi; +const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; + const Main = imports.ui.main; const SwipeTracker = imports.ui.swipeTracker; -const Me = imports.misc.extensionUtils.getCurrentExtension(); - +let Me; let opt; -let _firstRun = true; -let _vwGestureUpdateId; -let _originalGestureUpdateId; +var SwipeTrackerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('swipeTrackerModule', true); - reset = reset || !moduleEnabled; + this._firstActivation = true; + this.moduleEnabled = false; + } - // don't even touch this module if disabled - if (_firstRun && reset) - return; + cleanGlobals() { + Me = null; + opt = null; + } - _firstRun = false; + update(reset) { + this.moduleEnabled = opt.get('swipeTrackerModule'); + const conflict = false; - if (reset || !opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL - // original swipeTrackers' orientation and updateGesture function - Main.overview._swipeTracker.orientation = Clutter.Orientation.VERTICAL; - Main.wm._workspaceAnimation._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL; - Main.overview._swipeTracker._updateGesture = SwipeTracker.SwipeTracker.prototype._updateGesture; - if (_vwGestureUpdateId) { - Main.overview._swipeTracker._touchpadGesture.disconnect(_vwGestureUpdateId); - _vwGestureUpdateId = 0; + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); } - if (_originalGestureUpdateId) { - Main.overview._swipeTracker._touchpadGesture.unblock_signal_handler(_originalGestureUpdateId); - _originalGestureUpdateId = 0; + if (reset && this._firstActivation) + console.debug(' SwipeTrackerModule - Keeping untouched'); + } + + _activateModule() { + if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL + this._setVertical(); + } else { + this._setHorizontal(); } + console.debug(' SwipeTrackerModule - Activated'); + } - opt = null; - return; + _disableModule() { + this._setHorizontal(); + + console.debug(' SwipeTrackerModule - Disabled'); } - if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL + _setVertical() { // reverse swipe gestures for enter/leave overview and ws switching Main.overview._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL; Main.wm._workspaceAnimation._swipeTracker.orientation = Clutter.Orientation.VERTICAL; // overview's updateGesture() function should reflect ws tmb position to match appGrid/ws animation direction // function in connection cannot be overridden in prototype of its class because connected is actually another copy of the original function - if (!_originalGestureUpdateId) { - _originalGestureUpdateId = GObject.signal_handler_find(Main.overview._swipeTracker._touchpadGesture, { signalId: 'update' }); - Main.overview._swipeTracker._touchpadGesture.block_signal_handler(_originalGestureUpdateId); + if (!this._originalGestureUpdateId) { + this._originalGestureUpdateId = GObject.signal_handler_find(Main.overview._swipeTracker._touchpadGesture, { signalId: 'update' }); + Main.overview._swipeTracker._touchpadGesture.block_signal_handler(this._originalGestureUpdateId); Main.overview._swipeTracker._updateGesture = SwipeTrackerVertical._updateGesture; - _vwGestureUpdateId = Main.overview._swipeTracker._touchpadGesture.connect('update', SwipeTrackerVertical._updateGesture.bind(Main.overview._swipeTracker)); + this._vwGestureUpdateId = Main.overview._swipeTracker._touchpadGesture.connect('update', SwipeTrackerVertical._updateGesture.bind(Main.overview._swipeTracker)); + } + } + + _setHorizontal() { + // original swipeTrackers' orientation and updateGesture function + Main.overview._swipeTracker.orientation = Clutter.Orientation.VERTICAL; + Main.wm._workspaceAnimation._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL; + Main.overview._swipeTracker._updateGesture = SwipeTracker.SwipeTracker.prototype._updateGesture; + if (this._vwGestureUpdateId) { + Main.overview._swipeTracker._touchpadGesture.disconnect(this._vwGestureUpdateId); + this._vwGestureUpdateId = 0; + } + if (this._originalGestureUpdateId) { + Main.overview._swipeTracker._touchpadGesture.unblock_signal_handler(this._originalGestureUpdateId); + this._originalGestureUpdateId = 0; } } -} +}; const SwipeTrackerVertical = { _updateGesture(gesture, time, delta, distance) { diff --git a/extensions/44/vertical-workspaces/lib/util.js b/extensions/44/vertical-workspaces/lib/util.js index 5f5c069..9bc4365 100644 --- a/extensions/44/vertical-workspaces/lib/util.js +++ b/extensions/44/vertical-workspaces/lib/util.js @@ -10,16 +10,27 @@ 'use strict'; +const Clutter = imports.gi.Clutter; +const Meta = imports.gi.Meta; const Gi = imports._gi; -const { Shell, Meta, Clutter } = imports.gi; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Shell = imports.gi.Shell; -const Config = imports.misc.config; -const Main = imports.ui.main; +const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +let Me; -var shellVersion = parseFloat(Config.PACKAGE_VERSION); +let _installedExtensions; + +function init(me) { + Me = me; +} + +function cleanGlobals() { + Me = null; + _installedExtensions = null; +} var Overrides = class { constructor() { @@ -27,8 +38,13 @@ var Overrides = class { } addOverride(name, prototype, overrideList) { + const backup = this.overrideProto(prototype, overrideList, name); + // don't update originals when override's just refreshing, keep initial content + let originals = this._overrides[name]?.originals; + if (!originals) + originals = backup; this._overrides[name] = { - originals: this.overrideProto(prototype, overrideList), + originals, prototype, }; } @@ -38,15 +54,15 @@ var Overrides = class { if (!override) return false; - this.overrideProto(override.prototype, override.originals); - this._overrides[name] = undefined; + this.overrideProto(override.prototype, override.originals, name); + delete this._overrides[name]; return true; } removeAll() { for (let name in this._overrides) { this.removeOverride(name); - this._overrides[name] = undefined; + delete this._overrides[name]; } } @@ -54,13 +70,17 @@ var Overrides = class { proto[Gi.hook_up_vfunc_symbol](symbol, func); } - overrideProto(proto, overrides) { + overrideProto(proto, overrides, name) { const backup = {}; - + const originals = this._overrides[name]?.originals; for (let symbol in overrides) { if (symbol.startsWith('after_')) { const actualSymbol = symbol.slice('after_'.length); - const fn = proto[actualSymbol]; + let fn; + if (originals && originals[actualSymbol]) + fn = originals[actualSymbol]; + else + fn = proto[actualSymbol]; const afterFn = overrides[symbol]; proto[actualSymbol] = function (...args) { args = Array.prototype.slice.call(args); @@ -72,11 +92,11 @@ var Overrides = class { } else { backup[symbol] = proto[symbol]; if (symbol.startsWith('vfunc')) { - if (shellVersion < 42) + if (Me.shellVersion < 42) this.hookVfunc(proto, symbol.slice(6), overrides[symbol]); else this.hookVfunc(proto[Gi.gobject_prototype_symbol], symbol.slice(6), overrides[symbol]); - } else { + } else if (overrides[symbol] !== null) { proto[symbol] = overrides[symbol]; } } @@ -85,87 +105,21 @@ var Overrides = class { } }; -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() { +function openPreferences(metadata) { + if (!metadata) + metadata = Me.metadata; 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') { + if (win.get_title()?.includes(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')) { + } else if (win.wm_class?.includes('org.gnome.Shell.Extensions')) { // this is prefs window of another extension metaWin = win; isVW = false; @@ -182,17 +136,20 @@ function openPreferences() { } if (!metaWin || (metaWin && !isVW)) { - try { - Main.extensionManager.openExtensionPrefs(Me.metadata.uuid, '', {}); - } catch (e) { - log(e); - } + GLib.idle_add(GLib.PRIORITY_LOW, () => { + try { + Main.extensionManager.openExtensionPrefs(metadata.uuid, '', {}); + } catch (e) { + console.error(e); + } + }); } } function activateSearchProvider(prefix = '') { const searchEntry = Main.overview.searchEntry; - if (!searchEntry.get_text() || !searchEntry.get_text().startsWith(prefix)) { + const searchEntryText = searchEntry.get_text(); + if (!searchEntryText || (searchEntryText && !searchEntry.get_text().startsWith(prefix))) { prefix = `${prefix} `; const position = prefix.length; searchEntry.set_text(prefix); @@ -220,9 +177,16 @@ function reorderWorkspace(direction = 0) { global.workspace_manager.reorder_workspace(activeWs, targetIdx); } +function activateKeyboardForWorkspaceView() { + Main.ctrlAltTabManager._items.forEach(i => { + if (i.sortGroup === 1 && i.name === 'Windows') + Main.ctrlAltTabManager.focusGroup(i); + }); +} + function exposeWindows(adjustment, activateKeyboard) { // expose windows for static overview modes - if (!adjustment.value && !Main.overview._animationInProgress) { + if (!adjustment.value/* && !Main.overview._animationInProgress*/) { if (adjustment.value === 0) { adjustment.value = 0; adjustment.ease(1, { @@ -330,13 +294,48 @@ function isMoreRelevant(stringA, stringB, pattern) { 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 getEnabledExtensions(pattern = '') { + let result = []; + // extensionManager is unreliable at startup (if not all extensions were loaded) + // but gsettings key can contain removed extensions... + // therefore we have to look into filesystem, what's really installed + if (!_installedExtensions) { + const extensionFiles = [...collectFromDatadirs('extensions', true)]; + _installedExtensions = extensionFiles.map(({ info }) => { + let fileType = info.get_file_type(); + if (fileType !== Gio.FileType.DIRECTORY) + return null; + const uuid = info.get_name(); + return uuid; + }); + } + const enabled = Main.extensionManager._enabledExtensions; + result = _installedExtensions.filter(ext => enabled.includes(ext)); + return result.filter(uuid => uuid !== null && uuid.includes(pattern)); +} + +function* collectFromDatadirs(subdir, includeUserDir) { + let dataDirs = GLib.get_system_data_dirs(); + if (includeUserDir) + dataDirs.unshift(GLib.get_user_data_dir()); + + for (let i = 0; i < dataDirs.length; i++) { + let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]); + let dir = Gio.File.new_for_path(path); + + let fileEnum; + try { + fileEnum = dir.enumerate_children('standard::name,standard::type', + Gio.FileQueryInfoFlags.NONE, null); + } catch (e) { + fileEnum = null; + } + if (fileEnum !== null) { + let info; + while ((info = fileEnum.next_file(null))) + yield { dir: fileEnum.get_child(info), info }; + } + } } function getScrollDirection(event) { diff --git a/extensions/44/vertical-workspaces/lib/winTmb.js b/extensions/44/vertical-workspaces/lib/winTmb.js new file mode 100644 index 0000000..b18ea18 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/winTmb.js @@ -0,0 +1,525 @@ +/** + * V-Shell (Vertical Workspaces) + * WinTmb + * + * @author GdH <G-dH@github.com> + * @copyright 2021-2023 + * @license GPL-3.0 + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const St = imports.gi.St; + +const AltTab = imports.ui.altTab; +const DND = imports.ui.dnd; +const Main = imports.ui.main; + +let Me; +let opt; + +const SCROLL_ICON_OPACITY = 240; +const DRAG_OPACITY = 200; +const CLOSE_BTN_OPACITY = 240; + + +var WinTmbModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this._removeTimeouts(); + + this.moduleEnabled = opt.get('windowThumbnailModule'); + + reset = reset || !this.moduleEnabled; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WinTmb - Keeping untouched'); + } + + _activateModule() { + this._timeouts = {}; + if (!this._windowThumbnails) + this._windowThumbnails = []; + + Main.overview.connectObject('hidden', () => this.showThumbnails(), this); + console.debug(' WinTmb - Activated'); + } + + _disableModule() { + Main.overview.disconnectObject(this); + this._disconnectStateAdjustment(); + this.removeAllThumbnails(); + console.debug(' WinTmb - Disabled'); + } + + _removeTimeouts() { + if (this._timeouts) { + Object.values(this._timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + this._timeouts = null; + } + } + + createThumbnail(metaWin) { + const thumbnail = new WindowThumbnail(metaWin, { + 'height': Math.floor(opt.WINDOW_THUMBNAIL_SCALE * global.display.get_monitor_geometry(global.display.get_current_monitor()).height), + 'thumbnailsOnScreen': this._windowThumbnails.length, + }); + + this._windowThumbnails.push(thumbnail); + thumbnail.connect('removed', tmb => { + this._windowThumbnails.splice(this._windowThumbnails.indexOf(tmb), 1); + tmb.destroy(); + if (!this._windowThumbnails.length) + this._disconnectStateAdjustment(); + }); + + if (!this._stateAdjustmentConId) { + this._stateAdjustmentConId = Main.overview._overview.controls._stateAdjustment.connectObject('notify::value', () => { + if (!this._thumbnailsHidden && (!opt.OVERVIEW_MODE2 || opt.WORKSPACE_MODE)) + this.hideThumbnails(); + }, this); + } + } + + hideThumbnails() { + this._windowThumbnails.forEach(tmb => { + tmb.ease({ + opacity: 0, + duration: 200, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => tmb.hide(), + }); + }); + this._thumbnailsHidden = true; + } + + showThumbnails() { + this._windowThumbnails.forEach(tmb => { + tmb.show(); + tmb.ease({ + opacity: 255, + duration: 100, + mode: Clutter.AnimationMode.LINEAR, + }); + }); + this._thumbnailsHidden = false; + } + + removeAllThumbnails() { + this._windowThumbnails.forEach(tmb => tmb.remove()); + this._windowThumbnails = []; + } + + _disconnectStateAdjustment() { + Main.overview._overview.controls._stateAdjustment.disconnectObject(this); + } +}; + +const WindowThumbnail = GObject.registerClass({ + Signals: { 'removed': {} }, +}, class WindowThumbnail extends St.Widget { + _init(metaWin, args) { + this._hoverShowsPreview = false; + this._customOpacity = 255; + this._initTmbHeight = args.height; + this._minimumHeight = Math.floor(5 / 100 * global.display.get_monitor_geometry(global.display.get_current_monitor()).height); + this._scrollTimeout = 100; + this._positionOffset = args.thumbnailsOnScreen; + this._reverseTmbWheelFunc = false; + this._click_count = 1; + this._prevBtnPressTime = 0; + this.w = metaWin; + super._init({ + layout_manager: new Clutter.BinLayout(), + visible: true, + reactive: true, + can_focus: true, + track_hover: true, + }); + this.connect('button-release-event', this._onBtnReleased.bind(this)); + this.connect('scroll-event', this._onScrollEvent.bind(this)); + // this.connect('motion-event', this._onMouseMove.bind(this)); // may be useful in the future.. + + this._delegate = this; + this._draggable = DND.makeDraggable(this, { dragActorOpacity: DRAG_OPACITY }); + this._draggable.connect('drag-end', this._end_drag.bind(this)); + this._draggable.connect('drag-cancelled', this._end_drag.bind(this)); + this._draggable._animateDragEnd = eventTime => { + this._draggable._animationInProgress = true; + this._draggable._onAnimationComplete(this._draggable._dragActor, eventTime); + this.opacity = this._customOpacity; + }; + + this.clone = new Clutter.Clone({ reactive: true }); + Main.layoutManager.addChrome(this); + + this.window = this.w.get_compositor_private(); + + this.clone.set_source(this.window); + + this.add_child(this.clone); + this._addCloseButton(); + this._addScrollModeIcon(); + + this.connect('enter-event', () => { + global.display.set_cursor(Meta.Cursor.POINTING_HAND); + this._closeButton.opacity = CLOSE_BTN_OPACITY; + this._scrollModeBin.opacity = SCROLL_ICON_OPACITY; + if (this._hoverShowsPreview && !Main.overview._shown) { + this._closeButton.opacity = 50; + this._showWindowPreview(false, true); + } + }); + + this.connect('leave-event', () => { + global.display.set_cursor(Meta.Cursor.DEFAULT); + this._closeButton.opacity = 0; + this._scrollModeBin.opacity = 0; + if (this._winPreview) + this._destroyWindowPreview(); + }); + + this._setSize(true); + this.set_position(...this._getInitialPosition()); + this.show(); + this.window_id = this.w.get_id(); + this.tmbRedrawDirection = true; + + // remove thumbnail content and hide thumbnail if its window is destroyed + this.windowConnect = this.window.connect('destroy', () => { + if (this) + this.remove(); + }); + } + + _getInitialPosition() { + const offset = 20; + let monitor = Main.layoutManager.monitors[global.display.get_current_monitor()]; + let x = Math.min(monitor.x + monitor.width - (this.window.width * this.scale) - offset); + let y = Math.min(monitor.y + monitor.height - (this.window.height * this.scale) - offset - ((this._positionOffset * this._initTmbHeight) % (monitor.height - this._initTmbHeight))); + return [x, y]; + } + + _setSize(resetScale = false) { + if (resetScale) + this.scale = Math.min(1.0, this._initTmbHeight / this.window.height); + + const width = this.window.width * this.scale; + const height = this.window.height * this.scale; + this.set_size(width, height); + if (this.icon) { + this.icon.scale_x = this.scale; + this.icon.scale_y = this.scale; + } + + // when the scale of this. actor change, this.clone resize accordingly, + // but the reactive area of the actor doesn't change until the actor is redrawn + // this updates the actor's input region area: + Main.layoutManager._queueUpdateRegions(); + } + + /* _onMouseMove(actor, event) { + let [pos_x, pos_y] = event.get_coords(); + let state = event.get_state(); + if (this._ctrlPressed(state)) { + } + }*/ + + _onBtnReleased(actor, event) { + // Clutter.Event.click_count property in no longer available, since GS42 + if ((event.get_time() - this._prevBtnPressTime) < Clutter.Settings.get_default().double_click_time) + this._click_count += 1; + else + this._click_count = 1; + + this._prevBtnPressTime = event.get_time(); + + if (this._click_count === 2 && event.get_button() === Clutter.BUTTON_PRIMARY) + this.w.activate(global.get_current_time()); + + + const button = event.get_button(); + const state = event.get_state(); + switch (button) { + case Clutter.BUTTON_PRIMARY: + if (this._ctrlPressed(state)) { + this._setSize(); + } else { + this._reverseTmbWheelFunc = !this._reverseTmbWheelFunc; + this._scrollModeBin.set_child(this._reverseTmbWheelFunc ? this._scrollModeSourceIcon : this._scrollModeResizeIcon); + } + return Clutter.EVENT_STOP; + case Clutter.BUTTON_SECONDARY: + if (this._ctrlPressed(state)) { + this.remove(); + } else { + this._hoverShowsPreview = !this._hoverShowsPreview; + this._showWindowPreview(); + } + return Clutter.EVENT_STOP; + case Clutter.BUTTON_MIDDLE: + if (this._ctrlPressed(state)) + this.w.delete(global.get_current_time()); + return Clutter.EVENT_STOP; + default: + return Clutter.EVENT_PROPAGATE; + } + } + + _onScrollEvent(actor, event) { + let direction = Me.Util.getScrollDirection(event); + + if (this._actionTimeoutActive()) + return Clutter.EVENT_PROPAGATE; + let state = event.get_state(); + switch (direction) { + case Clutter.ScrollDirection.UP: + if (this._shiftPressed(state)) { + this.opacity = Math.min(255, this.opacity + 24); + this._customOpacity = this.opacity; + } else if (this._reverseTmbWheelFunc !== this._ctrlPressed(state)) { + this._switchSourceWin(-1); + } else if (this._reverseTmbWheelFunc === this._ctrlPressed(state)) { + this.scale = Math.max(0.05, this.scale - 0.025); + } + break; + case Clutter.ScrollDirection.DOWN: + if (this._shiftPressed(state)) { + this.opacity = Math.max(48, this.opacity - 24); + this._customOpacity = this.opacity; + } else if (this._reverseTmbWheelFunc !== this._ctrlPressed(state)) { + this._switchSourceWin(+1); + } else if (this._reverseTmbWheelFunc === this._ctrlPressed(state)) { + this.scale = Math.min(1, this.scale + 0.025); + } + break; + default: + return Clutter.EVENT_PROPAGATE; + } + this._setSize(); + return Clutter.EVENT_STOP; + } + + remove() { + if (this.clone) { + this.window.disconnect(this.windowConnect); + this.clone.set_source(null); + } + if (this._winPreview) + this._destroyWindowPreview(); + + this.emit('removed'); + } + + _end_drag() { + this.set_position(this._draggable._dragOffsetX + this._draggable._dragX, this._draggable._dragOffsetY + this._draggable._dragY); + this._setSize(); + } + + _ctrlPressed(state) { + return (state & Clutter.ModifierType.CONTROL_MASK) !== 0; + } + + _shiftPressed(state) { + return (state & Clutter.ModifierType.SHIFT_MASK) !== 0; + } + + _switchSourceWin(direction) { + let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null); + windows = windows.filter(w => !(w.skip_taskbar || w.minimized)); + let idx = -1; + for (let i = 0; i < windows.length; i++) { + if (windows[i] === this.w) { + idx = i + direction; + break; + } + } + idx = idx >= windows.length ? 0 : idx; + idx = idx < 0 ? windows.length - 1 : idx; + let w = windows[idx]; + let win = w.get_compositor_private(); + this.clone.set_source(win); + this.window.disconnect(this.windowConnect); + // the new thumbnail should be the same height as the previous one + this.scale = (this.scale * this.window.height) / win.height; + this.window = win; + this.windowConnect = this.window.connect('destroy', () => { + if (this) + this.remove(); + }); + this.w = w; + + if (this._winPreview) + this._showWindowPreview(true); + } + + _actionTimeoutActive() { + const timeout = this._reverseTmbWheelFunc ? this._scrollTimeout : this._scrollTimeout / 4; + if (!this._lastActionTime || Date.now() - this._lastActionTime > timeout) { + this._lastActionTime = Date.now(); + return false; + } + return true; + } + + /* _setIcon() { + let tracker = Shell.WindowTracker.get_default(); + let app = tracker.get_window_app(this.w); + let icon = app + ? app.create_icon_texture(this.height) + : new St.Icon({ icon_name: 'icon-missing', icon_size: this.height }); + icon.x_expand = icon.y_expand = true; + if (this.icon) + this.icon.destroy(); + this.icon = icon; + }*/ + + _addCloseButton() { + const closeButton = new St.Button({ + opacity: 0, + style_class: 'window-close', + child: new St.Icon({ icon_name: 'preview-close-symbolic' }), + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.START, + x_expand: true, + y_expand: true, + }); + + closeButton.set_style(` + margin: 3px; + background-color: rgba(200, 0, 0, 0.9); + `); + + closeButton.connect('clicked', () => { + this.remove(); + return Clutter.EVENT_STOP; + }); + + this._closeButton = closeButton; + this.add_child(this._closeButton); + } + + _addScrollModeIcon() { + this._scrollModeBin = new St.Bin({ + x_expand: true, + y_expand: true, + }); + this._scrollModeResizeIcon = new St.Icon({ + icon_name: 'view-fullscreen-symbolic', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + x_expand: true, + y_expand: true, + opacity: SCROLL_ICON_OPACITY, + style_class: 'icon-dropshadow', + scale_x: 0.5, + scale_y: 0.5, + }); + this._scrollModeResizeIcon.set_style(` + margin: 13px; + color: rgb(255, 255, 255); + box-shadow: 0 0 40px 40px rgba(0,0,0,0.7); + `); + this._scrollModeSourceIcon = new St.Icon({ + icon_name: 'media-skip-forward-symbolic', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + x_expand: true, + y_expand: true, + opacity: SCROLL_ICON_OPACITY, + style_class: 'icon-dropshadow', + scale_x: 0.5, + scale_y: 0.5, + }); + this._scrollModeSourceIcon.set_style(` + margin: 13px; + color: rgb(255, 255, 255); + box-shadow: 0 0 40px 40px rgba(0,0,0,0.7); + `); + this._scrollModeBin.set_child(this._scrollModeResizeIcon); + this.add_child(this._scrollModeBin); + this._scrollModeBin.opacity = 0; + } + + _showWindowPreview(update = false, dontDestroy = false) { + if (this._winPreview && !dontDestroy) { + this._destroyWindowPreview(); + this._previewCreationTime = 0; + this._closeButton.opacity = CLOSE_BTN_OPACITY; + if (!update) + return; + } + + if (!this._winPreview) { + this._winPreview = new AltTab.CyclerHighlight(); + global.window_group.add_actor(this._winPreview); + [this._winPreview._xPointer, this._winPreview._yPointer] = global.get_pointer(); + } + + if (!update) { + this._winPreview.opacity = 0; + this._winPreview.ease({ + opacity: 255, + duration: 70, + mode: Clutter.AnimationMode.LINEAR, + /* onComplete: () => { + this._closeButton.opacity = 50; + },*/ + }); + + this.ease({ + opacity: Math.min(50, this._customOpacity), + duration: 70, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => { + }, + }); + } else { + this._winPreview.opacity = 255; + } + this._winPreview.window = this.w; + this._winPreview._window = this.w; + global.window_group.set_child_above_sibling(this._winPreview, null); + } + + _destroyWindowPreview() { + if (this._winPreview) { + this._winPreview.ease({ + opacity: 0, + duration: 100, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => { + this._winPreview.destroy(); + this._winPreview = null; + this.opacity = this._customOpacity; + }, + }); + } + } +}); diff --git a/extensions/44/vertical-workspaces/lib/windowAttentionHandler.js b/extensions/44/vertical-workspaces/lib/windowAttentionHandler.js index 10703c2..a3db986 100644 --- a/extensions/44/vertical-workspaces/lib/windowAttentionHandler.js +++ b/extensions/44/vertical-workspaces/lib/windowAttentionHandler.js @@ -11,47 +11,69 @@ '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; +const WindowAttentionHandler = imports.ui.windowAttentionHandler; +let Me; let opt; -let _firstRun = false; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('winAttentionHandlerModule', true); - reset = reset || !moduleEnabled; +var WindowAttentionHandlerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - if (_firstRun && reset) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } - _firstRun = false; - if (reset) { - reset = true; - _updateConnections(reset); + cleanGlobals() { + Me = null; opt = null; - return; } - _updateConnections(); -} + update(reset) { + this.moduleEnabled = opt.get('windowAttentionHandlerModule'); + const conflict = false; -function _updateConnections(reset) { - global.display.disconnectObject(Main.windowAttentionHandler); + reset = reset || !this.moduleEnabled || conflict; - const handlerFnc = reset - ? Main.windowAttentionHandler._onWindowDemandsAttention - : WindowAttentionHandlerCommon._onWindowDemandsAttention; + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WindowAttentionHandlerModule - Keeping untouched'); + } + + _activateModule() { + this._updateConnections(); + console.debug(' WindowAttentionHandlerModule - Activated'); + } - global.display.connectObject( - 'window-demands-attention', handlerFnc.bind(Main.windowAttentionHandler), - 'window-marked-urgent', handlerFnc.bind(Main.windowAttentionHandler), - Main.windowAttentionHandler); -} + _disableModule() { + const reset = true; + this._updateConnections(reset); + + console.debug(' WindowAttentionHandlerModule - Disabled'); + } + + _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) { diff --git a/extensions/44/vertical-workspaces/lib/windowManager.js b/extensions/44/vertical-workspaces/lib/windowManager.js index 2d46b0b..0cae6aa 100644 --- a/extensions/44/vertical-workspaces/lib/windowManager.js +++ b/extensions/44/vertical-workspaces/lib/windowManager.js @@ -10,81 +10,101 @@ 'use strict'; -const { GObject, Clutter, Meta } = imports.gi; +const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; const Main = imports.ui.main; const WindowManager = imports.ui.windowManager; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -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 Me; let opt; -let _firstRun = true; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('windowManagerModule', true); - reset = reset || !moduleEnabled; +var WindowManagerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // don't even touch this module if disabled - if (_firstRun && reset) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; - _firstRun = false; + this._originalMinimizeSigId = 0; + this._minimizeSigId = 0; + this._originalUnminimizeSigId = 0; + this._unminimizeSigId = 0; + } - if (_overrides) - _overrides.removeAll(); + cleanGlobals() { + Me = null; + opt = null; + } + update(reset) { + this.moduleEnabled = opt.get('windowManagerModule'); + const conflict = false; - _replaceMinimizeFunction(reset); + reset = reset || !this.moduleEnabled || conflict; + // don't even touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WindowManagerModule - Keeping untouched'); + } - if (reset) { - _overrides = null; - opt = null; - return; + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WindowManager', WindowManager.WindowManager.prototype, WindowManagerCommon); + + if (!this._minimizeSigId) { + this._originalMinimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'minimize' }); + if (this._originalMinimizeSigId) { + Main.wm._shellwm.block_signal_handler(this._originalMinimizeSigId); + this._minimizeSigId = Main.wm._shellwm.connect('minimize', WindowManagerCommon._minimizeWindow.bind(Main.wm)); + } + + this._originalUnminimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'unminimize' }); + if (this._originalUnminimizeSigId) { + Main.wm._shellwm.block_signal_handler(this._originalUnminimizeSigId); + this._unminimizeSigId = Main.wm._shellwm.connect('unminimize', WindowManagerCommon._unminimizeWindow.bind(Main.wm)); + } + } + console.debug(' WindowManagerModule - Activated'); } - _overrides = new _Util.Overrides(); - - _overrides.addOverride('WindowManager', WindowManager.WindowManager.prototype, WindowManagerCommon); -} - -// ------------- Fix and adapt minimize/unminimize animations -------------------------------------- - -let _originalMinimizeSigId; -let _minimizeSigId; -let _originalUnminimizeSigId; -let _unminimizeSigId; - -function _replaceMinimizeFunction(reset = false) { - if (reset) { - Main.wm._shellwm.disconnect(_minimizeSigId); - _minimizeSigId = 0; - Main.wm._shellwm.unblock_signal_handler(_originalMinimizeSigId); - _originalMinimizeSigId = 0; - - Main.wm._shellwm.disconnect(_unminimizeSigId); - _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) { - Main.wm._shellwm.block_signal_handler(_originalMinimizeSigId); - _minimizeSigId = Main.wm._shellwm.connect('minimize', WindowManagerCommon._minimizeWindow.bind(Main.wm)); + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + if (this._minimizeSigId) { + Main.wm._shellwm.disconnect(this._minimizeSigId); + this._minimizeSigId = 0; + } + if (this._originalMinimizeSigId) { + Main.wm._shellwm.unblock_signal_handler(this._originalMinimizeSigId); + this._originalMinimizeSigId = 0; } - _originalUnminimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'unminimize' }); - if (_originalUnminimizeSigId) { - Main.wm._shellwm.block_signal_handler(_originalUnminimizeSigId); - _unminimizeSigId = Main.wm._shellwm.connect('unminimize', WindowManagerCommon._unminimizeWindow.bind(Main.wm)); + if (this._unminimizeSigId) { + Main.wm._shellwm.disconnect(this._unminimizeSigId); + this._unminimizeSigId = 0; + } + if (this._originalUnminimizeSigId) { + Main.wm._shellwm.unblock_signal_handler(this._originalUnminimizeSigId); + this._originalUnminimizeSigId = 0; } + + console.debug(' WindowManagerModule - Disabled'); } -} +}; // fix for mainstream bug - fullscreen windows should minimize using opacity transition // but its being applied directly on window actor and that doesn't work @@ -109,8 +129,8 @@ const WindowManagerCommon = { /* if (actor.meta_window.is_monitor_sized()) { actor.get_first_child().ease({ opacity: 0, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._minimizeWindowDone(shellwm, actor), }); } else { */ @@ -140,8 +160,8 @@ const WindowManagerCommon = { scale_y: yScale, x: xDest, y: yDest, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._minimizeWindowDone(shellwm, actor), }); // } @@ -176,8 +196,8 @@ const WindowManagerCommon = { actor.set_scale(1.0, 1.0); actor.ease({ opacity: 255, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._unminimizeWindowDone(shellwm, actor), }); } else { */ @@ -208,8 +228,8 @@ const WindowManagerCommon = { scale_y: 1, x: xDest, y: yDest, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._unminimizeWindowDone(shellwm, actor), }); // } diff --git a/extensions/44/vertical-workspaces/lib/windowPreview.js b/extensions/44/vertical-workspaces/lib/windowPreview.js index 5d2bd61..2766138 100644 --- a/extensions/44/vertical-workspaces/lib/windowPreview.js +++ b/extensions/44/vertical-workspaces/lib/windowPreview.js @@ -10,103 +10,372 @@ 'use strict'; -const { Clutter, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi; - +const Clutter = imports.gi.Clutter; +const Atk = imports.gi.Atk; +const GLib = imports.gi.GLib; +const Graphene = imports.gi.Graphene; +const Meta = imports.gi.Meta; +const Pango = imports.gi.Pango; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const DND = imports.ui.dnd; const Main = imports.ui.main; +const OverviewControls = imports.ui.overviewControls; const WindowPreview = imports.ui.windowPreview; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; -const shellVersion = _Util.shellVersion; +let Me; +let opt; -let _overrides; +const WINDOW_SCALE_TIME = 200; +const WINDOW_ACTIVE_SIZE_INC = 5; +const WINDOW_OVERLAY_FADE_TIME = 200; +const WINDOW_DND_SIZE = 256; +const DRAGGING_WINDOW_OPACITY = 100; -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.lib.windowSearchProvider.prefix; +const ControlsState = OverviewControls.ControlsState; -const ControlsState = imports.ui.overviewControls.ControlsState; +var WindowPreviewModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -let opt; -let _firstRun = true; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('windowPreviewModule', true); - reset = reset || !moduleEnabled; + cleanGlobals() { + Me = null; + opt = null; + } - // don't even touch this module if disabled - if (_firstRun && reset) - return; + update(reset) { + this.moduleEnabled = opt.get('windowPreviewModule'); + const conflict = false; - _firstRun = false; + reset = reset || !this.moduleEnabled || conflict; - if (_overrides) - _overrides.removeAll(); + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WindowPreviewModule - Keeping untouched'); + } + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); - if (reset) { - _overrides = null; - opt = null; - WindowPreview.WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 750; - return; + this._overrides.addOverride('WindowPreview', WindowPreview.WindowPreview.prototype, WindowPreviewCommon); + // 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; + console.debug(' WindowPreviewModule - Activated'); } - _overrides = new _Util.Overrides(); + _disableModule() { + // If WindowPreview._init was injected by another extension (like Burn My Windows) + // which enables/disables before V-Shell + // don't restore the original if it's not injected, + // because it would restore injected _init and recursion would freeze GS when extensions are enabled again. + // This can happen when all extension re-enabled, not only when screen is locked/unlocked + // If _init doesn't include "fn.apply(this, args)" when reset === true, some extension already restored the original + const skipReset = WindowPreview.WindowPreview.prototype._init.toString().includes('fn.apply(this, args)'); + if (this._overrides && skipReset) { + // skip restoring original _init() + this._overrides['_init'] = null; + } + + if (this._overrides) + this._overrides.removeAll(); - _overrides.addOverride('WindowPreview', WindowPreview.WindowPreview.prototype, WindowPreviewCommon); - // 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; -} + this._overrides = null; + + console.debug(' WindowPreviewModule - Disabled'); + } +}; const WindowPreviewCommon = { - // injection to _init() - after__init() { + _init(metaWindow, workspace, overviewAdjustment) { + this.metaWindow = metaWindow; + this.metaWindow._delegate = this; + this._windowActor = metaWindow.get_compositor_private(); + this._workspace = workspace; + this._overviewAdjustment = overviewAdjustment; + + const ICON_SIZE = opt.WIN_PREVIEW_ICON_SIZE; const ICON_OVERLAP = 0.7; - if (opt.WIN_PREVIEW_ICON_SIZE < 64) { - this.remove_child(this._icon); - this._icon.destroy(); - const tracker = Shell.WindowTracker.get_default(); - const app = tracker.get_window_app(this.metaWindow); - this._icon = app.create_icon_texture(opt.WIN_PREVIEW_ICON_SIZE); - this._icon.add_style_class_name('icon-dropshadow'); - this._icon.set({ - reactive: true, - pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + Shell.WindowPreview.prototype._init.bind(this)({ + reactive: true, + can_focus: true, + accessible_role: Atk.Role.PUSH_BUTTON, + offscreen_redirect: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY, + }); + + const windowContainer = new Clutter.Actor({ + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + }); + this.window_container = windowContainer; + + windowContainer.connect('notify::scale-x', + () => this._adjustOverlayOffsets()); + // gjs currently can't handle setting an actors layout manager during + // the initialization of the actor if that layout manager keeps track + // of its container, so set the layout manager after creating the + // container + windowContainer.layout_manager = new Shell.WindowPreviewLayout(); + this.add_child(windowContainer); + + this._addWindow(metaWindow); + + this._delegate = this; + + this._stackAbove = null; + + this._cachedBoundingBox = { + x: windowContainer.layout_manager.bounding_box.x1, + y: windowContainer.layout_manager.bounding_box.y1, + width: windowContainer.layout_manager.bounding_box.get_width(), + height: windowContainer.layout_manager.bounding_box.get_height(), + }; + + windowContainer.layout_manager.connect( + 'notify::bounding-box', layout => { + this._cachedBoundingBox = { + x: layout.bounding_box.x1, + y: layout.bounding_box.y1, + width: layout.bounding_box.get_width(), + height: layout.bounding_box.get_height(), + }; + + // A bounding box of 0x0 means all windows were removed + if (layout.bounding_box.get_area() > 0) + this.emit('size-changed'); }); - this._icon.add_constraint(new Clutter.BindConstraint({ - source: this.windowContainer, - coordinate: Clutter.BindCoordinate.POSITION, - })); - this._icon.add_constraint(new Clutter.AlignConstraint({ - source: this.windowContainer, - align_axis: Clutter.AlignAxis.X_AXIS, - factor: 0.5, - })); - this._icon.add_constraint(new Clutter.AlignConstraint({ - source: this.windowContainer, - align_axis: Clutter.AlignAxis.Y_AXIS, - pivot_point: new Graphene.Point({ x: -1, y: ICON_OVERLAP }), - factor: 1, - })); - this.add_child(this._icon); - if (opt.WIN_PREVIEW_ICON_SIZE < 22) { - // disable app icon - this._icon.hide(); + + this._windowActor.connectObject('destroy', () => this.destroy(), this); + + this._updateAttachedDialogs(); + + let clickAction = new Clutter.ClickAction(); + clickAction.connect('clicked', act => { + const button = act.get_button(); + if (button === Clutter.BUTTON_SECONDARY) { + if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 1) { + this._closeWinAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 2) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 3 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } + } else if (button === Clutter.BUTTON_MIDDLE) { + if (opt.WIN_PREVIEW_MID_BTN_ACTION === 1) { + this._closeWinAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_MID_BTN_ACTION === 2) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_MID_BTN_ACTION === 3 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } } - this._iconSize = opt.WIN_PREVIEW_ICON_SIZE; + return this._activate(); + }); + + + if (this._onLongPress) { + clickAction.connect('long-press', this._onLongPress.bind(this)); + } else { + clickAction.connect('long-press', (action, actor, state) => { + if (state === Clutter.LongPressState.ACTIVATE) + this.showOverlay(true); + return true; + }); } + this.connect('destroy', this._onDestroy.bind(this)); + + this._draggable = DND.makeDraggable(this, { + restoreOnSuccess: true, + manualMode: !!this._onLongPress, + dragActorMaxSize: WINDOW_DND_SIZE, + dragActorOpacity: DRAGGING_WINDOW_OPACITY, + }); + + // _draggable.addClickAction is new in GS45 + if (this._draggable.addClickAction) + this._draggable.addClickAction(clickAction); + else + this.add_action(clickAction); + + this._draggable.connect('drag-begin', this._onDragBegin.bind(this)); + this._draggable.connect('drag-cancelled', this._onDragCancelled.bind(this)); + this._draggable.connect('drag-end', this._onDragEnd.bind(this)); + this.inDrag = false; + + this._selected = false; + this._overlayEnabled = true; + this._overlayShown = false; + this._closeRequested = false; + this._idleHideOverlayId = 0; + + const tracker = Shell.WindowTracker.get_default(); + const app = tracker.get_window_app(this.metaWindow); + this._icon = app.create_icon_texture(ICON_SIZE); + this._icon.add_style_class_name('icon-dropshadow'); + this._icon.set({ + reactive: true, + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + }); + this._icon.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.POSITION, + })); + this._icon.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + factor: 0.5, + })); + this._icon.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: ICON_OVERLAP }), + factor: 1, + })); + + if (opt.WINDOW_ICON_CLICK_ACTION) { + const iconClickAction = new Clutter.ClickAction(); + iconClickAction.connect('clicked', act => { + if (act.get_button() === Clutter.BUTTON_PRIMARY) { + if (opt.WINDOW_ICON_CLICK_ACTION === 1) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WINDOW_ICON_CLICK_ACTION === 2 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } + } /* else if (act.get_button() === Clutter.BUTTON_SECONDARY) { + return Clutter.EVENT_STOP; + }*/ + return Clutter.EVENT_PROPAGATE; + }); + this._icon.add_action(iconClickAction); + } 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.set_child_above_sibling(this._title, null); + this._title = new St.Label({ + visible: false, + style_class: 'window-caption', + text: this._getCaption(), + reactive: true, + }); + this._title.clutter_text.single_line_mode = true; + this._title.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.X, + })); + + let offset; + if (opt.WIN_TITLES_POSITION < 2) { + // we cannot get proper title height before it gets to the stage, so 35 is estimated height + spacing + offset = -scaleFactor * (ICON_SIZE * ICON_OVERLAP + 35); + } else { + offset = scaleFactor * (ICON_SIZE * (1 - ICON_OVERLAP) + 4); + } + this._title.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.Y, + offset, + })); + this._title.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + factor: 0.5, + })); + this._title.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: 0 }), + factor: 1, + })); + this._title.clutter_text.ellipsize = Pango.EllipsizeMode.END; + this.label_actor = this._title; + this.metaWindow.connectObject( + 'notify::title', () => (this._title.text = this._getCaption()), + this); + + const layout = Meta.prefs_get_button_layout(); + this._closeButtonSide = + layout.left_buttons.includes(Meta.ButtonFunction.CLOSE) + ? St.Side.LEFT : St.Side.RIGHT; + if (Me.shellVersion < 43) { + this._closeButton = new St.Button({ + visible: false, + style_class: 'window-close', + child: new St.Icon({ icon_name: 'preview-close-symbolic' }), + }); + } else { + this._closeButton = new St.Button({ + visible: false, + style_class: 'window-close', + icon_name: 'preview-close-symbolic', + }); + } + this._closeButton.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.POSITION, + })); + this._closeButton.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + pivot_point: new Graphene.Point({ x: 0.5, y: -1 }), + factor: this._closeButtonSide === St.Side.LEFT ? 0 : 1, + })); + this._closeButton.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: 0.5 }), + factor: 0, + })); + this._closeButton.connect('clicked', () => this._deleteAll()); + + this.add_child(this._title); + this.add_child(this._icon); + this.add_child(this._closeButton); + + this._overviewAdjustment.connectObject( + 'notify::value', () => this._updateIconScale(), this); + this._updateIconScale(); + + this.connect('notify::realized', () => { + if (!this.realized) + return; + + this._title.ensure_style(); + this._icon.ensure_style(); + }); + + if (ICON_SIZE < 22) { + // disable app icon + this._icon.hide(); + } else { + this._updateIconScale(); + } + + + // if window is created while the overview is shown, icon and title should be visible immediately if (Main.overview._overview._controls._stateAdjustment.value < 1) { this._icon.scale_x = 0; @@ -124,9 +393,9 @@ const WindowPreviewCommon = { if (global.get_pointer()[0] === opt.showingPointerX || Main.overview._overview._controls._stateAdjustment.value < 1) return; - const adjustment = this._workspace._background._stateAdjustment; opt.WORKSPACE_MODE = 1; - _Util.exposeWindows(adjustment, false); + const view = this._workspace.get_parent(); + view.exposeWindows(this._workspace.metaWorkspace.index()); this.disconnect(this._wsStateConId); }); } @@ -136,53 +405,39 @@ const WindowPreviewCommon = { this._stateAdjustmentSigId = this._workspace.stateAdjustment.connect('notify::value', this._updateIconScale.bind(this)); } - // replace click action with custom one - const action = this.get_actions()[0]; - - const handlerId = GObject.signal_handler_find(action, { signalId: 'clicked' }); - if (handlerId) - action.disconnect(handlerId); + const metaWin = this.metaWindow; + if (opt.DASH_ISOLATE_WS && !metaWin._wsChangedConId) { + metaWin._wsChangedConId = metaWin.connect('workspace-changed', + () => Main.overview.dash._queueRedisplay()); + } else if (!opt.DASH_ISOLATE_WS && metaWin._wsChangedConId) { + metaWin.disconnect(metaWin._wsChangedConId); + } + }, - 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) { - 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; - }); + _closeWinAction() { + this.hide(); + this._deleteAll(); + }, - 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); + _removeLaters() { + // 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) { + if (Meta.later_remove) + Meta.later_remove(this._longPressLater); + else + global.compositor.get_laters().remove(this._longPressLater); + delete this._longPressLater; } }, + _searchAppWindowsAction() { + this._removeLaters(); + const tracker = Shell.WindowTracker.get_default(); + const appName = tracker.get_window_app(this.metaWindow).get_name(); + Me.Util.activateSearchProvider(`${Me.WSP_PREFIX} ${appName}`); + }, + _updateIconScale() { let { currentState, initialState, finalState } = this._overviewAdjustment.getStateTransitionParams(); @@ -192,7 +447,7 @@ const WindowPreviewCommon = { const visible = (initialState > ControlsState.HIDDEN || finalState > ControlsState.HIDDEN) && - !(finalState === ControlsState.APP_GRID && primaryMonitor); + !(finalState === ControlsState.APP_GRID && opt.WS_ANIMATION && primaryMonitor); let scale = 0; if (visible) @@ -210,7 +465,6 @@ const WindowPreviewCommon = { else if (primaryMonitor && currentState > ControlsState.WINDOW_PICKER) scale = 0; - // in static workspace mode show icon and title on windows expose if (opt.OVERVIEW_MODE) { if (currentState === 1) @@ -219,29 +473,32 @@ const WindowPreviewCommon = { return; } + if (!opt.WS_ANIMATION && (Main.overview._overview.controls._searchController.searchActive || + ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || + (initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER))) + ) + return; + + // 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 if (scale === 1) { - this._icon.ease({ - duration: 50, - scale_x: scale, - scale_y: scale, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, + this._icon.set({ + scale_x: 1, + scale_y: 1, }); this._title.ease({ duration: 100, opacity: 255, mode: Clutter.AnimationMode.EASE_OUT_QUAD, }); - } else if (this._icon.scale_x !== 0) { + } else { + this._title.opacity = 0; this._icon.set({ - scale_x: 0, - scale_y: 0, + scale_x: scale, + scale_y: scale, }); - 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 }, showOverlay(animate) { @@ -252,7 +509,7 @@ const WindowPreviewCommon = { return; this._overlayShown = true; - if (!opt.ALWAYS_ACTIVATE_SELECTED_WINDOW) + if (opt.WIN_TITLES_POSITION === 2) this._restack(); // If we're supposed to animate and an animation in our direction @@ -263,7 +520,7 @@ const WindowPreviewCommon = { ongoingTransition.get_interval().peek_final_value() === 255) return; - const toShow = this._windowCanClose() + const toShow = this._windowCanClose() && opt.SHOW_CLOSE_BUTTON ? [this._closeButton] : []; @@ -301,12 +558,12 @@ const WindowPreviewCommon = { if (!this._overlayShown) return; this._overlayShown = false; - if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW && Main.overview._overview.controls._stateAdjustment.value < 1) { - this.get_parent()?.set_child_above_sibling(this, null); + + if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW && Main.overview._overview.controls._stateAdjustment.value < 1) this._activateSelected = true; - } - if (!opt.ALWAYS_ACTIVATE_SELECTED_WINDOW) + + if (opt.WIN_TITLES_POSITION === 2) this._restack(); // If we're supposed to animate and an animation in our direction @@ -353,14 +610,11 @@ const WindowPreviewCommon = { this._delegate = null; if (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 (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, this._longPressLater); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, this._longPressLater); + delete this._longPressLater; } if (this._idleHideOverlayId > 0) { diff --git a/extensions/44/vertical-workspaces/lib/windowSearchProvider.js b/extensions/44/vertical-workspaces/lib/windowSearchProvider.js index 5f90784..b82f365 100644 --- a/extensions/44/vertical-workspaces/lib/windowSearchProvider.js +++ b/extensions/44/vertical-workspaces/lib/windowSearchProvider.js @@ -9,30 +9,23 @@ 'use strict'; -const { GLib, Gio, Meta, St, Shell } = imports.gi; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const St = imports.gi.St; const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; -const _Util = Me.imports.lib.util; +let Me; +let opt; // gettext -const _ = Settings._; - -const shellVersion = Settings.shellVersion; - -const ModifierType = imports.gi.Clutter.ModifierType; - -let windowSearchProvider; -let _enableTimeoutId = 0; +let _; // prefix helps to eliminate results from other search providers // so it needs to be something less common // needs to be accessible from vw module -var prefix = 'wq//'; - -let opt; +const PREFIX = 'wq//'; const Action = { NONE: 0, @@ -42,66 +35,77 @@ const Action = { MOVE_ALL_TO_WS: 4, }; -function getOverviewSearchResult() { - return Main.overview._overview.controls._searchController._searchResults; -} +var WindowSearchProviderModule = class { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + // export for other modules -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - if (!reset && opt.WINDOW_SEARCH_PROVIDER_ENABLED && !windowSearchProvider) { - enable(); - } else if (reset || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) { - disable(); + this._windowSearchProvider = null; + this._enableTimeoutId = 0; + } + + cleanGlobals() { + Me = null; opt = null; + _ = null; } -} - -function enable() { - // delay because Fedora had problem to register a new provider soon after Shell restarts - _enableTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 2000, - () => { - if (!windowSearchProvider) { - windowSearchProvider = new WindowSearchProvider(opt); - getOverviewSearchResult()._registerProvider( - windowSearchProvider - ); - } - _enableTimeoutId = 0; - return GLib.SOURCE_REMOVE; + + update(reset) { + this.moduleEnabled = opt.get('windowSearchProviderModule'); + + reset = reset || !this.moduleEnabled; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); } - ); -} + if (reset && this._firstActivation) + console.debug(' WindowSearchProviderModule - Keeping untouched'); + } -function disable() { - if (windowSearchProvider) { - getOverviewSearchResult()._unregisterProvider( - windowSearchProvider + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._windowSearchProvider) { + this._windowSearchProvider = new WindowSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._windowSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } ); - windowSearchProvider = null; + console.debug(' WindowSearchProviderModule - Activated'); } - if (_enableTimeoutId) { - GLib.source_remove(_enableTimeoutId); - _enableTimeoutId = 0; + + _disableModule() { + if (this._windowSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._windowSearchProvider); + this._windowSearchProvider = null; + } + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } + + console.debug(' WindowSearchProviderModule - Disabled'); } -} - -function makeResult(window, i) { - const app = Shell.WindowTracker.get_default().get_window_app(window); - const appName = app ? app.get_name() : 'Unknown'; - const windowTitle = window.get_title(); - 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, - windowTitle, - window, - }; -} + + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +}; const closeSelectedRegex = /^\/x!$/; const closeAllResultsRegex = /^\/xa!$/; @@ -110,15 +114,22 @@ const moveAllToWsRegex = /^\/ma[0-9]+$/; 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.canLaunchSearch = true; + this.id = 'open-windows'; + const appSystem = Shell.AppSystem.get_default(); + // use arbitrary app to get complete appInfo object + let appInfo = appSystem.lookup_app('com.matjakeman.ExtensionManager.desktop')?.get_app_info(); + if (!appInfo) + appInfo = appSystem.lookup_app('org.gnome.Extensions.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('true', _('Open Windows'), null); + appInfo.get_description = () => _('Search open windows'); + appInfo.get_name = () => _('Open Windows'); + appInfo.get_id = () => this.id; + appInfo.get_icon = () => Gio.icon_new_for_string('focus-windows-symbolic'); + appInfo.should_show = () => true; + + this.appInfo = appInfo; + this.canLaunchSearch = false; this.isRemoteProvider = false; this.action = 0; @@ -128,7 +139,7 @@ const WindowSearchProvider = class WindowSearchProvider { // do not modify original terms let termsCopy = [...terms]; // search for terms without prefix - termsCopy[0] = termsCopy[0].replace(prefix, ''); + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); /* if (gOptions.get('searchWindowsCommands')) { this.action = 0; @@ -167,9 +178,9 @@ const WindowSearchProvider = class WindowSearchProvider { let m; for (let key in candidates) { if (opt.SEARCH_FUZZY) - m = _Util.fuzzyMatch(term, candidates[key].name); + m = Me.Util.fuzzyMatch(term, candidates[key].name); else - m = _Util.strictMatch(term, candidates[key].name); + m = Me.Util.strictMatch(term, candidates[key].name); if (m !== -1) results.push({ weight: m, id: key }); @@ -178,7 +189,19 @@ const WindowSearchProvider = class WindowSearchProvider { 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)); + switch (opt.WINDOW_SEARCH_ORDER) { + case 1: // MRU - current ws first + results.sort((a, b) => (this.windows[a.id].window.get_workspace().index() !== currentWs) && (this.windows[b.id].window.get_workspace().index() === currentWs)); + break; + case 2: // MRU - by workspace + results.sort((a, b) => this.windows[a.id].window.get_workspace().index() > this.windows[b.id].window.get_workspace().index()); + break; + case 3: // Stable sequence - by workspace + results.sort((a, b) => this.windows[a.id].window.get_stable_sequence() > this.windows[b.id].window.get_stable_sequence()); + results.sort((a, b) => this.windows[a.id].window.get_workspace().index() > this.windows[b.id].window.get_workspace().index()); + break; + } + results.sort((a, b) => (_terms !== ' ') && (a.weight > 0 && b.weight === 0)); this.resultIds = results.map(item => item.id); @@ -187,7 +210,7 @@ const WindowSearchProvider = class WindowSearchProvider { getResultMetas(resultIds, callback = null) { const metas = resultIds.map(id => this.getResultMeta(id)); - if (shellVersion >= 43) + if (Me.shellVersion >= 43) return new Promise(resolve => resolve(metas)); else callback(metas); @@ -210,12 +233,29 @@ const WindowSearchProvider = class WindowSearchProvider { }; } + makeResult(window, i) { + const app = Shell.WindowTracker.get_default().get_window_app(window); + const appName = app ? app.get_name() : 'Unknown'; + const windowTitle = window.get_title(); + 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, + windowTitle, + window, + }; + } + launchSearch(/* terms, timeStamp*/) { + } activateResult(resultId/* , terms, timeStamp*/) { - const isCtrlPressed = _Util.isCtrlPressed(); - const isShiftPressed = _Util.isShiftPressed(); + const isCtrlPressed = Me.Util.isCtrlPressed(); + const isShiftPressed = Me.Util.isShiftPressed(); this.action = 0; this.targetWs = 0; @@ -276,15 +316,16 @@ const WindowSearchProvider = class WindowSearchProvider { 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()}`); + windows[`${i}-${v.get_id()}`] = this.makeResult(v, `${i}-${v.get_id()}`); return windows[`${i}-${v.get_id()}`]; } ); - if (shellVersion >= 43) + if (Me.shellVersion >= 43) return new Promise(resolve => resolve(this._getResultSet(terms))); else callback(this._getResultSet(terms)); + return null; } @@ -293,13 +334,15 @@ const WindowSearchProvider = class WindowSearchProvider { return results; } - getSubsearchResultSet(previousResults, terms, callback/* , cancellable*/) { - // if we return previous results, quick typers get non-actual results - callback(this._getResultSet(terms)); + getSubsearchResultSet(previousResults, terms, callback) { + if (Me.shellVersion < 43) { + this.getSubsearchResultSet42(terms, callback); + return null; + } + return this.getInitialResultSet(terms); } - /* createResultObject(resultMeta) { - const app = Shell.WindowTracker.get_default().get_window_app(resultMeta.id); - return new AppIcon(app); - }*/ + getSubsearchResultSet42(terms, callback) { + callback(this._getResultSet(terms)); + } }; diff --git a/extensions/44/vertical-workspaces/lib/workspace.js b/extensions/44/vertical-workspaces/lib/workspace.js index 3b61a6d..be60403 100644 --- a/extensions/44/vertical-workspaces/lib/workspace.js +++ b/extensions/44/vertical-workspaces/lib/workspace.js @@ -10,51 +10,68 @@ 'use strict'; -const { St, Graphene } = imports.gi; +const Graphene = imports.gi.Graphene; +const St = imports.gi.St; const Main = imports.ui.main; -const Util = imports.misc.util; const Workspace = imports.ui.workspace; +const Util = imports.misc.util; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; - -let _overrides; +let Me; let opt; -let _firstRun = true; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('workspaceModule', true); - reset = reset || !moduleEnabled; +var WorkspaceModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // don't even touch this module if disabled - if (_firstRun && reset) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } - _firstRun = false; + cleanGlobals() { + Me = null; + opt = null; + } - if (_overrides) - _overrides.removeAll(); + update(reset) { + this.moduleEnabled = opt.get('workspaceModule'); + const conflict = false; + reset = reset || !this.moduleEnabled || conflict; - if (reset) { - Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95; - _overrides = null; - opt = null; - return; + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceModule - Keeping untouched'); } - _overrides = new _Util.Overrides(); + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); - _overrides.addOverride('WorkspaceBackground', Workspace.WorkspaceBackground.prototype, WorkspaceBackground); + this._overrides.addOverride('WorkspaceBackground', Workspace.WorkspaceBackground.prototype, WorkspaceBackground); - // fix overlay base for Vertical Workspaces - _overrides.addOverride('WorkspaceLayout', Workspace.WorkspaceLayout.prototype, WorkspaceLayout); -} + // fix overlay base for Vertical Workspaces + this._overrides.addOverride('WorkspaceLayout', Workspace.WorkspaceLayout.prototype, WorkspaceLayout); + console.debug(' WorkspaceModule - Activated'); + } + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95; + + console.debug(' WorkspaceModule - Disabled'); + } +}; // workaround for upstream bug (that is not that invisible in default shell) // smaller window cannot be scaled below 0.95 (WINDOW_PREVIEW_MAXIMUM_SCALE) diff --git a/extensions/44/vertical-workspaces/lib/workspaceAnimation.js b/extensions/44/vertical-workspaces/lib/workspaceAnimation.js index 07008c6..d790895 100644 --- a/extensions/44/vertical-workspaces/lib/workspaceAnimation.js +++ b/extensions/44/vertical-workspaces/lib/workspaceAnimation.js @@ -9,81 +9,147 @@ */ 'use strict'; + 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.lib.util; +const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; + +let Me; +let opt; -// 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; +var WorkspaceAnimationModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + // first reference to constant defined using const in other module returns undefined, the MonitorGroup const will remain empty and unused + this.dummy = WorkspaceAnimation.MonitorGroup; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._origBaseDistance = null; + this._wsAnimationSwipeBeginId = 0; + this._wsAnimationSwipeUpdateId = 0; + this._wsAnimationSwipeEndId = 0; + } -let _origBaseDistance; -let _wsAnimationSwipeBeginId; -let _wsAnimationSwipeUpdateId; -let _wsAnimationSwipeEndId; + cleanGlobals() { + Me = null; + opt = null; + } -let _overrides; -let opt; -let _firstRun = true; + update(reset) { + this.moduleEnabled = opt.get('workspaceAnimationModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('workspaceAnimationModule', true); - reset = reset || !moduleEnabled; + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceAnimationModule - Keeping untouched'); + } - // don't even touch this module if disabled - if (_firstRun && reset) - return; + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); - _firstRun = false; + if (opt.STATIC_WS_SWITCHER_BG) { + this._overrideMonitorGroupProperty(); + this._overrides.addOverride('WorkspaceAnimationMonitorGroup', WorkspaceAnimation.MonitorGroup.prototype, MonitorGroup); + } - if (_overrides) - _overrides.removeAll(); + this._connectWsAnimationSwipeTracker(); + console.debug(' WorkspaceAnimationModule - Activated'); + } - if (reset || !moduleEnabled) { - _connectWsAnimationSwipeTracker(true); - _overrideMonitorGroupProperty(true); - _overrides = null; - opt = null; - return; + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + const reset = true; + this._connectWsAnimationSwipeTracker(reset); + this._overrideMonitorGroupProperty(reset); + console.debug(' WorkspaceAnimationModule - Disabled'); } - if (opt.STATIC_WS_SWITCHER_BG) { - _overrides = new _Util.Overrides(); - _overrideMonitorGroupProperty(); - _overrides.addOverride('WorkspaceAnimationMonitorGroup', WorkspaceAnimation.MonitorGroup.prototype, MonitorGroup); + _overrideMonitorGroupProperty(reset = false) { + if (!this._origBaseDistance) + this._origBaseDistance = Object.getOwnPropertyDescriptor(WorkspaceAnimation.MonitorGroup.prototype, 'baseDistance').get; + + let getter; + if (reset) { + if (this._origBaseDistance) + getter = { get: this._origBaseDistance }; + } else { + getter = { + 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); + } + + _connectWsAnimationSwipeTracker(reset = false) { + if (reset) { + if (this._wsAnimationSwipeBeginId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeBeginId); + this._wsAnimationSwipeBeginId = 0; + } + if (this._wsAnimationSwipeEndId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeEndId); + this._wsAnimationSwipeEndId = 0; + } + } else if (!this._wsAnimationSwipeBeginId) { + // display ws switcher popup when gesture begins and connect progress + this._wsAnimationSwipeBeginId = Main.wm._workspaceAnimation._swipeTracker.connect('begin', () => this._connectWsAnimationProgress(true)); + // we want to be sure that popup with the final ws index show up when gesture ends + this._wsAnimationSwipeEndId = Main.wm._workspaceAnimation._swipeTracker.connect('end', (tracker, duration, endProgress) => this._connectWsAnimationProgress(false, endProgress)); + } } - _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; - - let getter; - if (reset) { - if (_origBaseDistance) - getter = { get: _origBaseDistance }; - } else { - getter = { - 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; - }, - }; + _connectWsAnimationProgress(connect, endProgress = null) { + if (Main.overview.visible) + return; + + if (connect && !this._wsAnimationSwipeUpdateId) { + this._wsAnimationSwipeUpdateId = Main.wm._workspaceAnimation._swipeTracker.connect('update', (tracker, progress) => this._showWsSwitcherPopup(progress)); + } else if (!connect && this._wsAnimationSwipeUpdateId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeUpdateId); + this._wsAnimationSwipeUpdateId = 0; + this._showWsSwitcherPopup(Math.round(endProgress)); + } } - if (getter) - Object.defineProperty(WorkspaceAnimation.MonitorGroup.prototype, 'baseDistance', getter); -} + _showWsSwitcherPopup(progress) { + if (Main.overview.visible) + return; + + const wsIndex = Math.round(progress); + if (Main.wm._workspaceSwitcherPopup === null) { + Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); + Main.wm._workspaceSwitcherPopup.connect('destroy', () => { + Main.wm._workspaceSwitcherPopup = null; + }); + } + + Main.wm._workspaceSwitcherPopup.display(wsIndex); + } +}; const MonitorGroup = { // injection to _init() @@ -119,7 +185,9 @@ const MonitorGroup = { // 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()) && + !(metaWin?.wm_class === 'Gjs' && metaWin?.is_on_all_workspaces())) { // DING extension uses window with Gjs class // hide original window. we cannot use opacity since it also affects clones. // scaling them to 0 works well actor.scale_x = 0; @@ -136,49 +204,3 @@ const MonitorGroup = { }); }, }; - -function _connectWsAnimationSwipeTracker(reset = false) { - if (reset) { - if (_wsAnimationSwipeBeginId) { - Main.wm._workspaceAnimation._swipeTracker.disconnect(_wsAnimationSwipeBeginId); - _wsAnimationSwipeBeginId = 0; - } - if (_wsAnimationSwipeEndId) { - Main.wm._workspaceAnimation._swipeTracker.disconnect(_wsAnimationSwipeEndId); - _wsAnimationSwipeEndId = 0; - } - } else if (!_wsAnimationSwipeBeginId) { - // display ws switcher popup when gesture begins and connect progress - _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', (tracker, duration, endProgress) => _connectWsAnimationProgress(false, endProgress)); - } -} - -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(progress) { - if (Main.overview.visible) - return; - - const wsIndex = Math.round(progress); - if (Main.wm._workspaceSwitcherPopup === null) { - Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); - Main.wm._workspaceSwitcherPopup.connect('destroy', () => { - Main.wm._workspaceSwitcherPopup = null; - }); - } - - Main.wm._workspaceSwitcherPopup.display(wsIndex); -} diff --git a/extensions/44/vertical-workspaces/lib/workspaceSwitcherPopup.js b/extensions/44/vertical-workspaces/lib/workspaceSwitcherPopup.js index 972f35e..5bde6d0 100644 --- a/extensions/44/vertical-workspaces/lib/workspaceSwitcherPopup.js +++ b/extensions/44/vertical-workspaces/lib/workspaceSwitcherPopup.js @@ -13,52 +13,71 @@ const Main = imports.ui.main; const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +let Me; +let opt; -const _Util = Me.imports.lib.util; -let _overrides; +var WorkspaceSwitcherPopupModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -let opt; -let _firstRun = true; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('workspaceSwitcherPopupModule', true); - reset = reset || !moduleEnabled; + cleanGlobals() { + Me = null; + opt = null; + } - // don't even touch this module if disabled - if (_firstRun && reset) - return; + update(reset) { + this.moduleEnabled = opt.get('workspaceSwitcherPopupModule'); + const conflict = Me.Util.getEnabledExtensions('workspace-switcher-manager').length || + Me.Util.getEnabledExtensions('WsSwitcherPopupManager').length; - _firstRun = false; + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "WorkspaceSwitcherPopup" module disabled due to potential conflict with another extension`); - if (_overrides) - _overrides.removeAll(); + reset = reset || !this.moduleEnabled || conflict; - if (reset) { - _overrides = null; - opt = null; - return; + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceSwitcherPopupModule - Keeping untouched'); } - _overrides = new _Util.Overrides(); + _activateModule() { + if (!this._overrides) + this._overrides = new Me.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); + this._overrides.addOverride('WorkspaceSwitcherPopup', WorkspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype, WorkspaceSwitcherPopupCommon); + console.debug(' WorkspaceSwitcherPopupModule - Activated'); } -} -const WorkspaceSwitcherPopupOverride = { + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); + } +}; + +const WorkspaceSwitcherPopupCommon = { // 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]); + if (this.get_constraints()[0]) + this.remove_constraint(this.get_constraints()[0]); }, // injection to display() diff --git a/extensions/44/vertical-workspaces/lib/workspaceThumbnail.js b/extensions/44/vertical-workspaces/lib/workspaceThumbnail.js index d0bc206..844c224 100644 --- a/extensions/44/vertical-workspaces/lib/workspaceThumbnail.js +++ b/extensions/44/vertical-workspaces/lib/workspaceThumbnail.js @@ -10,56 +10,90 @@ 'use strict'; -const { GLib, Clutter, Graphene, Meta, Shell, St } = imports.gi; +const Clutter = imports.gi.Clutter; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const AppDisplay = imports.ui.appDisplay; +const Background = imports.ui.background; const DND = imports.ui.dnd; const Main = imports.ui.main; -const Background = imports.ui.background; +const OverviewControls = imports.ui.overviewControls; const WorkspaceThumbnail = imports.ui.workspaceThumbnail; + +let Me; +let opt; + const ThumbnailState = WorkspaceThumbnail.ThumbnailState; +const ControlsState = OverviewControls.ControlsState; -const ControlsState = imports.ui.overviewControls.ControlsState; +const WORKSPACE_CUT_SIZE = 10; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +var WorkspaceThumbnailModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -// gettext -const _ = Me.imports.lib.settings._; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } -const _Util = Me.imports.lib.util; -const shellVersion = _Util.shellVersion; + cleanGlobals() { + Me = null; + opt = null; + } -let _overrides; + update(reset) { + this.moduleEnabled = true; + const conflict = false; -const WORKSPACE_CUT_SIZE = 10; -const _originalMaxThumbnailScale = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE; + reset = reset || !this.moduleEnabled || conflict; -let opt = null; + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceThumbnailModule - Keeping untouched'); + } -function update(reset = false) { - if (_overrides) - _overrides.removeAll(); + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + if (!this._originalMaxThumbnailScale) + this._originalMaxThumbnailScale = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE; - if (reset) { - if (_originalMaxThumbnailScale) - WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = _originalMaxThumbnailScale; - _overrides = null; - opt = null; - return; - } + // don't limit max thumbnail scale for other clients than overview, specifically AATWS. + WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = 1; + // WorkspaceThumbnail.ThumbnailsBox._MAX_THUMBNAIL_SCALE = 1; + + this._overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon); + this._overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon); - opt = Me.imports.lib.settings.opt; - _overrides = new _Util.Overrides(); + // 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; + + console.debug(' WorkspaceThumbnailModule - Activated'); + } - // don't limit max thumbnail scale for other clients than overview, for example AATWS. - WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = 1; + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; - _overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon); - _overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon); + WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = this._originalMaxThumbnailScale; - // 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; -} + console.debug(' WorkspaceThumbnailModule - Disabled'); + } +}; const WorkspaceThumbnailCommon = { // injection to _init() @@ -70,6 +104,8 @@ const WorkspaceThumbnailCommon = { // unless border is removed if (opt.SHOW_WS_TMB_BG) this.add_style_class_name('ws-tmb-labeled'); + else + this.add_style_class_name('ws-tmb-transparent'); // add workspace thumbnails labels if enabled if (opt.SHOW_WST_LABELS) { // 0 - disable @@ -77,7 +113,7 @@ const WorkspaceThumbnailCommon = { 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 settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' }); const wsLabels = settings.get_strv('workspace-names'); if (wsLabels.length > wsIndex && wsLabels[wsIndex]) label += `: ${wsLabels[wsIndex]}`; @@ -129,7 +165,9 @@ const WorkspaceThumbnailCommon = { } }); this._nWindowsConId = this.metaWorkspace.connect('notify::n-windows', () => { - // wait for new information + if (this._updateLabelTimeout) + return; + // wait for new data this._updateLabelTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => { const newLabel = getLabel(); this._wsLabel.text = newLabel; @@ -168,7 +206,7 @@ const WorkspaceThumbnailCommon = { 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())) + if (opt.CLOSE_WS_BUTTON_MODE < 3 || (opt.CLOSE_WS_BUTTON_MODE === 3 && Me.Util.isCtrlPressed())) closeButton.add_style_class_name('workspace-close-button-hover'); } }); @@ -220,12 +258,6 @@ const WorkspaceThumbnailCommon = { this._viewport.set_child_below_sibling(this._bgManager.backgroundActor, null); - this.connect('destroy', () => { - if (this._bgManager) - this._bgManager.destroy(); - this._bgManager = null; - }); - // full brightness of the thumbnail bg draws unnecessary attention // there is a grey bg under the wallpaper this._bgManager.backgroundActor.opacity = 220; @@ -256,7 +288,7 @@ const WorkspaceThumbnailCommon = { this._lastCloseClickTime = Date.now(); return; } - } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !_Util.isCtrlPressed()) { + } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !Me.Util.isCtrlPressed()) { return; } @@ -335,7 +367,7 @@ const WorkspaceThumbnailCommon = { if (!source.app && source.shellWorkspaceLaunch) return DND.DragMotionResult.COPY_DROP; - if (source instanceof imports.ui.appDisplay.FolderIcon) + if (source instanceof AppDisplay.FolderIcon) return DND.DragMotionResult.COPY_DROP; @@ -369,8 +401,8 @@ const WorkspaceThumbnailCommon = { timestamp: time, }); return true; - } else if (source instanceof imports.ui.appDisplay.FolderIcon) { - if (shellVersion >= 44) { + } else if (source instanceof AppDisplay.FolderIcon) { + if (Me.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()); @@ -419,7 +451,7 @@ const ThumbnailsBoxCommon = { if (!source.metaWindow && (!source.app || !source.app.can_open_new_window()) && (source.app || !source.shellWorkspaceLaunch) && - !(source instanceof imports.ui.appDisplay.FolderIcon)) + !(source instanceof AppDisplay.FolderIcon)) return false; @@ -448,8 +480,8 @@ const ThumbnailsBoxCommon = { workspace: newWorkspaceIndex, timestamp: time, }); - } else if (source instanceof imports.ui.appDisplay.FolderIcon) { - if (shellVersion >= 44) { + } else if (source instanceof AppDisplay.FolderIcon) { + if (Me.shellVersion >= 44) { for (let app of source.view._apps) { // const app = Shell.AppSystem.get_default().lookup_app(id); app.open_new_window(newWorkspaceIndex); @@ -495,7 +527,7 @@ const ThumbnailsBoxCommon = { if (!source.metaWindow && (!source.app || !source.app.can_open_new_window()) && (source.app || !source.shellWorkspaceLaunch) && - source !== Main.xdndHandler && !(source instanceof imports.ui.appDisplay.FolderIcon)) + source !== Main.xdndHandler && !(source instanceof AppDisplay.FolderIcon)) return DND.DragMotionResult.CONTINUE; const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; @@ -551,18 +583,18 @@ const ThumbnailsBoxCommon = { return ThumbnailsBoxHorizontal._withinWorkspace.bind(this)(...args); }, - get_preferred_custom_width(...args) { + vfunc_get_preferred_width(...args) { if (this._boxOrientation) - return ThumbnailsBoxVertical.get_preferred_custom_width.bind(this)(...args); + return ThumbnailsBoxVertical.vfunc_get_preferred_width.bind(this)(...args); else - return ThumbnailsBoxHorizontal.get_preferred_custom_width.bind(this)(...args); + return ThumbnailsBoxHorizontal.vfunc_get_preferred_width.bind(this)(...args); }, - get_preferred_custom_height(...args) { + vfunc_get_preferred_height(...args) { if (this._boxOrientation) - return ThumbnailsBoxVertical.get_preferred_custom_height.bind(this)(...args); + return ThumbnailsBoxVertical.vfunc_get_preferred_height.bind(this)(...args); else - return ThumbnailsBoxHorizontal.get_preferred_custom_height.bind(this)(...args); + return ThumbnailsBoxHorizontal.vfunc_get_preferred_height.bind(this)(...args); }, vfunc_allocate(...args) { @@ -632,14 +664,9 @@ const ThumbnailsBoxVertical = { 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(forHeight) { - if (!this.visible) - return [0, 0]; - - if (forHeight === -1) - return this.get_preferred_custom_height(forHeight); + vfunc_get_preferred_width(forHeight) { + if (forHeight < 10) + return [this._porthole.width, this._porthole.width]; let themeNode = this.get_theme_node(); @@ -652,19 +679,14 @@ const ThumbnailsBoxVertical = { const avail = forHeight - totalSpacing; let scale = (avail / nWorkspaces) / this._porthole.height; - // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE); const width = Math.round(this._porthole.width * scale); return themeNode.adjust_preferred_height(width, width); }, - 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. + vfunc_get_preferred_height(forWidth) { + if (forWidth < 10) + return [0, this._porthole.height]; let themeNode = this.get_theme_node(); let spacing = themeNode.get_length('spacing'); @@ -674,15 +696,14 @@ const ThumbnailsBoxVertical = { let totalSpacing = (nWorkspaces - 3) * spacing; const ratio = this._porthole.width / this._porthole.height; - const tmbHeight = themeNode.adjust_for_width(_forWidth) / ratio; + const tmbHeight = themeNode.adjust_for_width(forWidth) / ratio; const naturalheight = this._thumbnails.reduce((accumulator, thumbnail/* , index*/) => { const progress = 1 - thumbnail.collapse_fraction; const height = tmbHeight * progress; return accumulator + height; }, 0); - - return themeNode.adjust_preferred_width(totalSpacing, naturalheight); + return themeNode.adjust_preferred_width(totalSpacing, Math.round(naturalheight)); }, // removes extra space (extraWidth in the original function), we need the box as accurate as possible @@ -759,16 +780,11 @@ const ThumbnailsBoxVertical = { this._dropPlaceholder.allocate_preferred_size( ...this._dropPlaceholder.get_position()); - 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(); - }); - } + const laterFunc = () => this._dropPlaceholder.hide(); + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, laterFunc); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, laterFunc); } let childBox = new Clutter.ActorBox(); @@ -796,16 +812,11 @@ const ThumbnailsBoxVertical = { this._dropPlaceholder.allocate(childBox); - 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(); - }); - } + const laterFunc = () => this._dropPlaceholder.show(); + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, laterFunc); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, laterFunc); y += placeholderHeight + spacing; } @@ -925,7 +936,10 @@ const ThumbnailsBoxHorizontal = { return x > workspaceX1 && x <= workspaceX2; }, - get_preferred_custom_height(forWidth) { + vfunc_get_preferred_height(forWidth) { + if (forWidth < 10) + return [this._porthole.height, this._porthole.height]; + let themeNode = this.get_theme_node(); forWidth = themeNode.adjust_for_width(forWidth); @@ -937,18 +951,15 @@ const ThumbnailsBoxHorizontal = { 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]; + vfunc_get_preferred_width(forHeight) { + if (forHeight < 10) + return [0, this._porthole.width]; let themeNode = this.get_theme_node(); @@ -959,13 +970,14 @@ const ThumbnailsBoxHorizontal = { const ratio = this._porthole.height / this._porthole.width; - const tmbWidth = themeNode.adjust_for_height(_forHeight) / ratio; + 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); }, @@ -1041,16 +1053,11 @@ const ThumbnailsBoxHorizontal = { this._dropPlaceholder.allocate_preferred_size( ...this._dropPlaceholder.get_position()); - 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(); - }); - } + const laterFunc = () => this._dropPlaceholder.hide(); + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, laterFunc); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, laterFunc); } let childBox = new Clutter.ActorBox(); @@ -1078,16 +1085,11 @@ const ThumbnailsBoxHorizontal = { this._dropPlaceholder.allocate(childBox); - 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(); - }); - } + const laterFunc = () => this._dropPlaceholder.show(); + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, laterFunc); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, laterFunc); x += placeholderWidth + spacing; } diff --git a/extensions/44/vertical-workspaces/lib/workspacesView.js b/extensions/44/vertical-workspaces/lib/workspacesView.js index e3575f1..3df7b22 100644 --- a/extensions/44/vertical-workspaces/lib/workspacesView.js +++ b/extensions/44/vertical-workspaces/lib/workspacesView.js @@ -10,62 +10,92 @@ 'use strict'; -const { GObject, Clutter, Meta, St } = imports.gi; +const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const St = imports.gi.St; const Main = imports.ui.main; -const Util = imports.misc.util; +const OverviewControls = imports.ui.overviewControls; const WorkspacesView = imports.ui.workspacesView; -// 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.lib.windowSearchProvider.prefix; -const SEARCH_RECENT_FILES_PREFIX = Me.imports.lib.recentFilesSearchProvider.prefix; - -const _Util = Me.imports.lib.util; -let _overrides; +const Util = imports.misc.util; +let Me; let opt; +const ControlsState = OverviewControls.ControlsState; +const FitMode = WorkspacesView.FitMode; -function update(reset = false) { - 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; +var WorkspacesViewModule = class { + constructor(me) { + // first reference to constant defined using const in other module returns undefined, the SecondaryMonitorDisplay const will remain empty and unused + this.dummy = WorkspacesView.SecondaryMonitorDisplay; - // 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); + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; } + update(reset) { + this.moduleEnabled = true; + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspacesViewModule - Keeping untouched'); + } - _overrides = new _Util.Overrides(); + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); - if (!cubeSupported) - _overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon); + const desktopCubeEnabled = Me.Util.getEnabledExtensions('desktop-cube@schneegans.github.com').length; + const desktopCubeConflict = desktopCubeEnabled && !opt.ORIENTATION && !opt.OVERVIEW_MODE; - _overrides.addOverride('WorkspacesDisplay', WorkspacesView.WorkspacesDisplay.prototype, WorkspacesDisplay); - _overrides.addOverride('ExtraWorkspaceView', WorkspacesView.ExtraWorkspaceView.prototype, ExtraWorkspaceView); + if (!desktopCubeConflict) + this._overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon); - if (opt.ORIENTATION) { - // switch internal workspace orientation in GS - global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1); - _overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayVertical); - } else { - _overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayHorizontal); + this._overrides.addOverride('WorkspacesDisplay', WorkspacesView.WorkspacesDisplay.prototype, WorkspacesDisplayCommon); + this._overrides.addOverride('ExtraWorkspaceView', WorkspacesView.ExtraWorkspaceView.prototype, ExtraWorkspaceViewCommon); + this._overrides.addOverride('SecondaryMonitorDisplayCommon', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayCommon); + + if (opt.ORIENTATION) { + // switch internal workspace orientation in GS + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1); + this._overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayVertical); + } else { + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1); + this._overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayHorizontal); + } + + console.debug(' WorkspacesViewModule - Activated'); } -} + + _disableModule() { + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1); + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' WorkspacesViewModule - Disabled'); + } +}; const WorkspacesViewCommon = { _getFirstFitSingleWorkspaceBox(box, spacing, vertical) { @@ -91,7 +121,6 @@ const WorkspacesViewCommon = { } const fitSingleBox = new Clutter.ActorBox({ x1, y1 }); - fitSingleBox.set_size(workspaceWidth, workspaceHeight); return fitSingleBox; @@ -143,21 +172,7 @@ const WorkspacesViewCommon = { }, _updateVisibility() { - // replaced in _updateWorkspacesState - /* let workspaceManager = global.workspace_manager; - let active = workspaceManager.get_active_workspace_index(); - - const fitMode = this._fitModeAdjustment.value; - const singleFitMode = fitMode === FitMode.SINGLE; - - for (let w = 0; w < this._workspaces.length; w++) { - let workspace = this._workspaces[w]; - - if (this._animating || this._gestureActive || !singleFitMode) - workspace.show(); - else - workspace.visible = Math.abs(w - active) <= opt.NUMBER_OF_VISIBLE_NEIGHBORS; - }*/ + // visibility handles _updateWorkspacesState() }, // disable scaling and hide inactive workspaces @@ -175,27 +190,21 @@ const WorkspacesViewCommon = { 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 - scaleX = 1; - scaleY = 0.1; - } else { - scaleX = 0.1; - scaleY = 1; - } - const wsScrollProgress = adj.value % 1; const secondaryMonitor = this._monitorIndex !== global.display.get_primary_monitor(); const blockSecondaryAppGrid = opt.OVERVIEW_MODE && currentState > 1; + // Hide inactive workspaces this._workspaces.forEach((w, index) => { if (!(blockSecondaryAppGrid && secondaryMonitor)) w.stateAdjustment.value = workspaceMode; - const distanceToCurrentWorkspace = Math.abs(adj.value - index); + let distance = adj.value - index; + const distanceToCurrentWorkspace = Math.abs(distance); const scaleProgress = 1 - Math.clamp(distanceToCurrentWorkspace, 0, 1); + // const scale = Util.lerp(0.94, 1, scaleProgress); + // w.set_scale(scale, scale); // 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 @@ -210,22 +219,28 @@ const WorkspacesViewCommon = { // 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 + // so we show them after the animation finished, move them to their position from outside of 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; + const directionNext = distance > 0; + if (!opt.ORIENTATION) { + const width = w.width * 0.6 * opt.WS_PREVIEW_SCALE; + w.translation_x = directionNext ? -width : width; + } + if (opt.ORIENTATION) { + const height = w.height * 0.6 * opt.WS_PREVIEW_SCALE; + w.translation_y = directionNext ? -height : height; + } + + w.opacity = 10; + w.get_parent().set_child_below_sibling(w, null); w.ease({ - duration: 100, - scale_x: 1, - scale_y: 1, + duration: 300, + translation_x: 0, + translation_y: 0, + opacity: 255, 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 @@ -238,9 +253,40 @@ const WorkspacesViewCommon = { w._background.opacity = 0; }); }, + + exposeWindows(workspaceIndex = null, callback) { + let adjustments = []; + if (workspaceIndex === null) { + this._workspaces.forEach(ws => { + adjustments.push(ws._background._stateAdjustment); + }); + } else { + adjustments.push(this._workspaces[workspaceIndex]._background._stateAdjustment); + } + + adjustments.forEach(adj => { + if (adj.value === 0) { + adj.value = 0; + adj.ease(1, { + duration: 200, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + opt.WORKSPACE_MODE = 1; + if (callback) + callback(); + }, + }); + } + }); + }, +}; + +const SecondaryMonitorDisplayCommon = { + exposeWindows(...args) { + this._workspacesView.exposeWindows(...args); + }, }; -// SecondaryMonitorDisplay Vertical const SecondaryMonitorDisplayVertical = { _getThumbnailParamsForState(state) { @@ -270,24 +316,6 @@ const SecondaryMonitorDisplayVertical = { return { opacity, scale, translationX }; }, - _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, - Math.round(scaledWidth)); - }, - _getWorkspacesBoxForState(state, box, padding, thumbnailsWidth, spacing) { // const { ControlsState } = OverviewControls; const workspaceBox = box.copy(); @@ -303,7 +331,7 @@ const SecondaryMonitorDisplayVertical = { break; yShift = 0; - if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED) { + if (opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible) { if (opt.PANEL_POSITION_TOP) yShift = Main.panel.height; else @@ -341,26 +369,40 @@ const SecondaryMonitorDisplayVertical = { const spacing = themeNode.get_length('spacing') * expandFraction; const padding = Math.round(0.1 * height); - let thumbnailsWidth = this._getThumbnailsWidth(contentBox, spacing); - let [, thumbnailsHeight] = this._thumbnails.get_preferred_custom_height(thumbnailsWidth); - thumbnailsHeight = Math.min(thumbnailsHeight, height - 2 * spacing); - + let thumbnailsWidth = 0; + let thumbnailsHeight = 0; this._thumbnails.visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible) { + const reduceBoxHeight = opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; + + thumbnailsWidth = width * opt.SEC_MAX_THUMBNAIL_SCALE; + + let totalTmbSpacing; + [totalTmbSpacing, thumbnailsHeight] = this._thumbnails.get_preferred_height(thumbnailsWidth); + thumbnailsHeight = Math.round(thumbnailsHeight + totalTmbSpacing); + + const thumbnailsHeightMax = height - spacing - reduceBoxHeight; + + if (thumbnailsHeight > thumbnailsHeightMax) { + thumbnailsHeight = thumbnailsHeightMax; + thumbnailsWidth = Math.round(this._thumbnails.get_preferred_width(thumbnailsHeight)[1]); + } + let wsTmbX; if (opt.SEC_WS_TMB_LEFT) { // left - wsTmbX = Math.round(spacing / 4); + wsTmbX = spacing / 2; this._thumbnails._positionLeft = true; } else { - wsTmbX = Math.round(width - spacing / 4 - thumbnailsWidth); + wsTmbX = width - spacing / 2 - thumbnailsWidth; this._thumbnails._positionLeft = false; } const childBox = new Clutter.ActorBox(); - const availSpace = height - thumbnailsHeight - 2 * spacing; + const availSpace = height - thumbnailsHeight; let wsTmbY = availSpace / 2; - wsTmbY -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbY - spacing; + wsTmbY -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbY; + wsTmbY += opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; childBox.set_origin(Math.round(wsTmbX), Math.round(wsTmbY)); childBox.set_size(thumbnailsWidth, thumbnailsHeight); @@ -390,22 +432,14 @@ const SecondaryMonitorDisplayVertical = { if (opt.OVERVIEW_MODE2) this.set_child_above_sibling(this._thumbnails, null); - const visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible === visible) return; this._thumbnails.show(); + this._thumbnails.visible = visible; this._updateThumbnailParams(); - this._thumbnails.ease_property('expand-fraction', visible ? 1 : 0, { - duration: SIDE_CONTROLS_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - this._thumbnails.visible = visible; - this._thumbnails._indicator.visible = visible; - }, - }); }, _updateThumbnailParams() { @@ -417,7 +451,6 @@ const SecondaryMonitorDisplayVertical = { if (!this._thumbnails._thumbnails.length) this._thumbnails._createThumbnails(); - const { initialState, finalState, progress } = this._overviewAdjustment.getStateTransitionParams(); @@ -474,7 +507,6 @@ const SecondaryMonitorDisplayVertical = { }, }; -// SecondaryMonitorDisplay Horizontal const SecondaryMonitorDisplayHorizontal = { _getThumbnailParamsForState(state) { // const { ControlsState } = OverviewControls; @@ -520,7 +552,7 @@ const SecondaryMonitorDisplayHorizontal = { break; yShift = 0; - if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED) { + if (opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible) { if (opt.PANEL_POSITION_TOP) yShift = Main.panel.height; else @@ -571,24 +603,36 @@ const SecondaryMonitorDisplayHorizontal = { const spacing = themeNode.get_length('spacing') * expandFraction; const padding = Math.round(0.1 * height); - let thumbnailsHeight = this._getThumbnailsHeight(contentBox); - let [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(thumbnailsHeight); - thumbnailsWidth = Math.min(thumbnailsWidth, width - 2 * spacing); - + let thumbnailsWidth = 0; + let thumbnailsHeight = 0; this._thumbnails.visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible) { + const reservedHeight = opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; + + thumbnailsHeight = height * opt.SEC_MAX_THUMBNAIL_SCALE; + + let totalTmbSpacing; + [totalTmbSpacing, thumbnailsWidth] = this._thumbnails.get_preferred_width(thumbnailsHeight); + thumbnailsWidth = Math.round(thumbnailsWidth + totalTmbSpacing); + + const thumbnailsWidthMax = width - spacing; + + if (thumbnailsWidth > thumbnailsWidthMax) { + thumbnailsWidth = thumbnailsWidthMax; + thumbnailsHeight = Math.round(this._thumbnails.get_preferred_height(thumbnailsWidth)[1]); + } + let wsTmbY; if (opt.SEC_WS_TMB_TOP) - wsTmbY = Math.round(spacing / 4); + wsTmbY = spacing / 2 + reservedHeight; else - wsTmbY = Math.round(height - spacing / 4 - thumbnailsHeight); - + wsTmbY = height - spacing / 2 - thumbnailsHeight; const childBox = new Clutter.ActorBox(); - const availSpace = width - thumbnailsWidth - 2 * spacing; + const availSpace = width - thumbnailsWidth; let wsTmbX = availSpace / 2; - wsTmbX -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbX - spacing; + wsTmbX -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbX; childBox.set_origin(Math.round(wsTmbX), Math.round(wsTmbY)); childBox.set_size(thumbnailsWidth, thumbnailsHeight); @@ -625,7 +669,6 @@ const SecondaryMonitorDisplayHorizontal = { if (!this._thumbnails._thumbnails.length) this._thumbnails._createThumbnails(); - const { initialState, finalState, progress } = this._overviewAdjustment.getStateTransitionParams(); @@ -682,7 +725,7 @@ const SecondaryMonitorDisplayHorizontal = { }, }; -const ExtraWorkspaceView = { +const ExtraWorkspaceViewCommon = { _updateWorkspaceMode() { const overviewState = this._overviewAdjustment.value; @@ -701,9 +744,23 @@ const ExtraWorkspaceView = { if (!opt.SHOW_WS_PREVIEW_BG && this._workspace._background.opacity) this._workspace._background.opacity = 0; }, + + exposeWindows() { + const adjustment = this._workspace._background._stateAdjustment; + if (adjustment.value === 0) { + adjustment.value = 0; + adjustment.ease(1, { + duration: 200, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + opt.WORKSPACE_MODE = 1; + }, + }); + } + }, }; -const WorkspacesDisplay = { +const WorkspacesDisplayCommon = { _updateWorkspacesViews() { for (let i = 0; i < this._workspacesViews.length; i++) this._workspacesViews[i].destroy(); @@ -761,8 +818,8 @@ const WorkspacesDisplay = { return Clutter.EVENT_STOP; } - if (_Util.isShiftPressed()) { - let direction = _Util.getScrollDirection(event); + if (Me.Util.isShiftPressed()) { + let direction = Me.Util.getScrollDirection(event); if (direction === null || (Date.now() - this._lastScrollTime) < 150) return Clutter.EVENT_STOP; this._lastScrollTime = Date.now(); @@ -775,11 +832,11 @@ const WorkspacesDisplay = { else direction = 0; - if (direction) { - _Util.reorderWorkspace(direction); + Me.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 => { + const primaryMonitorIndex = global.display.get_primary_monitor(); + Main.overview._overview._controls._workspacesDisplay._workspacesViews[primaryMonitorIndex]._workspaces.forEach(w => { w.visible = true; }); return Clutter.EVENT_STOP; @@ -806,7 +863,7 @@ const WorkspacesDisplay = { switch (symbol) { case Clutter.KEY_Return: case Clutter.KEY_KP_Enter: - if (_Util.isCtrlPressed()) { + if (Me.Util.isCtrlPressed()) { Main.ctrlAltTabManager._items.forEach(i => { if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i); @@ -836,17 +893,17 @@ const WorkspacesDisplay = { which = workspaceManager.n_workspaces - 1; break; case Clutter.KEY_space: - if (_Util.isCtrlPressed() && _Util.isShiftPressed()) { - _Util.openPreferences(); - } else if (_Util.isAltPressed()) { + if (Me.Util.isCtrlPressed() && Me.Util.isShiftPressed()) { + Me.Util.activateSearchProvider(Me.ESP_PREFIX); + } else if (Me.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) { - _Util.activateSearchProvider(SEARCH_WINDOWS_PREFIX); + } else if (opt.get('recentFilesSearchProviderModule') && Me.Util.isCtrlPressed()) { + Me.Util.activateSearchProvider(Me.RFSP_PREFIX); + } else if (opt.get('windowSearchProviderModule')) { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); } return Clutter.EVENT_STOP; @@ -867,16 +924,18 @@ const WorkspacesDisplay = { }); } 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); + const wsIndex = global.workspace_manager.get_active_workspace().index(); + // after expose animation activate keyboard for window selection + const callback = Me.Util.activateKeyboardForWorkspaceView; + this._workspacesViews.forEach( + view => { + view.exposeWindows(wsIndex, callback); + } + ); } else { if (state === 2) return Clutter.EVENT_PROPAGATE; - Main.ctrlAltTabManager._items.forEach(i => { - if (i.sortGroup === 1 && i.name === 'Windows') - Main.ctrlAltTabManager.focusGroup(i); - }); + Me.Util.activateKeyboardForWorkspaceView(); } return Clutter.EVENT_STOP; @@ -895,14 +954,14 @@ const WorkspacesDisplay = { // Otherwise it is a workspace index ws = workspaceManager.get_workspace_by_index(which); - if (_Util.isShiftPressed()) { + if (Me.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) - _Util.reorderWorkspace(direction); + Me.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; diff --git a/extensions/44/vertical-workspaces/metadata.json b/extensions/44/vertical-workspaces/metadata.json index dbfaa91..fa62b14 100644 --- a/extensions/44/vertical-workspaces/metadata.json +++ b/extensions/44/vertical-workspaces/metadata.json @@ -7,8 +7,16 @@ "43", "44" ], + "session-modes": [ + "user", + "unlock-dialog" + ], "url": "https://github.com/G-dH/vertical-workspaces", + "donations": { + "buymeacoffee": "georgdh" + }, "gettext-domain": "vertical-workspaces", "settings-schema": "org.gnome.shell.extensions.vertical-workspaces", - "version": 28 + "version-name": "44.10" } + diff --git a/extensions/44/vertical-workspaces/po/nl.po b/extensions/44/vertical-workspaces/po/nl.po new file mode 100644 index 0000000..2d9613e --- /dev/null +++ b/extensions/44/vertical-workspaces/po/nl.po @@ -0,0 +1,1551 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR GdH +# This file is distributed under the same license as the vertical-workspaces package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: vertical-workspaces\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-03-08 12:37+0100\n" +"PO-Revision-Date: 2023-05-04 14:03+0200\n" +"Last-Translator: Heimen Stoffels <vistausss@fastmail.com>\n" +"Language-Team: \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.2.2\n" + +#: dash.js:743 +msgid "Search Open Windows (Hotkey: Space)" +msgstr "Geopende vensters doorzoeken (sneltoets: spatiebalk)" + +#: dash.js:816 +msgid "Search Recent Files (Hotkey: Ctrl + Space)" +msgstr "Onlangs gebruikte bestanden doorzoeken (sneltoets: Ctrl+spatiebalk)" + +#: windowSearchProvider.js:117 windowSearchProvider.js:119 +msgid "Open Windows" +msgstr "Geopende vensters" + +#: windowSearchProvider.js:118 +msgid "List of open windows" +msgstr "Een lijst met geopende vensters" + +#: appDisplay.js:1317 +msgid "Force Quit" +msgstr "Toepassing gedwongen afsluiten" + +#: appDisplay.js:1335 +msgid "Move App to Current Workspace ( Shift + Click )" +msgstr "Toepassing verplaatsen naar huidig werkblad (Shift+muisklik)" + +#: prefs.js:47 prefs.js:1465 +msgid "Layout" +msgstr "Indeling" + +#: prefs.js:53 +msgid "Appearance" +msgstr "Vormgeving" + +#: prefs.js:59 +msgid "Behavior" +msgstr "Gedrag" + +#: prefs.js:65 +msgid "Misc" +msgstr "Overig" + +#: prefs.js:71 +msgid "About" +msgstr "Over" + +#: prefs.js:119 prefs.js:517 prefs.js:1030 prefs.js:1456 +msgid "Dash" +msgstr "Dash" + +#: prefs.js:125 +msgid "Dash Position" +msgstr "Dashlocatie" + +#: prefs.js:131 +msgid "Top" +msgstr "Bovenaan" + +#: prefs.js:132 +msgid "Right" +msgstr "Rechts" + +#: prefs.js:133 prefs.js:355 +msgid "Bottom" +msgstr "Onderaan" + +#: prefs.js:134 +msgid "Left" +msgstr "Links" + +#: prefs.js:135 prefs.js:393 prefs.js:557 prefs.js:572 prefs.js:587 +msgid "Hide" +msgstr "Verbergen" + +#: prefs.js:142 +msgid "Center Horizontal Dash to Workspace" +msgstr "Horizontale dash centreren op werkblad" + +#: prefs.js:143 +msgid "" +"If the Dash Position is set to Top or Bottom, the position will be " +"recalculated relative to the workspace preview instead of the screen. Works " +"only with the default Dash." +msgstr "" +"Als de dashlocatie boven- of onderaan is, dan wordt de locatie herberekend " +"op basis van de werkbladvoorvertoning in plaats van het scherm. Werkt alleen " +"met de standaarddash." + +#: prefs.js:160 +msgid "Fine Tune Dash Position" +msgstr "Dashlocatie aanpassen" + +#: prefs.js:161 +msgid "" +"Adjusts position of the dock on chosen axis. Works only with the default " +"Dash." +msgstr "" +"Pas de locatie van het dock op de gekozen as aan. Werkt alleen met de " +"standaarddash." + +#: prefs.js:170 +msgid "Workspace Thumbnails / Orientation" +msgstr "Werkbladminiaturen/-oriëntatie" + +#: prefs.js:176 +msgid "Thumbnails Position / Workspaces Orientation" +msgstr "Miniatuurlocatie/Werkbladoriëntatie" + +#: prefs.js:177 +msgid "" +"Position of the workspace thumbnails on the screen also sets orientation of " +"the workspaces to vertical or horizontal. You have two options to disable " +"workspace thumbnails, one sets workspaces to the vertical orientation, the " +"second one to horizontal." +msgstr "" +"De locatie van werkbladminiaturen op het scherm stelt tevens de oriëntatie " +"van werkbladen in op verticaal of horizontaal. U heeft twee mogelijkheden om " +"miniaturen uit te schakelen: u kunt de werkbladen instellen op verticaal of " +"horizontaal." + +#: prefs.js:183 +msgid "Left \t Vertical Orientation" +msgstr "Links \t Verticale oriëntatie" + +#: prefs.js:184 +msgid "Right \t Vertical Orientation" +msgstr "Rechts \t Horizontale oriëntatie" + +#: prefs.js:185 +msgid "Hide \t Set Vertical Orientation" +msgstr "Verbergen \t Verticale oriëntatie instellen" + +#: prefs.js:186 +msgid "Top \t Horizontal Orientation" +msgstr "Bovenaan \t Horizontale oriëntatie" + +#: prefs.js:187 +msgid "Bottom \t Horizontal Orientation" +msgstr "Onderaan \t Horizontale oriëntatie" + +#: prefs.js:188 +msgid "Hide \t Set Horizontal Orientation" +msgstr "Verbergen \t Horizontale oriëntatie instellen" + +#: prefs.js:204 prefs.js:409 +msgid "Fine Tune Workspace Thumbnails Position" +msgstr "Werkbladminiatuurlocatie instellen" + +#: prefs.js:205 +msgid "Adjusts workspace thumbnails vertical position." +msgstr "Pas de verticale positie van werkbladminiaturen aan." + +#: prefs.js:213 +msgid "Reserve Full Screen Height/Width for Thumbnails" +msgstr "Gehele scherm gebruiken voor miniaturen" + +#: prefs.js:214 +msgid "" +"The whole screen height/width will be reserved for workspace thumbnails at " +"the expense of space available for Dash (if the Dash is oriented in a " +"different axis)." +msgstr "" +"De gehele schermhoogte en -breedte zal worden gebruikt voor " +"werkbladminiaturen. Let op: dit gaat ten koste van de beschikbare dashruimte " +"(als de dash op een andere oriëntatie is ingesteld)." + +#: prefs.js:231 prefs.js:427 +msgid "Workspace Thumbnails Max Scale" +msgstr "Max. omvang van werkbladminiaturen" + +#: prefs.js:232 +msgid "" +"Adjusts maximum size of the workspace thumbnails (% relative to display " +"width)." +msgstr "" +"Pas de maximale omvang van werkbladminiaturen aan (% relatief in verhouding " +"tot de schermbreedte)." + +#: prefs.js:241 prefs.js:697 +msgid "Workspace Preview" +msgstr "Werkbladvoorvertoning" + +#: prefs.js:256 +msgid "Workspaces Scale" +msgstr "Werkbladomvang verkleinen" + +#: prefs.js:257 +msgid "" +"Scales down workspace previews so you can fit more of the adjacent " +"workspaces on the screen. Default size is calculated to use all available " +"space." +msgstr "" +"Maak de werkbladvoorvertoningen kleiner om meer werkbladen op het scherm te " +"tonen. De standaardomvang maakt gebruik van alle beschikbare ruimte." + +#: prefs.js:274 +msgid "Workspaces Spacing" +msgstr "Ruimte tussen werkbladen" + +#: prefs.js:275 +msgid "" +"Adjusts spacing between workspace previews so you can control how much of " +"the adjacent workspaces overlap to the current workspace overview. Default " +"value should set the adjacent workspaces out of the screen." +msgstr "" +"Pas de ruimte tussen werkbladvoorvertoningen aan zodat u zelf kunt bepalen " +"hoeveel werkbladen mogen overlappen. De standaardwaarde ligt buiten het " +"scherm." + +#: prefs.js:284 prefs.js:712 prefs.js:1072 +msgid "App Grid" +msgstr "Toepassingsrooster" + +#: prefs.js:290 +msgid "Center App Grid" +msgstr "Toepassingsrooster centreren" + +#: prefs.js:291 +msgid "" +"App grid in app view page will be centered to the display instead of the " +"available space. This option may have impact on the size of the grid, more " +"for narrower and small resolution displays, especially if workspace " +"thumbnails are bigger." +msgstr "" +"Toon het toepassingsrooster op het midden van het scherm in plaats van over " +"de gehele ruimte. Let op: deze optie kan van invloed zijn op de " +"roosteromvang. Op kleine schermen wordt het rooster smaller, vooral als de " +"werkbladminiaturen groter zijn." + +#: prefs.js:300 +msgid "Search View" +msgstr "Zoekweergave" + +#: prefs.js:306 +msgid "Center Search View" +msgstr "Zoekweergave centreren" + +#: prefs.js:307 +msgid "" +"Search view will be centered to the display instead of the available space." +msgstr "" +"Toon de zoekweergave op het midden van het scherm in plaats van over de " +"gehele ruimte." + +#: prefs.js:315 +msgid "Always Show Search Entry" +msgstr "Zoekvak altijd tonen" + +#: prefs.js:316 +msgid "" +"If disabled, the search entry field will be hidden when not in use, so the " +"workspace preview and app grid may take up more space." +msgstr "" +"Schakel uit om het zoekvak te verbergen als het niet wordt gebruikt, zodat " +"er meer ruimte voor de werkbladvoorvertoningen en het toepassingsrooster is." + +#: prefs.js:333 +msgid "Search Results Width" +msgstr "Breedte van zoekresultaten" + +#: prefs.js:334 +msgid "" +"Adjusts maximum width of search results view (% relative to default). This " +"allows you to fit more (or less) app icons into the app search result." +msgstr "" +"Pas de breedte van zoekresultaten aan (% relatief ten opzichte van " +"standaard). Hierdoor kunt u meer of minder pictogrammen in de resultaten " +"tonen." + +#: prefs.js:342 prefs.js:1483 +msgid "Panel" +msgstr "Bovenbalk" + +#: prefs.js:348 +msgid "Main Panel Position" +msgstr "Bovenbalklocatie" + +#: prefs.js:349 +msgid "Allows you to place the main panel at the bottom of your monitor." +msgstr "Verplaats de bovenbalk naar de onderkant van het scherm." + +#: prefs.js:354 +msgid "Top (Default)" +msgstr "Bovenaan (standaard)" + +#: prefs.js:362 +msgid "Main Panel Visibility" +msgstr "Zichtbaarheid van bovenbalk" + +#: prefs.js:363 +msgid "Main panel can be visible always, only in the overview or never." +msgstr "Toon de bovenbalk altijd, alleen op het overzicht of nooit." + +#: prefs.js:368 +msgid "Always Visible (Default)" +msgstr "Altijd tonen (standaard)" + +#: prefs.js:369 +msgid "Overview Only" +msgstr "Alleen op het overzicht" + +#: prefs.js:370 +msgid "Always Hidden" +msgstr "Altijd verbergen" + +#: prefs.js:378 +msgid "Secondary Monitors" +msgstr "Meerdere beeldschermen" + +#: prefs.js:384 +msgid "Workspace Thumbnails Position" +msgstr "Werkbladminiatuurlocaties" + +#: prefs.js:385 +msgid "" +"Allows you to place workspace thumbnails of secondary monitors on the " +"opposite side than on the primary monitor." +msgstr "Plaats werkbladminiaturen op andere beeldschermen dan het hoofdscherm." + +#: prefs.js:390 +msgid "Same as Primary" +msgstr "Gelijk aan hoofdscherm" + +#: prefs.js:391 +msgid "Left / Top" +msgstr "Links/Bovenaan" + +#: prefs.js:392 +msgid "Right / Bottom" +msgstr "Rechts/Onderaan" + +#: prefs.js:410 +msgid "Adjusts secondary monitors workspace thumbnails vertical position." +msgstr "Pas de verticale miniatuurpositie op andere beeldschermen aan." + +#: prefs.js:428 +msgid "" +"Adjusts maximum size of the workspace thumbnails (% relative to display " +"width) for secondary monitors." +msgstr "" +"Pas de maximale omvang van werkbladminiaturen op andere beeldschermen aan (% " +"relatief in verhouding tot de schermbreedte)." + +#: prefs.js:445 +msgid "Workspace Preview Scale" +msgstr "Werkbladvoorvertoningen verkleinen" + +#: prefs.js:446 +msgid "Scales down workspace previews on secondary monitors." +msgstr "Verklein de voorvertoningen op andere beeldschermen." + +#: prefs.js:454 +msgid "Shift Workspace Preview by Panel Height" +msgstr "Werkbladvoorvertoningen aanpassen aan bovenbalkhoogte" + +#: prefs.js:455 +msgid "" +"This option can help align overview of the secondary monitor with the " +"primary monitor." +msgstr "" +"Met deze optie kunt u het overzicht op een ander beeldscherm gelijkstellen " +"aan die op het hoofdscherm." + +#: prefs.js:464 +msgid "Workspace Switcher Popup" +msgstr "Werkbladwisselaarpop-up" + +#: prefs.js:480 +msgid "Horizontal Position (% from left)" +msgstr "Horizontale positie (in % vanaf linkerzijde)" + +#: prefs.js:481 +msgid "" +"This popup shows up when you switch workspace using a keyboard shortcut or " +"gesture outside of the overview. You can disable it on the Behavior tab. If " +"you want more control over the popup, try Workspace Switcher Manager " +"extension." +msgstr "" +"Deze pop-up wordt getoond als u van werkblad wisselt met behulp van een " +"sneltoets of gebaar buiten het overzicht om. U kunt de pop-up uitschakelen " +"op het tabblad ‘Gedrag’. Als u de pop-up naar eigen hand wilt zetten, " +"probeer dan de Workspace Switcher Manager-uitbreiding." + +#: prefs.js:499 +msgid "Vertical Position (% from top)" +msgstr "Verticale positie (in % vanaf bovenzijde)" + +#: prefs.js:523 +msgid "Dash Max Icon Size" +msgstr "Max. pictogramgrootte in dash" + +#: prefs.js:524 +msgid "Maximum size of Dash icons in pixels. Works only with default Dash." +msgstr "" +"De maximale pictogramgrootte, in pixels. Let op: dit werkt alleen in " +"combinatie met de standaarddash." + +#: prefs.js:529 prefs.js:732 prefs.js:752 prefs.js:853 +msgid "128" +msgstr "128" + +#: prefs.js:530 prefs.js:733 prefs.js:753 prefs.js:854 +msgid "112" +msgstr "112" + +#: prefs.js:531 prefs.js:734 prefs.js:754 prefs.js:855 +msgid "96" +msgstr "96" + +#: prefs.js:532 prefs.js:735 prefs.js:755 prefs.js:856 +msgid "80" +msgstr "80" + +#: prefs.js:533 prefs.js:676 prefs.js:736 prefs.js:756 prefs.js:857 +msgid "64" +msgstr "64" + +#: prefs.js:534 prefs.js:677 prefs.js:737 prefs.js:757 prefs.js:858 +msgid "48" +msgstr "48" + +#: prefs.js:535 prefs.js:678 prefs.js:758 prefs.js:859 +msgid "32" +msgstr "32" + +#: prefs.js:536 +msgid "24" +msgstr "24" + +#: prefs.js:537 +msgid "16" +msgstr "16" + +#: prefs.js:551 +msgid "Show Apps Icon Position" +msgstr "Locatie van ‘Toepassingen tonen’" + +#: prefs.js:552 +msgid "Sets the position of the \"Show Applications\" icon in the Dash." +msgstr "Stel de locatie van het pictogram ‘Toepassingen tonen’ in de dash in." + +#: prefs.js:558 prefs.js:573 prefs.js:588 +msgid "Start" +msgstr "Begin" + +#: prefs.js:559 prefs.js:574 prefs.js:589 +msgid "End" +msgstr "Einde" + +#: prefs.js:566 +msgid "Open Windows Icon Position" +msgstr "Locatie van ‘Geopende vensters’" + +#: prefs.js:567 +msgid "" +"This option adds \"Search Open Windows\" icon into dash (if window search " +"provider enabled on the Behavior tab) so you can directly toggle window " +"search provider results. Even if you disable this icon, you can use the " +"secondary mouse button click on the Show Apps Icon, or the Space hotkey to " +"access this feature." +msgstr "" +"Deze optie voegt een pictogram ‘Geopende vensters doorzoeken’ toe aan de " +"dash (als de vensterzoekdienst is ingeschakeld op het tabblad ‘Gedrag’), " +"zodat u direct kunt zoeken. Als u dit pictogram uitschakelt, dan kunt u deze " +"functie alsnog aanroepen met behulp van een rechtermuisklik op ‘Toepassingen " +"tonen’ of door te drukken op de spatiebalk." + +#: prefs.js:581 +msgid "Recent Files Icon Position" +msgstr "Locatie van ‘Onlangs gebruikte bestanden’" + +#: prefs.js:582 +msgid "" +"This option adds \"Search Recent Files\" icon into dash (if recent files " +"search provider enabled on the Behavior tab) so you can directly toggle " +"recent files search provider results. Even if you disable this icon, you can " +"use Ctrl + Space hotkey to access this feature." +msgstr "" +"Deze optie voegt een pictogram ‘Onlangs gebruikte bestanden doorzoeken’ toe " +"aan de dash (als de bestandszoekdienst is ingeschakeld op het tabblad " +"‘Gedrag’), zodat u direct kunt zoeken. Als u dit pictogram uitschakelt, dan " +"kunt u deze functie alsnog aanroepen met behulp van de sneltoets Ctrl + " +"spatiebalk." + +#: prefs.js:597 +msgid "Dash Background Opacity" +msgstr "Doorzichtigheid van dashachtergrond" + +#: prefs.js:598 +msgid "Adjusts the opacity of the dash background." +msgstr "Pas de doorzichtigheid van de dashachtergrond aan." + +#: prefs.js:614 +msgid "Dash Background Radius" +msgstr "Dash-achtergrondstraal" + +#: prefs.js:615 +msgid "" +"Adjusts the border radius of the dash background in pixels. 0 means default " +"value." +msgstr "" +"Pas de straal van de dashachtergrond aan (in pixels). 0 = standaardwaarde." + +#: prefs.js:623 prefs.js:1050 +msgid "Workspace Thumbnails" +msgstr "Werkbladminiaturen" + +#: prefs.js:629 +msgid "Show Workspace Thumbnail Labels" +msgstr "Werkbladminiatuurlabels tonen" + +#: prefs.js:630 +msgid "" +"Each workspace thumbnail can show its index and name (if defined in the " +"system settings) or name/title of its most recently used app/window." +msgstr "" +"Toon de namen van werkbladen (indien ingesteld in de systeemvoorkeuren) of " +"naam+titel van onlangs gebruikte toepassing/venster." + +#: prefs.js:635 prefs.js:680 prefs.js:1001 prefs.js:1062 prefs.js:1260 +#: prefs.js:1278 prefs.js:1296 prefs.js:1331 +msgid "Disable" +msgstr "Uitschakelen" + +#: prefs.js:636 +msgid "Index" +msgstr "Index" + +#: prefs.js:637 +msgid "Index + WS Name" +msgstr "Index + Werkbladnaam" + +#: prefs.js:638 +msgid "Index + App Name" +msgstr "Index + Toepassingsnaam" + +#: prefs.js:639 +msgid "Index + Window Title" +msgstr "Index + Venstertitel" + +#: prefs.js:646 +msgid "Show WS Thumbnail Label on Hover" +msgstr "Miniatuurlabels tonen na aanwijzen" + +#: prefs.js:647 +msgid "Show label only when the mouse pointer hovers over a thumbnail" +msgstr "Toon de labels alleen als u met de aanwijzer een miniatuur aanwijst." + +#: prefs.js:655 +msgid "Show Wallpaper in Workspace Thumbnails" +msgstr "Werkbladminiaturen voorzien van achtergrond" + +#: prefs.js:656 +msgid "All workspace thumbnails will include the current desktop background." +msgstr "Toon de huidige bureaubladachtergrond op werkbladminiaturen." + +#: prefs.js:664 +msgid "Window Preview" +msgstr "Venstervoorvertoning" + +#: prefs.js:670 +msgid "Window Preview App Icon Size" +msgstr "Pictogramgrootte in venstervoorvertoning" + +#: prefs.js:671 +msgid "Default size is 64." +msgstr "Standaard: 64." + +#: prefs.js:679 +msgid "22" +msgstr "22" + +#: prefs.js:687 +msgid "Always Show Window Titles" +msgstr "Venstertitels altijd tonen" + +#: prefs.js:688 +msgid "" +"All windows on the workspace preview will show their titles, not only the " +"one with the mouse pointer." +msgstr "" +"Toon venstertitels van alle vensters op de voorvertoning, niet alleen van " +"het venster onder de aanwijzer." + +#: prefs.js:703 +msgid "Show Workspace Preview Background" +msgstr "Werkbladvoorvertoning voorzien van achtergrond" + +#: prefs.js:704 +msgid "Allows you to hide the scaling background of the workspace preview." +msgstr "Toon of verberg de achtergrond op de werkbladvoorvertoning." + +#: prefs.js:718 +msgid "Icon Size" +msgstr "Pictogramgrootte" + +#: prefs.js:719 +msgid "" +"Allows to force fixed icon size and bypass the default adaptive algorithm." +msgstr "" +"Stel een vaste pictogramgrootte in ter vervanging van de standaard " +"aanpasbare grootte." + +#: prefs.js:724 prefs.js:751 +msgid "Adaptive (Default)" +msgstr "Aanpasbaar (standaard)" + +#: prefs.js:725 +msgid "256" +msgstr "256" + +#: prefs.js:726 +msgid "224" +msgstr "224" + +#: prefs.js:727 +msgid "208" +msgstr "208" + +#: prefs.js:728 +msgid "192" +msgstr "192" + +#: prefs.js:729 +msgid "176" +msgstr "176" + +#: prefs.js:730 +msgid "160" +msgstr "160" + +#: prefs.js:731 +msgid "144" +msgstr "144" + +#: prefs.js:745 +msgid "Folder Icon Size" +msgstr "Pictogramgrootte van mappen" + +#: prefs.js:746 +msgid "" +"Allows to disable the default adaptive algorithm and set a fixed size of " +"icons inside folders." +msgstr "" +"Stel een vaste pictogramgrootte in ter vervanging van de standaard " +"aanpasbare grootte." + +#: prefs.js:766 +msgid "Max App Folder Icon Grid Size" +msgstr "Max. grootte van mappen op rooster" + +#: prefs.js:767 +msgid "" +"Each folder icon shows (up to) 4 app icons as a preview of the folder " +"content, this option allows you to increase the number to 9 icons if folder " +"contains more than 4 or 8 apps. The latter avoids half empty folder icons." +msgstr "" +"Elk mappictogram toont maximaal 4 pictogrammen als voorvertoning. Met behulp " +"van deze optie kunt u het aantal verhogen naar maximaal 9 als de map 4 of 8 " +"toepassingen bevat. De laatste optie voorkomt halflege mappictogrammen." + +#: prefs.js:772 +msgid "2x2 (Default)" +msgstr "2x2 (standaard)" + +#: prefs.js:773 +msgid "3x3 for 5+ apps" +msgstr "3x3 (5 of meer toepassingen)" + +#: prefs.js:774 +msgid "3x3 for 9+ apps" +msgstr "3x3 (9 of meer toepassingen)" + +#: prefs.js:788 +msgid "Columns per Page (0 for adaptive grid)" +msgstr "Aantal kolommen per pagina (0 = aanpasbaar rooster)" + +#: prefs.js:789 +msgid "" +"Number of columns in application grid. If set to 0 (default setting) the " +"number will be set automatically to fit available height." +msgstr "" +"Het aantal kolommen van het toepassingsrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare hoogte." + +#: prefs.js:803 +msgid "Rows per Page (0 for adaptive grid)" +msgstr "Aantal rijen per pagina (0 = aanpasbaar rooster)" + +#: prefs.js:804 +msgid "" +"Number of rows in application grid. If set to 0 (default setting) the number " +"will be set automatically to fit available height." +msgstr "" +"Het aantal rijen van het toepassingsrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare hoogte." + +#: prefs.js:818 +msgid "Folder Columns per Page (0 for adaptive grid)" +msgstr "Aantal mapkolommen per pagina (0 = aanpasbaar rooster)" + +#: prefs.js:819 +msgid "" +"Number of columns in folder grid. If you leave the value on 0, the number of " +"columns will be calculated to fit all folder icons." +msgstr "" +"Het aantal kolommen van het mappenrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare pictogrammen." + +#: prefs.js:833 +msgid "Folder Rows per Page (0 for adaptive grid)" +msgstr "Aantal maprijen per pagina (0 = aanpasbaar rooster)" + +#: prefs.js:834 +msgid "" +"Number of rows in folder grid. If you leave the value on 0, the number of " +"rows will be calculated to fit all folder icons." +msgstr "" +"Het aantal rijen van het mappenrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare pictogrammen." + +#: prefs.js:841 prefs.js:1174 prefs.js:1492 +msgid "Search" +msgstr "Zoeken" + +#: prefs.js:847 +msgid "App Search Icon Size" +msgstr "Omvang van zoekresultaten" + +#: prefs.js:848 +msgid "Size of results provided by the App Search Provider." +msgstr "De omvang van toepassingszoekresultaten." + +#: prefs.js:878 +msgid "Max Search Results Rows" +msgstr "Aantal zoekresultaatrijen" + +#: prefs.js:879 +msgid "" +"Sets the maximum number of rows for result lists of all search providers " +"except window search provider which always lists all results." +msgstr "" +"Stel het maximumaantal rijen van resultaatlijsten van alle zoekdiensten (met " +"uitzondering van vensters) in." + +#: prefs.js:888 +msgid "Overview Background" +msgstr "Overzichtsachtergrond" + +#: prefs.js:894 +msgid "Show Static Background" +msgstr "Eigen achtergrond tonen" + +#: prefs.js:895 +msgid "Show static background wallpaper instead of the solid grey color." +msgstr "Stel een eigen achtergrond in in plaats van de grijze kleur." + +#: prefs.js:911 +msgid "Blur Window Picker Background" +msgstr "Vensterkiezerachtergrond vervagen" + +#: prefs.js:912 +msgid "Blur background wallpaper (if enabled) in the window picker view." +msgstr "Vervaag de achtergrond (indien ingeschakeld) van de vensterkiezer." + +#: prefs.js:928 +msgid "Blur App Grid/Search View Background" +msgstr "Toepassingsrooster-/Zoekweergave-achtergrond vervagen" + +#: prefs.js:929 +msgid "" +"Blur background wallpaper (if enabled) in the app grid and search results " +"views." +msgstr "" +"Vervaag de achtergrond (indien ingeschakeld) van het toepassingsrooster en " +"de zoekweergave." + +#: prefs.js:937 +msgid "Smooth Blur Transitions" +msgstr "Vloeiende vervaagovergangen" + +#: prefs.js:938 +msgid "" +"Makes blur transitions smoother but can impact overall smoothness of " +"overview animations." +msgstr "" +"Maak vervaagovergangen vloeiender. Let op: dit kan van invloed zijn op de " +"overige animaties." + +#: prefs.js:953 prefs.js:980 prefs.js:1002 +msgid "Overview" +msgstr "Overzicht" + +#: prefs.js:959 +msgid "Overview Mode" +msgstr "Overzichtsmodus" + +#: prefs.js:960 +msgid "" +"The Expose Windows on Hover mode do not expose the workspace preview windows " +"until the mouse pointer enters any window.\n" +"The Static Workspace mode does not scale the workspace preview, it only " +"shows Dash and workspace thumbnails over the desktop. Clicking on a " +"workspace thumbnail scales the ws preview and exposes its windows like in " +"the default overview mode." +msgstr "" +"De modus ‘Vensters tonen na aanwijzen’ toont geen werkbladminiaturen totdat " +"de aanwijzer een venster aanwijst.\n" +"De statische modus toont geen verkleinde werkbladminiaturen, maar alleen de " +"dash en miniaturen boven het bureaublad. Klik op een werkbladminiatuur om de " +"voorvertoning te verkleinen en alle vensters te tonen, net als in de " +"standaard overzichtsmodus." + +#: prefs.js:965 prefs.js:1259 prefs.js:1277 prefs.js:1317 +msgid "Default" +msgstr "Standaard" + +#: prefs.js:966 +msgid "Expose Windows on Hover" +msgstr "Vensters tonen na aanwijzen" + +#: prefs.js:967 +msgid "Static Workspace" +msgstr "Statische werkbladen" + +#: prefs.js:974 +msgid "Startup State" +msgstr "Opstartmodus" + +#: prefs.js:975 +msgid "Allows to change the state in which GNOME Shell starts a session." +msgstr "Geef aan in welke modus GNOME Shell een sessie dient te starten." + +#: prefs.js:981 +msgid "Desktop" +msgstr "Bureaublad" + +#: prefs.js:982 prefs.js:1003 +msgid "Applications" +msgstr "Toepassingen" + +#: prefs.js:989 +msgid "" +"Hot Corner (Install Custom Hot Corners - Extended extension for more options)" +msgstr "" +"Snelhoek (installeer voor meer mogelijkheden de uitbreiding ‘Custom Hot " +"Corners - Extended)" + +#: prefs.js:995 +msgid "Hot corner Action" +msgstr "Snelhoekactie" + +#: prefs.js:996 +msgid "" +"Disable or change behavior of hot corner. Holding down the Ctrl key while " +"hitting the hot corner switches between Overview/Applications actions." +msgstr "" +"Pas het gedrag van de snelhoek aan of schakel deze uit. Houd Ctrl ingedrukt " +"tijdens het aanwijzen van de snelhoek om te schakelen tussen het overzicht " +"en de toepassingen." + +#: prefs.js:1010 +msgid "Enable Hot Corner in Full-Screen mode" +msgstr "Snelhoek tonen in schermvullende modus" + +#: prefs.js:1011 +msgid "" +"If you often work with full-screen applications and want the hot corner to " +"be usable." +msgstr "" +"Als u regelmatig met schermvullende toepassingen werkt en de snelhoek wilt " +"blijven gebruiken." + +#: prefs.js:1020 +msgid "Show Ripples Animation" +msgstr "Golfanimatie tonen" + +#: prefs.js:1021 +msgid "Ripples animation shows up when you trigger hot corner." +msgstr "Toon een golfanimatie zodra u de snelhoek activeert." + +#: prefs.js:1036 +msgid "Dash Icon Click" +msgstr "Klikken op dashpictogram" + +#: prefs.js:1037 +msgid "" +"if the app you clicked on has more than one window and the recently used " +"window is not on the current workspace, the overview can switch to the " +"workspace with the recent window." +msgstr "" +"Als de aangeklikte toepassing meer dan dan één venster bevat en het onlangs " +"gebruikte venster niet op het huidige werkblad staat, dan kan worden " +"overgeschakeld naar het juiste werkblad." + +#: prefs.js:1042 +msgid "Activate Last Used Window Immediately" +msgstr "Laatstgebruikte venster focussen" + +#: prefs.js:1043 +msgid "Switch to Workspace with Recently Used Window" +msgstr "Overschakelen naar werkblad met laatstgebruikte venster" + +#: prefs.js:1056 +msgid "Close Workspace Button" +msgstr "Werkbladsluitknop" + +#: prefs.js:1057 +msgid "" +"The Close Workspace button appears on the workspace thumbnail when you hover " +"over it and allows you to close all windows on the workspace. You can choose " +"a safety lock to prevent accidental use." +msgstr "" +"De werkbladsluitknop wordt getoond zodra u een miniatuur aanwijst. Hiermee " +"kunt u alle vensters op het desbetreffende werkblad sluiten. U kunt " +"instellen dat u sluiten wilt voorkomen." + +#: prefs.js:1063 +msgid "Single Click" +msgstr "Eenmaal klikken" + +#: prefs.js:1064 +msgid "Double Click" +msgstr "Dubbelklikken" + +#: prefs.js:1065 +msgid "Ctrl Key + Click" +msgstr "Ctlr+klik" + +#: prefs.js:1078 +msgid "Apps Order" +msgstr "Toepassingsvolgorde" + +#: prefs.js:1079 +msgid "" +"Choose sorting method for the app grid. Note that sorting by alphabet and " +"usage ignores folders." +msgstr "" +"Kies de sorteermethode van het toepassingsrooster. Let op: sorteren op " +"alfabet of gebruik negeert mappen." + +#: prefs.js:1084 +msgid "Custom (Default)" +msgstr "Aangepast (standaard)" + +#: prefs.js:1085 +msgid "Alphabet" +msgstr "Alfabet" + +#: prefs.js:1086 +msgid "Usage" +msgstr "Gebruik" + +#: prefs.js:1093 +msgid "App Grid Content" +msgstr "Inhoud van toepassingsrooster" + +#: prefs.js:1094 +msgid "" +"The default Shell removes favorite apps, this option lets you duplicate them " +"in the grid or remove also running applications. Option \"Favorites and " +"Running First\" only works with the Alphabet and Usage sorting." +msgstr "" +"Standaard worden favoriete toepassingen verborgen. Met behulp van deze optie " +"kunt u ze klonen op het rooster of ook actieve toepassingen verbergen. Let " +"op: de optie ‘Favorieten en actieve bovenaan’ werkt alleen in combinatie met " +"sorteren op alfabet of gebruik." + +#: prefs.js:1099 +msgid "Include All" +msgstr "Alles" + +#: prefs.js:1100 +msgid "Include All - Favorites and Running First" +msgstr "Alles - Favorieten en actieve bovenaan" + +#: prefs.js:1101 +msgid "Exclude Favorites (Default)" +msgstr "Favorieten verbergen (standaard)" + +#: prefs.js:1102 +msgid "Exclude Running" +msgstr "Actieve verbergen" + +#: prefs.js:1103 +msgid "Exclude Favorites and Running" +msgstr "Favorieten en actieve verbergen" + +#: prefs.js:1110 +msgid "Active Icons in Folder Preview" +msgstr "Actieve pictogrammen op mapvoorvertoning" + +#: prefs.js:1111 +msgid "" +"If enabled, clicking an app icon in a folder preview directly opens the app " +"without having to open the folder first. Middle button opens new window of " +"the app without closing the overview, so you can open multiple apps in a row " +"on the current workspace and secondary button opens the folder." +msgstr "" +"Schakel in om op een toepassingspictogram te kunnen klikken vanuit een " +"mapvoorvertoning. Hierdoor hoeft niet eerst de map te worden geopend. Met " +"een middelklik opent u een nieuw venster zonder het overzicht te sluiten, " +"zodat u meerdere toepassingen per rij kunt openen. Met een rechtermuisklik " +"wordt de map geopend." + +#: prefs.js:1120 +msgid "Center Open Folders" +msgstr "Geopende mappen centreren" + +#: prefs.js:1121 +msgid "" +"App folder may open in the center of the screen or above the source folder " +"icon." +msgstr "" +"Toon mappen op het midden van het scherm in plaats van boven het " +"mappictogram." + +#: prefs.js:1130 +msgid "Allow Incomplete Pages" +msgstr "Onvolledige pagina's tonen" + +#: prefs.js:1131 +msgid "" +"If disabled, icons from the next page (if any) are automatically moved to " +"fill any empty slot left after an icon was (re)moved (to a folder for " +"example)." +msgstr "" +"Schakel uit om pictogrammen van de volgende pagina (indien beschikbaar) " +"automatisch te verplaatsen om gaten op te vullen als er pictogrammen " +"verplaatst of verwijderd zijn." + +#: prefs.js:1140 +msgid "App Labels Behavior" +msgstr "Toepassingslabels" + +#: prefs.js:1141 +msgid "Choose how and when to display app names." +msgstr "Geef aan hoe en wanneer er toepassingsnamen dienen te worden getoond." + +#: prefs.js:1146 +msgid "Ellipsized - Expand Selected (Default)" +msgstr "Ingekort - Selectie volledig tonen (standaard)" + +#: prefs.js:1147 +msgid "Always Expanded" +msgstr "Altijd volledig tonen" + +#: prefs.js:1148 +msgid "Hidden - Show Selected Only" +msgstr "Verborgen - Alleen selectie tonen" + +#: prefs.js:1154 +msgid "Reset App Grid Layout" +msgstr "Standaardindeling herstellen" + +#: prefs.js:1155 +msgid "" +"Removes all stored app grid icons positions, after the reset icons will be " +"ordered alphabetically." +msgstr "Wis alle pictogramlocaties en sorteer alle pictogrammen op alfabet." + +#: prefs.js:1163 +msgid "Remove App Grid Folders" +msgstr "Mappen verwijderen" + +#: prefs.js:1164 +msgid "Removes all folders, folder apps move to root grid." +msgstr "Verwijder alle mappen en verplaats toepassingen naar het hoofdrooster." + +#: prefs.js:1186 +msgid "Enable Window Search Provider" +msgstr "Vensters doorzoeken" + +#: prefs.js:1187 +msgid "" +"Activates the window search provider that adds open windows to the search " +"results. You can search app names and window titles. You can also use \"wq/" +"\" prefix to suppress results from other search providers." +msgstr "" +"Schakel de dienst om geopende vensters te zoeken in. U kunt zoeken op " +"toepassingsnamen en venstertitels. Ook kunt u ‘wq/’ voorafgaand aan een " +"zoekopdracht toevoegen om resultaten van andere diensten te verbergen." + +#: prefs.js:1195 +msgid "Enable Recent Files Search Provider" +msgstr "Onlangs gebruikte bestanden doorzoeken" + +#: prefs.js:1196 +msgid "" +"Activates the recent files search provider that can be triggered by a dash " +"icon, Ctrl + Space hotkey or by typing \"fq//\" prefix in the search entry " +"field. This option needs File History option enabled in the GNOME Privacy " +"settings." +msgstr "" +"Schakel de dienst in om onlangs gebruikte bestanden te doorzoeken. Dit kan " +"met behulp van een dashpictogram, Ctrl+spatiebalk of door ‘fq//’ voorafgaand " +"aan een zoekopdracht in te vullen. Let op: voor deze optie is " +"bestandsgeschiedenis vereist. Die optie vindt u in de GNOME-" +"privacyvoorkeuren." + +#: prefs.js:1204 +msgid "Enable Fuzzy Match" +msgstr "Onduidelijke overeenkomsten inschakelen" + +#: prefs.js:1205 +msgid "" +"Enabling the fuzzy match allows you to skip letters in the pattern you are " +"searching for and find \"Firefox\" even if you type \"ffx\". Works only for " +"the App, Window and Recent files search providers." +msgstr "" +"Schakel onduidelijke overeenkomsten in om letters over te slaan. Voorbeeld: " +"‘Firefox’ wordt gevonden als u ‘ffx’ typt. Let op: dit werkt alleen in " +"combinatie met de diensten toepassingen, vensters en onlangs gebruikte " +"bestanden." + +#: prefs.js:1223 +msgid "Animations - General" +msgstr "Animaties - Algemeen" + +#: prefs.js:1238 +msgid "Animation Speed" +msgstr "Animatiesnelheid" + +#: prefs.js:1239 +msgid "" +"Adjusts the global animation speed in % of the default duration - higher " +"value means slower animation." +msgstr "" +"Pas de algemene animatiesnelheid aan (in %). Hogere waarde = langzamere " +"animatie." + +#: prefs.js:1247 +msgid "Animations - Overview" +msgstr "Animaties - Overzicht" + +#: prefs.js:1253 +msgid "App Grid Animation" +msgstr "Animatie van toepassingsrooster" + +#: prefs.js:1254 +msgid "" +"When entering the App Grid view, the app grid animates from the edge of the " +"screen. You can choose direction, keep it Default (direction will be chosen " +"automatically) or disable the animation if you don't like it." +msgstr "" +"Toon een animatie vanaf de zijkant na het openen van het toepassingsrooster. " +"U kunt aangeven vanuit welke richting de animatie dient te komen (standaard: " +"automatisch) of de animatie in zijn geheel uitschakelen." + +#: prefs.js:1261 prefs.js:1279 +msgid "Right to Left" +msgstr "Van rechts naar links" + +#: prefs.js:1262 prefs.js:1280 +msgid "Left to Right" +msgstr "Van links naar rechts" + +#: prefs.js:1263 prefs.js:1281 +msgid "Bottom to Top" +msgstr "Van boven naar onder" + +#: prefs.js:1264 prefs.js:1282 +msgid "Top to Bottom" +msgstr "Van onder naar boven" + +#: prefs.js:1271 +msgid "Search View Animation" +msgstr "Animatie van zoekweergave" + +#: prefs.js:1272 +msgid "" +"When search is activated the search view with search results can animate " +"from the edge of the screen. You can choose direction, keep it Default " +"(currently Bottom to Top) or disable the animation if you don't like it." +msgstr "" +"Toon een animatie vanaf de zijkant na het openen van de zoekweergave. U kunt " +"aangeven vanuit welke richting de animatie dient te komen (standaard: van " +"onder naar boven) of de animatie in zijn geheel uitschakelen." + +#: prefs.js:1290 +msgid "Workspace Preview Animation" +msgstr "Animatie van werkbladminiaturen" + +#: prefs.js:1291 +msgid "" +"When entering / leaving the App Grid / Search view, the workspace preview " +"can animate to/from workspace thumbnail." +msgstr "" +"Toon een animatie na het openen en sluiten van het toepassingsrooster en de " +"zoekweergave." + +#: prefs.js:1297 +msgid "Enable" +msgstr "Inschakelen" + +#: prefs.js:1305 +msgid "Workspace Switcher" +msgstr "Werkbladwisselaar" + +#: prefs.js:1311 +msgid "Workspace Switcher Animation" +msgstr "Animatie van werkbladwisselaar" + +#: prefs.js:1312 +msgid "" +"Allows you to disable movement of the desktop background during workspace " +"switcher animation outside of the overview. The Static Background mode also " +"keeps Conky and desktop icons on their place during switching." +msgstr "" +"Hiermee kunt u de animatie van de bureaubladachtergrond op de " +"werkbladwisselaar buiten het overzicht uitschakelen. De statische modus laat " +"Conky en pictogrammen op hun plaats staan tijdens het wisselen." + +#: prefs.js:1318 +msgid "Static Background" +msgstr "Statische achtergrond" + +#: prefs.js:1325 +msgid "Workspace Switcher Popup Mode" +msgstr "Werkbladwisselaarpop-upmodus" + +#: prefs.js:1326 +msgid "" +"This popup shows up when you switch workspace using a keyboard shortcut or " +"gesture outside of the overview. You can to disable the popup at all, or " +"show it on the current monitor (the one with mouse pointer) instead of the " +"primary." +msgstr "" +"Deze pop-up wordt getoond als u van werkblad wisselt met behulp van een " +"sneltoets of gebaar buiten het overzicht. U kunt de pop-up in zijn geheel " +"uitschakelen of alleen op het huidige beeldscherm (het scherm met de " +"aanwijzer) tonen." + +#: prefs.js:1332 +msgid "Show on Primary Monitor (Default)" +msgstr "Tonen op hoofdscherm (standaard)" + +#: prefs.js:1333 +msgid "Show on Current Monitor" +msgstr "Tonen op huidig beeldscherm" + +#: prefs.js:1341 +msgid "Notifications" +msgstr "Meldingen" + +#: prefs.js:1347 +msgid "Notification Banner Position" +msgstr "Meldingslocatie" + +#: prefs.js:1348 +msgid "Choose where the notification pop-ups appear on the screen." +msgstr "Geef aan op welke locatie meldingen dienen te worden getoond." + +#: prefs.js:1353 +msgid "Top Left" +msgstr "Linksboven" + +#: prefs.js:1354 +msgid "Top Middle" +msgstr "Linksmidden" + +#: prefs.js:1355 +msgid "Top Right (Default)" +msgstr "Rechtsboven (standaard)" + +#: prefs.js:1356 +msgid "Bottom Left" +msgstr "Linksonder" + +#: prefs.js:1357 +msgid "Bottom Middle" +msgstr "Middenonder" + +#: prefs.js:1358 +msgid "Bottom Right" +msgstr "Rechtsonder" + +#: prefs.js:1365 +msgid "Window Attention Handler" +msgstr "Vensterfocusafhandeling" + +#: prefs.js:1366 +msgid "" +"When a window requires attention (often a new window), GNOME Shell shows you " +"a notification about it. You can disable popups of these messages " +"(notification will be pushed into the message tray silently) or focus the " +"source window immediately instead." +msgstr "" +"Als een venster om focus vraagt (doorgaans een nieuw venster), dan toont " +"GNOME Shell een melding. U kunt deze meldingen uitschakelen om ze direct " +"naar het berichtenvak te sturen of het venster direct focussen." + +#: prefs.js:1371 prefs.js:1386 +msgid "Show Notifications (Default)" +msgstr "Meldingen tonen (standaard)" + +#: prefs.js:1372 +msgid "Disable Notification Popups" +msgstr "Meldingen uitschakelen" + +#: prefs.js:1373 +msgid "Immediately Focus Window" +msgstr "Venster direct focussen" + +#: prefs.js:1380 +msgid "Favorites" +msgstr "Favorieten" + +#: prefs.js:1381 +msgid "Disable pin/unpin app notifications." +msgstr "Schakel meldingen omtrent favorieten uit." + +#: prefs.js:1387 +msgid "Disable Notifications" +msgstr "Meldingen uitschakelen" + +#: prefs.js:1402 +msgid "Keyboard" +msgstr "Toetsenbord" + +#: prefs.js:1408 +msgid "Override Page Up/Down Shortcuts" +msgstr "Page Up-/Down-sneltoetsgedrag aanpassen" + +#: prefs.js:1409 +msgid "" +"This option automatically overrides the (Shift +) Super + Page Up/Down " +"keyboard shortcuts for the current workspace orientation. If you encounter " +"any issues, check the configuration in the dconf editor." +msgstr "" +"Met deze optie kunt u het gedrag van de sneltoetsen (Shift +) Super + Page " +"Up/Down binnen de huidige werkbladoriëntatie aanpassen. Let op: als u " +"problemen ervaart, bekijk dan de configuratie in dconf-bewerker." + +#: prefs.js:1417 +msgid "Compatibility" +msgstr "Compatibiliteit" + +#: prefs.js:1423 +msgid "Fix for Dash to Dock" +msgstr "Dash-to-Dock-oplossing" + +#: prefs.js:1424 +msgid "" +"With the default Ubuntu Dock and other Dash To Dock forks, you may " +"experience issues with Activities overview after you change Dock position or " +"change monitors configuration. If you are experiencing such issues, try to " +"enable this option, or (better) disable/replace the dock extension." +msgstr "" +"Met het standaard Ubuntu-dock en andere Dash-to-Dock-varianten kunt u " +"problemen met het activiteitenoverzicht ervaren als u de docklocatie of " +"beeldschermvoorkeuren aanpast. In dat geval kunt u deze optie proberen of de " +"dockuitbreiding in kwestie uit te schakelen/te vervangen." + +#: prefs.js:1432 +msgid "" +"V-Shell Modules that can be disabled in case of conflict or misbehavior." +msgstr "" +"V-Shellmodules die kunnen worden uitgeschakeld bij problemen of conflicten." + +#: prefs.js:1438 +msgid "AppFavorites" +msgstr "Favoriete toepassingen" + +#: prefs.js:1439 +msgid "Pin/unpin app notification options." +msgstr "Opties omtrent favorietenmeldingen." + +#: prefs.js:1447 +msgid "AppDisplay / IconGrid" +msgstr "Toepassingsweergave/-rooster" + +#: prefs.js:1448 +msgid "App grid customization and options." +msgstr "Opties omtrent het aanpassen van het toepassingsrooster." + +#: prefs.js:1457 +msgid "Dash configuration options and support for vertical orientation." +msgstr "Dashopties en ondersteuning voor verticale oriëntatie." + +#: prefs.js:1466 +msgid "" +"Hot corner options, removes right panel barrier that collides with CHC-E " +"extension." +msgstr "" +"Snelhoekopties. Verwijder de barrière van de rechterbalk die botst met de " +"CHC-E-uitbreiding." + +#: prefs.js:1474 +msgid "MessageTray" +msgstr "Berichtenvak" + +#: prefs.js:1475 +msgid "Notification position options." +msgstr "Opties omtrent meldingslocaties." + +#: prefs.js:1484 +msgid "Panel options." +msgstr "Bovenbalkopties." + +#: prefs.js:1493 +msgid "Search view and app search provider customization and options." +msgstr "Zoekweergave- en zoekdienstopties." + +#: prefs.js:1501 +msgid "SwipeTracker" +msgstr "Gebaar-volgsysteem" + +#: prefs.js:1502 +msgid "Gestures for vertical workspace orientation." +msgstr "Gebaren in de verticale werkbladoriëntatie." + +#: prefs.js:1510 +msgid "WindowAttentionHandler" +msgstr "Vensterfocusafhandeling" + +#: prefs.js:1511 +msgid "Window attention handler options." +msgstr "Vensterfocusafhandelingsopties." + +#: prefs.js:1519 +msgid "WindowManager" +msgstr "Vensterbeheer" + +#: prefs.js:1520 +msgid "" +"Fixes an upstream bug in the minimization animation of a full-screen window." +msgstr "" +"Lost een bug omtrent de minimaliseeranimatie van schermvullende vensters op." + +#: prefs.js:1528 +msgid "WindowPreview" +msgstr "Venstervoorvertoning" + +#: prefs.js:1529 +msgid "" +"Window preview options, fixes an upstream bug that fills the system log with " +"errors when you close a window from an overview or exit the overview with a " +"gesture when any window is selected." +msgstr "" +"Venstervoorvertoningsopties. Lost een bug op omtrent het vastleggen van " +"foutmeldingen in het systeemlogboek na het sluiten van een venster vanuit " +"het overzicht of sluiten van het overzicht met een gebaar." + +#: prefs.js:1537 +msgid "Workspace" +msgstr "Werkblad" + +#: prefs.js:1538 +msgid "" +"Fixes workspace preview allocations for vertical workspaces orientation and " +"window scaling in static overview modes." +msgstr "" +"Lost een bug omtrent het toewijzen van werkbladvoorvertoningen in verticale " +"oriëntatie op, alsmede vensterschaling in de statische modus." + +#: prefs.js:1546 +msgid "WorkspaceAnimation" +msgstr "Werkbladanimatie" + +#: prefs.js:1547 +msgid "Static workspace animation option." +msgstr "Statische werkbladanimatie." + +#: prefs.js:1555 +msgid "WorkspaceSwitcherPopup" +msgstr "Werkbladwisselaarpop-up" + +#: prefs.js:1556 +msgid "Workspace switcher popup position options." +msgstr "Opties omtrent de werkbladwisselaarpop-up." + +#: prefs.js:1573 +msgid "Version" +msgstr "Versie" + +#: prefs.js:1579 +msgid "Reset all options" +msgstr "Standaardwaarden herstellen" + +#: prefs.js:1580 +msgid "Set all options to default values." +msgstr "Herstel alle standaardvoorkeuren." + +#: prefs.js:1586 +msgid "Links" +msgstr "Links" + +#: prefs.js:1590 +msgid "Homepage" +msgstr "Website" + +#: prefs.js:1591 +msgid "Source code and more info about this extension" +msgstr "Broncode en meer informatie over deze uitbreiding" + +#: prefs.js:1596 +msgid "Changelog" +msgstr "Wijzigingslog" + +#: prefs.js:1597 +msgid "See what's changed." +msgstr "Bekijk alle wijzigingen." + +#: prefs.js:1602 +msgid "GNOME Extensions" +msgstr "GNOME-uitbreidingen" + +#: prefs.js:1603 +msgid "Rate and comment the extension on GNOME Extensions site." +msgstr "Beoordeel deze uitbreiding op de GNOME-uitbreidingensite." + +#: prefs.js:1608 +msgid "Report a bug or suggest new feature" +msgstr "Bug melden of idee delen" + +#: prefs.js:1614 +msgid "Buy Me a Coffee" +msgstr "Trakteer me op koffie" + +#: prefs.js:1615 +msgid "If you like this extension, you can help me with my coffee expenses." +msgstr "" +"Als u deze uitbreiding graag gebruikt, dan kunt u me trakteren op een kopje " +"koffie." + +#: recentFilesSearchProvider.js:109 +msgid "Search recent files" +msgstr "Onlangs gebruikte bestanden doorzoeken" + +#: recentFilesSearchProvider.js:110 +msgid "Recent Files" +msgstr "Onlangs gebruikte bestanden" diff --git a/extensions/44/vertical-workspaces/prefs.js b/extensions/44/vertical-workspaces/prefs.js index 6cea321..b757b91 100644 --- a/extensions/44/vertical-workspaces/prefs.js +++ b/extensions/44/vertical-workspaces/prefs.js @@ -9,115 +9,142 @@ 'use strict'; -const { Gtk, GLib } = imports.gi; +const Gtk = imports.gi.Gtk; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; +const MyExtension = ExtensionUtils.getCurrentExtension(); +const OptionsFactory = MyExtension.imports.lib.optionsFactory; +const GObject = imports.gi.GObject; -const ItemFactory = Me.imports.lib.optionsFactory.ItemFactory; -const AdwPrefs = Me.imports.lib.optionsFactory.AdwPrefs; -const LegacyPrefs = Me.imports.lib.optionsFactory.LegacyPrefs; +let Me; +let _; +let opt; -const shellVersion = Settings.shellVersion; - -// gettext -const _ = Settings._; - -// libadwaita is available starting with GNOME Shell 42. -let Adw = null; -try { - Adw = imports.gi.Adw; -} catch (e) {} +function init() { + Me = {}; -let gOptions; -let pageList; -let itemFactory; + Me.shellVersion = parseFloat(imports.misc.config.PACKAGE_VERSION); + Me.imports = MyExtension.imports; + Me.metadata = MyExtension.metadata; + Me.gSettings = ExtensionUtils.getSettings(Me.metadata['settings-schema']); + Me.Settings = MyExtension.imports.lib.settings; + Me.gettext = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; -function init() { - ExtensionUtils.initTranslations(Me.metadata['gettext-domain']); - gOptions = new Settings.Options(); + Me.opt = new Me.Settings.Options(Me); + _ = Me.gettext; + opt = Me.opt; - itemFactory = new ItemFactory(gOptions); + OptionsFactory.init(Me); +} - pageList = [ +function _getPageList() { + const itemFactory = new OptionsFactory.ItemFactory(); + const pageList = [ + { + name: 'profiles', + title: _('Profiles'), + iconName: 'open-menu-symbolic', + optionList: _getProfilesOptionList(itemFactory), + }, { name: 'layout', title: _('Layout'), iconName: 'view-grid-symbolic', - optionList: _getLayoutOptionList(), + optionList: _getLayoutOptionList(itemFactory), }, { name: 'appearance', title: _('Appearance'), iconName: 'view-reveal-symbolic', - optionList: _getAppearanceOptionList(), + optionList: _getAppearanceOptionList(itemFactory), }, { name: 'behavior', title: _('Behavior'), iconName: 'system-run-symbolic', - optionList: _getBehaviorOptionList(), + optionList: _getBehaviorOptionList(itemFactory), + }, + { + name: 'modules', + title: _('Modules'), + iconName: 'application-x-addon-symbolic', + optionList: _getModulesOptionList(itemFactory), }, { name: 'misc', title: _('Misc'), iconName: 'preferences-other-symbolic', - optionList: _getMiscOptionList(), - }, - { - name: 'profiles', - title: _('Profiles'), - iconName: 'open-menu-symbolic', - optionList: _getProfilesOptionList(), + optionList: _getMiscOptionList(itemFactory), }, { name: 'about', title: _('About'), iconName: 'preferences-system-details-symbolic', - optionList: _getAboutOptionList(), + optionList: _getAboutOptionList(itemFactory), }, ]; + + return pageList; } function fillPreferencesWindow(window) { - window = new AdwPrefs(gOptions).getFilledWindow(window, pageList); + window = new OptionsFactory.AdwPrefs(opt).getFilledWindow(window, _getPageList()); window.connect('close-request', () => { - gOptions.destroy(); - gOptions = null; - itemFactory = null; - pageList = null; + opt.destroy(); + opt = null; + Me = null; + _ = null; }); window.set_default_size(800, 800); } -function buildPrefsWidget() { - const prefsWidget = new LegacyPrefs(gOptions).getPrefsWidget(pageList); - - prefsWidget.connect('realize', widget => { - const window = widget.get_root ? widget.get_root() : widget.get_toplevel(); - const width = 800; - const height = 800; - window.set_default_size(width, height); - const headerbar = window.get_titlebar(); - headerbar.title_widget = prefsWidget._stackSwitcher; - - const signal = Gtk.get_major_version() === 3 ? 'destroy' : 'close-request'; - window.connect(signal, () => { - gOptions.destroy(); - gOptions = null; - }); - }); +// //////////////////////////////////////////////////////////////////// +function _getProfilesOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + + optionList.push( + itemFactory.getRowWidget( + _('Custom Profiles'), + _('Sets of settings that can help you with the initial customization') + ) + ); + + optionList.push(itemFactory.getRowWidget( + _('Profile 1'), + null, + itemFactory.newPresetButton(opt, 1) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 2'), + null, + itemFactory.newPresetButton(opt, 2) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 3'), + null, + itemFactory.newPresetButton(opt, 3) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 4'), + null, + itemFactory.newPresetButton(opt, 4) + )); - return prefsWidget; + return optionList; } -// //////////////////////////////////////////////////////////////////// -function _getLayoutOptionList() { +function _getLayoutOptionList(itemFactory) { const optionList = []; // options item format: - // [text, caption, widget, settings-variable, options for combo] + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) optionList.push( itemFactory.getRowWidget( @@ -129,8 +156,8 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Dash Position'), null, - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'dashPosition', [ [_('Top'), 0], @@ -138,7 +165,8 @@ function _getLayoutOptionList() { [_('Bottom'), 2], [_('Left'), 3], [_('Hide'), 4], - ] + ], + 'dashModule' ) ); @@ -147,7 +175,9 @@ function _getLayoutOptionList() { _('Center Horizontal Dash to Workspace'), _('If the Dash Position is set to Top or Bottom, the position will be recalculated relative to the workspace preview instead of the screen'), itemFactory.newSwitch(), - 'centerDashToWs' + 'centerDashToWs', + null, + 'dashModule' ) ); @@ -165,7 +195,73 @@ function _getLayoutOptionList() { _('Fine Tune Dash Position'), _('Adjusts the position of the dash on the axis given by the orientation of the workspaces'), dashPositionScale, - 'dashPositionAdjust' + 'dashPositionAdjust', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Apps Icon Position'), + _('Sets the position of the "Show Applications" icon in the Dash'), + // // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'showAppsIconPosition', + [ + [_('Hide'), 2], + [_('Start'), 0], + [_('End'), 1], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Open Windows Icon Position'), + _('Allows to add "Search Open Windows" icon into Dash (if window search provider enabled on the Modules tab) so you can directly toggle window search provider results. You can also use the secondary mouse button click on the Show Apps Icon, or the Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowWindowsIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Recent Files Icon Position'), + _('Allows to add "Search Recent Files" icon into Dash (if recent files search provider enabled on the Modules tab) so you can directly toggle recent files search provider results. You can also use Ctrl + Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowRecentFilesIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Extensions Icon Position'), + _('Allows to add "Search Extensions" icon into Dash (if extensions search provider enabled on the Module tab) so you can directly toggle extensions search provider results. You can also use the Ctrl + Shift + Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowExtensionsIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' ) ); @@ -180,17 +276,17 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Thumbnails Position / Workspaces Orientation'), _('Position of the workspace thumbnails on the screen also sets orientation of the workspaces to vertical or horizontal. You have two options to disable workspace thumbnails, one sets workspaces to vertical orientation, the second one to horizontal.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'workspaceThumbnailsPosition', // this mess is just because of backward compatibility [ - [_('Left \t Vertical Orientation'), 0], - [_('Right \t Vertical Orientation'), 1], - [_('Hide \t Set Vertical Orientation'), 4], - [_('Top \t Horizontal Orientation'), 5], + [_('Left \t Vertical Orientation'), 0], + [_('Right \t Vertical Orientation'), 1], + [_('Hide \t Vertical Orientation'), 4], + [_('Top \t Horizontal Orientation'), 5], [_('Bottom \t Horizontal Orientation'), 6], - [_('Hide \t Set Horizontal Orientation'), 9], + [_('Hide \t Horizontal Orientation'), 9], ] ) ); @@ -233,7 +329,7 @@ function _getLayoutOptionList() { wsThumbnailScale.add_mark(13, Gtk.PositionType.TOP, null); optionList.push( itemFactory.getRowWidget( - _('Workspace Thumbnails Max Scale'), + _('Workspace Thumbnails Max Scale - Window Picker'), _('Adjusts maximum size of the workspace thumbnails in the overview (% relative to display width)'), wsThumbnailScale, 'wsThumbnailScale' @@ -248,11 +344,11 @@ function _getLayoutOptionList() { }); const wsThumbnailAppScale = itemFactory.newScale(wsThumbnailAppScaleAdjustment); - wsThumbnailAppScale.add_mark(0, Gtk.PositionType.TOP, null); + wsThumbnailAppScale.add_mark(13, Gtk.PositionType.TOP, null); optionList.push( itemFactory.getRowWidget( _('Workspace Thumbnails Max Scale - App View'), - _('Set to 0 to follow "Workspace Thumbnails Max Scale" scale. Allows you to set different thumbnails scale for the Applications view'), + _('Allows you to set different thumbnails scale for the Applications view'), wsThumbnailAppScale, 'wsThumbnailScaleAppGrid' ) @@ -330,7 +426,9 @@ function _getLayoutOptionList() { _('App Grid Page Width Scale'), _('Adjusts max app grid page width relative to the available space.'), agPageWidthScale, - 'appGridPageWidthScale' + 'appGridPageWidthScale', + null, + 'appDisplayModule' ) ); @@ -372,7 +470,9 @@ function _getLayoutOptionList() { _('Search Results Width'), _('Adjusts maximum width of search results view (% relative to default). This allows to fit more (or less) app icons into the app search result'), searchViewScale, - 'searchViewScale' + 'searchViewScale', + null, + 'searchModule' ) ); @@ -386,13 +486,14 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Main Panel Position'), _('Allows to place the main panel at the bottom of the primary display'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'panelPosition', [ [_('Top (Default)'), 0], [_('Bottom'), 1], - ] + ], + 'panelModule' ) ); @@ -400,15 +501,16 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Main Panel Visibility'), _('Allows to hide main panel when not needed'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'panelVisibility', [ [_('Always Visible (Default)'), 0], [_('Overview Only'), 1], [_('Always Hidden'), 2], // [_('Desktop View Only'), 3], - ] + ], + 'panelModule' ) ); @@ -434,7 +536,9 @@ function _getLayoutOptionList() { _('Horizontal Position (% from left)'), _('This popup shows up when you switch workspace using a keyboard shortcut or gesture outside of the overview. You can disable it on the "Behavior" tab. If you want more control over the popup, try the "Workspace Switcher Manager" extension'), hScale, - 'wsSwPopupHPosition' + 'wsSwPopupHPosition', + null, + 'workspaceSwitcherPopupModule' ) ); @@ -453,7 +557,9 @@ function _getLayoutOptionList() { _('Vertical Position (% from top)'), null, vScale, - 'wsSwPopupVPosition' + 'wsSwPopupVPosition', + null, + 'workspaceSwitcherPopupModule' ) ); @@ -467,17 +573,18 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Notification Banner Position'), _('Choose where the notification banners appear on the screen'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'notificationPosition', [ [_('Top Left'), 0], - [_('Top Center'), 1], - [_('Top Right (Default)'), 2], + [_('Top Center (Default)'), 1], + [_('Top Right'), 2], [_('Bottom Left'), 3], [_('Bottom Center'), 4], [_('Bottom Right'), 5], - ] + ], + 'messageTrayModule' ) ); @@ -485,8 +592,8 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('OSD Popup Position'), _('Choose where the OSD pop-ups (like sound volume level) appear on the screen'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'osdPosition', [ [_('Disable'), 0], @@ -497,7 +604,8 @@ function _getLayoutOptionList() { [_('Bottom Left'), 5], [_('Bottom Center (Default)'), 6], [_('Bottom Right'), 7], - ] + ], + 'osdWindowModule' ) ); @@ -511,8 +619,8 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Workspace Thumbnails Position'), _('Allows to place workspace thumbnails of secondary monitors on the opposite side than on the primary monitor'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'secWsThumbnailsPosition', [ [_('Same as Primary'), 2], @@ -543,7 +651,7 @@ function _getLayoutOptionList() { const secWsThumbnailScaleAdjustment = new Gtk.Adjustment({ upper: 30, - lower: 5, + lower: 0, step_increment: 1, page_increment: 1, }); @@ -591,10 +699,10 @@ function _getLayoutOptionList() { return optionList; } -function _getAppearanceOptionList() { +function _getAppearanceOptionList(itemFactory) { const optionList = []; // options item format: - // [text, caption, widget, settings-variable, options for combo] + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) // ---------------------------------------------------------------- optionList.push( @@ -607,8 +715,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Dash Max Icon Size'), _('Maximum size of Dash icons in pixels'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'dashMaxIconSize', [ [_('128'), 128], @@ -618,54 +726,23 @@ function _getAppearanceOptionList() { [_('64'), 64], [_('48'), 48], [_('32'), 32], - [_('24'), 24], - [_('16'), 16], - ] - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('Show Apps Icon Position'), - _('Sets the position of the "Show Applications" icon in the Dash'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), - 'showAppsIconPosition', - [ - [_('Hide'), 2], - [_('Start'), 0], - [_('End'), 1], - ] - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('Open Windows Icon Position'), - _('Allows to add "Search Open Windows" icon into Dash (if window search provider enabled on the Behavior tab) so you can directly toggle window search provider results. You can also use the secondary mouse button click on the Show Apps Icon, or the Space hotkey'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), - 'dashShowWindowsIcon', - [ - [_('Hide'), 0], - [_('Start'), 1], - [_('End'), 2], - ] + ], + 'dashModule' ) ); optionList.push( itemFactory.getRowWidget( - _('Recent Files Icon Position'), - _('Allows to add "Search Recent Files" icon into Dash (if recent files search provider enabled on the Behavior tab) so you can directly toggle recent files search provider results. You can also use Ctrl + Space hotkey'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), - 'dashShowRecentFilesIcon', + _('Dash Background Style'), + _('Allows you to change the background color of the dash to match the search results an app folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashBgColor', [ - [_('Hide'), 0], - [_('Start'), 1], - [_('End'), 2], - ] + [_('Default'), 0], + [_('Light'), 1], + ], + 'dashModule' ) ); @@ -682,7 +759,9 @@ function _getAppearanceOptionList() { _('Dash Background Opacity'), _('Adjusts the opacity of the Dash background'), dashBgOpacityScale, - 'dashBgOpacity' + 'dashBgOpacity', + null, + 'dashModule' ) ); @@ -699,7 +778,35 @@ function _getAppearanceOptionList() { _('Dash Background Radius'), _('Adjusts the border radius of the Dash background in pixels. 0 means the default value given by the current theme style'), dashBgRadiusScale, - 'dashBgRadius' + 'dashBgRadius', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Dash Background GNOME 3 Style'), + _('Background of the vertically oriented dash will imitate the GNOME 3 style'), + itemFactory.newSwitch(), + 'dashBgGS3Style', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Running App Indicator'), + _('Allows you to change style of the running app indicator under the app icon'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'runningDotStyle', + [ + [_('Dot (Default)'), 0], + [_('Line'), 1], + ], + 'dashModule' ) ); @@ -713,8 +820,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Show Workspace Thumbnail Labels'), _('Each workspace thumbnail can show label with its index and name (if defined in the system settings) or name/title of its most recently used app/window'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'showWsTmbLabels', [ [_('Disable'), 0], @@ -754,8 +861,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Window Preview App Icon Size'), null, - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'winPreviewIconSize', [ [_('64 (Default)'), 0], @@ -763,16 +870,35 @@ function _getAppearanceOptionList() { [_('32'), 2], [_('22'), 3], [_('Disable'), 4], - ] + ], + 'windowPreviewModule' ) ); optionList.push( itemFactory.getRowWidget( - _('Always Show Window Titles'), - _('All windows on the workspace preview will still show their titles, not only the one with the mouse pointer'), + _('Window Title Position / Visibility'), + _('Sets the position of the window title that is displayed when the mouse hovers over the window or can always be visible'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winTitlePosition', + [ + [_('Inside Window'), 0], + [_('Inside Window Always Visible'), 1], + [_('Below Window (Default)'), 2], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Close Window Button'), + _('Allows you to hide close window button'), itemFactory.newSwitch(), - 'alwaysShowWinTitles' + 'winPreviewShowCloseButton', + null, + 'windowPreviewModule' ) ); @@ -820,8 +946,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Icon Size'), _('Allows to set a fixed app grid icon size and bypass the default adaptive algorithm'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridIconSize', [ [_('Adaptive (Default)'), -1], @@ -839,7 +965,8 @@ function _getAppearanceOptionList() { [_('64'), 64], [_('48'), 48], // [_('32'), 32], - ] + ], + 'appDisplayModule' ) ); @@ -847,8 +974,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Folder Icon Size'), _('Allows to set a fixed icon size and bypass the default adaptive algorithm in the open folder dialog'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridFolderIconSize', [ [_('Adaptive (Default)'), -1], @@ -859,23 +986,24 @@ function _getAppearanceOptionList() { [_('64'), 64], [_('48'), 48], [_('32'), 32], - ] + ], + 'appDisplayModule' ) ); - const folderIconGridCombo = itemFactory.newComboBox(); + const folderIconGridCombo = itemFactory.newDropDown(); optionList.push( itemFactory.getRowWidget( _('Max App Folder Icon Grid Size'), _('Sets a grid size (number of icons) in the folder preview. 3x3 options automatically switches between 2x2 and 3x3 grid depending on the number of icons in the folder'), folderIconGridCombo, - // itemFactory.newDropDown(), 'appGridFolderIconGrid', [ [_('2x2 (Default)'), 2], [_('3x3 for 5+ apps'), 3], [_('3x3 for 9+ apps'), 4], - ] + ], + 'appDisplayModule' ) ); @@ -891,7 +1019,9 @@ function _getAppearanceOptionList() { _('Columns per Page (0 for adaptive grid)'), _('Number of columns in the application grid. If set to 0 (the default), the number will be set automatically to fit the available width'), columnsSpinBtn, - 'appGridColumns' + 'appGridColumns', + null, + 'appDisplayModule' )); const rowsAdjustment = new Gtk.Adjustment({ @@ -906,11 +1036,13 @@ function _getAppearanceOptionList() { _('Rows per Page (0 for adaptive grid)'), _('Number of rows in the application grid. If set to 0 (the default), the number will be set automatically to fit the available height'), rowsSpinBtn, - 'appGridRows' + 'appGridRows', + null, + 'appDisplayModule' )); const folderColumnsAdjustment = new Gtk.Adjustment({ - upper: 8, + upper: 15, lower: 0, step_increment: 1, page_increment: 1, @@ -921,11 +1053,13 @@ function _getAppearanceOptionList() { _('Folder Columns per Page (0 for adaptive grid)'), _('Number of columns in folder grid. If you leave the value at 0, the number of columns will be calculated to fit all the folder icons on one page'), folderColumnsSpinBtn, - 'appGridFolderColumns' + 'appGridFolderColumns', + null, + 'appDisplayModule' )); const folderRowsAdjustment = new Gtk.Adjustment({ - upper: 8, + upper: 15, lower: 0, step_increment: 1, page_increment: 1, @@ -936,7 +1070,9 @@ function _getAppearanceOptionList() { _('Folder Rows per Page (0 for adaptive grid)'), _('Number of rows in folder grid. If you leave the value at 0, the number of rows will be calculated to fit all the folder icons on one page'), folderRowsSpinBtn, - 'appGridFolderRows' + 'appGridFolderRows', + null, + 'appDisplayModule' )); const appGridSpacingAdjustment = new Gtk.Adjustment({ @@ -951,9 +1087,11 @@ function _getAppearanceOptionList() { optionList.push( itemFactory.getRowWidget( _('Grid Spacing'), - _('Adjusts spacing between icons.'), + _('Adjusts the spacing between icons in a grid, the real impact is on folders'), appGridSpacingScale, - 'appGridSpacing' + 'appGridSpacing', + null, + 'appDisplayModule' ) ); @@ -967,8 +1105,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('App Search Icon Size'), _('Size of results provided by the App Search Provider - smaller size allows to fit more results'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'searchIconSize', [ [_('128'), 128], @@ -978,7 +1116,8 @@ function _getAppearanceOptionList() { [_('64'), 64], [_('48'), 48], [_('32'), 32], - ] + ], + 'searchModule' ) ); @@ -999,7 +1138,9 @@ function _getAppearanceOptionList() { _('Max Search Results Rows'), _('Sets the maximum number of rows for result lists of all search providers except the window search provider which always lists all results'), maxSearchResultsSpinButton, - 'searchMaxResultsRows' + 'searchMaxResultsRows', + null, + 'searchModule' ) ); @@ -1036,6 +1177,23 @@ function _getAppearanceOptionList() { ) ); + const searchBrightnessBgAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 0, + step_increment: 1, + page_increment: 10, + }); + + const searchBgBrightnessScale = itemFactory.newScale(searchBrightnessBgAdjustment); + optionList.push( + itemFactory.getRowWidget( + _('Brightness for Search View'), + _('Allows you to set a lower background brightness for search view mode where text visibility is more important'), + searchBgBrightnessScale, + 'searchBgBrightness' + ) + ); + const blurBgAdjustment = new Gtk.Adjustment({ upper: 100, lower: 0, @@ -1079,11 +1237,36 @@ function _getAppearanceOptionList() { ) ); + + optionList.push( + itemFactory.getRowWidget( + _('Window Thumbnails (PIP)') + ) + ); + + const winTmbAdjustment = new Gtk.Adjustment({ + upper: 50, + lower: 5, + step_increment: 1, + page_increment: 1, + }); + + const winTmbScale = itemFactory.newScale(winTmbAdjustment); + winTmbScale.add_mark(15, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Default Window Thumbnail Scale (% of screen height)'), + _('Default scale of window thumbnail (like Picture In Picture) that you can create using the app icon menu or window preview action'), + winTmbScale, + 'windowThumbnailScale' + ) + ); + return optionList; } // ---------------------------------------------------------------- -function _getBehaviorOptionList() { +function _getBehaviorOptionList(itemFactory) { const optionList = []; optionList.push( @@ -1096,8 +1279,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Overview Mode'), _('The Expose Windows on Hover mode does not expose the workspace preview windows until the mouse pointer enters any window\nThe Static Workspace mode keeps the workspace static when you activate the overview, it only shows Dash, workspace thumbnails and search entry over the workspace and only clicking on an active workspace thumbnail activates the default overview'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'overviewMode', [ [_('Default'), 0], @@ -1111,8 +1294,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Startup State'), _('Allows to change the state in which GNOME Shell starts a session'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'startupState', [ [_('Overview (Default)'), 0], @@ -1124,23 +1307,61 @@ function _getBehaviorOptionList() { optionList.push( itemFactory.getRowWidget( + _('Escape Key Behavior'), + _('Allows you to close the overview with a single press of the Escape key, even from the application grid or from search, if the search entry field does not have focus'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'overviewEscBehavior', + [ + [_('Default'), 0], + [_('Close Overview'), 1], + ], + 'searchControllerModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( _('Overlay Key (Super/Windows)') ) ); optionList.push( itemFactory.getRowWidget( + _('Single-Press Action'), + _('Disable or change behavior when you press and release the Super key. The "Search Windows" options requires the WindowSearchProvider module to be activated'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'overlayKeyPrimary', + [ + [_('Disable'), 0], + [_('Follow Global Overview Mode'), 1], + [_('Overview (Default)'), 2], + [_('Applications'), 3], + [_('Overview - Static WS Preview'), 4], + [_('Overview - Static Workspace'), 5], + [_('Search Windows'), 6], + // [_('Search Recent Files'), 7], + ], + 'overlayKeyModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( _('Double-Press Action'), - _('Disable or change behavior when you double-press the Super key. The "Search" options require the respective search provider to be activated'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + _('Disable or change behavior when you double-press the Super key. The "Search Windows" option requires the WindowSearchProvider module to be activated. The "Static WS Overview - Expose Windows" option allows you to switch to default Activities Overview window picker view if you set static workspace (preview) for the single press/release Super key action'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'overlayKeySecondary', [ [_('Disable'), 0], [_('Applications (Default)'), 1], [_('Search Windows'), 2], - [_('Search Recent Files'), 3], - ] + [_('Overview - Window Picker'), 3], + // [_('Search Recent Files'), 4], + ], + 'overlayKeyModule' ) ); @@ -1154,24 +1375,28 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Hot Corner Action'), _('Disable or change behavior of the hot corner. Holding down the Ctrl key while hitting the hot corner switches between Overview/Applications actions'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'hotCornerAction', [ [_('Disable'), 0], - [_('Overview'), 1], - [_('Applications'), 2], - [_('Search Windows'), 3], - ] + [_('Follow Global Overview Mode'), 1], + [_('Overview - Window Picker'), 2], + [_('Applications'), 3], + [_('Overview - Static WS Preview'), 4], + [_('Overview - Static Workspace'), 5], + [_('Search Windows'), 6], + ], + 'layoutModule' ) ); optionList.push( itemFactory.getRowWidget( _('Hot Corner Position'), - _('Choose which corner of your monitors will be active. If you choose "Follow Dash" option, the corner will be placed near the left or top edge of the Dash. The last option extends the hot corner barrier to cover the entire ege of the monitor where Dash is located'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + _('Choose which corner of your monitors will be active. If you choose "Follow Dash" option, the corner will be placed near the left or top edge of the Dash. The last option extends the hot corner trigger to cover the entire ege of the monitor where Dash is located'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'hotCornerPosition', [ [_('Default'), 0], @@ -1181,7 +1406,8 @@ function _getBehaviorOptionList() { [_('Bottom Right'), 4], [_('Follow Dash'), 5], [_('Follow Dash - Hot Edge'), 6], - ] + ], + 'layoutModule' ) ); @@ -1190,8 +1416,9 @@ function _getBehaviorOptionList() { _('Enable Hot Corner in Full-Screen Mode'), _('If you often work with full-screen applications and want the hot corner to be usable'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'hotCornerFullscreen' + 'hotCornerFullscreen', + null, + 'layoutModule' ) ); @@ -1200,8 +1427,9 @@ function _getBehaviorOptionList() { _('Show Ripples Animation'), _('The ripple animation is played when the hot corner is activated. The ripple size has been reduced to be less distracting'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'hotCornerRipples' + 'hotCornerRipples', + null, + 'layoutModule' ) ); @@ -1213,16 +1441,29 @@ function _getBehaviorOptionList() { optionList.push( itemFactory.getRowWidget( + _('Isolate Workspaces'), + _('Dash will only show apps and windows from the current workspace'), + itemFactory.newSwitch(), + 'dashIsolateWorkspaces', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( _('App Icon - Click Behavior'), - _('if the app you clicked on has more than one window and the recently used window is not on the current workspace, the overview can switch to the workspace with the recent window'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + _('Choose your preferred behavior when clicking on an app icon. The "Prefer Current Workspace" option opens a new app window if not present in the current workspace. The "Open New Window" option also switches behavior of the middle click to "Activate" since its default behavior is to open a new window'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'dashShowWindowsBeforeActivation', [ [_('Activate App Immediately'), 0], [_('First Switch to Workspace'), 1], [_('Open New Window (if supported)'), 2], - ] + [_('Prefer Current Workspace'), 3], + ], + 'dashModule' ) ); @@ -1230,14 +1471,15 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('App Icon - Scroll Action'), _('Choose the behavior when scrolling over an app icon. The window cycler works with a list of windows sorted by the "Most Recently Used" and grouped by workspaces. Scrolling up cycles through previously used windows on the same workspace and then switches to another workspace, if any'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'dashIconScroll', [ [_('Default'), 0], [_('Cycle App Windows - Highlight Selected'), 1], [_('Cycle App Windows - Highlight App'), 2], - ] + ], + 'dashModule' ) ); @@ -1245,14 +1487,15 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Search Windows Icon - Scroll Action'), _('Choose the behavior when scrolling over the Search Windows icon. The window cycler works with a list of windows sorted by "Most Recently Used" of the current workspace or all workspaces. Scrolling up cycles through previously used windows on the same workspace, or all windows regardless workspace. This option is mainly useful for the static workspace overview mode.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'searchWindowsIconScroll', [ [_('Default'), 0], [_('Cycle All Windows'), 1], [_('Cycle Windows On Current WS'), 2], - ] + ], + 'dashModule' ) ); @@ -1266,8 +1509,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Close Workspace Button'), _('The Close Workspace button appears on the workspace thumbnail when you hover over it and allows you to close all windows on the workspace. You can choose a "safety lock" to prevent accidental use'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'closeWsButtonMode', [ [_('Disable'), 0], @@ -1286,23 +1529,64 @@ function _getBehaviorOptionList() { optionList.push( itemFactory.getRowWidget( - _('Always Activate Selected'), - _('If enabled, the currently selected window will be activated when leaving the Overview even without clicking. Usage example - press Super to open the Overview, place mouse pointer over a window, press Super again to activate the window'), - itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'alwaysActivateSelectedWindow' + _('Secondary Button Click Action'), + _('Allows you to add a secondary mouse click action to the window preview'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winPreviewSecBtnAction', + [ + [_('Activate Window (Default)'), 0], + [_('Close Window'), 1], + [_('Search For Same App Windows'), 2], + [_('Create Window Thumbnail - PIP'), 3], + ], + 'windowPreviewModule' ) ); - /* optionList.push( + optionList.push( + itemFactory.getRowWidget( + _('Middle Button Click Action'), + _('Allows you to add a middle mouse click action to the window preview'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winPreviewMidBtnAction', + [ + [_('Activate Window (Default)'), 0], + [_('Close Window'), 1], + [_('Search For Same App Windows'), 2], + [_('Create Window Thumbnail - PIP'), 3], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Icon Click Action'), + _('Select the action to take when the application icon on the window preview is clicked'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'windowIconClickAction', + [ + [_('Activate Window (Default)'), 0], + [_('Search For Same App Windows'), 1], + [_('Create Window Thumbnail - PIP'), 2], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( itemFactory.getRowWidget( - _('App Icon Activates Window Search'), - _('If enabled, clicking a window preview icon will activate a search view with the application name as the search term, so you can list all app windows from all workspaces and filter them by typing.'), + _('Always Activate Selected'), + _('If enabled, the currently selected window will be activated when leaving the Overview even without clicking. Usage example - press Super to open the Overview, place mouse pointer over a window, press Super again to activate the window'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'windowIconClickSearch' + 'alwaysActivateSelectedWindow', + null, + 'windowPreviewModule' ) - );*/ + ); optionList.push( itemFactory.getRowWidget( @@ -1312,16 +1596,34 @@ function _getBehaviorOptionList() { optionList.push( itemFactory.getRowWidget( - _('Apps Order'), - _('Choose sorting method for the app grid. Note that sorting by alphabet and usage ignores folders'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + _('App Grid Order'), + _('Choose sorting method for the app grid. Note that sorting by usage ignores folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridOrder', [ [_('Custom (Default)'), 0], + [_('Alphabet - Folders First'), 1], + [_('Alphabet - Folders Last'), 2], + [_('Usage - No Folders'), 3], + ], + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Folder Order'), + _('Choose sorting method for app folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appFolderOrder', + [ + [_('Custom (Default)'), 0], [_('Alphabet'), 1], [_('Usage'), 2], - ] + ], + 'appDisplayModule' ) ); @@ -1329,8 +1631,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('App Grid Content'), _('The default Shell removes favorite apps, this option allows to duplicate them in the grid or remove also running applications. Option "Favorites and Running First" only works with the Alphabet and Usage sorting'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridContent', [ [_('Include All'), 0], @@ -1338,7 +1640,8 @@ function _getBehaviorOptionList() { [_('Exclude Favorites (Default)'), 2], [_('Exclude Running'), 3], [_('Exclude Favorites and Running'), 4], - ] + ], + 'appDisplayModule' ) ); @@ -1347,8 +1650,9 @@ function _getBehaviorOptionList() { _('Active Icons in Folder Preview'), _('If enabled, icons in the folder review behaves like normal icons, you can activate or even drag them directly, without having to open the folder first'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'appGridActivePreview' + 'appGridActivePreview', + null, + 'appDisplayModule' ) ); @@ -1357,8 +1661,9 @@ function _getBehaviorOptionList() { _('Center Open Folders'), _('App folder may open in the center of the screen or above the source folder icon'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'appGridFolderCenter' + 'appGridFolderCenter', + null, + 'appDisplayModule' ) ); @@ -1367,8 +1672,9 @@ function _getBehaviorOptionList() { _('Allow Incomplete Pages'), _('If disabled, icons from the next page (if any) are automatically moved to fill any empty slot left after an icon was (re)moved (to a folder for example)'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'appGridIncompletePages' + 'appGridIncompletePages', + null, + 'appDisplayModule' ) ); @@ -1376,14 +1682,15 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('App Labels Behavior'), _('Choose how and when to display app names'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridNamesMode', [ [_('Ellipsized - Expand Selected (Default)'), 0], [_('Always Expanded'), 1], [_('Hidden - Show Selected Only'), 2], - ] + ], + 'appDisplayModule' ) ); @@ -1391,7 +1698,7 @@ function _getBehaviorOptionList() { _('Reset App Grid Layout'), _('Removes all stored app grid positions, after reset icons will be sorted alphabetically, except folder contents'), itemFactory.newResetButton(() => { - const settings = ExtensionUtils.getSettings('org.gnome.shell'); + const settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); settings.set_value('app-picker-layout', new GLib.Variant('aa{sv}', [])); }) )); @@ -1400,7 +1707,7 @@ function _getBehaviorOptionList() { _('Remove App Grid Folders'), _('Removes all folders, folder apps will move to the root grid'), itemFactory.newResetButton(() => { - const settings = ExtensionUtils.getSettings('org.gnome.desktop.app-folders'); + const settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' }); settings.set_strv('folder-children', []); }) )); @@ -1418,21 +1725,29 @@ function _getBehaviorOptionList() { ) );*/ - optionList.push( + /* optionList.push( itemFactory.getRowWidget( _('Enable Window Search Provider'), _('Activates the window search provider that adds open windows to the search results. You can search app names and window titles. You can also use "wq//" prefix (also by pressing the Space hotkey in the overview, or clicking dash icon) to suppress results from other search providers'), itemFactory.newSwitch(), 'searchWindowsEnable' ) - ); + );*/ optionList.push( itemFactory.getRowWidget( - _('Enable Recent Files Search Provider'), - _('Activates the recent files search provider that can be triggered by a dash icon, Ctrl + Space hotkey or by typing "fq//" prefix in the search entry field. This option needs File History option enabled in the GNOME Privacy settings'), - itemFactory.newSwitch(), - 'searchRecentFilesEnable' + _('Window Search Provider - Sorting'), + _('Choose the window sorting method'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'searchWindowsOrder', + [ + [_('Most Recently Used (MRU)'), 0], + [_('MRU - Current Workspace First'), 1], + [_('MRU - By Workspaces'), 2], + [_('Stable Sequence - By Workspaces'), 3], + ], + 'windowSearchProviderModule' ) ); @@ -1489,8 +1804,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('App Grid Animation'), _('When entering the App Grid view, the app grid animates from the edge of the screen. You can choose the direction, keep the Default (direction will be selected automatically) or disable the animation if you don\'t like it'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridAnimation', [ [_('Default'), 4], @@ -1507,8 +1822,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Search View Animation'), _('When search is activated the search view with search results can animate from the edge of the screen. You can choose the direction, keep the Default (currently Bottom to Top) or disable the animation if you don\'t like it.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'searchViewAnimation', [ [_('Default'), 4], @@ -1526,8 +1841,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Workspace Preview Animation'), _('When entering / leaving the App Grid / Search view, the workspace preview can animate to/from workspace thumbnail.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'workspaceAnimation', [ [_('Disable'), 0], @@ -1545,15 +1860,34 @@ function _getBehaviorOptionList() { optionList.push( itemFactory.getRowWidget( + _('Wraparound'), + _('Continue from the last workspace to the first and vice versa'), + itemFactory.newSwitch(), + 'wsSwitcherWraparound' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Ignore Last (empty) Workspace'), + _('In Dynamic workspaces mode, there is always one empty workspace at the end. Switcher can ignore this last workspace'), + itemFactory.newSwitch(), + 'wsSwitcherIgnoreLast' + ) + ); + + optionList.push( + itemFactory.getRowWidget( _('Workspace Switcher Animation'), _('Allows you to disable movement of the desktop background during workspace switcher animation outside of the overview. The Static Background mode also keeps Conky and desktop icons on their place during switching.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'workspaceSwitcherAnimation', [ [_('Default'), 0], [_('Static Background'), 1], - ] + ], + 'workspaceAnimationModule' ) ); @@ -1561,18 +1895,18 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Workspace Switcher Popup Mode'), _('This popup shows up when you switch workspace using a keyboard shortcut or gesture outside of the overview. You can to disable the popup at all, or show it on the current monitor (the one with mouse pointer) instead of the primary.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'wsSwPopupMode', [ [_('Disable'), 0], [_('Show on Primary Monitor (Default)'), 1], [_('Show on Current Monitor'), 2], - ] + ], + 'workspaceSwitcherPopupModule' ) ); - optionList.push( itemFactory.getRowWidget( _('Notifications') @@ -1583,14 +1917,15 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Window Attention Handler'), _('When a window requires attention (often a new window), GNOME Shell shows you a notification about it. You can disable popups of these messages (notification will be pushed into the message tray silently) or focus the source window immediately instead'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'windowAttentionMode', [ [_('Show Notifications (Default)'), 0], [_('Disable Notification Popups'), 1], [_('Immediately Focus Window'), 2], - ] + ], + 'windowAttentionHandlerModule' ) ); @@ -1598,96 +1933,63 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Favorites'), _('Disable pin/unpin app notifications'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'favoritesNotify', [ [_('Show Notifications (Default)'), 1], [_('Disable Notifications'), 0], - ] - ) - ); - - return optionList; -} - -function _getProfilesOptionList() { - const optionList = []; - // options item format: - // [text, caption, widget, settings-variable, options for combo] - - optionList.push( - itemFactory.getRowWidget( - _('Custom Profiles'), - _('Sets of settings that can help you with the initial customization') + ], + 'appFavoritesModule' ) ); - optionList.push(itemFactory.getRowWidget( - _('Profile 1'), - null, - itemFactory.newPresetButton(gOptions, 1) - )); - - optionList.push(itemFactory.getRowWidget( - _('Profile 2'), - null, - itemFactory.newPresetButton(gOptions, 2) - )); - - optionList.push(itemFactory.getRowWidget( - _('Profile 3'), - null, - itemFactory.newPresetButton(gOptions, 3) - )); - - optionList.push(itemFactory.getRowWidget( - _('Profile 4'), - null, - itemFactory.newPresetButton(gOptions, 4) - )); - return optionList; } -function _getMiscOptionList() { +function _getModulesOptionList(itemFactory) { const optionList = []; // options item format: - // [text, caption, widget, settings-variable, options for combo] - + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) optionList.push( itemFactory.getRowWidget( - _('Keyboard') + _('V-Shell Modules (allows you to disable modules that conflict with another extension)') ) ); optionList.push( itemFactory.getRowWidget( - _('Override Page Up/Down Shortcuts'), - _('This option automatically overrides the (Shift +) Super + Page Up/Down keyboard shortcuts for the current workspace orientation. If you encounter any issues, check the configuration in the dconf editor'), + _('WindowSearchProvider'), + _('Activates the window search provider that adds open windows to the search results. You can search app names and window titles. You can also use "wq//" prefix (also by pressing the Space hotkey in the overview, or clicking dash icon) to suppress results from other search providers'), itemFactory.newSwitch(), - 'enablePageShortcuts' + 'windowSearchProviderModule' ) ); optionList.push( itemFactory.getRowWidget( - _('Compatibility') + _('RecentFilesSearchProvider'), + _('Activates the recent files search provider that can be triggered by a dash icon, Ctrl + Space hotkey or by typing "fq//" prefix in the search entry field. This option needs File History option enabled in the GNOME Privacy settings'), + itemFactory.newSwitch(), + 'recentFilesSearchProviderModule' ) ); optionList.push( itemFactory.getRowWidget( - _('Fix for Dash to Dock'), - _('With the default Ubuntu Dock and other Dash To Dock forks, you may experience issues with Activities overview after you change Dock position or change monitors configuration. This option is enabled automatically if a replacement for the Dash is detected'), + _('ExtensionsSearchProvider'), + _('Activates the extensions search provider that adds extensions to the search results. You can also use "eq//" prefix (also by pressing the Ctrl + Shift + Space hotkey in the overview, or clicking dash icon) to suppress results from other search providers'), itemFactory.newSwitch(), - 'fixUbuntuDock' + 'extensionsSearchProviderModule' ) ); optionList.push( itemFactory.getRowWidget( - _('V-Shell Modules that can be disabled in case of conflict or misbehavior:') + _('AppDisplay / IconGrid'), + _('App grid customization and options'), + itemFactory.newSwitch(), + 'appDisplayModule' ) ); @@ -1702,15 +2004,6 @@ function _getMiscOptionList() { optionList.push( itemFactory.getRowWidget( - _('AppDisplay / IconGrid'), - _('App grid customization and options'), - itemFactory.newSwitch(), - 'appDisplayModule' - ) - ); - - optionList.push( - itemFactory.getRowWidget( _('Dash'), _('Dash customization and options, support for vertical orientation'), itemFactory.newSwitch(), @@ -1774,6 +2067,15 @@ function _getMiscOptionList() { optionList.push( itemFactory.getRowWidget( + _('SearchController'), + _('Escape key behavior options in the overview'), + itemFactory.newSwitch(), + 'searchControllerModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( _('SwipeTracker'), _('Gestures for vertical workspace orientation'), itemFactory.newSwitch(), @@ -1786,7 +2088,7 @@ function _getMiscOptionList() { _('WindowAttentionHandler'), _('Window attention handler options'), itemFactory.newSwitch(), - 'winAttentionHandlerModule' + 'windowAttentionHandlerModule' ) ); @@ -1810,6 +2112,15 @@ function _getMiscOptionList() { optionList.push( itemFactory.getRowWidget( + _('WindowThumbnail'), + _('Create Window Thumbnail (PIP) option in the app icon menu and window preview actions'), + itemFactory.newSwitch(), + 'windowThumbnailModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( _('Workspace'), _('Fixes workspace preview allocations for vertical workspaces orientation and window scaling in static overview modes'), itemFactory.newSwitch(), @@ -1838,17 +2149,89 @@ function _getMiscOptionList() { return optionList; } -function _getAboutOptionList() { +function _getMiscOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + + optionList.push( + itemFactory.getRowWidget( + _('Keyboard') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Override Page Up/Down Shortcuts'), + _('This option automatically overrides the (Shift +) Super + Page Up/Down keyboard shortcuts for the current workspace orientation. If you encounter any issues, check the configuration in the dconf editor'), + itemFactory.newSwitch(), + 'enablePageShortcuts' + ) + ); + + /* optionList.push( + itemFactory.getRowWidget( + _('Compatibility') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Improve compatibility with Dash to Dock'), + _('With the default Ubuntu Dock and other Dash To Dock forks, you may experience issues with Activities overview after you change Dock position or re-enable the extension. This option is enabled automatically if a replacement for the Dash is detected. In any case, using Dash to Dock extension with V-Shell is problematic and not recommended.'), + itemFactory.newSwitch(), + 'fixUbuntuDock' + ) + );*/ + + optionList.push( + itemFactory.getRowWidget( + _('Performance') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Smooth App Grid Animations'), + _('This option allows V-Shell to pre-realize app grid and app folders during session startup in order to avoid stuttering animations when using them for the first time. If enabled, the session startup needs a little bit more time to finish and necessary memory will be allocated at this time'), + itemFactory.newSwitch(), + 'appGridPerformance' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workarounds') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Fix New Window Not In Focus'), + _('If you often find that the app window you open from the Activities overview does not get focus, try enabling this option.'), + itemFactory.newSwitch(), + 'newWindowFocusFix' + ) + ); + + return optionList; +} + +function _getAboutOptionList(itemFactory) { const optionList = []; optionList.push(itemFactory.getRowWidget( Me.metadata.name )); + const versionName = Me.metadata['version-name'] ?? ''; + let version = Me.metadata['version'] ?? ''; + version = versionName && version ? `/${version}` : version; + const versionStr = `${versionName}${version}`; optionList.push(itemFactory.getRowWidget( _('Version'), null, - itemFactory.newLabel(Me.metadata.version.toString()) + itemFactory.newLabel(versionStr) )); optionList.push(itemFactory.getRowWidget( diff --git a/extensions/44/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml b/extensions/44/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml index 1bb4269..0a2ae51 100644 --- a/extensions/44/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml +++ b/extensions/44/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml @@ -14,7 +14,7 @@ <default>350</default> </key> <key type="i" name="ws-preview-scale"> - <default>100</default> + <default>95</default> </key> <key type="i" name="secondary-ws-preview-scale"> <default>100</default> @@ -23,7 +23,7 @@ <default>false</default> </key> <key type="i" name="dash-position"> - <default>0</default> + <default>2</default> </key> <key type="i" name="dash-position-adjust"> <default>0</default> @@ -37,6 +37,9 @@ <key type="i" name="dash-show-recent-files-icon"> <default>1</default> </key> + <key type="i" name="dash-show-extensions-icon"> + <default>1</default> + </key> <key type="i" name="wst-position-adjust"> <default>0</default> </key> @@ -77,10 +80,19 @@ <default>48</default> </key> <key type="i" name="dash-bg-opacity"> - <default>80</default> + <default>85</default> + </key> + <key type="i" name="dash-bg-color"> + <default>0</default> </key> <key type="i" name="dash-bg-radius"> - <default>17</default> + <default>18</default> + </key> + <key type="b" name="dash-bg-gs3-style"> + <default>true</default> + </key> + <key type="i" name="running-dot-style"> + <default>1</default> </key> <key type="b" name="enable-page-shortcuts"> <default>false</default> @@ -104,7 +116,10 @@ <default>40</default> </key> <key type="i" name="overview-bg-brightness"> - <default>95</default> + <default>60</default> + </key> + <key type="i" name="search-bg-brightness"> + <default>30</default> </key> <key type="b" name="smooth-blur-transitions"> <default>false</default> @@ -113,7 +128,7 @@ <default>4</default> </key> <key type="i" name="search-view-animation"> - <default>4</default> + <default>0</default> </key> <key type="i" name="workspace-animation"> <default>1</default> @@ -124,14 +139,11 @@ <key type="i" name="animation-speed-factor"> <default>100</default> </key> - <key type="b" name="fix-ubuntu-dock"> - <default>true</default> - </key> <key type="i" name="win-preview-icon-size"> <default>1</default> </key> - <key type="b" name="always-show-win-titles"> - <default>true</default> + <key type="i" name="win-title-position"> + <default>0</default> </key> <key type="i" name="startup-state"> <default>2</default> @@ -157,6 +169,9 @@ <key type="i" name="app-grid-order"> <default>0</default> </key> + <key type="i" name="app-folder-order"> + <default>0</default> + </key> <key type="i" name="app-grid-content"> <default>2</default> </key> @@ -176,7 +191,7 @@ <default>true</default> </key> <key type="i" name="app-grid-names"> - <default>0</default> + <default>1</default> </key> <key type="i" name="app-grid-folder-icon-grid"> <default>3</default> @@ -196,14 +211,17 @@ <key type="i" name="dash-icon-scroll"> <default>1</default> </key> + <key type="b" name="dash-isolate-workspaces"> + <default>false</default> + </key> <key type="i" name="search-windows-icon-scroll"> <default>1</default> </key> <key type="b" name="search-windows-enable"> <default>true</default> </key> - <key type="b" name="search-recent-files-enable"> - <default>true</default> + <key type="i" name="search-windows-order"> + <default>1</default> </key> <key type="b" name="search-fuzzy"> <default>false</default> @@ -229,6 +247,12 @@ <key type="i" name="ws-sw-popup-mode"> <default>1</default> </key> + <key type="b" name="ws-switcher-wraparound"> + <default>false</default> + </key> + <key type="b" name="ws-switcher-ignore-last"> + <default>false</default> + </key> <key type="i" name="favorites-notify"> <default>1</default> </key> @@ -242,10 +266,10 @@ <default>1</default> </key> <key type="i" name="hot-corner-position"> - <default>0</default> + <default>6</default> </key> <key type="b" name="hot-corner-fullscreen"> - <default>false</default> + <default>true</default> </key> <key type="b" name="hot-corner-ripples"> <default>true</default> @@ -253,19 +277,37 @@ <key type="b" name="always-activate-selected-window"> <default>false</default> </key> - <key type="b" name="window-icon-click-search"> + <key type="i" name="win-preview-sec-mouse-btn-action"> + <default>2</default> + </key> + <key type="i" name="win-preview-mid-mouse-btn-action"> + <default>0</default> + </key> + <key type="b" name="win-preview-show-close-button"> <default>true</default> </key> + <key type="i" name="window-icon-click-action"> + <default>1</default> + </key> <key type="i" name="overlay-key-primary"> <default>1</default> </key> <key type="i" name="overlay-key-secondary"> <default>1</default> </key> - - <key type="b" name="workspace-thumbnails-module"> + <key type="i" name="overview-esc-behavior"> + <default>0</default> + </key> + <key type="b" name="new-window-focus-fix"> + <default>false</default> + </key> + <key type="b" name="app-grid-performance"> <default>true</default> </key> + <key type="i" name="window-thumbnail-scale"> + <default>15</default> + </key> + <key type="b" name="workspace-switcher-popup-module"> <default>true</default> </key> @@ -284,9 +326,15 @@ <key type="b" name="win-attention-handler-module"> <default>true</default> </key> + <key type="b" name="window-thumbnail-module"> + <default>true</default> + </key> <key type="b" name="swipe-tracker-module"> <default>true</default> </key> + <key type="b" name="search-controller-module"> + <default>true</default> + </key> <key type="b" name="search-module"> <default>true</default> </key> @@ -314,6 +362,15 @@ <key type="b" name="app-favorites-module"> <default>true</default> </key> + <key type="b" name="window-search-provider-module"> + <default>true</default> + </key> + <key type="b" name="recent-files-search-provider-module"> + <default>true</default> + </key> + <key type="b" name="extensions-search-provider-module"> + <default>true</default> + </key> <key type="b" name="aaa-loading-profile"> <default>false</default> @@ -323,28 +380,28 @@ <default>""</default> </key> <key type="a{ss}" name="profile-data-1"> - <default>{'workspaceThumbnailsPosition': '1', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '3', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '-80', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '-80', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '0', 'secWsThumbnailScale': '13', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'appGridPageWidthScale': '100', 'dashBgOpacity': '80', 'dashBgRadius': '17', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'false', 'showBgInOverview': 'true', 'overviewBgBrightness': '30', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '0', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'fixUbuntuDock': 'true', 'winPreviewIconSize': '1', 'alwaysShowWinTitles': 'false', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '4', 'appGridFolderRows': '3', 'appGridSpacing': '12', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appGridNamesMode': '0', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'false', 'searchWindowsEnable': 'true', 'searchRecentFilesEnable': 'true', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '50', 'wsSwPopupMode': '1', 'favoritesNotify': '1', 'notificationPosition': '1', 'hotCornerAction': '1', 'hotCornerPosition': '0', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'workspaceThumbnailsModule': 'true', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'winAttentionHandlerModule': 'true', 'swipeTrackerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true'}</default> + <default>{'workspaceThumbnailsPosition': '1', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '3', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '-80', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '-80', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'dashShowExtensionsIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '13', 'secWsThumbnailScale': '13', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'dashBgOpacity': '80', 'dashBgColor': '0', 'dashBgRadius': '0', 'dashBgGS3Style': 'true', 'runningDotStyle': '1', 'enablePageShortcuts': 'false', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'false', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '30', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '40', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '0', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '100', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '1', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '0', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '3', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'}</default> </key> <key type="s" name="profile-name-2"> <default>""</default> </key> <key type="a{ss}" name="profile-data-2"> - <default>{'workspaceThumbnailsPosition': '5', 'wsMaxSpacing': '80', 'wsPreviewScale': '100', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '0', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '5', 'wsThumbnailScaleAppGrid': '15', 'secWsThumbnailScale': '13', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'appGridPageWidthScale': '80', 'dashBgOpacity': '100', 'dashBgRadius': '17', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'true', 'showBgInOverview': 'false', 'overviewBgBrightness': '30', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '0', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'fixUbuntuDock': 'true', 'winPreviewIconSize': '0', 'alwaysShowWinTitles': 'false', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '0', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '3', 'appGridFolderRows': '3', 'appGridSpacing': '12', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appGridNamesMode': '0', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'false', 'searchWindowsEnable': 'true', 'searchRecentFilesEnable': 'true', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'favoritesNotify': '1', 'notificationPosition': '1', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'workspaceThumbnailsModule': 'true', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'winAttentionHandlerModule': 'true', 'swipeTrackerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true'}</default> + <default>{'workspaceThumbnailsPosition': '5', 'wsMaxSpacing': '80', 'wsPreviewScale': '100', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '0', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'dashShowExtensionsIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '5', 'wsThumbnailScaleAppGrid': '15', 'secWsThumbnailScale': '5', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'dashBgOpacity': '20', 'dashBgColor': '1', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'false', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '50', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '50', 'appGridBgBlurSigma': '40', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '0', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'true', 'appGridPageWidthScale': '90', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '1', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'}</default> </key> <key type="s" name="profile-name-3"> <default>""</default> </key> <key type="a{ss}" name="profile-data-3"> - <default>{'workspaceThumbnailsPosition': '0', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '0', 'dashPositionAdjust': '-100', 'wsTmbPositionAdjust': '-100', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '0', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '0', 'secWsThumbnailScale': '13', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'appGridPageWidthScale': '90', 'dashBgOpacity': '85', 'dashBgRadius': '17', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'false', 'showBgInOverview': 'true', 'overviewBgBrightness': '95', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'fixUbuntuDock': 'true', 'winPreviewIconSize': '1', 'alwaysShowWinTitles': 'true', 'startupState': '2', 'overviewMode': '1', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridSpacing': '12', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appGridNamesMode': '0', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'searchWindowsEnable': 'true', 'searchRecentFilesEnable': 'true', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '2', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'favoritesNotify': '0', 'notificationPosition': '2', 'hotCornerAction': '1', 'hotCornerPosition': '1', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'workspaceThumbnailsModule': 'true', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'winAttentionHandlerModule': 'true', 'swipeTrackerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true'}</default> + <default>{'workspaceThumbnailsPosition': '0', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '0', 'dashPositionAdjust': '-100', 'wsTmbPositionAdjust': '-100', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'dashShowExtensionsIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '0', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '13', 'secWsThumbnailScale': '13', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'dashBgOpacity': '20', 'dashBgColor': '1', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'false', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '60', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '30', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '2', 'overviewMode': '1', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '90', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '0', 'notificationPosition': '2', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '1', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'}</default> </key> <key type="s" name="profile-name-4"> <default>""</default> </key> <key type="a{ss}" name="profile-data-4"> - <default>{'workspaceThumbnailsPosition': '6', 'wsMaxSpacing': '65', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '10', 'wsThumbnailScaleAppGrid': '0', 'secWsThumbnailScale': '10', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'appGridPageWidthScale': '90', 'dashBgOpacity': '85', 'dashBgRadius': '17', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'true', 'showBgInOverview': 'true', 'overviewBgBrightness': '100', 'overviewBgBlurSigma': '80', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'fixUbuntuDock': 'true', 'winPreviewIconSize': '2', 'alwaysShowWinTitles': 'true', 'startupState': '2', 'overviewMode': '2', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridSpacing': '12', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appGridNamesMode': '0', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'searchWindowsEnable': 'true', 'searchRecentFilesEnable': 'true', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '2', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'favoritesNotify': '0', 'notificationPosition': '1', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'workspaceThumbnailsModule': 'true', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'winAttentionHandlerModule': 'true', 'swipeTrackerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true'}</default> + <default>{'workspaceThumbnailsPosition': '6', 'wsMaxSpacing': '65', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'dashShowExtensionsIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '10', 'wsThumbnailScaleAppGrid': '10', 'secWsThumbnailScale': '10', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'dashBgOpacity': '100', 'dashBgColor': '0', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '60', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '80', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '2', 'overviewMode': '2', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '90', 'appGridSpacing': '5', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '0', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'}</default> </key> </schema> diff --git a/extensions/44/vertical-workspaces/stylesheet.css b/extensions/44/vertical-workspaces/stylesheet.css index 178cf1d..2febbb7 100644 --- a/extensions/44/vertical-workspaces/stylesheet.css +++ b/extensions/44/vertical-workspaces/stylesheet.css @@ -5,15 +5,18 @@ /* General dash */ #dash.vertical { - margin-right: 0px; - margin-left: 0px; - padding: 0px 0; + margin: 0px; + padding: 0px; } #dash.vertical .app-well-app, #dash.vertical .show-apps { - padding-right: 8px; - padding-left: 8px; + /* left/right padding exceeds dash bg by 6px to + cover spacing between dash and the edge of the screen + so the icons will be selectable even at the edge + this spacing must be accounted for in overview allocate() */ + padding-right: 16px; + padding-left: 16px; /*spacing between icons*/ padding-top: 1px; padding-bottom: 1px; @@ -35,48 +38,81 @@ #dash.vertical .app-well-app-running-dot { margin: 4px 0px; +} + +#dash.vertical .app-well-app-running-dot-custom { + margin: 4px 0px; width: 2px; height: 16px; } #dash.vertical .dash-background { - margin: 0; + margin: 0px; } -#dash.vertical-left .dash-background { +#dash.vertical-gs3-left .dash-background { border-radius: 0 18px 18px 0; border-left: 0px; padding: 8px 12px 8px 4px; + margin-left: 0; } -#dash.vertical-right .dash-background { +#dash.vertical-gs3-right .dash-background { border-radius: 18px 0 0 18px; border-right: 0px; padding: 8px 4px 8px 12px; + margin-right: 0; } -/* default horizontal dash */ -.v-shell-dash-background { - padding: 6px; +#dash.vertical-gs3-left { + margin-right: 6px; + margin-left: 0px; + padding: 0px; } -.app-well-app { - margin-left: 1px; - margin-right: 1px; +#dash.vertical-gs3-right { + margin-right: 0px; + margin-left: 6px; + padding: 0px; +} + +#dash.vertical-gs3-left .app-well-app, +#dash.vertical-gs3-left .show-apps, +#dash.vertical-gs3-right .app-well-app, +#dash.vertical-gs3-right .show-apps { + /* left/right padding exceeds dash bg by 6px to + cover spacing between dash and the edge of the screen + so the icons will be selectable even at the edge + this spacing must be accounted for in overview allocate() */ + padding-right: 9px; + padding-left: 9px; +} + +.dash-background-light { + background-color: rgb(200, 200, 200); + border-color: rgba(150, 150, 150, 0.4); +} + +.dash-background-reduced { + padding: 10px; } .app-well-app-running-dot { - margin-bottom: 6px; + margin-bottom: 12px; +} + +.app-well-app-running-dot-custom { + margin-bottom: 12px; width: 16px; height: 2px; } /* add shadow to the app grid app label to be readable if it overlaps light icon below */ -.overview-icon-with-label { - text-shadow: 1px 1px 3px rgba(33, 33, 33, 1); +.overview-icon-with-label, .folder-name-label { + text-shadow: 1px 1px 3px rgba(33, 33, 33, 0.5); } -/*adjustment for the vertical ws switcher indicator popup*/ +/* adjustment for the vertical ws switcher indicator popup*/ .ws-switcher-indicator { padding: 3px; margin: 5px; @@ -87,11 +123,11 @@ margin: 3px; } -/*ws thumbnails captions*/ +/* ws thumbnails captions */ .ws-tmb-label { padding: 2px; color: rgb(255, 255, 255); - background-color: rgba(10,10,10,0.8); + background-color: rgba(40,40,40,0.8); text-align: center; } @@ -103,7 +139,12 @@ border: 0px; } -/*app grid page indicatos*/ +.ws-tmb-transparent { + border: 0px; + background-color: rgba(200, 200, 200, 0.2); +} + +/* app grid page indicatos */ .page-indicator-icon { margin: 10px 10px 10px 10px; } @@ -128,32 +169,47 @@ border-radius: 0px 0px 100px 100px; } - -.search-entry, .search-entry-om2 { - padding: 5px; +/* +.search-entry{ + background-color: rgba(200, 200, 200, 0.1); + color: white; + border-color: rgba(160, 160, 160, 0.4); } +.search-entry:hover, +.search-entry:focus { + background-color: rgba(200, 200, 200, 0.2); +}*/ + .search-entry-om2 { - border: 5px; + color: white; + background-color: rgba(40, 40, 40, 1); } -.search-section-content { - background-color: rgba(20, 20, 20, 0.8); - color: white; +.search-entry-om2:hover, +.search-entry-om2:focus { + background-color: rgba(50, 50, 50, 1); } /* for static ws mode */ -.search-section-content-om2 { - background-color: rgba(20, 20, 20, 0.95); - color: white; - border-radius: 20px; - border: 1px, rgb(40, 40, 40); - padding-top: 12px; +/*.search-section-content-bg,*/ +.search-section-content-bg-om2 { + border-radius: 26px; + border: 1px, rgb(60, 60, 60); + padding-top: 15px; +} + +.search-section-content-bg-om2 { + background-color: rgb(40, 40, 40); +} + +.search-section-content { + background-color: rgba(200, 200, 200, 0.1); } /* "no results" / "searching..." text*/ -.search-statustext { - background-color: rgba(23,23,23, 0.95); +.search-statustext, .search-statustext-om2 { + background-color: rgba(200, 200, 200, 0.1); color: white; margin-top: 50px; padding: 30px; @@ -161,17 +217,18 @@ text-shadow: 0px 0px 5px rgb(23, 23, 23); } -.transparent-panel { - background-color: transparent; +.search-statustext-om2 { + background-color: rgb(40, 40, 40); + border: 1px rgba(200, 200, 200, 0.1); } -/*reduce spacing between app icons in search results*/ -.grid-search-results { - spacing: 2px; +#panel:overview, .transparent-panel { + background-color: transparent; } -.panel { - text-shadow: 1px 1px 3px rgba(33, 33, 33, 1); +/* reduce spacing between app icons in search results */ +.grid-search-results { + spacing: 4px; } /* hide vertical scroll bar, it's distracting in the search results */ @@ -222,3 +279,29 @@ StButton#vhandle { .osd-window { margin: 4em; } + +.app-folder .overview-icon, +.edit-folder-button, +.folder-name-entry { + background-color: rgba(200, 200, 200, 0.08); +} + +.app-folder-dialog-vshell { + background-color: rgba(200, 200, 200, 0.08); +} + +.app-folder-dialog { + border-color: rgba(160, 160, 160, 0.3); +} + +.edit-folder-button:hover, +.app-folder:hover .overview-icon, +.app-folder:focus .overview-icon { + background-color: rgba(200, 200, 200, 0.15); +} + +/* reduce quick menu buttons height in GS44 */ +/*.quick-toggle, +.quick-menu-toggle { + min-height: 42px; +}*/ |