diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-08 16:02:53 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-08 16:02:53 +0000 |
commit | 4d8b071804d73b7a733f2b7696fde40caf8800bb (patch) | |
tree | 86aefc1c0ea9c596bab0ecbc0c054a2378d6cd13 /extensions/44/vertical-workspaces/lib/dash.js | |
parent | Updating 44/no-overview to version 44 [68db01d]. (diff) | |
download | gnome-shell-extensions-extra-4d8b071804d73b7a733f2b7696fde40caf8800bb.tar.xz gnome-shell-extensions-extra-4d8b071804d73b7a733f2b7696fde40caf8800bb.zip |
Updating 44/vertical-workspaces to version 37+20231208 [0d82192].
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'extensions/44/vertical-workspaces/lib/dash.js')
-rw-r--r-- | extensions/44/vertical-workspaces/lib/dash.js | 1410 |
1 files changed, 865 insertions, 545 deletions
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); + }); + }, +}; |