summaryrefslogtreecommitdiffstats
path: root/extensions/44/vertical-workspaces/lib/dash.js
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/44/vertical-workspaces/lib/dash.js')
-rw-r--r--extensions/44/vertical-workspaces/lib/dash.js1410
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);
+ });
+ },
+};