/* * This file is part of the Dash-To-Panel extension for Gnome 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Credits: * This file is based on code from the Dash to Dock extension by micheleg * and code from the Taskbar extension by Zorin OS * * Code to re-anchor the panel was taken from Thoma5 BottomPanel: * https://github.com/Thoma5/gnome-shell-extension-bottompanel * * Pattern for moving clock based on Frippery Move Clock by R M Yorston * http://frippery.org/extensions/ * * Some code was also adapted from the upstream Gnome Shell source code. */ import * as Overview from './overview.js' import * as Panel from './panel.js' import * as PanelSettings from './panelSettings.js' import * as Proximity from './proximity.js' import * as Utils from './utils.js' import * as DesktopIconsIntegration from './desktopIconsIntegration.js' import { DTP_EXTENSION, SETTINGS, tracker } from './extension.js' import GLib from 'gi://GLib' import GObject from 'gi://GObject' import Clutter from 'gi://Clutter' import Meta from 'gi://Meta' import Shell from 'gi://Shell' import St from 'gi://St' import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js' import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js' import * as LookingGlass from 'resource:///org/gnome/shell/ui/lookingGlass.js' import * as Main from 'resource:///org/gnome/shell/ui/main.js' import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js' import { NotificationsMonitor } from './notificationsMonitor.js' import { Workspace } from 'resource:///org/gnome/shell/ui/workspace.js' import * as Layout from 'resource:///org/gnome/shell/ui/layout.js' import { InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js' import { SecondaryMonitorDisplay, WorkspacesView, } from 'resource:///org/gnome/shell/ui/workspacesView.js' export const PanelManager = class { constructor() { this.overview = new Overview.Overview(this) this._injectionManager = new InjectionManager() } enable(reset) { let dtpPrimaryIndex = PanelSettings.getPrimaryIndex( SETTINGS.get_string('primary-monitor'), ) this.allPanels = [] this.dtpPrimaryMonitor = Main.layoutManager.monitors[dtpPrimaryIndex] || Main.layoutManager.primaryMonitor this.proximityManager = new Proximity.ProximityManager() // g-s version 49 switched to clutter gestures if (!AppDisplay.AppIcon.prototype._removeMenuTimeout) AppDisplay.AppIcon.prototype._setPopupTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout = this._emptyFunc Main.layoutManager.findIndexForActor = (actor) => '_dtpIndex' in actor ? actor._dtpIndex : Layout.LayoutManager.prototype.findIndexForActor.call( Main.layoutManager, actor, ) if (this.dtpPrimaryMonitor) { this.primaryPanel = this._createPanel( this.dtpPrimaryMonitor, SETTINGS.get_boolean('stockgs-keep-top-panel'), ) this.allPanels.push(this.primaryPanel) this.overview.enable(this.primaryPanel) this.setFocusedMonitor(this.dtpPrimaryMonitor) } if (SETTINGS.get_boolean('multi-monitors')) { Main.layoutManager.monitors .filter((m) => m != this.dtpPrimaryMonitor) .forEach((m) => { this.allPanels.push(this._createPanel(m, true)) }) } global.dashToPanel.panels = this.allPanels global.dashToPanel.emit('panels-created') this._setDesktopIconsMargins() this._updatePanelElementPositions() if (reset) return this.notificationsMonitor = new NotificationsMonitor() this._desktopIconsUsableArea = new DesktopIconsIntegration.DesktopIconsUsableAreaClass() this._oldUpdatePanelBarrier = Main.layoutManager._updatePanelBarrier Main.layoutManager._updatePanelBarrier = (panel) => { let panelUpdates = panel ? [panel] : this.allPanels panelUpdates.forEach((p) => newUpdatePanelBarrier.call(Main.layoutManager, p), ) } Main.layoutManager._updatePanelBarrier() this._oldUpdateHotCorners = Main.layoutManager._updateHotCorners Main.layoutManager._updateHotCorners = newUpdateHotCorners.bind( Main.layoutManager, ) Main.layoutManager._updateHotCorners() this._forceHotCornerId = SETTINGS.connect( 'changed::stockgs-force-hotcorner', () => Main.layoutManager._updateHotCorners(), ) if (Main.layoutManager._interfaceSettings) { this._enableHotCornersId = Main.layoutManager._interfaceSettings.connect( 'changed::enable-hot-corners', () => Main.layoutManager._updateHotCorners(), ) } this._oldUpdateWorkspacesViews = Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = this._newUpdateWorkspacesViews.bind( Main.overview._overview._controls._workspacesDisplay, ) this._oldSetPrimaryWorkspaceVisible = Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = this._newSetPrimaryWorkspaceVisible.bind( Main.overview._overview._controls._workspacesDisplay, ) let panelManager = this this._injectionManager.overrideMethod( BoxPointer.BoxPointer.prototype, 'vfunc_get_preferred_height', () => function (forWidth) { let alloc = { min_size: 0, natural_size: 0 } ;[alloc.min_size, alloc.natural_size] = this.vfunc_get_preferred_height(forWidth) return panelManager._getBoxPointerPreferredHeight(this, alloc) }, ) let activitiesChild = Main.panel.statusArea.activities.get_first_child() if (activitiesChild?.constructor.name == 'WorkspaceIndicators') { this._injectionManager.overrideMethod( Object.getPrototypeOf( // WorkspaceDot in activities button activitiesChild.get_first_child(), ), 'vfunc_get_preferred_width', (get_preferred_width) => function (forHeight) { return Utils.getBoxLayoutVertical(this.get_parent()) ? [0, forHeight] : get_preferred_width.call(this, forHeight) }, ) } LookingGlass.LookingGlass.prototype._oldResize = LookingGlass.LookingGlass.prototype._resize LookingGlass.LookingGlass.prototype._resize = _newLookingGlassResize LookingGlass.LookingGlass.prototype._oldOpen = LookingGlass.LookingGlass.prototype.open LookingGlass.LookingGlass.prototype.open = _newLookingGlassOpen Main.messageTray._bannerBin.ease = (params) => { if (params.y === 0) { let panelOnPrimary = this.allPanels.find( (p) => p.monitor == Main.layoutManager.primaryMonitor, ) if ( panelOnPrimary && panelOnPrimary.intellihide?.enabled && panelOnPrimary.geom.position == St.Side.TOP && panelOnPrimary.panelBox.visible ) params.y += panelOnPrimary.geom.outerSize } Object.getPrototypeOf(Main.messageTray._bannerBin).ease.call( Main.messageTray._bannerBin, params, ) } this._signalsHandler = new Utils.GlobalSignalsHandler() //listen settings this._signalsHandler.add( [ SETTINGS, 'changed::global-border-radius', () => DTP_EXTENSION.resetGlobalStyles(), ], [ SETTINGS, [ 'changed::primary-monitor', 'changed::multi-monitors', 'changed::isolate-monitors', 'changed::panel-positions', 'changed::panel-lengths', 'changed::panel-anchors', 'changed::stockgs-keep-top-panel', ], (settings, settingChanged) => { PanelSettings.clearCache(settingChanged) this._reset() }, ], [ SETTINGS, 'changed::panel-element-positions', () => { PanelSettings.clearCache('panel-element-positions') this._updatePanelElementPositions() }, ], [ SETTINGS, 'changed::intellihide-key-toggle-text', () => this._setKeyBindings(true), ], [ SETTINGS, 'changed::panel-sizes', () => { GLib.idle_add(GLib.PRIORITY_LOW, () => { this._setDesktopIconsMargins() return GLib.SOURCE_REMOVE }) }, ], [ Utils.DisplayWrapper.getMonitorManager(), 'monitors-changed', async () => { if (Main.layoutManager.primaryMonitor) { await PanelSettings.setMonitorsInfo(SETTINGS).catch((e) => console.log(e), ) this._reset() } }, ], ) Panel.panelBoxes.forEach((c) => this._signalsHandler.add([ Main.panel[c], 'child-added', (parent, child) => { this.primaryPanel && child instanceof St.Bin && this._adjustPanelMenuButton( this._getPanelMenuButton(child.get_first_child()), this.primaryPanel.monitor, this.primaryPanel.geom.position, ) }, ]), ) this._setKeyBindings(true) // keep GS overview.js from blowing away custom panel styles if (!SETTINGS.get_boolean('stockgs-keep-top-panel')) Object.defineProperty(Main.panel, 'style', { configurable: true, set() {}, }) } disable(reset) { this.primaryPanel && this.overview.disable() this.proximityManager.destroy() if (AppDisplay.AppIcon.prototype._removeMenuTimeout == this._emptyFunc) { delete AppDisplay.AppIcon.prototype._setPopupTimeout delete AppDisplay.AppIcon.prototype._removeMenuTimeout } this.allPanels.forEach((p) => { p.taskbar.iconAnimator.pause() this._findPanelMenuButtons(p.panelBox).forEach((pmb) => { if (pmb.menu._boxPointer._dtpGetPreferredHeightId) { pmb.menu._boxPointer._container.disconnect( pmb.menu._boxPointer._dtpGetPreferredHeightId, ) } pmb.menu._boxPointer.sourceActor = pmb.menu._boxPointer._dtpSourceActor delete pmb.menu._boxPointer._dtpSourceActor pmb.menu._boxPointer._userArrowSide = St.Side.TOP }) this._removePanelBarriers(p) p.disable() Main.layoutManager._untrackActor(p) Main.layoutManager._untrackActor(p.panelBox) if (p.isStandalone) { p.panelBox.destroy() } else { p.panelBox.remove_child(p) p.remove_child(p.panel) p.panelBox.add_child(p.panel) p.panelBox.set_position(p.clipContainer.x, p.clipContainer.y) delete p.panelBox._dtpIndex p.clipContainer.remove_child(p.panelBox) Main.layoutManager.addChrome(p.panelBox, { affectsStruts: true, trackFullscreen: true, }) } Main.layoutManager.removeChrome(p.clipContainer) }) if (Main.layoutManager.primaryMonitor) { Main.layoutManager.panelBox.set_position( Main.layoutManager.primaryMonitor.x, Main.layoutManager.primaryMonitor.y, ) Main.layoutManager.panelBox.set_size( Main.layoutManager.primaryMonitor.width, -1, ) } if (reset) return this._injectionManager.clear() this._setKeyBindings(false) this.notificationsMonitor.destroy() this._signalsHandler.destroy() Main.layoutManager._updateHotCorners = this._oldUpdateHotCorners Main.layoutManager._updateHotCorners() delete Main.layoutManager.findIndexForActor SETTINGS.disconnect(this._forceHotCornerId) if (this._enableHotCornersId) { Main.layoutManager._interfaceSettings.disconnect(this._enableHotCornersId) } Main.layoutManager._updatePanelBarrier = this._oldUpdatePanelBarrier Main.layoutManager._updatePanelBarrier() Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = this._oldUpdateWorkspacesViews Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = this._oldSetPrimaryWorkspaceVisible LookingGlass.LookingGlass.prototype._resize = LookingGlass.LookingGlass.prototype._oldResize delete LookingGlass.LookingGlass.prototype._oldResize LookingGlass.LookingGlass.prototype.open = LookingGlass.LookingGlass.prototype._oldOpen delete LookingGlass.LookingGlass.prototype._oldOpen delete Main.messageTray._bannerBin.ease delete Main.panel.style this._desktopIconsUsableArea.destroy() this._desktopIconsUsableArea = null } _emptyFunc() {} _setDesktopIconsMargins() { this._desktopIconsUsableArea?.resetMargins() this.allPanels.forEach((p) => { switch (p.geom.position) { case St.Side.TOP: this._desktopIconsUsableArea?.setMargins( p.monitor.index, p.geom.outerSize, 0, 0, 0, ) break case St.Side.BOTTOM: this._desktopIconsUsableArea?.setMargins( p.monitor.index, 0, p.geom.outerSize, 0, 0, ) break case St.Side.LEFT: this._desktopIconsUsableArea?.setMargins( p.monitor.index, 0, 0, p.geom.outerSize, 0, ) break case St.Side.RIGHT: this._desktopIconsUsableArea?.setMargins( p.monitor.index, 0, 0, 0, p.geom.outerSize, ) break } }) } setFocusedMonitor(monitor) { this.focusedMonitorPanel = this.allPanels.find((p) => p.monitor == monitor) if (!this.checkIfFocusedMonitor(monitor)) { Main.overview._overview.clear_constraints() Main.overview._overview.add_constraint( new Layout.MonitorConstraint({ index: monitor.index }), ) Main.overview._overview._controls._workspacesDisplay._primaryIndex = monitor.index // https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2395 // The overview allocation used to calculate its workarea based on the monitor where the overview // was displayed, but it got changed back to always use the primary monitor. So now, temporarily assign // the primary monitor to dtp focused monitor while recalculating the overview workarea Main.layoutManager.primaryMonitor = monitor Main.overview._overview._controls.layout_manager._updateWorkAreaBox() Main.layoutManager.primaryMonitor = Main.layoutManager.monitors[Main.layoutManager.primaryIndex] } } showFocusedAppInOverview(app, keepOverviewOpen) { if (app == this.focusedApp) { if (!keepOverviewOpen && Main.overview._shown) Main.overview.hide() return } let isolateWorkspaces = SETTINGS.get_boolean('isolate-workspaces') let isolateMonitors = SETTINGS.get_boolean('isolate-monitors') this.focusedApp = app if (!this._signalsHandler.hasLabel('overview-spread')) { let hasWorkspaces = Main.sessionMode.hasWorkspaces let maybeDisableWorkspacesClick = () => { if (!isolateWorkspaces) Utils.getOverviewWorkspaces().forEach( (w) => (w._container.get_actions()[0].enabled = false), ) } let isIncludedWindow = (metaWindow) => !this.focusedApp || tracker.get_window_app(metaWindow) == this.focusedApp Main.sessionMode.hasWorkspaces = isolateWorkspaces maybeDisableWorkspacesClick() Workspace.prototype._oldIsMyWindow = Workspace.prototype._isMyWindow Workspace.prototype._isMyWindow = function (metaWindow) { if (!metaWindow) return false return ( isIncludedWindow(metaWindow) && (this.metaWorkspace === null || (!isolateWorkspaces && this.metaWorkspace.active) || (isolateWorkspaces && metaWindow.located_on_workspace(this.metaWorkspace))) && (!isolateMonitors || metaWindow.get_monitor() === this.monitorIndex) ) } this.focusedWorkspace = !isolateWorkspaces ? Utils.getCurrentWorkspace() : null this._signalsHandler.addWithLabel( 'overview-spread', [Main.overview, 'showing', maybeDisableWorkspacesClick], [ Main.overview, 'hidden', () => { Utils.getCurrentWorkspace() .list_windows() .forEach((w) => { if ( !w.minimized && !w.customJS_ding && global.display.focus_window != w && tracker.get_window_app(w) != this.focusedApp ) { let window = w.get_compositor_private() ;(window.get_first_child() || window).opacity = 0 Utils.animateWindowOpacity(window, { opacity: 255, time: 0.25, transition: 'easeOutQuad', }) } }) this.focusedApp = null this.focusedWorkspace = null this._signalsHandler.removeWithLabel('overview-spread') Main.sessionMode.hasWorkspaces = hasWorkspaces Workspace.prototype._isMyWindow = Workspace.prototype._oldIsMyWindow delete Workspace.prototype._oldIsMyWindow }, ], ) } if (Main.overview._shown) { Utils.getOverviewWorkspaces().forEach((w) => { let metaWindows = [] let metaWorkspace = w.metaWorkspace || Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() w._container.layout_manager._windows.forEach((info, preview) => preview.destroy(), ) if (this.focusedWorkspace && this.focusedWorkspace == metaWorkspace) metaWindows = Utils.getAllMetaWindows() else if (!this.focusedWorkspace) metaWindows = metaWorkspace.list_windows() metaWindows.forEach((mw) => w._doAddWindow(mw)) }) } else Main.overview.show() } _newSetPrimaryWorkspaceVisible(visible) { if (this._primaryVisible === visible) return this._primaryVisible = visible const primaryIndex = Main.overview._overview._controls._workspacesDisplay._primaryIndex const primaryWorkspace = this._workspacesViews[primaryIndex] if (primaryWorkspace) primaryWorkspace.visible = visible } _newUpdateWorkspacesViews() { for (let i = 0; i < this._workspacesViews.length; i++) this._workspacesViews[i].destroy() this._workspacesViews = [] let monitors = Main.layoutManager.monitors for (let i = 0; i < monitors.length; i++) { let view if (i === this._primaryIndex) { view = new WorkspacesView( i, this._controls, this._scrollAdjustment, this._fitModeAdjustment, this._overviewAdjustment, ) view.visible = this._primaryVisible this.bind_property( 'opacity', view, 'opacity', GObject.BindingFlags.SYNC_CREATE, ) this.add_child(view) } else { // No idea why atm, but we need the import at the top of this file and to use the // full imports ns here, otherwise SecondaryMonitorDisplay can't be used ¯\_(ツ)_/¯ view = new SecondaryMonitorDisplay( i, this._controls, this._scrollAdjustment, this._fitModeAdjustment, this._overviewAdjustment, ) Main.layoutManager.overviewGroup.add_child(view) } this._workspacesViews.push(view) } } checkIfFocusedMonitor(monitor) { return ( Main.overview._overview._controls._workspacesDisplay._primaryIndex == monitor.index ) } _createPanel(monitor, isStandalone) { let panelBox let panel let clipContainer = new Clutter.Actor() if (isStandalone) { panelBox = new Utils.createBoxLayout({ name: 'panelBox' }) } else { panelBox = Main.layoutManager.panelBox Main.layoutManager._untrackActor(panelBox) panelBox.remove_child(Main.panel) Main.layoutManager.removeChrome(panelBox) } Main.layoutManager.addChrome(clipContainer, { affectsInputRegion: false }) clipContainer.add_child(panelBox) panel = new Panel.Panel( this, monitor, clipContainer, panelBox, isStandalone, ) panelBox.add_child(panel) panel.enable() panelBox._dtpIndex = monitor.index panelBox.set_position(0, 0) panelBox.set_width(-1) Main.layoutManager.trackChrome(panel, { affectsInputRegion: true, affectsStruts: false, }) Main.layoutManager.trackChrome(panelBox, { trackFullscreen: true, affectsStruts: true, }) this._findPanelMenuButtons(panelBox).forEach((pmb) => this._adjustPanelMenuButton(pmb, monitor, panel.geom.position), ) panel.taskbar.iconAnimator.start() return panel } _reset() { this.disable(true) this.allPanels = [] this.enable(true) } _updatePanelElementPositions() { this.allPanels.forEach((p) => p.updateElementPositions()) } _adjustPanelMenuButton(button, monitor, arrowSide) { if (button) { button.menu._boxPointer._dtpSourceActor = button.menu._boxPointer.sourceActor button.menu._boxPointer.sourceActor = button button.menu._boxPointer._userArrowSide = arrowSide button.menu._boxPointer._dtpInPanel = 1 if (!button.menu._boxPointer.vfunc_get_preferred_height) { button.menu._boxPointer._dtpGetPreferredHeightId = button.menu._boxPointer._container.connect( 'get-preferred-height', (actor, forWidth, alloc) => { this._getBoxPointerPreferredHeight( button.menu._boxPointer, alloc, monitor, ) }, ) } } } _getBoxPointerPreferredHeight(boxPointer, alloc, monitor) { if ( boxPointer._dtpInPanel && boxPointer.sourceActor && SETTINGS.get_boolean('intellihide') ) { monitor = monitor || Main.layoutManager.findMonitorForActor(boxPointer.sourceActor) let panel = Utils.find( global.dashToPanel.panels, (p) => p.monitor == monitor, ) let excess = alloc.natural_size + panel.outerSize + 10 - monitor.height // 10 is arbitrary if (excess > 0) { alloc.natural_size -= excess } } return [alloc.min_size, alloc.natural_size] } _findPanelMenuButtons(container) { let panelMenuButtons = [] let panelMenuButton let find = (parent) => parent.get_children().forEach((c) => { if ((panelMenuButton = this._getPanelMenuButton(c))) { panelMenuButtons.push(panelMenuButton) } find(c) }) find(container) return panelMenuButtons } _removePanelBarriers(panel) { if (panel.isStandalone && panel._rightPanelBarrier) { panel._rightPanelBarrier.destroy() } if (panel._leftPanelBarrier) { panel._leftPanelBarrier.destroy() delete panel._leftPanelBarrier } } _getPanelMenuButton(obj) { return obj instanceof PanelMenu.Button && obj.menu?._boxPointer ? obj : 0 } _setKeyBindings(enable) { let keys = { 'intellihide-key-toggle': () => this.allPanels.forEach((p) => p.intellihide.toggle()), } Object.keys(keys).forEach((k) => { Utils.removeKeybinding(k) if (enable) { Utils.addKeybinding(k, SETTINGS, keys[k], Shell.ActionMode.NORMAL) } }) } } // This class drives long-running icon animations, to keep them running in sync // with each other. export const IconAnimator = class { constructor(actor) { this._count = 0 this._started = false this._animations = { dance: [], } this._timeline = new Clutter.Timeline({ duration: 3000, repeat_count: -1, }) /* Just use the construction property when no need to support 3.36 */ if (this._timeline.set_actor) this._timeline.set_actor(actor) this._timeline.connect('new-frame', () => { const progress = this._timeline.get_progress() const danceRotation = progress < 1 / 6 ? 15 * Math.sin(progress * 24 * Math.PI) : 0 const dancers = this._animations.dance for (let i = 0, iMax = dancers.length; i < iMax; i++) { dancers[i].target.rotation_angle_z = danceRotation } }) } destroy() { this._timeline.stop() this._timeline = null for (let name in this._animations) { const pairs = this._animations[name] for (let i = 0, iMax = pairs.length; i < iMax; i++) { const pair = pairs[i] pair.target.disconnect(pair.targetDestroyId) } } this._animations = null } pause() { if (this._started && this._count > 0) { this._timeline.stop() } this._started = false } start() { if (!this._started && this._count > 0) { this._timeline.start() } this._started = true } addAnimation(target, name) { // before adding a new animation, remove previous one to only have one running let animationNames = Object.keys(this._animations) for (let i = 0; i < animationNames.length; ++i) { let n = animationNames[i] let currentAnimationPair = this._animations[n].find( (p) => p.target == target, ) if (currentAnimationPair) { if (n == name) return // already have this animation running, nothing else to do this.removeAnimation(currentAnimationPair.target, n) } } const targetDestroyId = target.connect('destroy', () => this.removeAnimation(target, name), ) this._animations[name].push({ target: target, targetDestroyId: targetDestroyId, }) if (this._started && this._count === 0) { this._timeline.start() } this._count++ } removeAnimation(target, name) { const pairs = this._animations[name] for (let i = 0, iMax = pairs.length; i < iMax; i++) { const pair = pairs[i] if (pair.target === target) { target.disconnect(pair.targetDestroyId) pairs.splice(i, 1) this._count-- if (this._started && this._count === 0) { this._timeline.stop() } if (name == 'dance') target.rotation_angle_z = 0 return } } } } function newUpdateHotCorners() { // destroy old hot corners this.hotCorners.forEach(function (corner) { if (corner) corner.destroy() }) this.hotCorners = [] //global.settings is ubuntu specific setting to disable the hot corner (Tweak tool > Top Bar > Activities Overview Hot Corner) //this._interfaceSettings is for the setting to disable the hot corner introduced in gnome-shell 3.34 if ( (global.settings.list_keys().indexOf('enable-hot-corners') >= 0 && !global.settings.get_boolean('enable-hot-corners')) || (this._interfaceSettings && !this._interfaceSettings.get_boolean('enable-hot-corners')) ) { this.emit('hot-corners-changed') return } // build new hot corners for (let i = 0; i < this.monitors.length; i++) { let panel = Utils.find( global.dashToPanel.panels, (p) => p.monitor.index == i, ) let panelPosition = panel ? panel.geom.position : St.Side.BOTTOM let panelTopLeft = panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT let monitor = this.monitors[i] let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x let cornerY = monitor.y let haveTopLeftCorner = true // If the panel is on the bottom, unless this is explicitly forced, don't add a topleft // hot corner unless it is actually a top left panel. Otherwise, it stops the mouse // as you are dragging across. In the future, maybe we will automatically move the // hotcorner to the bottom when the panel is positioned at the bottom if ( i != this.primaryIndex || (!panelTopLeft && !SETTINGS.get_boolean('stockgs-force-hotcorner')) ) { // Check if we have a top left (right for RTL) corner. // I.e. if there is no monitor directly above or to the left(right) let besideX = this._rtl ? monitor.x + 1 : cornerX - 1 let besideY = cornerY let aboveX = cornerX let aboveY = cornerY - 1 for (let j = 0; j < this.monitors.length; j++) { if (i == j) continue let otherMonitor = this.monitors[j] if ( besideX >= otherMonitor.x && besideX < otherMonitor.x + otherMonitor.width && besideY >= otherMonitor.y && besideY < otherMonitor.y + otherMonitor.height ) { haveTopLeftCorner = false break } if ( aboveX >= otherMonitor.x && aboveX < otherMonitor.x + otherMonitor.width && aboveY >= otherMonitor.y && aboveY < otherMonitor.y + otherMonitor.height ) { haveTopLeftCorner = false break } } } if (haveTopLeftCorner) { let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY) corner.setBarrierSize = (size) => Object.getPrototypeOf(corner).setBarrierSize.call( corner, Math.min(size, Panel.GS_PANEL_SIZE), ) corner.setBarrierSize(panel ? panel.geom.innerSize : Panel.GS_PANEL_SIZE) this.hotCorners.push(corner) } else { this.hotCorners.push(null) } } this.emit('hot-corners-changed') } function newUpdatePanelBarrier(panel) { let barriers = { _rightPanelBarrier: [panel.isStandalone ? panel : this], _leftPanelBarrier: [panel], } Object.keys(barriers).forEach((k) => { let obj = barriers[k][0] if (obj[k]) { obj[k].destroy() obj[k] = null } }) if (!this.primaryMonitor || !panel.panelBox.height) { return } let barrierSize = Math.min(10, panel.panelBox.height) let fixed1 = panel.monitor.y let fixed2 = panel.monitor.y + barrierSize if (panel.geom.vertical) { barriers._rightPanelBarrier.push( panel.monitor.y + panel.monitor.height, Meta.BarrierDirection.NEGATIVE_Y, ) barriers._leftPanelBarrier.push( panel.monitor.y, Meta.BarrierDirection.POSITIVE_Y, ) } else { barriers._rightPanelBarrier.push( panel.monitor.x + panel.monitor.width, Meta.BarrierDirection.NEGATIVE_X, ) barriers._leftPanelBarrier.push( panel.monitor.x, Meta.BarrierDirection.POSITIVE_X, ) } switch (panel.geom.position) { //values are initialized as St.Side.TOP case St.Side.BOTTOM: fixed1 = panel.monitor.y + panel.monitor.height - barrierSize fixed2 = panel.monitor.y + panel.monitor.height break case St.Side.LEFT: fixed1 = panel.monitor.x + barrierSize fixed2 = panel.monitor.x break case St.Side.RIGHT: fixed1 = panel.monitor.x + panel.monitor.width - barrierSize fixed2 = panel.monitor.x + panel.monitor.width break } //remove left barrier if it overlaps one of the hotcorners for (let k in this.hotCorners) { let hc = this.hotCorners[k] if ( hc && hc._monitor == panel.monitor && (fixed1 == hc._x || fixed2 == hc._x || fixed1 == hc._y || fixed2 == hc._y) ) { delete barriers._leftPanelBarrier break } } Object.keys(barriers).forEach((k) => { let barrierOptions = { backend: global.backend, directions: barriers[k][2], } barrierOptions[panel.varCoord.c1] = barrierOptions[panel.varCoord.c2] = barriers[k][1] barrierOptions[panel.fixedCoord.c1] = fixed1 barrierOptions[panel.fixedCoord.c2] = fixed2 barriers[k][0][k] = new Meta.Barrier(barrierOptions) }) } function _newLookingGlassResize() { let primaryMonitorPanel = Utils.find( global.dashToPanel.panels, (p) => p.monitor == Main.layoutManager.primaryMonitor, ) let topOffset = primaryMonitorPanel.geom.position == St.Side.TOP ? primaryMonitorPanel.geom.outerSize + (SETTINGS.get_boolean('stockgs-keep-top-panel') ? Main.layoutManager.panelBox.height : 0) + 8 : Panel.GS_PANEL_SIZE this._oldResize() this._hiddenY = Main.layoutManager.primaryMonitor.y + topOffset - this.height this._targetY = this._hiddenY + this.height this.y = this._hiddenY this._objInspector.set_position( this.x + Math.floor(this.width * 0.1), this._targetY + Math.floor(this.height * 0.1), ) } function _newLookingGlassOpen() { if (this._open) return this._resize() this._oldOpen() }