diff options
Diffstat (limited to '')
-rw-r--r-- | extensions/vertical-workspaces/lib/dash.js (renamed from extensions/vertical-workspaces/dash.js) | 565 |
1 files changed, 461 insertions, 104 deletions
diff --git a/extensions/vertical-workspaces/dash.js b/extensions/vertical-workspaces/lib/dash.js index c4e3101..bf832bd 100644 --- a/extensions/vertical-workspaces/dash.js +++ b/extensions/vertical-workspaces/lib/dash.js @@ -1,5 +1,5 @@ /** - * Vertical Workspaces + * V-Shell (Vertical Workspaces) * dash.js * * @author GdH <G-dH@github.com> @@ -8,7 +8,7 @@ * modified dash module of https://github.com/RensAlthuis/vertical-overview extension */ -const { Clutter, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi; +const { Clutter, GObject, St, Shell, Meta } = imports.gi; const AppDisplay = imports.ui.appDisplay; const AppFavorites = imports.ui.appFavorites; const DND = imports.ui.dnd; @@ -16,42 +16,54 @@ const IconGrid = imports.ui.iconGrid; const Main = imports.ui.main; const Overview = imports.ui.overview; const Dash = imports.ui.dash; -const { DashIcon, DashItemContainer, getAppFromSource, DragPlaceholderItem } = imports.ui.dash; +const PopupMenu = imports.ui.popupMenu; +const { AppMenu } = imports.ui.appMenu; +const BoxPointer = imports.ui.boxpointer; +const AltTab = imports.ui.altTab; const Me = imports.misc.extensionUtils.getCurrentExtension(); -const Util = Me.imports.util; -const _ = Me.imports.settings._; +const Util = Me.imports.lib.util; +const _ = Me.imports.lib.settings._; -let verticalOverrides = {}; +const shellVersion = Util.shellVersion; let _origWorkId; let _newWorkId; let _showAppsIconBtnPressId; -// added values to achieve better ability to scale down according the available space +// added values to achieve a better ability to scale down according to available space var BaseIconSizes = [16, 24, 32, 40, 44, 48, 56, 64, 72, 80, 96, 112, 128]; -const RecentFilesSearchProviderPrefix = Me.imports.recentFilesSearchProvider.prefix; -const WindowSearchProviderPrefix = Me.imports.windowSearchProvider.prefix; +const RecentFilesSearchProviderPrefix = Me.imports.lib.recentFilesSearchProvider.prefix; +const WindowSearchProviderPrefix = Me.imports.lib.windowSearchProvider.prefix; let _overrides; +let opt; +let _firstRun = true; const DASH_ITEM_LABEL_SHOW_TIME = 150; -let opt; - function update(reset = false) { - if (_overrides) { + opt = Me.imports.lib.settings.opt; + const moduleEnabled = opt.get('dashModule', true); + reset = reset || !moduleEnabled; + + // don't even touch this module if disabled + if (_firstRun && reset) + return; + + _firstRun = false; + + if (_overrides) _overrides.removeAll(); - } - - opt = Me.imports.settings.opt; + + const dash = Main.overview._overview._controls.layoutManager._dash; setToHorizontal(); - dash.remove_style_class_name("vertical-overview"); - dash.remove_style_class_name("vertical-overview-left"); - dash.remove_style_class_name("vertical-overview-right"); + dash.remove_style_class_name('vertical'); + dash.remove_style_class_name('vertical-left'); + dash.remove_style_class_name('vertical-right'); if (reset) { _moveDashAppGridIcon(reset); @@ -60,6 +72,7 @@ function update(reset = false) { _updateRecentFilesIcon(false); dash.visible = true; dash._background.opacity = 255; + dash._background.remove_style_class_name('v-shell-dash-background'); _overrides = null; opt = null; return; @@ -67,13 +80,15 @@ function update(reset = false) { _overrides = new Util.Overrides(); - _overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerOverride); - _overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommonOverride); + _overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerCommon); + _overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommon); + _overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIconCommon); + _overrides.addOverride('DashIcon', Dash.DashIcon.prototype, DashIconCommon); if (opt.DASH_VERTICAL) { _overrides.addOverride('Dash', Dash.Dash.prototype, DashOverride); setToVertical(); - dash.add_style_class_name("vertical-overview"); + dash.add_style_class_name('vertical'); if (!_newWorkId) { _origWorkId = dash._workId; @@ -93,14 +108,15 @@ function update(reset = false) { _moveDashAppGridIcon(); _connectShowAppsIcon(); - if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) { + if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) dash._showWindowsIconClickedId = dash._showWindowsIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(WindowSearchProviderPrefix)); - } - if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) { + + if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) dash._recentFilesIconClickedId = dash._recentFilesIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(RecentFilesSearchProviderPrefix)); - } - Main.overview.dash._redisplay(); - Main.overview._overview._controls.layoutManager._dash.visible = opt.DASH_VISIBLE; + + dash.visible = opt.DASH_VISIBLE; + dash._background.add_style_class_name('v-shell-dash-background'); + dash._redisplay(); } function setToVertical() { @@ -128,13 +144,13 @@ function setToVertical() { dash._queueRedisplay(); dash._adjustIconSize(); - dash.add_style_class_name(opt.DASH_LEFT ? 'vertical-overview-left' : 'vertical-overview-right'); + dash.add_style_class_name(opt.DASH_LEFT ? 'vertical-left' : 'vertical-right'); } function setToHorizontal() { let dash = Main.overview._overview._controls.layoutManager._dash; if (_origWorkId) - dash._workId = _origWorkId; //pretty sure this is a leak, but there no provided way to disconnect these... + dash._workId = _origWorkId; // pretty sure this is a leak, but there no provided way to disconnect these... dash._box.layout_manager.orientation = Clutter.Orientation.HORIZONTAL; dash._dashContainer.layout_manager.orientation = Clutter.Orientation.HORIZONTAL; dash._dashContainer.y_expand = true; @@ -173,7 +189,7 @@ function _moveDashAppGridIcon(reset = false) { const index = dash._dashContainer.get_children().length - 1; dash._dashContainer.set_child_at_index(dash._showAppsIcon, index); } - if (!reset && appIconPosition === 2) {// 2 - hide + if (!reset && appIconPosition === 2) { // 2 - hide const style = opt.DASH_VERTICAL ? 'show-apps-icon-vertical-hide' : 'show-apps-icon-horizontal-hide'; dash._showAppsIcon.add_style_class_name(style); // for some reason even if the icon height in vertical mode should be set to 0 by the style, it stays visible in full size returning height 1px @@ -191,29 +207,27 @@ function _connectShowAppsIcon(reset = false) { Main.overview.dash._showAppsIcon.reactive = true; _showAppsIconBtnPressId = Main.overview.dash._showAppsIcon.connect('button-press-event', (actor, event) => { const button = event.get_button(); - if (button === Clutter.BUTTON_MIDDLE) { + if (button === Clutter.BUTTON_MIDDLE) Util.openPreferences(); - } else if (button === Clutter.BUTTON_SECONDARY) { + else if (button === Clutter.BUTTON_SECONDARY) Util.activateSearchProvider(WindowSearchProviderPrefix); - } else { + else return Clutter.EVENT_PROPAGATE; - } + return Clutter.EVENT_STOP; }); - } else { - if (_showAppsIconBtnPressId) { - Main.overview.dash._showAppsIcon.disconnect(_showAppsIconBtnPressId); - _showAppsIconBtnPressId = 0; - Main.overview.dash._showAppsIcon.reactive = false; - } + } else if (_showAppsIconBtnPressId) { + Main.overview.dash._showAppsIcon.disconnect(_showAppsIconBtnPressId); + _showAppsIconBtnPressId = 0; + Main.overview.dash._showAppsIcon.reactive = false; } } -var DashOverride = { - handleDragOver: function (source, actor, _x, y, _time) { - let app = getAppFromSource(source); +const DashOverride = { + handleDragOver(source, actor, _x, y, _time) { + let app = Dash.getAppFromSource(source); // Don't allow favoriting of transient apps - if (app == null || app.is_window_backed()) + if (app === null || app.is_window_backed()) return DND.DragMotionResult.NO_DROP; if (!global.settings.is_writable('favorite-apps')) @@ -257,7 +271,7 @@ var DashOverride = { this._dragPlaceholderPos = pos; // Don't allow positioning before or after self - if (favPos != -1 && (pos == favPos || pos == favPos + 1)) { + if (favPos !== -1 && (pos === favPos || pos === favPos + 1)) { this._clearDragPlaceholder(); return DND.DragMotionResult.CONTINUE; } @@ -273,7 +287,7 @@ var DashOverride = { fadeIn = true; } - this._dragPlaceholder = new DragPlaceholderItem(); + this._dragPlaceholder = new Dash.DragPlaceholderItem(); this._dragPlaceholder.child.set_width(this.iconSize / 2); this._dragPlaceholder.child.set_height(this.iconSize); this._box.insert_child_at_index(this._dragPlaceholder, @@ -284,7 +298,7 @@ var DashOverride = { if (!this._dragPlaceholder) return DND.DragMotionResult.NO_DROP; - let srcIsFavorite = favPos != -1; + let srcIsFavorite = favPos !== -1; if (srcIsFavorite) return DND.DragMotionResult.MOVE_DROP; @@ -292,7 +306,7 @@ var DashOverride = { return DND.DragMotionResult.COPY_DROP; }, - _redisplay: function () { + _redisplay() { let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); let running = this._appSystem.get_running(); @@ -343,7 +357,7 @@ var DashOverride = { let newApp = newApps.length > newIndex ? newApps[newIndex] : null; // No change at oldIndex/newIndex - if (oldApp == newApp) { + if (oldApp === newApp) { oldIndex++; newIndex++; continue; @@ -361,7 +375,7 @@ var DashOverride = { addedItems.push({ app: newApp, item: this._createAppItem(newApp), - pos: newIndex + pos: newIndex, }); newIndex++; continue; @@ -370,10 +384,10 @@ var DashOverride = { // App moved let nextApp = newApps.length > newIndex + 1 ? newApps[newIndex + 1] : null; - let insertHere = nextApp && nextApp == oldApp; + let insertHere = nextApp && nextApp === oldApp; let alreadyRemoved = removedActors.reduce((result, actor) => { let removedApp = actor.child._delegate.app; - return result || removedApp == newApp; + return result || removedApp === newApp; }, false); if (insertHere || alreadyRemoved) { @@ -381,7 +395,7 @@ var DashOverride = { addedItems.push({ app: newApp, item: newItem, - pos: newIndex + removedActors.length + pos: newIndex + removedActors.length, }); newIndex++; } else { @@ -439,10 +453,10 @@ var DashOverride = { width: this.iconSize, height: 1, }); - this._box.add_child(this._separator) + this._box.add_child(this._separator); } - //FIXME: separator placement is broken (also in original dash) + // FIXME: separator placement is broken (also in original dash) let pos = nFavorites; if (this._dragPlaceholder) pos++; @@ -456,8 +470,8 @@ var DashOverride = { this._box.queue_relayout(); }, - _createAppItem: function (app) { - let appIcon = new DashIcon(app); + _createAppItem(app) { + let appIcon = new Dash.DashIcon(app); let indicator = appIcon._dot; indicator.x_align = opt.DASH_LEFT ? Clutter.ActorAlign.START : Clutter.ActorAlign.END; @@ -468,7 +482,7 @@ var DashOverride = { this._itemMenuStateChanged(item, opened); }); - let item = new DashItemContainer(); + let item = new Dash.DashItemContainer(); item.setChild(appIcon); // Override default AppIcon label_actor, now the @@ -480,12 +494,12 @@ var DashOverride = { this._hookUpLabel(item, appIcon); return item; - } -} + }, +}; -var DashItemContainerOverride = { +const DashItemContainerCommon = { // move labels according dash position - showLabel: function() { + showLabel() { if (!this._labelText) return; @@ -500,7 +514,7 @@ var DashItemContainerOverride = { const labelWidth = this.label.get_width(); const labelHeight = this.label.get_height(); - const xOffset = Math.floor((itemWidth - labelWidth) / 2); + let xOffset = Math.floor((itemWidth - labelWidth) / 2); let x = Math.clamp(stageX + xOffset, 0, global.stage.width - labelWidth); let node = this.label.get_theme_node(); @@ -509,23 +523,18 @@ var DashItemContainerOverride = { if (opt.DASH_TOP) { const yOffset = itemHeight - labelHeight + 3 * node.get_length('-y-offset'); y = stageY + yOffset; - } else if (opt.DASH_BOTTOM) { const yOffset = node.get_length('-y-offset'); y = stageY - this.label.height - yOffset; - } else if (opt.DASH_RIGHT) { const yOffset = Math.floor((itemHeight - labelHeight) / 2); - - const xOffset = 4; + xOffset = 4; x = stageX - xOffset - this.label.width; y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); - - } if (opt.DASH_LEFT) { + } else if (opt.DASH_LEFT) { const yOffset = Math.floor((itemHeight - labelHeight) / 2); - - const xOffset = 4; + xOffset = 4; x = stageX + this.width + xOffset; y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); @@ -544,11 +553,17 @@ var DashItemContainerOverride = { duration: DASH_ITEM_LABEL_SHOW_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, }); - } -} + }, +}; + +const DashCommon = { + // use custom BaseIconSizes and add support for custom icons + _adjustIconSize() { + // if a user launches multiple apps at once, this function may be called again before the previous call has finished + // as a result, new icons will not reach their full size, or will be missing, if adding a new icon and changing the dash size due to lack of space at the same time + if (this._adjustingInProgress) + return; -var DashCommonOverride = { - _adjustIconSize: function () { // For the icon size, we only consider children which are "proper" // icons (i.e. ignoring drag placeholders) and which are not // animating out (which means they will be destroyed at the end of @@ -560,18 +575,19 @@ var DashCommonOverride = { !actor.animatingOut; }); - // add new custom icons into the calculation - if (this._showAppsIcon.visible) { + // add new custom icons to the list + if (this._showAppsIcon.visible) iconChildren.push(this._showAppsIcon); - } - if (this._showWindowsIcon) { + + if (this._showWindowsIcon) iconChildren.push(this._showWindowsIcon); - } - if (this._recentFilesIcon) { + + if (this._recentFilesIcon) iconChildren.push(this._recentFilesIcon); - } - if (!iconChildren.length) return; + + if (!iconChildren.length) + return; if (this._maxWidth === -1 || this._maxHeight === -1) return; @@ -593,12 +609,14 @@ var DashCommonOverride = { let firstButton = iconChildren[0].child; let firstIcon = firstButton._delegate.icon; - if (!firstIcon.icon) return; + if (!firstIcon.icon) + return; // Enforce valid spacings during the size request firstIcon.icon.ensure_style(); const [, , iconWidth, iconHeight] = firstIcon.icon.get_preferred_size(); const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size(); + let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; let availWidth, availHeight, maxIconSize; if (dashHorizontal) { @@ -614,7 +632,7 @@ var DashCommonOverride = { availHeight -= themeNode.get_vertical_padding(); availHeight -= buttonHeight - iconHeight; - maxIconSize = Math.min(availWidth / iconChildren.length, availHeight, opt.MAX_ICON_SIZE); + maxIconSize = Math.min(availWidth / iconChildren.length, availHeight, opt.MAX_ICON_SIZE * scaleFactor); } else { availWidth = this._maxWidth; availWidth -= this._background.get_theme_node().get_horizontal_padding(); @@ -626,10 +644,9 @@ var DashCommonOverride = { (iconChildren.length - 1) * spacing + 2 * this._background.get_theme_node().get_vertical_padding(); - maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE); + maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE * scaleFactor); } - let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; let iconSizes = BaseIconSizes.map(s => s * scaleFactor); let newIconSize = BaseIconSizes[0]; @@ -638,8 +655,11 @@ var DashCommonOverride = { newIconSize = BaseIconSizes[i]; } - /*if (newIconSize == this.iconSize) - return;*/ + if (newIconSize === this.iconSize) + return; + + // set the in-progress state here after all the possible cancels + this._adjustingInProgress = true; let oldIconSize = this.iconSize; this.iconSize = newIconSize; @@ -665,7 +685,7 @@ var DashCommonOverride = { // Scale the icon's texture to the previous size and // tween to the new size icon.icon.set_size(icon.icon.width * scale, - icon.icon.height * scale); + icon.icon.height * scale); icon.icon.ease({ width: targetWidth, @@ -683,23 +703,351 @@ var DashCommonOverride = { mode: Clutter.AnimationMode.EASE_OUT_QUAD, }); } + + this._adjustingInProgress = false; + }, +}; + +const DashIconCommon = { + after__init() { + if (opt.DASH_ICON_SCROLL) { + this._scrollConId = this.connect('scroll-event', _onScrollEvent.bind(this)); + this._leaveConId = this.connect('leave-event', _onLeaveEvent.bind(this)); + } }, +}; + +function _onScrollEvent(source, event) { + if ((this.app && !opt.DASH_ICON_SCROLL) || (this._isSearchWindowsIcon && !opt.SEARCH_WINDOWS_ICON_SCROLL)) { + if (this._scrollConId) + this.disconnect(this._scrollConId); + if (this._leaveConId) + this.disconnect(this._leaveConId); + return Clutter.EVENT_PROPAGATE; + } + + let direction = Util.getScrollDirection(event); + if (direction === Clutter.ScrollDirection.UP) + direction = 1; + else if (direction === Clutter.ScrollDirection.DOWN) + direction = -1; + else + return Clutter.EVENT_STOP; + + // avoid uncontrollable switching if smooth scroll wheel or trackpad is used + if (this._lastScroll && Date.now() - this._lastScroll < 160) + return Clutter.EVENT_STOP; + + this._lastScroll = Date.now(); + + _switchWindow.bind(this)(direction); + return Clutter.EVENT_STOP; } -function _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON) { +function _onLeaveEvent() { + if (!this._selectedMetaWin || this.has_pointer || this.toggleButton?.has_pointer) + return; + + this._selectedPreview._activateSelected = false; + this._selectedMetaWin = null; + this._scrolledWindows = null; + _showWindowPreview.bind(this)(null); +} + +function _switchWindow(direction) { + if (!this._scrolledWindows) { + // source is app icon + if (this.app) { + this._scrolledWindows = this.app.get_windows(); + const wsList = []; + this._scrolledWindows.forEach(w => { + const ws = w.get_workspace(); + if (!wsList.includes(ws)) + wsList.push(ws); + }); + // sort windows by workspaces in MRU order + this._scrolledWindows.sort((a, b) => wsList.indexOf(a.get_workspace()) > wsList.indexOf(b.get_workspace())); + // source is Search Windows icon + } else if (this._isSearchWindowsIcon) { + if (opt.SEARCH_WINDOWS_ICON_SCROLL === 1) // all windows + this._scrolledWindows = AltTab.getWindows(null); + else + this._scrolledWindows = AltTab.getWindows(global.workspace_manager.get_active_workspace()); + } + } + let windows = this._scrolledWindows; + + if (!windows.length) + return; + + // if window selection is in the process, the previewed window must be the current one + let currentWin = this._selectedMetaWin ? this._selectedMetaWin : windows[0]; + + const currentIdx = windows.indexOf(currentWin); + let targetIdx = currentIdx + direction; + + if (targetIdx > windows.length - 1) + targetIdx = 0; + else if (targetIdx < 0) + targetIdx = windows.length - 1; + + const metaWin = windows[targetIdx]; + _showWindowPreview.bind(this)(metaWin); + this._selectedMetaWin = metaWin; +} + +function _showWindowPreview(metaWin) { + const views = Main.overview._overview.controls._workspacesDisplay._workspacesViews; + const viewsIter = [views[0]]; + // secondary monitors use different structure + views.forEach(v => { + if (v._workspacesView) + viewsIter.push(v._workspacesView); + }); + + viewsIter.forEach(view => { + // if workspaces are on primary monitor only + if (!view || !view._workspaces) + return; + + view._workspaces.forEach(ws => { + ws._windows.forEach(windowPreview => { + // metaWin === null resets opacity + let opacity = metaWin ? 50 : 255; + windowPreview._activateSelected = false; + + // minimized windows are invisible if windows are not exposed (WORKSPACE_MODE === 0) + if (!windowPreview.opacity) + windowPreview.opacity = 255; + + // app windows set to lower opacity, so they can be recognized + if (this._scrolledWindows && this._scrolledWindows.includes(windowPreview.metaWindow)) { + if (opt.DASH_ICON_SCROLL === 2) + opacity = 254; + } + if (windowPreview.metaWindow === metaWin) { + if (metaWin && metaWin.get_workspace() !== global.workspace_manager.get_active_workspace()) + Main.wm.actionMoveWorkspace(metaWin.get_workspace()); + + windowPreview.get_parent().set_child_above_sibling(windowPreview, null); + + opacity = 255; + this._selectedPreview = windowPreview; + windowPreview._activateSelected = true; + } + + // if windows are exposed, highlight selected using opacity + if ((opt.OVERVIEW_MODE && opt.WORKSPACE_MODE) || !opt.OVERVIEW_MODE) { + if (metaWin && opacity === 255) + windowPreview.showOverlay(true); + else + windowPreview.hideOverlay(true); + windowPreview.ease({ + duration: 200, + opacity, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + }); + }); + }); +} + +const AppIconCommon = { + activate(button) { + const event = Clutter.get_current_event(); + const state = event ? event.get_state() : 0; + const isMiddleButton = button && button === Clutter.BUTTON_MIDDLE; + const isCtrlPressed = Util.isCtrlPressed(state); + const isShiftPressed = Util.isShiftPressed(state); + const openNewWindow = (this.app.can_open_new_window() && + this.app.state === Shell.AppState.RUNNING && + (isCtrlPressed || isMiddleButton) && !opt.DASH_CLICK_ACTION === 2) || + (opt.DASH_CLICK_ACTION === 2 && !this._selectedMetaWin && !isMiddleButton); + + const currentWS = global.workspace_manager.get_active_workspace(); + const appRecentWorkspace = _getAppRecentWorkspace(this.app); + // this feature shouldn't affect search results, dash icons don't have labels, so we use them as a condition + const showWidowsBeforeActivation = opt.DASH_CLICK_ACTION === 1 && !this.icon.label; + + let targetWindowOnCurrentWs = false; + if (opt.DASH_FOLLOW_RECENT_WIN) { + targetWindowOnCurrentWs = appRecentWorkspace === currentWS; + } else { + this.app.get_windows().forEach( + w => { + targetWindowOnCurrentWs = targetWindowOnCurrentWs || (w.get_workspace() === currentWS); + } + ); + } + + if ((this.app.state === Shell.AppState.STOPPED || openNewWindow) && !isShiftPressed) + this.animateLaunch(); + + if (openNewWindow) { + this.app.open_new_window(-1); + // if DASH_CLICK_ACTION == "SHOW_WINS_BEFORE", the app has more than one window and has no window on the current workspace, + // don't activate the app immediately, only move the overview to the workspace with the app's recent window + } else if (showWidowsBeforeActivation && !isShiftPressed && this.app.get_n_windows() > 1 && !targetWindowOnCurrentWs/* && !(opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE)*/) { + // this._scroll = true; + // this._scrollTime = Date.now(); + Main.wm.actionMoveWorkspace(appRecentWorkspace); + Main.overview.dash.showAppsButton.checked = false; + return; + } else if (this._selectedMetaWin) { + this._selectedMetaWin.activate(global.get_current_time()); + } else if (showWidowsBeforeActivation && opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE && !isShiftPressed && this.app.get_n_windows() > 1) { + // expose windows + Main.overview._overview._controls._thumbnailsBox._activateThumbnailAtPoint(0, 0, global.get_current_time(), true); + return; + } else if (opt.DASH_SHIFT_CLICK_MV && isShiftPressed && this.app.get_windows().length) { + this._moveAppToCurrentWorkspace(); + return; + } else if (isShiftPressed) { + return; + } else { + this.app.activate(); + } + + Main.overview.hide(); + }, + + _moveAppToCurrentWorkspace() { + this.app.get_windows().forEach(w => w.change_workspace(global.workspace_manager.get_active_workspace())); + }, + + popupMenu(side = St.Side.LEFT) { + if (shellVersion >= 42) + this.setForcedHighlight(true); + this._removeMenuTimeout(); + this.fake_release(); + + if (!this._getWindowsOnCurrentWs) { + this._getWindowsOnCurrentWs = function () { + const winList = []; + this.app.get_windows().forEach(w => { + if (w.get_workspace() === global.workspace_manager.get_active_workspace()) + winList.push(w); + }); + return winList; + }; + + this._windowsOnOtherWs = function () { + return (this.app.get_windows().length - this._getWindowsOnCurrentWs().length) > 0; + }; + } + + if (!this._menu) { + this._menu = new AppMenu(this, side, { + favoritesSection: true, + showSingleWindows: true, + }); + + this._menu.setApp(this.app); + this._openSigId = this._menu.connect('open-state-changed', (menu, isPoppedUp) => { + if (!isPoppedUp) + this._onMenuPoppedDown(); + }); + // Main.overview.connectObject('hiding', + this._hidingSigId = Main.overview.connect('hiding', + () => this._menu.close(), this); + + Main.uiGroup.add_actor(this._menu.actor); + this._menuManager.addMenu(this._menu); + } + + // once the menu is created, it stays unchanged and we need to modify our items based on current situation + if (this._addedMenuItems && this._addedMenuItems.length) + this._addedMenuItems.forEach(i => i.destroy()); + + + const popupItems = []; + + const separator = new PopupMenu.PopupSeparatorMenuItem(); + this._menu.addMenuItem(separator); + + if (this.app.get_n_windows()) { + // if (/* opt.APP_MENU_FORCE_QUIT*/true) {} + popupItems.push([_('Force Quit'), () => { + this.app.get_windows()[0].kill(); + }]); + + // if (opt.APP_MENU_CLOSE_WS) {} + const nWin = this._getWindowsOnCurrentWs().length; + if (nWin) { + popupItems.push([_(`Close ${nWin} Windows on Current Workspace`), () => { + const windows = this._getWindowsOnCurrentWs(); + let time = global.get_current_time(); + for (let win of windows) { + // increase time by 1 ms for each window to avoid errors from GS + win.delete(time++); + } + }]); + } + + if (/* opt.APP_MENU_MV_TO_WS && */this._windowsOnOtherWs()) + popupItems.push([_('Move App to Current Workspace ( Shift + Click )'), this._moveAppToCurrentWorkspace]); + } + + this._addedMenuItems = []; + this._addedMenuItems.push(separator); + popupItems.forEach(i => { + let item = new PopupMenu.PopupMenuItem(i[0]); + this._menu.addMenuItem(item); + item.connect('activate', i[1].bind(this)); + this._addedMenuItems.push(item); + }); + + this.emit('menu-state-changed', true); + + this._menu.open(BoxPointer.PopupAnimation.FULL); + this._menuManager.ignoreRelease(); + this.emit('sync-tooltip'); + + return false; + }, +}; + +function _getWindowApp(metaWin) { + const tracker = Shell.WindowTracker.get_default(); + return tracker.get_window_app(metaWin); +} + +function _getAppLastUsedWindow(app) { + let recentWin; + global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { + const winApp = _getWindowApp(metaWin); + if (!recentWin && winApp === app) + recentWin = metaWin; + }); + return recentWin; +} + +function _getAppRecentWorkspace(app) { + const recentWin = _getAppLastUsedWindow(app); + if (recentWin) + return recentWin.get_workspace(); + + return null; +} + +function _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON) { const dash = Main.overview._overview._controls.layoutManager._dash; const dashContainer = dash._dashContainer; if (dash._showWindowsIcon) { dashContainer.remove_child(dash._showWindowsIcon); - dash._showWindowsIconClickedId && dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId); + if (dash._showWindowsIconClickedId) + dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId); dash._showWindowsIconClickedId = undefined; - dash._showWindowsIcon && dash._showWindowsIcon.destroy(); + if (dash._showWindowsIcon) + dash._showWindowsIcon.destroy(); dash._showWindowsIcon = undefined; } - if (!show || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) return; + if (!show || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) + return; if (!dash._showWindowsIcon) { dash._showWindowsIcon = new ShowWindowsIcon(); @@ -708,22 +1056,23 @@ function _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON) { dash._hookUpLabel(dash._showWindowsIcon); } - dash._showWindowsIcon.icon.setIconSize(opt.MAX_ICON_SIZE); + dash._showWindowsIcon.icon.setIconSize(dash.iconSize); if (opt.SHOW_WINDOWS_ICON === 1) { dashContainer.set_child_at_index(dash._showWindowsIcon, 0); } else if (opt.SHOW_WINDOWS_ICON === 2) { - index = dashContainer.get_children().length - 1; + const index = dashContainer.get_children().length - 1; dashContainer.set_child_at_index(dash._showWindowsIcon, index); } Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); } -var ShowWindowsIcon = GObject.registerClass( +const ShowWindowsIcon = GObject.registerClass( class ShowWindowsIcon extends Dash.DashItemContainer { _init() { super._init(); + this._isSearchWindowsIcon = true; this._labelText = _('Search Open Windows (Hotkey: Space)'); this.toggleButton = new St.Button({ style_class: 'show-apps', @@ -744,6 +1093,12 @@ class ShowWindowsIcon extends Dash.DashItemContainer { this.toggleButton._delegate = this; this.setChild(this.toggleButton); + + if (opt.SEARCH_WINDOWS_ICON_SCROLL) { + this.reactive = true; + this._scrollConId = this.connect('scroll-event', _onScrollEvent.bind(this)); + this._leaveConId = this.connect('leave-event', _onLeaveEvent.bind(this)); + } } _createIcon(size) { @@ -758,19 +1113,21 @@ class ShowWindowsIcon extends Dash.DashItemContainer { }); function _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON) { - const dash = Main.overview._overview._controls.layoutManager._dash; const dashContainer = dash._dashContainer; if (dash._recentFilesIcon) { dashContainer.remove_child(dash._recentFilesIcon); - dash._recentFilesIconClickedId && dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId); + if (dash._recentFilesIconClickedId) + dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId); dash._recentFilesIconClickedId = undefined; - dash._recentFilesIcon && dash._recentFilesIcon.destroy(); + if (dash._recentFilesIcon) + dash._recentFilesIcon.destroy(); dash._recentFilesIcon = undefined; } - if (!show || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED) return; + if (!show || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED) + return; if (!dash._recentFilesIcon) { dash._recentFilesIcon = new ShowRecentFilesIcon(); @@ -779,18 +1136,18 @@ function _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON) { dash._hookUpLabel(dash._recentFilesIcon); } - dash._recentFilesIcon.icon.setIconSize(opt.MAX_ICON_SIZE); + dash._recentFilesIcon.icon.setIconSize(dash.iconSize); if (opt.SHOW_RECENT_FILES_ICON === 1) { dashContainer.set_child_at_index(dash._recentFilesIcon, 0); } else if (opt.SHOW_RECENT_FILES_ICON === 2) { - index = dashContainer.get_children().length - 1; + const index = dashContainer.get_children().length - 1; dashContainer.set_child_at_index(dash._recentFilesIcon, index); } Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); } -var ShowRecentFilesIcon = GObject.registerClass( +const ShowRecentFilesIcon = GObject.registerClass( class ShowRecentFilesIcon extends Dash.DashItemContainer { _init() { super._init(); |