Blob Blame History Raw
From a19128ef885d8a933af364651b21e519f583f82a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 29 May 2019 10:17:20 +0000
Subject: [PATCH 01/22] places-menu: Don't hardcode position

The extension currently assumes that we have the "Activities" button
at the left of the top bar. This is currently true, not only in the
regular session, but also in GNOME classic where the button is hidden
(but still present).

However this is about to change: We will stop taking over the button
from the apps-menu extension, and instead disable "Activities" from
the session mode definition.

Prepare for this by adding the places menu before the application menu
instead of assuming a hardcoded position.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/69
---
 extensions/places-menu/extension.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/extensions/places-menu/extension.js b/extensions/places-menu/extension.js
index c477a4a..5c038ae 100644
--- a/extensions/places-menu/extension.js
+++ b/extensions/places-menu/extension.js
@@ -135,9 +135,9 @@ let _indicator;
 function enable() {
     _indicator = new PlacesMenu;
 
-    let pos = 1;
+    let pos = Main.sessionMode.panel.left.indexOf('appMenu');
     if ('apps-menu' in Main.panel.statusArea)
-        pos = 2;
+        pos++;
     Main.panel.addToStatusArea('places-menu', _indicator, pos, 'left');
 }
 
-- 
2.21.0


From 075f6d45b242061be774ba5d3d8c2236cacd64c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 29 May 2019 08:32:03 +0000
Subject: [PATCH 02/22] apps-menu: Stop taking over Activities button

