Files
dash-to-panel/panelManager.js

772 lines
29 KiB
JavaScript
Raw Normal View History

2018-09-13 19:46:09 -04:00
/*
* 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 <http://www.gnu.org/licenses/>.
*
* 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.
*/
2023-08-13 03:25:09 +02:00
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 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 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 * as Layout from 'resource:///org/gnome/shell/ui/layout.js';
2023-09-23 07:09:34 -04:00
import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js';
2023-08-13 22:58:15 +02:00
import {SETTINGS} from './extension.js';
2023-08-20 02:57:57 +02:00
import {SecondaryMonitorDisplay, WorkspacesView} from 'resource:///org/gnome/shell/ui/workspacesView.js';
2018-09-13 19:46:09 -04:00
2023-08-13 20:32:43 +02:00
export const PanelManager = class {
2018-09-13 19:46:09 -04:00
2022-04-01 22:32:15 -04:00
constructor() {
2023-08-13 23:47:22 +02:00
this.overview = new Overview.Overview();
2020-05-12 19:00:51 -04:00
this.panelsElementPositions = {};
2023-09-23 07:09:34 -04:00
this._injectionManager = new InjectionManager();
2020-05-12 19:00:51 -04:00
this._saveMonitors();
2022-04-01 22:32:15 -04:00
}
2018-09-13 19:46:09 -04:00
2022-04-01 22:32:15 -04:00
enable(reset) {
2023-08-13 22:58:15 +02:00
let dtpPrimaryIndex = SETTINGS.get_int('primary-monitor');
Configure panel thickness, length, and anchor per-monitor Settings.ui: - Move thickness, length, and anchor from Style tab to Position tab. Group together with position in the same frame. schema: - Remove unpublished panel-length and panel-anchor settings, replacing them with panel-lengths and panel-anchors JSON objects (like panel-positions). - Remove unpublished anchor enum, since panel-anchors is being managed by the extension in JSON, not typed by the schema. - Deprecate panel-size in favour of new panel-sizes JSON, storing per-monitor panel sizes. - Mention that panel-position is deprecated. Introduce panelSettings.js: - Functions to fetch or set panel settings that are stored as JSON. Grown from now-removed getSettingsPositions() in panelPositions.js. prefs.js: - Group together the different UI widget label and value refreshing into method _updateWidgetSettingsForMonitor(). - Change multi-panel behaviour of _setAnchorLabels(). Previously, all panels shared the same anchor setting, and so setAnchorLabels considered all monitors. Now, panels are configured either independently, or sometimes all together; set labels according to orientation(s) of panel(s) being configured. - Omitting preventTop handling in refactored _setPanelPosition() method. Previously, it was written to set the first monitor's panel to Pos.BOTTOM if the user clicked the Top position radio button, if stockgs-keep-top-panel was also set. But the user can't activate the Top button anyway if stockgs is set (likely implemented later). panelManager.js: - Removing panelPositions, as it is not needed any more.
2020-10-27 16:04:19 -06:00
this.allPanels = [];
this.dtpPrimaryMonitor = Main.layoutManager.monitors[dtpPrimaryIndex] || Main.layoutManager.primaryMonitor;
this.proximityManager = new Proximity.ProximityManager();
if (this.dtpPrimaryMonitor) {
2023-08-13 22:58:15 +02:00
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);
}
2018-09-13 19:46:09 -04:00
2023-08-13 22:58:15 +02:00
if (SETTINGS.get_boolean('multi-monitors')) {
Main.layoutManager.monitors.filter(m => m != this.dtpPrimaryMonitor).forEach(m => {
2019-09-06 17:47:34 -04:00
this.allPanels.push(this._createPanel(m, true));
});
}
2019-07-06 19:05:13 -04:00
global.dashToPanel.panels = this.allPanels;
global.dashToPanel.emit('panels-created');
2019-07-06 19:05:13 -04:00
this.allPanels.forEach(p => {
2020-06-13 07:40:33 -04:00
let panelPosition = p.getPosition();
2019-08-31 10:00:36 -04:00
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
2019-08-31 10:00:36 -04:00
);
this._findPanelMenuButtons(p.panelBox).forEach(pmb => this._adjustPanelMenuButton(pmb, p.monitor, panelPosition));
p.taskbar.iconAnimator.start();
});
this._setDesktopIconsMargins();
//in 3.32, BoxPointer now inherits St.Widget
if (BoxPointer.BoxPointer.prototype.vfunc_get_preferred_height) {
let panelManager = this;
2023-09-23 07:09:34 -04:00
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);
});
}
2020-05-12 19:00:51 -04:00
this._updatePanelElementPositions();
if (reset) return;
2018-09-18 13:35:03 -04:00
2022-04-24 09:48:57 -04:00
this._desktopIconsUsableArea = new DesktopIconsIntegration.DesktopIconsUsableAreaClass();
2018-09-18 13:35:03 -04:00
this._oldUpdatePanelBarrier = Main.layoutManager._updatePanelBarrier;
Main.layoutManager._updatePanelBarrier = (panel) => {
let panelUpdates = panel ? [panel] : this.allPanels;
2019-09-03 21:46:10 -04:00
panelUpdates.forEach(p => newUpdatePanelBarrier.call(Main.layoutManager, p));
};
2018-09-18 13:35:03 -04:00
Main.layoutManager._updatePanelBarrier();
this._oldUpdateHotCorners = Main.layoutManager._updateHotCorners;
2022-04-04 13:25:45 -04:00
Main.layoutManager._updateHotCorners = newUpdateHotCorners.bind(Main.layoutManager);
2018-09-18 13:35:03 -04:00
Main.layoutManager._updateHotCorners();
2023-08-13 22:58:15 +02:00
this._forceHotCornerId = SETTINGS.connect('changed::stockgs-force-hotcorner', () => Main.layoutManager._updateHotCorners());
2019-10-05 12:44:16 -04:00
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);
2019-09-21 08:36:42 -04:00
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();
2020-04-07 17:04:03 -04:00
//listen settings
this._signalsHandler.add(
[
2023-08-13 22:58:15 +02:00
SETTINGS,
[
'changed::primary-monitor',
'changed::multi-monitors',
'changed::isolate-monitors',
'changed::panel-positions',
Configure panel thickness, length, and anchor per-monitor Settings.ui: - Move thickness, length, and anchor from Style tab to Position tab. Group together with position in the same frame. schema: - Remove unpublished panel-length and panel-anchor settings, replacing them with panel-lengths and panel-anchors JSON objects (like panel-positions). - Remove unpublished anchor enum, since panel-anchors is being managed by the extension in JSON, not typed by the schema. - Deprecate panel-size in favour of new panel-sizes JSON, storing per-monitor panel sizes. - Mention that panel-position is deprecated. Introduce panelSettings.js: - Functions to fetch or set panel settings that are stored as JSON. Grown from now-removed getSettingsPositions() in panelPositions.js. prefs.js: - Group together the different UI widget label and value refreshing into method _updateWidgetSettingsForMonitor(). - Change multi-panel behaviour of _setAnchorLabels(). Previously, all panels shared the same anchor setting, and so setAnchorLabels considered all monitors. Now, panels are configured either independently, or sometimes all together; set labels according to orientation(s) of panel(s) being configured. - Omitting preventTop handling in refactored _setPanelPosition() method. Previously, it was written to set the first monitor's panel to Pos.BOTTOM if the user clicked the Top position radio button, if stockgs-keep-top-panel was also set. But the user can't activate the Top button anyway if stockgs is set (likely implemented later). panelManager.js: - Removing panelPositions, as it is not needed any more.
2020-10-27 16:04:19 -06:00
'changed::panel-lengths',
'changed::panel-anchors',
'changed::stockgs-keep-top-panel'
],
() => this._reset()
],
2020-05-12 19:00:51 -04:00
[
2023-08-13 22:58:15 +02:00
SETTINGS,
2020-05-12 19:00:51 -04:00
'changed::panel-element-positions',
() => this._updatePanelElementPositions()
],
[
2023-08-13 22:58:15 +02:00
SETTINGS,
'changed::intellihide-key-toggle-text',
() => this._setKeyBindings(true)
],
[
2023-08-13 22:58:15 +02:00
SETTINGS,
'changed::panel-sizes',
() => {
GLib.idle_add(GLib.PRIORITY_LOW, () => {
this._setDesktopIconsMargins();
return GLib.SOURCE_REMOVE;
});
}
],
[
Utils.DisplayWrapper.getMonitorManager(),
'monitors-changed',
() => {
if (Main.layoutManager.primaryMonitor) {
this._saveMonitors();
this._reset();
}
}
]
);
2023-08-19 14:03:05 +02:00
Panel.panelBoxes.forEach(c => this._signalsHandler.add(
2022-09-29 16:48:48 -04:00
[
Main.panel[c],
'actor-added',
(parent, child) =>
this.primaryPanel &&
this._adjustPanelMenuButton(this._getPanelMenuButton(child), this.primaryPanel.monitor, this.primaryPanel.getPosition())
]
));
this._setKeyBindings(true);
// keep GS overview.js from blowing away custom panel styles
2023-08-13 22:58:15 +02:00
if(!SETTINGS.get_boolean('stockgs-keep-top-panel'))
2022-04-01 22:32:15 -04:00
Object.defineProperty(Main.panel, "style", {configurable: true, set(v) {}});
}
2018-09-13 19:46:09 -04:00
2022-04-01 22:32:15 -04:00
disable(reset) {
2022-09-29 16:48:48 -04:00
this.primaryPanel && this.overview.disable();
this.proximityManager.destroy();
2018-09-13 19:46:09 -04:00
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;
})
2018-10-19 12:02:17 -04:00
this._removePanelBarriers(p);
2018-09-13 19:46:09 -04:00
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);
2022-04-13 22:29:57 -04:00
p.remove_child(p.panel);
p.panelBox.add(p.panel);
p.panelBox.set_position(clipContainer.x, clipContainer.y);
clipContainer.remove_child(p.panelBox);
Main.layoutManager.addChrome(p.panelBox, { affectsStruts: true, trackFullscreen: true });
}
2018-09-18 13:35:03 -04:00
});
2023-09-23 07:09:34 -04:00
this._injectionManager.clear();
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;
2019-10-05 12:44:16 -04:00
this._setKeyBindings(false);
this._signalsHandler.destroy();
2018-09-18 13:35:03 -04:00
Main.layoutManager._updateHotCorners = this._oldUpdateHotCorners;
Main.layoutManager._updateHotCorners();
2023-08-13 22:58:15 +02:00
SETTINGS.disconnect(this._forceHotCornerId);
2019-10-05 12:44:16 -04:00
if (this._enableHotCornersId) {
Main.layoutManager._interfaceSettings.disconnect(this._enableHotCornersId);
}
2018-09-18 13:35:03 -04:00
Main.layoutManager._updatePanelBarrier = this._oldUpdatePanelBarrier;
Main.layoutManager._updatePanelBarrier();
Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = this._oldUpdateWorkspacesViews;
Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = this._oldSetPrimaryWorkspaceVisible;
2019-09-21 08:36:42 -04:00
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;
this._desktopIconsUsableArea.destroy();
this._desktopIconsUsableArea = null;
}
_setDesktopIconsMargins() {
this._desktopIconsUsableArea?.resetMargins();
this.allPanels.forEach(p => {
switch(p.geom.position) {
case St.Side.TOP:
this._desktopIconsUsableArea?.setMargins(p.monitor.index, p.geom.h, 0, 0, 0);
break;
case St.Side.BOTTOM:
this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, p.geom.h, 0, 0);
break;
case St.Side.LEFT:
this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, 0, p.geom.w, 0);
break;
case St.Side.RIGHT:
this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, 0, 0, p.geom.w);
break;
}
});
2022-04-01 22:32:15 -04:00
}
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;
}
}
_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 ¯\_(ツ)_/¯
2023-08-14 01:02:59 +02:00
view = new SecondaryMonitorDisplay(i,
this._controls,
this._scrollAdjustment,
this._fitModeAdjustment,
this._overviewAdjustment);
Main.layoutManager.overviewGroup.add_actor(view);
}
Update to Gnome 40 (#1303) * 1 * renamed ui.viewSelector to ui.searchController * add arrowIcon null check * add object null check * add _showAppsButton null check * fixed _workspacesDisplay * removed shadow-type properties * removed packing from ui * renamed margin_left/right to margin_start/end * renamed GtkRadioButton to GtkToggleButton, removed draw_indicator and image_position * removed xalign from buttons, stock -> icon_name, GtkFileChooserButton -> BtkButton (todo connect a GtkFileChooserNative), removed format-value (todo connect GtkScaleFormatValueFunc callback) * removed events, relief * comment arrowIcon.set_icon_name and _hbox.get_last_child out * called gtk4-builder-tool simplify --3to4 on Settings.ui * fix _workspaceDisplay * revert Settings.ui back to old gtk3 version * removed _builder.connect_signals_full and added a BuilderScope instead, use class specific add functions, removed show_all for widgets, updated margins* for GtkGrid * add try catch aroung object.connect * fixed _searchEntry path * disabled _newUpdateWorkspacesViews temporarily, because it is very buggy right now * fixed _searchEntry path (this time for real?) * dialog.show_all() -> dialog.show(), widget.get_toplevel() -> widget.get_root(), fixed adjustScrollableHeight (no longer crashes, still does not work correctly) * updated GtkGrid.attach calls * added Gtk.FileChooserNative for show_applications_icon_file_filebutton * added packing again to ui (commented) and also added layout row/column/width/height in exchange for top_attach/left_attach/colspan/rowspan * renamed colspan/rowspan to column-span/row-span * removed all packaing left_attach/top_attach * updated adjustScrollableHeight so the dialog will have a reasonable size again * limit shell-version to 40 since it is not backwards compatible right now * called _updateBackgrounds in layoutManager ,fixed overview <-> _overview typo? * pass Settings instead of Settings._settings to BuilderScope * workaround for showAppsIcon (_showAppsButton seems to be no longer be available) * replaced newlines in the ui file with the newline unicode symbol, so code block collapsing works peroperly * moved the scrolled window directly into each notebook tab, so the tab headers are always visible * fixed taskbarListBox children loops * commented non working elements out (panel on left/right, import/export settings, update) * renamed Settings to Preferences to avoid confusion with dconf settings object * updated this.widget to this.notebook * fixed dialogs size and expand * fixed window preview options having too many columns * removed all packing with expand/fill/position (already uncommented) * removed menu arrows since they are no longer recommended for gnome40 * removed adjustScrollableHeight, default sizes are acceptable * updated ui required version to gtk 4.0 * updated path to dash.showAppsButton * fixes for showAppsButton * fixed clickToExit * fixed import/export settings * added disable show overview on startup option * removed old show apps animation * fixed panel on left/right side * fixed scroll on volume icon no longer will scrolling on the volume icon switch workspaces as well * commented some setFocusedMonitor support out, caused issues on Xorg * removed oldDash * updated hide overview on startup, no longer closes overview after suspend etc * removed _newOverviewRelayout * removed _newUpdateWorkspacesViews * commented setFocusedMonitor out completly, because show overview on non primary monitor does not work properly for now * fixed typo * fixed merging error * check if func is valid in hookVfunc * updated ui to gtk4, fixed merging error * fixed layout for apply changes to all monitors * fix behaviour tab and app icon margin/padding grid * fix animate launching new windows grid row * fixed about tab Co-authored-by: Cole Gerdemann <corvettecole@gmail.com>
2021-05-08 15:07:25 +02:00
this._workspacesViews.push(view);
}
2022-04-01 22:32:15 -04:00
}
_saveMonitors() {
//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 keyMonitors = 'available-monitors';
let keyPrimary = 'primary-monitor';
let primaryIndex = Main.layoutManager.primaryIndex;
let newMonitors = [primaryIndex];
2023-08-13 22:58:15 +02:00
let savedMonitors = SETTINGS.get_value(keyMonitors).deep_unpack();
let dtpPrimaryIndex = SETTINGS.get_int(keyPrimary);
let newDtpPrimaryIndex = primaryIndex;
Main.layoutManager.monitors.filter(m => m.index != primaryIndex).forEach(m => newMonitors.push(m.index));
if (savedMonitors[0] != dtpPrimaryIndex) {
// dash to panel primary wasn't the gnome-shell primary (first index of available-monitors)
let savedIndex = savedMonitors.indexOf(dtpPrimaryIndex)
// default to primary if it was set to a monitor that is no longer available
newDtpPrimaryIndex = newMonitors[savedIndex];
newDtpPrimaryIndex = newDtpPrimaryIndex == null ? primaryIndex : newDtpPrimaryIndex;
}
2023-08-13 22:58:15 +02:00
SETTINGS.set_int(keyPrimary, newDtpPrimaryIndex);
SETTINGS.set_value(keyMonitors, new GLib.Variant('ai', newMonitors));
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
checkIfFocusedMonitor(monitor) {
Update to Gnome 40 (#1303) * 1 * renamed ui.viewSelector to ui.searchController * add arrowIcon null check * add object null check * add _showAppsButton null check * fixed _workspacesDisplay * removed shadow-type properties * removed packing from ui * renamed margin_left/right to margin_start/end * renamed GtkRadioButton to GtkToggleButton, removed draw_indicator and image_position * removed xalign from buttons, stock -> icon_name, GtkFileChooserButton -> BtkButton (todo connect a GtkFileChooserNative), removed format-value (todo connect GtkScaleFormatValueFunc callback) * removed events, relief * comment arrowIcon.set_icon_name and _hbox.get_last_child out * called gtk4-builder-tool simplify --3to4 on Settings.ui * fix _workspaceDisplay * revert Settings.ui back to old gtk3 version * removed _builder.connect_signals_full and added a BuilderScope instead, use class specific add functions, removed show_all for widgets, updated margins* for GtkGrid * add try catch aroung object.connect * fixed _searchEntry path * disabled _newUpdateWorkspacesViews temporarily, because it is very buggy right now * fixed _searchEntry path (this time for real?) * dialog.show_all() -> dialog.show(), widget.get_toplevel() -> widget.get_root(), fixed adjustScrollableHeight (no longer crashes, still does not work correctly) * updated GtkGrid.attach calls * added Gtk.FileChooserNative for show_applications_icon_file_filebutton * added packing again to ui (commented) and also added layout row/column/width/height in exchange for top_attach/left_attach/colspan/rowspan * renamed colspan/rowspan to column-span/row-span * removed all packaing left_attach/top_attach * updated adjustScrollableHeight so the dialog will have a reasonable size again * limit shell-version to 40 since it is not backwards compatible right now * called _updateBackgrounds in layoutManager ,fixed overview <-> _overview typo? * pass Settings instead of Settings._settings to BuilderScope * workaround for showAppsIcon (_showAppsButton seems to be no longer be available) * replaced newlines in the ui file with the newline unicode symbol, so code block collapsing works peroperly * moved the scrolled window directly into each notebook tab, so the tab headers are always visible * fixed taskbarListBox children loops * commented non working elements out (panel on left/right, import/export settings, update) * renamed Settings to Preferences to avoid confusion with dconf settings object * updated this.widget to this.notebook * fixed dialogs size and expand * fixed window preview options having too many columns * removed all packing with expand/fill/position (already uncommented) * removed menu arrows since they are no longer recommended for gnome40 * removed adjustScrollableHeight, default sizes are acceptable * updated ui required version to gtk 4.0 * updated path to dash.showAppsButton * fixes for showAppsButton * fixed clickToExit * fixed import/export settings * added disable show overview on startup option * removed old show apps animation * fixed panel on left/right side * fixed scroll on volume icon no longer will scrolling on the volume icon switch workspaces as well * commented some setFocusedMonitor support out, caused issues on Xorg * removed oldDash * updated hide overview on startup, no longer closes overview after suspend etc * removed _newOverviewRelayout * removed _newUpdateWorkspacesViews * commented setFocusedMonitor out completly, because show overview on non primary monitor does not work properly for now * fixed typo * fixed merging error * check if func is valid in hookVfunc * updated ui to gtk4, fixed merging error * fixed layout for apply changes to all monitors * fix behaviour tab and app icon margin/padding grid * fix animate launching new windows grid row * fixed about tab Co-authored-by: Cole Gerdemann <corvettecole@gmail.com>
2021-05-08 15:07:25 +02:00
return Main.overview._overview._controls._workspacesDisplay._primaryIndex == monitor.index;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_createPanel(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);
2022-04-13 22:29:57 -04:00
panelBox.remove_child(Main.panel);
Main.layoutManager.removeChrome(panelBox);
}
Main.layoutManager.addChrome(clipContainer, { affectsInputRegion: false });
clipContainer.add_child(panelBox);
Main.layoutManager.trackChrome(panelBox, { trackFullscreen: true, affectsStruts: true, affectsInputRegion: true });
2023-08-13 23:44:02 +02:00
panel = new Panel.Panel(this, monitor, panelBox, isStandalone);
panelBox.add(panel);
2019-09-06 17:47:34 -04:00
panel.enable();
panelBox.visible = true;
if (monitor.inFullscreen) {
panelBox.hide();
}
panelBox.set_position(0, 0);
2019-09-06 17:47:34 -04:00
return panel;
2022-04-01 22:32:15 -04:00
}
2019-09-06 17:47:34 -04:00
2022-04-01 22:32:15 -04:00
_reset() {
this.disable(true);
this.allPanels = [];
this.enable(true);
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_updatePanelElementPositions() {
2023-08-13 22:58:15 +02:00
this.panelsElementPositions = PanelSettings.getSettingsJson(SETTINGS, 'panel-element-positions');
2020-05-12 19:00:51 -04:00
this.allPanels.forEach(p => p.updateElementPositions());
2022-04-01 22:32:15 -04:00
}
2020-05-12 19:00:51 -04:00
2022-04-01 22:32:15 -04:00
_adjustPanelMenuButton(button, monitor, arrowSide) {
if (button) {
button.menu._boxPointer._dtpSourceActor = button.menu._boxPointer.sourceActor;
2022-04-13 22:29:57 -04:00
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);
});
}
}
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_getBoxPointerPreferredHeight(boxPointer, alloc, monitor) {
2023-08-13 22:58:15 +02:00
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);
2020-06-13 07:40:33 -04:00
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];
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_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;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_removePanelBarriers(panel) {
if (panel.isStandalone && panel._rightPanelBarrier) {
2018-10-19 12:02:17 -04:00
panel._rightPanelBarrier.destroy();
}
if (panel._leftPanelBarrier) {
panel._leftPanelBarrier.destroy();
2019-09-03 21:46:10 -04:00
delete panel._leftPanelBarrier;
}
2022-04-01 22:32:15 -04:00
}
2018-10-19 12:02:17 -04:00
2022-04-01 22:32:15 -04:00
_getPanelMenuButton(obj) {
return obj instanceof PanelMenu.Button && obj.menu?._boxPointer ? obj : 0;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
_setKeyBindings(enable) {
let keys = {
'intellihide-key-toggle': () => this.allPanels.forEach(p => p.intellihide.toggle())
};
Object.keys(keys).forEach(k => {
Utils.removeKeybinding(k);
if (enable) {
2023-08-13 22:58:15 +02:00
Utils.addKeybinding(k, SETTINGS, keys[k], Shell.ActionMode.NORMAL);
}
});
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
};
2018-09-18 13:35:03 -04:00
// This class drives long-running icon animations, to keep them running in sync
// with each other.
export const IconAnimator = class {
2022-04-01 22:32:15 -04:00
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++) {
2021-04-06 13:22:08 +01:00
dancers[i].target.rotation_angle_z = danceRotation;
}
});
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
destroy() {
this._timeline.stop();
this._timeline = null;
2021-05-04 21:11:36 -04:00
for (let name in this._animations) {
2021-04-06 13:22:08 +01:00
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;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
pause() {
if (this._started && this._count > 0) {
this._timeline.stop();
}
this._started = false;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
start() {
if (!this._started && this._count > 0) {
this._timeline.start();
}
this._started = true;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
addAnimation(target, name) {
2021-04-06 13:22:08 +01:00
const targetDestroyId = target.connect('destroy', () => this.removeAnimation(target, name));
2021-05-04 21:11:36 -04:00
this._animations[name].push({ target: target, targetDestroyId: targetDestroyId });
if (this._started && this._count === 0) {
this._timeline.start();
}
this._count++;
2022-04-01 22:32:15 -04:00
}
2022-04-01 22:32:15 -04:00
removeAnimation(target, name) {
2021-04-06 13:22:08 +01:00
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;
}
}
}
2022-04-01 22:32:15 -04:00
};
2018-09-18 13:35:03 -04:00
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)
2019-10-05 23:50:38 -04:00
//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')) ||
2019-10-05 23:50:38 -04:00
(this._interfaceSettings && !this._interfaceSettings.get_boolean('enable-hot-corners'))) {
2018-09-18 13:35:03 -04:00
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;
2020-06-13 07:40:33 -04:00
let panelTopLeft = panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT;
2018-09-18 13:35:03 -04:00
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
2023-08-13 22:58:15 +02:00
if (i != this.primaryIndex || (!panelTopLeft && !SETTINGS.get_boolean('stockgs-force-hotcorner'))) {
2018-09-18 13:35:03 -04:00
// 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);
2022-03-16 19:27:40 +01:00
corner.setBarrierSize = size => Object.getPrototypeOf(corner).setBarrierSize.call(corner, Math.min(size, 32));
corner.setBarrierSize(panel ? panel.dtpSize : 32);
2018-09-18 13:35:03 -04:00
this.hotCorners.push(corner);
} else {
this.hotCorners.push(null);
}
}
this.emit('hot-corners-changed');
}
2019-09-03 21:46:10 -04:00
function newUpdatePanelBarrier(panel) {
2018-10-19 12:02:17 -04:00
let barriers = {
_rightPanelBarrier: [(panel.isStandalone ? panel : this)],
_leftPanelBarrier: [panel]
2018-10-19 12:02:17 -04:00
};
Object.keys(barriers).forEach(k => {
let obj = barriers[k][0];
if (obj[k]) {
obj[k].destroy();
obj[k] = null;
2018-10-19 12:02:17 -04:00
}
});
if (!this.primaryMonitor || !panel.panelBox.height) {
2018-09-18 13:35:03 -04:00
return;
}
2019-09-03 21:46:10 -04:00
let barrierSize = Math.min(10, panel.panelBox.height);
let fixed1 = panel.monitor.y;
let fixed2 = panel.monitor.y + barrierSize;
2020-06-13 07:40:33 -04:00
if (panel.checkIfVertical()) {
barriers._rightPanelBarrier.push(panel.monitor.y + panel.monitor.height, Meta.BarrierDirection.NEGATIVE_Y);
barriers._leftPanelBarrier.push(panel.monitor.y, Meta.BarrierDirection.POSITIVE_Y);
2019-09-03 21:46:10 -04:00
} else {
barriers._rightPanelBarrier.push(panel.monitor.x + panel.monitor.width, Meta.BarrierDirection.NEGATIVE_X);
barriers._leftPanelBarrier.push(panel.monitor.x, Meta.BarrierDirection.POSITIVE_X);
}
2020-06-13 07:40:33 -04:00
switch (panel.getPosition()) {
2019-09-03 21:46:10 -04:00
//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;
2019-09-03 21:46:10 -04:00
break;
case St.Side.RIGHT:
fixed1 = panel.monitor.x + panel.monitor.width - barrierSize;
fixed2 = panel.monitor.x + panel.monitor.width;
2019-09-03 21:46:10 -04:00
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;
}
}
2018-10-19 12:02:17 -04:00
Object.keys(barriers).forEach(k => {
2019-09-03 21:46:10 -04:00
let barrierOptions = {
display: global.display,
directions: barriers[k][2]
2019-09-03 21:46:10 -04:00
};
2020-06-13 07:40:33 -04:00
barrierOptions[panel.varCoord.c1] = barrierOptions[panel.varCoord.c2] = barriers[k][1];
barrierOptions[panel.fixedCoord.c1] = fixed1;
barrierOptions[panel.fixedCoord.c2] = fixed2;
2019-09-03 21:46:10 -04:00
barriers[k][0][k] = new Meta.Barrier(barrierOptions);
2018-10-19 12:02:17 -04:00
});
2019-09-21 08:36:42 -04:00
}
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;
2019-09-21 08:36:42 -04:00
this._oldResize();
2022-04-13 22:29:57 -04:00
this._hiddenY = Main.layoutManager.primaryMonitor.y + topOffset - this.height;
this._targetY = this._hiddenY + this.height;
this.y = this._hiddenY;
2019-09-21 08:36:42 -04:00
2022-04-13 22:29:57 -04:00
this._objInspector.set_position(this.x + Math.floor(this.width * 0.1), this._targetY + Math.floor(this.height * 0.1));
2019-09-21 08:36:42 -04:00
}
function _newLookingGlassOpen() {
if (this._open)
return;
this._resize();
this._oldOpen();
}