Re-apply downstream patches
Re-apply rebased and updated version of the RHEL 9 downstream patches, with some exceptions: Branding is still TBD, so has been left out for now. The desktop-icons extension will be replaced by an upstreamed version of desktop-icons-ng, which is still work-in-progress. Both dash-to-dock and dash-to-panel will be moved to separate packages, based on the existing Fedora package. It was decided to drop the panel-favorites and updates-dialog extensions. Resolves: RHEL-34255
This commit is contained in:
parent
26d9bf818a
commit
e6414d8afc
32
0001-Include-top-icons-in-classic-session.patch
Normal file
32
0001-Include-top-icons-in-classic-session.patch
Normal file
@ -0,0 +1,32 @@
|
||||
From 714feb8e1646b8a3e60bfd95683622a8f125bcf5 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 23 Feb 2018 16:56:46 +0100
|
||||
Subject: [PATCH] Include top-icons in classic session
|
||||
|
||||
---
|
||||
meson.build | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 6a94e4e2..56a4bb57 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -34,6 +34,7 @@ classic_extensions = [
|
||||
'apps-menu',
|
||||
'places-menu',
|
||||
'launch-new-instance',
|
||||
+ 'top-icons',
|
||||
'window-list'
|
||||
]
|
||||
|
||||
@@ -44,7 +45,6 @@ default_extensions += [
|
||||
'light-style',
|
||||
'screenshot-window-sizer',
|
||||
'system-monitor',
|
||||
- 'top-icons',
|
||||
'windowsNavigator',
|
||||
'workspace-indicator'
|
||||
]
|
||||
--
|
||||
2.44.0
|
||||
|
30
0001-apps-menu-Set-label_actor-of-Category-items.patch
Normal file
30
0001-apps-menu-Set-label_actor-of-Category-items.patch
Normal file
@ -0,0 +1,30 @@
|
||||
From 4d04a035416867caec9d0aa83f90b4156f017ad0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 17 Mar 2016 17:15:38 +0100
|
||||
Subject: [PATCH] apps-menu: Set label_actor of Category items
|
||||
|
||||
Category items are based on BaseMenuItem rather than MenuItem,
|
||||
so the accessible relationship isn't set up automatically for us.
|
||||
---
|
||||
extensions/apps-menu/extension.js | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js
|
||||
index c32b61aa..17bcf3b5 100644
|
||||
--- a/extensions/apps-menu/extension.js
|
||||
+++ b/extensions/apps-menu/extension.js
|
||||
@@ -125,7 +125,10 @@ class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
|
||||
else
|
||||
name = _('Favorites');
|
||||
|
||||
- this.add_child(new St.Label({text: name}));
|
||||
+ const label = new St.Label({text: name});
|
||||
+ this.add_child(label);
|
||||
+ this.actor.label_actor = label;
|
||||
+
|
||||
this.connect('motion-event', this._onMotionEvent.bind(this));
|
||||
this.connect('notify::active', this._onActiveChanged.bind(this));
|
||||
}
|
||||
--
|
||||
2.44.0
|
||||
|
158
extra-extensions-0001-Add-top-icons-extension.patch
Normal file
158
extra-extensions-0001-Add-top-icons-extension.patch
Normal file
@ -0,0 +1,158 @@
|
||||
From 56da93fbbe675144ae8eb60daedb9f9d3e93be0f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 20 May 2015 17:44:50 +0200
|
||||
Subject: [PATCH 1/5] Add top-icons extension
|
||||
|
||||
---
|
||||
extensions/top-icons/extension.js | 91 +++++++++++++++++++++++++++
|
||||
extensions/top-icons/meson.build | 9 +++
|
||||
extensions/top-icons/metadata.json.in | 10 +++
|
||||
meson.build | 1 +
|
||||
4 files changed, 111 insertions(+)
|
||||
create mode 100644 extensions/top-icons/extension.js
|
||||
create mode 100644 extensions/top-icons/meson.build
|
||||
create mode 100644 extensions/top-icons/metadata.json.in
|
||||
|
||||
diff --git a/extensions/top-icons/extension.js b/extensions/top-icons/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..c28f1386
|
||||
--- /dev/null
|
||||
+++ b/extensions/top-icons/extension.js
|
||||
@@ -0,0 +1,91 @@
|
||||
+// SPDX-FileCopyrightText: 2018 Adel Gadllah <adel.gadllah@gmail.com>
|
||||
+// SPDX-FileCopyrightText: 2018 Florian Müllner <fmuellner@gnome.org>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import Clutter from 'gi://Clutter';
|
||||
+import Shell from 'gi://Shell';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import {Button as PanelButton} from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||||
+
|
||||
+const PANEL_ICON_SIZE = 16;
|
||||
+
|
||||
+const STANDARD_TRAY_ICON_IMPLEMENTATIONS = [
|
||||
+ 'bluetooth-applet',
|
||||
+ 'gnome-sound-applet',
|
||||
+ 'nm-applet',
|
||||
+ 'gnome-power-manager',
|
||||
+ 'keyboard',
|
||||
+ 'a11y-keyboard',
|
||||
+ 'kbd-scrolllock',
|
||||
+ 'kbd-numlock',
|
||||
+ 'kbd-capslock',
|
||||
+ 'ibus-ui-gtk',
|
||||
+];
|
||||
+
|
||||
+export default class SysTray {
|
||||
+ constructor() {
|
||||
+ this._icons = new Map();
|
||||
+ this._tray = null;
|
||||
+ }
|
||||
+
|
||||
+ _onTrayIconAdded(o, icon) {
|
||||
+ let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
|
||||
+ if (STANDARD_TRAY_ICON_IMPLEMENTATIONS.includes(wmClass))
|
||||
+ return;
|
||||
+
|
||||
+ let button = new PanelButton(0.5, null, true);
|
||||
+
|
||||
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
+ let iconSize = PANEL_ICON_SIZE * scaleFactor;
|
||||
+
|
||||
+ icon.set({
|
||||
+ width: iconSize,
|
||||
+ height: iconSize,
|
||||
+ x_align: Clutter.ActorAlign.CENTER,
|
||||
+ y_align: Clutter.ActorAlign.CENTER,
|
||||
+ });
|
||||
+
|
||||
+ let iconBin = new St.Widget({
|
||||
+ layout_manager: new Clutter.BinLayout(),
|
||||
+ style_class: 'system-status-icon',
|
||||
+ });
|
||||
+ iconBin.add_child(icon);
|
||||
+ button.add_child(iconBin);
|
||||
+
|
||||
+ this._icons.set(icon, button);
|
||||
+
|
||||
+ button.connect('button-release-event',
|
||||
+ (actor, event) => icon.click(event));
|
||||
+ button.connect('key-press-event',
|
||||
+ (actor, event) => icon.click(event));
|
||||
+
|
||||
+ const role = `${icon}`;
|
||||
+ Main.panel.addToStatusArea(role, button);
|
||||
+ }
|
||||
+
|
||||
+ _onTrayIconRemoved(o, icon) {
|
||||
+ const button = this._icons.get(icon);
|
||||
+ button?.destroy();
|
||||
+ this._icons.delete(icon);
|
||||
+ }
|
||||
+
|
||||
+ enable() {
|
||||
+ this._tray = new Shell.TrayManager();
|
||||
+ this._tray.connect('tray-icon-added',
|
||||
+ this._onTrayIconAdded.bind(this));
|
||||
+ this._tray.connect('tray-icon-removed',
|
||||
+ this._onTrayIconRemoved.bind(this));
|
||||
+ this._tray.manage_screen(Main.panel);
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ this._icons.forEach(button => button.destroy());
|
||||
+ this._icons.clear();
|
||||
+
|
||||
+ this._tray.unmanage_screen();
|
||||
+ this._tray = null;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/top-icons/meson.build b/extensions/top-icons/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..b30272ad
|
||||
--- /dev/null
|
||||
+++ b/extensions/top-icons/meson.build
|
||||
@@ -0,0 +1,9 @@
|
||||
+# SPDX-FileCopyrightText: 2018 Florian Müllner <fmuellner@gnome.org>
|
||||
+#
|
||||
+# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
diff --git a/extensions/top-icons/metadata.json.in b/extensions/top-icons/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..1d2e0bc2
|
||||
--- /dev/null
|
||||
+++ b/extensions/top-icons/metadata.json.in
|
||||
@@ -0,0 +1,10 @@
|
||||
+{
|
||||
+"extension-id": "@extension_id@",
|
||||
+"uuid": "@uuid@",
|
||||
+"settings-schema": "@gschemaname@",
|
||||
+"gettext-domain": "@gettext_domain@",
|
||||
+"name": "Top Icons",
|
||||
+"description": "Show legacy tray icons on top",
|
||||
+"shell-version": [ "@shell_current@" ],
|
||||
+"url": "@url@"
|
||||
+}
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 6d403512..4d2ca280 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -43,6 +43,7 @@ default_extensions += [
|
||||
'light-style',
|
||||
'screenshot-window-sizer',
|
||||
'system-monitor',
|
||||
+ 'top-icons',
|
||||
'windowsNavigator',
|
||||
'workspace-indicator'
|
||||
]
|
||||
--
|
||||
2.45.0
|
||||
|
181
extra-extensions-0002-Add-gesture-inhibitor-extension.patch
Normal file
181
extra-extensions-0002-Add-gesture-inhibitor-extension.patch
Normal file
@ -0,0 +1,181 @@
|
||||
From 4ca791c4d7872cb51ebc6cc90f906a9fcbb5b995 Mon Sep 17 00:00:00 2001
|
||||
From: Carlos Garnacho <carlosg@gnome.org>
|
||||
Date: Thu, 28 Jan 2021 00:06:12 +0100
|
||||
Subject: [PATCH 2/5] Add gesture-inhibitor extension
|
||||
|
||||
This extension may disable default GNOME Shell gestures.
|
||||
---
|
||||
extensions/gesture-inhibitor/extension.js | 79 +++++++++++++++++++
|
||||
extensions/gesture-inhibitor/meson.build | 8 ++
|
||||
extensions/gesture-inhibitor/metadata.json.in | 12 +++
|
||||
...l.extensions.gesture-inhibitor.gschema.xml | 25 ++++++
|
||||
meson.build | 1 +
|
||||
5 files changed, 125 insertions(+)
|
||||
create mode 100644 extensions/gesture-inhibitor/extension.js
|
||||
create mode 100644 extensions/gesture-inhibitor/meson.build
|
||||
create mode 100644 extensions/gesture-inhibitor/metadata.json.in
|
||||
create mode 100644 extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
|
||||
diff --git a/extensions/gesture-inhibitor/extension.js b/extensions/gesture-inhibitor/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..872020ba
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/extension.js
|
||||
@@ -0,0 +1,79 @@
|
||||
+// SPDX-FileCopyrightText: 2021 Carlos Garnacho <carlosg@gnome.org>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+//
|
||||
+
|
||||
+import Clutter from 'gi://Clutter';
|
||||
+import Gio from 'gi://Gio';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+
|
||||
+import {AppSwitchAction} from 'resource:///org/gnome/shell/ui/windowManager.js';
|
||||
+import {EdgeDragAction} from 'resource:///org/gnome/shell/ui/edgeDragAction.js';
|
||||
+
|
||||
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
+
|
||||
+export default class GestureInhibitorExtension extends Extension {
|
||||
+ constructor(metadata) {
|
||||
+ super(metadata);
|
||||
+
|
||||
+ let actions = global.stage.get_actions();
|
||||
+
|
||||
+ actions.forEach(a => {
|
||||
+ if (a instanceof AppSwitchAction)
|
||||
+ this._appSwitch = a;
|
||||
+ else if (a instanceof EdgeDragAction &&
|
||||
+ a._side === St.Side.BOTTOM)
|
||||
+ this._showOsk = a;
|
||||
+ else if (a instanceof EdgeDragAction &&
|
||||
+ a._side === St.Side.TOP)
|
||||
+ this._unfullscreen = a;
|
||||
+ });
|
||||
+
|
||||
+ this._map = [
|
||||
+ {setting: 'overview', action: Main.overview._swipeTracker},
|
||||
+ {setting: 'app-switch', action: this._appSwitch},
|
||||
+ {setting: 'show-osk', action: this._showOsk},
|
||||
+ {setting: 'unfullscreen', action: this._unfullscreen},
|
||||
+ {setting: 'workspace-switch', action: Main.wm._workspaceAnimation._swipeTracker},
|
||||
+ ];
|
||||
+
|
||||
+ this._enabledDesc = Object.getOwnPropertyDescriptor(
|
||||
+ Clutter.ActorMeta.prototype, 'enabled');
|
||||
+ }
|
||||
+
|
||||
+ _overrideEnabledSetter(obj, set) {
|
||||
+ if (!(obj instanceof Clutter.ActorMeta))
|
||||
+ return;
|
||||
+
|
||||
+ const desc = set
|
||||
+ ? {...this._enabledDesc, set}
|
||||
+ : {...this._enabledDesc};
|
||||
+ Object.defineProperty(obj, 'enabled', desc);
|
||||
+ }
|
||||
+
|
||||
+ enable() {
|
||||
+ const settings = this.getSettings();
|
||||
+
|
||||
+ this._map.forEach(m => {
|
||||
+ settings.bind(m.setting, m.action, 'enabled',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+
|
||||
+ this._overrideEnabledSetter(m.action, function (value) {
|
||||
+ if (settings.get_boolean(m.setting)) {
|
||||
+ // eslint-disable-next-line no-invalid-this
|
||||
+ this.set_enabled(value);
|
||||
+ }
|
||||
+ });
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ this._map.forEach(m => {
|
||||
+ Gio.Settings.unbind(m.action, 'enabled');
|
||||
+ this._overrideEnabledSetter(m.action);
|
||||
+ m.action.enabled = true;
|
||||
+ });
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/gesture-inhibitor/meson.build b/extensions/gesture-inhibitor/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..fdad5cc8
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/meson.build
|
||||
@@ -0,0 +1,8 @@
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
+
|
||||
+# extension_sources += files('prefs.js')
|
||||
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
diff --git a/extensions/gesture-inhibitor/metadata.json.in b/extensions/gesture-inhibitor/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..37d6a117
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/metadata.json.in
|
||||
@@ -0,0 +1,12 @@
|
||||
+{
|
||||
+ "uuid": "@uuid@",
|
||||
+ "extension-id": "@extension_id@",
|
||||
+ "settings-schema": "@gschemaname@",
|
||||
+ "gettext-domain": "@gettext_domain@",
|
||||
+ "name": "Gesture Inhibitor",
|
||||
+ "description": "Makes touchscreen gestures optional.",
|
||||
+ "shell-version": [ "@shell_current@" ],
|
||||
+ "original-authors": [ "cgarnach@redhat.com" ],
|
||||
+ "url": "@url@"
|
||||
+}
|
||||
+
|
||||
diff --git a/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
new file mode 100644
|
||||
index 00000000..b06d027a
|
||||
--- /dev/null
|
||||
+++ b/extensions/gesture-inhibitor/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
@@ -0,0 +1,25 @@
|
||||
+<schemalist>
|
||||
+ <schema id="org.gnome.shell.extensions.gesture-inhibitor" path="/org/gnome/shell/extensions/gesture-inhibitor/">
|
||||
+ <key name="show-osk" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show OSK gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="overview" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show Overview gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="app-switch" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Application switch gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="workspace-switch" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Workspace switch gesture</summary>
|
||||
+ </key>
|
||||
+ <key name="unfullscreen" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Unfullscreen gesture</summary>
|
||||
+ </key>
|
||||
+ </schema>
|
||||
+</schemalist>
|
||||
+
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 4d2ca280..c78d0cc6 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -51,6 +51,7 @@ default_extensions += [
|
||||
all_extensions = default_extensions
|
||||
all_extensions += [
|
||||
'auto-move-windows',
|
||||
+ 'gesture-inhibitor',
|
||||
'native-window-placement',
|
||||
'user-theme'
|
||||
]
|
||||
--
|
||||
2.45.0
|
||||
|
688
extra-extensions-0003-Add-classification-banner.patch
Normal file
688
extra-extensions-0003-Add-classification-banner.patch
Normal file
@ -0,0 +1,688 @@
|
||||
From fc39411ecf0431ecc39581bace4217a55ab028e9 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 2 Dec 2021 19:39:50 +0100
|
||||
Subject: [PATCH 3/5] Add classification-banner
|
||||
|
||||
---
|
||||
extensions/classification-banner/adwShim.js | 202 ++++++++++++++++++
|
||||
extensions/classification-banner/extension.js | 162 ++++++++++++++
|
||||
extensions/classification-banner/meson.build | 9 +
|
||||
.../classification-banner/metadata.json.in | 11 +
|
||||
...tensions.classification-banner.gschema.xml | 29 +++
|
||||
extensions/classification-banner/prefs.js | 192 +++++++++++++++++
|
||||
.../classification-banner/stylesheet.css | 3 +
|
||||
meson.build | 1 +
|
||||
8 files changed, 609 insertions(+)
|
||||
create mode 100644 extensions/classification-banner/adwShim.js
|
||||
create mode 100644 extensions/classification-banner/extension.js
|
||||
create mode 100644 extensions/classification-banner/meson.build
|
||||
create mode 100644 extensions/classification-banner/metadata.json.in
|
||||
create mode 100644 extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml
|
||||
create mode 100644 extensions/classification-banner/prefs.js
|
||||
create mode 100644 extensions/classification-banner/stylesheet.css
|
||||
|
||||
diff --git a/extensions/classification-banner/adwShim.js b/extensions/classification-banner/adwShim.js
|
||||
new file mode 100644
|
||||
index 00000000..46a8afca
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/adwShim.js
|
||||
@@ -0,0 +1,202 @@
|
||||
+/* exported init PreferencesPage PreferencesGroup ActionRow ComboRow */
|
||||
+const { Gio, GObject, Gtk } = imports.gi;
|
||||
+
|
||||
+function init() {
|
||||
+}
|
||||
+
|
||||
+var PreferencesGroup = GObject.registerClass(
|
||||
+class PreferencesGroup extends Gtk.Widget {
|
||||
+ _init(params) {
|
||||
+ super._init({
|
||||
+ ...params,
|
||||
+ layout_manager: new Gtk.BinLayout(),
|
||||
+ });
|
||||
+
|
||||
+ this._listBox = new Gtk.ListBox({
|
||||
+ css_classes: ['rich-list'],
|
||||
+ show_separators: true,
|
||||
+ selection_mode: Gtk.SelectionMode.NONE,
|
||||
+ });
|
||||
+
|
||||
+ const frame = new Gtk.Frame({ child: this._listBox });
|
||||
+ frame.set_parent(this);
|
||||
+ }
|
||||
+
|
||||
+ add(child) {
|
||||
+ this._listBox.append(child);
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+var PreferencesPage = GObject.registerClass(
|
||||
+class PreferencesPage extends Gtk.Widget {
|
||||
+ _init(params) {
|
||||
+ super._init({
|
||||
+ ...params,
|
||||
+ layout_manager: new Gtk.BinLayout(),
|
||||
+ });
|
||||
+
|
||||
+ const scrolledWindow = new Gtk.ScrolledWindow({
|
||||
+ hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
+ });
|
||||
+ scrolledWindow.set_parent(this);
|
||||
+
|
||||
+ this._box = new Gtk.Box({
|
||||
+ orientation: Gtk.Orientation.VERTICAL,
|
||||
+ halign: Gtk.Align.CENTER,
|
||||
+ spacing: 24,
|
||||
+ margin_top: 24,
|
||||
+ margin_bottom: 24,
|
||||
+ margin_start: 12,
|
||||
+ margin_end: 12,
|
||||
+ });
|
||||
+ scrolledWindow.set_child(this._box);
|
||||
+
|
||||
+ const provider = new Gtk.CssProvider();
|
||||
+ provider.load_from_data('* { min-width: 500px; }');
|
||||
+ this._box.get_style_context().add_provider(provider,
|
||||
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
+ }
|
||||
+
|
||||
+ add(child) {
|
||||
+ this._box.append(child);
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+var ActionRow = GObject.registerClass({
|
||||
+ Properties: {
|
||||
+ 'activatable-widget': GObject.ParamSpec.object(
|
||||
+ 'activatable-widget', 'activatable-widget', 'activatable-widget',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ Gtk.Widget),
|
||||
+ 'title': GObject.ParamSpec.string(
|
||||
+ 'title', 'title', 'title',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ null),
|
||||
+ },
|
||||
+}, class ActionRow extends Gtk.ListBoxRow {
|
||||
+ _init(params) {
|
||||
+ super._init(params);
|
||||
+
|
||||
+ const box = new Gtk.Box({
|
||||
+ spacing: 12,
|
||||
+ });
|
||||
+ this.set_child(box);
|
||||
+
|
||||
+ this._prefixes = new Gtk.Box({
|
||||
+ spacing: 12,
|
||||
+ visible: false,
|
||||
+ });
|
||||
+ box.append(this._prefixes);
|
||||
+
|
||||
+ this._title = new Gtk.Label({
|
||||
+ css_classes: ['title'],
|
||||
+ hexpand: true,
|
||||
+ xalign: 0,
|
||||
+ });
|
||||
+ box.append(this._title);
|
||||
+
|
||||
+ this._suffixes = new Gtk.Box({
|
||||
+ spacing: 12,
|
||||
+ visible: false,
|
||||
+ });
|
||||
+ box.append(this._suffixes);
|
||||
+
|
||||
+ this.bind_property('title',
|
||||
+ this._title, 'label',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
+
|
||||
+ this.connect('notify::parent', () => {
|
||||
+ const parent = this.get_parent();
|
||||
+ parent?.connect('row-activated', (list, row) => {
|
||||
+ if (row === this)
|
||||
+ this.activate();
|
||||
+ });
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ vfunc_activate() {
|
||||
+ this.activatable_widget?.mnemonic_activate(false);
|
||||
+ }
|
||||
+
|
||||
+ activate() {
|
||||
+ this.vfunc_activate();
|
||||
+ }
|
||||
+
|
||||
+ add_prefix(child) {
|
||||
+ this._prefixes.append(child);
|
||||
+ this._prefixes.show();
|
||||
+ }
|
||||
+
|
||||
+ add_suffix(child) {
|
||||
+ this._suffixes.append(child);
|
||||
+ this._suffixes.show();
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+var ComboRow = GObject.registerClass({
|
||||
+ Properties: {
|
||||
+ 'selected-item': GObject.ParamSpec.object(
|
||||
+ 'selected-item', 'selected-item', 'selected-item',
|
||||
+ GObject.ParamFlags.READABLE,
|
||||
+ GObject.Object),
|
||||
+ 'model': GObject.ParamSpec.object(
|
||||
+ 'model', 'model', 'model',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ Gio.ListModel),
|
||||
+ 'list-factory': GObject.ParamSpec.object(
|
||||
+ 'list-factory', 'list-factory', 'list-factory',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ Gtk.ListItemFactory),
|
||||
+ 'expression': Gtk.param_spec_expression(
|
||||
+ 'expression', 'expression', 'expression',
|
||||
+ GObject.ParamFlags.READWRITE),
|
||||
+ },
|
||||
+}, class ComboRow extends ActionRow {
|
||||
+ _init(params) {
|
||||
+ super._init({
|
||||
+ ...params,
|
||||
+ activatable: true,
|
||||
+ });
|
||||
+
|
||||
+ const box = new Gtk.Box({
|
||||
+ valign: Gtk.Align.CENTER,
|
||||
+ });
|
||||
+ box.append(new Gtk.Image({
|
||||
+ icon_name: 'pan-down-symbolic',
|
||||
+ }));
|
||||
+ this.add_suffix(box);
|
||||
+
|
||||
+ this._popover = new Gtk.Popover();
|
||||
+ this._popover.set_parent(box);
|
||||
+
|
||||
+ this._selection = new Gtk.SingleSelection();
|
||||
+ this._selected = -1;
|
||||
+
|
||||
+ this._listView = new Gtk.ListView({
|
||||
+ model: this._selection,
|
||||
+ single_click_activate: true,
|
||||
+ });
|
||||
+ this._popover.set_child(this._listView);
|
||||
+
|
||||
+ this._listView.connect('activate', (view, pos) => {
|
||||
+ this._selected = pos;
|
||||
+ this.notify('selected-item');
|
||||
+ this._popover.popdown();
|
||||
+ });
|
||||
+
|
||||
+ this.bind_property('model',
|
||||
+ this._selection, 'model',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
+ this.bind_property('list-factory',
|
||||
+ this._listView, 'factory',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
+ }
|
||||
+
|
||||
+ get selected_item() {
|
||||
+ return this._selection.selected_item;
|
||||
+ }
|
||||
+
|
||||
+ vfunc_activate() {
|
||||
+ this._popover.popup();
|
||||
+ }
|
||||
+});
|
||||
diff --git a/extensions/classification-banner/extension.js b/extensions/classification-banner/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..e872d57d
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/extension.js
|
||||
@@ -0,0 +1,162 @@
|
||||
+// SPDX-FileCopyrightText: 2021 Florian Müllner <fmuellner@gnome.org>
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import Clutter from 'gi://Clutter';
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GLib from 'gi://GLib';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import Shell from 'gi://Shell';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import {MonitorConstraint} from 'resource:///org/gnome/shell/ui/layout.js';
|
||||
+
|
||||
+class ClassificationBanner extends Clutter.Actor {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ #topBanner;
|
||||
+ #bottomBanner;
|
||||
+ #monitorConstraint;
|
||||
+ #settings;
|
||||
+
|
||||
+ constructor(index, settings) {
|
||||
+ const constraint = new MonitorConstraint({index});
|
||||
+ super({
|
||||
+ layout_manager: new Clutter.BinLayout(),
|
||||
+ constraints: constraint,
|
||||
+ });
|
||||
+ this.#monitorConstraint = constraint;
|
||||
+
|
||||
+ Shell.util_set_hidden_from_pick(this, true);
|
||||
+
|
||||
+ this.#settings = settings;
|
||||
+
|
||||
+ this.#topBanner = new St.BoxLayout({
|
||||
+ style_class: 'classification-banner',
|
||||
+ x_expand: true,
|
||||
+ y_expand: true,
|
||||
+ y_align: Clutter.ActorAlign.START,
|
||||
+ });
|
||||
+ this.add_child(this.#topBanner);
|
||||
+ this.#settings.bind('top-banner',
|
||||
+ this.#topBanner, 'visible',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+
|
||||
+ this.#bottomBanner = new St.BoxLayout({
|
||||
+ style_class: 'classification-banner',
|
||||
+ x_expand: true,
|
||||
+ y_expand: true,
|
||||
+ y_align: Clutter.ActorAlign.END,
|
||||
+ });
|
||||
+ this.add_child(this.#bottomBanner);
|
||||
+ this.#settings.bind('bottom-banner',
|
||||
+ this.#bottomBanner, 'visible',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+
|
||||
+ for (const banner of [this.#topBanner, this.#bottomBanner]) {
|
||||
+ const label = new St.Label({
|
||||
+ style_class: 'classification-message',
|
||||
+ x_align: Clutter.ActorAlign.CENTER,
|
||||
+ x_expand: true,
|
||||
+ });
|
||||
+ banner.add_child(label);
|
||||
+
|
||||
+ this.#settings.bind('message',
|
||||
+ label, 'text',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+ }
|
||||
+
|
||||
+ const hostLabel = new St.Label({
|
||||
+ style_class: 'classification-system-info',
|
||||
+ text: GLib.get_host_name(),
|
||||
+ });
|
||||
+ this.#topBanner.insert_child_at_index(hostLabel, 0);
|
||||
+ this.#settings.bind('system-info',
|
||||
+ hostLabel, 'visible',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+
|
||||
+ const userLabel = new St.Label({
|
||||
+ style_class: 'classification-system-info',
|
||||
+ text: GLib.get_user_name(),
|
||||
+ });
|
||||
+ this.#topBanner.add_child(userLabel);
|
||||
+ this.#settings.bind('system-info',
|
||||
+ userLabel, 'visible',
|
||||
+ Gio.SettingsBindFlags.GET);
|
||||
+
|
||||
+ global.display.connectObject('in-fullscreen-changed',
|
||||
+ () => this.#updateMonitorConstraint(), this);
|
||||
+ this.#updateMonitorConstraint();
|
||||
+
|
||||
+ this.#settings.connectObject(
|
||||
+ 'changed::color', () => this.#updateStyles(),
|
||||
+ 'changed::background-color', () => this.#updateStyles(),
|
||||
+ this);
|
||||
+ this.#updateStyles();
|
||||
+ }
|
||||
+
|
||||
+ #getColorSetting(key) {
|
||||
+ const str = this.#settings.get_string(key);
|
||||
+ const [valid, color] = Clutter.Color.from_string(str);
|
||||
+ if (!valid)
|
||||
+ return '';
|
||||
+ const {red, green, blue, alpha} = color;
|
||||
+ return `${key}: rgba(${red},${green},${blue},${alpha / 255});`;
|
||||
+ }
|
||||
+
|
||||
+ #updateMonitorConstraint() {
|
||||
+ const {index} = this.#monitorConstraint;
|
||||
+ this.#monitorConstraint.work_area =
|
||||
+ !global.display.get_monitor_in_fullscreen(index);
|
||||
+ }
|
||||
+
|
||||
+ #updateStyles() {
|
||||
+ const bgStyle = this.#getColorSetting('background-color');
|
||||
+ const fgStyle = this.#getColorSetting('color');
|
||||
+ const style = `${bgStyle}${fgStyle}`;
|
||||
+ this.#topBanner.set({style});
|
||||
+ this.#bottomBanner.set({style});
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+export default class ClassificationBannerExtension extends Extension {
|
||||
+ #banners = [];
|
||||
+
|
||||
+ #updateMonitors() {
|
||||
+ const {monitors, panelBox, primaryIndex} = Main.layoutManager;
|
||||
+ if (monitors.length !== this.#banners.length) {
|
||||
+ this.#clearBanners();
|
||||
+
|
||||
+ const settings = this.getSettings();
|
||||
+ for (let i = 0; i < monitors.length; i++) {
|
||||
+ const banner = new ClassificationBanner(i, settings);
|
||||
+ Main.uiGroup.add_child(banner);
|
||||
+ this.#banners.push(banner);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ const primaryBanner = this.#banners[primaryIndex];
|
||||
+ if (primaryBanner)
|
||||
+ Main.uiGroup.set_child_below_sibling(primaryBanner, panelBox);
|
||||
+ }
|
||||
+
|
||||
+ #clearBanners() {
|
||||
+ this.#banners.forEach(b => b.destroy());
|
||||
+ this.#banners = [];
|
||||
+ }
|
||||
+
|
||||
+ enable() {
|
||||
+ Main.layoutManager.connectObject('monitors-changed',
|
||||
+ () => this.#updateMonitors(), this);
|
||||
+ this.#updateMonitors();
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ Main.layoutManager.disconnectObject(this);
|
||||
+ this.#clearBanners();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/classification-banner/meson.build b/extensions/classification-banner/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..aa943741
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/meson.build
|
||||
@@ -0,0 +1,9 @@
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
+extension_data += files('stylesheet.css')
|
||||
+
|
||||
+extension_sources += files('prefs.js')
|
||||
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
diff --git a/extensions/classification-banner/metadata.json.in b/extensions/classification-banner/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..f93b1a2d
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/metadata.json.in
|
||||
@@ -0,0 +1,11 @@
|
||||
+{
|
||||
+"extension-id": "@extension_id@",
|
||||
+"uuid": "@uuid@",
|
||||
+"settings-schema": "@gschemaname@",
|
||||
+"gettext-domain": "@gettext_domain@",
|
||||
+"name": "Classification Banner",
|
||||
+"description": "Display classification level banner",
|
||||
+"shell-version": [ "@shell_current@" ],
|
||||
+"session-modes": [ "gdm", "unlock-dialog", "user" ],
|
||||
+"url": "@url@"
|
||||
+}
|
||||
diff --git a/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml b/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml
|
||||
new file mode 100644
|
||||
index 00000000..0314ef60
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/org.gnome.shell.extensions.classification-banner.gschema.xml
|
||||
@@ -0,0 +1,29 @@
|
||||
+<schemalist gettext-domain="gnome-shell-extensions">
|
||||
+ <schema id="org.gnome.shell.extensions.classification-banner"
|
||||
+ path="/org/gnome/shell/extensions/classification-banner/">
|
||||
+ <key name="top-banner" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show a banner at the top</summary>
|
||||
+ </key>
|
||||
+ <key name="bottom-banner" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show a banner at the bottom</summary>
|
||||
+ </key>
|
||||
+ <key name="message" type="s">
|
||||
+ <default>"UNCLASSIFIED"</default>
|
||||
+ <summary>classification message</summary>
|
||||
+ </key>
|
||||
+ <key name="color" type="s">
|
||||
+ <default>"#fff"</default>
|
||||
+ <summary>text color</summary>
|
||||
+ </key>
|
||||
+ <key name="background-color" type="s">
|
||||
+ <default>"rgba(0,122,51,0.75)"</default>
|
||||
+ <summary>background color</summary>
|
||||
+ </key>
|
||||
+ <key name="system-info" type="b">
|
||||
+ <default>false</default>
|
||||
+ <summary>Include system info in top banner</summary>
|
||||
+ </key>
|
||||
+ </schema>
|
||||
+</schemalist>
|
||||
diff --git a/extensions/classification-banner/prefs.js b/extensions/classification-banner/prefs.js
|
||||
new file mode 100644
|
||||
index 00000000..dc73ddae
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/prefs.js
|
||||
@@ -0,0 +1,192 @@
|
||||
+import Adw from 'gi://Adw';
|
||||
+import Gdk from 'gi://Gdk';
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import Gtk from 'gi://Gtk';
|
||||
+
|
||||
+import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||
+
|
||||
+class GenericPrefs extends Adw.PreferencesGroup {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ #actionGroup = new Gio.SimpleActionGroup();
|
||||
+ #settings;
|
||||
+
|
||||
+ constructor(settings) {
|
||||
+ super();
|
||||
+
|
||||
+ this.#settings = settings;
|
||||
+ this.insert_action_group('options', this.#actionGroup);
|
||||
+
|
||||
+ this.#actionGroup.add_action(settings.create_action('top-banner'));
|
||||
+ this.#actionGroup.add_action(settings.create_action('bottom-banner'));
|
||||
+ this.#actionGroup.add_action(settings.create_action('system-info'));
|
||||
+
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('Top Banner'),
|
||||
+ action_name: 'options.top-banner',
|
||||
+ }));
|
||||
+
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('Bottom Banner'),
|
||||
+ action_name: 'options.bottom-banner',
|
||||
+ }));
|
||||
+
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('System Info'),
|
||||
+ action_name: 'options.system-info',
|
||||
+ }));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class BannerPreset extends GObject.Object {
|
||||
+ static [GObject.properties] = {
|
||||
+ 'message': GObject.ParamSpec.string(
|
||||
+ 'message', 'message', 'message',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ null),
|
||||
+ 'color': GObject.ParamSpec.string(
|
||||
+ 'color', 'color', 'color',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ null),
|
||||
+ 'background-color': GObject.ParamSpec.string(
|
||||
+ 'background-color', 'background-color', 'background-color',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ null),
|
||||
+ };
|
||||
+
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class AppearancePrefs extends Adw.PreferencesGroup {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ #settings;
|
||||
+
|
||||
+ constructor(settings) {
|
||||
+ super();
|
||||
+
|
||||
+ this.#settings = settings;
|
||||
+
|
||||
+ const model = new Gio.ListStore({item_type: BannerPreset.$gtype});
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'UNCLASSIFIED',
|
||||
+ color: '#fff',
|
||||
+ background_color: 'rgba(0, 122, 51, 0.75)',
|
||||
+ }));
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'CONFIDENTIAL',
|
||||
+ color: '#fff',
|
||||
+ background_color: 'rgba(0, 51, 160, 0.75)',
|
||||
+ }));
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'SECRET',
|
||||
+ color: '#fff',
|
||||
+ background_color: 'rgba(200, 16, 46, 0.75)',
|
||||
+ }));
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'TOP SECRET',
|
||||
+ color: '#fff',
|
||||
+ background_color: 'rgba(255, 103, 31, 0.75)',
|
||||
+ }));
|
||||
+ model.append(new BannerPreset({
|
||||
+ message: 'TOP SECRET//SCI',
|
||||
+ color: '#000',
|
||||
+ background_color: 'rgba(247, 234, 72, 0.75)',
|
||||
+ }));
|
||||
+
|
||||
+ let row, activatableWidget;
|
||||
+ row = this.#createPresetsRow(model);
|
||||
+ row.connect('notify::selected-item', comboRow => {
|
||||
+ const {message, color, backgroundColor} = comboRow.selected_item;
|
||||
+ this.#settings.set_string('message', message);
|
||||
+ this.#settings.set_string('color', color);
|
||||
+ this.#settings.set_string('background-color', backgroundColor);
|
||||
+ });
|
||||
+ this.add(row);
|
||||
+
|
||||
+ activatableWidget = new Gtk.Entry({
|
||||
+ valign: Gtk.Align.CENTER,
|
||||
+ });
|
||||
+ this.#settings.bind('message',
|
||||
+ activatableWidget, 'text',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+ row = new Adw.ActionRow({title: _('Message'), activatableWidget});
|
||||
+ row.add_suffix(activatableWidget);
|
||||
+ this.add(row);
|
||||
+
|
||||
+ activatableWidget = this.#createColorButton('background-color', {
|
||||
+ use_alpha: true,
|
||||
+ });
|
||||
+ row = new Adw.ActionRow({title: _('Background color'), activatableWidget});
|
||||
+ row.add_suffix(activatableWidget);
|
||||
+ this.add(row);
|
||||
+
|
||||
+ activatableWidget = this.#createColorButton('color');
|
||||
+ row = new Adw.ActionRow({title: _('Text color'), activatableWidget});
|
||||
+ row.add_suffix(activatableWidget);
|
||||
+ this.add(row);
|
||||
+ }
|
||||
+
|
||||
+ #createPresetsRow(model) {
|
||||
+ const listFactory = new Gtk.SignalListItemFactory();
|
||||
+ listFactory.connect('setup',
|
||||
+ (f, item) => item.set_child(new Gtk.Label()));
|
||||
+ listFactory.connect('bind', (f, listItem) => {
|
||||
+ const {child, item} = listItem;
|
||||
+
|
||||
+ const provider = new Gtk.CssProvider();
|
||||
+ provider.load_from_data(`* {
|
||||
+ border-radius: 99px;
|
||||
+ padding: 6px;
|
||||
+ color: ${item.color};
|
||||
+ background-color: ${item.background_color};
|
||||
+ }`, -1);
|
||||
+ child.get_style_context().add_provider(provider,
|
||||
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
+ child.label = item.message;
|
||||
+ });
|
||||
+
|
||||
+ return new Adw.ComboRow({
|
||||
+ title: _('Presets'),
|
||||
+ model,
|
||||
+ listFactory,
|
||||
+ expression: Gtk.ConstantExpression.new_for_value(''),
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ #createColorButton(key, params = {}) {
|
||||
+ const rgba = new Gdk.RGBA();
|
||||
+ rgba.parse(this.#settings.get_string(key));
|
||||
+
|
||||
+ const button = new Gtk.ColorButton({
|
||||
+ ...params,
|
||||
+ rgba,
|
||||
+ valign: Gtk.Align.CENTER,
|
||||
+ });
|
||||
+ this.#settings.connect(`changed::${key}`, () => {
|
||||
+ const newRgba = new Gdk.RGBA();
|
||||
+ newRgba.parse(this.#settings.get_string(key));
|
||||
+ if (!newRgba.equal(button.rgba))
|
||||
+ button.set({rgba: newRgba});
|
||||
+ });
|
||||
+ button.connect('notify::rgba',
|
||||
+ () => this.#settings.set_string(key, button.rgba.to_string()));
|
||||
+ return button;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+export default class ClassificationPrefs extends ExtensionPreferences {
|
||||
+ getPreferencesWidget() {
|
||||
+ const page = new Adw.PreferencesPage();
|
||||
+ page.add(new AppearancePrefs(this.getSettings()));
|
||||
+ page.add(new GenericPrefs(this.getSettings()));
|
||||
+ return page;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/classification-banner/stylesheet.css b/extensions/classification-banner/stylesheet.css
|
||||
new file mode 100644
|
||||
index 00000000..fb6a697e
|
||||
--- /dev/null
|
||||
+++ b/extensions/classification-banner/stylesheet.css
|
||||
@@ -0,0 +1,3 @@
|
||||
+.classification-system-info { padding: 0 24px; }
|
||||
+.classification-message { font-weight: bold; }
|
||||
+.classification-banner { font-size: 0.9em; }
|
||||
diff --git a/meson.build b/meson.build
|
||||
index c78d0cc6..b3dac8de 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -51,6 +51,7 @@ default_extensions += [
|
||||
all_extensions = default_extensions
|
||||
all_extensions += [
|
||||
'auto-move-windows',
|
||||
+ 'classification-banner',
|
||||
'gesture-inhibitor',
|
||||
'native-window-placement',
|
||||
'user-theme'
|
||||
--
|
||||
2.45.0
|
||||
|
878
extra-extensions-0004-Add-heads-up-display.patch
Normal file
878
extra-extensions-0004-Add-heads-up-display.patch
Normal file
@ -0,0 +1,878 @@
|
||||
From 94e0261560490bdfe37df5d9f5ff1cbac9533d6a Mon Sep 17 00:00:00 2001
|
||||
From: Ray Strode <rstrode@redhat.com>
|
||||
Date: Tue, 24 Aug 2021 15:03:57 -0400
|
||||
Subject: [PATCH 4/5] Add heads-up-display
|
||||
|
||||
---
|
||||
extensions/heads-up-display/extension.js | 404 ++++++++++++++++++
|
||||
extensions/heads-up-display/headsUpMessage.js | 166 +++++++
|
||||
extensions/heads-up-display/meson.build | 13 +
|
||||
extensions/heads-up-display/metadata.json.in | 12 +
|
||||
...ll.extensions.heads-up-display.gschema.xml | 60 +++
|
||||
extensions/heads-up-display/prefs.js | 92 ++++
|
||||
extensions/heads-up-display/stylesheet.css | 38 ++
|
||||
meson.build | 1 +
|
||||
po/POTFILES.in | 1 +
|
||||
9 files changed, 787 insertions(+)
|
||||
create mode 100644 extensions/heads-up-display/extension.js
|
||||
create mode 100644 extensions/heads-up-display/headsUpMessage.js
|
||||
create mode 100644 extensions/heads-up-display/meson.build
|
||||
create mode 100644 extensions/heads-up-display/metadata.json.in
|
||||
create mode 100644 extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml
|
||||
create mode 100644 extensions/heads-up-display/prefs.js
|
||||
create mode 100644 extensions/heads-up-display/stylesheet.css
|
||||
|
||||
diff --git a/extensions/heads-up-display/extension.js b/extensions/heads-up-display/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..a71b5925
|
||||
--- /dev/null
|
||||
+++ b/extensions/heads-up-display/extension.js
|
||||
@@ -0,0 +1,404 @@
|
||||
+// SPDX-FileCopyrightText: 2021 Ray Strode <rstrode@redhat.com>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import GObject from 'gi://GObject';
|
||||
+import Meta from 'gi://Meta';
|
||||
+import Mtk from 'gi://Mtk';
|
||||
+
|
||||
+import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import {MonitorConstraint} from 'resource:///org/gnome/shell/ui/layout.js';
|
||||
+
|
||||
+import {HeadsUpMessage} from './headsUpMessage.js';
|
||||
+
|
||||
+var HeadsUpConstraint = GObject.registerClass({
|
||||
+ Properties: {
|
||||
+ 'offset': GObject.ParamSpec.int(
|
||||
+ 'offset', 'Offset', 'offset',
|
||||
+ GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
|
||||
+ -1, 0, -1),
|
||||
+ 'active': GObject.ParamSpec.boolean(
|
||||
+ 'active', 'Active', 'active',
|
||||
+ GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
|
||||
+ true),
|
||||
+ },
|
||||
+}, class HeadsUpConstraint extends MonitorConstraint {
|
||||
+ constructor(props) {
|
||||
+ super(props);
|
||||
+ this._offset = 0;
|
||||
+ this._active = true;
|
||||
+ }
|
||||
+
|
||||
+ get offset() {
|
||||
+ return this._offset;
|
||||
+ }
|
||||
+
|
||||
+ set offset(o) {
|
||||
+ this._offset = o;
|
||||
+ }
|
||||
+
|
||||
+ get active() {
|
||||
+ return this._active;
|
||||
+ }
|
||||
+
|
||||
+ set active(a) {
|
||||
+ this._active = a;
|
||||
+ }
|
||||
+
|
||||
+ vfunc_update_allocation(actor, actorBox) {
|
||||
+ if (!Main.layoutManager.primaryMonitor)
|
||||
+ return;
|
||||
+
|
||||
+ if (!this.active)
|
||||
+ return;
|
||||
+
|
||||
+ if (actor.has_allocation())
|
||||
+ return;
|
||||
+
|
||||
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||
+ actorBox.init_rect(workArea.x, workArea.y + this.offset, workArea.width, workArea.height - this.offset);
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+export default class HeadsUpDisplayExtension extends Extension {
|
||||
+ enable() {
|
||||
+ this._settings = this.getSettings('org.gnome.shell.extensions.heads-up-display');
|
||||
+ this._settings.connectObject('changed',
|
||||
+ () => this._updateMessage(), this);
|
||||
+
|
||||
+ this._idleMonitor = global.backend.get_core_idle_monitor();
|
||||
+ this._messageInhibitedUntilIdle = false;
|
||||
+ global.window_manager.connectObject('map',
|
||||
+ this._onWindowMap.bind(this), this);
|
||||
+
|
||||
+ if (Main.layoutManager._startingUp)
|
||||
+ Main.layoutManager.connectObject('startup-complete', () => this._onStartupComplete(), this);
|
||||
+ else
|
||||
+ this._onStartupComplete();
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ this._dismissMessage();
|
||||
+
|
||||
+ this._stopWatchingForIdle();
|
||||
+
|
||||
+ Main.sessionMode.disconnectObject(this);
|
||||
+ Main.overview.disconnectObject(this);
|
||||
+ Main.layoutManager.panelBox.disconnectObject(this);
|
||||
+ Main.layoutManager.disconnectObject(this);
|
||||
+ global.window_manager.disconnectObject(this);
|
||||
+
|
||||
+ if (this._screenShieldVisibleId) {
|
||||
+ Main.screenShield._dialog._clock.disconnect(this._screenShieldVisibleId);
|
||||
+ this._screenShieldVisibleId = 0;
|
||||
+ }
|
||||
+
|
||||
+ this._settings.disconnectObject(this);
|
||||
+ delete this._settings;
|
||||
+ }
|
||||
+
|
||||
+ _onWindowMap(shellwm, actor) {
|
||||
+ const windowObject = actor.meta_window;
|
||||
+ const windowType = windowObject.get_window_type();
|
||||
+
|
||||
+ if (windowType !== Meta.WindowType.NORMAL)
|
||||
+ return;
|
||||
+
|
||||
+ if (!this._message || !this._message.visible)
|
||||
+ return;
|
||||
+
|
||||
+ const messageRect = new Mtk.Rectangle({
|
||||
+ x: this._message.x,
|
||||
+ y: this._message.y,
|
||||
+ width: this._message.width,
|
||||
+ height: this._message.height,
|
||||
+ });
|
||||
+ const windowRect = windowObject.get_frame_rect();
|
||||
+
|
||||
+ if (windowRect.intersect(messageRect))
|
||||
+ windowObject.move_frame(false, windowRect.x, this._message.y + this._message.height);
|
||||
+ }
|
||||
+
|
||||
+ _onStartupComplete() {
|
||||
+ Main.overview.connectObject(
|
||||
+ 'showing', () => this._updateMessage(),
|
||||
+ 'hidden', () => this._updateMessage(),
|
||||
+ this);
|
||||
+ Main.layoutManager.panelBox.connectObject('notify::visible',
|
||||
+ () => this._updateMessage(), this);
|
||||
+ Main.sessionMode.connectObject('updated',
|
||||
+ () => this._onSessionModeUpdated(), this);
|
||||
+
|
||||
+ this._updateMessage();
|
||||
+ }
|
||||
+
|
||||
+ _onSessionModeUpdated() {
|
||||
+ if (!Main.sessionMode.hasWindows)
|
||||
+ this._messageInhibitedUntilIdle = false;
|
||||
+
|
||||
+ const dialog = Main.screenShield._dialog;
|
||||
+ if (!Main.sessionMode.isGreeter && dialog && !this._screenShieldVisibleId) {
|
||||
+ this._screenShieldVisibleId = dialog._clock.connect('notify::visible', this._updateMessage.bind(this));
|
||||
+ this._screenShieldDestroyId = dialog._clock.connect('destroy', () => {
|
||||
+ this._screenShieldVisibleId = 0;
|
||||
+ this._screenShieldDestroyId = 0;
|
||||
+ });
|
||||
+ }
|
||||
+ this._updateMessage();
|
||||
+ }
|
||||
+
|
||||
+ _stopWatchingForIdle() {
|
||||
+ if (this._idleWatchId) {
|
||||
+ this._idleMonitor.remove_watch(this._idleWatchId);
|
||||
+ this._idleWatchId = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (this._idleTimeoutChangedId) {
|
||||
+ this._settings.disconnect(this._idleTimeoutChangedId);
|
||||
+ this._idleTimeoutChangedId = 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _onIdleTimeoutChanged() {
|
||||
+ this._stopWatchingForIdle();
|
||||
+ this._messageInhibitedUntilIdle = false;
|
||||
+ }
|
||||
+
|
||||
+ _onUserIdle() {
|
||||
+ this._messageInhibitedUntilIdle = false;
|
||||
+ this._updateMessage();
|
||||
+ }
|
||||
+
|
||||
+ _watchForIdle() {
|
||||
+ this._stopWatchingForIdle();
|
||||
+
|
||||
+ const idleTimeout = this._settings.get_uint('idle-timeout');
|
||||
+
|
||||
+ this._idleTimeoutChangedId =
|
||||
+ this._settings.connect('changed::idle-timeout',
|
||||
+ this._onIdleTimeoutChanged.bind(this));
|
||||
+ this._idleWatchId = this._idleMonitor.add_idle_watch(idleTimeout * 1000,
|
||||
+ this._onUserIdle.bind(this));
|
||||
+ }
|
||||
+
|
||||
+ _updateMessage() {
|
||||
+ if (this._messageInhibitedUntilIdle) {
|
||||
+ if (this._message)
|
||||
+ this._dismissMessage();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ this._stopWatchingForIdle();
|
||||
+
|
||||
+ if (Main.sessionMode.hasOverview && Main.overview.visible) {
|
||||
+ this._dismissMessage();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!Main.layoutManager.panelBox.visible) {
|
||||
+ this._dismissMessage();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ let supportedModes = [];
|
||||
+
|
||||
+ if (this._settings.get_boolean('show-when-unlocked'))
|
||||
+ supportedModes.push('user');
|
||||
+
|
||||
+ if (this._settings.get_boolean('show-when-unlocking') ||
|
||||
+ this._settings.get_boolean('show-when-locked'))
|
||||
+ supportedModes.push('unlock-dialog');
|
||||
+
|
||||
+ if (this._settings.get_boolean('show-on-login-screen'))
|
||||
+ supportedModes.push('gdm');
|
||||
+
|
||||
+ if (!supportedModes.includes(Main.sessionMode.currentMode) &&
|
||||
+ !supportedModes.includes(Main.sessionMode.parentMode)) {
|
||||
+ this._dismissMessage();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (Main.sessionMode.currentMode === 'unlock-dialog') {
|
||||
+ const dialog = Main.screenShield._dialog;
|
||||
+ if (!this._settings.get_boolean('show-when-locked')) {
|
||||
+ if (dialog._clock.visible) {
|
||||
+ this._dismissMessage();
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!this._settings.get_boolean('show-when-unlocking')) {
|
||||
+ if (!dialog._clock.visible) {
|
||||
+ this._dismissMessage();
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ const heading = this._settings.get_string('message-heading');
|
||||
+ const body = this._settings.get_string('message-body');
|
||||
+
|
||||
+ if (!heading && !body) {
|
||||
+ this._dismissMessage();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!this._message) {
|
||||
+ this._message = new HeadsUpMessage(heading, body);
|
||||
+
|
||||
+ this._message.connect('notify::allocation', this._adaptSessionForMessage.bind(this));
|
||||
+ this._message.connect('clicked', this._onMessageClicked.bind(this));
|
||||
+ }
|
||||
+
|
||||
+ this._message.reactive = true;
|
||||
+ this._message.track_hover = true;
|
||||
+
|
||||
+ this._message.setHeading(heading);
|
||||
+ this._message.setBody(body);
|
||||
+
|
||||
+ if (!Main.sessionMode.hasWindows) {
|
||||
+ this._message.track_hover = false;
|
||||
+ this._message.reactive = false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _onMessageClicked() {
|
||||
+ if (!Main.sessionMode.hasWindows)
|
||||
+ return;
|
||||
+
|
||||
+ this._watchForIdle();
|
||||
+ this._messageInhibitedUntilIdle = true;
|
||||
+ this._updateMessage();
|
||||
+ }
|
||||
+
|
||||
+ _dismissMessage() {
|
||||
+ if (!this._message)
|
||||
+ return;
|
||||
+
|
||||
+ this._message.visible = false;
|
||||
+ this._message.destroy();
|
||||
+ this._message = null;
|
||||
+ this._resetMessageTray();
|
||||
+ this._resetLoginDialog();
|
||||
+ }
|
||||
+
|
||||
+ _resetMessageTray() {
|
||||
+ if (!Main.messageTray)
|
||||
+ return;
|
||||
+
|
||||
+ if (this._updateMessageTrayId) {
|
||||
+ global.stage.disconnect(this._updateMessageTrayId);
|
||||
+ this._updateMessageTrayId = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (this._messageTrayConstraint) {
|
||||
+ Main.messageTray.remove_constraint(this._messageTrayConstraint);
|
||||
+ this._messageTrayConstraint = null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _alignMessageTray() {
|
||||
+ if (!Main.messageTray)
|
||||
+ return;
|
||||
+
|
||||
+ if (!this._message || !this._message.visible) {
|
||||
+ this._resetMessageTray();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (this._updateMessageTrayId)
|
||||
+ return;
|
||||
+
|
||||
+ this._updateMessageTrayId = global.stage.connect('before-update', () => {
|
||||
+ if (!this._messageTrayConstraint) {
|
||||
+ this._messageTrayConstraint = new HeadsUpConstraint({primary: true});
|
||||
+
|
||||
+ Main.layoutManager.panelBox.bind_property('visible',
|
||||
+ this._messageTrayConstraint, 'active',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
+
|
||||
+ Main.messageTray.add_constraint(this._messageTrayConstraint);
|
||||
+ }
|
||||
+
|
||||
+ const panelBottom = Main.layoutManager.panelBox.y + Main.layoutManager.panelBox.height;
|
||||
+ const messageBottom = this._message.y + this._message.height;
|
||||
+
|
||||
+ this._messageTrayConstraint.offset = messageBottom - panelBottom;
|
||||
+ global.stage.disconnect(this._updateMessageTrayId);
|
||||
+ this._updateMessageTrayId = 0;
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ _resetLoginDialog() {
|
||||
+ if (!Main.sessionMode.isGreeter)
|
||||
+ return;
|
||||
+
|
||||
+ if (!Main.screenShield || !Main.screenShield._dialog)
|
||||
+ return;
|
||||
+
|
||||
+ const dialog = Main.screenShield._dialog;
|
||||
+
|
||||
+ if (this._authPromptAllocatedId) {
|
||||
+ dialog.disconnect(this._authPromptAllocatedId);
|
||||
+ this._authPromptAllocatedId = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (this._updateLoginDialogId) {
|
||||
+ global.stage.disconnect(this._updateLoginDialogId);
|
||||
+ this._updateLoginDialogId = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (this._loginDialogConstraint) {
|
||||
+ dialog.remove_constraint(this._loginDialogConstraint);
|
||||
+ this._loginDialogConstraint = null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _adaptLoginDialogForMessage() {
|
||||
+ if (!Main.sessionMode.isGreeter)
|
||||
+ return;
|
||||
+
|
||||
+ if (!Main.screenShield || !Main.screenShield._dialog)
|
||||
+ return;
|
||||
+
|
||||
+ if (!this._message || !this._message.visible) {
|
||||
+ this._resetLoginDialog();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const dialog = Main.screenShield._dialog;
|
||||
+
|
||||
+ if (this._updateLoginDialogId)
|
||||
+ return;
|
||||
+
|
||||
+ this._updateLoginDialogId = global.stage.connect('before-update', () => {
|
||||
+ let messageHeight = this._message.y + this._message.height;
|
||||
+ if (dialog._logoBin.visible)
|
||||
+ messageHeight -= dialog._logoBin.height;
|
||||
+
|
||||
+ if (!this._logindDialogConstraint) {
|
||||
+ this._loginDialogConstraint = new HeadsUpConstraint({primary: true});
|
||||
+ dialog.add_constraint(this._loginDialogConstraint);
|
||||
+ }
|
||||
+
|
||||
+ this._loginDialogConstraint.offset = messageHeight;
|
||||
+
|
||||
+ global.stage.disconnect(this._updateLoginDialogId);
|
||||
+ this._updateLoginDialogId = 0;
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ _adaptSessionForMessage() {
|
||||
+ this._alignMessageTray();
|
||||
+
|
||||
+ if (Main.sessionMode.isGreeter) {
|
||||
+ this._adaptLoginDialogForMessage();
|
||||
+ if (!this._authPromptAllocatedId) {
|
||||
+ const dialog = Main.screenShield._dialog;
|
||||
+ this._authPromptAllocatedId = dialog._authPrompt.connect('notify::allocation', this._adaptLoginDialogForMessage.bind(this));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/heads-up-display/headsUpMessage.js b/extensions/heads-up-display/headsUpMessage.js
|
||||
new file mode 100644
|
||||
index 00000000..30298847
|
||||
--- /dev/null
|
||||
+++ b/extensions/heads-up-display/headsUpMessage.js
|
||||
@@ -0,0 +1,166 @@
|
||||
+// SPDX-FileCopyrightText: 2021 Ray Strode <rstrode@redhat.com>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import Atk from 'gi://Atk';
|
||||
+import Clutter from 'gi://Clutter';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+
|
||||
+const HeadsUpMessageBodyLabel = GObject.registerClass({
|
||||
+}, class HeadsUpMessageBodyLabel extends St.Label {
|
||||
+ constructor(params) {
|
||||
+ super(params);
|
||||
+
|
||||
+ this._widthCoverage = 0.75;
|
||||
+ this._heightCoverage = 0.25;
|
||||
+
|
||||
+ global.display.connectObject('workareas-changed',
|
||||
+ () => this._getWorkAreaAndMeasureLineHeight());
|
||||
+ }
|
||||
+
|
||||
+ _getWorkAreaAndMeasureLineHeight() {
|
||||
+ if (!this.get_parent())
|
||||
+ return;
|
||||
+
|
||||
+ this._workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||
+
|
||||
+ this.clutter_text.single_line_mode = true;
|
||||
+ this.clutter_text.line_wrap = false;
|
||||
+
|
||||
+ this._lineHeight = super.vfunc_get_preferred_height(-1)[0];
|
||||
+
|
||||
+ this.clutter_text.single_line_mode = false;
|
||||
+ this.clutter_text.line_wrap = true;
|
||||
+ }
|
||||
+
|
||||
+ vfunc_parent_set() {
|
||||
+ this._getWorkAreaAndMeasureLineHeight();
|
||||
+ }
|
||||
+
|
||||
+ vfunc_get_preferred_width(forHeight) {
|
||||
+ const maxWidth = this._widthCoverage * this._workArea.width;
|
||||
+
|
||||
+ let [labelMinimumWidth, labelNaturalWidth] = super.vfunc_get_preferred_width(forHeight);
|
||||
+
|
||||
+ labelMinimumWidth = Math.min(labelMinimumWidth, maxWidth);
|
||||
+ labelNaturalWidth = Math.min(labelNaturalWidth, maxWidth);
|
||||
+
|
||||
+ return [labelMinimumWidth, labelNaturalWidth];
|
||||
+ }
|
||||
+
|
||||
+ vfunc_get_preferred_height(forWidth) {
|
||||
+ const labelHeightUpperBound = this._heightCoverage * this._workArea.height;
|
||||
+ const numberOfLines = Math.floor(labelHeightUpperBound / this._lineHeight);
|
||||
+ this._numberOfLines = Math.max(numberOfLines, 1);
|
||||
+
|
||||
+ const maxHeight = this._lineHeight * this._numberOfLines;
|
||||
+
|
||||
+ let [labelMinimumHeight, labelNaturalHeight] = super.vfunc_get_preferred_height(forWidth);
|
||||
+
|
||||
+ labelMinimumHeight = Math.min(labelMinimumHeight, maxHeight);
|
||||
+ labelNaturalHeight = Math.min(labelNaturalHeight, maxHeight);
|
||||
+
|
||||
+ return [labelMinimumHeight, labelNaturalHeight];
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+export const HeadsUpMessage = GObject.registerClass({
|
||||
+}, class HeadsUpMessage extends St.Button {
|
||||
+ constructor(heading, body) {
|
||||
+ super({
|
||||
+ style_class: 'message',
|
||||
+ accessible_role: Atk.Role.NOTIFICATION,
|
||||
+ can_focus: false,
|
||||
+ opacity: 0,
|
||||
+ });
|
||||
+
|
||||
+ Main.layoutManager.addChrome(this, {affectsInputRegion: true});
|
||||
+
|
||||
+ this.add_style_class_name('heads-up-display-message');
|
||||
+
|
||||
+ this.connect('destroy', () => this._onDestroy());
|
||||
+
|
||||
+ Main.layoutManager.panelBox.connectObject('notify::allocation',
|
||||
+ () => this._alignWithPanel());
|
||||
+ this.connect('notify::allocation',
|
||||
+ () => this._alignWithPanel());
|
||||
+
|
||||
+ const contentsBox = new St.BoxLayout({
|
||||
+ style_class: 'heads-up-message-content',
|
||||
+ vertical: true,
|
||||
+ x_align: Clutter.ActorAlign.CENTER,
|
||||
+ });
|
||||
+ this.add_child(contentsBox);
|
||||
+
|
||||
+ this._headingLabel = new St.Label({
|
||||
+ style_class: 'heads-up-message-heading',
|
||||
+ x_expand: true,
|
||||
+ x_align: Clutter.ActorAlign.CENTER,
|
||||
+ });
|
||||
+
|
||||
+ this.setHeading(heading);
|
||||
+ contentsBox.add_child(this._headingLabel);
|
||||
+
|
||||
+ this._bodyLabel = new HeadsUpMessageBodyLabel({
|
||||
+ style_class: 'heads-up-message-body',
|
||||
+ x_expand: true,
|
||||
+ y_expand: true,
|
||||
+ });
|
||||
+ contentsBox.add_child(this._bodyLabel);
|
||||
+
|
||||
+ this.setBody(body);
|
||||
+ }
|
||||
+
|
||||
+ vfunc_parent_set() {
|
||||
+ this._alignWithPanel();
|
||||
+ }
|
||||
+
|
||||
+ _alignWithPanel() {
|
||||
+ if (this._beforeUpdateId)
|
||||
+ return;
|
||||
+
|
||||
+ this._beforeUpdateId = global.stage.connect('before-update', () => {
|
||||
+ let x = Main.panel.x;
|
||||
+ let y = Main.panel.y + Main.panel.height;
|
||||
+
|
||||
+ x += Main.panel.width / 2;
|
||||
+ x -= this.width / 2;
|
||||
+ x = Math.floor(x);
|
||||
+ this.set_position(x, y);
|
||||
+ this.opacity = 255;
|
||||
+
|
||||
+ global.stage.disconnect(this._beforeUpdateId);
|
||||
+ this._beforeUpdateId = 0;
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ setHeading(text) {
|
||||
+ if (text) {
|
||||
+ const heading = text ? text.replace(/\n/g, ' ') : '';
|
||||
+ this._headingLabel.text = heading;
|
||||
+ this._headingLabel.visible = true;
|
||||
+ } else {
|
||||
+ this._headingLabel.text = text;
|
||||
+ this._headingLabel.visible = false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ setBody(text) {
|
||||
+ this._bodyLabel.text = text;
|
||||
+
|
||||
+ if (text)
|
||||
+ this._bodyLabel.visible = true;
|
||||
+ else
|
||||
+ this._bodyLabel.visible = false;
|
||||
+ }
|
||||
+
|
||||
+ _onDestroy() {
|
||||
+ if (this._beforeUpdateId) {
|
||||
+ global.stage.disconnect(this._beforeUpdateId);
|
||||
+ this._beforeUpdateId = 0;
|
||||
+ }
|
||||
+ }
|
||||
+});
|
||||
diff --git a/extensions/heads-up-display/meson.build b/extensions/heads-up-display/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..42ce222c
|
||||
--- /dev/null
|
||||
+++ b/extensions/heads-up-display/meson.build
|
||||
@@ -0,0 +1,13 @@
|
||||
+# SPDX-FileCopyrightText: 2021 Ray Strode <rstrode@redhat.com>
|
||||
+#
|
||||
+# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
+
|
||||
+extension_data += files('stylesheet.css')
|
||||
+extension_sources += files('headsUpMessage.js', 'prefs.js')
|
||||
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
diff --git a/extensions/heads-up-display/metadata.json.in b/extensions/heads-up-display/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..01bcd4df
|
||||
--- /dev/null
|
||||
+++ b/extensions/heads-up-display/metadata.json.in
|
||||
@@ -0,0 +1,12 @@
|
||||
+{
|
||||
+"extension-id": "@extension_id@",
|
||||
+"uuid": "@uuid@",
|
||||
+"settings-schema": "@gschemaname@",
|
||||
+"gettext-domain": "@gettext_domain@",
|
||||
+"name": "Heads-up Display Message",
|
||||
+"description": "Add a message to be displayed on screen always above all windows and chrome.",
|
||||
+"original-authors": [ "rstrode@redhat.com" ],
|
||||
+"shell-version": [ "@shell_current@" ],
|
||||
+"url": "@url@",
|
||||
+"session-modes": [ "gdm", "lock-screen", "unlock-dialog", "user" ]
|
||||
+}
|
||||
diff --git a/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml b/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml
|
||||
new file mode 100644
|
||||
index 00000000..1e2119c8
|
||||
--- /dev/null
|
||||
+++ b/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml
|
||||
@@ -0,0 +1,60 @@
|
||||
+<!--
|
||||
+SPDX-FileCopyrightText: 2021 Ray Strode <rstrode@redhat.com>
|
||||
+
|
||||
+SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+-->
|
||||
+
|
||||
+<schemalist gettext-domain="gnome-shell-extensions">
|
||||
+ <schema id="org.gnome.shell.extensions.heads-up-display"
|
||||
+ path="/org/gnome/shell/extensions/heads-up-display/">
|
||||
+ <key name="idle-timeout" type="u">
|
||||
+ <default>30</default>
|
||||
+ <summary>Idle Timeout</summary>
|
||||
+ <description>
|
||||
+ Number of seconds until message is reshown after user goes idle.
|
||||
+ </description>
|
||||
+ </key>
|
||||
+ <key name="message-heading" type="s">
|
||||
+ <default>""</default>
|
||||
+ <summary>Message to show at top of display</summary>
|
||||
+ <description>
|
||||
+ The top line of the heads up display message.
|
||||
+ </description>
|
||||
+ </key>
|
||||
+ <key name="message-body" type="s">
|
||||
+ <default>""</default>
|
||||
+ <summary>Banner message</summary>
|
||||
+ <description>
|
||||
+ A message to always show at the top of the screen.
|
||||
+ </description>
|
||||
+ </key>
|
||||
+ <key name="show-on-login-screen" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show on login screen</summary>
|
||||
+ <description>
|
||||
+ Whether or not the message should display on the login screen
|
||||
+ </description>
|
||||
+ </key>
|
||||
+ <key name="show-when-locked" type="b">
|
||||
+ <default>false</default>
|
||||
+ <summary>Show on screen shield</summary>
|
||||
+ <description>
|
||||
+ Whether or not the message should display when the screen is locked
|
||||
+ </description>
|
||||
+ </key>
|
||||
+ <key name="show-when-unlocking" type="b">
|
||||
+ <default>false</default>
|
||||
+ <summary>Show on unlock screen</summary>
|
||||
+ <description>
|
||||
+ Whether or not the message should display on the unlock screen.
|
||||
+ </description>
|
||||
+ </key>
|
||||
+ <key name="show-when-unlocked" type="b">
|
||||
+ <default>false</default>
|
||||
+ <summary>Show in user session</summary>
|
||||
+ <description>
|
||||
+ Whether or not the message should display when the screen is unlocked.
|
||||
+ </description>
|
||||
+ </key>
|
||||
+ </schema>
|
||||
+</schemalist>
|
||||
diff --git a/extensions/heads-up-display/prefs.js b/extensions/heads-up-display/prefs.js
|
||||
new file mode 100644
|
||||
index 00000000..304c8813
|
||||
--- /dev/null
|
||||
+++ b/extensions/heads-up-display/prefs.js
|
||||
@@ -0,0 +1,92 @@
|
||||
+// SPDX-FileCopyrightText: 2021 Ray Strode <rstrode@redhat.com>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import Adw from 'gi://Adw';
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import Gtk from 'gi://Gtk';
|
||||
+
|
||||
+import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||
+
|
||||
+class GeneralGroup extends Adw.PreferencesGroup {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ constructor(settings) {
|
||||
+ super();
|
||||
+
|
||||
+ const actionGroup = new Gio.SimpleActionGroup();
|
||||
+ this.insert_action_group('options', actionGroup);
|
||||
+
|
||||
+ actionGroup.add_action(settings.create_action('show-when-locked'));
|
||||
+ actionGroup.add_action(settings.create_action('show-when-unlocking'));
|
||||
+ actionGroup.add_action(settings.create_action('show-when-unlocked'));
|
||||
+
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('Show message when screen is locked'),
|
||||
+ action_name: 'options.show-when-locked',
|
||||
+ }));
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('Show message on unlock screen'),
|
||||
+ action_name: 'options.show-when-unlocking',
|
||||
+ }));
|
||||
+ this.add(new Adw.SwitchRow({
|
||||
+ title: _('Show message when screen is unlocked'),
|
||||
+ action_name: 'options.show-when-unlocked',
|
||||
+ }));
|
||||
+
|
||||
+ const spinRow = new Adw.SpinRow({
|
||||
+ title: _('Seconds after user goes idle before reshowing message'),
|
||||
+ adjustment: new Gtk.Adjustment({
|
||||
+ lower: 0,
|
||||
+ upper: 2147483647,
|
||||
+ step_increment: 1,
|
||||
+ page_increment: 60,
|
||||
+ page_size: 60,
|
||||
+ }),
|
||||
+ });
|
||||
+ settings.bind('idle-timeout',
|
||||
+ spinRow, 'value',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+ this.add(spinRow);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class MessageGroup extends Adw.PreferencesGroup {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ constructor(settings) {
|
||||
+ super({
|
||||
+ title: _('Message'),
|
||||
+ });
|
||||
+
|
||||
+ const textView = new Gtk.TextView({
|
||||
+ accepts_tab: false,
|
||||
+ wrap_mode: Gtk.WrapMode.WORD,
|
||||
+ top_margin: 6,
|
||||
+ bottom_margin: 6,
|
||||
+ left_margin: 6,
|
||||
+ right_margin: 6,
|
||||
+ vexpand: true,
|
||||
+ });
|
||||
+ textView.add_css_class('card');
|
||||
+
|
||||
+ settings.bind('message-body',
|
||||
+ textView.get_buffer(), 'text',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+ this.add(textView);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+export default class HeadsUpDisplayPrefs extends ExtensionPreferences {
|
||||
+ getPreferencesWidget() {
|
||||
+ const page = new Adw.PreferencesPage();
|
||||
+ page.add(new GeneralGroup(this.getSettings()));
|
||||
+ page.add(new MessageGroup(this.getSettings()));
|
||||
+ return page;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/heads-up-display/stylesheet.css b/extensions/heads-up-display/stylesheet.css
|
||||
new file mode 100644
|
||||
index 00000000..a1a34e3f
|
||||
--- /dev/null
|
||||
+++ b/extensions/heads-up-display/stylesheet.css
|
||||
@@ -0,0 +1,38 @@
|
||||
+/*
|
||||
+ * SPDX-FileCopyrightText: 2021 Ray Strode <rstrode@redhat.com>
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+ */
|
||||
+
|
||||
+.heads-up-display-message {
|
||||
+ background-color: rgba(0.24, 0.24, 0.24, 0.80);
|
||||
+ border: 1px solid black;
|
||||
+ border-radius: 6px;
|
||||
+ color: #eeeeec;
|
||||
+ font-size: 11pt;
|
||||
+ margin-top: 0.5em;
|
||||
+ margin-bottom: 0.5em;
|
||||
+ padding: 0.9em;
|
||||
+}
|
||||
+
|
||||
+.heads-up-display-message:insensitive {
|
||||
+ background-color: rgba(0.24, 0.24, 0.24, 0.33);
|
||||
+}
|
||||
+
|
||||
+.heads-up-display-message:hover {
|
||||
+ background-color: rgba(0.24, 0.24, 0.24, 0.2);
|
||||
+ border: 1px solid rgba(0.0, 0.0, 0.0, 0.5);
|
||||
+ color: #4d4d4d;
|
||||
+ transition-duration: 250ms;
|
||||
+}
|
||||
+
|
||||
+.heads-up-message-heading {
|
||||
+ height: 1.75em;
|
||||
+ font-size: 1.25em;
|
||||
+ font-weight: bold;
|
||||
+ text-align: center;
|
||||
+}
|
||||
+
|
||||
+.heads-up-message-body {
|
||||
+ text-align: center;
|
||||
+}
|
||||
diff --git a/meson.build b/meson.build
|
||||
index b3dac8de..b2a5d94d 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -40,6 +40,7 @@ classic_extensions = [
|
||||
default_extensions = classic_extensions
|
||||
default_extensions += [
|
||||
'drive-menu',
|
||||
+ 'heads-up-display',
|
||||
'light-style',
|
||||
'screenshot-window-sizer',
|
||||
'system-monitor',
|
||||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||||
index 4abfcfca..b77f6c21 100644
|
||||
--- a/po/POTFILES.in
|
||||
+++ b/po/POTFILES.in
|
||||
@@ -6,6 +6,7 @@ extensions/auto-move-windows/extension.js
|
||||
extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml
|
||||
extensions/auto-move-windows/prefs.js
|
||||
extensions/drive-menu/extension.js
|
||||
+extensions/heads-up-display/prefs.js
|
||||
extensions/native-window-placement/extension.js
|
||||
extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml
|
||||
extensions/places-menu/extension.js
|
||||
--
|
||||
2.45.0
|
||||
|
719
extra-extensions-0005-Add-custom-menu-extension.patch
Normal file
719
extra-extensions-0005-Add-custom-menu-extension.patch
Normal file
@ -0,0 +1,719 @@
|
||||
From 2e2bc1032163110993aa3433295b5d8c84978790 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 12 Jan 2023 19:43:52 +0100
|
||||
Subject: [PATCH 5/5] Add custom-menu extension
|
||||
|
||||
---
|
||||
extensions/custom-menu/config.js | 445 ++++++++++++++++++++++++
|
||||
extensions/custom-menu/extension.js | 201 +++++++++++
|
||||
extensions/custom-menu/meson.build | 7 +
|
||||
extensions/custom-menu/metadata.json.in | 10 +
|
||||
meson.build | 1 +
|
||||
5 files changed, 664 insertions(+)
|
||||
create mode 100644 extensions/custom-menu/config.js
|
||||
create mode 100644 extensions/custom-menu/extension.js
|
||||
create mode 100644 extensions/custom-menu/meson.build
|
||||
create mode 100644 extensions/custom-menu/metadata.json.in
|
||||
|
||||
diff --git a/extensions/custom-menu/config.js b/extensions/custom-menu/config.js
|
||||
new file mode 100644
|
||||
index 00000000..d08e3201
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/config.js
|
||||
@@ -0,0 +1,445 @@
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GLib from 'gi://GLib';
|
||||
+import Json from 'gi://Json';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
+
|
||||
+import {getLogger} from './extension.js';
|
||||
+
|
||||
+class Entry {
|
||||
+ constructor(prop) {
|
||||
+ this.type = prop.type;
|
||||
+ this.title = prop.title || "";
|
||||
+ this.__vars = prop.__vars || [];
|
||||
+ this.updateEnv(prop);
|
||||
+ }
|
||||
+
|
||||
+ setTitle(text) {
|
||||
+ this.item.label.get_clutter_text().set_text(text);
|
||||
+ }
|
||||
+
|
||||
+ updateEnv(prop) {
|
||||
+ this.__env = {}
|
||||
+ if (!this.__vars) return;
|
||||
+ for (let i in this.__vars) {
|
||||
+ let v = this.__vars[i];
|
||||
+ this.__env[v] = prop[v] ? String(prop[v]) : "";
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // the pulse function should be read as "a pulse arrives"
|
||||
+ pulse() {
|
||||
+ }
|
||||
+
|
||||
+ _try_destroy() {
|
||||
+ try {
|
||||
+ if (this.item && this.item.destroy) {
|
||||
+ this.item.destroy();
|
||||
+ }
|
||||
+ } catch(e) { /* Ignore all errors during destory*/ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class DerivedEntry {
|
||||
+ constructor(prop) {
|
||||
+ if (!prop.base) {
|
||||
+ throw new Error("Base entry not specified in type definition.");
|
||||
+ }
|
||||
+ this.base = prop.base;
|
||||
+ this.vars = prop.vars || [];
|
||||
+ delete prop.base;
|
||||
+ delete prop.vars;
|
||||
+ this.prop = prop;
|
||||
+ }
|
||||
+
|
||||
+ createInstance(addit_prop) {
|
||||
+ let cls = type_map[this.base];
|
||||
+ if (!cls) {
|
||||
+ throw new Error("Bad base class.");
|
||||
+ }
|
||||
+ if (cls.createInstance) {
|
||||
+ throw new Error("Not allowed to derive from dervied types");
|
||||
+ }
|
||||
+ for (let rp in this.prop) {
|
||||
+ addit_prop[rp] = this.prop[rp];
|
||||
+ }
|
||||
+ addit_prop.__vars = this.vars;
|
||||
+ let instance = new cls(addit_prop);
|
||||
+ return instance;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+let __pipeOpenQueue = [];
|
||||
+
|
||||
+/* callback: function (stdout, stderr, exit_status) { } */
|
||||
+function pipeOpen(cmdline, env, callback) {
|
||||
+ if (cmdline === undefined || callback === undefined) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ realPipeOpen(cmdline, env, callback);
|
||||
+ return true;
|
||||
+} /**/
|
||||
+
|
||||
+function realPipeOpen(cmdline, env, callback) {
|
||||
+ let user_cb = callback;
|
||||
+ let proc;
|
||||
+
|
||||
+ function wait_cb(_, _res) {
|
||||
+ let stdout_pipe = proc.get_stdout_pipe();
|
||||
+ let stderr_pipe = proc.get_stderr_pipe();
|
||||
+ let stdout_content;
|
||||
+ let stderr_content;
|
||||
+
|
||||
+ // Only the first GLib.MAXINT16 characters are fetched for optimization.
|
||||
+ stdout_pipe.read_bytes_async(GLib.MAXINT16, 0, null, function(osrc, ores) {
|
||||
+ const decoder = new TextDecoder();
|
||||
+ stdout_content = decoder.decode(stdout_pipe.read_bytes_finish(ores).get_data());
|
||||
+ stdout_pipe.close(null);
|
||||
+ stderr_pipe.read_bytes_async(GLib.MAXINT16, 0, null, function(esrc, eres) {
|
||||
+ stderr_content = decoder.decode(stderr_pipe.read_bytes_finish(eres).get_data());
|
||||
+ stderr_pipe.close(null);
|
||||
+ user_cb(stdout_content, stderr_content, proc.get_exit_status());
|
||||
+ });
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ if (user_cb) {
|
||||
+ let _pipedLauncher = new Gio.SubprocessLauncher({
|
||||
+ flags:
|
||||
+ Gio.SubprocessFlags.STDERR_PIPE |
|
||||
+ Gio.SubprocessFlags.STDOUT_PIPE
|
||||
+ });
|
||||
+ for (let key in env) {
|
||||
+ _pipedLauncher.setenv(key, env[key], true);
|
||||
+ }
|
||||
+ proc = _pipedLauncher.spawnv(['bash', '-c', cmdline]);
|
||||
+ proc.wait_async(null, wait_cb);
|
||||
+ } else {
|
||||
+ // Detached launcher is used to spawn commands that we are not concerned about its result.
|
||||
+ let _detacLauncher = new Gio.SubprocessLauncher();
|
||||
+ for (let key in env) {
|
||||
+ _detacLauncher.setenv(key, env[key], true);
|
||||
+ }
|
||||
+ proc = _detacLauncher.spawnv(['bash', '-c', cmdline]);
|
||||
+ }
|
||||
+ getLogger().info("Spawned " + cmdline);
|
||||
+ return proc.get_identifier();
|
||||
+}
|
||||
+
|
||||
+function _generalSpawn(command, env, title) {
|
||||
+ title = title || "Process";
|
||||
+ pipeOpen(command, env, function(stdout, stderr, exit_status) {
|
||||
+ if (exit_status != 0) {
|
||||
+ log
|
||||
+ getLogger().warning(stderr);
|
||||
+ getLogger().notify("proc", title + " exited with status " + exit_status, stderr);
|
||||
+ }
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+// Detect menu toggle on startup
|
||||
+function _toggleDetect(command, env, object) {
|
||||
+ pipeOpen(command, env, function(stdout, stderr, exit_status) {
|
||||
+ if (exit_status == 0) {
|
||||
+ object.item.setToggleState(true);
|
||||
+ }
|
||||
+ });
|
||||
+} /**/
|
||||
+
|
||||
+function quoteShellArg(arg) {
|
||||
+ arg = arg.replace(/'/g, "'\"'\"'");
|
||||
+ return "'" + arg + "'";
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * This cache is used to reduce detector cost.
|
||||
+ * Each time creating an item, it check if the result of this detector is cached,
|
||||
+ * which prevent the togglers from running detector on each creation.
|
||||
+ * This is useful especially in search mode.
|
||||
+ */
|
||||
+let _toggler_state_cache = { };
|
||||
+
|
||||
+class TogglerEntry extends Entry {
|
||||
+ constructor(prop) {
|
||||
+ super(prop);
|
||||
+ this.command_on = prop.command_on || "";
|
||||
+ this.command_off = prop.command_off || "";
|
||||
+ this.detector = prop.detector || "";
|
||||
+ this.auto_on = prop.auto_on || false;
|
||||
+ this.notify_when = prop.notify_when || [];
|
||||
+ // if the switch is manually turned off, auto_on is disabled.
|
||||
+ this._manually_switched_off = false;
|
||||
+ this.pulse(); // load initial state
|
||||
+ }
|
||||
+
|
||||
+ createItem() {
|
||||
+ this._try_destroy();
|
||||
+ this.item = new PopupMenu.PopupSwitchMenuItem(this.title, false);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ this.item.connect('toggled', this._onManuallyToggled.bind(this));
|
||||
+ this._loadState();
|
||||
+ _toggleDetect(this.detector, this.__env, this);
|
||||
+ return this.item;
|
||||
+ }
|
||||
+
|
||||
+ _onManuallyToggled(_, state) {
|
||||
+ // when switched on again, this flag will get cleared.
|
||||
+ this._manually_switched_off = !state;
|
||||
+ this._storeState(state);
|
||||
+ this._onToggled(state);
|
||||
+ }
|
||||
+
|
||||
+ _onToggled(state) {
|
||||
+ if (state) {
|
||||
+ _generalSpawn(this.command_on, this.__env, this.title);
|
||||
+ } else {
|
||||
+ _generalSpawn(this.command_off, this.__env, this.title);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _detect(callback) {
|
||||
+ // abort detecting if detector is an empty string
|
||||
+ if (!this.detector) {
|
||||
+ return;
|
||||
+ }
|
||||
+ pipeOpen(this.detector, this.__env, function(out) {
|
||||
+ out = String(out);
|
||||
+ callback(!Boolean(out.match(/^\s*$/)));
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ // compare the new state with cached state notify when state is different
|
||||
+ compareState(new_state) {
|
||||
+ let old_state = _toggler_state_cache[this.detector];
|
||||
+ if (old_state === undefined) return;
|
||||
+ if (old_state == new_state) return;
|
||||
+
|
||||
+ if (this.notify_when.indexOf(new_state ? "on" : "off") >= 0) {
|
||||
+ let not_str = this.title + (new_state ? " started." : " stopped.");
|
||||
+ if (!new_state && this.auto_on) {
|
||||
+ not_str += " Attempt to restart it now.";
|
||||
+ }
|
||||
+ getLogger().notify("state", not_str);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _storeState(state) {
|
||||
+ let hash = JSON.stringify({ env: this.__env, detector: this.detector });
|
||||
+ _toggler_state_cache[hash] = state;
|
||||
+ }
|
||||
+
|
||||
+ _loadState() {
|
||||
+ let hash = JSON.stringify({ env: this.__env, detector: this.detector });
|
||||
+ let state = _toggler_state_cache[hash];
|
||||
+ if (state !== undefined) {
|
||||
+ this.item.setToggleState(state); // doesn't emit 'toggled'
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pulse() {
|
||||
+ this._detect(state => {
|
||||
+ this.compareState(state);
|
||||
+ this._storeState(state);
|
||||
+ this._loadState();
|
||||
+ if (!state && !this._manually_switched_off && this.auto_on) {
|
||||
+ // do not call setToggleState here, because command_on may fail
|
||||
+ this._onToggled(this.item, true);
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ perform() {
|
||||
+ this.item.toggle();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class LauncherEntry extends Entry {
|
||||
+ constructor(prop) {
|
||||
+ super(prop);
|
||||
+ this.command = prop.command || "";
|
||||
+ }
|
||||
+
|
||||
+ createItem() {
|
||||
+ this._try_destroy();
|
||||
+ this.item = new PopupMenu.PopupMenuItem(this.title);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ this.item.connect('activate', this._onClicked.bind(this));
|
||||
+ return this.item;
|
||||
+ }
|
||||
+
|
||||
+ _onClicked(_) {
|
||||
+ _generalSpawn(this.command, this.__env, this.title);
|
||||
+ }
|
||||
+
|
||||
+ perform() {
|
||||
+ this.item.emit('activate');
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class SubMenuEntry extends Entry {
|
||||
+ constructor(prop) {
|
||||
+ super(prop);
|
||||
+
|
||||
+ if (prop.entries == undefined) {
|
||||
+ throw new Error("Expected entries provided in submenu entry.");
|
||||
+ }
|
||||
+ this.entries = [];
|
||||
+ for (let i in prop.entries) {
|
||||
+ let entry_prop = prop.entries[i];
|
||||
+ let entry = createEntry(entry_prop);
|
||||
+ this.entries.push(entry);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ createItem() {
|
||||
+ this._try_destroy();
|
||||
+ this.item = new PopupMenu.PopupSubMenuMenuItem(this.title);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ for (let i in this.entries) {
|
||||
+ let entry = this.entries[i];
|
||||
+ this.item.menu.addMenuItem(entry.createItem());
|
||||
+ }
|
||||
+ return this.item;
|
||||
+ }
|
||||
+
|
||||
+ pulse() {
|
||||
+ for (let i in this.entries) {
|
||||
+ let entry = this.entries[i];
|
||||
+ entry.pulse();
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class SeparatorEntry extends Entry {
|
||||
+ createItem() {
|
||||
+ this._try_destroy();
|
||||
+ this.item = new PopupMenu.PopupSeparatorMenuItem(this.title);
|
||||
+ this.item.label.get_clutter_text().set_use_markup(true);
|
||||
+ return this.item;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+let type_map = {};
|
||||
+
|
||||
+////////////////////////////////////////////////////////////////////////////////
|
||||
+// Config Loader loads config from JSON file.
|
||||
+
|
||||
+// convert Json Nodes (GLib based) to native javascript value.
|
||||
+function convertJson(node) {
|
||||
+ if (node.get_node_type() == Json.NodeType.VALUE) {
|
||||
+ return node.get_value();
|
||||
+ }
|
||||
+ if (node.get_node_type() == Json.NodeType.OBJECT) {
|
||||
+ let obj = {}
|
||||
+ node.get_object().foreach_member(function(_, k, v_n) {
|
||||
+ obj[k] = convertJson(v_n);
|
||||
+ });
|
||||
+ return obj;
|
||||
+ }
|
||||
+ if (node.get_node_type() == Json.NodeType.ARRAY) {
|
||||
+ let arr = []
|
||||
+ node.get_array().foreach_element(function(_, i, elem) {
|
||||
+ arr.push(convertJson(elem));
|
||||
+ });
|
||||
+ return arr;
|
||||
+ }
|
||||
+ return null;
|
||||
+}
|
||||
+
|
||||
+//
|
||||
+function createEntry(entry_prop) {
|
||||
+ if (!entry_prop.type) {
|
||||
+ throw new Error("No type specified in entry.");
|
||||
+ }
|
||||
+ let cls = type_map[entry_prop.type];
|
||||
+ if (!cls) {
|
||||
+ throw new Error("Incorrect type '" + entry_prop.type + "'");
|
||||
+ } else if (cls.createInstance) {
|
||||
+ return cls.createInstance(entry_prop);
|
||||
+ }
|
||||
+ return new cls(entry_prop);
|
||||
+}
|
||||
+
|
||||
+export class Loader {
|
||||
+ constructor(filename) {
|
||||
+ if (filename) {
|
||||
+ this.loadConfig(filename);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ loadConfig(filename) {
|
||||
+ // reset type_map everytime load the config
|
||||
+ type_map = {
|
||||
+ launcher: LauncherEntry,
|
||||
+ toggler: TogglerEntry,
|
||||
+ submenu: SubMenuEntry,
|
||||
+ separator: SeparatorEntry
|
||||
+ };
|
||||
+
|
||||
+ type_map.systemd = new DerivedEntry({
|
||||
+ base: 'toggler',
|
||||
+ vars: ['unit'],
|
||||
+ command_on: "pkexec systemctl start ${unit}",
|
||||
+ command_off: "pkexec systemctl stop ${unit}",
|
||||
+ detector: "systemctl status ${unit} | grep Active:\\\\s\\*activ[ei]",
|
||||
+ });
|
||||
+
|
||||
+ type_map.tmux = new DerivedEntry({
|
||||
+ base: 'toggler',
|
||||
+ vars: ['command', 'session'],
|
||||
+ command_on: 'tmux new -d -s ${session} bash -c "${command}"',
|
||||
+ command_off: 'tmux kill-session -t ${session}',
|
||||
+ detector: 'tmux has -t "${session}" 2>/dev/null && echo yes',
|
||||
+ });
|
||||
+
|
||||
+ /*
|
||||
+ * Refer to README file for detailed config file format.
|
||||
+ */
|
||||
+ this.entries = []; // CAUTION: remove all entries.
|
||||
+
|
||||
+ let config_parser = new Json.Parser();
|
||||
+ config_parser.load_from_file(filename);
|
||||
+ let conf = convertJson(config_parser.get_root());
|
||||
+ if (conf.entries == undefined) {
|
||||
+ throw new Error("Key 'entries' not found.");
|
||||
+ }
|
||||
+ if (conf.deftype) {
|
||||
+ for (let tname in conf.deftype) {
|
||||
+ if (type_map[tname]) {
|
||||
+ throw new Error("Type \""+tname+"\" duplicated.");
|
||||
+ }
|
||||
+ type_map[tname] = new DerivedEntry(conf.deftype[tname]);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (let conf_i in conf.entries) {
|
||||
+ let entry_prop = conf.entries[conf_i];
|
||||
+ this.entries.push(createEntry(entry_prop));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ saveDefaultConfig(filename) {
|
||||
+ // Write default config
|
||||
+ const PERMISSIONS_MODE = 0o640;
|
||||
+ const jsonString = JSON.stringify({
|
||||
+ "_homepage_": "https://github.com/andreabenini/gnome-plugin.custom-menu-panel",
|
||||
+ "_examples_": "https://github.com/andreabenini/gnome-plugin.custom-menu-panel/tree/main/examples",
|
||||
+ "entries": [ {
|
||||
+ "type": "launcher",
|
||||
+ "title": "Edit menu",
|
||||
+ "command": "gedit $HOME/.entries.json"
|
||||
+ } ]
|
||||
+ }, null, 4);
|
||||
+ let fileConfig = Gio.File.new_for_path(filename);
|
||||
+ if (GLib.mkdir_with_parents(fileConfig.get_parent().get_path(), PERMISSIONS_MODE) === 0) {
|
||||
+ fileConfig.replace_contents(jsonString, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null);
|
||||
+ }
|
||||
+ // Try to load newly saved file
|
||||
+ try {
|
||||
+ this.loadConfig(filename);
|
||||
+ } catch(e) {
|
||||
+ Main.notify(_('Cannot create and load file: '+filename));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/extensions/custom-menu/extension.js b/extensions/custom-menu/extension.js
|
||||
new file mode 100644
|
||||
index 00000000..9edbc548
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/extension.js
|
||||
@@ -0,0 +1,201 @@
|
||||
+/* extension.js
|
||||
+ *
|
||||
+ * 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 3 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/>.
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: GPL-3.0-or-later
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * @author Ben
|
||||
+ * @see https://github.com/andreabenini/gnome-plugin.custom-menu-panel
|
||||
+ */
|
||||
+
|
||||
+const CONFIGURATION_FILE = '/.entries.json';
|
||||
+
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GLib from 'gi://GLib';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
+
|
||||
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
+import * as BackgroundMenu from 'resource:///org/gnome/shell/ui/backgroundMenu.js';
|
||||
+import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||||
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
+
|
||||
+import * as Config from './config.js';
|
||||
+
|
||||
+const LOGGER_INFO = 0;
|
||||
+const LOGGER_WARNING = 1;
|
||||
+const LOGGER_ERROR = 2;
|
||||
+
|
||||
+const {BackgroundMenu: OriginalBackgroundMenu} = BackgroundMenu;
|
||||
+
|
||||
+
|
||||
+class CustomMenu extends PopupMenu.PopupMenu {
|
||||
+ constructor(sourceActor) {
|
||||
+ super(sourceActor, 0.0, St.Side.TOP, 0);
|
||||
+
|
||||
+ this._loadSetup();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * LOAD Program settings from .entries.json file
|
||||
+ */
|
||||
+ _loadSetup() {
|
||||
+ this.removeAll();
|
||||
+ // Loading configuration from file
|
||||
+ this.configLoader = new Config.Loader();
|
||||
+ try {
|
||||
+ this.configLoader.loadConfig(GLib.get_home_dir() + CONFIGURATION_FILE); // $HOME/.entries.json
|
||||
+ } catch(e) {
|
||||
+ this.configLoader.saveDefaultConfig(GLib.get_home_dir() + CONFIGURATION_FILE); // create default entries
|
||||
+ }
|
||||
+ // Build the menu
|
||||
+ let i = 0;
|
||||
+ for (let i in this.configLoader.entries) {
|
||||
+ let item = this.configLoader.entries[i].createItem();
|
||||
+ this.addMenuItem(item);
|
||||
+ }
|
||||
+ } /**/
|
||||
+}
|
||||
+
|
||||
+class CustomBackgroundMenu extends CustomMenu {
|
||||
+ constructor(layoutManager) {
|
||||
+ super(layoutManager.dummyCursor);
|
||||
+
|
||||
+ this.actor.add_style_class_name('background-menu');
|
||||
+
|
||||
+ layoutManager.uiGroup.add_actor(this.actor);
|
||||
+ this.actor.hide();
|
||||
+
|
||||
+ this.connect('open-state-changed', (menu, open) => {
|
||||
+ if (open)
|
||||
+ this._updateMaxHeight();
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ _updateMaxHeight() {
|
||||
+ const monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
|
||||
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
|
||||
+ const vMargins = this.actor.margin_top + this.actor.margin_bottom;
|
||||
+ const {y: offsetY} = this.sourceActor;
|
||||
+
|
||||
+ const maxHeight = Math.round((monitor.height - offsetY - vMargins) / scaleFactor);
|
||||
+ this.actor.style = `max-height: ${maxHeight}px;`;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+const Indicator = GObject.registerClass(
|
||||
+ class Indicator extends PanelMenu.Button {
|
||||
+ _init() {
|
||||
+ super._init(0.0, _('Custom Menu Panel Indicator'), true);
|
||||
+ this.add_child(new St.Icon({
|
||||
+ icon_name: 'view-list-bullet-symbolic',
|
||||
+ style_class: 'system-status-icon',
|
||||
+ }));
|
||||
+
|
||||
+ this.setMenu(new CustomMenu(this));
|
||||
+ }
|
||||
+ }
|
||||
+); /**/
|
||||
+
|
||||
+
|
||||
+class Logger {
|
||||
+ constructor(log_file) {
|
||||
+ this._log_file = log_file;
|
||||
+ // initailize log_backend
|
||||
+ if (!log_file) {
|
||||
+ this._initEmptyLog();
|
||||
+ } else if(log_file == "gnome-shell") {
|
||||
+ this._initGnomeLog();
|
||||
+ } else {
|
||||
+ this._initFileLog();
|
||||
+ }
|
||||
+ this.level = LOGGER_WARNING;
|
||||
+ this.info = function(t) {
|
||||
+ if (this.level <= LOGGER_INFO) {
|
||||
+ this.log(t);
|
||||
+ }
|
||||
+ };
|
||||
+ this.warning = function(t) {
|
||||
+ if (this.level <= LOGGER_WARNING) {
|
||||
+ this.log(t);
|
||||
+ }
|
||||
+ };
|
||||
+ this.error = function(t) {
|
||||
+ if (this.level <= LOGGER_ERROR) {
|
||||
+ this.log(t);
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ _initEmptyLog() {
|
||||
+ this.log = function(_) { };
|
||||
+ }
|
||||
+
|
||||
+ _initGnomeLog() {
|
||||
+ this.log = function(s) {
|
||||
+ global.log("custom-menu-panel> " + s);
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ _initFileLog() {
|
||||
+ this.log = function(s) {
|
||||
+ // all operations are synchronous: any needs to optimize?
|
||||
+ if (!this._output_file || !this._output_file.query_exists(null) || !this._fstream || this._fstream.is_closed()) {
|
||||
+ this._output_file = Gio.File.new_for_path(this._log_file);
|
||||
+ this._fstream = this._output_file.append_to(Gio.FileCreateFlags.NONE, null);
|
||||
+ if (!this._fstream instanceof Gio.FileIOStream) {
|
||||
+ this._initGnomeLog();
|
||||
+ this.log("IOError: Failed to append to " + this._log_file + " [Gio.IOErrorEnum:" + this._fstream + "]");
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ this._fstream.write(String(new Date())+" "+s+"\n", null);
|
||||
+ this._fstream.flush(null);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ notify(t, str, details) {
|
||||
+ this.ncond = this.ncond || ['proc', 'ext', 'state'];
|
||||
+ if (this.ncond.indexOf(t) < 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+ Main.notify(str, details || "");
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// lazy-evaluation
|
||||
+let logger = null;
|
||||
+export function getLogger() {
|
||||
+ if (logger === null) {
|
||||
+ logger = new Logger("gnome-shell");
|
||||
+ }
|
||||
+ return logger;
|
||||
+} /**/
|
||||
+
|
||||
+export default class CustomMenuExtension extends Extension {
|
||||
+ enable() {
|
||||
+ BackgroundMenu.BackgroundMenu = CustomBackgroundMenu;
|
||||
+ Main.layoutManager._updateBackgrounds();
|
||||
+ }
|
||||
+
|
||||
+ disable() {
|
||||
+ BackgroundMenu.BackgroundMenu = OriginalBackgroundMenu;
|
||||
+ Main.layoutManager._updateBackgrounds();
|
||||
+ }
|
||||
+} /**/
|
||||
diff --git a/extensions/custom-menu/meson.build b/extensions/custom-menu/meson.build
|
||||
new file mode 100644
|
||||
index 00000000..92450963
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/meson.build
|
||||
@@ -0,0 +1,7 @@
|
||||
+extension_data += configure_file(
|
||||
+ input: metadata_name + '.in',
|
||||
+ output: metadata_name,
|
||||
+ configuration: metadata_conf
|
||||
+)
|
||||
+
|
||||
+extension_sources += files('config.js')
|
||||
diff --git a/extensions/custom-menu/metadata.json.in b/extensions/custom-menu/metadata.json.in
|
||||
new file mode 100644
|
||||
index 00000000..054f639b
|
||||
--- /dev/null
|
||||
+++ b/extensions/custom-menu/metadata.json.in
|
||||
@@ -0,0 +1,10 @@
|
||||
+{
|
||||
+"extension-id": "@extension_id@",
|
||||
+"uuid": "@uuid@",
|
||||
+"settings-schema": "@gschemaname@",
|
||||
+"gettext-domain": "@gettext_domain@",
|
||||
+"name": "Custom menu",
|
||||
+"description": "Quick custom menu for launching your favorite applications",
|
||||
+"shell-version": [ "@shell_current@" ],
|
||||
+"url": "@url@"
|
||||
+}
|
||||
diff --git a/meson.build b/meson.build
|
||||
index b2a5d94d..bbb72889 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -53,6 +53,7 @@ all_extensions = default_extensions
|
||||
all_extensions += [
|
||||
'auto-move-windows',
|
||||
'classification-banner',
|
||||
+ 'custom-menu',
|
||||
'gesture-inhibitor',
|
||||
'native-window-placement',
|
||||
'user-theme'
|
||||
--
|
||||
2.45.0
|
||||
|
@ -21,6 +21,46 @@ BuildRequires: glib2%{?_isa}
|
||||
Requires: gnome-shell >= %{min_gs_version}
|
||||
BuildArch: noarch
|
||||
|
||||
|
||||
Patch: extra-extensions-0001-Add-top-icons-extension.patch
|
||||
Patch: extra-extensions-0002-Add-gesture-inhibitor-extension.patch
|
||||
Patch: extra-extensions-0003-Add-classification-banner.patch
|
||||
Patch: extra-extensions-0004-Add-heads-up-display.patch
|
||||
Patch: extra-extensions-0005-Add-custom-menu-extension.patch
|
||||
|
||||
Patch: 0001-Include-top-icons-in-classic-session.patch
|
||||
Patch: 0001-apps-menu-Set-label_actor-of-Category-items.patch
|
||||
Patch: prefer-window-icon.patch
|
||||
|
||||
Patch: more-ws-previews-0001-workspace-indicator-Move-indicator-code-into-separat.patch
|
||||
Patch: more-ws-previews-0002-workspace-indicator-Use-descendant-style-selectors.patch
|
||||
Patch: more-ws-previews-0003-window-list-Use-consistent-style-class-prefix.patch
|
||||
Patch: more-ws-previews-0004-workspace-indicator-Allow-overriding-base-style-clas.patch
|
||||
Patch: more-ws-previews-0005-window-list-Override-base-style-class.patch
|
||||
Patch: more-ws-previews-0006-window-list-Externally-adjust-workspace-menu.patch
|
||||
Patch: more-ws-previews-0007-window-list-Handle-changes-to-workspace-menu.patch
|
||||
Patch: more-ws-previews-0008-workspace-indicator-Don-t-use-SCHEMA-KEY-constants.patch
|
||||
Patch: more-ws-previews-0009-workspace-indicator-Use-existing-property.patch
|
||||
Patch: more-ws-previews-0010-workspace-indicator-Don-t-use-menu-section.patch
|
||||
Patch: more-ws-previews-0011-workspace-indicator-Support-showing-tooltips-above.patch
|
||||
Patch: more-ws-previews-0012-workspace-indicator-Only-change-top-bar-redirect-whe.patch
|
||||
Patch: more-ws-previews-0013-workspace-indicator-Small-cleanup.patch
|
||||
Patch: more-ws-previews-0014-workspace-indicator-Simplify-getting-status-text.patch
|
||||
Patch: more-ws-previews-0015-workspace-indicator-Include-n-workspaces-in-status-l.patch
|
||||
Patch: more-ws-previews-0016-workspace-indicator-Tweak-preview-style.patch
|
||||
Patch: more-ws-previews-0017-workspace-indicator-Support-light-style.patch
|
||||
Patch: more-ws-previews-0018-export-zips-Pick-up-non-default-stylesheets.patch
|
||||
Patch: more-ws-previews-0019-window-list-Use-actual-copy-of-workspace-indicator.patch
|
||||
Patch: more-ws-previews-0020-workspace-indicator-Simplify-scroll-handling.patch
|
||||
Patch: more-ws-previews-0021-workspace-indicator-Handle-active-indication-in-thum.patch
|
||||
Patch: more-ws-previews-0022-workspace-indicator-Split-out-WorkspacePreviews.patch
|
||||
Patch: more-ws-previews-0023-workspace-indicator-Handle-preview-overflow.patch
|
||||
Patch: more-ws-previews-0024-workspace-indicator-Support-labels-in-previews.patch
|
||||
Patch: more-ws-previews-0025-workspace-indicator-Stop-handling-vertical-layouts.patch
|
||||
Patch: more-ws-previews-0026-workspace-indicator-Also-show-previews-in-menu.patch
|
||||
Patch: more-ws-previews-0027-workspace-indicator-Make-previews-configurable.patch
|
||||
Patch: more-ws-previews-0028-window-list-Expose-workspace-preview-option.patch
|
||||
|
||||
%description
|
||||
GNOME Shell Extensions is a collection of extensions providing additional and
|
||||
optional functionality to GNOME Shell.
|
||||
@ -28,13 +68,18 @@ optional functionality to GNOME Shell.
|
||||
Enabled extensions:
|
||||
* apps-menu
|
||||
* auto-move-windows
|
||||
* classification-banner
|
||||
* custom-menu
|
||||
* drive-menu
|
||||
* gesture-inhibitor
|
||||
* heads-up-display
|
||||
* launch-new-instance
|
||||
* light-style
|
||||
* native-window-placement
|
||||
* places-menu
|
||||
* screenshot-window-sizer
|
||||
* system-monitor
|
||||
* top-icons
|
||||
* user-theme
|
||||
* window-list
|
||||
* windowsNavigator
|
||||
@ -101,6 +146,26 @@ workspace can be assigned to each application as soon as it creates a window, in
|
||||
a manner configurable with a GSettings key.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-classification-banner
|
||||
Summary: Display classification level banner in GNOME Shell
|
||||
Group: User Interface/Desktops
|
||||
License: GPLv2+
|
||||
Requires: %{pkg_prefix}-common = %{version}-%{release}
|
||||
|
||||
%description -n %{pkg_prefix}-classification-banner
|
||||
This GNOME Shell extension adds a banner that displays the classification level.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-custom-menu
|
||||
Summary: Add a custom menu to the desktop
|
||||
Group: User Interface/Desktops
|
||||
License: GPLv2+
|
||||
Requires: %{pkg_prefix}-common = %{version}-%{release}
|
||||
|
||||
%description -n %{pkg_prefix}-custom-menu
|
||||
This GNOME Shell extension adds a custom menu to the desktop background.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-drive-menu
|
||||
Summary: Drive status menu for GNOME Shell
|
||||
License: GPL-2.0-or-later
|
||||
@ -111,6 +176,27 @@ This GNOME Shell extension provides a panel status menu for accessing and
|
||||
unmounting removable devices.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-gesture-inhibitor
|
||||
Summary: Gesture inhibitor
|
||||
Group: User Interface/Desktops
|
||||
License: GPLv2+
|
||||
Requires: %{pkg_prefix}-common = %{version}-%{release}
|
||||
|
||||
%description -n %{pkg_prefix}-gesture-inhibitor
|
||||
This GNOME Shell extension allows disabling the default desktop gestures.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-heads-up-display
|
||||
Summary: Display persistent on-screen message
|
||||
Group: User Interface/Desktops
|
||||
License: GPLv3+
|
||||
Requires: %{pkg_prefix}-common = %{version}-%{release}
|
||||
|
||||
%description -n %{pkg_prefix}-heads-up-display
|
||||
This GNOME Shell extension displays a persistent message in the top middle of the screen.
|
||||
This message can appear on the login screen, lock screen, or regular user session.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-launch-new-instance
|
||||
Summary: Always launch a new application instance for GNOME Shell
|
||||
License: GPL-2.0-or-later
|
||||
@ -169,6 +255,15 @@ Requires: %{pkg_prefix}-common = %{version}-%{release}
|
||||
This GNOME Shell extension displays system usage information in the top bar.
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-top-icons
|
||||
Summary: Show legacy icons on top
|
||||
License: GPLv2+
|
||||
Requires: %{pkg_prefix}-common = %{version}-%{release}
|
||||
|
||||
%description -n %{pkg_prefix}-top-icons
|
||||
This GNOME Shell extension moves legacy tray icons into the top bar
|
||||
|
||||
|
||||
%package -n %{pkg_prefix}-user-theme
|
||||
Summary: Support for custom themes in GNOME Shell
|
||||
License: GPL-2.0-or-later
|
||||
@ -249,10 +344,29 @@ workspaces.
|
||||
%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.auto-move-windows.gschema.xml
|
||||
%{_datadir}/gnome-shell/extensions/auto-move-windows*/
|
||||
|
||||
%files -n %{pkg_prefix}-classification-banner
|
||||
%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.classification-banner.gschema.xml
|
||||
%{_datadir}/gnome-shell/extensions/classification-banner*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-custom-menu
|
||||
%{_datadir}/gnome-shell/extensions/custom-menu*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-drive-menu
|
||||
%{_datadir}/gnome-shell/extensions/drive-menu*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-gesture-inhibitor
|
||||
%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.gesture-inhibitor.gschema.xml
|
||||
%{_datadir}/gnome-shell/extensions/gesture-inhibitor*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-heads-up-display
|
||||
%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.heads-up-display.gschema.xml
|
||||
%{_datadir}/gnome-shell/extensions/heads-up-display*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-launch-new-instance
|
||||
%{_datadir}/gnome-shell/extensions/launch-new-instance*/
|
||||
|
||||
@ -280,6 +394,10 @@ workspaces.
|
||||
%{_datadir}/gnome-shell/extensions/system-monitor*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-top-icons
|
||||
%{_datadir}/gnome-shell/extensions/top-icons*/
|
||||
|
||||
|
||||
%files -n %{pkg_prefix}-user-theme
|
||||
%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.user-theme.gschema.xml
|
||||
%{_datadir}/gnome-shell/extensions/user-theme*/
|
||||
@ -296,6 +414,7 @@ workspaces.
|
||||
|
||||
%files -n %{pkg_prefix}-workspace-indicator
|
||||
%{_datadir}/gnome-shell/extensions/workspace-indicator*/
|
||||
%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.workspace-indicator.gschema.xml
|
||||
|
||||
|
||||
%changelog
|
||||
|
@ -0,0 +1,942 @@
|
||||
From 1f0681875eefd09df28630a74aabd1bf47f90dab Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 12:38:33 +0100
|
||||
Subject: [PATCH 01/28] workspace-indicator: Move indicator code into separate
|
||||
file
|
||||
|
||||
Shortly after the window-list extension was added, it gained a
|
||||
workspace switcher based on the workspace indicator extension.
|
||||
|
||||
Duplicating the code wasn't a big issue while the switcher was
|
||||
a simple menu, but since it gained previews with a fair bit of
|
||||
custom styling, syncing changes between the two extensions has
|
||||
become tedious, in particular as the two copies have slightly
|
||||
diverged over time.
|
||||
|
||||
In order to allow the two copies to converge again, the indicator
|
||||
code needs to be separate from the extension boilerplate, so
|
||||
split out the code into a separate module.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/extension.js | 432 +----------------
|
||||
extensions/workspace-indicator/meson.build | 2 +-
|
||||
.../workspace-indicator/workspaceIndicator.js | 438 ++++++++++++++++++
|
||||
po/POTFILES.in | 2 +-
|
||||
4 files changed, 442 insertions(+), 432 deletions(-)
|
||||
create mode 100644 extensions/workspace-indicator/workspaceIndicator.js
|
||||
|
||||
diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
|
||||
index f9c7dd9a..b383c919 100644
|
||||
--- a/extensions/workspace-indicator/extension.js
|
||||
+++ b/extensions/workspace-indicator/extension.js
|
||||
@@ -4,439 +4,11 @@
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
-// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
|
||||
-import Clutter from 'gi://Clutter';
|
||||
-import Gio from 'gi://Gio';
|
||||
-import GObject from 'gi://GObject';
|
||||
-import Meta from 'gi://Meta';
|
||||
-import St from 'gi://St';
|
||||
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
|
||||
-import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
-
|
||||
-import * as DND from 'resource:///org/gnome/shell/ui/dnd.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 PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
-
|
||||
-const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences';
|
||||
-const WORKSPACE_KEY = 'workspace-names';
|
||||
-
|
||||
-const TOOLTIP_OFFSET = 6;
|
||||
-const TOOLTIP_ANIMATION_TIME = 150;
|
||||
-
|
||||
-const MAX_THUMBNAILS = 6;
|
||||
-
|
||||
-class WindowPreview extends St.Button {
|
||||
- static {
|
||||
- GObject.registerClass(this);
|
||||
- }
|
||||
-
|
||||
- constructor(window) {
|
||||
- super({
|
||||
- style_class: 'workspace-indicator-window-preview',
|
||||
- });
|
||||
-
|
||||
- this._delegate = this;
|
||||
- DND.makeDraggable(this, {restoreOnSuccess: true});
|
||||
-
|
||||
- this._window = window;
|
||||
-
|
||||
- this._window.connectObject(
|
||||
- 'size-changed', () => this._checkRelayout(),
|
||||
- 'position-changed', () => this._checkRelayout(),
|
||||
- 'notify::minimized', this._updateVisible.bind(this),
|
||||
- 'notify::window-type', this._updateVisible.bind(this),
|
||||
- this);
|
||||
- this._updateVisible();
|
||||
-
|
||||
- global.display.connectObject('notify::focus-window',
|
||||
- this._onFocusChanged.bind(this), this);
|
||||
- this._onFocusChanged();
|
||||
- }
|
||||
-
|
||||
- // needed for DND
|
||||
- get metaWindow() {
|
||||
- return this._window;
|
||||
- }
|
||||
-
|
||||
- _onFocusChanged() {
|
||||
- if (global.display.focus_window === this._window)
|
||||
- this.add_style_class_name('active');
|
||||
- else
|
||||
- this.remove_style_class_name('active');
|
||||
- }
|
||||
-
|
||||
- _checkRelayout() {
|
||||
- const monitor = Main.layoutManager.findIndexForActor(this);
|
||||
- const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
||||
- if (this._window.get_frame_rect().overlap(workArea))
|
||||
- this.queue_relayout();
|
||||
- }
|
||||
-
|
||||
- _updateVisible() {
|
||||
- this.visible = this._window.window_type !== Meta.WindowType.DESKTOP &&
|
||||
- this._window.showing_on_its_workspace();
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-class WorkspaceLayout extends Clutter.LayoutManager {
|
||||
- static {
|
||||
- GObject.registerClass(this);
|
||||
- }
|
||||
-
|
||||
- vfunc_get_preferred_width() {
|
||||
- return [0, 0];
|
||||
- }
|
||||
-
|
||||
- vfunc_get_preferred_height() {
|
||||
- return [0, 0];
|
||||
- }
|
||||
-
|
||||
- vfunc_allocate(container, box) {
|
||||
- const monitor = Main.layoutManager.findIndexForActor(container);
|
||||
- const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
||||
- const hscale = box.get_width() / workArea.width;
|
||||
- const vscale = box.get_height() / workArea.height;
|
||||
-
|
||||
- for (const child of container) {
|
||||
- const childBox = new Clutter.ActorBox();
|
||||
- const frameRect = child.metaWindow.get_frame_rect();
|
||||
- childBox.set_size(
|
||||
- Math.round(Math.min(frameRect.width, workArea.width) * hscale),
|
||||
- Math.round(Math.min(frameRect.height, workArea.height) * vscale));
|
||||
- childBox.set_origin(
|
||||
- Math.round((frameRect.x - workArea.x) * hscale),
|
||||
- Math.round((frameRect.y - workArea.y) * vscale));
|
||||
- child.allocate(childBox);
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-class WorkspaceThumbnail extends St.Button {
|
||||
- static {
|
||||
- GObject.registerClass(this);
|
||||
- }
|
||||
-
|
||||
- constructor(index) {
|
||||
- super({
|
||||
- style_class: 'workspace',
|
||||
- child: new Clutter.Actor({
|
||||
- layout_manager: new WorkspaceLayout(),
|
||||
- clip_to_allocation: true,
|
||||
- x_expand: true,
|
||||
- y_expand: true,
|
||||
- }),
|
||||
- });
|
||||
-
|
||||
- this._tooltip = new St.Label({
|
||||
- style_class: 'dash-label',
|
||||
- visible: false,
|
||||
- });
|
||||
- Main.uiGroup.add_child(this._tooltip);
|
||||
-
|
||||
- this.connect('destroy', this._onDestroy.bind(this));
|
||||
- this.connect('notify::hover', this._syncTooltip.bind(this));
|
||||
-
|
||||
- this._index = index;
|
||||
- this._delegate = this; // needed for DND
|
||||
-
|
||||
- this._windowPreviews = new Map();
|
||||
-
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
- this._workspace = workspaceManager.get_workspace_by_index(index);
|
||||
-
|
||||
- this._workspace.connectObject(
|
||||
- 'window-added', (ws, window) => this._addWindow(window),
|
||||
- 'window-removed', (ws, window) => this._removeWindow(window),
|
||||
- this);
|
||||
-
|
||||
- global.display.connectObject('restacked',
|
||||
- this._onRestacked.bind(this), this);
|
||||
-
|
||||
- this._workspace.list_windows().forEach(w => this._addWindow(w));
|
||||
- this._onRestacked();
|
||||
- }
|
||||
-
|
||||
- acceptDrop(source) {
|
||||
- if (!source.metaWindow)
|
||||
- return false;
|
||||
-
|
||||
- this._moveWindow(source.metaWindow);
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- handleDragOver(source) {
|
||||
- if (source.metaWindow)
|
||||
- return DND.DragMotionResult.MOVE_DROP;
|
||||
- else
|
||||
- return DND.DragMotionResult.CONTINUE;
|
||||
- }
|
||||
-
|
||||
- _addWindow(window) {
|
||||
- if (this._windowPreviews.has(window))
|
||||
- return;
|
||||
-
|
||||
- let preview = new WindowPreview(window);
|
||||
- preview.connect('clicked', (a, btn) => this.emit('clicked', btn));
|
||||
- this._windowPreviews.set(window, preview);
|
||||
- this.child.add_child(preview);
|
||||
- }
|
||||
-
|
||||
- _removeWindow(window) {
|
||||
- let preview = this._windowPreviews.get(window);
|
||||
- if (!preview)
|
||||
- return;
|
||||
-
|
||||
- this._windowPreviews.delete(window);
|
||||
- preview.destroy();
|
||||
- }
|
||||
-
|
||||
- _onRestacked() {
|
||||
- let lastPreview = null;
|
||||
- let windows = global.get_window_actors().map(a => a.meta_window);
|
||||
- for (let i = 0; i < windows.length; i++) {
|
||||
- let preview = this._windowPreviews.get(windows[i]);
|
||||
- if (!preview)
|
||||
- continue;
|
||||
-
|
||||
- this.child.set_child_above_sibling(preview, lastPreview);
|
||||
- lastPreview = preview;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _moveWindow(window) {
|
||||
- let monitorIndex = Main.layoutManager.findIndexForActor(this);
|
||||
- if (monitorIndex !== window.get_monitor())
|
||||
- window.move_to_monitor(monitorIndex);
|
||||
- window.change_workspace_by_index(this._index, false);
|
||||
- }
|
||||
-
|
||||
- on_clicked() {
|
||||
- let ws = global.workspace_manager.get_workspace_by_index(this._index);
|
||||
- if (ws)
|
||||
- ws.activate(global.get_current_time());
|
||||
- }
|
||||
-
|
||||
- _syncTooltip() {
|
||||
- if (this.hover) {
|
||||
- this._tooltip.set({
|
||||
- text: Meta.prefs_get_workspace_name(this._index),
|
||||
- visible: true,
|
||||
- opacity: 0,
|
||||
- });
|
||||
-
|
||||
- const [stageX, stageY] = this.get_transformed_position();
|
||||
- const thumbWidth = this.allocation.get_width();
|
||||
- const thumbHeight = this.allocation.get_height();
|
||||
- const tipWidth = this._tooltip.width;
|
||||
- const xOffset = Math.floor((thumbWidth - tipWidth) / 2);
|
||||
- const monitor = Main.layoutManager.findMonitorForActor(this);
|
||||
- const x = Math.clamp(
|
||||
- stageX + xOffset,
|
||||
- monitor.x,
|
||||
- monitor.x + monitor.width - tipWidth);
|
||||
- const y = stageY + thumbHeight + TOOLTIP_OFFSET;
|
||||
- this._tooltip.set_position(x, y);
|
||||
- }
|
||||
-
|
||||
- this._tooltip.ease({
|
||||
- opacity: this.hover ? 255 : 0,
|
||||
- duration: TOOLTIP_ANIMATION_TIME,
|
||||
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
- onComplete: () => (this._tooltip.visible = this.hover),
|
||||
- });
|
||||
- }
|
||||
-
|
||||
- _onDestroy() {
|
||||
- this._tooltip.destroy();
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-class WorkspaceIndicator extends PanelMenu.Button {
|
||||
- static {
|
||||
- GObject.registerClass(this);
|
||||
- }
|
||||
-
|
||||
- constructor() {
|
||||
- super(0.5, _('Workspace Indicator'));
|
||||
-
|
||||
- let container = new St.Widget({
|
||||
- layout_manager: new Clutter.BinLayout(),
|
||||
- x_expand: true,
|
||||
- y_expand: true,
|
||||
- });
|
||||
- this.add_child(container);
|
||||
-
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
- this._statusLabel = new St.Label({
|
||||
- style_class: 'panel-workspace-indicator',
|
||||
- y_align: Clutter.ActorAlign.CENTER,
|
||||
- text: this._labelText(),
|
||||
- });
|
||||
-
|
||||
- container.add_child(this._statusLabel);
|
||||
-
|
||||
- this._thumbnailsBox = new St.BoxLayout({
|
||||
- style_class: 'panel-workspace-indicator-box',
|
||||
- y_expand: true,
|
||||
- reactive: true,
|
||||
- });
|
||||
-
|
||||
- container.add_child(this._thumbnailsBox);
|
||||
-
|
||||
- this._workspacesItems = [];
|
||||
- this._workspaceSection = new PopupMenu.PopupMenuSection();
|
||||
- this.menu.addMenuItem(this._workspaceSection);
|
||||
-
|
||||
- workspaceManager.connectObject(
|
||||
- 'notify::n-workspaces', this._nWorkspacesChanged.bind(this), GObject.ConnectFlags.AFTER,
|
||||
- 'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER,
|
||||
- 'notify::layout-rows', this._updateThumbnailVisibility.bind(this),
|
||||
- this);
|
||||
-
|
||||
- this.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
- this._thumbnailsBox.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
- this._createWorkspacesSection();
|
||||
- this._updateThumbnails();
|
||||
- this._updateThumbnailVisibility();
|
||||
-
|
||||
- this._settings = new Gio.Settings({schema_id: WORKSPACE_SCHEMA});
|
||||
- this._settings.connectObject(`changed::${WORKSPACE_KEY}`,
|
||||
- this._updateMenuLabels.bind(this), this);
|
||||
- }
|
||||
-
|
||||
- _onDestroy() {
|
||||
- Main.panel.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||||
-
|
||||
- super._onDestroy();
|
||||
- }
|
||||
-
|
||||
- _updateThumbnailVisibility() {
|
||||
- const {workspaceManager} = global;
|
||||
- const vertical = workspaceManager.layout_rows === -1;
|
||||
- const useMenu =
|
||||
- vertical || workspaceManager.n_workspaces > MAX_THUMBNAILS;
|
||||
- this.reactive = useMenu;
|
||||
-
|
||||
- this._statusLabel.visible = useMenu;
|
||||
- this._thumbnailsBox.visible = !useMenu;
|
||||
-
|
||||
- // Disable offscreen-redirect when showing the workspace switcher
|
||||
- // so that clip-to-allocation works
|
||||
- Main.panel.set_offscreen_redirect(useMenu
|
||||
- ? Clutter.OffscreenRedirect.ALWAYS
|
||||
- : Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY);
|
||||
- }
|
||||
-
|
||||
- _onWorkspaceSwitched() {
|
||||
- this._currentWorkspace = global.workspace_manager.get_active_workspace_index();
|
||||
|
||||
- this._updateMenuOrnament();
|
||||
- this._updateActiveThumbnail();
|
||||
-
|
||||
- this._statusLabel.set_text(this._labelText());
|
||||
- }
|
||||
-
|
||||
- _nWorkspacesChanged() {
|
||||
- this._createWorkspacesSection();
|
||||
- this._updateThumbnails();
|
||||
- this._updateThumbnailVisibility();
|
||||
- }
|
||||
-
|
||||
- _updateMenuOrnament() {
|
||||
- for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
- this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
||||
- ? PopupMenu.Ornament.DOT
|
||||
- : PopupMenu.Ornament.NO_DOT);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _updateActiveThumbnail() {
|
||||
- let thumbs = this._thumbnailsBox.get_children();
|
||||
- for (let i = 0; i < thumbs.length; i++) {
|
||||
- if (i === this._currentWorkspace)
|
||||
- thumbs[i].add_style_class_name('active');
|
||||
- else
|
||||
- thumbs[i].remove_style_class_name('active');
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _labelText(workspaceIndex) {
|
||||
- if (workspaceIndex === undefined) {
|
||||
- workspaceIndex = this._currentWorkspace;
|
||||
- return (workspaceIndex + 1).toString();
|
||||
- }
|
||||
- return Meta.prefs_get_workspace_name(workspaceIndex);
|
||||
- }
|
||||
-
|
||||
- _updateMenuLabels() {
|
||||
- for (let i = 0; i < this._workspacesItems.length; i++)
|
||||
- this._workspacesItems[i].label.text = this._labelText(i);
|
||||
- }
|
||||
-
|
||||
- _createWorkspacesSection() {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- this._workspaceSection.removeAll();
|
||||
- this._workspacesItems = [];
|
||||
- this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
-
|
||||
- let i = 0;
|
||||
- for (; i < workspaceManager.n_workspaces; i++) {
|
||||
- this._workspacesItems[i] = new PopupMenu.PopupMenuItem(this._labelText(i));
|
||||
- this._workspaceSection.addMenuItem(this._workspacesItems[i]);
|
||||
- this._workspacesItems[i].workspaceId = i;
|
||||
- this._workspacesItems[i].label_actor = this._statusLabel;
|
||||
- this._workspacesItems[i].connect('activate', (actor, _event) => {
|
||||
- this._activate(actor.workspaceId);
|
||||
- });
|
||||
-
|
||||
- this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
||||
- ? PopupMenu.Ornament.DOT
|
||||
- : PopupMenu.Ornament.NO_DOT);
|
||||
- }
|
||||
-
|
||||
- this._statusLabel.set_text(this._labelText());
|
||||
- }
|
||||
-
|
||||
- _updateThumbnails() {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- this._thumbnailsBox.destroy_all_children();
|
||||
-
|
||||
- for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
- let thumb = new WorkspaceThumbnail(i);
|
||||
- this._thumbnailsBox.add_child(thumb);
|
||||
- }
|
||||
- this._updateActiveThumbnail();
|
||||
- }
|
||||
-
|
||||
- _activate(index) {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- if (index >= 0 && index < workspaceManager.n_workspaces) {
|
||||
- let metaWorkspace = workspaceManager.get_workspace_by_index(index);
|
||||
- metaWorkspace.activate(global.get_current_time());
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _onScrollEvent(actor, event) {
|
||||
- let direction = event.get_scroll_direction();
|
||||
- let diff = 0;
|
||||
- if (direction === Clutter.ScrollDirection.DOWN)
|
||||
- diff = 1;
|
||||
- else if (direction === Clutter.ScrollDirection.UP)
|
||||
- diff = -1;
|
||||
- else
|
||||
- return;
|
||||
-
|
||||
-
|
||||
- let newIndex = global.workspace_manager.get_active_workspace_index() + diff;
|
||||
- this._activate(newIndex);
|
||||
- }
|
||||
-}
|
||||
+import {WorkspaceIndicator} from './workspaceIndicator.js';
|
||||
|
||||
export default class WorkspaceIndicatorExtension extends Extension {
|
||||
enable() {
|
||||
diff --git a/extensions/workspace-indicator/meson.build b/extensions/workspace-indicator/meson.build
|
||||
index 36daa535..6dd08dae 100644
|
||||
--- a/extensions/workspace-indicator/meson.build
|
||||
+++ b/extensions/workspace-indicator/meson.build
|
||||
@@ -9,4 +9,4 @@ extension_data += configure_file(
|
||||
)
|
||||
extension_data += files('stylesheet.css')
|
||||
|
||||
-extension_sources += files('prefs.js')
|
||||
+extension_sources += files('prefs.js', 'workspaceIndicator.js')
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
new file mode 100644
|
||||
index 00000000..6b0903d5
|
||||
--- /dev/null
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -0,0 +1,438 @@
|
||||
+// SPDX-FileCopyrightText: 2011 Erick Pérez Castellanos <erick.red@gmail.com>
|
||||
+// SPDX-FileCopyrightText: 2011 Giovanni Campagna <gcampagna@src.gnome.org>
|
||||
+// SPDX-FileCopyrightText: 2017 Florian Müllner <fmuellner@gnome.org>
|
||||
+//
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+
|
||||
+import Clutter from 'gi://Clutter';
|
||||
+import Gio from 'gi://Gio';
|
||||
+import GObject from 'gi://GObject';
|
||||
+import Meta from 'gi://Meta';
|
||||
+import St from 'gi://St';
|
||||
+
|
||||
+import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
+
|
||||
+import * as DND from 'resource:///org/gnome/shell/ui/dnd.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 PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
+
|
||||
+const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences';
|
||||
+const WORKSPACE_KEY = 'workspace-names';
|
||||
+
|
||||
+const TOOLTIP_OFFSET = 6;
|
||||
+const TOOLTIP_ANIMATION_TIME = 150;
|
||||
+
|
||||
+const MAX_THUMBNAILS = 6;
|
||||
+
|
||||
+class WindowPreview extends St.Button {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ constructor(window) {
|
||||
+ super({
|
||||
+ style_class: 'workspace-indicator-window-preview',
|
||||
+ });
|
||||
+
|
||||
+ this._delegate = this;
|
||||
+ DND.makeDraggable(this, {restoreOnSuccess: true});
|
||||
+
|
||||
+ this._window = window;
|
||||
+
|
||||
+ this._window.connectObject(
|
||||
+ 'size-changed', () => this._checkRelayout(),
|
||||
+ 'position-changed', () => this._checkRelayout(),
|
||||
+ 'notify::minimized', this._updateVisible.bind(this),
|
||||
+ 'notify::window-type', this._updateVisible.bind(this),
|
||||
+ this);
|
||||
+ this._updateVisible();
|
||||
+
|
||||
+ global.display.connectObject('notify::focus-window',
|
||||
+ this._onFocusChanged.bind(this), this);
|
||||
+ this._onFocusChanged();
|
||||
+ }
|
||||
+
|
||||
+ // needed for DND
|
||||
+ get metaWindow() {
|
||||
+ return this._window;
|
||||
+ }
|
||||
+
|
||||
+ _onFocusChanged() {
|
||||
+ if (global.display.focus_window === this._window)
|
||||
+ this.add_style_class_name('active');
|
||||
+ else
|
||||
+ this.remove_style_class_name('active');
|
||||
+ }
|
||||
+
|
||||
+ _checkRelayout() {
|
||||
+ const monitor = Main.layoutManager.findIndexForActor(this);
|
||||
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
||||
+ if (this._window.get_frame_rect().overlap(workArea))
|
||||
+ this.queue_relayout();
|
||||
+ }
|
||||
+
|
||||
+ _updateVisible() {
|
||||
+ this.visible = this._window.window_type !== Meta.WindowType.DESKTOP &&
|
||||
+ this._window.showing_on_its_workspace();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class WorkspaceLayout extends Clutter.LayoutManager {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ vfunc_get_preferred_width() {
|
||||
+ return [0, 0];
|
||||
+ }
|
||||
+
|
||||
+ vfunc_get_preferred_height() {
|
||||
+ return [0, 0];
|
||||
+ }
|
||||
+
|
||||
+ vfunc_allocate(container, box) {
|
||||
+ const monitor = Main.layoutManager.findIndexForActor(container);
|
||||
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
||||
+ const hscale = box.get_width() / workArea.width;
|
||||
+ const vscale = box.get_height() / workArea.height;
|
||||
+
|
||||
+ for (const child of container) {
|
||||
+ const childBox = new Clutter.ActorBox();
|
||||
+ const frameRect = child.metaWindow.get_frame_rect();
|
||||
+ childBox.set_size(
|
||||
+ Math.round(Math.min(frameRect.width, workArea.width) * hscale),
|
||||
+ Math.round(Math.min(frameRect.height, workArea.height) * vscale));
|
||||
+ childBox.set_origin(
|
||||
+ Math.round((frameRect.x - workArea.x) * hscale),
|
||||
+ Math.round((frameRect.y - workArea.y) * vscale));
|
||||
+ child.allocate(childBox);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class WorkspaceThumbnail extends St.Button {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ constructor(index) {
|
||||
+ super({
|
||||
+ style_class: 'workspace',
|
||||
+ child: new Clutter.Actor({
|
||||
+ layout_manager: new WorkspaceLayout(),
|
||||
+ clip_to_allocation: true,
|
||||
+ x_expand: true,
|
||||
+ y_expand: true,
|
||||
+ }),
|
||||
+ });
|
||||
+
|
||||
+ this._tooltip = new St.Label({
|
||||
+ style_class: 'dash-label',
|
||||
+ visible: false,
|
||||
+ });
|
||||
+ Main.uiGroup.add_child(this._tooltip);
|
||||
+
|
||||
+ this.connect('destroy', this._onDestroy.bind(this));
|
||||
+ this.connect('notify::hover', this._syncTooltip.bind(this));
|
||||
+
|
||||
+ this._index = index;
|
||||
+ this._delegate = this; // needed for DND
|
||||
+
|
||||
+ this._windowPreviews = new Map();
|
||||
+
|
||||
+ let workspaceManager = global.workspace_manager;
|
||||
+ this._workspace = workspaceManager.get_workspace_by_index(index);
|
||||
+
|
||||
+ this._workspace.connectObject(
|
||||
+ 'window-added', (ws, window) => this._addWindow(window),
|
||||
+ 'window-removed', (ws, window) => this._removeWindow(window),
|
||||
+ this);
|
||||
+
|
||||
+ global.display.connectObject('restacked',
|
||||
+ this._onRestacked.bind(this), this);
|
||||
+
|
||||
+ this._workspace.list_windows().forEach(w => this._addWindow(w));
|
||||
+ this._onRestacked();
|
||||
+ }
|
||||
+
|
||||
+ acceptDrop(source) {
|
||||
+ if (!source.metaWindow)
|
||||
+ return false;
|
||||
+
|
||||
+ this._moveWindow(source.metaWindow);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ handleDragOver(source) {
|
||||
+ if (source.metaWindow)
|
||||
+ return DND.DragMotionResult.MOVE_DROP;
|
||||
+ else
|
||||
+ return DND.DragMotionResult.CONTINUE;
|
||||
+ }
|
||||
+
|
||||
+ _addWindow(window) {
|
||||
+ if (this._windowPreviews.has(window))
|
||||
+ return;
|
||||
+
|
||||
+ let preview = new WindowPreview(window);
|
||||
+ preview.connect('clicked', (a, btn) => this.emit('clicked', btn));
|
||||
+ this._windowPreviews.set(window, preview);
|
||||
+ this.child.add_child(preview);
|
||||
+ }
|
||||
+
|
||||
+ _removeWindow(window) {
|
||||
+ let preview = this._windowPreviews.get(window);
|
||||
+ if (!preview)
|
||||
+ return;
|
||||
+
|
||||
+ this._windowPreviews.delete(window);
|
||||
+ preview.destroy();
|
||||
+ }
|
||||
+
|
||||
+ _onRestacked() {
|
||||
+ let lastPreview = null;
|
||||
+ let windows = global.get_window_actors().map(a => a.meta_window);
|
||||
+ for (let i = 0; i < windows.length; i++) {
|
||||
+ let preview = this._windowPreviews.get(windows[i]);
|
||||
+ if (!preview)
|
||||
+ continue;
|
||||
+
|
||||
+ this.child.set_child_above_sibling(preview, lastPreview);
|
||||
+ lastPreview = preview;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _moveWindow(window) {
|
||||
+ let monitorIndex = Main.layoutManager.findIndexForActor(this);
|
||||
+ if (monitorIndex !== window.get_monitor())
|
||||
+ window.move_to_monitor(monitorIndex);
|
||||
+ window.change_workspace_by_index(this._index, false);
|
||||
+ }
|
||||
+
|
||||
+ on_clicked() {
|
||||
+ let ws = global.workspace_manager.get_workspace_by_index(this._index);
|
||||
+ if (ws)
|
||||
+ ws.activate(global.get_current_time());
|
||||
+ }
|
||||
+
|
||||
+ _syncTooltip() {
|
||||
+ if (this.hover) {
|
||||
+ this._tooltip.set({
|
||||
+ text: Meta.prefs_get_workspace_name(this._index),
|
||||
+ visible: true,
|
||||
+ opacity: 0,
|
||||
+ });
|
||||
+
|
||||
+ const [stageX, stageY] = this.get_transformed_position();
|
||||
+ const thumbWidth = this.allocation.get_width();
|
||||
+ const thumbHeight = this.allocation.get_height();
|
||||
+ const tipWidth = this._tooltip.width;
|
||||
+ const xOffset = Math.floor((thumbWidth - tipWidth) / 2);
|
||||
+ const monitor = Main.layoutManager.findMonitorForActor(this);
|
||||
+ const x = Math.clamp(
|
||||
+ stageX + xOffset,
|
||||
+ monitor.x,
|
||||
+ monitor.x + monitor.width - tipWidth);
|
||||
+ const y = stageY + thumbHeight + TOOLTIP_OFFSET;
|
||||
+ this._tooltip.set_position(x, y);
|
||||
+ }
|
||||
+
|
||||
+ this._tooltip.ease({
|
||||
+ opacity: this.hover ? 255 : 0,
|
||||
+ duration: TOOLTIP_ANIMATION_TIME,
|
||||
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
+ onComplete: () => (this._tooltip.visible = this.hover),
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ _onDestroy() {
|
||||
+ this._tooltip.destroy();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ constructor() {
|
||||
+ super(0.5, _('Workspace Indicator'));
|
||||
+
|
||||
+ let container = new St.Widget({
|
||||
+ layout_manager: new Clutter.BinLayout(),
|
||||
+ x_expand: true,
|
||||
+ y_expand: true,
|
||||
+ });
|
||||
+ this.add_child(container);
|
||||
+
|
||||
+ let workspaceManager = global.workspace_manager;
|
||||
+
|
||||
+ this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
+ this._statusLabel = new St.Label({
|
||||
+ style_class: 'panel-workspace-indicator',
|
||||
+ y_align: Clutter.ActorAlign.CENTER,
|
||||
+ text: this._labelText(),
|
||||
+ });
|
||||
+
|
||||
+ container.add_child(this._statusLabel);
|
||||
+
|
||||
+ this._thumbnailsBox = new St.BoxLayout({
|
||||
+ style_class: 'panel-workspace-indicator-box',
|
||||
+ y_expand: true,
|
||||
+ reactive: true,
|
||||
+ });
|
||||
+
|
||||
+ container.add_child(this._thumbnailsBox);
|
||||
+
|
||||
+ this._workspacesItems = [];
|
||||
+ this._workspaceSection = new PopupMenu.PopupMenuSection();
|
||||
+ this.menu.addMenuItem(this._workspaceSection);
|
||||
+
|
||||
+ workspaceManager.connectObject(
|
||||
+ 'notify::n-workspaces', this._nWorkspacesChanged.bind(this), GObject.ConnectFlags.AFTER,
|
||||
+ 'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER,
|
||||
+ 'notify::layout-rows', this._updateThumbnailVisibility.bind(this),
|
||||
+ this);
|
||||
+
|
||||
+ this.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
+ this._thumbnailsBox.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
+ this._createWorkspacesSection();
|
||||
+ this._updateThumbnails();
|
||||
+ this._updateThumbnailVisibility();
|
||||
+
|
||||
+ this._settings = new Gio.Settings({schema_id: WORKSPACE_SCHEMA});
|
||||
+ this._settings.connectObject(`changed::${WORKSPACE_KEY}`,
|
||||
+ this._updateMenuLabels.bind(this), this);
|
||||
+ }
|
||||
+
|
||||
+ _onDestroy() {
|
||||
+ Main.panel.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||||
+
|
||||
+ super._onDestroy();
|
||||
+ }
|
||||
+
|
||||
+ _updateThumbnailVisibility() {
|
||||
+ const {workspaceManager} = global;
|
||||
+ const vertical = workspaceManager.layout_rows === -1;
|
||||
+ const useMenu =
|
||||
+ vertical || workspaceManager.n_workspaces > MAX_THUMBNAILS;
|
||||
+ this.reactive = useMenu;
|
||||
+
|
||||
+ this._statusLabel.visible = useMenu;
|
||||
+ this._thumbnailsBox.visible = !useMenu;
|
||||
+
|
||||
+ // Disable offscreen-redirect when showing the workspace switcher
|
||||
+ // so that clip-to-allocation works
|
||||
+ Main.panel.set_offscreen_redirect(useMenu
|
||||
+ ? Clutter.OffscreenRedirect.ALWAYS
|
||||
+ : Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY);
|
||||
+ }
|
||||
+
|
||||
+ _onWorkspaceSwitched() {
|
||||
+ this._currentWorkspace = global.workspace_manager.get_active_workspace_index();
|
||||
+
|
||||
+ this._updateMenuOrnament();
|
||||
+ this._updateActiveThumbnail();
|
||||
+
|
||||
+ this._statusLabel.set_text(this._labelText());
|
||||
+ }
|
||||
+
|
||||
+ _nWorkspacesChanged() {
|
||||
+ this._createWorkspacesSection();
|
||||
+ this._updateThumbnails();
|
||||
+ this._updateThumbnailVisibility();
|
||||
+ }
|
||||
+
|
||||
+ _updateMenuOrnament() {
|
||||
+ for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
+ this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
||||
+ ? PopupMenu.Ornament.DOT
|
||||
+ : PopupMenu.Ornament.NO_DOT);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _updateActiveThumbnail() {
|
||||
+ let thumbs = this._thumbnailsBox.get_children();
|
||||
+ for (let i = 0; i < thumbs.length; i++) {
|
||||
+ if (i === this._currentWorkspace)
|
||||
+ thumbs[i].add_style_class_name('active');
|
||||
+ else
|
||||
+ thumbs[i].remove_style_class_name('active');
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _labelText(workspaceIndex) {
|
||||
+ if (workspaceIndex === undefined) {
|
||||
+ workspaceIndex = this._currentWorkspace;
|
||||
+ return (workspaceIndex + 1).toString();
|
||||
+ }
|
||||
+ return Meta.prefs_get_workspace_name(workspaceIndex);
|
||||
+ }
|
||||
+
|
||||
+ _updateMenuLabels() {
|
||||
+ for (let i = 0; i < this._workspacesItems.length; i++)
|
||||
+ this._workspacesItems[i].label.text = this._labelText(i);
|
||||
+ }
|
||||
+
|
||||
+ _createWorkspacesSection() {
|
||||
+ let workspaceManager = global.workspace_manager;
|
||||
+
|
||||
+ this._workspaceSection.removeAll();
|
||||
+ this._workspacesItems = [];
|
||||
+ this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
+
|
||||
+ let i = 0;
|
||||
+ for (; i < workspaceManager.n_workspaces; i++) {
|
||||
+ this._workspacesItems[i] = new PopupMenu.PopupMenuItem(this._labelText(i));
|
||||
+ this._workspaceSection.addMenuItem(this._workspacesItems[i]);
|
||||
+ this._workspacesItems[i].workspaceId = i;
|
||||
+ this._workspacesItems[i].label_actor = this._statusLabel;
|
||||
+ this._workspacesItems[i].connect('activate', (actor, _event) => {
|
||||
+ this._activate(actor.workspaceId);
|
||||
+ });
|
||||
+
|
||||
+ this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
||||
+ ? PopupMenu.Ornament.DOT
|
||||
+ : PopupMenu.Ornament.NO_DOT);
|
||||
+ }
|
||||
+
|
||||
+ this._statusLabel.set_text(this._labelText());
|
||||
+ }
|
||||
+
|
||||
+ _updateThumbnails() {
|
||||
+ let workspaceManager = global.workspace_manager;
|
||||
+
|
||||
+ this._thumbnailsBox.destroy_all_children();
|
||||
+
|
||||
+ for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
+ let thumb = new WorkspaceThumbnail(i);
|
||||
+ this._thumbnailsBox.add_child(thumb);
|
||||
+ }
|
||||
+ this._updateActiveThumbnail();
|
||||
+ }
|
||||
+
|
||||
+ _activate(index) {
|
||||
+ let workspaceManager = global.workspace_manager;
|
||||
+
|
||||
+ if (index >= 0 && index < workspaceManager.n_workspaces) {
|
||||
+ let metaWorkspace = workspaceManager.get_workspace_by_index(index);
|
||||
+ metaWorkspace.activate(global.get_current_time());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _onScrollEvent(actor, event) {
|
||||
+ let direction = event.get_scroll_direction();
|
||||
+ let diff = 0;
|
||||
+ if (direction === Clutter.ScrollDirection.DOWN)
|
||||
+ diff = 1;
|
||||
+ else if (direction === Clutter.ScrollDirection.UP)
|
||||
+ diff = -1;
|
||||
+ else
|
||||
+ return;
|
||||
+
|
||||
+
|
||||
+ let newIndex = global.workspace_manager.get_active_workspace_index() + diff;
|
||||
+ this._activate(newIndex);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||||
index b77f6c21..182b2be0 100644
|
||||
--- a/po/POTFILES.in
|
||||
+++ b/po/POTFILES.in
|
||||
@@ -20,5 +20,5 @@ extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml
|
||||
extensions/window-list/prefs.js
|
||||
extensions/window-list/workspaceIndicator.js
|
||||
extensions/windowsNavigator/extension.js
|
||||
-extensions/workspace-indicator/extension.js
|
||||
extensions/workspace-indicator/prefs.js
|
||||
+extensions/workspace-indicator/workspaceIndicator.js
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,81 @@
|
||||
From 5bb91b4303bb0696dce4ad7aeb31035e89c1e9ce Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 19:09:38 +0100
|
||||
Subject: [PATCH 02/28] workspace-indicator: Use descendant style selectors
|
||||
|
||||
Add a style class to the indicator itself, and only select
|
||||
descendant elements. This allows using the briefer class names
|
||||
from the window-list extension without too much risk of conflicts.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/stylesheet.css | 8 ++++----
|
||||
extensions/workspace-indicator/workspaceIndicator.js | 6 ++++--
|
||||
2 files changed, 8 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/stylesheet.css b/extensions/workspace-indicator/stylesheet.css
|
||||
index 7b53a46f..749878c1 100644
|
||||
--- a/extensions/workspace-indicator/stylesheet.css
|
||||
+++ b/extensions/workspace-indicator/stylesheet.css
|
||||
@@ -5,23 +5,23 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
-.panel-workspace-indicator {
|
||||
+.workspace-indicator .status-label {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
-.panel-workspace-indicator-box {
|
||||
+.workspace-indicator .workspaces-box {
|
||||
padding: 4px 0;
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
-.panel-workspace-indicator-box .workspace {
|
||||
+.workspace-indicator .workspace {
|
||||
width: 40px;
|
||||
border: 2px solid #000;
|
||||
border-radius: 2px;
|
||||
background-color: #595959;
|
||||
}
|
||||
|
||||
-.panel-workspace-indicator-box .workspace.active {
|
||||
+.workspace-indicator .workspace.active {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 6b0903d5..4bf9c0a2 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -259,6 +259,8 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
constructor() {
|
||||
super(0.5, _('Workspace Indicator'));
|
||||
|
||||
+ this.add_style_class_name('workspace-indicator');
|
||||
+
|
||||
let container = new St.Widget({
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true,
|
||||
@@ -270,7 +272,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
this._statusLabel = new St.Label({
|
||||
- style_class: 'panel-workspace-indicator',
|
||||
+ style_class: 'status-label',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
text: this._labelText(),
|
||||
});
|
||||
@@ -278,7 +280,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
container.add_child(this._statusLabel);
|
||||
|
||||
this._thumbnailsBox = new St.BoxLayout({
|
||||
- style_class: 'panel-workspace-indicator-box',
|
||||
+ style_class: 'workspaces-box',
|
||||
y_expand: true,
|
||||
reactive: true,
|
||||
});
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,68 @@
|
||||
From 962983e8019817afae63807459eeaf3ff50eab03 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 12:48:43 +0100
|
||||
Subject: [PATCH 03/28] window-list: Use consistent style class prefix
|
||||
|
||||
This will eventually allow us to re-use the workspace-indicator
|
||||
extension without changing anything but the used prefix.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/window-list/stylesheet-dark.css | 4 ++--
|
||||
extensions/window-list/stylesheet-light.css | 4 ++--
|
||||
extensions/window-list/workspaceIndicator.js | 2 +-
|
||||
3 files changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/extensions/window-list/stylesheet-dark.css b/extensions/window-list/stylesheet-dark.css
|
||||
index b4c0a3b4..fbadf4d0 100644
|
||||
--- a/extensions/window-list/stylesheet-dark.css
|
||||
+++ b/extensions/window-list/stylesheet-dark.css
|
||||
@@ -102,12 +102,12 @@
|
||||
background-color: #3f3f3f;
|
||||
}
|
||||
|
||||
-.window-list-window-preview {
|
||||
+.window-list-workspace-indicator-window-preview {
|
||||
background-color: #bebebe;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
-.window-list-window-preview.active {
|
||||
+.window-list-workspace-indicator-window-preview.active {
|
||||
background-color: #d4d4d4;
|
||||
}
|
||||
|
||||
diff --git a/extensions/window-list/stylesheet-light.css b/extensions/window-list/stylesheet-light.css
|
||||
index e4d3a36c..e9352362 100644
|
||||
--- a/extensions/window-list/stylesheet-light.css
|
||||
+++ b/extensions/window-list/stylesheet-light.css
|
||||
@@ -61,11 +61,11 @@
|
||||
border-color: #888;
|
||||
}
|
||||
|
||||
-.window-list-window-preview {
|
||||
+.window-list-workspace-indicator-window-preview {
|
||||
background-color: #ededed;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
-.window-list-window-preview.active {
|
||||
+.window-list-workspace-indicator-window-preview.active {
|
||||
background-color: #f6f5f4;
|
||||
}
|
||||
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
|
||||
index 62ebffbd..5b11fe88 100644
|
||||
--- a/extensions/window-list/workspaceIndicator.js
|
||||
+++ b/extensions/window-list/workspaceIndicator.js
|
||||
@@ -27,7 +27,7 @@ class WindowPreview extends St.Button {
|
||||
|
||||
constructor(window) {
|
||||
super({
|
||||
- style_class: 'window-list-window-preview',
|
||||
+ style_class: 'window-list-workspace-indicator-window-preview',
|
||||
});
|
||||
|
||||
this._delegate = this;
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,57 @@
|
||||
From cadf2bb80984ec3b27b2d22ad65ec675230905c4 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 23 Feb 2024 01:59:15 +0100
|
||||
Subject: [PATCH 04/28] workspace-indicator: Allow overriding base style class
|
||||
|
||||
This will allow reusing the code from the window-list extension
|
||||
without limiting the ability to specify styling that only applies
|
||||
to one of the extensions.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
.../workspace-indicator/workspaceIndicator.js | 13 ++++++++++---
|
||||
1 file changed, 10 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 4bf9c0a2..cdcc67fe 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -25,6 +25,8 @@ const TOOLTIP_ANIMATION_TIME = 150;
|
||||
|
||||
const MAX_THUMBNAILS = 6;
|
||||
|
||||
+let baseStyleClassName = '';
|
||||
+
|
||||
class WindowPreview extends St.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
@@ -32,7 +34,7 @@ class WindowPreview extends St.Button {
|
||||
|
||||
constructor(window) {
|
||||
super({
|
||||
- style_class: 'workspace-indicator-window-preview',
|
||||
+ style_class: `${baseStyleClassName}-window-preview`,
|
||||
});
|
||||
|
||||
this._delegate = this;
|
||||
@@ -256,10 +258,15 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
- constructor() {
|
||||
+ constructor(params = {}) {
|
||||
super(0.5, _('Workspace Indicator'));
|
||||
|
||||
- this.add_style_class_name('workspace-indicator');
|
||||
+ const {
|
||||
+ baseStyleClass = 'workspace-indicator',
|
||||
+ } = params;
|
||||
+
|
||||
+ baseStyleClassName = baseStyleClass;
|
||||
+ this.add_style_class_name(baseStyleClassName);
|
||||
|
||||
let container = new St.Widget({
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,78 @@
|
||||
From a54815b7e6726af56c9300b9c209bb554c3a4a25 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 23 Feb 2024 01:58:50 +0100
|
||||
Subject: [PATCH 05/28] window-list: Override base style class
|
||||
|
||||
Apply the changes from the last commit to the workspace-indicator
|
||||
copy, and override the base style class from the extension.
|
||||
|
||||
This will eventually allow us to share the exact same code between
|
||||
the two extensions, but still use individual styling if necessary.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/window-list/extension.js | 5 ++++-
|
||||
extensions/window-list/workspaceIndicator.js | 15 ++++++++++++---
|
||||
2 files changed, 16 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index 034b72ba..ab5b042c 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -767,7 +767,10 @@ class WindowList extends St.Widget {
|
||||
let indicatorsBox = new St.BoxLayout({x_align: Clutter.ActorAlign.END});
|
||||
box.add_child(indicatorsBox);
|
||||
|
||||
- this._workspaceIndicator = new WorkspaceIndicator();
|
||||
+ this._workspaceIndicator = new WorkspaceIndicator({
|
||||
+ baseStyleClass: 'window-list-workspace-indicator',
|
||||
+ });
|
||||
+
|
||||
indicatorsBox.add_child(this._workspaceIndicator.container);
|
||||
|
||||
this._mutterSettings = new Gio.Settings({schema_id: 'org.gnome.mutter'});
|
||||
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
|
||||
index 5b11fe88..2c557e5b 100644
|
||||
--- a/extensions/window-list/workspaceIndicator.js
|
||||
+++ b/extensions/window-list/workspaceIndicator.js
|
||||
@@ -20,6 +20,8 @@ const TOOLTIP_ANIMATION_TIME = 150;
|
||||
|
||||
const MAX_THUMBNAILS = 6;
|
||||
|
||||
+let baseStyleClassName = '';
|
||||
+
|
||||
class WindowPreview extends St.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
@@ -27,7 +29,7 @@ class WindowPreview extends St.Button {
|
||||
|
||||
constructor(window) {
|
||||
super({
|
||||
- style_class: 'window-list-workspace-indicator-window-preview',
|
||||
+ style_class: `${baseStyleClassName}-window-preview`,
|
||||
});
|
||||
|
||||
this._delegate = this;
|
||||
@@ -251,10 +253,17 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
- constructor() {
|
||||
+ constructor(params = {}) {
|
||||
super(0.5, _('Workspace Indicator'), true);
|
||||
+
|
||||
+ const {
|
||||
+ baseStyleClass = 'workspace-indicator',
|
||||
+ } = params;
|
||||
+
|
||||
+ baseStyleClassName = baseStyleClass;
|
||||
+ this.add_style_class_name(baseStyleClassName);
|
||||
+
|
||||
this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM));
|
||||
- this.add_style_class_name('window-list-workspace-indicator');
|
||||
this.remove_style_class_name('panel-button');
|
||||
this.menu.actor.remove_style_class_name('panel-menu');
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,92 @@
|
||||
From b539608940eb5956bbf1cb9083b7171a9d2d6708 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 12:48:43 +0100
|
||||
Subject: [PATCH 06/28] window-list: Externally adjust workspace menu
|
||||
|
||||
In order to use a PanelMenu.Button in the bottom bar, we have
|
||||
to tweak its menu a bit.
|
||||
|
||||
We currently handle this inside the indicator, but that means the
|
||||
code diverges from the original code in the workspace-indicator
|
||||
extension.
|
||||
|
||||
Avoid this by using a small subclass that handles the adjustments.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/window-list/extension.js | 25 ++++++++++++++++++--
|
||||
extensions/window-list/workspaceIndicator.js | 6 +----
|
||||
2 files changed, 24 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index ab5b042c..1bb7cf60 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -767,10 +767,9 @@ class WindowList extends St.Widget {
|
||||
let indicatorsBox = new St.BoxLayout({x_align: Clutter.ActorAlign.END});
|
||||
box.add_child(indicatorsBox);
|
||||
|
||||
- this._workspaceIndicator = new WorkspaceIndicator({
|
||||
+ this._workspaceIndicator = new BottomWorkspaceIndicator({
|
||||
baseStyleClass: 'window-list-workspace-indicator',
|
||||
});
|
||||
-
|
||||
indicatorsBox.add_child(this._workspaceIndicator.container);
|
||||
|
||||
this._mutterSettings = new Gio.Settings({schema_id: 'org.gnome.mutter'});
|
||||
@@ -1100,6 +1099,28 @@ class WindowList extends St.Widget {
|
||||
}
|
||||
}
|
||||
|
||||
+class BottomWorkspaceIndicator extends WorkspaceIndicator {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ constructor(params) {
|
||||
+ super(params);
|
||||
+
|
||||
+ this.remove_style_class_name('panel-button');
|
||||
+ }
|
||||
+
|
||||
+ setMenu(menu) {
|
||||
+ super.setMenu(menu);
|
||||
+
|
||||
+ if (!menu)
|
||||
+ return;
|
||||
+
|
||||
+ this.menu.actor.updateArrowSide(St.Side.BOTTOM);
|
||||
+ this.menu.actor.remove_style_class_name('panel-menu');
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
export default class WindowListExtension extends Extension {
|
||||
constructor(metadata) {
|
||||
super(metadata);
|
||||
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
|
||||
index 2c557e5b..69167eb6 100644
|
||||
--- a/extensions/window-list/workspaceIndicator.js
|
||||
+++ b/extensions/window-list/workspaceIndicator.js
|
||||
@@ -254,7 +254,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
}
|
||||
|
||||
constructor(params = {}) {
|
||||
- super(0.5, _('Workspace Indicator'), true);
|
||||
+ super(0.5, _('Workspace Indicator'));
|
||||
|
||||
const {
|
||||
baseStyleClass = 'workspace-indicator',
|
||||
@@ -263,10 +263,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
baseStyleClassName = baseStyleClass;
|
||||
this.add_style_class_name(baseStyleClassName);
|
||||
|
||||
- this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM));
|
||||
- this.remove_style_class_name('panel-button');
|
||||
- this.menu.actor.remove_style_class_name('panel-menu');
|
||||
-
|
||||
let container = new St.Widget({
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true,
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,44 @@
|
||||
From dca2c8505c770958849fc8dda27715a09ceab3c5 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 21 Mar 2024 16:49:35 +0100
|
||||
Subject: [PATCH 07/28] window-list: Handle changes to workspace menu
|
||||
|
||||
For now the menu is always set at construction time, however this
|
||||
will change in the future. Prepare for that by handling the
|
||||
`menu-set` signal, similar to the top bar.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/window-list/extension.js | 9 ++++++++-
|
||||
1 file changed, 8 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index 1bb7cf60..3950c535 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -782,7 +782,9 @@ class WindowList extends St.Widget {
|
||||
this._updateWorkspaceIndicatorVisibility();
|
||||
|
||||
this._menuManager = new PopupMenu.PopupMenuManager(this);
|
||||
- this._menuManager.addMenu(this._workspaceIndicator.menu);
|
||||
+ this._workspaceIndicator.connectObject('menu-set',
|
||||
+ () => this._onWorkspaceMenuSet(), this);
|
||||
+ this._onWorkspaceMenuSet();
|
||||
|
||||
Main.layoutManager.addChrome(this, {
|
||||
affectsStruts: true,
|
||||
@@ -879,6 +881,11 @@ class WindowList extends St.Widget {
|
||||
children[newActive].activate();
|
||||
}
|
||||
|
||||
+ _onWorkspaceMenuSet() {
|
||||
+ if (this._workspaceIndicator.menu)
|
||||
+ this._menuManager.addMenu(this._workspaceIndicator.menu);
|
||||
+ }
|
||||
+
|
||||
_updatePosition() {
|
||||
this.set_position(
|
||||
this._monitor.x,
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,47 @@
|
||||
From c343e1d65ddc538972640e7da86429cbd4cf2829 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 15:58:39 +0100
|
||||
Subject: [PATCH 08/28] workspace-indicator: Don't use SCHEMA/KEY constants
|
||||
|
||||
Each constant is only used once, so all they do is disconnect
|
||||
the actual value from the code that uses it.
|
||||
|
||||
The copy in the window-list extension just uses the strings directly,
|
||||
do the same here.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/workspaceIndicator.js | 10 ++++------
|
||||
1 file changed, 4 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index cdcc67fe..fe54b95c 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -17,9 +17,6 @@ import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||||
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
|
||||
-const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences';
|
||||
-const WORKSPACE_KEY = 'workspace-names';
|
||||
-
|
||||
const TOOLTIP_OFFSET = 6;
|
||||
const TOOLTIP_ANIMATION_TIME = 150;
|
||||
|
||||
@@ -310,9 +307,10 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._updateThumbnails();
|
||||
this._updateThumbnailVisibility();
|
||||
|
||||
- this._settings = new Gio.Settings({schema_id: WORKSPACE_SCHEMA});
|
||||
- this._settings.connectObject(`changed::${WORKSPACE_KEY}`,
|
||||
- this._updateMenuLabels.bind(this), this);
|
||||
+ const desktopSettings =
|
||||
+ new Gio.Settings({schema_id: 'org.gnome.desktop.wm.preferences'});
|
||||
+ desktopSettings.connectObject('changed::workspace-names',
|
||||
+ () => this._updateMenuLabels(), this);
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,29 @@
|
||||
From 14ab607a7695297efe98b97ab023973055189cee Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 18:59:23 +0100
|
||||
Subject: [PATCH 09/28] workspace-indicator: Use existing property
|
||||
|
||||
We already track the current workspace index, use that
|
||||
instead of getting it from the workspace manager again.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/workspaceIndicator.js | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index fe54b95c..26c9d7ee 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -439,7 +439,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
return;
|
||||
|
||||
|
||||
- let newIndex = global.workspace_manager.get_active_workspace_index() + diff;
|
||||
+ const newIndex = this._currentWorkspace + diff;
|
||||
this._activate(newIndex);
|
||||
}
|
||||
}
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,70 @@
|
||||
From c83fd93d2e7cae124a5fddcb87f415688b3a63b6 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 16:14:24 +0100
|
||||
Subject: [PATCH 10/28] workspace-indicator: Don't use menu section
|
||||
|
||||
We never added anything else to the menu, so we can just operate
|
||||
on the entire menu instead of an intermediate section.
|
||||
|
||||
This removes another difference with the window-list copy.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/workspaceIndicator.js | 12 +++++-------
|
||||
1 file changed, 5 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 26c9d7ee..117c7a65 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -292,8 +292,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
container.add_child(this._thumbnailsBox);
|
||||
|
||||
this._workspacesItems = [];
|
||||
- this._workspaceSection = new PopupMenu.PopupMenuSection();
|
||||
- this.menu.addMenuItem(this._workspaceSection);
|
||||
|
||||
workspaceManager.connectObject(
|
||||
'notify::n-workspaces', this._nWorkspacesChanged.bind(this), GObject.ConnectFlags.AFTER,
|
||||
@@ -303,7 +301,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
this.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
this._thumbnailsBox.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
- this._createWorkspacesSection();
|
||||
+ this._updateMenu();
|
||||
this._updateThumbnails();
|
||||
this._updateThumbnailVisibility();
|
||||
|
||||
@@ -346,7 +344,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
}
|
||||
|
||||
_nWorkspacesChanged() {
|
||||
- this._createWorkspacesSection();
|
||||
+ this._updateMenu();
|
||||
this._updateThumbnails();
|
||||
this._updateThumbnailVisibility();
|
||||
}
|
||||
@@ -382,17 +380,17 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._workspacesItems[i].label.text = this._labelText(i);
|
||||
}
|
||||
|
||||
- _createWorkspacesSection() {
|
||||
+ _updateMenu() {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
|
||||
- this._workspaceSection.removeAll();
|
||||
+ this.menu.removeAll();
|
||||
this._workspacesItems = [];
|
||||
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
|
||||
let i = 0;
|
||||
for (; i < workspaceManager.n_workspaces; i++) {
|
||||
this._workspacesItems[i] = new PopupMenu.PopupMenuItem(this._labelText(i));
|
||||
- this._workspaceSection.addMenuItem(this._workspacesItems[i]);
|
||||
+ this.menu.addMenuItem(this._workspacesItems[i]);
|
||||
this._workspacesItems[i].workspaceId = i;
|
||||
this._workspacesItems[i].label_actor = this._statusLabel;
|
||||
this._workspacesItems[i].connect('activate', (actor, _event) => {
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,44 @@
|
||||
From 8105cdf701db7af78bc2e17da6e01172ce7362c3 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 13:05:15 +0100
|
||||
Subject: [PATCH 11/28] workspace-indicator: Support showing tooltips above
|
||||
|
||||
The indicator is located in the top bar, so tooltips are always
|
||||
shown below the previews. However supporting showing tooltips
|
||||
above previews when space permits allows the same code to be
|
||||
used in the copy that is included with the window-list extension.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/workspaceIndicator.js | 9 +++++----
|
||||
1 file changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 117c7a65..9a1055aa 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -224,16 +224,17 @@ class WorkspaceThumbnail extends St.Button {
|
||||
});
|
||||
|
||||
const [stageX, stageY] = this.get_transformed_position();
|
||||
- const thumbWidth = this.allocation.get_width();
|
||||
- const thumbHeight = this.allocation.get_height();
|
||||
- const tipWidth = this._tooltip.width;
|
||||
+ const [thumbWidth, thumbHeight] = this.allocation.get_size();
|
||||
+ const [tipWidth, tipHeight] = this._tooltip.get_size();
|
||||
const xOffset = Math.floor((thumbWidth - tipWidth) / 2);
|
||||
const monitor = Main.layoutManager.findMonitorForActor(this);
|
||||
const x = Math.clamp(
|
||||
stageX + xOffset,
|
||||
monitor.x,
|
||||
monitor.x + monitor.width - tipWidth);
|
||||
- const y = stageY + thumbHeight + TOOLTIP_OFFSET;
|
||||
+ const y = stageY - monitor.y > thumbHeight + TOOLTIP_OFFSET
|
||||
+ ? stageY - tipHeight - TOOLTIP_OFFSET // show above
|
||||
+ : stageY + thumbHeight + TOOLTIP_OFFSET; // show below
|
||||
this._tooltip.set_position(x, y);
|
||||
}
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,67 @@
|
||||
From d595d168ed0fccf57b1995a4d36169d978820a7b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 17:37:16 +0100
|
||||
Subject: [PATCH 12/28] workspace-indicator: Only change top bar redirect when
|
||||
in top bar
|
||||
|
||||
While this is always the case for the workspace indicator, adding
|
||||
the check will allow to use the same code in the window list.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
.../workspace-indicator/workspaceIndicator.js | 23 +++++++++++++++++--
|
||||
1 file changed, 21 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 9a1055aa..f118654e 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -302,6 +302,16 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
this.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
this._thumbnailsBox.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
+
|
||||
+ this._inTopBar = false;
|
||||
+ this.connect('notify::realized', () => {
|
||||
+ if (!this.realized)
|
||||
+ return;
|
||||
+
|
||||
+ this._inTopBar = Main.panel.contains(this);
|
||||
+ this._updateTopBarRedirect();
|
||||
+ });
|
||||
+
|
||||
this._updateMenu();
|
||||
this._updateThumbnails();
|
||||
this._updateThumbnailVisibility();
|
||||
@@ -313,7 +323,9 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
- Main.panel.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||||
+ if (this._inTopBar)
|
||||
+ Main.panel.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||||
+ this._inTopBar = false;
|
||||
|
||||
super._onDestroy();
|
||||
}
|
||||
@@ -328,9 +340,16 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._statusLabel.visible = useMenu;
|
||||
this._thumbnailsBox.visible = !useMenu;
|
||||
|
||||
+ this._updateTopBarRedirect();
|
||||
+ }
|
||||
+
|
||||
+ _updateTopBarRedirect() {
|
||||
+ if (!this._inTopBar)
|
||||
+ return;
|
||||
+
|
||||
// Disable offscreen-redirect when showing the workspace switcher
|
||||
// so that clip-to-allocation works
|
||||
- Main.panel.set_offscreen_redirect(useMenu
|
||||
+ Main.panel.set_offscreen_redirect(this._thumbnailsBox.visible
|
||||
? Clutter.OffscreenRedirect.ALWAYS
|
||||
: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY);
|
||||
}
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,49 @@
|
||||
From 49bfa46b5981e94b240780240115fcdabd46a178 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 16:13:00 +0100
|
||||
Subject: [PATCH 13/28] workspace-indicator: Small cleanup
|
||||
|
||||
The code to update the menu labels is a bit cleaner in the
|
||||
window-list extension, so use that.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
.../workspace-indicator/workspaceIndicator.js | 19 +++++++++----------
|
||||
1 file changed, 9 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index f118654e..e04e93a4 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -407,19 +407,18 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._workspacesItems = [];
|
||||
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
|
||||
- let i = 0;
|
||||
- for (; i < workspaceManager.n_workspaces; i++) {
|
||||
- this._workspacesItems[i] = new PopupMenu.PopupMenuItem(this._labelText(i));
|
||||
- this.menu.addMenuItem(this._workspacesItems[i]);
|
||||
- this._workspacesItems[i].workspaceId = i;
|
||||
- this._workspacesItems[i].label_actor = this._statusLabel;
|
||||
- this._workspacesItems[i].connect('activate', (actor, _event) => {
|
||||
- this._activate(actor.workspaceId);
|
||||
- });
|
||||
+ for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
+ const item = new PopupMenu.PopupMenuItem(this._labelText(i));
|
||||
|
||||
- this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
||||
+ item.connect('activate',
|
||||
+ () => this._activate(i));
|
||||
+
|
||||
+ item.setOrnament(i === this._currentWorkspace
|
||||
? PopupMenu.Ornament.DOT
|
||||
: PopupMenu.Ornament.NO_DOT);
|
||||
+
|
||||
+ this.menu.addMenuItem(item);
|
||||
+ this._workspacesItems[i] = item;
|
||||
}
|
||||
|
||||
this._statusLabel.set_text(this._labelText());
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,87 @@
|
||||
From 5455a97a04c6a2925f5113a85c2e09cff215941d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 16:13:00 +0100
|
||||
Subject: [PATCH 14/28] workspace-indicator: Simplify getting status text
|
||||
|
||||
Currently the same method is used to get the label text for the
|
||||
indicator itself and for the menu items.
|
||||
|
||||
A method that behaves significantly different depending on whether
|
||||
a parameter is passed is confusing, so only deal with the indicator
|
||||
label and directly use the mutter API to get the workspace names
|
||||
for menu items.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
.../workspace-indicator/workspaceIndicator.js | 24 +++++++++----------
|
||||
1 file changed, 12 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index e04e93a4..0538e6b2 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -279,7 +279,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._statusLabel = new St.Label({
|
||||
style_class: 'status-label',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
- text: this._labelText(),
|
||||
+ text: this._getStatusText(),
|
||||
});
|
||||
|
||||
container.add_child(this._statusLabel);
|
||||
@@ -360,7 +360,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._updateMenuOrnament();
|
||||
this._updateActiveThumbnail();
|
||||
|
||||
- this._statusLabel.set_text(this._labelText());
|
||||
+ this._statusLabel.set_text(this._getStatusText());
|
||||
}
|
||||
|
||||
_nWorkspacesChanged() {
|
||||
@@ -387,17 +387,16 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
}
|
||||
}
|
||||
|
||||
- _labelText(workspaceIndex) {
|
||||
- if (workspaceIndex === undefined) {
|
||||
- workspaceIndex = this._currentWorkspace;
|
||||
- return (workspaceIndex + 1).toString();
|
||||
- }
|
||||
- return Meta.prefs_get_workspace_name(workspaceIndex);
|
||||
+ _getStatusText() {
|
||||
+ const current = this._currentWorkspace + 1;
|
||||
+ return `${current}`;
|
||||
}
|
||||
|
||||
_updateMenuLabels() {
|
||||
- for (let i = 0; i < this._workspacesItems.length; i++)
|
||||
- this._workspacesItems[i].label.text = this._labelText(i);
|
||||
+ for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
+ const item = this._workspacesItems[i];
|
||||
+ item.label.text = Meta.prefs_get_workspace_name(i);
|
||||
+ }
|
||||
}
|
||||
|
||||
_updateMenu() {
|
||||
@@ -408,7 +407,8 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
|
||||
for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
- const item = new PopupMenu.PopupMenuItem(this._labelText(i));
|
||||
+ const name = Meta.prefs_get_workspace_name(i);
|
||||
+ const item = new PopupMenu.PopupMenuItem(name);
|
||||
|
||||
item.connect('activate',
|
||||
() => this._activate(i));
|
||||
@@ -421,7 +421,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._workspacesItems[i] = item;
|
||||
}
|
||||
|
||||
- this._statusLabel.set_text(this._labelText());
|
||||
+ this._statusLabel.set_text(this._getStatusText());
|
||||
}
|
||||
|
||||
_updateThumbnails() {
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,37 @@
|
||||
From f14ae4e2725b4a98ec2e11151c4162e7a6494604 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 16:35:09 +0100
|
||||
Subject: [PATCH 15/28] workspace-indicator: Include n-workspaces in status
|
||||
label
|
||||
|
||||
The two extensions currently use a slightly different label
|
||||
in menu mode:
|
||||
The workspace indicator uses the plain workspace number ("2"),
|
||||
while the window list includes the number of workspaces ("2 / 4").
|
||||
|
||||
The additional information seem useful, as well as the slightly
|
||||
bigger click/touch target, so copy the window-list behavior.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/workspaceIndicator.js | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 0538e6b2..594a9e51 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -388,8 +388,9 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
}
|
||||
|
||||
_getStatusText() {
|
||||
+ const {nWorkspaces} = global.workspace_manager;
|
||||
const current = this._currentWorkspace + 1;
|
||||
- return `${current}`;
|
||||
+ return `${current} / ${nWorkspaces}`;
|
||||
}
|
||||
|
||||
_updateMenuLabels() {
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,56 @@
|
||||
From aca00d6e3a8b7d1fc59b19e9855685989127d59f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 22 Feb 2024 04:45:23 +0100
|
||||
Subject: [PATCH 16/28] workspace-indicator: Tweak preview style
|
||||
|
||||
Sync sizes and padding with the window-list previews.
|
||||
|
||||
Tone down the colors a bit, but less then the current window-list
|
||||
style where workspaces blend too much into the background and
|
||||
the selection is unclear.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/stylesheet.css | 15 ++++++++-------
|
||||
1 file changed, 8 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/stylesheet.css b/extensions/workspace-indicator/stylesheet.css
|
||||
index 749878c1..3e2ba67f 100644
|
||||
--- a/extensions/workspace-indicator/stylesheet.css
|
||||
+++ b/extensions/workspace-indicator/stylesheet.css
|
||||
@@ -10,24 +10,25 @@
|
||||
}
|
||||
|
||||
.workspace-indicator .workspaces-box {
|
||||
- padding: 4px 0;
|
||||
- spacing: 4px;
|
||||
+ padding: 5px;
|
||||
+ spacing: 3px;
|
||||
}
|
||||
|
||||
.workspace-indicator .workspace {
|
||||
- width: 40px;
|
||||
- border: 2px solid #000;
|
||||
- border-radius: 2px;
|
||||
- background-color: #595959;
|
||||
+ width: 52px;
|
||||
+ border: 2px solid transparent;
|
||||
+ border-radius: 4px;
|
||||
+ background-color: #3f3f3f;
|
||||
}
|
||||
|
||||
.workspace-indicator .workspace.active {
|
||||
- border-color: #fff;
|
||||
+ border-color: #9f9f9f;
|
||||
}
|
||||
|
||||
.workspace-indicator-window-preview {
|
||||
background-color: #bebebe;
|
||||
border: 1px solid #828282;
|
||||
+ border-radius: 1px;
|
||||
}
|
||||
|
||||
.workspace-indicator-window-preview.active {
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,71 @@
|
||||
From 5d7cd70f55a3da9454bffaf0bbcb32dacc8e1971 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 23:22:58 +0100
|
||||
Subject: [PATCH 17/28] workspace-indicator: Support light style
|
||||
|
||||
The window-list extension already includes light styling for
|
||||
its copy of the workspace indicator. Just copy that over to
|
||||
support the light variant here as well.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/workspace-indicator/meson.build | 5 +++-
|
||||
.../{stylesheet.css => stylesheet-dark.css} | 0
|
||||
.../workspace-indicator/stylesheet-light.css | 25 +++++++++++++++++++
|
||||
3 files changed, 29 insertions(+), 1 deletion(-)
|
||||
rename extensions/workspace-indicator/{stylesheet.css => stylesheet-dark.css} (100%)
|
||||
create mode 100644 extensions/workspace-indicator/stylesheet-light.css
|
||||
|
||||
diff --git a/extensions/workspace-indicator/meson.build b/extensions/workspace-indicator/meson.build
|
||||
index 6dd08dae..dada5408 100644
|
||||
--- a/extensions/workspace-indicator/meson.build
|
||||
+++ b/extensions/workspace-indicator/meson.build
|
||||
@@ -7,6 +7,9 @@ extension_data += configure_file(
|
||||
output: metadata_name,
|
||||
configuration: metadata_conf
|
||||
)
|
||||
-extension_data += files('stylesheet.css')
|
||||
+extension_data += files(
|
||||
+ 'stylesheet-dark.css',
|
||||
+ 'stylesheet-light.css',
|
||||
+)
|
||||
|
||||
extension_sources += files('prefs.js', 'workspaceIndicator.js')
|
||||
diff --git a/extensions/workspace-indicator/stylesheet.css b/extensions/workspace-indicator/stylesheet-dark.css
|
||||
similarity index 100%
|
||||
rename from extensions/workspace-indicator/stylesheet.css
|
||||
rename to extensions/workspace-indicator/stylesheet-dark.css
|
||||
diff --git a/extensions/workspace-indicator/stylesheet-light.css b/extensions/workspace-indicator/stylesheet-light.css
|
||||
new file mode 100644
|
||||
index 00000000..049b6a38
|
||||
--- /dev/null
|
||||
+++ b/extensions/workspace-indicator/stylesheet-light.css
|
||||
@@ -0,0 +1,25 @@
|
||||
+/*
|
||||
+ * SPDX-FileCopyrightText: 2013 Florian Müllner <fmuellner@gnome.org>
|
||||
+ * SPDX-FileCopyrightText: 2015 Jakub Steiner <jimmac@gmail.com>
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+ */
|
||||
+
|
||||
+@import url("stylesheet-dark.css");
|
||||
+
|
||||
+.workspace-indicator .workspace {
|
||||
+ background-color: #ccc;
|
||||
+}
|
||||
+
|
||||
+.workspace-indicator .workspace.active {
|
||||
+ border-color: #888;
|
||||
+}
|
||||
+
|
||||
+.workspace-indicator-window-preview {
|
||||
+ background-color: #ededed;
|
||||
+ border: 1px solid #ccc;
|
||||
+}
|
||||
+
|
||||
+.workspace-indicator-window-preview.active {
|
||||
+ background-color: #f6f5f4;
|
||||
+}
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,30 @@
|
||||
From d0730c0d9314963bb784ebce408c29b8c2610fc4 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Mon, 15 Apr 2024 20:18:34 +0200
|
||||
Subject: [PATCH 18/28] export-zips: Pick up non-default stylesheets
|
||||
|
||||
The window-list extension is about to import the workspace-indicator
|
||||
stylesheet. Explicitly pack css files, so the stylesheet is included
|
||||
in the bundle.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
export-zips.sh | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/export-zips.sh b/export-zips.sh
|
||||
index 57c62860..2d7383c3 100755
|
||||
--- a/export-zips.sh
|
||||
+++ b/export-zips.sh
|
||||
@@ -39,7 +39,7 @@ for f in $extensiondir/*; do
|
||||
fi
|
||||
|
||||
cp $srcdir/NEWS $srcdir/COPYING $f
|
||||
- sources=(NEWS COPYING $(cd $f; ls *.js))
|
||||
+ sources=(NEWS COPYING $(cd $f; ls *.js *.css 2>/dev/null))
|
||||
|
||||
[ -d $f/icons ] && sources+=(icons)
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,589 @@
|
||||
From b15212a2d62ff93c1652ef717f778dda3a70f5e8 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Wed, 21 Feb 2024 13:08:52 +0100
|
||||
Subject: [PATCH 19/28] window-list: Use actual copy of workspace-indicator
|
||||
|
||||
We are now at a point where the code from the workspace-indicator
|
||||
extension is usable from the window-list.
|
||||
|
||||
However instead of updating the copy, go one step further and
|
||||
remove it altogether, and copy the required files at build time.
|
||||
|
||||
This ensures that future changes are picked up by both extensions
|
||||
without duplicating any work.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/307>
|
||||
---
|
||||
extensions/window-list/meson.build | 29 +-
|
||||
extensions/window-list/stylesheet-dark.css | 31 +-
|
||||
extensions/window-list/stylesheet-light.css | 20 +-
|
||||
extensions/window-list/workspaceIndicator.js | 435 -------------------
|
||||
4 files changed, 30 insertions(+), 485 deletions(-)
|
||||
delete mode 100644 extensions/window-list/workspaceIndicator.js
|
||||
|
||||
diff --git a/extensions/window-list/meson.build b/extensions/window-list/meson.build
|
||||
index 718cdf7a..6fd17007 100644
|
||||
--- a/extensions/window-list/meson.build
|
||||
+++ b/extensions/window-list/meson.build
|
||||
@@ -12,5 +12,32 @@ extension_data += files(
|
||||
'stylesheet-light.css'
|
||||
)
|
||||
|
||||
-extension_sources += files('prefs.js', 'workspaceIndicator.js')
|
||||
+transform_stylesheet = [
|
||||
+ 'sed', '-E',
|
||||
+ '-e', 's:^\.(workspace-indicator):.window-list-\\1:',
|
||||
+ '-e', '/^@import/d',
|
||||
+ '@INPUT@',
|
||||
+ ]
|
||||
+
|
||||
+workspaceIndicatorSources = [
|
||||
+ configure_file(
|
||||
+ input: '../workspace-indicator/workspaceIndicator.js',
|
||||
+ output: '@PLAINNAME@',
|
||||
+ copy: true,
|
||||
+ ),
|
||||
+ configure_file(
|
||||
+ input: '../workspace-indicator/stylesheet-dark.css',
|
||||
+ output: 'stylesheet-workspace-switcher-dark.css',
|
||||
+ command: transform_stylesheet,
|
||||
+ capture: true,
|
||||
+ ),
|
||||
+ configure_file(
|
||||
+ input: '../workspace-indicator/stylesheet-light.css',
|
||||
+ output: 'stylesheet-workspace-switcher-light.css',
|
||||
+ command: transform_stylesheet,
|
||||
+ capture: true,
|
||||
+ ),
|
||||
+]
|
||||
+
|
||||
+extension_sources += files('prefs.js') + workspaceIndicatorSources
|
||||
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
diff --git a/extensions/window-list/stylesheet-dark.css b/extensions/window-list/stylesheet-dark.css
|
||||
index fbadf4d0..9e024f2c 100644
|
||||
--- a/extensions/window-list/stylesheet-dark.css
|
||||
+++ b/extensions/window-list/stylesheet-dark.css
|
||||
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
+@import url("stylesheet-workspace-switcher-dark.css");
|
||||
|
||||
.window-list {
|
||||
spacing: 2px;
|
||||
@@ -81,36 +82,6 @@
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
-.window-list-workspace-indicator .status-label-bin {
|
||||
- background-color: rgba(200, 200, 200, 0.3);
|
||||
- padding: 5px;
|
||||
- margin: 3px;
|
||||
-}
|
||||
-
|
||||
-.window-list-workspace-indicator .workspaces-box {
|
||||
- spacing: 3px;
|
||||
- padding: 5px;
|
||||
-}
|
||||
-
|
||||
-.window-list-workspace-indicator .workspace {
|
||||
- width: 52px;
|
||||
- border-radius: 4px;
|
||||
- background-color: #1e1e1e;
|
||||
-}
|
||||
-
|
||||
-.window-list-workspace-indicator .workspace.active {
|
||||
- background-color: #3f3f3f;
|
||||
-}
|
||||
-
|
||||
-.window-list-workspace-indicator-window-preview {
|
||||
- background-color: #bebebe;
|
||||
- border-radius: 1px;
|
||||
-}
|
||||
-
|
||||
-.window-list-workspace-indicator-window-preview.active {
|
||||
- background-color: #d4d4d4;
|
||||
-}
|
||||
-
|
||||
.notification {
|
||||
font-weight: normal;
|
||||
}
|
||||
diff --git a/extensions/window-list/stylesheet-light.css b/extensions/window-list/stylesheet-light.css
|
||||
index e9352362..93a96581 100644
|
||||
--- a/extensions/window-list/stylesheet-light.css
|
||||
+++ b/extensions/window-list/stylesheet-light.css
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
@import url("stylesheet-dark.css");
|
||||
+@import url("stylesheet-workspace-switcher-light.css");
|
||||
|
||||
#panel.bottom-panel {
|
||||
border-top-width: 1px;
|
||||
@@ -50,22 +51,3 @@
|
||||
color: #888;
|
||||
box-shadow: none;
|
||||
}
|
||||
-
|
||||
-/* workspace switcher */
|
||||
-.window-list-workspace-indicator .workspace {
|
||||
- border: 2px solid #f6f5f4;
|
||||
- background-color: #ccc;
|
||||
-}
|
||||
-
|
||||
-.window-list-workspace-indicator .workspace.active {
|
||||
- border-color: #888;
|
||||
-}
|
||||
-
|
||||
-.window-list-workspace-indicator-window-preview {
|
||||
- background-color: #ededed;
|
||||
- border: 1px solid #ccc;
|
||||
-}
|
||||
-
|
||||
-.window-list-workspace-indicator-window-preview.active {
|
||||
- background-color: #f6f5f4;
|
||||
-}
|
||||
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
|
||||
deleted file mode 100644
|
||||
index 69167eb6..00000000
|
||||
--- a/extensions/window-list/workspaceIndicator.js
|
||||
+++ /dev/null
|
||||
@@ -1,435 +0,0 @@
|
||||
-// SPDX-FileCopyrightText: 2019 Florian Müllner <fmuellner@gnome.org>
|
||||
-//
|
||||
-// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-
|
||||
-import Clutter from 'gi://Clutter';
|
||||
-import Gio from 'gi://Gio';
|
||||
-import GObject from 'gi://GObject';
|
||||
-import Meta from 'gi://Meta';
|
||||
-import St from 'gi://St';
|
||||
-
|
||||
-import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
-
|
||||
-import * as DND from 'resource:///org/gnome/shell/ui/dnd.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 PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
-
|
||||
-const TOOLTIP_OFFSET = 6;
|
||||
-const TOOLTIP_ANIMATION_TIME = 150;
|
||||
-
|
||||
-const MAX_THUMBNAILS = 6;
|
||||
-
|
||||
-let baseStyleClassName = '';
|
||||
-
|
||||
-class WindowPreview extends St.Button {
|
||||
- static {
|
||||
- GObject.registerClass(this);
|
||||
- }
|
||||
-
|
||||
- constructor(window) {
|
||||
- super({
|
||||
- style_class: `${baseStyleClassName}-window-preview`,
|
||||
- });
|
||||
-
|
||||
- this._delegate = this;
|
||||
- DND.makeDraggable(this, {restoreOnSuccess: true});
|
||||
-
|
||||
- this._window = window;
|
||||
-
|
||||
- this._window.connectObject(
|
||||
- 'size-changed', () => this._checkRelayout(),
|
||||
- 'position-changed', () => this._checkRelayout(),
|
||||
- 'notify::minimized', this._updateVisible.bind(this),
|
||||
- 'notify::window-type', this._updateVisible.bind(this),
|
||||
- this);
|
||||
- this._updateVisible();
|
||||
-
|
||||
- global.display.connectObject('notify::focus-window',
|
||||
- this._onFocusChanged.bind(this), this);
|
||||
- this._onFocusChanged();
|
||||
- }
|
||||
-
|
||||
- // needed for DND
|
||||
- get metaWindow() {
|
||||
- return this._window;
|
||||
- }
|
||||
-
|
||||
- _onFocusChanged() {
|
||||
- if (global.display.focus_window === this._window)
|
||||
- this.add_style_class_name('active');
|
||||
- else
|
||||
- this.remove_style_class_name('active');
|
||||
- }
|
||||
-
|
||||
- _checkRelayout() {
|
||||
- const monitor = Main.layoutManager.findIndexForActor(this);
|
||||
- const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
||||
- if (this._window.get_frame_rect().overlap(workArea))
|
||||
- this.queue_relayout();
|
||||
- }
|
||||
-
|
||||
- _updateVisible() {
|
||||
- this.visible = this._window.window_type !== Meta.WindowType.DESKTOP &&
|
||||
- this._window.showing_on_its_workspace();
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-class WorkspaceLayout extends Clutter.LayoutManager {
|
||||
- static {
|
||||
- GObject.registerClass(this);
|
||||
- }
|
||||
-
|
||||
- vfunc_get_preferred_width() {
|
||||
- return [0, 0];
|
||||
- }
|
||||
-
|
||||
- vfunc_get_preferred_height() {
|
||||
- return [0, 0];
|
||||
- }
|
||||
-
|
||||
- vfunc_allocate(container, box) {
|
||||
- const monitor = Main.layoutManager.findIndexForActor(container);
|
||||
- const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor);
|
||||
- const hscale = box.get_width() / workArea.width;
|
||||
- const vscale = box.get_height() / workArea.height;
|
||||
-
|
||||
- for (const child of container) {
|
||||
- const childBox = new Clutter.ActorBox();
|
||||
- const frameRect = child.metaWindow.get_frame_rect();
|
||||
- childBox.set_size(
|
||||
- Math.round(Math.min(frameRect.width, workArea.width) * hscale),
|
||||
- Math.round(Math.min(frameRect.height, workArea.height) * vscale));
|
||||
- childBox.set_origin(
|
||||
- Math.round((frameRect.x - workArea.x) * hscale),
|
||||
- Math.round((frameRect.y - workArea.y) * vscale));
|
||||
- child.allocate(childBox);
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-class WorkspaceThumbnail extends St.Button {
|
||||
- static {
|
||||
- GObject.registerClass(this);
|
||||
- }
|
||||
-
|
||||
- constructor(index) {
|
||||
- super({
|
||||
- style_class: 'workspace',
|
||||
- child: new Clutter.Actor({
|
||||
- layout_manager: new WorkspaceLayout(),
|
||||
- clip_to_allocation: true,
|
||||
- x_expand: true,
|
||||
- y_expand: true,
|
||||
- }),
|
||||
- });
|
||||
-
|
||||
- this._tooltip = new St.Label({
|
||||
- style_class: 'dash-label',
|
||||
- visible: false,
|
||||
- });
|
||||
- Main.uiGroup.add_child(this._tooltip);
|
||||
-
|
||||
- this.connect('destroy', this._onDestroy.bind(this));
|
||||
- this.connect('notify::hover', this._syncTooltip.bind(this));
|
||||
-
|
||||
- this._index = index;
|
||||
- this._delegate = this; // needed for DND
|
||||
-
|
||||
- this._windowPreviews = new Map();
|
||||
-
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
- this._workspace = workspaceManager.get_workspace_by_index(index);
|
||||
-
|
||||
- this._workspace.connectObject(
|
||||
- 'window-added', (ws, window) => this._addWindow(window),
|
||||
- 'window-removed', (ws, window) => this._removeWindow(window),
|
||||
- this);
|
||||
-
|
||||
- global.display.connectObject('restacked',
|
||||
- this._onRestacked.bind(this), this);
|
||||
-
|
||||
- this._workspace.list_windows().forEach(w => this._addWindow(w));
|
||||
- this._onRestacked();
|
||||
- }
|
||||
-
|
||||
- acceptDrop(source) {
|
||||
- if (!source.metaWindow)
|
||||
- return false;
|
||||
-
|
||||
- this._moveWindow(source.metaWindow);
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- handleDragOver(source) {
|
||||
- if (source.metaWindow)
|
||||
- return DND.DragMotionResult.MOVE_DROP;
|
||||
- else
|
||||
- return DND.DragMotionResult.CONTINUE;
|
||||
- }
|
||||
-
|
||||
- _addWindow(window) {
|
||||
- if (this._windowPreviews.has(window))
|
||||
- return;
|
||||
-
|
||||
- let preview = new WindowPreview(window);
|
||||
- preview.connect('clicked', (a, btn) => this.emit('clicked', btn));
|
||||
- this._windowPreviews.set(window, preview);
|
||||
- this.child.add_child(preview);
|
||||
- }
|
||||
-
|
||||
- _removeWindow(window) {
|
||||
- let preview = this._windowPreviews.get(window);
|
||||
- if (!preview)
|
||||
- return;
|
||||
-
|
||||
- this._windowPreviews.delete(window);
|
||||
- preview.destroy();
|
||||
- }
|
||||
-
|
||||
- _onRestacked() {
|
||||
- let lastPreview = null;
|
||||
- let windows = global.get_window_actors().map(a => a.meta_window);
|
||||
- for (let i = 0; i < windows.length; i++) {
|
||||
- let preview = this._windowPreviews.get(windows[i]);
|
||||
- if (!preview)
|
||||
- continue;
|
||||
-
|
||||
- this.child.set_child_above_sibling(preview, lastPreview);
|
||||
- lastPreview = preview;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _moveWindow(window) {
|
||||
- let monitorIndex = Main.layoutManager.findIndexForActor(this);
|
||||
- if (monitorIndex !== window.get_monitor())
|
||||
- window.move_to_monitor(monitorIndex);
|
||||
- window.change_workspace_by_index(this._index, false);
|
||||
- }
|
||||
-
|
||||
- on_clicked() {
|
||||
- let ws = global.workspace_manager.get_workspace_by_index(this._index);
|
||||
- if (ws)
|
||||
- ws.activate(global.get_current_time());
|
||||
- }
|
||||
-
|
||||
- _syncTooltip() {
|
||||
- if (this.hover) {
|
||||
- this._tooltip.set({
|
||||
- text: Meta.prefs_get_workspace_name(this._index),
|
||||
- visible: true,
|
||||
- opacity: 0,
|
||||
- });
|
||||
-
|
||||
- const [stageX, stageY] = this.get_transformed_position();
|
||||
- const thumbWidth = this.allocation.get_width();
|
||||
- const tipWidth = this._tooltip.width;
|
||||
- const tipHeight = this._tooltip.height;
|
||||
- const xOffset = Math.floor((thumbWidth - tipWidth) / 2);
|
||||
- const monitor = Main.layoutManager.findMonitorForActor(this);
|
||||
- const x = Math.clamp(
|
||||
- stageX + xOffset,
|
||||
- monitor.x,
|
||||
- monitor.x + monitor.width - tipWidth);
|
||||
- const y = stageY - tipHeight - TOOLTIP_OFFSET;
|
||||
- this._tooltip.set_position(x, y);
|
||||
- }
|
||||
-
|
||||
- this._tooltip.ease({
|
||||
- opacity: this.hover ? 255 : 0,
|
||||
- duration: TOOLTIP_ANIMATION_TIME,
|
||||
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
- onComplete: () => (this._tooltip.visible = this.hover),
|
||||
- });
|
||||
- }
|
||||
-
|
||||
- _onDestroy() {
|
||||
- this._tooltip.destroy();
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
- static {
|
||||
- GObject.registerClass(this);
|
||||
- }
|
||||
-
|
||||
- constructor(params = {}) {
|
||||
- super(0.5, _('Workspace Indicator'));
|
||||
-
|
||||
- const {
|
||||
- baseStyleClass = 'workspace-indicator',
|
||||
- } = params;
|
||||
-
|
||||
- baseStyleClassName = baseStyleClass;
|
||||
- this.add_style_class_name(baseStyleClassName);
|
||||
-
|
||||
- let container = new St.Widget({
|
||||
- layout_manager: new Clutter.BinLayout(),
|
||||
- x_expand: true,
|
||||
- y_expand: true,
|
||||
- });
|
||||
- this.add_child(container);
|
||||
-
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
- this._statusLabel = new St.Label({text: this._getStatusText()});
|
||||
-
|
||||
- this._statusBin = new St.Bin({
|
||||
- style_class: 'status-label-bin',
|
||||
- x_expand: true,
|
||||
- y_expand: true,
|
||||
- child: this._statusLabel,
|
||||
- });
|
||||
- container.add_child(this._statusBin);
|
||||
-
|
||||
- this._thumbnailsBox = new St.BoxLayout({
|
||||
- style_class: 'workspaces-box',
|
||||
- y_expand: true,
|
||||
- reactive: true,
|
||||
- });
|
||||
- this._thumbnailsBox.connect('scroll-event',
|
||||
- this._onScrollEvent.bind(this));
|
||||
- container.add_child(this._thumbnailsBox);
|
||||
-
|
||||
- this._workspacesItems = [];
|
||||
-
|
||||
- workspaceManager.connectObject(
|
||||
- 'notify::n-workspaces', this._nWorkspacesChanged.bind(this), GObject.ConnectFlags.AFTER,
|
||||
- 'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER,
|
||||
- 'notify::layout-rows', this._updateThumbnailVisibility.bind(this),
|
||||
- this);
|
||||
-
|
||||
- this.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
- this._updateMenu();
|
||||
- this._updateThumbnails();
|
||||
- this._updateThumbnailVisibility();
|
||||
-
|
||||
- this._settings = new Gio.Settings({schema_id: 'org.gnome.desktop.wm.preferences'});
|
||||
- this._settings.connectObject('changed::workspace-names',
|
||||
- () => this._updateMenuLabels(), this);
|
||||
- }
|
||||
-
|
||||
- _updateThumbnailVisibility() {
|
||||
- const {workspaceManager} = global;
|
||||
- const vertical = workspaceManager.layout_rows === -1;
|
||||
- const useMenu =
|
||||
- vertical || workspaceManager.n_workspaces > MAX_THUMBNAILS;
|
||||
- this.reactive = useMenu;
|
||||
-
|
||||
- this._statusBin.visible = useMenu;
|
||||
- this._thumbnailsBox.visible = !useMenu;
|
||||
- }
|
||||
-
|
||||
- _onWorkspaceSwitched() {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
- this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
-
|
||||
- this._updateMenuOrnament();
|
||||
- this._updateActiveThumbnail();
|
||||
-
|
||||
- this._statusLabel.set_text(this._getStatusText());
|
||||
- }
|
||||
-
|
||||
- _nWorkspacesChanged() {
|
||||
- this._updateMenu();
|
||||
- this._updateThumbnails();
|
||||
- this._updateThumbnailVisibility();
|
||||
- }
|
||||
-
|
||||
- _updateMenuOrnament() {
|
||||
- for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
- this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
||||
- ? PopupMenu.Ornament.DOT
|
||||
- : PopupMenu.Ornament.NO_DOT);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _updateActiveThumbnail() {
|
||||
- let thumbs = this._thumbnailsBox.get_children();
|
||||
- for (let i = 0; i < thumbs.length; i++) {
|
||||
- if (i === this._currentWorkspace)
|
||||
- thumbs[i].add_style_class_name('active');
|
||||
- else
|
||||
- thumbs[i].remove_style_class_name('active');
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _getStatusText() {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
- let current = workspaceManager.get_active_workspace_index();
|
||||
- let total = workspaceManager.n_workspaces;
|
||||
-
|
||||
- return '%d / %d'.format(current + 1, total);
|
||||
- }
|
||||
-
|
||||
- _updateMenuLabels() {
|
||||
- for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
- let item = this._workspacesItems[i];
|
||||
- let name = Meta.prefs_get_workspace_name(i);
|
||||
- item.label.text = name;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _updateMenu() {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- this.menu.removeAll();
|
||||
- this._workspacesItems = [];
|
||||
- this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
-
|
||||
- for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
- let name = Meta.prefs_get_workspace_name(i);
|
||||
- let item = new PopupMenu.PopupMenuItem(name);
|
||||
- item.workspaceId = i;
|
||||
-
|
||||
- item.connect('activate', () => {
|
||||
- this._activate(item.workspaceId);
|
||||
- });
|
||||
-
|
||||
- item.setOrnament(i === this._currentWorkspace
|
||||
- ? PopupMenu.Ornament.DOT
|
||||
- : PopupMenu.Ornament.NO_DOT);
|
||||
-
|
||||
- this.menu.addMenuItem(item);
|
||||
- this._workspacesItems[i] = item;
|
||||
- }
|
||||
-
|
||||
- this._statusLabel.set_text(this._getStatusText());
|
||||
- }
|
||||
-
|
||||
- _updateThumbnails() {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- this._thumbnailsBox.destroy_all_children();
|
||||
-
|
||||
- for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
- let thumb = new WorkspaceThumbnail(i);
|
||||
- this._thumbnailsBox.add_child(thumb);
|
||||
- }
|
||||
- this._updateActiveThumbnail();
|
||||
- }
|
||||
-
|
||||
- _activate(index) {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- if (index >= 0 && index < workspaceManager.n_workspaces) {
|
||||
- let metaWorkspace = workspaceManager.get_workspace_by_index(index);
|
||||
- metaWorkspace.activate(global.get_current_time());
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _onScrollEvent(actor, event) {
|
||||
- let direction = event.get_scroll_direction();
|
||||
- let diff = 0;
|
||||
- if (direction === Clutter.ScrollDirection.DOWN)
|
||||
- diff = 1;
|
||||
- else if (direction === Clutter.ScrollDirection.UP)
|
||||
- diff = -1;
|
||||
- else
|
||||
- return;
|
||||
-
|
||||
- let newIndex = this._currentWorkspace + diff;
|
||||
- this._activate(newIndex);
|
||||
- }
|
||||
-}
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,53 @@
|
||||
From 3c1638195b33f9dfdd3df7847e88fab97188520a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 20 Feb 2024 17:39:49 +0100
|
||||
Subject: [PATCH 20/28] workspace-indicator: Simplify scroll handling
|
||||
|
||||
gnome-shell already includes a method for switching workspaces
|
||||
via scroll events. Use that instead of implementing our own.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
.../workspace-indicator/workspaceIndicator.js | 21 ++++---------------
|
||||
1 file changed, 4 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 594a9e51..14dd81d0 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -300,8 +300,10 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
'notify::layout-rows', this._updateThumbnailVisibility.bind(this),
|
||||
this);
|
||||
|
||||
- this.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
- this._thumbnailsBox.connect('scroll-event', this._onScrollEvent.bind(this));
|
||||
+ this.connect('scroll-event',
|
||||
+ (a, event) => Main.wm.handleWorkspaceScroll(event));
|
||||
+ this._thumbnailsBox.connect('scroll-event',
|
||||
+ (a, event) => Main.wm.handleWorkspaceScroll(event));
|
||||
|
||||
this._inTopBar = false;
|
||||
this.connect('notify::realized', () => {
|
||||
@@ -445,19 +447,4 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
metaWorkspace.activate(global.get_current_time());
|
||||
}
|
||||
}
|
||||
-
|
||||
- _onScrollEvent(actor, event) {
|
||||
- let direction = event.get_scroll_direction();
|
||||
- let diff = 0;
|
||||
- if (direction === Clutter.ScrollDirection.DOWN)
|
||||
- diff = 1;
|
||||
- else if (direction === Clutter.ScrollDirection.UP)
|
||||
- diff = -1;
|
||||
- else
|
||||
- return;
|
||||
-
|
||||
-
|
||||
- const newIndex = this._currentWorkspace + diff;
|
||||
- this._activate(newIndex);
|
||||
- }
|
||||
}
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,99 @@
|
||||
From 13dce7fcc1013a3cbb3a1e521e123a5d4ede75c5 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 27 Feb 2024 21:20:45 +0100
|
||||
Subject: [PATCH 21/28] workspace-indicator: Handle active indication in
|
||||
thumbnail
|
||||
|
||||
Meta.Workspace has had an `active` property for a while now, so
|
||||
we can use a property binding instead of tracking the active
|
||||
workspace ourselves.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
.../workspace-indicator/workspaceIndicator.js | 35 ++++++++++++-------
|
||||
1 file changed, 23 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 14dd81d0..bf6511a0 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -111,6 +111,13 @@ class WorkspaceLayout extends Clutter.LayoutManager {
|
||||
}
|
||||
|
||||
class WorkspaceThumbnail extends St.Button {
|
||||
+ static [GObject.properties] = {
|
||||
+ 'active': GObject.ParamSpec.boolean(
|
||||
+ 'active', '', '',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ false),
|
||||
+ };
|
||||
+
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
@@ -143,6 +150,10 @@ class WorkspaceThumbnail extends St.Button {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
this._workspace = workspaceManager.get_workspace_by_index(index);
|
||||
|
||||
+ this._workspace.bind_property('active',
|
||||
+ this, 'active',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
+
|
||||
this._workspace.connectObject(
|
||||
'window-added', (ws, window) => this._addWindow(window),
|
||||
'window-removed', (ws, window) => this._removeWindow(window),
|
||||
@@ -155,6 +166,18 @@ class WorkspaceThumbnail extends St.Button {
|
||||
this._onRestacked();
|
||||
}
|
||||
|
||||
+ get active() {
|
||||
+ return this.has_style_class_name('active');
|
||||
+ }
|
||||
+
|
||||
+ set active(active) {
|
||||
+ if (active)
|
||||
+ this.add_style_class_name('active');
|
||||
+ else
|
||||
+ this.remove_style_class_name('active');
|
||||
+ this.notify('active');
|
||||
+ }
|
||||
+
|
||||
acceptDrop(source) {
|
||||
if (!source.metaWindow)
|
||||
return false;
|
||||
@@ -360,7 +383,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._currentWorkspace = global.workspace_manager.get_active_workspace_index();
|
||||
|
||||
this._updateMenuOrnament();
|
||||
- this._updateActiveThumbnail();
|
||||
|
||||
this._statusLabel.set_text(this._getStatusText());
|
||||
}
|
||||
@@ -379,16 +401,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
}
|
||||
}
|
||||
|
||||
- _updateActiveThumbnail() {
|
||||
- let thumbs = this._thumbnailsBox.get_children();
|
||||
- for (let i = 0; i < thumbs.length; i++) {
|
||||
- if (i === this._currentWorkspace)
|
||||
- thumbs[i].add_style_class_name('active');
|
||||
- else
|
||||
- thumbs[i].remove_style_class_name('active');
|
||||
- }
|
||||
- }
|
||||
-
|
||||
_getStatusText() {
|
||||
const {nWorkspaces} = global.workspace_manager;
|
||||
const current = this._currentWorkspace + 1;
|
||||
@@ -436,7 +448,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
let thumb = new WorkspaceThumbnail(i);
|
||||
this._thumbnailsBox.add_child(thumb);
|
||||
}
|
||||
- this._updateActiveThumbnail();
|
||||
}
|
||||
|
||||
_activate(index) {
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,150 @@
|
||||
From 6b508c92c4996771cf79eb4d81e5d285b598fe96 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 20 Feb 2024 17:27:57 +0100
|
||||
Subject: [PATCH 22/28] workspace-indicator: Split out WorkspacePreviews
|
||||
|
||||
The previews will become a bit more complex soon, so spit them out
|
||||
into a dedicated class.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
.../workspace-indicator/workspaceIndicator.js | 72 ++++++++++++-------
|
||||
1 file changed, 47 insertions(+), 25 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index bf6511a0..73ebca6f 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -274,6 +274,49 @@ class WorkspaceThumbnail extends St.Button {
|
||||
}
|
||||
}
|
||||
|
||||
+class WorkspacePreviews extends Clutter.Actor {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ constructor(params) {
|
||||
+ super({
|
||||
+ ...params,
|
||||
+ layout_manager: new Clutter.BinLayout(),
|
||||
+ reactive: true,
|
||||
+ y_expand: true,
|
||||
+ });
|
||||
+
|
||||
+ this.connect('scroll-event',
|
||||
+ (a, event) => Main.wm.handleWorkspaceScroll(event));
|
||||
+
|
||||
+ const {workspaceManager} = global;
|
||||
+
|
||||
+ workspaceManager.connectObject(
|
||||
+ 'notify::n-workspaces', () => this._updateThumbnails(), GObject.ConnectFlags.AFTER,
|
||||
+ this);
|
||||
+
|
||||
+ this._thumbnailsBox = new St.BoxLayout({
|
||||
+ style_class: 'workspaces-box',
|
||||
+ y_expand: true,
|
||||
+ });
|
||||
+ this.add_child(this._thumbnailsBox);
|
||||
+
|
||||
+ this._updateThumbnails();
|
||||
+ }
|
||||
+
|
||||
+ _updateThumbnails() {
|
||||
+ const {nWorkspaces} = global.workspace_manager;
|
||||
+
|
||||
+ this._thumbnailsBox.destroy_all_children();
|
||||
+
|
||||
+ for (let i = 0; i < nWorkspaces; i++) {
|
||||
+ const thumb = new WorkspaceThumbnail(i);
|
||||
+ this._thumbnailsBox.add_child(thumb);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
@@ -304,16 +347,10 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
text: this._getStatusText(),
|
||||
});
|
||||
-
|
||||
container.add_child(this._statusLabel);
|
||||
|
||||
- this._thumbnailsBox = new St.BoxLayout({
|
||||
- style_class: 'workspaces-box',
|
||||
- y_expand: true,
|
||||
- reactive: true,
|
||||
- });
|
||||
-
|
||||
- container.add_child(this._thumbnailsBox);
|
||||
+ this._thumbnails = new WorkspacePreviews();
|
||||
+ container.add_child(this._thumbnails);
|
||||
|
||||
this._workspacesItems = [];
|
||||
|
||||
@@ -325,8 +362,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
this.connect('scroll-event',
|
||||
(a, event) => Main.wm.handleWorkspaceScroll(event));
|
||||
- this._thumbnailsBox.connect('scroll-event',
|
||||
- (a, event) => Main.wm.handleWorkspaceScroll(event));
|
||||
|
||||
this._inTopBar = false;
|
||||
this.connect('notify::realized', () => {
|
||||
@@ -338,7 +373,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
});
|
||||
|
||||
this._updateMenu();
|
||||
- this._updateThumbnails();
|
||||
this._updateThumbnailVisibility();
|
||||
|
||||
const desktopSettings =
|
||||
@@ -363,7 +397,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this.reactive = useMenu;
|
||||
|
||||
this._statusLabel.visible = useMenu;
|
||||
- this._thumbnailsBox.visible = !useMenu;
|
||||
+ this._thumbnails.visible = !useMenu;
|
||||
|
||||
this._updateTopBarRedirect();
|
||||
}
|
||||
@@ -374,7 +408,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
// Disable offscreen-redirect when showing the workspace switcher
|
||||
// so that clip-to-allocation works
|
||||
- Main.panel.set_offscreen_redirect(this._thumbnailsBox.visible
|
||||
+ Main.panel.set_offscreen_redirect(this._thumbnails.visible
|
||||
? Clutter.OffscreenRedirect.ALWAYS
|
||||
: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY);
|
||||
}
|
||||
@@ -389,7 +423,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
_nWorkspacesChanged() {
|
||||
this._updateMenu();
|
||||
- this._updateThumbnails();
|
||||
this._updateThumbnailVisibility();
|
||||
}
|
||||
|
||||
@@ -439,17 +472,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._statusLabel.set_text(this._getStatusText());
|
||||
}
|
||||
|
||||
- _updateThumbnails() {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- this._thumbnailsBox.destroy_all_children();
|
||||
-
|
||||
- for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
- let thumb = new WorkspaceThumbnail(i);
|
||||
- this._thumbnailsBox.add_child(thumb);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
_activate(index) {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,133 @@
|
||||
From 8d3d9ef8d8688999d959f1062a62e9f3b7f489fe Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Mon, 19 Feb 2024 14:42:04 +0100
|
||||
Subject: [PATCH 23/28] workspace-indicator: Handle preview overflow
|
||||
|
||||
We currently avoid previews from overflowing in most setups by
|
||||
artificially limiting them to a maximum of six workspaces.
|
||||
|
||||
Add some proper handling to also cover cases where space is more
|
||||
limited, and to allow removing the restriction in the future.
|
||||
|
||||
For that, wrap the previews in an auto-scrolling scroll view
|
||||
and add overflow indicators on each side.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
.../workspace-indicator/stylesheet-dark.css | 4 ++
|
||||
.../workspace-indicator/workspaceIndicator.js | 64 ++++++++++++++++++-
|
||||
2 files changed, 67 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css
|
||||
index 3e2ba67f..22d13370 100644
|
||||
--- a/extensions/workspace-indicator/stylesheet-dark.css
|
||||
+++ b/extensions/workspace-indicator/stylesheet-dark.css
|
||||
@@ -9,6 +9,10 @@
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
+.workspace-indicator .workspaces-view.hfade {
|
||||
+ -st-hfade-offset: 20px;
|
||||
+}
|
||||
+
|
||||
.workspace-indicator .workspaces-box {
|
||||
padding: 5px;
|
||||
spacing: 3px;
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 73ebca6f..314b9f45 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -20,6 +20,8 @@ import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
const TOOLTIP_OFFSET = 6;
|
||||
const TOOLTIP_ANIMATION_TIME = 150;
|
||||
|
||||
+const SCROLL_TIME = 100;
|
||||
+
|
||||
const MAX_THUMBNAILS = 6;
|
||||
|
||||
let baseStyleClassName = '';
|
||||
@@ -294,13 +296,29 @@ class WorkspacePreviews extends Clutter.Actor {
|
||||
|
||||
workspaceManager.connectObject(
|
||||
'notify::n-workspaces', () => this._updateThumbnails(), GObject.ConnectFlags.AFTER,
|
||||
+ 'workspace-switched', () => this._updateScrollPosition(),
|
||||
this);
|
||||
|
||||
+ this.connect('notify::mapped', () => {
|
||||
+ if (this.mapped)
|
||||
+ this._updateScrollPosition();
|
||||
+ });
|
||||
+
|
||||
this._thumbnailsBox = new St.BoxLayout({
|
||||
style_class: 'workspaces-box',
|
||||
y_expand: true,
|
||||
});
|
||||
- this.add_child(this._thumbnailsBox);
|
||||
+
|
||||
+ this._scrollView = new St.ScrollView({
|
||||
+ style_class: 'workspaces-view hfade',
|
||||
+ enable_mouse_scrolling: false,
|
||||
+ hscrollbar_policy: St.PolicyType.EXTERNAL,
|
||||
+ vscrollbar_policy: St.PolicyType.NEVER,
|
||||
+ y_expand: true,
|
||||
+ child: this._thumbnailsBox,
|
||||
+ });
|
||||
+
|
||||
+ this.add_child(this._scrollView);
|
||||
|
||||
this._updateThumbnails();
|
||||
}
|
||||
@@ -314,6 +332,50 @@ class WorkspacePreviews extends Clutter.Actor {
|
||||
const thumb = new WorkspaceThumbnail(i);
|
||||
this._thumbnailsBox.add_child(thumb);
|
||||
}
|
||||
+
|
||||
+ if (this.mapped)
|
||||
+ this._updateScrollPosition();
|
||||
+ }
|
||||
+
|
||||
+ _updateScrollPosition() {
|
||||
+ const adjustment = this._scrollView.hadjustment;
|
||||
+ const {upper, pageSize} = adjustment;
|
||||
+ let {value} = adjustment;
|
||||
+
|
||||
+ const activeWorkspace =
|
||||
+ [...this._thumbnailsBox].find(a => a.active);
|
||||
+
|
||||
+ if (!activeWorkspace)
|
||||
+ return;
|
||||
+
|
||||
+ let offset = 0;
|
||||
+ const hfade = this._scrollView.get_effect('fade');
|
||||
+ if (hfade)
|
||||
+ offset = hfade.fade_margins.left;
|
||||
+
|
||||
+ let {x1, x2} = activeWorkspace.get_allocation_box();
|
||||
+ let parent = activeWorkspace.get_parent();
|
||||
+ while (parent !== this._scrollView) {
|
||||
+ if (!parent)
|
||||
+ throw new Error('actor not in scroll view');
|
||||
+
|
||||
+ const box = parent.get_allocation_box();
|
||||
+ x1 += box.x1;
|
||||
+ x2 += box.x1;
|
||||
+ parent = parent.get_parent();
|
||||
+ }
|
||||
+
|
||||
+ if (x1 < value + offset)
|
||||
+ value = Math.max(0, x1 - offset);
|
||||
+ else if (x2 > value + pageSize - offset)
|
||||
+ value = Math.min(upper, x2 + offset - pageSize);
|
||||
+ else
|
||||
+ return;
|
||||
+
|
||||
+ adjustment.ease(value, {
|
||||
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
+ duration: SCROLL_TIME,
|
||||
+ });
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,157 @@
|
||||
From 3affa2e422de26862b4e473cfeeb89aea638df66 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Sun, 3 Mar 2024 15:05:23 +0100
|
||||
Subject: [PATCH 24/28] workspace-indicator: Support labels in previews
|
||||
|
||||
The space in the top bar is too limited to include the workspace
|
||||
names. However we'll soon replace the textual menu with a preview
|
||||
popover. We can use bigger previews there, so we can include the
|
||||
names to not lose functionality with regards to the current menu.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
.../workspace-indicator/workspaceIndicator.js | 56 +++++++++++++++++--
|
||||
1 file changed, 50 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 314b9f45..e6aa68bf 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -118,6 +118,10 @@ class WorkspaceThumbnail extends St.Button {
|
||||
'active', '', '',
|
||||
GObject.ParamFlags.READWRITE,
|
||||
false),
|
||||
+ 'show-label': GObject.ParamSpec.boolean(
|
||||
+ 'show-label', '', '',
|
||||
+ GObject.ParamFlags.READWRITE,
|
||||
+ false),
|
||||
};
|
||||
|
||||
static {
|
||||
@@ -125,7 +129,16 @@ class WorkspaceThumbnail extends St.Button {
|
||||
}
|
||||
|
||||
constructor(index) {
|
||||
- super({
|
||||
+ super();
|
||||
+
|
||||
+ const box = new St.BoxLayout({
|
||||
+ style_class: 'workspace-box',
|
||||
+ y_expand: true,
|
||||
+ vertical: true,
|
||||
+ });
|
||||
+ this.set_child(box);
|
||||
+
|
||||
+ this._preview = new St.Bin({
|
||||
style_class: 'workspace',
|
||||
child: new Clutter.Actor({
|
||||
layout_manager: new WorkspaceLayout(),
|
||||
@@ -133,7 +146,15 @@ class WorkspaceThumbnail extends St.Button {
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
}),
|
||||
+ y_expand: true,
|
||||
+ });
|
||||
+ box.add_child(this._preview);
|
||||
+
|
||||
+ this._label = new St.Label({
|
||||
+ x_align: Clutter.ActorAlign.CENTER,
|
||||
+ text: Meta.prefs_get_workspace_name(index),
|
||||
});
|
||||
+ box.add_child(this._label);
|
||||
|
||||
this._tooltip = new St.Label({
|
||||
style_class: 'dash-label',
|
||||
@@ -141,9 +162,19 @@ class WorkspaceThumbnail extends St.Button {
|
||||
});
|
||||
Main.uiGroup.add_child(this._tooltip);
|
||||
|
||||
+ this.bind_property('show-label',
|
||||
+ this._label, 'visible',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
+
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
this.connect('notify::hover', this._syncTooltip.bind(this));
|
||||
|
||||
+ const desktopSettings =
|
||||
+ new Gio.Settings({schema_id: 'org.gnome.desktop.wm.preferences'});
|
||||
+ desktopSettings.connectObject('changed::workspace-names', () => {
|
||||
+ this._label.text = Meta.prefs_get_workspace_name(index);
|
||||
+ }, this);
|
||||
+
|
||||
this._index = index;
|
||||
this._delegate = this; // needed for DND
|
||||
|
||||
@@ -169,14 +200,14 @@ class WorkspaceThumbnail extends St.Button {
|
||||
}
|
||||
|
||||
get active() {
|
||||
- return this.has_style_class_name('active');
|
||||
+ return this._preview.has_style_class_name('active');
|
||||
}
|
||||
|
||||
set active(active) {
|
||||
if (active)
|
||||
- this.add_style_class_name('active');
|
||||
+ this._preview.add_style_class_name('active');
|
||||
else
|
||||
- this.remove_style_class_name('active');
|
||||
+ this._preview.remove_style_class_name('active');
|
||||
this.notify('active');
|
||||
}
|
||||
|
||||
@@ -202,7 +233,7 @@ class WorkspaceThumbnail extends St.Button {
|
||||
let preview = new WindowPreview(window);
|
||||
preview.connect('clicked', (a, btn) => this.emit('clicked', btn));
|
||||
this._windowPreviews.set(window, preview);
|
||||
- this.child.add_child(preview);
|
||||
+ this._preview.child.add_child(preview);
|
||||
}
|
||||
|
||||
_removeWindow(window) {
|
||||
@@ -222,7 +253,7 @@ class WorkspaceThumbnail extends St.Button {
|
||||
if (!preview)
|
||||
continue;
|
||||
|
||||
- this.child.set_child_above_sibling(preview, lastPreview);
|
||||
+ this._preview.child.set_child_above_sibling(preview, lastPreview);
|
||||
lastPreview = preview;
|
||||
}
|
||||
}
|
||||
@@ -241,6 +272,9 @@ class WorkspaceThumbnail extends St.Button {
|
||||
}
|
||||
|
||||
_syncTooltip() {
|
||||
+ if (this.showLabel)
|
||||
+ return;
|
||||
+
|
||||
if (this.hover) {
|
||||
this._tooltip.set({
|
||||
text: Meta.prefs_get_workspace_name(this._index),
|
||||
@@ -277,6 +311,13 @@ class WorkspaceThumbnail extends St.Button {
|
||||
}
|
||||
|
||||
class WorkspacePreviews extends Clutter.Actor {
|
||||
+ static [GObject.properties] = {
|
||||
+ 'show-labels': GObject.ParamSpec.boolean(
|
||||
+ 'show-labels', '', '',
|
||||
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
|
||||
+ false),
|
||||
+ };
|
||||
+
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
@@ -330,6 +371,9 @@ class WorkspacePreviews extends Clutter.Actor {
|
||||
|
||||
for (let i = 0; i < nWorkspaces; i++) {
|
||||
const thumb = new WorkspaceThumbnail(i);
|
||||
+ this.bind_property('show-labels',
|
||||
+ thumb, 'show-label',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
this._thumbnailsBox.add_child(thumb);
|
||||
}
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,44 @@
|
||||
From 1805cceb598d1ed6fd2039453242b28e44e079e0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 20 Feb 2024 21:43:55 +0100
|
||||
Subject: [PATCH 25/28] workspace-indicator: Stop handling vertical layouts
|
||||
|
||||
Both the regular session and GNOME classic use a horizontal layout
|
||||
nowadays, so it doesn't seem worth to specifically handle vertical
|
||||
layouts anymore.
|
||||
|
||||
The extension will still work when the layout is changed (by some
|
||||
other extension), there will simply be a mismatch between horizontal
|
||||
previews and the actual layout.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
extensions/workspace-indicator/workspaceIndicator.js | 5 +----
|
||||
1 file changed, 1 insertion(+), 4 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index e6aa68bf..087d2d89 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -463,7 +463,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
workspaceManager.connectObject(
|
||||
'notify::n-workspaces', this._nWorkspacesChanged.bind(this), GObject.ConnectFlags.AFTER,
|
||||
'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER,
|
||||
- 'notify::layout-rows', this._updateThumbnailVisibility.bind(this),
|
||||
this);
|
||||
|
||||
this.connect('scroll-event',
|
||||
@@ -497,9 +496,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
_updateThumbnailVisibility() {
|
||||
const {workspaceManager} = global;
|
||||
- const vertical = workspaceManager.layout_rows === -1;
|
||||
- const useMenu =
|
||||
- vertical || workspaceManager.n_workspaces > MAX_THUMBNAILS;
|
||||
+ const useMenu = workspaceManager.n_workspaces > MAX_THUMBNAILS;
|
||||
this.reactive = useMenu;
|
||||
|
||||
this._statusLabel.visible = useMenu;
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,190 @@
|
||||
From f72c6ed223c3d348bdf32c25b54b6c44a826eb7d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Sun, 3 Mar 2024 15:05:23 +0100
|
||||
Subject: [PATCH 26/28] workspace-indicator: Also show previews in menu
|
||||
|
||||
Since the regular session also switched to horizontal workspaces,
|
||||
using a vertical menu has been a bit awkward.
|
||||
|
||||
Now that our previews have become more flexible, we can use them
|
||||
in the collapsed state as well as when embedded into the top bar.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
.../workspace-indicator/stylesheet-dark.css | 25 ++++++-
|
||||
.../workspace-indicator/workspaceIndicator.js | 74 +++----------------
|
||||
2 files changed, 36 insertions(+), 63 deletions(-)
|
||||
|
||||
diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css
|
||||
index 22d13370..b4a716b8 100644
|
||||
--- a/extensions/workspace-indicator/stylesheet-dark.css
|
||||
+++ b/extensions/workspace-indicator/stylesheet-dark.css
|
||||
@@ -13,18 +13,41 @@
|
||||
-st-hfade-offset: 20px;
|
||||
}
|
||||
|
||||
+.workspace-indicator-menu .workspaces-view {
|
||||
+ max-width: 480px;
|
||||
+}
|
||||
+
|
||||
.workspace-indicator .workspaces-box {
|
||||
padding: 5px;
|
||||
spacing: 3px;
|
||||
}
|
||||
|
||||
+.workspace-indicator-menu .workspaces-box {
|
||||
+ padding: 5px;
|
||||
+ spacing: 6px;
|
||||
+}
|
||||
+
|
||||
+.workspace-indicator-menu .workspace-box {
|
||||
+ spacing: 6px;
|
||||
+}
|
||||
+
|
||||
+.workspace-indicator-menu .workspace,
|
||||
.workspace-indicator .workspace {
|
||||
- width: 52px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 4px;
|
||||
background-color: #3f3f3f;
|
||||
}
|
||||
|
||||
+.workspace-indicator .workspace {
|
||||
+ width: 52px;
|
||||
+}
|
||||
+
|
||||
+.workspace-indicator-menu .workspace {
|
||||
+ height: 80px;
|
||||
+ width: 160px;
|
||||
+}
|
||||
+
|
||||
+.workspace-indicator-menu .workspace.active,
|
||||
.workspace-indicator .workspace.active {
|
||||
border-color: #9f9f9f;
|
||||
}
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index 087d2d89..a4d3bbee 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -429,7 +429,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
}
|
||||
|
||||
constructor(params = {}) {
|
||||
- super(0.5, _('Workspace Indicator'));
|
||||
+ super(0.5, _('Workspace Indicator'), true);
|
||||
|
||||
const {
|
||||
baseStyleClass = 'workspace-indicator',
|
||||
@@ -461,7 +461,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._workspacesItems = [];
|
||||
|
||||
workspaceManager.connectObject(
|
||||
- 'notify::n-workspaces', this._nWorkspacesChanged.bind(this), GObject.ConnectFlags.AFTER,
|
||||
+ 'notify::n-workspaces', this._updateThumbnailVisibility.bind(this), GObject.ConnectFlags.AFTER,
|
||||
'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER,
|
||||
this);
|
||||
|
||||
@@ -477,13 +477,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._updateTopBarRedirect();
|
||||
});
|
||||
|
||||
- this._updateMenu();
|
||||
this._updateThumbnailVisibility();
|
||||
-
|
||||
- const desktopSettings =
|
||||
- new Gio.Settings({schema_id: 'org.gnome.desktop.wm.preferences'});
|
||||
- desktopSettings.connectObject('changed::workspace-names',
|
||||
- () => this._updateMenuLabels(), this);
|
||||
}
|
||||
|
||||
_onDestroy() {
|
||||
@@ -502,6 +496,10 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._statusLabel.visible = useMenu;
|
||||
this._thumbnails.visible = !useMenu;
|
||||
|
||||
+ this.setMenu(useMenu
|
||||
+ ? this._createPreviewMenu()
|
||||
+ : null);
|
||||
+
|
||||
this._updateTopBarRedirect();
|
||||
}
|
||||
|
||||
@@ -518,69 +516,21 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
_onWorkspaceSwitched() {
|
||||
this._currentWorkspace = global.workspace_manager.get_active_workspace_index();
|
||||
-
|
||||
- this._updateMenuOrnament();
|
||||
-
|
||||
this._statusLabel.set_text(this._getStatusText());
|
||||
}
|
||||
|
||||
- _nWorkspacesChanged() {
|
||||
- this._updateMenu();
|
||||
- this._updateThumbnailVisibility();
|
||||
- }
|
||||
-
|
||||
- _updateMenuOrnament() {
|
||||
- for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
- this._workspacesItems[i].setOrnament(i === this._currentWorkspace
|
||||
- ? PopupMenu.Ornament.DOT
|
||||
- : PopupMenu.Ornament.NO_DOT);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
_getStatusText() {
|
||||
const {nWorkspaces} = global.workspace_manager;
|
||||
const current = this._currentWorkspace + 1;
|
||||
return `${current} / ${nWorkspaces}`;
|
||||
}
|
||||
|
||||
- _updateMenuLabels() {
|
||||
- for (let i = 0; i < this._workspacesItems.length; i++) {
|
||||
- const item = this._workspacesItems[i];
|
||||
- item.label.text = Meta.prefs_get_workspace_name(i);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- _updateMenu() {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- this.menu.removeAll();
|
||||
- this._workspacesItems = [];
|
||||
- this._currentWorkspace = workspaceManager.get_active_workspace_index();
|
||||
+ _createPreviewMenu() {
|
||||
+ const menu = new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP);
|
||||
|
||||
- for (let i = 0; i < workspaceManager.n_workspaces; i++) {
|
||||
- const name = Meta.prefs_get_workspace_name(i);
|
||||
- const item = new PopupMenu.PopupMenuItem(name);
|
||||
-
|
||||
- item.connect('activate',
|
||||
- () => this._activate(i));
|
||||
-
|
||||
- item.setOrnament(i === this._currentWorkspace
|
||||
- ? PopupMenu.Ornament.DOT
|
||||
- : PopupMenu.Ornament.NO_DOT);
|
||||
-
|
||||
- this.menu.addMenuItem(item);
|
||||
- this._workspacesItems[i] = item;
|
||||
- }
|
||||
-
|
||||
- this._statusLabel.set_text(this._getStatusText());
|
||||
- }
|
||||
-
|
||||
- _activate(index) {
|
||||
- let workspaceManager = global.workspace_manager;
|
||||
-
|
||||
- if (index >= 0 && index < workspaceManager.n_workspaces) {
|
||||
- let metaWorkspace = workspaceManager.get_workspace_by_index(index);
|
||||
- metaWorkspace.activate(global.get_current_time());
|
||||
- }
|
||||
+ const previews = new WorkspacePreviews({show_labels: true});
|
||||
+ menu.box.add_child(previews);
|
||||
+ menu.actor.add_style_class_name(`${baseStyleClassName}-menu`);
|
||||
+ return menu;
|
||||
}
|
||||
}
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,215 @@
|
||||
From 8d2b24290204be98423b3a952939895133bdc036 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 20 Feb 2024 22:00:57 +0100
|
||||
Subject: [PATCH 27/28] workspace-indicator: Make previews configurable
|
||||
|
||||
Now that previews scroll when there are too many workspaces,
|
||||
there is no longer a reason for the 6-workspace limit.
|
||||
|
||||
However some users do prefer the menu, so rather than drop it,
|
||||
turn it into a proper preference.
|
||||
|
||||
Closes
|
||||
https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/issues/336
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
extensions/window-list/extension.js | 1 +
|
||||
...e.shell.extensions.window-list.gschema.xml | 4 +++
|
||||
extensions/workspace-indicator/extension.js | 4 ++-
|
||||
extensions/workspace-indicator/meson.build | 1 +
|
||||
extensions/workspace-indicator/prefs.js | 26 +++++++++++++++++--
|
||||
...extensions.workspace-indicator.gschema.xml | 15 +++++++++++
|
||||
.../workspace-indicator/workspaceIndicator.js | 11 ++++----
|
||||
po/POTFILES.in | 1 +
|
||||
8 files changed, 55 insertions(+), 8 deletions(-)
|
||||
create mode 100644 extensions/workspace-indicator/schemas/org.gnome.shell.extensions.workspace-indicator.gschema.xml
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index 3950c535..227625e5 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -769,6 +769,7 @@ class WindowList extends St.Widget {
|
||||
|
||||
this._workspaceIndicator = new BottomWorkspaceIndicator({
|
||||
baseStyleClass: 'window-list-workspace-indicator',
|
||||
+ settings,
|
||||
});
|
||||
indicatorsBox.add_child(this._workspaceIndicator.container);
|
||||
|
||||
diff --git a/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml b/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml
|
||||
index 2ed680a5..46ff25cb 100644
|
||||
--- a/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml
|
||||
+++ b/extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml
|
||||
@@ -36,5 +36,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
only on the primary one.
|
||||
</description>
|
||||
</key>
|
||||
+ <key name="embed-previews" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show workspace previews in window list</summary>
|
||||
+ </key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
|
||||
index b383c919..ef24a750 100644
|
||||
--- a/extensions/workspace-indicator/extension.js
|
||||
+++ b/extensions/workspace-indicator/extension.js
|
||||
@@ -12,7 +12,9 @@ import {WorkspaceIndicator} from './workspaceIndicator.js';
|
||||
|
||||
export default class WorkspaceIndicatorExtension extends Extension {
|
||||
enable() {
|
||||
- this._indicator = new WorkspaceIndicator();
|
||||
+ this._indicator = new WorkspaceIndicator({
|
||||
+ settings: this.getSettings(),
|
||||
+ });
|
||||
Main.panel.addToStatusArea('workspace-indicator', this._indicator);
|
||||
}
|
||||
|
||||
diff --git a/extensions/workspace-indicator/meson.build b/extensions/workspace-indicator/meson.build
|
||||
index dada5408..9388085c 100644
|
||||
--- a/extensions/workspace-indicator/meson.build
|
||||
+++ b/extensions/workspace-indicator/meson.build
|
||||
@@ -11,5 +11,6 @@ extension_data += files(
|
||||
'stylesheet-dark.css',
|
||||
'stylesheet-light.css',
|
||||
)
|
||||
+extension_schemas += files('schemas/' + metadata_conf.get('gschemaname') + '.gschema.xml')
|
||||
|
||||
extension_sources += files('prefs.js', 'workspaceIndicator.js')
|
||||
diff --git a/extensions/workspace-indicator/prefs.js b/extensions/workspace-indicator/prefs.js
|
||||
index ea0546bf..b828ab8f 100644
|
||||
--- a/extensions/workspace-indicator/prefs.js
|
||||
+++ b/extensions/workspace-indicator/prefs.js
|
||||
@@ -18,6 +18,25 @@ const N_ = e => e;
|
||||
const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences';
|
||||
const WORKSPACE_KEY = 'workspace-names';
|
||||
|
||||
+class GeneralGroup extends Adw.PreferencesGroup {
|
||||
+ static {
|
||||
+ GObject.registerClass(this);
|
||||
+ }
|
||||
+
|
||||
+ constructor(settings) {
|
||||
+ super();
|
||||
+
|
||||
+ const row = new Adw.SwitchRow({
|
||||
+ title: _('Show Previews In Top Bar'),
|
||||
+ });
|
||||
+ this.add(row);
|
||||
+
|
||||
+ settings.bind('embed-previews',
|
||||
+ row, 'active',
|
||||
+ Gio.SettingsBindFlags.DEFAULT);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
class NewItem extends GObject.Object {}
|
||||
GObject.registerClass(NewItem);
|
||||
|
||||
@@ -119,7 +138,7 @@ class WorkspacesList extends GObject.Object {
|
||||
}
|
||||
}
|
||||
|
||||
-class WorkspaceSettingsWidget extends Adw.PreferencesGroup {
|
||||
+class WorkspacesGroup extends Adw.PreferencesGroup {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
|
||||
@@ -265,6 +284,9 @@ class NewWorkspaceRow extends Adw.PreferencesRow {
|
||||
|
||||
export default class WorkspaceIndicatorPrefs extends ExtensionPreferences {
|
||||
getPreferencesWidget() {
|
||||
- return new WorkspaceSettingsWidget();
|
||||
+ const page = new Adw.PreferencesPage();
|
||||
+ page.add(new GeneralGroup(this.getSettings()));
|
||||
+ page.add(new WorkspacesGroup());
|
||||
+ return page;
|
||||
}
|
||||
}
|
||||
diff --git a/extensions/workspace-indicator/schemas/org.gnome.shell.extensions.workspace-indicator.gschema.xml b/extensions/workspace-indicator/schemas/org.gnome.shell.extensions.workspace-indicator.gschema.xml
|
||||
new file mode 100644
|
||||
index 00000000..c7c634ca
|
||||
--- /dev/null
|
||||
+++ b/extensions/workspace-indicator/schemas/org.gnome.shell.extensions.workspace-indicator.gschema.xml
|
||||
@@ -0,0 +1,15 @@
|
||||
+<!--
|
||||
+SPDX-FileCopyrightText: 2024 Florian Müllner <fmuellner@gnome.org>
|
||||
+
|
||||
+SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+-->
|
||||
+
|
||||
+<schemalist gettext-domain="gnome-shell-extensions">
|
||||
+ <schema id="org.gnome.shell.extensions.workspace-indicator"
|
||||
+ path="/org/gnome/shell/extensions/workspace-indicator/">
|
||||
+ <key name="embed-previews" type="b">
|
||||
+ <default>true</default>
|
||||
+ <summary>Show workspace previews in top bar</summary>
|
||||
+ </key>
|
||||
+ </schema>
|
||||
+</schemalist>
|
||||
diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
index a4d3bbee..20d4caa2 100644
|
||||
--- a/extensions/workspace-indicator/workspaceIndicator.js
|
||||
+++ b/extensions/workspace-indicator/workspaceIndicator.js
|
||||
@@ -22,8 +22,6 @@ const TOOLTIP_ANIMATION_TIME = 150;
|
||||
|
||||
const SCROLL_TIME = 100;
|
||||
|
||||
-const MAX_THUMBNAILS = 6;
|
||||
-
|
||||
let baseStyleClassName = '';
|
||||
|
||||
class WindowPreview extends St.Button {
|
||||
@@ -433,8 +431,11 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
|
||||
const {
|
||||
baseStyleClass = 'workspace-indicator',
|
||||
+ settings,
|
||||
} = params;
|
||||
|
||||
+ this._settings = settings;
|
||||
+
|
||||
baseStyleClassName = baseStyleClass;
|
||||
this.add_style_class_name(baseStyleClassName);
|
||||
|
||||
@@ -461,7 +462,6 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._workspacesItems = [];
|
||||
|
||||
workspaceManager.connectObject(
|
||||
- 'notify::n-workspaces', this._updateThumbnailVisibility.bind(this), GObject.ConnectFlags.AFTER,
|
||||
'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER,
|
||||
this);
|
||||
|
||||
@@ -477,6 +477,8 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
this._updateTopBarRedirect();
|
||||
});
|
||||
|
||||
+ this._settings.connect('changed::embed-previews',
|
||||
+ () => this._updateThumbnailVisibility());
|
||||
this._updateThumbnailVisibility();
|
||||
}
|
||||
|
||||
@@ -489,8 +491,7 @@ export class WorkspaceIndicator extends PanelMenu.Button {
|
||||
}
|
||||
|
||||
_updateThumbnailVisibility() {
|
||||
- const {workspaceManager} = global;
|
||||
- const useMenu = workspaceManager.n_workspaces > MAX_THUMBNAILS;
|
||||
+ const useMenu = !this._settings.get_boolean('embed-previews');
|
||||
this.reactive = useMenu;
|
||||
|
||||
this._statusLabel.visible = useMenu;
|
||||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||||
index 182b2be0..e6e76039 100644
|
||||
--- a/po/POTFILES.in
|
||||
+++ b/po/POTFILES.in
|
||||
@@ -21,4 +21,5 @@ extensions/window-list/prefs.js
|
||||
extensions/window-list/workspaceIndicator.js
|
||||
extensions/windowsNavigator/extension.js
|
||||
extensions/workspace-indicator/prefs.js
|
||||
+extensions/workspace-indicator/schemas/org.gnome.shell.extensions.workspace-indicator.gschema.xml
|
||||
extensions/workspace-indicator/workspaceIndicator.js
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,40 @@
|
||||
From 2e7aa8ccd266b66c9641b7e7239e45e7317ff431 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Thu, 21 Mar 2024 17:27:09 +0100
|
||||
Subject: [PATCH 28/28] window-list: Expose workspace preview option
|
||||
|
||||
Now that we have the option, the window-list should expose it
|
||||
in its preference window like the workspace-indicator.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/316>
|
||||
---
|
||||
extensions/window-list/prefs.js | 13 +++++++++++++
|
||||
1 file changed, 13 insertions(+)
|
||||
|
||||
diff --git a/extensions/window-list/prefs.js b/extensions/window-list/prefs.js
|
||||
index 6b2d5958..194d1f9d 100644
|
||||
--- a/extensions/window-list/prefs.js
|
||||
+++ b/extensions/window-list/prefs.js
|
||||
@@ -81,6 +81,19 @@ class WindowListPrefsWidget extends Adw.PreferencesPage {
|
||||
});
|
||||
row.add_suffix(toggle);
|
||||
miscGroup.add(row);
|
||||
+
|
||||
+ toggle = new Gtk.Switch({
|
||||
+ action_name: 'window-list.embed-previews',
|
||||
+ valign: Gtk.Align.CENTER,
|
||||
+ });
|
||||
+ this._settings.bind('embed-previews',
|
||||
+ toggle, 'active', Gio.SettingsBindFlags.DEFAULT);
|
||||
+ row = new Adw.ActionRow({
|
||||
+ title: _('Show workspace previews'),
|
||||
+ activatable_widget: toggle,
|
||||
+ });
|
||||
+ row.add_suffix(toggle);
|
||||
+ miscGroup.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
72
prefer-window-icon.patch
Normal file
72
prefer-window-icon.patch
Normal file
@ -0,0 +1,72 @@
|
||||
From a344c1599edb64ffc4ad2b59de88616c6509bce8 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 19 Mar 2024 13:16:50 +0100
|
||||
Subject: [PATCH 1/2] window-list: Use more appropriate fallback icon
|
||||
|
||||
'icon-missing' is not an actual icon name. It somewhat works
|
||||
because an invalid icon name will fallback to the correct
|
||||
'image-missing', however for apps the generic app icon is
|
||||
a better fallback.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/315>
|
||||
---
|
||||
extensions/window-list/extension.js | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index 90bf34cd..c3ffe92f 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -165,7 +165,7 @@ class WindowTitle extends St.BoxLayout {
|
||||
this._icon.child = app.create_icon_texture(ICON_TEXTURE_SIZE);
|
||||
} else {
|
||||
this._icon.child = new St.Icon({
|
||||
- icon_name: 'icon-missing',
|
||||
+ icon_name: 'application-x-executable',
|
||||
icon_size: ICON_TEXTURE_SIZE,
|
||||
});
|
||||
}
|
||||
--
|
||||
2.44.0
|
||||
|
||||
|
||||
From c0cccebbdf543d25851872abdfdf119a5a9657aa Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Tue, 19 Mar 2024 14:07:12 +0100
|
||||
Subject: [PATCH 2/2] window-list: Override with window icon if available
|
||||
|
||||
---
|
||||
extensions/window-list/extension.js | 17 +++++++++++++++++
|
||||
1 file changed, 17 insertions(+)
|
||||
|
||||
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
|
||||
index c3ffe92f..034b72ba 100644
|
||||
--- a/extensions/window-list/extension.js
|
||||
+++ b/extensions/window-list/extension.js
|
||||
@@ -169,6 +169,23 @@ class WindowTitle extends St.BoxLayout {
|
||||
icon_size: ICON_TEXTURE_SIZE,
|
||||
});
|
||||
}
|
||||
+
|
||||
+ // Override with window icon if available
|
||||
+ if (this._hasWindowIcon()) {
|
||||
+ const textureCache = St.TextureCache.get_default();
|
||||
+ this._icon.child.gicon = textureCache.bind_cairo_surface_property(
|
||||
+ this._metaWindow, 'icon');
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _hasWindowIcon() {
|
||||
+ // HACK: GI cannot handle CairoSurface, so this
|
||||
+ // will throw if the icon property is null
|
||||
+ try {
|
||||
+ return this._metaWindow.icon !== null;
|
||||
+ } catch (e) {
|
||||
+ return true;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.44.0
|
||||
|
Loading…
Reference in New Issue
Block a user