diff options
Diffstat (limited to 'extensions/46/vertical-workspaces/lib/dash.js')
-rw-r--r-- | extensions/46/vertical-workspaces/lib/dash.js | 1310 |
1 files changed, 0 insertions, 1310 deletions
diff --git a/extensions/46/vertical-workspaces/lib/dash.js b/extensions/46/vertical-workspaces/lib/dash.js deleted file mode 100644 index 719c3de..0000000 --- a/extensions/46/vertical-workspaces/lib/dash.js +++ /dev/null @@ -1,1310 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * dash.js - * - * @author GdH <G-dH@github.com> - * @copyright 2022-2024 - * @license GPL-3.0 - */ - -'use strict'; - -import Clutter from 'gi://Clutter'; -import GLib from 'gi://GLib'; -import Meta from 'gi://Meta'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; - -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as Dash from 'resource:///org/gnome/shell/ui/dash.js'; -import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js'; -import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js'; -import * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js'; -import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js'; -import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; -import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; - -let Me; -let opt; -// gettext -let _; - -let _moduleEnabled; -let _timeouts; - -// added values to achieve a better ability to scale down according to available space -export const BaseIconSizes = [16, 24, 32, 40, 44, 48, 56, 64, 72, 80, 96, 112, 128]; - -const DASH_ITEM_LABEL_SHOW_TIME = 150; - -const shellVersion46 = !Clutter.Container; // Container has been removed in 46 - -export const DashModule = class { - constructor(me) { - Me = me; - opt = Me.opt; - _ = Me.gettext; - - this._firstActivation = true; - this.moduleEnabled = false; - this._overrides = null; - this._horizontalWorkId = null; - this._verticalWorkId = null; - this._showAppsIconBtnPressId = 0; - } - - 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); - - if (conflict && !reset) - console.warn(`[${Me.metadata.name}] Warning: "Dash" module disabled due to potential conflict with another extension`); - - 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'); - } - - 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(''); - } - } - - _activateModule() { - _moduleEnabled = true; - _timeouts = {}; - const dash = Main.overview._overview._controls.layoutManager._dash; - - if (!this._originalWorkId) - this._originalWorkId = dash._workId; - - if (!this._overrides) - this._overrides = new Me.Util.Overrides(); - - this._resetStyle(dash); - this.updateStyle(dash); - - 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 (shellVersion46) - dash.add_style_class_name('dash-46'); - - if (opt.DASH_VERTICAL) { - // this._overrides.addOverride('Dash', Dash.Dash.prototype, DashVerticalOverride); - dash.add_style_class_name(shellVersion46 - ? 'vertical-46' - : 'vertical' - ); - this._setOrientation(Clutter.Orientation.VERTICAL); - } else { - this._setOrientation(Clutter.Orientation.HORIZONTAL); - } - - if (opt.DASH_VERTICAL && opt.DASH_BG_GS3_STYLE) { - if (opt.DASH_LEFT) { - dash.add_style_class_name(shellVersion46 - ? 'vertical-46-gs3-left' - : 'vertical-gs3-left'); - } else if (opt.DASH_RIGHT) { - dash.add_style_class_name(shellVersion46 - ? 'vertical-46-gs3-right' - : 'vertical-gs3-right'); - } - } else { - dash.remove_style_class_name('vertical-gs3-left'); - dash.remove_style_class_name('vertical-gs3-right'); - dash.remove_style_class_name('vertical-46-gs3-left'); - dash.remove_style_class_name('vertical-46-gs3-right'); - } - - if (!this._customWorkId) - this._customWorkId = Main.initializeDeferredWork(dash._box, dash._redisplay.bind(dash)); - dash._workId = this._customWorkId; - - 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'); - } - - _disableModule() { - const dash = Main.overview._overview._controls.layoutManager._dash; - - if (this._overrides) - this._overrides.removeAll(); - this._overrides = null; - - dash._workId = this._originalWorkId; - - if (this._wmSwitchWsConId) { - global.windowManager.disconnect(this._wmSwitchWsConId); - this._wmSwitchWsConId = 0; - } - if (this._newWindowConId) { - global.windowManager.disconnect(this._newWindowConId); - this._newWindowConId = 0; - } - - const reset = true; - this._setOrientation(Clutter.Orientation.HORIZONTAL); - this._moveDashAppGridIcon(reset); - this._connectShowAppsIcon(reset); - - this._resetStyle(dash); - dash.visible = !this._conflict; - dash._background.opacity = 255; - - _moduleEnabled = false; - console.debug(' DashModule - Disabled'); - } - - _resetStyle(dash) { - dash.remove_style_class_name('vertical'); - dash.remove_style_class_name('vertical-46'); - dash.remove_style_class_name('vertical-gs3-left'); - dash.remove_style_class_name('vertical-gs3-right'); - dash.remove_style_class_name('vertical-46-gs3-left'); - dash.remove_style_class_name('vertical-46-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'); - dash._background.set_style(''); - } - - _removeTimeouts() { - if (_timeouts) { - Object.values(_timeouts).forEach(t => { - if (t) - GLib.source_remove(t); - }); - _timeouts = null; - } - } - - _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(); - } - - _moveDashAppGridIcon(reset = false) { - // move dash app grid icon to the front - const 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; - } - } - - _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; - } - } -}; - -function getAppFromSource(source) { - if (source instanceof AppDisplay.AppIcon) - return source.app; - else - return null; -} - -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 = itemHeight + (shellVersion46 ? 0 : -3); - 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 = shellVersion46 ? 8 : 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 = shellVersion46 ? 8 : 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 && - actor.child._delegate.app; - }); - // Apps currently in the dash - let oldApps = children.map(actor => actor.child._delegate.app); - // Apps supposed to be in the dash - let newApps = []; - - for (let id in favorites) - newApps.push(favorites[id]); - - for (let i = 0; i < running.length; i++) { - let app = running[i]; - if (app.get_id() in favorites) - continue; - newApps.push(app); - } - - // Figure out the actual changes to the list of items; we iterate - // over both the list of items currently in the dash and the list - // of items expected there, and collect additions and removals. - // Moves are both an addition and a removal, where the order of - // the operations depends on whether we encounter the position - // where the item has been added first or the one from where it - // was removed. - // There is an assumption that only one item is moved at a given - // time; when moving several items at once, everything will still - // end up at the right position, but there might be additional - // additions/removals (e.g. it might remove all the launchers - // and add them back in the new order even if a smaller set of - // additions and removals is possible). - // If above assumptions turns out to be a problem, we might need - // to use a more sophisticated algorithm, e.g. Longest Common - // Subsequence as used by diff. - let addedItems = []; - let removedActors = []; - - let newIndex = 0; - let oldIndex = 0; - while (newIndex < newApps.length || oldIndex < oldApps.length) { - let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null; - let newApp = newApps.length > newIndex ? newApps[newIndex] : null; - - // No change at oldIndex/newIndex - if (oldApp === newApp) { - oldIndex++; - newIndex++; - continue; - } - - // App removed at oldIndex - if (oldApp && !newApps.includes(oldApp)) { - removedActors.push(children[oldIndex]); - oldIndex++; - continue; - } - - // App added at newIndex - if (newApp && !oldApps.includes(newApp)) { - addedItems.push({ - app: newApp, - item: this._createAppItem(newApp), - pos: newIndex, - }); - newIndex++; - continue; - } - - // App moved - let nextApp = newApps.length > newIndex + 1 - ? newApps[newIndex + 1] : null; - let insertHere = nextApp && nextApp === oldApp; - let alreadyRemoved = removedActors.reduce((result, actor) => { - let removedApp = actor.child._delegate.app; - return result || removedApp === newApp; - }, false); - - if (insertHere || alreadyRemoved) { - let newItem = this._createAppItem(newApp); - addedItems.push({ - app: newApp, - item: newItem, - pos: newIndex + removedActors.length, - }); - newIndex++; - } else { - removedActors.push(children[oldIndex]); - oldIndex++; - } - } - - for (let i = 0; i < addedItems.length; i++) { - this._box.insert_child_at_index( - addedItems[i].item, - addedItems[i].pos); - } - - for (let i = 0; i < removedActors.length; i++) { - let item = removedActors[i]; - - // Don't animate item removal when the overview is transitioning - // or hidden - if (Main.overview.visible && !Main.overview.animationInProgress) - item.animateOutAndDestroy(); - else - item.destroy(); - } - - this._adjustIconSize(); - - // Skip animations on first run when adding the initial set - // of items, to avoid all items zooming in at once - - let animate = this._shownInitially && Main.overview.visible && - !Main.overview.animationInProgress; - - if (!this._shownInitially) - this._shownInitially = true; - - for (let i = 0; i < addedItems.length; i++) - addedItems[i].item.show(animate); - - // Update separator - const nFavorites = Object.keys(favorites).length; - const nIcons = children.length + addedItems.length - removedActors.length; - if (nFavorites > 0 && nFavorites < nIcons) { - // destroy the horizontal separator if it exists. - // this is incredibly janky, but I can't think of a better way atm. - if (this._separator && this._separator.height !== 1) { - this._separator.destroy(); - this._separator = null; - } - - if (!this._separator) { - this._separator = new St.Widget({ - style_class: 'dash-separator', - x_align: Clutter.ActorAlign.CENTER, - y_align: Clutter.ActorAlign.CENTER, - 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 + this._animatingPlaceholdersCount; - if (this._dragPlaceholder) - pos++; - this._box.set_child_at_index(this._separator, pos); - } else if (this._separator) { - this._separator.destroy(); - this._separator = null; - } - // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 - // Without it, StBoxLayout may use a stale size cache - this._box.queue_relayout(); - }, - - _createAppItem(app) { - let appIcon = new Dash.DashIcon(app); - - let indicator = appIcon._dot; - 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) => { - this._itemMenuStateChanged(item, opened); - }); - - let item = new Dash.DashItemContainer(); - item.setChild(appIcon); - - // Override default AppIcon label_actor, now the - // accessible_name is set at DashItemContainer.setLabelText - appIcon.label_actor = null; - item.setLabelText(app.get_name()); - - appIcon.icon.setIconSize(this.iconSize); - this._hookUpLabel(item, appIcon); - - return item; - }, - - // 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; - - // 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 - // the animation) - let iconChildren = this._box.get_children().filter(actor => { - return actor.child && - actor.child._delegate && - actor.child._delegate.icon && - !actor.animatingOut; - }); - - // add new custom icons to the list - if (this._showAppsIcon.visible) - iconChildren.push(this._showAppsIcon); - - - // showWindowsIcon and extensionsIcon can be provided by the WSP and ESP extensions - if (this._showWindowsIcon) - iconChildren.push(this._showWindowsIcon); - - if (this._extensionsIcon) - iconChildren.push(this._extensionsIcon); - - - if (!iconChildren.length) - return; - - if (this._maxWidth === -1 || this._maxHeight === -1) - return; - - const dashHorizontal = !opt.DASH_VERTICAL; - - const themeNode = this.get_theme_node(); - const maxAllocation = new Clutter.ActorBox({ - x1: 0, - y1: 0, - x2: dashHorizontal ? this._maxWidth : 42, // not whatever - y2: dashHorizontal ? 42 : this._maxHeight, - }); - - let maxContent = themeNode.get_content_box(maxAllocation); - - let spacing = themeNode.get_length('spacing'); - - let firstButton = iconChildren[0].child; - let firstIcon = firstButton._delegate.icon; - - 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) { - availWidth = maxContent.x2 - maxContent.x1; - // Subtract icon padding and box spacing from the available width - availWidth -= iconChildren.length * (buttonWidth - iconWidth) + - (iconChildren.length - 1) * spacing + - 2 * this._background.get_theme_node().get_horizontal_padding(); - - availHeight = this._maxHeight; - availHeight -= this.margin_top + this.margin_bottom; - availHeight -= this._background.get_theme_node().get_vertical_padding(); - availHeight -= themeNode.get_vertical_padding(); - availHeight -= buttonHeight - iconHeight; - - 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(); - availWidth -= themeNode.get_horizontal_padding(); - availWidth -= buttonWidth - iconWidth; - - availHeight = maxContent.y2 - maxContent.y1; - availHeight -= iconChildren.length * (buttonHeight - iconHeight) + - (iconChildren.length - 1) * spacing + - 2 * this._background.get_theme_node().get_vertical_padding(); - - maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE * scaleFactor); - } - - let iconSizes = BaseIconSizes.map(s => s * scaleFactor); - - let newIconSize = BaseIconSizes[0]; - for (let i = 0; i < iconSizes.length; i++) { - if (iconSizes[i] <= maxIconSize) - newIconSize = BaseIconSizes[i]; - } - - 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; - this.emit('icon-size-changed'); - - let scale = oldIconSize / newIconSize; - for (let i = 0; i < iconChildren.length; i++) { - let icon = iconChildren[i].child._delegate.icon; - - // Set the new size immediately, to keep the icons' sizes - // in sync with this.iconSize - icon.setIconSize(this.iconSize); - - // Don't animate the icon size change when the overview - // is transitioning, not visible or when initially filling - // the dash - if (!Main.overview.visible || Main.overview.animationInProgress || - !this._shownInitially) - continue; - - let [targetWidth, targetHeight] = icon.icon.get_size(); - - // 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.ease({ - width: targetWidth, - height: targetHeight, - duration: Dash.DASH_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } - - if (this._separator) { - this._separator.ease({ - width: dashHorizontal ? 1 : this.iconSize, - height: dashHorizontal ? this.iconSize : 1, - duration: Dash.DASH_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } - - this._adjustingInProgress = false; - }, - - handleDragOver(source, actor, x, y, _time) { - let app = 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(); // not exported in 45 - this._dragPlaceholder = new Dash.DashItemContainer(); - this._dragPlaceholder.setChild(new St.Bin({ style_class: 'placeholder' })); - 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 && !Me.Util.dashNotDefault()) { - this._scrollConId = this.connect('scroll-event', DashExtensions.onScrollEvent.bind(this)); - this._leaveConId = this.connect('leave-event', DashExtensions.onLeaveEvent.bind(this)); - } - - if (this._updateRunningDotStyle) - this._updateRunningDotStyle(); - }, - - /* popupMenu() { - const side = opt.DASH_VERTICAL ? St.Side.LEFT : St.Side.BOTTOM; - AppIconCommon.popupMenu.bind(this)(side); - },*/ - - _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; - - if (show) - this._dot.show(); - else - this._dot.hide(); - }, - - /* 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'); - - if (!this.label && shellVersion46) { - if (opt.DASH_VERTICAL) { - this._dot.translation_y = 0; - this._dot.translation_x = 0; // opt.DASH_LEFT ? -4 : 4; - } else { - this._dot.translation_y = 8; - this._dot.translation_x = 0; - } - } - }, - - activate(button) { - const event = Clutter.get_current_event(); - const state = event ? event.get_state() : 0; - const isMiddleButton = button && button === Clutter.BUTTON_MIDDLE; - const isCtrlPressed = Me.Util.isCtrlPressed(state); - const isShiftPressed = Me.Util.isShiftPressed(state); - - const currentWS = global.workspace_manager.get_active_workspace(); - 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; - - 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); - } - ); - } - - 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(); - - 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)*/) { - - 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_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) || ((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; - } 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) { - side = opt.DASH_VERTICAL ? St.Side.LEFT : St.Side.BOTTOM; - // AppIconCommon.popupMenu.bind(this)(side); - - 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.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_child(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++); - } - }]); - } - - - popupItems.push([_('Move App to Current Workspace ( Shift + Click )'), this._moveAppToCurrentWorkspace]); - // WTMB (Windows Thumbnails) extension required - if (global.windowThumbnails) { - popupItems.push([_('Create Window Thumbnail/PiP'), () => { - global.windowThumbnails?.createThumbnail(this.app.get_windows()[0]); - }]); - } - } - - 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)); - if (i[1] === this._moveAppToCurrentWorkspace && !this._windowsOnOtherWs()) - item.setSensitive(false); - 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; - }, - - _getWindowApp(metaWin) { - const tracker = Shell.WindowTracker.get_default(); - return tracker.get_window_app(metaWin); - }, - - _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; - }, - - _getAppRecentWorkspace(app) { - const recentWin = this._getAppLastUsedWindow(app); - if (recentWin) - return recentWin.get_workspace(); - - return null; - }, -}; - -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; - } - - 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; - - this._lastScroll = Date.now(); - - DashExtensions.switchWindow.bind(this)(direction); - return Clutter.EVENT_STOP; - }, - - onLeaveEvent() { - if (!this._selectedMetaWin || this.has_pointer || this.toggleButton?.has_pointer) - return; - - this._selectedPreview._activateSelected = false; - this._selectedMetaWin = null; - this._scrolledWindows = null; - DashExtensions.showWindowPreview.bind(this)(null); - }, - - - 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); - } - - 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 = Me.Util.getWindows(null); - else - this._scrolledWindows = Me.Util.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; - // const focusWindow = Me.Util.getWindows(null)[0]; // incompatible 45 - const focusWindow = Me.Util.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; - - if (targetIdx > windows.length - 1) - targetIdx = 0; - else if (targetIdx < 0) - targetIdx = windows.length - 1; - - const metaWin = windows[targetIdx]; - DashExtensions.showWindowPreview.bind(this)(metaWin); - this._selectedMetaWin = metaWin; - }, - - 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()); - 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'); - }, -}; - -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); - }); - }, -}; |