/* ======================================================================================================== * dockedWorkspaces.js - dock object that holds the workspaces thumbnailsBox * -------------------------------------------------------------------------------------------------------- * CREDITS: This code was copied from the dash-to-dock extension https://github.com/micheleg/dash-to-dock * and modified to create a workspaces dock. Many thanks to michele_g for a great extension. * * Part of this code also comes from gnome-shell-extensions: * http://git.gnome.org/browse/gnome-shell-extensions/ * ======================================================================================================== */ const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Shell = imports.gi.Shell; const Signals = imports.signals; const St = imports.gi.St; const Meta = imports.gi.Meta; const Params = imports.misc.params; const Main = imports.ui.main; const WorkspacesView = imports.ui.workspacesView; const WorkspaceThumbnail = imports.ui.workspaceThumbnail; const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; const Overview = imports.ui.overview; const OverviewControls = imports.ui.overviewControls; const Layout = imports.ui.layout; const AppDisplay = imports.ui.appDisplay; const ExtensionUtils = imports.misc.extensionUtils; const Config = imports.misc.config; const Me = ExtensionUtils.getCurrentExtension(); const Extension = Me.imports.extension; const Convenience = Me.imports.convenience; const MyWorkspaceThumbnail = Me.imports.myWorkspaceThumbnail; const ShortcutsPanel = Me.imports.shortcutsPanel; const MyWorkspaceSwitcher = Me.imports.myWorkspaceSwitcher; const MyPressureBarrier = Me.imports.myPressureBarrier; const DashToDock_UUID = "dash-to-dock@micxgx.gmail.com"; let DashToDockExtension = null; let DashToDock = null; const DOCK_EDGE_VISIBLE_WIDTH = 5; const DOCK_EDGE_VISIBLE_OVERVIEW_WIDTH = 32; const PRESSURE_TIMEOUT = 1000; let GSFunctions = {}; const IntellihideAction = { SHOW_FULL: 0, SHOW_PARTIAL: 1, SHOW_PARTIAL_FIXED: 2 }; const OverviewAction = { SHOW_FULL: 0, // Dock is always visible HIDE: 1, // Dock is always invisible. Visible on mouse hover SHOW_PARTIAL: 2 // Dock partially hidden. Visible on mouse hover }; const DockState = { HIDDEN: 0, SHOWING: 1, SHOWN: 2, HIDING: 3 }; // Return the actual position reverseing left and right in rtl function getPosition(settings) { let position = settings.get_enum('dock-position'); if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) { if (position == St.Side.LEFT) position = St.Side.RIGHT; else if (position == St.Side.RIGHT) position = St.Side.LEFT; } return position; } function getDockStateDesc(state) { let desc = ""; switch (state) { case DockState.HIDDEN: desc = "HIDDEN"; break; case DockState.SHOWING: desc = "SHOWING"; break; case DockState.SHOWN: desc = "SHOWN"; break; case DockState.HIDING: desc = "HIDING"; break; } return desc; } var MyThumbnailsSlider = GObject.registerClass({ Properties: { 'side': GObject.ParamSpec.enum( 'side', 'side', 'side', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, St.Side, St.Side.RIGHT), 'slidex': GObject.ParamSpec.double( 'slidex', 'slidex', 'slidex', GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT, 0, 1, 1), 'slideout-size': GObject.ParamSpec.double( 'slideout-size', 'slideout-size', 'slideout-size', GObject.ParamFlags.READWRITE, 0, Infinity, 1), 'partial-slideout-size': GObject.ParamSpec.double( 'partial-slideout-size', 'partial-slideout-size', 'partial-slideout-size', GObject.ParamFlags.READWRITE, 0, Infinity, DOCK_EDGE_VISIBLE_OVERVIEW_WIDTH + 1) } }, class WorkspacesToDock_MyThumbnailsSlider extends St.Bin { _init(params = {}) { // slide parameter: 1 = visible, 0 = hidden. this._side = params.side; this._slidex = params.slidex; this._slideoutSize = 1; this._partialSlideoutSize = DOCK_EDGE_VISIBLE_OVERVIEW_WIDTH + 1; super._init(params); } vfunc_allocate(box, flags) { this.set_allocation(box, flags); if (this.child == null) return; let availWidth = box.x2 - box.x1; let availHeight = box.y2 - box.y1; let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = this.child.get_preferred_size(); let childWidth = natChildWidth; let childHeight = natChildHeight; let childBox = new Clutter.ActorBox(); let 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) { childBox.x1 = 0; childBox.x2 = childWidth; childBox.y1 = (this._slidex -1) * (childHeight - slideoutSize); childBox.y2 = slideoutSize + this._slidex * (childHeight - slideoutSize); } this.child.allocate(childBox, flags); 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 vfunc_get_preferred_width(forHeight) { let slideoutSize = this._slideoutSize; let [minWidth, natWidth] = this.child.get_preferred_width(forHeight); if (this._side == St.Side.LEFT || this._side == St.Side.RIGHT) { minWidth = (minWidth) * this._slidex + slideoutSize; natWidth = (natWidth) * this._slidex + slideoutSize; } return [minWidth, natWidth]; } // Just the child height but taking into account the slided out part vfunc_get_preferred_height(forWidth) { let slideoutSize = this._slideoutSize; let [minHeight, natHeight] = this.child.get_preferred_height(forWidth); if (this._side == St.Side.TOP || this._side == St.Side.BOTTOM) { minHeight = (minHeight) * this._slidex + slideoutSize; natHeight = (natHeight) * this._slidex + slideoutSize; } return [minHeight, natHeight]; } set slidex(value) { if (this._slidex == value) return; this._slidex = value; this.notify('slidex'); this.queue_relayout(); } get slidex() { return this._slidex; } set slideoutSize(value) { if (this._slideoutSize == value) return; this._slideoutSize = value; this.notify('slideout-size'); } get slideoutSize() { return this._slideoutSize; } set partialSlideoutSize(value) { if (this._partialSlideoutSize == value) return; this._partialSlideoutSize = value; this.notify('partial-slideout-size'); } get partialSlideoutSize() { return this._partialSlideoutSize; } }); var DockedWorkspaces = class WorkspacesToDock_DockedWorkspaces { constructor() { this._gsCurrentVersion = Config.PACKAGE_VERSION.split('.'); this._settings = Convenience.getSettings('org.gnome.shell.extensions.workspaces-to-dock'); this._signalHandler = new Convenience.globalSignalHandler(); // Temporarily disable redisplay until initialized // NOTE: This prevents connected signals from trying to update dock visibility this._disableRedisplay = true; this._refreshThumbnailsOnRegionUpdate = true; // set RTL value this._rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; // Load settings this._bindSettingsChanges(); // Set position of dock this._position = getPosition(this._settings); this._isHorizontal = (this._position == St.Side.TOP || this._position == St.Side.BOTTOM); // Authohide current status. Not to be confused with autohide enable/disagle global (g)settings // Initially set to null - will be set during first enable/disable autohide this._autohideStatus = null; // Initialize dock state this._dockState = DockState.HIDDEN; // Initialize popup menu flag this._popupMenuShowing = false; // Initialize colors with generic values this._defaultBackground = {red:0, green:0, blue:0, alpha:0}; this._customBackground = {red:0, green:0, blue:0, alpha:0}; this._defaultBorder = {red:0, green:0, blue:0, alpha:0}; this._customBorder = {red:0, green:0, blue:0, alpha:0}; this._cssStylesheet = null; // Initialize pressure barrier variables this._canUsePressure = false; this._pressureSensed = false; this._pressureBarrier = null; this._barrier = null; this._removeBarrierTimeoutId = 0; // Override Gnome Shell functions this._overrideGnomeShellFunctions(); // Set the _monitor property to the primary monitor this._monitor = Main.layoutManager.primaryMonitor; // Create a new thumbnailsbox object this._workspaceAdjustment = Main.overview._overview._controls._workspaceAdjustment; this._thumbnailsBox = new MyWorkspaceThumbnail.MyThumbnailsBox(this._workspaceAdjustment, this); // Create a shortcuts panel object this._shortcutsPanel = new ShortcutsPanel.ShortcutsPanel(this); this._shortcutsPanel.connect("update-favorite-apps", this._onShortcutsPanelUpdated.bind(this)); this._shortcutsPanel.connect("update-running-apps", this._onShortcutsPanelUpdated.bind(this)); // Create custom workspace switcher this._workspaceSwitcher = null; if (this._isHorizontal && this._settings.get_boolean('horizontal-workspace-switching')) this._workspaceSwitcher = new MyWorkspaceSwitcher.WorkspaceSwitcher(); // Create position styles for dock container let positionStyleClass = ['top', 'right', 'bottom', 'left']; let styleClass = positionStyleClass[this._position]; if (this._settings.get_boolean('dock-fixed') || (this._settings.get_boolean('intellihide') && this._settings.get_enum('intellihide-action') == IntellihideAction.SHOW_PARTIAL_FIXED)) { styleClass += " fixed"; } let shortcutsPanelOrientation = this._settings.get_enum('shortcuts-panel-orientation'); if (this._settings.get_boolean('show-shortcuts-panel')) { if (shortcutsPanelOrientation == 1) { styleClass += " inside"; } else { styleClass += " outside"; } } if (this._settings.get_boolean('customize-height') && this._settings.get_int('customize-height-option') == 1) { if (this._settings.get_double('top-margin') == 0 || this._settings.get_double('bottom-margin') == 0) { styleClass += " fullheight"; } } // Set _extendContainer property if (this._settings.get_boolean('customize-height') && this._settings.get_int('customize-height-option') == 1) { this._extendContainer = true; } else { this._extendContainer = false; } // Set _centerContainer property if (this._settings.get_boolean('customize-height') && this._settings.get_boolean('center-thumbnails-on-dock')) { this._centerContainer = true; } else { this._centerContainer = false; } // Set _centerPanelsIndependently property if (this._centerContainer && this._settings.get_int('center-thumbnails-option') == 0) { this._centerPanelsIndependently = true; } else { this._centerPanelsIndependently = false; } // Initialize alignment and box packing variables let align; let packStart; let packVertical = this._isHorizontal? true : false; if (this._position == St.Side.TOP || this._position == St.Side.LEFT) { align = Clutter.ActorAlign.START; packStart = true; } else { align = Clutter.ActorAlign.END; packStart = false; } // Create the main dock and container this._dock = new St.BoxLayout({ name: 'workspacestodockDock', reactive: true, track_hover: true, vertical: packVertical, pack_start: !packStart, style_class: styleClass }); this._dockContainer = new St.BoxLayout({ name: 'workspacestodockDockContainer', reactive: false, track_hover: false, vertical: packVertical }); // Create the panels and container this._panels = new St.BoxLayout({ name: 'workspacestodockPanels', reactive: false, track_hover: false, vertical: packVertical, pack_start: !packStart, x_align: (this._centerContainer) ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START, y_align: (this._centerContainer) ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START }); this._panelsContainer = new St.BoxLayout({ name: 'workspacestodockPanelsContainer', reactive: false, track_hover: false, vertical: packVertical, pack_start: packStart, x_align: (this._centerContainer) ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START, y_align: (this._centerContainer) ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START }); this._panels.add_actor(this._panelsContainer); this._dockContainer.add_actor(this._panels); // Initialize keyboard toggle timeout this._toggleWithKeyboardTimeoutId = 0; // Connect the _dock hover, scroll and button release events this._checkHoverStatusId = 0; this._scrollWorkspaceSwitchDeadTimeId = 0; this._dock.connect("notify::hover", this._hoverChanged.bind(this)); this._dock.connect("scroll-event", this._onScrollEvent.bind(this)); this._dock.connect("button-release-event", this._onDockClicked.bind(this)); // Add workspaces, and shortcuts panel to dock container based on dock position // and shortcuts panel orientation if (shortcutsPanelOrientation == 1) { this._panelsContainer.add_actor(this._shortcutsPanel.actor); this._panelsContainer.add_actor(this._thumbnailsBox); } else { this._panelsContainer.add_actor(this._thumbnailsBox); this._panelsContainer.add_actor(this._shortcutsPanel.actor); } // Create the sliding actor whose allocation is to be tracked for input regions //let slideoutSize = this._settings.get_boolean('dock-edge-visible') ? this._triggerWidth + DOCK_EDGE_VISIBLE_WIDTH : this._triggerWidth; //this._slider = new MyThumbnailsSlider({side: this._position, slideout-size: slideoutSize}); // Slider alignment let xAlign, yAlign; if (this._isHorizontal) { xAlign = Clutter.ActorAlign.CENTER; if (this._position == St.Side.TOP) { yAlign = Clutter.ActorAlign.START; } else { yAlign = Clutter.ActorAlign.END; } } else { if (this._position == St.Side.LEFT) { xAlign = Clutter.ActorAlign.START; } else { xAlign = Clutter.ActorAlign.END; } yAlign = Clutter.ActorAlign.CENTER; } this._slider = new MyThumbnailsSlider({ side: this._position, x_align: xAlign, y_align: yAlign, }); // Create the dock main actor this.actor = new St.Bin({ name: 'workspacestodockMainActor', reactive: false, x_align: align, y_align: align }); this.actor._delegate = this; this._showId = this.actor.connect('show', this._initialize.bind(this)); // Add the dock to slider and then to the main container actor this._dock.add_actor(this._dockContainer); this._slider.set_child(this._dock); this.actor.set_child(this._slider); // Connect global signals let workspaceManager = global.workspace_manager; this._signalHandler.push( [ this._thumbnailsBox, 'notify::width', this._thumbnailsBoxResized.bind(this) ], [ this._thumbnailsBox, 'notify::height', this._thumbnailsBoxResized.bind(this) ], [ Main.layoutManager, 'monitors-changed', this._onMonitorsChanged.bind(this) ], [ St.ThemeContext.get_for_stage(global.stage), 'changed', this._onThemeChanged.bind(this) ], [ Main.extensionManager, 'extension-state-changed', this._onExtensionSystemStateChanged.bind(this) ], [ Main.overview.viewSelector, 'notify::y', this._updateYPosition.bind(this) ], [ global.display, 'in-fullscreen-changed', this._updateBarrier.bind(this) ], [ workspaceManager, 'workspace-added', this._onWorkspaceAdded.bind(this) ], [ workspaceManager, 'workspace-removed', this._onWorkspaceRemoved.bind(this) ] ); // Bind keyboard shortcuts if (this._settings.get_boolean('toggle-dock-with-keyboard-shortcut')) this._bindDockKeyboardShortcut(); // Connect DashToDock hover signal if the extension is already loaded and enabled this._hoveringDash = false; DashToDockExtension = Main.extensionManager.lookup(DashToDock_UUID); if (DashToDockExtension) { if (DashToDockExtension.state == ExtensionUtils.ExtensionState.ENABLED) { DashToDock = DashToDockExtension.imports.extension; if (DashToDock) { DashToDockExtension.hasDockPositionKey = false; if (DashToDock.dockManager) { DashToDockExtension.hasDockPositionKey = true; } else { var keys = DashToDock.dock._settings.list_keys(); if (keys.indexOf('dock-position') > -1) { DashToDockExtension.hasDockPositionKey = true; } } this._connectDashToDockSignals(); } } } // Intialize trigger spacing // We use this space to trigger the dock (show/hide) if the pressure barrier is not present (or disabled) // We also use this space for scrolling when the dock is hidden this._triggerWidth = 1; this._updateTriggerWidth(); // Hide the dock while initializing and setting position // But since we need to access its width, we use opacity this.actor.set_opacity(0); // 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.actor.connect('notify::allocation', Main.layoutManager._queueUpdateRegions.bind(Main.layoutManager)); // Create struts actor for tracking workspace region of fixed dock or partially fixed dock this._struts = new St.Bin({ reactive: false }); if (this._settings.get_boolean('dock-fixed') || (this._settings.get_boolean('intellihide') && this._settings.get_enum('intellihide-action') == IntellihideAction.SHOW_PARTIAL_FIXED)) { Main.uiGroup.add_child(this._struts); Main.layoutManager.uiGroup.set_child_below_sibling(this._struts, Main.layoutManager.modalDialogGroup); Main.layoutManager._trackActor(this._struts, {affectsStruts: true, trackFullscreen: true}); // Force region update to update workspace area Main.layoutManager._queueUpdateRegions(); } // Add aligning container without tracking it for input region (old affectsinputRegion: false that was removed). // The public method trackChrome requires the actor to be child of a tracked actor. Since I don't want the parent // to be tracked I use the private internal _trackActor instead. Main.uiGroup.add_child(this.actor); Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); if (this._settings.get_boolean('dock-fixed') || (this._settings.get_boolean('intellihide') && this._settings.get_enum('intellihide-action') == IntellihideAction.SHOW_PARTIAL_FIXED)) { Main.layoutManager._trackActor(this._slider, {trackFullscreen: true}); } else { if (this._settings.get_boolean('autohide-in-fullscreen')) { Main.layoutManager._trackActor(this._slider); } else { Main.layoutManager._trackActor(this._slider, {trackFullscreen: true}); } } } _initialize() { if(this._showId > 0){ this.actor.disconnect(this._showId); this._showId = 0; } // Show the thumbnailsBox. We need it to calculate the width of the dock. this._thumbnailsBox._createThumbnails(); // Set shortcuts panel visibility if (this._settings.get_boolean('show-shortcuts-panel')) { this._shortcutsPanel.actor.show(); } else { this._shortcutsPanel.actor.hide(); } // Set initial position and opacity this._resetPosition(); this.actor.set_opacity(255); this._disableRedisplay = false; // Now that the dock is on the stage and custom themes are loaded // retrieve background color and set background opacity this._updateAppearancePreferences(); // Setup pressure barrier (GS38+ only) this._updatePressureBarrier(); this._updateBarrier(); // NOTE: GS3.14+ thumbnailsBox width signal triggers ealier so now we need this. this._redisplay(); } destroy() { // Disconnect global signals first to prevent calls to functions during // the destroy process this._signalHandler.disconnect(); // Restore normal Gnome Shell functions this._restoreGnomeShellFunctions(); this._shortcutsPanel.destroy(); if (this._workspaceSwitcher) this._workspaceSwitcher.destroy(); // Unbind keyboard shortcuts if (this._settings.get_boolean('toggle-dock-with-keyboard-shortcut')) this._unbindDockKeyboardShortcut(); // Remove existing barrier this._removeBarrier(); if (this._pressureBarrier) { this._pressureBarrier.destroy(); this._pressureBarrier = null; } // Destroy thumbnailsBox this._thumbnailsBox.destroy(); this._slider.destroy(); this._struts.destroy(); // Destroy main clutter actor: this should be sufficient // From clutter documentation: // If the actor is inside a container, the actor will be removed. // When you destroy a container, its children will be destroyed as well. this.actor.destroy(); // Disconnect GSettings signals // We wait until after _restoreGnomeShellFunctions because we needed // settings to determine how to restore the dash this._settings.run_dispose(); this._settings = null; } // function called during init to override gnome shell 3.4/3.6/3.8 _overrideGnomeShellFunctions() { let self = this; // Hide normal Dash if (this._settings.get_boolean('hide-dash')) { // Hide normal dash Main.overview.dash.hide(); Main.overview.dash.set_width(1); } // Change source of swarm animation to shortcuts panel apps button if (self._settings.get_boolean('show-shortcuts-panel') && self._settings.get_boolean('shortcuts-panel-appsbutton-animation')) { GSFunctions['BaseAppView_doSpringAnimation'] = AppDisplay.BaseAppView.prototype._doSpringAnimation; AppDisplay.BaseAppView.prototype._doSpringAnimation = function(animationDirection){ this._grid.opacity = 255; this._grid.animateSpring( animationDirection, self._shortcutsPanel._appsButton); }; } // Hide normal workspaces thumbnailsBox Main.overview._overview._controls._thumbnailsSlider.opacity = 0; // Override WorkspaceSwitcherPopup _show function to prevent popup from showing when disabled GSFunctions['WorkspaceSwitcherPopup_show'] = WorkspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype._show; WorkspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype._show = function() { if (self._settings.get_boolean('hide-workspace-switcher-popup')) { return false; } else { let ret = GSFunctions['WorkspaceSwitcherPopup_show'].call(this); return ret; } }; // Extend LayoutManager _updateRegions function to destroy/create workspace thumbnails when completed. // NOTE1: needed because 'monitors-changed' signal doesn't wait for queued regions to update. // We need to wait so that the screen workspace workarea is adjusted before creating workspace thumbnails. // Otherwise when we move the primary workspace to another monitor, the workspace thumbnails won't adjust for the top panel. // NOTE2: also needed when dock-fixed is enabled/disabled to adjust for workspace area change GSFunctions['LayoutManager_updateRegions'] = Layout.LayoutManager.prototype._updateRegions; Layout.LayoutManager.prototype._updateRegions = function() { let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); let ret = GSFunctions['LayoutManager_updateRegions'].call(this); // SANITY CHECK: if (self._refreshThumbnailsOnRegionUpdate) { self._refreshThumbnailsOnRegionUpdate = false; if (this._settings) self._refreshThumbnails(); } else { if (self._workAreaWidth) { let widthTolerance = workArea.width * .01; let heightTolerance = workArea.height * .01; if (self._workAreaWidth < workArea.width-widthTolerance || self._workAreaWidth > workArea.width+widthTolerance) { self._refreshThumbnails(); } else if (self._workAreaHeight < workArea.height-heightTolerance || self._workAreaHeight > workArea.height+heightTolerance) { self._refreshThumbnails(); } } else { self._refreshThumbnails(); } } return ret; }; // Override geometry calculations of activities overview to use workspaces-to-dock instead of the default thumbnailsbox. // NOTE: This is needed for when the dock is positioned on a secondary monitor and also for when the shortcuts panel is visible // causing the dock to be wider than normal. GSFunctions['WorkspacesDisplay_updateWorkspacesActualGeometry'] = WorkspacesView.WorkspacesDisplay.prototype._updateWorkspacesActualGeometry; WorkspacesView.WorkspacesDisplay.prototype._updateWorkspacesActualGeometry = function() { if (!this._workspacesViews.length) return; let [x, y] = this.get_transformed_position(); let allocation = this.allocation; let width = allocation.x2 - allocation.x1; let height = allocation.y2 - allocation.y1; let spacing = Main.overview._overview._controls.get_theme_node().get_length('spacing'); let monitors = Main.layoutManager.monitors; // Get Dash monitor index let dashMonitorIndex = this._primaryIndex; let dashMultiMonitor = false; if (DashToDock) { if (DashToDock.dockManager) { dashMonitorIndex = DashToDock.dockManager._preferredMonitorIndex; dashMultiMonitor = DashToDock.dockManager._settings.get_boolean('multi-monitor'); } else { dashMonitorIndex = DashToDock.dock._settings.get_int('preferred-monitor'); if (dashMonitorIndex < 0 || dashMonitorIndex >= Main.layoutManager.monitors.length) { dashMonitorIndex = this._primaryIndex; } } } // Get thumbnails monitor index let preferredMonitorIndex = self._settings.get_int('preferred-monitor'); let thumbnailsMonitorIndex = (Main.layoutManager.primaryIndex + preferredMonitorIndex) % Main.layoutManager.monitors.length ; // Iterate through monitors for (let i = 0; i < monitors.length; i++) { let geometry = { x: monitors[i].x, y: monitors[i].y, width: monitors[i].width, height: monitors[i].height }; // Adjust index to point to correct dock // Only needed when using DashToDock.dockManager let idx; if (i == dashMonitorIndex || dashMultiMonitor) { idx = 0; } else if (i < dashMonitorIndex) { idx = i + 1; } // Adjust width for dash let dashWidth = 0; let dashHeight = 0; let monitorHasDashDock = false; if (DashToDock) { if (DashToDock.dockManager) { if (DashToDock.dockManager._allDocks[0]) { if (i == dashMonitorIndex || dashMultiMonitor) { monitorHasDashDock = true; if (DashToDock.dockManager._allDocks[idx]._position == St.Side.LEFT || DashToDock.dockManager._allDocks[idx]._position == St.Side.RIGHT) { dashWidth = DashToDock.dockManager._allDocks[idx]._box.width + spacing; } if (DashToDock.dockManager._allDocks[idx]._position == St.Side.TOP || DashToDock.dockManager._allDocks[idx]._position == St.Side.BOTTOM) { dashHeight = DashToDock.dockManager._allDocks[idx]._box.height + spacing; } } } } else { if (i == dashMonitorIndex) { monitorHasDashDock = true; if (DashToDockExtension.hasDockPositionKey) { if (DashToDock.dock._position == St.Side.LEFT || DashToDock.dock._position == St.Side.RIGHT) { dashWidth = DashToDock.dock._box.width + spacing; } if (DashToDock.dock._position == St.Side.TOP || DashToDock.dock._position == St.Side.BOTTOM) { dashHeight = DashToDock.dock._box.height + spacing; } } else { dashWidth = DashToDock.dock._box.width + spacing; } } } } else { if (!self._settings.get_boolean('hide-dash') && i == this._primaryIndex) { dashWidth = Main.overview._overview._controls._dashSlider.getVisibleWidth() + spacing; } } // Adjust width for workspaces thumbnails let thumbnailsWidth = 0; let thumbnailsHeight = 0; let monitorHasThumbnailsDock = false; if (i == thumbnailsMonitorIndex) { monitorHasThumbnailsDock = true; let fixedPosition = self._settings.get_boolean('dock-fixed'); let overviewAction = self._settings.get_enum('overview-action'); let visibleEdge = self._triggerWidth; if (self._settings.get_boolean('dock-edge-visible')) { visibleEdge = self._triggerWidth + DOCK_EDGE_VISIBLE_WIDTH; } if (self._position == St.Side.LEFT || self._position == St.Side.RIGHT) { if (fixedPosition) { thumbnailsWidth = self.actor.get_width() + spacing; } else { if (overviewAction == OverviewAction.HIDE) { thumbnailsWidth = visibleEdge; } else if (overviewAction == OverviewAction.SHOW_PARTIAL) { thumbnailsWidth = self._slider.partialSlideoutSize; } else { thumbnailsWidth = self.actor.get_width() + spacing; } } } if (self._position == St.Side.TOP || self._position == St.Side.BOTTOM) { if (fixedPosition) { thumbnailsHeight = self.actor.get_height() + spacing; } else { if (overviewAction == OverviewAction.HIDE) { thumbnailsHeight = visibleEdge; } else if (overviewAction == OverviewAction.SHOW_PARTIAL) { thumbnailsHeight = self._slider.partialSlideoutSize; } else { thumbnailsHeight = self.actor.get_height() + spacing; } } } } // Adjust x and width for workspacesView geometry let controlsWidth = dashWidth + thumbnailsWidth; if (DashToDock && DashToDockExtension.hasDockPositionKey) { if (DashToDock.dockManager) { if (DashToDock.dockManager._allDocks[0]) { // What if dash and thumbnailsbox are both on the same side? if ((monitorHasDashDock && DashToDock.dockManager._allDocks[idx]._position == St.Side.LEFT) && (monitorHasThumbnailsDock && self._position == St.Side.LEFT)) { controlsWidth = Math.max(dashWidth, thumbnailsWidth); geometry.x += controlsWidth; } else { if (monitorHasDashDock && DashToDock.dockManager._allDocks[idx]._position == St.Side.LEFT) { geometry.x += dashWidth; } if (monitorHasThumbnailsDock && self._position == St.Side.LEFT) { geometry.x += thumbnailsWidth; } } if ((monitorHasDashDock && DashToDock.dockManager._allDocks[idx]._position == St.Side.RIGHT) && (monitorHasThumbnailsDock && self._position == St.Side.RIGHT)) { controlsWidth = Math.max(dashWidth, thumbnailsWidth); } } } else { // What if dash and thumbnailsbox are both on the same side? if ((monitorHasDashDock && DashToDock.dock._position == St.Side.LEFT) && (monitorHasThumbnailsDock && self._position == St.Side.LEFT)) { controlsWidth = Math.max(dashWidth, thumbnailsWidth); geometry.x += controlsWidth; } else { if (monitorHasDashDock && DashToDock.dock._position == St.Side.LEFT) { geometry.x += dashWidth; } if (monitorHasThumbnailsDock && self._position == St.Side.LEFT) { geometry.x += thumbnailsWidth; } } if ((monitorHasDashDock && DashToDock.dock._position == St.Side.RIGHT) && (monitorHasThumbnailsDock && self._position == St.Side.RIGHT)) { controlsWidth = Math.max(dashWidth, thumbnailsWidth); } } } else { if (this.get_text_direction() == Clutter.TextDirection.LTR) { if (monitorHasThumbnailsDock && self._position == St.Side.LEFT) { controlsWidth = Math.max(dashWidth, thumbnailsWidth); geometry.x += controlsWidth; } else { geometry.x += dashWidth; } } else { if (monitorHasThumbnailsDock && self._position == St.Side.RIGHT) { controlsWidth = Math.max(dashWidth, thumbnailsWidth); } else { geometry.x += thumbnailsWidth; } } } geometry.width -= controlsWidth; // Adjust y and height for workspacesView geometry for primary monitor (top panel, etc.) if (i == this._primaryIndex) { geometry.y = y; if (height > 0) geometry.height = height; } // What if dash and thumbnailsBox are not on the primary monitor? let controlsHeight = dashHeight + thumbnailsHeight; if (DashToDock && DashToDockExtension.hasDockPositionKey) { if (DashToDock.dockManager) { if (DashToDock.dockManager._allDocks[0]) { if ((monitorHasDashDock && DashToDock.dockManager._allDocks[idx]._position == St.Side.TOP) && (monitorHasThumbnailsDock && self._position == St.Side.TOP)) { controlsHeight = Math.max(dashHeight, thumbnailsHeight); geometry.y += controlsHeight; } else { if (monitorHasDashDock && DashToDock.dockManager._allDocks[idx]._position == St.Side.TOP) { geometry.y += dashHeight; } if (monitorHasThumbnailsDock && self._position == St.Side.TOP) { geometry.y += thumbnailsHeight; } } if ((monitorHasDashDock && DashToDock.dockManager._allDocks[idx]._position == St.Side.BOTTOM) && (monitorHasThumbnailsDock && self._position == St.Side.BOTTOM)) { controlsHeight = Math.max(dashHeight, thumbnailsHeight); } } } else { if ((monitorHasDashDock && DashToDock.dock._position == St.Side.TOP) && (monitorHasThumbnailsDock && self._position == St.Side.TOP)) { controlsHeight = Math.max(dashHeight, thumbnailsHeight); geometry.y += controlsHeight; } else { if (monitorHasDashDock && DashToDock.dock._position == St.Side.TOP) { geometry.y += dashHeight; } if (monitorHasThumbnailsDock && self._position == St.Side.TOP) { geometry.y += thumbnailsHeight; } } if ((monitorHasDashDock && DashToDock.dock._position == St.Side.BOTTOM) && (monitorHasThumbnailsDock && self._position == St.Side.BOTTOM)) { controlsHeight = Math.max(dashHeight, thumbnailsHeight); } } } else { if (monitorHasThumbnailsDock && self._position == St.Side.TOP) { geometry.y += thumbnailsHeight; } } geometry.height -= controlsHeight; this._workspacesViews[i].setMyActualGeometry(geometry); } }; // This override is needed to prevent calls from updateWorkspacesActualGeometry bound to the workspacesDisplay object // without destroying and recreating Main.overview.viewSelector._workspacesDisplay. // We replace this function with a new setMyActualGeometry function (see below) // TODO: This is very hackish. We need to find a better way to accomplish this GSFunctions['WorkspacesViewBase_setActualGeometry'] = WorkspacesView.WorkspacesViewBase.prototype.setActualGeometry; WorkspacesView.WorkspacesViewBase.prototype.setActualGeometry = function(geom) { //GSFunctions['WorkspacesView_setActualGeometry'].call(this, geom); return; }; // This additional function replaces the WorkspacesView setActualGeometry function above. // TODO: This is very hackish. We need to find a better way to accomplish this WorkspacesView.WorkspacesViewBase.prototype.setMyActualGeometry = function(geom) { this._actualGeometry = geom; this._syncActualGeometry(); }; this._overrideComplete = true; } // function called during destroy to restore gnome shell 3.4/3.6/3.8 _restoreGnomeShellFunctions() { // Restore normal Dash if (this._settings.get_boolean('hide-dash')) { if (!DashToDock) { // Show normal dash (if no dash-to-dock) Main.overview.dash.show(); Main.overview.dash.set_width(-1); // This forces the recalculation of the icon size Main.overview.dash._maxHeight = -1; } } // Restore source of swarm animation to normal apps button if (GSFunctions['BaseAppView_doSpringAnimation']) { AppDisplay.BaseAppView.prototype._doSpringAnimation = GSFunctions['BaseAppView_doSpringAnimation']; } // Show normal workspaces thumbnailsBox Main.overview._overview._controls._thumbnailsSlider.opacity = 255; // Restore normal WorkspaceSwitcherPopup_show function WorkspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype._show = GSFunctions['WorkspaceSwitcherPopup_show']; // Restore normal LayoutManager _updateRegions function Layout.LayoutManager.prototype._updateRegions = GSFunctions['LayoutManager_updateRegions']; // Restore normal WorkspacesDisplay _updateworksapgesActualGeometray function WorkspacesView.WorkspacesDisplay.prototype._updateWorkspacesActualGeometry = GSFunctions['WorkspacesDisplay_updateWorkspacesActualGeometry']; // Restore normal WorkspacesView _setActualGeometry function WorkspacesView.WorkspacesViewBase.prototype.setActualGeometry = GSFunctions['WorkspacesViewBase_setActualGeometry']; WorkspacesView.WorkspacesViewBase.prototype.setMyActualGeometry = null; } // handler for when shortcuts panel is updated _onShortcutsPanelUpdated() { this._updateSize(); this._redisplay(); } // handler for when thumbnailsBox is resized _thumbnailsBoxResized() { this._updateSize(); this._redisplay(); } // handler for when dock y position is updated _updateYPosition() { this._updateSize(); } // handler for when workspaces are added _onWorkspaceAdded() { this._updateSize(); this._redisplay(); } // handler for when workspaces are removed _onWorkspaceRemoved() { this._updateSize(); this._redisplay(); } _updateTriggerWidth(force) { // Calculate and set triggerWidth let previousTriggerWidth = this._triggerWidth; if (this._settings.get_boolean('dock-fixed')) { this._triggerWidth = 0; } else if (this._settings.get_boolean('intellihide') && this._settings.get_enum('intellihide-action') == IntellihideAction.SHOW_PARTIAL_FIXED) { this._triggerWidth = 1; } else { if (!this._settings.get_boolean('dock-edge-visible') && this._settings.get_boolean('require-pressure-to-show') && this._settings.get_boolean('disable-scroll')) { if (this._pressureSensed) { this._triggerWidth = 1; } else if (this._dockState == DockState.SHOWN) { this._triggerWidth = 1; } else { this._triggerWidth = 0; } } else { this._triggerWidth = 1; } } if (previousTriggerWidth == this._triggerWidth && !force) return; if (!this._disableRedisplay) this._updateSize(); } // handler for when dock height is updated _updateHeight() { this._updateSize(); } // handler to bind settings when preferences changed _bindSettingsChanges() { this._settings.connect('changed::opaque-background', () => { this._updateBackgroundOpacity(); }); this._settings.connect('changed::background-opacity', () => { this._updateBackgroundOpacity(); }); this._settings.connect('changed::straight-corners', () => { this._updateStraightCorners(); }); this._settings.connect('changed::autohide', () => { this.emit('box-changed'); this._updateBarrier(); }); this._settings.connect('changed::preferred-monitor', () => { this._resetPosition(); this._redisplay(); }); this._settings.connect('changed::hide-dash', () => { if (this._settings.get_boolean('hide-dash')) { Main.overview.dash.hide(); Main.overview.dash.set_width(1); } else { if (!DashToDock) { // Show normal dash (if no dash-to-dock) Main.overview.dash.show(); Main.overview.dash.set_width(-1); // This forces the recalculation of the icon size Main.overview.dash._maxHeight = -1; } } }); this._settings.connect('changed::show-shortcuts-panel', () => { let shortcutsPanelOrientation = this._settings.get_enum('shortcuts-panel-orientation'); if (this._settings.get_boolean('show-shortcuts-panel')) { if (shortcutsPanelOrientation == 1) { this._dock.add_style_class_name('inside'); } this._shortcutsPanel.actor.show(); } else { if (shortcutsPanelOrientation == 1) { this._dock.remove_style_class_name('inside'); } this._shortcutsPanel.actor.hide(); } this._updateSize(); this._redisplay(); }); this._settings.connect('changed::shortcuts-panel-icon-size', () => { this._shortcutsPanel.refresh(); this._updateSize(); this._redisplay(); }); this._settings.connect('changed::shortcuts-panel-show-running', () => { this._shortcutsPanel.refresh(); this._updateSize(); this._redisplay(); }); this._settings.connect('changed::shortcuts-panel-show-places', () => { this._shortcutsPanel.refresh(); this._updateSize(); this._redisplay(); }); this._settings.connect('changed::dock-edge-visible', () => { this._updateTriggerWidth(true); this._redisplay(); }); this._settings.connect('changed::require-pressure-to-show', () => { this._updateTriggerWidth(true); this._redisplay(); }); this._settings.connect('changed::pressure-threshold', () => { this._updatePressureBarrier(); this._updateBarrier(); }); this._settings.connect('changed::use-pressure-speed-limit', () => { this._updatePressureBarrier(); this._updateBarrier(); }); this._settings.connect('changed::pressure-speed-limit', () => { this._updatePressureBarrier(); this._updateBarrier(); }); this._settings.connect('changed::disable-scroll', () => { this._updateTriggerWidth(true); this._redisplay(); }); this._settings.connect('changed::screen-edge-padding', () => { this._updateSize(); this._redisplay(); }); this._settings.connect('changed::customize-thumbnail', () => { // hide and show thumbnailsBox to resize thumbnails this._refreshThumbnails(); }); this._settings.connect('changed::thumbnail-size', () => { // hide and show thumbnailsBox to resize thumbnails this._refreshThumbnails(); }); this._settings.connect('changed::customize-thumbnail-visible-width', () => { this._updateTriggerWidth(true); this._redisplay(); }); this._settings.connect('changed::thumbnail-visible-width', () => { this._updateTriggerWidth(true); this._redisplay(); }); this._settings.connect('changed::workspace-captions', () => { // hide and show thumbnailsBox to reset workspace apps in caption this._refreshThumbnails(); }); this._settings.connect('changed::workspace-caption-position', () => { // hide and show thumbnailsBox to reset workspace apps in caption this._refreshThumbnails(); }); this._settings.connect('changed::workspace-caption-height', () => { // hide and show thumbnailsBox to reset workspace apps in caption this._refreshThumbnails(); }); this._settings.connect('changed::workspace-caption-items', () => { // hide and show thumbnailsBox to reset workspace apps in caption this._refreshThumbnails(); }); this._settings.connect('changed::workspace-caption-windowcount-image', () => { // hide and show thumbnailsBox to reset workspace apps in caption this._refreshThumbnails(); }); this._settings.connect('changed::workspace-caption-taskbar-icon-size', () => { // hide and show thumbnailsBox to reset workspace apps in caption this._refreshThumbnails(); }); this._settings.connect('changed::top-margin', () => { // Add or remove addtional style class when workspace is fixed and set to full height if (this._settings.get_boolean('customize-height') && this._settings.get_int('customize-height-option') == 1) { if (this._settings.get_double('top-margin') == 0 || this._settings.get_double('bottom-margin') == 0) { this._dock.add_style_class_name('fullheight'); } else { this._dock.remove_style_class_name('fullheight'); } } else { this._dock.remove_style_class_name('fullheight'); } this._updateSize(); }); this._settings.connect('changed::bottom-margin', () => { // Add or remove addtional style class when workspace is fixed and set to full height if (this._settings.get_boolean('customize-height') && this._settings.get_int('customize-height-option') == 1) { if (this._settings.get_double('top-margin') == 0 || this._settings.get_double('bottom-margin') == 0) { this._dock.add_style_class_name('fullheight'); } else { this._dock.remove_style_class_name('fullheight'); } } else { this._dock.remove_style_class_name('fullheight'); } this._updateSize(); }); this._settings.connect('changed::toggle-dock-with-keyboard-shortcut', () =>{ if (this._settings.get_boolean('toggle-dock-with-keyboard-shortcut')) this._bindDockKeyboardShortcut(); else this._unbindDockKeyboardShortcut(); }); } _updatePressureBarrier() { let self = this; this._canUsePressure = global.display.supports_extended_barriers(); let pressureThreshold = this._settings.get_double('pressure-threshold'); let speedLimit; if (this._settings.get_boolean('use-pressure-speed-limit')) speedLimit = this._settings.get_double('pressure-speed-limit'); // Remove existing pressure barrier if (this._pressureBarrier) { this._pressureBarrier.destroy(); this._pressureBarrier = null; } // Create new pressure barrier based on pressure threshold setting if (this._canUsePressure) { this._pressureBarrier = new MyPressureBarrier.MyPressureBarrier(pressureThreshold, speedLimit, PRESSURE_TIMEOUT, Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW); this._pressureBarrier.connect('trigger', function(barrier){ self._onPressureSensed(); }); this._pressureBarrier.connect('speed-exceeded', function(barrier){ self._onSpeedExceeded(); }); } } _bindDockKeyboardShortcut() { Main.wm.addKeybinding('dock-keyboard-shortcut', this._settings, Meta.KeyBindingFlags.NONE, Shell.ActionMode.NORMAL, () => { if (this._autohideStatus) { if (this._dockState == DockState.HIDDEN || this._dockState == DockState.HIDING) { this._toggleWithKeyboard(true); } else { this._toggleWithKeyboard(false); } } else { if (this._dockState == DockState.SHOWN || this._dockState == DockState.SHOWING) { this._toggleWithKeyboard(false); } else { this._toggleWithKeyboard(true); } } }); } _toggleWithKeyboard(show) { // Clear keyboard toggle timeout if (this._toggleWithKeyboardTimeoutId > 0) { GLib.source_remove(this._toggleWithKeyboardTimeoutId); this._toggleWithKeyboardTimeoutId = 0; } // Hide dock after timeout if (show) { this._show(); let timeout = this._settings.get_double('keyboard-toggle-timeout') * 1000; this._toggleWithKeyboardTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeout, () => { this._toggleWithKeyboard(false); }); } else { this._hide(); } } _unbindDockKeyboardShortcut() { Main.wm.removeKeybinding('dock-keyboard-shortcut'); } // Determine if mouse is hovering dock container _isHovering() { if (this._dock.hover) { return true; } else { return false; } } // handler for mouse hover events _hoverChanged() { if (this._settings.get_boolean('dock-fixed')) { return; } if (this._canUsePressure && this._settings.get_boolean('require-pressure-to-show') && this._barrier) { if (this._pressureSensed == false && this._dockState != DockState.SHOWN) { if (this._isHovering()) { return; } } } if (this._dockState == DockState.SHOWING) { // Prevent dock from getting stuck animated in when mouse is no longer hovering if (this._checkHoverStatusId > 0) { GLib.source_remove(this._checkHoverStatusId); this._checkHoverStatusId = 0; } this._checkHoverStatusId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, this._checkHoverStatus.bind(this)); return; } if (this._settings.get_boolean('require-click-to-show')) { // check if metaWin is maximized let workspaceManager = global.workspace_manager; let activeWorkspace = workspaceManager.get_active_workspace(); let maximized = false; let windows = global.get_window_actors(); for (let i = windows.length-1; i >= 0; i--) { let metaWin = windows[i].get_meta_window(); if (metaWin.get_workspace() == activeWorkspace) { if (metaWin.appears_focused && metaWin.maximized_horizontally) { maximized = true; break; } } } // set hovering flag if maximized // used by the _onDockClicked function (hover+click) if (maximized) { if (this._isHovering()) { this._hovering = true; if (this._dockState != DockState.SHOWN) { return; } } else { this._hovering = false; } } else { this._hovering = false; } } //Skip if dock is not in autohide mode for instance because it is shown by intellihide if (this._settings.get_boolean('autohide')) { if (this._isHovering()) { this._show(); } else { this._hide(); } } else { this._hide(); } } _checkHoverStatus() { if (this._checkHoverStatusId > 0) { GLib.source_remove(this._checkHoverStatusId); this._checkHoverStatusId = 0; } if (Extension.intellihide._toggledOverviewOnDrag == false) { if (this._toggleWithKeyboardTimeoutId == 0) { this._hoverChanged(); } } } // handler for mouse click events - works in conjuction with hover event to show dock for maxmized windows _onDockClicked(actor, event) { // Show overview if button is right click if (this._settings.get_boolean('toggle-overview')) { let button = event.get_button(); if (button == 3) { //right click if (Main.overview.visible) { Main.overview.hide(); // force normal mode } else { Main.overview.show(); // force overview mode } // pass right-click event on allowing it to bubble up return Clutter.EVENT_PROPAGATE; } } if (this._settings.get_boolean('require-click-to-show')) { if (this._hovering) { //Skip if dock is not in autohide mode for instance because it is shown by intellihide if (this._settings.get_boolean('autohide') && this._autohideStatus) { if (this._isHovering()) { this._show(); } else { this._hide(); } } this._hovering = false; } } return Clutter.EVENT_STOP; } _onSpeedExceeded() { // FIX ISSUE: #23 // Remove barrier so that mouse pointer can access monitors on other side of dock quickly // -------------- // CONTINUE IF // dock NOT in single monitor config // dock NOT on first monitor && in left position // dock NOT on last monitor && in right position if (this._settings.get_boolean('use-pressure-speed-limit')) { if ((Main.layoutManager.monitors.length > 1) && !(this._monitor == 0 && this._position == St.Side.LEFT) && !(this._monitor == Main.layoutManager.monitors.length-1 && this._position == St.Side.RIGHT)) { // Remove barrier immediately this._removeBarrier(); // Restore barrier after short timeout if (this._restoreBarrierTimeoutId > 0) { GLib.source_remove(this._restoreBarrierTimeoutId); this._restoreBarrierTimeoutId = 0; } this._restoreBarrierTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, this._updateBarrier.bind(this)); } } } // handler for mouse pressure sensed (GS38+ only) _onPressureSensed() { this._pressureSensed = true; this._updateTriggerWidth(); this._hoverChanged(); } _onDashToDockShowing() { //Skip if dock is not in dashtodock hover mode if (this._settings.get_boolean('dashtodock-hover')) { if (DashToDock && Main.overview.visible == false) { if (DashToDock.dockManager) { if (DashToDock.dockManager._allDocks[0]._box.hover) { this._hoveringDash = true; this._show(); } } else { if (DashToDock.dock._box.hover) { this._hoveringDash = true; this._show(); } } } } } _onDashToDockHiding() { //Skip if dock is not in dashtodock hover mode if (this._settings.get_boolean('dashtodock-hover')) { if (DashToDock) { this._hoveringDash = false; this._hide(); } } } _onDashToDockLeave() { // NOTE: Causing workspaces-to-dock to hide when switching workspaces in Gnome 3.14. // Remove until a workaround can be found. // this._hoveringDash = false; } // handler for DashToDock hover events _onDashToDockHoverChanged() { //Skip if dock is not in dashtodock hover mode if (this._settings.get_boolean('dashtodock-hover')) { if (DashToDock) { if (DashToDock.dockManager) { if (DashToDock.dockManager._allDocks[0]._box.hover) { if (Main.overview.visible == false) { this._hoveringDash = true; this._show(); } } else { this._hoveringDash = false; this._hide(); } } else { if (DashToDock.dock._box.hover) { if (Main.overview.visible == false) { this._hoveringDash = true; this._show(); } } else { this._hoveringDash = false; this._hide(); } } } } } _onDashToDockToggled() { this._hoveringDash = false; this._disconnectDashToDockSignals(); this._connectDashToDockSignals(); } _onDashToDockBoxDestroy() { this._hoveringDash = false; this._disconnectDashToDockSignals(); // Restore dock if still enabled if (this._checkDashToDockStatusId > 0) { GLib.source_remove(this._checkDashToDockStatusId); this._checkDashToDockStatusId = 0; } this._checkDashToDockStatusId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, this._checkDashToDockStatus.bind(this)); } _checkDashToDockStatus() { if (DashToDock) this._connectDashToDockSignals(); this._checkDashToDockStatusId = 0; } _disconnectDashToDockSignals() { this._signalHandler.disconnectWithLabel('DashToDockBoxHoverSignal'); this._signalHandler.disconnectWithLabel('DashToDockManagerSignal'); } _connectDashToDockSignals() { if (DashToDock) { // Connect DashToDock hover signal if (DashToDock.dockManager) { if (DashToDock.dockManager._allDocks[0]) { this._signalHandler.pushWithLabel( 'DashToDockBoxHoverSignal', [ DashToDock.dockManager._allDocks[0]._box, 'destroy', this._onDashToDockBoxDestroy.bind(this) ], [ DashToDock.dockManager._allDocks[0]._box, 'notify::hover', this._onDashToDockHoverChanged.bind(this) ], [ DashToDock.dockManager._allDocks[0]._box, 'leave-event', this._onDashToDockLeave.bind(this) ] ); this._signalHandler.pushWithLabel( 'DashToDockManagerSignal', [ DashToDock.dockManager._allDocks[0], 'showing', this._onDashToDockShowing.bind(this) ], [ DashToDock.dockManager._allDocks[0], 'hiding', this._onDashToDockHiding.bind(this) ], [ DashToDock.dockManager, 'toggled', this._onDashToDockToggled.bind(this) ] ); } } } } // handler for extensionSystem state changes _onExtensionSystemStateChanged(source, extension) { // Only looking for DashToDock state changes if (extension.uuid == DashToDock_UUID) { DashToDockExtension = extension; if (DashToDockExtension.state == ExtensionUtils.ExtensionState.ENABLED) { DashToDock = DashToDockExtension.imports.extension; if (DashToDock) { DashToDockExtension.hasDockPositionKey = false; if (DashToDock.dockManager) { DashToDockExtension.hasDockPositionKey = true; } else { var keys = DashToDock.dock._settings.list_keys(); if (keys.indexOf('dock-position') > -1) { DashToDockExtension.hasDockPositionKey = true; } } this._connectDashToDockSignals(); } } else if (extension.state == ExtensionUtils.ExtensionState.DISABLED || extension.state == ExtensionUtils.ExtensionState.UNINSTALLED) { DashToDock = null; this._hoveringDash = false; this._disconnectDashToDockSignals(); } } } // handler for mouse scroll events // Switches workspace by scrolling over the dock // This comes from desktop-scroller@obsidien.github.com _onScrollEvent(actor, event) { if (this._settings.get_boolean('disable-scroll') && this._autohideStatus && this._slider.slidex == 0 && // Need to check the slidex for partially showing dock (this._dockState == DockState.HIDDEN || this._dockState == DockState.HIDING)) return Clutter.EVENT_STOP; let workspaceManager = global.workspace_manager; let activeWs = workspaceManager.get_active_workspace(); let direction; switch (event.get_scroll_direction()) { case Clutter.ScrollDirection.UP: if (this._isHorizontal && this._settings.get_boolean('horizontal-workspace-switching')) { direction = Meta.MotionDirection.LEFT; } else { direction = Meta.MotionDirection.UP; } break; case Clutter.ScrollDirection.DOWN: if (this._isHorizontal && this._settings.get_boolean('horizontal-workspace-switching')) { direction = Meta.MotionDirection.RIGHT; } else { direction = Meta.MotionDirection.DOWN; } break; case Clutter.ScrollDirection.LEFT: if (this._isHorizontal && this._settings.get_boolean('horizontal-workspace-switching')) { direction = Meta.MotionDirection.LEFT; } break; case Clutter.ScrollDirection.RIGHT: if (this._isHorizontal && this._settings.get_boolean('horizontal-workspace-switching')) { direction = Meta.MotionDirection.RIGHT; } break; } if (direction) { if (this._settings.get_boolean('scroll-with-touchpad')) { // passingthru67: copied from dash-to-dock // Prevent scroll events from triggering too many workspace switches // by adding a 250ms deadtime between each scroll event. // Usefull on laptops when using a touchpad. // During the deadtime do nothing if(this._scrollWorkspaceSwitchDeadTimeId > 0) return false; else { this._scrollWorkspaceSwitchDeadTimeId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => { this._scrollWorkspaceSwitchDeadTimeId = 0; }); } } let ws = activeWs.get_neighbor(direction); if (Main.wm._workspaceSwitcherPopup == null) { Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); } // Set the workspaceSwitcherPopup actor to non reactive, // to prevent it from grabbing focus away from the dock Main.wm._workspaceSwitcherPopup.reactive = false; Main.wm._workspaceSwitcherPopup.connect('destroy', function() { Main.wm._workspaceSwitcherPopup = null; }); // Do not show wokspaceSwitcher in overview if (!Main.overview.visible) Main.wm._workspaceSwitcherPopup.display(direction, ws.index()); Main.wm.actionMoveWorkspace(ws); } return Clutter.EVENT_STOP; } // autohide function to show dock _show() { // Only show if dock hidden, is hiding, or is partially shown/hidden if (this._dockState == DockState.HIDDEN || this._dockState == DockState.HIDING || this._slider.slidex < 1) { this._removeAnimations(); // If the dock is hidden, wait this._settings.get_double('show-delay') before showing it; // otherwise show it immediately. let delay = 0; if (this._dockState == DockState.HIDDEN) delay = this._settings.get_double('show-delay'); this._animateIn(this._settings.get_double('animation-time'), delay, true); } } // autohide function to hide dock _hide() { this._updateTriggerWidth(); if (this._settings.get_boolean('dock-fixed')) { return; } if (this._isHovering() || (this._hoveringDash && !Main.overview._shown)) { return; } let intellihideAction = this._settings.get_enum('intellihide-action'); if (!Main.overview._shown && intellihideAction == IntellihideAction.SHOW_FULL && !this._autohideStatus) { return; } let overviewAction = this._settings.get_enum('overview-action'); if (Main.overview._shown && overviewAction == OverviewAction.SHOW_FULL && !this._autohideStatus) { return; } // Only hide if dock is shown, is showing, or is partially shown if (this._dockState == DockState.SHOWN || this._dockState == DockState.SHOWING || this._slider.slidex > 0) { this._removeAnimations(); // If the dock is shown, wait this._settings.get_double('show-delay') before hiding it; // otherwise hide it immediately. let delay = 0; if (this._dockState == DockState.SHOWN) delay = this._settings.get_double('hide-delay'); if (Main.overview._shown && Main.overview.viewSelector._activePage == Main.overview.viewSelector._workspacesPage) { this._animateOut(this._settings.get_double('animation-time'), delay, false); } else { this._animateOut(this._settings.get_double('animation-time'), delay, this._autohideStatus); } } } setPopupMenuFlag(showing) { this._popupMenuShowing = showing; if (!showing) { if (this._isHovering()) { this._dock.sync_hover(); } else { this._hide(); } } } // autohide function to animate the show dock process _animateIn(time, delay, force) { let sliderVariable = 1; let fixedPosition = this._settings.get_boolean('dock-fixed') let overviewAction = this._settings.get_enum('overview-action'); let intellihideAction = this._settings.get_enum('intellihide-action'); let intellihide = this._settings.get_boolean('intellihide') if (!force && !fixedPosition) { if ((Main.overview._shown && overviewAction == OverviewAction.SHOW_PARTIAL) || (!Main.overview._shown && intellihide && (intellihideAction == IntellihideAction.SHOW_PARTIAL || intellihideAction == IntellihideAction.SHOW_PARTIAL_FIXED))) { if (this._slider.partialSlideoutSize) { let fullsize; if (this._isHorizontal) { fullsize = this._dock.height; } else { fullsize = this._dock.width; } if (this._settings.get_boolean('dock-edge-visible')) { let triggerWidth = 1; // We need trigger width to always be set to 1 let slideoutSize = DOCK_EDGE_VISIBLE_WIDTH - triggerWidth; sliderVariable = (this._slider.partialSlideoutSize - slideoutSize) / fullsize; } else { sliderVariable = this._slider.partialSlideoutSize / fullsize; } } } else { this._dockState = DockState.SHOWING; } } else { this._dockState = DockState.SHOWING; } this._slider.ease_property('slidex', sliderVariable, { duration: time * 1000, delay: delay * 1000, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { if (!force && !fixedPosition) { if ((Main.overview._shown && overviewAction == OverviewAction.SHOW_PARTIAL) || (!Main.overview._shown && (intellihideAction == IntellihideAction.SHOW_PARTIAL || intellihideAction == IntellihideAction.SHOW_PARTIAL_FIXED))) { this._updateBarrier(); } else { this._dockState = DockState.SHOWN; // Remove barrier so that mouse pointer is released and can access 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); this._removeBarrierTimeoutId = 0; } this._removeBarrierTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, this._removeBarrier.bind(this)); this._updateTriggerWidth(); } } else { this._dockState = DockState.SHOWN; // Remove barrier so that mouse pointer is released and can access 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); this._removeBarrierTimeoutId = 0; } this._removeBarrierTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, this._removeBarrier.bind(this)); this._updateTriggerWidth(); } // Prevent dock from getting stuck animated in when mouse is no longer hovering if (this._checkHoverStatusId > 0) { GLib.source_remove(this._checkHoverStatusId); this._checkHoverStatusId = 0; } this._checkHoverStatusId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, this._checkHoverStatus.bind(this)); } }); } // autohide function to animate the hide dock process _animateOut(time, delay, force) { if (this._popupMenuShowing) return; this._dockState = DockState.HIDING; let sliderVariable = 0; let fixedPosition = this._settings.get_boolean('dock-fixed') let overviewAction = this._settings.get_enum('overview-action'); let intellihideAction = this._settings.get_enum('intellihide-action'); let intellihide = this._settings.get_boolean('intellihide') if (!force && !fixedPosition) { if ((Main.overview._shown && overviewAction == OverviewAction.SHOW_PARTIAL) || (!Main.overview._shown && intellihide && (intellihideAction == IntellihideAction.SHOW_PARTIAL || intellihideAction == IntellihideAction.SHOW_PARTIAL_FIXED))) { if (this._slider.partialSlideoutSize) { let fullsize; if (this._isHorizontal) { fullsize = this._dock.height; } else { fullsize = this._dock.width; } if (this._settings.get_boolean('dock-edge-visible')) { let triggerWidth = 1; // We need trigger width to always be set to 1 let slideoutSize = DOCK_EDGE_VISIBLE_WIDTH - triggerWidth; sliderVariable = (this._slider.partialSlideoutSize - slideoutSize) / fullsize; } else { sliderVariable = this._slider.partialSlideoutSize / fullsize; } } } } this._slider.ease_property('slidex', sliderVariable, { duration: time * 1000, delay: delay * 1000, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { this._dockState = DockState.HIDDEN; this._updateBarrier(); } }); } // autohide function to remove show-hide animations _removeAnimations() { this._slider.remove_all_transitions(); } // autohide function to fade out opaque background _fadeOutBackground(time, delay) { // CSS time is in ms this._thumbnailsBox.set_style('transition-duration:' + time*1000 + ';' + 'transition-delay:' + delay*1000 + ';' + 'background-color: rgba(0,0,0,0);' + 'border-color:' + this._defaultBorder); this._shortcutsPanel.actor.set_style('transition-duration:' + time*1000 + ';' + 'transition-delay:' + delay*1000 + ';' + 'background-color: rgba(0,0,0,0);' + 'border-color:' + this._defaultBorder); this._dockContainer.set_style('transition-duration:' + time*1000 + ';' + 'transition-delay:' + delay*1000 + ';' + 'background-color:' + this._defaultBackground + ';' + 'border-color:' + this._defaultBorder); } // autohide function to fade in opaque background _fadeInBackground(time, delay) { // CSS time is in ms this._thumbnailsBox.set_style('transition-duration:' + time*1000 + ';' + 'transition-delay:' + delay*1000 + ';' + 'background-color: rgba(0,0,0,0);' + 'border-color:' + this._customBorder); this._shortcutsPanel.actor.set_style('transition-duration:' + time*1000 + ';' + 'transition-delay:' + delay*1000 + ';' + 'background-color: rgba(0,0,0,0);' + 'border-color:' + this._customBorder); this._dockContainer.set_style('transition-duration:' + time*1000 + ';' + 'transition-delay:' + delay*1000 + ';' + 'background-color:' + this._customBackground + ';' + 'border-color:' + this._customBorder); } // This function handles hiding the dock when dock is in stationary-fixed // position but overlapped by gnome panel menus or meta popup windows fadeOutDock(time, delay) { if (Main.layoutManager._inOverview) { // Hide fixed dock when in overviewmode applications view this.actor.opacity = 0; } // Make thumbnail windowclones non-reactive // NOTE: Need this for when in overviewmode applications view and dock is in fixed mode. // Fixed dock has opacity set to 0 but is still reactive. this._dock.reactive = false; this._shortcutsPanel.setReactiveState(false); this._thumbnailsBox.reactive = false; for (let i = 0; i < this._thumbnailsBox._thumbnails.length; i++) { let thumbnail = this._thumbnailsBox._thumbnails[i]; thumbnail.setCaptionReactiveState(false); thumbnail.setWindowClonesReactiveState(false); } } // This function handles showing the dock when dock is stationary-fixed // position but overlapped by gnome panel menus or meta popup windows fadeInDock(time, delay) { this.actor.opacity = 255; // Return thumbnail windowclones to reactive state this._dock.reactive = true; this._shortcutsPanel.setReactiveState(true); this._thumbnailsBox.reactive = true; for (let i = 0; i < this._thumbnailsBox._thumbnails.length; i++) { let thumbnail = this._thumbnailsBox._thumbnails[i]; thumbnail.setCaptionReactiveState(true); thumbnail.setWindowClonesReactiveState(true); } // if (!this._workAreaHeight || !this._workAreaWidth) { // this._refreshThumbnailsOnRegionUpdate = true; // Main.layoutManager._queueUpdateRegions(); // } } // retrieve default background color _getBackgroundColor() { // Remove custom style let oldStyle = this._thumbnailsBox.get_style(); this._thumbnailsBox.set_style(null); // Prevent shell crash if the actor is not on the stage // It happens enabling/disabling repeatedly the extension if (!this._thumbnailsBox.get_stage()) return null; let themeNode = this._thumbnailsBox.get_theme_node(); this._thumbnailsBox.set_style(oldStyle); // Just in case the theme has different border colors .. // We want to find the inside border-color of the dock because it is // the side most visible to the user. We do this by finding the side // opposite the position let side = this._position + 2; if (side > 3) side = Math.abs(side - 4); let backgroundColor = themeNode.get_background_color(); let borderColor = themeNode.get_border_color(side); return [backgroundColor, borderColor]; } _updateAppearancePreferences() { this._updateStraightCorners(); this._updateBackgroundOpacity(); } _updateStraightCorners() { if (this._settings.get_boolean('straight-corners')) { this._dock.add_style_class_name('straight-corners'); } else { this._dock.remove_style_class_name('straight-corners'); } } // update background opacity based on preferences _updateBackgroundOpacity() { let [backgroundColor, borderColor] = this._getBackgroundColor(); if (backgroundColor) { // We check the background alpha for a minimum of .001 to prevent // division by 0 errors when calculating borderAlpha later let backgroundAlpha = Math.max(Math.round(backgroundColor.alpha/2.55)/100, .001); let newAlpha = this._settings.get_double('background-opacity'); this._defaultBackground = "rgba(" + backgroundColor.red + "," + backgroundColor.green + "," + backgroundColor.blue + "," + backgroundAlpha + ")"; this._customBackground = "rgba(" + backgroundColor.red + "," + backgroundColor.green + "," + backgroundColor.blue + "," + newAlpha + ")"; if (borderColor) { // The border and background alphas should remain in sync // We also limit the borderAlpha to a maximum of 1 (full opacity) let borderAlpha = Math.round(borderColor.alpha/2.55)/100; borderAlpha = Math.min((borderAlpha/backgroundAlpha)*newAlpha, 1); this._defaultBorder = "rgba(" + borderColor.red + "," + borderColor.green + "," + borderColor.blue + "," + Math.round(borderColor.alpha/2.55)/100 + ")"; this._customBorder = "rgba(" + borderColor.red + "," + borderColor.green + "," + borderColor.blue + "," + borderAlpha + ")"; } if (this._settings.get_boolean('opaque-background')) { this._fadeInBackground(this._settings.get_double('animation-time'), 0); } else { this._fadeOutBackground(this._settings.get_double('animation-time'), 0); } } } // handler for theme changes _onThemeChanged() { this._changeStylesheet(); if (!this._disableRedisplay) this._updateAppearancePreferences(); } // function to change stylesheets _changeStylesheet() { // Get css filename let filename = "workspaces-to-dock.css"; // Get new theme stylesheet let themeStylesheet = Main._getDefaultStylesheet(); if (Main.getThemeStylesheet()) themeStylesheet = Main.getThemeStylesheet(); // Get theme directory let themeDirectory = themeStylesheet.get_path() ? GLib.path_get_dirname(themeStylesheet.get_path()) : ""; // Test for workspacesToDock stylesheet let newStylesheet = null; if (themeDirectory != "") newStylesheet = Gio.file_new_for_path(themeDirectory + '/extensions/workspaces-to-dock/' + filename); if (!newStylesheet || !newStylesheet.query_exists(null)) { let defaultStylesheet = Gio.File.new_for_path(Me.path + "/themes/default/" + filename); if (defaultStylesheet.query_exists(null)) { newStylesheet = defaultStylesheet; } else { throw new Error(_("No Workspaces-To-Dock stylesheet found") + " (extension.js)."); } } if (Extension.workspacesToDockStylesheet && Extension.workspacesToDockStylesheet.equal(newStylesheet)) { return false; } // Change workspacesToDock stylesheet by updating theme let themeContext = St.ThemeContext.get_for_stage(global.stage); if (!themeContext) return false; let theme = themeContext.get_theme(); if (!theme) return false; let customStylesheets = theme.get_custom_stylesheets(); if (!customStylesheets) return false; let previousStylesheet = Extension.workspacesToDockStylesheet; Extension.workspacesToDockStylesheet = newStylesheet; // let newTheme = new St.Theme ({ application_stylesheet: themeStylesheet, // default_stylesheet: Main._defaultCssStylesheet }); let newTheme = new St.Theme ({ application_stylesheet: themeStylesheet }); for (let i = 0; i < customStylesheets.length; i++) { if (!customStylesheets[i].equal(previousStylesheet)) { newTheme.load_stylesheet(customStylesheets[i]); } } newTheme.load_stylesheet(Extension.workspacesToDockStylesheet); themeContext.set_theme (newTheme); if (!this._disableRedisplay) { this._refreshThumbnails(); } return true; } // resdiplay dock called if size-position changed due to dock resizing _redisplay() { if (this._disableRedisplay) return // Initial display of dock .. sets autohideStatus if (this._autohideStatus == null) { if (this._settings.get_boolean('dock-fixed')) { this._autohideStatus = false; this.fadeInDock(this._settings.get_double('animation-time'), 0); } else { // Initial animation is out .. intellihide will animate in if its needed this._removeAnimations(); this._animateOut(0, 0); this._autohideStatus = true; } } else { // Redisplay dock by animating back in .. necessary if thumbnailsBox size changed // even if dock is fixed if (this._autohideStatus) { if (!this._isHovering() && !(this._hoveringDash && !Main.overview._shown)) { this._removeAnimations(); this._animateOut(0, 0, true); } this._autohideStatus = true; } else { if (!this._isHovering() && !(this._hoveringDash && !Main.overview._shown)) { // had to comment out because GS3.4 fixed-dock isn't fully faded in yet when redisplay occurs again //this._removeAnimations(); this._animateIn(this._settings.get_double('animation-time'), 0); } this._autohideStatus = false; } } this._updateAppearancePreferences(); this._updateBarrier(); } // update the dock size and position _updateSize() { // Accommodate shortcuts panel in calculations let shortcutsPanelThickness = 0; if (this._settings.get_boolean('show-shortcuts-panel')) { if (this._isHorizontal) { shortcutsPanelThickness = this._shortcutsPanel.actor.height; } else { shortcutsPanelThickness = this._shortcutsPanel.actor.width; } } // Get workspace area // This takes into account primary monitor and any additional extensions // that may affect width and height calculations let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index); // get the scale factor let scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor; // Get screen edge padding from preferences and multiply it by scale_factor for HiDPI monitors let screenEdgePadding = this._settings.get_double('screen-edge-padding') * scale_factor; let x, y, width, height, anchorPoint; if (this._isHorizontal) { // Get x position and width if (this._settings.get_boolean('customize-height')) { let leftMargin = Math.floor(this._settings.get_double('top-margin') * this._monitor.width); let rightMargin = Math.floor(this._settings.get_double('bottom-margin') * this._monitor.width); x = workArea.x + leftMargin; width = workArea.width - leftMargin - rightMargin; } else { let margin = this._monitor.width * .1; x = this._monitor.x + margin; width = this._monitor.width - (margin * 2); } // Get y position, height, and anchorpoint height = this._thumbnailsBox._thumbnailsBoxHeight + shortcutsPanelThickness + screenEdgePadding; if (this._position == St.Side.TOP) { y = this._monitor.y; anchorPoint = Clutter.Gravity.NORTH_WEST; } else { y = this._monitor.y + this._monitor.height; anchorPoint = Clutter.Gravity.SOUTH_WEST; } } else { // Get x position, width, and anchorpoint width = this._thumbnailsBox._thumbnailsBoxWidth + shortcutsPanelThickness + screenEdgePadding; if (this._position == St.Side.LEFT) { x = this._monitor.x; anchorPoint = Clutter.Gravity.NORTH_WEST; } else { x = this._monitor.x + this._monitor.width; anchorPoint = Clutter.Gravity.NORTH_EAST; } // Get y position and height if (this._settings.get_boolean('customize-height')) { let topMargin = Math.floor(this._settings.get_double('top-margin') * this._monitor.height); let bottomMargin = Math.floor(this._settings.get_double('bottom-margin') * this._monitor.height); y = workArea.y + topMargin; height = workArea.height - topMargin - bottomMargin; } else { let controlsTop = 45; y = this._monitor.y + Main.panel.height + controlsTop + Main.overview.searchEntry.height; height = this._monitor.height - (y + Main.overview.searchEntry.height); } } //// skip updating if size is same ?? //if ((this.actor.y == y) && (this.actor.width == this._thumbnailsBox._thumbnailsBoxWidth + shortcutsPanelThickness) && (this.actor.height == height)) { //return; //} // Update position of main actor (used to detect window overlaps) this.actor.set_position(x, y); this._struts.set_position(x, y); // Update size of the main actor as well as the _dock & _panels inside // NOTE: Rather than rely on the builtin box layout mechanics, we control // both width and height based on the thumbnail & shortcuts panels. This // allows us to control the trigger space for showing/hiding and scrolling if (this._isHorizontal) { if (this._settings.get_boolean('customize-height')) { let [minThumbnailsBoxWidth, minThumbnailsBoxHeight, natThumbnailsBoxWidth, natThumbnailsBoxHeight] = this._thumbnailsBox.get_preferred_size(); let minShortcutsPanelWidth = 0, minShortcutsPanelHeight = 0, natShortcutsPanelWidth = 0, natShortcutsPanelHeight = 0; if (this._settings.get_boolean('show-shortcuts-panel')) { [minShortcutsPanelWidth, minShortcutsPanelHeight, natShortcutsPanelWidth, natShortcutsPanelHeight] = this._shortcutsPanel.actor.get_preferred_size(); } let containerWidth = natThumbnailsBoxWidth > natShortcutsPanelWidth ? natThumbnailsBoxWidth : natShortcutsPanelWidth; if (containerWidth > width) { containerWidth = width; } this._panelsContainer.set_size(containerWidth, height); this._panels.set_size(containerWidth, height); if (this._extendContainer) { this._dockContainer.set_size(width, height); this._dock.set_size(width, height + this._triggerWidth); this.actor.set_size(width, height + this._triggerWidth); } else { this._dockContainer.set_size(containerWidth, height); this._dock.set_size(containerWidth, height + this._triggerWidth); this.actor.set_size(containerWidth, height + this._triggerWidth); if (this._centerContainer) { if (width > containerWidth) { let xMiddle = x + Math.round((width - containerWidth) / 2); this.actor.set_position(xMiddle, y); } } } } else { this._panelsContainer.set_size(width, height); this._panels.set_size(width, height); this._dockContainer.set_size(width, height); this._dock.set_size(width, height + this._triggerWidth); this.actor.set_size(width, height + this._triggerWidth); } } else { if (this._settings.get_boolean('customize-height')) { let [minThumbnailsBoxWidth, minThumbnailsBoxHeight, natThumbnailsBoxWidth, natThumbnailsBoxHeight] = this._thumbnailsBox.get_preferred_size(); let minShortcutsPanelWidth = 0, minShortcutsPanelHeight = 0, natShortcutsPanelWidth = 0, natShortcutsPanelHeight = 0; if (this._settings.get_boolean('show-shortcuts-panel')) { [minShortcutsPanelWidth, minShortcutsPanelHeight, natShortcutsPanelWidth, natShortcutsPanelHeight] = this._shortcutsPanel.actor.get_preferred_size(); } let containerHeight = natThumbnailsBoxHeight > natShortcutsPanelHeight ? natThumbnailsBoxHeight : natShortcutsPanelHeight; if (containerHeight > height) { containerHeight = height; } this._panelsContainer.set_size(width, containerHeight); this._panels.set_size(width, containerHeight); if (this._extendContainer) { this._dockContainer.set_size(width, height); this._dock.set_size(width + this._triggerWidth, height); this.actor.set_size(width + this._triggerWidth, height); } else { this._dockContainer.set_size(width, containerHeight); this._dock.set_size(width + this._triggerWidth, containerHeight); this.actor.set_size(width + this._triggerWidth, containerHeight); if (this._centerContainer) { if (height > containerHeight) { let yMiddle = y + Math.round((height - containerHeight) / 2); this.actor.set_position(x, yMiddle); } } } } else { this._panelsContainer.set_size(width, height); this._panels.set_size(width, height); this._dockContainer.set_size(width, height); this._dock.set_size(width + this._triggerWidth, height); this.actor.set_size(width + this._triggerWidth, height); } } // Set anchor points this.actor.move_anchor_point_from_gravity(anchorPoint); this._struts.move_anchor_point_from_gravity(anchorPoint); // Update slider slideout width let slideoutSize = this._settings.get_boolean('dock-edge-visible') ? this._triggerWidth + DOCK_EDGE_VISIBLE_WIDTH : this._triggerWidth; this._slider.slideoutSize = slideoutSize; // Update slider partial width // NOTE: only effects slider width when dock is set to partially hide in overview let slidePartialVisibleWidth = this._triggerWidth + DOCK_EDGE_VISIBLE_OVERVIEW_WIDTH; if (this._settings.get_boolean('show-shortcuts-panel') && this._settings.get_enum('shortcuts-panel-orientation') == 1) { if (this._isHorizontal) { slidePartialVisibleWidth = this._shortcutsPanel.actor.height; } else { slidePartialVisibleWidth = this._shortcutsPanel.actor.width; } } else { // NOTE: Gnome css top panel height is 1.86em if (this._settings.get_boolean('customize-thumbnail-visible-width')) { slidePartialVisibleWidth = this._settings.get_double('thumbnail-visible-width'); } else { let themeVisibleWidth = this._thumbnailsBox.get_theme_node().get_length('visible-width'); if (themeVisibleWidth > 0) slidePartialVisibleWidth = themeVisibleWidth; } } this._slider.partialSlideoutSize = slidePartialVisibleWidth; // Set struts size if (!this._settings.get_boolean('dock-fixed') && (this._settings.get_boolean('intellihide') && this._settings.get_enum('intellihide-action') == IntellihideAction.SHOW_PARTIAL_FIXED)) { if (this._isHorizontal) { this._struts.set_size(width, slidePartialVisibleWidth); } else { this._struts.set_size(slidePartialVisibleWidth, height); } } else { this._struts.set_size(this.actor.width, this.actor.height); } } // 'Hard' reset dock positon: called on start and when monitor changes _resetPosition() { this._monitor = this._getMonitor(); this._updateSize(); this._updateAppearancePreferences(); this._updateBarrier(); } _onMonitorsChanged() { // Reset the dock position and redisplay this._resetPosition(); this._redisplay(); // this._refreshThumbnailsOnRegionUpdate = true; // Main.layoutManager._queueUpdateRegions(); } _refreshThumbnails() { let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); this._workAreaWidth = workArea.width; this._workAreaHeight = workArea.height; if (this._thumbnailsBox) { this._thumbnailsBox._destroyThumbnails(); this._thumbnailsBox._createThumbnails(); } // NOTE: restarting Gnome Shell with the dock height extended leaves the top of the dock hidden // under the shell's top bar. Resetting the position after a thumbnail refresh (during Region Updates) // fixes this issue. this._resetPosition(); } // Retrieve the preferred monitor _getMonitor() { // We are using Gdk in settings prefs which sets the primary monitor to 0 // The shell can assign a different number (Main.layoutManager.primaryMonitor) // This ensures that the indexing in the settings (Gdk) and in the shell are matched, // i.e. that we start counting from the primaryMonitorIndex let preferredMonitorIndex = this._settings.get_int('preferred-monitor'); let monitorIndex = (Main.layoutManager.primaryIndex + preferredMonitorIndex) % Main.layoutManager.monitors.length ; let monitor = Main.layoutManager.monitors[monitorIndex]; return monitor; } // Remove pressure barrier (GS38+ only) _removeBarrier() { if (this._barrier) { if (this._pressureBarrier) { this._pressureBarrier.removeBarrier(this._barrier); } this._barrier.destroy(); this._barrier = null; } // Remove barrier timeout if (this._removeBarrierTimeoutId > 0) { GLib.source_remove(this._removeBarrierTimeoutId); this._removeBarrierTimeoutId = 0; } return false; } // Update pressure barrier size (GS38+ only) _updateBarrier() { // Remove existing barrier this._removeBarrier(); // 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 // Note: dock in fixed possition doesn't use pressure barrier if (this.actor.visible && this._canUsePressure && this._settings.get_boolean('autohide') && this._settings.get_boolean('require-pressure-to-show') && !this._settings.get_boolean('dock-fixed')) { let x1, x2, y1, y2, direction; if(this._position==St.Side.LEFT){ x1 = this._monitor.x; x2 = this._monitor.x; y1 = this.actor.y; y2 = this.actor.y + this.actor.height; direction = Meta.BarrierDirection.POSITIVE_X; } else if(this._position==St.Side.RIGHT) { x1 = this._monitor.x + this._monitor.width; x2 = this._monitor.x + this._monitor.width; y1 = this.actor.y; y2 = this.actor.y + this.actor.height; direction = Meta.BarrierDirection.NEGATIVE_X; } else if(this._position==St.Side.TOP) { let hotCornerPadding = 1; x1 = this.actor.x + hotCornerPadding; x2 = this.actor.x + hotCornerPadding + this.actor.width; y1 = this._monitor.y; y2 = this._monitor.y; direction = Meta.BarrierDirection.POSITIVE_Y; } else if (this._position==St.Side.BOTTOM) { x1 = this.actor.x; x2 = this.actor.x + this.actor.width; y1 = this._monitor.y + this._monitor.height; y2 = this._monitor.y + this._monitor.height; direction = Meta.BarrierDirection.NEGATIVE_Y; } this._barrier = new Meta.Barrier({display: global.display, x1: x1, x2: x2, y1: y1, y2: y2, directions: direction}); if (this._pressureBarrier) { this._pressureBarrier.addBarrier(this._barrier); } } // Reset pressureSensed flag if (!this._isHovering() && this._dockState != DockState.SHOWN) { this._pressureSensed = false; this._updateTriggerWidth(); } } // Disable autohide effect, thus show workspaces disableAutoHide(force) { // NOTE: default functionality is to not force complete animateIn this._autohideStatus = false; if (this._dockState == DockState.HIDING || this._dockState == DockState.HIDDEN) { this._removeAnimations(); if (force) { this._animateIn(this._settings.get_double('animation-time'), 0, true); } else { this._animateIn(this._settings.get_double('animation-time'), 0); } } } // Enable autohide effect, hide workspaces enableAutoHide(dontforce) { // NOTE: default functionality is to force complete animateOut // autohide status shouldn't change if not completely animating out if (dontforce) { this._autohideStatus = false; } else { this._autohideStatus = true; } if (this._isHovering()) { this._dock.sync_hover(); } let delay = 0; // immediately fadein background if hide is blocked by mouseover, otherwise start fadein when dock is already hidden. if (this._settings.get_boolean('autohide')) { if (!this._isHovering() && !(this._hoveringDash && !Main.overview._shown)) { this._removeAnimations(); if (dontforce) { this._animateOut(this._settings.get_double('animation-time'), 0, false); } else { if (Main.overview._shown && Main.overview.viewSelector._activePage == Main.overview.viewSelector._workspacesPage) { this._animateOut(this._settings.get_double('animation-time'), 0, false); } else { this._animateOut(this._settings.get_double('animation-time'), 0, true); } } delay = this._settings.get_double('animation-time'); } } else { this._removeAnimations(); if (dontforce) { this._animateOut(this._settings.get_double('animation-time'), 0, false); } else { if (Main.overview._shown && Main.overview.viewSelector._activePage == Main.overview.viewSelector._workspacesPage) { this._animateOut(this._settings.get_double('animation-time'), 0, false); } else { this._animateOut(this._settings.get_double('animation-time'), 0, true); } } delay = this._settings.get_double('animation-time'); } } }; Signals.addSignalMethods(DockedWorkspaces.prototype);