diff options
Diffstat (limited to 'raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js')
| -rw-r--r-- | raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js | 2698 |
1 files changed, 0 insertions, 2698 deletions
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js deleted file mode 100644 index 393b1de..0000000 --- a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js +++ /dev/null @@ -1,2698 +0,0 @@ -// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- - -import { - Clutter, - GLib, - Gio, - GObject, - Meta, - Shell, - St, -} from './dependencies/gi.js'; - -import { - AppMenu, - AppDisplay, - Layout, - Main, - OverviewControls, - PointerWatcher, - Workspace, - WorkspacesView, - WorkspaceSwitcherPopup, -} from './dependencies/shell/ui.js'; - -import { - AnimationUtils, -} from './dependencies/shell/misc.js'; - -import { - AppIconsDecorator, - AppSpread, - DockDash, - DesktopIconsIntegration, - FileManager1API, - Intellihide, - LauncherAPI, - Locations, - NotificationsMonitor, - Theming, - Utils, -} from './imports.js'; - -import {Extension} from './dependencies/shell/extensions/extension.js'; - -// Use __ () and N__() for the extension gettext domain, and reuse -// the shell domain with the default _() and N_() -const {gettext: __} = Extension; - -const {signals: Signals} = imports; - -const DOCK_DWELL_CHECK_INTERVAL = 100; -const ICON_ANIMATOR_DURATION = 3000; -const STARTUP_ANIMATION_TIME = 500; - -export const State = Object.freeze({ - HIDDEN: 0, - SHOWING: 1, - SHOWN: 2, - HIDING: 3, -}); - -const scrollAction = Object.freeze({ - DO_NOTHING: 0, - CYCLE_WINDOWS: 1, - SWITCH_WORKSPACE: 2, -}); - -const Labels = Object.freeze({ - INITIALIZE: Symbol('initialize'), - ISOLATION: Symbol('isolation'), - LOCATIONS: Symbol('locations'), - MAIN_DASH: Symbol('main-dash'), - OLD_DASH_CHANGES: Symbol('old-dash-changes'), - SETTINGS: Symbol('settings'), - STARTUP_ANIMATION: Symbol('startup-animation'), - WORKSPACE_SWITCH_SCROLL: Symbol('workspace-switch-scroll'), -}); - -/** - * A simple St.Widget with one child whose allocation takes into account the - * slide out of its child via the slide-x property ([0:1]). - * - * Required since I want to track the input region of this container which is - * based on its allocation even if the child overflows the parent actor. By doing - * this the region of the dash that is slide-out is not stealing anymore the input - * regions making the extension usable when the primary monitor is the right one. - * - * The slide-x parameter can be used to directly animate the sliding. The parent - * must have a WEST (SOUTH) anchor_point to achieve the sliding to the RIGHT (BOTTOM) - * side. - */ -const DashSlideContainer = GObject.registerClass({ - Properties: { - 'monitor-index': GObject.ParamSpec.uint( - 'monitor-index', 'monitor-index', 'monitor-index', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, - 0, GLib.MAXUINT32, 0), - 'side': GObject.ParamSpec.enum( - 'side', 'side', 'side', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, - St.Side, St.Side.LEFT), - 'slide-x': GObject.ParamSpec.double( - 'slide-x', 'slide-x', 'slide-x', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, - 0, 1, 1), - }, -}, class DashSlideContainer extends St.Bin { - _init(params = {}) { - super._init(params); - - this._slideoutSize = 0; // minimum size when slided out - this.connect('notify::slide-x', () => this.queue_relayout()); - - if (this.side === St.Side.TOP && DockManager.settings.dockFixed) { - this._signalsHandler = new Utils.GlobalSignalsHandler(this); - this._signalsHandler.add(Main.panel, 'notify::height', - () => this.queue_relayout()); - } - } - - vfunc_allocate(box) { - const contentBox = this.get_theme_node().get_content_box(box); - - this.set_allocation(box); - - if (!this.child) - return; - - const availWidth = contentBox.x2 - contentBox.x1; - let availHeight = contentBox.y2 - contentBox.y1; - const [, , natChildWidth, natChildHeight] = - this.child.get_preferred_size(); - - const childWidth = natChildWidth; - const childHeight = natChildHeight; - - const childBox = new Clutter.ActorBox(); - - const slideoutSize = this._slideoutSize; - - if (this.side === St.Side.LEFT) { - childBox.x1 = (this.slideX - 1) * (childWidth - slideoutSize); - childBox.x2 = slideoutSize + this.slideX * (childWidth - slideoutSize); - childBox.y1 = 0; - childBox.y2 = childBox.y1 + childHeight; - } else if ((this.side === St.Side.RIGHT) || (this.side === St.Side.BOTTOM)) { - childBox.x1 = 0; - childBox.x2 = childWidth; - childBox.y1 = 0; - childBox.y2 = childBox.y1 + childHeight; - } else if (this.side === St.Side.TOP) { - const monitor = Main.layoutManager.monitors[this.monitorIndex]; - let yOffset = 0; - if (Main.panel.x === monitor.x && Main.panel.y === monitor.y && - DockManager.settings.dockFixed) - yOffset = Main.panel.height; - childBox.x1 = 0; - childBox.x2 = childWidth; - childBox.y1 = (this.slideX - 1) * (childHeight - slideoutSize) + yOffset; - childBox.y2 = slideoutSize + this.slideX * (childHeight - slideoutSize) + yOffset; - availHeight += yOffset; - } - - this.child.allocate(childBox); - - this.child.set_clip(-childBox.x1, -childBox.y1, - -childBox.x1 + availWidth, -childBox.y1 + availHeight); - } - - /** - * Just the child width but taking into account the slided out part - * - * @param forHeight - */ - vfunc_get_preferred_width(forHeight) { - let [minWidth, natWidth] = super.vfunc_get_preferred_width(forHeight || 0); - if ((this.side === St.Side.LEFT) || (this.side === St.Side.RIGHT)) { - minWidth = (minWidth - this._slideoutSize) * this.slideX + this._slideoutSize; - natWidth = (natWidth - this._slideoutSize) * this.slideX + this._slideoutSize; - } - return [minWidth, natWidth]; - } - - /** - * Just the child height but taking into account the slided out part - * - * @param forWidth - */ - vfunc_get_preferred_height(forWidth) { - let [minHeight, natHeight] = super.vfunc_get_preferred_height(forWidth || 0); - if ((this.side === St.Side.TOP) || (this.side === St.Side.BOTTOM)) { - minHeight = (minHeight - this._slideoutSize) * this.slideX + this._slideoutSize; - natHeight = (natHeight - this._slideoutSize) * this.slideX + this._slideoutSize; - - if (this.side === St.Side.TOP && DockManager.settings.dockFixed) { - const monitor = Main.layoutManager.monitors[this.monitorIndex]; - if (Main.panel.x === monitor.x && Main.panel.y === monitor.y) { - minHeight += Main.panel.height; - natHeight += Main.panel.height; - } - } - } - return [minHeight, natHeight]; - } -}); - -const DockedDash = GObject.registerClass({ - Properties: { - 'is-main': GObject.ParamSpec.boolean( - 'is-main', 'is-main', 'is-main', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, - false), - 'monitor-index': GObject.ParamSpec.uint( - 'monitor-index', 'monitor-index', 'monitor-index', - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, - 0, GLib.MAXUINT32, 0), - }, - Signals: { - 'showing': {}, - 'hiding': {}, - }, -}, class DashToDock extends St.Bin { - _init(params) { - this._position = Utils.getPosition(); - - // This is the centering actor - super._init({ - ...params, - name: 'dashtodockContainer', - reactive: false, - style_class: Theming.PositionStyleClass[this._position], - }); - - if (this.monitorIndex === undefined) { - // Hello turkish locale, gjs has instead defined this.monitorIndex - // See: https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/742 - this.monitorIndex = this.monitor_index; - } - - this._rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; - - // Load settings - const {settings} = DockManager; - this._isHorizontal = (this._position === St.Side.TOP) || (this._position === St.Side.BOTTOM); - - // Temporary ignore hover events linked to autohide for whatever reason - this._ignoreHover = false; - this._oldIgnoreHover = null; - // This variables are linked to the settings regardles of autohide or intellihide - // being temporary disable. Get set by _updateVisibilityMode; - this._autohideIsEnabled = null; - this._intellihideIsEnabled = null; - - // This variable marks if _disableUnredirect() is called - // to help restore the original state when intelihide is disabled. - this._unredirectDisabled = false; - - // Create intellihide object to monitor windows overlapping - this._intellihide = new Intellihide.Intellihide(this.monitorIndex); - - // initialize dock state - this._dockState = State.HIDDEN; - - // Put dock on the required monitor - this._monitor = Main.layoutManager.monitors[this.monitorIndex]; - - // this store size and the position where the dash is shown; - // used by intellihide module to check window overlap. - this.staticBox = new Clutter.ActorBox(); - - // Initialize pressure barrier variables - this._canUsePressure = false; - this._pressureBarrier = null; - this._barrier = null; - this._removeBarrierTimeoutId = 0; - - // Initialize dwelling system variables - this._dockDwelling = false; - this._dockWatch = null; - this._dockDwellUserTime = 0; - this._dockDwellTimeoutId = 0; - - // Create a new dash object - this.dash = new DockDash.DockDash(this.monitorIndex); - - if (Main.overview.isDummy || !settings.showShowAppsButton) - this.dash.hideShowAppsButton(); - - // Create the containers for sliding in and out and - // centering, turn on track hover - // This is the sliding actor whose allocation is to be tracked for input regions - this._slider = new DashSlideContainer({ - monitor_index: this._monitor.index, - side: this._position, - slide_x: Main.layoutManager._startingUp ? 0 : 1, - ...this._isHorizontal ? { - x_align: Clutter.ActorAlign.CENTER, - } : { - y_align: Clutter.ActorAlign.CENTER, - }, - }); - - // This is the actor whose hover status us tracked for autohide - this._box = new St.BoxLayout({ - name: 'dashtodockBox', - reactive: true, - track_hover: true, - }); - this._box.connect('notify::hover', this._hoverChanged.bind(this)); - - // Connect global signals - this._signalsHandler = new Utils.GlobalSignalsHandler(this); - this._bindSettingsChanges(); - this._signalsHandler.add([ - // update when workarea changes, for instance if other extensions modify the struts - // (like moving th panel at the bottom) - global.display, - 'workareas-changed', - this._resetPosition.bind(this), - ], [ - global.display, - 'in-fullscreen-changed', - this._updateBarrier.bind(this), - ], [ - // Monitor windows overlapping - this._intellihide, - 'status-changed', - this._updateDashVisibility.bind(this), - ], [ - this.dash, - 'menu-opened', - () => { - this._onMenuOpened(); - }, - ], [ - // sync hover after a popupmenu is closed - this.dash, - 'menu-closed', - () => { - this._onMenuClosed(); - }, - ], [ - this.dash, - 'notify::requires-visibility', - () => this._updateDashVisibility(), - ]); - - if (!Main.overview.isDummy) { - this._signalsHandler.add([ - Main.overview, - 'item-drag-begin', - this._onDragStart.bind(this), - ], [ - Main.overview, - 'item-drag-end', - this._onDragEnd.bind(this), - ], [ - Main.overview, - 'item-drag-cancelled', - this._onDragEnd.bind(this), - ], [ - Main.overview, - 'showing', - this._onOverviewShowing.bind(this), - ], [ - Main.overview, - 'hiding', - this._onOverviewHiding.bind(this), - ], - [ - Main.overview, - 'hidden', - this._onOverviewHidden.bind(this), - ]); - } - - this._themeManager = new Theming.ThemeManager(this); - this._signalsHandler.add(this._themeManager, 'updated', - () => this.dash.resetAppIcons()); - - this._signalsHandler.add(DockManager.iconTheme, 'changed', - () => this.dash.resetAppIcons()); - - // Since the actor is not a topLevel child and its parent is now not added to the Chrome, - // the allocation change of the parent container (slide in and slideout) doesn't trigger - // anymore an update of the input regions. Force the update manually. - this.connect('notify::allocation', - Main.layoutManager._queueUpdateRegions.bind(Main.layoutManager)); - - - // Since Clutter has no longer ClutterAllocationFlags, - // "allocation-changed" signal has been removed. MR !1245 - this.dash._container.connect('notify::allocation', this._updateStaticBox.bind(this)); - this._slider.connect(this._isHorizontal ? 'notify::x' : 'notify::y', - this._updateStaticBox.bind(this)); - - // Load optional features that need to be activated for one dock only - if (this.isMain) - this._enableExtraFeatures(); - // Load optional features that need to be activated once per dock - this._optionalScrollWorkspaceSwitch(); - - // Delay operations that require the shell to be fully loaded and with - // user theme applied. - - this._signalsHandler.addWithLabel(Labels.INITIALIZE, global.stage, - 'after-paint', () => this._initialize()); - - // Add dash container actor and the container to the Chrome. - this.set_child(this._slider); - this._slider.set_child(this._box); - this._box.add_child(this.dash); - - // Add aligning container without tracking it for input region - this._trackDock(); - - // Create and apply height/width constraint to the dash. - if (this._isHorizontal) { - this.connect('notify::width', () => { - this.dash.setMaxSize(this.width, this.height); - }); - } else { - this.connect('notify::height', () => { - this.dash.setMaxSize(this.width, this.height); - }); - } - - if (this._position === St.Side.RIGHT) { - this.connect('notify::width', () => - (this.translation_x = -this.width)); - } else if (this._position === St.Side.BOTTOM) { - this.connect('notify::height', () => - (this.translation_y = -this.height)); - } - - // Set initial position - this._resetPosition(); - - this.connect('destroy', this._onDestroy.bind(this)); - } - - get position() { - return this._position; - } - - get isHorizontal() { - return this._isHorizontal; - } - - _untrackDock() { - Main.layoutManager.untrackChrome(this); - } - - _trackDock() { - if (DockManager.settings.dockFixed) { - if (this.get_parent()) - Main.layoutManager.removeChrome(this); - Main.layoutManager.addChrome(this, { - trackFullscreen: true, - affectsStruts: true, - }); - } else { - if (this.get_parent()) - Main.layoutManager.removeChrome(this); - Main.layoutManager.addChrome(this); - } - } - - _initialize() { - this._signalsHandler.removeWithLabel(Labels.INITIALIZE); - - // Apply custom css class according to the settings - this._themeManager.updateCustomTheme(); - - this._updateVisibilityMode(); - - // In case we are already inside the overview when the extension is loaded, - // for instance on unlocking the screen if it was locked with the overview open. - if (Main.overview.visibleTarget) - this._onOverviewShowing(); - - this._updateAutoHideBarriers(); - } - - _onDestroy() { - // The dash, intellihide and themeManager have global signals as well internally - this.dash.destroy(); - this._intellihide.destroy(); - this._themeManager.destroy(); - - if (this._marginLater) { - Utils.laterRemove(this._marginLater); - delete this._marginLater; - } - - if (this._triggerTimeoutId) - GLib.source_remove(this._triggerTimeoutId); - - this._restoreUnredirect(); - - // Remove barrier timeout - if (this._removeBarrierTimeoutId > 0) - GLib.source_remove(this._removeBarrierTimeoutId); - - // Remove existing barrier - this._removeBarrier(); - - // Remove pointer watcher - if (this._dockWatch) { - PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); - this._dockWatch = null; - } - } - - _updateAutoHideBarriers() { - // Remove pointer watcher - if (this._dockWatch) { - PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); - this._dockWatch = null; - } - - // Setup pressure barrier (GS38+ only) - this._updatePressureBarrier(); - this._updateBarrier(); - - // setup dwelling system if pressure barriers are not available - this._setupDockDwellIfNeeded(); - } - - _bindSettingsChanges() { - const {settings} = DockManager; - this._signalsHandler.add([ - settings, - 'changed::scroll-action', - () => { - this._optionalScrollWorkspaceSwitch(); - }, - ], [ - settings, - 'changed::dash-max-icon-size', - () => { - this.dash.setIconSize(settings.dashMaxIconSize); - }, - ], [ - settings, - 'changed::icon-size-fixed', - () => { - this.dash.setIconSize(settings.dashMaxIconSize); - }, - ], [ - settings, - 'changed::show-favorites', - () => { - this.dash.resetAppIcons(); - }, - ], [ - settings, - 'changed::show-trash', - () => { - this.dash.resetAppIcons(); - }, - Utils.SignalsHandlerFlags.CONNECT_AFTER, - ], [ - settings, - 'changed::show-mounts', - () => { - this.dash.resetAppIcons(); - }, - Utils.SignalsHandlerFlags.CONNECT_AFTER, - ], [ - settings, - 'changed::isolate-locations', - () => this.dash.resetAppIcons(), - Utils.SignalsHandlerFlags.CONNECT_AFTER, - ], [ - settings, - 'changed::dance-urgent-applications', - () => this.dash.resetAppIcons(), - Utils.SignalsHandlerFlags.CONNECT_AFTER, - ], [ - settings, - 'changed::show-running', - () => { - this.dash.resetAppIcons(); - }, - ], [ - settings, - 'changed::show-apps-always-in-the-edge', - () => { - this.dash.updateShowAppsButton(); - }, - ], [ - settings, - 'changed::show-apps-at-top', - () => { - this.dash.updateShowAppsButton(); - }, - ], [ - settings, - 'changed::show-show-apps-button', - () => { - if (!Main.overview.isDummy && - settings.showShowAppsButton) - this.dash.showShowAppsButton(); - else - this.dash.hideShowAppsButton(); - }, - ], [ - settings, - 'changed::dock-fixed', - () => { - this._untrackDock(); - this._trackDock(); - - this._resetPosition(); - this._updateAutoHideBarriers(); - this._updateVisibilityMode(); - }, - ], [ - settings, - 'changed::manualhide', - () => { - this._updateVisibilityMode(); - }, - ], [ - settings, - 'changed::intellihide', - () => { - this._updateVisibilityMode(); - this._updateVisibleDesktop(); - }, - ], [ - settings, - 'changed::intellihide-mode', - () => { - this._intellihide.forceUpdate(); - }, - ], [ - settings, - 'changed::autohide', - () => { - this._updateVisibilityMode(); - this._updateAutoHideBarriers(); - }, - ], [ - settings, - 'changed::autohide-in-fullscreen', - this._updateBarrier.bind(this), - ], [ - settings, - 'changed::show-dock-urgent-notify', - () => { - this.dash.resetAppIcons(); - }, - ], - [ - settings, - 'changed::extend-height', - this._resetPosition.bind(this), - ], [ - settings, - 'changed::height-fraction', - this._resetPosition.bind(this), - ], [ - settings, - 'changed::always-center-icons', - () => this.dash.resetAppIcons(), - ], [ - settings, - 'changed::require-pressure-to-show', - () => this._updateAutoHideBarriers(), - ], [ - settings, - 'changed::pressure-threshold', - () => { - this._updatePressureBarrier(); - this._updateBarrier(); - }, - ]); - } - - _disableUnredirect() { - if (!this._unredirectDisabled) { - if (Meta.disable_unredirect_for_display !== undefined) - Meta.disable_unredirect_for_display(global.display); - else if (global.compositor.disable_unredirect !== undefined) - global.compositor.disable_unredirect(); - this._unredirectDisabled = true; - } - } - - _restoreUnredirect() { - if (this._unredirectDisabled) { - if (Meta.enable_unredirect_for_display !== undefined) - Meta.enable_unredirect_for_display(global.display); - else if (global.compositor.enable_unredirect !== undefined) - global.compositor.enable_unredirect(); - this._unredirectDisabled = false; - } - } - - /** - * This is call when visibility settings change - */ - _updateVisibilityMode() { - const {settings} = DockManager; - if (DockManager.settings.dockFixed || DockManager.settings.manualhide) { - this._autohideIsEnabled = false; - this._intellihideIsEnabled = false; - } else { - this._autohideIsEnabled = settings.autohide; - this._intellihideIsEnabled = settings.intellihide; - } - - if (this._autohideIsEnabled) - this.add_style_class_name('autohide'); - else - this.remove_style_class_name('autohide'); - - if (this._intellihideIsEnabled) { - this._intellihide.enable(); - } else { - this._intellihide.disable(); - this._restoreUnredirect(); - } - - this._updateDashVisibility(); - } - - /** - * Show/hide dash based on, in order of priority: - * overview visibility - * fixed mode - * intellihide - * autohide - * overview visibility - */ - _updateDashVisibility() { - if (DockManager.settings.manualhide) { - this._ignoreHover = true; - this._removeAnimations(); - this._animateOut(0, 0); - return; - } - - if (Main.overview.visibleTarget) - return; - - const {settings} = DockManager; - - if (DockManager.settings.dockFixed) { - this._removeAnimations(); - this._animateIn(settings.animationTime, 0); - } else if (this._intellihideIsEnabled) { - if (!this.dash.requiresVisibility && this._intellihide.getOverlapStatus()) { - this._ignoreHover = false; - // Do not hide if autohide is enabled and mouse is hover - if (!this._box.hover || !this._autohideIsEnabled) - this._animateOut(settings.animationTime, 0); - } else { - this._ignoreHover = true; - this._removeAnimations(); - this._animateIn(settings.animationTime, 0); - } - } else if (this._autohideIsEnabled) { - this._ignoreHover = false; - - if (this._box.hover || this.dash.requiresVisibility) - this._animateIn(settings.animationTime, 0); - else - this._animateOut(settings.animationTime, 0); - } else { - this._animateOut(settings.animationTime, 0); - } - } - - _onOverviewShowing() { - this.add_style_class_name('overview'); - - this._ignoreHover = true; - this._intellihide.disable(); - this._removeAnimations(); - this._animateIn(DockManager.settings.animationTime, 0); - } - - _onOverviewHiding() { - this._intellihide.enable(); - this._updateDashVisibility(); - } - - _onOverviewHidden() { - this.remove_style_class_name('overview'); - this._updateDashVisibility(); - } - - _onMenuOpened() { - this._ignoreHover = true; - } - - _onMenuClosed() { - this._ignoreHover = false; - this._box.sync_hover(); - this._updateDashVisibility(); - } - - _hoverChanged() { - if (!this._ignoreHover) { - // Skip if dock is not in autohide mode for instance because it is shown - // by intellihide. - if (this._autohideIsEnabled) { - if (this._box.hover || Main.overview.visible) - this._show(); - else - this._hide(); - } - } - } - - getDockState() { - return this._dockState; - } - - _show() { - this._delayedHide = false; - if ((this._dockState === State.HIDDEN) || (this._dockState === State.HIDING)) { - if (this._dockState === State.HIDING) - // suppress all potential queued transitions - i.e. added but not started, - // always give priority to show - this._removeAnimations(); - - this.emit('showing'); - this._animateIn(DockManager.settings.animationTime, 0); - } - } - - _hide() { - // If no hiding animation is running or queued - if ((this._dockState === State.SHOWN) || (this._dockState === State.SHOWING)) { - const {settings} = DockManager; - const delay = settings.hideDelay; - - if (this._dockState === State.SHOWING) { - // if a show already started, let it finish; queue hide without removing the show. - // to obtain this, we wait for the animateIn animation to be completed - this._delayedHide = true; - return; - } - - this.emit('hiding'); - this._animateOut(settings.animationTime, delay); - } - } - - _animateIn(time, delay) { - if (this._intellihideIsEnabled) - this._disableUnredirect(); - this._dockState = State.SHOWING; - this.dash.iconAnimator.start(); - this._delayedHide = false; - - this._slider.ease_property('slide-x', 1, { - duration: time * 1000, - delay: delay * 1000, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - this._dockState = State.SHOWN; - // Remove barrier so that mouse pointer is released and can - // monitors on other side of dock. - // NOTE: Delay needed to keep mouse from moving past dock and - // re-hiding dock immediately. This gives users an opportunity - // to hover over the dock - if (this._removeBarrierTimeoutId > 0) - GLib.source_remove(this._removeBarrierTimeoutId); - - if (!this._delayedHide) { - this._removeBarrierTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, 100, this._removeBarrier.bind(this)); - } else { - this._hide(); - } - }, - }); - } - - _animateOut(time, delay) { - this._dockState = State.HIDING; - - this._slider.ease_property('slide-x', 0, { - duration: time * 1000, - delay: delay * 1000, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - this._dockState = State.HIDDEN; - if (this._intellihideIsEnabled) - this._restoreUnredirect(); - // Remove queued barrier removal timeout if any - if (this._removeBarrierTimeoutId > 0) - GLib.source_remove(this._removeBarrierTimeoutId); - this._updateBarrier(); - this.dash.iconAnimator.pause(); - }, - }); - } - - /** - * Dwelling system based on the GNOME Shell 3.14 messageTray code. - */ - _setupDockDwellIfNeeded() { - // If we don't have extended barrier features, then we need - // to support the old tray dwelling mechanism. - if (this._autohideIsEnabled && - (!Utils.supportsExtendedBarriers() || - !DockManager.settings.requirePressureToShow)) { - const pointerWatcher = PointerWatcher.getPointerWatcher(); - this._dockWatch = pointerWatcher.addWatch( - DOCK_DWELL_CHECK_INTERVAL, this._checkDockDwell.bind(this)); - this._dockDwelling = false; - this._dockDwellUserTime = 0; - } - } - - _checkDockDwell(x, y) { - const workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index); - let shouldDwell; - // Check for the correct screen edge, extending the sensitive area to the whole workarea, - // minus 1 px to avoid conflicting with other active corners. - if (this._position === St.Side.LEFT) { - shouldDwell = (x === this._monitor.x) && (y > workArea.y) && - (y < workArea.y + workArea.height); - } else if (this._position === St.Side.RIGHT) { - shouldDwell = (x === this._monitor.x + this._monitor.width - 1) && - (y > workArea.y) && (y < workArea.y + workArea.height); - } else if (this._position === St.Side.TOP) { - shouldDwell = (y === this._monitor.y) && (x > workArea.x) && - (x < workArea.x + workArea.width); - } else if (this._position === St.Side.BOTTOM) { - shouldDwell = (y === this._monitor.y + this._monitor.height - 1) && - (x > workArea.x) && (x < workArea.x + workArea.width); - } - - if (shouldDwell) { - // We only set up dwell timeout when the user is not hovering over the dock - // already (!this._box.hover). - // The _dockDwelling variable is used so that we only try to - // fire off one dock dwell - if it fails (because, say, the user has the mouse down), - // we don't try again until the user moves the mouse up and down again. - if (!this._dockDwelling && !this._box.hover && (this._dockDwellTimeoutId === 0)) { - // Save the interaction timestamp so we can detect user input - const focusWindow = global.display.focus_window; - this._dockDwellUserTime = focusWindow ? focusWindow.user_time : 0; - - this._dockDwellTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - DockManager.settings.showDelay * 1000, - this._dockDwellTimeout.bind(this)); - GLib.Source.set_name_by_id(this._dockDwellTimeoutId, - '[dash-to-dock] this._dockDwellTimeout'); - } - this._dockDwelling = true; - } else { - this._cancelDockDwell(); - this._dockDwelling = false; - } - } - - _cancelDockDwell() { - if (this._dockDwellTimeoutId !== 0) { - GLib.source_remove(this._dockDwellTimeoutId); - this._dockDwellTimeoutId = 0; - } - } - - _dockDwellTimeout() { - this._dockDwellTimeoutId = 0; - - if (!DockManager.settings.autohideInFullscreen && - this._monitor.inFullscreen) - return GLib.SOURCE_REMOVE; - - // We don't want to open the tray when a modal dialog - // is up, so we check the modal count for that. When we are in the - // overview we have to take the overview's modal push into account - if (Main.modalCount > (Main.overview.visible ? 1 : 0)) - return GLib.SOURCE_REMOVE; - - // If the user interacted with the focus window since we started the tray - // dwell (by clicking or typing), don't activate the message tray - const focusWindow = global.display.focus_window; - const currentUserTime = focusWindow ? focusWindow.user_time : 0; - if (currentUserTime !== this._dockDwellUserTime) - return GLib.SOURCE_REMOVE; - - // Reuse the pressure version function, the logic is the same - this._onPressureSensed(); - return GLib.SOURCE_REMOVE; - } - - _updatePressureBarrier() { - const {settings} = DockManager; - this._canUsePressure = Utils.supportsExtendedBarriers(); - const {pressureThreshold} = settings; - - // Remove existing pressure barrier - if (this._pressureBarrier) { - this._pressureBarrier.destroy(); - this._pressureBarrier = null; - } - - if (this._barrier) { - this._barrier.destroy(); - this._barrier = null; - } - - // Create new pressure barrier based on pressure threshold setting - if (this._canUsePressure && this._autohideIsEnabled && - DockManager.settings.requirePressureToShow) { - this._pressureBarrier = new Layout.PressureBarrier( - pressureThreshold, settings.showDelay * 1000, - Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW); - this._pressureBarrier.connect('trigger', _barrier => { - if (!settings.autohideInFullscreen && this._monitor.inFullscreen) - return; - this._onPressureSensed(); - }); - } - } - - /** - * handler for mouse pressure sensed - */ - _onPressureSensed() { - if (Main.overview.visibleTarget) - return; - - if (this._triggerTimeoutId) - GLib.source_remove(this._triggerTimeoutId); - - // In case the mouse move away from the dock area before hovering it, - // in such case the leave event would never be triggered and the dock - // would stay visible forever. - this._triggerTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => { - const [x, y, mods_] = global.get_pointer(); - let shouldHide = true; - switch (this._position) { - case St.Side.LEFT: - if (x <= this.staticBox.x2 && - x >= this._monitor.x && - y >= this._monitor.y && - y <= this._monitor.y + this._monitor.height) - shouldHide = false; - - break; - case St.Side.RIGHT: - if (x >= this.staticBox.x1 && - x <= this._monitor.x + this._monitor.width && - y >= this._monitor.y && - y <= this._monitor.y + this._monitor.height) - shouldHide = false; - - break; - case St.Side.TOP: - if (x >= this._monitor.x && - x <= this._monitor.x + this._monitor.width && - y <= this.staticBox.y2 && - y >= this._monitor.y) - shouldHide = false; - - break; - case St.Side.BOTTOM: - if (x >= this._monitor.x && - x <= this._monitor.x + this._monitor.width && - y >= this.staticBox.y1 && - y <= this._monitor.y + this._monitor.height) - shouldHide = false; - } - if (shouldHide) { - this._triggerTimeoutId = 0; - this._hoverChanged(); - return GLib.SOURCE_REMOVE; - } else { - return GLib.SOURCE_CONTINUE; - } - }); - - this._show(); - } - - /** - * Remove pressure barrier - */ - _removeBarrier() { - if (this._barrier) { - if (this._pressureBarrier) - this._pressureBarrier.removeBarrier(this._barrier); - this._barrier.destroy(); - this._barrier = null; - } - this._removeBarrierTimeoutId = 0; - return false; - } - - /** - * Update pressure barrier size - */ - _updateBarrier() { - // Remove existing barrier - this._removeBarrier(); - - // The barrier needs to be removed in fullscreen with autohide disabled - // otherwise the mouse can get trapped on monitor. - if (this._monitor.inFullscreen && - !DockManager.settings.autohideInFullscreen) - return; - - // Manually reset pressure barrier - // This is necessary because we remove the pressure barrier when it is - // triggered to show the dock - if (this._pressureBarrier) { - this._pressureBarrier._reset(); - this._pressureBarrier._isTriggered = false; - } - - // Create new barrier - // The barrier extends to the whole workarea, minus 1 px to avoid - // conflicting with other active corners - // Note: dash in fixed position doesn't use pressure barrier. - if (this._canUsePressure && this._autohideIsEnabled && - DockManager.settings.requirePressureToShow) { - let x1, x2, y1, y2, direction; - const workArea = Main.layoutManager.getWorkAreaForMonitor( - this._monitor.index); - - if (this._position === St.Side.LEFT) { - x1 = this._monitor.x + 1; - x2 = x1; - y1 = workArea.y + 1; - y2 = workArea.y + workArea.height - 1; - direction = Meta.BarrierDirection.POSITIVE_X; - } else if (this._position === St.Side.RIGHT) { - x1 = this._monitor.x + this._monitor.width - 1; - x2 = x1; - y1 = workArea.y + 1; - y2 = workArea.y + workArea.height - 1; - direction = Meta.BarrierDirection.NEGATIVE_X; - } else if (this._position === St.Side.TOP) { - x1 = workArea.x + 1; - x2 = workArea.x + workArea.width - 1; - y1 = this._monitor.y; - y2 = y1; - direction = Meta.BarrierDirection.POSITIVE_Y; - } else if (this._position === St.Side.BOTTOM) { - x1 = workArea.x + 1; - x2 = workArea.x + workArea.width - 1; - y1 = this._monitor.y + this._monitor.height; - y2 = y1; - direction = Meta.BarrierDirection.NEGATIVE_Y; - } - - if (this._pressureBarrier && this._dockState === State.HIDDEN) { - this._barrier = new Meta.Barrier({ - backend: global.backend, - x1, - x2, - y1, - y2, - directions: direction, - }); - this._pressureBarrier.addBarrier(this._barrier); - } - } - } - - _isPrimaryMonitor() { - return this.monitorIndex === Main.layoutManager.primaryIndex; - } - - _resetPosition() { - // Ensure variables linked to settings are updated. - this._updateVisibilityMode(); - - const {dockFixed: fixedIsEnabled, dockExtended: extendHeight} = DockManager.settings; - - if (fixedIsEnabled) - this.add_style_class_name('fixed'); - else - this.remove_style_class_name('fixed'); - - // Note: do not use the workarea coordinates in the direction on which the dock is placed, - // to avoid a loop [position change -> workArea change -> position change] with - // fixed dock. - const workArea = Main.layoutManager.getWorkAreaForMonitor(this.monitorIndex); - - let fraction = DockManager.settings.heightFraction; - if (extendHeight) - fraction = 1; - else if ((fraction < 0) || (fraction > 1)) - fraction = 0.95; - - if (this._isHorizontal) { - this.width = Math.round(fraction * workArea.width); - - let posY = this._monitor.y; - if (this._position === St.Side.BOTTOM) - posY += this._monitor.height; - - this.x = workArea.x + Math.round((1 - fraction) / 2 * workArea.width); - this.y = posY; - - if (extendHeight) { - this.dash._container.set_width(this.width); - this.add_style_class_name('extended'); - } else { - this.dash._container.set_width(-1); - this.remove_style_class_name('extended'); - } - } else { - this.height = Math.round(fraction * workArea.height); - - let posX = this._monitor.x; - if (this._position === St.Side.RIGHT) - posX += this._monitor.width; - - this.x = posX; - this.y = workArea.y + Math.round((1 - fraction) / 2 * workArea.height); - - if (extendHeight) { - this.dash._container.set_height(this.height); - this.add_style_class_name('extended'); - } else { - this.dash._container.set_height(-1); - this.remove_style_class_name('extended'); - } - } - } - - _updateVisibleDesktop() { - if (!this._intellihideIsEnabled) - return; - - const {desktopIconsUsableArea} = DockManager.getDefault(); - if (this._position === St.Side.BOTTOM) - desktopIconsUsableArea.setMargins(this.monitorIndex, 0, this._box.height, 0, 0); - else if (this._position === St.Side.TOP) - desktopIconsUsableArea.setMargins(this.monitorIndex, this._box.height, 0, 0, 0); - else if (this._position === St.Side.RIGHT) - desktopIconsUsableArea.setMargins(this.monitorIndex, 0, 0, 0, this._box.width); - else if (this._position === St.Side.LEFT) - desktopIconsUsableArea.setMargins(this.monitorIndex, 0, 0, this._box.width, 0); - } - - _updateStaticBox() { - this.staticBox.init_rect( - this.x + this._slider.x - (this._position === St.Side.RIGHT ? this._box.width : 0), - this.y + this._slider.y - (this._position === St.Side.BOTTOM ? this._box.height : 0), - this._box.width, - this._box.height - ); - - this._intellihide.updateTargetBox(this.staticBox); - this._updateVisibleDesktop(); - } - - _removeAnimations() { - this._slider.remove_all_transitions(); - } - - _onDragStart() { - this._oldIgnoreHover = this._ignoreHover; - this._ignoreHover = true; - this._animateIn(DockManager.settings.animationTime, 0); - } - - _onDragEnd() { - if (this._oldIgnoreHover) - this._ignoreHover = this._oldIgnoreHover; - this._oldIgnoreHover = null; - this._box.sync_hover(); - this._updateDashVisibility(); - } - - /** - * Show dock and give key focus to it - */ - _onAccessibilityFocus() { - this._box.navigate_focus(null, St.DirectionType.TAB_FORWARD, false); - this._animateIn(DockManager.settings.animationTime, 0); - } - - // Optional features to be enabled only for the main Dock - _enableExtraFeatures() { - // Restore dash accessibility - Main.ctrlAltTabManager.addGroup( - this.dash, _('Dash'), 'user-bookmarks-symbolic', - {focusCallback: this._onAccessibilityFocus.bind(this)}); - } - - /** - * Switch workspace by scrolling over the dock - */ - _optionalScrollWorkspaceSwitch() { - const isEnabled = () => - DockManager.settings.scrollAction === scrollAction.SWITCH_WORKSPACE; - - const enable = () => { - this._signalsHandler.removeWithLabel(Labels.WORKSPACE_SWITCH_SCROLL); - - this._signalsHandler.addWithLabel(Labels.WORKSPACE_SWITCH_SCROLL, - this._box, 'scroll-event', (_, e) => onScrollEvent(e)); - }; - - const disable = () => { - this._signalsHandler.removeWithLabel(Labels.WORKSPACE_SWITCH_SCROLL); - - if (this._optionalScrollWorkspaceSwitchDeadTimeId) { - GLib.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId); - this._optionalScrollWorkspaceSwitchDeadTimeId = 0; - } - }; - - DockManager.settings.connect('changed::scroll-action', () => { - if (isEnabled()) - enable(); - else - disable(); - }); - - if (isEnabled()) - enable(); - - // This was inspired to desktop-scroller@obsidien.github.com - const onScrollEvent = event => { - // When in overview change workspace only in windows view - if (Main.overview.visible) - return false; - - const activeWs = global.workspace_manager.get_active_workspace(); - let direction = null; - - let prevDirection, nextDirection; - if (global.workspace_manager.layout_columns > global.workspace_manager.layout_rows) { - prevDirection = Meta.MotionDirection.UP; - nextDirection = Meta.MotionDirection.DOWN; - } else { - prevDirection = Meta.MotionDirection.LEFT; - nextDirection = Meta.MotionDirection.RIGHT; - } - - switch (event.get_scroll_direction()) { - case Clutter.ScrollDirection.UP: - direction = prevDirection; - break; - case Clutter.ScrollDirection.DOWN: - direction = nextDirection; - break; - case Clutter.ScrollDirection.SMOOTH: { - const [dx_, dy] = event.get_scroll_delta(); - if (dy < 0) - direction = prevDirection; - else if (dy > 0) - direction = nextDirection; - } - break; - } - - if (direction) { - // Prevent scroll events from triggering too many workspace switches - // by adding a 250ms dead time between each scroll event. - // Useful on laptops when using a touch pad. - - // During the deadtime do nothing - if (this._optionalScrollWorkspaceSwitchDeadTimeId) { - return false; - } else { - this._optionalScrollWorkspaceSwitchDeadTimeId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, 250, () => { - this._optionalScrollWorkspaceSwitchDeadTimeId = 0; - }); - } - - let ws; - - ws = activeWs.get_neighbor(direction); - - if (!Main.wm._workspaceSwitcherPopup) { - // Support Workspace Grid extension showing their custom - // Grid Workspace Switcher - if (global.workspace_manager.workspace_grid !== undefined) { - Main.wm._workspaceSwitcherPopup = - global.workspace_manager.workspace_grid.getWorkspaceSwitcherPopup(); - } else { - Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); - } - } - // Set the actor non reactive, so that it doesn't prevent the - // clicks events from reaching the dash actor. I can't see a reason - // why it should be reactive. - Main.wm._workspaceSwitcherPopup.reactive = false; - Main.wm._workspaceSwitcherPopup.connect('destroy', () => { - Main.wm._workspaceSwitcherPopup = null; - }); - - // If Workspace Grid is installed, let them handle the scroll behavior. - if (global.workspace_manager.workspace_grid !== undefined) - ws = global.workspace_manager.workspace_grid.actionMoveWorkspace(direction); - else - Main.wm.actionMoveWorkspace(ws); - - // Do not show workspaceSwitcher in overview - if (!Main.overview.visible) - Main.wm._workspaceSwitcherPopup.display(ws.index()); - - return true; - } else { - return false; - } - }; - } - - _activateApp(appIndex) { - const children = this.dash._box.get_children().filter(actor => { - return actor.child && - actor.child.app; - }); - - // Apps currently in the dash - const apps = children.map(actor => { - return actor.child; - }); - - // Activate with button = 1, i.e. same as left click - const button = 1; - if (appIndex < apps.length) - apps[appIndex].activate(button); - } -}); - -/* - * Handle keyboard shortcuts - */ -const NUM_HOTKEYS = 10; - -const KeyboardShortcuts = class DashToDockKeyboardShortcuts { - constructor() { - this._signalsHandler = new Utils.GlobalSignalsHandler(); - - this._hotKeysEnabled = false; - if (DockManager.settings.hotKeys) - this._enableHotKeys(); - - this._signalsHandler.add([ - DockManager.settings, - 'changed::hot-keys', - () => { - if (DockManager.settings.hotKeys) - this._enableHotKeys.bind(this)(); - else - this._disableHotKeys.bind(this)(); - }, - ]); - - this._optionalNumberOverlay(); - } - - destroy() { - DockManager.allDocks.forEach(dock => { - if (dock._numberOverlayTimeoutId) { - GLib.source_remove(dock._numberOverlayTimeoutId); - dock._numberOverlayTimeoutId = 0; - } - }); - - // Remove keybindings - this._disableHotKeys(); - this._disableExtraShortcut(); - this._signalsHandler.destroy(); - } - - _enableHotKeys() { - if (this._hotKeysEnabled) - return; - - // Setup keyboard bindings for dash elements - const keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-']; - const {mainDock} = DockManager.getDefault(); - keys.forEach(function (key) { - for (let i = 0; i < NUM_HOTKEYS; i++) { - const appNum = i; - Main.wm.addKeybinding(key + (i + 1), DockManager.settings, - Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, - Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, - () => { - mainDock._activateApp(appNum); - this._showOverlay(); - }); - } - }, this); - - this._hotKeysEnabled = true; - } - - _disableHotKeys() { - if (!this._hotKeysEnabled) - return; - - const keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-']; - keys.forEach(key => { - for (let i = 0; i < NUM_HOTKEYS; i++) - Main.wm.removeKeybinding(key + (i + 1)); - }, this); - - this._hotKeysEnabled = false; - } - - _optionalNumberOverlay() { - const {settings} = DockManager; - this._shortcutIsSet = false; - // Enable extra shortcut if either 'overlay' or 'show-dock' are true - if (settings.hotKeys && - (settings.hotkeysOverlay || settings.hotkeysShowDock)) - this._enableExtraShortcut(); - - this._signalsHandler.add([ - settings, - 'changed::hot-keys', - this._checkHotkeysOptions.bind(this), - ], [ - settings, - 'changed::hotkeys-overlay', - this._checkHotkeysOptions.bind(this), - ], [ - settings, - 'changed::hotkeys-show-dock', - this._checkHotkeysOptions.bind(this), - ]); - } - - _checkHotkeysOptions() { - const {settings} = DockManager; - - if (settings.hotKeys && - (settings.hotkeysOverlay || settings.hotkeysShowDock)) - this._enableExtraShortcut(); - else - this._disableExtraShortcut(); - } - - _enableExtraShortcut() { - if (!this._shortcutIsSet) { - Main.wm.addKeybinding('shortcut', DockManager.settings, - Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, - Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, - this._showOverlay.bind(this)); - this._shortcutIsSet = true; - } - } - - _disableExtraShortcut() { - if (this._shortcutIsSet) { - Main.wm.removeKeybinding('shortcut'); - this._shortcutIsSet = false; - } - } - - _showOverlay() { - for (const dock of DockManager.allDocks) { - if (DockManager.settings.hotkeysOverlay) - dock.dash.toggleNumberOverlay(true); - - // Restart the counting if the shortcut is pressed again - if (dock._numberOverlayTimeoutId) { - GLib.source_remove(dock._numberOverlayTimeoutId); - dock._numberOverlayTimeoutId = 0; - } - - // Hide the overlay/dock after the timeout - const timeout = DockManager.settings.shortcutTimeout * 1000; - dock._numberOverlayTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, timeout, () => { - dock._numberOverlayTimeoutId = 0; - dock.dash.toggleNumberOverlay(false); - // Hide the dock again if necessary - dock._updateDashVisibility(); - }); - - // Show the dock if it is hidden - if (DockManager.settings.hotkeysShowDock) { - const showDock = dock._intellihideIsEnabled || dock._autohideIsEnabled; - if (showDock) - dock._show(); - } - } - } -}; - -/** - * Isolate overview to open new windows for inactive apps - * Note: the future implementation is not fully contained here. - * Some bits are around in other methods of other classes. - * This class just take care of enabling/disabling the option. - */ -const WorkspaceIsolation = class DashToDockWorkspaceIsolation { - constructor() { - const {settings} = DockManager; - - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._injectionsHandler = new Utils.InjectionsHandler(); - - const updateAllDocks = () => { - DockManager.allDocks.forEach(dock => - dock.dash.resetAppIcons()); - if (settings.isolateWorkspaces || - settings.isolateMonitors) - this._enable.bind(this)(); - else - this._disable.bind(this)(); - }; - this._signalsHandler.add( - [settings, 'changed::isolate-workspaces', updateAllDocks], - [settings, 'changed::workspace-agnostic-urgent-windows', updateAllDocks], - [settings, 'changed::isolate-monitors', updateAllDocks] - ); - - if (settings.isolateWorkspaces || - settings.isolateMonitors) - this._enable(); - } - - _enable() { - // ensure I never double-register/inject - // although it should never happen - this._disable(); - - DockManager.allDocks.forEach(dock => { - this._signalsHandler.addWithLabel( - Labels.ISOLATION, - [global.display, 'restacked', () => dock.dash._queueRedisplay()], - [global.display, 'window-marked-urgent', () => dock.dash._queueRedisplay()], - [global.display, 'window-demands-attention', () => dock.dash._queueRedisplay()], - [global.window_manager, 'switch-workspace', () => dock.dash._queueRedisplay()] - ); - - // This last signal is only needed for monitor isolation, as windows - // might migrate from one monitor to another without triggering 'restacked' - if (DockManager.settings.isolateMonitors) { - this._signalsHandler.addWithLabel(Labels.ISOLATION, - global.display, - 'window-entered-monitor', - dock.dash._queueRedisplay.bind(dock.dash)); - } - }, this); - - /** - * here this is the Shell.App - */ - function IsolatedOverview() { - // These lines take care of Nautilus for icons on Desktop - const activeWorkspaceIndex = - global.workspaceManager.get_active_workspace_index(); - const windows = this.get_windows().filter(w => - !w.skipTaskbar && w.get_workspace().index() === activeWorkspaceIndex); - - if (windows.length) - return Main.activateWindow(windows[0]); - return this.open_new_window(-1); - } - - this._injectionsHandler.addWithLabel(Labels.ISOLATION, - Shell.App.prototype, - 'activate', - IsolatedOverview); - } - - _disable() { - this._signalsHandler.removeWithLabel(Labels.ISOLATION); - this._injectionsHandler.removeWithLabel(Labels.ISOLATION); - } - - destroy() { - this._signalsHandler.destroy(); - this._injectionsHandler.destroy(); - } -}; - - -export class DockManager { - constructor(extension) { - if (DockManager._singleton) - throw new Error('DashToDock has been already initialized'); - DockManager._singleton = this; - this._extension = extension; - this._signalsHandler = new Utils.GlobalSignalsHandler(this); - this._methodInjections = new Utils.InjectionsHandler(this); - this._vfuncInjections = new Utils.VFuncInjectionsHandler(this); - this._propertyInjections = new Utils.PropertyInjectionsHandler(this); - this._settings = this._extension.getSettings( - 'org.gnome.shell.extensions.dash-to-dock'); - this._appSwitcherSettings = new Gio.Settings({schema_id: 'org.gnome.shell.app-switcher'}); - this._mapSettingsValues(); - - this._iconTheme = new St.IconTheme(); - - this._desktopIconsUsableArea = new DesktopIconsIntegration.DesktopIconsUsableAreaClass(); - this._oldDash = Main.overview.isDummy ? null : Main.overview.dash; - this._discreteGpuAvailable = AppDisplay.discreteGpuAvailable; - this._appSpread = new AppSpread.AppSpread(); - this._notificationsMonitor = new NotificationsMonitor.NotificationsMonitor(); - - const needsRemoteModel = () => - !this._notificationsMonitor.dndMode && this._settings.showIconsEmblems; - - const ensureRemoteModel = () => { - if (needsRemoteModel && !this._remoteModel) { - this._remoteModel = new LauncherAPI.LauncherEntryRemoteModel(); - this._appIconsDecorator = new AppIconsDecorator.AppIconsDecorator(); - } else if (!needsRemoteModel) { - this._remoteModel?.destroy(); - delete this._remoteModel; - this._appIconsDecorator?.destroy(); - delete this._appIconsDecorator; - } - }; - ensureRemoteModel(); - - this._notificationsMonitor.connect('changed', ensureRemoteModel); - this._settings.connect('changed::show-icons-emblems', ensureRemoteModel); - - if (this._discreteGpuAvailable === undefined) { - const updateDiscreteGpuAvailable = () => { - const switcherooProxy = global.get_switcheroo_control(); - if (switcherooProxy) { - const prop = switcherooProxy.get_cached_property('HasDualGpu'); - this._discreteGpuAvailable = prop?.unpack() ?? false; - } else { - this._discreteGpuAvailable = false; - } - }; - this._signalsHandler.add(global, 'notify::switcheroo-control', - () => updateDiscreteGpuAvailable()); - updateDiscreteGpuAvailable(); - } - - // Connect relevant signals to the toggling function - this._bindSettingsChanges(); - - this._ensureLocations(); - - /* Array of all the docks created */ - this._allDocks = []; - this._createDocks(); - - this._overrideAppMenus(); - - // status variable: true when the overview is shown through the dash - // applications button. - this._forcedOverview = false; - } - - static getDefault() { - return DockManager._singleton; - } - - static get allDocks() { - return DockManager.getDefault()._allDocks; - } - - static get extension() { - return DockManager.getDefault().extension; - } - - static get settings() { - return DockManager.getDefault().settings; - } - - get extension() { - return this._extension; - } - - get settings() { - return this._settings; - } - - static get iconTheme() { - return DockManager.getDefault().iconTheme; - } - - get settings() { // eslint-disable-line no-dupe-class-members - return this._settings; - } - - get iconTheme() { - return this._iconTheme; - } - - get fm1Client() { - return this._fm1Client; - } - - get remoteModel() { - return this._remoteModel; - } - - get mainDock() { - return this._allDocks.length ? this._allDocks[0] : null; - } - - get removables() { - return this._removables; - } - - get trash() { - return this._trash; - } - - get desktopIconsUsableArea() { - return this._desktopIconsUsableArea; - } - - get discreteGpuAvailable() { - return AppDisplay.discreteGpuAvailable || this._discreteGpuAvailable; - } - - get appSpread() { - return this._appSpread; - } - - get notificationsMonitor() { - return this._notificationsMonitor; - } - - getDockByMonitor(monitorIndex) { - return this._allDocks.find(d => d.monitorIndex === monitorIndex); - } - - _ensureLocations() { - const {showMounts, showTrash} = this.settings; - - if (showTrash || showMounts) { - if (!this._fm1Client) - this._fm1Client = new FileManager1API.FileManager1Client(); - } else if (this._fm1Client) { - this._fm1Client.destroy(); - this._fm1Client = null; - } - - if (showMounts && !this._removables) { - this._removables = new Locations.Removables(); - } else if (!showMounts && this._removables) { - this._removables.destroy(); - this._removables = null; - } - - if (showTrash && !this._trash) { - this._trash = new Locations.Trash(); - } else if (!showTrash && this._trash) { - this._trash.destroy(); - this._trash = null; - } - - Locations.unWrapFileManagerApp(); - [this._methodInjections, this._propertyInjections].forEach( - injections => injections.removeWithLabel(Labels.LOCATIONS)); - - if (showMounts || showTrash) { - if (this.settings.isolateLocations) { - const fileManagerApp = Locations.wrapFileManagerApp(); - - this._methodInjections.addWithLabel(Labels.LOCATIONS, [ - Shell.AppSystem.prototype, 'get_running', - function (originalMethod, ...args) { - /* eslint-disable no-invalid-this */ - const runningApps = originalMethod.call(this, ...args); - const locationApps = Locations.getRunningApps(); - if (!locationApps.length) - return runningApps; - - const fileManagerIdx = runningApps.indexOf(fileManagerApp); - if (fileManagerIdx > -1 && fileManagerApp?.state !== Shell.AppState.RUNNING) - runningApps.splice(fileManagerIdx, 1); - - return [...runningApps, ...locationApps].sort(Utils.shellAppCompare); - /* eslint-enable no-invalid-this */ - }, - ], - [ - Shell.WindowTracker.prototype, 'get_window_app', - function (originalMethod, window) { - /* eslint-disable no-invalid-this */ - const locationApp = Locations.getRunningApps().find(a => - a.get_windows().includes(window)); - return locationApp ?? originalMethod.call(this, window); - /* eslint-enable no-invalid-this */ - }, - ], - [ - Shell.WindowTracker.prototype, 'get_app_from_pid', - function (originalMethod, pid) { - /* eslint-disable no-invalid-this */ - const locationApp = Locations.getRunningApps().find(a => - a.get_pids().includes(pid)); - return locationApp ?? originalMethod.call(this, pid); - /* eslint-enable no-invalid-this */ - }, - ]); - - const {get: defaultFocusAppGetter} = Object.getOwnPropertyDescriptor( - Shell.WindowTracker.prototype, 'focus_app'); - this._propertyInjections.addWithLabel(Labels.LOCATIONS, - Shell.WindowTracker.prototype, 'focus_app', { - get() { - const locationApp = Locations.getRunningApps().find(a => a.isFocused); - return locationApp ?? defaultFocusAppGetter.call(this); - }, - }); - } - } - } - - _toggle() { - if (this._toggleLater) - return; - - this._toggleLater = Utils.laterAdd(Meta.LaterType.BEFORE_REDRAW, () => { - delete this._toggleLater; - this._restoreDash(); - this._deleteDocks(); - this._createDocks(); - this.emit('toggled'); - }); - } - - _mapExternalSetting(settings, key, mappedKey, mapValueFunction) { - const camelMappedKey = mappedKey.replace(/-([a-z\d])/g, k => k[1].toUpperCase()); - - const dockPropertyDesc = Object.getOwnPropertyDescriptor(this.settings, camelMappedKey); - - if (!dockPropertyDesc) - throw new Error('Setting %s not found in dock'.format(mappedKey)); - - const mappedValue = () => mapValueFunction(settings.get_value(key).recursiveUnpack()); - Object.defineProperty(this.settings, camelMappedKey, { - get: () => mappedValue() ?? dockPropertyDesc.value, - set: value => { - if (mappedValue()) - dockPropertyDesc.value = value; - }, - }); - - this._signalsHandler.addWithLabel(Labels.SETTINGS, settings, - 'changed::%s'.format(key), () => { - this._signalsHandler.blockWithLabel(Labels.SETTINGS); - this.settings.emit('changed::%s'.format(mappedKey), mappedKey); - this._signalsHandler.unblockWithLabel(Labels.SETTINGS); - }); - } - - _mapSettingsValues() { - this.settings.settingsSchema.list_keys().forEach(key => { - const camelKey = key.replace(/-([a-z\d])/g, k => k[1].toUpperCase()); - const updateSetting = () => { - const schemaKey = this.settings.settingsSchema.get_key(key); - if (schemaKey.get_range().deepUnpack()[0] === 'enum') - this.settings[camelKey] = this.settings.get_enum(key); - else - this.settings[camelKey] = this.settings.get_value(key).recursiveUnpack(); - }; - updateSetting(); - this._signalsHandler.addWithLabel(Labels.SETTINGS, this.settings, - `changed::${key}`, updateSetting); - if (key !== camelKey) { - Object.defineProperty(this.settings, key, - {get: () => this.settings[camelKey]}); - } - }); - Object.defineProperties(this.settings, { - dockExtended: {get: () => this.settings.extendHeight}, - }); - } - - _bindSettingsChanges() { - // Connect relevant signals to the toggling function - this._signalsHandler.addWithLabel(Labels.SETTINGS, [ - Utils.getMonitorManager(), - 'monitors-changed', - this._toggle.bind(this), - ], [ - Main.sessionMode, - 'updated', - this._toggle.bind(this), - ], [ - this._settings, - 'changed::multi-monitor', - this._toggle.bind(this), - ], [ - this._settings, - 'changed::preferred-monitor', - this._toggle.bind(this), - ], [ - this._settings, - 'changed::preferred-monitor-by-connector', - this._toggle.bind(this), - ], [ - this._settings, - 'changed::dock-position', - this._toggle.bind(this), - ], [ - this._settings, - 'changed::extend-height', - () => this._adjustPanelCorners(), - ], [ - this._settings, - 'changed::dock-fixed', - () => this._adjustPanelCorners(), - ], [ - this._settings, - 'changed::show-trash', - () => this._ensureLocations(), - ], [ - this._settings, - 'changed::show-mounts', - () => this._ensureLocations(), - ], [ - this._settings, - 'changed::isolate-locations', - () => this._ensureLocations(), - ], [ - this._settings, - 'changed::intellihide', - () => { - if (!this._settings.intellihide) - this._desktopIconsUsableArea.resetMargins(); - }, - ]); - - this._mapExternalSetting(this._appSwitcherSettings, 'current-workspace-only', - 'isolate-workspaces', value => value || undefined); - } - - _createDocks() { - // If there are no monitors (headless configurations, but it can also - // happen temporary while disconnecting and reconnecting monitors), just - // do nothing. When a monitor will be connected we we'll be notified and - // and thus create the docks. This prevents pointing trying to access - // monitors throughout the code, were we are assuming that at least the - // primary monitor is present. - if (Main.layoutManager.monitors.length <= 0) - return; - - - this._preferredMonitorIndex = this.settings.preferredMonitor; - if (this._preferredMonitorIndex === -2) { - const monitorManager = Utils.getMonitorManager(); - this._preferredMonitorIndex = monitorManager.get_monitor_for_connector( - this.settings.preferredMonitorByConnector); - } else if (this._preferredMonitorIndex >= 0) { - // Primary monitor used to be always 0 in Gdk, but the shell has a different - // concept (where the order depends on mutter order). - // So even if now the extension settings may use the same logic of the shell - // we prefer not to break the previously configured systems, and so we still - // assume that the gsettings monitor numbering follows the old strategy. - // This ensure the indexing in the settings and in the shell are matched, - // i.e. that we start counting from the primaryMonitorIndex - this._preferredMonitorIndex = - (Main.layoutManager.primaryIndex + this._preferredMonitorIndex) % - Main.layoutManager.monitors.length; - } - - // In case of multi-monitor, we consider the dock on the primary monitor - // to be the preferred (main) one regardless of the settings the dock - // goes on the primary monitor also if the settings are inconsistent - // (e.g. desired monitor not connected). - if (this.settings.multiMonitor || - this._preferredMonitorIndex < 0 || - this._preferredMonitorIndex > Main.layoutManager.monitors.length - 1) - this._preferredMonitorIndex = Main.layoutManager.primaryIndex; - - - // First we create the main Dock, to get the extra features to bind to this one - let dock = new DockedDash({ - monitorIndex: this._preferredMonitorIndex, - isMain: true, - }); - this._allDocks.push(dock); - - // connect app icon into the view selector - dock.dash.showAppsButton.connect('notify::checked', - this._onShowAppsButtonToggled.bind(this)); - - // Make the necessary changes to Main.overview.dash - this._prepareMainDash(); - - // Adjust corners if necessary - this._adjustPanelCorners(); - - if (this.settings.multiMonitor) { - const nMon = Main.layoutManager.monitors.length; - for (let iMon = 0; iMon < nMon; iMon++) { - if (iMon === this._preferredMonitorIndex) - continue; - dock = new DockedDash({monitorIndex: iMon}); - this._allDocks.push(dock); - // connect app icon into the view selector - dock.dash.showAppsButton.connect('notify::checked', - this._onShowAppsButtonToggled.bind(this)); - } - } - - // Load optional features. We load *after* the docks are created, since - // we need to connect the signals to all dock instances. - this._workspaceIsolation = new WorkspaceIsolation(); - this._keyboardShortcuts = new KeyboardShortcuts(); - - this.emit('docks-ready'); - } - - _prepareStartupAnimation() { - DockManager.allDocks.forEach(dock => { - const {dash} = dock; - - dock.opacity = 255; - dash.set({ - opacity: 0, - translation_x: 0, - translation_y: 0, - }); - }); - } - - _runStartupAnimation() { - DockManager.allDocks.forEach(dock => { - const {dash} = dock; - - switch (dock.position) { - case St.Side.LEFT: - dash.translation_x = -dash.width; - break; - case St.Side.RIGHT: - dash.translation_x = dash.width; - break; - case St.Side.BOTTOM: - dash.translation_y = dash.height; - break; - case St.Side.TOP: - dash.translation_y = -dash.height; - break; - } - - dash.ease({ - opacity: 255, - translation_x: 0, - translation_y: 0, - duration: STARTUP_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - }); - } - - _prepareMainDash() { - // Ensure Main.overview.dash is set to our dash in dummy mode - // while just use the default getter otherwise. - // The getter must be dynamic and not set only when we've a dummy - // overview because the mode can change dynamically. - this._propertyInjections.removeWithLabel(Labels.MAIN_DASH); - const defaultDashGetter = Object.getOwnPropertyDescriptor( - Main.overview.constructor.prototype, 'dash').get; - this._propertyInjections.addWithLabel(Labels.MAIN_DASH, Main.overview, 'dash', { - get: () => Main.overview.isDummy - ? this.mainDock.dash : defaultDashGetter.call(Main.overview), - }); - - if (Main.overview.isDummy) - return; - - // Hide usual Dash - this._oldDash.hide(); - - // Also set dash width to 1, so it's almost not taken into account by code - // calculating the reserved space in the overview. The reason to keep it at 1 is - // to allow its visibility change to trigger an allocation of the appGrid which - // in turn is triggering the appsIcon spring animation, required when no other - // actors has this effect, i.e in horizontal mode and without the workspaceThumbnails - // 1 static workspace only) - this._oldDash.set_height(1); - - this._signalsHandler.addWithLabel(Labels.OLD_DASH_CHANGES, [ - this._oldDash, - 'notify::visible', - () => this._oldDash.hide(), - ], [ - this._oldDash, - 'notify::height', - () => this._oldDash.set_height(1), - ]); - - // Pretend I'm the dash: meant to make app grid swarm animation come from - // the right position of the appShowButton. - this.overviewControls.dash = this.mainDock.dash; - this.searchController._showAppsButton = this.mainDock.dash.showAppsButton; - - // We also need to ignore max-size changes - this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash, - 'setMaxSize', () => {}); - this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash, - 'allocate', () => {}); - // And to return the preferred height depending on the state - this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash, - 'get_preferred_height', (_originalMethod, ...args) => { - if (this.mainDock.isHorizontal && !this.settings.dockFixed) - return this.mainDock.get_preferred_height(...args); - return [0, 0]; - }); - - // FIXME: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2890 - // const { ControlsManagerLayout } = OverviewControls; - const ControlsManagerLayout = this.overviewControls.layout_manager.constructor; - - const maybeAdjustBoxSize = (state, box, spacing) => { - // ensure that an undefined value will be converted into a valid one - spacing = spacing ?? 0; - - if (state === OverviewControls.ControlsState.WINDOW_PICKER) { - const searchBox = this.overviewControls._searchEntry.get_allocation_box(); - const {shouldShow: wsThumbnails} = this.overviewControls._thumbnailsBox; - - if (!wsThumbnails) { - box.y1 += spacing; - box.y2 -= spacing; - } - - box.y2 -= searchBox.get_height() + 2 * spacing; - } - - return box; - }; - - const maybeAdjustBoxToDock = (state, box, spacing) => { - maybeAdjustBoxSize(state, box, spacing); - - if (this.mainDock.isHorizontal || this.settings.dockFixed) - return box; - - const [, preferredWidth] = this.mainDock.get_preferred_width( - box.get_height()); - - if (this.mainDock.position === St.Side.LEFT) - box.x1 += preferredWidth; - else if (this.mainDock.position === St.Side.RIGHT) - box.x2 -= preferredWidth; - - return box; - }; - - this._vfuncInjections.addWithLabel(Labels.MAIN_DASH, ControlsManagerLayout.prototype, - 'allocate', function (container) { - /* eslint-disable no-invalid-this */ - const oldPostAllocation = this._runPostAllocation; - this._runPostAllocation = () => {}; - - const monitor = Main.layoutManager.findMonitorForActor(container); - const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index); - const startX = workArea.x - monitor.x; - const startY = workArea.y - monitor.y; - const workAreaBox = new Clutter.ActorBox(); - workAreaBox.set_origin(startX, startY); - workAreaBox.set_size(workArea.width, workArea.height); - - // GNOME 46 changes "spacing" to "_spacing". - const spacing = this.spacing ?? this._spacing; - - maybeAdjustBoxToDock(undefined, workAreaBox, spacing); - const oldStartY = workAreaBox.y1; - - const propertyInjections = new Utils.PropertyInjectionsHandler(); - propertyInjections.add(Main.layoutManager.panelBox, 'height', {value: startY}); - - if (Main.layoutManager.panelBox.y === Main.layoutManager.primaryMonitor.y) - workAreaBox.y1 -= oldStartY; - - this.vfunc_allocate(container, workAreaBox); - - propertyInjections.destroy(); - workAreaBox.y1 = oldStartY; - - const adjustActorHorizontalAllocation = actor => { - if (!actor.visible || !workAreaBox.x1) - return; - - const contentBox = actor.get_allocation_box(); - contentBox.set_size(workAreaBox.get_width(), contentBox.get_height()); - contentBox.set_origin(workAreaBox.x1, contentBox.y1); - actor.allocate(contentBox); - }; - - [this._searchEntry, this._workspacesThumbnails, this._searchController].forEach( - actor => adjustActorHorizontalAllocation(actor)); - - this._runPostAllocation = oldPostAllocation; - this._runPostAllocation(); - /* eslint-enable no-invalid-this */ - }); - - /** - * This can be removed or bypassed when GNOME/gnome-shell!1892 will be merged - * - * @param originalFunction - * @param state - * @param workAreaBox - * @param {...any} args - */ - function workspaceBoxOriginFixer(originalFunction, state, workAreaBox, ...args) { - /* eslint-disable no-invalid-this */ - const workspaceBox = originalFunction.call(this, state, workAreaBox, ...args); - workspaceBox.set_origin(workAreaBox.x1, workspaceBox.y1); - return workspaceBox; - /* eslint-enable no-invalid-this */ - } - - this._methodInjections.addWithLabel(Labels.MAIN_DASH, [ - ControlsManagerLayout.prototype, - '_computeWorkspacesBoxForState', - function (originalFunction, state, ...args) { - /* eslint-disable no-invalid-this */ - if (state === OverviewControls.ControlsState.HIDDEN) - return originalFunction.call(this, state, ...args); - - const box = workspaceBoxOriginFixer.call(this, originalFunction, state, ...args); - // GNOME 46 changes "spacing" to "_spacing". - const spacing = this.spacing ?? this._spacing; - const dock = DockManager.getDefault().getDockByMonitor(Main.layoutManager.primaryIndex); - if (!dock) - return box; - else - return maybeAdjustBoxSize(state, box, spacing); - /* eslint-enable no-invalid-this */ - }, - ], [ - WorkspacesView.SecondaryMonitorDisplay.prototype, - '_getWorkspacesBoxForState', - function (originalFunction, state, ...args) { - /* eslint-disable no-invalid-this */ - if (state === OverviewControls.ControlsState.HIDDEN) - return originalFunction.call(this, state, ...args); - - const box = workspaceBoxOriginFixer.call(this, originalFunction, state, ...args); - const dock = DockManager.getDefault().getDockByMonitor(this._monitorIndex); - if (!dock) - return box; - if (state === OverviewControls.ControlsState.WINDOW_PICKER && - dock.position === St.Side.BOTTOM) { - const [, preferredHeight] = dock.get_preferred_height(box.get_width()); - box.y2 -= preferredHeight; - } - return box; - /* eslint-enable no-invalid-this */ - }, - ], [ - ControlsManagerLayout.prototype, - '_getAppDisplayBoxForState', - function (originalFunction, ...args) { - /* eslint-disable no-invalid-this */ - return workspaceBoxOriginFixer.call(this, originalFunction, ...args); - /* eslint-enable no-invalid-this */ - }, - ]); - - this._vfuncInjections.addWithLabel(Labels.MAIN_DASH, Workspace.WorkspaceBackground.prototype, - 'allocate', function (box) { - /* eslint-disable no-invalid-this */ - this.vfunc_allocate(box); - - // This code has been submitted upstream via GNOME/gnome-shell!1892 - // so can be removed when that gets merged (or bypassed on newer shell - // versions). - const monitor = Main.layoutManager.monitors[this._monitorIndex]; - const [contentWidth, contentHeight] = this._bin.get_content_box().get_size(); - const [mX1, mX2] = [monitor.x, monitor.x + monitor.width]; - const [mY1, mY2] = [monitor.y, monitor.y + monitor.height]; - const [wX1, wX2] = [this._workarea.x, this._workarea.x + this._workarea.width]; - const [wY1, wY2] = [this._workarea.y, this._workarea.y + this._workarea.height]; - const xScale = contentWidth / this._workarea.width; - const yScale = contentHeight / this._workarea.height; - const leftOffset = wX1 - mX1; - const topOffset = wY1 - mY1; - const rightOffset = mX2 - wX2; - const bottomOffset = mY2 - wY2; - - const contentBox = new Clutter.ActorBox(); - contentBox.set_origin(-leftOffset * xScale, -topOffset * yScale); - contentBox.set_size( - contentWidth + (leftOffset + rightOffset) * xScale, - contentHeight + (topOffset + bottomOffset) * yScale); - - this._backgroundGroup.allocate(contentBox); - /* eslint-enable no-invalid-this */ - }); - - // Reduce the space that the workspaces can use in secondary monitors - this._methodInjections.addWithLabel(Labels.MAIN_DASH, WorkspacesView.WorkspacesView.prototype, - '_getFirstFitAllWorkspaceBox', function (originalFunction, ...args) { - /* eslint-disable no-invalid-this */ - const box = originalFunction.call(this, ...args); - if (DockManager.settings.dockFixed || - this._monitorIndex === Main.layoutManager.primaryIndex) - return box; - - const dock = DockManager.getDefault().getDockByMonitor(this._monitorIndex); - if (!dock) - return box; - - if (dock.isHorizontal) { - const [, preferredHeight] = dock.get_preferred_height(box.get_width()); - box.y2 -= preferredHeight; - if (dock.position === St.Side.TOP) - box.set_origin(box.x1, box.y1 + preferredHeight); - } else { - const [, preferredWidth] = dock.get_preferred_width(box.get_height()); - box.x2 -= preferredWidth / 2; - if (dock.position === St.Side.LEFT) - box.set_origin(box.x1 + preferredWidth, box.y1); - } - return box; - /* eslint-enable no-invalid-this */ - }); - - if (AppDisplay.BaseAppView?.prototype?._pageForCoords) { - // Ensure we handle Dnd events happening on the dock when we're - // dragging from AppDisplay. - // Remove when merged - // https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2002 - this._methodInjections.addWithLabel(Labels.MAIN_DASH, - AppDisplay.BaseAppView.prototype, - '_pageForCoords', function (originalFunction, ...args) { - /* eslint-disable no-invalid-this */ - if (!this._scrollView.has_pointer) - return AppDisplay.SidePages.NONE; - return originalFunction.call(this, ...args); - /* eslint-enable no-invalid-this */ - }); - } - - if (Main.layoutManager._startingUp) { - this._prepareStartupAnimation(); - - const hadOverview = Main.sessionMode.hasOverview; - - // Convince LayoutManager to use the legacy startup animation: - // Reset overview controls state to HIDDEN, as skipping the startup - // overview leaves it stuck at WINDOW_PICKER - if (this._settings.disableOverviewOnStartup) { - Main.sessionMode.hasOverview = false; - Main.overview._overview.controls._stateAdjustment.value = - OverviewControls.ControlsState.HIDDEN; - } - - this._signalsHandler.addWithLabel(Labels.STARTUP_ANIMATION, - Main.layoutManager, 'startup-complete', () => { - this._signalsHandler.removeWithLabel(Labels.STARTUP_ANIMATION); - Main.sessionMode.hasOverview = hadOverview; - this._runStartupAnimation(); - }); - } - } - - _deleteDocks() { - if (!this._allDocks.length) - return; - - // Remove extra features - this._workspaceIsolation.destroy(); - this._keyboardShortcuts.destroy(); - this._desktopIconsUsableArea.resetMargins(); - - // Delete all docks - this._allDocks.forEach(d => d.destroy()); - this._allDocks = []; - - this.emit('docks-destroyed'); - } - - _restoreDash() { - if (!this._oldDash) - return; - - this._signalsHandler.removeWithLabel(Labels.OLD_DASH_CHANGES); - [this._methodInjections, this._vfuncInjections, this._propertyInjections].forEach( - injections => injections.removeWithLabel(Labels.MAIN_DASH)); - - this.overviewControls.layout_manager._dash = this._oldDash; - this.overviewControls.dash = this._oldDash; - this.searchController._showAppsButton = this._oldDash.showAppsButton; - Main.overview.dash.show(); - Main.overview.dash.set_height(-1); // reset default dash size - // This force the recalculation of the icon size - Main.overview.dash._maxHeight = -1; - } - - get overviewControls() { - return Main.overview._overview.controls; - } - - get searchController() { - return this.overviewControls._searchController; - } - - _onShowAppsButtonToggled(button) { - const {checked} = button; - const {overviewControls} = this; - - if (!Main.overview.visible) { - this.mainDock.dash.showAppsButton._fromDesktop = true; - Main.overview.show(OverviewControls.ControlsState.APP_GRID); - } else if (!checked && this.mainDock.dash.showAppsButton._fromDesktop) { - Main.overview.hide(); - this.mainDock.dash.showAppsButton._fromDesktop = false; - } else { - // TODO: I'm not sure how reliable this is, we might need to move the - // _onShowAppsButtonToggled logic into the extension. - if (!checked) - this.mainDock.dash.showAppsButton._fromDesktop = false; - - - // Instead of "syncing" the stock button, let's call its callback directly. - overviewControls._onShowAppsButtonToggled(); - } - - // Because we "disconnected" from the search controller, we have to manage its state. - this.searchController._setSearchActive(false); - } - - _overrideAppMenus() { - this._methodInjections.add(AppMenu.AppMenu.prototype, - '_updateFavoriteItem', function (originalFunction, ...args) { - /* eslint-disable no-invalid-this */ - originalFunction.call(this, ...args); - if (!this._toggleFavoriteItem.visible) - return; - - const {id} = this._app; - this._toggleFavoriteItem.label.text = this._appFavorites.isFavorite(id) - ? _('Unpin') : __('Pin to Dock'); - /* eslint-enable no-invalid-this */ - }); - } - - destroy() { - this.emit('destroy'); - if (this._toggleLater) { - Utils.laterRemove(this._toggleLater); - delete this._toggleLater; - } - this._restoreDash(); - this._deleteDocks(); - this._revertPanelCorners(); - if (this._oldSelectorMargin) - this.searchController.margin_bottom = this._oldSelectorMargin; - if (this._fm1Client) { - this._fm1Client.destroy(); - this._fm1Client = null; - } - this._notificationsMonitor.destroy(); - this._appSpread.destroy(); - this._trash?.destroy(); - this._trash = null; - Locations.unWrapFileManagerApp(); - this._removables?.destroy(); - this._removables = null; - this._iconTheme = null; - this._remoteModel?.destroy(); - this._appIconsDecorator?.destroy(); - this._settings = null; - this._appSwitcherSettings = null; - this._oldDash = null; - - this._desktopIconsUsableArea?.destroy(); - this._desktopIconsUsableArea = null; - this._extension = null; - DockManager._singleton = null; - } - - /** - * Adjust Panel corners, remove this when 41 won't be supported anymore - */ - _adjustPanelCorners() { - if (!this._hasPanelCorners()) - return; - - const position = Utils.getPosition(); - const isHorizontal = (position === St.Side.TOP) || (position === St.Side.BOTTOM); - const dockOnPrimary = this._settings.multiMonitor || - this._preferredMonitorIndex === Main.layoutManager.primaryIndex; - - if (!isHorizontal && dockOnPrimary && this.settings.dockExtended && this.settings.dockFixed) { - Main.panel._rightCorner.hide(); - Main.panel._leftCorner.hide(); - } else { - this._revertPanelCorners(); - } - } - - _revertPanelCorners() { - if (!this._hasPanelCorners()) - return; - - Main.panel._leftCorner.show(); - Main.panel._rightCorner.show(); - } - - _hasPanelCorners() { - return !!Main.panel?._rightCorner && !!Main.panel?._leftCorner; - } -} -Signals.addSignalMethods(DockManager.prototype); - -// This class drives long-running icon animations, to keep them running in sync -// with each other, and to save CPU by pausing them when the dock is hidden. -export class IconAnimator { - constructor(actor) { - this._count = 0; - this._started = false; - this._animations = { - wiggle: [], - }; - this._timeline = new Clutter.Timeline({ - duration: AnimationUtils.adjustAnimationTime(ICON_ANIMATOR_DURATION) || 1, - repeat_count: -1, - actor, - }); - - this._updateSettings(); - this._settingsChangedId = St.Settings.get().connect('notify', - () => this._updateSettings()); - - this._timeline.connect('new-frame', () => { - const progress = this._timeline.get_progress(); - const wiggleRotation = progress < 1 / 6 ? 15 * Math.sin(progress * 24 * Math.PI) : 0; - const wigglers = this._animations.wiggle; - for (let i = 0, iMax = wigglers.length; i < iMax; i++) - wigglers[i].target.rotation_angle_z = wiggleRotation; - }); - } - - _updateSettings() { - this._timeline.set_duration( - AnimationUtils.adjustAnimationTime(ICON_ANIMATOR_DURATION) || 1); - } - - destroy() { - St.Settings.get().disconnect(this._settingsChangedId); - this._timeline.stop(); - this._timeline = null; - for (const pairs of Object.values(this._animations)) { - 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; - } - - 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, 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.stop(); - - return; - } - } - } -} |