/* * 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. */ const Me = imports.misc.extensionUtils.getCurrentExtension(); const Overview = Me.imports.overview; const Panel = Me.imports.panel; const PanelSettings = Me.imports.panelSettings; const Proximity = Me.imports.proximity; const Taskbar = Me.imports.taskbar; const Utils = Me.imports.utils; const Config = imports.misc.config; const Lang = imports.lang; const Gi = imports._gi; const GLib = imports.gi.GLib; const Clutter = imports.gi.Clutter; const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; const St = imports.gi.St; const AppDisplay = imports.ui.appDisplay; const BoxPointer = imports.ui.boxpointer; const Dash = imports.ui.dash; const IconGrid = imports.ui.iconGrid; const LookingGlass = imports.ui.lookingGlass; const Main = imports.ui.main; const PanelMenu = imports.ui.panelMenu; const Layout = imports.ui.layout; const WM = imports.ui.windowManager; const WorkspacesView = imports.ui.workspacesView; var dtpPanelManager = Utils.defineClass({ Name: 'DashToPanel.PanelManager', _init: function() { this.overview = new Overview.dtpOverview(); this.panelsElementPositions = {}; this._saveMonitors(); Utils.getAppDisplayViews().forEach(v => { Utils.wrapActor(v.view); Utils.wrapActor(v.view._grid); }); }, enable: function(reset) { let dtpPrimaryIndex = Me.settings.get_int('primary-monitor'); this.dtpPrimaryMonitor = Main.layoutManager.monitors[dtpPrimaryIndex] || Main.layoutManager.primaryMonitor; this.proximityManager = new Proximity.ProximityManager(); this._oldGetShowAppsButton = imports.ui.main.overview.dash.showAppsButton; Utils.wrapActor(Main.panel); Utils.wrapActor(Main.overview.dash || 0); this.primaryPanel = this._createPanel(this.dtpPrimaryMonitor, Me.settings.get_boolean('stockgs-keep-top-panel')); this.allPanels = [ this.primaryPanel ]; this.overview.enable(this.primaryPanel); if (Me.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.allPanels.forEach(p => { let panelPosition = p.getPosition(); let leftOrRight = (panelPosition == St.Side.LEFT || panelPosition == St.Side.RIGHT); p.panelBox.set_size( leftOrRight ? -1 : p.geom.w + p.geom.lrPadding, leftOrRight ? p.geom.h + p.geom.tbPadding : -1 ); this._findPanelMenuButtons(p.panelBox).forEach(pmb => this._adjustPanelMenuButton(pmb, p.monitor, panelPosition)); p.taskbar.iconAnimator.start(); }); //in 3.32, BoxPointer now inherits St.Widget if (BoxPointer.BoxPointer.prototype.vfunc_get_preferred_height) { let panelManager = this; Utils.hookVfunc(BoxPointer.BoxPointer.prototype, 'get_preferred_height', function(forWidth) { let alloc = { min_size: 0, natural_size: 0 }; [alloc.min_size, alloc.natural_size] = this.vfunc_get_preferred_height(forWidth); return panelManager._getBoxPointerPreferredHeight(this, alloc); }); } this._updatePanelElementPositions(); this.setFocusedMonitor(this.dtpPrimaryMonitor); if (this.primaryPanel.checkIfVertical()) { Main.wm._getPositionForDirection = newGetPositionForDirection; } if (reset) return; if (Config.PACKAGE_VERSION > '3.35.1') { this._oldDoSpringAnimation = AppDisplay.BaseAppView.prototype._doSpringAnimation; AppDisplay.BaseAppView.prototype._doSpringAnimation = newDoSpringAnimation; } 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 = Lang.bind(Main.layoutManager, newUpdateHotCorners); Main.layoutManager._updateHotCorners(); this._forceHotCornerId = Me.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()); } Main.overview.getShowAppsButton = this._newGetShowAppsButton.bind(this); this._needsDashItemContainerAllocate = !Dash.DashItemContainer.prototype.hasOwnProperty('vfunc_allocate'); if (this._needsDashItemContainerAllocate) { Utils.hookVfunc(Dash.DashItemContainer.prototype, 'allocate', this._newDashItemContainerAllocate); } LookingGlass.LookingGlass.prototype._oldResize = LookingGlass.LookingGlass.prototype._resize; LookingGlass.LookingGlass.prototype._resize = _newLookingGlassResize; LookingGlass.LookingGlass.prototype._oldOpen = LookingGlass.LookingGlass.prototype.open; LookingGlass.LookingGlass.prototype.open = _newLookingGlassOpen; this._signalsHandler = new Utils.GlobalSignalsHandler(); if (Config.PACKAGE_VERSION > '3.35.9') { let currentAppsView; this._oldAnimateIconPosition = IconGrid.animateIconPosition; IconGrid.animateIconPosition = newAnimateIconPosition.bind(this); this._signalsHandler.add( [ Utils.DisplayWrapper.getScreen(), 'window-entered-monitor', () => this._needsIconAllocate = 1 ] ); Utils.getAppDisplayViews().forEach(v => { if (!v.control || v.control.has_style_pseudo_class('checked')) { currentAppsView = v; } if (v.control) { this._signalsHandler.add( [ v.control, 'clicked', () => { this._needsIconAllocate = currentAppsView != v; currentAppsView = v; } ] ); } this._signalsHandler.add( [ v.view, 'notify::visible', () => this._needsIconAllocate = !(currentAppsView != v && !v.view.visible) ], [ v.view._grid, 'animation-done', () => this._needsIconAllocate = 0 ] ); }); } //listen settings this._signalsHandler.add( [ Me.settings, [ 'changed::primary-monitor', 'changed::multi-monitors', 'changed::isolate-monitors', 'changed::panel-positions', 'changed::panel-lengths', 'changed::panel-anchors', 'changed::stockgs-keep-top-panel' ], () => this._reset() ], [ Me.settings, 'changed::panel-element-positions', () => this._updatePanelElementPositions() ], [ Me.settings, 'changed::intellihide-key-toggle-text', () => this._setKeyBindings(true) ], [ Utils.DisplayWrapper.getMonitorManager(), 'monitors-changed', () => { if (Main.layoutManager.primaryMonitor) { this._saveMonitors(); this._reset(); } } ] ); Panel.panelBoxes.forEach(c => this._signalsHandler.add( [Main.panel[c], 'actor-added', (parent, child) => this._adjustPanelMenuButton(this._getPanelMenuButton(child), this.primaryPanel.monitor, this.primaryPanel.getPosition())] )); this._setKeyBindings(true); // keep GS overview.js from blowing away custom panel styles if(!Me.settings.get_boolean('stockgs-keep-top-panel')) Object.defineProperty(Main.panel, "style", {configurable: true, set: function(v) {}}); }, disable: function(reset) { this.overview.disable(); this.proximityManager.destroy(); this.allPanels.forEach(p => { p.taskbar.iconAnimator.pause(); this._findPanelMenuButtons(p.panelBox).forEach(pmb => { if (pmb.menu._boxPointer._dtpGetPreferredHeightId) { pmb.menu._boxPointer._container.disconnect(pmb.menu._boxPointer._dtpGetPreferredHeightId); } pmb.menu._boxPointer.sourceActor = pmb.menu._boxPointer._dtpSourceActor; delete pmb.menu._boxPointer._dtpSourceActor; pmb.menu._boxPointer._userArrowSide = St.Side.TOP; }) this._removePanelBarriers(p); p.disable(); let clipContainer = p.panelBox.get_parent(); Main.layoutManager._untrackActor(p.panelBox); Main.layoutManager.removeChrome(clipContainer); if (p.isStandalone) { p.panelBox.destroy(); } else { p.panelBox.remove_child(p); p.remove_child(p.panel.actor); p.panelBox.add(p.panel.actor); p.panelBox.set_position(clipContainer.x, clipContainer.y); clipContainer.remove_child(p.panelBox); Main.layoutManager.addChrome(p.panelBox, { affectsStruts: true, trackFullscreen: true }); } }); if (BoxPointer.BoxPointer.prototype.vfunc_get_preferred_height) { Utils.hookVfunc(BoxPointer.BoxPointer.prototype, 'get_preferred_height', BoxPointer.BoxPointer.prototype.vfunc_get_preferred_height); } delete Main.wm._getPositionForDirection; if (Main.layoutManager.primaryMonitor) { Main.layoutManager.panelBox.set_position(Main.layoutManager.primaryMonitor.x, Main.layoutManager.primaryMonitor.y); Main.layoutManager.panelBox.set_size(Main.layoutManager.primaryMonitor.width, -1); } if (reset) return; this._setKeyBindings(false); this._signalsHandler.destroy(); Main.layoutManager._updateHotCorners = this._oldUpdateHotCorners; Main.layoutManager._updateHotCorners(); Me.settings.disconnect(this._forceHotCornerId); if (this._enableHotCornersId) { Main.layoutManager._interfaceSettings.disconnect(this._enableHotCornersId); } Main.layoutManager._updatePanelBarrier = this._oldUpdatePanelBarrier; Main.layoutManager._updatePanelBarrier(); Utils.getPanelGhost().set_size(-1, -1); if (this._oldDoSpringAnimation) { AppDisplay.BaseAppView.prototype._doSpringAnimation = this._oldDoSpringAnimation; } if (this._oldAnimateIconPosition) { IconGrid.animateIconPosition = this._oldAnimateIconPosition; } LookingGlass.LookingGlass.prototype._resize = LookingGlass.LookingGlass.prototype._oldResize; delete LookingGlass.LookingGlass.prototype._oldResize; LookingGlass.LookingGlass.prototype.open = LookingGlass.LookingGlass.prototype._oldOpen; delete LookingGlass.LookingGlass.prototype._oldOpen delete Main.panel.style; }, setFocusedMonitor: function(monitor, ignoreRelayout) { // todo show overview on non primary monitor is not working right now on gnome40 // this._needsIconAllocate = 1; // if (!this.checkIfFocusedMonitor(monitor)) { // Main.overview._overview._controls._workspacesDisplay._primaryIndex = monitor.index; // Main.overview._overview.clear_constraints(); // Main.overview._overview.add_constraint(new Layout.MonitorConstraint({ index: monitor.index })); // } }, _saveMonitors: function() { //Mutter meta_monitor_manager_get_primary_monitor (global.display.get_primary_monitor()) doesn't return the same //monitor as GDK gdk_screen_get_primary_monitor (imports.gi.Gdk.Screen.get_default().get_primary_monitor()). //Since the Mutter function is what's used in gnome-shell and we can't access it from the settings dialog, store //the monitors information in a setting so we can use the same monitor indexes as the ones in gnome-shell let primaryIndex = Main.layoutManager.primaryIndex; let monitors = [primaryIndex]; Main.layoutManager.monitors.filter(m => m.index != primaryIndex).forEach(m => monitors.push(m.index)); Me.settings.set_value('available-monitors', new GLib.Variant('ai', monitors)); }, checkIfFocusedMonitor: function(monitor) { return Main.overview._overview._controls._workspacesDisplay._primaryIndex == monitor.index; }, _createPanel: function(monitor, isStandalone) { let panelBox; let panel; let clipContainer = new Clutter.Actor(); if (isStandalone) { panelBox = new St.BoxLayout({ name: 'panelBox' }); } else { panelBox = Main.layoutManager.panelBox; Main.layoutManager._untrackActor(panelBox); panelBox.remove_child(Main.panel.actor); Main.layoutManager.removeChrome(panelBox); } Main.layoutManager.addChrome(clipContainer, { affectsInputRegion: false }); clipContainer.add_child(panelBox); Main.layoutManager.trackChrome(panelBox, { trackFullscreen: true, affectsStruts: true, affectsInputRegion: true }); panel = new Panel.dtpPanel(this, monitor, panelBox, isStandalone); panelBox.add(panel); panel.enable(); panelBox.visible = true; if (monitor.inFullscreen) { panelBox.hide(); } panelBox.set_position(0, 0); return panel; }, _reset: function() { this.disable(true); this.allPanels = []; this.enable(true); }, _updatePanelElementPositions: function() { this.panelsElementPositions = PanelSettings.getSettingsJson(Me.settings, 'panel-element-positions'); this.allPanels.forEach(p => p.updateElementPositions()); }, _adjustPanelMenuButton: function(button, monitor, arrowSide) { if (button) { Utils.wrapActor(button); button.menu._boxPointer._dtpSourceActor = button.menu._boxPointer.sourceActor; button.menu._boxPointer.sourceActor = button.actor; 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: function(boxPointer, alloc, monitor) { if (boxPointer._dtpInPanel && boxPointer.sourceActor && Me.settings.get_boolean('intellihide')) { monitor = monitor || Main.layoutManager.findMonitorForActor(boxPointer.sourceActor); let panel = Utils.find(global.dashToPanel.panels, p => p.monitor == monitor); let excess = alloc.natural_size + panel.dtpSize + 10 - monitor.height; // 10 is arbitrary if (excess > 0) { alloc.natural_size -= excess; } } return [alloc.min_size, alloc.natural_size]; }, _findPanelMenuButtons: function(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: function(panel) { if (panel.isStandalone && panel._rightPanelBarrier) { panel._rightPanelBarrier.destroy(); } if (panel._leftPanelBarrier) { panel._leftPanelBarrier.destroy(); delete panel._leftPanelBarrier; } }, _getPanelMenuButton: function(obj) { return obj._delegate && obj._delegate instanceof PanelMenu.Button ? obj._delegate : 0; }, _setKeyBindings: function(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, Me.settings, keys[k], Shell.ActionMode.NORMAL); } }); }, _newGetShowAppsButton: function() { let focusedMonitorIndex = Utils.findIndex(this.allPanels, p => this.checkIfFocusedMonitor(p.monitor)); return this.allPanels[focusedMonitorIndex].taskbar.showAppsButton; } }); // This class drives long-running icon animations, to keep them running in sync // with each other. var IconAnimator = Utils.defineClass({ Name: 'DashToPanel.IconAnimator', _init: function(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: function() { 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: function() { if (this._started && this._count > 0) { this._timeline.stop(); } this._started = false; }, start: function() { if (!this._started && this._count > 0) { this._timeline.start(); } this._started = true; }, addAnimation: function(target, name) { const targetDestroyId = target.connect('destroy', () => this.removeAnimation(target, name)); this._animations[name].push({ target: target, targetDestroyId: targetDestroyId }); if (this._started && this._count === 0) { this._timeline.start(); } this._count++; }, removeAnimation: function(target, name) { const pairs = this._animations[name]; for (let i = 0, iMax = pairs.length; i < iMax; i++) { const pair = pairs[i]; if (pair.target === target) { target.disconnect(pair.targetDestroyId); pairs.splice(i, 1); this._count--; if (this._started && this._count === 0) { this._timeline.stop(); } return; } } } }); function newGetPositionForDirection(direction, fromWs, toWs) { let [xDest, yDest] = WM.WindowManager.prototype._getPositionForDirection(direction, fromWs, toWs); if (direction == Meta.MotionDirection.UP || direction == Meta.MotionDirection.UP_LEFT || direction == Meta.MotionDirection.UP_RIGHT) { yDest -= Main.panel.height; } else if (direction != Meta.MotionDirection.LEFT && direction != Meta.MotionDirection.RIGHT) { yDest += Main.panel.height; } return [xDest, yDest]; } function newDoSpringAnimation(animationDirection) { this._grid.opacity = 255; this._grid.animateSpring(animationDirection, Main.overview.getShowAppsButton()); } function newAnimateIconPosition(icon, box, flags, nChangedIcons) { if (this._needsIconAllocate) { Utils.allocate(icon, box, flags); return; } return this._oldAnimateIconPosition(icon, box, flags, nChangedIcons);; } 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.getPosition() : St.Side.BOTTOM; let panelTopLeft = panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT; let monitor = this.monitors[i]; let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x; let cornerY = monitor.y; let haveTopLeftCorner = true; // If the panel is on the bottom, unless this is explicitly forced, don't add a topleft // hot corner unless it is actually a top left panel. Otherwise, it stops the mouse // as you are dragging across. In the future, maybe we will automatically move the // hotcorner to the bottom when the panel is positioned at the bottom if (i != this.primaryIndex || (!panelTopLeft && !Me.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 => corner.__proto__.setBarrierSize.call(corner, Math.min(size, 32)); corner.setBarrierSize(panel ? panel.dtpSize : 32); this.hotCorners.push(corner); } else { this.hotCorners.push(null); } } this.emit('hot-corners-changed'); } 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.checkIfVertical()) { barriers._rightPanelBarrier.push(panel.monitor.y + panel.monitor.height, Meta.BarrierDirection.POSITIVE_Y); barriers._leftPanelBarrier.push(panel.monitor.y, Meta.BarrierDirection.NEGATIVE_Y); } else { barriers._rightPanelBarrier.push(panel.monitor.x + panel.monitor.width, Meta.BarrierDirection.NEGATIVE_X); barriers._leftPanelBarrier.push(panel.monitor.x, Meta.BarrierDirection.POSITIVE_X); } switch (panel.getPosition()) { //values are initialized as St.Side.TOP case St.Side.BOTTOM: fixed1 = panel.monitor.y + panel.monitor.height - barrierSize; fixed2 = panel.monitor.y + panel.monitor.height; break; case St.Side.LEFT: fixed1 = panel.monitor.x; fixed2 = panel.monitor.x + barrierSize; break; case St.Side.RIGHT: fixed1 = panel.monitor.x + panel.monitor.width; fixed2 = panel.monitor.x + panel.monitor.width - barrierSize; 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 = { display: global.display, 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.getPosition() == St.Side.TOP ? primaryMonitorPanel.dtpSize + 8 : 32; this._oldResize(); Utils.wrapActor(this); Utils.wrapActor(this._objInspector); this._hiddenY = Main.layoutManager.primaryMonitor.y + topOffset - this.actor.height; this._targetY = this._hiddenY + this.actor.height; this.actor.y = this._hiddenY; this._objInspector.actor.set_position(this.actor.x + Math.floor(this.actor.width * 0.1), this._targetY + Math.floor(this.actor.height * 0.1)); } function _newLookingGlassOpen() { if (this._open) return; this._resize(); this._oldOpen(); }