From 7448ab74a8271feeaabb046abac16be0ef39b0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Tue, 17 Jun 2025 21:25:42 +0200 Subject: [PATCH] Make workspace names more prominent Resolves: https://issues.redhat.com/browse/RHEL-84268 --- gnome-shell-extensions.spec | 2 + improve-workspace-names.patch | 2296 +++++++++++++++++++++++++++++++++ 2 files changed, 2298 insertions(+) create mode 100644 improve-workspace-names.patch diff --git a/gnome-shell-extensions.spec b/gnome-shell-extensions.spec index 435fafd..df7cefa 100644 --- a/gnome-shell-extensions.spec +++ b/gnome-shell-extensions.spec @@ -40,6 +40,8 @@ Patch: window-list-reordering.patch Patch: heads-up-display-from-file.patch +Patch: improve-workspace-names.patch + %description GNOME Shell Extensions is a collection of extensions providing additional and optional functionality to GNOME Shell. diff --git a/improve-workspace-names.patch b/improve-workspace-names.patch new file mode 100644 index 0000000..26b154e --- /dev/null +++ b/improve-workspace-names.patch @@ -0,0 +1,2296 @@ +From 65b7117ba3c119f70f5d4d0ec2b8db8e58544fb4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 11 Oct 2024 12:10:36 +0200 +Subject: [PATCH 01/19] workspace-indicator: Split out workspaces prefs page + +The window-list extension already uses the extension code for +its embedded workspace indicator, this will allow it to do the +same for the preference page. + +Part-of: +--- + extensions/workspace-indicator/meson.build | 2 +- + extensions/workspace-indicator/prefs.js | 283 +---------------- + .../workspace-indicator/workspacePrefs.js | 295 ++++++++++++++++++ + po/POTFILES.in | 2 +- + 4 files changed, 300 insertions(+), 282 deletions(-) + create mode 100644 extensions/workspace-indicator/workspacePrefs.js + +diff --git a/extensions/workspace-indicator/meson.build b/extensions/workspace-indicator/meson.build +index 9388085c..c64e64a9 100644 +--- a/extensions/workspace-indicator/meson.build ++++ b/extensions/workspace-indicator/meson.build +@@ -13,4 +13,4 @@ extension_data += files( + ) + extension_schemas += files('schemas/' + metadata_conf.get('gschemaname') + '.gschema.xml') + +-extension_sources += files('prefs.js', 'workspaceIndicator.js') ++extension_sources += files('prefs.js', 'workspaceIndicator.js', 'workspacePrefs.js') +diff --git a/extensions/workspace-indicator/prefs.js b/extensions/workspace-indicator/prefs.js +index 122cf8dc..7ee988cb 100644 +--- a/extensions/workspace-indicator/prefs.js ++++ b/extensions/workspace-indicator/prefs.js +@@ -3,289 +3,12 @@ + // + // SPDX-License-Identifier: GPL-2.0-or-later + +-import Adw from 'gi://Adw'; +-import Gio from 'gi://Gio'; +-import GLib from 'gi://GLib'; +-import GObject from 'gi://GObject'; +-import Gtk from 'gi://Gtk'; +-import Pango from 'gi://Pango'; ++import {ExtensionPreferences} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +-import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; +- +-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); +- +-class NewItemModel extends GObject.Object { +- static [GObject.interfaces] = [Gio.ListModel]; +- static { +- GObject.registerClass(this); +- } +- +- #item = new NewItem(); +- +- vfunc_get_item_type() { +- return NewItem; +- } +- +- vfunc_get_n_items() { +- return 1; +- } +- +- vfunc_get_item(_pos) { +- return this.#item; +- } +-} +- +-class WorkspacesList extends GObject.Object { +- static [GObject.interfaces] = [Gio.ListModel]; +- static { +- GObject.registerClass(this); +- } +- +- #settings = new Gio.Settings({schema_id: WORKSPACE_SCHEMA}); +- #names = this.#settings.get_strv(WORKSPACE_KEY); +- #items = Gtk.StringList.new(this.#names); +- #changedId; +- +- constructor() { +- super(); +- +- this.#changedId = +- this.#settings.connect(`changed::${WORKSPACE_KEY}`, () => { +- const removed = this.#names.length; +- this.#names = this.#settings.get_strv(WORKSPACE_KEY); +- this.#items.splice(0, removed, this.#names); +- this.items_changed(0, removed, this.#names.length); +- }); +- } +- +- append() { +- const name = _('Workspace %d').format(this.#names.length + 1); +- +- this.#names.push(name); +- this.#settings.block_signal_handler(this.#changedId); +- this.#settings.set_strv(WORKSPACE_KEY, this.#names); +- this.#settings.unblock_signal_handler(this.#changedId); +- +- const pos = this.#items.get_n_items(); +- this.#items.append(name); +- this.items_changed(pos, 0, 1); +- } +- +- remove(name) { +- const pos = this.#names.indexOf(name); +- if (pos < 0) +- return; +- +- this.#names.splice(pos, 1); +- +- this.#settings.block_signal_handler(this.#changedId); +- this.#settings.set_strv(WORKSPACE_KEY, this.#names); +- this.#settings.unblock_signal_handler(this.#changedId); +- +- this.#items.remove(pos); +- this.items_changed(pos, 1, 0); +- } +- +- rename(oldName, newName) { +- const pos = this.#names.indexOf(oldName); +- if (pos < 0) +- return; +- +- this.#names.splice(pos, 1, newName); +- this.#items.splice(pos, 1, [newName]); +- +- this.#settings.block_signal_handler(this.#changedId); +- this.#settings.set_strv(WORKSPACE_KEY, this.#names); +- this.#settings.unblock_signal_handler(this.#changedId); +- } +- +- vfunc_get_item_type() { +- return Gtk.StringObject; +- } +- +- vfunc_get_n_items() { +- return this.#items.get_n_items(); +- } +- +- vfunc_get_item(pos) { +- return this.#items.get_item(pos); +- } +-} +- +-class WorkspacesGroup extends Adw.PreferencesGroup { +- static { +- GObject.registerClass(this); +- +- this.install_action('workspaces.add', null, +- self => self._workspaces.append()); +- this.install_action('workspaces.remove', 's', +- (self, name, param) => self._workspaces.remove(param.unpack())); +- this.install_action('workspaces.rename', '(ss)', +- (self, name, param) => self._workspaces.rename(...param.deepUnpack())); +- } +- +- constructor() { +- super({ +- title: _('Workspace Names'), +- }); +- +- this._workspaces = new WorkspacesList(); +- +- const store = new Gio.ListStore({item_type: Gio.ListModel}); +- const listModel = new Gtk.FlattenListModel({model: store}); +- store.append(this._workspaces); +- store.append(new NewItemModel()); +- +- this._list = new Gtk.ListBox({ +- selection_mode: Gtk.SelectionMode.NONE, +- css_classes: ['boxed-list'], +- }); +- this._list.connect('row-activated', (l, row) => row.edit()); +- this.add(this._list); +- +- this._list.bind_model(listModel, item => { +- return item instanceof NewItem +- ? new NewWorkspaceRow() +- : new WorkspaceRow(item.string); +- }); +- } +-} +- +-class WorkspaceRow extends Adw.PreferencesRow { +- static { +- GObject.registerClass(this); +- } +- +- constructor(name) { +- super({name}); +- +- const box = new Gtk.Box({ +- spacing: 12, +- margin_top: 6, +- margin_bottom: 6, +- margin_start: 6, +- margin_end: 6, +- }); +- +- const label = new Gtk.Label({ +- hexpand: true, +- xalign: 0, +- max_width_chars: 25, +- ellipsize: Pango.EllipsizeMode.END, +- }); +- this.bind_property('name', label, 'label', +- GObject.BindingFlags.SYNC_CREATE); +- box.append(label); +- +- const button = new Gtk.Button({ +- action_name: 'workspaces.remove', +- icon_name: 'edit-delete-symbolic', +- has_frame: false, +- }); +- box.append(button); +- +- this.bind_property_full('name', +- button, 'action-target', +- GObject.BindingFlags.SYNC_CREATE, +- (bind, target) => [true, new GLib.Variant('s', target)], +- null); +- +- this._entry = new Gtk.Entry({ +- max_width_chars: 25, +- }); +- +- const controller = new Gtk.ShortcutController(); +- controller.add_shortcut(new Gtk.Shortcut({ +- trigger: Gtk.ShortcutTrigger.parse_string('Escape'), +- action: Gtk.CallbackAction.new(() => { +- this._stopEdit(); +- return true; +- }), +- })); +- this._entry.add_controller(controller); +- +- this._stack = new Gtk.Stack(); +- this._stack.add_named(box, 'display'); +- this._stack.add_named(this._entry, 'edit'); +- this.child = this._stack; +- +- this._entry.connect('activate', () => { +- this.activate_action('workspaces.rename', +- new GLib.Variant('(ss)', [this.name, this._entry.text])); +- this.name = this._entry.text; +- this._stopEdit(); +- }); +- this._entry.connect('notify::has-focus', () => { +- if (this._entry.has_focus) +- return; +- this._stopEdit(); +- }); +- } +- +- edit() { +- this._entry.text = this.name; +- this._entry.grab_focus(); +- this._stack.visible_child_name = 'edit'; +- } +- +- _stopEdit() { +- this.grab_focus(); +- this._stack.visible_child_name = 'display'; +- } +-} +- +-class NewWorkspaceRow extends Adw.PreferencesRow { +- static { +- GObject.registerClass(this); +- } +- +- constructor() { +- super({ +- action_name: 'workspaces.add', +- child: new Gtk.Image({ +- icon_name: 'list-add-symbolic', +- pixel_size: 16, +- margin_top: 12, +- margin_bottom: 12, +- margin_start: 12, +- margin_end: 12, +- }), +- }); +- this.update_property( +- [Gtk.AccessibleProperty.LABEL], [_('Add Workspace')]); +- } +-} ++import {WorkspacesPage} from './workspacePrefs.js'; + + export default class WorkspaceIndicatorPrefs extends ExtensionPreferences { + getPreferencesWidget() { +- const page = new Adw.PreferencesPage(); +- page.add(new GeneralGroup(this.getSettings())); +- page.add(new WorkspacesGroup()); +- return page; ++ return new WorkspacesPage(this.getSettings()); + } + } +diff --git a/extensions/workspace-indicator/workspacePrefs.js b/extensions/workspace-indicator/workspacePrefs.js +new file mode 100644 +index 00000000..37386d27 +--- /dev/null ++++ b/extensions/workspace-indicator/workspacePrefs.js +@@ -0,0 +1,295 @@ ++// SPDX-FileCopyrightText: 2012 Giovanni Campagna ++// SPDX-FileCopyrightText: 2014 Florian Müllner ++// ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++import Adw from 'gi://Adw'; ++import Gio from 'gi://Gio'; ++import GLib from 'gi://GLib'; ++import GObject from 'gi://GObject'; ++import Gtk from 'gi://Gtk'; ++import Pango from 'gi://Pango'; ++ ++import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; ++ ++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); ++ ++class NewItemModel extends GObject.Object { ++ static [GObject.interfaces] = [Gio.ListModel]; ++ static { ++ GObject.registerClass(this); ++ } ++ ++ #item = new NewItem(); ++ ++ vfunc_get_item_type() { ++ return NewItem; ++ } ++ ++ vfunc_get_n_items() { ++ return 1; ++ } ++ ++ vfunc_get_item(_pos) { ++ return this.#item; ++ } ++} ++ ++class WorkspacesList extends GObject.Object { ++ static [GObject.interfaces] = [Gio.ListModel]; ++ static { ++ GObject.registerClass(this); ++ } ++ ++ #settings = new Gio.Settings({schema_id: WORKSPACE_SCHEMA}); ++ #names = this.#settings.get_strv(WORKSPACE_KEY); ++ #items = Gtk.StringList.new(this.#names); ++ #changedId; ++ ++ constructor() { ++ super(); ++ ++ this.#changedId = ++ this.#settings.connect(`changed::${WORKSPACE_KEY}`, () => { ++ const removed = this.#names.length; ++ this.#names = this.#settings.get_strv(WORKSPACE_KEY); ++ this.#items.splice(0, removed, this.#names); ++ this.items_changed(0, removed, this.#names.length); ++ }); ++ } ++ ++ append() { ++ const name = _('Workspace %d').format(this.#names.length + 1); ++ ++ this.#names.push(name); ++ this.#settings.block_signal_handler(this.#changedId); ++ this.#settings.set_strv(WORKSPACE_KEY, this.#names); ++ this.#settings.unblock_signal_handler(this.#changedId); ++ ++ const pos = this.#items.get_n_items(); ++ this.#items.append(name); ++ this.items_changed(pos, 0, 1); ++ } ++ ++ remove(name) { ++ const pos = this.#names.indexOf(name); ++ if (pos < 0) ++ return; ++ ++ this.#names.splice(pos, 1); ++ ++ this.#settings.block_signal_handler(this.#changedId); ++ this.#settings.set_strv(WORKSPACE_KEY, this.#names); ++ this.#settings.unblock_signal_handler(this.#changedId); ++ ++ this.#items.remove(pos); ++ this.items_changed(pos, 1, 0); ++ } ++ ++ rename(oldName, newName) { ++ const pos = this.#names.indexOf(oldName); ++ if (pos < 0) ++ return; ++ ++ this.#names.splice(pos, 1, newName); ++ this.#items.splice(pos, 1, [newName]); ++ ++ this.#settings.block_signal_handler(this.#changedId); ++ this.#settings.set_strv(WORKSPACE_KEY, this.#names); ++ this.#settings.unblock_signal_handler(this.#changedId); ++ } ++ ++ vfunc_get_item_type() { ++ return Gtk.StringObject; ++ } ++ ++ vfunc_get_n_items() { ++ return this.#items.get_n_items(); ++ } ++ ++ vfunc_get_item(pos) { ++ return this.#items.get_item(pos); ++ } ++} ++ ++class WorkspacesGroup extends Adw.PreferencesGroup { ++ static { ++ GObject.registerClass(this); ++ ++ this.install_action('workspaces.add', null, ++ self => self._workspaces.append()); ++ this.install_action('workspaces.remove', 's', ++ (self, name, param) => self._workspaces.remove(param.unpack())); ++ this.install_action('workspaces.rename', '(ss)', ++ (self, name, param) => self._workspaces.rename(...param.deepUnpack())); ++ } ++ ++ constructor() { ++ super({ ++ title: _('Workspace Names'), ++ }); ++ ++ this._workspaces = new WorkspacesList(); ++ ++ const store = new Gio.ListStore({item_type: Gio.ListModel}); ++ const listModel = new Gtk.FlattenListModel({model: store}); ++ store.append(this._workspaces); ++ store.append(new NewItemModel()); ++ ++ this._list = new Gtk.ListBox({ ++ selection_mode: Gtk.SelectionMode.NONE, ++ css_classes: ['boxed-list'], ++ }); ++ this._list.connect('row-activated', (l, row) => row.edit()); ++ this.add(this._list); ++ ++ this._list.bind_model(listModel, item => { ++ return item instanceof NewItem ++ ? new NewWorkspaceRow() ++ : new WorkspaceRow(item.string); ++ }); ++ } ++} ++ ++class WorkspaceRow extends Adw.PreferencesRow { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ constructor(name) { ++ super({name}); ++ ++ const box = new Gtk.Box({ ++ spacing: 12, ++ margin_top: 6, ++ margin_bottom: 6, ++ margin_start: 6, ++ margin_end: 6, ++ }); ++ ++ const label = new Gtk.Label({ ++ hexpand: true, ++ xalign: 0, ++ max_width_chars: 25, ++ ellipsize: Pango.EllipsizeMode.END, ++ }); ++ this.bind_property('name', label, 'label', ++ GObject.BindingFlags.SYNC_CREATE); ++ box.append(label); ++ ++ const button = new Gtk.Button({ ++ action_name: 'workspaces.remove', ++ icon_name: 'edit-delete-symbolic', ++ has_frame: false, ++ }); ++ box.append(button); ++ ++ this.bind_property_full('name', ++ button, 'action-target', ++ GObject.BindingFlags.SYNC_CREATE, ++ (bind, target) => [true, new GLib.Variant('s', target)], ++ null); ++ ++ this._entry = new Gtk.Entry({ ++ max_width_chars: 25, ++ }); ++ ++ const controller = new Gtk.ShortcutController(); ++ controller.add_shortcut(new Gtk.Shortcut({ ++ trigger: Gtk.ShortcutTrigger.parse_string('Escape'), ++ action: Gtk.CallbackAction.new(() => { ++ this._stopEdit(); ++ return true; ++ }), ++ })); ++ this._entry.add_controller(controller); ++ ++ this._stack = new Gtk.Stack(); ++ this._stack.add_named(box, 'display'); ++ this._stack.add_named(this._entry, 'edit'); ++ this.child = this._stack; ++ ++ this._entry.connect('activate', () => { ++ this.activate_action('workspaces.rename', ++ new GLib.Variant('(ss)', [this.name, this._entry.text])); ++ this.name = this._entry.text; ++ this._stopEdit(); ++ }); ++ this._entry.connect('notify::has-focus', () => { ++ if (this._entry.has_focus) ++ return; ++ this._stopEdit(); ++ }); ++ } ++ ++ edit() { ++ this._entry.text = this.name; ++ this._entry.grab_focus(); ++ this._stack.visible_child_name = 'edit'; ++ } ++ ++ _stopEdit() { ++ this.grab_focus(); ++ this._stack.visible_child_name = 'display'; ++ } ++} ++ ++class NewWorkspaceRow extends Adw.PreferencesRow { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ constructor() { ++ super({ ++ action_name: 'workspaces.add', ++ child: new Gtk.Image({ ++ icon_name: 'list-add-symbolic', ++ pixel_size: 16, ++ margin_top: 12, ++ margin_bottom: 12, ++ margin_start: 12, ++ margin_end: 12, ++ }), ++ }); ++ this.update_property( ++ [Gtk.AccessibleProperty.LABEL], [_('Add Workspace')]); ++ } ++} ++ ++export class WorkspacesPage extends Adw.PreferencesPage { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ constructor(settings) { ++ super(); ++ ++ this.add(new GeneralGroup(settings)); ++ this.add(new WorkspacesGroup()); ++ } ++} +diff --git a/po/POTFILES.in b/po/POTFILES.in +index b7cb8a7c..c0e18507 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -19,6 +19,6 @@ extensions/window-list/extension.js + extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml + extensions/window-list/prefs.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 ++extensions/workspace-indicator/workspacePrefs.js +-- +2.49.0 + + +From 90cf8a51ad277a7e44a6543bda8e5f2c892a759a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 11 Oct 2024 12:13:05 +0200 +Subject: [PATCH 02/19] workspace-indicator: Don't mention "top bar" in prefs + +The preferences will be shared with the window-list extension, +so avoid mentioning a specific placement. + +Part-of: +--- + extensions/workspace-indicator/workspacePrefs.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/extensions/workspace-indicator/workspacePrefs.js b/extensions/workspace-indicator/workspacePrefs.js +index 37386d27..12982471 100644 +--- a/extensions/workspace-indicator/workspacePrefs.js ++++ b/extensions/workspace-indicator/workspacePrefs.js +@@ -26,7 +26,7 @@ class GeneralGroup extends Adw.PreferencesGroup { + super(); + + const row = new Adw.SwitchRow({ +- title: _('Show Previews In Top Bar'), ++ title: _('Show Previews'), + }); + this.add(row); + +-- +2.49.0 + + +From 70998b97b53aa3ee1e6e7340e8c3a1ec47e3e6ec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 11 Oct 2024 12:31:36 +0200 +Subject: [PATCH 03/19] workspace-indicator: Set title and icon on prefs page + +The window-list extension will add the workspace prefs as +additional page, so it needs a title and icon for the +view switcher. + +Part-of: +--- + extensions/workspace-indicator/workspacePrefs.js | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/extensions/workspace-indicator/workspacePrefs.js b/extensions/workspace-indicator/workspacePrefs.js +index 12982471..0e48323f 100644 +--- a/extensions/workspace-indicator/workspacePrefs.js ++++ b/extensions/workspace-indicator/workspacePrefs.js +@@ -287,7 +287,10 @@ export class WorkspacesPage extends Adw.PreferencesPage { + } + + constructor(settings) { +- super(); ++ super({ ++ title: _('Workspaces'), ++ icon_name: 'view-grid-symbolic', ++ }); + + this.add(new GeneralGroup(settings)); + this.add(new WorkspacesGroup()); +-- +2.49.0 + + +From 036fd70f27246d71c22c89991aaa008bf75a24b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 11 Oct 2024 12:45:54 +0200 +Subject: [PATCH 04/19] window-list: Set title and icon on prefs page + +Like the workspace prefs page, the existing window list prefs +should set title and icon for the view switcher. + +Part-of: +--- + extensions/window-list/prefs.js | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/extensions/window-list/prefs.js b/extensions/window-list/prefs.js +index a77e7595..dbc9d87e 100644 +--- a/extensions/window-list/prefs.js ++++ b/extensions/window-list/prefs.js +@@ -17,7 +17,10 @@ class WindowListPrefsWidget extends Adw.PreferencesPage { + } + + constructor(settings) { +- super(); ++ super({ ++ title: _('Window List'), ++ icon_name: 'focus-windows-symbolic', ++ }); + + this._actionGroup = new Gio.SimpleActionGroup(); + this.insert_action_group('window-list', this._actionGroup); +-- +2.49.0 + + +From 8237851ad3c0b50276cea4027477ab191851db2c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 11 Oct 2024 12:45:54 +0200 +Subject: [PATCH 05/19] window-list: Remove workspace-previews setting from + prefs + +We are about to include the workspace prefs page from the +workspace-indicator extension, which already includes +the setting. + +Part-of: +--- + extensions/window-list/prefs.js | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/extensions/window-list/prefs.js b/extensions/window-list/prefs.js +index dbc9d87e..73bf8bef 100644 +--- a/extensions/window-list/prefs.js ++++ b/extensions/window-list/prefs.js +@@ -73,12 +73,6 @@ class WindowListPrefsWidget extends Adw.PreferencesPage { + action_name: 'window-list.display-all-workspaces', + }); + miscGroup.add(row); +- +- row = new Adw.SwitchRow({ +- title: _('Show workspace previews'), +- action_name: 'window-list.embed-previews', +- }); +- miscGroup.add(row); + } + } + +-- +2.49.0 + + +From 7de8baab6734df952be2dfe686531562d0719423 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 11 Oct 2024 12:45:54 +0200 +Subject: [PATCH 06/19] window-list: Add workspaces page to prefs + +This brings back the workspace-previews setting, and adds the +ability to change the workspace names. + +Given that those names are used as tooltips or preview titles, +it makes sense to allow editing them from the extension prefs +rather than relying on external tools (like dconf-editor). + +Part-of: +--- + extensions/window-list/meson.build | 1 + + extensions/window-list/prefs.js | 10 +++++++--- + 2 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/extensions/window-list/meson.build b/extensions/window-list/meson.build +index 6fd17007..e19c1d0f 100644 +--- a/extensions/window-list/meson.build ++++ b/extensions/window-list/meson.build +@@ -37,6 +37,7 @@ workspaceIndicatorSources = [ + command: transform_stylesheet, + capture: true, + ), ++ files('../workspace-indicator/workspacePrefs.js'), + ] + + extension_sources += files('prefs.js') + workspaceIndicatorSources +diff --git a/extensions/window-list/prefs.js b/extensions/window-list/prefs.js +index 73bf8bef..ca795067 100644 +--- a/extensions/window-list/prefs.js ++++ b/extensions/window-list/prefs.js +@@ -11,7 +11,9 @@ import Gtk from 'gi://Gtk'; + + import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +-class WindowListPrefsWidget extends Adw.PreferencesPage { ++import {WorkspacesPage} from './workspacePrefs.js'; ++ ++class WindowListPage extends Adw.PreferencesPage { + static { + GObject.registerClass(this); + } +@@ -77,7 +79,9 @@ class WindowListPrefsWidget extends Adw.PreferencesPage { + } + + export default class WindowListPrefs extends ExtensionPreferences { +- getPreferencesWidget() { +- return new WindowListPrefsWidget(this.getSettings()); ++ fillPreferencesWindow(window) { ++ const settings = this.getSettings(); ++ window.add(new WindowListPage(settings)); ++ window.add(new WorkspacesPage(settings)); + } + } +-- +2.49.0 + + +From 67096ef1e86089bad5efed2c23e9c2d4eeae5137 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 May 2025 16:31:57 +0200 +Subject: [PATCH 07/19] workspace-indicator: Remove left-over variable + +Part-of: +--- + extensions/workspace-indicator/workspaceIndicator.js | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index 18a8d4e9..aa9035e9 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -459,8 +459,6 @@ export class WorkspaceIndicator extends PanelMenu.Button { + this._thumbnails = new WorkspacePreviews(); + container.add_child(this._thumbnails); + +- this._workspacesItems = []; +- + workspaceManager.connectObject( + 'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER, + this); +-- +2.49.0 + + +From 44bfd77f5446a2fabec93fe16091d86d9ca7e8d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 May 2025 16:27:57 +0200 +Subject: [PATCH 08/19] workspace-indicator: Split out WorkspacesMenu + +The menu currently only contains the previews without any logic +on its own. This is about to change, so split the menu into a +separate class. + +Part-of: +--- + .../workspace-indicator/workspaceIndicator.js | 21 ++++++++++--------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index aa9035e9..6da21e8b 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -421,6 +421,16 @@ class WorkspacePreviews extends Clutter.Actor { + } + } + ++class WorkspacesMenu extends PopupMenu.PopupMenu { ++ constructor(sourceActor) { ++ super(sourceActor, 0.5, St.Side.TOP); ++ ++ const previews = new WorkspacePreviews({show_labels: true}); ++ this.box.add_child(previews); ++ this.actor.add_style_class_name(`${baseStyleClassName}-menu`); ++ } ++} ++ + export class WorkspaceIndicator extends PanelMenu.Button { + static { + GObject.registerClass(this); +@@ -496,7 +506,7 @@ export class WorkspaceIndicator extends PanelMenu.Button { + this._thumbnails.visible = !useMenu; + + this.setMenu(useMenu +- ? this._createPreviewMenu() ++ ? new WorkspacesMenu(this) + : null); + + this._updateTopBarRedirect(); +@@ -523,13 +533,4 @@ export class WorkspaceIndicator extends PanelMenu.Button { + const current = this._currentWorkspace + 1; + return `${current} / ${nWorkspaces}`; + } +- +- _createPreviewMenu() { +- const menu = new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP); +- +- const previews = new WorkspacePreviews({show_labels: true}); +- menu.box.add_child(previews); +- menu.actor.add_style_class_name(`${baseStyleClassName}-menu`); +- return menu; +- } + } +-- +2.49.0 + + +From c683c78e853a29bfb0c5a7524dd3d0c343e5ad46 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 May 2025 18:53:20 +0200 +Subject: [PATCH 09/19] workspace-indicator: Add back plain workspaces menu + +Unlike in the top bar, the previews in the menu were not too +successful. Change back to a regular menu with a list of workspace +names. + +Part-of: +--- + .../workspace-indicator/stylesheet-dark.css | 20 ------ + .../workspace-indicator/workspaceIndicator.js | 65 ++++++++++++++++++- + 2 files changed, 62 insertions(+), 23 deletions(-) + +diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css +index e70352f8..3440174e 100644 +--- a/extensions/workspace-indicator/stylesheet-dark.css ++++ b/extensions/workspace-indicator/stylesheet-dark.css +@@ -13,19 +13,10 @@ + -st-hfade-offset: 20px; + } + +-.workspace-indicator-menu .workspaces-view { +- max-width: 480px; +-} +- + .workspace-indicator .workspaces-box { + spacing: 3px; + } + +-.workspace-indicator-menu .workspaces-box { +- padding: 5px; +- spacing: 6px; +-} +- + .workspace-indicator .workspace-box { + padding-top: 5px; + padding-bottom: 5px; +@@ -40,11 +31,6 @@ + padding-right: 5px; + } + +-.workspace-indicator-menu .workspace-box { +- spacing: 6px; +-} +- +-.workspace-indicator-menu .workspace, + .workspace-indicator .workspace { + border: 1px solid transparent; + border-radius: 4px; +@@ -55,12 +41,6 @@ + width: 52px; + } + +-.workspace-indicator-menu .workspace { +- height: 80px; +- width: 160px; +-} +- +-.workspace-indicator-menu .workspace.active, + .workspace-indicator .workspace.active { + border-color: #fff; + } +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index 6da21e8b..9646126a 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -10,7 +10,7 @@ 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 {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'; +@@ -425,9 +425,68 @@ class WorkspacesMenu extends PopupMenu.PopupMenu { + constructor(sourceActor) { + super(sourceActor, 0.5, St.Side.TOP); + +- const previews = new WorkspacePreviews({show_labels: true}); +- this.box.add_child(previews); + this.actor.add_style_class_name(`${baseStyleClassName}-menu`); ++ ++ this._workspacesSection = new PopupMenu.PopupMenuSection(); ++ this.addMenuItem(this._workspacesSection); ++ ++ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); ++ ++ this.addAction(_('Settings'), () => { ++ const extension = Extension.lookupByURL(import.meta.url); ++ extension.openPreferences(); ++ }); ++ ++ this._desktopSettings = ++ new Gio.Settings({schema_id: 'org.gnome.desktop.wm.preferences'}); ++ this._desktopSettings.connectObject('changed::workspace-names', ++ () => this._updateWorkspaceLabels(), this); ++ ++ const {workspaceManager} = global; ++ workspaceManager.connectObject( ++ 'notify::n-workspaces', () => this._updateWorkspaceItems(), ++ 'workspace-switched', () => this._updateActiveIndicator(), ++ this.actor); ++ this._updateWorkspaceItems(); ++ } ++ ++ _updateWorkspaceItems() { ++ const {workspaceManager} = global; ++ const {nWorkspaces} = workspaceManager; ++ ++ const section = this._workspacesSection.actor; ++ while (section.get_n_children() < nWorkspaces) { ++ const item = new PopupMenu.PopupMenuItem(''); ++ item.connect('activate', (o, event) => { ++ const index = [...section].indexOf(item); ++ const workspace = workspaceManager.get_workspace_by_index(index); ++ workspace?.activate(event.get_time()); ++ }); ++ this._workspacesSection.addMenuItem(item); ++ } ++ ++ [...section].splice(nWorkspaces).forEach(item => item.destroy()); ++ ++ this._updateWorkspaceLabels(); ++ this._updateActiveIndicator(); ++ } ++ ++ _updateWorkspaceLabels() { ++ const items = [...this._workspacesSection.actor]; ++ items.forEach( ++ (item, i) => (item.label.text = Meta.prefs_get_workspace_name(i))); ++ } ++ ++ _updateActiveIndicator() { ++ const {workspaceManager} = global; ++ const active = workspaceManager.get_active_workspace_index(); ++ ++ const items = [...this._workspacesSection.actor]; ++ items.forEach((item, i) => { ++ item.setOrnament(i === active ++ ? PopupMenu.Ornament.CHECK ++ : PopupMenu.Ornament.NONE); ++ }); + } + } + +-- +2.49.0 + + +From dea3b220eccde0fc41ea79c761506e9a476c0098 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 May 2025 19:00:23 +0200 +Subject: [PATCH 10/19] workspace-indicator: Remove preview labels + +Previews are no longer used in the menu, so they are never +shown with labels. + +Part-of: +--- + .../workspace-indicator/workspaceIndicator.js | 39 +------------------ + 1 file changed, 2 insertions(+), 37 deletions(-) + +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index 9646126a..544680dd 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -116,10 +116,6 @@ class WorkspaceThumbnail extends St.Button { + 'active', null, null, + GObject.ParamFlags.READWRITE, + false), +- 'show-label': GObject.ParamSpec.boolean( +- 'show-label', null, null, +- GObject.ParamFlags.READWRITE, +- false), + }; + + static { +@@ -148,31 +144,15 @@ class WorkspaceThumbnail extends St.Button { + }); + 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', + visible: false, + }); + 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 + +@@ -270,9 +250,6 @@ class WorkspaceThumbnail extends St.Button { + } + + _syncTooltip() { +- if (this.showLabel) +- return; +- + if (this.hover) { + this._tooltip.set({ + text: Meta.prefs_get_workspace_name(this._index), +@@ -309,13 +286,6 @@ class WorkspaceThumbnail extends St.Button { + } + + class WorkspacePreviews extends Clutter.Actor { +- static [GObject.properties] = { +- 'show-labels': GObject.ParamSpec.boolean( +- 'show-labels', null, null, +- GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, +- false), +- }; +- + static { + GObject.registerClass(this); + } +@@ -367,13 +337,8 @@ class WorkspacePreviews extends Clutter.Actor { + + this._thumbnailsBox.destroy_all_children(); + +- 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); +- } ++ for (let i = 0; i < nWorkspaces; i++) ++ this._thumbnailsBox.add_child(new WorkspaceThumbnail(i)); + + if (this.mapped) + this._updateScrollPosition(); +-- +2.49.0 + + +From 85aac68515bd5a29359fd4cccfa3689511df63f1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 28 May 2025 02:16:33 +0200 +Subject: [PATCH 11/19] workspace-indicator: Include menu with previews + +The menu is currently only used when previews are disabled. But +as we are going to use the menu for changing workspace names, it +should be always available. So add it unconditionally, and show +it on right-click when using previews. + +Part-of: +--- + .../workspace-indicator/stylesheet-dark.css | 5 ++++ + .../workspace-indicator/workspaceIndicator.js | 25 +++++++++++++------ + 2 files changed, 23 insertions(+), 7 deletions(-) + +diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css +index 3440174e..19ef6ba1 100644 +--- a/extensions/workspace-indicator/stylesheet-dark.css ++++ b/extensions/workspace-indicator/stylesheet-dark.css +@@ -5,6 +5,11 @@ + * SPDX-License-Identifier: GPL-2.0-or-later + */ + ++.workspace-indicator.previews:active { ++ background-color: none !important; ++ box-shadow: none !important; ++} ++ + .workspace-indicator .status-label { + padding: 0 8px; + } +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index 544680dd..c2eda209 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -473,6 +473,8 @@ export class WorkspaceIndicator extends PanelMenu.Button { + baseStyleClassName = baseStyleClass; + this.add_style_class_name(baseStyleClassName); + ++ this.setMenu(new WorkspacesMenu(this)); ++ + let container = new St.Widget({ + layout_manager: new Clutter.BinLayout(), + x_expand: true, +@@ -493,6 +495,14 @@ export class WorkspaceIndicator extends PanelMenu.Button { + this._thumbnails = new WorkspacePreviews(); + container.add_child(this._thumbnails); + ++ this._thumbnails.connect('button-press-event', (a, event) => { ++ if (event.get_button() !== Clutter.BUTTON_SECONDARY) ++ return Clutter.EVENT_PROPAGATE; ++ ++ this.menu.toggle(); ++ return Clutter.EVENT_STOP; ++ }); ++ + workspaceManager.connectObject( + 'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER, + this); +@@ -523,15 +533,16 @@ export class WorkspaceIndicator extends PanelMenu.Button { + } + + _updateThumbnailVisibility() { +- const useMenu = !this._settings.get_boolean('embed-previews'); +- this.reactive = useMenu; ++ const usePreviews = this._settings.get_boolean('embed-previews'); ++ this.reactive = !usePreviews; + +- this._statusLabel.visible = useMenu; +- this._thumbnails.visible = !useMenu; ++ this._thumbnails.visible = usePreviews; ++ this._statusLabel.visible = !usePreviews; + +- this.setMenu(useMenu +- ? new WorkspacesMenu(this) +- : null); ++ if (usePreviews) ++ this.add_style_class_name('previews'); ++ else ++ this.remove_style_class_name('previews'); + + this._updateTopBarRedirect(); + } +-- +2.49.0 + + +From 1f7064c5bafe6ee8e45a45ba7175cfe710415f43 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 29 May 2025 14:53:37 +0200 +Subject: [PATCH 12/19] workspace-indicator: Expose active workspace name on + menu + +When not using previews, we currently use a numerical presentation +like "1 / 4" for the top bar button. We will change that to use +the active workspace name instead. + +As the menu already has to track workspace switches and name changes, +expose the active workspace name there, so that the button doesn't +have to duplicate the tracking. + +Part-of: +--- + .../workspace-indicator/workspaceIndicator.js | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index c2eda209..ec035031 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -404,8 +404,10 @@ class WorkspacesMenu extends PopupMenu.PopupMenu { + + this._desktopSettings = + new Gio.Settings({schema_id: 'org.gnome.desktop.wm.preferences'}); +- this._desktopSettings.connectObject('changed::workspace-names', +- () => this._updateWorkspaceLabels(), this); ++ this._desktopSettings.connectObject('changed::workspace-names', () => { ++ this._updateWorkspaceLabels(); ++ this.emit('active-name-changed'); ++ }, this); + + const {workspaceManager} = global; + workspaceManager.connectObject( +@@ -415,6 +417,12 @@ class WorkspacesMenu extends PopupMenu.PopupMenu { + this._updateWorkspaceItems(); + } + ++ get activeName() { ++ const {workspaceManager} = global; ++ const active = workspaceManager.get_active_workspace_index(); ++ return Meta.prefs_get_workspace_name(active); ++ } ++ + _updateWorkspaceItems() { + const {workspaceManager} = global; + const {nWorkspaces} = workspaceManager; +@@ -452,6 +460,7 @@ class WorkspacesMenu extends PopupMenu.PopupMenu { + ? PopupMenu.Ornament.CHECK + : PopupMenu.Ornament.NONE); + }); ++ this.emit('active-name-changed'); + } + } + +-- +2.49.0 + + +From d2efbd6354c6e0c8002bb290119278dd9818057e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 May 2025 20:59:58 +0200 +Subject: [PATCH 13/19] workspace-indicator: Show full name when using menu + +With workspace names becoming a more prominent feature, it makes +sense to expose them without opening the menu. + +Part-of: +--- + .../workspace-indicator/stylesheet-dark.css | 8 +++++ + .../workspace-indicator/workspaceIndicator.js | 33 ++++++++----------- + 2 files changed, 21 insertions(+), 20 deletions(-) + +diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css +index 19ef6ba1..5c502d93 100644 +--- a/extensions/workspace-indicator/stylesheet-dark.css ++++ b/extensions/workspace-indicator/stylesheet-dark.css +@@ -11,8 +11,16 @@ + } + + .workspace-indicator .status-label { ++ width: 8em; + padding: 0 8px; + } ++.workspace-indicator .status-label:ltr { padding-right: 4px; } ++.workspace-indicator .status-label:rtl { padding-left: 4px; } ++ ++.workspace-indicator .system-status-icon { ++ padding: 0 !important; ++ margin: 0 !important; ++} + + .workspace-indicator .workspaces-view.hfade { + -st-hfade-offset: 20px; +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index ec035031..b6ec924e 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -491,15 +491,23 @@ export class WorkspaceIndicator extends PanelMenu.Button { + }); + this.add_child(container); + +- let workspaceManager = global.workspace_manager; ++ this._statusBox = new St.BoxLayout(); ++ container.add_child(this._statusBox); + +- this._currentWorkspace = workspaceManager.get_active_workspace_index(); + this._statusLabel = new St.Label({ + style_class: 'status-label', ++ x_expand: true, + y_align: Clutter.ActorAlign.CENTER, +- text: this._getStatusText(), ++ text: this.menu.activeName, + }); +- container.add_child(this._statusLabel); ++ this._statusBox.add_child(this._statusLabel); ++ this._statusBox.add_child(new St.Icon({ ++ icon_name: 'pan-down-symbolic', ++ style_class: 'system-status-icon', ++ })); ++ ++ this.menu.connect('active-name-changed', ++ () => this._statusLabel.set_text(this.menu.activeName)); + + this._thumbnails = new WorkspacePreviews(); + container.add_child(this._thumbnails); +@@ -512,10 +520,6 @@ export class WorkspaceIndicator extends PanelMenu.Button { + return Clutter.EVENT_STOP; + }); + +- workspaceManager.connectObject( +- 'workspace-switched', this._onWorkspaceSwitched.bind(this), GObject.ConnectFlags.AFTER, +- this); +- + this.connect('scroll-event', + (a, event) => Main.wm.handleWorkspaceScroll(event)); + +@@ -546,7 +550,7 @@ export class WorkspaceIndicator extends PanelMenu.Button { + this.reactive = !usePreviews; + + this._thumbnails.visible = usePreviews; +- this._statusLabel.visible = !usePreviews; ++ this._statusBox.visible = !usePreviews; + + if (usePreviews) + this.add_style_class_name('previews'); +@@ -566,15 +570,4 @@ export class WorkspaceIndicator extends PanelMenu.Button { + ? Clutter.OffscreenRedirect.ALWAYS + : Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY); + } +- +- _onWorkspaceSwitched() { +- this._currentWorkspace = global.workspace_manager.get_active_workspace_index(); +- this._statusLabel.set_text(this._getStatusText()); +- } +- +- _getStatusText() { +- const {nWorkspaces} = global.workspace_manager; +- const current = this._currentWorkspace + 1; +- return `${current} / ${nWorkspaces}`; +- } + } +-- +2.49.0 + + +From 32d5de69d4d7625b11f6f1ae9065902441f3f3a2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 9 Jun 2025 18:10:14 +0200 +Subject: [PATCH 14/19] workspace-indicator: Add background when using name + label + +Panel buttons are flat, so the name+arrow are not immediately +recognizable as a single control. Address this by adding a +background to the button when using the name label. + +Part-of: +--- + extensions/workspace-indicator/stylesheet-dark.css | 14 ++++++++++++++ + .../workspace-indicator/stylesheet-light.css | 14 ++++++++++++++ + .../workspace-indicator/workspaceIndicator.js | 7 +++++-- + 3 files changed, 33 insertions(+), 2 deletions(-) + +diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css +index 5c502d93..95262671 100644 +--- a/extensions/workspace-indicator/stylesheet-dark.css ++++ b/extensions/workspace-indicator/stylesheet-dark.css +@@ -10,6 +10,20 @@ + box-shadow: none !important; + } + ++.workspace-indicator.name-label { ++ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.17) !important; ++} ++.workspace-indicator.name-label:hover, ++.workspace-indicator.name-label:focus { ++ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.28) !important; ++} ++.workspace-indicator.name-label:active { ++ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.32) !important; ++} ++.workspace-indicator.name-label:active:hover { ++ box-shadow: inset 0 0 0 100px rgba(255, 255, 255, 0.36) !important; ++} ++ + .workspace-indicator .status-label { + width: 8em; + padding: 0 8px; +diff --git a/extensions/workspace-indicator/stylesheet-light.css b/extensions/workspace-indicator/stylesheet-light.css +index 049b6a38..5191923c 100644 +--- a/extensions/workspace-indicator/stylesheet-light.css ++++ b/extensions/workspace-indicator/stylesheet-light.css +@@ -7,6 +7,20 @@ + + @import url("stylesheet-dark.css"); + ++.workspace-indicator.name-label { ++ box-shadow: inset 0 0 0 100px rgba(34, 34, 38, 0.17) !important; ++} ++.workspace-indicator.name-label:hover, ++.workspace-indicator.name-label:focus { ++ box-shadow: inset 0 0 0 100px rgba(34, 34, 38, 0.28) !important; ++} ++.workspace-indicator.name-label:active { ++ box-shadow: inset 0 0 0 100px rgba(34, 34, 38, 0.32) !important; ++} ++.workspace-indicator.name-label:active:hover { ++ box-shadow: inset 0 0 0 100px rgba(34, 34, 38, 0.36) !important; ++} ++ + .workspace-indicator .workspace { + background-color: #ccc; + } +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index b6ec924e..3def5a1d 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -552,10 +552,13 @@ export class WorkspaceIndicator extends PanelMenu.Button { + this._thumbnails.visible = usePreviews; + this._statusBox.visible = !usePreviews; + +- if (usePreviews) ++ if (usePreviews) { + this.add_style_class_name('previews'); +- else ++ this.remove_style_class_name('name-label'); ++ } else { + this.remove_style_class_name('previews'); ++ this.add_style_class_name('name-label'); ++ } + + this._updateTopBarRedirect(); + } +-- +2.49.0 + + +From c6f3afee3982285931b862b75a0dbedd626fe412 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 May 2025 21:07:08 +0200 +Subject: [PATCH 15/19] workspace-indicator: Refine preview settings + +Add a group title, and change the single switch row to radio rows +to explicitly choose between "Previews" and "Workspace Name". + +Part-of: +--- + .../workspace-indicator/workspacePrefs.js | 30 +++++++++++++++---- + 1 file changed, 25 insertions(+), 5 deletions(-) + +diff --git a/extensions/workspace-indicator/workspacePrefs.js b/extensions/workspace-indicator/workspacePrefs.js +index 0e48323f..84d7b8a6 100644 +--- a/extensions/workspace-indicator/workspacePrefs.js ++++ b/extensions/workspace-indicator/workspacePrefs.js +@@ -23,15 +23,35 @@ class GeneralGroup extends Adw.PreferencesGroup { + } + + constructor(settings) { +- super(); ++ super({ ++ title: _('Indicator'), ++ }); + +- const row = new Adw.SwitchRow({ +- title: _('Show Previews'), ++ const previewCheck = new Gtk.CheckButton(); ++ const previewRow = new Adw.ActionRow({ ++ title: _('Previews'), ++ activatable_widget: previewCheck, + }); +- this.add(row); ++ previewRow.add_prefix(previewCheck); ++ this.add(previewRow); ++ ++ const nameCheck = new Gtk.CheckButton({ ++ group: previewCheck, ++ }); ++ const nameRow = new Adw.ActionRow({ ++ title: _('Workspace Name'), ++ activatable_widget: nameCheck, ++ }); ++ nameRow.add_prefix(nameCheck); ++ this.add(nameRow); ++ ++ if (settings.get_boolean('embed-previews')) ++ previewCheck.active = true; ++ else ++ nameCheck.active = true; + + settings.bind('embed-previews', +- row, 'active', ++ previewCheck, 'active', + Gio.SettingsBindFlags.DEFAULT); + } + } +-- +2.49.0 + + +From a805b78fba735cdad778fefe873e05dcb0d3db85 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 May 2025 16:20:05 +0200 +Subject: [PATCH 16/19] workspace-indicator: Include workspace settings + +While the "Multitasking" panel in Settings already exposes workspace +settings, it makes sense to expose them in our prefs dialog as well +where they are more in context. + +Part-of: +--- + .../workspace-indicator/workspacePrefs.js | 70 +++++++++++++++++++ + 1 file changed, 70 insertions(+) + +diff --git a/extensions/workspace-indicator/workspacePrefs.js b/extensions/workspace-indicator/workspacePrefs.js +index 84d7b8a6..a6857cd7 100644 +--- a/extensions/workspace-indicator/workspacePrefs.js ++++ b/extensions/workspace-indicator/workspacePrefs.js +@@ -56,6 +56,75 @@ class GeneralGroup extends Adw.PreferencesGroup { + } + } + ++class BehaviorGroup extends Adw.PreferencesGroup { ++ static { ++ GObject.registerClass(this); ++ } ++ ++ constructor() { ++ super({ ++ title: _('Behavior'), ++ }); ++ ++ const dynamicCheck = new Gtk.CheckButton(); ++ const dynamicRow = new Adw.ActionRow({ ++ title: _('Dynamic'), ++ subtitle: _('Automatically removes empty workspaces.'), ++ activatable_widget: dynamicCheck, ++ }); ++ dynamicRow.add_prefix(dynamicCheck); ++ this.add(dynamicRow); ++ ++ const fixedCheck = new Gtk.CheckButton({ ++ group: dynamicCheck, ++ }); ++ const fixedRow = new Adw.ActionRow({ ++ title: _('Fixed Number'), ++ subtitle: _('Specify a number of permanent workspaces.'), ++ activatable_widget: fixedCheck, ++ }); ++ fixedRow.add_prefix(fixedCheck); ++ this.add(fixedRow); ++ ++ const adjustment = new Gtk.Adjustment({ ++ lower: 1, ++ step_increment: 1, ++ value: 4, ++ upper: 36, // hard limit in mutter ++ }); ++ const numRow = new Adw.SpinRow({ ++ title: _('Number of Workspaces'), ++ adjustment, ++ }); ++ this.add(numRow); ++ ++ const mutterSettings = new Gio.Settings({ ++ schema_id: 'org.gnome.mutter', ++ }); ++ ++ if (mutterSettings.get_boolean('dynamic-workspaces')) ++ dynamicCheck.active = true; ++ else ++ fixedCheck.active = true; ++ ++ mutterSettings.bind('dynamic-workspaces', ++ dynamicCheck, 'active', ++ Gio.SettingsBindFlags.DEFAULT); ++ ++ const desktopSettings = new Gio.Settings({ ++ schema_id: 'org.gnome.desktop.wm.preferences', ++ }); ++ ++ desktopSettings.bind('num-workspaces', ++ numRow, 'value', ++ Gio.SettingsBindFlags.DEFAULT); ++ ++ fixedCheck.bind_property('active', ++ numRow, 'sensitive', ++ GObject.BindingFlags.SYNC_CREATE); ++ } ++} ++ + class NewItem extends GObject.Object {} + GObject.registerClass(NewItem); + +@@ -313,6 +382,7 @@ export class WorkspacesPage extends Adw.PreferencesPage { + }); + + this.add(new GeneralGroup(settings)); ++ this.add(new BehaviorGroup()); + this.add(new WorkspacesGroup()); + } + } +-- +2.49.0 + + +From 1e6365a9b9006372849c0f50103aa9e326476739 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 28 May 2025 21:01:08 +0200 +Subject: [PATCH 17/19] workspace-indicator: Allow changing workspace names + from menu + +Instead of requiring the user to open the prefs dialog to change +workspace names, make the menu items themselves editable. + +Part-of: +--- + .../workspace-indicator/stylesheet-dark.css | 47 +++++++ + .../workspace-indicator/stylesheet-light.css | 16 +++ + .../workspace-indicator/workspaceIndicator.js | 121 +++++++++++++++++- + 3 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/extensions/workspace-indicator/stylesheet-dark.css b/extensions/workspace-indicator/stylesheet-dark.css +index 95262671..0453cb0f 100644 +--- a/extensions/workspace-indicator/stylesheet-dark.css ++++ b/extensions/workspace-indicator/stylesheet-dark.css +@@ -81,3 +81,50 @@ + .workspace-indicator-window-preview.active { + background-color: #d4d4d4; + } ++ ++.workspace-indicator-menu { ++ min-width: 17em; ++} ++ ++.workspace-indicator-menu .editable-menu-item.popup-menu-item { ++ padding: 3px 12px; ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button { ++ padding: 6px; ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat { ++ background-color: transparent; ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:hover { ++ background-color: st-transparentize(white, 90%); ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:active { ++ background-color: st-transparentize(white, 85%); ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:checked { ++ color: -st-accent-fg-color; ++ background-color: -st-accent-color; ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:checked:hover { ++ background-color: st-lighten(-st-accent-color, 10%); ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:checked:active { ++ background-color: st-lighten(-st-accent-color, 15%); ++} ++ ++.workspace-indicator-menu .editable-menu-item StLabel { ++ padding: 0 11px; ++ width: 6.5em; ++} ++ ++.workspace-indicator-menu .editable-menu-item StEntry { ++ padding: 9px 9px; ++ width: 6.5em; ++} +diff --git a/extensions/workspace-indicator/stylesheet-light.css b/extensions/workspace-indicator/stylesheet-light.css +index 5191923c..491add2a 100644 +--- a/extensions/workspace-indicator/stylesheet-light.css ++++ b/extensions/workspace-indicator/stylesheet-light.css +@@ -37,3 +37,19 @@ + .workspace-indicator-window-preview.active { + background-color: #f6f5f4; + } ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:hover { ++ background-color: st-transparentize(black, 90%); ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:active { ++ background-color: st-transparentize(black, 85%); ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:checked:hover { ++ background-color: st-darken(-st-accent-color, 10%); ++} ++ ++.workspace-indicator-menu .editable-menu-item .icon-button.flat:checked:active { ++ background-color: st-darken(-st-accent-color, 15%); ++} +diff --git a/extensions/workspace-indicator/workspaceIndicator.js b/extensions/workspace-indicator/workspaceIndicator.js +index 3def5a1d..d88f5ab2 100644 +--- a/extensions/workspace-indicator/workspaceIndicator.js ++++ b/extensions/workspace-indicator/workspaceIndicator.js +@@ -8,6 +8,7 @@ import Clutter from 'gi://Clutter'; + import Gio from 'gi://Gio'; + import GObject from 'gi://GObject'; + import Meta from 'gi://Meta'; ++import Shell from 'gi://Shell'; + import St from 'gi://St'; + + import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; +@@ -386,6 +387,117 @@ class WorkspacePreviews extends Clutter.Actor { + } + } + ++class EditableMenuItem extends PopupMenu.PopupBaseMenuItem { ++ static [GObject.signals] = { ++ 'edited': {}, ++ }; ++ ++ static { ++ GObject.registerClass(this); ++ } ++ ++ constructor() { ++ super({ ++ style_class: 'editable-menu-item', ++ }); ++ this.get_accessible()?.set_description( ++ _('Press %s to edit').format('e')); ++ ++ const stack = new Shell.Stack({ ++ x_expand: true, ++ x_align: Clutter.ActorAlign.START, ++ }); ++ this.add_child(stack); ++ ++ this.label = new St.Label({ ++ y_align: Clutter.ActorAlign.CENTER, ++ }); ++ stack.add_child(this.label); ++ this.label_actor = this.label; ++ ++ this._entry = new St.Entry({ ++ opacity: 0, ++ reactive: false, ++ }); ++ stack.add_child(this._entry); ++ ++ this.label.bind_property('text', ++ this._entry, 'text', ++ GObject.BindingFlags.DEFAULT); ++ ++ this._entry.clutter_text.connect('activate', ++ () => this._stopEditing()); ++ ++ this._editButton = new St.Button({ ++ style_class: 'icon-button flat', ++ icon_name: 'document-edit-symbolic', ++ button_mask: St.ButtonMask.ONE, ++ toggle_mode: true, ++ x_align: Clutter.ActorAlign.END, ++ y_align: Clutter.ActorAlign.CENTER, ++ }); ++ this.add_child(this._editButton); ++ ++ this._editButton.connect('notify::checked', () => { ++ if (this._editButton.checked) { ++ this._editButton.icon_name = 'ornament-check-symbolic'; ++ this._startEditing(); ++ } else { ++ this._editButton.icon_name = 'document-edit-symbolic'; ++ this._stopEditing(); ++ } ++ }); ++ this.connect('key-release-event', (o, event) => { ++ if (event.get_key_symbol() === Clutter.KEY_e) ++ this._editButton.checked = true; ++ }); ++ ++ global.stage.connectObject('notify::key-focus', () => { ++ const {keyFocus} = global.stage; ++ if (!keyFocus || !this.contains(keyFocus)) ++ this._stopEditing(); ++ }, this); ++ } ++ ++ _switchActor(from, to) { ++ to.reactive = true; ++ to.ease({ ++ opacity: 255, ++ duration: 300, ++ mode: Clutter.AnimationMode.EASE_OUT_QUAD, ++ }); ++ ++ from.ease({ ++ opacity: 0, ++ duration: 300, ++ mode: Clutter.AnimationMode.EASE_OUT_QUAD, ++ onComplete: () => { ++ from.reactive = false; ++ }, ++ }); ++ } ++ ++ _startEditing() { ++ this._switchActor(this.label, this._entry); ++ ++ this._entry.clutter_text.set_selection(0, -1); ++ this._entry.clutter_text.grab_key_focus(); ++ } ++ ++ _stopEditing() { ++ if (this.label.text !== this._entry.text) { ++ this.label.text = this._entry.text; ++ this.emit('edited'); ++ } ++ ++ if (this._editButton.checked) ++ this._editButton.checked = false; ++ ++ this._switchActor(this._entry, this.label); ++ this.navigate_focus(this, St.DirectionType.TAB_FORWARD, false); ++ } ++} ++ + class WorkspacesMenu extends PopupMenu.PopupMenu { + constructor(sourceActor) { + super(sourceActor, 0.5, St.Side.TOP); +@@ -429,12 +541,19 @@ class WorkspacesMenu extends PopupMenu.PopupMenu { + + const section = this._workspacesSection.actor; + while (section.get_n_children() < nWorkspaces) { +- const item = new PopupMenu.PopupMenuItem(''); ++ const item = new EditableMenuItem(); + item.connect('activate', (o, event) => { + const index = [...section].indexOf(item); + const workspace = workspaceManager.get_workspace_by_index(index); + workspace?.activate(event.get_time()); + }); ++ item.connect('edited', () => { ++ const nLabels = section.get_n_children(); ++ const oldNames = this._desktopSettings.get_strv('workspace-names'); ++ const newNames = [...section].map(c => c.label.text); ++ this._desktopSettings.set_strv('workspace-names', ++ [...newNames, ...oldNames.slice(nLabels)]); ++ }); + this._workspacesSection.addMenuItem(item); + } + +-- +2.49.0 + + +From 4b458dd19ed373cf76211f1397f4fde2d36fbcee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 28 May 2025 21:04:36 +0200 +Subject: [PATCH 18/19] workspace-indicator: Remove workspace names from prefs + +Now that names can be changed from the extension itself, we no +longer need to expose them in the prefs dialog. + +Part-of: +--- + .../workspace-indicator/workspacePrefs.js | 252 ------------------ + 1 file changed, 252 deletions(-) + +diff --git a/extensions/workspace-indicator/workspacePrefs.js b/extensions/workspace-indicator/workspacePrefs.js +index a6857cd7..68ddeb3f 100644 +--- a/extensions/workspace-indicator/workspacePrefs.js ++++ b/extensions/workspace-indicator/workspacePrefs.js +@@ -5,18 +5,12 @@ + + import Adw from 'gi://Adw'; + import Gio from 'gi://Gio'; +-import GLib from 'gi://GLib'; + import GObject from 'gi://GObject'; + import Gtk from 'gi://Gtk'; + import Pango from 'gi://Pango'; + + import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +-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); +@@ -125,251 +119,6 @@ class BehaviorGroup extends Adw.PreferencesGroup { + } + } + +-class NewItem extends GObject.Object {} +-GObject.registerClass(NewItem); +- +-class NewItemModel extends GObject.Object { +- static [GObject.interfaces] = [Gio.ListModel]; +- static { +- GObject.registerClass(this); +- } +- +- #item = new NewItem(); +- +- vfunc_get_item_type() { +- return NewItem; +- } +- +- vfunc_get_n_items() { +- return 1; +- } +- +- vfunc_get_item(_pos) { +- return this.#item; +- } +-} +- +-class WorkspacesList extends GObject.Object { +- static [GObject.interfaces] = [Gio.ListModel]; +- static { +- GObject.registerClass(this); +- } +- +- #settings = new Gio.Settings({schema_id: WORKSPACE_SCHEMA}); +- #names = this.#settings.get_strv(WORKSPACE_KEY); +- #items = Gtk.StringList.new(this.#names); +- #changedId; +- +- constructor() { +- super(); +- +- this.#changedId = +- this.#settings.connect(`changed::${WORKSPACE_KEY}`, () => { +- const removed = this.#names.length; +- this.#names = this.#settings.get_strv(WORKSPACE_KEY); +- this.#items.splice(0, removed, this.#names); +- this.items_changed(0, removed, this.#names.length); +- }); +- } +- +- append() { +- const name = _('Workspace %d').format(this.#names.length + 1); +- +- this.#names.push(name); +- this.#settings.block_signal_handler(this.#changedId); +- this.#settings.set_strv(WORKSPACE_KEY, this.#names); +- this.#settings.unblock_signal_handler(this.#changedId); +- +- const pos = this.#items.get_n_items(); +- this.#items.append(name); +- this.items_changed(pos, 0, 1); +- } +- +- remove(name) { +- const pos = this.#names.indexOf(name); +- if (pos < 0) +- return; +- +- this.#names.splice(pos, 1); +- +- this.#settings.block_signal_handler(this.#changedId); +- this.#settings.set_strv(WORKSPACE_KEY, this.#names); +- this.#settings.unblock_signal_handler(this.#changedId); +- +- this.#items.remove(pos); +- this.items_changed(pos, 1, 0); +- } +- +- rename(oldName, newName) { +- const pos = this.#names.indexOf(oldName); +- if (pos < 0) +- return; +- +- this.#names.splice(pos, 1, newName); +- this.#items.splice(pos, 1, [newName]); +- +- this.#settings.block_signal_handler(this.#changedId); +- this.#settings.set_strv(WORKSPACE_KEY, this.#names); +- this.#settings.unblock_signal_handler(this.#changedId); +- } +- +- vfunc_get_item_type() { +- return Gtk.StringObject; +- } +- +- vfunc_get_n_items() { +- return this.#items.get_n_items(); +- } +- +- vfunc_get_item(pos) { +- return this.#items.get_item(pos); +- } +-} +- +-class WorkspacesGroup extends Adw.PreferencesGroup { +- static { +- GObject.registerClass(this); +- +- this.install_action('workspaces.add', null, +- self => self._workspaces.append()); +- this.install_action('workspaces.remove', 's', +- (self, name, param) => self._workspaces.remove(param.unpack())); +- this.install_action('workspaces.rename', '(ss)', +- (self, name, param) => self._workspaces.rename(...param.deepUnpack())); +- } +- +- constructor() { +- super({ +- title: _('Workspace Names'), +- }); +- +- this._workspaces = new WorkspacesList(); +- +- const store = new Gio.ListStore({item_type: Gio.ListModel}); +- const listModel = new Gtk.FlattenListModel({model: store}); +- store.append(this._workspaces); +- store.append(new NewItemModel()); +- +- this._list = new Gtk.ListBox({ +- selection_mode: Gtk.SelectionMode.NONE, +- css_classes: ['boxed-list'], +- }); +- this._list.connect('row-activated', (l, row) => row.edit()); +- this.add(this._list); +- +- this._list.bind_model(listModel, item => { +- return item instanceof NewItem +- ? new NewWorkspaceRow() +- : new WorkspaceRow(item.string); +- }); +- } +-} +- +-class WorkspaceRow extends Adw.PreferencesRow { +- static { +- GObject.registerClass(this); +- } +- +- constructor(name) { +- super({name}); +- +- const box = new Gtk.Box({ +- spacing: 12, +- margin_top: 6, +- margin_bottom: 6, +- margin_start: 6, +- margin_end: 6, +- }); +- +- const label = new Gtk.Label({ +- hexpand: true, +- xalign: 0, +- max_width_chars: 25, +- ellipsize: Pango.EllipsizeMode.END, +- }); +- this.bind_property('name', label, 'label', +- GObject.BindingFlags.SYNC_CREATE); +- box.append(label); +- +- const button = new Gtk.Button({ +- action_name: 'workspaces.remove', +- icon_name: 'edit-delete-symbolic', +- has_frame: false, +- }); +- box.append(button); +- +- this.bind_property_full('name', +- button, 'action-target', +- GObject.BindingFlags.SYNC_CREATE, +- (bind, target) => [true, new GLib.Variant('s', target)], +- null); +- +- this._entry = new Gtk.Entry({ +- max_width_chars: 25, +- }); +- +- const controller = new Gtk.ShortcutController(); +- controller.add_shortcut(new Gtk.Shortcut({ +- trigger: Gtk.ShortcutTrigger.parse_string('Escape'), +- action: Gtk.CallbackAction.new(() => { +- this._stopEdit(); +- return true; +- }), +- })); +- this._entry.add_controller(controller); +- +- this._stack = new Gtk.Stack(); +- this._stack.add_named(box, 'display'); +- this._stack.add_named(this._entry, 'edit'); +- this.child = this._stack; +- +- this._entry.connect('activate', () => { +- this.activate_action('workspaces.rename', +- new GLib.Variant('(ss)', [this.name, this._entry.text])); +- this.name = this._entry.text; +- this._stopEdit(); +- }); +- this._entry.connect('notify::has-focus', () => { +- if (this._entry.has_focus) +- return; +- this._stopEdit(); +- }); +- } +- +- edit() { +- this._entry.text = this.name; +- this._entry.grab_focus(); +- this._stack.visible_child_name = 'edit'; +- } +- +- _stopEdit() { +- this.grab_focus(); +- this._stack.visible_child_name = 'display'; +- } +-} +- +-class NewWorkspaceRow extends Adw.PreferencesRow { +- static { +- GObject.registerClass(this); +- } +- +- constructor() { +- super({ +- action_name: 'workspaces.add', +- child: new Gtk.Image({ +- icon_name: 'list-add-symbolic', +- pixel_size: 16, +- margin_top: 12, +- margin_bottom: 12, +- margin_start: 12, +- margin_end: 12, +- }), +- }); +- this.update_property( +- [Gtk.AccessibleProperty.LABEL], [_('Add Workspace')]); +- } +-} +- + export class WorkspacesPage extends Adw.PreferencesPage { + static { + GObject.registerClass(this); +@@ -383,6 +132,5 @@ export class WorkspacesPage extends Adw.PreferencesPage { + + this.add(new GeneralGroup(settings)); + this.add(new BehaviorGroup()); +- this.add(new WorkspacesGroup()); + } + } +-- +2.49.0 + + +From 034aab08d5b090acc0f84c4f78b7d97779782aa6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 30 May 2025 16:39:22 +0200 +Subject: [PATCH 19/19] window-list: Adjust to workspace-indicator changes + +Keep the `.panel-button` class to get the expected hover/focus/active +styling when using a regular menu button, but remove the horizontal +padding when using previews for fittsability. + +Part-of: +--- + extensions/window-list/extension.js | 6 ------ + extensions/window-list/stylesheet-dark.css | 5 +++++ + 2 files changed, 5 insertions(+), 6 deletions(-) + +diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js +index a4268589..2d485fe3 100644 +--- a/extensions/window-list/extension.js ++++ b/extensions/window-list/extension.js +@@ -1380,12 +1380,6 @@ class BottomWorkspaceIndicator extends WorkspaceIndicator { + GObject.registerClass(this); + } + +- constructor(params) { +- super(params); +- +- this.remove_style_class_name('panel-button'); +- } +- + setMenu(menu) { + super.setMenu(menu); + +diff --git a/extensions/window-list/stylesheet-dark.css b/extensions/window-list/stylesheet-dark.css +index 4c06ebc0..3c5bf62a 100644 +--- a/extensions/window-list/stylesheet-dark.css ++++ b/extensions/window-list/stylesheet-dark.css +@@ -6,6 +6,11 @@ + */ + @import url("stylesheet-workspace-switcher-dark.css"); + ++.window-list-workspace-indicator.previews { ++ -natural-hpadding: 0 !important; ++ -minimum-hpadding: 0 !important; ++} ++ + .window-list { + spacing: 2px; + font-size: 10pt; +-- +2.49.0 +