/** * V-Shell (Vertical Workspaces) * panel.js * * @author GdH * @copyright 2022 - 2025 * @license GPL-3.0 * */ 'use strict'; import Clutter from 'gi://Clutter'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import * as Overview from 'resource:///org/gnome/shell/ui/overview.js'; let Me; let opt; const ANIMATION_TIME = Overview.ANIMATION_TIME; export const PanelModule = class { constructor(me) { Me = me; opt = Me.opt; this._firstActivation = true; this.moduleEnabled = false; this._overrides = null; this._showingOverviewConId = 0; this._hidingOverviewConId = 0; this._styleChangedConId = 0; } cleanGlobals() { Me = null; opt = null; } update(reset) { this.moduleEnabled = opt.get('panelModule'); const conflict = Me.Util.getEnabledExtensions('dash-to-panel').length || Me.Util.getEnabledExtensions('hidetopbar').length; if (conflict && !reset) console.warn(`[${Me.metadata.name}] Warning: "Panel" module disabled due to potential conflict with another extension`); reset = reset || !this.moduleEnabled || conflict; this.moduleEnabled = !reset; // don't touch original code if module disabled if (reset && !this._firstActivation) { this._disableModule(); } else if (!reset) { this._firstActivation = false; this._activateModule(); } if (reset && this._firstActivation) console.debug(' PanelModule - Keeping untouched'); } _activateModule() { if (!this._overrides) this._overrides = new Me.Util.Overrides(); const panelBox = Main.layoutManager.panelBox; panelBox.scale_y = 1; this._setPanelPosition(); this._updateStyleChangedConnection(); if (!opt.PANEL_MODE) { this._updateOverviewConnection(true); this._reparentPanel(false); panelBox.translation_y = 0; Main.panel.opacity = 255; this._setPanelStructs(true); } else if (opt.PANEL_OVERVIEW_ONLY) { if (opt.SHOW_WS_PREVIEW_BG) { this._reparentPanel(true); this._showPanel(true); } else { // if ws preview bg is disabled, panel can stay in uiGroup this._reparentPanel(false); this._showPanel(false); } this._updateOverviewConnection(); // _connectPanel(); } else if (opt.PANEL_DISABLED) { this._updateOverviewConnection(true); this._reparentPanel(false); panelBox.scale_y = 0; // _connectPanel(); } this._setPanelStructs(!opt.PANEL_MODE); Main.layoutManager._updateHotCorners(); Main.overview._overview.controls.layoutManager._updateWorkAreaBox(); this._overrides.addOverride('ActivitiesButton', Main.panel.statusArea.activities, ActivitiesButton); console.debug(' PanelModule - Activated'); } _disableModule() { const reset = true; this._setPanelPosition(reset); this._updateOverviewConnection(reset); this._reparentPanel(false); this._updateStyleChangedConnection(reset); const panelBox = Main.layoutManager.panelBox; panelBox.scale_y = 1; panelBox.translation_y = 0; Main.panel.opacity = 255; this._setPanelStructs(true); if (this._overrides) this._overrides.removeAll(); this._overrides = null; console.debug(' PanelModule - Disabled'); } _setPanelPosition(reset = false) { const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor()); const panelBox = Main.layoutManager.panelBox; const panelHeight = Main.panel.height; // panelBox height can be 0 after shell start if (opt.PANEL_POSITION_TOP || reset) panelBox.set_position(geometry.x, geometry.y); else panelBox.set_position(geometry.x, geometry.y + geometry.height - panelHeight); } _updateStyleChangedConnection(reset = false) { if (reset) { if (this._styleChangedConId) { Main.panel.disconnect(this._styleChangedConId); this._styleChangedConId = 0; } } else if (!this._styleChangedConId) { this._styleChangedConId = Main.panel.connect('style-changed', () => { this._updateStyle(); }); } } _updateStyle() { if (opt.OVERVIEW_MODE2 || !opt.PANEL_OVERVIEW_STYLE) Main.panel.remove_style_pseudo_class('overview'); else if (opt.PANEL_OVERVIEW_ONLY && !opt.OVERVIEW_MODE2) Main.panel.add_style_pseudo_class('overview'); } _updateOverviewConnection(reset = false) { if (reset) { if (this._hidingOverviewConId) { Main.overview.disconnect(this._hidingOverviewConId); this._hidingOverviewConId = 0; } if (this._showingOverviewConId) { Main.overview.disconnect(this._showingOverviewConId); this._showingOverviewConId = 0; } } else { if (!this._hidingOverviewConId) { this._hidingOverviewConId = Main.overview.connect('hiding', () => { this._showPanel(false); }); } if (!this._showingOverviewConId) { this._showingOverviewConId = Main.overview.connect('showing', () => { if (Main.layoutManager._startingUp) return; this._updateStyle(); this._showPanel(true); }); } } } _reparentPanel(reparent = false) { const panel = Main.layoutManager.panelBox; if (reparent && panel.get_parent() === Main.layoutManager.uiGroup && !Main.sessionMode.isLocked) { Main.layoutManager.uiGroup.remove_child(panel); Main.layoutManager.overviewGroup.add_child(panel); } else if ((!reparent || Main.sessionMode.isLocked) && panel.get_parent() === Main.layoutManager.overviewGroup) { Main.layoutManager.overviewGroup.remove_child(panel); // return the panel at default position, panel shouldn't cover objects that should be above Main.layoutManager.uiGroup.insert_child_at_index(panel, 4); } } _setPanelStructs(state) { Main.layoutManager._trackedActors.forEach(a => { if (a.actor === Main.layoutManager.panelBox) a.affectsStruts = state; }); // workaround to force maximized windows to resize after removing affectsStruts // simulation of minimal swipe gesture to the opposite direction // todo - needs better solution!!!!!!!!!!! // const direction = _getAppGridAnimationDirection() === 2 ? 1 : -1; // Main.overview._swipeTracker._beginTouchSwipe(null, global.get_current_time(), 1, 1); // Main.overview._swipeTracker._updateGesture(null, global.get_current_time(), direction, 1); // GLib.timeout_add(0, 50, () => Main.overview._swipeTracker._endGesture(global.get_current_time(), 1, true));*/ } _showPanel(show = true) { const panelBox = Main.layoutManager.panelBox; const panelHeight = Main.panel.height; const overviewGroup = Main.layoutManager.overviewGroup; if (panelBox.get_parent() === overviewGroup) { if (opt.OVERVIEW_MODE2) overviewGroup.set_child_above_sibling(panelBox, null); else overviewGroup.set_child_below_sibling(panelBox, Main.overview._overview); } if (opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2 && !Main.layoutManager.panelBox.translation_y) return; if (show) { panelBox.translation_y = opt.PANEL_POSITION_TOP ? -panelHeight : panelHeight; Main.panel.opacity = 255; let delay = 0; // Panel animation needs to wait until overview is visible if (opt.DELAY_OVERVIEW_ANIMATION) delay = global.display.get_tab_list(0, null).length * opt.DELAY_PER_WINDOW + 50; panelBox.ease({ delay, duration: ANIMATION_TIME, translation_y: 0, onComplete: () => { this._setPanelStructs(!opt.PANEL_MODE); }, }); } else if (!Main.layoutManager._startingUp) { panelBox.translation_y = 0; panelBox.ease({ duration: ANIMATION_TIME, translation_y: opt.PANEL_POSITION_TOP ? -panelHeight : panelHeight, onComplete: () => { Main.panel.opacity = 0; this._setPanelStructs(!opt.PANEL_MODE); }, }); } } }; const ActivitiesButton = { vfunc_event(event) { if (event.type() === Clutter.EventType.TOUCH_END || event.type() === Clutter.EventType.BUTTON_RELEASE) { if (Main.overview.shouldToggleByCornerOrButton()) { if (event.get_button() === Clutter.BUTTON_SECONDARY && !Main.overview.dash.showAppsButton.checked) { Main.overview.show(2); Main.overview.dash.showAppsButton.checked = true; } else { Main.overview.toggle(); } } } return Main.wm.handleWorkspaceScroll(event); }, };