We don't want the "Activities" button in GNOME Classic, but the current
way of handling it is confusing:

 - the button is hidden, but the corresponding hot corner
   sometimes works (when the application menu isn't open)

 - the button is effectively moved inside the menu, although
   it's clearly not an app or category

 - the apps-menu can be used independent from classic mode, in
   which case removing the "Activities" button may not be wanted

Address those points by removing any handling of the activities button
from the apps-menu extension. We will remove it again from the classic
session via a session mode tweak.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/69
---
 extensions/apps-menu/extension.js | 67 +++----------------------------
 1 file changed, 6 insertions(+), 61 deletions(-)

diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js
index cc399c6..d6c9c8b 100644
--- a/extensions/apps-menu/extension.js
+++ b/extensions/apps-menu/extension.js
@@ -25,22 +25,6 @@ const NAVIGATION_REGION_OVERSHOOT = 50;
 Gio._promisify(Gio._LocalFilePrototype, 'query_info_async', 'query_info_finish');
 Gio._promisify(Gio._LocalFilePrototype, 'set_attributes_async', 'set_attributes_finish');
 
-class ActivitiesMenuItem extends PopupMenu.PopupBaseMenuItem {
-    constructor(button) {
-        super();
-        this._button = button;
-        let label = new St.Label({ text: _('Activities Overview') });
-        this.actor.add_child(label);
-        this.actor.label_actor = label;
-    }
-
-    activate(event) {
-        this._button.menu.toggle();
-        Main.overview.toggle();
-        super.activate(event);
-    }
-}
-
 class ApplicationMenuItem extends PopupMenu.PopupBaseMenuItem {
     constructor(button, app) {
         super();
@@ -233,21 +217,6 @@ class ApplicationsMenu extends PopupMenu.PopupMenu {
         return false;
     }
 
-    open(animate) {
-        this._button.hotCorner.setBarrierSize(0);
-        if (this._button.hotCorner.actor) // fallback corner
-            this._button.hotCorner.actor.hide();
-        super.open(animate);
-    }
-
-    close(animate) {
-        let size = Main.layoutManager.panelBox.height;
-        this._button.hotCorner.setBarrierSize(size);
-        if (this._button.hotCorner.actor) // fallback corner
-            this._button.hotCorner.actor.show();
-        super.close(animate);
-    }
-
     toggle() {
         if (this.isOpen) {
             this._button.selectCategory(null);
@@ -381,7 +350,7 @@ Signals.addSignalMethods(DesktopTarget.prototype);
 
 let ApplicationsButton = GObject.registerClass(
 class ApplicationsButton extends PanelMenu.Button {
-    _init() {
+    _init(includeIcon) {
         super._init(1.0, null, false);
 
         this.setMenu(new ApplicationsMenu(this, 1.0, St.Side.TOP, this));
@@ -398,7 +367,8 @@ class ApplicationsButton extends PanelMenu.Button {
             '/usr/share/icons/hicolor/scalable/apps/start-here.svg');
         this._icon = new St.Icon({
             gicon: new Gio.FileIcon({ file: iconFile }),
-            style_class: 'panel-logo-icon'
+            style_class: 'panel-logo-icon',
+            visible: includeIcon
         });
         hbox.add_actor(this._icon);
 
@@ -414,8 +384,6 @@ class ApplicationsButton extends PanelMenu.Button {
         this.name = 'panelApplications';
         this.label_actor = this._label;
 
-        this.connect('captured-event', this._onCapturedEvent.bind(this));
-
         this._showingId = Main.overview.connect('showing', () => {
             this.add_accessible_state (Atk.StateType.CHECKED);
         });
@@ -457,10 +425,6 @@ class ApplicationsButton extends PanelMenu.Button {
         }
     }
 
-    get hotCorner() {
-        return Main.layoutManager.hotCorners[Main.layoutManager.primaryIndex];
-    }
-
     _createVertSeparator() {
         let separator = new St.DrawingArea({
             style_class: 'calendar-vertical-separator',
@@ -487,14 +451,6 @@ class ApplicationsButton extends PanelMenu.Button {
         this._desktopTarget.destroy();
     }
 
-    _onCapturedEvent(actor, event) {
-        if (event.type() == Clutter.EventType.BUTTON_PRESS) {
-            if (!Main.overview.shouldToggleByCornerOrButton())
-                return true;
-        }
-        return false;
-    }
-
     _onMenuKeyPress(actor, event) {
         let symbol = event.get_key_symbol();
         if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
@@ -638,14 +594,6 @@ class ApplicationsButton extends PanelMenu.Button {
             y_align: St.Align.START
         });
 
-        let activities = new ActivitiesMenuItem(this);
-        this.leftBox.add(activities.actor, {
-            expand: false,
-            x_fill: true,
-            y_fill: false,
-            y_align: St.Align.START
-        });
-
         this.applicationsBox = new St.BoxLayout({ vertical: true });
         this.applicationsScrollBox.add_actor(this.applicationsBox);
         this.categoriesBox = new St.BoxLayout({ vertical: true });
@@ -757,19 +705,16 @@ class ApplicationsButton extends PanelMenu.Button {
 });
 
 let appsMenuButton;
-let activitiesButton;
 
 function enable() {
-    activitiesButton = Main.panel.statusArea['activities'];
-    activitiesButton.container.hide();
-    appsMenuButton = new ApplicationsButton();
-    Main.panel.addToStatusArea('apps-menu', appsMenuButton, 1, 'left');
+    let index = Main.sessionMode.panel.left.indexOf('activities') + 1;
+    appsMenuButton = new ApplicationsButton(index == 0);
+    Main.panel.addToStatusArea('apps-menu', appsMenuButton, index, 'left');
 }
 
 function disable() {
     Main.panel.menuManager.removeMenu(appsMenuButton.menu);
     appsMenuButton.destroy();
-    activitiesButton.container.show();
 }
 
 function init() {
-- 
2.21.0


From e8b2f5206b659398931cfc961954b5d744d52728 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 7 Jun 2019 14:30:16 +0000
Subject: [PATCH 03/22] apps-menu: Stop hiding the overview when toggled

Now that the extension no longer doubles as the "Activities" button,
that behavior is confusing.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/69
---
 extensions/apps-menu/extension.js | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js
index d6c9c8b..76bcd1a 100644
--- a/extensions/apps-menu/extension.js
+++ b/extensions/apps-menu/extension.js
@@ -218,12 +218,8 @@ class ApplicationsMenu extends PopupMenu.PopupMenu {
     }
 
     toggle() {
-        if (this.isOpen) {
+        if (this.isOpen)
             this._button.selectCategory(null);
-        } else {
-            if (Main.overview.visible)
-                Main.overview.hide();
-        }
         super.toggle();
     }
 }
-- 
2.21.0


From bcd2c97e89bc90675525dbd05207ae25a91d39df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 7 Jun 2019 20:07:19 +0000
Subject: [PATCH 04/22] apps-menu: Hide overview when launching app

Now that we no longer hide the overview when the menu is opened,
it is possible to activate menu entries from the overview. Start
hiding the overview in that case, which is consistent with app
launching elsewhere.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/69
---
 extensions/apps-menu/extension.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js
index 76bcd1a..b9e7111 100644
--- a/extensions/apps-menu/extension.js
+++ b/extensions/apps-menu/extension.js
@@ -66,6 +66,8 @@ class ApplicationMenuItem extends PopupMenu.PopupBaseMenuItem {
         this._button.selectCategory(null);
         this._button.menu.toggle();
         super.activate(event);
+
+        Main.overview.hide();
     }
 
     setActive(active, params) {
-- 
2.21.0


From c118895cff4cedfb43f0ae875d5cd355f66f0db7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 29 May 2019 09:44:30 +0000
Subject: [PATCH 05/22] classic: Disable overview

The overview is one of the defining features of GNOME 3, and thus
almost by definition at odds with the classic session, which
emulates a traditional GNOME 2 desktop.

Even with the less prominent placement inside the application menu
it never quite fit in - it doesn't help that besides the different
UI paradigma, the overview keeps its "normal" styling which differs
greatly with classic's normal mode.

So besides removing the "Activities" button via the session mode
definition, now that the apps-menu extension doesn't replace it anymore,
disable the overview completely in the classic session.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/69
---
 data/classic.json.in | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/data/classic.json.in b/data/classic.json.in
index fdb3762..c1c0544 100644
--- a/data/classic.json.in
+++ b/data/classic.json.in
@@ -1,8 +1,9 @@
 {
     "parentMode": "user",
     "stylesheetName": "gnome-classic.css",
+    "hasOverview": false,
     "enabledExtensions": [@CLASSIC_EXTENSIONS@],
-    "panel": { "left": ["activities", "appMenu"],
+    "panel": { "left": ["appMenu"],
                "center": [],
                "right": ["a11y", "keyboard", "dateMenu", "aggregateMenu"]
              }
-- 
2.21.0


From 004affc147579e2b1a11614cd0c8faf99e307999 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 00:23:13 +0000
Subject: [PATCH 06/22] window-list: Split out workspaceIndicator

The extension has grown unwieldily big, so before starting to improve
on the workspace indicator, move it to its own source file.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70
---
 extensions/window-list/extension.js          | 130 +-----------------
 extensions/window-list/meson.build           |   2 +-
 extensions/window-list/workspaceIndicator.js | 135 +++++++++++++++++++
 3 files changed, 138 insertions(+), 129 deletions(-)
 create mode 100644 extensions/window-list/workspaceIndicator.js

diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
index e1ea742..5275d9c 100644
--- a/extensions/window-list/extension.js
+++ b/extensions/window-list/extension.js
@@ -1,13 +1,13 @@
 /* exported init */
-const { Clutter, Gio, GLib, GObject, Gtk, Meta, Shell, St } = imports.gi;
+const { Clutter, Gio, GLib, Gtk, Meta, Shell, St } = imports.gi;
 
 const DND = imports.ui.dnd;
 const Main = imports.ui.main;
-const PanelMenu = imports.ui.panelMenu;
 const PopupMenu = imports.ui.popupMenu;
 
 const ExtensionUtils = imports.misc.extensionUtils;
 const Me = ExtensionUtils.getCurrentExtension();
+const { WorkspaceIndicator } = Me.imports.workspaceIndicator;
 
 const Gettext = imports.gettext.domain('gnome-shell-extensions');
 const _ = Gettext.gettext;
@@ -644,132 +644,6 @@ class AppButton extends BaseButton {
 }
 
 
-let WorkspaceIndicator = GObject.registerClass(
-class WorkspaceIndicator extends PanelMenu.Button {
-    _init() {
-        super._init(0.0, _('Workspace Indicator'), true);
-        this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM));
-        this.add_style_class_name('window-list-workspace-indicator');
-        this.menu.actor.remove_style_class_name('panel-menu');
-
-        let container = new St.Widget({
-            layout_manager: new Clutter.BinLayout(),
-            x_expand: true,
-            y_expand: true
-        });
-        this.add_actor(container);
-
-        let workspaceManager = global.workspace_manager;
-
-        this._currentWorkspace = workspaceManager.get_active_workspace().index();
-        this.statusLabel = new St.Label({
-            text: this._getStatusText(),
-            x_align: Clutter.ActorAlign.CENTER,
-            y_align: Clutter.ActorAlign.CENTER
-        });
-        container.add_actor(this.statusLabel);
-
-        this.workspacesItems = [];
-
-        this._workspaceManagerSignals = [];
-        this._workspaceManagerSignals.push(workspaceManager.connect('notify::n-workspaces',
-                                                                    this._updateMenu.bind(this)));
-        this._workspaceManagerSignals.push(workspaceManager.connect_after('workspace-switched',
-                                                                          this._updateIndicator.bind(this)));
-
-        this.connect('scroll-event', this._onScrollEvent.bind(this));
-        this._updateMenu();
-
-        this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' });
-        this._settingsChangedId =
-            this._settings.connect('changed::workspace-names',
-                                   this._updateMenu.bind(this));
-    }
-
-    _onDestroy() {
-        for (let i = 0; i < this._workspaceManagerSignals.length; i++)
-            global.workspace_manager.disconnect(this._workspaceManagerSignals[i]);
-
-        if (this._settingsChangedId) {
-            this._settings.disconnect(this._settingsChangedId);
-            this._settingsChangedId = 0;
-        }
-
-        super._onDestroy();
-    }
-
-    _updateIndicator() {
-        this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
-        this._currentWorkspace = global.workspace_manager.get_active_workspace().index();
-        this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
-
-        this.statusLabel.set_text(this._getStatusText());
-    }
-
-    _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);
-    }
-
-    _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', (item, _event) => {
-                this._activate(item.workspaceId);
-            });
-
-            if (i == this._currentWorkspace)
-                item.setOrnament(PopupMenu.Ornament.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());
-        }
-    }
-
-    _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);
-    }
-
-    _allocate(actor, box, flags) {
-        if (actor.get_n_children() > 0)
-            actor.get_first_child().allocate(box, flags);
-    }
-});
-
 class WindowList {
     constructor(perMonitor, monitor) {
         this._perMonitor = perMonitor;
diff --git a/extensions/window-list/meson.build b/extensions/window-list/meson.build
index b4aa4db..74025f9 100644
--- a/extensions/window-list/meson.build
+++ b/extensions/window-list/meson.build
@@ -4,7 +4,7 @@ extension_data += configure_file(
   configuration: metadata_conf
 )
 
-extension_sources += files('prefs.js')
+extension_sources += files('prefs.js', 'workspaceIndicator.js')
 extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
 
 if classic_mode_enabled
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
new file mode 100644
index 0000000..fb3ffe7
--- /dev/null
+++ b/extensions/window-list/workspaceIndicator.js
@@ -0,0 +1,135 @@
+/* exported WorkspaceIndicator */
+const { Clutter, Gio, GObject, Meta, St } = imports.gi;
+
+const PanelMenu = imports.ui.panelMenu;
+const PopupMenu = imports.ui.popupMenu;
+
+const Gettext = imports.gettext.domain('gnome-shell-extensions');
+const _ = Gettext.gettext;
+
+var WorkspaceIndicator = GObject.registerClass(
+class WorkspaceIndicator extends PanelMenu.Button {
+    _init() {
+        super._init(0.0, _('Workspace Indicator'), true);
+        this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM));
+        this.add_style_class_name('window-list-workspace-indicator');
+        this.menu.actor.remove_style_class_name('panel-menu');
+
+        let container = new St.Widget({
+            layout_manager: new Clutter.BinLayout(),
+            x_expand: true,
+            y_expand: true
+        });
+        this.add_actor(container);
+
+        let workspaceManager = global.workspace_manager;
+
+        this._currentWorkspace = workspaceManager.get_active_workspace().index();
+        this.statusLabel = new St.Label({
+            text: this._getStatusText(),
+            x_align: Clutter.ActorAlign.CENTER,
+            y_align: Clutter.ActorAlign.CENTER
+        });
+        container.add_actor(this.statusLabel);
+
+        this.workspacesItems = [];
+
+        this._workspaceManagerSignals = [
+            workspaceManager.connect('notify::n-workspaces',
+                this._updateMenu.bind(this)),
+            workspaceManager.connect_after('workspace-switched',
+                this._updateIndicator.bind(this))
+        ];
+
+        this.connect('scroll-event', this._onScrollEvent.bind(this));
+        this._updateMenu();
+
+        this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' });
+        this._settingsChangedId = this._settings.connect(
+            'changed::workspace-names', this._updateMenu.bind(this));
+    }
+
+    _onDestroy() {
+        for (let i = 0; i < this._workspaceManagerSignals.length; i++)
+            global.workspace_manager.disconnect(this._workspaceManagerSignals[i]);
+
+        if (this._settingsChangedId) {
+            this._settings.disconnect(this._settingsChangedId);
+            this._settingsChangedId = 0;
+        }
+
+        super._onDestroy();
+    }
+
+    _updateIndicator() {
+        this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
+        this._currentWorkspace = global.workspace_manager.get_active_workspace().index();
+        this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
+
+        this.statusLabel.set_text(this._getStatusText());
+    }
+
+    _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);
+    }
+
+    _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', (item, _event) => {
+                this._activate(item.workspaceId);
+            });
+
+            if (i == this._currentWorkspace)
+                item.setOrnament(PopupMenu.Ornament.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());
+        }
+    }
+
+    _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);
+    }
+
+    _allocate(actor, box, flags) {
+        if (actor.get_n_children() > 0)
+            actor.get_first_child().allocate(box, flags);
+    }
+});
+
-- 
2.21.0


