From d04104ff8aff1853a449ad5878f4532016004d12 Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Fri, 31 Jan 2025 11:49:22 -0500 Subject: [PATCH] Format code --- .gitignore | 1 + .prettierrc | 5 + appIcons.js | 3461 ++++++++++++--------- desktopIconsIntegration.js | 198 +- eslint.config.js | 9 + extension.js | 183 +- intellihide.js | 833 ++--- overview.js | 930 +++--- package.json | 13 + panel.js | 2195 +++++++------ panelManager.js | 1547 +++++----- panelPositions.js | 68 +- panelSettings.js | 118 +- panelStyle.js | 543 ++-- prefs.js | 5977 ++++++++++++++++++++++-------------- progress.js | 1180 +++---- proximity.js | 427 +-- taskbar.js | 3006 +++++++++--------- transparency.js | 454 +-- utils.js | 1497 ++++----- windowPreview.js | 1862 ++++++----- yarn.lock | 583 ++++ 22 files changed, 14374 insertions(+), 10716 deletions(-) create mode 100644 .prettierrc create mode 100644 eslint.config.js create mode 100644 package.json create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore index e9fb3e4..23439fc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ dash-to-panel@jderose9.github.com*.zip *.mo po/dash-to-panel.pot ui/*.ui.h +node_modules/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..cab365e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "arrowParens": "always", + "singleQuote": true +} \ No newline at end of file diff --git a/appIcons.js b/appIcons.js index 3bfad20..f90bac1 100644 --- a/appIcons.js +++ b/appIcons.js @@ -21,78 +21,86 @@ * Some code was also adapted from the upstream Gnome Shell source code. */ +import Clutter from 'gi://Clutter' +import GLib from 'gi://GLib' +import Gio from 'gi://Gio' +import Graphene from 'gi://Graphene' +import GObject from 'gi://GObject' +import Mtk from 'gi://Mtk' +import Shell from 'gi://Shell' +import St from 'gi://St' -import Clutter from 'gi://Clutter'; -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; -import Graphene from 'gi://Graphene'; -import GObject from 'gi://GObject'; -import Mtk from 'gi://Mtk'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; +import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js' +import * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js' +import * as Dash from 'resource:///org/gnome/shell/ui/dash.js' +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js' +import * as Util from 'resource:///org/gnome/shell/misc/util.js' +import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js' +import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' -import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js'; -import * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js'; -import * as Dash from 'resource:///org/gnome/shell/ui/dash.js'; -import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; -import * as Util from 'resource:///org/gnome/shell/misc/util.js'; -import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js'; -import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js'; - -import * as Utils from './utils.js'; -import * as PanelSettings from './panelSettings.js'; -import * as Taskbar from './taskbar.js'; -import * as Progress from './progress.js'; -import { DTP_EXTENSION, SETTINGS, DESKTOPSETTINGS, TERMINALSETTINGS, EXTENSION_PATH } from './extension.js'; -import { gettext as _, ngettext } from 'resource:///org/gnome/shell/extensions/extension.js'; +import * as Utils from './utils.js' +import * as PanelSettings from './panelSettings.js' +import * as Taskbar from './taskbar.js' +import * as Progress from './progress.js' +import { + DTP_EXTENSION, + SETTINGS, + DESKTOPSETTINGS, + TERMINALSETTINGS, + EXTENSION_PATH, +} from './extension.js' +import { + gettext as _, + ngettext, +} from 'resource:///org/gnome/shell/extensions/extension.js' //timeout names -const T2 = 'mouseScrollTimeout'; -const T3 = 'showDotsTimeout'; -const T4 = 'overviewWindowDragEndTimeout'; -const T5 = 'switchWorkspaceTimeout'; -const T6 = 'displayProperIndicatorTimeout'; +const T2 = 'mouseScrollTimeout' +const T3 = 'showDotsTimeout' +const T4 = 'overviewWindowDragEndTimeout' +const T5 = 'switchWorkspaceTimeout' +const T6 = 'displayProperIndicatorTimeout' //right padding defined for .overview-label in stylesheet.css -const TITLE_RIGHT_PADDING = 8; -const DOUBLE_CLICK_DELAY_MS = 450; +const TITLE_RIGHT_PADDING = 8 +const DOUBLE_CLICK_DELAY_MS = 450 -let LABEL_GAP = 5; -let MAX_INDICATORS = 4; -export const DEFAULT_PADDING_SIZE = 4; +let LABEL_GAP = 5 +let MAX_INDICATORS = 4 +export const DEFAULT_PADDING_SIZE = 4 let APPICON_STYLE = { - NORMAL: "NORMAL", - SYMBOLIC: "SYMBOLIC", - GRAYSCALE: "GRAYSCALE" + NORMAL: 'NORMAL', + SYMBOLIC: 'SYMBOLIC', + GRAYSCALE: 'GRAYSCALE', } let DOT_STYLE = { - DOTS: "DOTS", - SQUARES: "SQUARES", - DASHES: "DASHES", - SEGMENTED: "SEGMENTED", - CILIORA: "CILIORA", - METRO: "METRO", - SOLID: "SOLID" + DOTS: 'DOTS', + SQUARES: 'SQUARES', + DASHES: 'DASHES', + SEGMENTED: 'SEGMENTED', + CILIORA: 'CILIORA', + METRO: 'METRO', + SOLID: 'SOLID', } let DOT_POSITION = { - TOP: "TOP", - BOTTOM: "BOTTOM", - LEFT: 'LEFT', - RIGHT: 'RIGHT' + TOP: 'TOP', + BOTTOM: 'BOTTOM', + LEFT: 'LEFT', + RIGHT: 'RIGHT', } -let recentlyClickedAppLoopId = 0; -let recentlyClickedApp = null; -let recentlyClickedAppWindows = null; -let recentlyClickedAppIndex = 0; -let recentlyClickedAppMonitorIndex; +let recentlyClickedAppLoopId = 0 +let recentlyClickedApp = null +let recentlyClickedAppWindows = null +let recentlyClickedAppIndex = 0 +let recentlyClickedAppMonitorIndex -let tracker = Shell.WindowTracker.get_default(); +let tracker = Shell.WindowTracker.get_default() /** * Extend AppIcon @@ -107,1311 +115,1720 @@ let tracker = Shell.WindowTracker.get_default(); * */ -export const TaskbarAppIcon = GObject.registerClass({ -}, class TaskbarAppIcon extends AppDisplay.AppIcon { - +export const TaskbarAppIcon = GObject.registerClass( + {}, + class TaskbarAppIcon extends AppDisplay.AppIcon { _init(appInfo, panel, iconParams, previewMenu, iconAnimator) { - this.dtpPanel = panel; - this._nWindows = 0; - this.window = appInfo.window; - this.isLauncher = appInfo.isLauncher; - this._previewMenu = previewMenu; - this.iconAnimator = iconAnimator; - this.lastClick = 0; - this._appicon_normalstyle = ''; - this._appicon_hoverstyle = ''; - this._appicon_pressedstyle = ''; + this.dtpPanel = panel + this._nWindows = 0 + this.window = appInfo.window + this.isLauncher = appInfo.isLauncher + this._previewMenu = previewMenu + this.iconAnimator = iconAnimator + this.lastClick = 0 + this._appicon_normalstyle = '' + this._appicon_hoverstyle = '' + this._appicon_pressedstyle = '' - super._init(appInfo.app, iconParams); + super._init(appInfo.app, iconParams) - this._timeoutsHandler = new Utils.TimeoutsHandler(); + this._timeoutsHandler = new Utils.TimeoutsHandler() - // Fix touchscreen issues before the listener is added by the parent constructor. - this._onTouchEvent = function (actor, event) { - if (event.type() == Clutter.EventType.TOUCH_BEGIN) { - // Open the popup menu on long press. - this._setPopupTimeout(); - } else if (this._menuTimeoutId != 0 && (event.type() == Clutter.EventType.TOUCH_END || event.type() == Clutter.EventType.TOUCH_CANCEL)) { - // Activate/launch the application. - this.activate(1); - this._removeMenuTimeout(); + // Fix touchscreen issues before the listener is added by the parent constructor. + this._onTouchEvent = function (actor, event) { + if (event.type() == Clutter.EventType.TOUCH_BEGIN) { + // Open the popup menu on long press. + this._setPopupTimeout() + } else if ( + this._menuTimeoutId != 0 && + (event.type() == Clutter.EventType.TOUCH_END || + event.type() == Clutter.EventType.TOUCH_CANCEL) + ) { + // Activate/launch the application. + this.activate(1) + this._removeMenuTimeout() + } + // Disable dragging via touch screen as it's buggy as hell. Not perfect for tablet users, but the alternative is way worse. + // Also, EVENT_PROPAGATE launches applications twice with this solution, so this.activate(1) above must only be called if there's already a window. + return Clutter.EVENT_STOP + } + // Hack for missing TOUCH_END event. + this._onLeaveEvent = function (actor, event) { + this.fake_release() + if (this._menuTimeoutId != 0) this.activate(1) // Activate/launch the application if TOUCH_END didn't fire. + this._removeMenuTimeout() + } + + this._dot.set_width(0) + this._isGroupApps = SETTINGS.get_boolean('group-apps') + + this._container = new St.Widget({ + style_class: 'dtp-container', + layout_manager: new Clutter.BinLayout(), + }) + this._dotsContainer = new St.Widget({ + style_class: 'dtp-dots-container', + layout_manager: new Clutter.BinLayout(), + }) + this._dtpIconContainer = new St.Widget({ + layout_manager: new Clutter.BinLayout(), + style: getIconContainerStyle(panel.checkIfVertical()), + }) + + this.remove_child(this._iconContainer) + + this._dtpIconContainer.add_child(this._iconContainer) + + if (appInfo.window) { + let box = new St.BoxLayout() + + this._windowTitle = new St.Label({ + y_align: Clutter.ActorAlign.CENTER, + x_align: Clutter.ActorAlign.START, + style_class: 'overview-label', + }) + + this._updateWindowTitle() + this._updateWindowTitleStyle() + + this._scaleFactorChangedId = Utils.getStageTheme().connect( + 'changed', + () => this._updateWindowTitleStyle(), + ) + + box.add_child(this._dtpIconContainer) + box.add_child(this._windowTitle) + + this._dotsContainer.add_child(box) + } else { + this._dotsContainer.add_child(this._dtpIconContainer) + } + + this._container.add_child(this._dotsContainer) + this.set_child(this._container) + + if (panel.checkIfVertical()) { + this.set_width(panel.geom.w) + } + + // Monitor windows-changes instead of app state. + // Keep using the same Id and function callback (that is extended) + if (this._stateChangedId > 0) { + this.app.disconnect(this._stateChangedId) + this._stateChangedId = 0 + } + + this._onAnimateAppiconHoverChanged() + this._onAppIconHoverHighlightChanged() + this._setAppIconPadding() + this._setAppIconStyle() + this._showDots() + + this._focusWindowChangedId = global.display.connect( + 'notify::focus-window', + this._onFocusAppChanged.bind(this), + ) + + this._windowEnteredMonitorId = this._windowLeftMonitorId = 0 + this._stateChangedId = this.app.connect( + 'windows-changed', + this.onWindowsChanged.bind(this), + ) + + if (!this.window) { + if (SETTINGS.get_boolean('isolate-monitors')) { + this._windowEnteredMonitorId = + Utils.DisplayWrapper.getScreen().connect( + 'window-entered-monitor', + this.onWindowEnteredOrLeft.bind(this), + ) + this._windowLeftMonitorId = Utils.DisplayWrapper.getScreen().connect( + 'window-left-monitor', + this.onWindowEnteredOrLeft.bind(this), + ) + } + + this._titleWindowChangeId = 0 + this._minimizedWindowChangeId = 0 + + this._fullscreenId = Utils.DisplayWrapper.getScreen().connect( + 'in-fullscreen-changed', + () => { + if ( + global.display.focus_window?.get_monitor() == + this.dtpPanel.monitor.index && + !this.dtpPanel.monitor.inFullscreen + ) { + this._resetDots(true) + this._displayProperIndicator() } - // Disable dragging via touch screen as it's buggy as hell. Not perfect for tablet users, but the alternative is way worse. - // Also, EVENT_PROPAGATE launches applications twice with this solution, so this.activate(1) above must only be called if there's already a window. - return Clutter.EVENT_STOP; - }; - // Hack for missing TOUCH_END event. - this._onLeaveEvent = function (actor, event) { - this.fake_release(); - if (this._menuTimeoutId != 0) this.activate(1); // Activate/launch the application if TOUCH_END didn't fire. - this._removeMenuTimeout(); - }; + }, + ) + } else { + this._titleWindowChangeId = this.window.connect( + 'notify::title', + this._updateWindowTitle.bind(this), + ) + this._minimizedWindowChangeId = this.window.connect( + 'notify::minimized', + this._updateWindowTitleStyle.bind(this), + ) + } - this._dot.set_width(0); - this._isGroupApps = SETTINGS.get_boolean('group-apps'); + this._scrollEventId = this.connect( + 'scroll-event', + this._onMouseScroll.bind(this), + ) - this._container = new St.Widget({ style_class: 'dtp-container', layout_manager: new Clutter.BinLayout() }); - this._dotsContainer = new St.Widget({ style_class: 'dtp-dots-container', layout_manager: new Clutter.BinLayout() }); - this._dtpIconContainer = new St.Widget({ layout_manager: new Clutter.BinLayout(), style: getIconContainerStyle(panel.checkIfVertical()) }); + this._overviewWindowDragEndId = Main.overview.connect( + 'window-drag-end', + this._onOverviewWindowDragEnd.bind(this), + ) - this.remove_child(this._iconContainer); + this._switchWorkspaceId = global.window_manager.connect( + 'switch-workspace', + this._onSwitchWorkspace.bind(this), + ) - this._dtpIconContainer.add_child(this._iconContainer); + this._hoverChangeId = this.connect('notify::hover', () => + this._onAppIconHoverChanged(), + ) - if (appInfo.window) { - let box = new St.BoxLayout(); + this._hoverChangeId2 = this.connect('notify::hover', () => + this._onAppIconHoverChanged_GtkWorkaround(), + ) + this._pressedChangedId = this.connect('notify::pressed', () => + this._onAppIconPressedChanged_GtkWorkaround(), + ) - this._windowTitle = new St.Label({ - y_align: Clutter.ActorAlign.CENTER, - x_align: Clutter.ActorAlign.START, - style_class: 'overview-label' - }); + this._dtpSettingsSignalIds = [ + SETTINGS.connect( + 'changed::animate-appicon-hover', + this._onAnimateAppiconHoverChanged.bind(this), + ), + SETTINGS.connect( + 'changed::animate-appicon-hover', + this._onAppIconHoverHighlightChanged.bind(this), + ), + SETTINGS.connect( + 'changed::highlight-appicon-hover', + this._onAppIconHoverHighlightChanged.bind(this), + ), + SETTINGS.connect( + 'changed::highlight-appicon-hover-background-color', + this._onAppIconHoverHighlightChanged.bind(this), + ), + SETTINGS.connect( + 'changed::highlight-appicon-pressed-background-color', + this._onAppIconHoverHighlightChanged.bind(this), + ), + SETTINGS.connect( + 'changed::highlight-appicon-hover-border-radius', + this._onAppIconHoverHighlightChanged.bind(this), + ), + SETTINGS.connect( + 'changed::dot-position', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-size', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-style-focused', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-style-unfocused', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-dominant', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-override', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-1', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-2', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-3', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-4', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-unfocused-different', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-unfocused-1', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-unfocused-2', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-unfocused-3', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::dot-color-unfocused-4', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::focus-highlight', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::focus-highlight-dominant', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::focus-highlight-color', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::focus-highlight-opacity', + this._settingsChangeRefresh.bind(this), + ), + SETTINGS.connect( + 'changed::group-apps-label-font-size', + this._updateWindowTitleStyle.bind(this), + ), + SETTINGS.connect( + 'changed::group-apps-label-font-weight', + this._updateWindowTitleStyle.bind(this), + ), + SETTINGS.connect( + 'changed::group-apps-label-font-color', + this._updateWindowTitleStyle.bind(this), + ), + SETTINGS.connect( + 'changed::group-apps-label-font-color-minimized', + this._updateWindowTitleStyle.bind(this), + ), + SETTINGS.connect( + 'changed::group-apps-label-max-width', + this._updateWindowTitleStyle.bind(this), + ), + SETTINGS.connect( + 'changed::group-apps-use-fixed-width', + this._updateWindowTitleStyle.bind(this), + ), + SETTINGS.connect( + 'changed::group-apps-underline-unfocused', + this._settingsChangeRefresh.bind(this), + ), + ] - this._updateWindowTitle(); - this._updateWindowTitleStyle(); + this._dtpSettingsSignalIds = this._dtpSettingsSignalIds.concat([ + SETTINGS.connect('changed::highlight-appicon-hover-border-radius', () => + this._setIconStyle(this._isFocusedWindow()), + ), + ]) - this._scaleFactorChangedId = Utils.getStageTheme().connect('changed', () => this._updateWindowTitleStyle()); + this._progressIndicator = new Progress.ProgressIndicator( + this, + panel.progressManager, + ) - box.add_child(this._dtpIconContainer); - box.add_child(this._windowTitle); - - this._dotsContainer.add_child(box); - } else { - this._dotsContainer.add_child(this._dtpIconContainer); - } - - this._container.add_child(this._dotsContainer); - this.set_child(this._container); - - if (panel.checkIfVertical()) { - this.set_width(panel.geom.w); - } - - // Monitor windows-changes instead of app state. - // Keep using the same Id and function callback (that is extended) - if (this._stateChangedId > 0) { - this.app.disconnect(this._stateChangedId); - this._stateChangedId = 0; - } - - this._onAnimateAppiconHoverChanged(); - this._onAppIconHoverHighlightChanged(); - this._setAppIconPadding(); - this._setAppIconStyle(); - this._showDots(); - - this._focusWindowChangedId = global.display.connect('notify::focus-window', - this._onFocusAppChanged.bind(this)); - - this._windowEnteredMonitorId = this._windowLeftMonitorId = 0; - this._stateChangedId = this.app.connect('windows-changed', this.onWindowsChanged.bind(this)); - - if (!this.window) { - if (SETTINGS.get_boolean('isolate-monitors')) { - this._windowEnteredMonitorId = Utils.DisplayWrapper.getScreen().connect('window-entered-monitor', this.onWindowEnteredOrLeft.bind(this)); - this._windowLeftMonitorId = Utils.DisplayWrapper.getScreen().connect('window-left-monitor', this.onWindowEnteredOrLeft.bind(this)); - } - - this._titleWindowChangeId = 0; - this._minimizedWindowChangeId = 0; - - this._fullscreenId = Utils.DisplayWrapper.getScreen().connect('in-fullscreen-changed', () => { - if ( - global.display.focus_window?.get_monitor() == this.dtpPanel.monitor.index && - !this.dtpPanel.monitor.inFullscreen - ) { - this._resetDots(true); - this._displayProperIndicator(); - } - }) - } else { - this._titleWindowChangeId = this.window.connect('notify::title', - this._updateWindowTitle.bind(this)); - - this._minimizedWindowChangeId = this.window.connect('notify::minimized', - this._updateWindowTitleStyle.bind(this)); - } - - this._scrollEventId = this.connect('scroll-event', this._onMouseScroll.bind(this)); - - this._overviewWindowDragEndId = Main.overview.connect('window-drag-end', - this._onOverviewWindowDragEnd.bind(this)); - - this._switchWorkspaceId = global.window_manager.connect('switch-workspace', - this._onSwitchWorkspace.bind(this)); - - this._hoverChangeId = this.connect('notify::hover', () => this._onAppIconHoverChanged()); - - this._hoverChangeId2 = this.connect('notify::hover', () => this._onAppIconHoverChanged_GtkWorkaround()); - this._pressedChangedId = this.connect('notify::pressed', () => this._onAppIconPressedChanged_GtkWorkaround()); - - - this._dtpSettingsSignalIds = [ - SETTINGS.connect('changed::animate-appicon-hover', this._onAnimateAppiconHoverChanged.bind(this)), - SETTINGS.connect('changed::animate-appicon-hover', this._onAppIconHoverHighlightChanged.bind(this)), - SETTINGS.connect('changed::highlight-appicon-hover', this._onAppIconHoverHighlightChanged.bind(this)), - SETTINGS.connect('changed::highlight-appicon-hover-background-color', this._onAppIconHoverHighlightChanged.bind(this)), - SETTINGS.connect('changed::highlight-appicon-pressed-background-color', this._onAppIconHoverHighlightChanged.bind(this)), - SETTINGS.connect('changed::highlight-appicon-hover-border-radius', this._onAppIconHoverHighlightChanged.bind(this)), - SETTINGS.connect('changed::dot-position', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-size', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-style-focused', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-style-unfocused', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-dominant', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-override', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-1', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-2', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-3', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-4', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-unfocused-different', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-unfocused-1', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-unfocused-2', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-unfocused-3', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-color-unfocused-4', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::focus-highlight', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::focus-highlight-dominant', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::focus-highlight-color', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::focus-highlight-opacity', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::group-apps-label-font-size', this._updateWindowTitleStyle.bind(this)), - SETTINGS.connect('changed::group-apps-label-font-weight', this._updateWindowTitleStyle.bind(this)), - SETTINGS.connect('changed::group-apps-label-font-color', this._updateWindowTitleStyle.bind(this)), - SETTINGS.connect('changed::group-apps-label-font-color-minimized', this._updateWindowTitleStyle.bind(this)), - SETTINGS.connect('changed::group-apps-label-max-width', this._updateWindowTitleStyle.bind(this)), - SETTINGS.connect('changed::group-apps-use-fixed-width', this._updateWindowTitleStyle.bind(this)), - SETTINGS.connect('changed::group-apps-underline-unfocused', this._settingsChangeRefresh.bind(this)), - ]; - - this._dtpSettingsSignalIds = this._dtpSettingsSignalIds.concat([ - SETTINGS.connect('changed::highlight-appicon-hover-border-radius', () => this._setIconStyle(this._isFocusedWindow())), - ]); - - - this._progressIndicator = new Progress.ProgressIndicator(this, panel.progressManager); - - this._numberOverlay(); + this._numberOverlay() } getDragActor() { - return this.app.create_icon_texture(this.dtpPanel.taskbar.iconSize); + return this.app.create_icon_texture(this.dtpPanel.taskbar.iconSize) } // Used by TaskbarItemContainer to animate appIcons on hover getCloneButton() { - // The source of the clone is this._dtpIconContainer, - // which contains the icon but no highlighting elements - // using this.actor directly would break DnD style. - let cloneSource = this._dtpIconContainer; - let clone = new Clutter.Clone({ - source: cloneSource, - x: this.child.x, y: this.child.y, - width: cloneSource.width, height: cloneSource.height, - pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), - opacity: 255, - reactive: false, - x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER, - }); + // The source of the clone is this._dtpIconContainer, + // which contains the icon but no highlighting elements + // using this.actor directly would break DnD style. + let cloneSource = this._dtpIconContainer + let clone = new Clutter.Clone({ + source: cloneSource, + x: this.child.x, + y: this.child.y, + width: cloneSource.width, + height: cloneSource.height, + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + opacity: 255, + reactive: false, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + }) - clone._delegate = this._delegate; + clone._delegate = this._delegate - // "clone" of this.actor - return new St.Button({ - child: clone, - x: this.x, y: this.y, - width: this.width, height: this.height, - reactive: false, - }); + // "clone" of this.actor + return new St.Button({ + child: clone, + x: this.x, + y: this.y, + width: this.width, + height: this.height, + reactive: false, + }) } shouldShowTooltip() { - if (!SETTINGS.get_boolean('show-tooltip') || - (!this.isLauncher && SETTINGS.get_boolean("show-window-previews") && - this.getAppIconInterestingWindows().length > 0)) { - return false; - } else { - return this.hover && !this.window && - (!this._menu || !this._menu.isOpen) && - (this._previewMenu.getCurrentAppIcon() !== this); - } + if ( + !SETTINGS.get_boolean('show-tooltip') || + (!this.isLauncher && + SETTINGS.get_boolean('show-window-previews') && + this.getAppIconInterestingWindows().length > 0) + ) { + return false + } else { + return ( + this.hover && + !this.window && + (!this._menu || !this._menu.isOpen) && + this._previewMenu.getCurrentAppIcon() !== this + ) + } } _onAppIconHoverChanged() { - if (!SETTINGS.get_boolean('show-window-previews') || - (!this.window && !this._nWindows)) { - return; - } + if ( + !SETTINGS.get_boolean('show-window-previews') || + (!this.window && !this._nWindows) + ) { + return + } - if (this.hover) { - this._previewMenu.requestOpen(this); - } else { - this._previewMenu.requestClose(); - } + if (this.hover) { + this._previewMenu.requestOpen(this) + } else { + this._previewMenu.requestClose() + } } _onDestroy() { - super._onDestroy(); + super._onDestroy() - this._timeoutsHandler.destroy(); + this._timeoutsHandler.destroy() - this._previewMenu.close(true); + this._previewMenu.close(true) - // Disconect global signals - if (this._stateChangedId > 0) { - this.app.disconnect(this._stateChangedId); - this._stateChangedId = 0; - } + // Disconect global signals + if (this._stateChangedId > 0) { + this.app.disconnect(this._stateChangedId) + this._stateChangedId = 0 + } - if (this._overviewWindowDragEndId) - Main.overview.disconnect(this._overviewWindowDragEndId); + if (this._overviewWindowDragEndId) + Main.overview.disconnect(this._overviewWindowDragEndId) - if (this._focusWindowChangedId) - global.display.disconnect(this._focusWindowChangedId); + if (this._focusWindowChangedId) + global.display.disconnect(this._focusWindowChangedId) - if (this._fullscreenId) - Utils.DisplayWrapper.getScreen().disconnect(this._fullscreenId); + if (this._fullscreenId) + Utils.DisplayWrapper.getScreen().disconnect(this._fullscreenId) - if (this._titleWindowChangeId) - this.window.disconnect(this._titleWindowChangeId); + if (this._titleWindowChangeId) + this.window.disconnect(this._titleWindowChangeId) - if (this._minimizedWindowChangeId) - this.window.disconnect(this._minimizedWindowChangeId); + if (this._minimizedWindowChangeId) + this.window.disconnect(this._minimizedWindowChangeId) - if (this._windowEnteredMonitorId) { - Utils.DisplayWrapper.getScreen().disconnect(this._windowEnteredMonitorId); - Utils.DisplayWrapper.getScreen().disconnect(this._windowLeftMonitorId); - } + if (this._windowEnteredMonitorId) { + Utils.DisplayWrapper.getScreen().disconnect( + this._windowEnteredMonitorId, + ) + Utils.DisplayWrapper.getScreen().disconnect(this._windowLeftMonitorId) + } - if (this._switchWorkspaceId) - global.window_manager.disconnect(this._switchWorkspaceId); + if (this._switchWorkspaceId) + global.window_manager.disconnect(this._switchWorkspaceId) - if (this._scaleFactorChangedId) - Utils.getStageTheme().disconnect(this._scaleFactorChangedId); + if (this._scaleFactorChangedId) + Utils.getStageTheme().disconnect(this._scaleFactorChangedId) - if (this._hoverChangeId) { - this.disconnect(this._hoverChangeId); - } - if (this._hoverChangeId2) { - this.disconnect(this._hoverChangeId2); - } - if (this._pressedChangedId) { - this.disconnect(this._pressedChangedId); - } + if (this._hoverChangeId) { + this.disconnect(this._hoverChangeId) + } + if (this._hoverChangeId2) { + this.disconnect(this._hoverChangeId2) + } + if (this._pressedChangedId) { + this.disconnect(this._pressedChangedId) + } - if (this._scrollEventId) { - this.disconnect(this._scrollEventId); - } + if (this._scrollEventId) { + this.disconnect(this._scrollEventId) + } - for (let i = 0; i < this._dtpSettingsSignalIds.length; ++i) { - SETTINGS.disconnect(this._dtpSettingsSignalIds[i]); - } + for (let i = 0; i < this._dtpSettingsSignalIds.length; ++i) { + SETTINGS.disconnect(this._dtpSettingsSignalIds[i]) + } } onWindowsChanged() { - this._updateWindows(); - this.updateIcon(); + this._updateWindows() + this.updateIcon() - if (this._isGroupApps) - this._setIconStyle(); + if (this._isGroupApps) this._setIconStyle() } onWindowEnteredOrLeft(display, number, metaWindow) { - if (number > 0 && tracker.get_window_app(metaWindow) == this.app) { - this._updateWindows(); - this._displayProperIndicator(); - } + if (number > 0 && tracker.get_window_app(metaWindow) == this.app) { + this._updateWindows() + this._displayProperIndicator() + } } updateTitleStyle() { - this._updateWindowTitleStyle(); + this._updateWindowTitleStyle() } // Update indicator and target for minimization animation updateIcon() { + // If (for unknown reason) the actor is not on the stage the reported size + // and position are random values, which might exceeds the integer range + // resulting in an error when assigned to the a rect. This is a more like + // a workaround to prevent flooding the system with errors. + if (this.get_stage() == null) return - // If (for unknown reason) the actor is not on the stage the reported size - // and position are random values, which might exceeds the integer range - // resulting in an error when assigned to the a rect. This is a more like - // a workaround to prevent flooding the system with errors. - if (this.get_stage() == null) - return; + let rect = new Mtk.Rectangle() - let rect = new Mtk.Rectangle(); + ;[rect.x, rect.y] = this.get_transformed_position() + ;[rect.width, rect.height] = this.get_transformed_size() - [rect.x, rect.y] = this.get_transformed_position(); - [rect.width, rect.height] = this.get_transformed_size(); - - let windows = this.window ? [this.window] : this.getAppIconInterestingWindows(true); - windows.forEach(function (w) { - w.set_icon_geometry(rect); - }); + let windows = this.window + ? [this.window] + : this.getAppIconInterestingWindows(true) + windows.forEach(function (w) { + w.set_icon_geometry(rect) + }) } _onAnimateAppiconHoverChanged() { - if (SETTINGS.get_boolean('animate-appicon-hover')) { - this._container.add_style_class_name('animate-appicon-hover'); + if (SETTINGS.get_boolean('animate-appicon-hover')) { + this._container.add_style_class_name('animate-appicon-hover') - // Workaround to prevent scaled icon from being ugly when it is animated on hover. - // It increases the "resolution" of the icon without changing the icon size. - this.icon.createIcon = (iconSize) => this.app.create_icon_texture(2 * iconSize); - this._iconIconBinActorAddedId = this.icon._iconBin.connect('child-added', () => { - let size = this.icon.iconSize * Utils.getScaleFactor() + // Workaround to prevent scaled icon from being ugly when it is animated on hover. + // It increases the "resolution" of the icon without changing the icon size. + this.icon.createIcon = (iconSize) => + this.app.create_icon_texture(2 * iconSize) + this._iconIconBinActorAddedId = this.icon._iconBin.connect( + 'child-added', + () => { + let size = this.icon.iconSize * Utils.getScaleFactor() - if (this.icon._iconBin.child.mapped) { - this.icon._iconBin.child.set_size(size, size); - } else { - let iconMappedId = this.icon._iconBin.child.connect('notify::mapped', () => { - this.icon._iconBin.child.set_size(size, size); - this.icon._iconBin.child.disconnect(iconMappedId); - }); - } - }); - if (this.icon._iconBin.child) - this.icon._createIconTexture(this.icon.iconSize); - } else { - this._container.remove_style_class_name('animate-appicon-hover'); - - if (this._iconIconBinActorAddedId) { - this.icon._iconBin.disconnect(this._iconIconBinActorAddedId); - this._iconIconBinActorAddedId = 0; - this.icon.createIcon = this._createIcon.bind(this); + if (this.icon._iconBin.child.mapped) { + this.icon._iconBin.child.set_size(size, size) + } else { + let iconMappedId = this.icon._iconBin.child.connect( + 'notify::mapped', + () => { + this.icon._iconBin.child.set_size(size, size) + this.icon._iconBin.child.disconnect(iconMappedId) + }, + ) } + }, + ) + if (this.icon._iconBin.child) + this.icon._createIconTexture(this.icon.iconSize) + } else { + this._container.remove_style_class_name('animate-appicon-hover') + + if (this._iconIconBinActorAddedId) { + this.icon._iconBin.disconnect(this._iconIconBinActorAddedId) + this._iconIconBinActorAddedId = 0 + this.icon.createIcon = this._createIcon.bind(this) } + } } _onAppIconHoverHighlightChanged() { - const background_color = SETTINGS.get_string('highlight-appicon-hover-background-color'); - const pressed_color = SETTINGS.get_string('highlight-appicon-pressed-background-color'); - const border_radius = SETTINGS.get_int('highlight-appicon-hover-border-radius'); + const background_color = SETTINGS.get_string( + 'highlight-appicon-hover-background-color', + ) + const pressed_color = SETTINGS.get_string( + 'highlight-appicon-pressed-background-color', + ) + const border_radius = SETTINGS.get_int( + 'highlight-appicon-hover-border-radius', + ) - // Some trickery needed to get the effect - const br = `border-radius: ${border_radius}px;`; - this._appicon_normalstyle = br; - this._container.set_style(this._appicon_normalstyle); - this._appicon_hoverstyle = `background-color: ${background_color}; ${br}`; - this._appicon_pressedstyle = `background-color: ${pressed_color}; ${br}`; + // Some trickery needed to get the effect + const br = `border-radius: ${border_radius}px;` + this._appicon_normalstyle = br + this._container.set_style(this._appicon_normalstyle) + this._appicon_hoverstyle = `background-color: ${background_color}; ${br}` + this._appicon_pressedstyle = `background-color: ${pressed_color}; ${br}` - if (SETTINGS.get_boolean('highlight-appicon-hover')) { - this._container.remove_style_class_name('no-highlight'); - } else { - this._container.add_style_class_name('no-highlight'); - this._appicon_normalstyle = ''; - this._appicon_hoverstyle = ''; - this._appicon_pressedstyle = ''; - } + if (SETTINGS.get_boolean('highlight-appicon-hover')) { + this._container.remove_style_class_name('no-highlight') + } else { + this._container.add_style_class_name('no-highlight') + this._appicon_normalstyle = '' + this._appicon_hoverstyle = '' + this._appicon_pressedstyle = '' + } } _onAppIconHoverChanged_GtkWorkaround() { - if (this.hover && this._appicon_hoverstyle) { - this._container.set_style(this._appicon_hoverstyle); - } else if (this._appicon_normalstyle) { - this._container.set_style(this._appicon_normalstyle); - } else { - this._container.set_style(''); - } + if (this.hover && this._appicon_hoverstyle) { + this._container.set_style(this._appicon_hoverstyle) + } else if (this._appicon_normalstyle) { + this._container.set_style(this._appicon_normalstyle) + } else { + this._container.set_style('') + } } _onAppIconPressedChanged_GtkWorkaround() { - if (this.pressed && this._appicon_pressedstyle) { - this._container.set_style(this._appicon_pressedstyle); - } else if (this.hover && this._appicon_hoverstyle) { - this._container.set_style(this._appicon_hoverstyle); - } else if (this._appicon_normalstyle) { - this._container.set_style(this._appicon_normalstyle); - } else { - this._container.set_style(''); - } + if (this.pressed && this._appicon_pressedstyle) { + this._container.set_style(this._appicon_pressedstyle) + } else if (this.hover && this._appicon_hoverstyle) { + this._container.set_style(this._appicon_hoverstyle) + } else if (this._appicon_normalstyle) { + this._container.set_style(this._appicon_normalstyle) + } else { + this._container.set_style('') + } } _onMouseScroll(actor, event) { - let scrollAction = SETTINGS.get_string('scroll-icon-action'); + let scrollAction = SETTINGS.get_string('scroll-icon-action') - if (scrollAction === 'PASS_THROUGH') { - return this.dtpPanel._onPanelMouseScroll(actor, event); - } else if (scrollAction === 'NOTHING' || (!this.window && !this._nWindows)) { - return; - } + if (scrollAction === 'PASS_THROUGH') { + return this.dtpPanel._onPanelMouseScroll(actor, event) + } else if ( + scrollAction === 'NOTHING' || + (!this.window && !this._nWindows) + ) { + return + } - let direction = Utils.getMouseScrollDirection(event); + let direction = Utils.getMouseScrollDirection(event) - if (direction && !this._timeoutsHandler.getId(T2)) { - this._timeoutsHandler.add([T2, SETTINGS.get_int('scroll-icon-delay'), () => { }]); + if (direction && !this._timeoutsHandler.getId(T2)) { + this._timeoutsHandler.add([ + T2, + SETTINGS.get_int('scroll-icon-delay'), + () => {}, + ]) - let windows = this.getAppIconInterestingWindows(); + let windows = this.getAppIconInterestingWindows() - windows.sort(Taskbar.sortWindowsCompareFunction); - Utils.activateSiblingWindow(windows, direction, this.window); - } + windows.sort(Taskbar.sortWindowsCompareFunction) + Utils.activateSiblingWindow(windows, direction, this.window) + } } _showDots() { - // Just update style if dots already exist - if (this._focusedDots && this._unfocusedDots) { - this._updateWindows(); - return; - } + // Just update style if dots already exist + if (this._focusedDots && this._unfocusedDots) { + this._updateWindows() + return + } - if (!this._isGroupApps) { - this._focusedDots = new St.Widget({ - layout_manager: new Clutter.BinLayout(), - x_expand: true, y_expand: true, - visible: false - }); + if (!this._isGroupApps) { + this._focusedDots = new St.Widget({ + layout_manager: new Clutter.BinLayout(), + x_expand: true, + y_expand: true, + visible: false, + }) - let mappedId = this.connect('notify::mapped', () => { - this._displayProperIndicator(); - this.disconnect(mappedId); - }); - } else { - this._focusedDots = new St.DrawingArea(), - this._unfocusedDots = new St.DrawingArea(); + let mappedId = this.connect('notify::mapped', () => { + this._displayProperIndicator() + this.disconnect(mappedId) + }) + } else { + ;(this._focusedDots = new St.DrawingArea()), + (this._unfocusedDots = new St.DrawingArea()) - this._focusedDots.connect('repaint', () => { - if (!this._dashItemContainer.animatingOut) - // don't draw and trigger more animations if the icon is in the middle of - // being removed from the panel - this._drawRunningIndicator(this._focusedDots, SETTINGS.get_string('dot-style-focused'), true); - }); + this._focusedDots.connect('repaint', () => { + if (!this._dashItemContainer.animatingOut) + // don't draw and trigger more animations if the icon is in the middle of + // being removed from the panel + this._drawRunningIndicator( + this._focusedDots, + SETTINGS.get_string('dot-style-focused'), + true, + ) + }) - this._unfocusedDots.connect('repaint', () => { - if (!this._dashItemContainer.animatingOut) - this._drawRunningIndicator(this._unfocusedDots, SETTINGS.get_string('dot-style-unfocused'), false); - }); + this._unfocusedDots.connect('repaint', () => { + if (!this._dashItemContainer.animatingOut) + this._drawRunningIndicator( + this._unfocusedDots, + SETTINGS.get_string('dot-style-unfocused'), + false, + ) + }) - this._dotsContainer.add_child(this._unfocusedDots); + this._dotsContainer.add_child(this._unfocusedDots) - this._updateWindows(); + this._updateWindows() - this._timeoutsHandler.add([T3, 0, () => { - this._resetDots(); - this._displayProperIndicator(); - }]); - } + this._timeoutsHandler.add([ + T3, + 0, + () => { + this._resetDots() + this._displayProperIndicator() + }, + ]) + } - this._dotsContainer.add_child(this._focusedDots); + this._dotsContainer.add_child(this._focusedDots) } _resetDots(ignoreSizeReset) { - let position = SETTINGS.get_string('dot-position'); - let isHorizontalDots = position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM; - let sizeProp = isHorizontalDots ? 'width' : 'height'; - let focusedDotStyle = SETTINGS.get_string('dot-style-focused'); - let unfocusedDotStyle = SETTINGS.get_string('dot-style-unfocused'); + let position = SETTINGS.get_string('dot-position') + let isHorizontalDots = + position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM + let sizeProp = isHorizontalDots ? 'width' : 'height' + let focusedDotStyle = SETTINGS.get_string('dot-style-focused') + let unfocusedDotStyle = SETTINGS.get_string('dot-style-unfocused') - this._focusedIsWide = this._isWideDotStyle(focusedDotStyle); - this._unfocusedIsWide = this._isWideDotStyle(unfocusedDotStyle); + this._focusedIsWide = this._isWideDotStyle(focusedDotStyle) + this._unfocusedIsWide = this._isWideDotStyle(unfocusedDotStyle) + ;[, this._containerSize] = + this._container[`get_preferred_${sizeProp}`](-1) - [, this._containerSize] = this._container[`get_preferred_${sizeProp}`](-1); + if (!ignoreSizeReset) { + ;[this._focusedDots, this._unfocusedDots].forEach((d) => { + d.set_size(-1, -1) + d.x_expand = d.y_expand = false - if (!ignoreSizeReset) { - [this._focusedDots, this._unfocusedDots].forEach(d => { - d.set_size(-1, -1); - d.x_expand = d.y_expand = false; - - d[sizeProp] = 1; - d[(isHorizontalDots ? 'y' : 'x') + '_expand'] = true; - }); - } + d[sizeProp] = 1 + d[(isHorizontalDots ? 'y' : 'x') + '_expand'] = true + }) + } } _settingsChangeRefresh() { - if (this._isGroupApps) { - this._updateWindows(); - this._resetDots(); - this._focusedDots.queue_repaint(); - this._unfocusedDots.queue_repaint(); - } + if (this._isGroupApps) { + this._updateWindows() + this._resetDots() + this._focusedDots.queue_repaint() + this._unfocusedDots.queue_repaint() + } - this._displayProperIndicator(); + this._displayProperIndicator() } _updateWindowTitleStyle() { - if (this._windowTitle) { - let useFixedWidth = SETTINGS.get_boolean('group-apps-use-fixed-width'); - let fontWeight = SETTINGS.get_string('group-apps-label-font-weight'); - let fontScale = DESKTOPSETTINGS.get_double('text-scaling-factor'); - let fontColor = this.window.minimized ? - SETTINGS.get_string('group-apps-label-font-color-minimized') : - SETTINGS.get_string('group-apps-label-font-color'); - let scaleFactor = Utils.getScaleFactor(); - let maxLabelWidth = SETTINGS.get_int('group-apps-label-max-width') * scaleFactor; - let variableWidth = !useFixedWidth || this.dtpPanel.checkIfVertical() || this.dtpPanel.taskbar.fullScrollView; + if (this._windowTitle) { + let useFixedWidth = SETTINGS.get_boolean('group-apps-use-fixed-width') + let fontWeight = SETTINGS.get_string('group-apps-label-font-weight') + let fontScale = DESKTOPSETTINGS.get_double('text-scaling-factor') + let fontColor = this.window.minimized + ? SETTINGS.get_string('group-apps-label-font-color-minimized') + : SETTINGS.get_string('group-apps-label-font-color') + let scaleFactor = Utils.getScaleFactor() + let maxLabelWidth = + SETTINGS.get_int('group-apps-label-max-width') * scaleFactor + let variableWidth = + !useFixedWidth || + this.dtpPanel.checkIfVertical() || + this.dtpPanel.taskbar.fullScrollView - this._windowTitle[(maxLabelWidth > 0 ? 'show' : 'hide')](); - this._windowTitle.set_width(variableWidth ? -1 : maxLabelWidth + TITLE_RIGHT_PADDING * scaleFactor); + this._windowTitle[maxLabelWidth > 0 ? 'show' : 'hide']() + this._windowTitle.set_width( + variableWidth + ? -1 + : maxLabelWidth + TITLE_RIGHT_PADDING * scaleFactor, + ) - this._windowTitle.clutter_text.natural_width = useFixedWidth ? maxLabelWidth : 0; - this._windowTitle.clutter_text.natural_width_set = useFixedWidth; + this._windowTitle.clutter_text.natural_width = useFixedWidth + ? maxLabelWidth + : 0 + this._windowTitle.clutter_text.natural_width_set = useFixedWidth - this._windowTitle.set_style('font-size: ' + SETTINGS.get_int('group-apps-label-font-size') * fontScale + 'px;' + - 'font-weight: ' + fontWeight + ';' + - (useFixedWidth ? '' : 'max-width: ' + maxLabelWidth + 'px;') + - 'color: ' + fontColor); - } + this._windowTitle.set_style( + 'font-size: ' + + SETTINGS.get_int('group-apps-label-font-size') * fontScale + + 'px;' + + 'font-weight: ' + + fontWeight + + ';' + + (useFixedWidth ? '' : 'max-width: ' + maxLabelWidth + 'px;') + + 'color: ' + + fontColor, + ) + } } _updateWindowTitle() { - if (this._windowTitle.text != this.window.title) { - this._windowTitle.text = (this.window.title ? this.window.title : this.app.get_name()).replace(/\r?\n|\r/g, '').trim(); + if (this._windowTitle.text != this.window.title) { + this._windowTitle.text = ( + this.window.title ? this.window.title : this.app.get_name() + ) + .replace(/\r?\n|\r/g, '') + .trim() - if (this._focusedDots) { - this._displayProperIndicator(); - } + if (this._focusedDots) { + this._displayProperIndicator() } + } } _setIconStyle(isFocused) { - let inlineStyle = 'margin: 0;'; + let inlineStyle = 'margin: 0;' - if (SETTINGS.get_boolean('focus-highlight') && - this._checkIfFocusedApp() && !this.isLauncher && - (!this.window || isFocused) && !this._isThemeProvidingIndicator() && this._checkIfMonitorHasFocus()) { - let focusedDotStyle = SETTINGS.get_string('dot-style-focused'); - let pos = SETTINGS.get_string('dot-position'); - let highlightMargin = this._focusedIsWide ? SETTINGS.get_int('dot-size') : 0; + if ( + SETTINGS.get_boolean('focus-highlight') && + this._checkIfFocusedApp() && + !this.isLauncher && + (!this.window || isFocused) && + !this._isThemeProvidingIndicator() && + this._checkIfMonitorHasFocus() + ) { + let focusedDotStyle = SETTINGS.get_string('dot-style-focused') + let pos = SETTINGS.get_string('dot-position') + let highlightMargin = this._focusedIsWide + ? SETTINGS.get_int('dot-size') + : 0 - if (!this.window) { - let containerWidth = this._dtpIconContainer.get_width() / Utils.getScaleFactor(); - let backgroundSize = containerWidth + "px " + - (containerWidth - (pos == DOT_POSITION.BOTTOM ? highlightMargin : 0)) + "px;"; + if (!this.window) { + let containerWidth = + this._dtpIconContainer.get_width() / Utils.getScaleFactor() + let backgroundSize = + containerWidth + + 'px ' + + (containerWidth - + (pos == DOT_POSITION.BOTTOM ? highlightMargin : 0)) + + 'px;' - if (focusedDotStyle == DOT_STYLE.CILIORA || focusedDotStyle == DOT_STYLE.SEGMENTED) - highlightMargin += 1; + if ( + focusedDotStyle == DOT_STYLE.CILIORA || + focusedDotStyle == DOT_STYLE.SEGMENTED + ) + highlightMargin += 1 - if (this._nWindows > 1 && focusedDotStyle == DOT_STYLE.METRO) { - let bgSvg = '/img/highlight_stacked_bg'; + if (this._nWindows > 1 && focusedDotStyle == DOT_STYLE.METRO) { + let bgSvg = '/img/highlight_stacked_bg' - if (pos == DOT_POSITION.LEFT || pos == DOT_POSITION.RIGHT) { - bgSvg += (this.dtpPanel.checkIfVertical() ? '_2' : '_3'); - } - - inlineStyle += "background-image: url('" + EXTENSION_PATH + bgSvg + ".svg');" + - "background-position: 0 " + (pos == DOT_POSITION.TOP ? highlightMargin : 0) + "px;" + - "background-size: " + backgroundSize; - } + if (pos == DOT_POSITION.LEFT || pos == DOT_POSITION.RIGHT) { + bgSvg += this.dtpPanel.checkIfVertical() ? '_2' : '_3' } - let highlightColor = this._getFocusHighlightColor(); - inlineStyle += "background-color: " + cssHexTocssRgba(highlightColor, SETTINGS.get_int('focus-highlight-opacity') * 0.01) + ";"; - inlineStyle += this._appicon_normalstyle; + inlineStyle += + "background-image: url('" + + EXTENSION_PATH + + bgSvg + + ".svg');" + + 'background-position: 0 ' + + (pos == DOT_POSITION.TOP ? highlightMargin : 0) + + 'px;' + + 'background-size: ' + + backgroundSize + } } - if (this._dotsContainer.get_style() != inlineStyle) { - this._dotsContainer.set_style(inlineStyle); - } + let highlightColor = this._getFocusHighlightColor() + inlineStyle += + 'background-color: ' + + cssHexTocssRgba( + highlightColor, + SETTINGS.get_int('focus-highlight-opacity') * 0.01, + ) + + ';' + inlineStyle += this._appicon_normalstyle + } + + if (this._dotsContainer.get_style() != inlineStyle) { + this._dotsContainer.set_style(inlineStyle) + } } _checkIfFocusedApp() { - return tracker.focus_app == this.app; + return tracker.focus_app == this.app } _checkIfMonitorHasFocus() { - return global.display.focus_window && - (!SETTINGS.get_boolean('multi-monitors') || // only check same monitor index if multi window is enabled. - !SETTINGS.get_boolean('isolate-monitors') || - global.display.focus_window.get_monitor() === this.dtpPanel.monitor.index); + return ( + global.display.focus_window && + (!SETTINGS.get_boolean('multi-monitors') || // only check same monitor index if multi window is enabled. + !SETTINGS.get_boolean('isolate-monitors') || + global.display.focus_window.get_monitor() === + this.dtpPanel.monitor.index) + ) } _setAppIconPadding() { - const padding = getIconPadding(this.dtpPanel.monitor.index); - const margin = SETTINGS.get_int('appicon-margin'); - const margin_todesktop = SETTINGS.get_int('appicon-margin-todesktop'); - const margin_toscreenborder = SETTINGS.get_int('appicon-margin-toscreenborder'); + const padding = getIconPadding(this.dtpPanel.monitor.index) + const margin = SETTINGS.get_int('appicon-margin') + const margin_todesktop = SETTINGS.get_int('appicon-margin-todesktop') + const margin_toscreenborder = SETTINGS.get_int( + 'appicon-margin-toscreenborder', + ) - let margin_style = ''; - const panelPosition = this.dtpPanel.getPosition(); - if (panelPosition == St.Side.TOP) { - margin_style = `${margin_toscreenborder}px ${margin}px ${margin_todesktop}px ${margin}px`; - } else if (panelPosition == St.Side.RIGHT) { - margin_style = `${margin}px ${margin_toscreenborder}px ${margin}px ${margin_todesktop}px`; - } else if (panelPosition == St.Side.LEFT) { - margin_style = `${margin}px ${margin_todesktop}px ${margin}px ${margin_toscreenborder}px`; - } else { - margin_style = `${margin_todesktop}px ${margin}px ${margin_toscreenborder}px ${margin}px`; - } - this.set_style(`padding: ${margin_style};`); - this._iconContainer.set_style('padding: ' + padding + 'px;'); + let margin_style = '' + const panelPosition = this.dtpPanel.getPosition() + if (panelPosition == St.Side.TOP) { + margin_style = `${margin_toscreenborder}px ${margin}px ${margin_todesktop}px ${margin}px` + } else if (panelPosition == St.Side.RIGHT) { + margin_style = `${margin}px ${margin_toscreenborder}px ${margin}px ${margin_todesktop}px` + } else if (panelPosition == St.Side.LEFT) { + margin_style = `${margin}px ${margin_todesktop}px ${margin}px ${margin_toscreenborder}px` + } else { + margin_style = `${margin_todesktop}px ${margin}px ${margin_toscreenborder}px ${margin}px` + } + this.set_style(`padding: ${margin_style};`) + this._iconContainer.set_style('padding: ' + padding + 'px;') } _setAppIconStyle() { - let appIconStyle = SETTINGS.get_string('appicon-style'); + let appIconStyle = SETTINGS.get_string('appicon-style') - if (appIconStyle === APPICON_STYLE.SYMBOLIC) { - this.add_style_class_name('symbolic-icon-style'); - } else if (appIconStyle === APPICON_STYLE.GRAYSCALE) { - this._iconContainer.add_effect_with_name('desaturate', new Clutter.DesaturateEffect({ factor: 1 })); - } + if (appIconStyle === APPICON_STYLE.SYMBOLIC) { + this.add_style_class_name('symbolic-icon-style') + } else if (appIconStyle === APPICON_STYLE.GRAYSCALE) { + this._iconContainer.add_effect_with_name( + 'desaturate', + new Clutter.DesaturateEffect({ factor: 1 }), + ) + } } popupMenu() { - this._removeMenuTimeout(); - this.fake_release(); + this._removeMenuTimeout() + this.fake_release() - if (!this._menu) { - this._menu = new TaskbarSecondaryMenu(this, this.dtpPanel.geom.position); - this._menu.setApp(this.app); - this._menu.connect('open-state-changed', (menu, isPoppedUp) => { - if (!isPoppedUp) - this._onMenuPoppedDown(); - else - this._previewMenu.close(true); - }); - let id = Main.overview.connect('hiding', () => { - this._menu.close(); - }); - this.connect('destroy', () => { - Main.overview.disconnect(id); - }); + if (!this._menu) { + this._menu = new TaskbarSecondaryMenu(this, this.dtpPanel.geom.position) + this._menu.setApp(this.app) + this._menu.connect('open-state-changed', (menu, isPoppedUp) => { + if (!isPoppedUp) this._onMenuPoppedDown() + else this._previewMenu.close(true) + }) + let id = Main.overview.connect('hiding', () => { + this._menu.close() + }) + this.connect('destroy', () => { + Main.overview.disconnect(id) + }) - // We want to keep the item hovered while the menu is up - this._menu.blockSourceEvents = true; + // We want to keep the item hovered while the menu is up + this._menu.blockSourceEvents = true - Main.uiGroup.add_child(this._menu.actor); - this._menuManager.addMenu(this._menu); - } - this._menu.updateQuitText(); + Main.uiGroup.add_child(this._menu.actor) + this._menuManager.addMenu(this._menu) + } + this._menu.updateQuitText() - this.emit('menu-state-changed', true); + this.emit('menu-state-changed', true) - this.set_hover(true); - this._menu.open(BoxPointer.PopupAnimation.FULL); - this._menuManager.ignoreRelease(); - this.emit('sync-tooltip'); + this.set_hover(true) + this._menu.open(BoxPointer.PopupAnimation.FULL) + this._menuManager.ignoreRelease() + this.emit('sync-tooltip') - return false; + return false } _onFocusAppChanged(windowTracker) { - this._displayProperIndicator(); + this._displayProperIndicator() } _onOverviewWindowDragEnd(windowTracker) { - this._timeoutsHandler.add([T4, 0, () => { - if (SETTINGS.get_boolean('isolate-workspaces')) - this._updateWindows() + this._timeoutsHandler.add([ + T4, + 0, + () => { + if (SETTINGS.get_boolean('isolate-workspaces')) this._updateWindows() - this._displayProperIndicator() - }]); + this._displayProperIndicator() + }, + ]) } _onSwitchWorkspace(windowTracker) { - if (this._isGroupApps) { - this._timeoutsHandler.add([T5, 0, () => this._displayProperIndicator()]); - } else { - this._displayProperIndicator(); - } + if (this._isGroupApps) { + this._timeoutsHandler.add([T5, 0, () => this._displayProperIndicator()]) + } else { + this._displayProperIndicator() + } } _displayProperIndicator() { - let isFocused = this._isFocusedWindow(); - let position = SETTINGS.get_string('dot-position'); - let isHorizontalDots = position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM; + let isFocused = this._isFocusedWindow() + let position = SETTINGS.get_string('dot-position') + let isHorizontalDots = + position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM - this._setIconStyle(isFocused); + this._setIconStyle(isFocused) - if (!this._isGroupApps) { - if (this.window && (SETTINGS.get_boolean('group-apps-underline-unfocused') || isFocused)) { - let align = Clutter.ActorAlign[position == DOT_POSITION.TOP || position == DOT_POSITION.LEFT ? 'START' : 'END']; + if (!this._isGroupApps) { + if ( + this.window && + (SETTINGS.get_boolean('group-apps-underline-unfocused') || isFocused) + ) { + let align = + Clutter.ActorAlign[ + position == DOT_POSITION.TOP || position == DOT_POSITION.LEFT + ? 'START' + : 'END' + ] - this._focusedDots.set_size(0, 0); - this._focusedDots[isHorizontalDots ? 'height' : 'width'] = this._getRunningIndicatorSize(); + this._focusedDots.set_size(0, 0) + this._focusedDots[isHorizontalDots ? 'height' : 'width'] = + this._getRunningIndicatorSize() - this._focusedDots.y_align = this._focusedDots.x_align = Clutter.ActorAlign.FILL; - this._focusedDots[(isHorizontalDots ? 'y' : 'x') + '_align'] = align; - this._focusedDots.background_color = this._getRunningIndicatorColor(isFocused); - this._focusedDots.show(); - } else if (this._focusedDots.visible) { - this._focusedDots.hide(); - } - } else { - let sizeProp = isHorizontalDots ? 'width' : 'height'; - let newFocusedDotsSize = 0; - let newFocusedDotsOpacity = 0; - let newUnfocusedDotsSize = 0; - let newUnfocusedDotsOpacity = 0; - - isFocused = this._checkIfFocusedApp() && this._checkIfMonitorHasFocus(); - - this._timeoutsHandler.add([T6, 0, () => { - if (isFocused) - this.add_style_class_name('focused'); - else - this.remove_style_class_name('focused'); - }]); - - if (this._focusedIsWide) { - newFocusedDotsSize = (isFocused && this._nWindows > 0) ? this._containerSize : 0; - newFocusedDotsOpacity = 255; - } else { - newFocusedDotsSize = this._containerSize; - newFocusedDotsOpacity = (isFocused && this._nWindows > 0) ? 255 : 0; - } - - if (this._unfocusedIsWide) { - newUnfocusedDotsSize = (!isFocused && this._nWindows > 0) ? this._containerSize : 0; - newUnfocusedDotsOpacity = 255; - } else { - newUnfocusedDotsSize = this._containerSize; - newUnfocusedDotsOpacity = (!isFocused && this._nWindows > 0) ? 255 : 0; - } - - // Only animate if... - // animation is enabled in settings - // AND (going from a wide style to a narrow style indicator or vice-versa - // OR going from an open app to a closed app or vice versa) - let animate = SETTINGS.get_boolean('animate-app-switch') && - ((this._focusedIsWide != this._unfocusedIsWide) || - (this._focusedDots[sizeProp] != newUnfocusedDotsSize || this._unfocusedDots[sizeProp] != newFocusedDotsSize)) - let duration = animate ? Taskbar.DASH_ANIMATION_TIME : 0.001; - - this._animateDotDisplay(this._focusedDots, newFocusedDotsSize, this._unfocusedDots, newUnfocusedDotsOpacity, sizeProp, duration); - this._animateDotDisplay(this._unfocusedDots, newUnfocusedDotsSize, this._focusedDots, newFocusedDotsOpacity, sizeProp, duration); + this._focusedDots.y_align = this._focusedDots.x_align = + Clutter.ActorAlign.FILL + this._focusedDots[(isHorizontalDots ? 'y' : 'x') + '_align'] = align + this._focusedDots.background_color = + this._getRunningIndicatorColor(isFocused) + this._focusedDots.show() + } else if (this._focusedDots.visible) { + this._focusedDots.hide() } + } else { + let sizeProp = isHorizontalDots ? 'width' : 'height' + let newFocusedDotsSize = 0 + let newFocusedDotsOpacity = 0 + let newUnfocusedDotsSize = 0 + let newUnfocusedDotsOpacity = 0 + + isFocused = this._checkIfFocusedApp() && this._checkIfMonitorHasFocus() + + this._timeoutsHandler.add([ + T6, + 0, + () => { + if (isFocused) this.add_style_class_name('focused') + else this.remove_style_class_name('focused') + }, + ]) + + if (this._focusedIsWide) { + newFocusedDotsSize = + isFocused && this._nWindows > 0 ? this._containerSize : 0 + newFocusedDotsOpacity = 255 + } else { + newFocusedDotsSize = this._containerSize + newFocusedDotsOpacity = isFocused && this._nWindows > 0 ? 255 : 0 + } + + if (this._unfocusedIsWide) { + newUnfocusedDotsSize = + !isFocused && this._nWindows > 0 ? this._containerSize : 0 + newUnfocusedDotsOpacity = 255 + } else { + newUnfocusedDotsSize = this._containerSize + newUnfocusedDotsOpacity = !isFocused && this._nWindows > 0 ? 255 : 0 + } + + // Only animate if... + // animation is enabled in settings + // AND (going from a wide style to a narrow style indicator or vice-versa + // OR going from an open app to a closed app or vice versa) + let animate = + SETTINGS.get_boolean('animate-app-switch') && + (this._focusedIsWide != this._unfocusedIsWide || + this._focusedDots[sizeProp] != newUnfocusedDotsSize || + this._unfocusedDots[sizeProp] != newFocusedDotsSize) + let duration = animate ? Taskbar.DASH_ANIMATION_TIME : 0.001 + + this._animateDotDisplay( + this._focusedDots, + newFocusedDotsSize, + this._unfocusedDots, + newUnfocusedDotsOpacity, + sizeProp, + duration, + ) + this._animateDotDisplay( + this._unfocusedDots, + newUnfocusedDotsSize, + this._focusedDots, + newFocusedDotsOpacity, + sizeProp, + duration, + ) + } } - _animateDotDisplay(dots, newSize, otherDots, newOtherOpacity, sizeProp, duration) { - Utils.stopAnimations(dots) + _animateDotDisplay( + dots, + newSize, + otherDots, + newOtherOpacity, + sizeProp, + duration, + ) { + Utils.stopAnimations(dots) - let tweenOpts = { - time: duration, - transition: 'easeInOutCubic', - onComplete: () => { - if (newOtherOpacity > 0) - otherDots.opacity = newOtherOpacity; - } - }; + let tweenOpts = { + time: duration, + transition: 'easeInOutCubic', + onComplete: () => { + if (newOtherOpacity > 0) otherDots.opacity = newOtherOpacity + }, + } - if (newOtherOpacity == 0) - otherDots.opacity = newOtherOpacity; + if (newOtherOpacity == 0) otherDots.opacity = newOtherOpacity - tweenOpts[sizeProp] = newSize; + tweenOpts[sizeProp] = newSize - Utils.animate(dots, tweenOpts); + Utils.animate(dots, tweenOpts) } _isFocusedWindow() { - let focusedWindow = global.display.focus_window; + let focusedWindow = global.display.focus_window - while (focusedWindow) { - if (focusedWindow == this.window) { - return true; - } - - focusedWindow = focusedWindow.get_transient_for(); + while (focusedWindow) { + if (focusedWindow == this.window) { + return true } - return false; + focusedWindow = focusedWindow.get_transient_for() + } + + return false } _isWideDotStyle(dotStyle) { - return dotStyle == DOT_STYLE.SEGMENTED || - dotStyle == DOT_STYLE.CILIORA || - dotStyle == DOT_STYLE.METRO || - dotStyle == DOT_STYLE.SOLID; + return ( + dotStyle == DOT_STYLE.SEGMENTED || + dotStyle == DOT_STYLE.CILIORA || + dotStyle == DOT_STYLE.METRO || + dotStyle == DOT_STYLE.SOLID + ) } _isThemeProvidingIndicator() { - // This is an attempt to determine if the theme is providing their own - // running indicator by way of a border image on the icon, for example in - // the theme Ciliora - return (this.icon.get_stage() && - this.icon.get_theme_node().get_border_image()); + // This is an attempt to determine if the theme is providing their own + // running indicator by way of a border image on the icon, for example in + // the theme Ciliora + return ( + this.icon.get_stage() && this.icon.get_theme_node().get_border_image() + ) } activate(button, modifiers, handleAsGrouped) { - let event = Clutter.get_current_event(); + let event = Clutter.get_current_event() - modifiers = event ? event.get_state() : modifiers || 0; + modifiers = event ? event.get_state() : modifiers || 0 - // Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.) - modifiers = modifiers & (Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK); + // Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.) + modifiers = + modifiers & + (Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK) - let ctrlPressed = modifiers & Clutter.ModifierType.CONTROL_MASK + let ctrlPressed = modifiers & Clutter.ModifierType.CONTROL_MASK - if (ctrlPressed) { - // CTRL-click or hotkey with ctrl - return this._launchNewInstance(true); - } + if (ctrlPressed) { + // CTRL-click or hotkey with ctrl + return this._launchNewInstance(true) + } - // We check what type of click we have and if the modifier SHIFT is - // being used. We then define what buttonAction should be for this - // event. - let buttonAction = 0; - let doubleClick; + // We check what type of click we have and if the modifier SHIFT is + // being used. We then define what buttonAction should be for this + // event. + let buttonAction = 0 + let doubleClick - if (button && button == 2) { - if (modifiers & Clutter.ModifierType.SHIFT_MASK) - buttonAction = SETTINGS.get_string('shift-middle-click-action'); - else - buttonAction = SETTINGS.get_string('middle-click-action'); - } - // fixed issue #1676 by checking for button 0 or 1 to also handle touchscreen - // input, probably not the proper fix as i'm not aware button 0 should exist - // but from using this fix for months it seems to not create any issues - else if (button === 0 || button === 1) { - let now = global.get_current_time() + if (button && button == 2) { + if (modifiers & Clutter.ModifierType.SHIFT_MASK) + buttonAction = SETTINGS.get_string('shift-middle-click-action') + else buttonAction = SETTINGS.get_string('middle-click-action') + } + // fixed issue #1676 by checking for button 0 or 1 to also handle touchscreen + // input, probably not the proper fix as i'm not aware button 0 should exist + // but from using this fix for months it seems to not create any issues + else if (button === 0 || button === 1) { + let now = global.get_current_time() - doubleClick = now - this.lastClick < DOUBLE_CLICK_DELAY_MS - this.lastClick = now + doubleClick = now - this.lastClick < DOUBLE_CLICK_DELAY_MS + this.lastClick = now - if (modifiers & Clutter.ModifierType.SHIFT_MASK) - buttonAction = SETTINGS.get_string('shift-click-action'); - else - buttonAction = SETTINGS.get_string('click-action'); - } + if (modifiers & Clutter.ModifierType.SHIFT_MASK) + buttonAction = SETTINGS.get_string('shift-click-action') + else buttonAction = SETTINGS.get_string('click-action') + } - let closePreview = () => this._previewMenu.close(SETTINGS.get_boolean('window-preview-hide-immediate-click')); - let appCount = this.getAppIconInterestingWindows().length; - let previewedAppIcon = this._previewMenu.getCurrentAppIcon(); + let closePreview = () => + this._previewMenu.close( + SETTINGS.get_boolean('window-preview-hide-immediate-click'), + ) + let appCount = this.getAppIconInterestingWindows().length + let previewedAppIcon = this._previewMenu.getCurrentAppIcon() - if (this.window || buttonAction != 'TOGGLE-SHOWPREVIEW') - closePreview() + if (this.window || buttonAction != 'TOGGLE-SHOWPREVIEW') closePreview() - // We check if the app is running, and that the # of windows is > 0 in - // case we use workspace isolation, - let appIsRunning = this.app.state == Shell.AppState.RUNNING && appCount > 0; + // We check if the app is running, and that the # of windows is > 0 in + // case we use workspace isolation, + let appIsRunning = + this.app.state == Shell.AppState.RUNNING && appCount > 0 - // We customize the action only when the application is already running - if (appIsRunning && !this.isLauncher) { - if (this.window && !handleAsGrouped) { - //ungrouped applications behaviors - switch (buttonAction) { - case 'RAISE': case 'CYCLE': case 'CYCLE-MIN': case 'MINIMIZE': case 'TOGGLE-SHOWPREVIEW': case 'TOGGLE-CYCLE': - if (!Main.overview._shown && - (buttonAction == 'MINIMIZE' || buttonAction == 'TOGGLE-SHOWPREVIEW' || buttonAction == 'TOGGLE-CYCLE' || buttonAction == 'CYCLE-MIN') && - (this._isFocusedWindow() || (buttonAction == 'MINIMIZE' && (button == 2 || modifiers & Clutter.ModifierType.SHIFT_MASK)))) { - this.window.minimize(); - } else { - Main.activateWindow(this.window); - } + // We customize the action only when the application is already running + if (appIsRunning && !this.isLauncher) { + if (this.window && !handleAsGrouped) { + //ungrouped applications behaviors + switch (buttonAction) { + case 'RAISE': + case 'CYCLE': + case 'CYCLE-MIN': + case 'MINIMIZE': + case 'TOGGLE-SHOWPREVIEW': + case 'TOGGLE-CYCLE': + if ( + !Main.overview._shown && + (buttonAction == 'MINIMIZE' || + buttonAction == 'TOGGLE-SHOWPREVIEW' || + buttonAction == 'TOGGLE-CYCLE' || + buttonAction == 'CYCLE-MIN') && + (this._isFocusedWindow() || + (buttonAction == 'MINIMIZE' && + (button == 2 || + modifiers & Clutter.ModifierType.SHIFT_MASK))) + ) { + this.window.minimize() + } else { + Main.activateWindow(this.window) + } - break; + break - case "LAUNCH": - this._launchNewInstance(); - break; + case 'LAUNCH': + this._launchNewInstance() + break - case "QUIT": - this.window.delete(global.get_current_time()); - break; + case 'QUIT': + this.window.delete(global.get_current_time()) + break + } + } else { + //grouped application behaviors + let monitor = this.dtpPanel.monitor + let appHasFocus = + this._checkIfFocusedApp() && this._checkIfMonitorHasFocus() + + switch (buttonAction) { + case 'RAISE': + activateAllWindows(this.app, monitor) + break + + case 'LAUNCH': + this._launchNewInstance() + break + + case 'MINIMIZE': + // In overview just activate the app, unless the acion is explicitely + // requested with a keyboard modifier + if (!Main.overview._shown || modifiers) { + // If we have button=2 or a modifier, allow minimization even if + // the app is not focused + if ( + appHasFocus || + button == 2 || + modifiers & Clutter.ModifierType.SHIFT_MASK + ) { + // minimize all windows on double click and always in the case of primary click without + // additional modifiers + let all_windows = (button == 1 && !modifiers) || doubleClick + minimizeWindow(this.app, all_windows, monitor) + } else activateAllWindows(this.app, monitor) + } else this.app.activate() + break + + case 'CYCLE': + if (!Main.overview._shown) { + if (appHasFocus) + cycleThroughWindows(this.app, false, false, monitor) + else { + activateFirstWindow(this.app, monitor) } - } else { - //grouped application behaviors - let monitor = this.dtpPanel.monitor; - let appHasFocus = this._checkIfFocusedApp() && this._checkIfMonitorHasFocus(); - - switch (buttonAction) { - case "RAISE": - activateAllWindows(this.app, monitor); - break; - - case "LAUNCH": - this._launchNewInstance(); - break; - - case "MINIMIZE": - // In overview just activate the app, unless the acion is explicitely - // requested with a keyboard modifier - if (!Main.overview._shown || modifiers) { - // If we have button=2 or a modifier, allow minimization even if - // the app is not focused - if (appHasFocus || button == 2 || modifiers & Clutter.ModifierType.SHIFT_MASK) { - // minimize all windows on double click and always in the case of primary click without - // additional modifiers - let all_windows = (button == 1 && !modifiers) || doubleClick; - minimizeWindow(this.app, all_windows, monitor); - } - else - activateAllWindows(this.app, monitor); - } - else - this.app.activate(); - break; - - case "CYCLE": - if (!Main.overview._shown) { - if (appHasFocus) - cycleThroughWindows(this.app, false, false, monitor); - else { - activateFirstWindow(this.app, monitor); - } - } - else - this.app.activate(); - break; - case "CYCLE-MIN": - if (!Main.overview._shown) { - if (appHasFocus || (recentlyClickedApp == this.app && recentlyClickedAppWindows[recentlyClickedAppIndex % recentlyClickedAppWindows.length] == "MINIMIZE")) - cycleThroughWindows(this.app, false, true, monitor); - else { - activateFirstWindow(this.app, monitor); - } - } - else - this.app.activate(); - break; - case "TOGGLE-SHOWPREVIEW": - if (!Main.overview._shown) { - if (appCount == 1) { - closePreview() - - if (appHasFocus) - minimizeWindow(this.app, false, monitor); - else - activateFirstWindow(this.app, monitor); - } else { - if (doubleClick) { - // minimize all windows if double clicked - closePreview() - minimizeWindow(this.app, true, monitor); - } else if (previewedAppIcon != this) { - this._previewMenu.open(this); - } - - this.emit('sync-tooltip'); - } - } - else - this.app.activate(); - break; - case "TOGGLE-CYCLE": - if (!Main.overview._shown) { - if (appCount == 1) { - if (appHasFocus) - minimizeWindow(this.app, false, monitor); - else - activateFirstWindow(this.app, monitor); - } else { - cycleThroughWindows(this.app, false, false, monitor); - } - } - else - this.app.activate(); - break; - case "QUIT": - closeAllWindows(this.app, monitor); - break; + } else this.app.activate() + break + case 'CYCLE-MIN': + if (!Main.overview._shown) { + if ( + appHasFocus || + (recentlyClickedApp == this.app && + recentlyClickedAppWindows[ + recentlyClickedAppIndex % recentlyClickedAppWindows.length + ] == 'MINIMIZE') + ) + cycleThroughWindows(this.app, false, true, monitor) + else { + activateFirstWindow(this.app, monitor) } - } - } - else { - this._launchNewInstance(); - } + } else this.app.activate() + break + case 'TOGGLE-SHOWPREVIEW': + if (!Main.overview._shown) { + if (appCount == 1) { + closePreview() - global.display.emit('grab-op-begin', null, null); - Main.overview.hide(); + if (appHasFocus) minimizeWindow(this.app, false, monitor) + else activateFirstWindow(this.app, monitor) + } else { + if (doubleClick) { + // minimize all windows if double clicked + closePreview() + minimizeWindow(this.app, true, monitor) + } else if (previewedAppIcon != this) { + this._previewMenu.open(this) + } + + this.emit('sync-tooltip') + } + } else this.app.activate() + break + case 'TOGGLE-CYCLE': + if (!Main.overview._shown) { + if (appCount == 1) { + if (appHasFocus) minimizeWindow(this.app, false, monitor) + else activateFirstWindow(this.app, monitor) + } else { + cycleThroughWindows(this.app, false, false, monitor) + } + } else this.app.activate() + break + case 'QUIT': + closeAllWindows(this.app, monitor) + break + } + } + } else { + this._launchNewInstance() + } + + global.display.emit('grab-op-begin', null, null) + Main.overview.hide() } _launchNewInstance(ctrlPressed) { - let maybeAnimate = () => SETTINGS.get_boolean('animate-window-launch') && this.animateLaunch() + let maybeAnimate = () => + SETTINGS.get_boolean('animate-window-launch') && this.animateLaunch() - if ((ctrlPressed || this.app.state == Shell.AppState.RUNNING) && - this.app.can_open_new_window()) { - maybeAnimate(); - this.app.open_new_window(-1); + if ( + (ctrlPressed || this.app.state == Shell.AppState.RUNNING) && + this.app.can_open_new_window() + ) { + maybeAnimate() + this.app.open_new_window(-1) + } else { + let windows = this.window ? [this.window] : this.app.get_windows() + + if (windows.length) { + Main.activateWindow(windows[0]) } else { - let windows = this.window ? [this.window] : this.app.get_windows(); - - if (windows.length) { - Main.activateWindow(windows[0]); - } else { - maybeAnimate(); - this.app.activate(); - } + maybeAnimate() + this.app.activate() } + } } _updateWindows() { - let windows = [this.window]; + let windows = [this.window] - if (!this.window) { - windows = this.getAppIconInterestingWindows(); + if (!this.window) { + windows = this.getAppIconInterestingWindows() - this._nWindows = windows.length; + this._nWindows = windows.length - for (let i = 1; i <= MAX_INDICATORS; i++) { - let className = 'running' + i; - if (i != this._nWindows) - this.remove_style_class_name(className); - else - this.add_style_class_name(className); - } + for (let i = 1; i <= MAX_INDICATORS; i++) { + let className = 'running' + i + if (i != this._nWindows) this.remove_style_class_name(className) + else this.add_style_class_name(className) } + } - this._previewMenu.update(this, windows); + this._previewMenu.update(this, windows) } _getRunningIndicatorCount() { - return Math.min(this._nWindows, MAX_INDICATORS); + return Math.min(this._nWindows, MAX_INDICATORS) } _getRunningIndicatorSize() { - return SETTINGS.get_int('dot-size') * Utils.getScaleFactor(); + return SETTINGS.get_int('dot-size') * Utils.getScaleFactor() } _getRunningIndicatorColor(isFocused) { - let color; - const fallbackColor = new Utils.ColorUtils.Color({ red: 82, green: 148, blue: 226, alpha: 255 }); + let color + const fallbackColor = new Utils.ColorUtils.Color({ + red: 82, + green: 148, + blue: 226, + alpha: 255, + }) - if (SETTINGS.get_boolean('dot-color-dominant')) { - let dce = new Utils.DominantColorExtractor(this.app); - let palette = dce._getColorPalette(); - if (palette) { - color = Utils.ColorUtils.color_from_string(palette.original)[1]; - } else { // unable to determine color, fall back to theme - let themeNode = this._dot.get_theme_node(); - color = themeNode.get_background_color(); - - // theme didn't provide one, use a default - if (color.alpha == 0) color = fallbackColor; - } - } else if (SETTINGS.get_boolean('dot-color-override')) { - let dotColorSettingPrefix = 'dot-color-'; - - if (!isFocused && SETTINGS.get_boolean('dot-color-unfocused-different')) - dotColorSettingPrefix = 'dot-color-unfocused-'; - - color = Utils.ColorUtils.color_from_string(SETTINGS.get_string(dotColorSettingPrefix + (this._getRunningIndicatorCount() || 1)))[1]; + if (SETTINGS.get_boolean('dot-color-dominant')) { + let dce = new Utils.DominantColorExtractor(this.app) + let palette = dce._getColorPalette() + if (palette) { + color = Utils.ColorUtils.color_from_string(palette.original)[1] } else { - // Re-use the style - background color, and border width and color - - // of the default dot - let themeNode = this._dot.get_theme_node(); - color = themeNode.get_background_color(); + // unable to determine color, fall back to theme + let themeNode = this._dot.get_theme_node() + color = themeNode.get_background_color() - // theme didn't provide one, use a default - if (color.alpha == 0) color = fallbackColor; + // theme didn't provide one, use a default + if (color.alpha == 0) color = fallbackColor } + } else if (SETTINGS.get_boolean('dot-color-override')) { + let dotColorSettingPrefix = 'dot-color-' - return color; + if (!isFocused && SETTINGS.get_boolean('dot-color-unfocused-different')) + dotColorSettingPrefix = 'dot-color-unfocused-' + + color = Utils.ColorUtils.color_from_string( + SETTINGS.get_string( + dotColorSettingPrefix + (this._getRunningIndicatorCount() || 1), + ), + )[1] + } else { + // Re-use the style - background color, and border width and color - + // of the default dot + let themeNode = this._dot.get_theme_node() + color = themeNode.get_background_color() + + // theme didn't provide one, use a default + if (color.alpha == 0) color = fallbackColor + } + + return color } _getFocusHighlightColor() { - if (SETTINGS.get_boolean('focus-highlight-dominant')) { - let dce = new Utils.DominantColorExtractor(this.app); - let palette = dce._getColorPalette(); - if (palette) return palette.original; - } - return SETTINGS.get_string('focus-highlight-color'); + if (SETTINGS.get_boolean('focus-highlight-dominant')) { + let dce = new Utils.DominantColorExtractor(this.app) + let palette = dce._getColorPalette() + if (palette) return palette.original + } + return SETTINGS.get_string('focus-highlight-color') } _drawRunningIndicator(area, type, isFocused) { - let n = this._getRunningIndicatorCount(); + let n = this._getRunningIndicatorCount() - if (!n) { - return; + if (!n) { + return + } + + let position = SETTINGS.get_string('dot-position') + let isHorizontalDots = + position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM + let bodyColor = this._getRunningIndicatorColor(isFocused) + let [areaWidth, areaHeight] = area.get_surface_size() + let cr = area.get_context() + let size = this._getRunningIndicatorSize() + + let areaSize = areaWidth + let startX = 0 + let startY = 0 + + if (isHorizontalDots) { + if (position == DOT_POSITION.BOTTOM) { + startY = areaHeight - size } + } else { + areaSize = areaHeight - let position = SETTINGS.get_string('dot-position'); - let isHorizontalDots = position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM; - let bodyColor = this._getRunningIndicatorColor(isFocused); - let [areaWidth, areaHeight] = area.get_surface_size(); - let cr = area.get_context(); - let size = this._getRunningIndicatorSize(); + if (position == DOT_POSITION.RIGHT) { + startX = areaWidth - size + } + } - let areaSize = areaWidth; - let startX = 0; - let startY = 0; - - if (isHorizontalDots) { - if (position == DOT_POSITION.BOTTOM) { - startY = areaHeight - size; - } + if (type == DOT_STYLE.SOLID || type == DOT_STYLE.METRO) { + if (type == DOT_STYLE.SOLID || n <= 1) { + cr.translate(startX, startY) + cr.setSourceColor(bodyColor) + cr.newSubPath() + cr.rectangle.apply( + cr, + [0, 0].concat( + isHorizontalDots ? [areaSize, size] : [size, areaSize], + ), + ) + cr.fill() } else { - areaSize = areaHeight; + let blackenedLength = (1 / 48) * areaSize // need to scale with the SVG for the stacked highlight + let darkenedLength = isFocused + ? (2 / 48) * areaSize + : (10 / 48) * areaSize + let blackenedColor = new Utils.ColorUtils.Color({ + red: bodyColor.red * 0.3, + green: bodyColor.green * 0.3, + blue: bodyColor.blue * 0.3, + alpha: bodyColor.alpha, + }) + let darkenedColor = new Utils.ColorUtils.Color({ + red: bodyColor.red * 0.7, + green: bodyColor.green * 0.7, + blue: bodyColor.blue * 0.7, + alpha: bodyColor.alpha, + }) + let solidDarkLength = areaSize - darkenedLength + let solidLength = solidDarkLength - blackenedLength - if (position == DOT_POSITION.RIGHT) { - startX = areaWidth - size; - } + cr.translate(startX, startY) + + cr.setSourceColor(bodyColor) + cr.newSubPath() + cr.rectangle.apply( + cr, + [0, 0].concat( + isHorizontalDots ? [solidLength, size] : [size, solidLength], + ), + ) + cr.fill() + cr.setSourceColor(blackenedColor) + cr.newSubPath() + cr.rectangle.apply( + cr, + isHorizontalDots + ? [solidLength, 0, 1, size] + : [0, solidLength, size, 1], + ) + cr.fill() + cr.setSourceColor(darkenedColor) + cr.newSubPath() + cr.rectangle.apply( + cr, + isHorizontalDots + ? [solidDarkLength, 0, darkenedLength, size] + : [0, solidDarkLength, size, darkenedLength], + ) + cr.fill() + } + } else { + let spacing = Math.ceil(areaSize / 18) // separation between the indicators + let length + let dist + let indicatorSize + let translate + let preDraw = () => {} + let draw + let drawDash = (i, dashLength) => { + dist = i * dashLength + i * spacing + cr.rectangle.apply( + cr, + isHorizontalDots + ? [dist, 0, dashLength, size] + : [0, dist, size, dashLength], + ) } - if (type == DOT_STYLE.SOLID || type == DOT_STYLE.METRO) { - if (type == DOT_STYLE.SOLID || n <= 1) { - cr.translate(startX, startY); - cr.setSourceColor(bodyColor); - cr.newSubPath(); - cr.rectangle.apply(cr, [0, 0].concat(isHorizontalDots ? [areaSize, size] : [size, areaSize])); - cr.fill(); - } else { - let blackenedLength = (1 / 48) * areaSize; // need to scale with the SVG for the stacked highlight - let darkenedLength = isFocused ? (2 / 48) * areaSize : (10 / 48) * areaSize; - let blackenedColor = new Utils.ColorUtils.Color({ red: bodyColor.red * .3, green: bodyColor.green * .3, blue: bodyColor.blue * .3, alpha: bodyColor.alpha }); - let darkenedColor = new Utils.ColorUtils.Color({ red: bodyColor.red * .7, green: bodyColor.green * .7, blue: bodyColor.blue * .7, alpha: bodyColor.alpha }); - let solidDarkLength = areaSize - darkenedLength; - let solidLength = solidDarkLength - blackenedLength; - - cr.translate(startX, startY); - - cr.setSourceColor(bodyColor); - cr.newSubPath(); - cr.rectangle.apply(cr, [0, 0].concat(isHorizontalDots ? [solidLength, size] : [size, solidLength])); - cr.fill(); - cr.setSourceColor(blackenedColor); - cr.newSubPath(); - cr.rectangle.apply(cr, isHorizontalDots ? [solidLength, 0, 1, size] : [0, solidLength, size, 1]); - cr.fill(); - cr.setSourceColor(darkenedColor); - cr.newSubPath(); - cr.rectangle.apply(cr, isHorizontalDots ? [solidDarkLength, 0, darkenedLength, size] : [0, solidDarkLength, size, darkenedLength]); - cr.fill(); + switch (type) { + case DOT_STYLE.CILIORA: + spacing = size + length = areaSize - size * (n - 1) - spacing * (n - 1) + translate = () => cr.translate(startX, startY) + preDraw = () => { + cr.newSubPath() + cr.rectangle.apply( + cr, + [0, 0].concat( + isHorizontalDots ? [length, size] : [size, length], + ), + ) } - } else { - let spacing = Math.ceil(areaSize / 18); // separation between the indicators - let length; - let dist; - let indicatorSize; - let translate; - let preDraw = () => { }; - let draw; - let drawDash = (i, dashLength) => { - dist = i * dashLength + i * spacing; - cr.rectangle.apply(cr, (isHorizontalDots ? [dist, 0, dashLength, size] : [0, dist, size, dashLength])); - }; - - switch (type) { - case DOT_STYLE.CILIORA: - spacing = size; - length = areaSize - (size * (n - 1)) - (spacing * (n - 1)); - translate = () => cr.translate(startX, startY); - preDraw = () => { - cr.newSubPath(); - cr.rectangle.apply(cr, [0, 0].concat(isHorizontalDots ? [length, size] : [size, length])); - }; - draw = i => { - dist = length + (i * spacing) + ((i - 1) * size); - cr.rectangle.apply(cr, (isHorizontalDots ? [dist, 0] : [0, dist]).concat([size, size])); - }; - break; - case DOT_STYLE.DOTS: - let radius = size / 2; - - translate = () => { - indicatorSize = Math.floor((areaSize - n * size - (n - 1) * spacing) / 2); - cr.translate.apply(cr, isHorizontalDots ? [indicatorSize, startY] : [startX, indicatorSize]); - } - draw = i => { - dist = (2 * i + 1) * radius + i * spacing; - cr.arc.apply(cr, (isHorizontalDots ? [dist, radius] : [radius, dist]).concat([radius, 0, 2 * Math.PI])); - }; - break; - case DOT_STYLE.SQUARES: - translate = () => { - indicatorSize = Math.floor((areaSize - n * size - (n - 1) * spacing) / 2); - cr.translate.apply(cr, isHorizontalDots ? [indicatorSize, startY] : [startX, indicatorSize]); - } - draw = i => { - dist = i * size + i * spacing; - cr.rectangle.apply(cr, (isHorizontalDots ? [dist, 0] : [0, dist]).concat([size, size])); - }; - break; - case DOT_STYLE.DASHES: - length = Math.floor(areaSize / 4) - spacing; - translate = () => { - indicatorSize = Math.floor((areaSize - n * length - (n - 1) * spacing) / 2); - cr.translate.apply(cr, isHorizontalDots ? [indicatorSize, startY] : [startX, indicatorSize]); - } - draw = i => drawDash(i, length); - break; - case DOT_STYLE.SEGMENTED: - length = Math.ceil((areaSize - ((n - 1) * spacing)) / n); - translate = () => cr.translate(startX, startY); - draw = i => drawDash(i, length); - break; + draw = (i) => { + dist = length + i * spacing + (i - 1) * size + cr.rectangle.apply( + cr, + (isHorizontalDots ? [dist, 0] : [0, dist]).concat([size, size]), + ) } + break + case DOT_STYLE.DOTS: + let radius = size / 2 - translate(); - - cr.setSourceColor(bodyColor); - preDraw(); - for (let i = 0; i < n; i++) { - cr.newSubPath(); - draw(i); + translate = () => { + indicatorSize = Math.floor( + (areaSize - n * size - (n - 1) * spacing) / 2, + ) + cr.translate.apply( + cr, + isHorizontalDots + ? [indicatorSize, startY] + : [startX, indicatorSize], + ) } - cr.fill(); + draw = (i) => { + dist = (2 * i + 1) * radius + i * spacing + cr.arc.apply( + cr, + (isHorizontalDots ? [dist, radius] : [radius, dist]).concat([ + radius, + 0, + 2 * Math.PI, + ]), + ) + } + break + case DOT_STYLE.SQUARES: + translate = () => { + indicatorSize = Math.floor( + (areaSize - n * size - (n - 1) * spacing) / 2, + ) + cr.translate.apply( + cr, + isHorizontalDots + ? [indicatorSize, startY] + : [startX, indicatorSize], + ) + } + draw = (i) => { + dist = i * size + i * spacing + cr.rectangle.apply( + cr, + (isHorizontalDots ? [dist, 0] : [0, dist]).concat([size, size]), + ) + } + break + case DOT_STYLE.DASHES: + length = Math.floor(areaSize / 4) - spacing + translate = () => { + indicatorSize = Math.floor( + (areaSize - n * length - (n - 1) * spacing) / 2, + ) + cr.translate.apply( + cr, + isHorizontalDots + ? [indicatorSize, startY] + : [startX, indicatorSize], + ) + } + draw = (i) => drawDash(i, length) + break + case DOT_STYLE.SEGMENTED: + length = Math.ceil((areaSize - (n - 1) * spacing) / n) + translate = () => cr.translate(startX, startY) + draw = (i) => drawDash(i, length) + break } - cr.$dispose(); + translate() + + cr.setSourceColor(bodyColor) + preDraw() + for (let i = 0; i < n; i++) { + cr.newSubPath() + draw(i) + } + cr.fill() + } + + cr.$dispose() } _numberOverlay() { - // Add label for a Hot-Key visual aid - this._numberOverlayLabel = new St.Label({ style_class: 'badge' }); - this._numberOverlayBin = new St.Bin({ - child: this._numberOverlayLabel, y: 2 - }); - this._numberOverlayLabel.add_style_class_name('number-overlay'); - this._numberOverlayOrder = -1; - this._numberOverlayBin.hide(); + // Add label for a Hot-Key visual aid + this._numberOverlayLabel = new St.Label({ style_class: 'badge' }) + this._numberOverlayBin = new St.Bin({ + child: this._numberOverlayLabel, + y: 2, + }) + this._numberOverlayLabel.add_style_class_name('number-overlay') + this._numberOverlayOrder = -1 + this._numberOverlayBin.hide() - this._dtpIconContainer.add_child(this._numberOverlayBin); + this._dtpIconContainer.add_child(this._numberOverlayBin) } updateHotkeyNumberOverlay() { - this.updateNumberOverlay(this._numberOverlayBin, true); + this.updateNumberOverlay(this._numberOverlayBin, true) } updateNumberOverlay(bin, fixedSize) { - // We apply an overall scale factor that might come from a HiDPI monitor. - // Clutter dimensions are in physical pixels, but CSS measures are in logical - // pixels, so make sure to consider the scale. - // Set the font size to something smaller than the whole icon so it is - // still visible. The border radius is large to make the shape circular - let [minWidth, natWidth] = this._dtpIconContainer.get_preferred_width(-1); - let font_size = Math.round(Math.max(12, 0.3 * natWidth) / Utils.getScaleFactor()); - let size = Math.round(font_size * 1.3); - let label = bin.child; - let style = 'font-size: ' + font_size + 'px;' + - 'border-radius: ' + this.icon.iconSize + 'px;' + - 'height: ' + size + 'px;'; + // We apply an overall scale factor that might come from a HiDPI monitor. + // Clutter dimensions are in physical pixels, but CSS measures are in logical + // pixels, so make sure to consider the scale. + // Set the font size to something smaller than the whole icon so it is + // still visible. The border radius is large to make the shape circular + let [minWidth, natWidth] = this._dtpIconContainer.get_preferred_width(-1) + let font_size = Math.round( + Math.max(12, 0.3 * natWidth) / Utils.getScaleFactor(), + ) + let size = Math.round(font_size * 1.3) + let label = bin.child + let style = + 'font-size: ' + + font_size + + 'px;' + + 'border-radius: ' + + this.icon.iconSize + + 'px;' + + 'height: ' + + size + + 'px;' - if (fixedSize || label.get_text().length == 1) { - style += 'width: ' + size + 'px;'; - } else { - style += 'padding: 0 2px;'; - } + if (fixedSize || label.get_text().length == 1) { + style += 'width: ' + size + 'px;' + } else { + style += 'padding: 0 2px;' + } - bin.x = 2; - label.set_style(style); + bin.x = 2 + label.set_style(style) } setNumberOverlay(number) { - this._numberOverlayOrder = number; - this._numberOverlayLabel.set_text(number.toString()); + this._numberOverlayOrder = number + this._numberOverlayLabel.set_text(number.toString()) } toggleNumberOverlay(activate) { - if (activate && this._numberOverlayOrder > -1) - this._numberOverlayBin.show(); - else - this._numberOverlayBin.hide(); + if (activate && this._numberOverlayOrder > -1) + this._numberOverlayBin.show() + else this._numberOverlayBin.hide() } handleDragOver(source, actor, x, y, time) { - if (source == Main.xdndHandler) { - this._previewMenu.close(true); - } + if (source == Main.xdndHandler) { + this._previewMenu.close(true) + } - return DND.DragMotionResult.CONTINUE; + return DND.DragMotionResult.CONTINUE } getAppIconInterestingWindows(isolateMonitors) { - return getInterestingWindows(this.app, this.dtpPanel.monitor, isolateMonitors); + return getInterestingWindows( + this.app, + this.dtpPanel.monitor, + isolateMonitors, + ) } -}); -TaskbarAppIcon.prototype.scaleAndFade = TaskbarAppIcon.prototype.undoScaleAndFade = () => { }; + }, +) +TaskbarAppIcon.prototype.scaleAndFade = + TaskbarAppIcon.prototype.undoScaleAndFade = () => {} export function minimizeWindow(app, param, monitor) { - // Param true make all app windows minimize - let windows = getInterestingWindows(app, monitor); - let current_workspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - for (let i = 0; i < windows.length; i++) { - let w = windows[i]; - if (w.get_workspace() == current_workspace && w.showing_on_its_workspace()) { - w.minimize(); - // Just minimize one window. By specification it should be the - // focused window on the current workspace. - if (!param) - break; - } + // Param true make all app windows minimize + let windows = getInterestingWindows(app, monitor) + let current_workspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + for (let i = 0; i < windows.length; i++) { + let w = windows[i] + if ( + w.get_workspace() == current_workspace && + w.showing_on_its_workspace() + ) { + w.minimize() + // Just minimize one window. By specification it should be the + // focused window on the current workspace. + if (!param) break } + } } /* @@ -1419,136 +1836,141 @@ export function minimizeWindow(app, param, monitor) { * This activates all windows in the current workspace. */ export function activateAllWindows(app, monitor) { + // First activate first window so workspace is switched if needed, + // then activate all other app windows in the current workspace. + let windows = getInterestingWindows(app, monitor) + let w = windows[0] + Main.activateWindow(w) + let activeWorkspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace_index() - // First activate first window so workspace is switched if needed, - // then activate all other app windows in the current workspace. - let windows = getInterestingWindows(app, monitor); - let w = windows[0]; - Main.activateWindow(w); - let activeWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace_index(); + if (windows.length <= 0) return - if (windows.length <= 0) - return; - - for (let i = windows.length - 1; i >= 0; i--) { - if (windows[i].get_workspace().index() == activeWorkspace) { - Main.activateWindow(windows[i]); - } + for (let i = windows.length - 1; i >= 0; i--) { + if (windows[i].get_workspace().index() == activeWorkspace) { + Main.activateWindow(windows[i]) } + } } export function activateFirstWindow(app, monitor) { - - let windows = getInterestingWindows(app, monitor); - Main.activateWindow(windows[0]); + let windows = getInterestingWindows(app, monitor) + Main.activateWindow(windows[0]) } export function cycleThroughWindows(app, reversed, shouldMinimize, monitor) { - // Store for a little amount of time last clicked app and its windows - // since the order changes upon window interaction - let MEMORY_TIME = 3000; + // Store for a little amount of time last clicked app and its windows + // since the order changes upon window interaction + let MEMORY_TIME = 3000 - let app_windows = getInterestingWindows(app, monitor); + let app_windows = getInterestingWindows(app, monitor) - if (shouldMinimize) - app_windows.push("MINIMIZE"); + if (shouldMinimize) app_windows.push('MINIMIZE') - if (recentlyClickedAppLoopId > 0) - GLib.Source.remove(recentlyClickedAppLoopId); + if (recentlyClickedAppLoopId > 0) GLib.Source.remove(recentlyClickedAppLoopId) - recentlyClickedAppLoopId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, - MEMORY_TIME, resetRecentlyClickedApp); + recentlyClickedAppLoopId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + MEMORY_TIME, + resetRecentlyClickedApp, + ) - // If there isn't already a list of windows for the current app, - // or the stored list is outdated, use the current windows list. - if (!recentlyClickedApp || - recentlyClickedApp.get_id() != app.get_id() || - recentlyClickedAppWindows.length != app_windows.length || - recentlyClickedAppMonitorIndex != monitor.index) { - recentlyClickedApp = app; - recentlyClickedAppWindows = app_windows; - recentlyClickedAppIndex = 0; - recentlyClickedAppMonitorIndex = monitor.index; - } + // If there isn't already a list of windows for the current app, + // or the stored list is outdated, use the current windows list. + if ( + !recentlyClickedApp || + recentlyClickedApp.get_id() != app.get_id() || + recentlyClickedAppWindows.length != app_windows.length || + recentlyClickedAppMonitorIndex != monitor.index + ) { + recentlyClickedApp = app + recentlyClickedAppWindows = app_windows + recentlyClickedAppIndex = 0 + recentlyClickedAppMonitorIndex = monitor.index + } - if (reversed) { - recentlyClickedAppIndex--; - if (recentlyClickedAppIndex < 0) recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1; - } else { - recentlyClickedAppIndex++; - } - let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length; + if (reversed) { + recentlyClickedAppIndex-- + if (recentlyClickedAppIndex < 0) + recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1 + } else { + recentlyClickedAppIndex++ + } + let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length - if (recentlyClickedAppWindows[index] === "MINIMIZE") - minimizeWindow(app, true, monitor); - else - Main.activateWindow(recentlyClickedAppWindows[index]); + if (recentlyClickedAppWindows[index] === 'MINIMIZE') + minimizeWindow(app, true, monitor) + else Main.activateWindow(recentlyClickedAppWindows[index]) } export function resetRecentlyClickedApp() { - if (recentlyClickedAppLoopId > 0) - GLib.Source.remove(recentlyClickedAppLoopId); + if (recentlyClickedAppLoopId > 0) GLib.Source.remove(recentlyClickedAppLoopId) - recentlyClickedAppLoopId = 0; - recentlyClickedApp = null; - recentlyClickedAppWindows = null; - recentlyClickedAppIndex = 0; - recentlyClickedAppMonitorIndex = null; + recentlyClickedAppLoopId = 0 + recentlyClickedApp = null + recentlyClickedAppWindows = null + recentlyClickedAppIndex = 0 + recentlyClickedAppMonitorIndex = null - return false; + return false } export function closeAllWindows(app, monitor) { - let windows = getInterestingWindows(app, monitor); - for (let i = 0; i < windows.length; i++) - windows[i].delete(global.get_current_time()); + let windows = getInterestingWindows(app, monitor) + for (let i = 0; i < windows.length; i++) + windows[i].delete(global.get_current_time()) } // Filter out unnecessary windows, for instance // nautilus desktop window. export function getInterestingWindows(app, monitor, isolateMonitors) { - let windows = ( - app ? - app.get_windows() : - global.get_window_actors().map(wa => wa.get_meta_window()) - ).filter(w => !w.skip_taskbar); + let windows = ( + app + ? app.get_windows() + : global.get_window_actors().map((wa) => wa.get_meta_window()) + ).filter((w) => !w.skip_taskbar) - // When using workspace or monitor isolation, we filter out windows - // that are not in the current workspace or on the same monitor as the appicon - if (SETTINGS.get_boolean('isolate-workspaces')) - windows = windows.filter(function (w) { - return w.get_workspace() && - w.get_workspace() == Utils.getCurrentWorkspace(); - }); + // When using workspace or monitor isolation, we filter out windows + // that are not in the current workspace or on the same monitor as the appicon + if (SETTINGS.get_boolean('isolate-workspaces')) + windows = windows.filter(function (w) { + return ( + w.get_workspace() && w.get_workspace() == Utils.getCurrentWorkspace() + ) + }) - if (monitor && SETTINGS.get_boolean('multi-monitors') && (isolateMonitors || SETTINGS.get_boolean('isolate-monitors'))) { - windows = windows.filter(function (w) { - return w.get_monitor() == monitor.index; - }); - } + if ( + monitor && + SETTINGS.get_boolean('multi-monitors') && + (isolateMonitors || SETTINGS.get_boolean('isolate-monitors')) + ) { + windows = windows.filter(function (w) { + return w.get_monitor() == monitor.index + }) + } - return windows; + return windows } export function cssHexTocssRgba(cssHex, opacity) { - let bigint = parseInt(cssHex.slice(1), 16); - let r = (bigint >> 16) & 255; - let g = (bigint >> 8) & 255; - let b = bigint & 255; + let bigint = parseInt(cssHex.slice(1), 16) + let r = (bigint >> 16) & 255 + let g = (bigint >> 8) & 255 + let b = bigint & 255 - return 'rgba(' + [r, g, b].join(',') + ',' + opacity + ')'; + return 'rgba(' + [r, g, b].join(',') + ',' + opacity + ')' } export function getIconPadding(monitorIndex) { - let panelSize = PanelSettings.getPanelSize(SETTINGS, monitorIndex); - let padding = SETTINGS.get_int('appicon-padding'); - let availSize = panelSize - Taskbar.MIN_ICON_SIZE - panelSize % 2; + let panelSize = PanelSettings.getPanelSize(SETTINGS, monitorIndex) + let padding = SETTINGS.get_int('appicon-padding') + let availSize = panelSize - Taskbar.MIN_ICON_SIZE - (panelSize % 2) - if (padding * 2 > availSize) { - padding = availSize * .5; - } + if (padding * 2 > availSize) { + padding = availSize * 0.5 + } - return padding; + return padding } /** @@ -1561,140 +1983,141 @@ export function getIconPadding(monitorIndex) { */ export class TaskbarSecondaryMenu extends AppMenu.AppMenu { + constructor(source, side) { + super(source, side) + // constructor parameter does nos work for some reason + this._enableFavorites = true + this._showSingleWindows = true - constructor(source, side) { - super(source, side); - // constructor parameter does nos work for some reason - this._enableFavorites = true; - this._showSingleWindows = true; - - // Remove "Show Details" menu item - if (!SETTINGS.get_boolean('secondarymenu-contains-showdetails')) { - let existingMenuItems = this._getMenuItems(); - for (let i = 0; i < existingMenuItems.length; i++) { - let item = existingMenuItems[i]; - if (item !== undefined && item.label !== undefined) { - if (item.label.text == "Show Details") { - this.box.remove_child(item.actor); - } - } - } + // Remove "Show Details" menu item + if (!SETTINGS.get_boolean('secondarymenu-contains-showdetails')) { + let existingMenuItems = this._getMenuItems() + for (let i = 0; i < existingMenuItems.length; i++) { + let item = existingMenuItems[i] + if (item !== undefined && item.label !== undefined) { + if (item.label.text == 'Show Details') { + this.box.remove_child(item.actor) + } } - - // replace quit item - delete this._quitItem; - this._quitItem = this.addAction(_('Quit'), () => this._quitFromTaskbar()); + } } - updateQuitText() { - let count = this.sourceActor.window ? 1 : - getInterestingWindows(this._app, this.sourceActor.dtpPanel.monitor).length; + // replace quit item + delete this._quitItem + this._quitItem = this.addAction(_('Quit'), () => this._quitFromTaskbar()) + } - if (count > 0) { - let quitFromTaskbarMenuText = ""; - if (count == 1) - quitFromTaskbarMenuText = _("Quit"); - else - quitFromTaskbarMenuText = ngettext('Quit %d Window', 'Quit %d Windows', count).format(count); + updateQuitText() { + let count = this.sourceActor.window + ? 1 + : getInterestingWindows(this._app, this.sourceActor.dtpPanel.monitor) + .length - this._quitItem.label.set_text(quitFromTaskbarMenuText); - } + if (count > 0) { + let quitFromTaskbarMenuText = '' + if (count == 1) quitFromTaskbarMenuText = _('Quit') + else + quitFromTaskbarMenuText = ngettext( + 'Quit %d Window', + 'Quit %d Windows', + count, + ).format(count) + + this._quitItem.label.set_text(quitFromTaskbarMenuText) } + } - _quitFromTaskbar() { - let time = global.get_current_time() - let windows = - this.sourceActor.window ? // ungrouped applications - [this.sourceActor.window] : - getInterestingWindows(this._app, this.sourceActor.dtpPanel.monitor) + _quitFromTaskbar() { + let time = global.get_current_time() + let windows = this.sourceActor.window // ungrouped applications + ? [this.sourceActor.window] + : getInterestingWindows(this._app, this.sourceActor.dtpPanel.monitor) - if (windows.length == this._app.get_windows().length) - this._app.request_quit() + if (windows.length == this._app.get_windows().length) + this._app.request_quit() - GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - windows.forEach((w) => !!w.get_compositor_private() && w.delete(time++)); + GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + windows.forEach((w) => !!w.get_compositor_private() && w.delete(time++)) - return GLib.SOURCE_REMOVE; - }); - } + return GLib.SOURCE_REMOVE + }) + } - setApp(app) { - super.setApp(app); - - this._detailsItem.visible = !app.hideDetails - } + setApp(app) { + super.setApp(app) + + this._detailsItem.visible = !app.hideDetails + } } /** * This function is used for extendDashItemContainer */ export function ItemShowLabel() { - if (!this._labelText) - return; + if (!this._labelText) return - this.label.set_text(this._labelText); - this.label.opacity = 0; - this.label.show(); + this.label.set_text(this._labelText) + this.label.opacity = 0 + this.label.show() - let [stageX, stageY] = this.get_transformed_position(); - let node = this.label.get_theme_node(); + let [stageX, stageY] = this.get_transformed_position() + let node = this.label.get_theme_node() - let itemWidth = this.allocation.x2 - this.allocation.x1; - let itemHeight = this.allocation.y2 - this.allocation.y1; + let itemWidth = this.allocation.x2 - this.allocation.x1 + let itemHeight = this.allocation.y2 - this.allocation.y1 - let labelWidth = this.label.get_width(); - let labelHeight = this.label.get_height(); + let labelWidth = this.label.get_width() + let labelHeight = this.label.get_height() - let position = this._dtpPanel.getPosition(); - let labelOffset = node.get_length('-x-offset'); + let position = this._dtpPanel.getPosition() + let labelOffset = node.get_length('-x-offset') - // From TaskbarItemContainer - if (this._getIconAnimationOffset) - labelOffset += this._getIconAnimationOffset(); + // From TaskbarItemContainer + if (this._getIconAnimationOffset) + labelOffset += this._getIconAnimationOffset() - let xOffset = Math.floor((itemWidth - labelWidth) / 2); - let x = stageX + xOffset - let y = stageY + (itemHeight - labelHeight) * .5; + let xOffset = Math.floor((itemWidth - labelWidth) / 2) + let x = stageX + xOffset + let y = stageY + (itemHeight - labelHeight) * 0.5 - switch (position) { - case St.Side.TOP: - y = stageY + labelOffset + itemHeight; - break; - case St.Side.BOTTOM: - y = stageY - labelHeight - labelOffset; - break; - case St.Side.LEFT: - x = stageX + labelOffset + itemWidth; - break; - case St.Side.RIGHT: - x = stageX - labelWidth - labelOffset; - break; - } + switch (position) { + case St.Side.TOP: + y = stageY + labelOffset + itemHeight + break + case St.Side.BOTTOM: + y = stageY - labelHeight - labelOffset + break + case St.Side.LEFT: + x = stageX + labelOffset + itemWidth + break + case St.Side.RIGHT: + x = stageX - labelWidth - labelOffset + break + } - // keep the label inside the screen border - // Only needed for the x coordinate. + // keep the label inside the screen border + // Only needed for the x coordinate. - // Leave a few pixel gap - let gap = LABEL_GAP; - let monitor = Main.layoutManager.findMonitorForActor(this); - if (x - monitor.x < gap) - x += monitor.x - x + labelOffset; - else if (x + labelWidth > monitor.x + monitor.width - gap) - x -= x + labelWidth - (monitor.x + monitor.width) + gap; + // Leave a few pixel gap + let gap = LABEL_GAP + let monitor = Main.layoutManager.findMonitorForActor(this) + if (x - monitor.x < gap) x += monitor.x - x + labelOffset + else if (x + labelWidth > monitor.x + monitor.width - gap) + x -= x + labelWidth - (monitor.x + monitor.width) + gap - this.label.set_position(Math.round(x), Math.round(y)); + this.label.set_position(Math.round(x), Math.round(y)) - let duration = Dash.DASH_ITEM_LABEL_SHOW_TIME; + let duration = Dash.DASH_ITEM_LABEL_SHOW_TIME - if (duration > 1) { - duration /= 1000; - } + if (duration > 1) { + duration /= 1000 + } - Utils.animate(this.label, { - opacity: 255, - time: duration, - transition: 'easeOutQuad', - }); + Utils.animate(this.label, { + opacity: 255, + time: duration, + transition: 'easeOutQuad', + }) } /** @@ -1710,304 +2133,340 @@ export function ItemShowLabel() { * */ export const ShowAppsIconWrapper = class extends EventEmitter { + constructor(dtpPanel) { + super() - constructor(dtpPanel) { - super(); + this.realShowAppsIcon = new Dash.ShowAppsIcon() - this.realShowAppsIcon = new Dash.ShowAppsIcon(); - - /* the variable equivalent to toggleButton has a different name in the appIcon class + /* the variable equivalent to toggleButton has a different name in the appIcon class (actor): duplicate reference to easily reuse appIcon methods */ - this.actor = this.realShowAppsIcon.toggleButton; - this.realShowAppsIcon.show(false); + this.actor = this.realShowAppsIcon.toggleButton + this.realShowAppsIcon.show(false) - // Re-use appIcon methods - this._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout; - this._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout; - this._onKeyboardPopupMenu = AppDisplay.AppIcon.prototype._onKeyboardPopupMenu; + // Re-use appIcon methods + this._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout + this._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout + this._onKeyboardPopupMenu = + AppDisplay.AppIcon.prototype._onKeyboardPopupMenu - // No action on clicked (showing of the appsview is controlled elsewhere) - this._onClicked = (actor, button) => this._removeMenuTimeout(); + // No action on clicked (showing of the appsview is controlled elsewhere) + this._onClicked = (actor, button) => this._removeMenuTimeout() - this.actor.connect('leave-event', this._onLeaveEvent.bind(this)); - this.actor.connect('button-press-event', this._onButtonPress.bind(this)); - this.actor.connect('touch-event', this._onTouchEvent.bind(this)); - this.actor.connect('clicked', this._onClicked.bind(this)); - this.actor.connect('popup-menu', this._onKeyboardPopupMenu.bind(this)); + this.actor.connect('leave-event', this._onLeaveEvent.bind(this)) + this.actor.connect('button-press-event', this._onButtonPress.bind(this)) + this.actor.connect('touch-event', this._onTouchEvent.bind(this)) + this.actor.connect('clicked', this._onClicked.bind(this)) + this.actor.connect('popup-menu', this._onKeyboardPopupMenu.bind(this)) - this._menu = null; - this._menuManager = new PopupMenu.PopupMenuManager(this.actor); - this._menuTimeoutId = 0; + this._menu = null + this._menuManager = new PopupMenu.PopupMenuManager(this.actor) + this._menuTimeoutId = 0 - this.realShowAppsIcon._dtpPanel = dtpPanel; - Taskbar.extendDashItemContainer(this.realShowAppsIcon); + this.realShowAppsIcon._dtpPanel = dtpPanel + Taskbar.extendDashItemContainer(this.realShowAppsIcon) - let customIconPath = SETTINGS.get_string('show-apps-icon-file'); + let customIconPath = SETTINGS.get_string('show-apps-icon-file') - this.realShowAppsIcon.icon.createIcon = function (size) { - this._iconActor = new St.Icon({ - icon_name: 'view-app-grid-symbolic', - icon_size: size, - style_class: 'show-apps-icon', - track_hover: true - }); + this.realShowAppsIcon.icon.createIcon = function (size) { + this._iconActor = new St.Icon({ + icon_name: 'view-app-grid-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }) - if (customIconPath) { - this._iconActor.gicon = new Gio.FileIcon({ file: Gio.File.new_for_path(customIconPath) }); - } + if (customIconPath) { + this._iconActor.gicon = new Gio.FileIcon({ + file: Gio.File.new_for_path(customIconPath), + }) + } - return this._iconActor; - }; - - this._changedShowAppsIconId = SETTINGS.connect('changed::show-apps-icon-file', () => { - customIconPath = SETTINGS.get_string('show-apps-icon-file'); - this.realShowAppsIcon.icon._createIconTexture(this.realShowAppsIcon.icon.iconSize); - }); - - this._changedAppIconPaddingId = SETTINGS.connect('changed::appicon-padding', () => this.setShowAppsPadding()); - this._changedAppIconSidePaddingId = SETTINGS.connect('changed::show-apps-icon-side-padding', () => this.setShowAppsPadding()); - - this.setShowAppsPadding(); + return this._iconActor } - _onButtonPress(_actor, event) { - let button = event.get_button(); - if (button == 1) { - this._setPopupTimeout(); - } else if (button == 3) { - this.popupMenu(); - return Clutter.EVENT_STOP; - } - return Clutter.EVENT_PROPAGATE; + this._changedShowAppsIconId = SETTINGS.connect( + 'changed::show-apps-icon-file', + () => { + customIconPath = SETTINGS.get_string('show-apps-icon-file') + this.realShowAppsIcon.icon._createIconTexture( + this.realShowAppsIcon.icon.iconSize, + ) + }, + ) + + this._changedAppIconPaddingId = SETTINGS.connect( + 'changed::appicon-padding', + () => this.setShowAppsPadding(), + ) + this._changedAppIconSidePaddingId = SETTINGS.connect( + 'changed::show-apps-icon-side-padding', + () => this.setShowAppsPadding(), + ) + + this.setShowAppsPadding() + } + + _onButtonPress(_actor, event) { + let button = event.get_button() + if (button == 1) { + this._setPopupTimeout() + } else if (button == 3) { + this.popupMenu() + return Clutter.EVENT_STOP } + return Clutter.EVENT_PROPAGATE + } - _onLeaveEvent(_actor, _event) { - this.actor.fake_release(); - this._removeMenuTimeout(); + _onLeaveEvent(_actor, _event) { + this.actor.fake_release() + this._removeMenuTimeout() + } + + _onTouchEvent(actor, event) { + if (event.type() == Clutter.EventType.TOUCH_BEGIN) this._setPopupTimeout() + + return Clutter.EVENT_PROPAGATE + } + + _onMenuPoppedDown() { + this._menu.sourceActor = this.actor + this.actor.sync_hover() + this.emit('menu-state-changed', false) + } + + setShowAppsPadding() { + let padding = getIconPadding(this.realShowAppsIcon._dtpPanel.monitor.index) + let sidePadding = SETTINGS.get_int('show-apps-icon-side-padding') + let isVertical = this.realShowAppsIcon._dtpPanel.checkIfVertical() + + this.actor.set_style( + 'padding:' + + (padding + (isVertical ? sidePadding : 0)) + + 'px ' + + (padding + (isVertical ? 0 : sidePadding)) + + 'px;', + ) + } + + createMenu() { + if (!this._menu) { + this._menu = new MyShowAppsIconMenu( + this.realShowAppsIcon, + this.realShowAppsIcon._dtpPanel, + ) + this._menu.connect('open-state-changed', (menu, isPoppedUp) => { + if (!isPoppedUp) this._onMenuPoppedDown() + }) + let id = Main.overview.connect('hiding', () => { + this._menu.close() + }) + this._menu.actor.connect('destroy', () => { + Main.overview.disconnect(id) + }) + + // We want to keep the item hovered while the menu is up + this._menu.blockSourceEvents = true + + Main.uiGroup.add_child(this._menu.actor) + this._menuManager.addMenu(this._menu) } + } - _onTouchEvent(actor, event) { - if (event.type() == Clutter.EventType.TOUCH_BEGIN) - this._setPopupTimeout(); + popupMenu(sourceActor = null) { + this._removeMenuTimeout() + this.actor.fake_release() + this.createMenu() - return Clutter.EVENT_PROPAGATE; - } + this._menu.updateItems( + sourceActor == null ? this.realShowAppsIcon : sourceActor, + ) - _onMenuPoppedDown() { - this._menu.sourceActor = this.actor; - this.actor.sync_hover(); - this.emit('menu-state-changed', false); - } + this.actor.set_hover(true) + this._menu.open(BoxPointer.PopupAnimation.FULL) + this._menuManager.ignoreRelease() + this.emit('sync-tooltip') - setShowAppsPadding() { - let padding = getIconPadding(this.realShowAppsIcon._dtpPanel.monitor.index); - let sidePadding = SETTINGS.get_int('show-apps-icon-side-padding'); - let isVertical = this.realShowAppsIcon._dtpPanel.checkIfVertical(); + return false + } - this.actor.set_style('padding:' + (padding + (isVertical ? sidePadding : 0)) + 'px ' + (padding + (isVertical ? 0 : sidePadding)) + 'px;'); - } + shouldShowTooltip() { + return ( + SETTINGS.get_boolean('show-tooltip') && + this.actor.hover && + (!this._menu || !this._menu.isOpen) + ) + } - createMenu() { - if (!this._menu) { - this._menu = new MyShowAppsIconMenu(this.realShowAppsIcon, this.realShowAppsIcon._dtpPanel); - this._menu.connect('open-state-changed', (menu, isPoppedUp) => { - if (!isPoppedUp) - this._onMenuPoppedDown(); - }); - let id = Main.overview.connect('hiding', () => { - this._menu.close(); - }); - this._menu.actor.connect('destroy', () => { - Main.overview.disconnect(id); - }); + destroy() { + SETTINGS.disconnect(this._changedShowAppsIconId) + SETTINGS.disconnect(this._changedAppIconSidePaddingId) + SETTINGS.disconnect(this._changedAppIconPaddingId) - // We want to keep the item hovered while the menu is up - this._menu.blockSourceEvents = true; - - Main.uiGroup.add_child(this._menu.actor); - this._menuManager.addMenu(this._menu); - } - } - - popupMenu(sourceActor = null) { - this._removeMenuTimeout(); - this.actor.fake_release(); - this.createMenu(); - - this._menu.updateItems(sourceActor == null ? this.realShowAppsIcon : sourceActor); - - this.actor.set_hover(true); - this._menu.open(BoxPointer.PopupAnimation.FULL); - this._menuManager.ignoreRelease(); - this.emit('sync-tooltip'); - - return false; - } - - shouldShowTooltip() { - return SETTINGS.get_boolean('show-tooltip') && - (this.actor.hover && (!this._menu || !this._menu.isOpen)); - } - - destroy() { - SETTINGS.disconnect(this._changedShowAppsIconId); - SETTINGS.disconnect(this._changedAppIconSidePaddingId); - SETTINGS.disconnect(this._changedAppIconPaddingId); - - this.realShowAppsIcon.destroy(); - } -}; + this.realShowAppsIcon.destroy() + } +} /** * A menu for the showAppsIcon */ export const MyShowAppsIconMenu = class extends PopupMenu.PopupMenu { + constructor(actor, dtpPanel) { + super(actor, 0, dtpPanel.getPosition()) - constructor(actor, dtpPanel) { - super(actor, 0, dtpPanel.getPosition()); + this._dtpPanel = dtpPanel - this._dtpPanel = dtpPanel; + this.updateItems(actor) + } - this.updateItems(actor); + updateItems(sourceActor) { + this.sourceActor = sourceActor + + this.removeAll() + + if (this.sourceActor != Main.layoutManager.dummyCursor) { + this._appendItem({ + title: _('Power options'), + cmd: ['gnome-control-center', 'power'], + }) + + this._appendItem({ + title: _('Event logs'), + cmd: ['gnome-logs'], + }) + + this._appendItem({ + title: _('System'), + cmd: ['gnome-control-center', 'info-overview'], + }) + + this._appendItem({ + title: _('Device Management'), + cmd: ['gnome-control-center', 'display'], + }) + + this._appendItem({ + title: _('Disk Management'), + cmd: ['gnome-disks'], + }) + + this._appendList( + SETTINGS.get_strv('show-apps-button-context-menu-commands'), + SETTINGS.get_strv('show-apps-button-context-menu-titles'), + ) + + this._appendSeparator() } - updateItems(sourceActor) { - this.sourceActor = sourceActor; + this._appendItem({ + title: _('Terminal'), + cmd: [TERMINALSETTINGS.get_string('exec')], + }) - this.removeAll(); + this._appendItem({ + title: _('System monitor'), + cmd: ['gnome-system-monitor'], + }) - if (this.sourceActor != Main.layoutManager.dummyCursor) { - this._appendItem({ - title: _('Power options'), - cmd: ['gnome-control-center', 'power'] - }); + this._appendItem({ + title: _('Files'), + cmd: ['nautilus'], + }) - this._appendItem({ - title: _('Event logs'), - cmd: ['gnome-logs'] - }); + this._appendItem({ + title: _('Extensions'), + cmd: ['gnome-extensions-app'], + }) - this._appendItem({ - title: _('System'), - cmd: ['gnome-control-center', 'info-overview'] - }); + this._appendItem({ + title: _('Settings'), + cmd: ['gnome-control-center'], + }) - this._appendItem({ - title: _('Device Management'), - cmd: ['gnome-control-center', 'display'] - }); + this._appendList( + SETTINGS.get_strv('panel-context-menu-commands'), + SETTINGS.get_strv('panel-context-menu-titles'), + ) - this._appendItem({ - title: _('Disk Management'), - cmd: ['gnome-disks'] - }); + this._appendSeparator() - this._appendList( - SETTINGS.get_strv('show-apps-button-context-menu-commands'), - SETTINGS.get_strv('show-apps-button-context-menu-titles') - ) + let lockTaskbarMenuItem = this._appendMenuItem( + SETTINGS.get_boolean('taskbar-locked') + ? _('Unlock taskbar') + : _('Lock taskbar'), + ) + lockTaskbarMenuItem.connect('activate', () => { + SETTINGS.set_boolean( + 'taskbar-locked', + !SETTINGS.get_boolean('taskbar-locked'), + ) + }) - this._appendSeparator(); - } + let settingsMenuItem = this._appendMenuItem(_('Dash to Panel Settings')) + settingsMenuItem.connect('activate', () => DTP_EXTENSION.openPreferences()) - this._appendItem({ - title: _('Terminal'), - cmd: [TERMINALSETTINGS.get_string('exec')] - }); + if (this.sourceActor == Main.layoutManager.dummyCursor) { + this._appendSeparator() + let item = this._appendMenuItem( + this._dtpPanel._restoreWindowList + ? _('Restore Windows') + : _('Show Desktop'), + ) + item.connect( + 'activate', + this._dtpPanel._onShowDesktopButtonPress.bind(this._dtpPanel), + ) + } + } - this._appendItem({ - title: _('System monitor'), - cmd: ['gnome-system-monitor'] - }); + // Only add menu entries for commands that exist in path + _appendItem(info) { + if (GLib.find_program_in_path(info.cmd[0])) { + let item = this._appendMenuItem(_(info.title)) - this._appendItem({ - title: _('Files'), - cmd: ['nautilus'] - }); - - this._appendItem({ - title: _('Extensions'), - cmd: ['gnome-extensions-app'] - }); - - this._appendItem({ - title: _('Settings'), - cmd: ['gnome-control-center'] - }); - - this._appendList( - SETTINGS.get_strv('panel-context-menu-commands'), - SETTINGS.get_strv('panel-context-menu-titles') - ) - - this._appendSeparator(); - - let lockTaskbarMenuItem = this._appendMenuItem(SETTINGS.get_boolean('taskbar-locked') ? _('Unlock taskbar') : _('Lock taskbar')); - lockTaskbarMenuItem.connect('activate', () => { - SETTINGS.set_boolean('taskbar-locked', !SETTINGS.get_boolean('taskbar-locked')); - }); - - let settingsMenuItem = this._appendMenuItem(_('Dash to Panel Settings')); - settingsMenuItem.connect('activate', () => DTP_EXTENSION.openPreferences()) - - if (this.sourceActor == Main.layoutManager.dummyCursor) { - this._appendSeparator(); - let item = this._appendMenuItem(this._dtpPanel._restoreWindowList ? _('Restore Windows') : _('Show Desktop')); - item.connect('activate', this._dtpPanel._onShowDesktopButtonPress.bind(this._dtpPanel)); - } + item.connect('activate', function () { + print('activated: ' + info.title) + Util.spawn(info.cmd) + }) + return item } + return null + } - // Only add menu entries for commands that exist in path - _appendItem(info) { - if (GLib.find_program_in_path(info.cmd[0])) { - let item = this._appendMenuItem(_(info.title)); - - item.connect('activate', function () { - print("activated: " + info.title); - Util.spawn(info.cmd); - }); - return item; - } - - return null; + _appendList(commandList, titleList) { + if (commandList.length != titleList.length) { + return } - _appendList(commandList, titleList) { - if (commandList.length != titleList.length) { - return; - } - - for (let entry = 0; entry < commandList.length; entry++) { - this._appendItem({ - title: titleList[entry], - cmd: commandList[entry].split(' ') - }); - } + for (let entry = 0; entry < commandList.length; entry++) { + this._appendItem({ + title: titleList[entry], + cmd: commandList[entry].split(' '), + }) } + } - _appendSeparator() { - let separator = new PopupMenu.PopupSeparatorMenuItem(); - this.addMenuItem(separator); - } - - _appendMenuItem(labelText) { - // FIXME: app-well-menu-item style - let item = new PopupMenu.PopupMenuItem(labelText); - this.addMenuItem(item); - return item; - } -}; + _appendSeparator() { + let separator = new PopupMenu.PopupSeparatorMenuItem() + this.addMenuItem(separator) + } + _appendMenuItem(labelText) { + // FIXME: app-well-menu-item style + let item = new PopupMenu.PopupMenuItem(labelText) + this.addMenuItem(item) + return item + } +} export const getIconContainerStyle = function (isVertical) { - let style = 'padding: '; + let style = 'padding: ' - if (SETTINGS.get_boolean('group-apps')) { - style += (isVertical ? '0;' : '0 ' + DEFAULT_PADDING_SIZE + 'px;'); - } else { - style += (isVertical ? '' : '0 ') + DEFAULT_PADDING_SIZE + 'px;'; - } + if (SETTINGS.get_boolean('group-apps')) { + style += isVertical ? '0;' : '0 ' + DEFAULT_PADDING_SIZE + 'px;' + } else { + style += (isVertical ? '' : '0 ') + DEFAULT_PADDING_SIZE + 'px;' + } - return style; + return style } diff --git a/desktopIconsIntegration.js b/desktopIconsIntegration.js index e403516..669fb6d 100644 --- a/desktopIconsIntegration.js +++ b/desktopIconsIntegration.js @@ -55,111 +55,117 @@ * *******************************************************************************/ -import GLib from 'gi://GLib'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js'; -import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; +import GLib from 'gi://GLib' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js' +import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js' -const IDENTIFIER_UUID = "130cbc66-235c-4bd6-8571-98d2d8bba5e2"; +const IDENTIFIER_UUID = '130cbc66-235c-4bd6-8571-98d2d8bba5e2' export class DesktopIconsUsableAreaClass { - _checkIfExtensionIsEnabled(extension) { - return (extension?.state === ExtensionUtils.ExtensionState.ENABLED) || - (extension?.state === ExtensionUtils.ExtensionState.ACTIVE); - } + _checkIfExtensionIsEnabled(extension) { + return ( + extension?.state === ExtensionUtils.ExtensionState.ENABLED || + extension?.state === ExtensionUtils.ExtensionState.ACTIVE + ) + } - constructor() { - const Me = Extension.lookupByURL(import.meta.url); - this._UUID = Me.uuid; - this._extensionManager = Main.extensionManager; - this._timedMarginsID = 0; - this._margins = {}; - this._emID = this._extensionManager.connect('extension-state-changed', (_obj, extension) => { - if (!extension) - return; + constructor() { + const Me = Extension.lookupByURL(import.meta.url) + this._UUID = Me.uuid + this._extensionManager = Main.extensionManager + this._timedMarginsID = 0 + this._margins = {} + this._emID = this._extensionManager.connect( + 'extension-state-changed', + (_obj, extension) => { + if (!extension) return - // If an extension is being enabled and lacks the DesktopIconsUsableArea object, we can avoid launching a refresh - if (this._checkIfExtensionIsEnabled(extension)) { - this._sendMarginsToExtension(extension); - return; - } - // if the extension is being disabled, we must do a full refresh, because if there were other extensions originally - // loaded after that extension, those extensions will be disabled and enabled again without notification - this._changedMargins(); - }); - } - - /** - * Sets or updates the top, bottom, left and right margins for a - * monitor. Values are measured from the monitor border (and NOT from - * the workspace border). - * - * @param {int} monitor Monitor number to which set the margins. - * A negative value means "the primary monitor". - * @param {int} top Top margin in pixels - * @param {int} bottom Bottom margin in pixels - * @param {int} left Left margin in pixels - * @param {int} right Right margin in pixels - */ - setMargins(monitor, top, bottom, left, right) { - this._margins[monitor] = { - 'top': top, - 'bottom': bottom, - 'left': left, - 'right': right - }; - this._changedMargins(); - } - - /** - * Clears the current margins. Must be called before configuring the monitors - * margins with setMargins(). - */ - resetMargins() { - this._margins = {}; - this._changedMargins(); - } - - /** - * Disconnects all the signals and removes the margins. - */ - destroy() { - if (this._emID) { - this._extensionManager.disconnect(this._emID); - this._emID = 0; + // If an extension is being enabled and lacks the DesktopIconsUsableArea object, we can avoid launching a refresh + if (this._checkIfExtensionIsEnabled(extension)) { + this._sendMarginsToExtension(extension) + return } - if (this._timedMarginsID) { - GLib.source_remove(this._timedMarginsID); - this._timedMarginsID = 0; - } - this._margins = null; - this._changedMargins(); - } + // if the extension is being disabled, we must do a full refresh, because if there were other extensions originally + // loaded after that extension, those extensions will be disabled and enabled again without notification + this._changedMargins() + }, + ) + } - _changedMargins() { - if (this._timedMarginsID) { - GLib.source_remove(this._timedMarginsID); - } - this._timedMarginsID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, ()=> { - this._sendMarginsToAll(); - this._timedMarginsID = 0; - return GLib.SOURCE_REMOVE; - }); + /** + * Sets or updates the top, bottom, left and right margins for a + * monitor. Values are measured from the monitor border (and NOT from + * the workspace border). + * + * @param {int} monitor Monitor number to which set the margins. + * A negative value means "the primary monitor". + * @param {int} top Top margin in pixels + * @param {int} bottom Bottom margin in pixels + * @param {int} left Left margin in pixels + * @param {int} right Right margin in pixels + */ + setMargins(monitor, top, bottom, left, right) { + this._margins[monitor] = { + top: top, + bottom: bottom, + left: left, + right: right, } + this._changedMargins() + } - _sendMarginsToAll() { - this._extensionManager.getUuids().forEach(uuid => - this._sendMarginsToExtension(this._extensionManager.lookup(uuid))); + /** + * Clears the current margins. Must be called before configuring the monitors + * margins with setMargins(). + */ + resetMargins() { + this._margins = {} + this._changedMargins() + } + + /** + * Disconnects all the signals and removes the margins. + */ + destroy() { + if (this._emID) { + this._extensionManager.disconnect(this._emID) + this._emID = 0 } - - _sendMarginsToExtension(extension) { - // check that the extension is an extension that has the logic to accept - // working margins - if (!this._checkIfExtensionIsEnabled(extension)) - return; - - const usableArea = extension?.stateObj?.DesktopIconsUsableArea; - if (usableArea?.uuid === IDENTIFIER_UUID) - usableArea.setMarginsForExtension(this._UUID, this._margins); + if (this._timedMarginsID) { + GLib.source_remove(this._timedMarginsID) + this._timedMarginsID = 0 } + this._margins = null + this._changedMargins() + } + + _changedMargins() { + if (this._timedMarginsID) { + GLib.source_remove(this._timedMarginsID) + } + this._timedMarginsID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => { + this._sendMarginsToAll() + this._timedMarginsID = 0 + return GLib.SOURCE_REMOVE + }) + } + + _sendMarginsToAll() { + this._extensionManager + .getUuids() + .forEach((uuid) => + this._sendMarginsToExtension(this._extensionManager.lookup(uuid)), + ) + } + + _sendMarginsToExtension(extension) { + // check that the extension is an extension that has the logic to accept + // working margins + if (!this._checkIfExtensionIsEnabled(extension)) return + + const usableArea = extension?.stateObj?.DesktopIconsUsableArea + if (usableArea?.uuid === IDENTIFIER_UUID) + usableArea.setMarginsForExtension(this._UUID, this._margins) + } } diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..5ac7b1d --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,9 @@ +import globals from 'globals' +import pluginJs from '@eslint/js' +import eslintConfigPrettier from 'eslint-config-prettier' + +export default [ + { languageOptions: { globals: globals.node } }, + pluginJs.configs.recommended, + eslintConfigPrettier, +] diff --git a/extension.js b/extension.js index 13928dd..ab3faee 100644 --- a/extension.js +++ b/extension.js @@ -17,114 +17,129 @@ * */ +import Gio from 'gi://Gio' -import Gio from 'gi://Gio'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' +import { + Extension, + gettext as _, +} from 'resource:///org/gnome/shell/extensions/extension.js' -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; -import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; +import * as PanelManager from './panelManager.js' +import * as AppIcons from './appIcons.js' -import * as PanelManager from './panelManager.js'; -import * as AppIcons from './appIcons.js'; +const UBUNTU_DOCK_UUID = 'ubuntu-dock@ubuntu.com' +let panelManager +let extensionChangedHandler +let startupCompleteHandler +let extensionSystem = Main.extensionManager -const UBUNTU_DOCK_UUID = 'ubuntu-dock@ubuntu.com'; - -let panelManager; -let extensionChangedHandler; -let startupCompleteHandler; -let extensionSystem = Main.extensionManager; - -export let DTP_EXTENSION = null; -export let SETTINGS = null; -export let DESKTOPSETTINGS = null; -export let TERMINALSETTINGS = null; -export let PERSISTENTSTORAGE = null; -export let EXTENSION_UUID = null; -export let EXTENSION_PATH = null; +export let DTP_EXTENSION = null +export let SETTINGS = null +export let DESKTOPSETTINGS = null +export let TERMINALSETTINGS = null +export let PERSISTENTSTORAGE = null +export let EXTENSION_UUID = null +export let EXTENSION_PATH = null export default class DashToPanelExtension extends Extension { - constructor(metadata) { - super(metadata); + constructor(metadata) { + super(metadata) - this._realHasOverview = Main.sessionMode.hasOverview; - - //create an object that persists until gnome-shell is restarted, even if the extension is disabled - PERSISTENTSTORAGE = {}; - } + this._realHasOverview = Main.sessionMode.hasOverview - enable() { - DTP_EXTENSION = this; + //create an object that persists until gnome-shell is restarted, even if the extension is disabled + PERSISTENTSTORAGE = {} + } - // The Ubuntu Dock extension might get enabled after this extension - extensionChangedHandler = extensionSystem.connect('extension-state-changed', (data, extension) => { - if (extension.uuid === UBUNTU_DOCK_UUID && extension.state === 1) { - _enable(this); - } - }); + enable() { + DTP_EXTENSION = this - //create a global object that can emit signals and conveniently expose functionalities to other extensions - global.dashToPanel = new EventEmitter(); - - _enable(this); - } - - disable(reset = false) { - panelManager.disable(); - - DTP_EXTENSION = null; - SETTINGS = null; - DESKTOPSETTINGS = null; - TERMINALSETTINGS = null; - panelManager = null; - - if (!reset) { - extensionSystem.disconnect(extensionChangedHandler); - delete global.dashToPanel; - - AppIcons.resetRecentlyClickedApp(); + // The Ubuntu Dock extension might get enabled after this extension + extensionChangedHandler = extensionSystem.connect( + 'extension-state-changed', + (data, extension) => { + if (extension.uuid === UBUNTU_DOCK_UUID && extension.state === 1) { + _enable(this) } + }, + ) - if (startupCompleteHandler) { - Main.layoutManager.disconnect(startupCompleteHandler); - startupCompleteHandler = null; - } + //create a global object that can emit signals and conveniently expose functionalities to other extensions + global.dashToPanel = new EventEmitter() - Main.sessionMode.hasOverview = this._realHasOverview; + _enable(this) + } + + disable(reset = false) { + panelManager.disable() + + DTP_EXTENSION = null + SETTINGS = null + DESKTOPSETTINGS = null + TERMINALSETTINGS = null + panelManager = null + + if (!reset) { + extensionSystem.disconnect(extensionChangedHandler) + delete global.dashToPanel + + AppIcons.resetRecentlyClickedApp() } + + if (startupCompleteHandler) { + Main.layoutManager.disconnect(startupCompleteHandler) + startupCompleteHandler = null + } + + Main.sessionMode.hasOverview = this._realHasOverview + } } function _enable(extension) { - let enabled = global.settings.get_strv('enabled-extensions'); + let enabled = global.settings.get_strv('enabled-extensions') - if (enabled?.indexOf(UBUNTU_DOCK_UUID) >= 0) - extensionSystem.disableExtension(UBUNTU_DOCK_UUID); + if (enabled?.indexOf(UBUNTU_DOCK_UUID) >= 0) + extensionSystem.disableExtension(UBUNTU_DOCK_UUID) - if (panelManager) - return + if (panelManager) return - SETTINGS = extension.getSettings('org.gnome.shell.extensions.dash-to-panel'); - DESKTOPSETTINGS = new Gio.Settings({schema_id: 'org.gnome.desktop.interface'}); - TERMINALSETTINGS = new Gio.Settings({schema_id: 'org.gnome.desktop.default-applications.terminal'}) - EXTENSION_UUID = extension.uuid - EXTENSION_PATH = extension.path + SETTINGS = extension.getSettings('org.gnome.shell.extensions.dash-to-panel') + DESKTOPSETTINGS = new Gio.Settings({ + schema_id: 'org.gnome.desktop.interface', + }) + TERMINALSETTINGS = new Gio.Settings({ + schema_id: 'org.gnome.desktop.default-applications.terminal', + }) + EXTENSION_UUID = extension.uuid + EXTENSION_PATH = extension.path - Main.layoutManager.startInOverview = !SETTINGS.get_boolean('hide-overview-on-startup'); + Main.layoutManager.startInOverview = !SETTINGS.get_boolean( + 'hide-overview-on-startup', + ) - if (SETTINGS.get_boolean('hide-overview-on-startup') && Main.layoutManager._startingUp) { - Main.sessionMode.hasOverview = false; - startupCompleteHandler = Main.layoutManager.connect('startup-complete', () => { - Main.sessionMode.hasOverview = extension._realHasOverview - }); - } + if ( + SETTINGS.get_boolean('hide-overview-on-startup') && + Main.layoutManager._startingUp + ) { + Main.sessionMode.hasOverview = false + startupCompleteHandler = Main.layoutManager.connect( + 'startup-complete', + () => { + Main.sessionMode.hasOverview = extension._realHasOverview + }, + ) + } - // show the donate icon every 120 days (10368000000 milliseconds) - let donateIconUnixtime = SETTINGS.get_string('hide-donate-icon-unixtime') + // show the donate icon every 120 days (10368000000 milliseconds) + let donateIconUnixtime = SETTINGS.get_string('hide-donate-icon-unixtime') - if (donateIconUnixtime && donateIconUnixtime < Date.now() - 10368000000) - SETTINGS.set_string('hide-donate-icon-unixtime', '') + if (donateIconUnixtime && donateIconUnixtime < Date.now() - 10368000000) + SETTINGS.set_string('hide-donate-icon-unixtime', '') - panelManager = new PanelManager.PanelManager(); + panelManager = new PanelManager.PanelManager() - panelManager.enable(); -} \ No newline at end of file + panelManager.enable() +} diff --git a/intellihide.js b/intellihide.js index 9bc157e..4ccb762 100644 --- a/intellihide.js +++ b/intellihide.js @@ -15,413 +15,474 @@ * along with this program. If not, see . */ -import Clutter from 'gi://Clutter'; -import Meta from 'gi://Meta'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; +import Clutter from 'gi://Clutter' +import Meta from 'gi://Meta' +import Shell from 'gi://Shell' +import St from 'gi://St' -import * as GrabHelper from 'resource:///org/gnome/shell/ui/grabHelper.js'; -import * as Layout from 'resource:///org/gnome/shell/ui/layout.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; -import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js'; +import * as GrabHelper from 'resource:///org/gnome/shell/ui/grabHelper.js' +import * as Layout from 'resource:///org/gnome/shell/ui/layout.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js' +import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js' -import * as Proximity from './proximity.js'; -import * as Utils from './utils.js'; -import {SETTINGS} from './extension.js'; +import * as Proximity from './proximity.js' +import * as Utils from './utils.js' +import { SETTINGS } from './extension.js' //timeout intervals -const CHECK_POINTER_MS = 200; -const CHECK_GRAB_MS = 400; -const POST_ANIMATE_MS = 50; -const MIN_UPDATE_MS = 250; +const CHECK_POINTER_MS = 200 +const CHECK_GRAB_MS = 400 +const POST_ANIMATE_MS = 50 +const MIN_UPDATE_MS = 250 //timeout names -const T1 = 'checkGrabTimeout'; -const T2 = 'limitUpdateTimeout'; -const T3 = 'postAnimateTimeout'; +const T1 = 'checkGrabTimeout' +const T2 = 'limitUpdateTimeout' +const T3 = 'postAnimateTimeout' -const SIDE_CONTROLS_ANIMATION_TIME = OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / (OverviewControls.SIDE_CONTROLS_ANIMATION_TIME > 1 ? 1000 : 1); +const SIDE_CONTROLS_ANIMATION_TIME = + OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / + (OverviewControls.SIDE_CONTROLS_ANIMATION_TIME > 1 ? 1000 : 1) export const Hold = { - NONE: 0, - TEMPORARY: 1, - PERMANENT: 2 -}; + NONE: 0, + TEMPORARY: 1, + PERMANENT: 2, +} export const Intellihide = class { + constructor(dtpPanel) { + this._dtpPanel = dtpPanel + this._panelBox = dtpPanel.panelBox + this._panelManager = dtpPanel.panelManager + this._proximityManager = this._panelManager.proximityManager + this._holdStatus = Hold.NONE - constructor(dtpPanel) { - this._dtpPanel = dtpPanel; - this._panelBox = dtpPanel.panelBox; - this._panelManager = dtpPanel.panelManager; - this._proximityManager = this._panelManager.proximityManager; - this._holdStatus = Hold.NONE; - - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._timeoutsHandler = new Utils.TimeoutsHandler(); + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() - this._intellihideChangedId = SETTINGS.connect('changed::intellihide', () => this._changeEnabledStatus()); - this._intellihideOnlySecondaryChangedId = SETTINGS.connect('changed::intellihide-only-secondary', () => this._changeEnabledStatus()); + this._intellihideChangedId = SETTINGS.connect('changed::intellihide', () => + this._changeEnabledStatus(), + ) + this._intellihideOnlySecondaryChangedId = SETTINGS.connect( + 'changed::intellihide-only-secondary', + () => this._changeEnabledStatus(), + ) - this.enabled = false; - this._changeEnabledStatus(); + this.enabled = false + this._changeEnabledStatus() + } + + enable() { + this.enabled = true + this._monitor = this._dtpPanel.monitor + this._animationDestination = -1 + this._pendingUpdate = false + this._hoveredOut = false + this._windowOverlap = false + this._translationProp = + 'translation_' + (this._dtpPanel.checkIfVertical() ? 'x' : 'y') + + this._panelBox.translation_y = 0 + this._panelBox.translation_x = 0 + + this._setTrackPanel(true) + this._bindGeneralSignals() + + if (SETTINGS.get_boolean('intellihide-hide-from-windows')) { + this._proximityWatchId = this._proximityManager.createWatch( + this._panelBox.get_parent(), + this._dtpPanel.monitor.index, + Proximity.Mode[SETTINGS.get_string('intellihide-behaviour')], + 0, + 0, + (overlap) => { + this._windowOverlap = overlap + this._queueUpdatePanelPosition() + }, + ) } - enable() { - this.enabled = true; - this._monitor = this._dtpPanel.monitor; - this._animationDestination = -1; - this._pendingUpdate = false; - this._hoveredOut = false; - this._windowOverlap = false; - this._translationProp = 'translation_' + (this._dtpPanel.checkIfVertical() ? 'x' : 'y'); + this._setRevealMechanism() + this._queueUpdatePanelPosition() + } - this._panelBox.translation_y = 0; - this._panelBox.translation_x = 0; + disable(reset) { + if (this._proximityWatchId) { + this._proximityManager.removeWatch(this._proximityWatchId) + } - this._setTrackPanel(true); - this._bindGeneralSignals(); + this._setTrackPanel(false) - if (SETTINGS.get_boolean('intellihide-hide-from-windows')) { - this._proximityWatchId = this._proximityManager.createWatch( - this._panelBox.get_parent(), - this._dtpPanel.monitor.index, - Proximity.Mode[SETTINGS.get_string('intellihide-behaviour')], - 0, 0, - overlap => { - this._windowOverlap = overlap; - this._queueUpdatePanelPosition(); - } - ); + this._signalsHandler.destroy() + this._timeoutsHandler.destroy() + + this._removeRevealMechanism() + + this._revealPanel(!reset) + + this.enabled = false + } + + destroy() { + SETTINGS.disconnect(this._intellihideChangedId) + SETTINGS.disconnect(this._intellihideOnlySecondaryChangedId) + + if (this.enabled) { + this.disable() + } + } + + toggle() { + this[this._holdStatus & Hold.PERMANENT ? 'release' : 'revealAndHold']( + Hold.PERMANENT, + ) + } + + revealAndHold(holdStatus) { + if (this.enabled && !this._holdStatus) { + this._revealPanel() + } + + this._holdStatus |= holdStatus + } + + release(holdStatus) { + this._holdStatus -= holdStatus + + if (this.enabled && !this._holdStatus) { + this._queueUpdatePanelPosition() + } + } + + reset() { + this.disable(true) + this.enable() + } + + _changeEnabledStatus() { + let intellihide = SETTINGS.get_boolean('intellihide') + let onlySecondary = SETTINGS.get_boolean('intellihide-only-secondary') + let enabled = intellihide && !(this._dtpPanel.isPrimary && onlySecondary) + + if (this.enabled !== enabled) { + this[enabled ? 'enable' : 'disable']() + } + } + + _bindGeneralSignals() { + this._signalsHandler.add( + [ + this._dtpPanel.taskbar, + ['menu-closed', 'end-drag'], + () => { + this._panelBox.sync_hover() + this._onHoverChanged() + }, + ], + [ + SETTINGS, + [ + 'changed::intellihide-use-pressure', + 'changed::intellihide-hide-from-windows', + 'changed::intellihide-behaviour', + 'changed::intellihide-pressure-threshold', + 'changed::intellihide-pressure-time', + ], + () => this.reset(), + ], + [this._panelBox, 'notify::hover', () => this._onHoverChanged()], + [ + this._dtpPanel.taskbar.previewMenu, + 'open-state-changed', + () => this._queueUpdatePanelPosition(), + ], + [ + Main.overview, + ['showing', 'hiding'], + () => this._queueUpdatePanelPosition(), + ], + ) + + if (Meta.is_wayland_compositor()) { + this._signalsHandler.add([ + this._panelBox, + 'notify::visible', + () => Utils.setDisplayUnredirect(!this._panelBox.visible), + ]) + } + } + + _onHoverChanged() { + this._hoveredOut = !this._panelBox.hover + this._queueUpdatePanelPosition() + } + + _setTrackPanel(enable) { + let actorData = Utils.getTrackedActorData(this._panelBox) + + actorData.affectsStruts = !enable + actorData.trackFullscreen = !enable + + this._panelBox.track_hover = enable + this._panelBox.reactive = enable + this._panelBox.visible = enable ? enable : this._panelBox.visible + + Main.layoutManager._queueUpdateRegions() + } + + _setRevealMechanism() { + let barriers = Meta.BackendCapabilities.BARRIERS + + if ( + (global.backend.capabilities & barriers) === barriers && + SETTINGS.get_boolean('intellihide-use-pressure') + ) { + this._edgeBarrier = this._createBarrier() + this._pressureBarrier = new Layout.PressureBarrier( + SETTINGS.get_int('intellihide-pressure-threshold'), + SETTINGS.get_int('intellihide-pressure-time'), + Shell.ActionMode.NORMAL, + ) + this._pressureBarrier.addBarrier(this._edgeBarrier) + this._signalsHandler.add([ + this._pressureBarrier, + 'trigger', + () => this._queueUpdatePanelPosition(true), + ]) + } else { + this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch( + CHECK_POINTER_MS, + (x, y) => this._checkMousePointer(x, y), + ) + } + } + + _removeRevealMechanism() { + if (this._pointerWatch) { + PointerWatcher.getPointerWatcher()._removeWatch(this._pointerWatch) + } + + if (this._pressureBarrier) { + this._pressureBarrier.destroy() + this._edgeBarrier.destroy() + + this._pressureBarrier = 0 + } + } + + _createBarrier() { + let position = this._dtpPanel.geom.position + let opts = { backend: global.backend } + + if (this._dtpPanel.checkIfVertical()) { + opts.y1 = this._monitor.y + opts.y2 = this._monitor.y + this._monitor.height + opts.x1 = opts.x2 = this._monitor.x + } else { + opts.x1 = this._monitor.x + opts.x2 = this._monitor.x + this._monitor.width + opts.y1 = opts.y2 = this._monitor.y + } + + if (position == St.Side.TOP) { + opts.directions = Meta.BarrierDirection.POSITIVE_Y + } else if (position == St.Side.BOTTOM) { + opts.y1 = opts.y2 = opts.y1 + this._monitor.height + opts.directions = Meta.BarrierDirection.NEGATIVE_Y + } else if (position == St.Side.LEFT) { + opts.directions = Meta.BarrierDirection.POSITIVE_X + } else { + opts.x1 = opts.x2 = opts.x1 + this._monitor.width + opts.directions = Meta.BarrierDirection.NEGATIVE_X + } + + return new Meta.Barrier(opts) + } + + _checkMousePointer(x, y) { + let position = this._dtpPanel.geom.position + + if ( + !this._panelBox.hover && + !Main.overview.visible && + ((position == St.Side.TOP && y <= this._monitor.y + 1) || + (position == St.Side.BOTTOM && + y >= this._monitor.y + this._monitor.height - 1) || + (position == St.Side.LEFT && x <= this._monitor.x + 1) || + (position == St.Side.RIGHT && + x >= this._monitor.x + this._monitor.width - 1)) && + x >= this._monitor.x && + x < this._monitor.x + this._monitor.width && + y >= this._monitor.y && + y < this._monitor.y + this._monitor.height + ) { + this._queueUpdatePanelPosition(true) + } + } + + _queueUpdatePanelPosition(fromRevealMechanism) { + if ( + !fromRevealMechanism && + this._timeoutsHandler.getId(T2) && + !Main.overview.visible + ) { + //unless this is a mouse interaction or entering/leaving the overview, limit the number + //of updates, but remember to update again when the limit timeout is reached + this._pendingUpdate = true + } else if (!this._holdStatus) { + this._checkIfShouldBeVisible(fromRevealMechanism) + ? this._revealPanel() + : this._hidePanel() + this._timeoutsHandler.add([ + T2, + MIN_UPDATE_MS, + () => this._endLimitUpdate(), + ]) + } + } + + _endLimitUpdate() { + if (this._pendingUpdate) { + this._pendingUpdate = false + this._queueUpdatePanelPosition() + } + } + + _checkIfShouldBeVisible(fromRevealMechanism) { + if ( + Main.overview.visibleTarget || + this._dtpPanel.taskbar.previewMenu.opened || + this._dtpPanel.taskbar._dragMonitor || + this._panelBox.get_hover() || + this._checkIfGrab() + ) { + return true + } + + if (fromRevealMechanism) { + let mouseBtnIsPressed = + global.get_pointer()[2] & Clutter.ModifierType.BUTTON1_MASK + + //the user is trying to reveal the panel + if (this._monitor.inFullscreen && !mouseBtnIsPressed) { + return SETTINGS.get_boolean('intellihide-show-in-fullscreen') + } + + return !mouseBtnIsPressed + } + + if (!SETTINGS.get_boolean('intellihide-hide-from-windows')) { + return this._panelBox.hover + } + + return !this._windowOverlap + } + + _checkIfGrab() { + let isGrab + + if (GrabHelper._grabHelperStack) + // gnome-shell < 42 + isGrab = GrabHelper._grabHelperStack.some( + (gh) => gh._owner == this._dtpPanel.panel, + ) + else if (global.stage.get_grab_actor) { + // gnome-shell >= 42 + let grabActor = global.stage.get_grab_actor() + let sourceActor = grabActor?._sourceActor || grabActor + + isGrab = + sourceActor && + (sourceActor == Main.layoutManager.dummyCursor || + this._dtpPanel.statusArea.quickSettings?.menu.actor.contains( + sourceActor, + ) || + this._dtpPanel.panel.contains(sourceActor)) + } + + if (isGrab) + //there currently is a grab on a child of the panel, check again soon to catch its release + this._timeoutsHandler.add([ + T1, + CHECK_GRAB_MS, + () => this._queueUpdatePanelPosition(), + ]) + + return isGrab + } + + _revealPanel(immediate) { + if (!this._panelBox.visible) { + this._panelBox.visible = true + this._dtpPanel.taskbar._shownInitially = false + } + + this._animatePanel(0, immediate) + } + + _hidePanel(immediate) { + let position = this._dtpPanel.geom.position + let size = + this._panelBox[ + position == St.Side.LEFT || position == St.Side.RIGHT + ? 'width' + : 'height' + ] + let coefficient = + position == St.Side.TOP || position == St.Side.LEFT ? -1 : 1 + + this._animatePanel(size * coefficient, immediate) + } + + _animatePanel(destination, immediate) { + let animating = Utils.isAnimating(this._panelBox, this._translationProp) + + if ( + !( + (animating && destination === this._animationDestination) || + (!animating && destination === this._panelBox[this._translationProp]) + ) + ) { + //the panel isn't already at, or animating to the asked destination + if (animating) { + Utils.stopAnimations(this._panelBox) + } + + this._animationDestination = destination + + if (immediate) { + this._panelBox[this._translationProp] = destination + this._panelBox.visible = !destination + } else { + let tweenOpts = { + //when entering/leaving the overview, use its animation time instead of the one from the settings + time: Main.overview.visible + ? SIDE_CONTROLS_ANIMATION_TIME + : SETTINGS.get_int('intellihide-animation-time') * 0.001, + //only delay the animation when hiding the panel after the user hovered out + delay: + destination != 0 && this._hoveredOut + ? SETTINGS.get_int('intellihide-close-delay') * 0.001 + : 0, + transition: 'easeOutQuad', + onComplete: () => { + this._panelBox.visible = !destination + Main.layoutManager._queueUpdateRegions() + this._timeoutsHandler.add([ + T3, + POST_ANIMATE_MS, + () => this._queueUpdatePanelPosition(), + ]) + }, } - this._setRevealMechanism(); - this._queueUpdatePanelPosition(); + tweenOpts[this._translationProp] = destination + Utils.animate(this._panelBox, tweenOpts) + } } - disable(reset) { - if (this._proximityWatchId) { - this._proximityManager.removeWatch(this._proximityWatchId); - } - - this._setTrackPanel(false); - - this._signalsHandler.destroy(); - this._timeoutsHandler.destroy(); - - this._removeRevealMechanism(); - - this._revealPanel(!reset); - - this.enabled = false; - } - - destroy() { - SETTINGS.disconnect(this._intellihideChangedId); - SETTINGS.disconnect(this._intellihideOnlySecondaryChangedId); - - if (this.enabled) { - this.disable(); - } - } - - toggle() { - this[this._holdStatus & Hold.PERMANENT ? 'release' : 'revealAndHold'](Hold.PERMANENT); - } - - revealAndHold(holdStatus) { - if (this.enabled && !this._holdStatus) { - this._revealPanel(); - } - - this._holdStatus |= holdStatus; - } - - release(holdStatus) { - this._holdStatus -= holdStatus; - - if (this.enabled && !this._holdStatus) { - this._queueUpdatePanelPosition(); - } - } - - reset() { - this.disable(true); - this.enable(); - } - - _changeEnabledStatus() { - let intellihide = SETTINGS.get_boolean('intellihide'); - let onlySecondary = SETTINGS.get_boolean('intellihide-only-secondary'); - let enabled = intellihide && !(this._dtpPanel.isPrimary && onlySecondary); - - if (this.enabled !== enabled) { - this[enabled ? 'enable' : 'disable'](); - } - } - - _bindGeneralSignals() { - this._signalsHandler.add( - [ - this._dtpPanel.taskbar, - ['menu-closed', 'end-drag'], - () => { - this._panelBox.sync_hover(); - this._onHoverChanged(); - } - ], - [ - SETTINGS, - [ - 'changed::intellihide-use-pressure', - 'changed::intellihide-hide-from-windows', - 'changed::intellihide-behaviour', - 'changed::intellihide-pressure-threshold', - 'changed::intellihide-pressure-time' - ], - () => this.reset() - ], - [ - this._panelBox, - 'notify::hover', - () => this._onHoverChanged() - ], - [ - this._dtpPanel.taskbar.previewMenu, - 'open-state-changed', - () => this._queueUpdatePanelPosition() - ], - [ - Main.overview, - [ - 'showing', - 'hiding' - ], - () => this._queueUpdatePanelPosition() - ] - ); - - if (Meta.is_wayland_compositor()) { - this._signalsHandler.add([ - this._panelBox, - 'notify::visible', - () => Utils.setDisplayUnredirect(!this._panelBox.visible) - ]); - } - } - - _onHoverChanged() { - this._hoveredOut = !this._panelBox.hover; - this._queueUpdatePanelPosition(); - } - - _setTrackPanel(enable) { - let actorData = Utils.getTrackedActorData(this._panelBox) - - actorData.affectsStruts = !enable; - actorData.trackFullscreen = !enable; - - this._panelBox.track_hover = enable; - this._panelBox.reactive = enable; - this._panelBox.visible = enable ? enable : this._panelBox.visible; - - Main.layoutManager._queueUpdateRegions(); - } - - _setRevealMechanism() { - let barriers = Meta.BackendCapabilities.BARRIERS - - if ((global.backend.capabilities & barriers) === barriers && SETTINGS.get_boolean('intellihide-use-pressure')) { - this._edgeBarrier = this._createBarrier(); - this._pressureBarrier = new Layout.PressureBarrier( - SETTINGS.get_int('intellihide-pressure-threshold'), - SETTINGS.get_int('intellihide-pressure-time'), - Shell.ActionMode.NORMAL - ); - this._pressureBarrier.addBarrier(this._edgeBarrier); - this._signalsHandler.add([this._pressureBarrier, 'trigger', () => this._queueUpdatePanelPosition(true)]); - } else { - this._pointerWatch = PointerWatcher.getPointerWatcher() - .addWatch(CHECK_POINTER_MS, (x, y) => this._checkMousePointer(x, y)); - } - } - - _removeRevealMechanism() { - if (this._pointerWatch) { - PointerWatcher.getPointerWatcher()._removeWatch(this._pointerWatch); - } - - if (this._pressureBarrier) { - this._pressureBarrier.destroy(); - this._edgeBarrier.destroy(); - - this._pressureBarrier = 0; - } - } - - _createBarrier() { - let position = this._dtpPanel.geom.position; - let opts = { backend: global.backend }; - - if (this._dtpPanel.checkIfVertical()) { - opts.y1 = this._monitor.y; - opts.y2 = this._monitor.y + this._monitor.height; - opts.x1 = opts.x2 = this._monitor.x; - } else { - opts.x1 = this._monitor.x; - opts.x2 = this._monitor.x + this._monitor.width; - opts.y1 = opts.y2 = this._monitor.y; - } - - if (position == St.Side.TOP) { - opts.directions = Meta.BarrierDirection.POSITIVE_Y; - } else if (position == St.Side.BOTTOM) { - opts.y1 = opts.y2 = opts.y1 + this._monitor.height; - opts.directions = Meta.BarrierDirection.NEGATIVE_Y; - } else if (position == St.Side.LEFT) { - opts.directions = Meta.BarrierDirection.POSITIVE_X; - } else { - opts.x1 = opts.x2 = opts.x1 + this._monitor.width; - opts.directions = Meta.BarrierDirection.NEGATIVE_X; - } - - return new Meta.Barrier(opts); - } - - _checkMousePointer(x, y) { - let position = this._dtpPanel.geom.position; - - if (!this._panelBox.hover && !Main.overview.visible && - ((position == St.Side.TOP && y <= this._monitor.y + 1) || - (position == St.Side.BOTTOM && y >= this._monitor.y + this._monitor.height - 1) || - (position == St.Side.LEFT && x <= this._monitor.x + 1) || - (position == St.Side.RIGHT && x >= this._monitor.x + this._monitor.width - 1)) && - ((x >= this._monitor.x && x < this._monitor.x + this._monitor.width) && - (y >= this._monitor.y && y < this._monitor.y + this._monitor.height))) { - this._queueUpdatePanelPosition(true); - } - } - - _queueUpdatePanelPosition(fromRevealMechanism) { - if (!fromRevealMechanism && this._timeoutsHandler.getId(T2) && !Main.overview.visible) { - //unless this is a mouse interaction or entering/leaving the overview, limit the number - //of updates, but remember to update again when the limit timeout is reached - this._pendingUpdate = true; - } else if (!this._holdStatus) { - this._checkIfShouldBeVisible(fromRevealMechanism) ? this._revealPanel() : this._hidePanel(); - this._timeoutsHandler.add([T2, MIN_UPDATE_MS, () => this._endLimitUpdate()]); - } - } - - _endLimitUpdate() { - if (this._pendingUpdate) { - this._pendingUpdate = false; - this._queueUpdatePanelPosition(); - } - } - - _checkIfShouldBeVisible(fromRevealMechanism) { - if (Main.overview.visibleTarget || this._dtpPanel.taskbar.previewMenu.opened || - this._dtpPanel.taskbar._dragMonitor || this._panelBox.get_hover() || this._checkIfGrab()) { - return true; - } - - if (fromRevealMechanism) { - let mouseBtnIsPressed = global.get_pointer()[2] & Clutter.ModifierType.BUTTON1_MASK; - - //the user is trying to reveal the panel - if (this._monitor.inFullscreen && !mouseBtnIsPressed) { - return SETTINGS.get_boolean('intellihide-show-in-fullscreen'); - } - - return !mouseBtnIsPressed; - } - - if (!SETTINGS.get_boolean('intellihide-hide-from-windows')) { - return this._panelBox.hover; - } - - return !this._windowOverlap; - } - - _checkIfGrab() { - let isGrab - - if (GrabHelper._grabHelperStack) - // gnome-shell < 42 - isGrab = GrabHelper._grabHelperStack.some(gh => gh._owner == this._dtpPanel.panel) - else if (global.stage.get_grab_actor) { - // gnome-shell >= 42 - let grabActor = global.stage.get_grab_actor() - let sourceActor = grabActor?._sourceActor || grabActor - - isGrab = sourceActor && - (sourceActor == Main.layoutManager.dummyCursor || - this._dtpPanel.statusArea.quickSettings?.menu.actor.contains(sourceActor) || - this._dtpPanel.panel.contains(sourceActor)) - } - - if (isGrab) - //there currently is a grab on a child of the panel, check again soon to catch its release - this._timeoutsHandler.add([T1, CHECK_GRAB_MS, () => this._queueUpdatePanelPosition()]); - - return isGrab; - } - - _revealPanel(immediate) { - if (!this._panelBox.visible) { - this._panelBox.visible = true; - this._dtpPanel.taskbar._shownInitially = false; - } - - this._animatePanel(0, immediate); - } - - _hidePanel(immediate) { - let position = this._dtpPanel.geom.position; - let size = this._panelBox[position == St.Side.LEFT || position == St.Side.RIGHT ? 'width' : 'height']; - let coefficient = position == St.Side.TOP || position == St.Side.LEFT ? -1 : 1; - - this._animatePanel(size * coefficient, immediate); - } - - _animatePanel(destination, immediate) { - let animating = Utils.isAnimating(this._panelBox, this._translationProp); - - if (!((animating && destination === this._animationDestination) || - (!animating && destination === this._panelBox[this._translationProp]))) { - //the panel isn't already at, or animating to the asked destination - if (animating) { - Utils.stopAnimations(this._panelBox); - } - - this._animationDestination = destination; - - if (immediate) { - this._panelBox[this._translationProp] = destination; - this._panelBox.visible = !destination; - } else { - let tweenOpts = { - //when entering/leaving the overview, use its animation time instead of the one from the settings - time: Main.overview.visible ? - SIDE_CONTROLS_ANIMATION_TIME : - SETTINGS.get_int('intellihide-animation-time') * 0.001, - //only delay the animation when hiding the panel after the user hovered out - delay: destination != 0 && this._hoveredOut ? SETTINGS.get_int('intellihide-close-delay') * 0.001 : 0, - transition: 'easeOutQuad', - onComplete: () => { - this._panelBox.visible = !destination; - Main.layoutManager._queueUpdateRegions(); - this._timeoutsHandler.add([T3, POST_ANIMATE_MS, () => this._queueUpdatePanelPosition()]); - } - }; - - tweenOpts[this._translationProp] = destination; - Utils.animate(this._panelBox, tweenOpts); - } - } - - this._hoveredOut = false; - } + this._hoveredOut = false + } } diff --git a/overview.js b/overview.js index 79b7ba9..28a0ca3 100644 --- a/overview.js +++ b/overview.js @@ -16,503 +16,537 @@ * * Credits: * This file is based on code from the Dash to Dock extension by micheleg - * + * * Some code was also adapted from the upstream Gnome Shell source code. */ -import * as Intellihide from './intellihide.js'; -import * as Utils from './utils.js'; +import * as Intellihide from './intellihide.js' +import * as Utils from './utils.js' -import Clutter from 'gi://Clutter'; -import Gio from 'gi://Gio'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as WindowManager from 'resource:///org/gnome/shell/ui/windowManager.js'; -import {WindowPreview} from 'resource:///org/gnome/shell/ui/windowPreview.js'; -import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; -import {SETTINGS} from './extension.js'; +import Clutter from 'gi://Clutter' +import Gio from 'gi://Gio' +import Shell from 'gi://Shell' +import St from 'gi://St' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as WindowManager from 'resource:///org/gnome/shell/ui/windowManager.js' +import { WindowPreview } from 'resource:///org/gnome/shell/ui/windowPreview.js' +import { InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js' +import { SETTINGS } from './extension.js' -const GS_HOTKEYS_KEY = 'switch-to-application-'; +const GS_HOTKEYS_KEY = 'switch-to-application-' // When the dash is shown, workspace window preview bottom labels go over it (default // gnome-shell behavior), but when the extension hides the dash, leave some space // so those labels don't go over a bottom panel -const LABEL_MARGIN = 60; +const LABEL_MARGIN = 60 //timeout names -const T1 = 'swipeEndTimeout'; -const T2 = 'numberOverlayTimeout'; +const T1 = 'swipeEndTimeout' +const T2 = 'numberOverlayTimeout' export const Overview = class { + constructor() { + this._injectionManager = new InjectionManager() + this._numHotkeys = 10 + } - constructor() { - this._injectionManager = new InjectionManager(); - this._numHotkeys = 10; + enable(primaryPanel) { + this._panel = primaryPanel + this.taskbar = primaryPanel.taskbar + + this._injectionsHandler = new Utils.InjectionsHandler() + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() + + this._optionalWorkspaceIsolation() + this._optionalHotKeys() + this._optionalNumberOverlay() + this._optionalClickToExit() + + this.toggleDash() + this._adaptAlloc() + + this._signalsHandler.add([ + SETTINGS, + ['changed::stockgs-keep-dash', 'changed::panel-sizes'], + () => this.toggleDash(), + ]) + } + + disable() { + this._signalsHandler.destroy() + this._injectionsHandler.destroy() + this._timeoutsHandler.destroy() + this._injectionManager.clear() + + this.toggleDash(true) + + // Remove key bindings + this._disableHotKeys() + this._disableExtraShortcut() + this._disableClickToExit() + } + + toggleDash(visible) { + if (visible === undefined) { + visible = SETTINGS.get_boolean('stockgs-keep-dash') } - enable (primaryPanel) { - this._panel = primaryPanel; - this.taskbar = primaryPanel.taskbar; + let visibilityFunc = visible ? 'show' : 'hide' + let height = visible ? -1 : LABEL_MARGIN * Utils.getScaleFactor() + let overviewControls = Main.overview._overview._controls - this._injectionsHandler = new Utils.InjectionsHandler(); - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._timeoutsHandler = new Utils.TimeoutsHandler(); + overviewControls.dash[visibilityFunc]() + overviewControls.dash.set_height(height) + } - this._optionalWorkspaceIsolation(); - this._optionalHotKeys(); - this._optionalNumberOverlay(); - this._optionalClickToExit(); + _adaptAlloc() { + let overviewControls = Main.overview._overview._controls - this.toggleDash(); - this._adaptAlloc(); + this._injectionManager.overrideMethod( + Object.getPrototypeOf(overviewControls), + 'vfunc_allocate', + (originalAllocate) => (box) => { + let focusedPanel = this._panel.panelManager.focusedMonitorPanel - this._signalsHandler.add([ - SETTINGS, - [ - 'changed::stockgs-keep-dash', - 'changed::panel-sizes' - ], - () => this.toggleDash() - ]); - } + if (focusedPanel) { + let position = focusedPanel.geom.position + let isBottom = position == St.Side.BOTTOM - disable() { - this._signalsHandler.destroy(); - this._injectionsHandler.destroy(); - this._timeoutsHandler.destroy(); - this._injectionManager.clear(); + if (focusedPanel.intellihide?.enabled) { + // Panel intellihide is enabled (struts aren't taken into account on overview allocation), + // dynamically modify the overview box to follow the reveal/hide animation + let { transitioning, finalState, progress } = + overviewControls._stateAdjustment.getStateTransitionParams() + let size = + focusedPanel.geom[focusedPanel.checkIfVertical() ? 'w' : 'h'] * + (transitioning + ? Math.abs((finalState != 0 ? 0 : 1) - progress) + : 1) - this.toggleDash(true); - - // Remove key bindings - this._disableHotKeys(); - this._disableExtraShortcut(); - this._disableClickToExit(); - } - - toggleDash(visible) { - if (visible === undefined) { - visible = SETTINGS.get_boolean('stockgs-keep-dash'); + if (isBottom || position == St.Side.RIGHT) + box[focusedPanel.fixedCoord.c2] -= size + else box[focusedPanel.fixedCoord.c1] += size + } else if (isBottom) + // The default overview allocation takes into account external + // struts, everywhere but the bottom where the dash is usually fixed anyway. + // If there is a bottom panel under the dash location, give it some space here + box.y2 -= focusedPanel.geom.h } - let visibilityFunc = visible ? 'show' : 'hide'; - let height = visible ? -1 : LABEL_MARGIN * Utils.getScaleFactor(); - let overviewControls = Main.overview._overview._controls; + originalAllocate.call(overviewControls, box) + }, + ) + } - overviewControls.dash[visibilityFunc](); - overviewControls.dash.set_height(height); + /** + * Isolate overview to open new windows for inactive apps + */ + _optionalWorkspaceIsolation() { + let label = 'optionalWorkspaceIsolation' + + let enable = () => { + this._injectionsHandler.removeWithLabel(label) + + this._injectionsHandler.addWithLabel(label, [ + Shell.App.prototype, + 'activate', + IsolatedOverview, + ]) + + this._signalsHandler.removeWithLabel(label) + + this._signalsHandler.addWithLabel(label, [ + global.window_manager, + 'switch-workspace', + () => + this._panel.panelManager.allPanels.forEach((p) => + p.taskbar.handleIsolatedWorkspaceSwitch(), + ), + ]) } - _adaptAlloc() { - let overviewControls = Main.overview._overview._controls + let disable = () => { + this._signalsHandler.removeWithLabel(label) + this._injectionsHandler.removeWithLabel(label) + } - this._injectionManager.overrideMethod(Object.getPrototypeOf(overviewControls), 'vfunc_allocate', - (originalAllocate) => - (box) => { - let focusedPanel = this._panel.panelManager.focusedMonitorPanel - - if (focusedPanel) { - let position = focusedPanel.geom.position - let isBottom = position == St.Side.BOTTOM + function IsolatedOverview() { + // These lines take care of Nautilus for icons on Desktop + let activeWorkspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + let windows = this.get_windows().filter( + (w) => w.get_workspace().index() == activeWorkspace.index(), + ) - if (focusedPanel.intellihide?.enabled) { - // Panel intellihide is enabled (struts aren't taken into account on overview allocation), - // dynamically modify the overview box to follow the reveal/hide animation - let { transitioning, finalState, progress } = overviewControls._stateAdjustment.getStateTransitionParams() - let size = focusedPanel.geom[focusedPanel.checkIfVertical() ? 'w' : 'h'] * - (transitioning ? Math.abs((finalState != 0 ? 0 : 1) - progress) : 1) + if ( + windows.length > 0 && + (!(windows.length == 1 && windows[0].skip_taskbar) || + this.is_on_workspace(activeWorkspace)) + ) + return Main.activateWindow(windows[0]) - if (isBottom || position == St.Side.RIGHT) - box[focusedPanel.fixedCoord.c2] -= size - else - box[focusedPanel.fixedCoord.c1] += size - } else if (isBottom) - // The default overview allocation takes into account external - // struts, everywhere but the bottom where the dash is usually fixed anyway. - // If there is a bottom panel under the dash location, give it some space here - box.y2 -= focusedPanel.geom.h - } - - originalAllocate.call(overviewControls, box) + return this.open_new_window(-1) + } + + this._signalsHandler.add([ + SETTINGS, + 'changed::isolate-workspaces', + () => { + this._panel.panelManager.allPanels.forEach((p) => + p.taskbar.resetAppIcons(), + ) + + if (SETTINGS.get_boolean('isolate-workspaces')) enable() + else disable() + }, + ]) + + if (SETTINGS.get_boolean('isolate-workspaces')) enable() + } + + // Hotkeys + _activateApp(appIndex, modifiers) { + let seenApps = {} + let apps = [] + + this.taskbar._getAppIcons().forEach((appIcon) => { + if (!seenApps[appIcon.app] || this.taskbar.allowSplitApps) { + apps.push(appIcon) + } + + seenApps[appIcon.app] = (seenApps[appIcon.app] || 0) + 1 + }) + + this._showOverlay() + + if (appIndex < apps.length) { + let appIcon = apps[appIndex] + let seenAppCount = seenApps[appIcon.app] + let windowCount = + appIcon.window || appIcon._hotkeysCycle + ? seenAppCount + : appIcon._nWindows + + if ( + SETTINGS.get_boolean('shortcut-previews') && + windowCount > 1 && + !( + modifiers & + ~(Clutter.ModifierType.MOD1_MASK | Clutter.ModifierType.SUPER_MASK) + ) + ) { + //ignore the alt (MOD1_MASK) and super key (SUPER_MASK) + if ( + this._hotkeyPreviewCycleInfo && + this._hotkeyPreviewCycleInfo.appIcon != appIcon + ) { + this._endHotkeyPreviewCycle() + } + + if (!this._hotkeyPreviewCycleInfo) { + this._hotkeyPreviewCycleInfo = { + appIcon: appIcon, + currentWindow: appIcon.window, + keyFocusOutId: appIcon.connect('key-focus-out', () => + appIcon.grab_key_focus(), + ), + capturedEventId: global.stage.connect( + 'captured-event', + (actor, e) => { + if ( + e.type() == Clutter.EventType.KEY_RELEASE && + e.get_key_symbol() == (Clutter.KEY_Super_L || Clutter.Super_L) + ) { + this._endHotkeyPreviewCycle(true) } - ); - } - /** - * Isolate overview to open new windows for inactive apps - */ - _optionalWorkspaceIsolation() { - let label = 'optionalWorkspaceIsolation'; - - let enable = () => { - this._injectionsHandler.removeWithLabel(label); + return Clutter.EVENT_PROPAGATE + }, + ), + } - this._injectionsHandler.addWithLabel(label, [ - Shell.App.prototype, - 'activate', - IsolatedOverview - ]); - - this._signalsHandler.removeWithLabel(label); - - this._signalsHandler.addWithLabel(label, [ - global.window_manager, - 'switch-workspace', - () => this._panel.panelManager.allPanels.forEach(p => p.taskbar.handleIsolatedWorkspaceSwitch()) - ]); + appIcon._hotkeysCycle = appIcon.window + appIcon.window = null + appIcon._previewMenu.open(appIcon, true) + appIcon.grab_key_focus() } - let disable = () => { - this._signalsHandler.removeWithLabel(label); - this._injectionsHandler.removeWithLabel(label); + appIcon._previewMenu.focusNext() + } else { + // Activate with button = 1, i.e. same as left click + let button = 1 + this._endHotkeyPreviewCycle() + appIcon.activate(button, modifiers, !this.taskbar.allowSplitApps) + } + } + } + + _endHotkeyPreviewCycle(focusWindow) { + if (this._hotkeyPreviewCycleInfo) { + global.stage.disconnect(this._hotkeyPreviewCycleInfo.capturedEventId) + this._hotkeyPreviewCycleInfo.appIcon.disconnect( + this._hotkeyPreviewCycleInfo.keyFocusOutId, + ) + + if (focusWindow) { + this._hotkeyPreviewCycleInfo.appIcon._previewMenu.activateFocused() + } else this._hotkeyPreviewCycleInfo.appIcon._previewMenu.close() + + this._hotkeyPreviewCycleInfo.appIcon.window = + this._hotkeyPreviewCycleInfo.currentWindow + delete this._hotkeyPreviewCycleInfo.appIcon._hotkeysCycle + this._hotkeyPreviewCycleInfo = 0 + } + } + + _optionalHotKeys() { + this._hotKeysEnabled = false + if (SETTINGS.get_boolean('hot-keys')) this._enableHotKeys() + + this._signalsHandler.add([ + SETTINGS, + 'changed::hot-keys', + () => { + if (SETTINGS.get_boolean('hot-keys')) this._enableHotKeys() + else this._disableHotKeys() + }, + ]) + } + + _resetHotkeys() { + this._disableHotKeys() + this._enableHotKeys() + } + + _enableHotKeys() { + if (this._hotKeysEnabled) return + + //3.32 introduced app hotkeys, disable them to prevent conflicts + if (Main.wm._switchToApplication) { + for (let i = 1; i < 10; ++i) { + Utils.removeKeybinding(GS_HOTKEYS_KEY + i) + } + } + + // Setup keyboard bindings for taskbar elements + let shortcutNumKeys = SETTINGS.get_string('shortcut-num-keys') + let bothNumKeys = shortcutNumKeys == 'BOTH' + let keys = [] + let prefixModifiers = Clutter.ModifierType.SUPER_MASK + + if (SETTINGS.get_string('hotkey-prefix-text') == 'SuperAlt') + prefixModifiers |= Clutter.ModifierType.MOD1_MASK + + if (bothNumKeys || shortcutNumKeys == 'NUM_ROW') { + keys.push('app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-') // Regular numbers + } + + if (bothNumKeys || shortcutNumKeys == 'NUM_KEYPAD') { + keys.push('app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-') // Key-pad numbers + } + + keys.forEach(function (key) { + let modifiers = prefixModifiers + + // for some reason, in gnome-shell >= 40 Clutter.get_current_event() is now empty + // for keyboard events. Create here the modifiers that are needed in appicon.activate + modifiers |= + key.indexOf('-shift-') >= 0 ? Clutter.ModifierType.SHIFT_MASK : 0 + modifiers |= + key.indexOf('-ctrl-') >= 0 ? Clutter.ModifierType.CONTROL_MASK : 0 + + for (let i = 0; i < this._numHotkeys; i++) { + let appNum = i + + Utils.addKeybinding(key + (i + 1), SETTINGS, () => + this._activateApp(appNum, modifiers), + ) + } + }, this) + + this._hotKeysEnabled = true + + if (SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS') + this.taskbar.toggleNumberOverlay(true) + } + + _disableHotKeys() { + if (!this._hotKeysEnabled) return + + let keys = [ + 'app-hotkey-', + 'app-shift-hotkey-', + 'app-ctrl-hotkey-', // Regular numbers + 'app-hotkey-kp-', + 'app-shift-hotkey-kp-', + 'app-ctrl-hotkey-kp-', + ] // Key-pad numbers + keys.forEach(function (key) { + for (let i = 0; i < this._numHotkeys; i++) { + Utils.removeKeybinding(key + (i + 1)) + } + }, this) + + if (Main.wm._switchToApplication) { + let gsSettings = new Gio.Settings({ + schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA, + }) + + for (let i = 1; i < 10; ++i) { + Utils.addKeybinding( + GS_HOTKEYS_KEY + i, + gsSettings, + Main.wm._switchToApplication.bind(Main.wm), + ) + } + } + + this._hotKeysEnabled = false + + this.taskbar.toggleNumberOverlay(false) + } + + _optionalNumberOverlay() { + // Enable extra shortcut + if (SETTINGS.get_boolean('hot-keys')) this._enableExtraShortcut() + + this._signalsHandler.add( + [SETTINGS, 'changed::hot-keys', this._checkHotkeysOptions.bind(this)], + [ + SETTINGS, + 'changed::hotkeys-overlay-combo', + () => { + if ( + SETTINGS.get_boolean('hot-keys') && + SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS' + ) + this.taskbar.toggleNumberOverlay(true) + else this.taskbar.toggleNumberOverlay(false) + }, + ], + [SETTINGS, 'changed::shortcut-num-keys', () => this._resetHotkeys()], + ) + } + + _checkHotkeysOptions() { + if (SETTINGS.get_boolean('hot-keys')) this._enableExtraShortcut() + else this._disableExtraShortcut() + } + + _enableExtraShortcut() { + Utils.addKeybinding('shortcut', SETTINGS, () => this._showOverlay(true)) + } + + _disableExtraShortcut() { + Utils.removeKeybinding('shortcut') + } + + _showOverlay(overlayFromShortcut) { + //wait for intellihide timeout initialization + if (!this._panel.intellihide) { + return + } + + // Restart the counting if the shortcut is pressed again + let hotkey_option = SETTINGS.get_string('hotkeys-overlay-combo') + + if (hotkey_option === 'NEVER') return + + if (hotkey_option === 'TEMPORARILY' || overlayFromShortcut) + this.taskbar.toggleNumberOverlay(true) + + this._panel.intellihide.revealAndHold(Intellihide.Hold.TEMPORARY) + + let timeout = SETTINGS.get_int('overlay-timeout') + + if (overlayFromShortcut) { + timeout = SETTINGS.get_int('shortcut-timeout') + } + + // Hide the overlay/dock after the timeout + this._timeoutsHandler.add([ + T2, + timeout, + () => { + if (hotkey_option != 'ALWAYS') { + this.taskbar.toggleNumberOverlay(false) } - function IsolatedOverview() { - // These lines take care of Nautilus for icons on Desktop - let activeWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - let windows = this.get_windows().filter(w => w.get_workspace().index() == activeWorkspace.index()); + this._panel.intellihide.release(Intellihide.Hold.TEMPORARY) + }, + ]) + } - if (windows.length > 0 && - (!(windows.length == 1 && windows[0].skip_taskbar) || - this.is_on_workspace(activeWorkspace))) - return Main.activateWindow(windows[0]); - - return this.open_new_window(-1); - } + _optionalClickToExit() { + this._clickToExitEnabled = false + if (SETTINGS.get_boolean('overview-click-to-exit')) + this._enableClickToExit() - this._signalsHandler.add([ - SETTINGS, - 'changed::isolate-workspaces', - () => { - this._panel.panelManager.allPanels.forEach(p => p.taskbar.resetAppIcons()); - - if (SETTINGS.get_boolean('isolate-workspaces')) - enable(); - else - disable(); - } - ]); - - if (SETTINGS.get_boolean('isolate-workspaces')) - enable(); - } - - // Hotkeys - _activateApp(appIndex, modifiers) { - let seenApps = {}; - let apps = []; - - this.taskbar._getAppIcons().forEach(appIcon => { - if (!seenApps[appIcon.app] || this.taskbar.allowSplitApps) { - apps.push(appIcon); - } - - seenApps[appIcon.app] = (seenApps[appIcon.app] || 0) + 1; - }); - - this._showOverlay(); - - if (appIndex < apps.length) { - let appIcon = apps[appIndex]; - let seenAppCount = seenApps[appIcon.app]; - let windowCount = appIcon.window || appIcon._hotkeysCycle ? seenAppCount : appIcon._nWindows; - - if (SETTINGS.get_boolean('shortcut-previews') && windowCount > 1 && - !(modifiers & ~(Clutter.ModifierType.MOD1_MASK | Clutter.ModifierType.SUPER_MASK))) { //ignore the alt (MOD1_MASK) and super key (SUPER_MASK) - if (this._hotkeyPreviewCycleInfo && this._hotkeyPreviewCycleInfo.appIcon != appIcon) { - this._endHotkeyPreviewCycle(); - } - - if (!this._hotkeyPreviewCycleInfo) { - this._hotkeyPreviewCycleInfo = { - appIcon: appIcon, - currentWindow: appIcon.window, - keyFocusOutId: appIcon.connect('key-focus-out', () => appIcon.grab_key_focus()), - capturedEventId: global.stage.connect('captured-event', (actor, e) => { - if (e.type() == Clutter.EventType.KEY_RELEASE && e.get_key_symbol() == (Clutter.KEY_Super_L || Clutter.Super_L)) { - this._endHotkeyPreviewCycle(true); - } - - return Clutter.EVENT_PROPAGATE; - }) - }; - - appIcon._hotkeysCycle = appIcon.window; - appIcon.window = null; - appIcon._previewMenu.open(appIcon, true); - appIcon.grab_key_focus(); - } - - appIcon._previewMenu.focusNext(); - } else { - // Activate with button = 1, i.e. same as left click - let button = 1; - this._endHotkeyPreviewCycle(); - appIcon.activate(button, modifiers, !this.taskbar.allowSplitApps); - } - } - } - - _endHotkeyPreviewCycle(focusWindow) { - if (this._hotkeyPreviewCycleInfo) { - global.stage.disconnect(this._hotkeyPreviewCycleInfo.capturedEventId); - this._hotkeyPreviewCycleInfo.appIcon.disconnect(this._hotkeyPreviewCycleInfo.keyFocusOutId); - - if (focusWindow) { - this._hotkeyPreviewCycleInfo.appIcon._previewMenu.activateFocused(); - } else - this._hotkeyPreviewCycleInfo.appIcon._previewMenu.close() - - this._hotkeyPreviewCycleInfo.appIcon.window = this._hotkeyPreviewCycleInfo.currentWindow; - delete this._hotkeyPreviewCycleInfo.appIcon._hotkeysCycle; - this._hotkeyPreviewCycleInfo = 0; - } - } - - _optionalHotKeys() { - this._hotKeysEnabled = false; - if (SETTINGS.get_boolean('hot-keys')) - this._enableHotKeys(); - - this._signalsHandler.add([ - SETTINGS, - 'changed::hot-keys', - () => { - if (SETTINGS.get_boolean('hot-keys')) - this._enableHotKeys(); - else - this._disableHotKeys(); - } - ]); - } - - _resetHotkeys() { - this._disableHotKeys(); - this._enableHotKeys(); - } - - _enableHotKeys() { - if (this._hotKeysEnabled) - return; - - //3.32 introduced app hotkeys, disable them to prevent conflicts - if (Main.wm._switchToApplication) { - for (let i = 1; i < 10; ++i) { - Utils.removeKeybinding(GS_HOTKEYS_KEY + i); - } - } - - // Setup keyboard bindings for taskbar elements - let shortcutNumKeys = SETTINGS.get_string('shortcut-num-keys'); - let bothNumKeys = shortcutNumKeys == 'BOTH'; - let keys = []; - let prefixModifiers = Clutter.ModifierType.SUPER_MASK - - if (SETTINGS.get_string('hotkey-prefix-text') == 'SuperAlt') - prefixModifiers |= Clutter.ModifierType.MOD1_MASK - - if (bothNumKeys || shortcutNumKeys == 'NUM_ROW') { - keys.push('app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'); // Regular numbers - } - - if (bothNumKeys || shortcutNumKeys == 'NUM_KEYPAD') { - keys.push('app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-'); // Key-pad numbers - } - - keys.forEach( function(key) { - let modifiers = prefixModifiers - - // for some reason, in gnome-shell >= 40 Clutter.get_current_event() is now empty - // for keyboard events. Create here the modifiers that are needed in appicon.activate - modifiers |= (key.indexOf('-shift-') >= 0 ? Clutter.ModifierType.SHIFT_MASK : 0) - modifiers |= (key.indexOf('-ctrl-') >= 0 ? Clutter.ModifierType.CONTROL_MASK : 0) - - for (let i = 0; i < this._numHotkeys; i++) { - let appNum = i; - - Utils.addKeybinding(key + (i + 1), SETTINGS, () => this._activateApp(appNum, modifiers)); - } - }, this); - - this._hotKeysEnabled = true; - - if (SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS') - this.taskbar.toggleNumberOverlay(true); - } - - _disableHotKeys() { - if (!this._hotKeysEnabled) - return; - - let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-', // Regular numbers - 'app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-']; // Key-pad numbers - keys.forEach( function(key) { - for (let i = 0; i < this._numHotkeys; i++) { - Utils.removeKeybinding(key + (i + 1)); - } - }, this); - - if (Main.wm._switchToApplication) { - let gsSettings = new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }); - - for (let i = 1; i < 10; ++i) { - Utils.addKeybinding(GS_HOTKEYS_KEY + i, gsSettings, Main.wm._switchToApplication.bind(Main.wm)); - } - } - - this._hotKeysEnabled = false; - - this.taskbar.toggleNumberOverlay(false); - } - - _optionalNumberOverlay() { - // Enable extra shortcut - if (SETTINGS.get_boolean('hot-keys')) - this._enableExtraShortcut(); - - this._signalsHandler.add([ - SETTINGS, - 'changed::hot-keys', - this._checkHotkeysOptions.bind(this) - ], [ - SETTINGS, - 'changed::hotkeys-overlay-combo', - () => { - if (SETTINGS.get_boolean('hot-keys') && SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS') - this.taskbar.toggleNumberOverlay(true); - else - this.taskbar.toggleNumberOverlay(false); - } - ], [ - SETTINGS, - 'changed::shortcut-num-keys', - () => this._resetHotkeys() - ]); - } - - _checkHotkeysOptions() { - if (SETTINGS.get_boolean('hot-keys')) - this._enableExtraShortcut(); - else - this._disableExtraShortcut(); - } - - _enableExtraShortcut() { - Utils.addKeybinding('shortcut', SETTINGS, () => this._showOverlay(true)); - } - - _disableExtraShortcut() { - Utils.removeKeybinding('shortcut'); - } - - _showOverlay(overlayFromShortcut) { - //wait for intellihide timeout initialization - if (!this._panel.intellihide) { - return; - } - - // Restart the counting if the shortcut is pressed again - let hotkey_option = SETTINGS.get_string('hotkeys-overlay-combo'); - - if (hotkey_option === 'NEVER') - return; - - if (hotkey_option === 'TEMPORARILY' || overlayFromShortcut) - this.taskbar.toggleNumberOverlay(true); - - this._panel.intellihide.revealAndHold(Intellihide.Hold.TEMPORARY); - - let timeout = SETTINGS.get_int('overlay-timeout'); - - if (overlayFromShortcut) { - timeout = SETTINGS.get_int('shortcut-timeout'); - } - - // Hide the overlay/dock after the timeout - this._timeoutsHandler.add([T2, timeout, () => { - if (hotkey_option != 'ALWAYS') { - this.taskbar.toggleNumberOverlay(false); - } - - this._panel.intellihide.release(Intellihide.Hold.TEMPORARY); - }]); - } - - _optionalClickToExit() { - this._clickToExitEnabled = false; + this._signalsHandler.add([ + SETTINGS, + 'changed::overview-click-to-exit', + () => { if (SETTINGS.get_boolean('overview-click-to-exit')) - this._enableClickToExit(); + this._enableClickToExit() + else this._disableClickToExit() + }, + ]) + } - this._signalsHandler.add([ - SETTINGS, - 'changed::overview-click-to-exit', - () => { - if (SETTINGS.get_boolean('overview-click-to-exit')) - this._enableClickToExit(); - else - this._disableClickToExit(); - } - ]); - } + _enableClickToExit() { + if (this._clickToExitEnabled) return - _enableClickToExit() { - if (this._clickToExitEnabled) - return; + this._signalsHandler.addWithLabel('click-to-exit', [ + Main.layoutManager.overviewGroup, + 'button-release-event', + () => { + let [x, y] = global.get_pointer() + let pickedActor = global.stage.get_actor_at_pos( + Clutter.PickMode.REACTIVE, + x, + y, + ) - this._signalsHandler.addWithLabel('click-to-exit', [ - Main.layoutManager.overviewGroup, - 'button-release-event', - () => { - let [x, y] = global.get_pointer(); - let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); - - if (pickedActor) { - let parent = pickedActor.get_parent(); + if (pickedActor) { + let parent = pickedActor.get_parent() - if ( - ( - pickedActor.has_style_class_name && - pickedActor.has_style_class_name('apps-scroll-view') && - !pickedActor.has_style_pseudo_class('first-child') - ) || ( - parent?.has_style_class_name && - parent.has_style_class_name('window-picker') - ) || - Main.overview._overview._controls._searchEntryBin.contains(pickedActor) || - pickedActor instanceof WindowPreview - ) - return Clutter.EVENT_PROPAGATE - } + if ( + (pickedActor.has_style_class_name && + pickedActor.has_style_class_name('apps-scroll-view') && + !pickedActor.has_style_pseudo_class('first-child')) || + (parent?.has_style_class_name && + parent.has_style_class_name('window-picker')) || + Main.overview._overview._controls._searchEntryBin.contains( + pickedActor, + ) || + pickedActor instanceof WindowPreview + ) + return Clutter.EVENT_PROPAGATE + } - Main.overview.toggle() - } - ]); + Main.overview.toggle() + }, + ]) - this._clickToExitEnabled = true; - } + this._clickToExitEnabled = true + } - _disableClickToExit() { - if (!this._clickToExitEnabled) - return; - - this._signalsHandler.removeWithLabel('click-to-exit') + _disableClickToExit() { + if (!this._clickToExitEnabled) return - this._clickToExitEnabled = false; - } + this._signalsHandler.removeWithLabel('click-to-exit') - _onSwipeBegin() { - this._swiping = true; - return true; - } + this._clickToExitEnabled = false + } - _onSwipeEnd() { - this._timeoutsHandler.add([ - T1, - 0, - () => this._swiping = false - ]); - return true; - } + _onSwipeBegin() { + this._swiping = true + return true + } + _onSwipeEnd() { + this._timeoutsHandler.add([T1, 0, () => (this._swiping = false)]) + return true + } } diff --git a/package.json b/package.json new file mode 100644 index 0000000..4937228 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "type": "module", + "scripts": { + "lint": "prettier 'src/**/*.js' --write && eslint 'src/**/*.js' --fix" + }, + "devDependencies": { + "@eslint/js": "^9.19.0", + "eslint": "^9.19.0", + "eslint-config-prettier": "^10.0.1", + "globals": "^15.14.0", + "prettier": "^3.4.2" + } +} diff --git a/panel.js b/panel.js index a0bfdac..f5745fb 100644 --- a/panel.js +++ b/panel.js @@ -27,1239 +27,1484 @@ * Some code was also adapted from the upstream Gnome Shell source code. */ -import Clutter from 'gi://Clutter'; -import GObject from 'gi://GObject'; -import * as AppIcons from './appIcons.js'; -import * as Utils from './utils.js'; -import * as Taskbar from './taskbar.js'; -import * as TaskbarItemContainer from './taskbar.js'; -import * as Pos from './panelPositions.js'; -import * as PanelSettings from './panelSettings.js'; -import * as PanelStyle from './panelStyle.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as Dash from 'resource:///org/gnome/shell/ui/dash.js'; -import * as CtrlAltTab from 'resource:///org/gnome/shell/ui/ctrlAltTab.js'; -import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; -import St from 'gi://St'; -import Meta from 'gi://Meta'; -import Pango from 'gi://Pango'; -import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; -import Shell from 'gi://Shell'; -import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; -import * as DateMenu from 'resource:///org/gnome/shell/ui/dateMenu.js'; -import * as Volume from 'resource:///org/gnome/shell/ui/status/volume.js'; -import * as Progress from './progress.js'; +import Clutter from 'gi://Clutter' +import GObject from 'gi://GObject' +import * as AppIcons from './appIcons.js' +import * as Utils from './utils.js' +import * as Taskbar from './taskbar.js' +import * as TaskbarItemContainer from './taskbar.js' +import * as Pos from './panelPositions.js' +import * as PanelSettings from './panelSettings.js' +import * as PanelStyle from './panelStyle.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as Dash from 'resource:///org/gnome/shell/ui/dash.js' +import * as CtrlAltTab from 'resource:///org/gnome/shell/ui/ctrlAltTab.js' +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js' +import St from 'gi://St' +import Meta from 'gi://Meta' +import Pango from 'gi://Pango' +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js' +import Shell from 'gi://Shell' +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js' +import * as DateMenu from 'resource:///org/gnome/shell/ui/dateMenu.js' +import * as Volume from 'resource:///org/gnome/shell/ui/status/volume.js' +import * as Progress from './progress.js' -import * as Intellihide from './intellihide.js'; -import * as Transparency from './transparency.js'; -import {SETTINGS, DESKTOPSETTINGS, PERSISTENTSTORAGE} from './extension.js'; -import {gettext as _, InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; +import * as Intellihide from './intellihide.js' +import * as Transparency from './transparency.js' +import { SETTINGS, DESKTOPSETTINGS, PERSISTENTSTORAGE } from './extension.js' +import { + gettext as _, + InjectionManager, +} from 'resource:///org/gnome/shell/extensions/extension.js' -let tracker = Shell.WindowTracker.get_default(); -export const panelBoxes = ['_leftBox', '_centerBox', '_rightBox']; +let tracker = Shell.WindowTracker.get_default() +export const panelBoxes = ['_leftBox', '_centerBox', '_rightBox'] //timeout names -const T2 = 'startIntellihideTimeout'; -const T4 = 'showDesktopTimeout'; -const T5 = 'trackerFocusAppTimeout'; -const T6 = 'scrollPanelDelayTimeout'; -const T7 = 'waitPanelBoxAllocation'; - -export const Panel = GObject.registerClass({ -}, class Panel extends St.Widget { +const T2 = 'startIntellihideTimeout' +const T4 = 'showDesktopTimeout' +const T5 = 'trackerFocusAppTimeout' +const T6 = 'scrollPanelDelayTimeout' +const T7 = 'waitPanelBoxAllocation' +export const Panel = GObject.registerClass( + {}, + class Panel extends St.Widget { _init(panelManager, monitor, panelBox, isStandalone) { - super._init({ layout_manager: new Clutter.BinLayout() }); + super._init({ layout_manager: new Clutter.BinLayout() }) - this._timeoutsHandler = new Utils.TimeoutsHandler(); - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._injectionManager = new InjectionManager(); + this._timeoutsHandler = new Utils.TimeoutsHandler() + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._injectionManager = new InjectionManager() - this.panelManager = panelManager; - this.panelStyle = new PanelStyle.PanelStyle(); + this.panelManager = panelManager + this.panelStyle = new PanelStyle.PanelStyle() - this.monitor = monitor; - this.panelBox = panelBox; + this.monitor = monitor + this.panelBox = panelBox - // when the original gnome-shell top panel is kept, all panels are "standalone", - // so in this case use isPrimary to get the panel on the primary dtp monitor, which - // might be different from the system's primary monitor. - this.isStandalone = isStandalone; - this.isPrimary = !isStandalone || (SETTINGS.get_boolean('stockgs-keep-top-panel') && - monitor == panelManager.dtpPrimaryMonitor); + // when the original gnome-shell top panel is kept, all panels are "standalone", + // so in this case use isPrimary to get the panel on the primary dtp monitor, which + // might be different from the system's primary monitor. + this.isStandalone = isStandalone + this.isPrimary = + !isStandalone || + (SETTINGS.get_boolean('stockgs-keep-top-panel') && + monitor == panelManager.dtpPrimaryMonitor) - this._sessionStyle = null; - this._unmappedButtons = []; - this._elementGroups = []; + this._sessionStyle = null + this._unmappedButtons = [] + this._elementGroups = [] - let systemMenuInfo = Utils.getSystemMenuInfo(); + let systemMenuInfo = Utils.getSystemMenuInfo() - if (isStandalone) { - this.panel = new SecondaryPanel({ name: 'panel', reactive: true }); - this.statusArea = this.panel.statusArea = {}; + if (isStandalone) { + this.panel = new SecondaryPanel({ name: 'panel', reactive: true }) + this.statusArea = this.panel.statusArea = {} - //next 3 functions are needed by other extensions to add elements to the secondary panel - this.panel.addToStatusArea = function(role, indicator, position, box) { - return Main.panel.addToStatusArea.call(this, role, indicator, position, box); - }; - - this.panel._addToPanelBox = function(role, indicator, position, box) { - Main.panel._addToPanelBox.call(this, role, indicator, position, box); - }; - - this.panel._onMenuSet = function(indicator) { - Main.panel._onMenuSet.call(this, indicator); - }; - - this._leftBox = this.panel._leftBox = new St.BoxLayout({ name: 'panelLeft' }); - this._centerBox = this.panel._centerBox = new St.BoxLayout({ name: 'panelCenter' }); - this._rightBox = this.panel._rightBox = new St.BoxLayout({ name: 'panelRight' }); - - this.menuManager = this.panel.menuManager = new PopupMenu.PopupMenuManager(this.panel); - - this._setPanelMenu(systemMenuInfo.name, systemMenuInfo.constructor, this.panel); - this._setPanelMenu('dateMenu', DateMenu.DateMenuButton, this.panel); - this._setPanelMenu('activities', Main.panel.statusArea.activities.constructor, this.panel); - - this.panel.add_child(this._leftBox); - this.panel.add_child(this._centerBox); - this.panel.add_child(this._rightBox); - } else { - this.panel = Main.panel; - this.statusArea = Main.panel.statusArea; - this.menuManager = Main.panel.menuManager; - - panelBoxes.forEach(p => this[p] = Main.panel[p]); - - ['activities', systemMenuInfo.name, 'dateMenu'].forEach(b => { - let container = this.statusArea[b].container; - let parent = container.get_parent(); - let siblings = parent.get_children(); - let index = siblings.indexOf(container); - - container._dtpOriginalParent = parent; - container._dtpOriginalIndex = index && index == siblings.length - 1 ? -1: index; - parent ? parent.remove_child(container) : null; - this.panel.add_child(container); - }); + //next 3 functions are needed by other extensions to add elements to the secondary panel + this.panel.addToStatusArea = function (role, indicator, position, box) { + return Main.panel.addToStatusArea.call( + this, + role, + indicator, + position, + box, + ) } - // Create a wrapper around the real showAppsIcon in order to add a popupMenu. Most of - // its behavior is handled by the taskbar, but its positioning is done at the panel level - this.showAppsIconWrapper = new AppIcons.ShowAppsIconWrapper(this); - this.panel.add_child(this.showAppsIconWrapper.realShowAppsIcon); - - this.panel._delegate = this; - - this.add_child(this.panel); - - if (Main.panel._onButtonPress || Main.panel._tryDragWindow) { - this._signalsHandler.add([ - this.panel, - [ - 'button-press-event', - 'touch-event' - ], - this._onButtonPress.bind(this) - ]); + this.panel._addToPanelBox = function (role, indicator, position, box) { + Main.panel._addToPanelBox.call(this, role, indicator, position, box) } - if (Main.panel._onKeyPress) { - this._signalsHandler.add([this.panel, 'key-press-event', Main.panel._onKeyPress.bind(this)]); + this.panel._onMenuSet = function (indicator) { + Main.panel._onMenuSet.call(this, indicator) } - Main.ctrlAltTabManager.addGroup(this, _("Top Bar")+" "+ monitor.index, 'focus-top-bar-symbolic', - { sortGroup: CtrlAltTab.SortGroup.TOP }); + this._leftBox = this.panel._leftBox = new St.BoxLayout({ + name: 'panelLeft', + }) + this._centerBox = this.panel._centerBox = new St.BoxLayout({ + name: 'panelCenter', + }) + this._rightBox = this.panel._rightBox = new St.BoxLayout({ + name: 'panelRight', + }) + + this.menuManager = this.panel.menuManager = + new PopupMenu.PopupMenuManager(this.panel) + + this._setPanelMenu( + systemMenuInfo.name, + systemMenuInfo.constructor, + this.panel, + ) + this._setPanelMenu('dateMenu', DateMenu.DateMenuButton, this.panel) + this._setPanelMenu( + 'activities', + Main.panel.statusArea.activities.constructor, + this.panel, + ) + + this.panel.add_child(this._leftBox) + this.panel.add_child(this._centerBox) + this.panel.add_child(this._rightBox) + } else { + this.panel = Main.panel + this.statusArea = Main.panel.statusArea + this.menuManager = Main.panel.menuManager + + panelBoxes.forEach((p) => (this[p] = Main.panel[p])) + ;['activities', systemMenuInfo.name, 'dateMenu'].forEach((b) => { + let container = this.statusArea[b].container + let parent = container.get_parent() + let siblings = parent.get_children() + let index = siblings.indexOf(container) + + container._dtpOriginalParent = parent + container._dtpOriginalIndex = + index && index == siblings.length - 1 ? -1 : index + parent ? parent.remove_child(container) : null + this.panel.add_child(container) + }) + } + + // Create a wrapper around the real showAppsIcon in order to add a popupMenu. Most of + // its behavior is handled by the taskbar, but its positioning is done at the panel level + this.showAppsIconWrapper = new AppIcons.ShowAppsIconWrapper(this) + this.panel.add_child(this.showAppsIconWrapper.realShowAppsIcon) + + this.panel._delegate = this + + this.add_child(this.panel) + + if (Main.panel._onButtonPress || Main.panel._tryDragWindow) { + this._signalsHandler.add([ + this.panel, + ['button-press-event', 'touch-event'], + this._onButtonPress.bind(this), + ]) + } + + if (Main.panel._onKeyPress) { + this._signalsHandler.add([ + this.panel, + 'key-press-event', + Main.panel._onKeyPress.bind(this), + ]) + } + + Main.ctrlAltTabManager.addGroup( + this, + _('Top Bar') + ' ' + monitor.index, + 'focus-top-bar-symbolic', + { sortGroup: CtrlAltTab.SortGroup.TOP }, + ) } - enable () { - let { name: systemMenuName } = Utils.getSystemMenuInfo(); + enable() { + let { name: systemMenuName } = Utils.getSystemMenuInfo() - if (this.statusArea[systemMenuName] && this.statusArea[systemMenuName]._volumeOutput) { - Utils.getIndicators(this.statusArea[systemMenuName]._volumeOutput)._dtpIgnoreScroll = 1; + if ( + this.statusArea[systemMenuName] && + this.statusArea[systemMenuName]._volumeOutput + ) { + Utils.getIndicators( + this.statusArea[systemMenuName]._volumeOutput, + )._dtpIgnoreScroll = 1 + } + + this.geom = this.getGeometry() + + this._setPanelPosition() + + if (!this.isStandalone) { + this._injectionManager.overrideMethod( + Object.getPrototypeOf(this.panel), + 'vfunc_allocate', + () => (box) => this._mainPanelAllocate(box), + ) + + // remove the extra space before the clock when the message-indicator is displayed + if (DateMenu.IndicatorPad) { + this._injectionManager.overrideMethod( + DateMenu.IndicatorPad.prototype, + 'vfunc_get_preferred_width', + () => () => [0, 0], + ) + this._injectionManager.overrideMethod( + DateMenu.IndicatorPad.prototype, + 'vfunc_get_preferred_height', + () => () => [0, 0], + ) } + } - this.geom = this.getGeometry(); + if (!DateMenu.IndicatorPad && this.statusArea.dateMenu) { + //3.36 switched to a size constraint applied on an anonymous child + let indicatorPad = this.statusArea.dateMenu + .get_first_child() + .get_first_child() - this._setPanelPosition(); + this._dateMenuIndicatorPadContraints = indicatorPad.get_constraints() + indicatorPad.clear_constraints() + } - if (!this.isStandalone) { - this._injectionManager.overrideMethod(Object.getPrototypeOf(this.panel), 'vfunc_allocate', () => (box) => this._mainPanelAllocate(box)); + this.menuManager._oldChangeMenu = this.menuManager._changeMenu + this.menuManager._changeMenu = (menu) => { + if (!SETTINGS.get_boolean('stockgs-panelbtn-click-only')) { + this.menuManager._oldChangeMenu(menu) + } + } - // remove the extra space before the clock when the message-indicator is displayed - if (DateMenu.IndicatorPad) { - this._injectionManager.overrideMethod(DateMenu.IndicatorPad.prototype, 'vfunc_get_preferred_width', () => () => [0,0]); - this._injectionManager.overrideMethod(DateMenu.IndicatorPad.prototype, 'vfunc_get_preferred_height', () => () => [0,0]); + this.dynamicTransparency = new Transparency.DynamicTransparency(this) + + this.taskbar = new Taskbar.Taskbar(this) + + this.panel.add_child(this.taskbar.actor) + + this._setShowDesktopButton(true) + + this._setAllocationMap() + + this.panel.add_style_class_name( + 'dashtopanelMainPanel ' + this.getOrientation(), + ) + + this._timeoutsHandler.add([ + T2, + SETTINGS.get_int('intellihide-enable-start-delay'), + () => (this.intellihide = new Intellihide.Intellihide(this)), + ]) + + this._signalsHandler.add( + // this is to catch changes to the theme or window scale factor + [ + Utils.getStageTheme(), + 'changed', + () => (this._resetGeometry(), this._setShowDesktopButtonStyle()), + ], + [ + // sync hover after a popupmenu is closed + this.taskbar, + 'menu-closed', + () => this.panel.sync_hover(), + ], + [Main.overview, ['showing', 'hiding'], () => this._adjustForOverview()], + [ + Main.overview, + 'hidden', + () => { + if (this.isPrimary) { + //reset the primary monitor when exiting the overview + this.panelManager.setFocusedMonitor(this.monitor) } - } - - if (!DateMenu.IndicatorPad && this.statusArea.dateMenu) { - //3.36 switched to a size constraint applied on an anonymous child - let indicatorPad = this.statusArea.dateMenu.get_first_child().get_first_child(); - - this._dateMenuIndicatorPadContraints = indicatorPad.get_constraints(); - indicatorPad.clear_constraints(); - } - - this.menuManager._oldChangeMenu = this.menuManager._changeMenu; - this.menuManager._changeMenu = (menu) => { - if (!SETTINGS.get_boolean('stockgs-panelbtn-click-only')) { - this.menuManager._oldChangeMenu(menu); + }, + ], + [ + this.statusArea.activities, + 'captured-event', + (actor, e) => { + if ( + e.type() == Clutter.EventType.BUTTON_PRESS || + e.type() == Clutter.EventType.TOUCH_BEGIN + ) { + //temporarily use as primary the monitor on which the activities btn was clicked + this.panelManager.setFocusedMonitor(this.monitor) } - }; + }, + ], + [ + this._centerBox, + 'child-added', + () => this._onBoxActorAdded(this._centerBox), + ], + [ + this._rightBox, + 'child-added', + () => this._onBoxActorAdded(this._rightBox), + ], + [this.panel, 'scroll-event', this._onPanelMouseScroll.bind(this)], + [Main.layoutManager, 'startup-complete', () => this._resetGeometry()], + ) - this.dynamicTransparency = new Transparency.DynamicTransparency(this); + this._bindSettingsChanges() - this.taskbar = new Taskbar.Taskbar(this); + this.panelStyle.enable(this) - this.panel.add_child(this.taskbar.actor); - - this._setShowDesktopButton(true); - - this._setAllocationMap(); - - this.panel.add_style_class_name('dashtopanelMainPanel ' + this.getOrientation()); - - this._timeoutsHandler.add([T2, SETTINGS.get_int('intellihide-enable-start-delay'), () => this.intellihide = new Intellihide.Intellihide(this)]); - - this._signalsHandler.add( - // this is to catch changes to the theme or window scale factor - [ - Utils.getStageTheme(), - 'changed', - () => (this._resetGeometry(), this._setShowDesktopButtonStyle()), - ], - [ - // sync hover after a popupmenu is closed - this.taskbar, - 'menu-closed', - () => this.panel.sync_hover() - ], - [ - Main.overview, - [ - 'showing', - 'hiding' - ], - () => this._adjustForOverview() - ], - [ - Main.overview, - 'hidden', - () => { - if (this.isPrimary) { - //reset the primary monitor when exiting the overview - this.panelManager.setFocusedMonitor(this.monitor); - } - } - ], - [ - this.statusArea.activities, - 'captured-event', - (actor, e) => { - if (e.type() == Clutter.EventType.BUTTON_PRESS || e.type() == Clutter.EventType.TOUCH_BEGIN) { - //temporarily use as primary the monitor on which the activities btn was clicked - this.panelManager.setFocusedMonitor(this.monitor); - } - } - ], - [ - this._centerBox, - 'child-added', - () => this._onBoxActorAdded(this._centerBox) - ], - [ - this._rightBox, - 'child-added', - () => this._onBoxActorAdded(this._rightBox) - ], - [ - this.panel, - 'scroll-event', - this._onPanelMouseScroll.bind(this) - ], - [ - Main.layoutManager, - 'startup-complete', - () => this._resetGeometry() - ] - ); - - this._bindSettingsChanges(); - - this.panelStyle.enable(this); - - if (this.checkIfVertical()) { - this._signalsHandler.add([ - this.panelBox, - 'notify::visible', - () => { - if (this.panelBox.visible) { - this._refreshVerticalAlloc(); - } - } - ]); - - if (this.statusArea.dateMenu) { - this._formatVerticalClock(); - - this._signalsHandler.add([ - this.statusArea.dateMenu._clock, - 'notify::clock', - () => this._formatVerticalClock() - ]); + if (this.checkIfVertical()) { + this._signalsHandler.add([ + this.panelBox, + 'notify::visible', + () => { + if (this.panelBox.visible) { + this._refreshVerticalAlloc() } + }, + ]) + + if (this.statusArea.dateMenu) { + this._formatVerticalClock() + + this._signalsHandler.add([ + this.statusArea.dateMenu._clock, + 'notify::clock', + () => this._formatVerticalClock(), + ]) } + } - // Since we are usually visible but not usually changing, make sure - // most repaint requests don't actually require us to repaint anything. - // This saves significant CPU when repainting the screen. - this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + // Since we are usually visible but not usually changing, make sure + // most repaint requests don't actually require us to repaint anything. + // This saves significant CPU when repainting the screen. + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS) - this._initProgressManager(); + this._initProgressManager() } disable() { - this.panelStyle.disable(); + this.panelStyle.disable() - this._timeoutsHandler.destroy(); - this._signalsHandler.destroy(); + this._timeoutsHandler.destroy() + this._signalsHandler.destroy() - this.panel.remove_child(this.taskbar.actor); + this.panel.remove_child(this.taskbar.actor) - if (this.intellihide) { - this.intellihide.destroy(); + if (this.intellihide) { + this.intellihide.destroy() + } + + this.dynamicTransparency.destroy() + + this.progressManager.destroy() + + this.taskbar.destroy() + this.showAppsIconWrapper.destroy() + + this.menuManager._changeMenu = this.menuManager._oldChangeMenu + + this._unmappedButtons.forEach((a) => this._disconnectVisibleId(a)) + + if (this.statusArea.dateMenu) { + this.statusArea.dateMenu._clockDisplay.text = + this.statusArea.dateMenu._clock.clock + this.statusArea.dateMenu._clockDisplay.clutter_text.set_width(-1) + + if (this._dateMenuIndicatorPadContraints) { + let indicatorPad = this.statusArea.dateMenu + .get_first_child() + .get_first_child() + + this._dateMenuIndicatorPadContraints.forEach((c) => + indicatorPad.add_constraint(c), + ) + } + } + + this._setVertical(this.panel, false) + this._setVertical(this._centerBox, false) + this._setVertical(this._rightBox, false) + + let { name: systemMenuName } = Utils.getSystemMenuInfo() + + if (!this.isStandalone) { + ;['vertical', 'horizontal', 'dashtopanelMainPanel'].forEach((c) => + this.panel.remove_style_class_name(c), + ) + + if (!Main.sessionMode.isLocked) { + ;['activities', systemMenuName, 'dateMenu'].forEach((b) => { + let container = this.statusArea[b].container + let originalParent = container._dtpOriginalParent + + this.panel.remove_child(container) + + originalParent && + originalParent.insert_child_at_index( + container, + Math.min( + container._dtpOriginalIndex, + originalParent.get_children().length - 1, + ), + ) + + delete container._dtpOriginalParent + delete container._dtpOriginalIndex + }) } - this.dynamicTransparency.destroy(); + this._setShowDesktopButton(false) - this.progressManager.destroy(); + delete Utils.getIndicators( + this.statusArea[systemMenuName]._volumeOutput, + )._dtpIgnoreScroll - this.taskbar.destroy(); - this.showAppsIconWrapper.destroy(); + this._injectionManager.clear() - this.menuManager._changeMenu = this.menuManager._oldChangeMenu; + this.panel._delegate = this.panel + } else { + this._removePanelMenu('dateMenu') + this._removePanelMenu(systemMenuName) + this._removePanelMenu('activities') + } - this._unmappedButtons.forEach(a => this._disconnectVisibleId(a)); - - if (this.statusArea.dateMenu) { - this.statusArea.dateMenu._clockDisplay.text = this.statusArea.dateMenu._clock.clock; - this.statusArea.dateMenu._clockDisplay.clutter_text.set_width(-1); - - if (this._dateMenuIndicatorPadContraints) { - let indicatorPad = this.statusArea.dateMenu.get_first_child().get_first_child(); - - this._dateMenuIndicatorPadContraints.forEach(c => indicatorPad.add_constraint(c)); - } - } - - this._setVertical(this.panel, false); - this._setVertical(this._centerBox, false); - this._setVertical(this._rightBox, false); - - let { name: systemMenuName } = Utils.getSystemMenuInfo(); - - if (!this.isStandalone) { - ['vertical', 'horizontal', 'dashtopanelMainPanel'].forEach(c => this.panel.remove_style_class_name(c)); - - if (!Main.sessionMode.isLocked) { - ['activities', systemMenuName, 'dateMenu'].forEach(b => { - let container = this.statusArea[b].container; - let originalParent = container._dtpOriginalParent; - - this.panel.remove_child(container); - - originalParent && originalParent.insert_child_at_index( - container, - Math.min(container._dtpOriginalIndex, originalParent.get_children().length - 1) - ); - - delete container._dtpOriginalParent; - delete container._dtpOriginalIndex; - }); - } - - this._setShowDesktopButton(false); - - delete Utils.getIndicators(this.statusArea[systemMenuName]._volumeOutput)._dtpIgnoreScroll; - - this._injectionManager.clear(); - - this.panel._delegate = this.panel; - } else { - this._removePanelMenu('dateMenu'); - this._removePanelMenu(systemMenuName); - this._removePanelMenu('activities'); - } - - Main.ctrlAltTabManager.removeGroup(this); + Main.ctrlAltTabManager.removeGroup(this) } getPosition() { - let position = PanelSettings.getPanelPosition(SETTINGS, this.monitor.index); + let position = PanelSettings.getPanelPosition( + SETTINGS, + this.monitor.index, + ) - if (position == Pos.TOP) { - return St.Side.TOP; - } else if (position == Pos.RIGHT) { - return St.Side.RIGHT; - } else if (position == Pos.BOTTOM) { - return St.Side.BOTTOM; - } + if (position == Pos.TOP) { + return St.Side.TOP + } else if (position == Pos.RIGHT) { + return St.Side.RIGHT + } else if (position == Pos.BOTTOM) { + return St.Side.BOTTOM + } - return St.Side.LEFT; + return St.Side.LEFT } checkIfVertical() { - let position = this.getPosition(); + let position = this.getPosition() - return (position == St.Side.LEFT || position == St.Side.RIGHT); + return position == St.Side.LEFT || position == St.Side.RIGHT } getOrientation() { - return (this.checkIfVertical() ? 'vertical' : 'horizontal'); + return this.checkIfVertical() ? 'vertical' : 'horizontal' } updateElementPositions() { - let panelPositions = this.panelManager.panelsElementPositions[this.monitor.index] || Pos.defaults; + let panelPositions = + this.panelManager.panelsElementPositions[this.monitor.index] || + Pos.defaults - this._updateGroupedElements(panelPositions); + this._updateGroupedElements(panelPositions) - this.panel.hide(); - this.panel.show(); + this.panel.hide() + this.panel.show() } _updateGroupedElements(panelPositions) { - let previousPosition = 0; - let previousCenteredPosition = 0; - let currentGroup = -1; + let previousPosition = 0 + let previousCenteredPosition = 0 + let currentGroup = -1 - this._elementGroups = []; + this._elementGroups = [] - panelPositions.forEach(pos => { - let allocationMap = this.allocationMap[pos.element]; + panelPositions.forEach((pos) => { + let allocationMap = this.allocationMap[pos.element] - if (allocationMap.actor) { - allocationMap.actor.visible = pos.visible; + if (allocationMap.actor) { + allocationMap.actor.visible = pos.visible - if (!pos.visible) { - return; - } + if (!pos.visible) { + return + } - let currentPosition = pos.position; - let isCentered = Pos.checkIfCentered(currentPosition); + let currentPosition = pos.position + let isCentered = Pos.checkIfCentered(currentPosition) - if (currentPosition == Pos.STACKED_TL && previousPosition == Pos.STACKED_BR) { - currentPosition = Pos.STACKED_BR; - } + if ( + currentPosition == Pos.STACKED_TL && + previousPosition == Pos.STACKED_BR + ) { + currentPosition = Pos.STACKED_BR + } - if (!previousPosition || - (previousPosition == Pos.STACKED_TL && currentPosition != Pos.STACKED_TL) || - (previousPosition != Pos.STACKED_BR && currentPosition == Pos.STACKED_BR) || - (isCentered && previousPosition != currentPosition && previousPosition != Pos.STACKED_BR)) { - this._elementGroups[++currentGroup] = { elements: [], index: this._elementGroups.length, expandableIndex: -1 }; - previousCenteredPosition = 0; - } - - if (pos.element == Pos.TASKBAR) { - this._elementGroups[currentGroup].expandableIndex = this._elementGroups[currentGroup].elements.length; - } - - if (isCentered && !this._elementGroups[currentGroup].isCentered) { - this._elementGroups[currentGroup].isCentered = 1; - previousCenteredPosition = currentPosition; - } - - this._elementGroups[currentGroup].position = previousCenteredPosition || currentPosition; - this._elementGroups[currentGroup].elements.push(allocationMap); - - allocationMap.position = currentPosition; - previousPosition = currentPosition; + if ( + !previousPosition || + (previousPosition == Pos.STACKED_TL && + currentPosition != Pos.STACKED_TL) || + (previousPosition != Pos.STACKED_BR && + currentPosition == Pos.STACKED_BR) || + (isCentered && + previousPosition != currentPosition && + previousPosition != Pos.STACKED_BR) + ) { + this._elementGroups[++currentGroup] = { + elements: [], + index: this._elementGroups.length, + expandableIndex: -1, } - }); + previousCenteredPosition = 0 + } + + if (pos.element == Pos.TASKBAR) { + this._elementGroups[currentGroup].expandableIndex = + this._elementGroups[currentGroup].elements.length + } + + if (isCentered && !this._elementGroups[currentGroup].isCentered) { + this._elementGroups[currentGroup].isCentered = 1 + previousCenteredPosition = currentPosition + } + + this._elementGroups[currentGroup].position = + previousCenteredPosition || currentPosition + this._elementGroups[currentGroup].elements.push(allocationMap) + + allocationMap.position = currentPosition + previousPosition = currentPosition + } + }) } _bindSettingsChanges() { - let isVertical = this.checkIfVertical(); + let isVertical = this.checkIfVertical() - this._signalsHandler.add( - [ - SETTINGS, - [ - 'changed::panel-sizes', - 'changed::appicon-margin-todesktop', - 'changed::appicon-margin-toscreenborder', - 'changed::group-apps' - ], - () => this._resetGeometry() - ], - [ - SETTINGS, - [ - 'changed::appicon-margin', - 'changed::appicon-padding' - ], - () => this.taskbar.resetAppIcons() - ], - [ - SETTINGS, - [ - 'changed::showdesktop-button-width', - 'changed::trans-use-custom-bg', - 'changed::desktop-line-use-custom-color', - 'changed::desktop-line-custom-color', - 'changed::trans-bg-color' - ], - () => this._setShowDesktopButtonStyle() - ], - [ - DESKTOPSETTINGS, - 'changed::clock-format', - () => { - this._clockFormat = null; + this._signalsHandler.add( + [ + SETTINGS, + [ + 'changed::panel-sizes', + 'changed::appicon-margin-todesktop', + 'changed::appicon-margin-toscreenborder', + 'changed::group-apps', + ], + () => this._resetGeometry(), + ], + [ + SETTINGS, + ['changed::appicon-margin', 'changed::appicon-padding'], + () => this.taskbar.resetAppIcons(), + ], + [ + SETTINGS, + [ + 'changed::showdesktop-button-width', + 'changed::trans-use-custom-bg', + 'changed::desktop-line-use-custom-color', + 'changed::desktop-line-custom-color', + 'changed::trans-bg-color', + ], + () => this._setShowDesktopButtonStyle(), + ], + [ + DESKTOPSETTINGS, + 'changed::clock-format', + () => { + this._clockFormat = null - if (isVertical) { - this._formatVerticalClock(); - } - } - ], - [ - SETTINGS, - 'changed::progress-show-bar', - () => this._initProgressManager() - ], - [ - SETTINGS, - 'changed::progress-show-count', - () => this._initProgressManager() - ] - ); + if (isVertical) { + this._formatVerticalClock() + } + }, + ], + [ + SETTINGS, + 'changed::progress-show-bar', + () => this._initProgressManager(), + ], + [ + SETTINGS, + 'changed::progress-show-count', + () => this._initProgressManager(), + ], + ) - if (isVertical) { - this._signalsHandler.add([SETTINGS, 'changed::group-apps-label-max-width', () => this._resetGeometry()]); - } + if (isVertical) { + this._signalsHandler.add([ + SETTINGS, + 'changed::group-apps-label-max-width', + () => this._resetGeometry(), + ]) + } } _setPanelMenu(propName, constr, container) { - if (!this.statusArea[propName]) { - this.statusArea[propName] = this._getPanelMenu(propName, constr); - this.menuManager.addMenu(this.statusArea[propName].menu); - container.insert_child_at_index(this.statusArea[propName].container, 0); - } + if (!this.statusArea[propName]) { + this.statusArea[propName] = this._getPanelMenu(propName, constr) + this.menuManager.addMenu(this.statusArea[propName].menu) + container.insert_child_at_index(this.statusArea[propName].container, 0) + } } _removePanelMenu(propName) { - if (this.statusArea[propName]) { - let parent = this.statusArea[propName].container.get_parent(); + if (this.statusArea[propName]) { + let parent = this.statusArea[propName].container.get_parent() - if (parent) { - parent.remove_child(this.statusArea[propName].container); - } - - //calling this.statusArea[propName].destroy(); is buggy for now, gnome-shell never - //destroys those panel menus... - //since we can't destroy the menu (hence properly disconnect its signals), let's - //store it so the next time a panel needs one of its kind, we can reuse it instead - //of creating a new one - let panelMenu = this.statusArea[propName]; - - this.menuManager.removeMenu(panelMenu.menu); - PERSISTENTSTORAGE[propName].push(panelMenu); - this.statusArea[propName] = null; + if (parent) { + parent.remove_child(this.statusArea[propName].container) } + + //calling this.statusArea[propName].destroy(); is buggy for now, gnome-shell never + //destroys those panel menus... + //since we can't destroy the menu (hence properly disconnect its signals), let's + //store it so the next time a panel needs one of its kind, we can reuse it instead + //of creating a new one + let panelMenu = this.statusArea[propName] + + this.menuManager.removeMenu(panelMenu.menu) + PERSISTENTSTORAGE[propName].push(panelMenu) + this.statusArea[propName] = null + } } _getPanelMenu(propName, constr) { - PERSISTENTSTORAGE[propName] = PERSISTENTSTORAGE[propName] || []; + PERSISTENTSTORAGE[propName] = PERSISTENTSTORAGE[propName] || [] - if (!PERSISTENTSTORAGE[propName].length) { - PERSISTENTSTORAGE[propName].push(new constr()); - } + if (!PERSISTENTSTORAGE[propName].length) { + PERSISTENTSTORAGE[propName].push(new constr()) + } - return PERSISTENTSTORAGE[propName].pop(); + return PERSISTENTSTORAGE[propName].pop() } _adjustForOverview() { - let isFocusedMonitor = this.panelManager.checkIfFocusedMonitor(this.monitor); - let isOverview = !!Main.overview.visibleTarget; - let isOverviewFocusedMonitor = isOverview && isFocusedMonitor; - let isShown = !isOverview || isOverviewFocusedMonitor; - let actorData = Utils.getTrackedActorData(this.panelBox) + let isFocusedMonitor = this.panelManager.checkIfFocusedMonitor( + this.monitor, + ) + let isOverview = !!Main.overview.visibleTarget + let isOverviewFocusedMonitor = isOverview && isFocusedMonitor + let isShown = !isOverview || isOverviewFocusedMonitor + let actorData = Utils.getTrackedActorData(this.panelBox) - // prevent the "chrome" to update the panelbox visibility while in overview - actorData.trackFullscreen = !isOverview + // prevent the "chrome" to update the panelbox visibility while in overview + actorData.trackFullscreen = !isOverview - this.panelBox[isShown ? 'show' : 'hide'](); + this.panelBox[isShown ? 'show' : 'hide']() } _resetGeometry() { - this.geom = this.getGeometry(); - this._setPanelPosition(); - this.taskbar.resetAppIcons(true); - this.dynamicTransparency.updateExternalStyle(); + this.geom = this.getGeometry() + this._setPanelPosition() + this.taskbar.resetAppIcons(true) + this.dynamicTransparency.updateExternalStyle() - if (this.intellihide && this.intellihide.enabled) { - this.intellihide.reset(); - } + if (this.intellihide && this.intellihide.enabled) { + this.intellihide.reset() + } - if (this.checkIfVertical()) { - this.showAppsIconWrapper.realShowAppsIcon.toggleButton.set_width(this.geom.w); - this._refreshVerticalAlloc(); - } + if (this.checkIfVertical()) { + this.showAppsIconWrapper.realShowAppsIcon.toggleButton.set_width( + this.geom.w, + ) + this._refreshVerticalAlloc() + } } getGeometry() { - let scaleFactor = Utils.getScaleFactor(); - let panelBoxTheme = this.panelBox.get_theme_node(); - let lrPadding = panelBoxTheme.get_padding(St.Side.RIGHT) + panelBoxTheme.get_padding(St.Side.LEFT); - let topPadding = panelBoxTheme.get_padding(St.Side.TOP); - let tbPadding = topPadding + panelBoxTheme.get_padding(St.Side.BOTTOM); - let position = this.getPosition(); - let length = PanelSettings.getPanelLength(SETTINGS, this.monitor.index) / 100; - let anchor = PanelSettings.getPanelAnchor(SETTINGS, this.monitor.index); - let anchorPlaceOnMonitor = 0; - let gsTopPanelOffset = 0; - let x = 0, y = 0; - let w = 0, h = 0; + let scaleFactor = Utils.getScaleFactor() + let panelBoxTheme = this.panelBox.get_theme_node() + let lrPadding = + panelBoxTheme.get_padding(St.Side.RIGHT) + + panelBoxTheme.get_padding(St.Side.LEFT) + let topPadding = panelBoxTheme.get_padding(St.Side.TOP) + let tbPadding = topPadding + panelBoxTheme.get_padding(St.Side.BOTTOM) + let position = this.getPosition() + let length = + PanelSettings.getPanelLength(SETTINGS, this.monitor.index) / 100 + let anchor = PanelSettings.getPanelAnchor(SETTINGS, this.monitor.index) + let anchorPlaceOnMonitor = 0 + let gsTopPanelOffset = 0 + let x = 0, + y = 0 + let w = 0, + h = 0 - const panelSize = PanelSettings.getPanelSize(SETTINGS, this.monitor.index) + SETTINGS.get_int('appicon-margin-todesktop') + SETTINGS.get_int('appicon-margin-toscreenborder'); - this.dtpSize = panelSize * scaleFactor; + const panelSize = + PanelSettings.getPanelSize(SETTINGS, this.monitor.index) + + SETTINGS.get_int('appicon-margin-todesktop') + + SETTINGS.get_int('appicon-margin-toscreenborder') + this.dtpSize = panelSize * scaleFactor - if (SETTINGS.get_boolean('stockgs-keep-top-panel') && Main.layoutManager.primaryMonitor == this.monitor) { - gsTopPanelOffset = Main.layoutManager.panelBox.height - topPadding; + if ( + SETTINGS.get_boolean('stockgs-keep-top-panel') && + Main.layoutManager.primaryMonitor == this.monitor + ) { + gsTopPanelOffset = Main.layoutManager.panelBox.height - topPadding + } + + if (this.checkIfVertical()) { + if (!SETTINGS.get_boolean('group-apps')) { + // add window title width and side padding of _dtpIconContainer when vertical + this.dtpSize += + SETTINGS.get_int('group-apps-label-max-width') + + (AppIcons.DEFAULT_PADDING_SIZE * 2) / scaleFactor } - if (this.checkIfVertical()) { - if (!SETTINGS.get_boolean('group-apps')) { - // add window title width and side padding of _dtpIconContainer when vertical - this.dtpSize += SETTINGS.get_int('group-apps-label-max-width') + AppIcons.DEFAULT_PADDING_SIZE * 2 / scaleFactor; - } + ;(this.sizeFunc = 'get_preferred_height'), + (this.fixedCoord = { c1: 'x1', c2: 'x2' }) + this.varCoord = { c1: 'y1', c2: 'y2' } - this.sizeFunc = 'get_preferred_height', - this.fixedCoord = { c1: 'x1', c2: 'x2' } - this.varCoord = { c1: 'y1', c2: 'y2' }; + w = this.dtpSize + h = this.monitor.height * length - tbPadding - gsTopPanelOffset + } else { + this.sizeFunc = 'get_preferred_width' + this.fixedCoord = { c1: 'y1', c2: 'y2' } + this.varCoord = { c1: 'x1', c2: 'x2' } - w = this.dtpSize; - h = this.monitor.height * length - tbPadding - gsTopPanelOffset; + w = this.monitor.width * length - lrPadding + h = this.dtpSize + } + + if (position == St.Side.TOP || position == St.Side.LEFT) { + x = this.monitor.x + y = this.monitor.y + gsTopPanelOffset + } else if (position == St.Side.RIGHT) { + x = this.monitor.x + this.monitor.width - this.dtpSize - lrPadding + y = this.monitor.y + gsTopPanelOffset + } else { + //BOTTOM + x = this.monitor.x + y = this.monitor.y + this.monitor.height - this.dtpSize - tbPadding + } + + if (this.checkIfVertical()) { + let viewHeight = this.monitor.height - gsTopPanelOffset + + if (anchor === Pos.MIDDLE) { + anchorPlaceOnMonitor = (viewHeight - h) / 2 + } else if (anchor === Pos.END) { + anchorPlaceOnMonitor = viewHeight - h } else { - this.sizeFunc = 'get_preferred_width'; - this.fixedCoord = { c1: 'y1', c2: 'y2' }; - this.varCoord = { c1: 'x1', c2: 'x2' }; - - w = this.monitor.width * length - lrPadding; - h = this.dtpSize; + // Pos.START + anchorPlaceOnMonitor = 0 } - - if (position == St.Side.TOP || position == St.Side.LEFT) { - x = this.monitor.x; - y = this.monitor.y + gsTopPanelOffset; - } else if (position == St.Side.RIGHT) { - x = this.monitor.x + this.monitor.width - this.dtpSize - lrPadding; - y = this.monitor.y + gsTopPanelOffset; - } else { //BOTTOM - x = this.monitor.x; - y = this.monitor.y + this.monitor.height - this.dtpSize - tbPadding; - } - - if (this.checkIfVertical()) { - let viewHeight = this.monitor.height - gsTopPanelOffset; - - if (anchor === Pos.MIDDLE) { - anchorPlaceOnMonitor = (viewHeight - h) / 2; - } else if (anchor === Pos.END) { - anchorPlaceOnMonitor = viewHeight - h; - } else { // Pos.START - anchorPlaceOnMonitor = 0; - } - y = y + anchorPlaceOnMonitor; + y = y + anchorPlaceOnMonitor + } else { + if (anchor === Pos.MIDDLE) { + anchorPlaceOnMonitor = (this.monitor.width - w) / 2 + } else if (anchor === Pos.END) { + anchorPlaceOnMonitor = this.monitor.width - w } else { - if (anchor === Pos.MIDDLE) { - anchorPlaceOnMonitor = (this.monitor.width - w) / 2; - } else if (anchor === Pos.END) { - anchorPlaceOnMonitor = this.monitor.width - w; - } else { // Pos.START - anchorPlaceOnMonitor = 0; - } - x = x + anchorPlaceOnMonitor; + // Pos.START + anchorPlaceOnMonitor = 0 } + x = x + anchorPlaceOnMonitor + } - return { - x, y, - w, h, - lrPadding, - tbPadding, - position - }; + return { + x, + y, + w, + h, + lrPadding, + tbPadding, + position, + } } _setAllocationMap() { - this.allocationMap = {}; - let setMap = (name, actor) => this.allocationMap[name] = { - actor: actor, - box: new Clutter.ActorBox() - }; + this.allocationMap = {} + let setMap = (name, actor) => + (this.allocationMap[name] = { + actor: actor, + box: new Clutter.ActorBox(), + }) - setMap(Pos.SHOW_APPS_BTN, this.showAppsIconWrapper.realShowAppsIcon); - setMap(Pos.ACTIVITIES_BTN, this.statusArea.activities ? this.statusArea.activities.container : 0); - setMap(Pos.LEFT_BOX, this._leftBox); - setMap(Pos.TASKBAR, this.taskbar.actor); - setMap(Pos.CENTER_BOX, this._centerBox); - setMap(Pos.DATE_MENU, this.statusArea.dateMenu.container); - setMap(Pos.SYSTEM_MENU, this.statusArea[Utils.getSystemMenuInfo().name].container); - setMap(Pos.RIGHT_BOX, this._rightBox); - setMap(Pos.DESKTOP_BTN, this._showDesktopButton); + setMap(Pos.SHOW_APPS_BTN, this.showAppsIconWrapper.realShowAppsIcon) + setMap( + Pos.ACTIVITIES_BTN, + this.statusArea.activities ? this.statusArea.activities.container : 0, + ) + setMap(Pos.LEFT_BOX, this._leftBox) + setMap(Pos.TASKBAR, this.taskbar.actor) + setMap(Pos.CENTER_BOX, this._centerBox) + setMap(Pos.DATE_MENU, this.statusArea.dateMenu.container) + setMap( + Pos.SYSTEM_MENU, + this.statusArea[Utils.getSystemMenuInfo().name].container, + ) + setMap(Pos.RIGHT_BOX, this._rightBox) + setMap(Pos.DESKTOP_BTN, this._showDesktopButton) } _mainPanelAllocate(box) { - this.panel.set_allocation(box); + this.panel.set_allocation(box) } vfunc_allocate(box) { - this.set_allocation(box); + this.set_allocation(box) - let fixed = 0; - let centeredMonitorGroup; - let panelAlloc = new Clutter.ActorBox({ x1: 0, y1: 0, x2: this.geom.w, y2: this.geom.h }); - let assignGroupSize = (group, update) => { - group.size = 0; - group.tlOffset = 0; - group.brOffset = 0; + let fixed = 0 + let centeredMonitorGroup + let panelAlloc = new Clutter.ActorBox({ + x1: 0, + y1: 0, + x2: this.geom.w, + y2: this.geom.h, + }) + let assignGroupSize = (group, update) => { + group.size = 0 + group.tlOffset = 0 + group.brOffset = 0 - group.elements.forEach(element => { - if (!update) { - element.box[this.fixedCoord.c1] = panelAlloc[this.fixedCoord.c1]; - element.box[this.fixedCoord.c2] = panelAlloc[this.fixedCoord.c2]; - element.natSize = element.actor[this.sizeFunc](-1)[1]; - } + group.elements.forEach((element) => { + if (!update) { + element.box[this.fixedCoord.c1] = panelAlloc[this.fixedCoord.c1] + element.box[this.fixedCoord.c2] = panelAlloc[this.fixedCoord.c2] + element.natSize = element.actor[this.sizeFunc](-1)[1] + } - if (!group.isCentered || Pos.checkIfCentered(element.position)) { - group.size += element.natSize; - } else if (element.position == Pos.STACKED_TL) { - group.tlOffset += element.natSize; - } else { // Pos.STACKED_BR - group.brOffset += element.natSize; - } - }); + if (!group.isCentered || Pos.checkIfCentered(element.position)) { + group.size += element.natSize + } else if (element.position == Pos.STACKED_TL) { + group.tlOffset += element.natSize + } else { + // Pos.STACKED_BR + group.brOffset += element.natSize + } + }) - if (group.isCentered) { - group.size += Math.max(group.tlOffset, group.brOffset) * 2; - group.tlOffset = Math.max(group.tlOffset - group.brOffset, 0); + if (group.isCentered) { + group.size += Math.max(group.tlOffset, group.brOffset) * 2 + group.tlOffset = Math.max(group.tlOffset - group.brOffset, 0) + } + } + let allocateGroup = (group, tlLimit, brLimit) => { + let startPosition = tlLimit + let currentPosition = 0 + + if (group.expandableIndex >= 0) { + let availableSize = brLimit - tlLimit + let expandable = group.elements[group.expandableIndex] + let i = 0 + let l = this._elementGroups.length + let tlSize = 0 + let brSize = 0 + + if ( + centeredMonitorGroup && + (centeredMonitorGroup != group || + expandable.position != Pos.CENTERED_MONITOR) + ) { + if ( + centeredMonitorGroup.index < group.index || + (centeredMonitorGroup == group && + expandable.position == Pos.STACKED_TL) + ) { + i = centeredMonitorGroup.index + } else { + l = centeredMonitorGroup.index } - }; - let allocateGroup = (group, tlLimit, brLimit) => { - let startPosition = tlLimit; - let currentPosition = 0; + } - if (group.expandableIndex >= 0) { - let availableSize = brLimit - tlLimit; - let expandable = group.elements[group.expandableIndex]; - let i = 0; - let l = this._elementGroups.length; - let tlSize = 0; - let brSize = 0; + for (; i < l; ++i) { + let refGroup = this._elementGroups[i] - if (centeredMonitorGroup && (centeredMonitorGroup != group || expandable.position != Pos.CENTERED_MONITOR)) { - if (centeredMonitorGroup.index < group.index || (centeredMonitorGroup == group && expandable.position == Pos.STACKED_TL)) { - i = centeredMonitorGroup.index; - } else { - l = centeredMonitorGroup.index; - } - } - - for (; i < l; ++i) { - let refGroup = this._elementGroups[i]; - - if (i < group.index && (!refGroup.fixed || refGroup[this.varCoord.c2] > tlLimit)) { - tlSize += refGroup.size; - } else if (i > group.index && (!refGroup.fixed || refGroup[this.varCoord.c1] < brLimit)) { - brSize += refGroup.size; - } - } - - if (group.isCentered) { - availableSize -= Math.max(tlSize, brSize) * 2; - } else { - availableSize -= tlSize + brSize; - } - - if (availableSize < group.size) { - expandable.natSize -= (group.size - availableSize) * (group.isCentered && !Pos.checkIfCentered(expandable.position) ? .5 : 1); - assignGroupSize(group, true); - } + if ( + i < group.index && + (!refGroup.fixed || refGroup[this.varCoord.c2] > tlLimit) + ) { + tlSize += refGroup.size + } else if ( + i > group.index && + (!refGroup.fixed || refGroup[this.varCoord.c1] < brLimit) + ) { + brSize += refGroup.size } + } - if (group.isCentered) { - startPosition = tlLimit + (brLimit - tlLimit - group.size) * .5; - } else if (group.position == Pos.STACKED_BR) { - startPosition = brLimit - group.size; - } + if (group.isCentered) { + availableSize -= Math.max(tlSize, brSize) * 2 + } else { + availableSize -= tlSize + brSize + } - currentPosition = group.tlOffset + startPosition; - - group.elements.forEach(element => { - element.box[this.varCoord.c1] = Math.round(currentPosition); - element.box[this.varCoord.c2] = Math.round((currentPosition += element.natSize)); - - element.actor.allocate(element.box); - }); - - group[this.varCoord.c1] = startPosition; - group[this.varCoord.c2] = currentPosition; - group.fixed = 1; - ++fixed; - }; - - this.panel.allocate(panelAlloc); - - this._elementGroups.forEach(group => { - group.fixed = 0; - - assignGroupSize(group); - - if (group.position == Pos.CENTERED_MONITOR) { - centeredMonitorGroup = group; - } - }); - - if (centeredMonitorGroup) { - allocateGroup(centeredMonitorGroup, panelAlloc[this.varCoord.c1], panelAlloc[this.varCoord.c2]); + if (availableSize < group.size) { + expandable.natSize -= + (group.size - availableSize) * + (group.isCentered && !Pos.checkIfCentered(expandable.position) + ? 0.5 + : 1) + assignGroupSize(group, true) + } } - let iterations = 0; //failsafe - while (fixed < this._elementGroups.length && ++iterations < 10) { - for (let i = 0, l = this._elementGroups.length; i < l; ++i) { - let group = this._elementGroups[i]; - - if (group.fixed) { - continue; - } - - let prevGroup = this._elementGroups[i - 1]; - let nextGroup = this._elementGroups[i + 1]; - let prevLimit = prevGroup && prevGroup.fixed ? prevGroup[this.varCoord.c2] : - centeredMonitorGroup && group.index > centeredMonitorGroup.index ? centeredMonitorGroup[this.varCoord.c2] : panelAlloc[this.varCoord.c1]; - let nextLimit = nextGroup && nextGroup.fixed ? nextGroup[this.varCoord.c1] : - centeredMonitorGroup && group.index < centeredMonitorGroup.index ? centeredMonitorGroup[this.varCoord.c1] : panelAlloc[this.varCoord.c2]; - - if (group.position == Pos.STACKED_TL) { - allocateGroup(group, panelAlloc[this.varCoord.c1], nextLimit); - } else if (group.position == Pos.STACKED_BR) { - allocateGroup(group, prevLimit, panelAlloc[this.varCoord.c2]); - } else if ((!prevGroup || prevGroup.fixed) && (!nextGroup || nextGroup.fixed)) { // CENTERED - allocateGroup(group, prevLimit, nextLimit); - } - } + if (group.isCentered) { + startPosition = tlLimit + (brLimit - tlLimit - group.size) * 0.5 + } else if (group.position == Pos.STACKED_BR) { + startPosition = brLimit - group.size } + + currentPosition = group.tlOffset + startPosition + + group.elements.forEach((element) => { + element.box[this.varCoord.c1] = Math.round(currentPosition) + element.box[this.varCoord.c2] = Math.round( + (currentPosition += element.natSize), + ) + + element.actor.allocate(element.box) + }) + + group[this.varCoord.c1] = startPosition + group[this.varCoord.c2] = currentPosition + group.fixed = 1 + ++fixed + } + + this.panel.allocate(panelAlloc) + + this._elementGroups.forEach((group) => { + group.fixed = 0 + + assignGroupSize(group) + + if (group.position == Pos.CENTERED_MONITOR) { + centeredMonitorGroup = group + } + }) + + if (centeredMonitorGroup) { + allocateGroup( + centeredMonitorGroup, + panelAlloc[this.varCoord.c1], + panelAlloc[this.varCoord.c2], + ) + } + + let iterations = 0 //failsafe + while (fixed < this._elementGroups.length && ++iterations < 10) { + for (let i = 0, l = this._elementGroups.length; i < l; ++i) { + let group = this._elementGroups[i] + + if (group.fixed) { + continue + } + + let prevGroup = this._elementGroups[i - 1] + let nextGroup = this._elementGroups[i + 1] + let prevLimit = + prevGroup && prevGroup.fixed + ? prevGroup[this.varCoord.c2] + : centeredMonitorGroup && group.index > centeredMonitorGroup.index + ? centeredMonitorGroup[this.varCoord.c2] + : panelAlloc[this.varCoord.c1] + let nextLimit = + nextGroup && nextGroup.fixed + ? nextGroup[this.varCoord.c1] + : centeredMonitorGroup && group.index < centeredMonitorGroup.index + ? centeredMonitorGroup[this.varCoord.c1] + : panelAlloc[this.varCoord.c2] + + if (group.position == Pos.STACKED_TL) { + allocateGroup(group, panelAlloc[this.varCoord.c1], nextLimit) + } else if (group.position == Pos.STACKED_BR) { + allocateGroup(group, prevLimit, panelAlloc[this.varCoord.c2]) + } else if ( + (!prevGroup || prevGroup.fixed) && + (!nextGroup || nextGroup.fixed) + ) { + // CENTERED + allocateGroup(group, prevLimit, nextLimit) + } + } + } } _setPanelPosition() { - let clipContainer = this.panelBox.get_parent(); + let clipContainer = this.panelBox.get_parent() - this.set_size(this.geom.w, this.geom.h); - clipContainer.set_position(this.geom.x, this.geom.y); + this.set_size(this.geom.w, this.geom.h) + clipContainer.set_position(this.geom.x, this.geom.y) - this._setVertical(this.panel, this.checkIfVertical()); + this._setVertical(this.panel, this.checkIfVertical()) - // styles for theming - Object.keys(St.Side).forEach(p => { - let cssName = 'dashtopanel' + p.charAt(0) + p.slice(1).toLowerCase(); + // styles for theming + Object.keys(St.Side).forEach((p) => { + let cssName = 'dashtopanel' + p.charAt(0) + p.slice(1).toLowerCase() - this.panel[(St.Side[p] == this.geom.position ? 'add' : 'remove') + '_style_class_name'](cssName); - }); + this.panel[ + (St.Side[p] == this.geom.position ? 'add' : 'remove') + + '_style_class_name' + ](cssName) + }) - this._setPanelClip(clipContainer); + this._setPanelClip(clipContainer) - Main.layoutManager._updateHotCorners(); - Main.layoutManager._updatePanelBarrier(this); + Main.layoutManager._updateHotCorners() + Main.layoutManager._updatePanelBarrier(this) } _setPanelClip(clipContainer) { - clipContainer = clipContainer || this.panelBox.get_parent(); - this._timeoutsHandler.add([T7, 0, () => Utils.setClip(clipContainer, clipContainer.x, clipContainer.y, this.panelBox.width, this.panelBox.height)]); + clipContainer = clipContainer || this.panelBox.get_parent() + this._timeoutsHandler.add([ + T7, + 0, + () => + Utils.setClip( + clipContainer, + clipContainer.x, + clipContainer.y, + this.panelBox.width, + this.panelBox.height, + ), + ]) } _onButtonPress(actor, event) { - let type = event.type(); - let isPress = type == Clutter.EventType.BUTTON_PRESS; - let button = isPress ? event.get_button() : -1; - let [stageX, stageY] = event.get_coords(); + let type = event.type() + let isPress = type == Clutter.EventType.BUTTON_PRESS + let button = isPress ? event.get_button() : -1 + let [stageX, stageY] = event.get_coords() - if (button == 3 && global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, stageX, stageY) == this.panel) { - //right click on an empty part of the panel, temporarily borrow and display the showapps context menu - Main.layoutManager.setDummyCursorGeometry(stageX, stageY, 0, 0); + if ( + button == 3 && + global.stage.get_actor_at_pos( + Clutter.PickMode.REACTIVE, + stageX, + stageY, + ) == this.panel + ) { + //right click on an empty part of the panel, temporarily borrow and display the showapps context menu + Main.layoutManager.setDummyCursorGeometry(stageX, stageY, 0, 0) - this.showAppsIconWrapper.createMenu(); - this.showAppsIconWrapper.popupMenu(Main.layoutManager.dummyCursor); + this.showAppsIconWrapper.createMenu() + this.showAppsIconWrapper.popupMenu(Main.layoutManager.dummyCursor) - return Clutter.EVENT_STOP; - } else { - const targetActor = global.stage.get_event_actor(event); + return Clutter.EVENT_STOP + } else { + const targetActor = global.stage.get_event_actor(event) - if (Main.modalCount > 0 || targetActor != actor || - (!isPress && type != Clutter.EventType.TOUCH_BEGIN) || - (isPress && button != 1)) { - return Clutter.EVENT_PROPAGATE; - } + if ( + Main.modalCount > 0 || + targetActor != actor || + (!isPress && type != Clutter.EventType.TOUCH_BEGIN) || + (isPress && button != 1) + ) { + return Clutter.EVENT_PROPAGATE } + } - let params = this.checkIfVertical() ? [stageY, 'y', 'height'] : [stageX, 'x', 'width']; - let dragWindow = this._getDraggableWindowForPosition.apply(this, params.concat(['maximized_' + this.getOrientation() + 'ly'])); + let params = this.checkIfVertical() + ? [stageY, 'y', 'height'] + : [stageX, 'x', 'width'] + let dragWindow = this._getDraggableWindowForPosition.apply( + this, + params.concat(['maximized_' + this.getOrientation() + 'ly']), + ) - if (!dragWindow) - return Clutter.EVENT_PROPAGATE; + if (!dragWindow) return Clutter.EVENT_PROPAGATE - global.display.begin_grab_op(dragWindow, - Meta.GrabOp.MOVING, - false, /* pointer grab */ - true, /* frame action */ - button, - event.get_state(), - event.get_time(), - stageX, stageY); + global.display.begin_grab_op( + dragWindow, + Meta.GrabOp.MOVING, + false /* pointer grab */, + true /* frame action */, + button, + event.get_state(), + event.get_time(), + stageX, + stageY, + ) - return Clutter.EVENT_STOP; + return Clutter.EVENT_STOP } - _getDraggableWindowForPosition(stageCoord, coord, dimension, maximizedProp) { - let workspace = Utils.getCurrentWorkspace(); - let allWindowsByStacking = global.display.sort_windows_by_stacking( - workspace.list_windows() - ).reverse(); + _getDraggableWindowForPosition( + stageCoord, + coord, + dimension, + maximizedProp, + ) { + let workspace = Utils.getCurrentWorkspace() + let allWindowsByStacking = global.display + .sort_windows_by_stacking(workspace.list_windows()) + .reverse() - return Utils.find(allWindowsByStacking, metaWindow => { - let rect = metaWindow.get_frame_rect(); + return Utils.find(allWindowsByStacking, (metaWindow) => { + let rect = metaWindow.get_frame_rect() - return metaWindow.get_monitor() == this.monitor.index && - metaWindow.showing_on_its_workspace() && - metaWindow.get_window_type() != Meta.WindowType.DESKTOP && - metaWindow[maximizedProp] && - stageCoord > rect[coord] && stageCoord < rect[coord] + rect[dimension]; - }); + return ( + metaWindow.get_monitor() == this.monitor.index && + metaWindow.showing_on_its_workspace() && + metaWindow.get_window_type() != Meta.WindowType.DESKTOP && + metaWindow[maximizedProp] && + stageCoord > rect[coord] && + stageCoord < rect[coord] + rect[dimension] + ) + }) } _onBoxActorAdded(box) { - if (this.checkIfVertical()) { - this._setVertical(box, true); - } + if (this.checkIfVertical()) { + this._setVertical(box, true) + } } _refreshVerticalAlloc() { - this._setVertical(this._centerBox, true); - this._setVertical(this._rightBox, true); - this._formatVerticalClock(); + this._setVertical(this._centerBox, true) + this._setVertical(this._rightBox, true) + this._formatVerticalClock() } _setVertical(actor, isVertical) { - let _set = (actor, isVertical) => { - if (!actor || actor instanceof Dash.DashItemContainer || actor instanceof TaskbarItemContainer.TaskbarItemContainer) { - return; - } + let _set = (actor, isVertical) => { + if ( + !actor || + actor instanceof Dash.DashItemContainer || + actor instanceof TaskbarItemContainer.TaskbarItemContainer + ) { + return + } - if (actor instanceof St.BoxLayout) { - actor.vertical = isVertical; - } else if ( - actor != this.statusArea.appMenu && - ((actor._delegate || actor) instanceof PanelMenu.ButtonBox || actor == this.statusArea.quickSettings) - ) { - let child = actor.get_first_child(); + if (actor instanceof St.BoxLayout) { + actor.vertical = isVertical + } else if ( + actor != this.statusArea.appMenu && + ((actor._delegate || actor) instanceof PanelMenu.ButtonBox || + actor == this.statusArea.quickSettings) + ) { + let child = actor.get_first_child() - if (isVertical && !actor.visible && !actor._dtpVisibleId) { - this._unmappedButtons.push(actor); - actor._dtpVisibleId = actor.connect('notify::visible', () => { - this._disconnectVisibleId(actor); - this._refreshVerticalAlloc(); - }); - actor._dtpDestroyId = actor.connect('destroy', () => this._disconnectVisibleId(actor)); - } + if (isVertical && !actor.visible && !actor._dtpVisibleId) { + this._unmappedButtons.push(actor) + actor._dtpVisibleId = actor.connect('notify::visible', () => { + this._disconnectVisibleId(actor) + this._refreshVerticalAlloc() + }) + actor._dtpDestroyId = actor.connect('destroy', () => + this._disconnectVisibleId(actor), + ) + } - if (child) { - let [, natWidth] = actor.get_preferred_width(-1); + if (child) { + let [, natWidth] = actor.get_preferred_width(-1) - child.x_align = Clutter.ActorAlign[isVertical ? 'CENTER' : 'START']; - actor.set_width(isVertical ? this.dtpSize : -1); - isVertical = isVertical && (natWidth > this.dtpSize); - actor[(isVertical ? 'add' : 'remove') + '_style_class_name']('vertical'); - } - } + child.x_align = Clutter.ActorAlign[isVertical ? 'CENTER' : 'START'] + actor.set_width(isVertical ? this.dtpSize : -1) + isVertical = isVertical && natWidth > this.dtpSize + actor[(isVertical ? 'add' : 'remove') + '_style_class_name']( + 'vertical', + ) + } + } - actor.get_children().forEach(c => _set(c, isVertical)); - }; + actor.get_children().forEach((c) => _set(c, isVertical)) + } - _set(actor, false); + _set(actor, false) - if (isVertical) - _set(actor, isVertical); + if (isVertical) _set(actor, isVertical) } _disconnectVisibleId(actor) { - actor.disconnect(actor._dtpVisibleId); - actor.disconnect(actor._dtpDestroyId); + actor.disconnect(actor._dtpVisibleId) + actor.disconnect(actor._dtpDestroyId) - delete actor._dtpVisibleId; - delete actor._dtpDestroyId; + delete actor._dtpVisibleId + delete actor._dtpDestroyId - this._unmappedButtons.splice(this._unmappedButtons.indexOf(actor), 1); + this._unmappedButtons.splice(this._unmappedButtons.indexOf(actor), 1) } _formatVerticalClock() { - // https://github.com/GNOME/gnome-desktop/blob/master/libgnome-desktop/gnome-wall-clock.c#L310 - if (this.statusArea.dateMenu) { - let datetime = this.statusArea.dateMenu._clock.clock; - let datetimeParts = datetime.split(' '); - let time = datetimeParts[1]; - let clockText = this.statusArea.dateMenu._clockDisplay.clutter_text; - let setClockText = (text, useTimeSeparator) => { - let stacks = text instanceof Array; - let separator = `\n ${useTimeSeparator ? '‧‧' : '—' } \n`; + // https://github.com/GNOME/gnome-desktop/blob/master/libgnome-desktop/gnome-wall-clock.c#L310 + if (this.statusArea.dateMenu) { + let datetime = this.statusArea.dateMenu._clock.clock + let datetimeParts = datetime.split(' ') + let time = datetimeParts[1] + let clockText = this.statusArea.dateMenu._clockDisplay.clutter_text + let setClockText = (text, useTimeSeparator) => { + let stacks = text instanceof Array + let separator = `\n ${useTimeSeparator ? '‧‧' : '—'} \n` - clockText.set_text((stacks ? text.join(separator) : text).trim()); - clockText.set_use_markup(stacks); - clockText.get_allocation_box(); + clockText.set_text((stacks ? text.join(separator) : text).trim()) + clockText.set_use_markup(stacks) + clockText.get_allocation_box() - return !clockText.get_layout().is_ellipsized(); - }; - - if (clockText.ellipsize == Pango.EllipsizeMode.NONE) { - //on gnome-shell 3.36.4, the clockdisplay isn't ellipsize anymore, so set it back - clockText.ellipsize = Pango.EllipsizeMode.END; - } - - clockText.natural_width = this.dtpSize; - - if (!time) { - datetimeParts = datetime.split(' '); - time = datetimeParts.pop(); - datetimeParts = [datetimeParts.join(' '), time]; - } - - if (!setClockText(datetime) && - !setClockText(datetimeParts) && - !setClockText(time)) { - let timeParts = time.split('∶'); - - if (!this._clockFormat) { - this._clockFormat = DESKTOPSETTINGS.get_string('clock-format'); - } - - if (this._clockFormat == '12h') { - timeParts.push.apply(timeParts, timeParts.pop().split(' ')); - } - - setClockText(timeParts, true); - } + return !clockText.get_layout().is_ellipsized() } + + if (clockText.ellipsize == Pango.EllipsizeMode.NONE) { + //on gnome-shell 3.36.4, the clockdisplay isn't ellipsize anymore, so set it back + clockText.ellipsize = Pango.EllipsizeMode.END + } + + clockText.natural_width = this.dtpSize + + if (!time) { + datetimeParts = datetime.split(' ') + time = datetimeParts.pop() + datetimeParts = [datetimeParts.join(' '), time] + } + + if ( + !setClockText(datetime) && + !setClockText(datetimeParts) && + !setClockText(time) + ) { + let timeParts = time.split('∶') + + if (!this._clockFormat) { + this._clockFormat = DESKTOPSETTINGS.get_string('clock-format') + } + + if (this._clockFormat == '12h') { + timeParts.push.apply(timeParts, timeParts.pop().split(' ')) + } + + setClockText(timeParts, true) + } + } } _setShowDesktopButton(add) { - if (add) { - if(this._showDesktopButton) - return; + if (add) { + if (this._showDesktopButton) return - this._showDesktopButton = new St.Bin({ style_class: 'showdesktop-button', - reactive: true, - can_focus: true, - // x_fill: true, - // y_fill: true, - track_hover: true }); + this._showDesktopButton = new St.Bin({ + style_class: 'showdesktop-button', + reactive: true, + can_focus: true, + // x_fill: true, + // y_fill: true, + track_hover: true, + }) - this._setShowDesktopButtonStyle(); + this._setShowDesktopButtonStyle() - this._showDesktopButton.connect('touch-event', (actor, event) => { - if (event.type() == Clutter.EventType.TOUCH_BEGIN) { - this._onShowDesktopButtonPress(); - } - }); - this._showDesktopButton.connect('button-press-event', () => this._onShowDesktopButtonPress()); - this._showDesktopButton.connect('enter-event', () => { - this._showDesktopButton.add_style_class_name(this._getBackgroundBrightness() ? - 'showdesktop-button-light-hovered' : 'showdesktop-button-dark-hovered'); + this._showDesktopButton.connect('touch-event', (actor, event) => { + if (event.type() == Clutter.EventType.TOUCH_BEGIN) { + this._onShowDesktopButtonPress() + } + }) + this._showDesktopButton.connect('button-press-event', () => + this._onShowDesktopButtonPress(), + ) + this._showDesktopButton.connect('enter-event', () => { + this._showDesktopButton.add_style_class_name( + this._getBackgroundBrightness() + ? 'showdesktop-button-light-hovered' + : 'showdesktop-button-dark-hovered', + ) - if (SETTINGS.get_boolean('show-showdesktop-hover')) { - this._timeoutsHandler.add([T4, SETTINGS.get_int('show-showdesktop-delay'), () => { - this._hiddenDesktopWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - this._toggleWorkspaceWindows(true, this._hiddenDesktopWorkspace); - }]); - } - }); + if (SETTINGS.get_boolean('show-showdesktop-hover')) { + this._timeoutsHandler.add([ + T4, + SETTINGS.get_int('show-showdesktop-delay'), + () => { + this._hiddenDesktopWorkspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + this._toggleWorkspaceWindows(true, this._hiddenDesktopWorkspace) + }, + ]) + } + }) - this._showDesktopButton.connect('leave-event', () => { - this._showDesktopButton.remove_style_class_name(this._getBackgroundBrightness() ? - 'showdesktop-button-light-hovered' : 'showdesktop-button-dark-hovered'); + this._showDesktopButton.connect('leave-event', () => { + this._showDesktopButton.remove_style_class_name( + this._getBackgroundBrightness() + ? 'showdesktop-button-light-hovered' + : 'showdesktop-button-dark-hovered', + ) - if (SETTINGS.get_boolean('show-showdesktop-hover')) { - if (this._timeoutsHandler.getId(T4)) { - this._timeoutsHandler.remove(T4); - } else if (this._hiddenDesktopWorkspace) { - this._toggleWorkspaceWindows(false, this._hiddenDesktopWorkspace); - } - } - }); + if (SETTINGS.get_boolean('show-showdesktop-hover')) { + if (this._timeoutsHandler.getId(T4)) { + this._timeoutsHandler.remove(T4) + } else if (this._hiddenDesktopWorkspace) { + this._toggleWorkspaceWindows(false, this._hiddenDesktopWorkspace) + } + } + }) - this.panel.add_child(this._showDesktopButton); - } else { - if(!this._showDesktopButton) - return; + this.panel.add_child(this._showDesktopButton) + } else { + if (!this._showDesktopButton) return - this.panel.remove_child(this._showDesktopButton); - this._showDesktopButton.destroy(); - this._showDesktopButton = null; - } + this.panel.remove_child(this._showDesktopButton) + this._showDesktopButton.destroy() + this._showDesktopButton = null + } } _setShowDesktopButtonStyle() { - let rgb = this._getBackgroundBrightness() ? "rgba(55, 55, 55, .2)" : "rgba(200, 200, 200, .2)"; + let rgb = this._getBackgroundBrightness() + ? 'rgba(55, 55, 55, .2)' + : 'rgba(200, 200, 200, .2)' - let isLineCustom = SETTINGS.get_boolean('desktop-line-use-custom-color'); - rgb = isLineCustom ? SETTINGS.get_string('desktop-line-custom-color') : rgb; + let isLineCustom = SETTINGS.get_boolean('desktop-line-use-custom-color') + rgb = isLineCustom + ? SETTINGS.get_string('desktop-line-custom-color') + : rgb - if (this._showDesktopButton) { - let buttonSize = SETTINGS.get_int('showdesktop-button-width') + 'px;'; - let isVertical = this.checkIfVertical(); + if (this._showDesktopButton) { + let buttonSize = SETTINGS.get_int('showdesktop-button-width') + 'px;' + let isVertical = this.checkIfVertical() - let sytle = "border: 0 solid " + rgb + ";"; - sytle += isVertical ? 'border-top-width:1px;height:' + buttonSize : 'border-left-width:1px;width:' + buttonSize; + let sytle = 'border: 0 solid ' + rgb + ';' + sytle += isVertical + ? 'border-top-width:1px;height:' + buttonSize + : 'border-left-width:1px;width:' + buttonSize - this._showDesktopButton.set_style(sytle); - this._showDesktopButton[(isVertical ? 'x' : 'y') + '_expand'] = true; - } + this._showDesktopButton.set_style(sytle) + this._showDesktopButton[(isVertical ? 'x' : 'y') + '_expand'] = true + } } // _getBackgroundBrightness: return true if panel has a bright background color _getBackgroundBrightness() { - return Utils.checkIfColorIsBright(this.dynamicTransparency.backgroundColorRgb); + return Utils.checkIfColorIsBright( + this.dynamicTransparency.backgroundColorRgb, + ) } _toggleWorkspaceWindows(hide, workspace) { - let time = SETTINGS.get_int('show-showdesktop-time') * .001; + let time = SETTINGS.get_int('show-showdesktop-time') * 0.001 - workspace.list_windows().forEach(w => { - if (!w.minimized && !w.customJS_ding) { - let tweenOpts = { - opacity: hide ? 0 : 255, - time: time, - transition: 'easeOutQuad' - }; + workspace.list_windows().forEach((w) => { + if (!w.minimized && !w.customJS_ding) { + let tweenOpts = { + opacity: hide ? 0 : 255, + time: time, + transition: 'easeOutQuad', + } - Utils.animateWindowOpacity(w.get_compositor_private(), tweenOpts); - } - }); + Utils.animateWindowOpacity(w.get_compositor_private(), tweenOpts) + } + }) } _onShowDesktopButtonPress() { - let label = 'trackerFocusApp'; + let label = 'trackerFocusApp' - this._signalsHandler.removeWithLabel(label); - this._timeoutsHandler.remove(T5); + this._signalsHandler.removeWithLabel(label) + this._timeoutsHandler.remove(T5) - if(this._restoreWindowList && this._restoreWindowList.length) { - this._timeoutsHandler.remove(T4); + if (this._restoreWindowList && this._restoreWindowList.length) { + this._timeoutsHandler.remove(T4) - let current_workspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - let windows = current_workspace.list_windows(); - this._restoreWindowList.forEach(function(w) { - if(windows.indexOf(w) > -1) - Main.activateWindow(w); - }); - this._restoreWindowList = null; - } else { - let current_workspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - let windows = current_workspace.list_windows().filter(function (w) { - return w.showing_on_its_workspace() && !w.skip_taskbar; - }); - windows = global.display.sort_windows_by_stacking(windows); + let current_workspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + let windows = current_workspace.list_windows() + this._restoreWindowList.forEach(function (w) { + if (windows.indexOf(w) > -1) Main.activateWindow(w) + }) + this._restoreWindowList = null + } else { + let current_workspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + let windows = current_workspace.list_windows().filter(function (w) { + return w.showing_on_its_workspace() && !w.skip_taskbar + }) + windows = global.display.sort_windows_by_stacking(windows) - windows.forEach(function(w) { - w.minimize(); - }); + windows.forEach(function (w) { + w.minimize() + }) - this._restoreWindowList = windows; + this._restoreWindowList = windows - this._timeoutsHandler.add([T5, 20, () => this._signalsHandler.addWithLabel( - label, - [ - tracker, - 'notify::focus-app', - () => this._restoreWindowList = null - ] - )]); - } + this._timeoutsHandler.add([ + T5, + 20, + () => + this._signalsHandler.addWithLabel(label, [ + tracker, + 'notify::focus-app', + () => (this._restoreWindowList = null), + ]), + ]) + } - Main.overview.hide(); + Main.overview.hide() } _onPanelMouseScroll(actor, event) { - let scrollAction = SETTINGS.get_string('scroll-panel-action'); - let direction = Utils.getMouseScrollDirection(event); + let scrollAction = SETTINGS.get_string('scroll-panel-action') + let direction = Utils.getMouseScrollDirection(event) - const targetActor = global.stage.get_event_actor(event); + const targetActor = global.stage.get_event_actor(event) - if (!this._checkIfIgnoredScrollSource(targetActor) && !this._timeoutsHandler.getId(T6)) { - if (direction && scrollAction === 'SWITCH_WORKSPACE') { - let args = [global.display]; + if ( + !this._checkIfIgnoredScrollSource(targetActor) && + !this._timeoutsHandler.getId(T6) + ) { + if (direction && scrollAction === 'SWITCH_WORKSPACE') { + let args = [global.display] - //adjust for horizontal workspaces - if (Utils.DisplayWrapper.getWorkspaceManager().layout_rows === 1) { - direction = direction == 'up' ? 'left' : 'right'; - } + //adjust for horizontal workspaces + if (Utils.DisplayWrapper.getWorkspaceManager().layout_rows === 1) { + direction = direction == 'up' ? 'left' : 'right' + } - //gnome-shell < 3.30 needs an additional "screen" param - global.screen ? args.push(global.screen) : 0; + //gnome-shell < 3.30 needs an additional "screen" param + global.screen ? args.push(global.screen) : 0 - let showWsPopup = SETTINGS.get_boolean('scroll-panel-show-ws-popup'); - showWsPopup ? 0 : Main.wm._workspaceSwitcherPopup = { display: () => {} }; - Main.wm._showWorkspaceSwitcher.apply(Main.wm, args.concat([0, { get_name: () => 'switch---' + direction }])); - showWsPopup ? 0 : Main.wm._workspaceSwitcherPopup = null; - } else if (direction && scrollAction === 'CYCLE_WINDOWS') { - let windows = this.taskbar.getAppInfos().reduce((ws, appInfo) => ws.concat(appInfo.windows), []); + let showWsPopup = SETTINGS.get_boolean('scroll-panel-show-ws-popup') + showWsPopup + ? 0 + : (Main.wm._workspaceSwitcherPopup = { display: () => {} }) + Main.wm._showWorkspaceSwitcher.apply( + Main.wm, + args.concat([0, { get_name: () => 'switch---' + direction }]), + ) + showWsPopup ? 0 : (Main.wm._workspaceSwitcherPopup = null) + } else if (direction && scrollAction === 'CYCLE_WINDOWS') { + let windows = this.taskbar + .getAppInfos() + .reduce((ws, appInfo) => ws.concat(appInfo.windows), []) - Utils.activateSiblingWindow(windows, direction); - } else if (scrollAction === 'CHANGE_VOLUME' && !event.is_pointer_emulated()) { - let proto = Volume.OutputIndicator.prototype; - let func = proto._handleScrollEvent || proto.vfunc_scroll_event || proto._onScrollEvent; - let indicator = Main.panel.statusArea[Utils.getSystemMenuInfo().name]._volumeOutput; + Utils.activateSiblingWindow(windows, direction) + } else if ( + scrollAction === 'CHANGE_VOLUME' && + !event.is_pointer_emulated() + ) { + let proto = Volume.OutputIndicator.prototype + let func = + proto._handleScrollEvent || + proto.vfunc_scroll_event || + proto._onScrollEvent + let indicator = + Main.panel.statusArea[Utils.getSystemMenuInfo().name]._volumeOutput - if (indicator.quickSettingsItems) - // new quick settings menu in gnome-shell > 42 - func(indicator.quickSettingsItems[0], event); - else - func.call(indicator, 0, event); - } else { - return; - } - - const scrollDelay = SETTINGS.get_int('scroll-panel-delay'); - - if (scrollDelay) { - this._timeoutsHandler.add([T6, scrollDelay, () => {}]); - } + if (indicator.quickSettingsItems) + // new quick settings menu in gnome-shell > 42 + func(indicator.quickSettingsItems[0], event) + else func.call(indicator, 0, event) + } else { + return } + + const scrollDelay = SETTINGS.get_int('scroll-panel-delay') + + if (scrollDelay) { + this._timeoutsHandler.add([T6, scrollDelay, () => {}]) + } + } } _checkIfIgnoredScrollSource(source) { - let ignoredConstr = ['WorkspaceIndicator']; + let ignoredConstr = ['WorkspaceIndicator'] - return source.get_parent()._dtpIgnoreScroll || ignoredConstr.indexOf(source.constructor.name) >= 0; + return ( + source.get_parent()._dtpIgnoreScroll || + ignoredConstr.indexOf(source.constructor.name) >= 0 + ) } _initProgressManager() { - const progressVisible = SETTINGS.get_boolean('progress-show-bar'); - const countVisible = SETTINGS.get_boolean('progress-show-count'); - const pm = this.progressManager; + const progressVisible = SETTINGS.get_boolean('progress-show-bar') + const countVisible = SETTINGS.get_boolean('progress-show-count') + const pm = this.progressManager - if(!pm && (progressVisible || countVisible)) - this.progressManager = new Progress.ProgressManager(); - else if (pm) - Object.keys(pm._entriesByDBusName).forEach((k) => pm._entriesByDBusName[k].setCountVisible(countVisible)); + if (!pm && (progressVisible || countVisible)) + this.progressManager = new Progress.ProgressManager() + else if (pm) + Object.keys(pm._entriesByDBusName).forEach((k) => + pm._entriesByDBusName[k].setCountVisible(countVisible), + ) } -}); - -export const SecondaryPanel = GObject.registerClass({ -}, class SecondaryPanel extends St.Widget { + }, +) +export const SecondaryPanel = GObject.registerClass( + {}, + class SecondaryPanel extends St.Widget { _init(params) { - super._init(params); + super._init(params) } vfunc_allocate(box) { - this.set_allocation(box); + this.set_allocation(box) } -}); + }, +) diff --git a/panelManager.js b/panelManager.js index 9073739..897eea0 100755 --- a/panelManager.js +++ b/panelManager.js @@ -17,765 +17,938 @@ * Credits: * This file is based on code from the Dash to Dock extension by micheleg * and code from the Taskbar extension by Zorin OS - * + * * Code to re-anchor the panel was taken from Thoma5 BottomPanel: * https://github.com/Thoma5/gnome-shell-extension-bottompanel - * + * * Pattern for moving clock based on Frippery Move Clock by R M Yorston * http://frippery.org/extensions/ - * + * * Some code was also adapted from the upstream Gnome Shell source code. */ -import * as Overview from './overview.js'; -import * as Panel from './panel.js'; -import * as PanelSettings from './panelSettings.js'; -import * as Proximity from './proximity.js'; -import * as Utils from './utils.js'; -import * as DesktopIconsIntegration from './desktopIconsIntegration.js'; +import * as Overview from './overview.js' +import * as Panel from './panel.js' +import * as PanelSettings from './panelSettings.js' +import * as Proximity from './proximity.js' +import * as Utils from './utils.js' +import * as DesktopIconsIntegration from './desktopIconsIntegration.js' -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import Clutter from 'gi://Clutter'; -import Meta from 'gi://Meta'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; - -import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js'; -import * as LookingGlass from 'resource:///org/gnome/shell/ui/lookingGlass.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; -import * as Layout from 'resource:///org/gnome/shell/ui/layout.js'; -import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; -import {SETTINGS} from './extension.js'; -import {SecondaryMonitorDisplay, WorkspacesView} from 'resource:///org/gnome/shell/ui/workspacesView.js'; +import GLib from 'gi://GLib' +import GObject from 'gi://GObject' +import Clutter from 'gi://Clutter' +import Meta from 'gi://Meta' +import Shell from 'gi://Shell' +import St from 'gi://St' +import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js' +import * as LookingGlass from 'resource:///org/gnome/shell/ui/lookingGlass.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js' +import * as Layout from 'resource:///org/gnome/shell/ui/layout.js' +import { InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js' +import { SETTINGS } from './extension.js' +import { + SecondaryMonitorDisplay, + WorkspacesView, +} from 'resource:///org/gnome/shell/ui/workspacesView.js' export const PanelManager = class { + constructor() { + this.overview = new Overview.Overview() + this.panelsElementPositions = {} + this._injectionManager = new InjectionManager() - constructor() { - this.overview = new Overview.Overview(); - this.panelsElementPositions = {}; - this._injectionManager = new InjectionManager(); + this._saveMonitors() + } - this._saveMonitors(); + enable(reset) { + let dtpPrimaryIndex = SETTINGS.get_int('primary-monitor') + + this.allPanels = [] + this.dtpPrimaryMonitor = + Main.layoutManager.monitors[dtpPrimaryIndex] || + Main.layoutManager.primaryMonitor + this.proximityManager = new Proximity.ProximityManager() + + if (this.dtpPrimaryMonitor) { + this.primaryPanel = this._createPanel( + this.dtpPrimaryMonitor, + SETTINGS.get_boolean('stockgs-keep-top-panel'), + ) + this.allPanels.push(this.primaryPanel) + this.overview.enable(this.primaryPanel) + + this.setFocusedMonitor(this.dtpPrimaryMonitor) } - enable(reset) { - let dtpPrimaryIndex = SETTINGS.get_int('primary-monitor'); + if (SETTINGS.get_boolean('multi-monitors')) { + Main.layoutManager.monitors + .filter((m) => m != this.dtpPrimaryMonitor) + .forEach((m) => { + this.allPanels.push(this._createPanel(m, true)) + }) + } - this.allPanels = []; - this.dtpPrimaryMonitor = Main.layoutManager.monitors[dtpPrimaryIndex] || Main.layoutManager.primaryMonitor; - this.proximityManager = new Proximity.ProximityManager(); + global.dashToPanel.panels = this.allPanels + global.dashToPanel.emit('panels-created') - if (this.dtpPrimaryMonitor) { - this.primaryPanel = this._createPanel(this.dtpPrimaryMonitor, SETTINGS.get_boolean('stockgs-keep-top-panel')); - this.allPanels.push(this.primaryPanel); - this.overview.enable(this.primaryPanel); + this.allPanels.forEach((p) => { + let panelPosition = p.getPosition() + let leftOrRight = + panelPosition == St.Side.LEFT || panelPosition == St.Side.RIGHT - this.setFocusedMonitor(this.dtpPrimaryMonitor); + p.panelBox.set_size( + leftOrRight ? -1 : p.geom.w + p.geom.lrPadding, + leftOrRight ? p.geom.h + p.geom.tbPadding : -1, + ) + + this._findPanelMenuButtons(p.panelBox).forEach((pmb) => + this._adjustPanelMenuButton(pmb, p.monitor, panelPosition), + ) + + p.taskbar.iconAnimator.start() + }) + + this._setDesktopIconsMargins() + //in 3.32, BoxPointer now inherits St.Widget + if (BoxPointer.BoxPointer.prototype.vfunc_get_preferred_height) { + let panelManager = this + + this._injectionManager.overrideMethod( + BoxPointer.BoxPointer.prototype, + 'vfunc_get_preferred_height', + () => + function (forWidth) { + let alloc = { min_size: 0, natural_size: 0 } + + ;[alloc.min_size, alloc.natural_size] = + this.vfunc_get_preferred_height(forWidth) + + return panelManager._getBoxPointerPreferredHeight(this, alloc) + }, + ) + } + + this._updatePanelElementPositions() + + if (reset) return + + this._desktopIconsUsableArea = + new DesktopIconsIntegration.DesktopIconsUsableAreaClass() + + this._oldUpdatePanelBarrier = Main.layoutManager._updatePanelBarrier + Main.layoutManager._updatePanelBarrier = (panel) => { + let panelUpdates = panel ? [panel] : this.allPanels + + panelUpdates.forEach((p) => + newUpdatePanelBarrier.call(Main.layoutManager, p), + ) + } + Main.layoutManager._updatePanelBarrier() + + this._oldUpdateHotCorners = Main.layoutManager._updateHotCorners + Main.layoutManager._updateHotCorners = newUpdateHotCorners.bind( + Main.layoutManager, + ) + Main.layoutManager._updateHotCorners() + + this._forceHotCornerId = SETTINGS.connect( + 'changed::stockgs-force-hotcorner', + () => Main.layoutManager._updateHotCorners(), + ) + + if (Main.layoutManager._interfaceSettings) { + this._enableHotCornersId = Main.layoutManager._interfaceSettings.connect( + 'changed::enable-hot-corners', + () => Main.layoutManager._updateHotCorners(), + ) + } + + this._oldUpdateWorkspacesViews = + Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews + Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = + this._newUpdateWorkspacesViews.bind( + Main.overview._overview._controls._workspacesDisplay, + ) + + this._oldSetPrimaryWorkspaceVisible = + Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible + Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = + this._newSetPrimaryWorkspaceVisible.bind( + Main.overview._overview._controls._workspacesDisplay, + ) + + LookingGlass.LookingGlass.prototype._oldResize = + LookingGlass.LookingGlass.prototype._resize + LookingGlass.LookingGlass.prototype._resize = _newLookingGlassResize + + LookingGlass.LookingGlass.prototype._oldOpen = + LookingGlass.LookingGlass.prototype.open + LookingGlass.LookingGlass.prototype.open = _newLookingGlassOpen + + this._signalsHandler = new Utils.GlobalSignalsHandler() + + //listen settings + this._signalsHandler.add( + [ + SETTINGS, + [ + 'changed::primary-monitor', + 'changed::multi-monitors', + 'changed::isolate-monitors', + 'changed::panel-positions', + 'changed::panel-lengths', + 'changed::panel-anchors', + 'changed::stockgs-keep-top-panel', + ], + () => this._reset(), + ], + [ + SETTINGS, + 'changed::panel-element-positions', + () => this._updatePanelElementPositions(), + ], + [ + SETTINGS, + 'changed::intellihide-key-toggle-text', + () => this._setKeyBindings(true), + ], + [ + SETTINGS, + 'changed::panel-sizes', + () => { + GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._setDesktopIconsMargins() + return GLib.SOURCE_REMOVE + }) + }, + ], + [ + Utils.DisplayWrapper.getMonitorManager(), + 'monitors-changed', + () => { + if (Main.layoutManager.primaryMonitor) { + this._saveMonitors() + this._reset() + } + }, + ], + ) + + Panel.panelBoxes.forEach((c) => + this._signalsHandler.add([ + Main.panel[c], + 'child-added', + (parent, child) => { + this.primaryPanel && + child instanceof St.Bin && + this._adjustPanelMenuButton( + this._getPanelMenuButton(child.get_first_child()), + this.primaryPanel.monitor, + this.primaryPanel.getPosition(), + ) + }, + ]), + ) + + this._setKeyBindings(true) + + // keep GS overview.js from blowing away custom panel styles + if (!SETTINGS.get_boolean('stockgs-keep-top-panel')) + Object.defineProperty(Main.panel, 'style', { + configurable: true, + set(v) {}, + }) + } + + disable(reset) { + this.primaryPanel && this.overview.disable() + this.proximityManager.destroy() + + this.allPanels.forEach((p) => { + p.taskbar.iconAnimator.pause() + + this._findPanelMenuButtons(p.panelBox).forEach((pmb) => { + if (pmb.menu._boxPointer._dtpGetPreferredHeightId) { + pmb.menu._boxPointer._container.disconnect( + pmb.menu._boxPointer._dtpGetPreferredHeightId, + ) } - if (SETTINGS.get_boolean('multi-monitors')) { - Main.layoutManager.monitors.filter(m => m != this.dtpPrimaryMonitor).forEach(m => { - this.allPanels.push(this._createPanel(m, true)); - }); + pmb.menu._boxPointer.sourceActor = pmb.menu._boxPointer._dtpSourceActor + delete pmb.menu._boxPointer._dtpSourceActor + pmb.menu._boxPointer._userArrowSide = St.Side.TOP + }) + + this._removePanelBarriers(p) + + p.disable() + + let clipContainer = p.panelBox.get_parent() + + Main.layoutManager._untrackActor(p.panelBox) + Main.layoutManager.removeChrome(clipContainer) + + if (p.isStandalone) { + p.panelBox.destroy() + } else { + p.panelBox.remove_child(p) + p.remove_child(p.panel) + p.panelBox.add_child(p.panel) + + p.panelBox.set_position(clipContainer.x, clipContainer.y) + + clipContainer.remove_child(p.panelBox) + Main.layoutManager.addChrome(p.panelBox, { + affectsStruts: true, + trackFullscreen: true, + }) + } + }) + + this._injectionManager.clear() + + if (Main.layoutManager.primaryMonitor) { + Main.layoutManager.panelBox.set_position( + Main.layoutManager.primaryMonitor.x, + Main.layoutManager.primaryMonitor.y, + ) + Main.layoutManager.panelBox.set_size( + Main.layoutManager.primaryMonitor.width, + -1, + ) + } + + if (reset) return + + this._setKeyBindings(false) + + this._signalsHandler.destroy() + + Main.layoutManager._updateHotCorners = this._oldUpdateHotCorners + Main.layoutManager._updateHotCorners() + + SETTINGS.disconnect(this._forceHotCornerId) + + if (this._enableHotCornersId) { + Main.layoutManager._interfaceSettings.disconnect(this._enableHotCornersId) + } + + Main.layoutManager._updatePanelBarrier = this._oldUpdatePanelBarrier + Main.layoutManager._updatePanelBarrier() + + Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = + this._oldUpdateWorkspacesViews + Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = + this._oldSetPrimaryWorkspaceVisible + + LookingGlass.LookingGlass.prototype._resize = + LookingGlass.LookingGlass.prototype._oldResize + delete LookingGlass.LookingGlass.prototype._oldResize + + LookingGlass.LookingGlass.prototype.open = + LookingGlass.LookingGlass.prototype._oldOpen + delete LookingGlass.LookingGlass.prototype._oldOpen + + delete Main.panel.style + this._desktopIconsUsableArea.destroy() + this._desktopIconsUsableArea = null + } + + _setDesktopIconsMargins() { + this._desktopIconsUsableArea?.resetMargins() + this.allPanels.forEach((p) => { + switch (p.geom.position) { + case St.Side.TOP: + this._desktopIconsUsableArea?.setMargins( + p.monitor.index, + p.geom.h, + 0, + 0, + 0, + ) + break + case St.Side.BOTTOM: + this._desktopIconsUsableArea?.setMargins( + p.monitor.index, + 0, + p.geom.h, + 0, + 0, + ) + break + case St.Side.LEFT: + this._desktopIconsUsableArea?.setMargins( + p.monitor.index, + 0, + 0, + p.geom.w, + 0, + ) + break + case St.Side.RIGHT: + this._desktopIconsUsableArea?.setMargins( + p.monitor.index, + 0, + 0, + 0, + p.geom.w, + ) + break + } + }) + } + + setFocusedMonitor(monitor) { + this.focusedMonitorPanel = this.allPanels.find((p) => p.monitor == monitor) + + if (!this.checkIfFocusedMonitor(monitor)) { + Main.overview._overview.clear_constraints() + Main.overview._overview.add_constraint( + new Layout.MonitorConstraint({ index: monitor.index }), + ) + + Main.overview._overview._controls._workspacesDisplay._primaryIndex = + monitor.index + + // https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2395 + // The overview allocation used to calculate its workarea based on the monitor where the overview + // was displayed, but it got changed back to always use the primary monitor. So now, temporarily assign + // the primary monitor to dtp focused monitor while recalculating the overview workarea + Main.layoutManager.primaryMonitor = monitor + Main.overview._overview._controls.layout_manager._updateWorkAreaBox() + Main.layoutManager.primaryMonitor = + Main.layoutManager.monitors[Main.layoutManager.primaryIndex] + } + } + + _newSetPrimaryWorkspaceVisible(visible) { + if (this._primaryVisible === visible) return + + this._primaryVisible = visible + + const primaryIndex = + Main.overview._overview._controls._workspacesDisplay._primaryIndex + const primaryWorkspace = this._workspacesViews[primaryIndex] + if (primaryWorkspace) primaryWorkspace.visible = visible + } + + _newUpdateWorkspacesViews() { + for (let i = 0; i < this._workspacesViews.length; i++) + this._workspacesViews[i].destroy() + + this._workspacesViews = [] + let monitors = Main.layoutManager.monitors + for (let i = 0; i < monitors.length; i++) { + let view + if (i === this._primaryIndex) { + view = new WorkspacesView( + i, + this._controls, + this._scrollAdjustment, + this._fitModeAdjustment, + this._overviewAdjustment, + ) + + view.visible = this._primaryVisible + this.bind_property( + 'opacity', + view, + 'opacity', + GObject.BindingFlags.SYNC_CREATE, + ) + this.add_child(view) + } else { + // No idea why atm, but we need the import at the top of this file and to use the + // full imports ns here, otherwise SecondaryMonitorDisplay can't be used ¯\_(ツ)_/¯ + view = new SecondaryMonitorDisplay( + i, + this._controls, + this._scrollAdjustment, + this._fitModeAdjustment, + this._overviewAdjustment, + ) + Main.layoutManager.overviewGroup.add_child(view) + } + + this._workspacesViews.push(view) + } + } + + _saveMonitors() { + //Mutter meta_monitor_manager_get_primary_monitor (global.display.get_primary_monitor()) doesn't return the same + //monitor as GDK gdk_screen_get_primary_monitor (imports.gi.Gdk.Screen.get_default().get_primary_monitor()). + //Since the Mutter function is what's used in gnome-shell and we can't access it from the settings dialog, store + //the monitors information in a setting so we can use the same monitor indexes as the ones in gnome-shell + let keyMonitors = 'available-monitors' + let keyPrimary = 'primary-monitor' + let primaryIndex = Main.layoutManager.primaryIndex + let newMonitors = [primaryIndex] + let savedMonitors = SETTINGS.get_value(keyMonitors).deep_unpack() + let dtpPrimaryIndex = SETTINGS.get_int(keyPrimary) + let newDtpPrimaryIndex = primaryIndex + + Main.layoutManager.monitors + .filter((m) => m.index != primaryIndex) + .forEach((m) => newMonitors.push(m.index)) + + if (savedMonitors[0] != dtpPrimaryIndex) { + // dash to panel primary wasn't the gnome-shell primary (first index of available-monitors) + let savedIndex = savedMonitors.indexOf(dtpPrimaryIndex) + + // default to primary if it was set to a monitor that is no longer available + newDtpPrimaryIndex = newMonitors[savedIndex] + newDtpPrimaryIndex = + newDtpPrimaryIndex == null ? primaryIndex : newDtpPrimaryIndex + } + + SETTINGS.set_int(keyPrimary, newDtpPrimaryIndex) + SETTINGS.set_value(keyMonitors, new GLib.Variant('ai', newMonitors)) + } + + checkIfFocusedMonitor(monitor) { + return ( + Main.overview._overview._controls._workspacesDisplay._primaryIndex == + monitor.index + ) + } + + _createPanel(monitor, isStandalone) { + let panelBox + let panel + let clipContainer = new Clutter.Actor() + + if (isStandalone) { + panelBox = new St.BoxLayout({ name: 'panelBox' }) + } else { + panelBox = Main.layoutManager.panelBox + Main.layoutManager._untrackActor(panelBox) + panelBox.remove_child(Main.panel) + Main.layoutManager.removeChrome(panelBox) + } + + Main.layoutManager.addChrome(clipContainer, { affectsInputRegion: false }) + clipContainer.add_child(panelBox) + Main.layoutManager.trackChrome(panelBox, { + trackFullscreen: true, + affectsStruts: true, + affectsInputRegion: true, + }) + + panel = new Panel.Panel(this, monitor, panelBox, isStandalone) + panelBox.add_child(panel) + panel.enable() + + panelBox.visible = true + if (monitor.inFullscreen) { + panelBox.hide() + } + panelBox.set_position(0, 0) + + return panel + } + + _reset() { + this.disable(true) + this.allPanels = [] + this.enable(true) + } + + _updatePanelElementPositions() { + this.panelsElementPositions = PanelSettings.getSettingsJson( + SETTINGS, + 'panel-element-positions', + ) + this.allPanels.forEach((p) => p.updateElementPositions()) + } + + _adjustPanelMenuButton(button, monitor, arrowSide) { + if (button) { + button.menu._boxPointer._dtpSourceActor = + button.menu._boxPointer.sourceActor + button.menu._boxPointer.sourceActor = button + button.menu._boxPointer._userArrowSide = arrowSide + button.menu._boxPointer._dtpInPanel = 1 + + if (!button.menu._boxPointer.vfunc_get_preferred_height) { + button.menu._boxPointer._dtpGetPreferredHeightId = + button.menu._boxPointer._container.connect( + 'get-preferred-height', + (actor, forWidth, alloc) => { + this._getBoxPointerPreferredHeight( + button.menu._boxPointer, + alloc, + monitor, + ) + }, + ) + } + } + } + + _getBoxPointerPreferredHeight(boxPointer, alloc, monitor) { + if ( + boxPointer._dtpInPanel && + boxPointer.sourceActor && + SETTINGS.get_boolean('intellihide') + ) { + monitor = + monitor || + Main.layoutManager.findMonitorForActor(boxPointer.sourceActor) + let panel = Utils.find( + global.dashToPanel.panels, + (p) => p.monitor == monitor, + ) + let excess = alloc.natural_size + panel.dtpSize + 10 - monitor.height // 10 is arbitrary + + if (excess > 0) { + alloc.natural_size -= excess + } + } + + return [alloc.min_size, alloc.natural_size] + } + + _findPanelMenuButtons(container) { + let panelMenuButtons = [] + let panelMenuButton + + let find = (parent) => + parent.get_children().forEach((c) => { + if ((panelMenuButton = this._getPanelMenuButton(c))) { + panelMenuButtons.push(panelMenuButton) } - global.dashToPanel.panels = this.allPanels; - global.dashToPanel.emit('panels-created'); + find(c) + }) - this.allPanels.forEach(p => { - let panelPosition = p.getPosition(); - let leftOrRight = (panelPosition == St.Side.LEFT || panelPosition == St.Side.RIGHT); - - p.panelBox.set_size( - leftOrRight ? -1 : p.geom.w + p.geom.lrPadding, - leftOrRight ? p.geom.h + p.geom.tbPadding : -1 - ); + find(container) - this._findPanelMenuButtons(p.panelBox).forEach(pmb => this._adjustPanelMenuButton(pmb, p.monitor, panelPosition)); - - p.taskbar.iconAnimator.start(); - }); + return panelMenuButtons + } - this._setDesktopIconsMargins(); - //in 3.32, BoxPointer now inherits St.Widget - if (BoxPointer.BoxPointer.prototype.vfunc_get_preferred_height) { - let panelManager = this; - - this._injectionManager.overrideMethod(BoxPointer.BoxPointer.prototype, 'vfunc_get_preferred_height', () => function(forWidth) { - let alloc = { min_size: 0, natural_size: 0 }; - - [alloc.min_size, alloc.natural_size] = this.vfunc_get_preferred_height(forWidth); - - return panelManager._getBoxPointerPreferredHeight(this, alloc); - }); - } - - this._updatePanelElementPositions(); - - if (reset) return; - - this._desktopIconsUsableArea = new DesktopIconsIntegration.DesktopIconsUsableAreaClass(); - - this._oldUpdatePanelBarrier = Main.layoutManager._updatePanelBarrier; - Main.layoutManager._updatePanelBarrier = (panel) => { - let panelUpdates = panel ? [panel] : this.allPanels; - - panelUpdates.forEach(p => newUpdatePanelBarrier.call(Main.layoutManager, p)); - }; - Main.layoutManager._updatePanelBarrier(); - - this._oldUpdateHotCorners = Main.layoutManager._updateHotCorners; - Main.layoutManager._updateHotCorners = newUpdateHotCorners.bind(Main.layoutManager); - Main.layoutManager._updateHotCorners(); - - this._forceHotCornerId = SETTINGS.connect('changed::stockgs-force-hotcorner', () => Main.layoutManager._updateHotCorners()); - - if (Main.layoutManager._interfaceSettings) { - this._enableHotCornersId = Main.layoutManager._interfaceSettings.connect('changed::enable-hot-corners', () => Main.layoutManager._updateHotCorners()); - } - - this._oldUpdateWorkspacesViews = Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews; - Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = this._newUpdateWorkspacesViews.bind(Main.overview._overview._controls._workspacesDisplay); - - this._oldSetPrimaryWorkspaceVisible = Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible - Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = this._newSetPrimaryWorkspaceVisible.bind(Main.overview._overview._controls._workspacesDisplay); - - LookingGlass.LookingGlass.prototype._oldResize = LookingGlass.LookingGlass.prototype._resize; - LookingGlass.LookingGlass.prototype._resize = _newLookingGlassResize; - - LookingGlass.LookingGlass.prototype._oldOpen = LookingGlass.LookingGlass.prototype.open; - LookingGlass.LookingGlass.prototype.open = _newLookingGlassOpen; - - this._signalsHandler = new Utils.GlobalSignalsHandler(); - - //listen settings - this._signalsHandler.add( - [ - SETTINGS, - [ - 'changed::primary-monitor', - 'changed::multi-monitors', - 'changed::isolate-monitors', - 'changed::panel-positions', - 'changed::panel-lengths', - 'changed::panel-anchors', - 'changed::stockgs-keep-top-panel' - ], - () => this._reset() - ], - [ - SETTINGS, - 'changed::panel-element-positions', - () => this._updatePanelElementPositions() - ], - [ - SETTINGS, - 'changed::intellihide-key-toggle-text', - () => this._setKeyBindings(true) - ], - [ - SETTINGS, - 'changed::panel-sizes', - () => { - GLib.idle_add(GLib.PRIORITY_LOW, () => { - this._setDesktopIconsMargins(); - return GLib.SOURCE_REMOVE; - }); - } - ], - [ - Utils.DisplayWrapper.getMonitorManager(), - 'monitors-changed', - () => { - if (Main.layoutManager.primaryMonitor) { - this._saveMonitors(); - this._reset(); - } - } - ] - ); - - Panel.panelBoxes.forEach(c => this._signalsHandler.add( - [ - Main.panel[c], - 'child-added', - (parent, child) => { - this.primaryPanel && - child instanceof St.Bin && - this._adjustPanelMenuButton(this._getPanelMenuButton(child.get_first_child()), this.primaryPanel.monitor, this.primaryPanel.getPosition()) - } - ] - )); - - this._setKeyBindings(true); - - // keep GS overview.js from blowing away custom panel styles - if(!SETTINGS.get_boolean('stockgs-keep-top-panel')) - Object.defineProperty(Main.panel, "style", {configurable: true, set(v) {}}); + _removePanelBarriers(panel) { + if (panel.isStandalone && panel._rightPanelBarrier) { + panel._rightPanelBarrier.destroy() } - disable(reset) { - this.primaryPanel && this.overview.disable(); - this.proximityManager.destroy(); + if (panel._leftPanelBarrier) { + panel._leftPanelBarrier.destroy() + delete panel._leftPanelBarrier + } + } - this.allPanels.forEach(p => { - p.taskbar.iconAnimator.pause(); + _getPanelMenuButton(obj) { + return obj instanceof PanelMenu.Button && obj.menu?._boxPointer ? obj : 0 + } - this._findPanelMenuButtons(p.panelBox).forEach(pmb => { - if (pmb.menu._boxPointer._dtpGetPreferredHeightId) { - pmb.menu._boxPointer._container.disconnect(pmb.menu._boxPointer._dtpGetPreferredHeightId); - } - - pmb.menu._boxPointer.sourceActor = pmb.menu._boxPointer._dtpSourceActor; - delete pmb.menu._boxPointer._dtpSourceActor; - pmb.menu._boxPointer._userArrowSide = St.Side.TOP; - }) - - this._removePanelBarriers(p); - - p.disable(); - - let clipContainer = p.panelBox.get_parent(); - - Main.layoutManager._untrackActor(p.panelBox); - Main.layoutManager.removeChrome(clipContainer); - - if (p.isStandalone) { - p.panelBox.destroy(); - } else { - p.panelBox.remove_child(p); - p.remove_child(p.panel); - p.panelBox.add_child(p.panel); - - p.panelBox.set_position(clipContainer.x, clipContainer.y); - - clipContainer.remove_child(p.panelBox); - Main.layoutManager.addChrome(p.panelBox, { affectsStruts: true, trackFullscreen: true }); - } - }); - - this._injectionManager.clear(); - - if (Main.layoutManager.primaryMonitor) { - Main.layoutManager.panelBox.set_position(Main.layoutManager.primaryMonitor.x, Main.layoutManager.primaryMonitor.y); - Main.layoutManager.panelBox.set_size(Main.layoutManager.primaryMonitor.width, -1); - } - - if (reset) return; - - this._setKeyBindings(false); - - this._signalsHandler.destroy(); - - Main.layoutManager._updateHotCorners = this._oldUpdateHotCorners; - Main.layoutManager._updateHotCorners(); - - SETTINGS.disconnect(this._forceHotCornerId); - - if (this._enableHotCornersId) { - Main.layoutManager._interfaceSettings.disconnect(this._enableHotCornersId); - } - - Main.layoutManager._updatePanelBarrier = this._oldUpdatePanelBarrier; - Main.layoutManager._updatePanelBarrier(); - - Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = this._oldUpdateWorkspacesViews; - Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = this._oldSetPrimaryWorkspaceVisible; - - LookingGlass.LookingGlass.prototype._resize = LookingGlass.LookingGlass.prototype._oldResize; - delete LookingGlass.LookingGlass.prototype._oldResize; - - LookingGlass.LookingGlass.prototype.open = LookingGlass.LookingGlass.prototype._oldOpen; - delete LookingGlass.LookingGlass.prototype._oldOpen - - delete Main.panel.style; - this._desktopIconsUsableArea.destroy(); - this._desktopIconsUsableArea = null; + _setKeyBindings(enable) { + let keys = { + 'intellihide-key-toggle': () => + this.allPanels.forEach((p) => p.intellihide.toggle()), } - _setDesktopIconsMargins() { - this._desktopIconsUsableArea?.resetMargins(); - this.allPanels.forEach(p => { - switch(p.geom.position) { - case St.Side.TOP: - this._desktopIconsUsableArea?.setMargins(p.monitor.index, p.geom.h, 0, 0, 0); - break; - case St.Side.BOTTOM: - this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, p.geom.h, 0, 0); - break; - case St.Side.LEFT: - this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, 0, p.geom.w, 0); - break; - case St.Side.RIGHT: - this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, 0, 0, p.geom.w); - break; - } - }); - } + Object.keys(keys).forEach((k) => { + Utils.removeKeybinding(k) - setFocusedMonitor(monitor) { - this.focusedMonitorPanel = this.allPanels.find(p => p.monitor == monitor) - - if (!this.checkIfFocusedMonitor(monitor)) { - Main.overview._overview.clear_constraints(); - Main.overview._overview.add_constraint(new Layout.MonitorConstraint({ index: monitor.index })); - - Main.overview._overview._controls._workspacesDisplay._primaryIndex = monitor.index; - - // https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2395 - // The overview allocation used to calculate its workarea based on the monitor where the overview - // was displayed, but it got changed back to always use the primary monitor. So now, temporarily assign - // the primary monitor to dtp focused monitor while recalculating the overview workarea - Main.layoutManager.primaryMonitor = monitor - Main.overview._overview._controls.layout_manager._updateWorkAreaBox() - Main.layoutManager.primaryMonitor = Main.layoutManager.monitors[Main.layoutManager.primaryIndex] - } - } - - _newSetPrimaryWorkspaceVisible(visible) { - if (this._primaryVisible === visible) - return; - - this._primaryVisible = visible; - - const primaryIndex = Main.overview._overview._controls._workspacesDisplay._primaryIndex; - const primaryWorkspace = this._workspacesViews[primaryIndex]; - if (primaryWorkspace) - primaryWorkspace.visible = visible; - } - - _newUpdateWorkspacesViews() { - for (let i = 0; i < this._workspacesViews.length; i++) - this._workspacesViews[i].destroy(); - - this._workspacesViews = []; - let monitors = Main.layoutManager.monitors; - for (let i = 0; i < monitors.length; i++) { - let view; - if (i === this._primaryIndex) { - view = new WorkspacesView(i, - this._controls, - this._scrollAdjustment, - this._fitModeAdjustment, - this._overviewAdjustment); - - view.visible = this._primaryVisible; - this.bind_property('opacity', view, 'opacity', GObject.BindingFlags.SYNC_CREATE); - this.add_child(view); - } else { - // No idea why atm, but we need the import at the top of this file and to use the - // full imports ns here, otherwise SecondaryMonitorDisplay can't be used ¯\_(ツ)_/¯ - view = new SecondaryMonitorDisplay(i, - this._controls, - this._scrollAdjustment, - this._fitModeAdjustment, - this._overviewAdjustment); - Main.layoutManager.overviewGroup.add_child(view); - } - - this._workspacesViews.push(view); - } - } - - _saveMonitors() { - //Mutter meta_monitor_manager_get_primary_monitor (global.display.get_primary_monitor()) doesn't return the same - //monitor as GDK gdk_screen_get_primary_monitor (imports.gi.Gdk.Screen.get_default().get_primary_monitor()). - //Since the Mutter function is what's used in gnome-shell and we can't access it from the settings dialog, store - //the monitors information in a setting so we can use the same monitor indexes as the ones in gnome-shell - let keyMonitors = 'available-monitors'; - let keyPrimary = 'primary-monitor'; - let primaryIndex = Main.layoutManager.primaryIndex; - let newMonitors = [primaryIndex]; - let savedMonitors = SETTINGS.get_value(keyMonitors).deep_unpack(); - let dtpPrimaryIndex = SETTINGS.get_int(keyPrimary); - let newDtpPrimaryIndex = primaryIndex; - - Main.layoutManager.monitors.filter(m => m.index != primaryIndex).forEach(m => newMonitors.push(m.index)); - - if (savedMonitors[0] != dtpPrimaryIndex) { - // dash to panel primary wasn't the gnome-shell primary (first index of available-monitors) - let savedIndex = savedMonitors.indexOf(dtpPrimaryIndex) - - // default to primary if it was set to a monitor that is no longer available - newDtpPrimaryIndex = newMonitors[savedIndex]; - newDtpPrimaryIndex = newDtpPrimaryIndex == null ? primaryIndex : newDtpPrimaryIndex; - } - - SETTINGS.set_int(keyPrimary, newDtpPrimaryIndex); - SETTINGS.set_value(keyMonitors, new GLib.Variant('ai', newMonitors)); - } - - checkIfFocusedMonitor(monitor) { - return Main.overview._overview._controls._workspacesDisplay._primaryIndex == monitor.index; - } - - _createPanel(monitor, isStandalone) { - let panelBox; - let panel; - let clipContainer = new Clutter.Actor(); - - if (isStandalone) { - panelBox = new St.BoxLayout({ name: 'panelBox' }); - } else { - panelBox = Main.layoutManager.panelBox; - Main.layoutManager._untrackActor(panelBox); - panelBox.remove_child(Main.panel); - Main.layoutManager.removeChrome(panelBox); - } - - Main.layoutManager.addChrome(clipContainer, { affectsInputRegion: false }); - clipContainer.add_child(panelBox); - Main.layoutManager.trackChrome(panelBox, { trackFullscreen: true, affectsStruts: true, affectsInputRegion: true }); - - panel = new Panel.Panel(this, monitor, panelBox, isStandalone); - panelBox.add_child(panel); - panel.enable(); - - panelBox.visible = true; - if (monitor.inFullscreen) { - panelBox.hide(); - } - panelBox.set_position(0, 0); - - return panel; - } - - _reset() { - this.disable(true); - this.allPanels = []; - this.enable(true); - } - - _updatePanelElementPositions() { - this.panelsElementPositions = PanelSettings.getSettingsJson(SETTINGS, 'panel-element-positions'); - this.allPanels.forEach(p => p.updateElementPositions()); - } - - _adjustPanelMenuButton(button, monitor, arrowSide) { - if (button) { - button.menu._boxPointer._dtpSourceActor = button.menu._boxPointer.sourceActor; - button.menu._boxPointer.sourceActor = button; - button.menu._boxPointer._userArrowSide = arrowSide; - button.menu._boxPointer._dtpInPanel = 1; - - if (!button.menu._boxPointer.vfunc_get_preferred_height) { - button.menu._boxPointer._dtpGetPreferredHeightId = button.menu._boxPointer._container.connect('get-preferred-height', (actor, forWidth, alloc) => { - this._getBoxPointerPreferredHeight(button.menu._boxPointer, alloc, monitor); - }); - } - } - } - - _getBoxPointerPreferredHeight(boxPointer, alloc, monitor) { - if (boxPointer._dtpInPanel && boxPointer.sourceActor && SETTINGS.get_boolean('intellihide')) { - monitor = monitor || Main.layoutManager.findMonitorForActor(boxPointer.sourceActor); - let panel = Utils.find(global.dashToPanel.panels, p => p.monitor == monitor); - let excess = alloc.natural_size + panel.dtpSize + 10 - monitor.height; // 10 is arbitrary - - if (excess > 0) { - alloc.natural_size -= excess; - } - } - - return [alloc.min_size, alloc.natural_size]; - } - - _findPanelMenuButtons(container) { - let panelMenuButtons = []; - let panelMenuButton; - - let find = parent => parent.get_children().forEach(c => { - if ((panelMenuButton = this._getPanelMenuButton(c))) { - panelMenuButtons.push(panelMenuButton); - } - - find(c); - }); - - find(container); - - return panelMenuButtons; - } - - _removePanelBarriers(panel) { - if (panel.isStandalone && panel._rightPanelBarrier) { - panel._rightPanelBarrier.destroy(); - } - - if (panel._leftPanelBarrier) { - panel._leftPanelBarrier.destroy(); - delete panel._leftPanelBarrier; - } - } - - _getPanelMenuButton(obj) { - return obj instanceof PanelMenu.Button && obj.menu?._boxPointer ? obj : 0; - } - - _setKeyBindings(enable) { - let keys = { - 'intellihide-key-toggle': () => this.allPanels.forEach(p => p.intellihide.toggle()) - }; - - Object.keys(keys).forEach(k => { - Utils.removeKeybinding(k); - - if (enable) { - Utils.addKeybinding(k, SETTINGS, keys[k], Shell.ActionMode.NORMAL); - } - }); - } - -}; + if (enable) { + Utils.addKeybinding(k, SETTINGS, keys[k], Shell.ActionMode.NORMAL) + } + }) + } +} // This class drives long-running icon animations, to keep them running in sync // with each other. export const IconAnimator = class { - - constructor(actor) { - this._count = 0; - this._started = false; - this._animations = { - dance: [], - }; - this._timeline = new Clutter.Timeline({ - duration: 3000, - repeat_count: -1, - }); - - /* Just use the construction property when no need to support 3.36 */ - if (this._timeline.set_actor) - this._timeline.set_actor(actor); - - this._timeline.connect('new-frame', () => { - const progress = this._timeline.get_progress(); - const danceRotation = progress < 1/6 ? 15*Math.sin(progress*24*Math.PI) : 0; - const dancers = this._animations.dance; - for (let i = 0, iMax = dancers.length; i < iMax; i++) { - dancers[i].target.rotation_angle_z = danceRotation; - } - }); + constructor(actor) { + this._count = 0 + this._started = false + this._animations = { + dance: [], } + this._timeline = new Clutter.Timeline({ + duration: 3000, + repeat_count: -1, + }) - destroy() { - this._timeline.stop(); - this._timeline = null; - for (let name in this._animations) { - const pairs = this._animations[name]; - for (let i = 0, iMax = pairs.length; i < iMax; i++) { - const pair = pairs[i]; - pair.target.disconnect(pair.targetDestroyId); - } - } - this._animations = null; + /* Just use the construction property when no need to support 3.36 */ + if (this._timeline.set_actor) this._timeline.set_actor(actor) + + this._timeline.connect('new-frame', () => { + const progress = this._timeline.get_progress() + const danceRotation = + progress < 1 / 6 ? 15 * Math.sin(progress * 24 * Math.PI) : 0 + const dancers = this._animations.dance + for (let i = 0, iMax = dancers.length; i < iMax; i++) { + dancers[i].target.rotation_angle_z = danceRotation + } + }) + } + + destroy() { + this._timeline.stop() + this._timeline = null + for (let name in this._animations) { + const pairs = this._animations[name] + for (let i = 0, iMax = pairs.length; i < iMax; i++) { + const pair = pairs[i] + pair.target.disconnect(pair.targetDestroyId) + } } + this._animations = null + } - pause() { - if (this._started && this._count > 0) { - this._timeline.stop(); - } - this._started = false; + pause() { + if (this._started && this._count > 0) { + this._timeline.stop() } + this._started = false + } - start() { - if (!this._started && this._count > 0) { - this._timeline.start(); - } - this._started = true; + start() { + if (!this._started && this._count > 0) { + this._timeline.start() } + this._started = true + } - addAnimation(target, name) { - const targetDestroyId = target.connect('destroy', () => this.removeAnimation(target, name)); - this._animations[name].push({ target: target, targetDestroyId: targetDestroyId }); + addAnimation(target, name) { + const targetDestroyId = target.connect('destroy', () => + this.removeAnimation(target, name), + ) + this._animations[name].push({ + target: target, + targetDestroyId: targetDestroyId, + }) + if (this._started && this._count === 0) { + this._timeline.start() + } + this._count++ + } + + removeAnimation(target, name) { + const pairs = this._animations[name] + for (let i = 0, iMax = pairs.length; i < iMax; i++) { + const pair = pairs[i] + if (pair.target === target) { + target.disconnect(pair.targetDestroyId) + pairs.splice(i, 1) + this._count-- if (this._started && this._count === 0) { - this._timeline.start(); + this._timeline.stop() } - this._count++; + return + } } - - removeAnimation(target, name) { - const pairs = this._animations[name]; - for (let i = 0, iMax = pairs.length; i < iMax; i++) { - const pair = pairs[i]; - if (pair.target === target) { - target.disconnect(pair.targetDestroyId); - pairs.splice(i, 1); - this._count--; - if (this._started && this._count === 0) { - this._timeline.stop(); - } - return; - } - } - } -}; + } +} function newUpdateHotCorners() { - // destroy old hot corners - this.hotCorners.forEach(function(corner) { - if (corner) - corner.destroy(); - }); - this.hotCorners = []; + // destroy old hot corners + this.hotCorners.forEach(function (corner) { + if (corner) corner.destroy() + }) + this.hotCorners = [] - //global.settings is ubuntu specific setting to disable the hot corner (Tweak tool > Top Bar > Activities Overview Hot Corner) - //this._interfaceSettings is for the setting to disable the hot corner introduced in gnome-shell 3.34 - if ((global.settings.list_keys().indexOf('enable-hot-corners') >= 0 && !global.settings.get_boolean('enable-hot-corners')) || - (this._interfaceSettings && !this._interfaceSettings.get_boolean('enable-hot-corners'))) { - this.emit('hot-corners-changed'); - return; + //global.settings is ubuntu specific setting to disable the hot corner (Tweak tool > Top Bar > Activities Overview Hot Corner) + //this._interfaceSettings is for the setting to disable the hot corner introduced in gnome-shell 3.34 + if ( + (global.settings.list_keys().indexOf('enable-hot-corners') >= 0 && + !global.settings.get_boolean('enable-hot-corners')) || + (this._interfaceSettings && + !this._interfaceSettings.get_boolean('enable-hot-corners')) + ) { + this.emit('hot-corners-changed') + return + } + + // build new hot corners + for (let i = 0; i < this.monitors.length; i++) { + let panel = Utils.find( + global.dashToPanel.panels, + (p) => p.monitor.index == i, + ) + let panelPosition = panel ? panel.getPosition() : St.Side.BOTTOM + let panelTopLeft = + panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT + let monitor = this.monitors[i] + let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x + let cornerY = monitor.y + + let haveTopLeftCorner = true + + // If the panel is on the bottom, unless this is explicitly forced, don't add a topleft + // hot corner unless it is actually a top left panel. Otherwise, it stops the mouse + // as you are dragging across. In the future, maybe we will automatically move the + // hotcorner to the bottom when the panel is positioned at the bottom + if ( + i != this.primaryIndex || + (!panelTopLeft && !SETTINGS.get_boolean('stockgs-force-hotcorner')) + ) { + // Check if we have a top left (right for RTL) corner. + // I.e. if there is no monitor directly above or to the left(right) + let besideX = this._rtl ? monitor.x + 1 : cornerX - 1 + let besideY = cornerY + let aboveX = cornerX + let aboveY = cornerY - 1 + + for (let j = 0; j < this.monitors.length; j++) { + if (i == j) continue + let otherMonitor = this.monitors[j] + if ( + besideX >= otherMonitor.x && + besideX < otherMonitor.x + otherMonitor.width && + besideY >= otherMonitor.y && + besideY < otherMonitor.y + otherMonitor.height + ) { + haveTopLeftCorner = false + break + } + if ( + aboveX >= otherMonitor.x && + aboveX < otherMonitor.x + otherMonitor.width && + aboveY >= otherMonitor.y && + aboveY < otherMonitor.y + otherMonitor.height + ) { + haveTopLeftCorner = false + break + } + } } - // build new hot corners - for (let i = 0; i < this.monitors.length; i++) { - let panel = Utils.find(global.dashToPanel.panels, p => p.monitor.index == i); - let panelPosition = panel ? panel.getPosition() : St.Side.BOTTOM; - let panelTopLeft = panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT; - let monitor = this.monitors[i]; - let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x; - let cornerY = monitor.y; + if (haveTopLeftCorner) { + let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY) - let haveTopLeftCorner = true; - - // If the panel is on the bottom, unless this is explicitly forced, don't add a topleft - // hot corner unless it is actually a top left panel. Otherwise, it stops the mouse - // as you are dragging across. In the future, maybe we will automatically move the - // hotcorner to the bottom when the panel is positioned at the bottom - if (i != this.primaryIndex || (!panelTopLeft && !SETTINGS.get_boolean('stockgs-force-hotcorner'))) { - // Check if we have a top left (right for RTL) corner. - // I.e. if there is no monitor directly above or to the left(right) - let besideX = this._rtl ? monitor.x + 1 : cornerX - 1; - let besideY = cornerY; - let aboveX = cornerX; - let aboveY = cornerY - 1; - - for (let j = 0; j < this.monitors.length; j++) { - if (i == j) - continue; - let otherMonitor = this.monitors[j]; - if (besideX >= otherMonitor.x && - besideX < otherMonitor.x + otherMonitor.width && - besideY >= otherMonitor.y && - besideY < otherMonitor.y + otherMonitor.height) { - haveTopLeftCorner = false; - break; - } - if (aboveX >= otherMonitor.x && - aboveX < otherMonitor.x + otherMonitor.width && - aboveY >= otherMonitor.y && - aboveY < otherMonitor.y + otherMonitor.height) { - haveTopLeftCorner = false; - break; - } - } - } - - if (haveTopLeftCorner) { - let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY); - - corner.setBarrierSize = size => Object.getPrototypeOf(corner).setBarrierSize.call(corner, Math.min(size, 32)); - corner.setBarrierSize(panel ? panel.dtpSize : 32); - this.hotCorners.push(corner); - } else { - this.hotCorners.push(null); - } + corner.setBarrierSize = (size) => + Object.getPrototypeOf(corner).setBarrierSize.call( + corner, + Math.min(size, 32), + ) + corner.setBarrierSize(panel ? panel.dtpSize : 32) + this.hotCorners.push(corner) + } else { + this.hotCorners.push(null) } + } - this.emit('hot-corners-changed'); + this.emit('hot-corners-changed') } function newUpdatePanelBarrier(panel) { - let barriers = { - _rightPanelBarrier: [(panel.isStandalone ? panel : this)], - _leftPanelBarrier: [panel] - }; + let barriers = { + _rightPanelBarrier: [panel.isStandalone ? panel : this], + _leftPanelBarrier: [panel], + } - Object.keys(barriers).forEach(k => { - let obj = barriers[k][0]; + Object.keys(barriers).forEach((k) => { + let obj = barriers[k][0] - if (obj[k]) { - obj[k].destroy(); - obj[k] = null; - } - }); + if (obj[k]) { + obj[k].destroy() + obj[k] = null + } + }) - if (!this.primaryMonitor || !panel.panelBox.height) { - return; + if (!this.primaryMonitor || !panel.panelBox.height) { + return + } + + let barrierSize = Math.min(10, panel.panelBox.height) + let fixed1 = panel.monitor.y + let fixed2 = panel.monitor.y + barrierSize + + if (panel.checkIfVertical()) { + barriers._rightPanelBarrier.push( + panel.monitor.y + panel.monitor.height, + Meta.BarrierDirection.NEGATIVE_Y, + ) + barriers._leftPanelBarrier.push( + panel.monitor.y, + Meta.BarrierDirection.POSITIVE_Y, + ) + } else { + barriers._rightPanelBarrier.push( + panel.monitor.x + panel.monitor.width, + Meta.BarrierDirection.NEGATIVE_X, + ) + barriers._leftPanelBarrier.push( + panel.monitor.x, + Meta.BarrierDirection.POSITIVE_X, + ) + } + + switch (panel.getPosition()) { + //values are initialized as St.Side.TOP + case St.Side.BOTTOM: + fixed1 = panel.monitor.y + panel.monitor.height - barrierSize + fixed2 = panel.monitor.y + panel.monitor.height + break + case St.Side.LEFT: + fixed1 = panel.monitor.x + barrierSize + fixed2 = panel.monitor.x + break + case St.Side.RIGHT: + fixed1 = panel.monitor.x + panel.monitor.width - barrierSize + fixed2 = panel.monitor.x + panel.monitor.width + break + } + + //remove left barrier if it overlaps one of the hotcorners + for (let k in this.hotCorners) { + let hc = this.hotCorners[k] + + if ( + hc && + hc._monitor == panel.monitor && + (fixed1 == hc._x || fixed2 == hc._x || fixed1 == hc._y || fixed2 == hc._y) + ) { + delete barriers._leftPanelBarrier + break + } + } + + Object.keys(barriers).forEach((k) => { + let barrierOptions = { + backend: global.backend, + directions: barriers[k][2], } - let barrierSize = Math.min(10, panel.panelBox.height); - let fixed1 = panel.monitor.y; - let fixed2 = panel.monitor.y + barrierSize; - - if (panel.checkIfVertical()) { - barriers._rightPanelBarrier.push(panel.monitor.y + panel.monitor.height, Meta.BarrierDirection.NEGATIVE_Y); - barriers._leftPanelBarrier.push(panel.monitor.y, Meta.BarrierDirection.POSITIVE_Y); - } else { - barriers._rightPanelBarrier.push(panel.monitor.x + panel.monitor.width, Meta.BarrierDirection.NEGATIVE_X); - barriers._leftPanelBarrier.push(panel.monitor.x, Meta.BarrierDirection.POSITIVE_X); - } + barrierOptions[panel.varCoord.c1] = barrierOptions[panel.varCoord.c2] = + barriers[k][1] + barrierOptions[panel.fixedCoord.c1] = fixed1 + barrierOptions[panel.fixedCoord.c2] = fixed2 - switch (panel.getPosition()) { - //values are initialized as St.Side.TOP - case St.Side.BOTTOM: - fixed1 = panel.monitor.y + panel.monitor.height - barrierSize; - fixed2 = panel.monitor.y + panel.monitor.height; - break; - case St.Side.LEFT: - fixed1 = panel.monitor.x + barrierSize; - fixed2 = panel.monitor.x; - break; - case St.Side.RIGHT: - fixed1 = panel.monitor.x + panel.monitor.width - barrierSize; - fixed2 = panel.monitor.x + panel.monitor.width; - break; - } - - //remove left barrier if it overlaps one of the hotcorners - for (let k in this.hotCorners) { - let hc = this.hotCorners[k]; - - if (hc && hc._monitor == panel.monitor && - ((fixed1 == hc._x || fixed2 == hc._x) || fixed1 == hc._y || fixed2 == hc._y)) { - delete barriers._leftPanelBarrier; - break; - } - } - - Object.keys(barriers).forEach(k => { - let barrierOptions = { - backend: global.backend, - directions: barriers[k][2] - }; - - barrierOptions[panel.varCoord.c1] = barrierOptions[panel.varCoord.c2] = barriers[k][1]; - barrierOptions[panel.fixedCoord.c1] = fixed1; - barrierOptions[panel.fixedCoord.c2] = fixed2; - - barriers[k][0][k] = new Meta.Barrier(barrierOptions); - }); + barriers[k][0][k] = new Meta.Barrier(barrierOptions) + }) } function _newLookingGlassResize() { - let primaryMonitorPanel = Utils.find(global.dashToPanel.panels, p => p.monitor == Main.layoutManager.primaryMonitor); - let topOffset = primaryMonitorPanel.getPosition() == St.Side.TOP ? primaryMonitorPanel.dtpSize + 8 : 32; + let primaryMonitorPanel = Utils.find( + global.dashToPanel.panels, + (p) => p.monitor == Main.layoutManager.primaryMonitor, + ) + let topOffset = + primaryMonitorPanel.getPosition() == St.Side.TOP + ? primaryMonitorPanel.dtpSize + 8 + : 32 - this._oldResize(); + this._oldResize() - this._hiddenY = Main.layoutManager.primaryMonitor.y + topOffset - this.height; - this._targetY = this._hiddenY + this.height; - this.y = this._hiddenY; + this._hiddenY = Main.layoutManager.primaryMonitor.y + topOffset - this.height + this._targetY = this._hiddenY + this.height + this.y = this._hiddenY - this._objInspector.set_position(this.x + Math.floor(this.width * 0.1), this._targetY + Math.floor(this.height * 0.1)); + this._objInspector.set_position( + this.x + Math.floor(this.width * 0.1), + this._targetY + Math.floor(this.height * 0.1), + ) } function _newLookingGlassOpen() { - if (this._open) - return; + if (this._open) return - this._resize(); - this._oldOpen(); + this._resize() + this._oldOpen() } diff --git a/panelPositions.js b/panelPositions.js index a970bdd..bf39133 100644 --- a/panelPositions.js +++ b/panelPositions.js @@ -15,47 +15,47 @@ * along with this program. If not, see . */ -export const SHOW_APPS_BTN = 'showAppsButton'; -export const ACTIVITIES_BTN = 'activitiesButton'; -export const TASKBAR = 'taskbar'; -export const DATE_MENU = 'dateMenu'; -export const SYSTEM_MENU = 'systemMenu'; -export const LEFT_BOX = 'leftBox'; -export const CENTER_BOX = 'centerBox'; -export const RIGHT_BOX = 'rightBox'; -export const DESKTOP_BTN = 'desktopButton'; +export const SHOW_APPS_BTN = 'showAppsButton' +export const ACTIVITIES_BTN = 'activitiesButton' +export const TASKBAR = 'taskbar' +export const DATE_MENU = 'dateMenu' +export const SYSTEM_MENU = 'systemMenu' +export const LEFT_BOX = 'leftBox' +export const CENTER_BOX = 'centerBox' +export const RIGHT_BOX = 'rightBox' +export const DESKTOP_BTN = 'desktopButton' -export const STACKED_TL = 'stackedTL'; -export const STACKED_BR = 'stackedBR'; -export const CENTERED = 'centered'; -export const CENTERED_MONITOR = 'centerMonitor'; +export const STACKED_TL = 'stackedTL' +export const STACKED_BR = 'stackedBR' +export const CENTERED = 'centered' +export const CENTERED_MONITOR = 'centerMonitor' -export const TOP = 'TOP'; -export const BOTTOM = 'BOTTOM'; -export const LEFT = 'LEFT'; -export const RIGHT = 'RIGHT'; +export const TOP = 'TOP' +export const BOTTOM = 'BOTTOM' +export const LEFT = 'LEFT' +export const RIGHT = 'RIGHT' -export const START = 'START'; -export const MIDDLE = 'MIDDLE'; -export const END = 'END'; +export const START = 'START' +export const MIDDLE = 'MIDDLE' +export const END = 'END' export const defaults = [ - { element: SHOW_APPS_BTN, visible: true, position: STACKED_TL }, - { element: ACTIVITIES_BTN, visible: false, position: STACKED_TL }, - { element: LEFT_BOX, visible: true, position: STACKED_TL }, - { element: TASKBAR, visible: true, position: STACKED_TL }, - { element: CENTER_BOX, visible: true, position: STACKED_BR }, - { element: RIGHT_BOX, visible: true, position: STACKED_BR }, - { element: DATE_MENU, visible: true, position: STACKED_BR }, - { element: SYSTEM_MENU, visible: true, position: STACKED_BR }, - { element: DESKTOP_BTN, visible: true, position: STACKED_BR }, -]; + { element: SHOW_APPS_BTN, visible: true, position: STACKED_TL }, + { element: ACTIVITIES_BTN, visible: false, position: STACKED_TL }, + { element: LEFT_BOX, visible: true, position: STACKED_TL }, + { element: TASKBAR, visible: true, position: STACKED_TL }, + { element: CENTER_BOX, visible: true, position: STACKED_BR }, + { element: RIGHT_BOX, visible: true, position: STACKED_BR }, + { element: DATE_MENU, visible: true, position: STACKED_BR }, + { element: SYSTEM_MENU, visible: true, position: STACKED_BR }, + { element: DESKTOP_BTN, visible: true, position: STACKED_BR }, +] -export const optionDialogFunctions = {}; +export const optionDialogFunctions = {} -optionDialogFunctions[SHOW_APPS_BTN] = '_showShowAppsButtonOptions'; -optionDialogFunctions[DESKTOP_BTN] = '_showDesktopButtonOptions'; +optionDialogFunctions[SHOW_APPS_BTN] = '_showShowAppsButtonOptions' +optionDialogFunctions[DESKTOP_BTN] = '_showDesktopButtonOptions' export function checkIfCentered(position) { - return position == CENTERED || position == CENTERED_MONITOR; + return position == CENTERED || position == CENTERED_MONITOR } diff --git a/panelSettings.js b/panelSettings.js index fc2d60c..bfb73e6 100644 --- a/panelSettings.js +++ b/panelSettings.js @@ -15,43 +15,43 @@ * along with this program. If not, see . */ -import * as Pos from './panelPositions.js'; +import * as Pos from './panelPositions.js' /** Return object representing a settings value that is stored as JSON. */ export function getSettingsJson(settings, setting) { - try { - return JSON.parse(settings.get_string(setting)); - } catch(e) { - log('Error parsing positions: ' + e.message); - } + try { + return JSON.parse(settings.get_string(setting)) + } catch (e) { + log('Error parsing positions: ' + e.message) + } } /** Write value object as JSON to setting in settings. */ export function setSettingsJson(settings, setting, value) { - try { - const json = JSON.stringify(value); - settings.set_string(setting, json); - } catch(e) { - log('Error serializing setting: ' + e.message); - } + try { + const json = JSON.stringify(value) + settings.set_string(setting, json) + } catch (e) { + log('Error serializing setting: ' + e.message) + } } /** Returns size of panel on a specific monitor, in pixels. */ export function getPanelSize(settings, monitorIndex) { - const sizes = getSettingsJson(settings, 'panel-sizes'); - // Pull in deprecated setting if panel-sizes does not have setting for monitor. - const fallbackSize = settings.get_int('panel-size'); - const theDefault = 48; - return sizes[monitorIndex] || fallbackSize || theDefault; + const sizes = getSettingsJson(settings, 'panel-sizes') + // Pull in deprecated setting if panel-sizes does not have setting for monitor. + const fallbackSize = settings.get_int('panel-size') + const theDefault = 48 + return sizes[monitorIndex] || fallbackSize || theDefault } export function setPanelSize(settings, monitorIndex, value) { - if (!(Number.isInteger(value) && value <= 128 && value >= 16)) { - log('Not setting invalid panel size: ' + value); - return; - } - let sizes = getSettingsJson(settings, 'panel-sizes'); - sizes[monitorIndex] = value; - setSettingsJson(settings, 'panel-sizes', sizes); + if (!(Number.isInteger(value) && value <= 128 && value >= 16)) { + log('Not setting invalid panel size: ' + value) + return + } + let sizes = getSettingsJson(settings, 'panel-sizes') + sizes[monitorIndex] = value + setSettingsJson(settings, 'panel-sizes', sizes) } /** @@ -59,53 +59,59 @@ export function setPanelSize(settings, monitorIndex, value) { * from settings. e.g. 100 */ export function getPanelLength(settings, monitorIndex) { - const lengths = getSettingsJson(settings, 'panel-lengths'); - const theDefault = 100; - return lengths[monitorIndex] || theDefault; + const lengths = getSettingsJson(settings, 'panel-lengths') + const theDefault = 100 + return lengths[monitorIndex] || theDefault } export function setPanelLength(settings, monitorIndex, value) { - if (!(Number.isInteger(value) && value <= 100 && value >= 0)) { - log('Not setting invalid panel length: ' + value); - return; - } - let lengths = getSettingsJson(settings, 'panel-lengths'); - lengths[monitorIndex] = value; - setSettingsJson(settings, 'panel-lengths', lengths); + if (!(Number.isInteger(value) && value <= 100 && value >= 0)) { + log('Not setting invalid panel length: ' + value) + return + } + let lengths = getSettingsJson(settings, 'panel-lengths') + lengths[monitorIndex] = value + setSettingsJson(settings, 'panel-lengths', lengths) } /** Returns position of panel on a specific monitor. */ export function getPanelPosition(settings, monitorIndex) { - const positions = getSettingsJson(settings, 'panel-positions'); - const fallbackPosition = settings.get_string('panel-position'); - const theDefault = Pos.BOTTOM; - return positions[monitorIndex] || fallbackPosition || theDefault; + const positions = getSettingsJson(settings, 'panel-positions') + const fallbackPosition = settings.get_string('panel-position') + const theDefault = Pos.BOTTOM + return positions[monitorIndex] || fallbackPosition || theDefault } export function setPanelPosition(settings, monitorIndex, value) { - if (!(value === Pos.TOP || value === Pos.BOTTOM || value === Pos.LEFT - || value === Pos.RIGHT)) { - log('Not setting invalid panel position: ' + value); - return; - } - const positions = getSettingsJson(settings, 'panel-positions'); - positions[monitorIndex] = value; - setSettingsJson(settings, 'panel-positions', positions); + if ( + !( + value === Pos.TOP || + value === Pos.BOTTOM || + value === Pos.LEFT || + value === Pos.RIGHT + ) + ) { + log('Not setting invalid panel position: ' + value) + return + } + const positions = getSettingsJson(settings, 'panel-positions') + positions[monitorIndex] = value + setSettingsJson(settings, 'panel-positions', positions) } /** Returns anchor location of panel on a specific monitor. */ export function getPanelAnchor(settings, monitorIndex) { - const anchors = getSettingsJson(settings, 'panel-anchors'); - const theDefault = Pos.MIDDLE; - return anchors[monitorIndex] || theDefault; + const anchors = getSettingsJson(settings, 'panel-anchors') + const theDefault = Pos.MIDDLE + return anchors[monitorIndex] || theDefault } export function setPanelAnchor(settings, monitorIndex, value) { - if (!(value === Pos.START || value === Pos.MIDDLE || value === Pos.END)) { - log('Not setting invalid panel anchor: ' + value); - return; - } - const anchors = getSettingsJson(settings, 'panel-anchors'); - anchors[monitorIndex] = value; - setSettingsJson(settings, 'panel-anchors', anchors); + if (!(value === Pos.START || value === Pos.MIDDLE || value === Pos.END)) { + log('Not setting invalid panel anchor: ' + value) + return + } + const anchors = getSettingsJson(settings, 'panel-anchors') + anchors[monitorIndex] = value + setSettingsJson(settings, 'panel-anchors', anchors) } diff --git a/panelStyle.js b/panelStyle.js index 353e3ef..0ffb875 100644 --- a/panelStyle.js +++ b/panelStyle.js @@ -21,294 +21,323 @@ * mathematical.coffee@gmail.com */ -import * as Utils from './utils.js'; -import {SETTINGS} from './extension.js'; - +import * as Utils from './utils.js' +import { SETTINGS } from './extension.js' export const PanelStyle = class { + enable(panel) { + this.panel = panel - enable(panel) { - this.panel = panel; + this._applyStyles() - this._applyStyles(); + this._bindSettingsChanges() + } - this._bindSettingsChanges(); + disable() { + for (let i = 0; i < this._dtpSettingsSignalIds.length; ++i) { + SETTINGS.disconnect(this._dtpSettingsSignalIds[i]) } - disable() { - for (let i = 0; i < this._dtpSettingsSignalIds.length; ++i) { - SETTINGS.disconnect(this._dtpSettingsSignalIds[i]); + this._removeStyles() + } + + _bindSettingsChanges() { + let configKeys = [ + 'tray-size', + 'leftbox-size', + 'tray-padding', + 'leftbox-padding', + 'status-icon-padding', + ] + + this._dtpSettingsSignalIds = [] + + for (let i in configKeys) { + this._dtpSettingsSignalIds.push( + SETTINGS.connect('changed::' + configKeys[i], () => { + this._removeStyles() + this._applyStyles() + }), + ) + } + } + + _applyStyles() { + this._rightBoxOperations = [] + + let trayPadding = SETTINGS.get_int('tray-padding') + let isVertical = this.panel.checkIfVertical() + let paddingStyle = 'padding: ' + (isVertical ? '%dpx 0' : '0 %dpx') + + if (trayPadding >= 0) { + let operation = {} + let trayPaddingStyleLine + + if (isVertical) { + trayPaddingStyleLine = paddingStyle.format(trayPadding) + operation.compareFn = function (actor) { + let parent = actor.get_parent() + return ( + (parent && + parent.has_style_class_name && + parent.has_style_class_name('panel-button') && + !parent.has_style_class_name('clock-display')) || + (actor.has_style_class_name && actor.has_style_class_name('clock')) + ) + } + } else { + trayPaddingStyleLine = '-natural-hpadding: %dpx'.format(trayPadding) + if (trayPadding < 6) { + trayPaddingStyleLine += '; -minimum-hpadding: %dpx'.format( + trayPadding, + ) } - this._removeStyles(); + operation.compareFn = function (actor) { + return ( + actor.has_style_class_name && + actor.has_style_class_name('panel-button') + ) + } + } + + operation.applyFn = (actor, operationIdx) => { + this._overrideStyle(actor, trayPaddingStyleLine, operationIdx) + this._refreshPanelButton(actor) + } + this._rightBoxOperations.push(operation) } - _bindSettingsChanges() { - let configKeys = [ - "tray-size", - "leftbox-size", - "tray-padding", - "leftbox-padding", - "status-icon-padding", - ]; - - this._dtpSettingsSignalIds = []; - - for(let i in configKeys) { - this._dtpSettingsSignalIds.push(SETTINGS.connect('changed::' + configKeys[i], () => { - this._removeStyles(); - this._applyStyles(); - })); - } + let statusIconPadding = SETTINGS.get_int('status-icon-padding') + if (statusIconPadding >= 0) { + let statusIconPaddingStyleLine = paddingStyle.format(statusIconPadding) + let operation = {} + operation.compareFn = function (actor) { + return ( + actor.has_style_class_name && + actor.has_style_class_name('system-status-icon') + ) + } + operation.applyFn = (actor, operationIdx) => { + this._overrideStyle(actor, statusIconPaddingStyleLine, operationIdx) + } + this._rightBoxOperations.push(operation) } - _applyStyles() { - this._rightBoxOperations = []; - - let trayPadding = SETTINGS.get_int('tray-padding'); - let isVertical = this.panel.checkIfVertical(); - let paddingStyle = 'padding: ' + (isVertical ? '%dpx 0' : '0 %dpx'); + let trayContentSize = SETTINGS.get_int('tray-size') + if (trayContentSize > 0) { + let trayIconSizeStyleLine = 'icon-size: %dpx'.format(trayContentSize) + let operation = {} + operation.compareFn = function (actor) { + return actor.constructor && actor.constructor.name == 'St_Icon' + } + operation.applyFn = (actor, operationIdx) => { + this._overrideStyle(actor, trayIconSizeStyleLine, operationIdx) + } + this._rightBoxOperations.push(operation) - if(trayPadding >= 0) { - let operation = {}; - let trayPaddingStyleLine; + let trayContentSizeStyleLine = 'font-size: %dpx'.format(trayContentSize) + operation = {} + operation.compareFn = function (actor) { + return actor.constructor && actor.constructor.name == 'St_Label' + } + operation.applyFn = (actor, operationIdx) => { + this._overrideStyle(actor, trayContentSizeStyleLine, operationIdx) + } + this._rightBoxOperations.push(operation) - if (isVertical) { - trayPaddingStyleLine = paddingStyle.format(trayPadding); - operation.compareFn = function (actor) { - let parent = actor.get_parent(); - return ((parent && parent.has_style_class_name && (parent.has_style_class_name('panel-button') && !parent.has_style_class_name('clock-display'))) || - (actor.has_style_class_name && actor.has_style_class_name('clock'))); - }; - } else { - trayPaddingStyleLine = '-natural-hpadding: %dpx'.format(trayPadding); - if (trayPadding < 6) { - trayPaddingStyleLine += '; -minimum-hpadding: %dpx'.format(trayPadding); - } - - operation.compareFn = function (actor) { - return (actor.has_style_class_name && actor.has_style_class_name('panel-button')); - }; - } - - operation.applyFn = (actor, operationIdx) => { - this._overrideStyle(actor, trayPaddingStyleLine, operationIdx); - this._refreshPanelButton(actor); - }; - this._rightBoxOperations.push(operation); - } - - let statusIconPadding = SETTINGS.get_int('status-icon-padding'); - if(statusIconPadding >= 0) { - let statusIconPaddingStyleLine = paddingStyle.format(statusIconPadding) - let operation = {}; - operation.compareFn = function (actor) { - return (actor.has_style_class_name && actor.has_style_class_name('system-status-icon')); - }; - operation.applyFn = (actor, operationIdx) => { - this._overrideStyle(actor, statusIconPaddingStyleLine, operationIdx); - }; - this._rightBoxOperations.push(operation); - } - - let trayContentSize = SETTINGS.get_int('tray-size'); - if(trayContentSize > 0) { - let trayIconSizeStyleLine = 'icon-size: %dpx'.format(trayContentSize) - let operation = {}; - operation.compareFn = function (actor) { - return (actor.constructor && actor.constructor.name == 'St_Icon'); - }; - operation.applyFn = (actor, operationIdx) => { - this._overrideStyle(actor, trayIconSizeStyleLine, operationIdx); - }; - this._rightBoxOperations.push(operation); - - let trayContentSizeStyleLine = 'font-size: %dpx'.format(trayContentSize) - operation = {}; - operation.compareFn = function (actor) { - return (actor.constructor && actor.constructor.name == 'St_Label'); - }; - operation.applyFn = (actor, operationIdx) => { - this._overrideStyle(actor, trayContentSizeStyleLine, operationIdx); - }; - this._rightBoxOperations.push(operation); - - this._overrideStyle(this.panel._rightBox, trayContentSizeStyleLine, 0); - this._overrideStyle(this.panel._centerBox, trayContentSizeStyleLine, 0); - } - - // center box has been moved next to the right box and will be treated the same - this._centerBoxOperations = this._rightBoxOperations; - - this._leftBoxOperations = []; - - let leftboxPadding = SETTINGS.get_int('leftbox-padding'); - if(leftboxPadding >= 0) { - let leftboxPaddingStyleLine = paddingStyle.format(leftboxPadding); - let operation = {}; - operation.compareFn = function (actor) { - let parent = actor.get_parent(); - return (parent && parent.has_style_class_name && parent.has_style_class_name('panel-button')); - }; - operation.applyFn = (actor, operationIdx) => { - this._overrideStyle(actor, leftboxPaddingStyleLine, operationIdx); - }; - this._leftBoxOperations.push(operation); - } - - let leftboxContentSize = SETTINGS.get_int('leftbox-size'); - if(leftboxContentSize > 0) { - let leftboxIconSizeStyleLine = 'icon-size: %dpx'.format(leftboxContentSize) - let operation = {}; - operation.compareFn = function (actor) { - return (actor.constructor && actor.constructor.name == 'St_Icon'); - }; - operation.applyFn = (actor, operationIdx) => { - this._overrideStyle(actor, leftboxIconSizeStyleLine, operationIdx); - }; - this._leftBoxOperations.push(operation); - - let leftboxContentSizeStyleLine = 'font-size: %dpx'.format(leftboxContentSize) - operation = {}; - operation.compareFn = function (actor) { - return (actor.constructor && actor.constructor.name == 'St_Label'); - }; - operation.applyFn = (actor, operationIdx) => { - this._overrideStyle(actor, leftboxContentSizeStyleLine, operationIdx); - }; - this._leftBoxOperations.push(operation); - - this._overrideStyle(this.panel._leftBox, leftboxContentSizeStyleLine, 0); - } - - this._applyStylesRecursively(); - - /* connect signal */ - this._rightBoxActorAddedID = this.panel._rightBox.connect('child-added', - (container, actor) => { - if(this._rightBoxOperations.length && !this._ignoreAddedChild) - this._recursiveApply(actor, this._rightBoxOperations); - - this._ignoreAddedChild = 0; - } - ); - this._centerBoxActorAddedID = this.panel._centerBox.connect('child-added', - (container, actor) => { - if(this._centerBoxOperations.length && !this._ignoreAddedChild) - this._recursiveApply(actor, this._centerBoxOperations); - - this._ignoreAddedChild = 0; - } - ); - this._leftBoxActorAddedID = this.panel._leftBox.connect('child-added', - (container, actor) => { - if(this._leftBoxOperations.length) - this._recursiveApply(actor, this._leftBoxOperations); - } - ); + this._overrideStyle(this.panel._rightBox, trayContentSizeStyleLine, 0) + this._overrideStyle(this.panel._centerBox, trayContentSizeStyleLine, 0) } - _removeStyles() { - /* disconnect signal */ - if (this._rightBoxActorAddedID) - this.panel._rightBox.disconnect(this._rightBoxActorAddedID); - if (this._centerBoxActorAddedID) - this.panel._centerBox.disconnect(this._centerBoxActorAddedID); - if (this._leftBoxActorAddedID) - this.panel._leftBox.disconnect(this._leftBoxActorAddedID); + // center box has been moved next to the right box and will be treated the same + this._centerBoxOperations = this._rightBoxOperations - this._restoreOriginalStyle(this.panel._rightBox); - this._restoreOriginalStyle(this.panel._centerBox); - this._restoreOriginalStyle(this.panel._leftBox); + this._leftBoxOperations = [] - this._applyStylesRecursively(true); + let leftboxPadding = SETTINGS.get_int('leftbox-padding') + if (leftboxPadding >= 0) { + let leftboxPaddingStyleLine = paddingStyle.format(leftboxPadding) + let operation = {} + operation.compareFn = function (actor) { + let parent = actor.get_parent() + return ( + parent && + parent.has_style_class_name && + parent.has_style_class_name('panel-button') + ) + } + operation.applyFn = (actor, operationIdx) => { + this._overrideStyle(actor, leftboxPaddingStyleLine, operationIdx) + } + this._leftBoxOperations.push(operation) } - _applyStylesRecursively(restore) { - /*recurse actors */ - if(this._rightBoxOperations.length) { - // add the system menu as we move it from the rightbox to the panel to position it independently - let children = this.panel._rightBox.get_children().concat([this.panel.statusArea[Utils.getSystemMenuInfo().name].container]); - for(let i in children) - this._recursiveApply(children[i], this._rightBoxOperations, restore); - } + let leftboxContentSize = SETTINGS.get_int('leftbox-size') + if (leftboxContentSize > 0) { + let leftboxIconSizeStyleLine = 'icon-size: %dpx'.format( + leftboxContentSize, + ) + let operation = {} + operation.compareFn = function (actor) { + return actor.constructor && actor.constructor.name == 'St_Icon' + } + operation.applyFn = (actor, operationIdx) => { + this._overrideStyle(actor, leftboxIconSizeStyleLine, operationIdx) + } + this._leftBoxOperations.push(operation) - if(this._centerBoxOperations.length) { - // add the date menu as we move it from the centerbox to the panel to position it independently - let children = this.panel._centerBox.get_children().concat([this.panel.statusArea.dateMenu.container]); - for(let i in children) - this._recursiveApply(children[i], this._centerBoxOperations, restore); - } + let leftboxContentSizeStyleLine = 'font-size: %dpx'.format( + leftboxContentSize, + ) + operation = {} + operation.compareFn = function (actor) { + return actor.constructor && actor.constructor.name == 'St_Label' + } + operation.applyFn = (actor, operationIdx) => { + this._overrideStyle(actor, leftboxContentSizeStyleLine, operationIdx) + } + this._leftBoxOperations.push(operation) - if(this._leftBoxOperations.length) { - let children = this.panel._leftBox.get_children(); - for(let i in children) - this._recursiveApply(children[i], this._leftBoxOperations, restore); - } + this._overrideStyle(this.panel._leftBox, leftboxContentSizeStyleLine, 0) } - _recursiveApply(actor, operations, restore) { - for(let i in operations) { - let o = operations[i]; - if(o.compareFn(actor)) - if(restore) - o.restoreFn ? o.restoreFn(actor) : this._restoreOriginalStyle(actor); - else - o.applyFn(actor, i); - } + this._applyStylesRecursively() - if(actor.get_children) { - let children = actor.get_children(); - for(let i in children) { - this._recursiveApply(children[i], operations, restore); - } - } - } - - _overrideStyle(actor, styleLine, operationIdx) { - if (actor._dtp_original_inline_style === undefined) { - actor._dtp_original_inline_style = actor.get_style(); - } + /* connect signal */ + this._rightBoxActorAddedID = this.panel._rightBox.connect( + 'child-added', + (container, actor) => { + if (this._rightBoxOperations.length && !this._ignoreAddedChild) + this._recursiveApply(actor, this._rightBoxOperations) - if(actor._dtp_style_overrides === undefined) { - actor._dtp_style_overrides = {}; - } + this._ignoreAddedChild = 0 + }, + ) + this._centerBoxActorAddedID = this.panel._centerBox.connect( + 'child-added', + (container, actor) => { + if (this._centerBoxOperations.length && !this._ignoreAddedChild) + this._recursiveApply(actor, this._centerBoxOperations) - actor._dtp_style_overrides[operationIdx] = styleLine; - let newStyleLine = ''; - for(let i in actor._dtp_style_overrides) - newStyleLine += actor._dtp_style_overrides[i] + '; '; - actor.set_style(newStyleLine + (actor._dtp_original_inline_style || '')); - } + this._ignoreAddedChild = 0 + }, + ) + this._leftBoxActorAddedID = this.panel._leftBox.connect( + 'child-added', + (container, actor) => { + if (this._leftBoxOperations.length) + this._recursiveApply(actor, this._leftBoxOperations) + }, + ) + } - _restoreOriginalStyle(actor) { - if (actor._dtp_original_inline_style !== undefined) { - actor.set_style(actor._dtp_original_inline_style); - delete actor._dtp_original_inline_style; - delete actor._dtp_style_overrides; - } + _removeStyles() { + /* disconnect signal */ + if (this._rightBoxActorAddedID) + this.panel._rightBox.disconnect(this._rightBoxActorAddedID) + if (this._centerBoxActorAddedID) + this.panel._centerBox.disconnect(this._centerBoxActorAddedID) + if (this._leftBoxActorAddedID) + this.panel._leftBox.disconnect(this._leftBoxActorAddedID) - if (actor.has_style_class_name('panel-button')) { - this._refreshPanelButton(actor); - } + this._restoreOriginalStyle(this.panel._rightBox) + this._restoreOriginalStyle(this.panel._centerBox) + this._restoreOriginalStyle(this.panel._leftBox) + + this._applyStylesRecursively(true) + } + + _applyStylesRecursively(restore) { + /*recurse actors */ + if (this._rightBoxOperations.length) { + // add the system menu as we move it from the rightbox to the panel to position it independently + let children = this.panel._rightBox + .get_children() + .concat([ + this.panel.statusArea[Utils.getSystemMenuInfo().name].container, + ]) + for (let i in children) + this._recursiveApply(children[i], this._rightBoxOperations, restore) } - _refreshPanelButton(actor) { - if (actor.visible) { - //force gnome 3.34+ to refresh (having problem with the -natural-hpadding) - let parent = actor.get_parent(); - let children = parent.get_children(); - let actorIndex = 0; - - if (children.length > 1) { - actorIndex = children.indexOf(actor); - } - - this._ignoreAddedChild = [this.panel._centerBox, this.panel._rightBox].indexOf(parent) >= 0; - - parent.remove_child(actor); - parent.insert_child_at_index(actor, actorIndex); - } + if (this._centerBoxOperations.length) { + // add the date menu as we move it from the centerbox to the panel to position it independently + let children = this.panel._centerBox + .get_children() + .concat([this.panel.statusArea.dateMenu.container]) + for (let i in children) + this._recursiveApply(children[i], this._centerBoxOperations, restore) } - + + if (this._leftBoxOperations.length) { + let children = this.panel._leftBox.get_children() + for (let i in children) + this._recursiveApply(children[i], this._leftBoxOperations, restore) + } + } + + _recursiveApply(actor, operations, restore) { + for (let i in operations) { + let o = operations[i] + if (o.compareFn(actor)) + if (restore) + o.restoreFn ? o.restoreFn(actor) : this._restoreOriginalStyle(actor) + else o.applyFn(actor, i) + } + + if (actor.get_children) { + let children = actor.get_children() + for (let i in children) { + this._recursiveApply(children[i], operations, restore) + } + } + } + + _overrideStyle(actor, styleLine, operationIdx) { + if (actor._dtp_original_inline_style === undefined) { + actor._dtp_original_inline_style = actor.get_style() + } + + if (actor._dtp_style_overrides === undefined) { + actor._dtp_style_overrides = {} + } + + actor._dtp_style_overrides[operationIdx] = styleLine + let newStyleLine = '' + for (let i in actor._dtp_style_overrides) + newStyleLine += actor._dtp_style_overrides[i] + '; ' + actor.set_style(newStyleLine + (actor._dtp_original_inline_style || '')) + } + + _restoreOriginalStyle(actor) { + if (actor._dtp_original_inline_style !== undefined) { + actor.set_style(actor._dtp_original_inline_style) + delete actor._dtp_original_inline_style + delete actor._dtp_style_overrides + } + + if (actor.has_style_class_name('panel-button')) { + this._refreshPanelButton(actor) + } + } + + _refreshPanelButton(actor) { + if (actor.visible) { + //force gnome 3.34+ to refresh (having problem with the -natural-hpadding) + let parent = actor.get_parent() + let children = parent.get_children() + let actorIndex = 0 + + if (children.length > 1) { + actorIndex = children.indexOf(actor) + } + + this._ignoreAddedChild = + [this.panel._centerBox, this.panel._rightBox].indexOf(parent) >= 0 + + parent.remove_child(actor) + parent.insert_child_at_index(actor, actorIndex) + } + } } diff --git a/prefs.js b/prefs.js index db51cdb..f670cce 100644 --- a/prefs.js +++ b/prefs.js @@ -20,28 +20,32 @@ * Some code was also adapted from the upstream Gnome Shell source code. */ -import GdkPixbuf from 'gi://GdkPixbuf'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import Gtk from 'gi://Gtk'; -import Gdk from 'gi://Gdk'; +import GdkPixbuf from 'gi://GdkPixbuf' +import Gio from 'gi://Gio' +import GLib from 'gi://GLib' +import GObject from 'gi://GObject' +import Gtk from 'gi://Gtk' +import Gdk from 'gi://Gdk' -import * as PanelSettings from './panelSettings.js'; -import * as Pos from './panelPositions.js'; +import * as PanelSettings from './panelSettings.js' +import * as Pos from './panelPositions.js' -import {ExtensionPreferences, gettext as _, ngettext} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; +import { + ExtensionPreferences, + gettext as _, + ngettext, +} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js' -const SCALE_UPDATE_TIMEOUT = 500; -const DEFAULT_PANEL_SIZES = [ 128, 96, 64, 48, 32, 24, 16 ]; -const DEFAULT_FONT_SIZES = [ 96, 64, 48, 32, 24, 16, 0 ]; -const DEFAULT_MARGIN_SIZES = [ 32, 24, 16, 12, 8, 4, 0 ]; -const DEFAULT_PADDING_SIZES = [ 32, 24, 16, 12, 8, 4, 0, -1 ]; +const SCALE_UPDATE_TIMEOUT = 500 +const DEFAULT_PANEL_SIZES = [128, 96, 64, 48, 32, 24, 16] +const DEFAULT_FONT_SIZES = [96, 64, 48, 32, 24, 16, 0] +const DEFAULT_MARGIN_SIZES = [32, 24, 16, 12, 8, 4, 0] +const DEFAULT_PADDING_SIZES = [32, 24, 16, 12, 8, 4, 0, -1] // Minimum length could be 0, but a higher value may help prevent confusion about where the panel went. -const LENGTH_MARKS = [ 100, 90, 80, 70, 60, 50, 40, 30, 20, 10 ]; -const MAX_WINDOW_INDICATOR = 4; +const LENGTH_MARKS = [100, 90, 80, 70, 60, 50, 40, 30, 20, 10] +const MAX_WINDOW_INDICATOR = 4 -const SCHEMA_PATH = '/org/gnome/shell/extensions/dash-to-panel/'; +const SCHEMA_PATH = '/org/gnome/shell/extensions/dash-to-panel/' /** * This function was copied from the activities-config extension @@ -49,2446 +53,3807 @@ const SCHEMA_PATH = '/org/gnome/shell/extensions/dash-to-panel/'; * by Norman L. Smith. */ function cssHexString(css) { - let rrggbb = '#'; - let start; - for (let loop = 0; loop < 3; loop++) { - let end = 0; - let xx = ''; - for (let loop = 0; loop < 2; loop++) { - while (true) { - let x = css.slice(end, end + 1); - if ((x == '(') || (x == ',') || (x == ')')) - break; - end++; - } - if (loop == 0) { - end++; - start = end; - } - } - xx = parseInt(css.slice(start, end)).toString(16); - if (xx.length == 1) - xx = '0' + xx; - rrggbb += xx; - css = css.slice(end); + let rrggbb = '#' + let start + for (let loop = 0; loop < 3; loop++) { + let end = 0 + let xx = '' + for (let loop = 0; loop < 2; loop++) { + while (true) { + let x = css.slice(end, end + 1) + if (x == '(' || x == ',' || x == ')') break + end++ + } + if (loop == 0) { + end++ + start = end + } } - return rrggbb; + xx = parseInt(css.slice(start, end)).toString(16) + if (xx.length == 1) xx = '0' + xx + rrggbb += xx + css = css.slice(end) + } + return rrggbb } function setShortcut(settings, shortcutName) { - let shortcut_text = settings.get_string(shortcutName + '-text'); - let [success, key, mods] = Gtk.accelerator_parse(shortcut_text); + let shortcut_text = settings.get_string(shortcutName + '-text') + let [success, key, mods] = Gtk.accelerator_parse(shortcut_text) - if (success && Gtk.accelerator_valid(key, mods)) { - let shortcut = Gtk.accelerator_name(key, mods); - settings.set_strv(shortcutName, [shortcut]); - } - else { - settings.set_strv(shortcutName, []); - } + if (success && Gtk.accelerator_valid(key, mods)) { + let shortcut = Gtk.accelerator_name(key, mods) + settings.set_strv(shortcutName, [shortcut]) + } else { + settings.set_strv(shortcutName, []) + } } function checkHotkeyPrefix(settings) { - settings.delay(); + settings.delay() - let hotkeyPrefix = settings.get_string('hotkey-prefix-text'); - if (hotkeyPrefix == 'Super') - hotkeyPrefix = ''; - else if (hotkeyPrefix == 'SuperAlt') - hotkeyPrefix = ''; - let [ , , mods] = Gtk.accelerator_parse(hotkeyPrefix); - let [ , , shift_mods] = Gtk.accelerator_parse('' + hotkeyPrefix); - let [ , , ctrl_mods] = Gtk.accelerator_parse('' + hotkeyPrefix); + let hotkeyPrefix = settings.get_string('hotkey-prefix-text') + if (hotkeyPrefix == 'Super') hotkeyPrefix = '' + else if (hotkeyPrefix == 'SuperAlt') hotkeyPrefix = '' + let [, , mods] = Gtk.accelerator_parse(hotkeyPrefix) + let [, , shift_mods] = Gtk.accelerator_parse('' + hotkeyPrefix) + let [, , ctrl_mods] = Gtk.accelerator_parse('' + hotkeyPrefix) - let numHotkeys = 10; - for (let i = 1; i <= numHotkeys; i++) { - let number = i; - if (number == 10) - number = 0; - let key = Gdk.keyval_from_name(number.toString()); - let key_kp = Gdk.keyval_from_name('KP_' + number.toString()); - if (Gtk.accelerator_valid(key, mods)) { - let shortcut = Gtk.accelerator_name(key, mods); - let shortcut_kp = Gtk.accelerator_name(key_kp, mods); + let numHotkeys = 10 + for (let i = 1; i <= numHotkeys; i++) { + let number = i + if (number == 10) number = 0 + let key = Gdk.keyval_from_name(number.toString()) + let key_kp = Gdk.keyval_from_name('KP_' + number.toString()) + if (Gtk.accelerator_valid(key, mods)) { + let shortcut = Gtk.accelerator_name(key, mods) + let shortcut_kp = Gtk.accelerator_name(key_kp, mods) - // Setup shortcut strings - settings.set_strv('app-hotkey-' + i, [shortcut]); - settings.set_strv('app-hotkey-kp-' + i, [shortcut_kp]); + // Setup shortcut strings + settings.set_strv('app-hotkey-' + i, [shortcut]) + settings.set_strv('app-hotkey-kp-' + i, [shortcut_kp]) - // With - shortcut = Gtk.accelerator_name(key, shift_mods); - shortcut_kp = Gtk.accelerator_name(key_kp, shift_mods); - settings.set_strv('app-shift-hotkey-' + i, [shortcut]); - settings.set_strv('app-shift-hotkey-kp-' + i, [shortcut_kp]); + // With + shortcut = Gtk.accelerator_name(key, shift_mods) + shortcut_kp = Gtk.accelerator_name(key_kp, shift_mods) + settings.set_strv('app-shift-hotkey-' + i, [shortcut]) + settings.set_strv('app-shift-hotkey-kp-' + i, [shortcut_kp]) - // With - shortcut = Gtk.accelerator_name(key, ctrl_mods); - shortcut_kp = Gtk.accelerator_name(key_kp, ctrl_mods); - settings.set_strv('app-ctrl-hotkey-' + i, [shortcut]); - settings.set_strv('app-ctrl-hotkey-kp-' + i, [shortcut_kp]); - } - else { - // Reset default settings for the relevant keys if the - // accelerators are invalid - let keys = ['app-hotkey-' + i, 'app-shift-hotkey-' + i, 'app-ctrl-hotkey-' + i, // Regular numbers - 'app-hotkey-kp-' + i, 'app-shift-hotkey-kp-' + i, 'app-ctrl-hotkey-kp-' + i]; // Key-pad numbers - keys.forEach(function(val) { - settings.set_value(val, settings.get_default_value(val)); - }, this); - } + // With + shortcut = Gtk.accelerator_name(key, ctrl_mods) + shortcut_kp = Gtk.accelerator_name(key_kp, ctrl_mods) + settings.set_strv('app-ctrl-hotkey-' + i, [shortcut]) + settings.set_strv('app-ctrl-hotkey-kp-' + i, [shortcut_kp]) + } else { + // Reset default settings for the relevant keys if the + // accelerators are invalid + let keys = [ + 'app-hotkey-' + i, + 'app-shift-hotkey-' + i, + 'app-ctrl-hotkey-' + i, // Regular numbers + 'app-hotkey-kp-' + i, + 'app-shift-hotkey-kp-' + i, + 'app-ctrl-hotkey-kp-' + i, + ] // Key-pad numbers + keys.forEach(function (val) { + settings.set_value(val, settings.get_default_value(val)) + }, this) } + } - settings.apply(); + settings.apply() } function mergeObjects(main, bck) { - for (const prop in bck) { - if (!main.hasOwnProperty(prop) && bck.hasOwnProperty(prop)) { - main[prop] = bck[prop]; - } + for (const prop in bck) { + if (!main.hasOwnProperty(prop) && bck.hasOwnProperty(prop)) { + main[prop] = bck[prop] } + } - return main; + return main } const Preferences = class { + constructor(window, settings, path) { + // this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.dash-to-panel'); + this._rtl = Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL + this._builder = new Gtk.Builder() + this._builder.set_scope(new BuilderScope(this)) + this._settings = settings + this._path = path - constructor(window, settings, path) { - // this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.dash-to-panel'); - this._rtl = (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL); - this._builder = new Gtk.Builder(); - this._builder.set_scope(new BuilderScope(this)); - this._settings = settings; - this._path = path; + this._metadata = ExtensionPreferences.lookupByURL(import.meta.url).metadata + this._builder.set_translation_domain(this._metadata['gettext-domain']) - this._metadata = ExtensionPreferences.lookupByURL(import.meta.url).metadata; - this._builder.set_translation_domain(this._metadata['gettext-domain']); + window.set_search_enabled(true) - window.set_search_enabled(true); + // dialogs + this._builder.add_from_file( + this._path + '/ui/BoxAnimateAppIconHoverOptions.ui', + ) + this._builder.add_from_file( + this._path + '/ui/BoxHighlightAppIconHoverOptions.ui', + ) + this._builder.add_from_file(this._path + '/ui/BoxDotOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxShowDesktopOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxDynamicOpacityOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxIntellihideOptions.ui') + this._builder.add_from_file( + this._path + '/ui/BoxShowApplicationsOptions.ui', + ) + this._builder.add_from_file(this._path + '/ui/BoxWindowPreviewOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxGroupAppsOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxMiddleClickOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxOverlayShortcut.ui') + this._builder.add_from_file(this._path + '/ui/BoxSecondaryMenuOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxScrollPanelOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxScrollIconOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxAdvancedOptions.ui') - // dialogs - this._builder.add_from_file(this._path + '/ui/BoxAnimateAppIconHoverOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxHighlightAppIconHoverOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxDotOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxShowDesktopOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxDynamicOpacityOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxIntellihideOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxShowApplicationsOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxWindowPreviewOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxGroupAppsOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxMiddleClickOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxOverlayShortcut.ui'); - this._builder.add_from_file(this._path + '/ui/BoxSecondaryMenuOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxScrollPanelOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxScrollIconOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxAdvancedOptions.ui'); + // pages + this._builder.add_from_file(this._path + '/ui/SettingsPosition.ui') + let pagePosition = this._builder.get_object('position') + window.add(pagePosition) - // pages - this._builder.add_from_file(this._path + '/ui/SettingsPosition.ui'); - let pagePosition = this._builder.get_object('position'); - window.add(pagePosition); + this._builder.add_from_file(this._path + '/ui/SettingsStyle.ui') + let pageStyle = this._builder.get_object('style') + window.add(pageStyle) - this._builder.add_from_file(this._path + '/ui/SettingsStyle.ui'); - let pageStyle = this._builder.get_object('style'); - window.add(pageStyle); + this._builder.add_from_file(this._path + '/ui/SettingsBehavior.ui') + let pageBehavior = this._builder.get_object('behavior') + window.add(pageBehavior) - this._builder.add_from_file(this._path + '/ui/SettingsBehavior.ui'); - let pageBehavior = this._builder.get_object('behavior'); - window.add(pageBehavior); + this._builder.add_from_file(this._path + '/ui/SettingsAction.ui') + let pageAction = this._builder.get_object('action') + window.add(pageAction) - this._builder.add_from_file(this._path + '/ui/SettingsAction.ui'); - let pageAction = this._builder.get_object('action'); - window.add(pageAction); + this._builder.add_from_file(this._path + '/ui/SettingsFineTune.ui') + let pageFineTune = this._builder.get_object('finetune') + window.add(pageFineTune) - this._builder.add_from_file(this._path + '/ui/SettingsFineTune.ui'); - let pageFineTune = this._builder.get_object('finetune'); - window.add(pageFineTune); + this._builder.add_from_file(this._path + '/ui/SettingsDonation.ui') + let pageDonation = this._builder.get_object('donation') + window.add(pageDonation) - this._builder.add_from_file(this._path + '/ui/SettingsDonation.ui'); - let pageDonation = this._builder.get_object('donation'); - window.add(pageDonation); + this._builder.add_from_file(this._path + '/ui/SettingsAbout.ui') + let pageAbout = this._builder.get_object('about') + window.add(pageAbout) - this._builder.add_from_file(this._path + '/ui/SettingsAbout.ui'); - let pageAbout = this._builder.get_object('about'); - window.add(pageAbout); + let listbox = this._builder.get_object('taskbar_display_listbox') + let provider = new Gtk.CssProvider() + provider.load_from_data('list { background-color: transparent; }', -1) + let context = listbox.get_style_context() + context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) - let listbox = this._builder.get_object('taskbar_display_listbox'); - let provider = new Gtk.CssProvider(); - provider.load_from_data('list { background-color: transparent; }', -1); - let context = listbox.get_style_context(); - context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + // set the window as notebook, it is being used as parent for dialogs + this.notebook = window - // set the window as notebook, it is being used as parent for dialogs - this.notebook = window; + // Timeout to delay the update of the settings + this._panel_size_timeout = 0 + this._dot_height_timeout = 0 + this._tray_size_timeout = 0 + this._leftbox_size_timeout = 0 + this._appicon_margin_timeout = 0 + this._appicon_margin_todesktop_timeout = 0 + this._appicon_margin_toscreenborder_timeout = 0 + this._appicon_padding_timeout = 0 + this._opacity_timeout = 0 + this._tray_padding_timeout = 0 + this._statusicon_padding_timeout = 0 + this._leftbox_padding_timeout = 0 + this._addFormatValueCallbacks() + this._bindSettings() - // Timeout to delay the update of the settings - this._panel_size_timeout = 0; - this._dot_height_timeout = 0; - this._tray_size_timeout = 0; - this._leftbox_size_timeout = 0; - this._appicon_margin_timeout = 0; - this._appicon_margin_todesktop_timeout = 0; - this._appicon_margin_toscreenborder_timeout = 0; - this._appicon_padding_timeout = 0; - this._opacity_timeout = 0; - this._tray_padding_timeout = 0; - this._statusicon_padding_timeout = 0; - this._leftbox_padding_timeout = 0; - this._addFormatValueCallbacks(); - this._bindSettings(); + let maybeGoToPage = () => { + let targetPageName = settings.get_string('target-prefs-page') - let maybeGoToPage = () => { - let targetPageName = settings.get_string('target-prefs-page') - - if (targetPageName) { - window.set_visible_page_name(targetPageName) - settings.set_string('target-prefs-page', '') - } - } - - settings.connect('changed::target-prefs-page', maybeGoToPage); - - maybeGoToPage(); + if (targetPageName) { + window.set_visible_page_name(targetPageName) + settings.set_string('target-prefs-page', '') + } } - /** - * Connect signals - */ - _connector(builder, object, signal, handler) { - object.connect(signal, this._SignalHandler[handler].bind(this)); + settings.connect('changed::target-prefs-page', maybeGoToPage) + + maybeGoToPage() + } + + /** + * Connect signals + */ + _connector(builder, object, signal, handler) { + object.connect(signal, this._SignalHandler[handler].bind(this)) + } + + _updateVerticalRelatedOptions() { + let position = this._getPanelPosition(this._currentMonitorIndex) + let isVertical = position == Pos.LEFT || position == Pos.RIGHT + let showDesktopWidthLabel = this._builder.get_object( + 'show_showdesktop_width_label', + ) + + showDesktopWidthLabel.set_title( + isVertical + ? _('Show Desktop button height (px)') + : _('Show Desktop button width (px)'), + ) + + this._displayPanelPositionsForMonitor(this._currentMonitorIndex) + } + + _maybeDisableTopPosition() { + let keepTopPanel = this._settings.get_boolean('stockgs-keep-top-panel') + let monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + let topAvailable = + !keepTopPanel || + (!monitorSync && this._currentMonitorIndex != this.monitors[0]) + let topRadio = this._builder.get_object('position_top_button') + + topRadio.set_sensitive(topAvailable) + topRadio.set_tooltip_text( + !topAvailable + ? _('Unavailable when gnome-shell top panel is present') + : '', + ) + } + + _getPanelPosition(monitorIndex) { + return PanelSettings.getPanelPosition(this._settings, monitorIndex) + } + + _setPanelPosition(position) { + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? this.monitors + : [this._currentMonitorIndex] + monitorsToSetFor.forEach((monitorIndex) => { + PanelSettings.setPanelPosition(this._settings, monitorIndex, position) + }) + this._setAnchorLabels(this._currentMonitorIndex) + } + + _setPositionRadios(position) { + this._ignorePositionRadios = true + + switch (position) { + case Pos.BOTTOM: + this._builder.get_object('position_bottom_button').set_active(true) + break + case Pos.TOP: + this._builder.get_object('position_top_button').set_active(true) + break + case Pos.LEFT: + this._builder.get_object('position_left_button').set_active(true) + break + case Pos.RIGHT: + this._builder.get_object('position_right_button').set_active(true) + break } - _updateVerticalRelatedOptions() { - let position = this._getPanelPosition(this._currentMonitorIndex); - let isVertical = position == Pos.LEFT || position == Pos.RIGHT; - let showDesktopWidthLabel = this._builder.get_object('show_showdesktop_width_label'); + this._ignorePositionRadios = false + } - showDesktopWidthLabel.set_title(isVertical ? _('Show Desktop button height (px)') : _('Show Desktop button width (px)')); + /** + * Set panel anchor combo labels according to whether the monitor's panel is vertical + * or horizontal, or if all monitors' panels are being configured and they are a mix + * of vertical and horizontal. + */ + _setAnchorLabels(currentMonitorIndex) { + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync ? this.monitors : [currentMonitorIndex] + const allVertical = monitorsToSetFor.every((i) => { + const position = PanelSettings.getPanelPosition(this._settings, i) + return position === Pos.LEFT || position === Pos.RIGHT + }) + const allHorizontal = monitorsToSetFor.every((i) => { + const position = PanelSettings.getPanelPosition(this._settings, i) + return position === Pos.TOP || position === Pos.BOTTOM + }) - this._displayPanelPositionsForMonitor(this._currentMonitorIndex); + const anchor_combo = this._builder.get_object('panel_anchor_combo') + anchor_combo.remove_all() + + if (allHorizontal) { + anchor_combo.append(Pos.START, _('Left')) + anchor_combo.append(Pos.MIDDLE, _('Center')) + anchor_combo.append(Pos.END, _('Right')) + } else if (allVertical) { + anchor_combo.append(Pos.START, _('Top')) + anchor_combo.append(Pos.MIDDLE, _('Middle')) + anchor_combo.append(Pos.END, _('Bottom')) + } else { + // Setting for a mix of horizontal and vertical panels on different monitors. + anchor_combo.append(Pos.START, _('Start')) + anchor_combo.append(Pos.MIDDLE, _('Middle')) + anchor_combo.append(Pos.END, _('End')) } - _maybeDisableTopPosition() { - let keepTopPanel = this._settings.get_boolean('stockgs-keep-top-panel'); - let monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - let topAvailable = !keepTopPanel || (!monitorSync && this._currentMonitorIndex != this.monitors[0]); - let topRadio = this._builder.get_object('position_top_button'); + // Set combo box after re-populating its options. But only if it's for a single-panel + // configuration, or a multi-panel configuration where they all have the same anchor + // setting. So don't set the combo box if there is a multi-panel configuration with + // different anchor settings. + const someAnchor = PanelSettings.getPanelAnchor( + this._settings, + currentMonitorIndex, + ) + if ( + monitorsToSetFor.every( + (i) => PanelSettings.getPanelAnchor(this._settings, i) === someAnchor, + ) + ) { + const panel_anchor = PanelSettings.getPanelAnchor( + this._settings, + currentMonitorIndex, + ) + this._builder.get_object('panel_anchor_combo').set_active_id(panel_anchor) + } + } - topRadio.set_sensitive(topAvailable); - topRadio.set_tooltip_text(!topAvailable ? _('Unavailable when gnome-shell top panel is present') : ''); + /** + * When a monitor is selected, update the widgets for panel position, size, anchoring, + * and contents so they accurately show the settings for the panel on that monitor. + */ + _updateWidgetSettingsForMonitor(monitorIndex) { + // Update display of panel screen position setting + this._maybeDisableTopPosition() + const panelPosition = this._getPanelPosition(monitorIndex) + this._setPositionRadios(panelPosition) + + // Update display of thickness, length, and anchor settings + const panel_size_scale = this._builder.get_object('panel_size_scale') + const size = PanelSettings.getPanelSize(this._settings, monitorIndex) + panel_size_scale.set_value(size) + + const panel_length_scale = this._builder.get_object('panel_length_scale') + const length = PanelSettings.getPanelLength(this._settings, monitorIndex) + panel_length_scale.set_value(length) + this._setAnchorWidgetSensitivity(length) + + this._setAnchorLabels(monitorIndex) + + // Update display of panel content settings + this._displayPanelPositionsForMonitor(monitorIndex) + } + + /** + * Anchor is only relevant if panel length is less than 100%. Enable or disable + * anchor widget sensitivity accordingly. + */ + _setAnchorWidgetSensitivity(panelLength) { + const isPartialLength = panelLength < 100 + this._builder + .get_object('panel_anchor_label') + .set_sensitive(isPartialLength) + this._builder + .get_object('panel_anchor_combo') + .set_sensitive(isPartialLength) + } + + _displayPanelPositionsForMonitor(monitorIndex) { + let taskbarListBox = this._builder.get_object('taskbar_display_listbox') + + while (taskbarListBox.get_first_child()) { + taskbarListBox.remove(taskbarListBox.get_first_child()) } - _getPanelPosition(monitorIndex) { - return PanelSettings.getPanelPosition(this._settings, monitorIndex); + let labels = {} + let panelPosition = this._getPanelPosition(monitorIndex) + let isVertical = panelPosition == Pos.LEFT || panelPosition == Pos.RIGHT + let panelElementPositionsSettings = PanelSettings.getSettingsJson( + this._settings, + 'panel-element-positions', + ) + let panelElementPositions = + panelElementPositionsSettings[monitorIndex] || Pos.defaults + let updateElementsSettings = () => { + let newPanelElementPositions = [] + let monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + let monitors = monitorSync ? this.monitors : [monitorIndex] + + let child = taskbarListBox.get_first_child() + while (child != null) { + newPanelElementPositions.push({ + element: child.id, + visible: child.visibleToggleBtn.get_active(), + position: child.positionCombo.get_active_id(), + }) + child = child.get_next_sibling() + } + + monitors.forEach( + (m) => (panelElementPositionsSettings[m] = newPanelElementPositions), + ) + this._settings.set_string( + 'panel-element-positions', + JSON.stringify(panelElementPositionsSettings), + ) } - _setPanelPosition(position) { - const monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this.monitors : [this._currentMonitorIndex]; - monitorsToSetFor.forEach(monitorIndex => { - PanelSettings.setPanelPosition(this._settings, monitorIndex, position); - }); - this._setAnchorLabels(this._currentMonitorIndex); - } + labels[Pos.SHOW_APPS_BTN] = _('Show Applications button') + labels[Pos.ACTIVITIES_BTN] = _('Activities button') + labels[Pos.TASKBAR] = _('Taskbar') + labels[Pos.DATE_MENU] = _('Date menu') + labels[Pos.SYSTEM_MENU] = _('System menu') + labels[Pos.LEFT_BOX] = _('Left box') + labels[Pos.CENTER_BOX] = _('Center box') + labels[Pos.RIGHT_BOX] = _('Right box') + labels[Pos.DESKTOP_BTN] = _('Desktop button') - _setPositionRadios(position) { - this._ignorePositionRadios = true; + panelElementPositions.forEach((el) => { + let row = new Gtk.ListBoxRow() + let grid = new Gtk.Grid({ + margin_start: 12, + margin_end: 12, + column_spacing: 8, + }) + let upDownGrid = new Gtk.Grid({ column_spacing: 2 }) + let upBtn = new Gtk.Button({ tooltip_text: _('Move up') }) + let upImg = new Gtk.Image({ icon_name: 'go-up-symbolic', pixel_size: 12 }) + let downBtn = new Gtk.Button({ tooltip_text: _('Move down') }) + let downImg = new Gtk.Image({ + icon_name: 'go-down-symbolic', + pixel_size: 12, + }) + let visibleToggleBtn = new Gtk.ToggleButton({ + label: _('Visible'), + active: el.visible, + }) + let positionCombo = new Gtk.ComboBoxText({ + tooltip_text: _('Select element position'), + }) + let upDownClickHandler = (limit) => { + let index = row.get_index() - switch (position) { - case Pos.BOTTOM: - this._builder.get_object('position_bottom_button').set_active(true); - break; - case Pos.TOP: - this._builder.get_object('position_top_button').set_active(true); - break; - case Pos.LEFT: - this._builder.get_object('position_left_button').set_active(true); - break; - case Pos.RIGHT: - this._builder.get_object('position_right_button').set_active(true); - break; + if (index != limit) { + taskbarListBox.remove(row) + taskbarListBox.insert(row, index + (!limit ? -1 : 1)) + updateElementsSettings() } - - this._ignorePositionRadios = false; - } - - /** - * Set panel anchor combo labels according to whether the monitor's panel is vertical - * or horizontal, or if all monitors' panels are being configured and they are a mix - * of vertical and horizontal. - */ - _setAnchorLabels(currentMonitorIndex) { - const monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this.monitors : [currentMonitorIndex]; - const allVertical = monitorsToSetFor.every(i => { - const position = PanelSettings.getPanelPosition(this._settings, i); - return position === Pos.LEFT || position === Pos.RIGHT - }); - const allHorizontal = monitorsToSetFor.every(i => { - const position = PanelSettings.getPanelPosition(this._settings, i); - return position === Pos.TOP || position === Pos.BOTTOM; - }); - - const anchor_combo = this._builder.get_object('panel_anchor_combo'); - anchor_combo.remove_all(); - - if (allHorizontal) { - anchor_combo.append(Pos.START, _('Left')); - anchor_combo.append(Pos.MIDDLE, _('Center')); - anchor_combo.append(Pos.END, _('Right')); - } else if (allVertical) { - anchor_combo.append(Pos.START, _('Top')); - anchor_combo.append(Pos.MIDDLE, _('Middle')); - anchor_combo.append(Pos.END, _('Bottom')); - } else { - // Setting for a mix of horizontal and vertical panels on different monitors. - anchor_combo.append(Pos.START, _('Start')); - anchor_combo.append(Pos.MIDDLE, _('Middle')); - anchor_combo.append(Pos.END, _('End')); - } - - // Set combo box after re-populating its options. But only if it's for a single-panel - // configuration, or a multi-panel configuration where they all have the same anchor - // setting. So don't set the combo box if there is a multi-panel configuration with - // different anchor settings. - const someAnchor = PanelSettings.getPanelAnchor(this._settings, currentMonitorIndex); - if (monitorsToSetFor.every(i => - PanelSettings.getPanelAnchor(this._settings, i) === someAnchor)) { - const panel_anchor = PanelSettings.getPanelAnchor(this._settings, currentMonitorIndex); - this._builder.get_object('panel_anchor_combo').set_active_id(panel_anchor); - } - } - - /** - * When a monitor is selected, update the widgets for panel position, size, anchoring, - * and contents so they accurately show the settings for the panel on that monitor. - */ - _updateWidgetSettingsForMonitor(monitorIndex) { - // Update display of panel screen position setting - this._maybeDisableTopPosition(); - const panelPosition = this._getPanelPosition(monitorIndex); - this._setPositionRadios(panelPosition); - - // Update display of thickness, length, and anchor settings - const panel_size_scale = this._builder.get_object('panel_size_scale'); - const size = PanelSettings.getPanelSize(this._settings, monitorIndex); - panel_size_scale.set_value(size); - - const panel_length_scale = this._builder.get_object('panel_length_scale'); - const length = PanelSettings.getPanelLength(this._settings, monitorIndex); - panel_length_scale.set_value(length); - this._setAnchorWidgetSensitivity(length); - - this._setAnchorLabels(monitorIndex); - - // Update display of panel content settings - this._displayPanelPositionsForMonitor(monitorIndex); - } - - /** - * Anchor is only relevant if panel length is less than 100%. Enable or disable - * anchor widget sensitivity accordingly. - */ - _setAnchorWidgetSensitivity(panelLength) { - const isPartialLength = panelLength < 100; - this._builder.get_object('panel_anchor_label').set_sensitive(isPartialLength); - this._builder.get_object('panel_anchor_combo').set_sensitive(isPartialLength); - } - - _displayPanelPositionsForMonitor(monitorIndex) { - let taskbarListBox = this._builder.get_object('taskbar_display_listbox'); - - while(taskbarListBox.get_first_child()) - { - taskbarListBox.remove(taskbarListBox.get_first_child()); - } - - let labels = {}; - let panelPosition = this._getPanelPosition(monitorIndex); - let isVertical = panelPosition == Pos.LEFT || panelPosition == Pos.RIGHT; - let panelElementPositionsSettings = PanelSettings.getSettingsJson(this._settings, 'panel-element-positions'); - let panelElementPositions = panelElementPositionsSettings[monitorIndex] || Pos.defaults; - let updateElementsSettings = () => { - let newPanelElementPositions = []; - let monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - let monitors = monitorSync ? this.monitors : [monitorIndex]; - - let child = taskbarListBox.get_first_child(); - while (child != null) - { - newPanelElementPositions.push({ - element: child.id, - visible: child.visibleToggleBtn.get_active(), - position: child.positionCombo.get_active_id() - }); - child = child.get_next_sibling(); - } - - monitors.forEach(m => panelElementPositionsSettings[m] = newPanelElementPositions); - this._settings.set_string('panel-element-positions', JSON.stringify(panelElementPositionsSettings)); - }; - - - labels[Pos.SHOW_APPS_BTN] = _('Show Applications button'); - labels[Pos.ACTIVITIES_BTN] = _('Activities button'); - labels[Pos.TASKBAR] = _('Taskbar'); - labels[Pos.DATE_MENU] = _('Date menu'); - labels[Pos.SYSTEM_MENU] = _('System menu'); - labels[Pos.LEFT_BOX] = _('Left box'); - labels[Pos.CENTER_BOX] = _('Center box'); - labels[Pos.RIGHT_BOX] = _('Right box'); - labels[Pos.DESKTOP_BTN] = _('Desktop button'); - - panelElementPositions.forEach(el => { - let row = new Gtk.ListBoxRow(); - let grid = new Gtk.Grid({ margin_start: 12, margin_end: 12, column_spacing: 8 }); - let upDownGrid = new Gtk.Grid({ column_spacing: 2 }); - let upBtn = new Gtk.Button({ tooltip_text: _('Move up') }); - let upImg = new Gtk.Image({ icon_name: 'go-up-symbolic', pixel_size: 12 }); - let downBtn = new Gtk.Button({ tooltip_text: _('Move down') }); - let downImg = new Gtk.Image({ icon_name: 'go-down-symbolic', pixel_size: 12 }); - let visibleToggleBtn = new Gtk.ToggleButton({ label: _('Visible'), active: el.visible }); - let positionCombo = new Gtk.ComboBoxText({ tooltip_text: _('Select element position') }); - let upDownClickHandler = limit => { - let index = row.get_index(); - - if (index != limit) { - taskbarListBox.remove(row); - taskbarListBox.insert(row, index + (!limit ? -1 : 1)); - updateElementsSettings(); - } - }; - - positionCombo.append(Pos.STACKED_TL, isVertical ? _('Stacked to top') : _('Stacked to left')); - positionCombo.append(Pos.STACKED_BR, isVertical ? _('Stacked to bottom') :_('Stacked to right')); - positionCombo.append(Pos.CENTERED, _('Centered')); - positionCombo.append(Pos.CENTERED_MONITOR, _('Monitor Center')); - positionCombo.set_active_id(el.position); - - upBtn.connect('clicked', () => upDownClickHandler(0)); - downBtn.connect('clicked', () => upDownClickHandler(panelElementPositions.length - 1)); - visibleToggleBtn.connect('toggled', () => updateElementsSettings()); - positionCombo.connect('changed', () => updateElementsSettings()); - - upBtn.set_child(upImg); - downBtn.set_child(downImg); - - upDownGrid.attach(upBtn, 0, 0, 1, 1); - upDownGrid.attach(downBtn, 1, 0, 1, 1); - - grid.attach(upDownGrid, 0, 0, 1, 1); - grid.attach(new Gtk.Label({ label: labels[el.element], xalign: 0, hexpand: true }), 1, 0, 1, 1); - - if (Pos.optionDialogFunctions[el.element]) { - let cogImg = new Gtk.Image({ icon_name: 'emblem-system-symbolic' }); - let optionsBtn = new Gtk.Button({ tooltip_text: _('More options') }); - - optionsBtn.get_style_context().add_class('circular'); - optionsBtn.set_child(cogImg); - grid.attach(optionsBtn, 2, 0, 1, 1); - - optionsBtn.connect('clicked', () => this[Pos.optionDialogFunctions[el.element]]()); - } - - grid.attach(visibleToggleBtn, 3, 0, 1, 1); - grid.attach(positionCombo, 4, 0, 1, 1); - - row.id = el.element; - row.visibleToggleBtn = visibleToggleBtn; - row.positionCombo = positionCombo; - - row.set_child(grid); - taskbarListBox.insert(row, -1); - }); - } - - _createPreferencesDialog(title, content, reset_function = null) { - let dialog; - - dialog = new Gtk.Dialog({ title: title, - transient_for: this.notebook.get_root(), - use_header_bar: true, - modal: true }); - - // GTK+ leaves positive values for application-defined response ids. - // Use +1 for the reset action - if (reset_function != null) - dialog.add_button(_('Reset to defaults'), 1); - - dialog.get_content_area().append(content); - - dialog.connect('response', (dialog, id) => { - if (id == 1) { - // restore default settings - if (reset_function) - reset_function(); - } else { - // remove the settings content so it doesn't get destroyed; - dialog.get_content_area().remove(content); - dialog.destroy(); - } - return; - }); - - return dialog; - } - - _showShowAppsButtonOptions() { - let box = this._builder.get_object('show_applications_options'); - - let dialog = this._createPreferencesDialog(_('Show Applications options'), box, () => - { - // restore default settings - this._settings.set_value('show-apps-icon-side-padding', this._settings.get_default_value('show-apps-icon-side-padding')); - this._builder.get_object('show_applications_side_padding_spinbutton').set_value(this._settings.get_int('show-apps-icon-side-padding')); - this._settings.set_value('show-apps-override-escape', this._settings.get_default_value('show-apps-override-escape')); - handleIconChange.call(this, null); - }); - - let fileChooserButton = this._builder.get_object('show_applications_icon_file_filebutton'); - let fileChooser = new Gtk.FileChooserNative({ title: _('Open icon'), transient_for: dialog }); - let fileImage = this._builder.get_object('show_applications_current_icon_image'); - let fileFilter = new Gtk.FileFilter(); - fileFilter.add_pixbuf_formats(); - fileChooser.filter = fileFilter; - - let handleIconChange = function(newIconPath) { - if (newIconPath && GLib.file_test(newIconPath, GLib.FileTest.EXISTS)) { - let file = Gio.File.new_for_path(newIconPath); - let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(file.read(null), 32, 32, true, null); - - fileImage.set_from_pixbuf(pixbuf); - fileChooser.set_file(file); - fileChooserButton.set_label(newIconPath); - } else { - newIconPath = ''; - fileImage.set_from_icon_name('view-app-grid-symbolic'); - let picturesFolder = Gio.File.new_for_path(GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES)); - fileChooser.set_file(picturesFolder); - fileChooserButton.set_label("(None)"); - } - - this._settings.set_string('show-apps-icon-file', newIconPath || ''); - }; - - fileChooserButton.connect('clicked', () => { - fileChooser.show(); - }); - - fileChooser.connect('response', widget => handleIconChange.call(this, widget.get_file().get_path())); - handleIconChange.call(this, this._settings.get_string('show-apps-icon-file')); - - // we have to destroy the fileChooser as well - dialog.connect('response', (dialog, id) => { - if (id != 1) { - fileChooser.destroy(); - } - return; - }); - - dialog.show(); - dialog.set_default_size(1, 1); - } - - _showDesktopButtonOptions() { - let box = this._builder.get_object('box_show_showdesktop_options'); - - let dialog = this._createPreferencesDialog(_('Show Desktop options'), box, () => - { - // restore default settings - this._settings.set_value('showdesktop-button-width', this._settings.get_default_value('showdesktop-button-width')); - this._builder.get_object('show_showdesktop_width_spinbutton').set_value(this._settings.get_int('showdesktop-button-width')); - - this._settings.set_value('desktop-line-use-custom-color', this._settings.get_default_value('desktop-line-use-custom-color')); - - this._settings.set_value('show-showdesktop-hover', this._settings.get_default_value('show-showdesktop-hover')); - - this._settings.set_value('show-showdesktop-delay', this._settings.get_default_value('show-showdesktop-delay')); - this._builder.get_object('show_showdesktop_delay_spinbutton').set_value(this._settings.get_int('show-showdesktop-delay')); - - this._settings.set_value('show-showdesktop-time', this._settings.get_default_value('show-showdesktop-time')); - this._builder.get_object('show_showdesktop_time_spinbutton').set_value(this._settings.get_int('show-showdesktop-time')); - }); - - this._builder.get_object('show_showdesktop_width_spinbutton').set_value(this._settings.get_int('showdesktop-button-width')); - this._builder.get_object('show_showdesktop_width_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('showdesktop-button-width', widget.get_value()); - }); - - this._builder.get_object('show_showdesktop_delay_spinbutton').set_value(this._settings.get_int('show-showdesktop-delay')); - this._builder.get_object('show_showdesktop_delay_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('show-showdesktop-delay', widget.get_value()); - }); - - this._builder.get_object('show_showdesktop_time_spinbutton').set_value(this._settings.get_int('show-showdesktop-time')); - this._builder.get_object('show_showdesktop_time_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('show-showdesktop-time', widget.get_value()); - }); - - dialog.show(); - dialog.set_default_size(1, 1); - } - - _addFormatValueCallbacks() { - // position - this._builder.get_object('panel_size_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - - // style - this._builder.get_object('appicon_margin_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - this._builder.get_object('appicon_margin_todesktop_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - this._builder.get_object('appicon_margin_toscreenborder_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - - this._builder.get_object('appicon_padding_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - - // fine-tune box1 - this._builder.get_object('tray_size_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - - this._builder.get_object('leftbox_size_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - - // fine-tune box2 - this._builder.get_object('tray_padding_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - - this._builder.get_object('statusicon_padding_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - - this._builder.get_object('leftbox_padding_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - - // animate hovering app icons dialog - this._builder.get_object('animate_appicon_hover_options_duration_scale') - .set_format_value_func((scale, value) => { - return _("%d ms").format(value); - }); - - this._builder.get_object('animate_appicon_hover_options_rotation_scale') - .set_format_value_func((scale, value) => { - return _("%d °").format(value); - }); - - this._builder.get_object('animate_appicon_hover_options_travel_scale') - .set_format_value_func((scale, value) => { - return _("%d %%").format(value); - }); - - this._builder.get_object('animate_appicon_hover_options_zoom_scale') - .set_format_value_func((scale, value) => { - return _("%d %%").format(value); - }); - - this._builder.get_object('animate_appicon_hover_options_convexity_scale') - .set_format_value_func((scale, value) => { - return _("%.1f").format(value); - }); - - this._builder.get_object('animate_appicon_hover_options_extent_scale') - .set_format_value_func((scale, value) => { - return ngettext("%d icon", "%d icons", value).format(value); - }); - - // highlight appicon on hover dialog - this._builder.get_object('highlight_appicon_borderradius') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - } - - _bindSettings() { - // size options - let panel_size_scale = this._builder.get_object('panel_size_scale'); - panel_size_scale.set_range(DEFAULT_PANEL_SIZES[DEFAULT_PANEL_SIZES.length - 1], DEFAULT_PANEL_SIZES[0]); - DEFAULT_PANEL_SIZES.slice(1, -1).forEach(function(val) { - panel_size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()); - }); - - // Correct for rtl languages - if (this._rtl) { - // Flip value position: this is not done automatically - panel_size_scale.set_value_pos(Gtk.PositionType.LEFT); - // I suppose due to a bug, having a more than one mark and one above a value of 100 - // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable - // and then manually inverting it - panel_size_scale.set_flippable(false); - panel_size_scale.set_inverted(true); - } - - // App icon style option - this._builder.get_object('appicon_style_combo').set_active_id(this._settings.get_string('appicon-style')); - this._builder.get_object('appicon_style_combo').connect('changed', (widget) => { - this._settings.set_string('appicon-style', widget.get_active_id()); - }); - - // Dots Position option - let dotPosition = this._settings.get_string('dot-position'); - - switch (dotPosition) { - case 'BOTTOM': - this._builder.get_object('dots_bottom_button').set_active(true); - break; - case 'TOP': - this._builder.get_object('dots_top_button').set_active(true); - break; - case 'LEFT': - this._builder.get_object('dots_left_button').set_active(true); - break; - case 'RIGHT': - this._builder.get_object('dots_right_button').set_active(true); - break; - } - - this._builder.get_object('dot_style_focused_combo').set_active_id(this._settings.get_string('dot-style-focused')); - this._builder.get_object('dot_style_focused_combo').connect('changed', (widget) => { - this._settings.set_string('dot-style-focused', widget.get_active_id()); - }); - - this._builder.get_object('dot_style_unfocused_combo').set_active_id(this._settings.get_string('dot-style-unfocused')); - this._builder.get_object('dot_style_unfocused_combo').connect('changed', (widget) => { - this._settings.set_string('dot-style-unfocused', widget.get_active_id()); - }); - - for (let i = 1; i <= MAX_WINDOW_INDICATOR; i++) { - let idx = i; - this._builder.get_object('dot_color_' + idx + '_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('dot-color-' + idx, hexString); - }); - - this._builder.get_object('dot_color_unfocused_' + idx + '_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('dot-color-unfocused-' + idx, hexString); - }); - } - - this._builder.get_object('dot_color_apply_all_button').connect('clicked', () => { - for (let i = 2; i <= MAX_WINDOW_INDICATOR; i++) { - this._settings.set_value('dot-color-' + i, this._settings.get_value('dot-color-1')); - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('dot-color-' + i)); - this._builder.get_object('dot_color_' + i + '_colorbutton').set_rgba(rgba); - } - }); - - this._builder.get_object('dot_color_unfocused_apply_all_button').connect('clicked', () => { - for (let i = 2; i <= MAX_WINDOW_INDICATOR; i++) { - this._settings.set_value('dot-color-unfocused-' + i, this._settings.get_value('dot-color-unfocused-1')); - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('dot-color-unfocused-' + i)); - this._builder.get_object('dot_color_unfocused_' + i + '_colorbutton').set_rgba(rgba); - } - }); - - this._builder.get_object('focus_highlight_color_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('focus-highlight-color', hexString); - }); - - this._builder.get_object('dot_style_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_dots_options'); - - let dialog = this._createPreferencesDialog(_('Running Indicator Options'), box, () => - { - // restore default settings - this._settings.set_value('dot-color-dominant', this._settings.get_default_value('dot-color-dominant')); - this._settings.set_value('dot-color-override', this._settings.get_default_value('dot-color-override')); - this._settings.set_value('dot-color-unfocused-different', this._settings.get_default_value('dot-color-unfocused-different')); - - this._settings.set_value('focus-highlight-color', this._settings.get_default_value('focus-highlight-color')); - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('focus-highlight-color')); - this._builder.get_object('focus_highlight_color_colorbutton').set_rgba(rgba); - - this._settings.set_value('focus-highlight-opacity', this._settings.get_default_value('focus-highlight-opacity')); - this._builder.get_object('focus_highlight_opacity_spinbutton').set_value(this._settings.get_int('focus-highlight-opacity')); - - for (let i = 1; i <= MAX_WINDOW_INDICATOR; i++) { - this._settings.set_value('dot-color-' + i, this._settings.get_default_value('dot-color-' + i)); - rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('dot-color-' + i)); - this._builder.get_object('dot_color_' + i + '_colorbutton').set_rgba(rgba); - - this._settings.set_value('dot-color-unfocused-' + i, this._settings.get_default_value('dot-color-unfocused-' + i)); - rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('dot-color-unfocused-' + i)); - this._builder.get_object('dot_color_unfocused_' + i + '_colorbutton').set_rgba(rgba); - } - - this._settings.set_value('dot-size', this._settings.get_default_value('dot-size')); - this._builder.get_object('dot_size_spinbutton').set_value(this._settings.get_int('dot-size')); - - this._settings.set_value('focus-highlight', this._settings.get_default_value('focus-highlight')); - this._settings.set_value('focus-highlight-dominant', this._settings.get_default_value('focus-highlight-dominant')); - }); - - this._settings.bind('dot-color-dominant', - this._builder.get_object('dot_color_dominant_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('dot-color-override', - this._builder.get_object('dot_color_override_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - // when either becomes active, turn the other off - this._builder.get_object('dot_color_dominant_switch').connect('state-set', (widget) => { - if (widget.get_active()) this._settings.set_boolean('dot-color-override', false); - }); - this._builder.get_object('dot_color_override_switch').connect('state-set', (widget) => { - if (widget.get_active()) this._settings.set_boolean('dot-color-dominant', false); - else this._settings.set_boolean('dot-color-unfocused-different', false); - }); - - this._settings.bind('dot-color-unfocused-different', - this._builder.get_object('dot_color_unfocused_different_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('dot-color-override', - this._builder.get_object('grid_dot_color'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('dot-color-override', - this._builder.get_object('dot_color_unfocused_box'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('dot-color-unfocused-different', - this._builder.get_object('grid_dot_color_unfocused'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - for (let i = 1; i <= MAX_WINDOW_INDICATOR; i++) { - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('dot-color-' + i)); - this._builder.get_object('dot_color_' + i + '_colorbutton').set_rgba(rgba); - - rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('dot-color-unfocused-' + i)); - this._builder.get_object('dot_color_unfocused_' + i + '_colorbutton').set_rgba(rgba); - } - - this._settings.bind('focus-highlight', - this._builder.get_object('focus_highlight_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('focus-highlight', - this._builder.get_object('grid_focus_highlight_options'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('focus-highlight-dominant', - this._builder.get_object('focus_highlight_dominant_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('focus-highlight-dominant', - this._builder.get_object('focus_highlight_color_label'), - 'sensitive', - Gio.SettingsBindFlags.INVERT_BOOLEAN); - - this._settings.bind('focus-highlight-dominant', - this._builder.get_object('focus_highlight_color_colorbutton'), - 'sensitive', - Gio.SettingsBindFlags.INVERT_BOOLEAN); - - - (function() { - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('focus-highlight-color')); - this._builder.get_object('focus_highlight_color_colorbutton').set_rgba(rgba); - }).apply(this); - - this._builder.get_object('focus_highlight_opacity_spinbutton').set_value(this._settings.get_int('focus-highlight-opacity')); - this._builder.get_object('focus_highlight_opacity_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('focus-highlight-opacity', widget.get_value()); - }); - - this._builder.get_object('dot_size_spinbutton').set_value(this._settings.get_int('dot-size')); - this._builder.get_object('dot_size_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('dot-size', widget.get_value()); - }); - - dialog.show(); - dialog.set_default_size(1, 1); - - }); - - //multi-monitor - this.monitors = this._settings.get_value('available-monitors').deep_unpack(); - - let dtpPrimaryMonitorIndex = this.monitors.indexOf(this._settings.get_int('primary-monitor')); - - if (dtpPrimaryMonitorIndex < 0) { - dtpPrimaryMonitorIndex = 0; - } - - this._currentMonitorIndex = this.monitors[dtpPrimaryMonitorIndex]; - - this._settings.connect('changed::panel-positions', () => this._updateVerticalRelatedOptions()); - this._updateVerticalRelatedOptions(); - - for (let i = 0; i < this.monitors.length; ++i) { - //the gnome-shell primary index is the first one in the "available-monitors" setting - let label = !i ? _('Primary monitor') : _('Monitor ') + (i + 1); - - this._builder.get_object('multimon_primary_combo').append_text(label); - this._builder.get_object('taskbar_position_monitor_combo').append_text(label); - } - - this._builder.get_object('multimon_primary_combo').set_active(dtpPrimaryMonitorIndex); - this._builder.get_object('taskbar_position_monitor_combo').set_active(dtpPrimaryMonitorIndex); - - this._settings.bind('panel-element-positions-monitors-sync', - this._builder.get_object('taskbar_position_sync_button'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('panel-element-positions-monitors-sync', - this._builder.get_object('taskbar_position_monitor_combo'), - 'sensitive', - Gio.SettingsBindFlags.INVERT_BOOLEAN); - - this._settings.connect('changed::panel-element-positions-monitors-sync', () => { - this._maybeDisableTopPosition(); - // The anchor combo box may has different labels for single- or all-monitor configuration. - this._setAnchorLabels(this._currentMonitorIndex); - }); - - this._builder.get_object('multimon_primary_combo').connect('changed', (widget) => { - this._settings.set_int('primary-monitor', this.monitors[widget.get_active()]); - }); - - this._builder.get_object('taskbar_position_monitor_combo').connect('changed', (widget) => { - this._currentMonitorIndex = this.monitors[widget.get_active()]; - this._updateWidgetSettingsForMonitor(this._currentMonitorIndex); - }); - - this._settings.bind('multi-monitors', - this._builder.get_object('multimon_multi_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - if (this.monitors.length === 1) { - this._builder.get_object('multimon_multi_switch').set_sensitive(false); - } - - const panel_length_scale = this._builder.get_object('panel_length_scale'); - panel_length_scale.connect('value-changed', (widget) => { - const value = widget.get_value(); - const monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this.monitors : [this._currentMonitorIndex]; - monitorsToSetFor.forEach(monitorIndex => { - PanelSettings.setPanelLength(this._settings, monitorIndex, value); - }); - - this._setAnchorWidgetSensitivity(value); - }); - - this._builder.get_object('panel_anchor_combo').connect('changed', (widget) => { - const value = widget.get_active_id(); - // Value can be null while anchor labels are being swapped out - if (value !== null) { - const monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this.monitors : [this._currentMonitorIndex]; - monitorsToSetFor.forEach(monitorIndex => { - PanelSettings.setPanelAnchor(this._settings, monitorIndex, value); - }); - } - }); - - this._updateWidgetSettingsForMonitor(this._currentMonitorIndex); - - //dynamic opacity - this._settings.bind('trans-use-custom-bg', - this._builder.get_object('trans_bg_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-custom-bg', - this._builder.get_object('trans_bg_color_colorbutton'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('trans-bg-color')); - this._builder.get_object('trans_bg_color_colorbutton').set_rgba(rgba); - - this._builder.get_object('trans_bg_color_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('trans-bg-color', hexString); - }); - - this._settings.bind('trans-use-custom-opacity', - this._builder.get_object('trans_opacity_override_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-custom-opacity', - this._builder.get_object('trans_opacity_box'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-custom-opacity', - this._builder.get_object('trans_opacity_box2'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('trans_opacity_override_switch').connect('notify::active', (widget) => { - if (!widget.get_active()) - this._builder.get_object('trans_dyn_switch').set_active(false); - }); - - this._builder.get_object('trans_opacity_spinbutton').set_value(this._settings.get_double('trans-panel-opacity') * 100); - this._builder.get_object('trans_opacity_spinbutton').connect('value-changed', (widget) => { - this._settings.set_double('trans-panel-opacity', widget.get_value() * 0.01); - }); - - this._settings.bind('trans-use-dynamic-opacity', - this._builder.get_object('trans_dyn_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-dynamic-opacity', - this._builder.get_object('trans_dyn_options_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-dynamic-behavior', - this._builder.get_object('trans_options_window_type_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-custom-gradient', - this._builder.get_object('trans_gradient_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-custom-gradient', - this._builder.get_object('trans_gradient_box'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-custom-gradient', - this._builder.get_object('trans_gradient_box2'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - rgba.parse(this._settings.get_string('trans-gradient-top-color')); - this._builder.get_object('trans_gradient_color1_colorbutton').set_rgba(rgba); - - this._builder.get_object('trans_gradient_color1_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('trans-gradient-top-color', hexString); - }); - - this._builder.get_object('trans_gradient_color1_spinbutton').set_value(this._settings.get_double('trans-gradient-top-opacity') * 100); - this._builder.get_object('trans_gradient_color1_spinbutton').connect('value-changed', (widget) => { - this._settings.set_double('trans-gradient-top-opacity', widget.get_value() * 0.01); - }); - - rgba.parse(this._settings.get_string('trans-gradient-bottom-color')); - this._builder.get_object('trans_gradient_color2_colorbutton').set_rgba(rgba); - - this._builder.get_object('trans_gradient_color2_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('trans-gradient-bottom-color', hexString); - }); - - this._builder.get_object('trans_gradient_color2_spinbutton').set_value(this._settings.get_double('trans-gradient-bottom-opacity') * 100); - this._builder.get_object('trans_gradient_color2_spinbutton').connect('value-changed', (widget) => { - this._settings.set_double('trans-gradient-bottom-opacity', widget.get_value() * 0.01); - }); - - this._builder.get_object('trans_options_distance_spinbutton').set_value(this._settings.get_int('trans-dynamic-distance')); - this._builder.get_object('trans_options_distance_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('trans-dynamic-distance', widget.get_value()); - }); - - this._builder.get_object('trans_options_min_opacity_spinbutton').set_value(this._settings.get_double('trans-dynamic-anim-target') * 100); - this._builder.get_object('trans_options_min_opacity_spinbutton').connect('value-changed', (widget) => { - this._settings.set_double('trans-dynamic-anim-target', widget.get_value() * 0.01); - }); - - this._builder.get_object('trans_options_anim_time_spinbutton').set_value(this._settings.get_int('trans-dynamic-anim-time')); - this._builder.get_object('trans_options_anim_time_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('trans-dynamic-anim-time', widget.get_value()); - }); - - this._builder.get_object('trans_dyn_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_dynamic_opacity_options'); - - let dialog = this._createPreferencesDialog(_('Dynamic opacity options'), box, () => - { - // restore default settings - this._settings.set_value('trans-dynamic-behavior', this._settings.get_default_value('trans-dynamic-behavior')); - - this._settings.set_value('trans-dynamic-distance', this._settings.get_default_value('trans-dynamic-distance')); - this._builder.get_object('trans_options_distance_spinbutton').set_value(this._settings.get_int('trans-dynamic-distance')); - - this._settings.set_value('trans-dynamic-anim-target', this._settings.get_default_value('trans-dynamic-anim-target')); - this._builder.get_object('trans_options_min_opacity_spinbutton').set_value(this._settings.get_double('trans-dynamic-anim-target') * 100); - - this._settings.set_value('trans-dynamic-anim-time', this._settings.get_default_value('trans-dynamic-anim-time')); - this._builder.get_object('trans_options_anim_time_spinbutton').set_value(this._settings.get_int('trans-dynamic-anim-time')); - }); - - dialog.show(); - dialog.set_default_size(1, 1); - - }); - - this._settings.bind('desktop-line-use-custom-color', - this._builder.get_object('override_show_desktop_line_color_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('desktop-line-use-custom-color', - this._builder.get_object('override_show_desktop_line_color_colorbutton'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - rgba.parse(this._settings.get_string('desktop-line-custom-color')); - this._builder.get_object('override_show_desktop_line_color_colorbutton').set_rgba(rgba); - this._builder.get_object('override_show_desktop_line_color_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - this._settings.set_string('desktop-line-custom-color', css); - }); - - - this._settings.bind('intellihide', - this._builder.get_object('intellihide_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide', - this._builder.get_object('intellihide_options_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-hide-from-windows', - this._builder.get_object('intellihide_window_hide_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-hide-from-windows', - this._builder.get_object('intellihide_behaviour_options'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-behaviour', - this._builder.get_object('intellihide_behaviour_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-use-pressure', - this._builder.get_object('intellihide_use_pressure_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-use-pressure', - this._builder.get_object('intellihide_use_pressure_options'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-use-pressure', - this._builder.get_object('intellihide_use_pressure_options2'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-show-in-fullscreen', - this._builder.get_object('intellihide_show_in_fullscreen_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-only-secondary', - this._builder.get_object('intellihide_only_secondary_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('multi-monitors', - this._builder.get_object('grid_intellihide_only_secondary'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('multimon_multi_switch').connect('notify::active', (widget) => { - if (!widget.get_active()) - this._builder.get_object('intellihide_only_secondary_switch').set_active(false); - }); - - this._builder.get_object('intellihide_pressure_threshold_spinbutton').set_value(this._settings.get_int('intellihide-pressure-threshold')); - this._builder.get_object('intellihide_pressure_threshold_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('intellihide-pressure-threshold', widget.get_value()); - }); - - this._builder.get_object('intellihide_pressure_time_spinbutton').set_value(this._settings.get_int('intellihide-pressure-time')); - this._builder.get_object('intellihide_pressure_time_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('intellihide-pressure-time', widget.get_value()); - }); - - this._settings.bind('intellihide-key-toggle-text', - this._builder.get_object('intellihide_toggle_entry'), - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.connect('changed::intellihide-key-toggle-text', () => setShortcut(this._settings, 'intellihide-key-toggle')); - - this._builder.get_object('intellihide_animation_time_spinbutton').set_value(this._settings.get_int('intellihide-animation-time')); - this._builder.get_object('intellihide_animation_time_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('intellihide-animation-time', widget.get_value()); - }); - - this._builder.get_object('intellihide_close_delay_spinbutton').set_value(this._settings.get_int('intellihide-close-delay')); - this._builder.get_object('intellihide_close_delay_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('intellihide-close-delay', widget.get_value()); - }); - - this._builder.get_object('intellihide_enable_start_delay_spinbutton').set_value(this._settings.get_int('intellihide-enable-start-delay')); - this._builder.get_object('intellihide_enable_start_delay_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('intellihide-enable-start-delay', widget.get_value()); - }); - - this._builder.get_object('intellihide_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_intellihide_options'); - - let dialog = this._createPreferencesDialog(_('Intellihide options'), box, () => - { - // restore default settings - this._settings.set_value('intellihide-hide-from-windows', this._settings.get_default_value('intellihide-hide-from-windows')); - this._settings.set_value('intellihide-behaviour', this._settings.get_default_value('intellihide-behaviour')); - this._settings.set_value('intellihide-use-pressure', this._settings.get_default_value('intellihide-use-pressure')); - this._settings.set_value('intellihide-show-in-fullscreen', this._settings.get_default_value('intellihide-show-in-fullscreen')); - this._settings.set_value('intellihide-only-secondary', this._settings.get_default_value('intellihide-only-secondary')); - - this._settings.set_value('intellihide-pressure-threshold', this._settings.get_default_value('intellihide-pressure-threshold')); - this._builder.get_object('intellihide_pressure_threshold_spinbutton').set_value(this._settings.get_int('intellihide-pressure-threshold')); - - this._settings.set_value('intellihide-pressure-time', this._settings.get_default_value('intellihide-pressure-time')); - this._builder.get_object('intellihide_pressure_time_spinbutton').set_value(this._settings.get_int('intellihide-pressure-time')); - - this._settings.set_value('intellihide-key-toggle-text', this._settings.get_default_value('intellihide-key-toggle-text')); - - this._settings.set_value('intellihide-animation-time', this._settings.get_default_value('intellihide-animation-time')); - this._builder.get_object('intellihide_animation_time_spinbutton').set_value(this._settings.get_int('intellihide-animation-time')); - - this._settings.set_value('intellihide-close-delay', this._settings.get_default_value('intellihide-close-delay')); - this._builder.get_object('intellihide_close_delay_spinbutton').set_value(this._settings.get_int('intellihide-close-delay')); - - this._settings.set_value('intellihide-enable-start-delay', this._settings.get_default_value('intellihide-enable-start-delay')); - this._builder.get_object('intellihide_enable_start_delay_spinbutton').set_value(this._settings.get_int('intellihide-enable-start-delay')); - }); - - dialog.show(); - dialog.set_default_size(1, 1); - - }); - - // Behavior panel - - this._builder.get_object('show_applications_side_padding_spinbutton').set_value(this._settings.get_int('show-apps-icon-side-padding')); - this._builder.get_object('show_applications_side_padding_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('show-apps-icon-side-padding', widget.get_value()); - }); - - this._settings.bind('show-apps-override-escape', - this._builder.get_object('show_applications_esc_key_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-showdesktop-hover', - this._builder.get_object('show_showdesktop_hide_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-showdesktop-hover', - this._builder.get_object('grid_show_showdesktop_hide_options'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-showdesktop-hover', - this._builder.get_object('grid_show_showdesktop_hide_options2'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-window-previews', - this._builder.get_object('show_window_previews_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-window-previews', - this._builder.get_object('show_window_previews_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-tooltip', - this._builder.get_object('show_tooltip_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-favorites', - this._builder.get_object('show_favorite_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-favorites-all-monitors', - this._builder.get_object('multimon_multi_show_favorites_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-favorites', - this._builder.get_object('multimon_multi_show_favorites_switch'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-running-apps', - this._builder.get_object('show_runnning_apps_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._setPreviewTitlePosition(); - - this._builder.get_object('grid_preview_title_font_color_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('window-preview-title-font-color', hexString); - }); - - this._builder.get_object('show_window_previews_button').connect('clicked', () => { - let scrolledWindow = this._builder.get_object('box_window_preview_options'); - - let dialog = this._createPreferencesDialog(_('Window preview options'), scrolledWindow, () => - { - // restore default settings - this._settings.set_value('show-window-previews-timeout', this._settings.get_default_value('show-window-previews-timeout')); - this._builder.get_object('preview_timeout_spinbutton').set_value(this._settings.get_int('show-window-previews-timeout')); - - this._settings.set_value('leave-timeout', this._settings.get_default_value('leave-timeout')); - this._builder.get_object('leave_timeout_spinbutton').set_value(this._settings.get_int('leave-timeout')); - - this._settings.set_value('window-preview-hide-immediate-click', this._settings.get_default_value('window-preview-hide-immediate-click')); - - this._settings.set_value('window-preview-animation-time', this._settings.get_default_value('window-preview-animation-time')); - this._builder.get_object('animation_time_spinbutton').set_value(this._settings.get_int('window-preview-animation-time')); - - this._settings.set_value('preview-use-custom-opacity', this._settings.get_default_value('preview-use-custom-opacity')); - - this._settings.set_value('window-preview-use-custom-icon-size', this._settings.get_default_value('window-preview-use-custom-icon-size')); - - this._settings.set_value('preview-custom-opacity', this._settings.get_default_value('preview-custom-opacity')); - this._builder.get_object('preview_custom_opacity_spinbutton').set_value(this._settings.get_int('preview-custom-opacity')); - - this._settings.set_value('window-preview-title-position', this._settings.get_default_value('window-preview-title-position')); - this._setPreviewTitlePosition(); - - this._settings.set_value('peek-mode', this._settings.get_default_value('peek-mode')); - this._settings.set_value('window-preview-show-title', this._settings.get_default_value('window-preview-show-title')); - this._settings.set_value('enter-peek-mode-timeout', this._settings.get_default_value('enter-peek-mode-timeout')); - this._builder.get_object('enter_peek_mode_timeout_spinbutton').set_value(this._settings.get_int('enter-peek-mode-timeout')); - this._settings.set_value('peek-mode-opacity', this._settings.get_default_value('peek-mode-opacity')); - this._builder.get_object('peek_mode_opacity_spinbutton').set_value(this._settings.get_int('peek-mode-opacity')); - - this._settings.set_value('window-preview-size', this._settings.get_default_value('window-preview-size')); - this._builder.get_object('preview_size_spinbutton').set_value(this._settings.get_int('window-preview-size')); - - this._settings.set_value('window-preview-fixed-x', this._settings.get_default_value('window-preview-fixed-x')); - this._settings.set_value('window-preview-fixed-y', this._settings.get_default_value('window-preview-fixed-y')); - - this._settings.set_value('window-preview-aspect-ratio-x', this._settings.get_default_value('window-preview-aspect-ratio-x')); - this._builder.get_object('preview_aspect_ratio_x_combo').set_active_id(this._settings.get_int('window-preview-aspect-ratio-x').toString()); - - this._settings.set_value('window-preview-aspect-ratio-y', this._settings.get_default_value('window-preview-aspect-ratio-y')); - this._builder.get_object('preview_aspect_ratio_y_combo').set_active_id(this._settings.get_int('window-preview-aspect-ratio-y').toString()); - - this._settings.set_value('window-preview-padding', this._settings.get_default_value('window-preview-padding')); - this._builder.get_object('preview_padding_spinbutton').set_value(this._settings.get_int('window-preview-padding')); - - this._settings.set_value('preview-middle-click-close', this._settings.get_default_value('preview-middle-click-close')); - - this._settings.set_value('window-preview-title-font-size', this._settings.get_default_value('window-preview-title-font-size')); - this._builder.get_object('preview_title_size_spinbutton').set_value(this._settings.get_int('window-preview-title-font-size')); - - this._settings.set_value('window-preview-custom-icon-size', this._settings.get_default_value('window-preview-custom-icon-size')); - this._builder.get_object('preview_custom_icon_size_spinbutton').set_value(this._settings.get_int('window-preview-custom-icon-size')); - - this._settings.set_value('window-preview-title-font-weight', this._settings.get_default_value('window-preview-title-font-weight')); - this._builder.get_object('grid_preview_title_weight_combo').set_active_id(this._settings.get_string('window-preview-title-font-weight')); - - this._settings.set_value('window-preview-title-font-color', this._settings.get_default_value('window-preview-title-font-color')); - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('window-preview-title-font-color')); - this._builder.get_object('grid_preview_title_font_color_colorbutton').set_rgba(rgba); - }); - - this._builder.get_object('preview_timeout_spinbutton').set_value(this._settings.get_int('show-window-previews-timeout')); - this._builder.get_object('preview_timeout_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('show-window-previews-timeout', widget.get_value()); - }); - - this._settings.bind('preview-middle-click-close', - this._builder.get_object('preview_middle_click_close_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('window-preview-fixed-x', - this._builder.get_object('preview_aspect_ratio_x_fixed_togglebutton'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('window-preview-fixed-y', - this._builder.get_object('preview_aspect_ratio_y_fixed_togglebutton'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('preview-use-custom-opacity', - this._builder.get_object('preview_custom_opacity_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('preview-use-custom-opacity', - this._builder.get_object('preview_custom_opacity_spinbutton'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('window-preview-use-custom-icon-size', - this._builder.get_object('preview_custom_icon_size_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('window-preview-use-custom-icon-size', - this._builder.get_object('preview_custom_icon_size_spinbutton'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('preview_custom_opacity_spinbutton').set_value(this._settings.get_int('preview-custom-opacity')); - this._builder.get_object('preview_custom_opacity_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('preview-custom-opacity', widget.get_value()); - }); - - this._settings.bind('peek-mode', - this._builder.get_object('peek_mode_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('peek-mode', - this._builder.get_object('grid_enter_peek_mode_timeout'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('peek-mode', - this._builder.get_object('grid_peek_mode_opacity'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('window-preview-show-title', - this._builder.get_object('preview_show_title_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('window-preview-show-title', - this._builder.get_object('grid_preview_custom_icon_size'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('window-preview-show-title', - this._builder.get_object('grid_preview_title_size'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('window-preview-show-title', - this._builder.get_object('grid_preview_title_weight'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('window-preview-show-title', - this._builder.get_object('grid_preview_title_font_color'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('enter_peek_mode_timeout_spinbutton').set_value(this._settings.get_int('enter-peek-mode-timeout')); - this._builder.get_object('enter_peek_mode_timeout_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('enter-peek-mode-timeout', widget.get_value()); - }); - - this._builder.get_object('leave_timeout_spinbutton').set_value(this._settings.get_int('leave-timeout')); - this._builder.get_object('leave_timeout_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('leave-timeout', widget.get_value()); - }); - - this._settings.bind('window-preview-hide-immediate-click', - this._builder.get_object('preview_immediate_click_button'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('animation_time_spinbutton').set_value(this._settings.get_int('window-preview-animation-time')); - this._builder.get_object('animation_time_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('window-preview-animation-time', widget.get_value()); - }); - - this._builder.get_object('peek_mode_opacity_spinbutton').set_value(this._settings.get_int('peek-mode-opacity')); - this._builder.get_object('peek_mode_opacity_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('peek-mode-opacity', widget.get_value()); - }); - - this._builder.get_object('preview_size_spinbutton').set_value(this._settings.get_int('window-preview-size')); - this._builder.get_object('preview_size_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('window-preview-size', widget.get_value()); - }); - - this._builder.get_object('preview_aspect_ratio_x_combo').set_active_id(this._settings.get_int('window-preview-aspect-ratio-x').toString()); - this._builder.get_object('preview_aspect_ratio_x_combo').connect('changed', (widget) => { - this._settings.set_int('window-preview-aspect-ratio-x', parseInt(widget.get_active_id(), 10)); - }); - - this._builder.get_object('preview_aspect_ratio_y_combo').set_active_id(this._settings.get_int('window-preview-aspect-ratio-y').toString()); - this._builder.get_object('preview_aspect_ratio_y_combo').connect('changed', (widget) => { - this._settings.set_int('window-preview-aspect-ratio-y', parseInt(widget.get_active_id(), 10)); - }); - - this._builder.get_object('preview_padding_spinbutton').set_value(this._settings.get_int('window-preview-padding')); - this._builder.get_object('preview_padding_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('window-preview-padding', widget.get_value()); - }); - - this._builder.get_object('preview_title_size_spinbutton').set_value(this._settings.get_int('window-preview-title-font-size')); - this._builder.get_object('preview_title_size_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('window-preview-title-font-size', widget.get_value()); - }); - - this._builder.get_object('preview_custom_icon_size_spinbutton').set_value(this._settings.get_int('window-preview-custom-icon-size')); - this._builder.get_object('preview_custom_icon_size_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('window-preview-custom-icon-size', widget.get_value()); - }); - - this._builder.get_object('grid_preview_title_weight_combo').set_active_id(this._settings.get_string('window-preview-title-font-weight')); - this._builder.get_object('grid_preview_title_weight_combo').connect('changed', (widget) => { - this._settings.set_string('window-preview-title-font-weight', widget.get_active_id()); - }); - - (function() { - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('window-preview-title-font-color')); - this._builder.get_object('grid_preview_title_font_color_colorbutton').set_rgba(rgba); - }).apply(this); - - dialog.show(); - - }); - - this._settings.bind('isolate-workspaces', - this._builder.get_object('isolate_workspaces_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('isolate-monitors', - this._builder.get_object('multimon_multi_isolate_monitor_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('overview-click-to-exit', - this._builder.get_object('clicktoexit_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('hide-overview-on-startup', - this._builder.get_object('hide_overview_on_startup_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('group-apps', - this._builder.get_object('group_apps_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN); - - this._settings.bind('group-apps', - this._builder.get_object('show_group_apps_options_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN); - - this._settings.bind('progress-show-count', - this._builder.get_object('show_notification_badge_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('group_apps_label_font_color_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('group-apps-label-font-color', hexString); - }); - - this._builder.get_object('group_apps_label_font_color_minimized_colorbutton').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - let hexString = cssHexString(css); - this._settings.set_string('group-apps-label-font-color-minimized', hexString); - }); - - this._settings.bind('group-apps-use-fixed-width', - this._builder.get_object('group_apps_use_fixed_width_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('group-apps-underline-unfocused', - this._builder.get_object('group_apps_underline_unfocused_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('group-apps-use-launchers', - this._builder.get_object('group_apps_use_launchers_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('show_group_apps_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_group_apps_options'); - - let dialog = this._createPreferencesDialog(_('Ungrouped application options'), box, () => - { - // restore default settings - this._settings.set_value('group-apps-label-font-size', this._settings.get_default_value('group-apps-label-font-size')); - this._builder.get_object('group_apps_label_font_size_spinbutton').set_value(this._settings.get_int('group-apps-label-font-size')); - - this._settings.set_value('group-apps-label-font-weight', this._settings.get_default_value('group-apps-label-font-weight')); - this._builder.get_object('group_apps_label_font_weight_combo').set_active_id(this._settings.get_string('group-apps-label-font-weight')); - - this._settings.set_value('group-apps-label-font-color', this._settings.get_default_value('group-apps-label-font-color')); - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('group-apps-label-font-color')); - this._builder.get_object('group_apps_label_font_color_colorbutton').set_rgba(rgba); - - this._settings.set_value('group-apps-label-font-color-minimized', this._settings.get_default_value('group-apps-label-font-color-minimized')); - let minimizedFontColor = new Gdk.RGBA(); - minimizedFontColor.parse(this._settings.get_string('group-apps-label-font-color-minimized')); - this._builder.get_object('group_apps_label_font_color_minimized_colorbutton').set_rgba(minimizedFontColor); - - this._settings.set_value('group-apps-label-max-width', this._settings.get_default_value('group-apps-label-max-width')); - this._builder.get_object('group_apps_label_max_width_spinbutton').set_value(this._settings.get_int('group-apps-label-max-width')); - - this._settings.set_value('group-apps-use-fixed-width', this._settings.get_default_value('group-apps-use-fixed-width')); - this._settings.set_value('group-apps-underline-unfocused', this._settings.get_default_value('group-apps-underline-unfocused')); - this._settings.set_value('group-apps-use-launchers', this._settings.get_default_value('group-apps-use-launchers')); - }); - - this._builder.get_object('group_apps_label_font_size_spinbutton').set_value(this._settings.get_int('group-apps-label-font-size')); - this._builder.get_object('group_apps_label_font_size_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('group-apps-label-font-size', widget.get_value()); - }); - - this._builder.get_object('group_apps_label_font_weight_combo').set_active_id(this._settings.get_string('group-apps-label-font-weight')); - this._builder.get_object('group_apps_label_font_weight_combo').connect('changed', (widget) => { - this._settings.set_string('group-apps-label-font-weight', widget.get_active_id()); - }); - - (function() { - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('group-apps-label-font-color')); - this._builder.get_object('group_apps_label_font_color_colorbutton').set_rgba(rgba); - }).apply(this); - - (function() { - let rgba = new Gdk.RGBA(); - rgba.parse(this._settings.get_string('group-apps-label-font-color-minimized')); - this._builder.get_object('group_apps_label_font_color_minimized_colorbutton').set_rgba(rgba); - }).apply(this); - - this._builder.get_object('group_apps_label_max_width_spinbutton').set_value(this._settings.get_int('group-apps-label-max-width')); - this._builder.get_object('group_apps_label_max_width_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('group-apps-label-max-width', widget.get_value()); - }); - - dialog.show(); - dialog.set_default_size(600, 1); - - }); - - this._builder.get_object('click_action_combo').set_active_id(this._settings.get_string('click-action')); - this._builder.get_object('click_action_combo').connect('changed', (widget) => { - this._settings.set_string('click-action', widget.get_active_id()); - }); - - this._builder.get_object('shift_click_action_combo').connect('changed', (widget) => { - this._settings.set_string('shift-click-action', widget.get_active_id()); - }); - - this._builder.get_object('middle_click_action_combo').connect('changed', (widget) => { - this._settings.set_string('middle-click-action', widget.get_active_id()); - }); - this._builder.get_object('shift_middle_click_action_combo').connect('changed', (widget) => { - this._settings.set_string('shift-middle-click-action', widget.get_active_id()); - }); - - // Create dialog for middle-click options - this._builder.get_object('middle_click_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_middle_click_options'); - - let dialog = this._createPreferencesDialog(_('Customize middle-click behavior'), box, () => - { - // restore default settings for the relevant keys - let keys = ['shift-click-action', 'middle-click-action', 'shift-middle-click-action']; - keys.forEach(function(val) { - this._settings.set_value(val, this._settings.get_default_value(val)); - }, this); - this._builder.get_object('shift_click_action_combo').set_active_id(this._settings.get_string('shift-click-action')); - this._builder.get_object('middle_click_action_combo').set_active_id(this._settings.get_string('middle-click-action')); - this._builder.get_object('shift_middle_click_action_combo').set_active_id(this._settings.get_string('shift-middle-click-action')); - }); - - this._builder.get_object('shift_click_action_combo').set_active_id(this._settings.get_string('shift-click-action')); - - this._builder.get_object('middle_click_action_combo').set_active_id(this._settings.get_string('middle-click-action')); - - this._builder.get_object('shift_middle_click_action_combo').set_active_id(this._settings.get_string('shift-middle-click-action')); - - this._settings.bind('shift-click-action', - this._builder.get_object('shift_click_action_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('middle-click-action', - this._builder.get_object('middle_click_action_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('shift-middle-click-action', - this._builder.get_object('shift_middle_click_action_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - dialog.show(); - dialog.set_default_size(700, 1); - - }); - - this._builder.get_object('scroll_panel_combo').set_active_id(this._settings.get_string('scroll-panel-action')); - this._builder.get_object('scroll_panel_combo').connect('changed', (widget) => { - this._settings.set_string('scroll-panel-action', widget.get_active_id()); - }); - - this._builder.get_object('scroll_icon_combo').set_active_id(this._settings.get_string('scroll-icon-action')); - this._builder.get_object('scroll_icon_combo').connect('changed', (widget) => { - this._settings.set_string('scroll-icon-action', widget.get_active_id()); - }); - - // Create dialog for panel scroll options - this._builder.get_object('scroll_panel_options_button').connect('clicked', () => { - let box = this._builder.get_object('scroll_panel_options_box'); - - let dialog = this._createPreferencesDialog(_('Customize panel scroll behavior'), box, () => - { - // restore default settings - this._settings.set_value('scroll-panel-delay', this._settings.get_default_value('scroll-panel-delay')); - this._builder.get_object('scroll_panel_options_delay_spinbutton').set_value(this._settings.get_int('scroll-panel-delay')); - - this._settings.set_value('scroll-panel-show-ws-popup', this._settings.get_default_value('scroll-panel-show-ws-popup')); - }); - - this._builder.get_object('scroll_panel_options_delay_spinbutton').set_value(this._settings.get_int('scroll-panel-delay')); - this._builder.get_object('scroll_panel_options_delay_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('scroll-panel-delay', widget.get_value()); - }); - - this._settings.bind('scroll-panel-show-ws-popup', - this._builder.get_object('scroll_panel_options_show_ws_popup_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - dialog.show(); - dialog.set_default_size(640, 1); - - }); - - // Create dialog for icon scroll options - this._builder.get_object('scroll_icon_options_button').connect('clicked', () => { - let box = this._builder.get_object('scroll_icon_options_box'); - - let dialog = this._createPreferencesDialog(_('Customize icon scroll behavior'), box, () => - { - // restore default settings - this._settings.set_value('scroll-icon-delay', this._settings.get_default_value('scroll-icon-delay')); - this._builder.get_object('scroll_icon_options_delay_spinbutton').set_value(this._settings.get_int('scroll-icon-delay')); - }); - - this._builder.get_object('scroll_icon_options_delay_spinbutton').set_value(this._settings.get_int('scroll-icon-delay')); - this._builder.get_object('scroll_icon_options_delay_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('scroll-icon-delay', widget.get_value()); - }); - - dialog.show(); - dialog.set_default_size(640, 1); - - }); - - this._settings.bind('hot-keys', - this._builder.get_object('hot_keys_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('hot-keys', - this._builder.get_object('overlay_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('overlay_combo').connect('changed', (widget) => { - this._settings.set_string('hotkeys-overlay-combo', widget.get_active_id()); - }); - - this._settings.bind('shortcut-previews', - this._builder.get_object('shortcut_preview_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('shortcut_num_keys_combo').set_active_id(this._settings.get_string('shortcut-num-keys')); - this._builder.get_object('shortcut_num_keys_combo').connect('changed', (widget) => { - this._settings.set_string('shortcut-num-keys', widget.get_active_id()); - }); - - this._settings.connect('changed::hotkey-prefix-text', () => {checkHotkeyPrefix(this._settings);}); - - this._builder.get_object('hotkey_prefix_combo').set_active_id(this._settings.get_string('hotkey-prefix-text')); - - this._settings.bind('hotkey-prefix-text', - this._builder.get_object('hotkey_prefix_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('overlay_combo').set_active_id(this._settings.get_string('hotkeys-overlay-combo')); - - this._settings.bind('hotkeys-overlay-combo', - this._builder.get_object('overlay_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('overlay-timeout', - this._builder.get_object('timeout_spinbutton'), - 'value', - Gio.SettingsBindFlags.DEFAULT); - if (this._settings.get_string('hotkeys-overlay-combo') !== 'TEMPORARILY') { - this._builder.get_object('timeout_spinbutton').set_sensitive(false); - } - - this._settings.connect('changed::hotkeys-overlay-combo', () => { - if (this._settings.get_string('hotkeys-overlay-combo') !== 'TEMPORARILY') - this._builder.get_object('timeout_spinbutton').set_sensitive(false); - else - this._builder.get_object('timeout_spinbutton').set_sensitive(true); - }); - - this._settings.bind('shortcut-text', - this._builder.get_object('shortcut_entry'), - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.connect('changed::shortcut-text', () => {setShortcut(this._settings, 'shortcut');}); - - // Create dialog for number overlay options - this._builder.get_object('overlay_button').connect('clicked', () => { - let box = this._builder.get_object('box_overlay_shortcut'); - - let dialog = this._createPreferencesDialog(_('Advanced hotkeys options'), box, () => - { - // restore default settings for the relevant keys - let keys = ['hotkey-prefix-text', 'shortcut-text', 'hotkeys-overlay-combo', 'overlay-timeout', 'shortcut-previews']; - keys.forEach(function(val) { - this._settings.set_value(val, this._settings.get_default_value(val)); - }, this); - }); - - dialog.show(); - dialog.set_default_size(600, 1); - - }); - - // setup dialog for secondary menu options - this._builder.get_object('secondarymenu_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_secondarymenu_options'); - - let dialog = this._createPreferencesDialog(_('Secondary Menu Options'), box, () => - { - // restore default settings - this._settings.set_value('secondarymenu-contains-appmenu', this._settings.get_default_value('secondarymenu-contains-appmenu')); - this._settings.set_value('secondarymenu-contains-showdetails', this._settings.get_default_value('secondarymenu-contains-showdetails')); - }); - - // TODO setting secondarymenu-contains-appmenu is not being used anywhere - this._settings.bind('secondarymenu-contains-appmenu', - this._builder.get_object('secondarymenu_appmenu_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('secondarymenu-contains-showdetails', - this._builder.get_object('secondarymenu_showdetails_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - dialog.show(); - dialog.set_default_size(480, 1); - - }); - - // setup dialog for advanced options - this._builder.get_object('button_advanced_options').connect('clicked', () => { - let box = this._builder.get_object('box_advanced_options'); - - let dialog = this._createPreferencesDialog(_('Advanced Options'), box); - - dialog.show(); - dialog.set_default_size(480, 1); - - }); - - // Fine-tune panel - - let sizeScales = [ - {objectName: 'tray_size_scale', valueName: 'tray-size', range: DEFAULT_FONT_SIZES }, - {objectName: 'leftbox_size_scale', valueName: 'leftbox-size', range: DEFAULT_FONT_SIZES }, - {objectName: 'appicon_margin_scale', valueName: 'appicon-margin', range: DEFAULT_MARGIN_SIZES }, - {objectName: 'appicon_margin_todesktop_scale', valueName: 'appicon-margin-todesktop', range: DEFAULT_MARGIN_SIZES }, - {objectName: 'appicon_margin_toscreenborder_scale', valueName: 'appicon-margin-toscreenborder', range: DEFAULT_MARGIN_SIZES }, - {objectName: 'appicon_padding_scale', valueName: 'appicon-padding', range: DEFAULT_MARGIN_SIZES }, - {objectName: 'tray_padding_scale', valueName: 'tray-padding', range: DEFAULT_PADDING_SIZES }, - {objectName: 'leftbox_padding_scale', valueName: 'leftbox-padding', range: DEFAULT_PADDING_SIZES }, - {objectName: 'statusicon_padding_scale', valueName: 'status-icon-padding', range: DEFAULT_PADDING_SIZES }, - {objectName: 'panel_length_scale', valueName: '', range: LENGTH_MARKS }, - {objectName: 'highlight_appicon_borderradius', valueName: 'highlight-appicon-hover-border-radius', range: [ 16, 12, 8, 4, 2, 0 ] }, - ]; - - for(const idx in sizeScales) { - let size_scale = this._builder.get_object(sizeScales[idx].objectName); - let range = sizeScales[idx].range; - size_scale.set_range(range[range.length - 1], range[0]); - let value; - if (sizeScales[idx].objectName === 'panel_length_scale') { - value = PanelSettings.getPanelLength(this._settings, this._currentMonitorIndex); - } else { - value = this._settings.get_int(sizeScales[idx].valueName); - } - size_scale.set_value(value); - // Add marks from range arrays, omitting the first and last values. - range.slice(1, -1).forEach(function(val) { - size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()); - }); - - // Corrent for rtl languages - if (this._rtl) { - // Flip value position: this is not done automatically - size_scale.set_value_pos(Gtk.PositionType.LEFT); - // I suppose due to a bug, having a more than one mark and one above a value of 100 - // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable - // and then manually inverting it - size_scale.set_flippable(false); - size_scale.set_inverted(true); - } - } - - this._settings.bind('animate-app-switch', - this._builder.get_object('animate_app_switch_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('animate-window-launch', - this._builder.get_object('animate_window_launch_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('animate-appicon-hover', - this._builder.get_object('animate_appicon_hover_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('animate-appicon-hover', - this._builder.get_object('animate_appicon_hover_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - { - this._settings.bind('animate-appicon-hover-animation-type', - this._builder.get_object('animate_appicon_hover_options_type_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - let scales = [ - ['animate_appicon_hover_options_duration_scale', 'animate-appicon-hover-animation-duration', 1], - ['animate_appicon_hover_options_rotation_scale', 'animate-appicon-hover-animation-rotation', 1], - ['animate_appicon_hover_options_travel_scale', 'animate-appicon-hover-animation-travel', 100], - ['animate_appicon_hover_options_zoom_scale', 'animate-appicon-hover-animation-zoom', 100], - ['animate_appicon_hover_options_convexity_scale', 'animate-appicon-hover-animation-convexity', 1], - ['animate_appicon_hover_options_extent_scale', 'animate-appicon-hover-animation-extent', 1], - ]; - - let updateScale = scale => { - let [id, key, factor] = scale; - let type = this._settings.get_string('animate-appicon-hover-animation-type'); - let value = this._settings.get_value(key).deep_unpack()[type]; - let defaultValue = this._settings.get_default_value(key).deep_unpack()[type]; - this._builder.get_object(id).sensitive = defaultValue !== undefined; - this._builder.get_object(id).set_value(value * factor || 0); - this._builder.get_object(id).clear_marks(); - this._builder.get_object(id).add_mark(defaultValue * factor, Gtk.PositionType.TOP, - defaultValue !== undefined ? (defaultValue * factor).toString() : ' '); - }; - - scales.forEach(scale => { - let [id, key, factor] = scale; - this._settings.connect('changed::' + key, () => updateScale(scale)); - this._builder.get_object(id).connect('value-changed', widget => { - let type = this._settings.get_string('animate-appicon-hover-animation-type'); - let variant = this._settings.get_value(key); - let unpacked = variant.deep_unpack(); - if (unpacked[type] != widget.get_value() / factor) { - unpacked[type] = widget.get_value() / factor; - this._settings.set_value(key, new GLib.Variant(variant.get_type_string(), unpacked)); - } - }); - }); - - this._settings.connect('changed::animate-appicon-hover-animation-type', () => scales.forEach(updateScale)); - scales.forEach(updateScale); - } - - this._builder.get_object('animate_appicon_hover_button').connect('clicked', () => { - let box = this._builder.get_object('animate_appicon_hover_options'); - - let dialog = this._createPreferencesDialog(_('App icon animation options'), box, () => - { - // restore default settings - this._settings.set_value('animate-appicon-hover-animation-type', this._settings.get_default_value('animate-appicon-hover-animation-type')); - this._settings.set_value('animate-appicon-hover-animation-duration', this._settings.get_default_value('animate-appicon-hover-animation-duration')); - this._settings.set_value('animate-appicon-hover-animation-rotation', this._settings.get_default_value('animate-appicon-hover-animation-rotation')); - this._settings.set_value('animate-appicon-hover-animation-travel', this._settings.get_default_value('animate-appicon-hover-animation-travel')); - this._settings.set_value('animate-appicon-hover-animation-zoom', this._settings.get_default_value('animate-appicon-hover-animation-zoom')); - this._settings.set_value('animate-appicon-hover-animation-convexity', this._settings.get_default_value('animate-appicon-hover-animation-convexity')); - this._settings.set_value('animate-appicon-hover-animation-extent', this._settings.get_default_value('animate-appicon-hover-animation-extent')); - }); - - dialog.show(); - - }); - - this._settings.bind('highlight-appicon-hover', - this._builder.get_object('highlight_appicon_hover_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('highlight-appicon-hover', - this._builder.get_object('highlight_appicon_hover_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - { - rgba.parse(this._settings.get_string('highlight-appicon-hover-background-color')); - this._builder.get_object('highlight_appicon_color').set_rgba(rgba); - this._builder.get_object('highlight_appicon_color').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - this._settings.set_string('highlight-appicon-hover-background-color', css); - }); - - rgba.parse(this._settings.get_string('highlight-appicon-pressed-background-color')); - this._builder.get_object('pressed_appicon_color').set_rgba(rgba); - this._builder.get_object('pressed_appicon_color').connect('color-set', (button) => { - let rgba = button.get_rgba(); - let css = rgba.to_string(); - this._settings.set_string('highlight-appicon-pressed-background-color', css); - }); - - let scales = [ - ['highlight_appicon_borderradius', 'highlight-appicon-hover-border-radius'], - ]; - - const updateScale = scale => { - let [id, key] = scale; - this._builder.get_object(id).set_value(this._settings.get_int(key)); - }; - scales.forEach(scale => { - updateScale(scale); - let [id, key] = scale; - this._builder.get_object(id).connect('value-changed', widget => { - this._settings.set_int(key, widget.get_value()); - }); - }); - - } - - this._builder.get_object('highlight_appicon_hover_button').connect('clicked', () => { - let box = this._builder.get_object('highlight_appicon_hover_options'); - - let dialog = this._createPreferencesDialog(_('App icon highlight options'), box, () => - { - // restore default settings - this._settings.set_value('highlight-appicon-hover-background-color', this._settings.get_default_value('highlight-appicon-hover-background-color')); - rgba.parse(this._settings.get_string('highlight-appicon-hover-background-color')); - this._builder.get_object('highlight_appicon_color').set_rgba(rgba); - this._settings.set_value('highlight-appicon-pressed-background-color', this._settings.get_default_value('highlight-appicon-pressed-background-color')); - rgba.parse(this._settings.get_string('highlight-appicon-pressed-background-color')); - this._builder.get_object('pressed_appicon_color').set_rgba(rgba); - this._settings.set_value('highlight-appicon-hover-border-radius', this._settings.get_default_value('highlight-appicon-hover-border-radius')); - this._builder.get_object('highlight_appicon_borderradius').set_value(this._settings.get_int('highlight-appicon-hover-border-radius')); - }); - - dialog.show(); - - }); - - this._settings.bind('stockgs-keep-dash', - this._builder.get_object('stockgs_dash_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('stockgs-keep-top-panel', - this._builder.get_object('stockgs_top_panel_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - - - this._settings.connect('changed::stockgs-keep-top-panel', () => this._maybeDisableTopPosition()); - - this._maybeDisableTopPosition(); - - this._settings.bind('stockgs-panelbtn-click-only', - this._builder.get_object('stockgs_panelbtn_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('stockgs-force-hotcorner', - this._builder.get_object('stockgs_hotcorner_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - // About Panel - - this._builder.get_object('extension_version').set_label(this._metadata.version.toString() + (this._metadata.commit ? ' (' + this._metadata.commit + ')' : '')); - - this._builder.get_object('importexport_export_button').connect('clicked', widget => { - this._showFileChooser( - _('Export settings'), - { action: Gtk.FileChooserAction.SAVE }, - "Save", - filename => { - let file = Gio.file_new_for_path(filename); - let raw = file.replace(null, false, Gio.FileCreateFlags.NONE, null); - let out = Gio.BufferedOutputStream.new_sized(raw, 4096); - - out.write_all(GLib.spawn_command_line_sync('dconf dump ' + SCHEMA_PATH)[1], null); - out.close(null); - } - ); - }); - - this._builder.get_object('importexport_import_button').connect('clicked', widget => { - this._showFileChooser( - _('Import settings'), - { action: Gtk.FileChooserAction.OPEN }, - "Open", - filename => { - if (filename && GLib.file_test(filename, GLib.FileTest.EXISTS)) { - let settingsFile = Gio.File.new_for_path(filename); - let [ , pid, stdin, stdout, stderr] = - GLib.spawn_async_with_pipes( - null, - ['dconf', 'load', SCHEMA_PATH], - null, - GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, - null - ); - - stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true }); - GLib.close(stdout); - GLib.close(stderr); - - stdin.splice(settingsFile.read(null), Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | Gio.OutputStreamSpliceFlags.CLOSE_TARGET, null); - } - } - ); - }); - - // Donation panel - - let revealDonateTimeout = 0; - let donationIconSwitch = this._builder.get_object('donation_icon_switch') - let donationRevealer = this._builder.get_object('donation_revealer'); - let hiddenDonateIcon = !!this._settings.get_string('hide-donate-icon-unixtime') - - this._builder.get_object('donation_logo').set_from_file(`${this._path}/img/dash-to-panel-logo-light.svg`) - this._builder.get_object('paypal_logo').set_from_file(`${this._path}/img/paypal.png`) - this._builder.get_object('stripe_logo').set_from_file(`${this._path}/img/stripe.png`) - this._builder.get_object('kofi_logo').set_from_file(`${this._path}/img/kofi.png`) - - donationIconSwitch.set_active(hiddenDonateIcon) - donationRevealer.set_reveal_child(hiddenDonateIcon) - - donationIconSwitch.connect('notify::active', (widget) => - this._settings.set_string('hide-donate-icon-unixtime', widget.get_active() ? Date.now().toString() : '') + } + + positionCombo.append( + Pos.STACKED_TL, + isVertical ? _('Stacked to top') : _('Stacked to left'), + ) + positionCombo.append( + Pos.STACKED_BR, + isVertical ? _('Stacked to bottom') : _('Stacked to right'), + ) + positionCombo.append(Pos.CENTERED, _('Centered')) + positionCombo.append(Pos.CENTERED_MONITOR, _('Monitor Center')) + positionCombo.set_active_id(el.position) + + upBtn.connect('clicked', () => upDownClickHandler(0)) + downBtn.connect('clicked', () => + upDownClickHandler(panelElementPositions.length - 1), + ) + visibleToggleBtn.connect('toggled', () => updateElementsSettings()) + positionCombo.connect('changed', () => updateElementsSettings()) + + upBtn.set_child(upImg) + downBtn.set_child(downImg) + + upDownGrid.attach(upBtn, 0, 0, 1, 1) + upDownGrid.attach(downBtn, 1, 0, 1, 1) + + grid.attach(upDownGrid, 0, 0, 1, 1) + grid.attach( + new Gtk.Label({ label: labels[el.element], xalign: 0, hexpand: true }), + 1, + 0, + 1, + 1, + ) + + if (Pos.optionDialogFunctions[el.element]) { + let cogImg = new Gtk.Image({ icon_name: 'emblem-system-symbolic' }) + let optionsBtn = new Gtk.Button({ tooltip_text: _('More options') }) + + optionsBtn.get_style_context().add_class('circular') + optionsBtn.set_child(cogImg) + grid.attach(optionsBtn, 2, 0, 1, 1) + + optionsBtn.connect('clicked', () => + this[Pos.optionDialogFunctions[el.element]](), ) - - this.notebook.connect('notify::visible-page', () => { - clearTimeout(revealDonateTimeout) - - if (this.notebook.visible_page_name == 'donation' && !donationRevealer.get_reveal_child()) - revealDonateTimeout = setTimeout(() => donationRevealer.set_reveal_child(true), 10000) + } + + grid.attach(visibleToggleBtn, 3, 0, 1, 1) + grid.attach(positionCombo, 4, 0, 1, 1) + + row.id = el.element + row.visibleToggleBtn = visibleToggleBtn + row.positionCombo = positionCombo + + row.set_child(grid) + taskbarListBox.insert(row, -1) + }) + } + + _createPreferencesDialog(title, content, reset_function = null) { + let dialog + + dialog = new Gtk.Dialog({ + title: title, + transient_for: this.notebook.get_root(), + use_header_bar: true, + modal: true, + }) + + // GTK+ leaves positive values for application-defined response ids. + // Use +1 for the reset action + if (reset_function != null) dialog.add_button(_('Reset to defaults'), 1) + + dialog.get_content_area().append(content) + + dialog.connect('response', (dialog, id) => { + if (id == 1) { + // restore default settings + if (reset_function) reset_function() + } else { + // remove the settings content so it doesn't get destroyed; + dialog.get_content_area().remove(content) + dialog.destroy() + } + return + }) + + return dialog + } + + _showShowAppsButtonOptions() { + let box = this._builder.get_object('show_applications_options') + + let dialog = this._createPreferencesDialog( + _('Show Applications options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'show-apps-icon-side-padding', + this._settings.get_default_value('show-apps-icon-side-padding'), + ) + this._builder + .get_object('show_applications_side_padding_spinbutton') + .set_value(this._settings.get_int('show-apps-icon-side-padding')) + this._settings.set_value( + 'show-apps-override-escape', + this._settings.get_default_value('show-apps-override-escape'), + ) + handleIconChange.call(this, null) + }, + ) + + let fileChooserButton = this._builder.get_object( + 'show_applications_icon_file_filebutton', + ) + let fileChooser = new Gtk.FileChooserNative({ + title: _('Open icon'), + transient_for: dialog, + }) + let fileImage = this._builder.get_object( + 'show_applications_current_icon_image', + ) + let fileFilter = new Gtk.FileFilter() + fileFilter.add_pixbuf_formats() + fileChooser.filter = fileFilter + + let handleIconChange = function (newIconPath) { + if (newIconPath && GLib.file_test(newIconPath, GLib.FileTest.EXISTS)) { + let file = Gio.File.new_for_path(newIconPath) + let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale( + file.read(null), + 32, + 32, + true, + null, + ) + + fileImage.set_from_pixbuf(pixbuf) + fileChooser.set_file(file) + fileChooserButton.set_label(newIconPath) + } else { + newIconPath = '' + fileImage.set_from_icon_name('view-app-grid-symbolic') + let picturesFolder = Gio.File.new_for_path( + GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES), + ) + fileChooser.set_file(picturesFolder) + fileChooserButton.set_label('(None)') + } + + this._settings.set_string('show-apps-icon-file', newIconPath || '') + } + + fileChooserButton.connect('clicked', () => { + fileChooser.show() + }) + + fileChooser.connect('response', (widget) => + handleIconChange.call(this, widget.get_file().get_path()), + ) + handleIconChange.call( + this, + this._settings.get_string('show-apps-icon-file'), + ) + + // we have to destroy the fileChooser as well + dialog.connect('response', (dialog, id) => { + if (id != 1) { + fileChooser.destroy() + } + return + }) + + dialog.show() + dialog.set_default_size(1, 1) + } + + _showDesktopButtonOptions() { + let box = this._builder.get_object('box_show_showdesktop_options') + + let dialog = this._createPreferencesDialog( + _('Show Desktop options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'showdesktop-button-width', + this._settings.get_default_value('showdesktop-button-width'), + ) + this._builder + .get_object('show_showdesktop_width_spinbutton') + .set_value(this._settings.get_int('showdesktop-button-width')) + + this._settings.set_value( + 'desktop-line-use-custom-color', + this._settings.get_default_value('desktop-line-use-custom-color'), + ) + + this._settings.set_value( + 'show-showdesktop-hover', + this._settings.get_default_value('show-showdesktop-hover'), + ) + + this._settings.set_value( + 'show-showdesktop-delay', + this._settings.get_default_value('show-showdesktop-delay'), + ) + this._builder + .get_object('show_showdesktop_delay_spinbutton') + .set_value(this._settings.get_int('show-showdesktop-delay')) + + this._settings.set_value( + 'show-showdesktop-time', + this._settings.get_default_value('show-showdesktop-time'), + ) + this._builder + .get_object('show_showdesktop_time_spinbutton') + .set_value(this._settings.get_int('show-showdesktop-time')) + }, + ) + + this._builder + .get_object('show_showdesktop_width_spinbutton') + .set_value(this._settings.get_int('showdesktop-button-width')) + this._builder + .get_object('show_showdesktop_width_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('showdesktop-button-width', widget.get_value()) + }) + + this._builder + .get_object('show_showdesktop_delay_spinbutton') + .set_value(this._settings.get_int('show-showdesktop-delay')) + this._builder + .get_object('show_showdesktop_delay_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('show-showdesktop-delay', widget.get_value()) + }) + + this._builder + .get_object('show_showdesktop_time_spinbutton') + .set_value(this._settings.get_int('show-showdesktop-time')) + this._builder + .get_object('show_showdesktop_time_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('show-showdesktop-time', widget.get_value()) + }) + + dialog.show() + dialog.set_default_size(1, 1) + } + + _addFormatValueCallbacks() { + // position + this._builder + .get_object('panel_size_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + + // style + this._builder + .get_object('appicon_margin_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + this._builder + .get_object('appicon_margin_todesktop_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + this._builder + .get_object('appicon_margin_toscreenborder_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + + this._builder + .get_object('appicon_padding_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + + // fine-tune box1 + this._builder + .get_object('tray_size_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + + this._builder + .get_object('leftbox_size_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + + // fine-tune box2 + this._builder + .get_object('tray_padding_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + + this._builder + .get_object('statusicon_padding_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + + this._builder + .get_object('leftbox_padding_scale') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + + // animate hovering app icons dialog + this._builder + .get_object('animate_appicon_hover_options_duration_scale') + .set_format_value_func((scale, value) => { + return _('%d ms').format(value) + }) + + this._builder + .get_object('animate_appicon_hover_options_rotation_scale') + .set_format_value_func((scale, value) => { + return _('%d °').format(value) + }) + + this._builder + .get_object('animate_appicon_hover_options_travel_scale') + .set_format_value_func((scale, value) => { + return _('%d %%').format(value) + }) + + this._builder + .get_object('animate_appicon_hover_options_zoom_scale') + .set_format_value_func((scale, value) => { + return _('%d %%').format(value) + }) + + this._builder + .get_object('animate_appicon_hover_options_convexity_scale') + .set_format_value_func((scale, value) => { + return _('%.1f').format(value) + }) + + this._builder + .get_object('animate_appicon_hover_options_extent_scale') + .set_format_value_func((scale, value) => { + return ngettext('%d icon', '%d icons', value).format(value) + }) + + // highlight appicon on hover dialog + this._builder + .get_object('highlight_appicon_borderradius') + .set_format_value_func((scale, value) => { + return value + ' px' + }) + } + + _bindSettings() { + // size options + let panel_size_scale = this._builder.get_object('panel_size_scale') + panel_size_scale.set_range( + DEFAULT_PANEL_SIZES[DEFAULT_PANEL_SIZES.length - 1], + DEFAULT_PANEL_SIZES[0], + ) + DEFAULT_PANEL_SIZES.slice(1, -1).forEach(function (val) { + panel_size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()) + }) + + // Correct for rtl languages + if (this._rtl) { + // Flip value position: this is not done automatically + panel_size_scale.set_value_pos(Gtk.PositionType.LEFT) + // I suppose due to a bug, having a more than one mark and one above a value of 100 + // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable + // and then manually inverting it + panel_size_scale.set_flippable(false) + panel_size_scale.set_inverted(true) + } + + // App icon style option + this._builder + .get_object('appicon_style_combo') + .set_active_id(this._settings.get_string('appicon-style')) + this._builder + .get_object('appicon_style_combo') + .connect('changed', (widget) => { + this._settings.set_string('appicon-style', widget.get_active_id()) + }) + + // Dots Position option + let dotPosition = this._settings.get_string('dot-position') + + switch (dotPosition) { + case 'BOTTOM': + this._builder.get_object('dots_bottom_button').set_active(true) + break + case 'TOP': + this._builder.get_object('dots_top_button').set_active(true) + break + case 'LEFT': + this._builder.get_object('dots_left_button').set_active(true) + break + case 'RIGHT': + this._builder.get_object('dots_right_button').set_active(true) + break + } + + this._builder + .get_object('dot_style_focused_combo') + .set_active_id(this._settings.get_string('dot-style-focused')) + this._builder + .get_object('dot_style_focused_combo') + .connect('changed', (widget) => { + this._settings.set_string('dot-style-focused', widget.get_active_id()) + }) + + this._builder + .get_object('dot_style_unfocused_combo') + .set_active_id(this._settings.get_string('dot-style-unfocused')) + this._builder + .get_object('dot_style_unfocused_combo') + .connect('changed', (widget) => { + this._settings.set_string('dot-style-unfocused', widget.get_active_id()) + }) + + for (let i = 1; i <= MAX_WINDOW_INDICATOR; i++) { + let idx = i + this._builder + .get_object('dot_color_' + idx + '_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string('dot-color-' + idx, hexString) + }) + + this._builder + .get_object('dot_color_unfocused_' + idx + '_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string('dot-color-unfocused-' + idx, hexString) }) } - _setPreviewTitlePosition() { - switch (this._settings.get_string('window-preview-title-position')) { - case 'BOTTOM': - this._builder.get_object('preview_title_position_bottom_button').set_active(true); - break; - case 'TOP': - this._builder.get_object('preview_title_position_top_button').set_active(true); - break; + this._builder + .get_object('dot_color_apply_all_button') + .connect('clicked', () => { + for (let i = 2; i <= MAX_WINDOW_INDICATOR; i++) { + this._settings.set_value( + 'dot-color-' + i, + this._settings.get_value('dot-color-1'), + ) + let rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('dot-color-' + i)) + this._builder + .get_object('dot_color_' + i + '_colorbutton') + .set_rgba(rgba) } + }) + + this._builder + .get_object('dot_color_unfocused_apply_all_button') + .connect('clicked', () => { + for (let i = 2; i <= MAX_WINDOW_INDICATOR; i++) { + this._settings.set_value( + 'dot-color-unfocused-' + i, + this._settings.get_value('dot-color-unfocused-1'), + ) + let rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('dot-color-unfocused-' + i)) + this._builder + .get_object('dot_color_unfocused_' + i + '_colorbutton') + .set_rgba(rgba) + } + }) + + this._builder + .get_object('focus_highlight_color_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string('focus-highlight-color', hexString) + }) + + this._builder + .get_object('dot_style_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_dots_options') + + let dialog = this._createPreferencesDialog( + _('Running Indicator Options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'dot-color-dominant', + this._settings.get_default_value('dot-color-dominant'), + ) + this._settings.set_value( + 'dot-color-override', + this._settings.get_default_value('dot-color-override'), + ) + this._settings.set_value( + 'dot-color-unfocused-different', + this._settings.get_default_value('dot-color-unfocused-different'), + ) + + this._settings.set_value( + 'focus-highlight-color', + this._settings.get_default_value('focus-highlight-color'), + ) + let rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('focus-highlight-color')) + this._builder + .get_object('focus_highlight_color_colorbutton') + .set_rgba(rgba) + + this._settings.set_value( + 'focus-highlight-opacity', + this._settings.get_default_value('focus-highlight-opacity'), + ) + this._builder + .get_object('focus_highlight_opacity_spinbutton') + .set_value(this._settings.get_int('focus-highlight-opacity')) + + for (let i = 1; i <= MAX_WINDOW_INDICATOR; i++) { + this._settings.set_value( + 'dot-color-' + i, + this._settings.get_default_value('dot-color-' + i), + ) + rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('dot-color-' + i)) + this._builder + .get_object('dot_color_' + i + '_colorbutton') + .set_rgba(rgba) + + this._settings.set_value( + 'dot-color-unfocused-' + i, + this._settings.get_default_value('dot-color-unfocused-' + i), + ) + rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('dot-color-unfocused-' + i)) + this._builder + .get_object('dot_color_unfocused_' + i + '_colorbutton') + .set_rgba(rgba) + } + + this._settings.set_value( + 'dot-size', + this._settings.get_default_value('dot-size'), + ) + this._builder + .get_object('dot_size_spinbutton') + .set_value(this._settings.get_int('dot-size')) + + this._settings.set_value( + 'focus-highlight', + this._settings.get_default_value('focus-highlight'), + ) + this._settings.set_value( + 'focus-highlight-dominant', + this._settings.get_default_value('focus-highlight-dominant'), + ) + }, + ) + + this._settings.bind( + 'dot-color-dominant', + this._builder.get_object('dot_color_dominant_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'dot-color-override', + this._builder.get_object('dot_color_override_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + // when either becomes active, turn the other off + this._builder + .get_object('dot_color_dominant_switch') + .connect('state-set', (widget) => { + if (widget.get_active()) + this._settings.set_boolean('dot-color-override', false) + }) + this._builder + .get_object('dot_color_override_switch') + .connect('state-set', (widget) => { + if (widget.get_active()) + this._settings.set_boolean('dot-color-dominant', false) + else + this._settings.set_boolean('dot-color-unfocused-different', false) + }) + + this._settings.bind( + 'dot-color-unfocused-different', + this._builder.get_object('dot_color_unfocused_different_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'dot-color-override', + this._builder.get_object('grid_dot_color'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'dot-color-override', + this._builder.get_object('dot_color_unfocused_box'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'dot-color-unfocused-different', + this._builder.get_object('grid_dot_color_unfocused'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + for (let i = 1; i <= MAX_WINDOW_INDICATOR; i++) { + let rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('dot-color-' + i)) + this._builder + .get_object('dot_color_' + i + '_colorbutton') + .set_rgba(rgba) + + rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('dot-color-unfocused-' + i)) + this._builder + .get_object('dot_color_unfocused_' + i + '_colorbutton') + .set_rgba(rgba) + } + + this._settings.bind( + 'focus-highlight', + this._builder.get_object('focus_highlight_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'focus-highlight', + this._builder.get_object('grid_focus_highlight_options'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'focus-highlight-dominant', + this._builder.get_object('focus_highlight_dominant_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'focus-highlight-dominant', + this._builder.get_object('focus_highlight_color_label'), + 'sensitive', + Gio.SettingsBindFlags.INVERT_BOOLEAN, + ) + + this._settings.bind( + 'focus-highlight-dominant', + this._builder.get_object('focus_highlight_color_colorbutton'), + 'sensitive', + Gio.SettingsBindFlags.INVERT_BOOLEAN, + ) + ;(function () { + let rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('focus-highlight-color')) + this._builder + .get_object('focus_highlight_color_colorbutton') + .set_rgba(rgba) + }).apply(this) + + this._builder + .get_object('focus_highlight_opacity_spinbutton') + .set_value(this._settings.get_int('focus-highlight-opacity')) + this._builder + .get_object('focus_highlight_opacity_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'focus-highlight-opacity', + widget.get_value(), + ) + }) + + this._builder + .get_object('dot_size_spinbutton') + .set_value(this._settings.get_int('dot-size')) + this._builder + .get_object('dot_size_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('dot-size', widget.get_value()) + }) + + dialog.show() + dialog.set_default_size(1, 1) + }) + + //multi-monitor + this.monitors = this._settings.get_value('available-monitors').deep_unpack() + + let dtpPrimaryMonitorIndex = this.monitors.indexOf( + this._settings.get_int('primary-monitor'), + ) + + if (dtpPrimaryMonitorIndex < 0) { + dtpPrimaryMonitorIndex = 0 } - _showFileChooser(title, params, acceptBtn, acceptHandler) { - let dialog = new Gtk.FileChooserDialog(mergeObjects({ title: title, transient_for: this.notebook.get_root() }, params)); + this._currentMonitorIndex = this.monitors[dtpPrimaryMonitorIndex] - dialog.add_button("Cancel", Gtk.ResponseType.CANCEL); - dialog.add_button(acceptBtn, Gtk.ResponseType.ACCEPT); + this._settings.connect('changed::panel-positions', () => + this._updateVerticalRelatedOptions(), + ) + this._updateVerticalRelatedOptions() - dialog.show(); + for (let i = 0; i < this.monitors.length; ++i) { + //the gnome-shell primary index is the first one in the "available-monitors" setting + let label = !i ? _('Primary monitor') : _('Monitor ') + (i + 1) - dialog.connect('response', (dialog, id) => { - if (id == Gtk.ResponseType.ACCEPT) - acceptHandler.call(this, dialog.get_file().get_path()); - - dialog.destroy(); - }); + this._builder.get_object('multimon_primary_combo').append_text(label) + this._builder + .get_object('taskbar_position_monitor_combo') + .append_text(label) } + + this._builder + .get_object('multimon_primary_combo') + .set_active(dtpPrimaryMonitorIndex) + this._builder + .get_object('taskbar_position_monitor_combo') + .set_active(dtpPrimaryMonitorIndex) + + this._settings.bind( + 'panel-element-positions-monitors-sync', + this._builder.get_object('taskbar_position_sync_button'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'panel-element-positions-monitors-sync', + this._builder.get_object('taskbar_position_monitor_combo'), + 'sensitive', + Gio.SettingsBindFlags.INVERT_BOOLEAN, + ) + + this._settings.connect( + 'changed::panel-element-positions-monitors-sync', + () => { + this._maybeDisableTopPosition() + // The anchor combo box may has different labels for single- or all-monitor configuration. + this._setAnchorLabels(this._currentMonitorIndex) + }, + ) + + this._builder + .get_object('multimon_primary_combo') + .connect('changed', (widget) => { + this._settings.set_int( + 'primary-monitor', + this.monitors[widget.get_active()], + ) + }) + + this._builder + .get_object('taskbar_position_monitor_combo') + .connect('changed', (widget) => { + this._currentMonitorIndex = this.monitors[widget.get_active()] + this._updateWidgetSettingsForMonitor(this._currentMonitorIndex) + }) + + this._settings.bind( + 'multi-monitors', + this._builder.get_object('multimon_multi_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + if (this.monitors.length === 1) { + this._builder.get_object('multimon_multi_switch').set_sensitive(false) + } + + const panel_length_scale = this._builder.get_object('panel_length_scale') + panel_length_scale.connect('value-changed', (widget) => { + const value = widget.get_value() + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? this.monitors + : [this._currentMonitorIndex] + monitorsToSetFor.forEach((monitorIndex) => { + PanelSettings.setPanelLength(this._settings, monitorIndex, value) + }) + + this._setAnchorWidgetSensitivity(value) + }) + + this._builder + .get_object('panel_anchor_combo') + .connect('changed', (widget) => { + const value = widget.get_active_id() + // Value can be null while anchor labels are being swapped out + if (value !== null) { + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? this.monitors + : [this._currentMonitorIndex] + monitorsToSetFor.forEach((monitorIndex) => { + PanelSettings.setPanelAnchor(this._settings, monitorIndex, value) + }) + } + }) + + this._updateWidgetSettingsForMonitor(this._currentMonitorIndex) + + //dynamic opacity + this._settings.bind( + 'trans-use-custom-bg', + this._builder.get_object('trans_bg_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-custom-bg', + this._builder.get_object('trans_bg_color_colorbutton'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + let rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('trans-bg-color')) + this._builder.get_object('trans_bg_color_colorbutton').set_rgba(rgba) + + this._builder + .get_object('trans_bg_color_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string('trans-bg-color', hexString) + }) + + this._settings.bind( + 'trans-use-custom-opacity', + this._builder.get_object('trans_opacity_override_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-custom-opacity', + this._builder.get_object('trans_opacity_box'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-custom-opacity', + this._builder.get_object('trans_opacity_box2'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('trans_opacity_override_switch') + .connect('notify::active', (widget) => { + if (!widget.get_active()) + this._builder.get_object('trans_dyn_switch').set_active(false) + }) + + this._builder + .get_object('trans_opacity_spinbutton') + .set_value(this._settings.get_double('trans-panel-opacity') * 100) + this._builder + .get_object('trans_opacity_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_double( + 'trans-panel-opacity', + widget.get_value() * 0.01, + ) + }) + + this._settings.bind( + 'trans-use-dynamic-opacity', + this._builder.get_object('trans_dyn_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-dynamic-opacity', + this._builder.get_object('trans_dyn_options_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-dynamic-behavior', + this._builder.get_object('trans_options_window_type_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-custom-gradient', + this._builder.get_object('trans_gradient_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-custom-gradient', + this._builder.get_object('trans_gradient_box'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-custom-gradient', + this._builder.get_object('trans_gradient_box2'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + rgba.parse(this._settings.get_string('trans-gradient-top-color')) + this._builder.get_object('trans_gradient_color1_colorbutton').set_rgba(rgba) + + this._builder + .get_object('trans_gradient_color1_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string('trans-gradient-top-color', hexString) + }) + + this._builder + .get_object('trans_gradient_color1_spinbutton') + .set_value(this._settings.get_double('trans-gradient-top-opacity') * 100) + this._builder + .get_object('trans_gradient_color1_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_double( + 'trans-gradient-top-opacity', + widget.get_value() * 0.01, + ) + }) + + rgba.parse(this._settings.get_string('trans-gradient-bottom-color')) + this._builder.get_object('trans_gradient_color2_colorbutton').set_rgba(rgba) + + this._builder + .get_object('trans_gradient_color2_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string('trans-gradient-bottom-color', hexString) + }) + + this._builder + .get_object('trans_gradient_color2_spinbutton') + .set_value( + this._settings.get_double('trans-gradient-bottom-opacity') * 100, + ) + this._builder + .get_object('trans_gradient_color2_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_double( + 'trans-gradient-bottom-opacity', + widget.get_value() * 0.01, + ) + }) + + this._builder + .get_object('trans_options_distance_spinbutton') + .set_value(this._settings.get_int('trans-dynamic-distance')) + this._builder + .get_object('trans_options_distance_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('trans-dynamic-distance', widget.get_value()) + }) + + this._builder + .get_object('trans_options_min_opacity_spinbutton') + .set_value(this._settings.get_double('trans-dynamic-anim-target') * 100) + this._builder + .get_object('trans_options_min_opacity_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_double( + 'trans-dynamic-anim-target', + widget.get_value() * 0.01, + ) + }) + + this._builder + .get_object('trans_options_anim_time_spinbutton') + .set_value(this._settings.get_int('trans-dynamic-anim-time')) + this._builder + .get_object('trans_options_anim_time_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('trans-dynamic-anim-time', widget.get_value()) + }) + + this._builder + .get_object('trans_dyn_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_dynamic_opacity_options') + + let dialog = this._createPreferencesDialog( + _('Dynamic opacity options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'trans-dynamic-behavior', + this._settings.get_default_value('trans-dynamic-behavior'), + ) + + this._settings.set_value( + 'trans-dynamic-distance', + this._settings.get_default_value('trans-dynamic-distance'), + ) + this._builder + .get_object('trans_options_distance_spinbutton') + .set_value(this._settings.get_int('trans-dynamic-distance')) + + this._settings.set_value( + 'trans-dynamic-anim-target', + this._settings.get_default_value('trans-dynamic-anim-target'), + ) + this._builder + .get_object('trans_options_min_opacity_spinbutton') + .set_value( + this._settings.get_double('trans-dynamic-anim-target') * 100, + ) + + this._settings.set_value( + 'trans-dynamic-anim-time', + this._settings.get_default_value('trans-dynamic-anim-time'), + ) + this._builder + .get_object('trans_options_anim_time_spinbutton') + .set_value(this._settings.get_int('trans-dynamic-anim-time')) + }, + ) + + dialog.show() + dialog.set_default_size(1, 1) + }) + + this._settings.bind( + 'desktop-line-use-custom-color', + this._builder.get_object('override_show_desktop_line_color_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'desktop-line-use-custom-color', + this._builder.get_object('override_show_desktop_line_color_colorbutton'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + rgba.parse(this._settings.get_string('desktop-line-custom-color')) + this._builder + .get_object('override_show_desktop_line_color_colorbutton') + .set_rgba(rgba) + this._builder + .get_object('override_show_desktop_line_color_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + this._settings.set_string('desktop-line-custom-color', css) + }) + + this._settings.bind( + 'intellihide', + this._builder.get_object('intellihide_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide', + this._builder.get_object('intellihide_options_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-hide-from-windows', + this._builder.get_object('intellihide_window_hide_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-hide-from-windows', + this._builder.get_object('intellihide_behaviour_options'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-behaviour', + this._builder.get_object('intellihide_behaviour_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-use-pressure', + this._builder.get_object('intellihide_use_pressure_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-use-pressure', + this._builder.get_object('intellihide_use_pressure_options'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-use-pressure', + this._builder.get_object('intellihide_use_pressure_options2'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-show-in-fullscreen', + this._builder.get_object('intellihide_show_in_fullscreen_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-only-secondary', + this._builder.get_object('intellihide_only_secondary_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'multi-monitors', + this._builder.get_object('grid_intellihide_only_secondary'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('multimon_multi_switch') + .connect('notify::active', (widget) => { + if (!widget.get_active()) + this._builder + .get_object('intellihide_only_secondary_switch') + .set_active(false) + }) + + this._builder + .get_object('intellihide_pressure_threshold_spinbutton') + .set_value(this._settings.get_int('intellihide-pressure-threshold')) + this._builder + .get_object('intellihide_pressure_threshold_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'intellihide-pressure-threshold', + widget.get_value(), + ) + }) + + this._builder + .get_object('intellihide_pressure_time_spinbutton') + .set_value(this._settings.get_int('intellihide-pressure-time')) + this._builder + .get_object('intellihide_pressure_time_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('intellihide-pressure-time', widget.get_value()) + }) + + this._settings.bind( + 'intellihide-key-toggle-text', + this._builder.get_object('intellihide_toggle_entry'), + 'text', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.connect('changed::intellihide-key-toggle-text', () => + setShortcut(this._settings, 'intellihide-key-toggle'), + ) + + this._builder + .get_object('intellihide_animation_time_spinbutton') + .set_value(this._settings.get_int('intellihide-animation-time')) + this._builder + .get_object('intellihide_animation_time_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('intellihide-animation-time', widget.get_value()) + }) + + this._builder + .get_object('intellihide_close_delay_spinbutton') + .set_value(this._settings.get_int('intellihide-close-delay')) + this._builder + .get_object('intellihide_close_delay_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('intellihide-close-delay', widget.get_value()) + }) + + this._builder + .get_object('intellihide_enable_start_delay_spinbutton') + .set_value(this._settings.get_int('intellihide-enable-start-delay')) + this._builder + .get_object('intellihide_enable_start_delay_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'intellihide-enable-start-delay', + widget.get_value(), + ) + }) + + this._builder + .get_object('intellihide_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_intellihide_options') + + let dialog = this._createPreferencesDialog( + _('Intellihide options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'intellihide-hide-from-windows', + this._settings.get_default_value('intellihide-hide-from-windows'), + ) + this._settings.set_value( + 'intellihide-behaviour', + this._settings.get_default_value('intellihide-behaviour'), + ) + this._settings.set_value( + 'intellihide-use-pressure', + this._settings.get_default_value('intellihide-use-pressure'), + ) + this._settings.set_value( + 'intellihide-show-in-fullscreen', + this._settings.get_default_value( + 'intellihide-show-in-fullscreen', + ), + ) + this._settings.set_value( + 'intellihide-only-secondary', + this._settings.get_default_value('intellihide-only-secondary'), + ) + + this._settings.set_value( + 'intellihide-pressure-threshold', + this._settings.get_default_value( + 'intellihide-pressure-threshold', + ), + ) + this._builder + .get_object('intellihide_pressure_threshold_spinbutton') + .set_value( + this._settings.get_int('intellihide-pressure-threshold'), + ) + + this._settings.set_value( + 'intellihide-pressure-time', + this._settings.get_default_value('intellihide-pressure-time'), + ) + this._builder + .get_object('intellihide_pressure_time_spinbutton') + .set_value(this._settings.get_int('intellihide-pressure-time')) + + this._settings.set_value( + 'intellihide-key-toggle-text', + this._settings.get_default_value('intellihide-key-toggle-text'), + ) + + this._settings.set_value( + 'intellihide-animation-time', + this._settings.get_default_value('intellihide-animation-time'), + ) + this._builder + .get_object('intellihide_animation_time_spinbutton') + .set_value(this._settings.get_int('intellihide-animation-time')) + + this._settings.set_value( + 'intellihide-close-delay', + this._settings.get_default_value('intellihide-close-delay'), + ) + this._builder + .get_object('intellihide_close_delay_spinbutton') + .set_value(this._settings.get_int('intellihide-close-delay')) + + this._settings.set_value( + 'intellihide-enable-start-delay', + this._settings.get_default_value( + 'intellihide-enable-start-delay', + ), + ) + this._builder + .get_object('intellihide_enable_start_delay_spinbutton') + .set_value( + this._settings.get_int('intellihide-enable-start-delay'), + ) + }, + ) + + dialog.show() + dialog.set_default_size(1, 1) + }) + + // Behavior panel + + this._builder + .get_object('show_applications_side_padding_spinbutton') + .set_value(this._settings.get_int('show-apps-icon-side-padding')) + this._builder + .get_object('show_applications_side_padding_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'show-apps-icon-side-padding', + widget.get_value(), + ) + }) + + this._settings.bind( + 'show-apps-override-escape', + this._builder.get_object('show_applications_esc_key_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-showdesktop-hover', + this._builder.get_object('show_showdesktop_hide_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-showdesktop-hover', + this._builder.get_object('grid_show_showdesktop_hide_options'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-showdesktop-hover', + this._builder.get_object('grid_show_showdesktop_hide_options2'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-window-previews', + this._builder.get_object('show_window_previews_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-window-previews', + this._builder.get_object('show_window_previews_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-tooltip', + this._builder.get_object('show_tooltip_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-favorites', + this._builder.get_object('show_favorite_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-favorites-all-monitors', + this._builder.get_object('multimon_multi_show_favorites_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-favorites', + this._builder.get_object('multimon_multi_show_favorites_switch'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-running-apps', + this._builder.get_object('show_runnning_apps_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._setPreviewTitlePosition() + + this._builder + .get_object('grid_preview_title_font_color_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string('window-preview-title-font-color', hexString) + }) + + this._builder + .get_object('show_window_previews_button') + .connect('clicked', () => { + let scrolledWindow = this._builder.get_object( + 'box_window_preview_options', + ) + + let dialog = this._createPreferencesDialog( + _('Window preview options'), + scrolledWindow, + () => { + // restore default settings + this._settings.set_value( + 'show-window-previews-timeout', + this._settings.get_default_value('show-window-previews-timeout'), + ) + this._builder + .get_object('preview_timeout_spinbutton') + .set_value(this._settings.get_int('show-window-previews-timeout')) + + this._settings.set_value( + 'leave-timeout', + this._settings.get_default_value('leave-timeout'), + ) + this._builder + .get_object('leave_timeout_spinbutton') + .set_value(this._settings.get_int('leave-timeout')) + + this._settings.set_value( + 'window-preview-hide-immediate-click', + this._settings.get_default_value( + 'window-preview-hide-immediate-click', + ), + ) + + this._settings.set_value( + 'window-preview-animation-time', + this._settings.get_default_value('window-preview-animation-time'), + ) + this._builder + .get_object('animation_time_spinbutton') + .set_value( + this._settings.get_int('window-preview-animation-time'), + ) + + this._settings.set_value( + 'preview-use-custom-opacity', + this._settings.get_default_value('preview-use-custom-opacity'), + ) + + this._settings.set_value( + 'window-preview-use-custom-icon-size', + this._settings.get_default_value( + 'window-preview-use-custom-icon-size', + ), + ) + + this._settings.set_value( + 'preview-custom-opacity', + this._settings.get_default_value('preview-custom-opacity'), + ) + this._builder + .get_object('preview_custom_opacity_spinbutton') + .set_value(this._settings.get_int('preview-custom-opacity')) + + this._settings.set_value( + 'window-preview-title-position', + this._settings.get_default_value('window-preview-title-position'), + ) + this._setPreviewTitlePosition() + + this._settings.set_value( + 'peek-mode', + this._settings.get_default_value('peek-mode'), + ) + this._settings.set_value( + 'window-preview-show-title', + this._settings.get_default_value('window-preview-show-title'), + ) + this._settings.set_value( + 'enter-peek-mode-timeout', + this._settings.get_default_value('enter-peek-mode-timeout'), + ) + this._builder + .get_object('enter_peek_mode_timeout_spinbutton') + .set_value(this._settings.get_int('enter-peek-mode-timeout')) + this._settings.set_value( + 'peek-mode-opacity', + this._settings.get_default_value('peek-mode-opacity'), + ) + this._builder + .get_object('peek_mode_opacity_spinbutton') + .set_value(this._settings.get_int('peek-mode-opacity')) + + this._settings.set_value( + 'window-preview-size', + this._settings.get_default_value('window-preview-size'), + ) + this._builder + .get_object('preview_size_spinbutton') + .set_value(this._settings.get_int('window-preview-size')) + + this._settings.set_value( + 'window-preview-fixed-x', + this._settings.get_default_value('window-preview-fixed-x'), + ) + this._settings.set_value( + 'window-preview-fixed-y', + this._settings.get_default_value('window-preview-fixed-y'), + ) + + this._settings.set_value( + 'window-preview-aspect-ratio-x', + this._settings.get_default_value('window-preview-aspect-ratio-x'), + ) + this._builder + .get_object('preview_aspect_ratio_x_combo') + .set_active_id( + this._settings + .get_int('window-preview-aspect-ratio-x') + .toString(), + ) + + this._settings.set_value( + 'window-preview-aspect-ratio-y', + this._settings.get_default_value('window-preview-aspect-ratio-y'), + ) + this._builder + .get_object('preview_aspect_ratio_y_combo') + .set_active_id( + this._settings + .get_int('window-preview-aspect-ratio-y') + .toString(), + ) + + this._settings.set_value( + 'window-preview-padding', + this._settings.get_default_value('window-preview-padding'), + ) + this._builder + .get_object('preview_padding_spinbutton') + .set_value(this._settings.get_int('window-preview-padding')) + + this._settings.set_value( + 'preview-middle-click-close', + this._settings.get_default_value('preview-middle-click-close'), + ) + + this._settings.set_value( + 'window-preview-title-font-size', + this._settings.get_default_value( + 'window-preview-title-font-size', + ), + ) + this._builder + .get_object('preview_title_size_spinbutton') + .set_value( + this._settings.get_int('window-preview-title-font-size'), + ) + + this._settings.set_value( + 'window-preview-custom-icon-size', + this._settings.get_default_value( + 'window-preview-custom-icon-size', + ), + ) + this._builder + .get_object('preview_custom_icon_size_spinbutton') + .set_value( + this._settings.get_int('window-preview-custom-icon-size'), + ) + + this._settings.set_value( + 'window-preview-title-font-weight', + this._settings.get_default_value( + 'window-preview-title-font-weight', + ), + ) + this._builder + .get_object('grid_preview_title_weight_combo') + .set_active_id( + this._settings.get_string('window-preview-title-font-weight'), + ) + + this._settings.set_value( + 'window-preview-title-font-color', + this._settings.get_default_value( + 'window-preview-title-font-color', + ), + ) + let rgba = new Gdk.RGBA() + rgba.parse( + this._settings.get_string('window-preview-title-font-color'), + ) + this._builder + .get_object('grid_preview_title_font_color_colorbutton') + .set_rgba(rgba) + }, + ) + + this._builder + .get_object('preview_timeout_spinbutton') + .set_value(this._settings.get_int('show-window-previews-timeout')) + this._builder + .get_object('preview_timeout_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'show-window-previews-timeout', + widget.get_value(), + ) + }) + + this._settings.bind( + 'preview-middle-click-close', + this._builder.get_object('preview_middle_click_close_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'window-preview-fixed-x', + this._builder.get_object('preview_aspect_ratio_x_fixed_togglebutton'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'window-preview-fixed-y', + this._builder.get_object('preview_aspect_ratio_y_fixed_togglebutton'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'preview-use-custom-opacity', + this._builder.get_object('preview_custom_opacity_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'preview-use-custom-opacity', + this._builder.get_object('preview_custom_opacity_spinbutton'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'window-preview-use-custom-icon-size', + this._builder.get_object('preview_custom_icon_size_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'window-preview-use-custom-icon-size', + this._builder.get_object('preview_custom_icon_size_spinbutton'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('preview_custom_opacity_spinbutton') + .set_value(this._settings.get_int('preview-custom-opacity')) + this._builder + .get_object('preview_custom_opacity_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('preview-custom-opacity', widget.get_value()) + }) + + this._settings.bind( + 'peek-mode', + this._builder.get_object('peek_mode_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'peek-mode', + this._builder.get_object('grid_enter_peek_mode_timeout'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'peek-mode', + this._builder.get_object('grid_peek_mode_opacity'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'window-preview-show-title', + this._builder.get_object('preview_show_title_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'window-preview-show-title', + this._builder.get_object('grid_preview_custom_icon_size'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'window-preview-show-title', + this._builder.get_object('grid_preview_title_size'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'window-preview-show-title', + this._builder.get_object('grid_preview_title_weight'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'window-preview-show-title', + this._builder.get_object('grid_preview_title_font_color'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('enter_peek_mode_timeout_spinbutton') + .set_value(this._settings.get_int('enter-peek-mode-timeout')) + this._builder + .get_object('enter_peek_mode_timeout_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'enter-peek-mode-timeout', + widget.get_value(), + ) + }) + + this._builder + .get_object('leave_timeout_spinbutton') + .set_value(this._settings.get_int('leave-timeout')) + this._builder + .get_object('leave_timeout_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('leave-timeout', widget.get_value()) + }) + + this._settings.bind( + 'window-preview-hide-immediate-click', + this._builder.get_object('preview_immediate_click_button'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('animation_time_spinbutton') + .set_value(this._settings.get_int('window-preview-animation-time')) + this._builder + .get_object('animation_time_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'window-preview-animation-time', + widget.get_value(), + ) + }) + + this._builder + .get_object('peek_mode_opacity_spinbutton') + .set_value(this._settings.get_int('peek-mode-opacity')) + this._builder + .get_object('peek_mode_opacity_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('peek-mode-opacity', widget.get_value()) + }) + + this._builder + .get_object('preview_size_spinbutton') + .set_value(this._settings.get_int('window-preview-size')) + this._builder + .get_object('preview_size_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('window-preview-size', widget.get_value()) + }) + + this._builder + .get_object('preview_aspect_ratio_x_combo') + .set_active_id( + this._settings.get_int('window-preview-aspect-ratio-x').toString(), + ) + this._builder + .get_object('preview_aspect_ratio_x_combo') + .connect('changed', (widget) => { + this._settings.set_int( + 'window-preview-aspect-ratio-x', + parseInt(widget.get_active_id(), 10), + ) + }) + + this._builder + .get_object('preview_aspect_ratio_y_combo') + .set_active_id( + this._settings.get_int('window-preview-aspect-ratio-y').toString(), + ) + this._builder + .get_object('preview_aspect_ratio_y_combo') + .connect('changed', (widget) => { + this._settings.set_int( + 'window-preview-aspect-ratio-y', + parseInt(widget.get_active_id(), 10), + ) + }) + + this._builder + .get_object('preview_padding_spinbutton') + .set_value(this._settings.get_int('window-preview-padding')) + this._builder + .get_object('preview_padding_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('window-preview-padding', widget.get_value()) + }) + + this._builder + .get_object('preview_title_size_spinbutton') + .set_value(this._settings.get_int('window-preview-title-font-size')) + this._builder + .get_object('preview_title_size_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'window-preview-title-font-size', + widget.get_value(), + ) + }) + + this._builder + .get_object('preview_custom_icon_size_spinbutton') + .set_value(this._settings.get_int('window-preview-custom-icon-size')) + this._builder + .get_object('preview_custom_icon_size_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'window-preview-custom-icon-size', + widget.get_value(), + ) + }) + + this._builder + .get_object('grid_preview_title_weight_combo') + .set_active_id( + this._settings.get_string('window-preview-title-font-weight'), + ) + this._builder + .get_object('grid_preview_title_weight_combo') + .connect('changed', (widget) => { + this._settings.set_string( + 'window-preview-title-font-weight', + widget.get_active_id(), + ) + }) + ;(function () { + let rgba = new Gdk.RGBA() + rgba.parse( + this._settings.get_string('window-preview-title-font-color'), + ) + this._builder + .get_object('grid_preview_title_font_color_colorbutton') + .set_rgba(rgba) + }).apply(this) + + dialog.show() + }) + + this._settings.bind( + 'isolate-workspaces', + this._builder.get_object('isolate_workspaces_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'isolate-monitors', + this._builder.get_object('multimon_multi_isolate_monitor_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'overview-click-to-exit', + this._builder.get_object('clicktoexit_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'hide-overview-on-startup', + this._builder.get_object('hide_overview_on_startup_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'group-apps', + this._builder.get_object('group_apps_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN, + ) + + this._settings.bind( + 'group-apps', + this._builder.get_object('show_group_apps_options_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN, + ) + + this._settings.bind( + 'progress-show-count', + this._builder.get_object('show_notification_badge_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('group_apps_label_font_color_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string('group-apps-label-font-color', hexString) + }) + + this._builder + .get_object('group_apps_label_font_color_minimized_colorbutton') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + let hexString = cssHexString(css) + this._settings.set_string( + 'group-apps-label-font-color-minimized', + hexString, + ) + }) + + this._settings.bind( + 'group-apps-use-fixed-width', + this._builder.get_object('group_apps_use_fixed_width_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'group-apps-underline-unfocused', + this._builder.get_object('group_apps_underline_unfocused_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'group-apps-use-launchers', + this._builder.get_object('group_apps_use_launchers_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('show_group_apps_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_group_apps_options') + + let dialog = this._createPreferencesDialog( + _('Ungrouped application options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'group-apps-label-font-size', + this._settings.get_default_value('group-apps-label-font-size'), + ) + this._builder + .get_object('group_apps_label_font_size_spinbutton') + .set_value(this._settings.get_int('group-apps-label-font-size')) + + this._settings.set_value( + 'group-apps-label-font-weight', + this._settings.get_default_value('group-apps-label-font-weight'), + ) + this._builder + .get_object('group_apps_label_font_weight_combo') + .set_active_id( + this._settings.get_string('group-apps-label-font-weight'), + ) + + this._settings.set_value( + 'group-apps-label-font-color', + this._settings.get_default_value('group-apps-label-font-color'), + ) + let rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('group-apps-label-font-color')) + this._builder + .get_object('group_apps_label_font_color_colorbutton') + .set_rgba(rgba) + + this._settings.set_value( + 'group-apps-label-font-color-minimized', + this._settings.get_default_value( + 'group-apps-label-font-color-minimized', + ), + ) + let minimizedFontColor = new Gdk.RGBA() + minimizedFontColor.parse( + this._settings.get_string( + 'group-apps-label-font-color-minimized', + ), + ) + this._builder + .get_object('group_apps_label_font_color_minimized_colorbutton') + .set_rgba(minimizedFontColor) + + this._settings.set_value( + 'group-apps-label-max-width', + this._settings.get_default_value('group-apps-label-max-width'), + ) + this._builder + .get_object('group_apps_label_max_width_spinbutton') + .set_value(this._settings.get_int('group-apps-label-max-width')) + + this._settings.set_value( + 'group-apps-use-fixed-width', + this._settings.get_default_value('group-apps-use-fixed-width'), + ) + this._settings.set_value( + 'group-apps-underline-unfocused', + this._settings.get_default_value( + 'group-apps-underline-unfocused', + ), + ) + this._settings.set_value( + 'group-apps-use-launchers', + this._settings.get_default_value('group-apps-use-launchers'), + ) + }, + ) + + this._builder + .get_object('group_apps_label_font_size_spinbutton') + .set_value(this._settings.get_int('group-apps-label-font-size')) + this._builder + .get_object('group_apps_label_font_size_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'group-apps-label-font-size', + widget.get_value(), + ) + }) + + this._builder + .get_object('group_apps_label_font_weight_combo') + .set_active_id( + this._settings.get_string('group-apps-label-font-weight'), + ) + this._builder + .get_object('group_apps_label_font_weight_combo') + .connect('changed', (widget) => { + this._settings.set_string( + 'group-apps-label-font-weight', + widget.get_active_id(), + ) + }) + ;(function () { + let rgba = new Gdk.RGBA() + rgba.parse(this._settings.get_string('group-apps-label-font-color')) + this._builder + .get_object('group_apps_label_font_color_colorbutton') + .set_rgba(rgba) + }).apply(this) + ;(function () { + let rgba = new Gdk.RGBA() + rgba.parse( + this._settings.get_string('group-apps-label-font-color-minimized'), + ) + this._builder + .get_object('group_apps_label_font_color_minimized_colorbutton') + .set_rgba(rgba) + }).apply(this) + + this._builder + .get_object('group_apps_label_max_width_spinbutton') + .set_value(this._settings.get_int('group-apps-label-max-width')) + this._builder + .get_object('group_apps_label_max_width_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'group-apps-label-max-width', + widget.get_value(), + ) + }) + + dialog.show() + dialog.set_default_size(600, 1) + }) + + this._builder + .get_object('click_action_combo') + .set_active_id(this._settings.get_string('click-action')) + this._builder + .get_object('click_action_combo') + .connect('changed', (widget) => { + this._settings.set_string('click-action', widget.get_active_id()) + }) + + this._builder + .get_object('shift_click_action_combo') + .connect('changed', (widget) => { + this._settings.set_string('shift-click-action', widget.get_active_id()) + }) + + this._builder + .get_object('middle_click_action_combo') + .connect('changed', (widget) => { + this._settings.set_string('middle-click-action', widget.get_active_id()) + }) + this._builder + .get_object('shift_middle_click_action_combo') + .connect('changed', (widget) => { + this._settings.set_string( + 'shift-middle-click-action', + widget.get_active_id(), + ) + }) + + // Create dialog for middle-click options + this._builder + .get_object('middle_click_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_middle_click_options') + + let dialog = this._createPreferencesDialog( + _('Customize middle-click behavior'), + box, + () => { + // restore default settings for the relevant keys + let keys = [ + 'shift-click-action', + 'middle-click-action', + 'shift-middle-click-action', + ] + keys.forEach(function (val) { + this._settings.set_value( + val, + this._settings.get_default_value(val), + ) + }, this) + this._builder + .get_object('shift_click_action_combo') + .set_active_id(this._settings.get_string('shift-click-action')) + this._builder + .get_object('middle_click_action_combo') + .set_active_id(this._settings.get_string('middle-click-action')) + this._builder + .get_object('shift_middle_click_action_combo') + .set_active_id( + this._settings.get_string('shift-middle-click-action'), + ) + }, + ) + + this._builder + .get_object('shift_click_action_combo') + .set_active_id(this._settings.get_string('shift-click-action')) + + this._builder + .get_object('middle_click_action_combo') + .set_active_id(this._settings.get_string('middle-click-action')) + + this._builder + .get_object('shift_middle_click_action_combo') + .set_active_id(this._settings.get_string('shift-middle-click-action')) + + this._settings.bind( + 'shift-click-action', + this._builder.get_object('shift_click_action_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'middle-click-action', + this._builder.get_object('middle_click_action_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'shift-middle-click-action', + this._builder.get_object('shift_middle_click_action_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + dialog.show() + dialog.set_default_size(700, 1) + }) + + this._builder + .get_object('scroll_panel_combo') + .set_active_id(this._settings.get_string('scroll-panel-action')) + this._builder + .get_object('scroll_panel_combo') + .connect('changed', (widget) => { + this._settings.set_string('scroll-panel-action', widget.get_active_id()) + }) + + this._builder + .get_object('scroll_icon_combo') + .set_active_id(this._settings.get_string('scroll-icon-action')) + this._builder + .get_object('scroll_icon_combo') + .connect('changed', (widget) => { + this._settings.set_string('scroll-icon-action', widget.get_active_id()) + }) + + // Create dialog for panel scroll options + this._builder + .get_object('scroll_panel_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('scroll_panel_options_box') + + let dialog = this._createPreferencesDialog( + _('Customize panel scroll behavior'), + box, + () => { + // restore default settings + this._settings.set_value( + 'scroll-panel-delay', + this._settings.get_default_value('scroll-panel-delay'), + ) + this._builder + .get_object('scroll_panel_options_delay_spinbutton') + .set_value(this._settings.get_int('scroll-panel-delay')) + + this._settings.set_value( + 'scroll-panel-show-ws-popup', + this._settings.get_default_value('scroll-panel-show-ws-popup'), + ) + }, + ) + + this._builder + .get_object('scroll_panel_options_delay_spinbutton') + .set_value(this._settings.get_int('scroll-panel-delay')) + this._builder + .get_object('scroll_panel_options_delay_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('scroll-panel-delay', widget.get_value()) + }) + + this._settings.bind( + 'scroll-panel-show-ws-popup', + this._builder.get_object('scroll_panel_options_show_ws_popup_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + dialog.show() + dialog.set_default_size(640, 1) + }) + + // Create dialog for icon scroll options + this._builder + .get_object('scroll_icon_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('scroll_icon_options_box') + + let dialog = this._createPreferencesDialog( + _('Customize icon scroll behavior'), + box, + () => { + // restore default settings + this._settings.set_value( + 'scroll-icon-delay', + this._settings.get_default_value('scroll-icon-delay'), + ) + this._builder + .get_object('scroll_icon_options_delay_spinbutton') + .set_value(this._settings.get_int('scroll-icon-delay')) + }, + ) + + this._builder + .get_object('scroll_icon_options_delay_spinbutton') + .set_value(this._settings.get_int('scroll-icon-delay')) + this._builder + .get_object('scroll_icon_options_delay_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('scroll-icon-delay', widget.get_value()) + }) + + dialog.show() + dialog.set_default_size(640, 1) + }) + + this._settings.bind( + 'hot-keys', + this._builder.get_object('hot_keys_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'hot-keys', + this._builder.get_object('overlay_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder.get_object('overlay_combo').connect('changed', (widget) => { + this._settings.set_string('hotkeys-overlay-combo', widget.get_active_id()) + }) + + this._settings.bind( + 'shortcut-previews', + this._builder.get_object('shortcut_preview_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('shortcut_num_keys_combo') + .set_active_id(this._settings.get_string('shortcut-num-keys')) + this._builder + .get_object('shortcut_num_keys_combo') + .connect('changed', (widget) => { + this._settings.set_string('shortcut-num-keys', widget.get_active_id()) + }) + + this._settings.connect('changed::hotkey-prefix-text', () => { + checkHotkeyPrefix(this._settings) + }) + + this._builder + .get_object('hotkey_prefix_combo') + .set_active_id(this._settings.get_string('hotkey-prefix-text')) + + this._settings.bind( + 'hotkey-prefix-text', + this._builder.get_object('hotkey_prefix_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('overlay_combo') + .set_active_id(this._settings.get_string('hotkeys-overlay-combo')) + + this._settings.bind( + 'hotkeys-overlay-combo', + this._builder.get_object('overlay_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'overlay-timeout', + this._builder.get_object('timeout_spinbutton'), + 'value', + Gio.SettingsBindFlags.DEFAULT, + ) + if (this._settings.get_string('hotkeys-overlay-combo') !== 'TEMPORARILY') { + this._builder.get_object('timeout_spinbutton').set_sensitive(false) + } + + this._settings.connect('changed::hotkeys-overlay-combo', () => { + if (this._settings.get_string('hotkeys-overlay-combo') !== 'TEMPORARILY') + this._builder.get_object('timeout_spinbutton').set_sensitive(false) + else this._builder.get_object('timeout_spinbutton').set_sensitive(true) + }) + + this._settings.bind( + 'shortcut-text', + this._builder.get_object('shortcut_entry'), + 'text', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.connect('changed::shortcut-text', () => { + setShortcut(this._settings, 'shortcut') + }) + + // Create dialog for number overlay options + this._builder.get_object('overlay_button').connect('clicked', () => { + let box = this._builder.get_object('box_overlay_shortcut') + + let dialog = this._createPreferencesDialog( + _('Advanced hotkeys options'), + box, + () => { + // restore default settings for the relevant keys + let keys = [ + 'hotkey-prefix-text', + 'shortcut-text', + 'hotkeys-overlay-combo', + 'overlay-timeout', + 'shortcut-previews', + ] + keys.forEach(function (val) { + this._settings.set_value(val, this._settings.get_default_value(val)) + }, this) + }, + ) + + dialog.show() + dialog.set_default_size(600, 1) + }) + + // setup dialog for secondary menu options + this._builder + .get_object('secondarymenu_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_secondarymenu_options') + + let dialog = this._createPreferencesDialog( + _('Secondary Menu Options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'secondarymenu-contains-appmenu', + this._settings.get_default_value( + 'secondarymenu-contains-appmenu', + ), + ) + this._settings.set_value( + 'secondarymenu-contains-showdetails', + this._settings.get_default_value( + 'secondarymenu-contains-showdetails', + ), + ) + }, + ) + + // TODO setting secondarymenu-contains-appmenu is not being used anywhere + this._settings.bind( + 'secondarymenu-contains-appmenu', + this._builder.get_object('secondarymenu_appmenu_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'secondarymenu-contains-showdetails', + this._builder.get_object('secondarymenu_showdetails_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + dialog.show() + dialog.set_default_size(480, 1) + }) + + // setup dialog for advanced options + this._builder + .get_object('button_advanced_options') + .connect('clicked', () => { + let box = this._builder.get_object('box_advanced_options') + + let dialog = this._createPreferencesDialog(_('Advanced Options'), box) + + dialog.show() + dialog.set_default_size(480, 1) + }) + + // Fine-tune panel + + let sizeScales = [ + { + objectName: 'tray_size_scale', + valueName: 'tray-size', + range: DEFAULT_FONT_SIZES, + }, + { + objectName: 'leftbox_size_scale', + valueName: 'leftbox-size', + range: DEFAULT_FONT_SIZES, + }, + { + objectName: 'appicon_margin_scale', + valueName: 'appicon-margin', + range: DEFAULT_MARGIN_SIZES, + }, + { + objectName: 'appicon_margin_todesktop_scale', + valueName: 'appicon-margin-todesktop', + range: DEFAULT_MARGIN_SIZES, + }, + { + objectName: 'appicon_margin_toscreenborder_scale', + valueName: 'appicon-margin-toscreenborder', + range: DEFAULT_MARGIN_SIZES, + }, + { + objectName: 'appicon_padding_scale', + valueName: 'appicon-padding', + range: DEFAULT_MARGIN_SIZES, + }, + { + objectName: 'tray_padding_scale', + valueName: 'tray-padding', + range: DEFAULT_PADDING_SIZES, + }, + { + objectName: 'leftbox_padding_scale', + valueName: 'leftbox-padding', + range: DEFAULT_PADDING_SIZES, + }, + { + objectName: 'statusicon_padding_scale', + valueName: 'status-icon-padding', + range: DEFAULT_PADDING_SIZES, + }, + { objectName: 'panel_length_scale', valueName: '', range: LENGTH_MARKS }, + { + objectName: 'highlight_appicon_borderradius', + valueName: 'highlight-appicon-hover-border-radius', + range: [16, 12, 8, 4, 2, 0], + }, + ] + + for (const idx in sizeScales) { + let size_scale = this._builder.get_object(sizeScales[idx].objectName) + let range = sizeScales[idx].range + size_scale.set_range(range[range.length - 1], range[0]) + let value + if (sizeScales[idx].objectName === 'panel_length_scale') { + value = PanelSettings.getPanelLength( + this._settings, + this._currentMonitorIndex, + ) + } else { + value = this._settings.get_int(sizeScales[idx].valueName) + } + size_scale.set_value(value) + // Add marks from range arrays, omitting the first and last values. + range.slice(1, -1).forEach(function (val) { + size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()) + }) + + // Corrent for rtl languages + if (this._rtl) { + // Flip value position: this is not done automatically + size_scale.set_value_pos(Gtk.PositionType.LEFT) + // I suppose due to a bug, having a more than one mark and one above a value of 100 + // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable + // and then manually inverting it + size_scale.set_flippable(false) + size_scale.set_inverted(true) + } + } + + this._settings.bind( + 'animate-app-switch', + this._builder.get_object('animate_app_switch_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'animate-window-launch', + this._builder.get_object('animate_window_launch_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'animate-appicon-hover', + this._builder.get_object('animate_appicon_hover_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'animate-appicon-hover', + this._builder.get_object('animate_appicon_hover_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + { + this._settings.bind( + 'animate-appicon-hover-animation-type', + this._builder.get_object('animate_appicon_hover_options_type_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + let scales = [ + [ + 'animate_appicon_hover_options_duration_scale', + 'animate-appicon-hover-animation-duration', + 1, + ], + [ + 'animate_appicon_hover_options_rotation_scale', + 'animate-appicon-hover-animation-rotation', + 1, + ], + [ + 'animate_appicon_hover_options_travel_scale', + 'animate-appicon-hover-animation-travel', + 100, + ], + [ + 'animate_appicon_hover_options_zoom_scale', + 'animate-appicon-hover-animation-zoom', + 100, + ], + [ + 'animate_appicon_hover_options_convexity_scale', + 'animate-appicon-hover-animation-convexity', + 1, + ], + [ + 'animate_appicon_hover_options_extent_scale', + 'animate-appicon-hover-animation-extent', + 1, + ], + ] + + let updateScale = (scale) => { + let [id, key, factor] = scale + let type = this._settings.get_string( + 'animate-appicon-hover-animation-type', + ) + let value = this._settings.get_value(key).deep_unpack()[type] + let defaultValue = this._settings.get_default_value(key).deep_unpack()[ + type + ] + this._builder.get_object(id).sensitive = defaultValue !== undefined + this._builder.get_object(id).set_value(value * factor || 0) + this._builder.get_object(id).clear_marks() + this._builder + .get_object(id) + .add_mark( + defaultValue * factor, + Gtk.PositionType.TOP, + defaultValue !== undefined + ? (defaultValue * factor).toString() + : ' ', + ) + } + + scales.forEach((scale) => { + let [id, key, factor] = scale + this._settings.connect('changed::' + key, () => updateScale(scale)) + this._builder.get_object(id).connect('value-changed', (widget) => { + let type = this._settings.get_string( + 'animate-appicon-hover-animation-type', + ) + let variant = this._settings.get_value(key) + let unpacked = variant.deep_unpack() + if (unpacked[type] != widget.get_value() / factor) { + unpacked[type] = widget.get_value() / factor + this._settings.set_value( + key, + new GLib.Variant(variant.get_type_string(), unpacked), + ) + } + }) + }) + + this._settings.connect( + 'changed::animate-appicon-hover-animation-type', + () => scales.forEach(updateScale), + ) + scales.forEach(updateScale) + } + + this._builder + .get_object('animate_appicon_hover_button') + .connect('clicked', () => { + let box = this._builder.get_object('animate_appicon_hover_options') + + let dialog = this._createPreferencesDialog( + _('App icon animation options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'animate-appicon-hover-animation-type', + this._settings.get_default_value( + 'animate-appicon-hover-animation-type', + ), + ) + this._settings.set_value( + 'animate-appicon-hover-animation-duration', + this._settings.get_default_value( + 'animate-appicon-hover-animation-duration', + ), + ) + this._settings.set_value( + 'animate-appicon-hover-animation-rotation', + this._settings.get_default_value( + 'animate-appicon-hover-animation-rotation', + ), + ) + this._settings.set_value( + 'animate-appicon-hover-animation-travel', + this._settings.get_default_value( + 'animate-appicon-hover-animation-travel', + ), + ) + this._settings.set_value( + 'animate-appicon-hover-animation-zoom', + this._settings.get_default_value( + 'animate-appicon-hover-animation-zoom', + ), + ) + this._settings.set_value( + 'animate-appicon-hover-animation-convexity', + this._settings.get_default_value( + 'animate-appicon-hover-animation-convexity', + ), + ) + this._settings.set_value( + 'animate-appicon-hover-animation-extent', + this._settings.get_default_value( + 'animate-appicon-hover-animation-extent', + ), + ) + }, + ) + + dialog.show() + }) + + this._settings.bind( + 'highlight-appicon-hover', + this._builder.get_object('highlight_appicon_hover_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'highlight-appicon-hover', + this._builder.get_object('highlight_appicon_hover_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + { + rgba.parse( + this._settings.get_string('highlight-appicon-hover-background-color'), + ) + this._builder.get_object('highlight_appicon_color').set_rgba(rgba) + this._builder + .get_object('highlight_appicon_color') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + this._settings.set_string( + 'highlight-appicon-hover-background-color', + css, + ) + }) + + rgba.parse( + this._settings.get_string('highlight-appicon-pressed-background-color'), + ) + this._builder.get_object('pressed_appicon_color').set_rgba(rgba) + this._builder + .get_object('pressed_appicon_color') + .connect('color-set', (button) => { + let rgba = button.get_rgba() + let css = rgba.to_string() + this._settings.set_string( + 'highlight-appicon-pressed-background-color', + css, + ) + }) + + let scales = [ + [ + 'highlight_appicon_borderradius', + 'highlight-appicon-hover-border-radius', + ], + ] + + const updateScale = (scale) => { + let [id, key] = scale + this._builder.get_object(id).set_value(this._settings.get_int(key)) + } + scales.forEach((scale) => { + updateScale(scale) + let [id, key] = scale + this._builder.get_object(id).connect('value-changed', (widget) => { + this._settings.set_int(key, widget.get_value()) + }) + }) + } + + this._builder + .get_object('highlight_appicon_hover_button') + .connect('clicked', () => { + let box = this._builder.get_object('highlight_appicon_hover_options') + + let dialog = this._createPreferencesDialog( + _('App icon highlight options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'highlight-appicon-hover-background-color', + this._settings.get_default_value( + 'highlight-appicon-hover-background-color', + ), + ) + rgba.parse( + this._settings.get_string( + 'highlight-appicon-hover-background-color', + ), + ) + this._builder.get_object('highlight_appicon_color').set_rgba(rgba) + this._settings.set_value( + 'highlight-appicon-pressed-background-color', + this._settings.get_default_value( + 'highlight-appicon-pressed-background-color', + ), + ) + rgba.parse( + this._settings.get_string( + 'highlight-appicon-pressed-background-color', + ), + ) + this._builder.get_object('pressed_appicon_color').set_rgba(rgba) + this._settings.set_value( + 'highlight-appicon-hover-border-radius', + this._settings.get_default_value( + 'highlight-appicon-hover-border-radius', + ), + ) + this._builder + .get_object('highlight_appicon_borderradius') + .set_value( + this._settings.get_int('highlight-appicon-hover-border-radius'), + ) + }, + ) + + dialog.show() + }) + + this._settings.bind( + 'stockgs-keep-dash', + this._builder.get_object('stockgs_dash_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'stockgs-keep-top-panel', + this._builder.get_object('stockgs_top_panel_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.connect('changed::stockgs-keep-top-panel', () => + this._maybeDisableTopPosition(), + ) + + this._maybeDisableTopPosition() + + this._settings.bind( + 'stockgs-panelbtn-click-only', + this._builder.get_object('stockgs_panelbtn_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'stockgs-force-hotcorner', + this._builder.get_object('stockgs_hotcorner_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + // About Panel + + this._builder + .get_object('extension_version') + .set_label( + this._metadata.version.toString() + + (this._metadata.commit ? ' (' + this._metadata.commit + ')' : ''), + ) + + this._builder + .get_object('importexport_export_button') + .connect('clicked', (widget) => { + this._showFileChooser( + _('Export settings'), + { action: Gtk.FileChooserAction.SAVE }, + 'Save', + (filename) => { + let file = Gio.file_new_for_path(filename) + let raw = file.replace(null, false, Gio.FileCreateFlags.NONE, null) + let out = Gio.BufferedOutputStream.new_sized(raw, 4096) + + out.write_all( + GLib.spawn_command_line_sync('dconf dump ' + SCHEMA_PATH)[1], + null, + ) + out.close(null) + }, + ) + }) + + this._builder + .get_object('importexport_import_button') + .connect('clicked', (widget) => { + this._showFileChooser( + _('Import settings'), + { action: Gtk.FileChooserAction.OPEN }, + 'Open', + (filename) => { + if (filename && GLib.file_test(filename, GLib.FileTest.EXISTS)) { + let settingsFile = Gio.File.new_for_path(filename) + let [, pid, stdin, stdout, stderr] = GLib.spawn_async_with_pipes( + null, + ['dconf', 'load', SCHEMA_PATH], + null, + GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, + null, + ) + + stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true }) + GLib.close(stdout) + GLib.close(stderr) + + stdin.splice( + settingsFile.read(null), + Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | + Gio.OutputStreamSpliceFlags.CLOSE_TARGET, + null, + ) + } + }, + ) + }) + + // Donation panel + + let revealDonateTimeout = 0 + let donationIconSwitch = this._builder.get_object('donation_icon_switch') + let donationRevealer = this._builder.get_object('donation_revealer') + let hiddenDonateIcon = !!this._settings.get_string( + 'hide-donate-icon-unixtime', + ) + + this._builder + .get_object('donation_logo') + .set_from_file(`${this._path}/img/dash-to-panel-logo-light.svg`) + this._builder + .get_object('paypal_logo') + .set_from_file(`${this._path}/img/paypal.png`) + this._builder + .get_object('stripe_logo') + .set_from_file(`${this._path}/img/stripe.png`) + this._builder + .get_object('kofi_logo') + .set_from_file(`${this._path}/img/kofi.png`) + + donationIconSwitch.set_active(hiddenDonateIcon) + donationRevealer.set_reveal_child(hiddenDonateIcon) + + donationIconSwitch.connect('notify::active', (widget) => + this._settings.set_string( + 'hide-donate-icon-unixtime', + widget.get_active() ? Date.now().toString() : '', + ), + ) + + this.notebook.connect('notify::visible-page', () => { + clearTimeout(revealDonateTimeout) + + if ( + this.notebook.visible_page_name == 'donation' && + !donationRevealer.get_reveal_child() + ) + revealDonateTimeout = setTimeout( + () => donationRevealer.set_reveal_child(true), + 10000, + ) + }) + } + + _setPreviewTitlePosition() { + switch (this._settings.get_string('window-preview-title-position')) { + case 'BOTTOM': + this._builder + .get_object('preview_title_position_bottom_button') + .set_active(true) + break + case 'TOP': + this._builder + .get_object('preview_title_position_top_button') + .set_active(true) + break + } + } + + _showFileChooser(title, params, acceptBtn, acceptHandler) { + let dialog = new Gtk.FileChooserDialog( + mergeObjects( + { title: title, transient_for: this.notebook.get_root() }, + params, + ), + ) + + dialog.add_button('Cancel', Gtk.ResponseType.CANCEL) + dialog.add_button(acceptBtn, Gtk.ResponseType.ACCEPT) + + dialog.show() + + dialog.connect('response', (dialog, id) => { + if (id == Gtk.ResponseType.ACCEPT) + acceptHandler.call(this, dialog.get_file().get_path()) + + dialog.destroy() + }) + } } - -const BuilderScope = GObject.registerClass({ +const BuilderScope = GObject.registerClass( + { Implements: [Gtk.BuilderScope], -}, class BuilderScope extends GObject.Object { - + }, + class BuilderScope extends GObject.Object { _init(preferences) { - this._preferences = preferences; - super._init(); + this._preferences = preferences + super._init() } vfunc_create_closure(builder, handlerName, flags, connectObject) { - if (flags & Gtk.BuilderClosureFlags.SWAPPED) - throw new Error('Unsupported template signal flag "swapped"'); + if (flags & Gtk.BuilderClosureFlags.SWAPPED) + throw new Error('Unsupported template signal flag "swapped"') - if (typeof this[handlerName] === 'undefined') - throw new Error(`${handlerName} is undefined`); + if (typeof this[handlerName] === 'undefined') + throw new Error(`${handlerName} is undefined`) - return this[handlerName].bind(connectObject || this); + return this[handlerName].bind(connectObject || this) } on_btn_click(connectObject) { - connectObject.set_label("Clicked"); + connectObject.set_label('Clicked') } position_bottom_button_clicked_cb(button) { - if (!this._preferences._ignorePositionRadios && button.get_active()) this._preferences._setPanelPosition(Pos.BOTTOM); + if (!this._preferences._ignorePositionRadios && button.get_active()) + this._preferences._setPanelPosition(Pos.BOTTOM) } position_top_button_clicked_cb(button) { - if (!this._preferences._ignorePositionRadios && button.get_active()) this._preferences._setPanelPosition(Pos.TOP); + if (!this._preferences._ignorePositionRadios && button.get_active()) + this._preferences._setPanelPosition(Pos.TOP) } position_left_button_clicked_cb(button) { - if (!this._preferences._ignorePositionRadios && button.get_active()) this._preferences._setPanelPosition(Pos.LEFT); + if (!this._preferences._ignorePositionRadios && button.get_active()) + this._preferences._setPanelPosition(Pos.LEFT) } position_right_button_clicked_cb(button) { - if (!this._preferences._ignorePositionRadios && button.get_active()) this._preferences._setPanelPosition(Pos.RIGHT); + if (!this._preferences._ignorePositionRadios && button.get_active()) + this._preferences._setPanelPosition(Pos.RIGHT) } dots_bottom_button_toggled_cb(button) { - if (button.get_active()) - this._preferences._settings.set_string('dot-position', "BOTTOM"); + if (button.get_active()) + this._preferences._settings.set_string('dot-position', 'BOTTOM') } dots_top_button_toggled_cb(button) { - if (button.get_active()) - this._preferences._settings.set_string('dot-position', "TOP"); + if (button.get_active()) + this._preferences._settings.set_string('dot-position', 'TOP') } dots_left_button_toggled_cb(button) { - if (button.get_active()) - this._preferences._settings.set_string('dot-position', "LEFT"); + if (button.get_active()) + this._preferences._settings.set_string('dot-position', 'LEFT') } dots_right_button_toggled_cb(button) { - if (button.get_active()) - this._preferences._settings.set_string('dot-position', "RIGHT"); + if (button.get_active()) + this._preferences._settings.set_string('dot-position', 'RIGHT') } preview_title_position_bottom_button_toggled_cb(button) { - if (button.get_active()) - this._preferences._settings.set_string('window-preview-title-position', 'BOTTOM'); + if (button.get_active()) + this._preferences._settings.set_string( + 'window-preview-title-position', + 'BOTTOM', + ) } preview_title_position_top_button_toggled_cb(button) { - if (button.get_active()) - this._preferences._settings.set_string('window-preview-title-position', 'TOP'); + if (button.get_active()) + this._preferences._settings.set_string( + 'window-preview-title-position', + 'TOP', + ) } panel_size_scale_value_changed_cb(scale) { - // Avoid settings the size continuously - if (this._preferences._panel_size_timeout > 0) - GLib.Source.remove(this._preferences._panel_size_timeout); + // Avoid settings the size continuously + if (this._preferences._panel_size_timeout > 0) + GLib.Source.remove(this._preferences._panel_size_timeout) - this._preferences._panel_size_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - const value = scale.get_value(); - const monitorSync = this._preferences._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this._preferences.monitors : [this._preferences._currentMonitorIndex]; - monitorsToSetFor.forEach(monitorIndex => { - PanelSettings.setPanelSize(this._preferences._settings, monitorIndex, value); - }); + this._preferences._panel_size_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + const value = scale.get_value() + const monitorSync = this._preferences._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? this._preferences.monitors + : [this._preferences._currentMonitorIndex] + monitorsToSetFor.forEach((monitorIndex) => { + PanelSettings.setPanelSize( + this._preferences._settings, + monitorIndex, + value, + ) + }) - this._preferences._panel_size_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._panel_size_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } tray_size_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._tray_size_timeout > 0) - GLib.Source.remove(this._preferences._tray_size_timeout); + // Avoid settings the size consinuosly + if (this._preferences._tray_size_timeout > 0) + GLib.Source.remove(this._preferences._tray_size_timeout) - this._preferences._tray_size_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('tray-size', scale.get_value()); - this._preferences._tray_size_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._tray_size_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + this._preferences._settings.set_int('tray-size', scale.get_value()) + this._preferences._tray_size_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } leftbox_size_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._leftbox_size_timeout > 0) - GLib.Source.remove(this._preferences._leftbox_size_timeout); + // Avoid settings the size consinuosly + if (this._preferences._leftbox_size_timeout > 0) + GLib.Source.remove(this._preferences._leftbox_size_timeout) - this._preferences._leftbox_size_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('leftbox-size', scale.get_value()); - this._preferences._leftbox_size_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._leftbox_size_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + this._preferences._settings.set_int('leftbox-size', scale.get_value()) + this._preferences._leftbox_size_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } appicon_margin_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._appicon_margin_timeout > 0) - GLib.Source.remove(this._preferences._appicon_margin_timeout); + // Avoid settings the size consinuosly + if (this._preferences._appicon_margin_timeout > 0) + GLib.Source.remove(this._preferences._appicon_margin_timeout) - this._preferences._appicon_margin_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('appicon-margin', scale.get_value()); - this._preferences._appicon_margin_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._appicon_margin_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + this._preferences._settings.set_int( + 'appicon-margin', + scale.get_value(), + ) + this._preferences._appicon_margin_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } appicon_margin_todesktop_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._appicon_margin_todesktop_timeout > 0) - GLib.Source.remove(this._preferences._appicon_margin_todesktop_timeout); + // Avoid settings the size consinuosly + if (this._preferences._appicon_margin_todesktop_timeout > 0) + GLib.Source.remove(this._preferences._appicon_margin_todesktop_timeout) - this._preferences._appicon_margin_todesktop_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('appicon-margin-todesktop', scale.get_value()); - this._preferences._appicon_margin_todesktop_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._appicon_margin_todesktop_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + this._preferences._settings.set_int( + 'appicon-margin-todesktop', + scale.get_value(), + ) + this._preferences._appicon_margin_todesktop_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } appicon_margin_toscreenborder_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._appicon_margin_toscreenborder_timeout > 0) - GLib.Source.remove(this._preferences._appicon_margin_toscreenborder_timeout); + // Avoid settings the size consinuosly + if (this._preferences._appicon_margin_toscreenborder_timeout > 0) + GLib.Source.remove( + this._preferences._appicon_margin_toscreenborder_timeout, + ) - this._preferences._appicon_margin_toscreenborder_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('appicon-margin-toscreenborder', scale.get_value()); - this._preferences._appicon_margin_toscreenborder_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._appicon_margin_toscreenborder_timeout = + GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { + this._preferences._settings.set_int( + 'appicon-margin-toscreenborder', + scale.get_value(), + ) + this._preferences._appicon_margin_toscreenborder_timeout = 0 + return GLib.SOURCE_REMOVE + }) } appicon_padding_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._appicon_padding_timeout > 0) - GLib.Source.remove(this._preferences._appicon_padding_timeout); + // Avoid settings the size consinuosly + if (this._preferences._appicon_padding_timeout > 0) + GLib.Source.remove(this._preferences._appicon_padding_timeout) - this._preferences._appicon_padding_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('appicon-padding', scale.get_value()); - this._preferences._appicon_padding_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._appicon_padding_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + this._preferences._settings.set_int( + 'appicon-padding', + scale.get_value(), + ) + this._preferences._appicon_padding_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } tray_padding_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._tray_padding_timeout > 0) - GLib.Source.remove(this._preferences._tray_padding_timeout); + // Avoid settings the size consinuosly + if (this._preferences._tray_padding_timeout > 0) + GLib.Source.remove(this._preferences._tray_padding_timeout) - this._preferences._tray_padding_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('tray-padding', scale.get_value()); - this._preferences._tray_padding_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._tray_padding_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + this._preferences._settings.set_int('tray-padding', scale.get_value()) + this._preferences._tray_padding_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } statusicon_padding_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._statusicon_padding_timeout > 0) - GLib.Source.remove(this._preferences._statusicon_padding_timeout); + // Avoid settings the size consinuosly + if (this._preferences._statusicon_padding_timeout > 0) + GLib.Source.remove(this._preferences._statusicon_padding_timeout) - this._preferences._statusicon_padding_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('status-icon-padding', scale.get_value()); - this._preferences._statusicon_padding_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._statusicon_padding_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + this._preferences._settings.set_int( + 'status-icon-padding', + scale.get_value(), + ) + this._preferences._statusicon_padding_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } leftbox_padding_scale_value_changed_cb(scale) { - // Avoid settings the size consinuosly - if (this._preferences._leftbox_padding_timeout > 0) - GLib.Source.remove(this._preferences._leftbox_padding_timeout); + // Avoid settings the size consinuosly + if (this._preferences._leftbox_padding_timeout > 0) + GLib.Source.remove(this._preferences._leftbox_padding_timeout) - this._preferences._leftbox_padding_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - this._preferences._settings.set_int('leftbox-padding', scale.get_value()); - this._preferences._leftbox_padding_timeout = 0; - return GLib.SOURCE_REMOVE; - }); + this._preferences._leftbox_padding_timeout = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + this._preferences._settings.set_int( + 'leftbox-padding', + scale.get_value(), + ) + this._preferences._leftbox_padding_timeout = 0 + return GLib.SOURCE_REMOVE + }, + ) } -}); - + }, +) export default class DashToPanelPreferences extends ExtensionPreferences { - fillPreferencesWindow(window) { - window._settings = this.getSettings('org.gnome.shell.extensions.dash-to-panel'); + fillPreferencesWindow(window) { + window._settings = this.getSettings( + 'org.gnome.shell.extensions.dash-to-panel', + ) - // use default width or window - window.set_default_size(0, 740); + // use default width or window + window.set_default_size(0, 740) - let preferences = new Preferences(window, window._settings, this.path); - } + let preferences = new Preferences(window, window._settings, this.path) + } } diff --git a/progress.js b/progress.js index 48105ab..b3fedd3 100644 --- a/progress.js +++ b/progress.js @@ -19,579 +19,683 @@ * This file is based on code from the Dash to Dock extension by micheleg */ -import Cairo from 'cairo'; -import Gio from 'gi://Gio'; -import Clutter from 'gi://Clutter'; -import Pango from 'gi://Pango'; -import St from 'gi://St'; -import * as Utils from './utils.js'; -import {SETTINGS} from './extension.js'; - -import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; +import Cairo from 'cairo' +import Gio from 'gi://Gio' +import Clutter from 'gi://Clutter' +import Pango from 'gi://Pango' +import St from 'gi://St' +import * as Utils from './utils.js' +import { SETTINGS } from './extension.js' +import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' export const ProgressManager = class extends EventEmitter { + constructor() { + super() - constructor() { - super(); + this._entriesByDBusName = {} - this._entriesByDBusName = {}; + this._launcher_entry_dbus_signal_id = Gio.DBus.session.signal_subscribe( + null, // sender + 'com.canonical.Unity.LauncherEntry', // iface + null, // member + null, // path + null, // arg0 + Gio.DBusSignalFlags.NONE, + this._onEntrySignalReceived.bind(this), + ) - this._launcher_entry_dbus_signal_id = - Gio.DBus.session.signal_subscribe(null, // sender - 'com.canonical.Unity.LauncherEntry', // iface - null, // member - null, // path - null, // arg0 - Gio.DBusSignalFlags.NONE, - this._onEntrySignalReceived.bind(this)); + this._dbus_name_owner_changed_signal_id = Gio.DBus.session.signal_subscribe( + 'org.freedesktop.DBus', // sender + 'org.freedesktop.DBus', // interface + 'NameOwnerChanged', // member + '/org/freedesktop/DBus', // path + null, // arg0 + Gio.DBusSignalFlags.NONE, + this._onDBusNameOwnerChanged.bind(this), + ) - this._dbus_name_owner_changed_signal_id = - Gio.DBus.session.signal_subscribe('org.freedesktop.DBus', // sender - 'org.freedesktop.DBus', // interface - 'NameOwnerChanged', // member - '/org/freedesktop/DBus', // path - null, // arg0 - Gio.DBusSignalFlags.NONE, - this._onDBusNameOwnerChanged.bind(this)); + this._acquireUnityDBus() + } - this._acquireUnityDBus(); + destroy() { + if (this._launcher_entry_dbus_signal_id) { + Gio.DBus.session.signal_unsubscribe(this._launcher_entry_dbus_signal_id) } - destroy() { - if (this._launcher_entry_dbus_signal_id) { - Gio.DBus.session.signal_unsubscribe(this._launcher_entry_dbus_signal_id); - } - - if (this._dbus_name_owner_changed_signal_id) { - Gio.DBus.session.signal_unsubscribe(this._dbus_name_owner_changed_signal_id); - } - - this._releaseUnityDBus(); + if (this._dbus_name_owner_changed_signal_id) { + Gio.DBus.session.signal_unsubscribe( + this._dbus_name_owner_changed_signal_id, + ) } - size() { - return Object.keys(this._entriesByDBusName).length; + this._releaseUnityDBus() + } + + size() { + return Object.keys(this._entriesByDBusName).length + } + + lookupByDBusName(dbusName) { + return this._entriesByDBusName.hasOwnProperty(dbusName) + ? this._entriesByDBusName[dbusName] + : null + } + + lookupById(appId) { + let ret = [] + for (let dbusName in this._entriesByDBusName) { + let entry = this._entriesByDBusName[dbusName] + if (entry && entry.appId() == appId) { + ret.push(entry) + } } - lookupByDBusName(dbusName) { - return this._entriesByDBusName.hasOwnProperty(dbusName) ? this._entriesByDBusName[dbusName] : null; + return ret + } + + addEntry(entry) { + let existingEntry = this.lookupByDBusName(entry.dbusName()) + if (existingEntry) { + existingEntry.update(entry) + } else { + this._entriesByDBusName[entry.dbusName()] = entry + this.emit('progress-entry-added', entry) + } + } + + removeEntry(entry) { + delete this._entriesByDBusName[entry.dbusName()] + this.emit('progress-entry-removed', entry) + } + + _acquireUnityDBus() { + if (!this._unity_bus_id) { + Gio.DBus.session.own_name( + 'com.canonical.Unity', + Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, + null, + null, + ) + } + } + + _releaseUnityDBus() { + if (this._unity_bus_id) { + Gio.DBus.session.unown_name(this._unity_bus_id) + this._unity_bus_id = 0 + } + } + + _onEntrySignalReceived( + connection, + sender_name, + object_path, + interface_name, + signal_name, + parameters, + user_data, + ) { + if (!parameters || !signal_name) return + + if (signal_name == 'Update') { + if (!sender_name) { + return + } + + this._handleUpdateRequest(sender_name, parameters) + } + } + + _onDBusNameOwnerChanged( + connection, + sender_name, + object_path, + interface_name, + signal_name, + parameters, + user_data, + ) { + if (!parameters || !this.size()) return + + let [name, before, after] = parameters.deep_unpack() + + if (!after) { + if (this._entriesByDBusName.hasOwnProperty(before)) { + this.removeEntry(this._entriesByDBusName[before]) + } + } + } + + _handleUpdateRequest(senderName, parameters) { + if (!senderName || !parameters) { + return } - lookupById(appId) { - let ret = []; - for (let dbusName in this._entriesByDBusName) { - let entry = this._entriesByDBusName[dbusName]; - if (entry && entry.appId() == appId) { - ret.push(entry); - } - } + let [appUri, properties] = parameters.deep_unpack() + let appId = appUri.replace(/(^\w+:|^)\/\//, '') + let entry = this.lookupByDBusName(senderName) - return ret; - } - - addEntry(entry) { - let existingEntry = this.lookupByDBusName(entry.dbusName()); - if (existingEntry) { - existingEntry.update(entry); - } else { - this._entriesByDBusName[entry.dbusName()] = entry; - this.emit('progress-entry-added', entry); - } - } - - removeEntry(entry) { - delete this._entriesByDBusName[entry.dbusName()] - this.emit('progress-entry-removed', entry); - } - - _acquireUnityDBus() { - if (!this._unity_bus_id) { - Gio.DBus.session.own_name('com.canonical.Unity', - Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null); - } - } - - _releaseUnityDBus() { - if (this._unity_bus_id) { - Gio.DBus.session.unown_name(this._unity_bus_id); - this._unity_bus_id = 0; - } - } - - _onEntrySignalReceived(connection, sender_name, object_path, - interface_name, signal_name, parameters, user_data) { - if (!parameters || !signal_name) - return; - - if (signal_name == 'Update') { - if (!sender_name) { - return; - } - - this._handleUpdateRequest(sender_name, parameters); - } - } - - _onDBusNameOwnerChanged(connection, sender_name, object_path, - interface_name, signal_name, parameters, user_data) { - if (!parameters || !this.size()) - return; - - let [name, before, after] = parameters.deep_unpack(); - - if (!after) { - if (this._entriesByDBusName.hasOwnProperty(before)) { - this.removeEntry(this._entriesByDBusName[before]); - } - } - } - - _handleUpdateRequest(senderName, parameters) { - if (!senderName || !parameters) { - return; - } - - let [appUri, properties] = parameters.deep_unpack(); - let appId = appUri.replace(/(^\w+:|^)\/\//, ''); - let entry = this.lookupByDBusName(senderName); - - if (entry) { - entry.setDBusName(senderName); - entry.update(properties); - } else { - let entry = new AppProgress(senderName, appId, properties); - this.addEntry(entry); - } - } -}; - -export class AppProgress extends EventEmitter { - - constructor(dbusName, appId, properties) { - super(); - - this._dbusName = dbusName; - this._appId = appId; - this._count = 0; - this._countVisible = false; - this._progress = 0.0; - this._progressVisible = false; - this._urgent = false; - this.update(properties); - } - - appId() { - return this._appId; - } - - dbusName() { - return this._dbusName; - } - - count() { - return this._count; - } - - setCount(count) { - if (this._count != count) { - this._count = count; - this.emit('count-changed', this._count); - } - } - - countVisible() { - return this._countVisible; - } - - setCountVisible(countVisible) { - if (this._countVisible != countVisible) { - this._countVisible = countVisible; - this.emit('count-visible-changed', this._countVisible); - } - } - - progress() { - return this._progress; - } - - setProgress(progress) { - if (this._progress != progress) { - this._progress = progress; - this.emit('progress-changed', this._progress); - } - } - - progressVisible() { - return this._progressVisible; - } - - setProgressVisible(progressVisible) { - if (this._progressVisible != progressVisible) { - this._progressVisible = progressVisible; - this.emit('progress-visible-changed', this._progressVisible); - } - } - - urgent() { - return this._urgent; - } - - setUrgent(urgent) { - if (this._urgent != urgent) { - this._urgent = urgent; - this.emit('urgent-changed', this._urgent); - } - } - - setDBusName(dbusName) { - if (this._dbusName != dbusName) { - let oldName = this._dbusName; - this._dbusName = dbusName; - this.emit('dbus-name-changed', oldName); - } - } - - update(other) { - if (other instanceof AppProgress) { - this.setDBusName(other.dbusName()) - this.setCount(other.count()); - this.setCountVisible(other.countVisible()); - this.setProgress(other.progress()); - this.setProgressVisible(other.progressVisible()) - this.setUrgent(other.urgent()); - } else { - for (let property in other) { - if (other.hasOwnProperty(property)) { - if (property == 'count') { - this.setCount(other[property].get_int64()); - } else if (property == 'count-visible') { - this.setCountVisible(SETTINGS.get_boolean('progress-show-count') && other[property].get_boolean()); - } else if (property == 'progress') { - this.setProgress(other[property].get_double()); - } else if (property == 'progress-visible') { - this.setProgressVisible(SETTINGS.get_boolean('progress-show-bar') && other[property].get_boolean()); - } else if (property == 'urgent') { - this.setUrgent(other[property].get_boolean()); - } else { - // Not implemented yet - } - } - } - } + if (entry) { + entry.setDBusName(senderName) + entry.update(properties) + } else { + let entry = new AppProgress(senderName, appId, properties) + this.addEntry(entry) } + } } +export class AppProgress extends EventEmitter { + constructor(dbusName, appId, properties) { + super() + + this._dbusName = dbusName + this._appId = appId + this._count = 0 + this._countVisible = false + this._progress = 0.0 + this._progressVisible = false + this._urgent = false + this.update(properties) + } + + appId() { + return this._appId + } + + dbusName() { + return this._dbusName + } + + count() { + return this._count + } + + setCount(count) { + if (this._count != count) { + this._count = count + this.emit('count-changed', this._count) + } + } + + countVisible() { + return this._countVisible + } + + setCountVisible(countVisible) { + if (this._countVisible != countVisible) { + this._countVisible = countVisible + this.emit('count-visible-changed', this._countVisible) + } + } + + progress() { + return this._progress + } + + setProgress(progress) { + if (this._progress != progress) { + this._progress = progress + this.emit('progress-changed', this._progress) + } + } + + progressVisible() { + return this._progressVisible + } + + setProgressVisible(progressVisible) { + if (this._progressVisible != progressVisible) { + this._progressVisible = progressVisible + this.emit('progress-visible-changed', this._progressVisible) + } + } + + urgent() { + return this._urgent + } + + setUrgent(urgent) { + if (this._urgent != urgent) { + this._urgent = urgent + this.emit('urgent-changed', this._urgent) + } + } + + setDBusName(dbusName) { + if (this._dbusName != dbusName) { + let oldName = this._dbusName + this._dbusName = dbusName + this.emit('dbus-name-changed', oldName) + } + } + + update(other) { + if (other instanceof AppProgress) { + this.setDBusName(other.dbusName()) + this.setCount(other.count()) + this.setCountVisible(other.countVisible()) + this.setProgress(other.progress()) + this.setProgressVisible(other.progressVisible()) + this.setUrgent(other.urgent()) + } else { + for (let property in other) { + if (other.hasOwnProperty(property)) { + if (property == 'count') { + this.setCount(other[property].get_int64()) + } else if (property == 'count-visible') { + this.setCountVisible( + SETTINGS.get_boolean('progress-show-count') && + other[property].get_boolean(), + ) + } else if (property == 'progress') { + this.setProgress(other[property].get_double()) + } else if (property == 'progress-visible') { + this.setProgressVisible( + SETTINGS.get_boolean('progress-show-bar') && + other[property].get_boolean(), + ) + } else if (property == 'urgent') { + this.setUrgent(other[property].get_boolean()) + } else { + // Not implemented yet + } + } + } + } + } +} export const ProgressIndicator = class { + constructor(source, progressManager) { + this._source = source + this._progressManager = progressManager + this._signalsHandler = new Utils.GlobalSignalsHandler() - constructor(source, progressManager) { - this._source = source; - this._progressManager = progressManager; - this._signalsHandler = new Utils.GlobalSignalsHandler(); + this._sourceDestroyId = this._source.connect('destroy', () => { + this._signalsHandler.destroy() + }) - this._sourceDestroyId = this._source.connect('destroy', () => { - this._signalsHandler.destroy(); - }); + this._notificationBadgeLabel = new St.Label({ style_class: 'badge' }) + this._notificationBadgeBin = new St.Bin({ + child: this._notificationBadgeLabel, + y: 2, + x: 2, + }) + this._notificationBadgeLabel.add_style_class_name('notification-badge') + this._notificationBadgeCount = 0 + this._notificationBadgeBin.hide() - this._notificationBadgeLabel = new St.Label({ style_class: 'badge' }); - this._notificationBadgeBin = new St.Bin({ - child: this._notificationBadgeLabel, y: 2, x: 2 - }); - this._notificationBadgeLabel.add_style_class_name('notification-badge'); - this._notificationBadgeCount = 0; - this._notificationBadgeBin.hide(); + this._source._dtpIconContainer.add_child(this._notificationBadgeBin) + this._source._dtpIconContainer.connect( + 'notify::allocation', + this.updateNotificationBadge.bind(this), + ) - this._source._dtpIconContainer.add_child(this._notificationBadgeBin); - this._source._dtpIconContainer.connect('notify::allocation', this.updateNotificationBadge.bind(this)); + this._progressManagerEntries = [] + this._progressManager.lookupById(this._source.app.id).forEach((entry) => { + this.insertEntry(entry) + }) - this._progressManagerEntries = []; - this._progressManager.lookupById(this._source.app.id).forEach( - (entry) => { - this.insertEntry(entry); - } - ); + this._signalsHandler.add( + [ + this._progressManager, + 'progress-entry-added', + this._onEntryAdded.bind(this), + ], + [ + this._progressManager, + 'progress-entry-removed', + this._onEntryRemoved.bind(this), + ], + ) + } - this._signalsHandler.add([ - this._progressManager, - 'progress-entry-added', - this._onEntryAdded.bind(this) - ], [ - this._progressManager, - 'progress-entry-removed', - this._onEntryRemoved.bind(this) - ]); + destroy() { + this._source.disconnect(this._sourceDestroyId) + this._signalsHandler.destroy() + } + + _onEntryAdded(appProgress, entry) { + if (!entry || !entry.appId()) return + if ( + this._source && + this._source.app && + this._source.app.id == entry.appId() + ) { + this.insertEntry(entry) + } + } + + _onEntryRemoved(appProgress, entry) { + if (!entry || !entry.appId()) return + + if ( + this._source && + this._source.app && + this._source.app.id == entry.appId() + ) { + this.removeEntry(entry) + } + } + + updateNotificationBadge() { + this._source.updateNumberOverlay(this._notificationBadgeBin) + this._notificationBadgeLabel.clutter_text.ellipsize = + Pango.EllipsizeMode.MIDDLE + } + + _notificationBadgeCountToText(count) { + if (count <= 9999) { + return count.toString() + } else if (count < 1e5) { + let thousands = count / 1e3 + return thousands.toFixed(1).toString() + 'k' + } else if (count < 1e6) { + let thousands = count / 1e3 + return thousands.toFixed(0).toString() + 'k' + } else if (count < 1e8) { + let millions = count / 1e6 + return millions.toFixed(1).toString() + 'M' + } else if (count < 1e9) { + let millions = count / 1e6 + return millions.toFixed(0).toString() + 'M' + } else { + let billions = count / 1e9 + return billions.toFixed(1).toString() + 'B' + } + } + + setNotificationBadge(count) { + this._notificationBadgeCount = count + let text = this._notificationBadgeCountToText(count) + this._notificationBadgeLabel.set_text(text) + } + + toggleNotificationBadge(activate) { + if (activate && this._notificationBadgeCount > 0) { + this.updateNotificationBadge() + this._notificationBadgeBin.show() + } else this._notificationBadgeBin.hide() + } + + _showProgressOverlay() { + if (this._progressOverlayArea) { + this._updateProgressOverlay() + return } - destroy() { - this._source.disconnect(this._sourceDestroyId); - this._signalsHandler.destroy(); + this._progressOverlayArea = new St.DrawingArea({ + x_expand: true, + y_expand: true, + }) + this._progressOverlayArea.add_style_class_name('progress-bar') + this._progressOverlayArea.connect('repaint', () => { + this._drawProgressOverlay(this._progressOverlayArea) + }) + + this._source._iconContainer.add_child(this._progressOverlayArea) + let node = this._progressOverlayArea.get_theme_node() + + let [hasColor, color] = node.lookup_color('-progress-bar-background', false) + if (hasColor) this._progressbar_background = color + else + this._progressbar_background = new Utils.ColorUtils.Color({ + red: 204, + green: 204, + blue: 204, + alpha: 255, + }) + ;[hasColor, color] = node.lookup_color('-progress-bar-border', false) + if (hasColor) this._progressbar_border = color + else + this._progressbar_border = new Utils.ColorUtils.Color({ + red: 230, + green: 230, + blue: 230, + alpha: 255, + }) + + this._updateProgressOverlay() + } + + _hideProgressOverlay() { + if (this._progressOverlayArea) this._progressOverlayArea.destroy() + + this._progressOverlayArea = null + this._progressbar_background = null + this._progressbar_border = null + } + + _updateProgressOverlay() { + if (this._progressOverlayArea) { + this._progressOverlayArea.queue_repaint() } + } - _onEntryAdded(appProgress, entry) { - if (!entry || !entry.appId()) - return; - if (this._source && this._source.app && this._source.app.id == entry.appId()) { - this.insertEntry(entry); - } + _drawProgressOverlay(area) { + let scaleFactor = Utils.getScaleFactor() + let [surfaceWidth, surfaceHeight] = area.get_surface_size() + let cr = area.get_context() + + let iconSize = this._source.icon.iconSize * scaleFactor + + let x = Math.floor((surfaceWidth - iconSize) / 2) + let y = Math.floor((surfaceHeight - iconSize) / 2) + + let lineWidth = Math.floor(1.0 * scaleFactor) + let padding = Math.floor(iconSize * 0.05) + let width = iconSize - 2.0 * padding + let height = Math.floor(Math.min(18.0 * scaleFactor, 0.2 * iconSize)) + x += padding + y += iconSize - height - padding + + cr.setLineWidth(lineWidth) + + // Draw the outer stroke + let stroke = new Cairo.LinearGradient(0, y, 0, y + height) + let fill = null + stroke.addColorStopRGBA(0.5, 0.5, 0.5, 0.5, 0.1) + stroke.addColorStopRGBA(0.9, 0.8, 0.8, 0.8, 0.4) + Utils.drawRoundedLine( + cr, + x + lineWidth / 2.0, + y + lineWidth / 2.0, + width, + height, + true, + true, + stroke, + fill, + ) + + // Draw the background + x += lineWidth + y += lineWidth + width -= 2.0 * lineWidth + height -= 2.0 * lineWidth + + stroke = Cairo.SolidPattern.createRGBA(0.2, 0.2, 0.2, 0.9) + fill = new Cairo.LinearGradient(0, y, 0, y + height) + fill.addColorStopRGBA(0.4, 0.25, 0.25, 0.25, 1.0) + fill.addColorStopRGBA(0.9, 0.35, 0.35, 0.35, 1.0) + Utils.drawRoundedLine( + cr, + x + lineWidth / 2.0, + y + lineWidth / 2.0, + width, + height, + true, + true, + stroke, + fill, + ) + + // Draw the finished bar + x += lineWidth + y += lineWidth + width -= 2.0 * lineWidth + height -= 2.0 * lineWidth + + let finishedWidth = Math.ceil(this._progress * width) + + let bg = this._progressbar_background + let bd = this._progressbar_border + + stroke = Cairo.SolidPattern.createRGBA( + bd.red / 255, + bd.green / 255, + bd.blue / 255, + bd.alpha / 255, + ) + fill = Cairo.SolidPattern.createRGBA( + bg.red / 255, + bg.green / 255, + bg.blue / 255, + bg.alpha / 255, + ) + + if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) + Utils.drawRoundedLine( + cr, + x + lineWidth / 2.0 + width - finishedWidth, + y + lineWidth / 2.0, + finishedWidth, + height, + true, + true, + stroke, + fill, + ) + else + Utils.drawRoundedLine( + cr, + x + lineWidth / 2.0, + y + lineWidth / 2.0, + finishedWidth, + height, + true, + true, + stroke, + fill, + ) + + cr.$dispose() + } + + setProgress(progress) { + this._progress = Math.min(Math.max(progress, 0.0), 1.0) + this._updateProgressOverlay() + } + + toggleProgressOverlay(activate) { + if (activate) { + this._showProgressOverlay() + } else { + this._hideProgressOverlay() } + } - _onEntryRemoved(appProgress, entry) { - if (!entry || !entry.appId()) - return; + insertEntry(appProgress) { + if ( + !appProgress || + this._progressManagerEntries.indexOf(appProgress) !== -1 + ) + return - if (this._source && this._source.app && this._source.app.id == entry.appId()) { - this.removeEntry(entry); - } + this._progressManagerEntries.push(appProgress) + this._selectEntry(appProgress) + } + + removeEntry(appProgress) { + if (!appProgress || this._progressManagerEntries.indexOf(appProgress) == -1) + return + + this._progressManagerEntries.splice( + this._progressManagerEntries.indexOf(appProgress), + 1, + ) + + if (this._progressManagerEntries.length > 0) { + this._selectEntry( + this._progressManagerEntries[this._progressManagerEntries.length - 1], + ) + } else { + this.setNotificationBadge(0) + this.toggleNotificationBadge(false) + this.setProgress(0) + this.toggleProgressOverlay(false) + this.setUrgent(false) } + } - updateNotificationBadge() { - this._source.updateNumberOverlay(this._notificationBadgeBin); - this._notificationBadgeLabel.clutter_text.ellipsize = Pango.EllipsizeMode.MIDDLE; + _selectEntry(appProgress) { + if (!appProgress) return + + this._signalsHandler.removeWithLabel('progress-entry') + + this._signalsHandler.addWithLabel( + 'progress-entry', + [ + appProgress, + 'count-changed', + (appProgress, value) => { + this.setNotificationBadge(value) + }, + ], + [ + appProgress, + 'count-visible-changed', + (appProgress, value) => { + this.toggleNotificationBadge(value) + }, + ], + [ + appProgress, + 'progress-changed', + (appProgress, value) => { + this.setProgress(value) + }, + ], + [ + appProgress, + 'progress-visible-changed', + (appProgress, value) => { + this.toggleProgressOverlay(value) + }, + ], + [ + appProgress, + 'urgent-changed', + (appProgress, value) => { + this.setUrgent(value) + }, + ], + ) + + this.setNotificationBadge(appProgress.count()) + this.toggleNotificationBadge(appProgress.countVisible()) + this.setProgress(appProgress.progress()) + this.toggleProgressOverlay(appProgress.progressVisible()) + + this._isUrgent = false + } + + setUrgent(urgent) { + const icon = this._source.icon._iconBin + if (urgent) { + if (!this._isUrgent) { + icon.set_pivot_point(0.5, 0.5) + this._source.iconAnimator.addAnimation(icon, 'dance') + this._isUrgent = true + } + } else { + if (this._isUrgent) { + this._source.iconAnimator.removeAnimation(icon, 'dance') + this._isUrgent = false + } + icon.rotation_angle_z = 0 } - - _notificationBadgeCountToText(count) { - if (count <= 9999) { - return count.toString(); - } else if (count < 1e5) { - let thousands = count / 1e3; - return thousands.toFixed(1).toString() + "k"; - } else if (count < 1e6) { - let thousands = count / 1e3; - return thousands.toFixed(0).toString() + "k"; - } else if (count < 1e8) { - let millions = count / 1e6; - return millions.toFixed(1).toString() + "M"; - } else if (count < 1e9) { - let millions = count / 1e6; - return millions.toFixed(0).toString() + "M"; - } else { - let billions = count / 1e9; - return billions.toFixed(1).toString() + "B"; - } - } - - setNotificationBadge(count) { - this._notificationBadgeCount = count; - let text = this._notificationBadgeCountToText(count); - this._notificationBadgeLabel.set_text(text); - } - - toggleNotificationBadge(activate) { - if (activate && this._notificationBadgeCount > 0) { - this.updateNotificationBadge(); - this._notificationBadgeBin.show(); - } - else - this._notificationBadgeBin.hide(); - } - - _showProgressOverlay() { - if (this._progressOverlayArea) { - this._updateProgressOverlay(); - return; - } - - this._progressOverlayArea = new St.DrawingArea({x_expand: true, y_expand: true}); - this._progressOverlayArea.add_style_class_name('progress-bar'); - this._progressOverlayArea.connect('repaint', () => { - this._drawProgressOverlay(this._progressOverlayArea); - }); - - this._source._iconContainer.add_child(this._progressOverlayArea); - let node = this._progressOverlayArea.get_theme_node(); - - let [hasColor, color] = node.lookup_color('-progress-bar-background', false); - if (hasColor) - this._progressbar_background = color - else - this._progressbar_background = new Utils.ColorUtils.Color({red: 204, green: 204, blue: 204, alpha: 255}); - - [hasColor, color] = node.lookup_color('-progress-bar-border', false); - if (hasColor) - this._progressbar_border = color; - else - this._progressbar_border = new Utils.ColorUtils.Color({red: 230, green: 230, blue: 230, alpha: 255}); - - this._updateProgressOverlay(); - } - - _hideProgressOverlay() { - if (this._progressOverlayArea) - this._progressOverlayArea.destroy(); - - this._progressOverlayArea = null; - this._progressbar_background = null; - this._progressbar_border = null; - } - - _updateProgressOverlay() { - - if (this._progressOverlayArea) { - this._progressOverlayArea.queue_repaint(); - } - } - - _drawProgressOverlay(area) { - let scaleFactor = Utils.getScaleFactor(); - let [surfaceWidth, surfaceHeight] = area.get_surface_size(); - let cr = area.get_context(); - - let iconSize = this._source.icon.iconSize * scaleFactor; - - let x = Math.floor((surfaceWidth - iconSize) / 2); - let y = Math.floor((surfaceHeight - iconSize) / 2); - - let lineWidth = Math.floor(1.0 * scaleFactor); - let padding = Math.floor(iconSize * 0.05); - let width = iconSize - 2.0*padding; - let height = Math.floor(Math.min(18.0*scaleFactor, 0.20*iconSize)); - x += padding; - y += iconSize - height - padding; - - cr.setLineWidth(lineWidth); - - // Draw the outer stroke - let stroke = new Cairo.LinearGradient(0, y, 0, y + height); - let fill = null; - stroke.addColorStopRGBA(0.5, 0.5, 0.5, 0.5, 0.1); - stroke.addColorStopRGBA(0.9, 0.8, 0.8, 0.8, 0.4); - Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, width, height, true, true, stroke, fill); - - // Draw the background - x += lineWidth; - y += lineWidth; - width -= 2.0*lineWidth; - height -= 2.0*lineWidth; - - stroke = Cairo.SolidPattern.createRGBA(0.20, 0.20, 0.20, 0.9); - fill = new Cairo.LinearGradient(0, y, 0, y + height); - fill.addColorStopRGBA(0.4, 0.25, 0.25, 0.25, 1.0); - fill.addColorStopRGBA(0.9, 0.35, 0.35, 0.35, 1.0); - Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, width, height, true, true, stroke, fill); - - // Draw the finished bar - x += lineWidth; - y += lineWidth; - width -= 2.0*lineWidth; - height -= 2.0*lineWidth; - - let finishedWidth = Math.ceil(this._progress * width); - - let bg = this._progressbar_background; - let bd = this._progressbar_border; - - stroke = Cairo.SolidPattern.createRGBA(bd.red/255, bd.green/255, bd.blue/255, bd.alpha/255); - fill = Cairo.SolidPattern.createRGBA(bg.red/255, bg.green/255, bg.blue/255, bg.alpha/255); - - if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) - Utils.drawRoundedLine(cr, x + lineWidth/2.0 + width - finishedWidth, y + lineWidth/2.0, finishedWidth, height, true, true, stroke, fill); - else - Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, finishedWidth, height, true, true, stroke, fill); - - cr.$dispose(); - } - - setProgress(progress) { - this._progress = Math.min(Math.max(progress, 0.0), 1.0); - this._updateProgressOverlay(); - } - - toggleProgressOverlay(activate) { - if (activate) { - this._showProgressOverlay(); - } - else { - this._hideProgressOverlay(); - } - } - - insertEntry(appProgress) { - if (!appProgress || this._progressManagerEntries.indexOf(appProgress) !== -1) - return; - - this._progressManagerEntries.push(appProgress); - this._selectEntry(appProgress); - } - - removeEntry(appProgress) { - if (!appProgress || this._progressManagerEntries.indexOf(appProgress) == -1) - return; - - this._progressManagerEntries.splice(this._progressManagerEntries.indexOf(appProgress), 1); - - if (this._progressManagerEntries.length > 0) { - this._selectEntry(this._progressManagerEntries[this._progressManagerEntries.length-1]); - } else { - this.setNotificationBadge(0); - this.toggleNotificationBadge(false); - this.setProgress(0); - this.toggleProgressOverlay(false); - this.setUrgent(false); - } - } - - _selectEntry(appProgress) { - if (!appProgress) - return; - - this._signalsHandler.removeWithLabel('progress-entry'); - - this._signalsHandler.addWithLabel('progress-entry', - [ - appProgress, - 'count-changed', - (appProgress, value) => { - this.setNotificationBadge(value); - } - ], [ - appProgress, - 'count-visible-changed', - (appProgress, value) => { - this.toggleNotificationBadge(value); - } - ], [ - appProgress, - 'progress-changed', - (appProgress, value) => { - this.setProgress(value); - } - ], [ - appProgress, - 'progress-visible-changed', - (appProgress, value) => { - this.toggleProgressOverlay(value); - } - ], [ - appProgress, - 'urgent-changed', - (appProgress, value) => { - this.setUrgent(value) - } - ]); - - this.setNotificationBadge(appProgress.count()); - this.toggleNotificationBadge(appProgress.countVisible()); - this.setProgress(appProgress.progress()); - this.toggleProgressOverlay(appProgress.progressVisible()); - - this._isUrgent = false; - } - - setUrgent(urgent) { - const icon = this._source.icon._iconBin; - if (urgent) { - if (!this._isUrgent) { - icon.set_pivot_point(0.5, 0.5); - this._source.iconAnimator.addAnimation(icon, 'dance'); - this._isUrgent = true; - } - } else { - if (this._isUrgent) { - this._source.iconAnimator.removeAnimation(icon, 'dance'); - this._isUrgent = false; - } - icon.rotation_angle_z = 0; - } - } -}; + } +} diff --git a/proximity.js b/proximity.js index eff1548..91d8232 100644 --- a/proximity.js +++ b/proximity.js @@ -15,242 +15,267 @@ * along with this program. If not, see . */ -import Meta from 'gi://Meta'; -import Mtk from 'gi://Mtk'; +import Meta from 'gi://Meta' +import Mtk from 'gi://Mtk' -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js' -import * as Utils from './utils.js'; +import * as Utils from './utils.js' //timeout intervals -const MIN_UPDATE_MS = 200; +const MIN_UPDATE_MS = 200 //timeout names -const T1 = 'limitUpdateTimeout'; +const T1 = 'limitUpdateTimeout' export const Mode = { - ALL_WINDOWS: 0, - FOCUSED_WINDOWS: 1, - MAXIMIZED_WINDOWS: 2 -}; + ALL_WINDOWS: 0, + FOCUSED_WINDOWS: 1, + MAXIMIZED_WINDOWS: 2, +} export class ProximityWatch { + constructor(actor, monitorIndex, mode, xThreshold, yThreshold, handler) { + this.actor = actor + this.monitorIndex = monitorIndex + this.overlap = false + this.mode = mode + this.threshold = [xThreshold, yThreshold] + this.handler = handler - constructor(actor, monitorIndex, mode, xThreshold, yThreshold, handler) { - this.actor = actor; - this.monitorIndex = monitorIndex - this.overlap = false; - this.mode = mode; - this.threshold = [xThreshold, yThreshold]; - this.handler = handler; + this._allocationChangedId = actor.connect('notify::allocation', () => + this._updateWatchRect(), + ) - this._allocationChangedId = actor.connect('notify::allocation', () => this._updateWatchRect()); + this._updateWatchRect() + } - this._updateWatchRect(); - } + destroy() { + this.actor.disconnect(this._allocationChangedId) + } - destroy() { - this.actor.disconnect(this._allocationChangedId); - } + _updateWatchRect() { + let [actorX, actorY] = this.actor.get_position() - _updateWatchRect() { - let [actorX, actorY] = this.actor.get_position(); - - this.rect = new Mtk.Rectangle({ - x: actorX - this.threshold[0], - y: actorY - this.threshold[1], - width: this.actor.width + this.threshold[0] * 2, - height: this.actor.height + this.threshold[1] * 2 - }); - } -}; + this.rect = new Mtk.Rectangle({ + x: actorX - this.threshold[0], + y: actorY - this.threshold[1], + width: this.actor.width + this.threshold[0] * 2, + height: this.actor.height + this.threshold[1] * 2, + }) + } +} export const ProximityManager = class { + constructor() { + this._counter = 1 + this._watches = {} + this._focusedWindowInfo = null - constructor() { - this._counter = 1; - this._watches = {}; - this._focusedWindowInfo = null; + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._timeoutsHandler = new Utils.TimeoutsHandler(); + this._bindSignals() + this._setFocusedWindow() + } - this._bindSignals(); - this._setFocusedWindow(); + createWatch(actor, monitorIndex, mode, xThreshold, yThreshold, handler) { + let watch = new ProximityWatch( + actor, + monitorIndex, + mode, + xThreshold, + yThreshold, + handler, + ) + + this._watches[this._counter] = watch + this.update() + + return this._counter++ + } + + removeWatch(id) { + if (this._watches[id]) { + this._watches[id].destroy() + delete this._watches[id] } + } - createWatch(actor, monitorIndex, mode, xThreshold, yThreshold, handler) { - let watch = new ProximityWatch(actor, monitorIndex, mode, xThreshold, yThreshold, handler); + update() { + this._queueUpdate(true) + } - this._watches[this._counter] = watch; - this.update(); - - return this._counter++; + destroy() { + this._signalsHandler.destroy() + this._timeoutsHandler.destroy() + this._disconnectFocusedWindow() + Object.keys(this._watches).forEach((id) => this.removeWatch(id)) + } + + _bindSignals() { + this._signalsHandler.add( + [global.window_manager, 'switch-workspace', () => this._queueUpdate()], + [Main.overview, 'hidden', () => this._queueUpdate()], + [ + global.display, + 'notify::focus-window', + () => { + this._setFocusedWindow() + this._queueUpdate() + }, + ], + [global.display, 'restacked', () => this._queueUpdate()], + ) + } + + _setFocusedWindow() { + this._disconnectFocusedWindow() + + let focusedWindow = global.display.focus_window + + if (focusedWindow) { + let focusedWindowInfo = this._getFocusedWindowInfo(focusedWindow) + + if ( + focusedWindowInfo && + this._checkIfHandledWindowType(focusedWindowInfo.metaWindow) + ) { + focusedWindowInfo.allocationId = focusedWindowInfo.window.connect( + 'notify::allocation', + () => this._queueUpdate(), + ) + focusedWindowInfo.destroyId = focusedWindowInfo.window.connect( + 'destroy', + () => this._disconnectFocusedWindow(true), + ) + + this._focusedWindowInfo = focusedWindowInfo + } } + } - removeWatch(id) { - if (this._watches[id]) { - this._watches[id].destroy(); - delete this._watches[id]; + _getFocusedWindowInfo(focusedWindow) { + let window = focusedWindow.get_compositor_private() + let focusedWindowInfo + + if (window) { + focusedWindowInfo = { window: window } + focusedWindowInfo.metaWindow = focusedWindow + + if (focusedWindow.is_attached_dialog()) { + let mainMetaWindow = focusedWindow.get_transient_for() + + if ( + focusedWindowInfo.metaWindow.get_frame_rect().height < + mainMetaWindow.get_frame_rect().height + ) { + focusedWindowInfo.window = mainMetaWindow.get_compositor_private() + focusedWindowInfo.metaWindow = mainMetaWindow } + } } - update() { - this._queueUpdate(true); + return focusedWindowInfo + } + + _disconnectFocusedWindow(destroy) { + if (this._focusedWindowInfo && !destroy) { + this._focusedWindowInfo.window.disconnect( + this._focusedWindowInfo.allocationId, + ) + this._focusedWindowInfo.window.disconnect( + this._focusedWindowInfo.destroyId, + ) } - destroy() { - this._signalsHandler.destroy(); - this._timeoutsHandler.destroy(); - this._disconnectFocusedWindow(); - Object.keys(this._watches).forEach(id => this.removeWatch(id)); + this._focusedWindowInfo = null + } + + _getHandledWindows() { + return Utils.getCurrentWorkspace() + .list_windows() + .filter((mw) => this._checkIfHandledWindow(mw)) + } + + _checkIfHandledWindow(metaWindow) { + return ( + metaWindow && + !metaWindow.minimized && + !metaWindow.customJS_ding && + this._checkIfHandledWindowType(metaWindow) + ) + } + + _checkIfHandledWindowType(metaWindow) { + let metaWindowType = metaWindow.get_window_type() + + //https://www.roojs.org/seed/gir-1.2-gtk-3.0/seed/Meta.WindowType.html + return ( + metaWindowType <= Meta.WindowType.SPLASHSCREEN && + metaWindowType != Meta.WindowType.DESKTOP + ) + } + + _queueUpdate(noDelay) { + if (!noDelay && this._timeoutsHandler.getId(T1)) { + //limit the number of updates + this._pendingUpdate = true + return } - _bindSignals() { - this._signalsHandler.add( - [ - global.window_manager, - 'switch-workspace', - () => this._queueUpdate() - ], - [ - Main.overview, - 'hidden', - () => this._queueUpdate() - ], - [ - global.display, - 'notify::focus-window', - () => { - this._setFocusedWindow(); - this._queueUpdate(); - } - ], - [ - global.display, - 'restacked', - () => this._queueUpdate() - ] - ); + this._timeoutsHandler.add([T1, MIN_UPDATE_MS, () => this._endLimitUpdate()]) + + let metaWindows = this._getHandledWindows() + + Object.keys(this._watches).forEach((id) => { + let watch = this._watches[id] + let overlap = !!this._update(watch, metaWindows) + + if (overlap !== watch.overlap) { + watch.handler(overlap) + watch.overlap = overlap + } + }) + } + + _endLimitUpdate() { + if (this._pendingUpdate) { + this._pendingUpdate = false + this._queueUpdate() } + } - _setFocusedWindow() { - this._disconnectFocusedWindow(); + _update(watch, metaWindows) { + if (watch.mode === Mode.FOCUSED_WINDOWS) + return ( + this._focusedWindowInfo && + this._checkIfHandledWindow(this._focusedWindowInfo.metaWindow) && + this._checkProximity(this._focusedWindowInfo.metaWindow, watch) + ) - let focusedWindow = global.display.focus_window; + if (watch.mode === Mode.MAXIMIZED_WINDOWS) + return metaWindows.some( + (mw) => + mw.maximized_vertically && + mw.maximized_horizontally && + mw.get_monitor() == watch.monitorIndex, + ) - if (focusedWindow) { - let focusedWindowInfo = this._getFocusedWindowInfo(focusedWindow); + //Mode.ALL_WINDOWS + return metaWindows.some((mw) => this._checkProximity(mw, watch)) + } - if (focusedWindowInfo && this._checkIfHandledWindowType(focusedWindowInfo.metaWindow)) { - focusedWindowInfo.allocationId = focusedWindowInfo.window.connect('notify::allocation', () => this._queueUpdate()); - focusedWindowInfo.destroyId = focusedWindowInfo.window.connect('destroy', () => this._disconnectFocusedWindow(true)); - - this._focusedWindowInfo = focusedWindowInfo; - } - } - } + _checkProximity(metaWindow, watch) { + let windowRect = metaWindow.get_frame_rect() - _getFocusedWindowInfo(focusedWindow) { - let window = focusedWindow.get_compositor_private(); - let focusedWindowInfo; - - if (window) { - focusedWindowInfo = { window: window }; - focusedWindowInfo.metaWindow = focusedWindow; - - if (focusedWindow.is_attached_dialog()) { - let mainMetaWindow = focusedWindow.get_transient_for(); - - if (focusedWindowInfo.metaWindow.get_frame_rect().height < mainMetaWindow.get_frame_rect().height) { - focusedWindowInfo.window = mainMetaWindow.get_compositor_private(); - focusedWindowInfo.metaWindow = mainMetaWindow; - } - } - } - - return focusedWindowInfo; - } - - _disconnectFocusedWindow(destroy) { - if (this._focusedWindowInfo && !destroy) { - this._focusedWindowInfo.window.disconnect(this._focusedWindowInfo.allocationId); - this._focusedWindowInfo.window.disconnect(this._focusedWindowInfo.destroyId); - } - - this._focusedWindowInfo = null; - } - - _getHandledWindows() { - return Utils.getCurrentWorkspace() - .list_windows() - .filter(mw => this._checkIfHandledWindow(mw)); - } - - _checkIfHandledWindow(metaWindow) { - return metaWindow && - !metaWindow.minimized && - !metaWindow.customJS_ding && - this._checkIfHandledWindowType(metaWindow); - } - - _checkIfHandledWindowType(metaWindow) { - let metaWindowType = metaWindow.get_window_type(); - - //https://www.roojs.org/seed/gir-1.2-gtk-3.0/seed/Meta.WindowType.html - return metaWindowType <= Meta.WindowType.SPLASHSCREEN && - metaWindowType != Meta.WindowType.DESKTOP; - } - - _queueUpdate(noDelay) { - if (!noDelay && this._timeoutsHandler.getId(T1)) { - //limit the number of updates - this._pendingUpdate = true; - return; - } - - this._timeoutsHandler.add([T1, MIN_UPDATE_MS, () => this._endLimitUpdate()]); - - let metaWindows = this._getHandledWindows(); - - Object.keys(this._watches).forEach(id => { - let watch = this._watches[id]; - let overlap = !!this._update(watch, metaWindows); - - if (overlap !== watch.overlap) { - watch.handler(overlap); - watch.overlap = overlap; - } - }); - } - - _endLimitUpdate() { - if (this._pendingUpdate) { - this._pendingUpdate = false; - this._queueUpdate(); - } - } - - _update(watch, metaWindows) { - if (watch.mode === Mode.FOCUSED_WINDOWS) - return (this._focusedWindowInfo && - this._checkIfHandledWindow(this._focusedWindowInfo.metaWindow) && - this._checkProximity(this._focusedWindowInfo.metaWindow, watch)); - - if (watch.mode === Mode.MAXIMIZED_WINDOWS) - return metaWindows.some(mw => mw.maximized_vertically && mw.maximized_horizontally && - mw.get_monitor() == watch.monitorIndex); - - //Mode.ALL_WINDOWS - return metaWindows.some(mw => this._checkProximity(mw, watch)); - } - - _checkProximity(metaWindow, watch) { - let windowRect = metaWindow.get_frame_rect(); - - return windowRect.overlap(watch.rect) && - ((!watch.threshold[0] && !watch.threshold[1]) || - metaWindow.get_monitor() == watch.monitorIndex || - windowRect.overlap(global.display.get_monitor_geometry(watch.monitorIndex))); - } -}; + return ( + windowRect.overlap(watch.rect) && + ((!watch.threshold[0] && !watch.threshold[1]) || + metaWindow.get_monitor() == watch.monitorIndex || + windowRect.overlap( + global.display.get_monitor_geometry(watch.monitorIndex), + )) + ) + } +} diff --git a/taskbar.js b/taskbar.js index 9b63cba..68bae37 100644 --- a/taskbar.js +++ b/taskbar.js @@ -21,34 +21,33 @@ * Some code was also adapted from the upstream Gnome Shell source code. */ +import Clutter from 'gi://Clutter' +import Gio from 'gi://Gio' +import GLib from 'gi://GLib' +import GObject from 'gi://GObject' +import Graphene from 'gi://Graphene' +import Shell from 'gi://Shell' +import St from 'gi://St' -import Clutter from 'gi://Clutter'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import Graphene from 'gi://Graphene'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; +import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js' +import * as Dash from 'resource:///org/gnome/shell/ui/dash.js' +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' -import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js'; -import * as Dash from 'resource:///org/gnome/shell/ui/dash.js'; -import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; +import * as AppIcons from './appIcons.js' +import * as PanelManager from './panelManager.js' +import * as PanelSettings from './panelSettings.js' +import * as Pos from './panelPositions.js' +import * as Utils from './utils.js' +import * as WindowPreview from './windowPreview.js' +import { DTP_EXTENSION, SETTINGS } from './extension.js' -import * as AppIcons from './appIcons.js'; -import * as PanelManager from './panelManager.js'; -import * as PanelSettings from './panelSettings.js'; -import * as Pos from './panelPositions.js'; -import * as Utils from './utils.js'; -import * as WindowPreview from './windowPreview.js'; -import {DTP_EXTENSION, SETTINGS} from './extension.js'; +const SearchController = Main.overview.searchController -const SearchController = Main.overview.searchController; - -export const DASH_ANIMATION_TIME = .2; // Dash.DASH_ANIMATION_TIME is now private -const DASH_ITEM_HOVER_TIMEOUT = .3; // Dash.DASH_ITEM_HOVER_TIMEOUT is now private -export const MIN_ICON_SIZE = 4; +export const DASH_ANIMATION_TIME = 0.2 // Dash.DASH_ANIMATION_TIME is now private +const DASH_ITEM_HOVER_TIMEOUT = 0.3 // Dash.DASH_ITEM_HOVER_TIMEOUT is now private +export const MIN_ICON_SIZE = 4 const T1 = 'ensureAppIconVisibilityTimeout' const T2 = 'showLabelTimeout' @@ -56,58 +55,62 @@ const T3 = 'resetHoverTimeout' const T4 = 'donateAppTimeout' let donateDummyApp = { - hideDetails: true, - app_info: { - should_show: () => false, - list_actions: () => ['opts'], - get_action_name: (action) => action == 'opts' ? _('Donation options') : '', - }, - connect: () => [], - connectObject: () => [], - get_id: () => 'dtp_donate', - get_windows: () => [], - can_open_new_window: () => false, - launch_action: function(action) { action == 'opts' ? this.activate() : null }, - get_name: function() { - return this.isActive() ? _('Thank you!') : _('Please donate :)') - }, - create_icon_texture: function(size) { - let iconParams = { - icon_name: this.isActive() ? 'face-smile-big-symbolic' : 'emote-love-symbolic', - icon_size: size - } - - if (SETTINGS.get_string('appicon-style') !== 'SYMBOLIC') - iconParams.style = `color: ${this.isActive() ? '#FFC730' : '#C71807'}` - - return new St.Icon(iconParams) - }, - activate: function() { - SETTINGS.set_string('target-prefs-page', 'donation') - - if (this.isActive()) - return - - DTP_EXTENSION.openPreferences() - this._taskbar._timeoutsHandler.add([T4, 5000, this.forceRefresh.bind(this)]) - this.forceRefresh() - }, - forceRefresh: function() { - setDonateApp.call(this._taskbar) - this._taskbar._queueRedisplay() - }, - isActive: function() { - return !!this._taskbar._timeoutsHandler.getId(T4) + hideDetails: true, + app_info: { + should_show: () => false, + list_actions: () => ['opts'], + get_action_name: (action) => + action == 'opts' ? _('Donation options') : '', + }, + connect: () => [], + connectObject: () => [], + get_id: () => 'dtp_donate', + get_windows: () => [], + can_open_new_window: () => false, + launch_action: function (action) { + action == 'opts' ? this.activate() : null + }, + get_name: function () { + return this.isActive() ? _('Thank you!') : _('Please donate :)') + }, + create_icon_texture: function (size) { + let iconParams = { + icon_name: this.isActive() + ? 'face-smile-big-symbolic' + : 'emote-love-symbolic', + icon_size: size, } + + if (SETTINGS.get_string('appicon-style') !== 'SYMBOLIC') + iconParams.style = `color: ${this.isActive() ? '#FFC730' : '#C71807'}` + + return new St.Icon(iconParams) + }, + activate: function () { + SETTINGS.set_string('target-prefs-page', 'donation') + + if (this.isActive()) return + + DTP_EXTENSION.openPreferences() + this._taskbar._timeoutsHandler.add([T4, 5000, this.forceRefresh.bind(this)]) + this.forceRefresh() + }, + forceRefresh: function () { + setDonateApp.call(this._taskbar) + this._taskbar._queueRedisplay() + }, + isActive: function () { + return !!this._taskbar._timeoutsHandler.getId(T4) + }, } function setDonateApp() { - delete this._donateApp + delete this._donateApp - if (!SETTINGS.get_string('hide-donate-icon-unixtime')) { - this._donateApp = Object.create(donateDummyApp) - this._donateApp._taskbar = this - } + if (!SETTINGS.get_string('hide-donate-icon-unixtime')) { + this._donateApp = Object.create(donateDummyApp) + this._donateApp._taskbar = this + } } /** @@ -120,46 +123,57 @@ function setDonateApp() { */ export function extendDashItemContainer(dashItemContainer) { - dashItemContainer.showLabel = AppIcons.ItemShowLabel; + dashItemContainer.showLabel = AppIcons.ItemShowLabel } const iconAnimationSettings = { - _getDictValue(key) { - let type = SETTINGS.get_string('animate-appicon-hover-animation-type'); - return SETTINGS.get_value(key).deep_unpack()[type] || 0; - }, + _getDictValue(key) { + let type = SETTINGS.get_string('animate-appicon-hover-animation-type') + return SETTINGS.get_value(key).deep_unpack()[type] || 0 + }, - get type() { - if (!SETTINGS.get_boolean('animate-appicon-hover')) - return ""; + get type() { + if (!SETTINGS.get_boolean('animate-appicon-hover')) return '' - return SETTINGS.get_string('animate-appicon-hover-animation-type'); - }, + return SETTINGS.get_string('animate-appicon-hover-animation-type') + }, - get convexity() { - return Math.max(0, this._getDictValue('animate-appicon-hover-animation-convexity')); - }, + get convexity() { + return Math.max( + 0, + this._getDictValue('animate-appicon-hover-animation-convexity'), + ) + }, - get duration() { - return this._getDictValue('animate-appicon-hover-animation-duration'); - }, + get duration() { + return this._getDictValue('animate-appicon-hover-animation-duration') + }, - get extent() { - return Math.max(1, this._getDictValue('animate-appicon-hover-animation-extent')); - }, + get extent() { + return Math.max( + 1, + this._getDictValue('animate-appicon-hover-animation-extent'), + ) + }, - get rotation() { - return this._getDictValue('animate-appicon-hover-animation-rotation'); - }, + get rotation() { + return this._getDictValue('animate-appicon-hover-animation-rotation') + }, - get travel() { - return Math.max(-1, this._getDictValue('animate-appicon-hover-animation-travel')); - }, + get travel() { + return Math.max( + -1, + this._getDictValue('animate-appicon-hover-animation-travel'), + ) + }, - get zoom() { - return Math.max(0.5, this._getDictValue('animate-appicon-hover-animation-zoom')); - }, -}; + get zoom() { + return Math.max( + 0.5, + this._getDictValue('animate-appicon-hover-animation-zoom'), + ) + }, +} /* This class is a fork of the upstream DashActor class (ui.dash.js) * @@ -167,72 +181,98 @@ const iconAnimationSettings = { * - modified chldBox calculations for when 'show-apps-at-top' option is checked * - handle horizontal dash */ -export const TaskbarActor = GObject.registerClass({ -}, class TaskbarActor extends St.Widget { +export const TaskbarActor = GObject.registerClass( + {}, + class TaskbarActor extends St.Widget { _init(delegate) { - this._delegate = delegate; - this._currentBackgroundColor = 0; - super._init({ name: 'dashtopanelTaskbar', - layout_manager: new Clutter.BoxLayout({ orientation: Clutter.Orientation[delegate.dtpPanel.getOrientation().toUpperCase()] }), - clip_to_allocation: true }); + this._delegate = delegate + this._currentBackgroundColor = 0 + super._init({ + name: 'dashtopanelTaskbar', + layout_manager: new Clutter.BoxLayout({ + orientation: + Clutter.Orientation[ + delegate.dtpPanel.getOrientation().toUpperCase() + ], + }), + clip_to_allocation: true, + }) } vfunc_allocate(box) { - this.set_allocation(box); + this.set_allocation(box) - let panel = this._delegate.dtpPanel; - let availFixedSize = box[panel.fixedCoord.c2] - box[panel.fixedCoord.c1]; - let availVarSize = box[panel.varCoord.c2] - box[panel.varCoord.c1]; - let [dummy, scrollview, leftFade, rightFade] = this.get_children(); - let [, natSize] = this[panel.sizeFunc](availFixedSize); - let childBox = new Clutter.ActorBox(); - let orientation = panel.getOrientation(); + let panel = this._delegate.dtpPanel + let availFixedSize = box[panel.fixedCoord.c2] - box[panel.fixedCoord.c1] + let availVarSize = box[panel.varCoord.c2] - box[panel.varCoord.c1] + let [dummy, scrollview, leftFade, rightFade] = this.get_children() + let [, natSize] = this[panel.sizeFunc](availFixedSize) + let childBox = new Clutter.ActorBox() + let orientation = panel.getOrientation() - dummy.allocate(childBox); + dummy.allocate(childBox) - childBox[panel.varCoord.c1] = box[panel.varCoord.c1]; - childBox[panel.varCoord.c2] = Math.min(availVarSize, natSize); - childBox[panel.fixedCoord.c1] = box[panel.fixedCoord.c1]; - childBox[panel.fixedCoord.c2] = box[panel.fixedCoord.c2]; + childBox[panel.varCoord.c1] = box[panel.varCoord.c1] + childBox[panel.varCoord.c2] = Math.min(availVarSize, natSize) + childBox[panel.fixedCoord.c1] = box[panel.fixedCoord.c1] + childBox[panel.fixedCoord.c2] = box[panel.fixedCoord.c2] - scrollview.allocate(childBox); + scrollview.allocate(childBox) - let [value, , upper, , , pageSize] = scrollview[orientation[0] + 'adjustment'].get_values(); - upper = Math.floor(upper); - scrollview._dtpFadeSize = upper > pageSize ? this._delegate.iconSize : 0; + let [value, , upper, , , pageSize] = + scrollview[orientation[0] + 'adjustment'].get_values() + upper = Math.floor(upper) + scrollview._dtpFadeSize = upper > pageSize ? this._delegate.iconSize : 0 - if (this._currentBackgroundColor !== panel.dynamicTransparency.currentBackgroundColor) { - this._currentBackgroundColor = panel.dynamicTransparency.currentBackgroundColor; - let gradientStyle = 'background-gradient-start: ' + this._currentBackgroundColor + - 'background-gradient-direction: ' + orientation; + if ( + this._currentBackgroundColor !== + panel.dynamicTransparency.currentBackgroundColor + ) { + this._currentBackgroundColor = + panel.dynamicTransparency.currentBackgroundColor + let gradientStyle = + 'background-gradient-start: ' + + this._currentBackgroundColor + + 'background-gradient-direction: ' + + orientation - leftFade.set_style(gradientStyle); - rightFade.set_style(gradientStyle); - } + leftFade.set_style(gradientStyle) + rightFade.set_style(gradientStyle) + } - childBox[panel.varCoord.c2] = childBox[panel.varCoord.c1] + (value > 0 ? scrollview._dtpFadeSize : 0); - leftFade.allocate(childBox); + childBox[panel.varCoord.c2] = + childBox[panel.varCoord.c1] + (value > 0 ? scrollview._dtpFadeSize : 0) + leftFade.allocate(childBox) - childBox[panel.varCoord.c1] = box[panel.varCoord.c2] - (value + pageSize < upper ? scrollview._dtpFadeSize : 0); - childBox[panel.varCoord.c2] = box[panel.varCoord.c2]; - rightFade.allocate(childBox); + childBox[panel.varCoord.c1] = + box[panel.varCoord.c2] - + (value + pageSize < upper ? scrollview._dtpFadeSize : 0) + childBox[panel.varCoord.c2] = box[panel.varCoord.c2] + rightFade.allocate(childBox) } // We want to request the natural size of all our children // as our natural width, so we chain up to StWidget (which // then calls BoxLayout) vfunc_get_preferred_width(forHeight) { - let [, natWidth] = St.Widget.prototype.vfunc_get_preferred_width.call(this, forHeight); + let [, natWidth] = St.Widget.prototype.vfunc_get_preferred_width.call( + this, + forHeight, + ) - return [0, natWidth]; + return [0, natWidth] } vfunc_get_preferred_height(forWidth) { - let [, natHeight] = St.Widget.prototype.vfunc_get_preferred_height.call(this, forWidth); + let [, natHeight] = St.Widget.prototype.vfunc_get_preferred_height.call( + this, + forWidth, + ) - return [0, natHeight]; + return [0, natHeight] } -}); + }, +) /* This class is a fork of the upstream dash class (ui.dash.js) * @@ -249,1380 +289,1582 @@ export const TaskbarActor = GObject.registerClass({ */ export const Taskbar = class extends EventEmitter { + constructor(panel) { + super() - constructor(panel) { - super(); + this.dtpPanel = panel - this.dtpPanel = panel; + // start at smallest size due to running indicator drawing area expanding but not shrinking + this.iconSize = 16 - // start at smallest size due to running indicator drawing area expanding but not shrinking - this.iconSize = 16; + this._shownInitially = false - this._shownInitially = false; + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._timeoutsHandler = new Utils.TimeoutsHandler(); + this._labelShowing = false + this.fullScrollView = 0 - this._labelShowing = false; - this.fullScrollView = 0; + let isVertical = panel.checkIfVertical() - let isVertical = panel.checkIfVertical(); + this._box = new St.BoxLayout({ + vertical: isVertical, + clip_to_allocation: false, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + }) - this._box = new St.BoxLayout({ vertical: isVertical, - clip_to_allocation: false, - x_align: Clutter.ActorAlign.START, - y_align: Clutter.ActorAlign.START }); + this._container = new TaskbarActor(this) + this._scrollView = new St.ScrollView({ + name: 'dashtopanelScrollview', + hscrollbar_policy: St.PolicyType.NEVER, + vscrollbar_policy: St.PolicyType.NEVER, + enable_mouse_scrolling: true, + }) - this._container = new TaskbarActor(this); - this._scrollView = new St.ScrollView({ name: 'dashtopanelScrollview', - hscrollbar_policy: St.PolicyType.NEVER, - vscrollbar_policy: St.PolicyType.NEVER, - enable_mouse_scrolling: true }); + this._scrollView.connect('leave-event', this._onLeaveEvent.bind(this)) + this._scrollView.connect('motion-event', this._onMotionEvent.bind(this)) + this._scrollView.connect('scroll-event', this._onScrollEvent.bind(this)) + this._scrollView.add_child(this._box) - this._scrollView.connect('leave-event', this._onLeaveEvent.bind(this)); - this._scrollView.connect('motion-event', this._onMotionEvent.bind(this)); - this._scrollView.connect('scroll-event', this._onScrollEvent.bind(this)); - this._scrollView.add_child(this._box); + this._showAppsIconWrapper = panel.showAppsIconWrapper + this._showAppsIconWrapper.connect( + 'menu-state-changed', + (showAppsIconWrapper, opened) => { + this._itemMenuStateChanged(showAppsIconWrapper, opened) + }, + ) + // an instance of the showAppsIcon class is encapsulated in the wrapper + this._showAppsIcon = this._showAppsIconWrapper.realShowAppsIcon + this.showAppsButton = this._showAppsIcon.toggleButton - this._showAppsIconWrapper = panel.showAppsIconWrapper; - this._showAppsIconWrapper.connect('menu-state-changed', (showAppsIconWrapper, opened) => { - this._itemMenuStateChanged(showAppsIconWrapper, opened); - }); - // an instance of the showAppsIcon class is encapsulated in the wrapper - this._showAppsIcon = this._showAppsIconWrapper.realShowAppsIcon; - this.showAppsButton = this._showAppsIcon.toggleButton; + if (isVertical) { + this.showAppsButton.set_width(panel.geom.w) + } - if (isVertical) { - this.showAppsButton.set_width(panel.geom.w); - } + this.showAppsButton.connect( + 'notify::checked', + this._onShowAppsButtonToggled.bind(this), + ) - this.showAppsButton.connect('notify::checked', this._onShowAppsButtonToggled.bind(this)); + this.showAppsButton.checked = SearchController._showAppsButton + ? SearchController._showAppsButton.checked + : false - this.showAppsButton.checked = (SearchController._showAppsButton) ? SearchController._showAppsButton.checked : false; + this._showAppsIcon.childScale = 1 + this._showAppsIcon.childOpacity = 255 + this._showAppsIcon.icon.setIconSize(this.iconSize) + this._hookUpLabel(this._showAppsIcon, this._showAppsIconWrapper) - this._showAppsIcon.childScale = 1; - this._showAppsIcon.childOpacity = 255; - this._showAppsIcon.icon.setIconSize(this.iconSize); - this._hookUpLabel(this._showAppsIcon, this._showAppsIconWrapper); + this._container.add_child(new St.Widget({ width: 0, reactive: false })) + this._container.add_child(this._scrollView) - this._container.add_child(new St.Widget({ width: 0, reactive: false })); - this._container.add_child(this._scrollView); + let orientation = panel.getOrientation() + let fadeStyle = 'background-gradient-direction:' + orientation + let fade1 = new St.Widget({ + style_class: 'scrollview-fade', + reactive: false, + }) + let fade2 = new St.Widget({ + style_class: 'scrollview-fade', + reactive: false, + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + rotation_angle_z: 180, + }) - let orientation = panel.getOrientation(); - let fadeStyle = 'background-gradient-direction:' + orientation; - let fade1 = new St.Widget({ style_class: 'scrollview-fade', reactive: false }); - let fade2 = new St.Widget({ style_class: 'scrollview-fade', - reactive: false, - pivot_point: new Graphene.Point({ x: .5, y: .5 }), - rotation_angle_z: 180 }); + fade1.set_style(fadeStyle) + fade2.set_style(fadeStyle) - fade1.set_style(fadeStyle); - fade2.set_style(fadeStyle); + this._container.add_child(fade1) + this._container.add_child(fade2) - this._container.add_child(fade1); - this._container.add_child(fade2); + this.previewMenu = new WindowPreview.PreviewMenu(panel) + this.previewMenu.enable() - this.previewMenu = new WindowPreview.PreviewMenu(panel); - this.previewMenu.enable(); + let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL + this.actor = new St.Bin({ + child: this._container, + y_align: Clutter.ActorAlign.START, + x_align: rtl ? Clutter.ActorAlign.END : Clutter.ActorAlign.START, + }) - let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; - this.actor = new St.Bin({ - child: this._container, - y_align: Clutter.ActorAlign.START, - x_align: rtl ? Clutter.ActorAlign.END : Clutter.ActorAlign.START - }); + const adjustment = this._scrollView[orientation[0] + 'adjustment'] - const adjustment = this._scrollView[orientation[0] + 'adjustment']; + this._workId = Main.initializeDeferredWork( + this._box, + this._redisplay.bind(this), + ) - this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this)); + this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }) - this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); + this._appSystem = Shell.AppSystem.get_default() - this._appSystem = Shell.AppSystem.get_default(); + this.iconAnimator = new PanelManager.IconAnimator(this.dtpPanel.panel) - this.iconAnimator = new PanelManager.IconAnimator(this.dtpPanel.panel); + this._signalsHandler.add( + [this.dtpPanel.panel, 'notify::height', () => this._queueRedisplay()], + [this.dtpPanel.panel, 'notify::width', () => this._queueRedisplay()], + [ + this._appSystem, + 'installed-changed', + () => { + AppFavorites.getAppFavorites().reload() + this._queueRedisplay() + }, + ], + [this._appSystem, 'app-state-changed', this._queueRedisplay.bind(this)], + [ + AppFavorites.getAppFavorites(), + 'changed', + this._queueRedisplay.bind(this), + ], + [ + global.window_manager, + 'switch-workspace', + () => this._connectWorkspaceSignals(), + ], + [ + Utils.DisplayWrapper.getScreen(), + ['window-entered-monitor', 'window-left-monitor'], + () => { + if (SETTINGS.get_boolean('isolate-monitors')) { + this._queueRedisplay() + } + }, + ], + [Main.overview, 'item-drag-begin', this._onDragBegin.bind(this)], + [Main.overview, 'item-drag-end', this._onDragEnd.bind(this)], + [Main.overview, 'item-drag-cancelled', this._onDragCancelled.bind(this)], + [ + // Ensure the ShowAppsButton status is kept in sync + SearchController._showAppsButton, + 'notify::checked', + this._syncShowAppsButtonToggled.bind(this), + ], + [ + SETTINGS, + [ + 'changed::dot-size', + 'changed::show-favorites', + 'changed::show-running-apps', + 'changed::show-favorites-all-monitors', + 'changed::hide-donate-icon-unixtime', + ], + () => { + setAttributes() + this._redisplay() + }, + ], + [ + SETTINGS, + 'changed::group-apps', + () => { + setAttributes() + this._connectWorkspaceSignals() + }, + ], + [ + SETTINGS, + [ + 'changed::appicon-style', + 'changed::group-apps-use-launchers', + 'changed::taskbar-locked', + ], + () => { + setAttributes() + this.resetAppIcons() + }, + ], + [ + adjustment, + ['notify::upper', 'notify::pageSize'], + () => this._onScrollSizeChange(adjustment), + ], + ) - this._signalsHandler.add( - [ - this.dtpPanel.panel, - 'notify::height', - () => this._queueRedisplay() - ], - [ - this.dtpPanel.panel, - 'notify::width', - () => this._queueRedisplay() - ], - [ - this._appSystem, - 'installed-changed', - () => { - AppFavorites.getAppFavorites().reload(); - this._queueRedisplay(); - } - ], - [ - this._appSystem, - 'app-state-changed', - this._queueRedisplay.bind(this) - ], - [ - AppFavorites.getAppFavorites(), - 'changed', - this._queueRedisplay.bind(this) - ], - [ - global.window_manager, - 'switch-workspace', - () => this._connectWorkspaceSignals() - ], - [ - Utils.DisplayWrapper.getScreen(), - [ - 'window-entered-monitor', - 'window-left-monitor' - ], - () => { - if (SETTINGS.get_boolean('isolate-monitors')) { - this._queueRedisplay(); - } - } - ], - [ - Main.overview, - 'item-drag-begin', - this._onDragBegin.bind(this) - ], - [ - Main.overview, - 'item-drag-end', - this._onDragEnd.bind(this) - ], - [ - Main.overview, - 'item-drag-cancelled', - this._onDragCancelled.bind(this) - ], - [ - // Ensure the ShowAppsButton status is kept in sync - SearchController._showAppsButton, - 'notify::checked', - this._syncShowAppsButtonToggled.bind(this) - ], - [ - SETTINGS, - [ - 'changed::dot-size', - 'changed::show-favorites', - 'changed::show-running-apps', - 'changed::show-favorites-all-monitors', - 'changed::hide-donate-icon-unixtime' - ], - () => { - setAttributes() - this._redisplay() - } - ], - [ - SETTINGS, - 'changed::group-apps', - () => { - setAttributes() - this._connectWorkspaceSignals(); - } - ], - [ - SETTINGS, - [ - 'changed::appicon-style', - 'changed::group-apps-use-launchers', - 'changed::taskbar-locked' - ], - () => { - setAttributes() - this.resetAppIcons() - } - ], - [ - adjustment, - [ - 'notify::upper', - 'notify::pageSize' - ], - () => this._onScrollSizeChange(adjustment) + let setAttributes = () => { + this.isGroupApps = SETTINGS.get_boolean('group-apps') + this.usingLaunchers = + !this.isGroupApps && SETTINGS.get_boolean('group-apps-use-launchers') + this.showFavorites = + SETTINGS.get_boolean('show-favorites') && + (this.dtpPanel.isPrimary || + SETTINGS.get_boolean('show-favorites-all-monitors')) + this.showRunningApps = SETTINGS.get_boolean('show-running-apps') + this.allowSplitApps = + this.usingLaunchers || (!this.isGroupApps && !this.showFavorites) + + setDonateApp.call(this) + } + + setAttributes() + + this._onScrollSizeChange(adjustment) + this._connectWorkspaceSignals() + } + + destroy() { + if (this._waitIdleId) { + GLib.source_remove(this._waitIdleId) + this._waitIdleId = 0 + } + + this._timeoutsHandler.destroy() + this.iconAnimator.destroy() + + this._signalsHandler.destroy() + this._signalsHandler = 0 + + this._container.destroy() + + this.previewMenu.disable() + this.previewMenu.destroy() + + this._disconnectWorkspaceSignals() + } + + _dropIconAnimations() { + this._getTaskbarIcons().forEach((item) => { + item.raise(0) + item.stretch(0) + }) + } + + _updateIconAnimations(pointerX, pointerY) { + this._iconAnimationTimestamp = Date.now() + let type = iconAnimationSettings.type + + if (!pointerX || !pointerY) [pointerX, pointerY] = global.get_pointer() + + this._getTaskbarIcons().forEach((item) => { + let [x, y] = item.get_transformed_position() + let [width, height] = item.get_transformed_size() + let [centerX, centerY] = [x + width / 2, y + height / 2] + let size = this._box.vertical ? height : width + let difference = this._box.vertical + ? pointerY - centerY + : pointerX - centerX + let distance = Math.abs(difference) + let maxDistance = (iconAnimationSettings.extent / 2) * size + + if (type == 'PLANK') { + // Make the position stable for items that are far from the pointer. + let translation = + distance <= maxDistance + ? distance / (2 + (8 * distance) / maxDistance) + : // the previous expression with distance = maxDistance + maxDistance / 10 + + if (difference > 0) translation *= -1 + + item.stretch(translation) + } + + if (distance <= maxDistance) { + let level = (maxDistance - distance) / maxDistance + level = Math.pow(level, iconAnimationSettings.convexity) + item.raise(level) + } else { + item.raise(0) + } + }) + } + + _onLeaveEvent(actor) { + let [stageX, stageY] = global.get_pointer() + let [success, x, y] = actor.transform_stage_point(stageX, stageY) + if ( + success && + !actor.allocation.contains(x, y) && + (iconAnimationSettings.type == 'RIPPLE' || + iconAnimationSettings.type == 'PLANK') + ) + this._dropIconAnimations() + + return Clutter.EVENT_PROPAGATE + } + + _onMotionEvent(actor_, event) { + if ( + iconAnimationSettings.type == 'RIPPLE' || + iconAnimationSettings.type == 'PLANK' + ) { + let timestamp = Date.now() + if ( + !this._iconAnimationTimestamp || + timestamp - this._iconAnimationTimestamp >= + iconAnimationSettings.duration / 2 + ) { + let [pointerX, pointerY] = event.get_coords() + this._updateIconAnimations(pointerX, pointerY) + } + } + + return Clutter.EVENT_PROPAGATE + } + + _onScrollEvent(actor, event) { + let orientation = this.dtpPanel.getOrientation() + + // reset timeout to avid conflicts with the mousehover event + this._timeoutsHandler.add([T1, 0, () => (this._swiping = false)]) + + // Skip to avoid double events mouse + if (event.is_pointer_emulated()) return Clutter.EVENT_STOP + + let adjustment, delta + + adjustment = this._scrollView[orientation[0] + 'adjustment'] + + let increment = adjustment.step_increment + + switch (event.get_scroll_direction()) { + case Clutter.ScrollDirection.UP: + case Clutter.ScrollDirection.LEFT: + delta = -increment + break + case Clutter.ScrollDirection.DOWN: + case Clutter.ScrollDirection.RIGHT: + delta = +increment + break + case Clutter.ScrollDirection.SMOOTH: + let [dx, dy] = event.get_scroll_delta() + delta = dy * increment + delta += dx * increment + break + } + + adjustment.set_value(adjustment.get_value() + delta) + + return Clutter.EVENT_STOP + } + + _onScrollSizeChange(adjustment) { + // Update minimization animation target position on scrollview change. + this._updateAppIcons() + + // When applications are ungrouped and there is some empty space on the horizontal taskbar, + // force a fixed label width to prevent the icons from "wiggling" when an animation runs + // (adding or removing an icon). When the taskbar is full, revert to a dynamic label width + // to allow them to resize and make room for new icons. + if (!this.dtpPanel.checkIfVertical() && !this.isGroupApps) { + let initial = this.fullScrollView + + if ( + !this.fullScrollView && + Math.floor(adjustment.upper) > adjustment.page_size + ) { + this.fullScrollView = adjustment.page_size + } else if (adjustment.page_size < this.fullScrollView) { + this.fullScrollView = 0 + } + + if (initial != this.fullScrollView && !this._waitIdleId) { + this._waitIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + this._getAppIcons().forEach((a) => a.updateTitleStyle()) + this._waitIdleId = 0 + + return GLib.SOURCE_REMOVE + }) + } + } + } + + _onDragBegin() { + this._dragCancelled = false + this._dragMonitor = { + dragMotion: this._onDragMotion.bind(this), + } + DND.addDragMonitor(this._dragMonitor) + + if (this._box.get_n_children() == 0) { + this._emptyDropTarget = new Dash.EmptyDropTargetItem() + this._box.insert_child_at_index(this._emptyDropTarget, 0) + this._emptyDropTarget.show(true) + } + + this._toggleFavoriteHighlight(true) + } + + _onDragCancelled() { + this._dragCancelled = true + + if (this._dragInfo) { + this._box.set_child_at_index( + this._dragInfo[1]._dashItemContainer, + this._dragInfo[0], + ) + } + + this._endDrag() + } + + _onDragEnd() { + if (this._dragCancelled) return + + this._endDrag() + } + + _endDrag() { + if ( + this._dragInfo && + this._dragInfo[1]._dashItemContainer instanceof DragPlaceholderItem + ) { + this._box.remove_child(this._dragInfo[1]._dashItemContainer) + this._dragInfo[1]._dashItemContainer.destroy() + delete this._dragInfo[1]._dashItemContainer + } + + this._dragInfo = null + this._clearEmptyDropTarget() + this._showAppsIcon.setDragApp(null) + DND.removeDragMonitor(this._dragMonitor) + + this._dragMonitor = null + this.emit('end-drag') + + this._toggleFavoriteHighlight() + } + + _onDragMotion(dragEvent) { + let app = Dash.Dash.getAppFromSource(dragEvent.source) + if (app == null) return DND.DragMotionResult.CONTINUE + + let showAppsHovered = this._showAppsIcon.contains(dragEvent.targetActor) + + if (showAppsHovered) this._showAppsIcon.setDragApp(app) + else this._showAppsIcon.setDragApp(null) + + return DND.DragMotionResult.CONTINUE + } + + _toggleFavoriteHighlight(show) { + let appFavorites = AppFavorites.getAppFavorites() + let cssFuncName = (show ? 'add' : 'remove') + '_style_class_name' + + if (this.showFavorites) + this._getAppIcons() + .filter( + (appIcon) => + (this.usingLaunchers && appIcon.isLauncher) || + (!this.usingLaunchers && + appFavorites.isFavorite(appIcon.app.get_id())), + ) + .forEach((fav) => fav._container[cssFuncName]('favorite')) + } + + handleIsolatedWorkspaceSwitch() { + this._shownInitially = this.isGroupApps + this._queueRedisplay() + } + + _connectWorkspaceSignals() { + this._disconnectWorkspaceSignals() + + this._lastWorkspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + + this._workspaceWindowAddedId = this._lastWorkspace.connect( + 'window-added', + () => this._queueRedisplay(), + ) + this._workspaceWindowRemovedId = this._lastWorkspace.connect( + 'window-removed', + () => this._queueRedisplay(), + ) + } + + _disconnectWorkspaceSignals() { + if (this._lastWorkspace) { + this._lastWorkspace.disconnect(this._workspaceWindowAddedId) + this._lastWorkspace.disconnect(this._workspaceWindowRemovedId) + + this._lastWorkspace = null + } + } + + _queueRedisplay() { + Main.queueDeferredWork(this._workId) + } + + _hookUpLabel(item, syncHandler) { + item.child.connect('notify::hover', () => { + this._syncLabel(item, syncHandler) + }) + + syncHandler.connect('sync-tooltip', () => { + this._syncLabel(item, syncHandler) + }) + } + + _createAppItem(app, window, isLauncher) { + let appIcon = new AppIcons.TaskbarAppIcon( + { + app, + window, + isLauncher, + }, + this.dtpPanel, + { + setSizeManually: true, + showLabel: false, + isDraggable: !SETTINGS.get_boolean('taskbar-locked'), + }, + this.previewMenu, + this.iconAnimator, + ) + + if (appIcon._draggable) { + appIcon._draggable.connect('drag-begin', () => { + appIcon.opacity = 0 + appIcon.isDragged = 1 + this._dropIconAnimations() + }) + appIcon._draggable.connect('drag-end', () => { + appIcon.opacity = 255 + delete appIcon.isDragged + this._updateAppIcons() + }) + } + + appIcon.connect('menu-state-changed', (appIcon, opened) => { + this._itemMenuStateChanged(item, opened) + }) + + let item = new TaskbarItemContainer() + + item._dtpPanel = this.dtpPanel + extendDashItemContainer(item) + + item.setChild(appIcon) + appIcon._dashItemContainer = item + + appIcon.connect('notify::hover', () => { + if (appIcon.hover) { + this._timeoutsHandler.add([ + T1, + 100, + () => + Utils.ensureActorVisibleInScrollView( + this._scrollView, + appIcon, + this._scrollView._dtpFadeSize, + ), + ]) + + if (!appIcon.isDragged && iconAnimationSettings.type == 'SIMPLE') + appIcon.get_parent().raise(1) + else if ( + !appIcon.isDragged && + (iconAnimationSettings.type == 'RIPPLE' || + iconAnimationSettings.type == 'PLANK') + ) + this._updateIconAnimations() + } else { + this._timeoutsHandler.remove(T1) + + if (!appIcon.isDragged && iconAnimationSettings.type == 'SIMPLE') + appIcon.get_parent().raise(0) + } + }) + + appIcon.connect('clicked', (actor) => { + Utils.ensureActorVisibleInScrollView( + this._scrollView, + actor, + this._scrollView._dtpFadeSize, + ) + }) + + appIcon.connect('key-focus-in', (actor) => { + let [x_shift, y_shift] = Utils.ensureActorVisibleInScrollView( + this._scrollView, + actor, + this._scrollView._dtpFadeSize, + ) + + // This signal is triggered also by mouse click. The popup menu is opened at the original + // coordinates. Thus correct for the shift which is going to be applied to the scrollview. + if (appIcon._menu) { + appIcon._menu._boxPointer.xOffset = -x_shift + appIcon._menu._boxPointer.yOffset = -y_shift + } + }) + + // 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 + } + + // Return an array with the "proper" appIcons currently in the taskbar + _getAppIcons() { + // Only consider children which are "proper" icons and which are not + // animating out (which means they will be destroyed at the end of + // the animation) + return this._getTaskbarIcons().map(function (actor) { + return actor.child._delegate + }) + } + + _getTaskbarIcons(includeAnimated) { + return this._box.get_children().filter(function (actor) { + return ( + actor.child && + actor.child._delegate && + actor.child._delegate.icon && + (includeAnimated || !actor.animatingOut) + ) + }) + } + + _updateAppIcons() { + let appIcons = this._getAppIcons() + + appIcons + .filter((icon) => icon.constructor === AppIcons.TaskbarAppIcon) + .forEach((icon) => { + icon.updateIcon() + }) + } + + _itemMenuStateChanged(item, opened) { + // When the menu closes, it calls sync_hover, which means + // that the notify::hover handler does everything we need to. + if (opened) { + this._timeoutsHandler.remove(T2) + + item.hideLabel() + } else { + // I want to listen from outside when a menu is closed. I used to + // add a custom signal to the appIcon, since gnome 3.8 the signal + // calling this callback was added upstream. + this.emit('menu-closed') + + // The icon menu grabs the events and, once it is closed, the pointer is maybe + // no longer over the taskbar and the animations are not dropped. + if ( + iconAnimationSettings.type == 'RIPPLE' || + iconAnimationSettings.type == 'PLANK' + ) { + this._scrollView.sync_hover() + if (!this._scrollView.hover) this._dropIconAnimations() + } + } + } + + _syncLabel(item, syncHandler) { + let shouldShow = syncHandler + ? syncHandler.shouldShowTooltip() + : item.child.get_hover() + + if (shouldShow) { + if (!this._timeoutsHandler.getId(T2)) { + let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT + + this._timeoutsHandler.add([ + T2, + timeout, + () => { + this._labelShowing = true + item.showLabel() + }, + ]) + + this._timeoutsHandler.remove(T3) + } + } else { + this._timeoutsHandler.remove(T2) + + item.hideLabel() + if (this._labelShowing) { + this._timeoutsHandler.add([ + T3, + DASH_ITEM_HOVER_TIMEOUT, + () => (this._labelShowing = false), + ]) + } + } + } + + _adjustIconSize() { + const thisMonitorIndex = this.dtpPanel.monitor.index + let panelSize = PanelSettings.getPanelSize(SETTINGS, thisMonitorIndex) + let availSize = panelSize - SETTINGS.get_int('appicon-padding') * 2 + let minIconSize = MIN_ICON_SIZE + (panelSize % 2) + + if (availSize == this.iconSize) return + + if (availSize < minIconSize) { + availSize = minIconSize + } + + // For the icon size, we only consider children which are "proper" + // icons and which are not animating out (which means they will be + // destroyed at the end of the animation) + let iconChildren = this._getTaskbarIcons().concat([this._showAppsIcon]) + let scale = this.iconSize / availSize + + this.iconSize = availSize + + 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, or when initially filling + // the taskbar + if (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) + + Utils.animate(icon.icon, { + width: targetWidth, + height: targetHeight, + time: DASH_ANIMATION_TIME, + transition: 'easeOutQuad', + }) + } + } + + sortAppsCompareFunction(appA, appB) { + return ( + getAppStableSequence(appA, this.dtpPanel.monitor) - + getAppStableSequence(appB, this.dtpPanel.monitor) + ) + } + + getAppInfos() { + //get the user's favorite apps + let favoriteApps = this.showFavorites + ? AppFavorites.getAppFavorites().getFavorites() + : [] + + //find the apps that should be in the taskbar: the favorites first, then add the running apps + // When using isolation, we filter out apps that have no windows in + // the current workspace (this check is done in AppIcons.getInterestingWindows) + let runningApps = this.showRunningApps + ? this._getRunningApps().sort(this.sortAppsCompareFunction.bind(this)) + : [] + let appInfos + + if (this.allowSplitApps) { + appInfos = this._createAppInfos(favoriteApps, [], true).concat( + this._createAppInfos(runningApps).filter( + (appInfo) => appInfo.windows.length, + ), + ) + } else { + appInfos = this._createAppInfos( + favoriteApps.concat( + runningApps.filter((app) => favoriteApps.indexOf(app) < 0), + ), + ).filter( + (appInfo) => + appInfo.windows.length || favoriteApps.indexOf(appInfo.app) >= 0, + ) + } + + if (this._donateApp) + appInfos = [ + { + app: this._donateApp, + isLauncher: true, + windows: [], + }, + ...appInfos, + ] + + return appInfos + } + + _redisplay() { + if (!this._signalsHandler) { + return + } + + //get the currently displayed appIcons + let currentAppIcons = this._getTaskbarIcons() + let expectedAppInfos = this.getAppInfos() + + //remove the appIcons which are not in the expected apps list + for (let i = currentAppIcons.length - 1; i > -1; --i) { + let appIcon = currentAppIcons[i].child._delegate + let appIndex = Utils.findIndex( + expectedAppInfos, + (appInfo) => + appInfo.app == appIcon.app && + (!this.allowSplitApps || + this.isGroupApps || + appInfo.windows[0] == appIcon.window) && + appInfo.isLauncher == appIcon.isLauncher, + ) + + if ( + appIndex < 0 || + (appIcon.window && + (this.isGroupApps || + expectedAppInfos[appIndex].windows.indexOf(appIcon.window) < 0)) || + (!appIcon.window && + !appIcon.isLauncher && + !this.isGroupApps && + expectedAppInfos[appIndex].windows.length) + ) { + currentAppIcons[i][ + this._shownInitially ? 'animateOutAndDestroy' : 'destroy' + ]() + currentAppIcons.splice(i, 1) + } + } + + //if needed, reorder the existing appIcons and create the missing ones + let currentPosition = 0 + for (let i = 0, l = expectedAppInfos.length; i < l; ++i) { + let neededAppIcons = + this.isGroupApps || !expectedAppInfos[i].windows.length + ? [ + { + app: expectedAppInfos[i].app, + window: null, + isLauncher: expectedAppInfos[i].isLauncher, + }, ] - ); + : expectedAppInfos[i].windows.map((window) => ({ + app: expectedAppInfos[i].app, + window: window, + isLauncher: false, + })) - let setAttributes = () => { - this.isGroupApps = SETTINGS.get_boolean('group-apps'); - this.usingLaunchers = !this.isGroupApps && SETTINGS.get_boolean('group-apps-use-launchers'); - this.showFavorites = SETTINGS.get_boolean('show-favorites') && - (this.dtpPanel.isPrimary || SETTINGS.get_boolean('show-favorites-all-monitors')) - this.showRunningApps = SETTINGS.get_boolean('show-running-apps') - this.allowSplitApps = this.usingLaunchers || (!this.isGroupApps && !this.showFavorites) - - setDonateApp.call(this) + for (let j = 0, ll = neededAppIcons.length; j < ll; ++j) { + //check if the icon already exists + let matchingAppIconIndex = Utils.findIndex( + currentAppIcons, + (appIcon) => + appIcon.child._delegate.app == neededAppIcons[j].app && + appIcon.child._delegate.window == neededAppIcons[j].window, + ) + + if ( + matchingAppIconIndex > 0 && + matchingAppIconIndex != currentPosition + ) { + //moved icon, reposition it + this._box.remove_child(currentAppIcons[matchingAppIconIndex]) + this._box.insert_child_at_index( + currentAppIcons[matchingAppIconIndex], + currentPosition, + ) + } else if (matchingAppIconIndex < 0) { + //the icon doesn't exist yet, create a new one + let newAppIcon = this._createAppItem( + neededAppIcons[j].app, + neededAppIcons[j].window, + neededAppIcons[j].isLauncher, + ) + + this._box.insert_child_at_index(newAppIcon, currentPosition) + currentAppIcons.splice(currentPosition, 0, newAppIcon) + + // Skip animations on first run when adding the initial set + // of items, to avoid all items zooming in at once + newAppIcon.show(this._shownInitially) } - setAttributes() - - this._onScrollSizeChange(adjustment); - this._connectWorkspaceSignals(); + ++currentPosition + } } - destroy() { - if (this._waitIdleId) { - GLib.source_remove(this._waitIdleId); - this._waitIdleId = 0; - } + this._adjustIconSize() - this._timeoutsHandler.destroy(); - this.iconAnimator.destroy(); + // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 + // Without it, StBoxLayout may use a stale size cache + this._box.queue_relayout() - this._signalsHandler.destroy(); - this._signalsHandler = 0; + // This is required for icon reordering when the scrollview is used. + this._updateAppIcons() - this._container.destroy(); - - this.previewMenu.disable(); - this.previewMenu.destroy(); - - this._disconnectWorkspaceSignals(); + // This will update the size, and the corresponding number for each icon on the primary panel + if (this.dtpPanel.isPrimary) { + this._updateNumberOverlay() } - _dropIconAnimations() { - this._getTaskbarIcons().forEach(item => { - item.raise(0); - item.stretch(0); - }); + this._shownInitially = true + } + + _getRunningApps() { + let tracker = Shell.WindowTracker.get_default() + let windows = global.get_window_actors() + let apps = [] + + for (let i = 0, l = windows.length; i < l; ++i) { + let app = tracker.get_window_app(windows[i].metaWindow) + + if (app && apps.indexOf(app) < 0) { + apps.push(app) + } } - _updateIconAnimations(pointerX, pointerY) { - this._iconAnimationTimestamp = Date.now(); - let type = iconAnimationSettings.type; + return apps + } - if (!pointerX || !pointerY) - [pointerX, pointerY] = global.get_pointer(); + _createAppInfos(apps, defaultWindows, defaultIsLauncher) { + if (this.allowSplitApps && !defaultIsLauncher) { + let separateApps = [] - this._getTaskbarIcons().forEach(item => { - let [x, y] = item.get_transformed_position(); - let [width, height] = item.get_transformed_size(); - let [centerX, centerY] = [x + width / 2, y + height / 2]; - let size = this._box.vertical ? height : width; - let difference = this._box.vertical ? pointerY - centerY : pointerX - centerX; - let distance = Math.abs(difference); - let maxDistance = (iconAnimationSettings.extent / 2) * size; + if (apps.length) { + let tracker = Shell.WindowTracker.get_default() + let windows = AppIcons.getInterestingWindows( + null, + this.dtpPanel.monitor, + ).sort(sortWindowsCompareFunction) - if (type == 'PLANK') { - // Make the position stable for items that are far from the pointer. - let translation = distance <= maxDistance ? - distance / (2 + 8 * distance / maxDistance) : - // the previous expression with distance = maxDistance - maxDistance / 10; + windows.forEach((w) => { + let windowApp = tracker.get_window_app(w) - if (difference > 0) - translation *= -1; + if (apps.indexOf(windowApp) >= 0) + separateApps.push({ + app: windowApp, + isLauncher: false, + windows: [w], + }) + }) + } - item.stretch(translation); + return separateApps + } + + return apps.map((app) => ({ + app: app, + isLauncher: defaultIsLauncher || false, + windows: + defaultWindows || + AppIcons.getInterestingWindows(app, this.dtpPanel.monitor).sort( + sortWindowsCompareFunction, + ), + })) + } + + // Reset the displayed apps icon to mantain the correct order + resetAppIcons(geometryChange) { + let children = this._getTaskbarIcons(true) + + for (let i = 0; i < children.length; i++) { + let item = children[i] + item.destroy() + } + + // to avoid ugly animations, just suppress them like when taskbar is first loaded. + this._shownInitially = false + this._redisplay() + + if (geometryChange && this.dtpPanel.checkIfVertical()) { + this.previewMenu._updateClip() + } + } + + _updateNumberOverlay() { + let seenApps = {} + let counter = 0 + + this._getAppIcons().forEach((icon) => { + if (!seenApps[icon.app] || this.allowSplitApps) { + seenApps[icon.app] = 1 + counter++ + } + + if (counter <= 10) { + icon.setNumberOverlay(counter == 10 ? 0 : counter) + } else { + // No overlay after 10 + icon.setNumberOverlay(-1) + } + + icon.updateHotkeyNumberOverlay() + }) + + if ( + SETTINGS.get_boolean('hot-keys') && + SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS' + ) + this.toggleNumberOverlay(true) + } + + toggleNumberOverlay(activate) { + let appIcons = this._getAppIcons() + appIcons.forEach(function (icon) { + icon.toggleNumberOverlay(activate) + }) + } + + _clearEmptyDropTarget() { + if (this._emptyDropTarget) { + this._emptyDropTarget.animateOutAndDestroy() + this._emptyDropTarget = null + } + } + + handleDragOver(source, actor, x, y, time) { + if (source == Main.xdndHandler) return DND.DragMotionResult.CONTINUE + + // Don't allow favoriting of transient apps + if (source.app == null || source.app.is_window_backed()) + return DND.DragMotionResult.NO_DROP + + if (!this._settings.is_writable('favorite-apps')) + return DND.DragMotionResult.NO_DROP + + let isVertical = this.dtpPanel.checkIfVertical() + + if (!this._box.contains(source) && !source._dashItemContainer) { + //not an appIcon of the taskbar, probably from the applications view + source._dashItemContainer = new DragPlaceholderItem( + source, + this.iconSize, + isVertical, + ) + this._box.insert_child_above(source._dashItemContainer, null) + } + + let sizeProp = isVertical ? 'height' : 'width' + let posProp = isVertical ? 'y' : 'x' + let pos = isVertical ? y : x + + let currentAppIcons = this._getAppIcons() + let sourceIndex = currentAppIcons.indexOf(source) + let hoveredIndex = Utils.findIndex( + currentAppIcons, + (appIcon) => + pos >= appIcon._dashItemContainer[posProp] && + pos <= + appIcon._dashItemContainer[posProp] + + appIcon._dashItemContainer[sizeProp], + ) + + if (!this._dragInfo) { + this._dragInfo = [sourceIndex, source] + } + + if (hoveredIndex >= 0) { + let isLeft = + pos < + currentAppIcons[hoveredIndex]._dashItemContainer[posProp] + + currentAppIcons[hoveredIndex]._dashItemContainer[sizeProp] * 0.5 + let prevIcon = currentAppIcons[hoveredIndex - 1] + let nextIcon = currentAppIcons[hoveredIndex + 1] + + // Don't allow positioning before or after self and between icons of same app if ungrouped and showing favorites + if ( + !( + hoveredIndex === sourceIndex || + (isLeft && hoveredIndex - 1 == sourceIndex) || + (!this.allowSplitApps && + isLeft && + hoveredIndex - 1 >= 0 && + source.app != prevIcon.app && + prevIcon.app == currentAppIcons[hoveredIndex].app) || + (!isLeft && hoveredIndex + 1 == sourceIndex) || + (!this.allowSplitApps && + !isLeft && + hoveredIndex + 1 < currentAppIcons.length && + source.app != nextIcon.app && + nextIcon.app == currentAppIcons[hoveredIndex].app) + ) + ) { + this._box.set_child_at_index(source._dashItemContainer, hoveredIndex) + + // Ensure the next and previous icon are visible when moving the icon + // (I assume there's room for both of them) + if (hoveredIndex > 1) + Utils.ensureActorVisibleInScrollView( + this._scrollView, + this._box.get_children()[hoveredIndex - 1], + this._scrollView._dtpFadeSize, + ) + if (hoveredIndex < this._box.get_children().length - 1) + Utils.ensureActorVisibleInScrollView( + this._scrollView, + this._box.get_children()[hoveredIndex + 1], + this._scrollView._dtpFadeSize, + ) + } + } + + return this._dragInfo[0] !== sourceIndex + ? DND.DragMotionResult.MOVE_DROP + : DND.DragMotionResult.CONTINUE + } + + // Draggable target interface + acceptDrop(source, actor, x, y, time) { + // Don't allow favoriting of transient apps + if ( + !this._dragInfo || + !source.app || + source.app.is_window_backed() || + !this._settings.is_writable('favorite-apps') + ) { + return false + } + + let appIcons = this._getAppIcons() + let sourceIndex = appIcons.indexOf(source) + let usingLaunchers = !this.isGroupApps && this.usingLaunchers + + // dragging the icon to its original position + if (this._dragInfo[0] === sourceIndex) { + return true + } + + let appFavorites = AppFavorites.getAppFavorites() + let sourceAppId = source.app.get_id() + let appIsFavorite = + this.showFavorites && appFavorites.isFavorite(sourceAppId) + let replacingIndex = + sourceIndex + (sourceIndex > this._dragInfo[0] ? -1 : 1) + let favoriteIndex = + replacingIndex >= 0 + ? appFavorites.getFavorites().indexOf(appIcons[replacingIndex].app) + : 0 + let sameApps = this.allowSplitApps + ? [] + : appIcons.filter((a) => a != source && a.app == source.app) + let favoritesCount = 0 + let position = 0 + let interestingWindows = {} + let getAppWindows = (app) => { + if (!interestingWindows[app]) { + interestingWindows[app] = AppIcons.getInterestingWindows( + app, + this.dtpPanel.monitor, + ) + } + + let appWindows = interestingWindows[app] //prevents "reference to undefined property Symbol.toPrimitive" warning + return appWindows + } + + if ( + sameApps.length && + (!appIcons[sourceIndex - 1] || + appIcons[sourceIndex - 1].app !== source.app) && + (!appIcons[sourceIndex + 1] || + appIcons[sourceIndex + 1].app !== source.app) + ) { + appIcons.splice(appIcons.indexOf(sameApps[0]), sameApps.length) + Array.prototype.splice.apply( + appIcons, + [sourceIndex + 1, 0].concat(sameApps), + ) + } + + for (let i = 0, l = appIcons.length; i < l; ++i) { + let windows = [] + + if (!usingLaunchers || (!source.isLauncher && !appIcons[i].isLauncher)) { + windows = appIcons[i].window + ? [appIcons[i].window] + : getAppWindows(appIcons[i].app) + } + + windows.forEach((w) => (w._dtpPosition = position++)) + + if ( + this.showFavorites && + ((usingLaunchers && appIcons[i].isLauncher) || + (!usingLaunchers && + appFavorites.isFavorite(appIcons[i].app.get_id()))) + ) { + ++favoritesCount + } + } + + if (sourceIndex < favoritesCount) { + if (appIsFavorite) { + appFavorites.moveFavoriteToPos(sourceAppId, favoriteIndex) + } else { + appFavorites.addFavoriteAtPos(sourceAppId, favoriteIndex) + } + } else if ( + appIsFavorite && + this.showFavorites && + (!usingLaunchers || source.isLauncher) + ) { + appFavorites.removeFavorite(sourceAppId) + } + + appFavorites.emit('changed') + + return true + } + + _onShowAppsButtonToggled() { + // Sync the status of the default appButtons. Only if the two statuses are + // different, that means the user interacted with the extension provided + // application button, cutomize the behaviour. Otherwise the shell has changed the + // status (due to the _syncShowAppsButtonToggled function below) and it + // has already performed the desired action. + let selector = SearchController + + if ( + selector._showAppsButton && + selector._showAppsButton.checked !== this.showAppsButton.checked + ) { + // find visible view + + if (this.showAppsButton.checked) { + if (SETTINGS.get_boolean('show-apps-override-escape')) { + //override escape key to return to the desktop when entering the overview using the showapps button + SearchController._onStageKeyPress = function (actor, event) { + if ( + Main.modalCount == 1 && + event.get_key_symbol() === Clutter.KEY_Escape + ) { + this._searchActive ? this.reset() : Main.overview.hide() + + return Clutter.EVENT_STOP } - if (distance <= maxDistance) { - let level = (maxDistance - distance) / maxDistance; - level = Math.pow(level, iconAnimationSettings.convexity); - item.raise(level); - } else { - item.raise(0); - } - }); - } + return Object.getPrototypeOf(this)._onStageKeyPress.call( + this, + actor, + event, + ) + } - _onLeaveEvent(actor) { - let [stageX, stageY] = global.get_pointer(); - let [success, x, y] = actor.transform_stage_point(stageX, stageY); - if (success && !actor.allocation.contains(x, y) && (iconAnimationSettings.type == 'RIPPLE' || iconAnimationSettings.type == 'PLANK')) - this._dropIconAnimations(); - - return Clutter.EVENT_PROPAGATE; - } - - _onMotionEvent(actor_, event) { - if (iconAnimationSettings.type == 'RIPPLE' || iconAnimationSettings.type == 'PLANK') { - let timestamp = Date.now(); - if (!this._iconAnimationTimestamp || - (timestamp - this._iconAnimationTimestamp >= iconAnimationSettings.duration / 2)) { - let [pointerX, pointerY] = event.get_coords(); - this._updateIconAnimations(pointerX, pointerY); - } + let overviewHiddenId = Main.overview.connect('hidden', () => { + Main.overview.disconnect(overviewHiddenId) + delete SearchController._onStageKeyPress + }) } - return Clutter.EVENT_PROPAGATE; - } - - _onScrollEvent(actor, event) { - - let orientation = this.dtpPanel.getOrientation(); - - // reset timeout to avid conflicts with the mousehover event - this._timeoutsHandler.add([T1, 0, - () => this._swiping = false - ]); - - // Skip to avoid double events mouse - if (event.is_pointer_emulated()) - return Clutter.EVENT_STOP; - - let adjustment, delta; - - adjustment = this._scrollView[orientation[0] + 'adjustment']; - - let increment = adjustment.step_increment; - - switch ( event.get_scroll_direction() ) { - case Clutter.ScrollDirection.UP: - case Clutter.ScrollDirection.LEFT: - delta = -increment; - break; - case Clutter.ScrollDirection.DOWN: - case Clutter.ScrollDirection.RIGHT: - delta = +increment; - break; - case Clutter.ScrollDirection.SMOOTH: - let [dx, dy] = event.get_scroll_delta(); - delta = dy*increment; - delta += dx*increment; - break; - + // force exiting overview if needed + if (!Main.overview._shown) { + this.forcedOverview = true } - adjustment.set_value(adjustment.get_value() + delta); + //temporarily use as primary the monitor on which the showapps btn was clicked, this is + //restored by the panel when exiting the overview + this.dtpPanel.panelManager.setFocusedMonitor(this.dtpPanel.monitor) - return Clutter.EVENT_STOP; - - } - - _onScrollSizeChange(adjustment) { - // Update minimization animation target position on scrollview change. - this._updateAppIcons(); - - // When applications are ungrouped and there is some empty space on the horizontal taskbar, - // force a fixed label width to prevent the icons from "wiggling" when an animation runs - // (adding or removing an icon). When the taskbar is full, revert to a dynamic label width - // to allow them to resize and make room for new icons. - if (!this.dtpPanel.checkIfVertical() && !this.isGroupApps) { - let initial = this.fullScrollView; - - if (!this.fullScrollView && Math.floor(adjustment.upper) > adjustment.page_size) { - this.fullScrollView = adjustment.page_size; - } else if (adjustment.page_size < this.fullScrollView) { - this.fullScrollView = 0; - } - - if (initial != this.fullScrollView && !this._waitIdleId) { - this._waitIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - this._getAppIcons().forEach(a => a.updateTitleStyle()) - this._waitIdleId = 0; - - return GLib.SOURCE_REMOVE; - }); - } - } - } - - _onDragBegin() { - this._dragCancelled = false; - this._dragMonitor = { - dragMotion: this._onDragMotion.bind(this) - }; - DND.addDragMonitor(this._dragMonitor); - - if (this._box.get_n_children() == 0) { - this._emptyDropTarget = new Dash.EmptyDropTargetItem(); - this._box.insert_child_at_index(this._emptyDropTarget, 0); - this._emptyDropTarget.show(true); - } - - this._toggleFavoriteHighlight(true); - } - - _onDragCancelled() { - this._dragCancelled = true; - - if (this._dragInfo) { - this._box.set_child_at_index(this._dragInfo[1]._dashItemContainer, this._dragInfo[0]); - } - - this._endDrag(); - } - - _onDragEnd() { - if (this._dragCancelled) - return; - - this._endDrag(); - } - - _endDrag() { - if (this._dragInfo && this._dragInfo[1]._dashItemContainer instanceof DragPlaceholderItem) { - this._box.remove_child(this._dragInfo[1]._dashItemContainer); - this._dragInfo[1]._dashItemContainer.destroy(); - delete this._dragInfo[1]._dashItemContainer; - } - - this._dragInfo = null; - this._clearEmptyDropTarget(); - this._showAppsIcon.setDragApp(null); - DND.removeDragMonitor(this._dragMonitor); - - this._dragMonitor = null; - this.emit('end-drag'); - - this._toggleFavoriteHighlight(); - } - - _onDragMotion(dragEvent) { - let app = Dash.Dash.getAppFromSource(dragEvent.source); - if (app == null) - return DND.DragMotionResult.CONTINUE; - - let showAppsHovered = this._showAppsIcon.contains(dragEvent.targetActor); - - if (showAppsHovered) - this._showAppsIcon.setDragApp(app); - else - this._showAppsIcon.setDragApp(null); - - return DND.DragMotionResult.CONTINUE; - } - - _toggleFavoriteHighlight(show) { - let appFavorites = AppFavorites.getAppFavorites(); - let cssFuncName = (show ? 'add' : 'remove') + '_style_class_name'; - - if (this.showFavorites) - this._getAppIcons().filter(appIcon => (this.usingLaunchers && appIcon.isLauncher) || - (!this.usingLaunchers && appFavorites.isFavorite(appIcon.app.get_id()))) - .forEach(fav => fav._container[cssFuncName]('favorite')); - } - - handleIsolatedWorkspaceSwitch() { - this._shownInitially = this.isGroupApps; - this._queueRedisplay(); - } - - _connectWorkspaceSignals() { - this._disconnectWorkspaceSignals(); - - this._lastWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - - this._workspaceWindowAddedId = this._lastWorkspace.connect('window-added', () => this._queueRedisplay()); - this._workspaceWindowRemovedId = this._lastWorkspace.connect('window-removed', () => this._queueRedisplay()); - } - - _disconnectWorkspaceSignals() { - if (this._lastWorkspace) { - this._lastWorkspace.disconnect(this._workspaceWindowAddedId); - this._lastWorkspace.disconnect(this._workspaceWindowRemovedId); - - this._lastWorkspace = null; - } - } - - _queueRedisplay() { - Main.queueDeferredWork(this._workId); - } - - _hookUpLabel(item, syncHandler) { - item.child.connect('notify::hover', () => { - this._syncLabel(item, syncHandler); - }); - - syncHandler.connect('sync-tooltip', () => { - this._syncLabel(item, syncHandler); - }); - } - - _createAppItem(app, window, isLauncher) { - let appIcon = new AppIcons.TaskbarAppIcon( - { - app, - window, - isLauncher - }, - this.dtpPanel, - { - setSizeManually: true, - showLabel: false, - isDraggable: !SETTINGS.get_boolean('taskbar-locked'), - }, - this.previewMenu, - this.iconAnimator - ); - - if (appIcon._draggable) { - appIcon._draggable.connect('drag-begin', - () => { - appIcon.opacity = 0; - appIcon.isDragged = 1; - this._dropIconAnimations(); - }); - appIcon._draggable.connect('drag-end', - () => { - appIcon.opacity = 255; - delete appIcon.isDragged; - this._updateAppIcons(); - }); - } - - appIcon.connect('menu-state-changed', - (appIcon, opened) => { - this._itemMenuStateChanged(item, opened); - }); - - let item = new TaskbarItemContainer(); - - item._dtpPanel = this.dtpPanel - extendDashItemContainer(item); - - item.setChild(appIcon); - appIcon._dashItemContainer = item; - - appIcon.connect('notify::hover', () => { - if (appIcon.hover){ - this._timeoutsHandler.add([T1, 100, - () => Utils.ensureActorVisibleInScrollView(this._scrollView, appIcon, this._scrollView._dtpFadeSize) - ]) - - if (!appIcon.isDragged && iconAnimationSettings.type == 'SIMPLE') - appIcon.get_parent().raise(1); - else if (!appIcon.isDragged && (iconAnimationSettings.type == 'RIPPLE' || iconAnimationSettings.type == 'PLANK')) - this._updateIconAnimations(); - } else { - this._timeoutsHandler.remove(T1) - - if (!appIcon.isDragged && iconAnimationSettings.type == 'SIMPLE') - appIcon.get_parent().raise(0); - } - }); - - appIcon.connect('clicked', - (actor) => { - Utils.ensureActorVisibleInScrollView(this._scrollView, actor, this._scrollView._dtpFadeSize); - }); - - appIcon.connect('key-focus-in', (actor) => { - let [x_shift, y_shift] = Utils.ensureActorVisibleInScrollView(this._scrollView, actor, this._scrollView._dtpFadeSize); - - // This signal is triggered also by mouse click. The popup menu is opened at the original - // coordinates. Thus correct for the shift which is going to be applied to the scrollview. - if (appIcon._menu) { - appIcon._menu._boxPointer.xOffset = -x_shift; - appIcon._menu._boxPointer.yOffset = -y_shift; - } - }); - - // 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; - } - - // Return an array with the "proper" appIcons currently in the taskbar - _getAppIcons() { - // Only consider children which are "proper" icons and which are not - // animating out (which means they will be destroyed at the end of - // the animation) - return this._getTaskbarIcons().map(function(actor){ - return actor.child._delegate; - }); - } - - _getTaskbarIcons(includeAnimated) { - return this._box.get_children().filter(function(actor) { - return actor.child && - actor.child._delegate && - actor.child._delegate.icon && - (includeAnimated || !actor.animatingOut); - }); - } - - _updateAppIcons() { - let appIcons = this._getAppIcons(); - - appIcons.filter(icon => icon.constructor === AppIcons.TaskbarAppIcon).forEach(icon => { - icon.updateIcon(); - }); - } - - _itemMenuStateChanged(item, opened) { - // When the menu closes, it calls sync_hover, which means - // that the notify::hover handler does everything we need to. - if (opened) { - this._timeoutsHandler.remove(T2) - - item.hideLabel(); + // Finally show the overview + selector._showAppsButton.checked = true + Main.overview.show(2 /*APP_GRID*/) + } else { + if (this.forcedOverview) { + // force exiting overview if needed + Main.overview.hide() } else { - // I want to listen from outside when a menu is closed. I used to - // add a custom signal to the appIcon, since gnome 3.8 the signal - // calling this callback was added upstream. - this.emit('menu-closed'); - - // The icon menu grabs the events and, once it is closed, the pointer is maybe - // no longer over the taskbar and the animations are not dropped. - if (iconAnimationSettings.type == 'RIPPLE' || iconAnimationSettings.type == 'PLANK') { - this._scrollView.sync_hover(); - if (!this._scrollView.hover) - this._dropIconAnimations(); - } + selector._showAppsButton.checked = false } + + this.forcedOverview = false + } } + } - _syncLabel(item, syncHandler) { - let shouldShow = syncHandler ? syncHandler.shouldShowTooltip() : item.child.get_hover(); + _syncShowAppsButtonToggled() { + let status = SearchController._showAppsButton.checked + if (this.showAppsButton.checked !== status) + this.showAppsButton.checked = status + } - if (shouldShow) { - if (!this._timeoutsHandler.getId(T2)) { - let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT; + showShowAppsButton() { + this.showAppsButton.visible = true + this.showAppsButton.set_width(-1) + this.showAppsButton.set_height(-1) + } - this._timeoutsHandler.add([T2, timeout, - () => { - this._labelShowing = true; - item.showLabel(); - } - ]); + popupFocusedAppSecondaryMenu() { + let appIcons = this._getAppIcons() + let tracker = Shell.WindowTracker.get_default() - this._timeoutsHandler.remove(T3) - } - } else { - this._timeoutsHandler.remove(T2) + for (let i in appIcons) { + if (appIcons[i].app == tracker.focus_app) { + let appIcon = appIcons[i] + if (appIcon._menu && appIcon._menu.isOpen) appIcon._menu.close() + else appIcon.popupMenu() - item.hideLabel(); - if (this._labelShowing) { - this._timeoutsHandler.add([T3, DASH_ITEM_HOVER_TIMEOUT, - () => this._labelShowing = false - ]); - } - } + appIcon.sync_hover() + break + } } + } +} - _adjustIconSize() { - const thisMonitorIndex = this.dtpPanel.monitor.index; - let panelSize = PanelSettings.getPanelSize(SETTINGS, thisMonitorIndex); - let availSize = panelSize - SETTINGS.get_int('appicon-padding') * 2; - let minIconSize = MIN_ICON_SIZE + panelSize % 2; - - if (availSize == this.iconSize) - return; - - if (availSize < minIconSize) { - availSize = minIconSize; - } - - // For the icon size, we only consider children which are "proper" - // icons and which are not animating out (which means they will be - // destroyed at the end of the animation) - let iconChildren = this._getTaskbarIcons().concat([this._showAppsIcon]); - let scale = this.iconSize / availSize; - - this.iconSize = availSize; - - 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, or when initially filling - // the taskbar - if (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); - - Utils.animate(icon.icon, - { width: targetWidth, - height: targetHeight, - time: DASH_ANIMATION_TIME, - transition: 'easeOutQuad', - }); - } - } - - sortAppsCompareFunction(appA, appB) { - return getAppStableSequence(appA, this.dtpPanel.monitor) - - getAppStableSequence(appB, this.dtpPanel.monitor); - } - - getAppInfos() { - //get the user's favorite apps - let favoriteApps = this.showFavorites ? AppFavorites.getAppFavorites().getFavorites() : []; - - //find the apps that should be in the taskbar: the favorites first, then add the running apps - // When using isolation, we filter out apps that have no windows in - // the current workspace (this check is done in AppIcons.getInterestingWindows) - let runningApps = this.showRunningApps ? this._getRunningApps().sort(this.sortAppsCompareFunction.bind(this)) : []; - let appInfos - - if (this.allowSplitApps) { - appInfos = this._createAppInfos(favoriteApps, [], true) - .concat(this._createAppInfos(runningApps) - .filter(appInfo => appInfo.windows.length)); - } else { - appInfos = this._createAppInfos(favoriteApps.concat(runningApps.filter(app => favoriteApps.indexOf(app) < 0))) - .filter(appInfo => appInfo.windows.length || favoriteApps.indexOf(appInfo.app) >= 0); - } - - if (this._donateApp) - appInfos = [ - { - app: this._donateApp, - isLauncher: true, - windows: [], - }, - ...appInfos - ] - - return appInfos - } - - _redisplay() { - if (!this._signalsHandler) { - return; - } - - //get the currently displayed appIcons - let currentAppIcons = this._getTaskbarIcons(); - let expectedAppInfos = this.getAppInfos(); - - //remove the appIcons which are not in the expected apps list - for (let i = currentAppIcons.length - 1; i > -1; --i) { - let appIcon = currentAppIcons[i].child._delegate; - let appIndex = Utils.findIndex(expectedAppInfos, appInfo => appInfo.app == appIcon.app && - (!this.allowSplitApps || this.isGroupApps || appInfo.windows[0] == appIcon.window) && - appInfo.isLauncher == appIcon.isLauncher); - - if (appIndex < 0 || - (appIcon.window && (this.isGroupApps || expectedAppInfos[appIndex].windows.indexOf(appIcon.window) < 0)) || - (!appIcon.window && !appIcon.isLauncher && - !this.isGroupApps && expectedAppInfos[appIndex].windows.length)) { - currentAppIcons[i][this._shownInitially ? 'animateOutAndDestroy' : 'destroy'](); - currentAppIcons.splice(i, 1); - } - } - - //if needed, reorder the existing appIcons and create the missing ones - let currentPosition = 0; - for (let i = 0, l = expectedAppInfos.length; i < l; ++i) { - let neededAppIcons = this.isGroupApps || !expectedAppInfos[i].windows.length ? - [{ app: expectedAppInfos[i].app, window: null, isLauncher: expectedAppInfos[i].isLauncher }] : - expectedAppInfos[i].windows.map(window => ({ app: expectedAppInfos[i].app, window: window, isLauncher: false })); - - for (let j = 0, ll = neededAppIcons.length; j < ll; ++j) { - //check if the icon already exists - let matchingAppIconIndex = Utils.findIndex(currentAppIcons, appIcon => appIcon.child._delegate.app == neededAppIcons[j].app && - appIcon.child._delegate.window == neededAppIcons[j].window); - - if (matchingAppIconIndex > 0 && matchingAppIconIndex != currentPosition) { - //moved icon, reposition it - this._box.remove_child(currentAppIcons[matchingAppIconIndex]); - this._box.insert_child_at_index(currentAppIcons[matchingAppIconIndex], currentPosition); - } else if (matchingAppIconIndex < 0) { - //the icon doesn't exist yet, create a new one - let newAppIcon = this._createAppItem(neededAppIcons[j].app, neededAppIcons[j].window, neededAppIcons[j].isLauncher); - - this._box.insert_child_at_index(newAppIcon, currentPosition); - currentAppIcons.splice(currentPosition, 0, newAppIcon); - - // Skip animations on first run when adding the initial set - // of items, to avoid all items zooming in at once - newAppIcon.show(this._shownInitially); - } - - ++currentPosition; - } - } - - this._adjustIconSize(); - - // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 - // Without it, StBoxLayout may use a stale size cache - this._box.queue_relayout(); - - // This is required for icon reordering when the scrollview is used. - this._updateAppIcons(); - - // This will update the size, and the corresponding number for each icon on the primary panel - if (this.dtpPanel.isPrimary) { - this._updateNumberOverlay(); - } - - this._shownInitially = true; - } - - _getRunningApps() { - let tracker = Shell.WindowTracker.get_default(); - let windows = global.get_window_actors(); - let apps = []; - - for (let i = 0, l = windows.length; i < l; ++i) { - let app = tracker.get_window_app(windows[i].metaWindow); - - if (app && apps.indexOf(app) < 0) { - apps.push(app); - } - } - - return apps; - } - - _createAppInfos(apps, defaultWindows, defaultIsLauncher) { - if (this.allowSplitApps && !defaultIsLauncher) { - let separateApps = [] - - if (apps.length) { - let tracker = Shell.WindowTracker.get_default(); - let windows = AppIcons.getInterestingWindows(null, this.dtpPanel.monitor) - .sort(sortWindowsCompareFunction) - - windows.forEach(w => { - let windowApp = tracker.get_window_app(w) - - if (apps.indexOf(windowApp) >= 0) - separateApps.push({ - app: windowApp, - isLauncher: false, - windows: [w] - }) - }) - } - - return separateApps - } - - return apps.map(app => ({ - app: app, - isLauncher: defaultIsLauncher || false, - windows: defaultWindows || AppIcons.getInterestingWindows(app, this.dtpPanel.monitor) - .sort(sortWindowsCompareFunction) - })); - } - - // Reset the displayed apps icon to mantain the correct order - resetAppIcons (geometryChange) { - let children = this._getTaskbarIcons(true); - - for (let i = 0; i < children.length; i++) { - let item = children[i]; - item.destroy(); - } - - // to avoid ugly animations, just suppress them like when taskbar is first loaded. - this._shownInitially = false; - this._redisplay(); - - if (geometryChange && this.dtpPanel.checkIfVertical()) { - this.previewMenu._updateClip(); - } - } - - _updateNumberOverlay() { - let seenApps = {}; - let counter = 0; - - this._getAppIcons().forEach(icon => { - if (!seenApps[icon.app] || this.allowSplitApps) { - seenApps[icon.app] = 1; - counter++; - } - - if (counter <= 10) { - icon.setNumberOverlay(counter == 10 ? 0 : counter); - } else { - // No overlay after 10 - icon.setNumberOverlay(-1); - } - - icon.updateHotkeyNumberOverlay(); - }); - - if (SETTINGS.get_boolean('hot-keys') && - SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS') - this.toggleNumberOverlay(true); - } - - toggleNumberOverlay(activate) { - let appIcons = this._getAppIcons(); - appIcons.forEach(function(icon) { - icon.toggleNumberOverlay(activate); - }); - } - - _clearEmptyDropTarget() { - if (this._emptyDropTarget) { - this._emptyDropTarget.animateOutAndDestroy(); - this._emptyDropTarget = null; - } - } - - handleDragOver(source, actor, x, y, time) { - if (source == Main.xdndHandler) - return DND.DragMotionResult.CONTINUE; - - // Don't allow favoriting of transient apps - if (source.app == null || source.app.is_window_backed()) - return DND.DragMotionResult.NO_DROP; - - if (!this._settings.is_writable('favorite-apps')) - return DND.DragMotionResult.NO_DROP; - - let isVertical = this.dtpPanel.checkIfVertical(); - - if (!this._box.contains(source) && !source._dashItemContainer) { - //not an appIcon of the taskbar, probably from the applications view - source._dashItemContainer = new DragPlaceholderItem(source, this.iconSize, isVertical); - this._box.insert_child_above(source._dashItemContainer, null); - } - - let sizeProp = isVertical ? 'height' : 'width'; - let posProp = isVertical ? 'y' : 'x'; - let pos = isVertical ? y : x; - - let currentAppIcons = this._getAppIcons(); - let sourceIndex = currentAppIcons.indexOf(source); - let hoveredIndex = Utils.findIndex(currentAppIcons, - appIcon => pos >= appIcon._dashItemContainer[posProp] && - pos <= (appIcon._dashItemContainer[posProp] + appIcon._dashItemContainer[sizeProp])); - - if (!this._dragInfo) { - this._dragInfo = [sourceIndex, source]; - } - - if (hoveredIndex >= 0) { - let isLeft = pos < currentAppIcons[hoveredIndex]._dashItemContainer[posProp] + currentAppIcons[hoveredIndex]._dashItemContainer[sizeProp] * .5; - let prevIcon = currentAppIcons[hoveredIndex - 1] - let nextIcon = currentAppIcons[hoveredIndex + 1] - - // Don't allow positioning before or after self and between icons of same app if ungrouped and showing favorites - if (!(hoveredIndex === sourceIndex || - (isLeft && hoveredIndex - 1 == sourceIndex) || - (!this.allowSplitApps && isLeft && hoveredIndex - 1 >= 0 && source.app != prevIcon.app && - prevIcon.app == currentAppIcons[hoveredIndex].app) || - (!isLeft && hoveredIndex + 1 == sourceIndex) || - (!this.allowSplitApps && !isLeft && hoveredIndex + 1 < currentAppIcons.length && source.app != nextIcon.app && - nextIcon.app == currentAppIcons[hoveredIndex].app))) { - this._box.set_child_at_index(source._dashItemContainer, hoveredIndex); - - // Ensure the next and previous icon are visible when moving the icon - // (I assume there's room for both of them) - if (hoveredIndex > 1) - Utils.ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex-1], this._scrollView._dtpFadeSize); - if (hoveredIndex < this._box.get_children().length-1) - Utils.ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex+1], this._scrollView._dtpFadeSize); - } - } - - return this._dragInfo[0] !== sourceIndex ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.CONTINUE; - } - - // Draggable target interface - acceptDrop (source, actor, x, y, time) { - // Don't allow favoriting of transient apps - if (!this._dragInfo || !source.app || source.app.is_window_backed() || - !this._settings.is_writable('favorite-apps')) { - return false; - } - - let appIcons = this._getAppIcons(); - let sourceIndex = appIcons.indexOf(source); - let usingLaunchers = !this.isGroupApps && this.usingLaunchers; - - // dragging the icon to its original position - if (this._dragInfo[0] === sourceIndex) { - return true; - } - - let appFavorites = AppFavorites.getAppFavorites(); - let sourceAppId = source.app.get_id(); - let appIsFavorite = this.showFavorites && appFavorites.isFavorite(sourceAppId); - let replacingIndex = sourceIndex + (sourceIndex > this._dragInfo[0] ? -1 : 1); - let favoriteIndex = replacingIndex >= 0 ? appFavorites.getFavorites().indexOf(appIcons[replacingIndex].app) : 0; - let sameApps = this.allowSplitApps ? [] : appIcons.filter(a => a != source && a.app == source.app); - let favoritesCount = 0; - let position = 0; - let interestingWindows = {}; - let getAppWindows = app => { - if (!interestingWindows[app]) { - interestingWindows[app] = AppIcons.getInterestingWindows(app, this.dtpPanel.monitor); - } - - let appWindows = interestingWindows[app]; //prevents "reference to undefined property Symbol.toPrimitive" warning - return appWindows; - }; - - if (sameApps.length && - ((!appIcons[sourceIndex - 1] || appIcons[sourceIndex - 1].app !== source.app) && - (!appIcons[sourceIndex + 1] || appIcons[sourceIndex + 1].app !== source.app))) { - appIcons.splice(appIcons.indexOf(sameApps[0]), sameApps.length); - Array.prototype.splice.apply(appIcons, [sourceIndex + 1, 0].concat(sameApps)); - } - - for (let i = 0, l = appIcons.length; i < l; ++i) { - let windows = []; - - if (!usingLaunchers || (!source.isLauncher && !appIcons[i].isLauncher)) { - windows = appIcons[i].window ? [appIcons[i].window] : getAppWindows(appIcons[i].app); - } - - windows.forEach(w => w._dtpPosition = position++); - - if (this.showFavorites && - ((usingLaunchers && appIcons[i].isLauncher) || - (!usingLaunchers && appFavorites.isFavorite(appIcons[i].app.get_id())))) { - ++favoritesCount; - } - } - - if (sourceIndex < favoritesCount) { - if (appIsFavorite) { - appFavorites.moveFavoriteToPos(sourceAppId, favoriteIndex); - } else { - appFavorites.addFavoriteAtPos(sourceAppId, favoriteIndex); - } - } else if (appIsFavorite && this.showFavorites && (!usingLaunchers || source.isLauncher)) { - appFavorites.removeFavorite(sourceAppId); - } - - appFavorites.emit('changed'); - - return true; - } - - _onShowAppsButtonToggled() { - // Sync the status of the default appButtons. Only if the two statuses are - // different, that means the user interacted with the extension provided - // application button, cutomize the behaviour. Otherwise the shell has changed the - // status (due to the _syncShowAppsButtonToggled function below) and it - // has already performed the desired action. - let selector = SearchController; - - if (selector._showAppsButton && - selector._showAppsButton.checked !== this.showAppsButton.checked) { - // find visible view - - if (this.showAppsButton.checked) { - if (SETTINGS.get_boolean('show-apps-override-escape')) { - //override escape key to return to the desktop when entering the overview using the showapps button - SearchController._onStageKeyPress = function(actor, event) { - if (Main.modalCount == 1 && event.get_key_symbol() === Clutter.KEY_Escape) { - this._searchActive ? this.reset() : Main.overview.hide(); - - return Clutter.EVENT_STOP; - } - - return Object.getPrototypeOf(this)._onStageKeyPress.call(this, actor, event); - }; - - let overviewHiddenId = Main.overview.connect('hidden', () => { - Main.overview.disconnect(overviewHiddenId); - delete SearchController._onStageKeyPress; - }); - } - - // force exiting overview if needed - if (!Main.overview._shown) { - this.forcedOverview = true; - } - - //temporarily use as primary the monitor on which the showapps btn was clicked, this is - //restored by the panel when exiting the overview - this.dtpPanel.panelManager.setFocusedMonitor(this.dtpPanel.monitor); - - // Finally show the overview - selector._showAppsButton.checked = true; - Main.overview.show(2 /*APP_GRID*/); - } - else { - if (this.forcedOverview) { - // force exiting overview if needed - Main.overview.hide(); - } else { - selector._showAppsButton.checked = false; - } - - this.forcedOverview = false; - } - } - } - - _syncShowAppsButtonToggled() { - let status = SearchController._showAppsButton.checked; - if (this.showAppsButton.checked !== status) - this.showAppsButton.checked = status; - } - - showShowAppsButton() { - this.showAppsButton.visible = true; - this.showAppsButton.set_width(-1); - this.showAppsButton.set_height(-1); - } - - popupFocusedAppSecondaryMenu() { - let appIcons = this._getAppIcons(); - let tracker = Shell.WindowTracker.get_default(); - - for(let i in appIcons) { - if(appIcons[i].app == tracker.focus_app) { - let appIcon = appIcons[i]; - if(appIcon._menu && appIcon._menu.isOpen) - appIcon._menu.close(); - else - appIcon.popupMenu(); - - appIcon.sync_hover(); - break; - } - } - } -}; - -export const TaskbarItemContainer = GObject.registerClass({ - -}, class TaskbarItemContainer extends Dash.DashItemContainer { - +export const TaskbarItemContainer = GObject.registerClass( + {}, + class TaskbarItemContainer extends Dash.DashItemContainer { _init() { - super._init() - this.x_expand = this.y_expand = false + super._init() + this.x_expand = this.y_expand = false } vfunc_allocate(box) { - if (this.child == null) - return; + if (this.child == null) return - this.set_allocation(box); + this.set_allocation(box) - let availWidth = box.x2 - box.x1; - let availHeight = box.y2 - box.y1; - let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = this.child.get_preferred_size(); - let [childScaleX, childScaleY] = this.child.get_scale(); + let availWidth = box.x2 - box.x1 + let availHeight = box.y2 - box.y1 + let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = + this.child.get_preferred_size() + let [childScaleX, childScaleY] = this.child.get_scale() - let childWidth = Math.min(natChildWidth * childScaleX, availWidth); - let childHeight = Math.min(natChildHeight * childScaleY, availHeight); - let childBox = new Clutter.ActorBox(); + let childWidth = Math.min(natChildWidth * childScaleX, availWidth) + let childHeight = Math.min(natChildHeight * childScaleY, availHeight) + let childBox = new Clutter.ActorBox() - childBox.x1 = (availWidth - childWidth) / 2; - childBox.y1 = (availHeight - childHeight) / 2; - childBox.x2 = childBox.x1 + childWidth; - childBox.y2 = childBox.y1 + childHeight; + childBox.x1 = (availWidth - childWidth) / 2 + childBox.y1 = (availHeight - childHeight) / 2 + childBox.x2 = childBox.x1 + childWidth + childBox.y2 = childBox.y1 + childHeight - this.child.allocate(childBox); + this.child.allocate(childBox) } // In case appIcon is removed from the taskbar while it is hovered, // restore opacity before dashItemContainer.animateOutAndDestroy does the destroy animation. animateOutAndDestroy() { - if (this._raisedClone) { - this._raisedClone.source.opacity = 255; - this._raisedClone.destroy(); - } + if (this._raisedClone) { + this._raisedClone.source.opacity = 255 + this._raisedClone.destroy() + } - super.animateOutAndDestroy(); + super.animateOutAndDestroy() } // For ItemShowLabel _getIconAnimationOffset() { - if (!SETTINGS.get_boolean('animate-appicon-hover')) - return 0; + if (!SETTINGS.get_boolean('animate-appicon-hover')) return 0 - let travel = iconAnimationSettings.travel; - let zoom = iconAnimationSettings.zoom; - return this._dtpPanel.dtpSize * Math.max(0, travel + (zoom - 1) / 2); + let travel = iconAnimationSettings.travel + let zoom = iconAnimationSettings.zoom + return this._dtpPanel.dtpSize * Math.max(0, travel + (zoom - 1) / 2) } _updateCloneContainerPosition(cloneContainer) { - let [stageX, stageY] = this.get_transformed_position(); + let [stageX, stageY] = this.get_transformed_position() - cloneContainer.set_position( - stageX - this._dtpPanel.panelBox.translation_x - this.translation_x, - stageY - this._dtpPanel.panelBox.translation_y - this.translation_y - ); + cloneContainer.set_position( + stageX - this._dtpPanel.panelBox.translation_x - this.translation_x, + stageY - this._dtpPanel.panelBox.translation_y - this.translation_y, + ) } _createRaisedClone() { - let [width, height] = this.get_transformed_size(); + let [width, height] = this.get_transformed_size() - // "clone" of this child (appIcon actor) - let cloneButton = this.child._delegate.getCloneButton(); + // "clone" of this child (appIcon actor) + let cloneButton = this.child._delegate.getCloneButton() - // "clone" of this (taskbarItemContainer) - let cloneContainer = new St.Bin({ - child: cloneButton, - width: width, height: height, - reactive: false, - }); + // "clone" of this (taskbarItemContainer) + let cloneContainer = new St.Bin({ + child: cloneButton, + width: width, + height: height, + reactive: false, + }) - this._updateCloneContainerPosition(cloneContainer); + this._updateCloneContainerPosition(cloneContainer) - // For the stretch animation - let boundProperty = this._dtpPanel.checkIfVertical() ? 'translation_y' : 'translation_x'; - this.bind_property(boundProperty, cloneContainer, boundProperty, GObject.BindingFlags.SYNC_CREATE); + // For the stretch animation + let boundProperty = this._dtpPanel.checkIfVertical() + ? 'translation_y' + : 'translation_x' + this.bind_property( + boundProperty, + cloneContainer, + boundProperty, + GObject.BindingFlags.SYNC_CREATE, + ) + // The clone follows its source when the taskbar is scrolled. + let taskbarScrollView = this.get_parent().get_parent() + let adjustment = this._dtpPanel.checkIfVertical() + ? taskbarScrollView.get_vadjustment() + : taskbarScrollView.get_hadjustment() + let adjustmentChangedId = adjustment.connect('notify::value', () => + this._updateCloneContainerPosition(cloneContainer), + ) - // The clone follows its source when the taskbar is scrolled. - let taskbarScrollView = this.get_parent().get_parent(); - let adjustment = this._dtpPanel.checkIfVertical() ? taskbarScrollView.get_vadjustment() : taskbarScrollView.get_hadjustment(); - let adjustmentChangedId = adjustment.connect('notify::value', () => this._updateCloneContainerPosition(cloneContainer)); + // Update clone position when an item is added to / removed from the taskbar. + let taskbarBox = this.get_parent() + let taskbarBoxAllocationChangedId = taskbarBox.connect( + 'notify::allocation', + () => this._updateCloneContainerPosition(cloneContainer), + ) - // Update clone position when an item is added to / removed from the taskbar. - let taskbarBox = this.get_parent(); - let taskbarBoxAllocationChangedId = taskbarBox.connect('notify::allocation', () => this._updateCloneContainerPosition(cloneContainer)); + // The clone itself + this._raisedClone = cloneButton.child + this._raisedClone.connect('destroy', () => { + adjustment.disconnect(adjustmentChangedId) + taskbarBox.disconnect(taskbarBoxAllocationChangedId) + GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + cloneContainer.destroy() + return GLib.SOURCE_REMOVE + }) + delete this._raisedClone + }) - // The clone itself - this._raisedClone = cloneButton.child; - this._raisedClone.connect('destroy', () => { - adjustment.disconnect(adjustmentChangedId); - taskbarBox.disconnect(taskbarBoxAllocationChangedId); - GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - cloneContainer.destroy(); - return GLib.SOURCE_REMOVE; - }); - delete this._raisedClone; - }); - - this._raisedClone.source.opacity = 0; - Main.uiGroup.add_child(cloneContainer); + this._raisedClone.source.opacity = 0 + Main.uiGroup.add_child(cloneContainer) } // Animate the clone. // AppIcon actors cannot go outside the taskbar so the animation is done with a clone. // If level is zero, the clone is dropped and destroyed. raise(level) { - if (this._raisedClone) - Utils.stopAnimations(this._raisedClone); - else if (level) - this._createRaisedClone(); - else - return; + if (this._raisedClone) Utils.stopAnimations(this._raisedClone) + else if (level) this._createRaisedClone() + else return - let panelPosition = this._dtpPanel.getPosition(); - let panelElementPositions = this._dtpPanel.panelManager.panelsElementPositions[this._dtpPanel.monitor.index] || Pos.defaults; - let taskbarPosition = panelElementPositions.filter(pos => pos.element == 'taskbar')[0].position; + let panelPosition = this._dtpPanel.getPosition() + let panelElementPositions = + this._dtpPanel.panelManager.panelsElementPositions[ + this._dtpPanel.monitor.index + ] || Pos.defaults + let taskbarPosition = panelElementPositions.filter( + (pos) => pos.element == 'taskbar', + )[0].position - let vertical = panelPosition == St.Side.LEFT || panelPosition == St.Side.RIGHT; - let translationDirection = panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT ? 1 : -1; - let rotationDirection; - if (panelPosition == St.Side.LEFT || taskbarPosition == Pos.STACKED_TL) - rotationDirection = -1; - else if (panelPosition == St.Side.RIGHT || taskbarPosition == Pos.STACKED_BR) - rotationDirection = 1; - else { - let items = this.get_parent().get_children(); - let index = items.indexOf(this); - rotationDirection = (index - (items.length - 1) / 2) / ((items.length - 1) / 2); - } + let vertical = + panelPosition == St.Side.LEFT || panelPosition == St.Side.RIGHT + let translationDirection = + panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT ? 1 : -1 + let rotationDirection + if (panelPosition == St.Side.LEFT || taskbarPosition == Pos.STACKED_TL) + rotationDirection = -1 + else if ( + panelPosition == St.Side.RIGHT || + taskbarPosition == Pos.STACKED_BR + ) + rotationDirection = 1 + else { + let items = this.get_parent().get_children() + let index = items.indexOf(this) + rotationDirection = + (index - (items.length - 1) / 2) / ((items.length - 1) / 2) + } - let duration = iconAnimationSettings.duration / 1000; - let rotation = iconAnimationSettings.rotation; - let travel = iconAnimationSettings.travel; - let zoom = iconAnimationSettings.zoom; + let duration = iconAnimationSettings.duration / 1000 + let rotation = iconAnimationSettings.rotation + let travel = iconAnimationSettings.travel + let zoom = iconAnimationSettings.zoom - // level is about 1 for the icon that is hovered, less for others. - // time depends on the translation to do. - let [width, height] = this._raisedClone.source.get_transformed_size(); - let translationMax = (vertical ? width : height) * (travel + (zoom - 1) / 2); - let translationEnd = translationMax * level; - let translationDone = vertical ? this._raisedClone.translation_x : this._raisedClone.translation_y; - let translationTodo = Math.sign(travel)*Math.abs(translationEnd - translationDone); - let scale = 1 + (zoom - 1) * level; - let rotationAngleZ = rotationDirection * rotation * level; - let time = Math.abs(duration * translationTodo / translationMax); + // level is about 1 for the icon that is hovered, less for others. + // time depends on the translation to do. + let [width, height] = this._raisedClone.source.get_transformed_size() + let translationMax = + (vertical ? width : height) * (travel + (zoom - 1) / 2) + let translationEnd = translationMax * level + let translationDone = vertical + ? this._raisedClone.translation_x + : this._raisedClone.translation_y + let translationTodo = + Math.sign(travel) * Math.abs(translationEnd - translationDone) + let scale = 1 + (zoom - 1) * level + let rotationAngleZ = rotationDirection * rotation * level + let time = Math.abs((duration * translationTodo) / translationMax) - let options = { - scale_x: scale, scale_y: scale, - rotation_angle_z: rotationAngleZ, - time: time, - transition: 'easeOutQuad', - onComplete: () => { - if (!level) { - this._raisedClone.source.opacity = 255; - this._raisedClone.destroy(); - delete this._raisedClone; - } - } - }; - options[vertical ? 'translation_x' : 'translation_y'] = translationDirection * translationEnd; + let options = { + scale_x: scale, + scale_y: scale, + rotation_angle_z: rotationAngleZ, + time: time, + transition: 'easeOutQuad', + onComplete: () => { + if (!level) { + this._raisedClone.source.opacity = 255 + this._raisedClone.destroy() + delete this._raisedClone + } + }, + } + options[vertical ? 'translation_x' : 'translation_y'] = + translationDirection * translationEnd - Utils.animate(this._raisedClone, options); + Utils.animate(this._raisedClone, options) } // Animate this and cloneContainer, since cloneContainer translation is bound to this. stretch(translation) { - let duration = iconAnimationSettings.duration / 1000; - let zoom = iconAnimationSettings.zoom; - let animatedProperty = this._dtpPanel.checkIfVertical() ? 'translation_y' : 'translation_x'; - let isShowing = this.opacity != 255 || this.child.opacity != 255; + let duration = iconAnimationSettings.duration / 1000 + let zoom = iconAnimationSettings.zoom + let animatedProperty = this._dtpPanel.checkIfVertical() + ? 'translation_y' + : 'translation_x' + let isShowing = this.opacity != 255 || this.child.opacity != 255 - if (isShowing) { - // Do no stop the animation initiated in DashItemContainer.show. - this[animatedProperty] = zoom * translation; - } else { - let options = { - time: duration, - transition: 'easeOutQuad', - }; - options[animatedProperty] = zoom * translation; - - Utils.stopAnimations(this); - Utils.animate(this, options); + if (isShowing) { + // Do no stop the animation initiated in DashItemContainer.show. + this[animatedProperty] = zoom * translation + } else { + let options = { + time: duration, + transition: 'easeOutQuad', } + options[animatedProperty] = zoom * translation + + Utils.stopAnimations(this) + Utils.animate(this, options) + } } -}); - -const DragPlaceholderItem = GObject.registerClass({ -}, class DragPlaceholderItem extends St.Widget { + }, +) +const DragPlaceholderItem = GObject.registerClass( + {}, + class DragPlaceholderItem extends St.Widget { _init(appIcon, iconSize, isVertical) { - super._init({ style: AppIcons.getIconContainerStyle(isVertical), layout_manager: new Clutter.BinLayout() }); + super._init({ + style: AppIcons.getIconContainerStyle(isVertical), + layout_manager: new Clutter.BinLayout(), + }) - this.child = { _delegate: appIcon }; + this.child = { _delegate: appIcon } - this._clone = new Clutter.Clone({ - source: appIcon.icon._iconBin, - width: iconSize, - height: iconSize - }); + this._clone = new Clutter.Clone({ + source: appIcon.icon._iconBin, + width: iconSize, + height: iconSize, + }) - this.add_child(this._clone); + this.add_child(this._clone) } destroy() { - this._clone.destroy(); - super.destroy(); + this._clone.destroy() + super.destroy() } -}); + }, +) export function getAppStableSequence(app, monitor) { - let windows = AppIcons.getInterestingWindows(app, monitor); + let windows = AppIcons.getInterestingWindows(app, monitor) - return windows.reduce((prevWindow, window) => { - return Math.min(prevWindow, getWindowStableSequence(window)); - }, Infinity); + return windows.reduce((prevWindow, window) => { + return Math.min(prevWindow, getWindowStableSequence(window)) + }, Infinity) } export function sortWindowsCompareFunction(windowA, windowB) { - return getWindowStableSequence(windowA) - getWindowStableSequence(windowB); + return getWindowStableSequence(windowA) - getWindowStableSequence(windowB) } export function getWindowStableSequence(window) { - return ('_dtpPosition' in window ? window._dtpPosition : window.get_stable_sequence()); + return '_dtpPosition' in window + ? window._dtpPosition + : window.get_stable_sequence() } diff --git a/transparency.js b/transparency.js index 25ff00f..19da558 100644 --- a/transparency.js +++ b/transparency.js @@ -15,238 +15,254 @@ * along with this program. If not, see . */ -import GdkPixbuf from 'gi://GdkPixbuf'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import St from 'gi://St'; +import GdkPixbuf from 'gi://GdkPixbuf' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import St from 'gi://St' -import * as Proximity from './proximity.js'; -import * as Utils from './utils.js'; -import {SETTINGS} from './extension.js'; +import * as Proximity from './proximity.js' +import * as Utils from './utils.js' +import { SETTINGS } from './extension.js' export const DynamicTransparency = class { + constructor(dtpPanel) { + this._dtpPanel = dtpPanel + this._proximityManager = dtpPanel.panelManager.proximityManager + this._proximityWatchId = 0 + this.currentBackgroundColor = 0 - constructor(dtpPanel) { - this._dtpPanel = dtpPanel; - this._proximityManager = dtpPanel.panelManager.proximityManager; - this._proximityWatchId = 0; - this.currentBackgroundColor = 0; + this._initialPanelStyle = dtpPanel.panel.get_style() - this._initialPanelStyle = dtpPanel.panel.get_style(); + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._bindSignals() - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._bindSignals(); + this._updateAnimationDuration() + this._updateAllAndSet() + this._updateProximityWatch() + } - this._updateAnimationDuration(); - this._updateAllAndSet(); - this._updateProximityWatch(); + destroy() { + this._signalsHandler.destroy() + this._proximityManager.removeWatch(this._proximityWatchId) + + this._dtpPanel.panel.set_style(this._initialPanelStyle) + } + + updateExternalStyle() { + this._updateComplementaryStyles() + this._setBackground() + } + + _bindSignals() { + this._signalsHandler.add( + [Utils.getStageTheme(), 'changed', () => this._updateAllAndSet()], + [Main.overview, ['showing', 'hiding'], () => this._updateAlphaAndSet()], + [ + SETTINGS, + ['changed::trans-use-custom-bg', 'changed::trans-bg-color'], + () => this._updateColorAndSet(), + ], + [ + SETTINGS, + [ + 'changed::trans-use-custom-opacity', + 'changed::trans-panel-opacity', + 'changed::trans-bg-color', + 'changed::trans-dynamic-anim-target', + 'changed::trans-use-dynamic-opacity', + ], + () => this._updateAlphaAndSet(), + ], + [ + SETTINGS, + [ + 'changed::trans-use-custom-gradient', + 'changed::trans-gradient-top-color', + 'changed::trans-gradient-bottom-color', + 'changed::trans-gradient-top-opacity', + 'changed::trans-gradient-bottom-opacity', + ], + () => this._updateGradientAndSet(), + ], + [ + SETTINGS, + [ + 'changed::trans-dynamic-behavior', + 'changed::trans-use-dynamic-opacity', + 'changed::trans-dynamic-distance', + ], + () => this._updateProximityWatch(), + ], + [ + SETTINGS, + 'changed::trans-dynamic-anim-time', + () => this._updateAnimationDuration(), + ], + ) + } + + _updateProximityWatch() { + this._proximityManager.removeWatch(this._proximityWatchId) + + if (SETTINGS.get_boolean('trans-use-dynamic-opacity')) { + let isVertical = this._dtpPanel.checkIfVertical() + let threshold = SETTINGS.get_int('trans-dynamic-distance') + + this._windowOverlap = false + this._updateAlphaAndSet() + + this._proximityWatchId = this._proximityManager.createWatch( + this._dtpPanel.panelBox.get_parent(), + this._dtpPanel.monitor.index, + Proximity.Mode[SETTINGS.get_string('trans-dynamic-behavior')], + isVertical ? threshold : 0, + isVertical ? 0 : threshold, + (overlap) => { + this._windowOverlap = overlap + this._updateAlphaAndSet() + }, + ) + } + } + + _updateAnimationDuration() { + this.animationDuration = + SETTINGS.get_int('trans-dynamic-anim-time') * 0.001 + 's;' + } + + _updateAllAndSet() { + let themeBackground = this._getThemeBackground(true) + + this._updateColor(themeBackground) + this._updateAlpha(themeBackground) + this._updateComplementaryStyles() + this._updateGradient() + this._setBackground() + this._setGradient() + } + + _updateColorAndSet() { + this._updateColor() + this._setBackground() + } + + _updateAlphaAndSet() { + this._updateAlpha() + this._setBackground() + } + + _updateGradientAndSet() { + this._updateGradient() + this._setGradient() + } + + _updateComplementaryStyles() { + let panelThemeNode = this._dtpPanel.panel.get_theme_node() + + this._complementaryStyles = + 'border-radius: ' + panelThemeNode.get_border_radius(0) + 'px;' + } + + _updateColor(themeBackground) { + this.backgroundColorRgb = SETTINGS.get_boolean('trans-use-custom-bg') + ? SETTINGS.get_string('trans-bg-color') + : themeBackground || this._getThemeBackground() + } + + _updateAlpha(themeBackground) { + if ( + this._windowOverlap && + !Main.overview.visibleTarget && + SETTINGS.get_boolean('trans-use-dynamic-opacity') + ) { + this.alpha = SETTINGS.get_double('trans-dynamic-anim-target') + } else { + this.alpha = SETTINGS.get_boolean('trans-use-custom-opacity') + ? SETTINGS.get_double('trans-panel-opacity') + : (themeBackground || this._getThemeBackground()).alpha * 0.003921569 // 1 / 255 = 0.003921569 + } + } + + _updateGradient() { + this._gradientStyle = '' + + if (SETTINGS.get_boolean('trans-use-custom-gradient')) { + this._gradientStyle += + 'background-gradient-direction: ' + + (this._dtpPanel.checkIfVertical() ? 'horizontal;' : 'vertical;') + + 'background-gradient-start: ' + + Utils.getrgbaColor( + SETTINGS.get_string('trans-gradient-top-color'), + SETTINGS.get_double('trans-gradient-top-opacity'), + ) + + 'background-gradient-end: ' + + Utils.getrgbaColor( + SETTINGS.get_string('trans-gradient-bottom-color'), + SETTINGS.get_double('trans-gradient-bottom-opacity'), + ) + } + } + + _setBackground() { + this.currentBackgroundColor = Utils.getrgbaColor( + this.backgroundColorRgb, + this.alpha, + ) + + let transition = 'transition-duration:' + this.animationDuration + + this._dtpPanel.set_style( + 'background-color: ' + + this.currentBackgroundColor + + transition + + this._complementaryStyles, + ) + } + + _setGradient() { + this._dtpPanel.panel.set_style( + 'background: none; ' + + 'border-image: none; ' + + 'background-image: none; ' + + this._gradientStyle + + 'transition-duration:' + + this.animationDuration, + ) + } + + _getThemeBackground(reload) { + if (reload || !this._themeBackground) { + let fakePanel = new St.Bin({ name: 'panel' }) + Main.uiGroup.add_child(fakePanel) + let fakeTheme = fakePanel.get_theme_node() + this._themeBackground = + this._getBackgroundImageColor(fakeTheme) || + fakeTheme.get_background_color() + Main.uiGroup.remove_child(fakePanel) } - destroy() { - this._signalsHandler.destroy(); - this._proximityManager.removeWatch(this._proximityWatchId); + return this._themeBackground + } - this._dtpPanel.panel.set_style(this._initialPanelStyle); - } + _getBackgroundImageColor(theme) { + let bg = null - updateExternalStyle() { - this._updateComplementaryStyles(); - this._setBackground(); - } + try { + let imageFile = + theme.get_background_image() || theme.get_border_image().get_file() - _bindSignals() { - this._signalsHandler.add( - [ - Utils.getStageTheme(), - 'changed', - () => this._updateAllAndSet() - ], - [ - Main.overview, - [ - 'showing', - 'hiding' - ], - () => this._updateAlphaAndSet() - ], - [ - SETTINGS, - [ - 'changed::trans-use-custom-bg', - 'changed::trans-bg-color' - ], - () => this._updateColorAndSet() - ], - [ - SETTINGS, - [ - 'changed::trans-use-custom-opacity', - 'changed::trans-panel-opacity', - 'changed::trans-bg-color', - 'changed::trans-dynamic-anim-target', - 'changed::trans-use-dynamic-opacity' - ], - () => this._updateAlphaAndSet() - ], - [ - SETTINGS, - [ - 'changed::trans-use-custom-gradient', - 'changed::trans-gradient-top-color', - 'changed::trans-gradient-bottom-color', - 'changed::trans-gradient-top-opacity', - 'changed::trans-gradient-bottom-opacity' - ], - () => this._updateGradientAndSet() - ], - [ - SETTINGS, - [ - 'changed::trans-dynamic-behavior', - 'changed::trans-use-dynamic-opacity', - 'changed::trans-dynamic-distance' - ], - () => this._updateProximityWatch() - ], - [ - SETTINGS, - 'changed::trans-dynamic-anim-time', - () => this._updateAnimationDuration() - ] - ); - } + if (imageFile) { + let imageBuf = GdkPixbuf.Pixbuf.new_from_file(imageFile.get_path()) + let pixels = imageBuf.get_pixels() - _updateProximityWatch() { - this._proximityManager.removeWatch(this._proximityWatchId); - - if (SETTINGS.get_boolean('trans-use-dynamic-opacity')) { - let isVertical = this._dtpPanel.checkIfVertical(); - let threshold = SETTINGS.get_int('trans-dynamic-distance'); - - this._windowOverlap = false; - this._updateAlphaAndSet() - - this._proximityWatchId = this._proximityManager.createWatch( - this._dtpPanel.panelBox.get_parent(), - this._dtpPanel.monitor.index, - Proximity.Mode[SETTINGS.get_string('trans-dynamic-behavior')], - isVertical ? threshold : 0, - isVertical ? 0 : threshold, - overlap => { - this._windowOverlap = overlap; - this._updateAlphaAndSet(); - } - ); + bg = { + red: pixels[0], + green: pixels[1], + blue: pixels[2], + alpha: pixels[3], } - } + } + } catch (error) {} - _updateAnimationDuration() { - this.animationDuration = (SETTINGS.get_int('trans-dynamic-anim-time') * 0.001) + 's;'; - } - - _updateAllAndSet() { - let themeBackground = this._getThemeBackground(true); - - this._updateColor(themeBackground); - this._updateAlpha(themeBackground); - this._updateComplementaryStyles(); - this._updateGradient(); - this._setBackground(); - this._setGradient(); - } - - _updateColorAndSet() { - this._updateColor(); - this._setBackground(); - } - - _updateAlphaAndSet() { - this._updateAlpha(); - this._setBackground(); - } - - _updateGradientAndSet() { - this._updateGradient(); - this._setGradient(); - } - - _updateComplementaryStyles() { - let panelThemeNode = this._dtpPanel.panel.get_theme_node(); - - this._complementaryStyles = 'border-radius: ' + panelThemeNode.get_border_radius(0) + 'px;'; - } - - _updateColor(themeBackground) { - this.backgroundColorRgb = SETTINGS.get_boolean('trans-use-custom-bg') ? - SETTINGS.get_string('trans-bg-color') : - (themeBackground || this._getThemeBackground()); - } - - _updateAlpha(themeBackground) { - if (this._windowOverlap && !Main.overview.visibleTarget && SETTINGS.get_boolean('trans-use-dynamic-opacity')) { - this.alpha = SETTINGS.get_double('trans-dynamic-anim-target'); - } else { - this.alpha = SETTINGS.get_boolean('trans-use-custom-opacity') ? - SETTINGS.get_double('trans-panel-opacity') : - (themeBackground || this._getThemeBackground()).alpha * 0.003921569; // 1 / 255 = 0.003921569 - } - } - - _updateGradient() { - this._gradientStyle = ''; - - if (SETTINGS.get_boolean('trans-use-custom-gradient')) { - this._gradientStyle += 'background-gradient-direction: ' + (this._dtpPanel.checkIfVertical() ? 'horizontal;' : 'vertical;') + - 'background-gradient-start: ' + Utils.getrgbaColor(SETTINGS.get_string('trans-gradient-top-color'), - SETTINGS.get_double('trans-gradient-top-opacity')) + - 'background-gradient-end: ' + Utils.getrgbaColor(SETTINGS.get_string('trans-gradient-bottom-color'), - SETTINGS.get_double('trans-gradient-bottom-opacity')); - } - } - - _setBackground() { - this.currentBackgroundColor = Utils.getrgbaColor(this.backgroundColorRgb, this.alpha); - - let transition = 'transition-duration:' + this.animationDuration; - - this._dtpPanel.set_style('background-color: ' + this.currentBackgroundColor + transition + this._complementaryStyles); - } - - _setGradient() { - this._dtpPanel.panel.set_style( - 'background: none; ' + - 'border-image: none; ' + - 'background-image: none; ' + - this._gradientStyle + - 'transition-duration:' + this.animationDuration - ); - } - - _getThemeBackground(reload) { - if (reload || !this._themeBackground) { - let fakePanel = new St.Bin({ name: 'panel' }); - Main.uiGroup.add_child(fakePanel); - let fakeTheme = fakePanel.get_theme_node() - this._themeBackground = this._getBackgroundImageColor(fakeTheme) || fakeTheme.get_background_color(); - Main.uiGroup.remove_child(fakePanel); - } - - return this._themeBackground; - } - - _getBackgroundImageColor(theme) { - let bg = null; - - try { - let imageFile = theme.get_background_image() || theme.get_border_image().get_file(); - - if (imageFile) { - let imageBuf = GdkPixbuf.Pixbuf.new_from_file(imageFile.get_path()); - let pixels = imageBuf.get_pixels(); - - bg = { red: pixels[0], green: pixels[1], blue: pixels[2], alpha: pixels[3] }; - } - } catch (error) {} - - return bg; - } + return bg + } } diff --git a/utils.js b/utils.js index df67ad9..b7bd090 100644 --- a/utils.js +++ b/utils.js @@ -21,869 +21,894 @@ * Some code was also adapted from the upstream Gnome Shell source code. */ -import Clutter from 'gi://Clutter'; -import Cogl from 'gi://Cogl'; -import GdkPixbuf from 'gi://GdkPixbuf'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import Graphene from 'gi://Graphene'; -import Meta from 'gi://Meta'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; -import * as Util from 'resource:///org/gnome/shell/misc/util.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js'; +import Clutter from 'gi://Clutter' +import Cogl from 'gi://Cogl' +import GdkPixbuf from 'gi://GdkPixbuf' +import Gio from 'gi://Gio' +import GLib from 'gi://GLib' +import Graphene from 'gi://Graphene' +import Meta from 'gi://Meta' +import Shell from 'gi://Shell' +import St from 'gi://St' +import * as Util from 'resource:///org/gnome/shell/misc/util.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js' -const SCROLL_TIME = Util.SCROLL_TIME / (Util.SCROLL_TIME > 1 ? 1000 : 1); +const SCROLL_TIME = Util.SCROLL_TIME / (Util.SCROLL_TIME > 1 ? 1000 : 1) // simplify global signals and function injections handling // abstract class -export const BasicHandler = class { +export const BasicHandler = class { + constructor() { + this._storage = new Object() + } - constructor() { - this._storage = new Object(); + add(/*unlimited 3-long array arguments*/) { + // convert arguments object to array, concatenate with generic + let args = [].concat('generic', [].slice.call(arguments)) + // call addWithLabel with ags as if they were passed arguments + this.addWithLabel.apply(this, args) + } + + destroy() { + for (let label in this._storage) this.removeWithLabel(label) + } + + addWithLabel(label /* plus unlimited 3-long array arguments*/) { + if (this._storage[label] == undefined) this._storage[label] = new Array() + + // skip first element of the arguments + for (let i = 1; i < arguments.length; i++) { + let item = this._storage[label] + let handlers = this._create(arguments[i]) + + for (let j = 0, l = handlers.length; j < l; ++j) { + item.push(handlers[j]) + } } + } - add(/*unlimited 3-long array arguments*/){ + removeWithLabel(label) { + if (this._storage[label]) { + for (let i = 0; i < this._storage[label].length; i++) { + this._remove(this._storage[label][i]) + } - // convert arguments object to array, concatenate with generic - let args = [].concat('generic', [].slice.call(arguments)); - // call addWithLabel with ags as if they were passed arguments - this.addWithLabel.apply(this, args); + delete this._storage[label] } + } - destroy() { - for( let label in this._storage ) - this.removeWithLabel(label); - } + /* Virtual methods to be implemented by subclass */ + // create single element to be stored in the storage structure + _create(item) { + throw new Error('no implementation of _create in ' + this) + } - addWithLabel( label /* plus unlimited 3-long array arguments*/) { - - if(this._storage[label] == undefined) - this._storage[label] = new Array(); - - // skip first element of the arguments - for( let i = 1; i < arguments.length; i++ ) { - let item = this._storage[label]; - let handlers = this._create(arguments[i]); - - for (let j = 0, l = handlers.length; j < l; ++j) { - item.push(handlers[j]); - } - } - - } - - removeWithLabel(label){ - - if(this._storage[label]) { - for( let i = 0; i < this._storage[label].length; i++ ) { - this._remove(this._storage[label][i]); - } - - delete this._storage[label]; - } - } - - /* Virtual methods to be implemented by subclass */ - // create single element to be stored in the storage structure - _create(item){ - throw new Error('no implementation of _create in ' + this); - } - - // correctly delete single element - _remove(item){ - throw new Error('no implementation of _remove in ' + this); - } + // correctly delete single element + _remove(item) { + throw new Error('no implementation of _remove in ' + this) + } } // Manage global signals export const GlobalSignalsHandler = class extends BasicHandler { + _create(item) { + let handlers = [] - _create(item) { - let handlers = []; + item[1] = [].concat(item[1]) - item[1] = [].concat(item[1]); + for (let i = 0, l = item[1].length; i < l; ++i) { + let object = item[0] + let event = item[1][i] + let callback = item[2] + try { + let id = object.connect(event, callback) - for (let i = 0, l = item[1].length; i < l; ++i) { - let object = item[0]; - let event = item[1][i]; - let callback = item[2] - try { - let id = object.connect(event, callback); - - handlers.push([object, id]); - } catch (e) - { - - } - } - - return handlers; + handlers.push([object, id]) + } catch (e) {} } - _remove(item){ - item[0].disconnect(item[1]); - } -}; + return handlers + } + + _remove(item) { + item[0].disconnect(item[1]) + } +} /** * Manage function injection: both instances and prototype can be overridden * and restored */ export const InjectionsHandler = class extends BasicHandler { + _create(item) { + let object = item[0] + let name = item[1] + let injectedFunction = item[2] + let original = object[name] - _create(item) { - let object = item[0]; - let name = item[1]; - let injectedFunction = item[2]; - let original = object[name]; + object[name] = injectedFunction + return [[object, name, injectedFunction, original]] + } - object[name] = injectedFunction; - return [[object, name, injectedFunction, original]]; - } - - _remove(item) { - let object = item[0]; - let name = item[1]; - let original = item[3]; - object[name] = original; - } -}; + _remove(item) { + let object = item[0] + let name = item[1] + let original = item[3] + object[name] = original + } +} /** * Manage timeouts: the added timeouts have their id reset on completion */ export const TimeoutsHandler = class extends BasicHandler { + _create(item) { + let name = item[0] + let delay = item[1] + let timeoutHandler = item[2] - _create(item) { - let name = item[0]; - let delay = item[1]; - let timeoutHandler = item[2]; + this._remove(item) - this._remove(item); + this[name] = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => { + this[name] = 0 + timeoutHandler() - this[name] = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => { - this[name] = 0; - timeoutHandler(); + return GLib.SOURCE_REMOVE + }) - return GLib.SOURCE_REMOVE; - }); + return [[name]] + } - return [[name]]; + remove(name) { + this._remove([name]) + } + + _remove(item) { + let name = item[0] + + if (this[name]) { + GLib.Source.remove(this[name]) + this[name] = 0 } + } - remove(name) { - this._remove([name]) - } - - _remove(item) { - let name = item[0]; - - if (this[name]) { - GLib.Source.remove(this[name]); - this[name] = 0; - } - } - - getId(name) { - return this[name] ? this[name] : 0; - } -}; + getId(name) { + return this[name] ? this[name] : 0 + } +} // This is wrapper to maintain compatibility with GNOME-Shell 3.30+ as well as // previous versions. export const DisplayWrapper = { - getScreen() { - return global.screen || global.display; - }, + getScreen() { + return global.screen || global.display + }, - getWorkspaceManager() { - return global.screen || global.workspace_manager; - }, + getWorkspaceManager() { + return global.screen || global.workspace_manager + }, - getMonitorManager() { - return global.screen || global.backend.get_monitor_manager(); - } -}; + getMonitorManager() { + return global.screen || global.backend.get_monitor_manager() + }, +} let unredirectEnabled = true export const setDisplayUnredirect = (enable) => { - if (enable && !unredirectEnabled) - Meta.enable_unredirect_for_display(global.display); - else if (!enable && unredirectEnabled) - Meta.disable_unredirect_for_display(global.display); + if (enable && !unredirectEnabled) + Meta.enable_unredirect_for_display(global.display) + else if (!enable && unredirectEnabled) + Meta.disable_unredirect_for_display(global.display) - unredirectEnabled = enable; -}; - -export const getSystemMenuInfo = function() { - return { - name: 'quickSettings', - constructor: Main.panel.statusArea.quickSettings.constructor - }; + unredirectEnabled = enable } -export const getCurrentWorkspace = function() { - return DisplayWrapper.getWorkspaceManager().get_active_workspace(); -}; +export const getSystemMenuInfo = function () { + return { + name: 'quickSettings', + constructor: Main.panel.statusArea.quickSettings.constructor, + } +} -export const getWorkspaceByIndex = function(index) { - return DisplayWrapper.getWorkspaceManager().get_workspace_by_index(index); -}; +export const getCurrentWorkspace = function () { + return DisplayWrapper.getWorkspaceManager().get_active_workspace() +} -export const getWorkspaceCount = function() { - return DisplayWrapper.getWorkspaceManager().n_workspaces; -}; +export const getWorkspaceByIndex = function (index) { + return DisplayWrapper.getWorkspaceManager().get_workspace_by_index(index) +} -export const getStageTheme = function() { - return St.ThemeContext.get_for_stage(global.stage); -}; +export const getWorkspaceCount = function () { + return DisplayWrapper.getWorkspaceManager().n_workspaces +} -export const getScaleFactor = function() { - return getStageTheme().scale_factor || 1; -}; +export const getStageTheme = function () { + return St.ThemeContext.get_for_stage(global.stage) +} -export const findIndex = function(array, predicate) { - if (array) { - if (Array.prototype.findIndex) { - return array.findIndex(predicate); - } +export const getScaleFactor = function () { + return getStageTheme().scale_factor || 1 +} - for (let i = 0, l = array.length; i < l; ++i) { - if (predicate(array[i])) { - return i; - } - } +export const findIndex = function (array, predicate) { + if (array) { + if (Array.prototype.findIndex) { + return array.findIndex(predicate) } - return -1; -}; - -export const find = function(array, predicate) { - let index = findIndex(array, predicate); - - if (index > -1) { - return array[index]; + for (let i = 0, l = array.length; i < l; ++i) { + if (predicate(array[i])) { + return i + } } -}; + } -export const mergeObjects = function(main, bck) { - for (const prop in bck) { - if (!main.hasOwnProperty(prop) && bck.hasOwnProperty(prop)) { - main[prop] = bck[prop]; - } + return -1 +} + +export const find = function (array, predicate) { + let index = findIndex(array, predicate) + + if (index > -1) { + return array[index] + } +} + +export const mergeObjects = function (main, bck) { + for (const prop in bck) { + if (!main.hasOwnProperty(prop) && bck.hasOwnProperty(prop)) { + main[prop] = bck[prop] } + } - return main; -}; + return main +} export const getTrackedActorData = (actor) => { - let trackedIndex = Main.layoutManager._findActor(actor); - - if (trackedIndex >= 0) - return Main.layoutManager._trackedActors[trackedIndex] + let trackedIndex = Main.layoutManager._findActor(actor) + + if (trackedIndex >= 0) return Main.layoutManager._trackedActors[trackedIndex] } -export const getTransformedAllocation = function(actor) { - let extents = actor.get_transformed_extents(); - let topLeft = extents.get_top_left(); - let bottomRight = extents.get_bottom_right(); +export const getTransformedAllocation = function (actor) { + let extents = actor.get_transformed_extents() + let topLeft = extents.get_top_left() + let bottomRight = extents.get_bottom_right() - return { x1: topLeft.x, x2: bottomRight.x, y1: topLeft.y, y2: bottomRight.y }; -}; - -export const setClip = function(actor, x, y, width, height) { - actor.set_clip(0, 0, width, height); - actor.set_position(x, y); - actor.set_size(width, height); -}; - -export const addKeybinding = function(key, settings, handler, modes) { - if (!Main.wm._allowedKeybindings[key]) { - Main.wm.addKeybinding( - key, - settings, - Meta.KeyBindingFlags.NONE, - modes || (Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW), - handler - ); - } -}; - -export const removeKeybinding = function(key) { - if (Main.wm._allowedKeybindings[key]) { - Main.wm.removeKeybinding(key); - } -}; - -export const getrgbColor = function(color) { - color = typeof color === 'string' ? ColorUtils.color_from_string(color)[1] : color; - - return { red: color.red, green: color.green, blue: color.blue }; -}; - -export const getrgbaColor = function(color, alpha, offset) { - if (alpha <= 0) { - return 'transparent; '; - } - - let rgb = getrgbColor(color); - - if (offset) { - ['red', 'green', 'blue'].forEach(k => { - rgb[k] = Math.min(255, Math.max(0, rgb[k] + offset)); - - if (rgb[k] == color[k]) { - rgb[k] = Math.min(255, Math.max(0, rgb[k] - offset)); - } - }); - } - - return 'rgba(' + rgb.red + ',' + rgb.green + ',' + rgb.blue + ',' + (Math.floor(alpha * 100) * 0.01) + '); ' ; -}; - -export const checkIfColorIsBright = function(color) { - let rgb = getrgbColor(color); - let brightness = 0.2126 * rgb.red + 0.7152 * rgb.green + 0.0722 * rgb.blue; - - return brightness > 128; -}; - -export const getMouseScrollDirection = function(event) { - let direction; - - switch (event.get_scroll_direction()) { - case Clutter.ScrollDirection.UP: - case Clutter.ScrollDirection.LEFT: - direction = 'up'; - break; - case Clutter.ScrollDirection.DOWN: - case Clutter.ScrollDirection.RIGHT: - direction = 'down'; - break; - } - - return direction; -}; - -export const checkIfWindowHasTransient = function(window) { - let hasTransient; - - window.foreach_transient(t => !(hasTransient = true)); - - return hasTransient; -}; - -export const activateSiblingWindow = function(windows, direction, startWindow) { - let windowIndex = windows.indexOf(global.display.focus_window); - let nextWindowIndex = windowIndex < 0 ? - startWindow ? windows.indexOf(startWindow) : 0 : - windowIndex + (direction == 'up' ? -1 : 1); - - if (nextWindowIndex == windows.length) { - nextWindowIndex = 0; - } else if (nextWindowIndex < 0) { - nextWindowIndex = windows.length - 1; - } - - if (windowIndex != nextWindowIndex) { - Main.activateWindow(windows[nextWindowIndex]); - } -}; - -export const animateWindowOpacity = function(window, tweenOpts) { - //there currently is a mutter bug with the windowactor opacity, starting with 3.34 - //https://gitlab.gnome.org/GNOME/mutter/issues/836 - - //since 3.36, a workaround is to use the windowactor's child for the fade animation - //this leaves a "shadow" on the desktop, so the windowactor needs to be hidden - //when the animation is complete - let visible = tweenOpts.opacity > 0; - let windowActor = window; - let initialOpacity = window.opacity; - - window = windowActor.get_first_child() || windowActor; - - if (!windowActor.visible && visible) { - window.opacity = 0; - windowActor.visible = visible; - tweenOpts.opacity = Math.min(initialOpacity, tweenOpts.opacity); - } - - if (!visible) { - tweenOpts.onComplete = () => { - windowActor.visible = visible; - window.opacity = initialOpacity; - }; - } - - animate(window, tweenOpts); -}; - -export const animate = function(actor, options) { - //the original animations used Tweener instead of Clutter animations, so we - //use "time" and "delay" properties defined in seconds, as opposed to Clutter - //animations "duration" and "delay" which are defined in milliseconds - if (options.delay) { - options.delay = options.delay * 1000; - } - - options.duration = options.time * 1000; - delete options.time; - - if (options.transition) { - //map Tweener easing equations to Clutter animation modes - options.mode = { - 'easeInCubic': Clutter.AnimationMode.EASE_IN_CUBIC, - 'easeInOutCubic': Clutter.AnimationMode.EASE_IN_OUT_CUBIC, - 'easeInOutQuad': Clutter.AnimationMode.EASE_IN_OUT_QUAD, - 'easeOutQuad': Clutter.AnimationMode.EASE_OUT_QUAD - }[options.transition] || Clutter.AnimationMode.LINEAR; - - delete options.transition; - } - - let params = [options]; - - if ('value' in options && actor instanceof St.Adjustment) { - params.unshift(options.value); - delete options.value; - } - - actor.ease.apply(actor, params); + return { x1: topLeft.x, x2: bottomRight.x, y1: topLeft.y, y2: bottomRight.y } } -export const isAnimating = function(actor, prop) { - return !!actor.get_transition(prop); +export const setClip = function (actor, x, y, width, height) { + actor.set_clip(0, 0, width, height) + actor.set_position(x, y) + actor.set_size(width, height) } -export const stopAnimations = function(actor) { - actor.remove_all_transitions(); +export const addKeybinding = function (key, settings, handler, modes) { + if (!Main.wm._allowedKeybindings[key]) { + Main.wm.addKeybinding( + key, + settings, + Meta.KeyBindingFlags.NONE, + modes || Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, + handler, + ) + } } -export const getIndicators = function(delegate) { - if (delegate instanceof St.BoxLayout) { - return delegate; +export const removeKeybinding = function (key) { + if (Main.wm._allowedKeybindings[key]) { + Main.wm.removeKeybinding(key) + } +} + +export const getrgbColor = function (color) { + color = + typeof color === 'string' ? ColorUtils.color_from_string(color)[1] : color + + return { red: color.red, green: color.green, blue: color.blue } +} + +export const getrgbaColor = function (color, alpha, offset) { + if (alpha <= 0) { + return 'transparent; ' + } + + let rgb = getrgbColor(color) + + if (offset) { + ;['red', 'green', 'blue'].forEach((k) => { + rgb[k] = Math.min(255, Math.max(0, rgb[k] + offset)) + + if (rgb[k] == color[k]) { + rgb[k] = Math.min(255, Math.max(0, rgb[k] - offset)) + } + }) + } + + return ( + 'rgba(' + + rgb.red + + ',' + + rgb.green + + ',' + + rgb.blue + + ',' + + Math.floor(alpha * 100) * 0.01 + + '); ' + ) +} + +export const checkIfColorIsBright = function (color) { + let rgb = getrgbColor(color) + let brightness = 0.2126 * rgb.red + 0.7152 * rgb.green + 0.0722 * rgb.blue + + return brightness > 128 +} + +export const getMouseScrollDirection = function (event) { + let direction + + switch (event.get_scroll_direction()) { + case Clutter.ScrollDirection.UP: + case Clutter.ScrollDirection.LEFT: + direction = 'up' + break + case Clutter.ScrollDirection.DOWN: + case Clutter.ScrollDirection.RIGHT: + direction = 'down' + break + } + + return direction +} + +export const checkIfWindowHasTransient = function (window) { + let hasTransient + + window.foreach_transient((t) => !(hasTransient = true)) + + return hasTransient +} + +export const activateSiblingWindow = function ( + windows, + direction, + startWindow, +) { + let windowIndex = windows.indexOf(global.display.focus_window) + let nextWindowIndex = + windowIndex < 0 + ? startWindow + ? windows.indexOf(startWindow) + : 0 + : windowIndex + (direction == 'up' ? -1 : 1) + + if (nextWindowIndex == windows.length) { + nextWindowIndex = 0 + } else if (nextWindowIndex < 0) { + nextWindowIndex = windows.length - 1 + } + + if (windowIndex != nextWindowIndex) { + Main.activateWindow(windows[nextWindowIndex]) + } +} + +export const animateWindowOpacity = function (window, tweenOpts) { + //there currently is a mutter bug with the windowactor opacity, starting with 3.34 + //https://gitlab.gnome.org/GNOME/mutter/issues/836 + + //since 3.36, a workaround is to use the windowactor's child for the fade animation + //this leaves a "shadow" on the desktop, so the windowactor needs to be hidden + //when the animation is complete + let visible = tweenOpts.opacity > 0 + let windowActor = window + let initialOpacity = window.opacity + + window = windowActor.get_first_child() || windowActor + + if (!windowActor.visible && visible) { + window.opacity = 0 + windowActor.visible = visible + tweenOpts.opacity = Math.min(initialOpacity, tweenOpts.opacity) + } + + if (!visible) { + tweenOpts.onComplete = () => { + windowActor.visible = visible + window.opacity = initialOpacity + } + } + + animate(window, tweenOpts) +} + +export const animate = function (actor, options) { + //the original animations used Tweener instead of Clutter animations, so we + //use "time" and "delay" properties defined in seconds, as opposed to Clutter + //animations "duration" and "delay" which are defined in milliseconds + if (options.delay) { + options.delay = options.delay * 1000 + } + + options.duration = options.time * 1000 + delete options.time + + if (options.transition) { + //map Tweener easing equations to Clutter animation modes + options.mode = + { + easeInCubic: Clutter.AnimationMode.EASE_IN_CUBIC, + easeInOutCubic: Clutter.AnimationMode.EASE_IN_OUT_CUBIC, + easeInOutQuad: Clutter.AnimationMode.EASE_IN_OUT_QUAD, + easeOutQuad: Clutter.AnimationMode.EASE_OUT_QUAD, + }[options.transition] || Clutter.AnimationMode.LINEAR + + delete options.transition + } + + let params = [options] + + if ('value' in options && actor instanceof St.Adjustment) { + params.unshift(options.value) + delete options.value + } + + actor.ease.apply(actor, params) +} + +export const isAnimating = function (actor, prop) { + return !!actor.get_transition(prop) +} + +export const stopAnimations = function (actor) { + actor.remove_all_transitions() +} + +export const getIndicators = function (delegate) { + if (delegate instanceof St.BoxLayout) { + return delegate + } + + return delegate.indicators +} + +export const getPoint = function (coords) { + return new Graphene.Point(coords) +} + +export const notify = function (text, iconName, action, isTransient) { + let source = new MessageTray.SystemNotificationSource() + let notification = new MessageTray.Notification(source, 'Dash to Panel', text) + let notifyFunc = source.showNotification || source.notify + + if (iconName) { + source.createIcon = function () { + return new St.Icon({ icon_name: iconName }) + } + } + + if (action) { + if (!(action instanceof Array)) { + action = [action] } - return delegate.indicators; + action.forEach((a) => notification.addAction(a.text, a.func)) + } + + Main.messageTray.add(source) + + notification.setTransient(isTransient) + notifyFunc.call(source, notification) } -export const getPoint = function(coords) { - return new Graphene.Point(coords); -} - -export const notify = function(text, iconName, action, isTransient) { - let source = new MessageTray.SystemNotificationSource(); - let notification = new MessageTray.Notification(source, 'Dash to Panel', text); - let notifyFunc = source.showNotification || source.notify; - - if (iconName) { - source.createIcon = function() { - return new St.Icon({ icon_name: iconName }); - }; - } - - if (action) { - if (!(action instanceof Array)) { - action = [action]; - } - - action.forEach(a => notification.addAction(a.text, a.func)); - } - - Main.messageTray.add(source); - - notification.setTransient(isTransient); - notifyFunc.call(source, notification); -}; - /* * This is a copy of the same function in utils.js, but also adjust horizontal scrolling * and perform few further cheks on the current value to avoid changing the values when * it would be clamp to the current one in any case. * Return the amount of shift applied -*/ -export const ensureActorVisibleInScrollView = function(scrollView, actor, fadeSize, onComplete) { - const vadjustment = scrollView.vadjustment; - const hadjustment = scrollView.hadjustment; - let [vvalue, vlower, vupper, vstepIncrement, vpageIncrement, vpageSize] = vadjustment.get_values(); - let [hvalue, hlower, hupper, hstepIncrement, hpageIncrement, hpageSize] = hadjustment.get_values(); + */ +export const ensureActorVisibleInScrollView = function ( + scrollView, + actor, + fadeSize, + onComplete, +) { + const vadjustment = scrollView.vadjustment + const hadjustment = scrollView.hadjustment + let [vvalue, vlower, vupper, vstepIncrement, vpageIncrement, vpageSize] = + vadjustment.get_values() + let [hvalue, hlower, hupper, hstepIncrement, hpageIncrement, hpageSize] = + hadjustment.get_values() - let [hvalue0, vvalue0] = [hvalue, vvalue]; + let [hvalue0, vvalue0] = [hvalue, vvalue] - let voffset = fadeSize; - let hoffset = fadeSize; - - let box = actor.get_allocation_box(); - let y1 = box.y1, y2 = box.y2, x1 = box.x1, x2 = box.x2; + let voffset = fadeSize + let hoffset = fadeSize - let parent = actor.get_parent(); - while (parent != scrollView) { - if (!parent) - throw new Error("actor not in scroll view"); + let box = actor.get_allocation_box() + let y1 = box.y1, + y2 = box.y2, + x1 = box.x1, + x2 = box.x2 - let box = parent.get_allocation_box(); - y1 += box.y1; - y2 += box.y1; - x1 += box.x1; - x2 += box.x1; - parent = parent.get_parent(); - } + let parent = actor.get_parent() + while (parent != scrollView) { + if (!parent) throw new Error('actor not in scroll view') - if (y1 < vvalue + voffset) - vvalue = Math.max(0, y1 - voffset); - else if (vvalue < vupper - vpageSize && y2 > vvalue + vpageSize - voffset) - vvalue = Math.min(vupper -vpageSize, y2 + voffset - vpageSize); + let box = parent.get_allocation_box() + y1 += box.y1 + y2 += box.y1 + x1 += box.x1 + x2 += box.x1 + parent = parent.get_parent() + } - if (x1 < hvalue + hoffset) - hvalue = Math.max(0, x1 - hoffset); - else if (hvalue < hupper - hpageSize && x2 > hvalue + hpageSize - hoffset) - hvalue = Math.min(hupper - hpageSize, x2 + hoffset - hpageSize); + if (y1 < vvalue + voffset) vvalue = Math.max(0, y1 - voffset) + else if (vvalue < vupper - vpageSize && y2 > vvalue + vpageSize - voffset) + vvalue = Math.min(vupper - vpageSize, y2 + voffset - vpageSize) - let tweenOpts = { - time: SCROLL_TIME, - onComplete: onComplete || (() => {}), - transition: 'easeOutQuad' - }; + if (x1 < hvalue + hoffset) hvalue = Math.max(0, x1 - hoffset) + else if (hvalue < hupper - hpageSize && x2 > hvalue + hpageSize - hoffset) + hvalue = Math.min(hupper - hpageSize, x2 + hoffset - hpageSize) - if (vvalue !== vvalue0) { - animate(vadjustment, mergeObjects(tweenOpts, { value: vvalue })); - } + let tweenOpts = { + time: SCROLL_TIME, + onComplete: onComplete || (() => {}), + transition: 'easeOutQuad', + } - if (hvalue !== hvalue0) { - animate(hadjustment, mergeObjects(tweenOpts, { value: hvalue })); - } + if (vvalue !== vvalue0) { + animate(vadjustment, mergeObjects(tweenOpts, { value: vvalue })) + } - return [hvalue- hvalue0, vvalue - vvalue0]; + if (hvalue !== hvalue0) { + animate(hadjustment, mergeObjects(tweenOpts, { value: hvalue })) + } + + return [hvalue - hvalue0, vvalue - vvalue0] } - + /** * ColorUtils is adapted from https://github.com/micheleg/dash-to-dock */ let colorNs = Clutter.Color ? Clutter : Cogl export const ColorUtils = { - color_from_string: colorNs.color_from_string, - Color: colorNs.Color, + color_from_string: colorNs.color_from_string, + Color: colorNs.Color, - colorLuminance(r, g, b, dlum) { - // Darken or brighten color by a fraction dlum - // Each rgb value is modified by the same fraction. - // Return "#rrggbb" strin + colorLuminance(r, g, b, dlum) { + // Darken or brighten color by a fraction dlum + // Each rgb value is modified by the same fraction. + // Return "#rrggbb" strin - let rgbString = '#'; + let rgbString = '#' - rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(r*(1+dlum), 0), 255)), 2); - rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(g*(1+dlum), 0), 255)), 2); - rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(b*(1+dlum), 0), 255)), 2); + rgbString += ColorUtils._decimalToHex( + Math.round(Math.min(Math.max(r * (1 + dlum), 0), 255)), + 2, + ) + rgbString += ColorUtils._decimalToHex( + Math.round(Math.min(Math.max(g * (1 + dlum), 0), 255)), + 2, + ) + rgbString += ColorUtils._decimalToHex( + Math.round(Math.min(Math.max(b * (1 + dlum), 0), 255)), + 2, + ) - return rgbString; - }, + return rgbString + }, - _decimalToHex(d, padding) { - // Convert decimal to an hexadecimal string adding the desired padding + _decimalToHex(d, padding) { + // Convert decimal to an hexadecimal string adding the desired padding - let hex = d.toString(16); - while (hex.length < padding) - hex = '0'+ hex; - return hex; - }, + let hex = d.toString(16) + while (hex.length < padding) hex = '0' + hex + return hex + }, - HSVtoRGB(h, s, v) { - // Convert hsv ([0-1, 0-1, 0-1]) to rgb ([0-255, 0-255, 0-255]). - // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV - // here with h = [0,1] instead of [0, 360] - // Accept either (h,s,v) independently or {h:h, s:s, v:v} object. - // Return {r:r, g:g, b:b} object. + HSVtoRGB(h, s, v) { + // Convert hsv ([0-1, 0-1, 0-1]) to rgb ([0-255, 0-255, 0-255]). + // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV + // here with h = [0,1] instead of [0, 360] + // Accept either (h,s,v) independently or {h:h, s:s, v:v} object. + // Return {r:r, g:g, b:b} object. - if (arguments.length === 1) { - s = h.s; - v = h.v; - h = h.h; - } - - let r,g,b; - let c = v*s; - let h1 = h*6; - let x = c*(1 - Math.abs(h1 % 2 - 1)); - let m = v - c; - - if (h1 <=1) - r = c + m, g = x + m, b = m; - else if (h1 <=2) - r = x + m, g = c + m, b = m; - else if (h1 <=3) - r = m, g = c + m, b = x + m; - else if (h1 <=4) - r = m, g = x + m, b = c + m; - else if (h1 <=5) - r = x + m, g = m, b = c + m; - else - r = c + m, g = m, b = x + m; - - return { - r: Math.round(r * 255), - g: Math.round(g * 255), - b: Math.round(b * 255) - }; - }, - - RGBtoHSV(r, g, b) { - // Convert rgb ([0-255, 0-255, 0-255]) to hsv ([0-1, 0-1, 0-1]). - // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV - // here with h = [0,1] instead of [0, 360] - // Accept either (r,g,b) independently or {r:r, g:g, b:b} object. - // Return {h:h, s:s, v:v} object. - - if (arguments.length === 1) { - r = r.r; - g = r.g; - b = r.b; - } - - let h,s,v; - - let M = Math.max(r, g, b); - let m = Math.min(r, g, b); - let c = M - m; - - if (c == 0) - h = 0; - else if (M == r) - h = ((g-b)/c) % 6; - else if (M == g) - h = (b-r)/c + 2; - else - h = (r-g)/c + 4; - - h = h/6; - v = M/255; - if (M !== 0) - s = c/M; - else - s = 0; - - return {h: h, s: s, v: v}; + if (arguments.length === 1) { + s = h.s + v = h.v + h = h.h } -}; + + let r, g, b + let c = v * s + let h1 = h * 6 + let x = c * (1 - Math.abs((h1 % 2) - 1)) + let m = v - c + + if (h1 <= 1) (r = c + m), (g = x + m), (b = m) + else if (h1 <= 2) (r = x + m), (g = c + m), (b = m) + else if (h1 <= 3) (r = m), (g = c + m), (b = x + m) + else if (h1 <= 4) (r = m), (g = x + m), (b = c + m) + else if (h1 <= 5) (r = x + m), (g = m), (b = c + m) + else (r = c + m), (g = m), (b = x + m) + + return { + r: Math.round(r * 255), + g: Math.round(g * 255), + b: Math.round(b * 255), + } + }, + + RGBtoHSV(r, g, b) { + // Convert rgb ([0-255, 0-255, 0-255]) to hsv ([0-1, 0-1, 0-1]). + // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV + // here with h = [0,1] instead of [0, 360] + // Accept either (r,g,b) independently or {r:r, g:g, b:b} object. + // Return {h:h, s:s, v:v} object. + + if (arguments.length === 1) { + r = r.r + g = r.g + b = r.b + } + + let h, s, v + + let M = Math.max(r, g, b) + let m = Math.min(r, g, b) + let c = M - m + + if (c == 0) h = 0 + else if (M == r) h = ((g - b) / c) % 6 + else if (M == g) h = (b - r) / c + 2 + else h = (r - g) / c + 4 + + h = h / 6 + v = M / 255 + if (M !== 0) s = c / M + else s = 0 + + return { h: h, s: s, v: v } + }, +} /** * DominantColorExtractor is adapted from https://github.com/micheleg/dash-to-dock */ -let themeLoader = null; -let iconCacheMap = new Map(); -const MAX_CACHED_ITEMS = 1000; -const BATCH_SIZE_TO_DELETE = 50; -const DOMINANT_COLOR_ICON_SIZE = 64; +let themeLoader = null +let iconCacheMap = new Map() +const MAX_CACHED_ITEMS = 1000 +const BATCH_SIZE_TO_DELETE = 50 +const DOMINANT_COLOR_ICON_SIZE = 64 export const DominantColorExtractor = class { + constructor(app) { + this._app = app + } - constructor(app){ - this._app = app; + /** + * Try to get the pixel buffer for the current icon, if not fail gracefully + */ + _getIconPixBuf() { + let iconTexture = this._app.create_icon_texture(16) + + if (themeLoader === null) { + themeLoader = new St.IconTheme() } - /** - * Try to get the pixel buffer for the current icon, if not fail gracefully - */ - _getIconPixBuf() { - let iconTexture = this._app.create_icon_texture(16); - - if (themeLoader === null) { - themeLoader = new St.IconTheme(); - } - - // Unable to load the icon texture, use fallback - if (iconTexture instanceof St.Icon === false) { - return null; - } - - iconTexture = iconTexture.get_gicon(); - - // Unable to load the icon texture, use fallback - if (iconTexture === null) { - return null; - } - - if (iconTexture instanceof Gio.FileIcon) { - // Use GdkPixBuf to load the pixel buffer from the provided file path - return GdkPixbuf.Pixbuf.new_from_file(iconTexture.get_file().get_path()); - } - - // Get the pixel buffer from the icon theme - if (iconTexture instanceof Gio.ThemedIcon) { - let icon_info = themeLoader.lookup_icon(iconTexture.get_names()[0], - DOMINANT_COLOR_ICON_SIZE, 0); - - if (icon_info !== null) { - return icon_info.load_icon(); - } - } - - return null; + // Unable to load the icon texture, use fallback + if (iconTexture instanceof St.Icon === false) { + return null } - /** - * The backlight color choosing algorithm was mostly ported to javascript from the - * Unity7 C++ source of Canonicals: - * https://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/launcher/LauncherIcon.cpp - * so it more or less works the same way. - */ - _getColorPalette() { - if (iconCacheMap.get(this._app.get_id())) { - // We already know the answer - return iconCacheMap.get(this._app.get_id()); - } + iconTexture = iconTexture.get_gicon() - let pixBuf = this._getIconPixBuf(); - if (pixBuf == null) - return null; - - let pixels = pixBuf.get_pixels(), - offset = 0; - - let total = 0, - rTotal = 0, - gTotal = 0, - bTotal = 0; - - let resample_y = 1, - resample_x = 1; - - // Resampling of large icons - // We resample icons larger than twice the desired size, as the resampling - // to a size s - // DOMINANT_COLOR_ICON_SIZE < s < 2*DOMINANT_COLOR_ICON_SIZE, - // most of the case exactly DOMINANT_COLOR_ICON_SIZE as the icon size is tipycally - // a multiple of it. - let width = pixBuf.get_width(); - let height = pixBuf.get_height(); - - // Resample - if (height >= 2* DOMINANT_COLOR_ICON_SIZE) - resample_y = Math.floor(height/DOMINANT_COLOR_ICON_SIZE); - - if (width >= 2* DOMINANT_COLOR_ICON_SIZE) - resample_x = Math.floor(width/DOMINANT_COLOR_ICON_SIZE); - - if (resample_x !==1 || resample_y !== 1) - pixels = this._resamplePixels(pixels, resample_x, resample_y); - - // computing the limit outside the for (where it would be repeated at each iteration) - // for performance reasons - let limit = pixels.length; - for (let offset = 0; offset < limit; offset+=4) { - let r = pixels[offset], - g = pixels[offset + 1], - b = pixels[offset + 2], - a = pixels[offset + 3]; - - let saturation = (Math.max(r,g, b) - Math.min(r,g, b)); - let relevance = 0.1 * 255 * 255 + 0.9 * a * saturation; - - rTotal += r * relevance; - gTotal += g * relevance; - bTotal += b * relevance; - - total += relevance; - } - - total = total * 255; - - let r = rTotal / total, - g = gTotal / total, - b = bTotal / total; - - let hsv = ColorUtils.RGBtoHSV(r * 255, g * 255, b * 255); - - if (hsv.s > 0.15) - hsv.s = 0.65; - hsv.v = 0.90; - - let rgb = ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v); - - // Cache the result. - let backgroundColor = { - lighter: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, 0.2), - original: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, 0), - darker: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, -0.5) - }; - - if (iconCacheMap.size >= MAX_CACHED_ITEMS) { - //delete oldest cached values (which are in order of insertions) - let ctr=0; - for (let key of iconCacheMap.keys()) { - if (++ctr > BATCH_SIZE_TO_DELETE) - break; - iconCacheMap.delete(key); - } - } - - iconCacheMap.set(this._app.get_id(), backgroundColor); - - return backgroundColor; + // Unable to load the icon texture, use fallback + if (iconTexture === null) { + return null } - /** - * Downsample large icons before scanning for the backlight color to - * improve performance. - * - * @param pixBuf - * @param pixels - * @param resampleX - * @param resampleY - * - * @return []; - */ - _resamplePixels(pixels, resampleX, resampleY) { - let resampledPixels = []; - // computing the limit outside the for (where it would be repeated at each iteration) - // for performance reasons - let limit = pixels.length / (resampleX * resampleY) / 4; - for (let i = 0; i < limit; i++) { - let pixel = i * resampleX * resampleY; - - resampledPixels.push(pixels[pixel * 4]); - resampledPixels.push(pixels[pixel * 4 + 1]); - resampledPixels.push(pixels[pixel * 4 + 2]); - resampledPixels.push(pixels[pixel * 4 + 3]); - } - - return resampledPixels; + if (iconTexture instanceof Gio.FileIcon) { + // Use GdkPixBuf to load the pixel buffer from the provided file path + return GdkPixbuf.Pixbuf.new_from_file(iconTexture.get_file().get_path()) } -}; + // Get the pixel buffer from the icon theme + if (iconTexture instanceof Gio.ThemedIcon) { + let icon_info = themeLoader.lookup_icon( + iconTexture.get_names()[0], + DOMINANT_COLOR_ICON_SIZE, + 0, + ) -export const drawRoundedLine = function(cr, x, y, width, height, isRoundLeft, isRoundRight, stroke, fill) { - if (height > width) { - y += Math.floor((height - width) / 2.0); - height = width; + if (icon_info !== null) { + return icon_info.load_icon() + } } - - height = 2.0 * Math.floor(height / 2.0); - - const leftRadius = isRoundLeft ? height / 2.0 : 0.0; - const rightRadius = isRoundRight ? height / 2.0 : 0.0; - - cr.moveTo(x + width - rightRadius, y); - cr.lineTo(x + leftRadius, y); - if (isRoundLeft) - cr.arcNegative(x + leftRadius, y + leftRadius, leftRadius, -Math.PI/2, Math.PI/2); - else - cr.lineTo(x, y + height); - cr.lineTo(x + width - rightRadius, y + height); - if (isRoundRight) - cr.arcNegative(x + width - rightRadius, y + rightRadius, rightRadius, Math.PI/2, -Math.PI/2); - else - cr.lineTo(x + width, y); - cr.closePath(); - - if (fill != null) { - cr.setSource(fill); - cr.fillPreserve(); + + return null + } + + /** + * The backlight color choosing algorithm was mostly ported to javascript from the + * Unity7 C++ source of Canonicals: + * https://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/launcher/LauncherIcon.cpp + * so it more or less works the same way. + */ + _getColorPalette() { + if (iconCacheMap.get(this._app.get_id())) { + // We already know the answer + return iconCacheMap.get(this._app.get_id()) } - if (stroke != null) - cr.setSource(stroke); - cr.stroke(); + + let pixBuf = this._getIconPixBuf() + if (pixBuf == null) return null + + let pixels = pixBuf.get_pixels(), + offset = 0 + + let total = 0, + rTotal = 0, + gTotal = 0, + bTotal = 0 + + let resample_y = 1, + resample_x = 1 + + // Resampling of large icons + // We resample icons larger than twice the desired size, as the resampling + // to a size s + // DOMINANT_COLOR_ICON_SIZE < s < 2*DOMINANT_COLOR_ICON_SIZE, + // most of the case exactly DOMINANT_COLOR_ICON_SIZE as the icon size is tipycally + // a multiple of it. + let width = pixBuf.get_width() + let height = pixBuf.get_height() + + // Resample + if (height >= 2 * DOMINANT_COLOR_ICON_SIZE) + resample_y = Math.floor(height / DOMINANT_COLOR_ICON_SIZE) + + if (width >= 2 * DOMINANT_COLOR_ICON_SIZE) + resample_x = Math.floor(width / DOMINANT_COLOR_ICON_SIZE) + + if (resample_x !== 1 || resample_y !== 1) + pixels = this._resamplePixels(pixels, resample_x, resample_y) + + // computing the limit outside the for (where it would be repeated at each iteration) + // for performance reasons + let limit = pixels.length + for (let offset = 0; offset < limit; offset += 4) { + let r = pixels[offset], + g = pixels[offset + 1], + b = pixels[offset + 2], + a = pixels[offset + 3] + + let saturation = Math.max(r, g, b) - Math.min(r, g, b) + let relevance = 0.1 * 255 * 255 + 0.9 * a * saturation + + rTotal += r * relevance + gTotal += g * relevance + bTotal += b * relevance + + total += relevance + } + + total = total * 255 + + let r = rTotal / total, + g = gTotal / total, + b = bTotal / total + + let hsv = ColorUtils.RGBtoHSV(r * 255, g * 255, b * 255) + + if (hsv.s > 0.15) hsv.s = 0.65 + hsv.v = 0.9 + + let rgb = ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v) + + // Cache the result. + let backgroundColor = { + lighter: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, 0.2), + original: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, 0), + darker: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, -0.5), + } + + if (iconCacheMap.size >= MAX_CACHED_ITEMS) { + //delete oldest cached values (which are in order of insertions) + let ctr = 0 + for (let key of iconCacheMap.keys()) { + if (++ctr > BATCH_SIZE_TO_DELETE) break + iconCacheMap.delete(key) + } + } + + iconCacheMap.set(this._app.get_id(), backgroundColor) + + return backgroundColor + } + + /** + * Downsample large icons before scanning for the backlight color to + * improve performance. + * + * @param pixBuf + * @param pixels + * @param resampleX + * @param resampleY + * + * @return []; + */ + _resamplePixels(pixels, resampleX, resampleY) { + let resampledPixels = [] + // computing the limit outside the for (where it would be repeated at each iteration) + // for performance reasons + let limit = pixels.length / (resampleX * resampleY) / 4 + for (let i = 0; i < limit; i++) { + let pixel = i * resampleX * resampleY + + resampledPixels.push(pixels[pixel * 4]) + resampledPixels.push(pixels[pixel * 4 + 1]) + resampledPixels.push(pixels[pixel * 4 + 2]) + resampledPixels.push(pixels[pixel * 4 + 3]) + } + + return resampledPixels + } +} + +export const drawRoundedLine = function ( + cr, + x, + y, + width, + height, + isRoundLeft, + isRoundRight, + stroke, + fill, +) { + if (height > width) { + y += Math.floor((height - width) / 2.0) + height = width + } + + height = 2.0 * Math.floor(height / 2.0) + + const leftRadius = isRoundLeft ? height / 2.0 : 0.0 + const rightRadius = isRoundRight ? height / 2.0 : 0.0 + + cr.moveTo(x + width - rightRadius, y) + cr.lineTo(x + leftRadius, y) + if (isRoundLeft) + cr.arcNegative( + x + leftRadius, + y + leftRadius, + leftRadius, + -Math.PI / 2, + Math.PI / 2, + ) + else cr.lineTo(x, y + height) + cr.lineTo(x + width - rightRadius, y + height) + if (isRoundRight) + cr.arcNegative( + x + width - rightRadius, + y + rightRadius, + rightRadius, + Math.PI / 2, + -Math.PI / 2, + ) + else cr.lineTo(x + width, y) + cr.closePath() + + if (fill != null) { + cr.setSource(fill) + cr.fillPreserve() + } + if (stroke != null) cr.setSource(stroke) + cr.stroke() } diff --git a/windowPreview.js b/windowPreview.js index cbbdf1a..3773cb0 100644 --- a/windowPreview.js +++ b/windowPreview.js @@ -15,1138 +15,1380 @@ * along with this program. If not, see . */ -import GObject from 'gi://GObject'; -import Clutter from 'gi://Clutter'; -import GLib from 'gi://GLib'; -import Graphene from 'gi://Graphene'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import Meta from 'gi://Meta'; -import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; -import St from 'gi://St'; +import GObject from 'gi://GObject' +import Clutter from 'gi://Clutter' +import GLib from 'gi://GLib' +import Graphene from 'gi://Graphene' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import Meta from 'gi://Meta' +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js' +import St from 'gi://St' -import * as Taskbar from './taskbar.js'; -import * as Utils from './utils.js'; -import {SETTINGS, DESKTOPSETTINGS} from './extension.js'; -import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; +import * as Taskbar from './taskbar.js' +import * as Utils from './utils.js' +import { SETTINGS, DESKTOPSETTINGS } from './extension.js' +import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js' //timeout intervals -const ENSURE_VISIBLE_MS = 200; +const ENSURE_VISIBLE_MS = 200 //timeout names -const T1 = 'openMenuTimeout'; -const T2 = 'closeMenuTimeout'; -const T3 = 'peekTimeout'; -const T4 = 'ensureVisibleTimeout'; +const T1 = 'openMenuTimeout' +const T2 = 'closeMenuTimeout' +const T3 = 'peekTimeout' +const T4 = 'ensureVisibleTimeout' -const MAX_TRANSLATION = 40; -const HEADER_HEIGHT = 38; -const MAX_CLOSE_BUTTON_SIZE = 30; -const MIN_DIMENSION = 100; -const FOCUSED_COLOR_OFFSET = 24; -const HEADER_COLOR_OFFSET = -12; -const FADE_SIZE = 36; -const PEEK_INDEX_PROP = '_dtpPeekInitialIndex'; +const MAX_TRANSLATION = 40 +const HEADER_HEIGHT = 38 +const MAX_CLOSE_BUTTON_SIZE = 30 +const MIN_DIMENSION = 100 +const FOCUSED_COLOR_OFFSET = 24 +const HEADER_COLOR_OFFSET = -12 +const FADE_SIZE = 36 +const PEEK_INDEX_PROP = '_dtpPeekInitialIndex' -let headerHeight = 0; -let alphaBg = 0; -let isLeftButtons = false; -let isTopHeader = true; -let isManualStyling = false; -let scaleFactor = 1; -let animationTime = 0; -let aspectRatio = {}; - -export const PreviewMenu = GObject.registerClass({ - Signals: { 'open-state-changed': {} } -}, class PreviewMenu extends St.Widget { +let headerHeight = 0 +let alphaBg = 0 +let isLeftButtons = false +let isTopHeader = true +let isManualStyling = false +let scaleFactor = 1 +let animationTime = 0 +let aspectRatio = {} +export const PreviewMenu = GObject.registerClass( + { + Signals: { 'open-state-changed': {} }, + }, + class PreviewMenu extends St.Widget { _init(panel) { - super._init({ layout_manager: new Clutter.BinLayout() }); + super._init({ layout_manager: new Clutter.BinLayout() }) - let geom = panel.geom; - this.panel = panel; - this.currentAppIcon = null; - this._focusedPreview = null; - this._peekedWindow = null; - this.allowCloseWindow = true; - this.peekInitialWorkspaceIndex = -1; - this.opened = false; - this.isVertical = geom.position == St.Side.LEFT || geom.position == St.Side.RIGHT; - this._translationProp = 'translation_' + (this.isVertical ? 'x' : 'y'); - this._translationDirection = (geom.position == St.Side.TOP || geom.position == St.Side.LEFT ? -1 : 1); - this._translationOffset = Math.min(panel.dtpSize, MAX_TRANSLATION) * this._translationDirection; + let geom = panel.geom + this.panel = panel + this.currentAppIcon = null + this._focusedPreview = null + this._peekedWindow = null + this.allowCloseWindow = true + this.peekInitialWorkspaceIndex = -1 + this.opened = false + this.isVertical = + geom.position == St.Side.LEFT || geom.position == St.Side.RIGHT + this._translationProp = 'translation_' + (this.isVertical ? 'x' : 'y') + this._translationDirection = + geom.position == St.Side.TOP || geom.position == St.Side.LEFT ? -1 : 1 + this._translationOffset = + Math.min(panel.dtpSize, MAX_TRANSLATION) * this._translationDirection - this.menu = new St.Widget({ - name: 'preview-menu', - layout_manager: new Clutter.BinLayout(), - reactive: true, - track_hover: true, - x_expand: true, - y_expand: true, - x_align: Clutter.ActorAlign[geom.position != St.Side.RIGHT ? 'START' : 'END'], - y_align: Clutter.ActorAlign[geom.position != St.Side.BOTTOM ? 'START' : 'END'] - }); - this._box = new St.BoxLayout({ vertical: this.isVertical }); - this._scrollView = new St.ScrollView({ - name: 'dashtopanelPreviewScrollview', - hscrollbar_policy: St.PolicyType.NEVER, - vscrollbar_policy: St.PolicyType.NEVER, - enable_mouse_scrolling: true, - y_expand: !this.isVertical - }); + this.menu = new St.Widget({ + name: 'preview-menu', + layout_manager: new Clutter.BinLayout(), + reactive: true, + track_hover: true, + x_expand: true, + y_expand: true, + x_align: + Clutter.ActorAlign[geom.position != St.Side.RIGHT ? 'START' : 'END'], + y_align: + Clutter.ActorAlign[geom.position != St.Side.BOTTOM ? 'START' : 'END'], + }) + this._box = new St.BoxLayout({ vertical: this.isVertical }) + this._scrollView = new St.ScrollView({ + name: 'dashtopanelPreviewScrollview', + hscrollbar_policy: St.PolicyType.NEVER, + vscrollbar_policy: St.PolicyType.NEVER, + enable_mouse_scrolling: true, + y_expand: !this.isVertical, + }) - this._scrollView.add_child(this._box); - this.menu.add_child(this._scrollView); - this.add_child(this.menu); + this._scrollView.add_child(this._box) + this.menu.add_child(this._scrollView) + this.add_child(this.menu) } enable() { - this._timeoutsHandler = new Utils.TimeoutsHandler(); - this._signalsHandler = new Utils.GlobalSignalsHandler(); + this._timeoutsHandler = new Utils.TimeoutsHandler() + this._signalsHandler = new Utils.GlobalSignalsHandler() - Main.layoutManager.addChrome(this, { affectsInputRegion: false }); - Main.layoutManager.trackChrome(this.menu, { affectsInputRegion: true }); - - this._resetHiddenState(); - this._refreshGlobals(); - this._updateClip(); - this.menu.set_position(1, 1); + Main.layoutManager.addChrome(this, { affectsInputRegion: false }) + Main.layoutManager.trackChrome(this.menu, { affectsInputRegion: true }) - this._signalsHandler.add( - [ - this.menu, - 'notify::hover', - () => this._onHoverChanged() - ], - [ - this._scrollView, - 'scroll-event', - this._onScrollEvent.bind(this) - ], - [ - this.panel.panelBox, - 'style-changed', - () => this._updateClip() - ], - [ - Utils.DisplayWrapper.getScreen(), - 'in-fullscreen-changed', - () => { - if (global.display.focus_window && global.display.focus_window.is_fullscreen()) { - this.close(true); - } - } - ], - [ - SETTINGS, - [ - 'changed::panel-sizes', - 'changed::window-preview-size', - 'changed::window-preview-padding', - 'changed::window-preview-show-title' - ], - () => { - this._refreshGlobals(); - this._updateClip(); - } - ] - ); + this._resetHiddenState() + this._refreshGlobals() + this._updateClip() + this.menu.set_position(1, 1) + + this._signalsHandler.add( + [this.menu, 'notify::hover', () => this._onHoverChanged()], + [this._scrollView, 'scroll-event', this._onScrollEvent.bind(this)], + [this.panel.panelBox, 'style-changed', () => this._updateClip()], + [ + Utils.DisplayWrapper.getScreen(), + 'in-fullscreen-changed', + () => { + if ( + global.display.focus_window && + global.display.focus_window.is_fullscreen() + ) { + this.close(true) + } + }, + ], + [ + SETTINGS, + [ + 'changed::panel-sizes', + 'changed::window-preview-size', + 'changed::window-preview-padding', + 'changed::window-preview-show-title', + ], + () => { + this._refreshGlobals() + this._updateClip() + }, + ], + ) } disable() { - this._timeoutsHandler.destroy(); - this._signalsHandler.destroy(); + this._timeoutsHandler.destroy() + this._signalsHandler.destroy() - this.close(true); + this.close(true) - Main.layoutManager.untrackChrome(this.menu); - Main.layoutManager.removeChrome(this); + Main.layoutManager.untrackChrome(this.menu) + Main.layoutManager.removeChrome(this) } requestOpen(appIcon) { - let timeout = SETTINGS.get_int('show-window-previews-timeout'); + let timeout = SETTINGS.get_int('show-window-previews-timeout') - if (this.opened) { - timeout = Math.min(100, timeout); - } + if (this.opened) { + timeout = Math.min(100, timeout) + } - this._endOpenCloseTimeouts(); - this._timeoutsHandler.add([T1, timeout, () => this.open(appIcon)]); + this._endOpenCloseTimeouts() + this._timeoutsHandler.add([T1, timeout, () => this.open(appIcon)]) } requestClose() { - this._endOpenCloseTimeouts(); - this._addCloseTimeout(); + this._endOpenCloseTimeouts() + this._addCloseTimeout() } open(appIcon, preventCloseWindow) { - if (this.currentAppIcon != appIcon) { - this.currentAppIcon = appIcon; - this.allowCloseWindow = !preventCloseWindow; + if (this.currentAppIcon != appIcon) { + this.currentAppIcon = appIcon + this.allowCloseWindow = !preventCloseWindow - if (!this.opened) { - this._refreshGlobals(); - - this.set_height(this.clipHeight); - this.show(); - - setStyle(this.menu, 'background: ' + Utils.getrgbaColor(this.panel.dynamicTransparency.backgroundColorRgb, alphaBg)); - } + if (!this.opened) { + this._refreshGlobals() - this._mergeWindows(appIcon); - this._updatePosition(); - this._animateOpenOrClose(true); + this.set_height(this.clipHeight) + this.show() - this._setReactive(true); - this._setOpenedState(true); + setStyle( + this.menu, + 'background: ' + + Utils.getrgbaColor( + this.panel.dynamicTransparency.backgroundColorRgb, + alphaBg, + ), + ) } + + this._mergeWindows(appIcon) + this._updatePosition() + this._animateOpenOrClose(true) + + this._setReactive(true) + this._setOpenedState(true) + } } close(immediate) { - this._endOpenCloseTimeouts(); - this._removeFocus(); - this._endPeek(); - - if (immediate) { - Utils.stopAnimations(this.menu); - this._resetHiddenState(); - } else { - this._animateOpenOrClose(false, () => this._resetHiddenState()); - } + this._endOpenCloseTimeouts() + this._removeFocus() + this._endPeek() - this._setReactive(false); - this.currentAppIcon = null; + if (immediate) { + Utils.stopAnimations(this.menu) + this._resetHiddenState() + } else { + this._animateOpenOrClose(false, () => this._resetHiddenState()) + } + + this._setReactive(false) + this.currentAppIcon = null } update(appIcon, windows) { - if (this.currentAppIcon == appIcon) { - if (windows && !windows.length) { - this.close(); - } else { - this._addAndRemoveWindows(windows); - this._updatePosition(); - } + if (this.currentAppIcon == appIcon) { + if (windows && !windows.length) { + this.close() + } else { + this._addAndRemoveWindows(windows) + this._updatePosition() } + } } updatePosition() { - this._updatePosition(); + this._updatePosition() } focusNext() { - let previews = this._box.get_children(); - let currentIndex = this._focusedPreview ? previews.indexOf(this._focusedPreview) : -1; - let nextIndex = currentIndex + 1; - - nextIndex = previews[nextIndex] ? nextIndex : 0; + let previews = this._box.get_children() + let currentIndex = this._focusedPreview + ? previews.indexOf(this._focusedPreview) + : -1 + let nextIndex = currentIndex + 1 - if (previews[nextIndex]) { - this._removeFocus(); - previews[nextIndex].setFocus(true); - this._focusedPreview = previews[nextIndex]; - } + nextIndex = previews[nextIndex] ? nextIndex : 0 - return nextIndex; + if (previews[nextIndex]) { + this._removeFocus() + previews[nextIndex].setFocus(true) + this._focusedPreview = previews[nextIndex] + } + + return nextIndex } activateFocused() { - if (this.opened && this._focusedPreview) { - this._focusedPreview.activate(); - } + if (this.opened && this._focusedPreview) { + this._focusedPreview.activate() + } } requestPeek(window) { - this._timeoutsHandler.remove(T3); + this._timeoutsHandler.remove(T3) - if (SETTINGS.get_boolean('peek-mode')) { - if (this.peekInitialWorkspaceIndex < 0) { - this._timeoutsHandler.add([T3, SETTINGS.get_int('enter-peek-mode-timeout'), () => this._peek(window)]); - } else { - this._peek(window); - } + if (SETTINGS.get_boolean('peek-mode')) { + if (this.peekInitialWorkspaceIndex < 0) { + this._timeoutsHandler.add([ + T3, + SETTINGS.get_int('enter-peek-mode-timeout'), + () => this._peek(window), + ]) + } else { + this._peek(window) } + } } endPeekHere() { - this._endPeek(true); + this._endPeek(true) } ensureVisible(preview) { - let [ , upper, pageSize] = this._getScrollAdjustmentValues(); - - if (upper > pageSize) { - this._timeoutsHandler.add([ - T4, - ENSURE_VISIBLE_MS, - () => Utils.ensureActorVisibleInScrollView(this._scrollView, preview, MIN_DIMENSION, () => this._updateScrollFade()) - ]); - } + let [, upper, pageSize] = this._getScrollAdjustmentValues() + + if (upper > pageSize) { + this._timeoutsHandler.add([ + T4, + ENSURE_VISIBLE_MS, + () => + Utils.ensureActorVisibleInScrollView( + this._scrollView, + preview, + MIN_DIMENSION, + () => this._updateScrollFade(), + ), + ]) + } } getCurrentAppIcon() { - return this.currentAppIcon; + return this.currentAppIcon } _setReactive(reactive) { - this._box.get_children().forEach(c => c.reactive = reactive); - this.menu.reactive = reactive; + this._box.get_children().forEach((c) => (c.reactive = reactive)) + this.menu.reactive = reactive } _setOpenedState(opened) { - this.opened = opened; - this.emit('open-state-changed'); + this.opened = opened + this.emit('open-state-changed') } _resetHiddenState() { - this.hide(); - this.set_height(0); - this._setOpenedState(false); - this.menu.opacity = 0; - this.menu[this._translationProp] = this._translationOffset; - this._box.get_children().forEach(c => c.destroy()); + this.hide() + this.set_height(0) + this._setOpenedState(false) + this.menu.opacity = 0 + this.menu[this._translationProp] = this._translationOffset + this._box.get_children().forEach((c) => c.destroy()) } _removeFocus() { - if (this._focusedPreview) { - this._focusedPreview.setFocus(false); - this._focusedPreview = null; - } + if (this._focusedPreview) { + this._focusedPreview.setFocus(false) + this._focusedPreview = null + } } _mergeWindows(appIcon, windows) { - windows = windows || (appIcon.window ? [appIcon.window] : appIcon.getAppIconInterestingWindows()); - windows.sort(Taskbar.sortWindowsCompareFunction); - - let currentPreviews = this._box.get_children(); - let l = Math.max(windows.length, currentPreviews.length); + windows = + windows || + (appIcon.window + ? [appIcon.window] + : appIcon.getAppIconInterestingWindows()) + windows.sort(Taskbar.sortWindowsCompareFunction) - for (let i = 0; i < l; ++i) { - if (currentPreviews[i] && windows[i]) { - currentPreviews[i].assignWindow(windows[i], this.opened); - } else if (!currentPreviews[i]) { - this._addNewPreview(windows[i]); - } else if (!windows[i]) { - currentPreviews[i][!this.opened ? 'destroy' : 'animateOut'](); - } + let currentPreviews = this._box.get_children() + let l = Math.max(windows.length, currentPreviews.length) + + for (let i = 0; i < l; ++i) { + if (currentPreviews[i] && windows[i]) { + currentPreviews[i].assignWindow(windows[i], this.opened) + } else if (!currentPreviews[i]) { + this._addNewPreview(windows[i]) + } else if (!windows[i]) { + currentPreviews[i][!this.opened ? 'destroy' : 'animateOut']() } + } } _addAndRemoveWindows(windows) { - let currentPreviews = this._box.get_children(); + let currentPreviews = this._box.get_children() - windows.sort(Taskbar.sortWindowsCompareFunction); + windows.sort(Taskbar.sortWindowsCompareFunction) - for (let i = 0, l = windows.length; i < l; ++i) { - let currentIndex = Utils.findIndex(currentPreviews, c => c.window == windows[i]); - - if (currentIndex < 0) { - this._addNewPreview(windows[i]); - } else { - currentPreviews[currentIndex].assignWindow(windows[i]); - currentPreviews.splice(currentIndex, 1); + for (let i = 0, l = windows.length; i < l; ++i) { + let currentIndex = Utils.findIndex( + currentPreviews, + (c) => c.window == windows[i], + ) - if (this._peekedWindow && this._peekedWindow == windows[i]) { - this.requestPeek(windows[i]); - } - } + if (currentIndex < 0) { + this._addNewPreview(windows[i]) + } else { + currentPreviews[currentIndex].assignWindow(windows[i]) + currentPreviews.splice(currentIndex, 1) + + if (this._peekedWindow && this._peekedWindow == windows[i]) { + this.requestPeek(windows[i]) + } } + } - currentPreviews.forEach(c => c.animateOut()); + currentPreviews.forEach((c) => c.animateOut()) } _addNewPreview(window) { - let preview = new Preview(this); + let preview = new Preview(this) - this._box.add_child(preview); - preview.adjustOnStage(); - preview.assignWindow(window, this.opened); + this._box.add_child(preview) + preview.adjustOnStage() + preview.assignWindow(window, this.opened) } _addCloseTimeout() { - this._timeoutsHandler.add([T2, SETTINGS.get_int('leave-timeout'), () => this.close()]); + this._timeoutsHandler.add([ + T2, + SETTINGS.get_int('leave-timeout'), + () => this.close(), + ]) } _onHoverChanged() { - this._endOpenCloseTimeouts(); + this._endOpenCloseTimeouts() - if (this.currentAppIcon && !this.menu.hover) { - this._addCloseTimeout(); - this._endPeek(); - } + if (this.currentAppIcon && !this.menu.hover) { + this._addCloseTimeout() + this._endPeek() + } } _onScrollEvent(actor, event) { - if (!event.is_pointer_emulated()) { - let vOrh = this.isVertical ? 'v' : 'h'; - let adjustment = this._scrollView['get_' + vOrh + 'scroll_bar']().get_adjustment(); - let increment = adjustment.step_increment; - let delta = increment; + if (!event.is_pointer_emulated()) { + let vOrh = this.isVertical ? 'v' : 'h' + let adjustment = + this._scrollView['get_' + vOrh + 'scroll_bar']().get_adjustment() + let increment = adjustment.step_increment + let delta = increment - switch (event.get_scroll_direction()) { - case Clutter.ScrollDirection.UP: - delta = -increment; - break; - case Clutter.ScrollDirection.SMOOTH: - let [dx, dy] = event.get_scroll_delta(); - delta = dy * increment; - delta += dx * increment; - break; - } - - adjustment.set_value(adjustment.get_value() + delta); - this._updateScrollFade(); + switch (event.get_scroll_direction()) { + case Clutter.ScrollDirection.UP: + delta = -increment + break + case Clutter.ScrollDirection.SMOOTH: + let [dx, dy] = event.get_scroll_delta() + delta = dy * increment + delta += dx * increment + break } - return Clutter.EVENT_STOP; + adjustment.set_value(adjustment.get_value() + delta) + this._updateScrollFade() + } + + return Clutter.EVENT_STOP } _endOpenCloseTimeouts() { - this._timeoutsHandler.remove(T1); - this._timeoutsHandler.remove(T2); - this._timeoutsHandler.remove(T4); + this._timeoutsHandler.remove(T1) + this._timeoutsHandler.remove(T2) + this._timeoutsHandler.remove(T4) } _refreshGlobals() { - isLeftButtons = Meta.prefs_get_button_layout().left_buttons.indexOf(Meta.ButtonFunction.CLOSE) >= 0; - isTopHeader = SETTINGS.get_string('window-preview-title-position') == 'TOP'; - isManualStyling = SETTINGS.get_boolean('window-preview-manual-styling'); - scaleFactor = Utils.getScaleFactor(); - headerHeight = SETTINGS.get_boolean('window-preview-show-title') ? HEADER_HEIGHT * scaleFactor : 0; - animationTime = SETTINGS.get_int('window-preview-animation-time') * .001; - aspectRatio.x = { - size: SETTINGS.get_int('window-preview-aspect-ratio-x'), - fixed: SETTINGS.get_boolean('window-preview-fixed-x') - }; - aspectRatio.y = { - size: SETTINGS.get_int('window-preview-aspect-ratio-y'), - fixed: SETTINGS.get_boolean('window-preview-fixed-y') - }; - - alphaBg = SETTINGS.get_boolean('preview-use-custom-opacity') ? - SETTINGS.get_int('preview-custom-opacity') * .01 : - this.panel.dynamicTransparency.alpha; + isLeftButtons = + Meta.prefs_get_button_layout().left_buttons.indexOf( + Meta.ButtonFunction.CLOSE, + ) >= 0 + isTopHeader = + SETTINGS.get_string('window-preview-title-position') == 'TOP' + isManualStyling = SETTINGS.get_boolean('window-preview-manual-styling') + scaleFactor = Utils.getScaleFactor() + headerHeight = SETTINGS.get_boolean('window-preview-show-title') + ? HEADER_HEIGHT * scaleFactor + : 0 + animationTime = SETTINGS.get_int('window-preview-animation-time') * 0.001 + aspectRatio.x = { + size: SETTINGS.get_int('window-preview-aspect-ratio-x'), + fixed: SETTINGS.get_boolean('window-preview-fixed-x'), + } + aspectRatio.y = { + size: SETTINGS.get_int('window-preview-aspect-ratio-y'), + fixed: SETTINGS.get_boolean('window-preview-fixed-y'), + } + + alphaBg = SETTINGS.get_boolean('preview-use-custom-opacity') + ? SETTINGS.get_int('preview-custom-opacity') * 0.01 + : this.panel.dynamicTransparency.alpha } _updateClip() { - let x, y, w; - let geom = this.panel.getGeometry(); - let panelBoxTheme = this.panel.panelBox.get_theme_node(); - let previewSize = (SETTINGS.get_int('window-preview-size') + - SETTINGS.get_int('window-preview-padding') * 2) * scaleFactor; - - if (this.isVertical) { - w = previewSize; - this.clipHeight = this.panel.monitor.height; - y = this.panel.monitor.y; - } else { - w = this.panel.monitor.width; - this.clipHeight = (previewSize + headerHeight); - x = this.panel.monitor.x; - } + let x, y, w + let geom = this.panel.getGeometry() + let panelBoxTheme = this.panel.panelBox.get_theme_node() + let previewSize = + (SETTINGS.get_int('window-preview-size') + + SETTINGS.get_int('window-preview-padding') * 2) * + scaleFactor - if (geom.position == St.Side.LEFT) { - x = this.panel.monitor.x + this.panel.dtpSize + panelBoxTheme.get_padding(St.Side.LEFT); - } else if (geom.position == St.Side.RIGHT) { - x = this.panel.monitor.x + this.panel.monitor.width - (this.panel.dtpSize + previewSize) - panelBoxTheme.get_padding(St.Side.RIGHT); - } else if (geom.position == St.Side.TOP) { - y = this.panel.monitor.y + this.panel.dtpSize + panelBoxTheme.get_padding(St.Side.TOP); - } else { //St.Side.BOTTOM - y = this.panel.monitor.y + this.panel.monitor.height - (this.panel.dtpSize + panelBoxTheme.get_padding(St.Side.BOTTOM) + previewSize + headerHeight); - } + if (this.isVertical) { + w = previewSize + this.clipHeight = this.panel.monitor.height + y = this.panel.monitor.y + } else { + w = this.panel.monitor.width + this.clipHeight = previewSize + headerHeight + x = this.panel.monitor.x + } - Utils.setClip(this, x, y, w, this.clipHeight); + if (geom.position == St.Side.LEFT) { + x = + this.panel.monitor.x + + this.panel.dtpSize + + panelBoxTheme.get_padding(St.Side.LEFT) + } else if (geom.position == St.Side.RIGHT) { + x = + this.panel.monitor.x + + this.panel.monitor.width - + (this.panel.dtpSize + previewSize) - + panelBoxTheme.get_padding(St.Side.RIGHT) + } else if (geom.position == St.Side.TOP) { + y = + this.panel.monitor.y + + this.panel.dtpSize + + panelBoxTheme.get_padding(St.Side.TOP) + } else { + //St.Side.BOTTOM + y = + this.panel.monitor.y + + this.panel.monitor.height - + (this.panel.dtpSize + + panelBoxTheme.get_padding(St.Side.BOTTOM) + + previewSize + + headerHeight) + } + + Utils.setClip(this, x, y, w, this.clipHeight) } _updatePosition() { - let sourceNode = this.currentAppIcon.get_theme_node(); - let sourceContentBox = sourceNode.get_content_box(this.currentAppIcon.get_allocation_box()); - let sourceAllocation = Utils.getTransformedAllocation(this.currentAppIcon); - let [previewsWidth, previewsHeight] = this._getPreviewsSize(); - let appIconMargin = SETTINGS.get_int('appicon-margin') / scaleFactor; - let x = 0, y = 0; + let sourceNode = this.currentAppIcon.get_theme_node() + let sourceContentBox = sourceNode.get_content_box( + this.currentAppIcon.get_allocation_box(), + ) + let sourceAllocation = Utils.getTransformedAllocation(this.currentAppIcon) + let [previewsWidth, previewsHeight] = this._getPreviewsSize() + let appIconMargin = SETTINGS.get_int('appicon-margin') / scaleFactor + let x = 0, + y = 0 - previewsWidth = Math.min(previewsWidth, this.panel.monitor.width); - previewsHeight = Math.min(previewsHeight, this.panel.monitor.height); - this._updateScrollFade(previewsWidth < this.panel.monitor.width && previewsHeight < this.panel.monitor.height); - - if (this.isVertical) { - y = sourceAllocation.y1 + appIconMargin - this.panel.monitor.y + (sourceContentBox.y2 - sourceContentBox.y1 - previewsHeight) * .5; - y = Math.max(y, 0); - y = Math.min(y, this.panel.monitor.height - previewsHeight); - } else { - x = sourceAllocation.x1 + appIconMargin - this.panel.monitor.x + (sourceContentBox.x2 - sourceContentBox.x1 - previewsWidth) * .5; - x = Math.max(x, 0); - x = Math.min(x, this.panel.monitor.width - previewsWidth); - } + previewsWidth = Math.min(previewsWidth, this.panel.monitor.width) + previewsHeight = Math.min(previewsHeight, this.panel.monitor.height) + this._updateScrollFade( + previewsWidth < this.panel.monitor.width && + previewsHeight < this.panel.monitor.height, + ) - if (!this.opened) { - this.menu.set_position(x, y); - this.menu.set_size(previewsWidth, previewsHeight); - } else { - Utils.animate(this.menu, getTweenOpts({ x: x, y: y, width: previewsWidth, height: previewsHeight })); - } + if (this.isVertical) { + y = + sourceAllocation.y1 + + appIconMargin - + this.panel.monitor.y + + (sourceContentBox.y2 - sourceContentBox.y1 - previewsHeight) * 0.5 + y = Math.max(y, 0) + y = Math.min(y, this.panel.monitor.height - previewsHeight) + } else { + x = + sourceAllocation.x1 + + appIconMargin - + this.panel.monitor.x + + (sourceContentBox.x2 - sourceContentBox.x1 - previewsWidth) * 0.5 + x = Math.max(x, 0) + x = Math.min(x, this.panel.monitor.width - previewsWidth) + } + + if (!this.opened) { + this.menu.set_position(x, y) + this.menu.set_size(previewsWidth, previewsHeight) + } else { + Utils.animate( + this.menu, + getTweenOpts({ + x: x, + y: y, + width: previewsWidth, + height: previewsHeight, + }), + ) + } } _updateScrollFade(remove) { - let [value, upper, pageSize] = this._getScrollAdjustmentValues(); - let needsFade = Math.round(upper) > Math.round(pageSize); - let fadeWidgets = this.menu.get_children().filter(c => c != this._scrollView); - - if (!remove && needsFade) { - if (!fadeWidgets.length) { - fadeWidgets.push(this._getFadeWidget()); - fadeWidgets.push(this._getFadeWidget(true)); - - this.menu.add_child(fadeWidgets[0]); - this.menu.add_child(fadeWidgets[1]); - } - - fadeWidgets[0].visible = value > 0; - fadeWidgets[1].visible = value + pageSize < upper; - } else if (remove || (!needsFade && fadeWidgets.length)) { - fadeWidgets.forEach(fw => fw.destroy()); + let [value, upper, pageSize] = this._getScrollAdjustmentValues() + let needsFade = Math.round(upper) > Math.round(pageSize) + let fadeWidgets = this.menu + .get_children() + .filter((c) => c != this._scrollView) + + if (!remove && needsFade) { + if (!fadeWidgets.length) { + fadeWidgets.push(this._getFadeWidget()) + fadeWidgets.push(this._getFadeWidget(true)) + + this.menu.add_child(fadeWidgets[0]) + this.menu.add_child(fadeWidgets[1]) } + + fadeWidgets[0].visible = value > 0 + fadeWidgets[1].visible = value + pageSize < upper + } else if (remove || (!needsFade && fadeWidgets.length)) { + fadeWidgets.forEach((fw) => fw.destroy()) + } } _getScrollAdjustmentValues() { - let [value , , upper, , , pageSize] = this._scrollView[(this.isVertical ? 'v' : 'h') + 'adjustment'].get_values(); + let [value, , upper, , , pageSize] = + this._scrollView[ + (this.isVertical ? 'v' : 'h') + 'adjustment' + ].get_values() - return [value, upper, pageSize]; + return [value, upper, pageSize] } _getFadeWidget(end) { - let x = 0, y = 0; - let startBg = Utils.getrgbaColor(this.panel.dynamicTransparency.backgroundColorRgb, Math.min(alphaBg + .1, 1)); - let endBg = Utils.getrgbaColor(this.panel.dynamicTransparency.backgroundColorRgb, 0) - let fadeStyle = 'background-gradient-start:' + startBg + - 'background-gradient-end:' + endBg + - 'background-gradient-direction:' + this.panel.getOrientation(); + let x = 0, + y = 0 + let startBg = Utils.getrgbaColor( + this.panel.dynamicTransparency.backgroundColorRgb, + Math.min(alphaBg + 0.1, 1), + ) + let endBg = Utils.getrgbaColor( + this.panel.dynamicTransparency.backgroundColorRgb, + 0, + ) + let fadeStyle = + 'background-gradient-start:' + + startBg + + 'background-gradient-end:' + + endBg + + 'background-gradient-direction:' + + this.panel.getOrientation() - if (this.isVertical) { - y = end ? this.panel.monitor.height - FADE_SIZE : 0; - } else { - x = end ? this.panel.monitor.width - FADE_SIZE : 0; - } + if (this.isVertical) { + y = end ? this.panel.monitor.height - FADE_SIZE : 0 + } else { + x = end ? this.panel.monitor.width - FADE_SIZE : 0 + } - let fadeWidget = new St.Widget({ - reactive: false, - pivot_point: new Graphene.Point({ x: .5, y: .5 }), - rotation_angle_z: end ? 180 : 0, - style: fadeStyle, - x: x, y: y, - width: this.isVertical ? this.width : FADE_SIZE, - height: this.isVertical ? FADE_SIZE : this.height - }); + let fadeWidget = new St.Widget({ + reactive: false, + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + rotation_angle_z: end ? 180 : 0, + style: fadeStyle, + x: x, + y: y, + width: this.isVertical ? this.width : FADE_SIZE, + height: this.isVertical ? FADE_SIZE : this.height, + }) - return fadeWidget; + return fadeWidget } _getPreviewsSize() { - let previewsWidth = 0; - let previewsHeight = 0; + let previewsWidth = 0 + let previewsHeight = 0 - this._box.get_children().forEach(c => { - if (!c.animatingOut) { - let [width, height] = c.getSize(); + this._box.get_children().forEach((c) => { + if (!c.animatingOut) { + let [width, height] = c.getSize() - if (this.isVertical) { - previewsWidth = Math.max(width, previewsWidth); - previewsHeight += height; - } else { - previewsWidth += width; - previewsHeight = Math.max(height, previewsHeight); - } - } - }); + if (this.isVertical) { + previewsWidth = Math.max(width, previewsWidth) + previewsHeight += height + } else { + previewsWidth += width + previewsHeight = Math.max(height, previewsHeight) + } + } + }) - return [previewsWidth, previewsHeight]; + return [previewsWidth, previewsHeight] } _animateOpenOrClose(show, onComplete) { - let isTranslationAnimation = this.menu[this._translationProp] != 0; - let tweenOpts = { - opacity: show ? 255 : 0, - transition: show ? 'easeInOutQuad' : 'easeInCubic', - onComplete: () => { - if (isTranslationAnimation) { - Main.layoutManager._queueUpdateRegions(); - } - - (onComplete || (() => {}))(); - } - }; + let isTranslationAnimation = this.menu[this._translationProp] != 0 + let tweenOpts = { + opacity: show ? 255 : 0, + transition: show ? 'easeInOutQuad' : 'easeInCubic', + onComplete: () => { + if (isTranslationAnimation) { + Main.layoutManager._queueUpdateRegions() + } - tweenOpts[this._translationProp] = show ? this._translationDirection : this._translationOffset; + ;(onComplete || (() => {}))() + }, + } - Utils.animate(this.menu, getTweenOpts(tweenOpts)); + tweenOpts[this._translationProp] = show + ? this._translationDirection + : this._translationOffset + + Utils.animate(this.menu, getTweenOpts(tweenOpts)) } _peek(window) { - let currentWorkspace = Utils.getCurrentWorkspace(); - let windowWorkspace = window.get_workspace(); - let focusWindow = () => this._focusMetaWindow(SETTINGS.get_int('peek-mode-opacity'), window); - - this._restorePeekedWindowStack(); + let currentWorkspace = Utils.getCurrentWorkspace() + let windowWorkspace = window.get_workspace() + let focusWindow = () => + this._focusMetaWindow(SETTINGS.get_int('peek-mode-opacity'), window) - if (this._peekedWindow && windowWorkspace != currentWorkspace) { - currentWorkspace.list_windows().forEach(mw => this.animateWindowOpacity(mw, null, 255)) - } + this._restorePeekedWindowStack() - this._peekedWindow = window; - - if (currentWorkspace != windowWorkspace) { - this._switchToWorkspaceImmediate(windowWorkspace.index()); - this._timeoutsHandler.add([T3, 100, focusWindow]); - } else { - focusWindow(); - } + if (this._peekedWindow && windowWorkspace != currentWorkspace) { + currentWorkspace + .list_windows() + .forEach((mw) => this.animateWindowOpacity(mw, null, 255)) + } - if (this.peekInitialWorkspaceIndex < 0) { - this.peekInitialWorkspaceIndex = currentWorkspace.index(); - } + this._peekedWindow = window + + if (currentWorkspace != windowWorkspace) { + this._switchToWorkspaceImmediate(windowWorkspace.index()) + this._timeoutsHandler.add([T3, 100, focusWindow]) + } else { + focusWindow() + } + + if (this.peekInitialWorkspaceIndex < 0) { + this.peekInitialWorkspaceIndex = currentWorkspace.index() + } } _endPeek(stayHere) { - this._timeoutsHandler.remove(T3); + this._timeoutsHandler.remove(T3) - if (this._peekedWindow) { - let immediate = !stayHere && this.peekInitialWorkspaceIndex != Utils.getCurrentWorkspace().index(); + if (this._peekedWindow) { + let immediate = + !stayHere && + this.peekInitialWorkspaceIndex != Utils.getCurrentWorkspace().index() - this._restorePeekedWindowStack(); - this._focusMetaWindow(255, this._peekedWindow, immediate, true); - this._peekedWindow = null; + this._restorePeekedWindowStack() + this._focusMetaWindow(255, this._peekedWindow, immediate, true) + this._peekedWindow = null - if (!stayHere) { - this._switchToWorkspaceImmediate(this.peekInitialWorkspaceIndex); - } - - this.peekInitialWorkspaceIndex = -1; + if (!stayHere) { + this._switchToWorkspaceImmediate(this.peekInitialWorkspaceIndex) } + + this.peekInitialWorkspaceIndex = -1 + } } _switchToWorkspaceImmediate(workspaceIndex) { - let workspace = Utils.getWorkspaceByIndex(workspaceIndex); - let shouldAnimate = Main.wm._shouldAnimate; + let workspace = Utils.getWorkspaceByIndex(workspaceIndex) + let shouldAnimate = Main.wm._shouldAnimate - if (!workspace || (!workspace.list_windows().length && - workspaceIndex < Utils.getWorkspaceCount() - 1)) { - workspace = Utils.getCurrentWorkspace(); - } + if ( + !workspace || + (!workspace.list_windows().length && + workspaceIndex < Utils.getWorkspaceCount() - 1) + ) { + workspace = Utils.getCurrentWorkspace() + } - Main.wm._shouldAnimate = () => false; - workspace.activate(global.display.get_current_time_roundtrip()); - Main.wm._shouldAnimate = shouldAnimate; + Main.wm._shouldAnimate = () => false + workspace.activate(global.display.get_current_time_roundtrip()) + Main.wm._shouldAnimate = shouldAnimate } _focusMetaWindow(dimOpacity, window, immediate, ignoreFocus) { - window.get_workspace().list_windows().forEach(mw => { - let wa = mw.get_compositor_private(); - let isFocused = !ignoreFocus && mw == window; + window + .get_workspace() + .list_windows() + .forEach((mw) => { + let wa = mw.get_compositor_private() + let isFocused = !ignoreFocus && mw == window - if (wa) { - if (isFocused) { - mw[PEEK_INDEX_PROP] = wa.get_parent().get_children().indexOf(wa); - wa.get_parent().set_child_above_sibling(wa, null); - } - - if (isFocused && mw.minimized) { - wa.show(); - } - - this.animateWindowOpacity(mw, wa, isFocused ? 255 : dimOpacity, immediate) + if (wa) { + if (isFocused) { + mw[PEEK_INDEX_PROP] = wa.get_parent().get_children().indexOf(wa) + wa.get_parent().set_child_above_sibling(wa, null) } - }); + + if (isFocused && mw.minimized) { + wa.show() + } + + this.animateWindowOpacity( + mw, + wa, + isFocused ? 255 : dimOpacity, + immediate, + ) + } + }) } animateWindowOpacity(metaWindow, windowActor, opacity, immediate) { - windowActor = windowActor || metaWindow.get_compositor_private(); - - if (windowActor && !metaWindow.minimized) { - let tweenOpts = getTweenOpts({ opacity }); + windowActor = windowActor || metaWindow.get_compositor_private() - if (immediate && !metaWindow.is_on_all_workspaces()) { - tweenOpts.time = 0; - } - - Utils.animateWindowOpacity(windowActor, tweenOpts); + if (windowActor && !metaWindow.minimized) { + let tweenOpts = getTweenOpts({ opacity }) + + if (immediate && !metaWindow.is_on_all_workspaces()) { + tweenOpts.time = 0 } + + Utils.animateWindowOpacity(windowActor, tweenOpts) + } } _restorePeekedWindowStack() { - let windowActor = this._peekedWindow ? this._peekedWindow.get_compositor_private() : null; + let windowActor = this._peekedWindow + ? this._peekedWindow.get_compositor_private() + : null - if (windowActor) { - if (this._peekedWindow.hasOwnProperty(PEEK_INDEX_PROP)) { - windowActor.get_parent().set_child_at_index(windowActor, this._peekedWindow[PEEK_INDEX_PROP]); - delete this._peekedWindow[PEEK_INDEX_PROP]; - } - - if (this._peekedWindow.minimized) { - windowActor.hide(); - } + if (windowActor) { + if (this._peekedWindow.hasOwnProperty(PEEK_INDEX_PROP)) { + windowActor + .get_parent() + .set_child_at_index( + windowActor, + this._peekedWindow[PEEK_INDEX_PROP], + ) + delete this._peekedWindow[PEEK_INDEX_PROP] } + + if (this._peekedWindow.minimized) { + windowActor.hide() + } + } } -}); - -export const Preview = GObject.registerClass({ -}, class Preview extends St.Widget { + }, +) +export const Preview = GObject.registerClass( + {}, + class Preview extends St.Widget { _init(previewMenu) { - super._init({ - style_class: 'preview-container', - reactive: true, - track_hover: true, - layout_manager: new Clutter.BinLayout() - }); + super._init({ + style_class: 'preview-container', + reactive: true, + track_hover: true, + layout_manager: new Clutter.BinLayout(), + }) - this.window = null; - this._waitWindowId = 0; - this._needsCloseButton = true; - this.cloneWidth = this.cloneHeight = 0; - this._previewMenu = previewMenu; - this._padding = SETTINGS.get_int('window-preview-padding') * scaleFactor; - this._previewDimensions = this._getPreviewDimensions(); - this.animatingOut = false; + this.window = null + this._waitWindowId = 0 + this._needsCloseButton = true + this.cloneWidth = this.cloneHeight = 0 + this._previewMenu = previewMenu + this._padding = SETTINGS.get_int('window-preview-padding') * scaleFactor + this._previewDimensions = this._getPreviewDimensions() + this.animatingOut = false - let box = new St.Widget({ layout_manager: new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL }), y_expand: true }); - let [previewBinWidth, previewBinHeight] = this._getBinSize(); - let closeButton = new St.Button({ style_class: 'window-close', accessible_name: 'Close window' }); + let box = new St.Widget({ + layout_manager: new Clutter.BoxLayout({ + orientation: Clutter.Orientation.VERTICAL, + }), + y_expand: true, + }) + let [previewBinWidth, previewBinHeight] = this._getBinSize() + let closeButton = new St.Button({ + style_class: 'window-close', + accessible_name: 'Close window', + }) - closeButton.add_child(new St.Icon({ icon_name: 'window-close-symbolic' })); + closeButton.add_child(new St.Icon({ icon_name: 'window-close-symbolic' })) - this._closeButtonBin = new St.Widget({ - style_class: 'preview-close-btn-container', - layout_manager: new Clutter.BinLayout(), - opacity: 0, - x_expand: true, y_expand: true, - x_align: Clutter.ActorAlign[isLeftButtons ? 'START' : 'END'], - y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END'] - }); + this._closeButtonBin = new St.Widget({ + style_class: 'preview-close-btn-container', + layout_manager: new Clutter.BinLayout(), + opacity: 0, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign[isLeftButtons ? 'START' : 'END'], + y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END'], + }) - this._closeButtonBin.add_child(closeButton); + this._closeButtonBin.add_child(closeButton) - this._previewBin = new St.Widget({ - layout_manager: new Clutter.BinLayout(), - x_expand: true, y_expand: true, - style: 'padding: ' + this._padding / scaleFactor + 'px;' - }); + this._previewBin = new St.Widget({ + layout_manager: new Clutter.BinLayout(), + x_expand: true, + y_expand: true, + style: 'padding: ' + this._padding / scaleFactor + 'px;', + }) - this._previewBin.set_size(previewBinWidth, previewBinHeight); + this._previewBin.set_size(previewBinWidth, previewBinHeight) - box.add_child(this._previewBin); - - if (headerHeight) { - let headerBox = new St.Widget({ - style_class: 'preview-header-box', - layout_manager: new Clutter.BoxLayout(), - x_expand: true, - y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END'] - }); - - setStyle(headerBox, this._getBackgroundColor(HEADER_COLOR_OFFSET, 1)); - this._workspaceIndicator = new St.Label({ y_align: Clutter.ActorAlign.CENTER }); - this._windowTitle = new St.Label({ y_align: Clutter.ActorAlign.CENTER, x_expand: true }); + box.add_child(this._previewBin) - this._iconBin = new St.Widget({ layout_manager: new Clutter.BinLayout() }); - this._iconBin.set_size(headerHeight, headerHeight); - - headerBox.add_child(this._iconBin); - headerBox.insert_child_at_index(this._workspaceIndicator, isLeftButtons ? 0 : 1); - headerBox.insert_child_at_index(this._windowTitle, isLeftButtons ? 1 : 2); + if (headerHeight) { + let headerBox = new St.Widget({ + style_class: 'preview-header-box', + layout_manager: new Clutter.BoxLayout(), + x_expand: true, + y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END'], + }) - box.insert_child_at_index(headerBox, isTopHeader ? 0 : 1); - } + setStyle(headerBox, this._getBackgroundColor(HEADER_COLOR_OFFSET, 1)) + this._workspaceIndicator = new St.Label({ + y_align: Clutter.ActorAlign.CENTER, + }) + this._windowTitle = new St.Label({ + y_align: Clutter.ActorAlign.CENTER, + x_expand: true, + }) - this.add_child(box); - this.add_child(this._closeButtonBin); + this._iconBin = new St.Widget({ + layout_manager: new Clutter.BinLayout(), + }) + this._iconBin.set_size(headerHeight, headerHeight) - closeButton.connect('clicked', () => this._onCloseBtnClick()); - this.connect('notify::hover', () => this._onHoverChanged()); - this.connect('button-release-event', (actor, e) => this._onButtonReleaseEvent(e)); - this.connect('destroy', () => this._onDestroy()); + headerBox.add_child(this._iconBin) + headerBox.insert_child_at_index( + this._workspaceIndicator, + isLeftButtons ? 0 : 1, + ) + headerBox.insert_child_at_index( + this._windowTitle, + isLeftButtons ? 1 : 2, + ) + + box.insert_child_at_index(headerBox, isTopHeader ? 0 : 1) + } + + this.add_child(box) + this.add_child(this._closeButtonBin) + + closeButton.connect('clicked', () => this._onCloseBtnClick()) + this.connect('notify::hover', () => this._onHoverChanged()) + this.connect('button-release-event', (actor, e) => + this._onButtonReleaseEvent(e), + ) + this.connect('destroy', () => this._onDestroy()) } adjustOnStage() { - let closeButton = this._closeButtonBin.get_first_child(); - let closeButtonHeight = closeButton.height; - let maxCloseButtonSize = MAX_CLOSE_BUTTON_SIZE * scaleFactor; - let closeButtonBorderRadius = ''; + let closeButton = this._closeButtonBin.get_first_child() + let closeButtonHeight = closeButton.height + let maxCloseButtonSize = MAX_CLOSE_BUTTON_SIZE * scaleFactor + let closeButtonBorderRadius = '' - if (closeButtonHeight > maxCloseButtonSize) { - closeButtonHeight = maxCloseButtonSize; - closeButton.set_size(closeButtonHeight, closeButtonHeight); + if (closeButtonHeight > maxCloseButtonSize) { + closeButtonHeight = maxCloseButtonSize + closeButton.set_size(closeButtonHeight, closeButtonHeight) + } + + if (!headerHeight) { + closeButtonBorderRadius = 'border-radius: ' + + if (isTopHeader) { + closeButtonBorderRadius += isLeftButtons ? '0 0 4px 0;' : '0 0 0 4px;' + } else { + closeButtonBorderRadius += isLeftButtons ? '0 4px 0 0;' : '4px 0 0 0;' } + } - if (!headerHeight) { - closeButtonBorderRadius = 'border-radius: '; - - if (isTopHeader) { - closeButtonBorderRadius += (isLeftButtons ? '0 0 4px 0;' : '0 0 0 4px;'); - } else { - closeButtonBorderRadius += (isLeftButtons ? '0 4px 0 0;' : '4px 0 0 0;'); - } - } - - setStyle( - this._closeButtonBin, - 'padding: ' + (headerHeight ? Math.round((headerHeight - closeButtonHeight) * .5 / scaleFactor) : 4) + 'px;' + - this._getBackgroundColor(HEADER_COLOR_OFFSET, headerHeight ? 1 : .6) + - closeButtonBorderRadius - ); + setStyle( + this._closeButtonBin, + 'padding: ' + + (headerHeight + ? Math.round( + ((headerHeight - closeButtonHeight) * 0.5) / scaleFactor, + ) + : 4) + + 'px;' + + this._getBackgroundColor( + HEADER_COLOR_OFFSET, + headerHeight ? 1 : 0.6, + ) + + closeButtonBorderRadius, + ) } assignWindow(window, animateSize) { - if (this.window != window) { - let _assignWindowClone = () => { - if (window.get_compositor_private()) { - let cloneBin = this._getWindowCloneBin(window); - - this._resizeClone(cloneBin, window); - this._addClone(cloneBin, animateSize); - this._previewMenu.updatePosition(); - } else if (!this._waitWindowId) { - this._waitWindowId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - this._waitWindowId = 0; + if (this.window != window) { + let _assignWindowClone = () => { + if (window.get_compositor_private()) { + let cloneBin = this._getWindowCloneBin(window) - if (this._previewMenu.opened) { - _assignWindowClone(); - } + this._resizeClone(cloneBin, window) + this._addClone(cloneBin, animateSize) + this._previewMenu.updatePosition() + } else if (!this._waitWindowId) { + this._waitWindowId = GLib.idle_add( + GLib.PRIORITY_DEFAULT_IDLE, + () => { + this._waitWindowId = 0 - return GLib.SOURCE_REMOVE; - }); + if (this._previewMenu.opened) { + _assignWindowClone() } - }; - _assignWindowClone(); + return GLib.SOURCE_REMOVE + }, + ) + } } - this._cancelAnimateOut(); - this._removeWindowSignals(); - this.window = window; - this._needsCloseButton = this._previewMenu.allowCloseWindow && window.can_close() && !Utils.checkIfWindowHasTransient(window); - this._updateHeader(); + _assignWindowClone() + } + + this._cancelAnimateOut() + this._removeWindowSignals() + this.window = window + this._needsCloseButton = + this._previewMenu.allowCloseWindow && + window.can_close() && + !Utils.checkIfWindowHasTransient(window) + this._updateHeader() } animateOut() { - if (!this.animatingOut) { - let tweenOpts = getTweenOpts({ opacity: 0, width: 0, height: 0, onComplete: () => this.destroy() }); + if (!this.animatingOut) { + let tweenOpts = getTweenOpts({ + opacity: 0, + width: 0, + height: 0, + onComplete: () => this.destroy(), + }) - this.animatingOut = true; + this.animatingOut = true - Utils.stopAnimations(this); - Utils.animate(this, tweenOpts); - } + Utils.stopAnimations(this) + Utils.animate(this, tweenOpts) + } } getSize() { - let [binWidth, binHeight] = this._getBinSize(); + let [binWidth, binHeight] = this._getBinSize() - binWidth = Math.max(binWidth, this.cloneWidth + this._padding * 2); - binHeight = Math.max(binHeight, this.cloneHeight + this._padding * 2) + headerHeight; + binWidth = Math.max(binWidth, this.cloneWidth + this._padding * 2) + binHeight = + Math.max(binHeight, this.cloneHeight + this._padding * 2) + headerHeight - return [binWidth, binHeight]; + return [binWidth, binHeight] } setFocus(focused) { - this._hideOrShowCloseButton(!focused); - setStyle(this, this._getBackgroundColor(FOCUSED_COLOR_OFFSET, focused ? '-' : 0)); + this._hideOrShowCloseButton(!focused) + setStyle( + this, + this._getBackgroundColor(FOCUSED_COLOR_OFFSET, focused ? '-' : 0), + ) - if (focused) { - this._previewMenu.ensureVisible(this); - this._previewMenu.requestPeek(this.window); - } + if (focused) { + this._previewMenu.ensureVisible(this) + this._previewMenu.requestPeek(this.window) + } } activate() { - this._previewMenu.endPeekHere(); - this._previewMenu.close(); - Main.activateWindow(this.window); + this._previewMenu.endPeekHere() + this._previewMenu.close() + Main.activateWindow(this.window) } _onDestroy() { - if (this._waitWindowId) { - GLib.source_remove(this._waitWindowId); - this._waitWindowId = 0; - } + if (this._waitWindowId) { + GLib.source_remove(this._waitWindowId) + this._waitWindowId = 0 + } - this._removeWindowSignals(); + this._removeWindowSignals() } _onHoverChanged() { - this.setFocus(this.hover); + this.setFocus(this.hover) } _onCloseBtnClick() { - this._hideOrShowCloseButton(true); - this.reactive = false; + this._hideOrShowCloseButton(true) + this.reactive = false - if (!SETTINGS.get_boolean('group-apps')) { - this._previewMenu.close(); - } else { - this._previewMenu.endPeekHere(); - } + if (!SETTINGS.get_boolean('group-apps')) { + this._previewMenu.close() + } else { + this._previewMenu.endPeekHere() + } - this.window.delete(global.get_current_time()); + this.window.delete(global.get_current_time()) } _onButtonReleaseEvent(e) { - switch (e.get_button()) { - case 1: // Left click - this.activate(); - break; - case 2: // Middle click - if (SETTINGS.get_boolean('preview-middle-click-close')) { - this._onCloseBtnClick(); - } - break; - case 3: // Right click - this._showContextMenu(e); - break; - } + switch (e.get_button()) { + case 1: // Left click + this.activate() + break + case 2: // Middle click + if (SETTINGS.get_boolean('preview-middle-click-close')) { + this._onCloseBtnClick() + } + break + case 3: // Right click + this._showContextMenu(e) + break + } - return Clutter.EVENT_STOP; + return Clutter.EVENT_STOP } _cancelAnimateOut() { - if (this.animatingOut) { - this.animatingOut = false; + if (this.animatingOut) { + this.animatingOut = false - Utils.stopAnimations(this); - Utils.animate(this, getTweenOpts({ opacity: 255, width: this.cloneWidth, height: this.cloneHeight })); - } + Utils.stopAnimations(this) + Utils.animate( + this, + getTweenOpts({ + opacity: 255, + width: this.cloneWidth, + height: this.cloneHeight, + }), + ) + } } _showContextMenu(e) { - let coords = e.get_coords(); - let currentWorkspace = this._previewMenu.peekInitialWorkspaceIndex < 0 ? - Utils.getCurrentWorkspace() : - Utils.getWorkspaceByIndex(this._previewMenu.peekInitialWorkspaceIndex); + let coords = e.get_coords() + let currentWorkspace = + this._previewMenu.peekInitialWorkspaceIndex < 0 + ? Utils.getCurrentWorkspace() + : Utils.getWorkspaceByIndex( + this._previewMenu.peekInitialWorkspaceIndex, + ) - Main.wm._showWindowMenu(null, this.window, Meta.WindowMenuType.WM, { - x: coords[0], - y: coords[1], - width: 0, - height: 0 - }); + Main.wm._showWindowMenu(null, this.window, Meta.WindowMenuType.WM, { + x: coords[0], + y: coords[1], + width: 0, + height: 0, + }) - let menu = Main.wm._windowMenuManager._manager._menus[0]; + let menu = Main.wm._windowMenuManager._manager._menus[0] - menu.connect('open-state-changed', () => this._previewMenu.menu.sync_hover()); - this._previewMenu.menu.sync_hover(); + menu.connect('open-state-changed', () => + this._previewMenu.menu.sync_hover(), + ) + this._previewMenu.menu.sync_hover() - if (this.window.get_workspace() != currentWorkspace) { - let menuItem = new PopupMenu.PopupMenuItem(_('Move to current Workspace') + ' [' + (currentWorkspace.index() + 1) + ']'); - let menuItems = menu.box.get_children(); - let insertIndex = Utils.findIndex(menuItems, c => c._delegate instanceof PopupMenu.PopupSeparatorMenuItem); + if (this.window.get_workspace() != currentWorkspace) { + let menuItem = new PopupMenu.PopupMenuItem( + _('Move to current Workspace') + + ' [' + + (currentWorkspace.index() + 1) + + ']', + ) + let menuItems = menu.box.get_children() + let insertIndex = Utils.findIndex( + menuItems, + (c) => c._delegate instanceof PopupMenu.PopupSeparatorMenuItem, + ) - insertIndex = insertIndex >= 0 ? insertIndex : menuItems.length - 1; - menu.addMenuItem(menuItem, insertIndex); - menuItem.connect('activate', () => this.window.change_workspace(currentWorkspace)); - } + insertIndex = insertIndex >= 0 ? insertIndex : menuItems.length - 1 + menu.addMenuItem(menuItem, insertIndex) + menuItem.connect('activate', () => + this.window.change_workspace(currentWorkspace), + ) + } } _removeWindowSignals() { - if (this._titleWindowChangeId) { - this.window.disconnect(this._titleWindowChangeId); - this._titleWindowChangeId = 0; - } + if (this._titleWindowChangeId) { + this.window.disconnect(this._titleWindowChangeId) + this._titleWindowChangeId = 0 + } } _updateHeader() { - if (headerHeight) { - let iconTextureSize = SETTINGS.get_boolean('window-preview-use-custom-icon-size') ? - SETTINGS.get_int('window-preview-custom-icon-size') : - headerHeight / scaleFactor * .6; - let icon = this._previewMenu.getCurrentAppIcon().app.create_icon_texture(iconTextureSize); - let workspaceIndex = ''; - let workspaceStyle = null; - let fontScale = DESKTOPSETTINGS.get_double('text-scaling-factor'); - let commonTitleStyles = 'color: ' + SETTINGS.get_string('window-preview-title-font-color') + ';' + - 'font-size: ' + SETTINGS.get_int('window-preview-title-font-size') * fontScale + 'px;' + - 'font-weight: ' + SETTINGS.get_string('window-preview-title-font-weight') + ';'; - - this._iconBin.destroy_all_children(); - this._iconBin.add_child(icon); + if (headerHeight) { + let iconTextureSize = SETTINGS.get_boolean( + 'window-preview-use-custom-icon-size', + ) + ? SETTINGS.get_int('window-preview-custom-icon-size') + : (headerHeight / scaleFactor) * 0.6 + let icon = this._previewMenu + .getCurrentAppIcon() + .app.create_icon_texture(iconTextureSize) + let workspaceIndex = '' + let workspaceStyle = null + let fontScale = DESKTOPSETTINGS.get_double('text-scaling-factor') + let commonTitleStyles = + 'color: ' + + SETTINGS.get_string('window-preview-title-font-color') + + ';' + + 'font-size: ' + + SETTINGS.get_int('window-preview-title-font-size') * fontScale + + 'px;' + + 'font-weight: ' + + SETTINGS.get_string('window-preview-title-font-weight') + + ';' - if (!SETTINGS.get_boolean('isolate-workspaces')) { - workspaceIndex = (this.window.get_workspace().index() + 1).toString(); - workspaceStyle = 'margin: 0 4px 0 ' + (isLeftButtons ? Math.round((headerHeight - icon.width) * .5) + 'px' : '0') + '; padding: 0 4px;' + - 'border: 2px solid ' + this._getRgbaColor(FOCUSED_COLOR_OFFSET, .8) + 'border-radius: 2px;' + commonTitleStyles; - } - - this._workspaceIndicator.text = workspaceIndex; - setStyle(this._workspaceIndicator, workspaceStyle); + this._iconBin.destroy_all_children() + this._iconBin.add_child(icon) - this._titleWindowChangeId = this.window.connect('notify::title', () => this._updateWindowTitle()); - setStyle(this._windowTitle, 'max-width: 0px; padding-right: 4px;' + commonTitleStyles); - this._updateWindowTitle(); + if (!SETTINGS.get_boolean('isolate-workspaces')) { + workspaceIndex = (this.window.get_workspace().index() + 1).toString() + workspaceStyle = + 'margin: 0 4px 0 ' + + (isLeftButtons + ? Math.round((headerHeight - icon.width) * 0.5) + 'px' + : '0') + + '; padding: 0 4px;' + + 'border: 2px solid ' + + this._getRgbaColor(FOCUSED_COLOR_OFFSET, 0.8) + + 'border-radius: 2px;' + + commonTitleStyles } + + this._workspaceIndicator.text = workspaceIndex + setStyle(this._workspaceIndicator, workspaceStyle) + + this._titleWindowChangeId = this.window.connect('notify::title', () => + this._updateWindowTitle(), + ) + setStyle( + this._windowTitle, + 'max-width: 0px; padding-right: 4px;' + commonTitleStyles, + ) + this._updateWindowTitle() + } } _updateWindowTitle() { - this._windowTitle.text = this.window.title; + this._windowTitle.text = this.window.title } _hideOrShowCloseButton(hide) { - if (this._needsCloseButton) { - Utils.animate(this._closeButtonBin, getTweenOpts({ opacity: hide ? 0 : 255 })); - } + if (this._needsCloseButton) { + Utils.animate( + this._closeButtonBin, + getTweenOpts({ opacity: hide ? 0 : 255 }), + ) + } } _getBackgroundColor(offset, alpha) { - return 'background-color: ' + this._getRgbaColor(offset, alpha) + - 'transition-duration:' + this._previewMenu.panel.dynamicTransparency.animationDuration; + return ( + 'background-color: ' + + this._getRgbaColor(offset, alpha) + + 'transition-duration:' + + this._previewMenu.panel.dynamicTransparency.animationDuration + ) } _getRgbaColor(offset, alpha) { - alpha = Math.abs(alpha); + alpha = Math.abs(alpha) - if (isNaN(alpha)) { - alpha = alphaBg; - } + if (isNaN(alpha)) { + alpha = alphaBg + } - return Utils.getrgbaColor(this._previewMenu.panel.dynamicTransparency.backgroundColorRgb, alpha, offset); + return Utils.getrgbaColor( + this._previewMenu.panel.dynamicTransparency.backgroundColorRgb, + alpha, + offset, + ) } _addClone(newCloneBin, animateSize) { - let currentClones = this._previewBin.get_children(); - let newCloneOpts = getTweenOpts({ opacity: 255 }); - - this._previewBin.add_child(newCloneBin); + let currentClones = this._previewBin.get_children() + let newCloneOpts = getTweenOpts({ opacity: 255 }) - if (currentClones.length) { - let currentCloneBin = currentClones.pop(); - let currentCloneOpts = getTweenOpts({ opacity: 0, onComplete: () => currentCloneBin.destroy() }); + this._previewBin.add_child(newCloneBin) - if (newCloneBin.width > currentCloneBin.width) { - newCloneOpts.width = newCloneBin.width; - newCloneBin.width = currentCloneBin.width; - } else { - currentCloneOpts.width = newCloneBin.width; - } + if (currentClones.length) { + let currentCloneBin = currentClones.pop() + let currentCloneOpts = getTweenOpts({ + opacity: 0, + onComplete: () => currentCloneBin.destroy(), + }) - if (newCloneBin.height > currentCloneBin.height) { - newCloneOpts.height = newCloneBin.height; - newCloneBin.height = currentCloneBin.height; - } else { - currentCloneOpts.height = newCloneBin.height; - } - - currentClones.forEach(c => c.destroy()); - Utils.animate(currentCloneBin, currentCloneOpts); - } else if (animateSize) { - newCloneBin.width = 0; - newCloneBin.height = 0; - newCloneOpts.width = this.cloneWidth; - newCloneOpts.height = this.cloneHeight; + if (newCloneBin.width > currentCloneBin.width) { + newCloneOpts.width = newCloneBin.width + newCloneBin.width = currentCloneBin.width + } else { + currentCloneOpts.width = newCloneBin.width } - Utils.animate(newCloneBin, newCloneOpts); - } - - _getWindowCloneBin(window) { - let frameRect = window.get_frame_rect(); - let bufferRect = window.get_buffer_rect(); - let clone = new Clutter.Clone({ source: window.get_compositor_private() }); - let cloneBin = new St.Widget({ - opacity: 0, - layout_manager: frameRect.width != bufferRect.width || - frameRect.height != bufferRect.height ? - new WindowCloneLayout(frameRect, bufferRect) : - new Clutter.BinLayout() - }); - - cloneBin.add_child(clone); + if (newCloneBin.height > currentCloneBin.height) { + newCloneOpts.height = newCloneBin.height + newCloneBin.height = currentCloneBin.height + } else { + currentCloneOpts.height = newCloneBin.height + } - return cloneBin; + currentClones.forEach((c) => c.destroy()) + Utils.animate(currentCloneBin, currentCloneOpts) + } else if (animateSize) { + newCloneBin.width = 0 + newCloneBin.height = 0 + newCloneOpts.width = this.cloneWidth + newCloneOpts.height = this.cloneHeight + } + + Utils.animate(newCloneBin, newCloneOpts) + } + + _getWindowCloneBin(window) { + let frameRect = window.get_frame_rect() + let bufferRect = window.get_buffer_rect() + let clone = new Clutter.Clone({ source: window.get_compositor_private() }) + let cloneBin = new St.Widget({ + opacity: 0, + layout_manager: + frameRect.width != bufferRect.width || + frameRect.height != bufferRect.height + ? new WindowCloneLayout(frameRect, bufferRect) + : new Clutter.BinLayout(), + }) + + cloneBin.add_child(clone) + + return cloneBin } _getBinSize() { - let [fixedWidth, fixedHeight] = this._previewDimensions; + let [fixedWidth, fixedHeight] = this._previewDimensions - return [ - aspectRatio.x.fixed ? fixedWidth + this._padding * 2 : -1, - aspectRatio.y.fixed ? fixedHeight + this._padding * 2 : -1 - ]; + return [ + aspectRatio.x.fixed ? fixedWidth + this._padding * 2 : -1, + aspectRatio.y.fixed ? fixedHeight + this._padding * 2 : -1, + ] } _resizeClone(cloneBin, window) { - let frameRect = cloneBin.layout_manager.frameRect || window.get_frame_rect(); - let [fixedWidth, fixedHeight] = this._previewDimensions; - let ratio = Math.min(fixedWidth / frameRect.width, fixedHeight / frameRect.height, 1); - let cloneWidth = frameRect.width * ratio; - let cloneHeight = frameRect.height * ratio; - - let clonePaddingTB = cloneHeight < MIN_DIMENSION ? MIN_DIMENSION - cloneHeight : 0; - let clonePaddingLR = cloneWidth < MIN_DIMENSION ? MIN_DIMENSION - cloneWidth : 0; - let clonePaddingTop = clonePaddingTB * .5; - let clonePaddingLeft = clonePaddingLR * .5; - - this.cloneWidth = cloneWidth + clonePaddingLR * scaleFactor; - this.cloneHeight = cloneHeight + clonePaddingTB * scaleFactor; + let frameRect = + cloneBin.layout_manager.frameRect || window.get_frame_rect() + let [fixedWidth, fixedHeight] = this._previewDimensions + let ratio = Math.min( + fixedWidth / frameRect.width, + fixedHeight / frameRect.height, + 1, + ) + let cloneWidth = frameRect.width * ratio + let cloneHeight = frameRect.height * ratio - cloneBin.set_style('padding: ' + clonePaddingTop + 'px ' + clonePaddingLeft + 'px;'); - cloneBin.layout_manager.ratio = ratio; - cloneBin.layout_manager.padding = [clonePaddingLeft * scaleFactor, clonePaddingTop * scaleFactor]; + let clonePaddingTB = + cloneHeight < MIN_DIMENSION ? MIN_DIMENSION - cloneHeight : 0 + let clonePaddingLR = + cloneWidth < MIN_DIMENSION ? MIN_DIMENSION - cloneWidth : 0 + let clonePaddingTop = clonePaddingTB * 0.5 + let clonePaddingLeft = clonePaddingLR * 0.5 - cloneBin.get_first_child().set_size(cloneWidth, cloneHeight); + this.cloneWidth = cloneWidth + clonePaddingLR * scaleFactor + this.cloneHeight = cloneHeight + clonePaddingTB * scaleFactor + + cloneBin.set_style( + 'padding: ' + clonePaddingTop + 'px ' + clonePaddingLeft + 'px;', + ) + cloneBin.layout_manager.ratio = ratio + cloneBin.layout_manager.padding = [ + clonePaddingLeft * scaleFactor, + clonePaddingTop * scaleFactor, + ] + + cloneBin.get_first_child().set_size(cloneWidth, cloneHeight) } _getPreviewDimensions() { - let size = SETTINGS.get_int('window-preview-size') * scaleFactor; - let w, h; + let size = SETTINGS.get_int('window-preview-size') * scaleFactor + let w, h - if (this._previewMenu.isVertical) { - w = size; - h = w * aspectRatio.y.size / aspectRatio.x.size; - } else { - h = size; - w = h * aspectRatio.x.size / aspectRatio.y.size; - } + if (this._previewMenu.isVertical) { + w = size + h = (w * aspectRatio.y.size) / aspectRatio.x.size + } else { + h = size + w = (h * aspectRatio.x.size) / aspectRatio.y.size + } - return [w, h]; + return [w, h] } -}); - -export const WindowCloneLayout = GObject.registerClass({ -}, class WindowCloneLayout extends Clutter.BinLayout { + }, +) +export const WindowCloneLayout = GObject.registerClass( + {}, + class WindowCloneLayout extends Clutter.BinLayout { _init(frameRect, bufferRect) { - super._init(); + super._init() - //the buffer_rect contains the transparent padding that must be removed - this.frameRect = frameRect; - this.bufferRect = bufferRect; + //the buffer_rect contains the transparent padding that must be removed + this.frameRect = frameRect + this.bufferRect = bufferRect } vfunc_allocate(actor, box) { - let [width, height] = box.get_size(); + let [width, height] = box.get_size() - box.set_origin( - (this.bufferRect.x - this.frameRect.x) * this.ratio + this.padding[0], - (this.bufferRect.y - this.frameRect.y) * this.ratio + this.padding[1] - ); + box.set_origin( + (this.bufferRect.x - this.frameRect.x) * this.ratio + this.padding[0], + (this.bufferRect.y - this.frameRect.y) * this.ratio + this.padding[1], + ) - box.set_size( - width + (this.bufferRect.width - this.frameRect.width) * this.ratio, - height + (this.bufferRect.height - this.frameRect.height) * this.ratio - ); + box.set_size( + width + (this.bufferRect.width - this.frameRect.width) * this.ratio, + height + (this.bufferRect.height - this.frameRect.height) * this.ratio, + ) - actor.get_first_child().allocate(box); + actor.get_first_child().allocate(box) } -}); + }, +) export function setStyle(actor, style) { - if (!isManualStyling) { - actor.set_style(style); - } + if (!isManualStyling) { + actor.set_style(style) + } } export function getTweenOpts(opts) { - let defaults = { - time: animationTime, - transition: 'easeInOutQuad' - }; + let defaults = { + time: animationTime, + transition: 'easeInOutQuad', + } - return Utils.mergeObjects(opts || {}, defaults); + return Utils.mergeObjects(opts || {}, defaults) } diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..3f5af8f --- /dev/null +++ b/yarn.lock @@ -0,0 +1,583 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.19.0": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.1.tgz#734aaea2c40be22bbb1f2a9dac687c57a6a4c984" + integrity sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA== + dependencies: + "@eslint/object-schema" "^2.1.5" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/core@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.10.0.tgz#23727063c21b335f752dbb3a16450f6f9cbc9091" + integrity sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" + integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.19.0", "@eslint/js@^9.19.0": + version "9.19.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.19.0.tgz#51dbb140ed6b49d05adc0b171c41e1a8713b7789" + integrity sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ== + +"@eslint/object-schema@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.5.tgz#8670a8f6258a2be5b2c620ff314a1d984c23eb2e" + integrity sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ== + +"@eslint/plugin-kit@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz#ee07372035539e7847ef834e3f5e7b79f09e3a81" + integrity sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A== + dependencies: + "@eslint/core" "^0.10.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== + +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.3.1, debug@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-prettier@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz#fbb03bfc8db0651df9ce4e8b7150d11c5fe3addf" + integrity sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw== + +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.19.0: + version "9.19.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.19.0.tgz#ffa1d265fc4205e0f8464330d35f09e1d548b1bf" + integrity sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.0" + "@eslint/core" "^0.10.0" + "@eslint/eslintrc" "^3.2.0" + "@eslint/js" "9.19.0" + "@eslint/plugin-kit" "^0.2.5" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.1" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^15.14.0: + version "15.14.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.14.0.tgz#b8fd3a8941ff3b4d38f3319d433b61bbb482e73f" + integrity sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" + integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==