From cceda34bfcd3de806a7b72bb532e1f8a248accee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 17:39:53 +0000
Subject: [PATCH 07/22] window-list: Use a more specific GTypeName for
 workspace indicator

Now that the class inherits from GObject, the generic name easily
conflicts with other classes otherwise, for example with the one
from the workspace-indicator extension.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70
---
 extensions/window-list/workspaceIndicator.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index fb3ffe7..8ac43eb 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -7,8 +7,9 @@ const PopupMenu = imports.ui.popupMenu;
 const Gettext = imports.gettext.domain('gnome-shell-extensions');
 const _ = Gettext.gettext;
 
-var WorkspaceIndicator = GObject.registerClass(
-class WorkspaceIndicator extends PanelMenu.Button {
+var WorkspaceIndicator = GObject.registerClass({
+    GTypeName: 'WindowListWorkspaceIndicator'
+}, class WorkspaceIndicator extends PanelMenu.Button {
     _init() {
         super._init(0.0, _('Workspace Indicator'), true);
         this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM));
-- 
2.21.0


From 57a97bef2e2cfb88324e9d1540254dca8f5195e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 04:54:50 +0200
Subject: [PATCH 08/22] window-list: Make some properties private

There's no reason why they should be public.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70
---
 extensions/window-list/workspaceIndicator.js | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index 8ac43eb..7c0360a 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -26,14 +26,14 @@ var WorkspaceIndicator = GObject.registerClass({
         let workspaceManager = global.workspace_manager;
 
         this._currentWorkspace = workspaceManager.get_active_workspace().index();
-        this.statusLabel = new St.Label({
+        this._statusLabel = new St.Label({
             text: this._getStatusText(),
             x_align: Clutter.ActorAlign.CENTER,
             y_align: Clutter.ActorAlign.CENTER
         });
-        container.add_actor(this.statusLabel);
+        container.add_actor(this._statusLabel);
 
-        this.workspacesItems = [];
+        this._workspacesItems = [];
 
         this._workspaceManagerSignals = [
             workspaceManager.connect('notify::n-workspaces',
@@ -63,11 +63,11 @@ var WorkspaceIndicator = GObject.registerClass({
     }
 
     _updateIndicator() {
-        this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
+        this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
         this._currentWorkspace = global.workspace_manager.get_active_workspace().index();
-        this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
+        this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
 
-        this.statusLabel.set_text(this._getStatusText());
+        this._statusLabel.set_text(this._getStatusText());
     }
 
     _getStatusText() {
@@ -82,7 +82,7 @@ var WorkspaceIndicator = GObject.registerClass({
         let workspaceManager = global.workspace_manager;
 
         this.menu.removeAll();
-        this.workspacesItems = [];
+        this._workspacesItems = [];
         this._currentWorkspace = workspaceManager.get_active_workspace().index();
 
         for (let i = 0; i < workspaceManager.n_workspaces; i++) {
@@ -98,10 +98,10 @@ var WorkspaceIndicator = GObject.registerClass({
                 item.setOrnament(PopupMenu.Ornament.DOT);
 
             this.menu.addMenuItem(item);
-            this.workspacesItems[i] = item;
+            this._workspacesItems[i] = item;
         }
 
-        this.statusLabel.set_text(this._getStatusText());
+        this._statusLabel.set_text(this._getStatusText());
     }
 
     _activate(index) {
-- 
2.21.0


From 8097536f35b9c64f24e8c1c6c8211024c4f8f01d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 04:57:39 +0200
Subject: [PATCH 09/22] window-list: Update workspace names in-place

There's no good reason to rebuild the entire menu on workspace names
changes, we can simply update the labels in-place.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70
---
 extensions/window-list/workspaceIndicator.js | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index 7c0360a..9888838 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -47,7 +47,7 @@ var WorkspaceIndicator = GObject.registerClass({
 
         this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' });
         this._settingsChangedId = this._settings.connect(
-            'changed::workspace-names', this._updateMenu.bind(this));
+            'changed::workspace-names', this._updateMenuLabels.bind(this));
     }
 
     _onDestroy() {
@@ -78,6 +78,14 @@ var WorkspaceIndicator = GObject.registerClass({
         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;
 
-- 
2.21.0


From c51e35e0dc9937ab19577be41654f914f1cfe894 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 04:59:19 +0200
Subject: [PATCH 10/22] window-list: Minor cleanup

Mutter has a dedicated method for getting the index of the active
workspace, use that instead of getting first the active workspace
and then its index.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70
---
 extensions/window-list/workspaceIndicator.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index 9888838..1f2e1c1 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -25,7 +25,7 @@ var WorkspaceIndicator = GObject.registerClass({
 
         let workspaceManager = global.workspace_manager;
 
-        this._currentWorkspace = workspaceManager.get_active_workspace().index();
+        this._currentWorkspace = workspaceManager.get_active_workspace_index();
         this._statusLabel = new St.Label({
             text: this._getStatusText(),
             x_align: Clutter.ActorAlign.CENTER,
@@ -64,7 +64,7 @@ var WorkspaceIndicator = GObject.registerClass({
 
     _updateIndicator() {
         this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
-        this._currentWorkspace = global.workspace_manager.get_active_workspace().index();
+        this._currentWorkspace = global.workspace_manager.get_active_workspace_index();
         this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
 
         this._statusLabel.set_text(this._getStatusText());
@@ -72,7 +72,7 @@ var WorkspaceIndicator = GObject.registerClass({
 
     _getStatusText() {
         let workspaceManager = global.workspace_manager;
-        let current = workspaceManager.get_active_workspace().index();
+        let current = workspaceManager.get_active_workspace_index();
         let total = workspaceManager.n_workspaces;
 
         return '%d / %d'.format(current + 1, total);
@@ -91,7 +91,7 @@ var WorkspaceIndicator = GObject.registerClass({
 
         this.menu.removeAll();
         this._workspacesItems = [];
-        this._currentWorkspace = workspaceManager.get_active_workspace().index();
+        this._currentWorkspace = workspaceManager.get_active_workspace_index();
 
         for (let i = 0; i < workspaceManager.n_workspaces; i++) {
             let name = Meta.prefs_get_workspace_name(i);
-- 
2.21.0


From 2eb09e7cc7e830bf4004a7c25a46d285e5e0bac1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 05:11:34 +0200
Subject: [PATCH 11/22] window-list: Improve workspace label styling

The border currently looks off - it extends all the way vertically
and leaves zero spacing to the label horizontally. Fix both issues
by setting appropriate padding/margins.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70
---
 extensions/window-list/stylesheet.css        |  8 +++-----
 extensions/window-list/workspaceIndicator.js | 13 ++++++++-----
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css
index f5285cb..3d3f2d7 100644
--- a/extensions/window-list/stylesheet.css
+++ b/extensions/window-list/stylesheet.css
@@ -70,13 +70,11 @@
   height: 24px;
 }
 
-.window-list-workspace-indicator {
-  padding: 3px;
-}
-
-.window-list-workspace-indicator > StWidget {
+.window-list-workspace-indicator .status-label-bin {
   background-color: rgba(200, 200, 200, .3);
   border: 1px solid #cccccc;
+  padding: 0 3px;
+  margin: 3px 0;
 }
 
 .notification {
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index 1f2e1c1..598c516 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -26,12 +26,15 @@ var WorkspaceIndicator = GObject.registerClass({
         let workspaceManager = global.workspace_manager;
 
         this._currentWorkspace = workspaceManager.get_active_workspace_index();
-        this._statusLabel = new St.Label({
-            text: this._getStatusText(),
-            x_align: Clutter.ActorAlign.CENTER,
-            y_align: Clutter.ActorAlign.CENTER
+        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_actor(this._statusLabel);
+        container.add_actor(this._statusBin);
 
         this._workspacesItems = [];
 
-- 
2.21.0


From 477a532a5f5fb2f8a1ddcfc2277fe62fa1a7fb74 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 05:08:31 +0200
Subject: [PATCH 12/22] window-list: Refactor workspace signal handlers

We are about to support a separate representation if horizontal
workspaces are used. To prepare for that, rename the handlers to
something more generic and split out menu-specific bits into a
dedicated help function.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70
---
 extensions/window-list/workspaceIndicator.js | 25 +++++++++++++++-----
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index 598c516..78ca97e 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -40,9 +40,9 @@ var WorkspaceIndicator = GObject.registerClass({
 
         this._workspaceManagerSignals = [
             workspaceManager.connect('notify::n-workspaces',
-                this._updateMenu.bind(this)),
+                this._nWorkspacesChanged.bind(this)),
             workspaceManager.connect_after('workspace-switched',
-                this._updateIndicator.bind(this))
+                this._onWorkspaceSwitched.bind(this))
         ];
 
         this.connect('scroll-event', this._onScrollEvent.bind(this));
@@ -65,14 +65,27 @@ var WorkspaceIndicator = GObject.registerClass({
         super._onDestroy();
     }
 
-    _updateIndicator() {
-        this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
-        this._currentWorkspace = global.workspace_manager.get_active_workspace_index();
-        this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
+    _onWorkspaceSwitched() {
+        let workspaceManager = global.workspace_manager;
+        this._currentWorkspace = workspaceManager.get_active_workspace_index();
+
+        this._updateMenuOrnament();
 
         this._statusLabel.set_text(this._getStatusText());
     }
 
+    _nWorkspacesChanged() {
+        this._updateMenu();
+    }
+
+    _updateMenuOrnament() {
+        for (let i = 0; i < this._workspacesItems.length; i++) {
+            this._workspacesItems[i].setOrnament(i == this._currentWorkspace
+                ? PopupMenu.Ornament.DOT
+                : PopupMenu.Ornament.NONE);
+        }
+    }
+
     _getStatusText() {
         let workspaceManager = global.workspace_manager;
         let current = workspaceManager.get_active_workspace_index();
-- 
2.21.0


From 8d2a00ef3a1bbd9c5bccd1542049b22a3bb2a79d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 02:53:38 +0000
Subject: [PATCH 13/22] window-list: Support horizontal workspace layout

Unlike in GNOME 2, the workspace indicator we display in the window list
isn't a workspace switcher, but a menu button that allows switching
workspaces via its menu. The reason for that is that a horizontal
in-place switcher would be at odds with the vertical workspace layout
used in GNOME 3.

However that reasoning doesn't apply when the layout is changed to a
horizontal one, so replace the button with a traditional workspace
switcher in that case.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70
---
 extensions/window-list/stylesheet.css        | 24 +++++++
 extensions/window-list/workspaceIndicator.js | 66 +++++++++++++++++++-
 2 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css
index 3d3f2d7..b383b97 100644
--- a/extensions/window-list/stylesheet.css
+++ b/extensions/window-list/stylesheet.css
@@ -77,6 +77,30 @@
   margin: 3px 0;
 }
 
+.window-list-workspace-indicator .workspaces-box {
+  spacing: 3px;
+  padding: 3px;
+}
+
+.window-list-workspace-indicator .workspace {
+  border: 1px solid #cccccc;
+  width: 52px;
+}
+
+.window-list-workspace-indicator .workspace:first-child:ltr,
+.window-list-workspace-indicator .workspace:last-child:rtl {
+  border-radius: 4px 0 0 4px;
+}
+
+.window-list-workspace-indicator .workspace:first-child:rtl,
+.window-list-workspace-indicator .workspace:last-child:ltr {
+  border-radius: 0 4px 4px 0;
+}
+
+.window-list-workspace-indicator .workspace.active {
+  background-color: rgba(200, 200, 200, .3);
+}
+
 .notification {
   font-weight: normal;
 }
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index 78ca97e..1258ed2 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -7,6 +7,25 @@ const PopupMenu = imports.ui.popupMenu;
 const Gettext = imports.gettext.domain('gnome-shell-extensions');
 const _ = Gettext.gettext;
 
+let WorkspaceThumbnail = GObject.registerClass({
+    GTypeName: 'WindowListWorkspaceThumbnail'
+}, class WorkspaceThumbnail extends St.Button {
+    _init(index) {
+        super._init({
+            style_class: 'workspace'
+        });
+
+        this._index = index;
+    }
+
+    // eslint-disable-next-line camelcase
+    on_clicked() {
+        let ws = global.workspace_manager.get_workspace_by_index(this._index);
+        if (ws)
+            ws.activate(global.get_current_time());
+    }
+});
+
 var WorkspaceIndicator = GObject.registerClass({
     GTypeName: 'WindowListWorkspaceIndicator'
 }, class WorkspaceIndicator extends PanelMenu.Button {
@@ -36,17 +55,30 @@ var WorkspaceIndicator = GObject.registerClass({
         });
         container.add_actor(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_actor(this._thumbnailsBox);
+
         this._workspacesItems = [];
 
         this._workspaceManagerSignals = [
             workspaceManager.connect('notify::n-workspaces',
                 this._nWorkspacesChanged.bind(this)),
             workspaceManager.connect_after('workspace-switched',
-                this._onWorkspaceSwitched.bind(this))
+                this._onWorkspaceSwitched.bind(this)),
+            workspaceManager.connect('notify::layout-rows',
+                this._onWorkspaceOrientationChanged.bind(this))
         ];
 
         this.connect('scroll-event', this._onScrollEvent.bind(this));
         this._updateMenu();
+        this._updateThumbnails();
+        this._onWorkspaceOrientationChanged();
 
         this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' });
         this._settingsChangedId = this._settings.connect(
@@ -65,17 +97,27 @@ var WorkspaceIndicator = GObject.registerClass({
         super._onDestroy();
     }
 
+    _onWorkspaceOrientationChanged() {
+        let vertical = global.workspace_manager.layout_rows == -1;
+        this.reactive = vertical;
+
+        this._statusBin.visible = vertical;
+        this._thumbnailsBox.visible = !vertical;
+    }
+
     _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();
     }
 
     _updateMenuOrnament() {
@@ -86,6 +128,16 @@ var WorkspaceIndicator = GObject.registerClass({
         }
     }
 
+    _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();
@@ -128,6 +180,18 @@ var WorkspaceIndicator = GObject.registerClass({
         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_actor(thumb);
+        }
+        this._updateActiveThumbnail();
+    }
+
     _activate(index) {
         let workspaceManager = global.workspace_manager;
 
-- 
2.21.0


From 09e1fab05fe622ea811f9618d7f97ca228f4c282 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 5 Jun 2019 03:31:16 +0000
Subject: [PATCH 14/22] classic: Add 'horizontal-workspaces' extension

Vertical workspaces are another defining characteristics of GNOME 3,
and thus rather un-classic. That switch was driven by the overall
layout of the overview, and now that we disable the overview in
GNOME Classic, we can just return to the traditional workspace
layout as well.

Add a small extension that does just that.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/72
---
 extensions/horizontal-workspaces/extension.js  | 18 ++++++++++++++++++
 extensions/horizontal-workspaces/meson.build   |  5 +++++
 .../horizontal-workspaces/metadata.json.in     | 10 ++++++++++
 .../horizontal-workspaces/stylesheet.css       |  1 +
 meson.build                                    |  1 +
 5 files changed, 35 insertions(+)
 create mode 100644 extensions/horizontal-workspaces/extension.js
 create mode 100644 extensions/horizontal-workspaces/meson.build
 create mode 100644 extensions/horizontal-workspaces/metadata.json.in
 create mode 100644 extensions/horizontal-workspaces/stylesheet.css

diff --git a/extensions/horizontal-workspaces/extension.js b/extensions/horizontal-workspaces/extension.js
new file mode 100644
index 0000000..b3937ce
--- /dev/null
+++ b/extensions/horizontal-workspaces/extension.js
@@ -0,0 +1,18 @@
+/* exported enable disable */
+const { Meta } = imports.gi;
+
+function enable() {
+    global.workspace_manager.override_workspace_layout(
+        Meta.DisplayCorner.TOPLEFT,
+        false,
+        1,
+        -1);
+}
+
+function disable() {
+    global.workspace_manager.override_workspace_layout(
+        Meta.DisplayCorner.TOPLEFT,
+        false,
+        -1,
+        1);
+}
diff --git a/extensions/horizontal-workspaces/meson.build b/extensions/horizontal-workspaces/meson.build
new file mode 100644
index 0000000..48504f6
--- /dev/null
+++ b/extensions/horizontal-workspaces/meson.build
@@ -0,0 +1,5 @@
+extension_data += configure_file(
+  input: metadata_name + '.in',
+  output: metadata_name,
+  configuration: metadata_conf
+)
diff --git a/extensions/horizontal-workspaces/metadata.json.in b/extensions/horizontal-workspaces/metadata.json.in
new file mode 100644
index 0000000..f109e06
--- /dev/null
+++ b/extensions/horizontal-workspaces/metadata.json.in
@@ -0,0 +1,10 @@
+{
+"extension-id": "@extension_id@",
+"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
+"gettext-domain": "@gettext_domain@",
+"name": "Horizontal workspaces",
+"description": "Use a horizontal workspace layout",
+"shell-version": [ "@shell_current@" ],
+"url": "@url@"
+}
diff --git a/extensions/horizontal-workspaces/stylesheet.css b/extensions/horizontal-workspaces/stylesheet.css
new file mode 100644
index 0000000..25134b6
--- /dev/null
+++ b/extensions/horizontal-workspaces/stylesheet.css
@@ -0,0 +1 @@
+/* This extensions requires no special styling */
diff --git a/meson.build b/meson.build
index 32743ed..23bd5ad 100644
--- a/meson.build
+++ b/meson.build
@@ -34,6 +34,7 @@ uuid_suffix = '@gnome-shell-extensions.gcampax.github.com'
 classic_extensions = [
   'apps-menu',
   'desktop-icons',
+  'horizontal-workspaces',
   'places-menu',
   'launch-new-instance',
   'top-icons',
-- 
2.21.0


From 85a49ec1e4eb01a32f9bf9b3b8a1bde805936d7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 14 May 2019 19:51:22 +0200
Subject: [PATCH 15/22] window-list: Add window picker button
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

With the latest changes, GNOME Classic has become so classic that it
is bordering dull. Salvage at least a tiny piece of GNOME 3 in form
of a window-pick button which toggles an exposé-like reduced overview.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/73
---
 extensions/window-list/classic.css     |  20 +-
 extensions/window-list/extension.js    |  36 +++-
 extensions/window-list/meson.build     |   2 +-
 extensions/window-list/stylesheet.css  |  27 ++-
 extensions/window-list/windowPicker.js | 250 +++++++++++++++++++++++++
 5 files changed, 322 insertions(+), 13 deletions(-)
 create mode 100644 extensions/window-list/windowPicker.js

diff --git a/extensions/window-list/classic.css b/extensions/window-list/classic.css
index f3c44a3..c506bea 100644
--- a/extensions/window-list/classic.css
+++ b/extensions/window-list/classic.css
@@ -6,14 +6,13 @@
     height: 2.25em ;
   }
 
-  .bottom-panel .window-button > StWidget {
+  .bottom-panel .window-button > StWidget,
+  .bottom-panel .window-picker-toggle > StWidget {
     background-gradient-drection: vertical;
     background-color: #fff;
     background-gradient-start: #fff;
     background-gradient-end: #eee;
     color: #000;
-    -st-natural-width: 18.7em;
-    max-width: 18.75em;
     color: #2e3436;
     background-color: #eee;
     border-radius: 2px;
@@ -22,7 +21,17 @@
     text-shadow: 0 0 transparent;
   }
 
-  .bottom-panel .window-button:hover > StWidget {
+  .bottom-panel .window-button > StWidget {
+    -st-natural-width: 18.7em;
+    max-width: 18.75em;
+  }
+
+  .bottom-panel .window-picker-toggle > StWidet {
+    border: 1px solid rgba(0,0,0,0.3);
+  }
+
+  .bottom-panel .window-button:hover > StWidget,
+  .bottom-panel .window-picker-toggle:hover > StWidget {
     background-color: #f9f9f9;
   }
 
@@ -31,7 +40,8 @@
     box-shadow: inset 1px 1px 2px rgba(0,0,0,0.5);
   }
 
-  .bottom-panel .window-button.focused > StWidget {
+  .bottom-panel .window-button.focused > StWidget,
+  .bottom-panel .window-picker-toggle:checked > StWidget {
     background-color: #ddd;
     box-shadow: inset 1px 1px 1px rgba(0,0,0,0.5);
   }
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
index 5275d9c..1f854aa 100644
--- a/extensions/window-list/extension.js
+++ b/extensions/window-list/extension.js
@@ -3,10 +3,13 @@ const { Clutter, Gio, GLib, Gtk, Meta, Shell, St } = imports.gi;
 
 const DND = imports.ui.dnd;
 const Main = imports.ui.main;
+const Overview = imports.ui.overview;
 const PopupMenu = imports.ui.popupMenu;
+const Tweener = imports.ui.tweener;
 
 const ExtensionUtils = imports.misc.extensionUtils;
 const Me = ExtensionUtils.getCurrentExtension();
+const { WindowPicker, WindowPickerToggle } = Me.imports.windowPicker;
 const { WorkspaceIndicator } = Me.imports.workspaceIndicator;
 
 const Gettext = imports.gettext.domain('gnome-shell-extensions');
@@ -661,6 +664,12 @@ class WindowList {
         let box = new St.BoxLayout({ x_expand: true, y_expand: true });
         this.actor.add_actor(box);
 
+        let toggle = new WindowPickerToggle();
+        box.add_actor(toggle);
+
+        toggle.connect('notify::checked',
+            this._updateWindowListVisibility.bind(this));
+
         let layout = new Clutter.BoxLayout({ homogeneous: true });
         this._windowList = new St.Widget({
             style_class: 'window-list',
@@ -810,6 +819,19 @@ class WindowList {
         this._workspaceIndicator.actor.visible = hasWorkspaces && workspacesOnMonitor;
     }
 
+    _updateWindowListVisibility() {
+        let visible = !Main.windowPicker.visible;
+
+        Tweener.addTween(this._windowList, {
+            opacity: visible ? 255 : 0,
+            transition: 'ease-out-quad',
+            time: Overview.ANIMATION_TIME
+        });
+
+        this._windowList.reactive = visible;
+        this._windowList.get_children().forEach(c => c.reactive = visible);
+    }
+
     _getPreferredUngroupedWindowListWidth() {
         if (this._windowList.get_n_children() == 0)
             return this._windowList.get_preferred_width(-1)[1];
@@ -1080,7 +1102,7 @@ class WindowList {
 class Extension {
     constructor() {
         this._windowLists = null;
-        this._injections = {};
+        this._hideOverviewOrig = Main.overview.hide;
     }
 
     enable() {
@@ -1095,6 +1117,13 @@ class Extension {
             Main.layoutManager.connect('monitors-changed',
                                        this._buildWindowLists.bind(this));
 
+        Main.windowPicker = new WindowPicker();
+
+        Main.overview.hide = () => {
+            Main.windowPicker.close();
+            this._hideOverviewOrig.call(Main.overview);
+        };
+
         this._buildWindowLists();
     }
 
@@ -1125,6 +1154,11 @@ class Extension {
             windowList.actor.destroy();
         });
         this._windowLists = null;
+
+        Main.windowPicker.actor.destroy();
+        delete Main.windowPicker;
+
+        Main.overview.hide = this._hideOverviewOrig;
     }
 
     someWindowListContains(actor) {
diff --git a/extensions/window-list/meson.build b/extensions/window-list/meson.build
index 74025f9..34d7c3f 100644
--- a/extensions/window-list/meson.build
+++ b/extensions/window-list/meson.build
@@ -4,7 +4,7 @@ extension_data += configure_file(
   configuration: metadata_conf
 )
 
-extension_sources += files('prefs.js', 'workspaceIndicator.js')
+extension_sources += files('prefs.js', 'windowPicker.js', 'workspaceIndicator.js')
 extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
 
 if classic_mode_enabled
diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css
index b383b97..7aee64f 100644
--- a/extensions/window-list/stylesheet.css
+++ b/extensions/window-list/stylesheet.css
@@ -26,9 +26,8 @@
   spacing: 4px;
 }
 
-.window-button > StWidget {
-  -st-natural-width: 18.75em;
-  max-width: 18.75em;
+.window-button > StWidget,
+.window-picker-toggle > StWidget {
   color: #bbb;
   background-color: black;
   border-radius: 4px;
@@ -37,7 +36,21 @@
   text-shadow: 1px 1px 4px rgba(0,0,0,0.8);
 }
 
-.window-button:hover > StWidget {
+.window-picker-toggle {
+  padding: 3px;
+}
+
+.window-picker-toggle > StWidet {
+  border: 1px solid rgba(255,255,255,0.3);
+}
+
+.window-button > StWidget {
+  -st-natural-width: 18.75em;
+  max-width: 18.75em;
+}
+
+.window-button:hover > StWidget,
+.window-picker-toggle:hover > StWidget {
   color: white;
   background-color: #1f1f1f;
 }
@@ -47,12 +60,14 @@
   box-shadow: inset 2px 2px 4px rgba(255,255,255,0.5);
 }
 
-.window-button.focused > StWidget {
+.window-button.focused > StWidget,
+.window-picker-toggle:checked > StWidget {
   color: white;
   box-shadow: inset 1px 1px 4px rgba(255,255,255,0.7);
 }
 
-.window-button.focused:active > StWidget {
+.window-button.focused:active > StWidget,
+.window-picker-toggle:checked:active > StWidget {
   box-shadow: inset 2px 2px 4px rgba(255,255,255,0.7);
 }
 
diff --git a/extensions/window-list/windowPicker.js b/extensions/window-list/windowPicker.js
new file mode 100644
index 0000000..484f6bb
--- /dev/null
+++ b/extensions/window-list/windowPicker.js
@@ -0,0 +1,250 @@
+/* exported WindowPicker, WindowPickerToggle */
+const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
+const Signals = imports.signals;
+
+const Layout = imports.ui.layout;
+const Main = imports.ui.main;
+const Overview = imports.ui.overview;
+const { WorkspacesDisplay } = imports.ui.workspacesView;
+
+let MyWorkspacesDisplay = class extends WorkspacesDisplay {
+    constructor() {
+        super();
+
+        this.actor.add_constraint(
+            new Layout.MonitorConstraint({
+                primary: true,
+                work_area: true
+            }));
+
+        this.actor.connect('destroy', this._onDestroy.bind(this));
+
+        this._workareasChangedId = global.display.connect('workareas-changed',
+            this._onWorkAreasChanged.bind(this));
+        this._onWorkAreasChanged();
+    }
+
+    show(...args) {
+        if (this._scrollEventId == 0)
+            this._scrollEventId = Main.windowPicker.connect('scroll-event',
+                this._onScrollEvent.bind(this));
+
+        super.show(...args);
+    }
+
+    hide(...args) {
+        if (this._scrollEventId > 0)
+            Main.windowPicker.disconnect(this._scrollEventId);
+        this._scrollEventId = 0;
+
+        super.hide(...args);
+    }
+
+    _onWorkAreasChanged() {
+        let { primaryIndex } = Main.layoutManager;
+        let workarea = Main.layoutManager.getWorkAreaForMonitor(primaryIndex);
+        this.setWorkspacesFullGeometry(workarea);
+    }
+
+    _updateWorkspacesViews() {
+        super._updateWorkspacesViews();
+
+        this._workspacesViews.forEach(v => {
+            Main.layoutManager.overviewGroup.remove_actor(v.actor);
+            Main.windowPicker.actor.add_actor(v.actor);
+        });
+    }
+
+    _onDestroy() {
+        if (this._workareasChangedId)
+            global.display.disconnect(this._workareasChangedId);
+        this._workareasChangedId = 0;
+    }
+};
+
+var WindowPicker = class {
+    constructor() {
+        this._visible = false;
+        this._modal = false;
+
+        this.actor = new Clutter.Actor();
+
+        this.actor.connect('destroy', this._onDestroy.bind(this));
+
+        global.bind_property('screen-width',
+            this.actor, 'width',
+            GObject.BindingFlags.SYNC_CREATE);
+        global.bind_property('screen-height',
+            this.actor, 'height',
+            GObject.BindingFlags.SYNC_CREATE);
+
+        this._backgroundGroup = new Meta.BackgroundGroup({ reactive: true });
+        this.actor.add_child(this._backgroundGroup);
+
+        this._backgroundGroup.connect('scroll-event', (a, ev) => {
+            this.emit('scroll-event', ev);
+        });
+
+        // Trick WorkspacesDisplay constructor into adding actions here
+        let addActionOrig = Main.overview.addAction;
+        Main.overview.addAction = a => this._backgroundGroup.add_action(a);
+
+        this._workspacesDisplay = new MyWorkspacesDisplay();
+        this.actor.add_child(this._workspacesDisplay.actor);
+
+        Main.overview.addAction = addActionOrig;
+
+        this._bgManagers = [];
+
+        this._monitorsChangedId = Main.layoutManager.connect('monitors-changed',
+            this._updateBackgrounds.bind(this));
+        this._updateBackgrounds();
+
+        Main.uiGroup.insert_child_below(this.actor, global.window_group);
+    }
+
+    get visible() {
+        return this._visible;
+    }
+
+    open() {
+        if (this._visible)
+            return;
+
+        this._visible = true;
+
+        if (!this._syncGrab())
+            return;
+
+        this._fakeOverviewVisible(true);
+        this._shadeBackgrounds();
+        this._fakeOverviewAnimation();
+        this._workspacesDisplay.show(false);
+
+        this.emit('open-state-changed', this._visible);
+    }
+
+    close() {
+        if (!this._visible)
+            return;
+
+        this._visible = false;
+
+        if (!this._syncGrab())
+            return;
+
+        this._workspacesDisplay.animateFromOverview(false);
+        this._unshadeBackgrounds();
+        this._fakeOverviewAnimation(() => {
+            this._workspacesDisplay.hide();
+            this._fakeOverviewVisible(false);
+        });
+
+        this.emit('open-state-changed', this._visible);
+    }
+
+    _fakeOverviewAnimation(onComplete) {
+        Main.overview.animationInProgress = true;
+        GLib.timeout_add(
+            GLib.PRIORITY_DEFAULT,
+            Overview.ANIMATION_TIME * 1000,
+            () => {
+                Main.overview.animationInProgress = false;
+                if (onComplete)
+                    onComplete();
+            });
+    }
+
+    _fakeOverviewVisible(visible) {
+        // Fake overview state for WorkspacesDisplay
+        Main.overview.visible = visible;
+
+        // Hide real windows
+        Main.layoutManager._inOverview = visible;
+        Main.layoutManager._updateVisibility();
+    }
+
+    _syncGrab() {
+        if (this._visible) {
+            if (this._modal)
+                return true;
+
+            this._modal = Main.pushModal(this.actor, {
+                actionMode: Shell.ActionMode.OVERVIEW
+            });
+
+            if (!this._modal) {
+                this.hide();
+                return false;
+            }
+        } else if (this._modal) {
+            Main.popModal(this.actor);
+            this._modal = false;
+        }
+        return true;
+    }
+
+    _onDestroy() {
+        if (this._monitorsChangedId)
+            Main.layoutManager.disconnect(this._monitorsChangedId);
+        this._monitorsChangedId = 0;
+    }
+
+    _updateBackgrounds() {
+        Main.overview._updateBackgrounds.call(this);
+    }
+
+    _shadeBackgrounds() {
+        Main.overview._shadeBackgrounds.call(this);
+    }
+
+    _unshadeBackgrounds() {
+        Main.overview._unshadeBackgrounds.call(this);
+    }
+};
+Signals.addSignalMethods(WindowPicker.prototype);
+
+var WindowPickerToggle = GObject.registerClass(
+class WindowPickerToggle extends St.Button {
+    _init() {
+        let iconBin = new St.Widget({
+            layout_manager: new Clutter.BinLayout()
+        });
+        iconBin.add_child(new St.Icon({
+            icon_name: 'focus-windows-symbolic',
+            icon_size: 16,
+            x_expand: true,
+            y_expand: true,
+            x_align: Clutter.ActorAlign.CENTER,
+            y_align: Clutter.ActorAlign.CENTER
+        }));
+        super._init({
+            style_class: 'window-picker-toggle',
+            child: iconBin,
+            visible: !Main.sessionMode.hasOverview,
+            x_fill: true,
+            y_fill: true,
+            toggle_mode: true
+        });
+
+        this.connect('notify::checked', () => {
+            if (this.checked)
+                Main.windowPicker.open();
+            else
+                Main.windowPicker.close();
+        });
+
+        if (!Main.sessionMode.hasOverview) {
+            global.display.connect('overlay-key', () => {
+                if (!Main.windowPicker.visible)
+                    Main.windowPicker.open();
+                else
+                    Main.windowPicker.close();
+            });
+        }
+
+        Main.windowPicker.connect('open-state-changed', () => {
+            this.checked = Main.windowPicker.visible;
+        });
+    }
+});
-- 
2.21.0


From 36f9a7ea01eed0481572459afc81f8fb433b95d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 11 Jun 2019 23:01:20 +0000
Subject: [PATCH 16/22] window-list: Turn workspace thumbs into drop targets

It makes some sense to allow using the workspace indicator for moving
windows between workspaces as well as for workspace switching. This
applies particularly in GNOME classic after we disabled the overview
there, so that there is again a non-shortcut way of moving windows
between workspaces.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/74
---
 extensions/window-list/workspaceIndicator.js | 35 +++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index 1258ed2..8a93e84 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -1,6 +1,7 @@
 /* exported WorkspaceIndicator */
-const { Clutter, Gio, GObject, Meta, St } = imports.gi;
+const { Clutter, Gio, GObject, Meta, Shell, St } = imports.gi;
 
+const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
 const PopupMenu = imports.ui.popupMenu;
 
@@ -16,6 +17,38 @@ let WorkspaceThumbnail = GObject.registerClass({
         });
 
         this._index = index;
+        this._delegate = this; // needed for DND
+
+        this._grabOpEndId = global.display.connect('grab-op-end',
+            (dpy, dpy_, window, grabOp) => {
+                if (grabOp != Meta.GrabOp.MOVING)
+                    return;
+
+                let [x, y] = global.get_pointer();
+                let alloc = Shell.util_get_transformed_allocation(this);
+                if (alloc.contains(x, y))
+                    this._moveWindow(window);
+            });
+
+        this.connect('destroy', () => {
+            global.display.disconnect(this._grabOpEndId);
+        });
+    }
+
+    acceptDrop(source) {
+        if (!source.realWindow)
+            return false;
+
+        let window = source.realWindow.get_meta_window();
+        this._moveWindow(window);
+        return true;
+    }
+
+    _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);
     }
 
     // eslint-disable-next-line camelcase
-- 
2.21.0


From 3ac15a848809da6053c224e86c5f5c72243f6c5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sun, 9 Jun 2019 22:58:29 +0000
Subject: [PATCH 17/22] workspace-indicator: Make some properties private

There's no reason why they should be public.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/71
---
 extensions/workspace-indicator/extension.js | 30 ++++++++++-----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
index 3be1268..76224b9 100644
--- a/extensions/workspace-indicator/extension.js
+++ b/extensions/workspace-indicator/extension.js
@@ -23,14 +23,14 @@ class WorkspaceIndicator extends PanelMenu.Button {
         let workspaceManager = global.workspace_manager;
 
         this._currentWorkspace = workspaceManager.get_active_workspace().index();
-        this.statusLabel = new St.Label({
+        this._statusLabel = new St.Label({
             y_align: Clutter.ActorAlign.CENTER,
             text: this._labelText()
         });
 
-        this.add_actor(this.statusLabel);
+        this.add_actor(this._statusLabel);
 
-        this.workspacesItems = [];
+        this._workspacesItems = [];
         this._workspaceSection = new PopupMenu.PopupMenuSection();
         this.menu.addMenuItem(this._workspaceSection);
 
@@ -46,7 +46,7 @@ class WorkspaceIndicator extends PanelMenu.Button {
         this._createWorkspacesSection();
 
         //styling
-        this.statusLabel.add_style_class_name('panel-workspace-indicator');
+        this._statusLabel.add_style_class_name('panel-workspace-indicator');
 
         this._settings = new Gio.Settings({ schema_id: WORKSPACE_SCHEMA });
         this._settingsChangedId =
@@ -67,11 +67,11 @@ class WorkspaceIndicator extends PanelMenu.Button {
     }
 
     _updateIndicator() {
-        this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
+        this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
         this._currentWorkspace = global.workspace_manager.get_active_workspace().index();
-        this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
+        this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
 
-        this.statusLabel.set_text(this._labelText());
+        this._statusLabel.set_text(this._labelText());
     }
 
     _labelText(workspaceIndex) {
@@ -86,24 +86,24 @@ class WorkspaceIndicator extends PanelMenu.Button {
         let workspaceManager = global.workspace_manager;
 
         this._workspaceSection.removeAll();
-        this.workspacesItems = [];
+        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._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);
             });
 
             if (i == this._currentWorkspace)
-                this.workspacesItems[i].setOrnament(PopupMenu.Ornament.DOT);
+                this._workspacesItems[i].setOrnament(PopupMenu.Ornament.DOT);
         }
 
-        this.statusLabel.set_text(this._labelText());
+        this._statusLabel.set_text(this._labelText());
     }
 
     _activate(index) {
-- 
2.21.0


From 2b1cb8c6b27d63ed7093f4b171f0837a193061f5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sun, 9 Jun 2019 23:03:55 +0000
Subject: [PATCH 18/22] workspace-indicator: Update workspace names in-place

There's no good reason to rebuild the entire menu on workspace names
changes, we can simply update the labels in-place.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/71
---
 extensions/workspace-indicator/extension.js | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
index 76224b9..6bcac0c 100644
--- a/extensions/workspace-indicator/extension.js
+++ b/extensions/workspace-indicator/extension.js
@@ -49,9 +49,9 @@ class WorkspaceIndicator extends PanelMenu.Button {
         this._statusLabel.add_style_class_name('panel-workspace-indicator');
 
         this._settings = new Gio.Settings({ schema_id: WORKSPACE_SCHEMA });
-        this._settingsChangedId =
-            this._settings.connect(`changed::${WORKSPACE_KEY}`,
-                                   this._createWorkspacesSection.bind(this));
+        this._settingsChangedId = this._settings.connect(
+            `changed::${WORKSPACE_KEY}`,
+            this._updateMenuLabels.bind(this));
     }
 
     _onDestroy() {
@@ -82,6 +82,11 @@ class WorkspaceIndicator extends PanelMenu.Button {
         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;
 
-- 
2.21.0


From a16a7c47475a7d19da43597b810ea9224af745b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sun, 9 Jun 2019 23:05:00 +0000
Subject: [PATCH 19/22] workspace-indicator: Minor cleanup

Mutter has a dedicated method for getting the index of the active
workspace, use that instead of getting first the active workspace
and then its index.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/71
---
 extensions/workspace-indicator/extension.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
index 6bcac0c..dc31a5c 100644
--- a/extensions/workspace-indicator/extension.js
+++ b/extensions/workspace-indicator/extension.js
@@ -22,7 +22,7 @@ class WorkspaceIndicator extends PanelMenu.Button {
 
         let workspaceManager = global.workspace_manager;
 
-        this._currentWorkspace = workspaceManager.get_active_workspace().index();
+        this._currentWorkspace = workspaceManager.get_active_workspace_index();
         this._statusLabel = new St.Label({
             y_align: Clutter.ActorAlign.CENTER,
             text: this._labelText()
@@ -68,7 +68,7 @@ class WorkspaceIndicator extends PanelMenu.Button {
 
     _updateIndicator() {
         this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
-        this._currentWorkspace = global.workspace_manager.get_active_workspace().index();
+        this._currentWorkspace = global.workspace_manager.get_active_workspace_index();
         this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
 
         this._statusLabel.set_text(this._labelText());
@@ -92,7 +92,7 @@ class WorkspaceIndicator extends PanelMenu.Button {
 
         this._workspaceSection.removeAll();
         this._workspacesItems = [];
-        this._currentWorkspace = workspaceManager.get_active_workspace().index();
+        this._currentWorkspace = workspaceManager.get_active_workspace_index();
 
         let i = 0;
         for (; i < workspaceManager.n_workspaces; i++) {
@@ -131,7 +131,7 @@ class WorkspaceIndicator extends PanelMenu.Button {
             return;
         }
 
-        let newIndex = global.workspace_manager.get_active_workspace().index() + diff;
+        let newIndex = global.workspace_manager.get_active_workspace_index() + diff;
         this._activate(newIndex);
     }
 });
-- 
2.21.0


From 707dc4df33efd9779e0839c816c1bfd1efffc699 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sun, 9 Jun 2019 23:09:12 +0000
Subject: [PATCH 20/22] workspace-indicator: Refactor workspace signal handlers

We are about to support a separate representation if horizontal
workspaces are used. To prepare for that, rename the handlers to
something more generic and split out menu-specific bits into a
dedicated help function.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/71
---
 extensions/workspace-indicator/extension.js | 31 ++++++++++++++-------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
index dc31a5c..1961b1c 100644
--- a/extensions/workspace-indicator/extension.js
+++ b/extensions/workspace-indicator/extension.js
@@ -34,13 +34,12 @@ class WorkspaceIndicator extends PanelMenu.Button {
         this._workspaceSection = new PopupMenu.PopupMenuSection();
         this.menu.addMenuItem(this._workspaceSection);
 
-        this._workspaceManagerSignals = [];
-        this._workspaceManagerSignals.push(workspaceManager.connect_after('workspace-added',
-                                                                          this._createWorkspacesSection.bind(this)));
-        this._workspaceManagerSignals.push(workspaceManager.connect_after('workspace-removed',
-                                                                          this._createWorkspacesSection.bind(this)));
-        this._workspaceManagerSignals.push(workspaceManager.connect_after('workspace-switched',
-                                                                          this._updateIndicator.bind(this)));
+        this._workspaceManagerSignals = [
+            workspaceManager.connect_after('notify::n-workspaces',
+                this._nWorkspacesChanged.bind(this)),
+            workspaceManager.connect_after('workspace-switched',
+                this._onWorkspaceSwitched.bind(this))
+        ];
 
         this.connect('scroll-event', this._onScrollEvent.bind(this));
         this._createWorkspacesSection();
@@ -66,14 +65,26 @@ class WorkspaceIndicator extends PanelMenu.Button {
         super._onDestroy();
     }
 
-    _updateIndicator() {
-        this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE);
+    _onWorkspaceSwitched() {
         this._currentWorkspace = global.workspace_manager.get_active_workspace_index();
-        this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT);
+
+        this._updateMenuOrnament();
 
         this._statusLabel.set_text(this._labelText());
     }
 
+    _nWorkspacesChanged() {
+        this._createWorkspacesSection();
+    }
+
+    _updateMenuOrnament() {
+        for (let i = 0; i < this._workspacesItems.length; i++) {
+            this._workspacesItems[i].setOrnament(i == this._currentWorkspace
+                ? PopupMenu.Ornament.DOT
+                : PopupMenu.Ornament.NONE);
+        }
+    }
+
     _labelText(workspaceIndex) {
         if (workspaceIndex == undefined) {
             workspaceIndex = this._currentWorkspace;
-- 
2.21.0


From 83ad6a01f58ae451cc3086a01cdfd500eaa536d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sun, 9 Jun 2019 23:17:35 +0000
Subject: [PATCH 21/22] workspace-indicator: Minor cleanup

Pass the style class at construction time instead of setting it later.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/71
---
 extensions/workspace-indicator/extension.js | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
index 1961b1c..18f5450 100644
--- a/extensions/workspace-indicator/extension.js
+++ b/extensions/workspace-indicator/extension.js
@@ -24,6 +24,7 @@ class WorkspaceIndicator extends PanelMenu.Button {
 
         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()
         });
@@ -44,9 +45,6 @@ class WorkspaceIndicator extends PanelMenu.Button {
         this.connect('scroll-event', this._onScrollEvent.bind(this));
         this._createWorkspacesSection();
 
-        //styling
-        this._statusLabel.add_style_class_name('panel-workspace-indicator');
-
         this._settings = new Gio.Settings({ schema_id: WORKSPACE_SCHEMA });
         this._settingsChangedId = this._settings.connect(
             `changed::${WORKSPACE_KEY}`,
-- 
2.21.0


From 4e128c9cbc631fa47f9e5959212c35d9c6d53a01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sun, 9 Jun 2019 23:45:24 +0000
Subject: [PATCH 22/22] workspace-indicator: Support horizontal workspace
 layout

Just like we did for the workspace indicator in the window-list, improve
the handling of horizontal workspace layouts by showing the switcher
in-place instead of delegating the functionality to a menu.

https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/71
---
 extensions/workspace-indicator/extension.js   | 63 ++++++++++++++++++-
 extensions/workspace-indicator/stylesheet.css | 18 +++++-
 2 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
index 18f5450..5f7cb7c 100644
--- a/extensions/workspace-indicator/extension.js
+++ b/extensions/workspace-indicator/extension.js
@@ -20,6 +20,13 @@ class WorkspaceIndicator extends PanelMenu.Button {
     _init() {
         super._init(0.0, _('Workspace Indicator'));
 
+        let container = new St.Widget({
+            layout_manager: new Clutter.BinLayout(),
+            x_expand: true,
+            y_expand: true
+        });
+        this.add_actor(container);
+
         let workspaceManager = global.workspace_manager;
 
         this._currentWorkspace = workspaceManager.get_active_workspace_index();
@@ -29,7 +36,15 @@ class WorkspaceIndicator extends PanelMenu.Button {
             text: this._labelText()
         });
 
-        this.add_actor(this._statusLabel);
+        container.add_actor(this._statusLabel);
+
+        this._thumbnailsBox = new St.BoxLayout({
+            style_class: 'panel-workspace-indicator-box',
+            y_expand: true,
+            reactive: true
+        });
+
+        container.add_actor(this._thumbnailsBox);
 
         this._workspacesItems = [];
         this._workspaceSection = new PopupMenu.PopupMenuSection();
@@ -39,11 +54,16 @@ class WorkspaceIndicator extends PanelMenu.Button {
             workspaceManager.connect_after('notify::n-workspaces',
                 this._nWorkspacesChanged.bind(this)),
             workspaceManager.connect_after('workspace-switched',
-                this._onWorkspaceSwitched.bind(this))
+                this._onWorkspaceSwitched.bind(this)),
+            workspaceManager.connect('notify::layout-rows',
+                this._onWorkspaceOrientationChanged.bind(this))
         ];
 
         this.connect('scroll-event', this._onScrollEvent.bind(this));
+        this._thumbnailsBox.connect('scroll-event', this._onScrollEvent.bind(this));
         this._createWorkspacesSection();
+        this._updateThumbnails();
+        this._onWorkspaceOrientationChanged();
 
         this._settings = new Gio.Settings({ schema_id: WORKSPACE_SCHEMA });
         this._settingsChangedId = this._settings.connect(
@@ -63,16 +83,26 @@ class WorkspaceIndicator extends PanelMenu.Button {
         super._onDestroy();
     }
 
+    _onWorkspaceOrientationChanged() {
+        let vertical = global.workspace_manager.layout_rows == -1;
+        this.reactive = vertical;
+
+        this._statusLabel.visible = vertical;
+        this._thumbnailsBox.visible = !vertical;
+    }
+
     _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();
     }
 
     _updateMenuOrnament() {
@@ -83,6 +113,16 @@ 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');
+        }
+    }
+
     _labelText(workspaceIndex) {
         if (workspaceIndex == undefined) {
             workspaceIndex = this._currentWorkspace;
@@ -120,6 +160,25 @@ class WorkspaceIndicator extends PanelMenu.Button {
         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 St.Button({
+                style_class: 'workspace',
+                child: new St.Label({ text: `${i + 1}` })
+            });
+            thumb.connect('clicked', () => {
+                this._activate(i);
+            });
+
+            this._thumbnailsBox.add_actor(thumb);
+        }
+        this._updateActiveThumbnail();
+    }
+
     _activate(index) {
         let workspaceManager = global.workspace_manager;
 
diff --git a/extensions/workspace-indicator/stylesheet.css b/extensions/workspace-indicator/stylesheet.css
index 1271f1c..a15081e 100644
--- a/extensions/workspace-indicator/stylesheet.css
+++ b/extensions/workspace-indicator/stylesheet.css
@@ -1,5 +1,19 @@
-.panel-workspace-indicator {
+.panel-workspace-indicator,
+.panel-workspace-indicator-box .workspace {
 	padding: 0 8px;
-	background-color: rgba(200, 200, 200, .5);
 	border: 1px solid #cccccc;
 }
+
+.panel-workspace-indicator,
+.panel-workspace-indicator-box .workspace.active {
+	background-color: rgba(200, 200, 200, .5);
+}
+
+.panel-workspace-indicator-box .workspace {
+    background-color: rgba(200, 200, 200, .3);
+    border-left-width: 0;
+}
+
+.panel-workspace-indicator-box .workspace:first-child {
+    border-left-width: 1px;
+}
-- 
2.21.0