diff --git a/SOURCES/0001-A-couple-of-more-backport-fixes.patch b/SOURCES/0001-A-couple-of-more-backport-fixes.patch new file mode 100644 index 0000000..c67a99e --- /dev/null +++ b/SOURCES/0001-A-couple-of-more-backport-fixes.patch @@ -0,0 +1,39 @@ +From 269f2433b79668143e601f88af74cac0bbe2d77f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 3 Dec 2019 15:51:06 +0100 +Subject: [PATCH] A couple of more backport fixes + +--- + extensions/horizontal-workspaces/extension.js | 2 +- + extensions/window-list/windowPicker.js | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/extensions/horizontal-workspaces/extension.js b/extensions/horizontal-workspaces/extension.js +index bade48b..c9a3d13 100644 +--- a/extensions/horizontal-workspaces/extension.js ++++ b/extensions/horizontal-workspaces/extension.js +@@ -17,7 +17,7 @@ class Extension { + -1); + + ThumbnailsBox.prototype._updateSwitcherVisibility = function() { +- this.hide(); ++ this.actor.hide(); + }; + } + +diff --git a/extensions/window-list/windowPicker.js b/extensions/window-list/windowPicker.js +index 461a4d1..4895ae5 100644 +--- a/extensions/window-list/windowPicker.js ++++ b/extensions/window-list/windowPicker.js +@@ -199,7 +199,7 @@ var WindowPicker = class { + }); + + if (!this._modal) { +- this.hide(); ++ this.actor.hide(); + return false; + } + } else if (this._modal) { +-- +2.23.0 + diff --git a/SOURCES/0001-Add-extra-osk-keys-extension.patch b/SOURCES/0001-Add-extra-osk-keys-extension.patch new file mode 100644 index 0000000..27e754a --- /dev/null +++ b/SOURCES/0001-Add-extra-osk-keys-extension.patch @@ -0,0 +1,309 @@ +From 2ebc78ce92ae615d36ae80420b737b6f1583c7c8 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Fri, 9 Aug 2019 14:46:44 +0200 +Subject: [PATCH] Add extra-osk-keys extension + +Originally improveosk by Simon Schumann, +https://extensions.gnome.org/extension/1631/improve-onscreen-keyboard/ +--- + extensions/extra-osk-keys/extension.js | 230 +++++++++++++++++++++ + extensions/extra-osk-keys/meson.build | 5 + + extensions/extra-osk-keys/metadata.json.in | 10 + + extensions/extra-osk-keys/stylesheet.css | 6 + + meson.build | 1 + + 5 files changed, 252 insertions(+) + create mode 100644 extensions/extra-osk-keys/extension.js + create mode 100644 extensions/extra-osk-keys/meson.build + create mode 100644 extensions/extra-osk-keys/metadata.json.in + create mode 100644 extensions/extra-osk-keys/stylesheet.css + +diff --git a/extensions/extra-osk-keys/extension.js b/extensions/extra-osk-keys/extension.js +new file mode 100644 +index 0000000..23221d7 +--- /dev/null ++++ b/extensions/extra-osk-keys/extension.js +@@ -0,0 +1,230 @@ ++ ++const St = imports.gi.St; ++const Main = imports.ui.main; ++const Keyboard = imports.ui.keyboard; ++const EdgeDragAction = imports.ui.edgeDragAction; ++const Shell = imports.gi.Shell; ++const Lang = imports.lang; ++const Clutter = imports.gi.Clutter; ++const Workspace = imports.ui.workspace ++const Tweener = imports.ui.tweener; ++const Overview = imports.ui.overview; ++const Layout = imports.ui.layout; ++ ++var defaultKeyboardDelay; ++var Backup_DefaultKeysForRow; ++var Backup_contructor; ++var Backup_keyvalPress; ++var Backup_keyvalRelease; ++ ++ ++function init() { ++ defaultKeyboardDelay = Layout.KEYBOARD_ANIMATION_TIME; ++ Backup_DefaultKeysForRow = Keyboard.Keyboard.prototype['_getDefaultKeysForRow']; ++ Backup_contructor = Keyboard.KeyboardController.prototype['constructor']; ++ Backup_keyvalPress = Keyboard.KeyboardController.prototype['keyvalPress']; ++ Backup_keyvalRelease = Keyboard.KeyboardController.prototype['keyvalRelease']; ++} ++ ++function enable() { ++ Main.layoutManager.removeChrome(Main.layoutManager.keyboardBox); ++ ++ var KeyboardIsSetup = true; ++ try { ++ Main.keyboard._destroyKeyboard(); ++ } catch (e) { ++ if(e instanceof TypeError) { ++ // In case the keyboard is currently disabled in accessability settings, attempting to _destroyKeyboard() yields a TypeError ("TypeError: this.actor is null") ++ // This doesn't affect functionality, so proceed as usual. The only difference is that we do not automatically _setupKeyboard at the end of this enable() (let the user enable the keyboard in accessability settings) ++ KeyboardIsSetup = false; ++ } else { ++ // Something different happened ++ throw e; ++ } ++ } ++ ++ ++ Keyboard.Keyboard.prototype['_getDefaultKeysForRow'] = function(row, numRows, level) { ++ ++ let defaultKeysPreMod = [ ++ [ [{ label: 'Esc', width: 1, keyval: Clutter.KEY_Escape }], [{ label: '↹', width: 1.5, keyval: Clutter.KEY_Tab }], [{ width: 1.5, level: 1, extraClassName: 'shift-key-lowercase' }], [{ label: 'Ctrl', width: 1, keyval: Clutter.KEY_Control_L, extraClassName: 'control-key' }, { label: '◆', width: 1, keyval: Clutter.KEY_Super_L, extraClassName: 'super-key' }, { label: 'Alt', width: 1, keyval: Clutter.KEY_Alt_L, extraClassName: 'alt-key' }] ], ++ [ [{ label: 'Esc', width: 1, keyval: Clutter.KEY_Escape }], [{ label: '↹', width: 1.5, keyval: Clutter.KEY_Tab }], [{ width: 1.5, level: 0, extraClassName: 'shift-key-uppercase' }], [{ label: 'Ctrl', width: 1, keyval: Clutter.KEY_Control_L, extraClassName: 'control-key' }, { label: '◆', width: 1, keyval: Clutter.KEY_Super_L, extraClassName: 'super-key' }, { label: 'Alt', width: 1, keyval: Clutter.KEY_Alt_L, extraClassName: 'alt-key' }] ], ++ [ [{ label: 'Esc', width: 1, keyval: Clutter.KEY_Escape }], [{ label: '↹', width: 1.5, keyval: Clutter.KEY_Tab }], [{ label: '=/= numRows - 2) { ++ let defaultRow = row - (numRows - 2) + 2; ++ return [defaultKeysPreMod[level][defaultRow], defaultKeysPostMod[level][defaultRow]]; ++ } else { ++ return [null, null]; ++ } ++ } ++ ++ Keyboard.KeyboardController.prototype['constructor'] = function() { ++ let deviceManager = Clutter.DeviceManager.get_default(); ++ this._virtualDevice = deviceManager.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE); ++ ++ this._inputSourceManager = InputSourceManager.getInputSourceManager(); ++ this._sourceChangedId = this._inputSourceManager.connect('current-source-changed', ++ this._onSourceChanged.bind(this)); ++ this._sourcesModifiedId = this._inputSourceManager.connect ('sources-changed', ++ this._onSourcesModified.bind(this)); ++ this._currentSource = this._inputSourceManager.currentSource; ++ ++ this._controlActive = false; ++ this._superActive = false; ++ this._altActive = false; ++ ++ Main.inputMethod.connect('notify::content-purpose', ++ this._onContentPurposeHintsChanged.bind(this)); ++ Main.inputMethod.connect('notify::content-hints', ++ this._onContentPurposeHintsChanged.bind(this)); ++ Main.inputMethod.connect('input-panel-state', (o, state) => { ++ this.emit('panel-state', state); ++ }); ++ } ++ ++ ++ Keyboard.KeyboardController.prototype['keyvalPress'] = function(keyval) { ++ if(keyval==Clutter.KEY_Control_L) { ++ this._controlActive = !this._controlActive; // This allows to revert an accidental tap on Ctrl by tapping on it again ++ } ++ if(keyval==Clutter.KEY_Super_L) { ++ this._superActive = !this._superActive; ++ } ++ if(keyval==Clutter.KEY_Alt_L) { ++ this._altActive = !this._altActive; ++ } ++ ++ if(this._controlActive) ++ { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Control_L, Clutter.KeyState.PRESSED); ++ Main.layoutManager.keyboardBox.add_style_class_name("control-key-latched"); ++ } else { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Control_L, Clutter.KeyState.RELEASED); ++ Main.layoutManager.keyboardBox.remove_style_class_name("control-key-latched"); ++ } ++ if(this._superActive) ++ { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Super_L, Clutter.KeyState.PRESSED); ++ Main.layoutManager.keyboardBox.add_style_class_name("super-key-latched"); ++ } else { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Super_L, Clutter.KeyState.RELEASED); ++ Main.layoutManager.keyboardBox.remove_style_class_name("super-key-latched"); ++ } ++ if(this._altActive) ++ { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Alt_L, Clutter.KeyState.PRESSED); ++ Main.layoutManager.keyboardBox.add_style_class_name("alt-key-latched"); ++ } else { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Alt_L, Clutter.KeyState.RELEASED); ++ Main.layoutManager.keyboardBox.remove_style_class_name("alt-key-latched"); ++ } ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ keyval, Clutter.KeyState.PRESSED); ++ } ++ ++ ++ Keyboard.KeyboardController.prototype['keyvalRelease'] = function(keyval) { ++ if(keyval==Clutter.KEY_Control_L || keyval==Clutter.KEY_Alt_L || keyval==Clutter.KEY_Super_L) { ++ return; ++ } ++ ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ keyval, Clutter.KeyState.RELEASED); ++ ++ if(this._controlActive) ++ { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Control_L, Clutter.KeyState.RELEASED); ++ this._controlActive = false; ++ Main.layoutManager.keyboardBox.remove_style_class_name("control-key-latched"); ++ } ++ if(this._superActive) ++ { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Super_L, Clutter.KeyState.RELEASED); ++ this._superActive = false; ++ Main.layoutManager.keyboardBox.remove_style_class_name("super-key-latched"); ++ } ++ if(this._altActive) ++ { ++ this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), ++ Clutter.KEY_Alt_L, Clutter.KeyState.RELEASED); ++ this._altActive = false; ++ Main.layoutManager.keyboardBox.remove_style_class_name("alt-key-latched"); ++ } ++ ++ } ++ ++ Layout.KEYBOARD_ANIMATION_TIME = 0; ++ if(KeyboardIsSetup) { ++ Main.keyboard._setupKeyboard(); ++ } ++ Main.layoutManager.addChrome( Main.layoutManager.keyboardBox, { affectsStruts: true, trackFullscreen: false }); ++} ++ ++function disable() { ++ Main.layoutManager.removeChrome(Main.layoutManager.keyboardBox); ++ ++ var KeyboardIsSetup = true; ++ try { ++ Main.keyboard._destroyKeyboard(); ++ } catch (e) { ++ if(e instanceof TypeError) { ++ // In case the keyboard is currently disabled in accessability settings, attempting to _destroyKeyboard() yields a TypeError ("TypeError: this.actor is null") ++ // This doesn't affect functionality, so proceed as usual. The only difference is that we do not automatically _setupKeyboard at the end of this enable() (let the user enable the keyboard in accessability settings) ++ KeyboardIsSetup = false; ++ } else { ++ // Something different happened ++ throw e; ++ } ++ } ++ ++ Keyboard.Keyboard.prototype['_getDefaultKeysForRow'] = Backup_DefaultKeysForRow; ++ Keyboard.KeyboardController.prototype['constructor'] = Backup_contructor; ++ Keyboard.KeyboardController.prototype['keyvalPress'] = Backup_keyvalPress; ++ Keyboard.KeyboardController.prototype['keyvalRelease'] = Backup_keyvalRelease; ++ Layout.KEYBOARD_ANIMATION_TIME = defaultKeyboardDelay; ++ if(KeyboardIsSetup) { ++ Main.keyboard._setupKeyboard(); ++ } ++ Main.layoutManager.addChrome( Main.layoutManager.keyboardBox); ++} +diff --git a/extensions/extra-osk-keys/meson.build b/extensions/extra-osk-keys/meson.build +new file mode 100644 +index 0000000..48504f6 +--- /dev/null ++++ b/extensions/extra-osk-keys/meson.build +@@ -0,0 +1,5 @@ ++extension_data += configure_file( ++ input: metadata_name + '.in', ++ output: metadata_name, ++ configuration: metadata_conf ++) +diff --git a/extensions/extra-osk-keys/metadata.json.in b/extensions/extra-osk-keys/metadata.json.in +new file mode 100644 +index 0000000..39dfdd3 +--- /dev/null ++++ b/extensions/extra-osk-keys/metadata.json.in +@@ -0,0 +1,10 @@ ++{ ++"extension-id": "@extension_id@", ++"uuid": "@uuid@", ++"gettext-domain": "@gettext_domain@", ++"name": "Extra Onscreen Keyboard Keys", ++"description": "Adds extra keys to gnome-shell onscreen keyboard.", ++"original-authors": [ "simon.schumann@web.de" ], ++"shell-version": [ "@shell_current@" ], ++"url": "@url@" ++} +diff --git a/extensions/extra-osk-keys/stylesheet.css b/extensions/extra-osk-keys/stylesheet.css +new file mode 100644 +index 0000000..010cfb3 +--- /dev/null ++++ b/extensions/extra-osk-keys/stylesheet.css +@@ -0,0 +1,6 @@ ++.control-key-latched .control-key, ++.super-key-latched .super-key, ++.alt-key-latched .alt-key { ++ background-color: #55a54c; ++} ++ +diff --git a/meson.build b/meson.build +index df2be33..fa4aa9c 100644 +--- a/meson.build ++++ b/meson.build +@@ -56,6 +56,7 @@ all_extensions += [ + 'dash-to-dock', + 'disable-screenshield', + 'example', ++ 'extra-osk-keys', + 'native-window-placement', + 'no-hot-corner', + 'panel-favorites', +-- +2.23.0.rc1 + diff --git a/SOURCES/0001-Update-style.patch b/SOURCES/0001-Update-style.patch index 091f008..aa62709 100644 --- a/SOURCES/0001-Update-style.patch +++ b/SOURCES/0001-Update-style.patch @@ -8,10 +8,20 @@ Subject: [PATCH] Update style 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/data/gnome-shell-sass/_common.scss b/data/gnome-shell-sass/_common.scss -index 2f05098df..de3a9cdbc 100644 +index f83b0d8..ac493ff 100644 --- a/data/gnome-shell-sass/_common.scss +++ b/data/gnome-shell-sass/_common.scss -@@ -776,6 +776,11 @@ StScrollBar { +@@ -683,7 +683,8 @@ StScrollBar { + spacing: 8px; + } + +- .ws-switcher-active-up, .ws-switcher-active-down { ++ .ws-switcher-active-up, .ws-switcher-active-down, ++ .ws-switcher-active-left, .ws-switcher-active-right { + height: 50px; + background-color: $selected_bg_color; + color: $selected_fg_color; +@@ -776,6 +777,11 @@ StScrollBar { //dimensions of the icon are hardcoded } @@ -23,7 +33,16 @@ index 2f05098df..de3a9cdbc 100644 .system-status-icon, .app-menu-icon > StIcon, .popup-menu-arrow { -@@ -1397,6 +1402,14 @@ StScrollBar { +@@ -1111,7 +1117,7 @@ StScrollBar { + .aggregate-menu { + min-width: 21em; + .popup-menu-icon { padding: 0 4px; } +- .popup-sub-menu .popup-menu-item :first-child { ++ .popup-sub-menu .popup-menu-item > :first-child { + &:ltr { /* 12px spacing + 2*4px padding */ + padding-left: 20px; margin-left: 1.09em; } + &:rtl { /* 12px spacing + 2*4px padding */ +@@ -1397,6 +1403,14 @@ StScrollBar { } @@ -38,7 +57,7 @@ index 2f05098df..de3a9cdbc 100644 .app-well-app-running-dot { //running apps indicator width: 10px; height: 3px; background-color: $selected_bg_color; -@@ -1769,7 +1782,12 @@ StScrollBar { +@@ -1769,7 +1783,12 @@ StScrollBar { .login-dialog-banner { color: darken($osd_fg_color,10%); } .login-dialog-button-box { spacing: 5px; } .login-dialog-message-warning { color: $warning_color; } @@ -52,7 +71,7 @@ index 2f05098df..de3a9cdbc 100644 .login-dialog-user-selection-box { padding: 100px 0px; } .login-dialog-not-listed-label { padding-left: 2px; -@@ -1825,6 +1843,10 @@ StScrollBar { +@@ -1825,6 +1844,10 @@ StScrollBar { padding-bottom: 12px; spacing: 8px; width: 23em; @@ -63,3 +82,6 @@ index 2f05098df..de3a9cdbc 100644 } .login-dialog-prompt-label { +-- +2.21.0 + diff --git a/SOURCES/0001-classic-Shade-panel-in-overview.patch b/SOURCES/0001-classic-Shade-panel-in-overview.patch deleted file mode 100644 index 2d943e4..0000000 --- a/SOURCES/0001-classic-Shade-panel-in-overview.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 91ed30147a69d53d7c170b65602be5f90851666e Mon Sep 17 00:00:00 2001 -From: Jakub Steiner -Date: Tue, 14 Jan 2014 17:00:23 +0100 -Subject: [PATCH] classic: Shade panel in overview - -... rather than using the top bar styling (negative space), -base the overview panel on the classic grey and "darken" -for overview. ---- - data/gnome-classic.scss | 8 +++----- - 1 file changed, 3 insertions(+), 5 deletions(-) - -diff --git a/data/gnome-classic.scss b/data/gnome-classic.scss -index 9e23506..e8f4803 100644 ---- a/data/gnome-classic.scss -+++ b/data/gnome-classic.scss -@@ -19,11 +19,9 @@ $variant: 'light'; - border-bottom: 1px solid #666; - app-icon-bottom-clip: 0px; - &:overview { -- background-color: #000; -- background-gradient-end: #000; -- border-top-color: #000; -- border-bottom: 1px solid #000; -- .panel-button { color: #fff; } -+ background-color: darken($bg_color,5%); -+ background-gradient-end: darken($bg_color,10%); -+ .panel-button { color: darken($fg_color,5%); } - } - - .panel-button { --- -2.17.1 - diff --git a/SOURCES/0001-window-list-workspace-indicator-Set-reactiveness-of-.patch b/SOURCES/0001-window-list-workspace-indicator-Set-reactiveness-of-.patch new file mode 100644 index 0000000..1853ae4 --- /dev/null +++ b/SOURCES/0001-window-list-workspace-indicator-Set-reactiveness-of-.patch @@ -0,0 +1,31 @@ +From 4e8db57a1c6a205e50b864ed9bebc9a25cfc12ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 25 Sep 2019 10:45:29 +0200 +Subject: [PATCH] window-list/workspace-indicator: Set reactiveness of actual + actor + +Setting this.reactive on the workspace indicator instance doesn't change +the reactiveness of the corresponding clutter actor, as this is the +GNOME 3.28 backport where the actor is wrapped in ShellGenericContainer. + +Fix this by instead setting this.actor.reactive. +--- + extensions/window-list/workspaceIndicator.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js +index c0dd65d..f19d1c5 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -273,7 +273,7 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + + _onWorkspaceOrientationChanged() { + let vertical = global.screen.layout_rows == -1; +- this.reactive = vertical; ++ this.actor.reactive = vertical; + + this._statusBin.visible = vertical; + this._thumbnailsBox.visible = !vertical; +-- +2.21.0 + diff --git a/SOURCES/more-classic-classic-mode.patch b/SOURCES/more-classic-classic-mode.patch new file mode 100644 index 0000000..2e60b40 --- /dev/null +++ b/SOURCES/more-classic-classic-mode.patch @@ -0,0 +1,3313 @@ +From 05833ca853e5e661cf43f59734ca0a29219159fd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 18 Jul 2019 00:39:49 +0200 +Subject: [PATCH 01/33] apps-menu: Add drop-shadow to application icons + +... to make sure they are readable on light backgrounds. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/issues/168 +--- + extensions/apps-menu/extension.js | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js +index 49a05c7..860cb77 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -112,7 +112,9 @@ class ApplicationMenuItem extends PopupMenu.PopupBaseMenuItem { + } + + _updateIcon() { +- this._iconBin.set_child(this.getDragActor()); ++ let icon = this.getDragActor(); ++ icon.style_class = 'icon-dropshadow'; ++ this._iconBin.set_child(icon); + } + }; + +-- +2.21.0 + + +From 4ace6d5da8e0edc590706af8afb8cfc843a06af4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 29 May 2019 10:17:20 +0000 +Subject: [PATCH 02/33] 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 429e81d..cdecb7b 100644 +--- a/extensions/places-menu/extension.js ++++ b/extensions/places-menu/extension.js +@@ -133,9 +133,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 829929350bda3a03795c43d303273a7c9ff225b3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 27 Jun 2019 03:57:53 +0200 +Subject: [PATCH 03/33] apps-menu: Add missing chain-up + +PanelMenu.Button is a bit weird in that it also "contains" its parent +actor. That container is supposed to be destroyed with the button, but +as we currently don't chain up to the parent class' _onDestroy(), we +leave behind an empty container every time the extension is disabled. + +Fix this by adding the missing chain-up. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/75 +--- + 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 860cb77..08eda10 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -491,6 +491,8 @@ class ApplicationsButton extends PanelMenu.Button { + } + + _onDestroy() { ++ super._onDestroy(); ++ + Main.overview.disconnect(this._showingId); + Main.overview.disconnect(this._hidingId); + appSys.disconnect(this._installedChangedId); +-- +2.21.0 + + +From 83e93a115f7375991893877ea82f2409569fee0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 29 May 2019 08:32:03 +0000 +Subject: [PATCH 04/33] 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 | 63 +++---------------------------- + 1 file changed, 6 insertions(+), 57 deletions(-) + +diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js +index 08eda10..9e02946 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -30,22 +30,6 @@ const HORIZ_FACTOR = 5; + const MENU_HEIGHT_OFFSET = 132; + const NAVIGATION_REGION_OVERSHOOT = 50; + +-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(); +@@ -244,21 +228,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, null); +@@ -407,7 +376,7 @@ class DesktopTarget { + Signals.addSignalMethods(DesktopTarget.prototype); + + class ApplicationsButton extends PanelMenu.Button { +- constructor() { ++ constructor(includeIcon) { + super(1.0, null, false); + + this.setMenu(new ApplicationsMenu(this.actor, 1.0, St.Side.TOP, this)); +@@ -422,7 +391,8 @@ class ApplicationsButton extends PanelMenu.Button { + + let iconFile = Gio.File.new_for_path('/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); + + this._label = new St.Label({ text: _("Applications"), +@@ -435,7 +405,6 @@ class ApplicationsButton extends PanelMenu.Button { + this.actor.name = 'panelApplications'; + this.actor.label_actor = this._label; + +- this.actor.connect('captured-event', this._onCapturedEvent.bind(this)); + this.actor.connect('destroy', this._onDestroy.bind(this)); + + this._showingId = Main.overview.connect('showing', () => { +@@ -479,10 +448,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', + pseudo_class: 'highlighted' }); +@@ -509,14 +474,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) { +@@ -651,11 +608,6 @@ class ApplicationsButton extends PanelMenu.Button { + x_fill: true, y_fill: true, + 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 }); +@@ -760,19 +712,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(metadata) { +-- +2.21.0 + + +From ea66ad17fe7b5d1a8071a6aa693603be5e62ae27 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 7 Jun 2019 14:30:16 +0000 +Subject: [PATCH 05/33] 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 9e02946..50fe55a 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -229,12 +229,8 @@ class ApplicationsMenu extends PopupMenu.PopupMenu { + } + + toggle() { +- if (this.isOpen) { ++ if (this.isOpen) + this._button.selectCategory(null, null); +- } else { +- if (Main.overview.visible) +- Main.overview.hide(); +- } + super.toggle(); + } + }; +-- +2.21.0 + + +From 8b850a7f9b17cafbd417f597650a2a13cf8cfefa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 7 Jun 2019 20:07:19 +0000 +Subject: [PATCH 06/33] 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 50fe55a..8d59e6d 100644 +--- a/extensions/apps-menu/extension.js ++++ b/extensions/apps-menu/extension.js +@@ -75,6 +75,8 @@ class ApplicationMenuItem extends PopupMenu.PopupBaseMenuItem { + this._button.selectCategory(null, null); + this._button.menu.toggle(); + super.activate(event); ++ ++ Main.overview.hide(); + } + + setActive(active, params) { +-- +2.21.0 + + +From 9762c3507177fe3a420bb228549aca03757d73c5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 29 May 2019 09:44:30 +0000 +Subject: [PATCH 07/33] 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 1fee42083afafef861fc57e5d886e4e00945a19c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 14 May 2019 19:51:22 +0200 +Subject: [PATCH 08/33] 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 | 260 +++++++++++++++++++++++++ + 5 files changed, 332 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 716a324..f4610f9 100644 +--- a/extensions/window-list/extension.js ++++ b/extensions/window-list/extension.js +@@ -8,12 +8,15 @@ const St = imports.gi.St; + + const DND = imports.ui.dnd; + const Main = imports.ui.main; ++const Overview = imports.ui.overview; + const PanelMenu = imports.ui.panelMenu; + const PopupMenu = imports.ui.popupMenu; ++const Tweener = imports.ui.tweener; + + const ExtensionUtils = imports.misc.extensionUtils; + const Me = ExtensionUtils.getCurrentExtension(); + const Convenience = Me.imports.convenience; ++const { WindowPicker, WindowPickerToggle } = Me.imports.windowPicker; + + const Gettext = imports.gettext.domain('gnome-shell-extensions'); + const _ = Gettext.gettext; +@@ -769,6 +772,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', + reactive: true, +@@ -931,6 +940,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]; +@@ -1205,7 +1227,7 @@ class WindowList { + class Extension { + constructor() { + this._windowLists = null; +- this._injections = {}; ++ this._hideOverviewOrig = Main.overview.hide; + } + + enable() { +@@ -1220,6 +1242,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(); + } + +@@ -1250,6 +1279,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 b4aa4db..5b1f5f5 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', 'windowPicker.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 f5285cb..91383ab 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..59875d5 +--- /dev/null ++++ b/extensions/window-list/windowPicker.js +@@ -0,0 +1,260 @@ ++/* 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.screen.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.screen.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._overlayKeyId = 0; ++ ++ this.connect('destroy', this._onDestroy.bind(this)); ++ ++ this.connect('notify::checked', () => { ++ if (this.checked) ++ Main.windowPicker.open(); ++ else ++ Main.windowPicker.close(); ++ }); ++ ++ if (!Main.sessionMode.hasOverview) { ++ this._overlayKeyId = 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; ++ }); ++ } ++ ++ _onDestroy() { ++ if (this._overlayKeyId) ++ global.display.disconnect(this._overlayKeyId); ++ this._overlayKeyId == 0; ++ } ++}); +-- +2.21.0 + + +From ea3f6be47066e1e4a2f86824576a17bc09bcd39d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sat, 29 Jun 2019 02:52:45 +0200 +Subject: [PATCH 09/33] window-list: Fix resetting handler ID + +This is embarrassing, although destroy() is expected to only run once, +so the bug shouldn't have an effect in practice. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/78 +--- + extensions/window-list/windowPicker.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/extensions/window-list/windowPicker.js b/extensions/window-list/windowPicker.js +index 59875d5..c8a3dd6 100644 +--- a/extensions/window-list/windowPicker.js ++++ b/extensions/window-list/windowPicker.js +@@ -255,6 +255,6 @@ class WindowPickerToggle extends St.Button { + _onDestroy() { + if (this._overlayKeyId) + global.display.disconnect(this._overlayKeyId); +- this._overlayKeyId == 0; ++ this._overlayKeyId = 0; + } + }); +-- +2.21.0 + + +From eaa96905569de6640bd59cb89aa47fcdb8f486c4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 2 Jul 2019 17:39:55 +0200 +Subject: [PATCH 10/33] window-list: Move super-key handling into WindowPicker + +We have an option to put a window list on each monitor, so we may have +more than one window picker toggle. We don't want each of those try to +toggle the window picker simultanuously, so move handling of the super +key directly into the picker. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/80 +--- + extensions/window-list/windowPicker.js | 34 ++++++++++++-------------- + 1 file changed, 15 insertions(+), 19 deletions(-) + +diff --git a/extensions/window-list/windowPicker.js b/extensions/window-list/windowPicker.js +index c8a3dd6..088ebf2 100644 +--- a/extensions/window-list/windowPicker.js ++++ b/extensions/window-list/windowPicker.js +@@ -67,6 +67,8 @@ var WindowPicker = class { + this._visible = false; + this._modal = false; + ++ this._overlayKeyId = 0; ++ + this.actor = new Clutter.Actor(); + + this.actor.connect('destroy', this._onDestroy.bind(this)); +@@ -101,6 +103,15 @@ var WindowPicker = class { + this._updateBackgrounds(); + + Main.uiGroup.insert_child_below(this.actor, global.window_group); ++ ++ if (!Main.sessionMode.hasOverview) { ++ this._overlayKeyId = global.display.connect('overlay-key', () => { ++ if (!this._visible) ++ this.open(); ++ else ++ this.close(); ++ }); ++ } + } + + get visible() { +@@ -188,6 +199,10 @@ var WindowPicker = class { + if (this._monitorsChangedId) + Main.layoutManager.disconnect(this._monitorsChangedId); + this._monitorsChangedId = 0; ++ ++ if (this._overlayKeyId) ++ global.display.disconnect(this._overlayKeyId); ++ this._overlayKeyId = 0; + } + + _updateBackgrounds() { +@@ -227,10 +242,6 @@ class WindowPickerToggle extends St.Button { + toggle_mode: true + }); + +- this._overlayKeyId = 0; +- +- this.connect('destroy', this._onDestroy.bind(this)); +- + this.connect('notify::checked', () => { + if (this.checked) + Main.windowPicker.open(); +@@ -238,23 +249,8 @@ class WindowPickerToggle extends St.Button { + Main.windowPicker.close(); + }); + +- if (!Main.sessionMode.hasOverview) { +- this._overlayKeyId = 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; + }); + } +- +- _onDestroy() { +- if (this._overlayKeyId) +- global.display.disconnect(this._overlayKeyId); +- this._overlayKeyId = 0; +- } + }); +-- +2.21.0 + + +From 381290b9938339768501e34aee6f21d7040e9394 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 2 Jul 2019 18:12:31 +0200 +Subject: [PATCH 11/33] window-list: Handle closing window picker with Escape + +Just like the overview can be closed with Escape, it makes sense to +allow the same for the window picker (in addition to pressing super +repeatedly). + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/80 +--- + extensions/window-list/windowPicker.js | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/extensions/window-list/windowPicker.js b/extensions/window-list/windowPicker.js +index 088ebf2..461a4d1 100644 +--- a/extensions/window-list/windowPicker.js ++++ b/extensions/window-list/windowPicker.js +@@ -68,6 +68,7 @@ var WindowPicker = class { + this._modal = false; + + this._overlayKeyId = 0; ++ this._stageKeyPressId = 0; + + this.actor = new Clutter.Actor(); + +@@ -132,6 +133,16 @@ var WindowPicker = class { + this._fakeOverviewAnimation(); + this._workspacesDisplay.show(false); + ++ this._stageKeyPressId = global.stage.connect('key-press-event', ++ (a, event) => { ++ let sym = event.get_key_symbol(); ++ if (sym == Clutter.KEY_Escape) { ++ this.close(); ++ return Clutter.EVENT_STOP; ++ } ++ return Clutter.EVENT_PROPAGATE; ++ }); ++ + this.emit('open-state-changed', this._visible); + } + +@@ -151,6 +162,9 @@ var WindowPicker = class { + this._fakeOverviewVisible(false); + }); + ++ global.stage.disconnect(this._stageKeyPressId); ++ this._stageKeyPressId = 0; ++ + this.emit('open-state-changed', this._visible); + } + +@@ -203,6 +217,10 @@ var WindowPicker = class { + if (this._overlayKeyId) + global.display.disconnect(this._overlayKeyId); + this._overlayKeyId = 0; ++ ++ if (this._stageKeyPressId) ++ global.stage.disconnect(this._stageKeyPressId); ++ this._stageKeyPressId = 0; + } + + _updateBackgrounds() { +-- +2.21.0 + + +From 22a3bb1051ebda5cba050cdc95114673dae7dab8 Mon Sep 17 00:00:00 2001 +From: Jakub Steiner +Date: Mon, 15 Jul 2019 23:40:09 +0200 +Subject: [PATCH 12/33] classic: hover state for panel buttons + +- prelight before active +- lighten up slightly, similar to what the default does (inverted) + +Fixes https://gitlab.gnome.org/GNOME/gnome-shell-extensions/issues/169 +--- + data/gnome-classic.scss | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/data/gnome-classic.scss b/data/gnome-classic.scss +index e8f4803..a231534 100644 +--- a/data/gnome-classic.scss ++++ b/data/gnome-classic.scss +@@ -30,18 +30,20 @@ $variant: 'light'; + font-weight: normal; + color: $fg_color; + text-shadow: none; ++ &:hover { ++ color: lighten($fg_color,10%); ++ text-shadow: none; ++ & .system-status-icon { icon-shadow: none; } ++ } + &:active, &:overview, &:focus, &:checked { + // Trick due to St limitations. It needs a background to draw + // a box-shadow +- background-color: $selected_bg_color !important; +- color: $selected_fg_color !important; ++ background-color: $selected_bg_color; ++ color: $selected_fg_color; + box-shadow: none; + & > .system-status-icon { icon-shadow: none; } + } +- &:hover { +- text-shadow: none; +- & .system-status-icon { icon-shadow: none; } +- } ++ + .app-menu-icon { width: 0; height: 0; margin: 0; } // shell's display:none; :D + + .system-status-icon { +-- +2.21.0 + + +From da9d0ffad1b75413027f95c112be9d6a5e92ba78 Mon Sep 17 00:00:00 2001 +From: Jakub Steiner +Date: Mon, 15 Jul 2019 23:03:41 +0200 +Subject: [PATCH 13/33] classic: Update window-list styling + +Make buttons flatter, rounder to match default styling. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/82 +--- + extensions/window-list/classic.css | 25 +++++++++---------------- + 1 file changed, 9 insertions(+), 16 deletions(-) + +diff --git a/extensions/window-list/classic.css b/extensions/window-list/classic.css +index c506bea..cc967e0 100644 +--- a/extensions/window-list/classic.css ++++ b/extensions/window-list/classic.css +@@ -4,21 +4,18 @@ + border-top-width: 1px; + border-bottom-width: 0px; + height: 2.25em ; ++ padding: 2px; + } + + .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; + color: #2e3436; + background-color: #eee; +- border-radius: 2px; ++ border-radius: 3px; + padding: 3px 6px 1px; +- box-shadow: inset -1px -1px 1px rgba(0,0,0,0.5); +- text-shadow: 0 0 transparent; ++ box-shadow: none; ++ text-shadow: none; ++ border: 1px solid rgba(0,0,0,0.2); + } + + .bottom-panel .window-button > StWidget { +@@ -26,10 +23,6 @@ + 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; +@@ -37,13 +30,13 @@ + + .bottom-panel .window-button:active > StWidget, + .bottom-panel .window-button:focus > StWidget { +- box-shadow: inset 1px 1px 2px rgba(0,0,0,0.5); ++ box-shadow: inset 0 1px 3px rgba(0,0,0,0.1); + } + + .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); ++ background-color: #ccc; ++ box-shadow: inset 0 1px 3px rgba(0,0,0,0.1); + } + + .bottom-panel .window-button.focused:hover > StWidget { +@@ -52,5 +45,5 @@ + + .bottom-panel .window-button.minimized > StWidget { + color: #888; +- box-shadow: inset -1px -1px 1px rgba(0,0,0,0.5); ++ box-shadow: none; + } +-- +2.21.0 + + +From f9268e6d38e174d78a98f1e64734a00917724032 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 5 Jun 2019 00:23:13 +0000 +Subject: [PATCH 14/33] 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 | 115 +---------------- + extensions/window-list/meson.build | 2 +- + extensions/window-list/workspaceIndicator.js | 127 +++++++++++++++++++ + 3 files changed, 129 insertions(+), 115 deletions(-) + create mode 100644 extensions/window-list/workspaceIndicator.js + +diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js +index f4610f9..061421a 100644 +--- a/extensions/window-list/extension.js ++++ b/extensions/window-list/extension.js +@@ -9,7 +9,6 @@ const St = imports.gi.St; + const DND = imports.ui.dnd; + const Main = imports.ui.main; + const Overview = imports.ui.overview; +-const PanelMenu = imports.ui.panelMenu; + const PopupMenu = imports.ui.popupMenu; + const Tweener = imports.ui.tweener; + +@@ -17,6 +16,7 @@ const ExtensionUtils = imports.misc.extensionUtils; + const Me = ExtensionUtils.getCurrentExtension(); + const Convenience = Me.imports.convenience; + const { WindowPicker, WindowPickerToggle } = Me.imports.windowPicker; ++const { WorkspaceIndicator } = Me.imports.workspaceIndicator; + + const Gettext = imports.gettext.domain('gnome-shell-extensions'); + const _ = Gettext.gettext; +@@ -644,119 +644,6 @@ class AppButton extends BaseButton { + }; + + +-class WorkspaceIndicator extends PanelMenu.Button { +- constructor() { +- super(0.0, _("Workspace Indicator"), true); +- this.setMenu(new PopupMenu.PopupMenu(this.actor, 0.0, St.Side.BOTTOM)); +- this.actor.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.actor.add_actor(container); +- +- this._currentWorkspace = global.screen.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._screenSignals = []; +- this._screenSignals.push(global.screen.connect('notify::n-workspaces', +- this._updateMenu.bind(this))); +- this._screenSignals.push(global.screen.connect_after('workspace-switched', +- this._updateIndicator.bind(this))); +- +- this.actor.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)); +- } +- +- destroy() { +- for (let i = 0; i < this._screenSignals.length; i++) +- global.screen.disconnect(this._screenSignals[i]); +- +- if (this._settingsChangedId) { +- this._settings.disconnect(this._settingsChangedId); +- this._settingsChangedId = 0; +- } +- +- super.destroy(); +- } +- +- _updateIndicator() { +- this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE); +- this._currentWorkspace = global.screen.get_active_workspace().index(); +- this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT); +- +- this.statusLabel.set_text(this._getStatusText()); +- } +- +- _getStatusText() { +- let current = global.screen.get_active_workspace().index(); +- let total = global.screen.n_workspaces; +- +- return '%d / %d'.format(current + 1, total); +- } +- +- _updateMenu() { +- this.menu.removeAll(); +- this.workspacesItems = []; +- this._currentWorkspace = global.screen.get_active_workspace().index(); +- +- for(let i = 0; i < global.screen.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) { +- if(index >= 0 && index < global.screen.n_workspaces) { +- let metaWorkspace = global.screen.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 5b1f5f5..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', 'windowPicker.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/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js +new file mode 100644 +index 0000000..ac4037b +--- /dev/null ++++ b/extensions/window-list/workspaceIndicator.js +@@ -0,0 +1,127 @@ ++/* 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 = class WorkspaceIndicator extends PanelMenu.Button { ++ constructor() { ++ super(0.0, _('Workspace Indicator'), true); ++ this.setMenu(new PopupMenu.PopupMenu(this, 0.0, St.Side.BOTTOM)); ++ this.actor.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.actor.add_actor(container); ++ ++ this._currentWorkspace = global.screen.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._screenSignals = [ ++ global.screen.connect('notify::n-workspaces', ++ this._updateMenu.bind(this)), ++ global.screen.connect_after('workspace-switched', ++ this._updateIndicator.bind(this)) ++ ]; ++ ++ this.actor.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)); ++ } ++ ++ destroy() { ++ for (let i = 0; i < this._screenSignals.length; i++) ++ global.screen.disconnect(this._screenSignals[i]); ++ ++ if (this._settingsChangedId) { ++ this._settings.disconnect(this._settingsChangedId); ++ this._settingsChangedId = 0; ++ } ++ ++ super.destroy(); ++ } ++ ++ _updateIndicator() { ++ this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE); ++ this._currentWorkspace = global.screen.get_active_workspace().index(); ++ this.workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT); ++ ++ this.statusLabel.set_text(this._getStatusText()); ++ } ++ ++ _getStatusText() { ++ let current = global.screen.get_active_workspace().index(); ++ let total = global.screen.n_workspaces; ++ ++ return '%d / %d'.format(current + 1, total); ++ } ++ ++ _updateMenu() { ++ this.menu.removeAll(); ++ this.workspacesItems = []; ++ this._currentWorkspace = global.screen.get_active_workspace().index(); ++ ++ for (let i = 0; i < global.screen.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) { ++ if (index >= 0 && index < global.screen.n_workspaces) { ++ let metaWorkspace = global.screen.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 aa6079ff69f942420f806523463337fe81b23121 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 5 Jun 2019 04:54:50 +0200 +Subject: [PATCH 15/33] 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 ac4037b..cd3e394 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -22,14 +22,14 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + this.actor.add_actor(container); + + this._currentWorkspace = global.screen.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._screenSignals = [ + global.screen.connect('notify::n-workspaces', +@@ -59,11 +59,11 @@ var WorkspaceIndicator = 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.screen.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() { +@@ -75,7 +75,7 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + + _updateMenu() { + this.menu.removeAll(); +- this.workspacesItems = []; ++ this._workspacesItems = []; + this._currentWorkspace = global.screen.get_active_workspace().index(); + + for (let i = 0; i < global.screen.n_workspaces; i++) { +@@ -91,10 +91,10 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + 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 54f5bf585056267f63e2a28153ae709325afe9ca Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 5 Jun 2019 04:57:39 +0200 +Subject: [PATCH 16/33] 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 cd3e394..5157e6e 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -43,7 +43,7 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + + 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)); + } + + destroy() { +@@ -73,6 +73,14 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + 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() { + this.menu.removeAll(); + this._workspacesItems = []; +-- +2.21.0 + + +From a2fb1382b9ac7c50c556c1a41dc19ceca43bd320 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 5 Jun 2019 04:59:19 +0200 +Subject: [PATCH 17/33] 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 5157e6e..f9778f7 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -21,7 +21,7 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + }); + this.actor.add_actor(container); + +- this._currentWorkspace = global.screen.get_active_workspace().index(); ++ this._currentWorkspace = global.screen.get_active_workspace_index(); + this._statusLabel = new St.Label({ + text: this._getStatusText(), + x_align: Clutter.ActorAlign.CENTER, +@@ -60,14 +60,14 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + + _updateIndicator() { + this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE); +- this._currentWorkspace = global.screen.get_active_workspace().index(); ++ this._currentWorkspace = global.screen.get_active_workspace_index(); + this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT); + + this._statusLabel.set_text(this._getStatusText()); + } + + _getStatusText() { +- let current = global.screen.get_active_workspace().index(); ++ let current = global.screen.get_active_workspace_index(); + let total = global.screen.n_workspaces; + + return '%d / %d'.format(current + 1, total); +@@ -84,7 +84,7 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + _updateMenu() { + this.menu.removeAll(); + this._workspacesItems = []; +- this._currentWorkspace = global.screen.get_active_workspace().index(); ++ this._currentWorkspace = global.screen.get_active_workspace_index(); + + for (let i = 0; i < global.screen.n_workspaces; i++) { + let name = Meta.prefs_get_workspace_name(i); +-- +2.21.0 + + +From fe93812275cc44f01a4c3877fc5efdb9121da78a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 5 Jun 2019 05:11:34 +0200 +Subject: [PATCH 18/33] 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 91383ab..bab8f76 100644 +--- a/extensions/window-list/stylesheet.css ++++ b/extensions/window-list/stylesheet.css +@@ -85,13 +85,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 f9778f7..132d621 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -22,12 +22,15 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + this.actor.add_actor(container); + + this._currentWorkspace = global.screen.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 2edb23239c7d3c2120d59cfe74cba36674be515f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 5 Jun 2019 05:08:31 +0200 +Subject: [PATCH 19/33] 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 helper function. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/70 +--- + extensions/window-list/workspaceIndicator.js | 22 +++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js +index 132d621..6352763 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -36,9 +36,9 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + + this._screenSignals = [ + global.screen.connect('notify::n-workspaces', +- this._updateMenu.bind(this)), ++ this._nWorkspacesChanged.bind(this)), + global.screen.connect_after('workspace-switched', +- this._updateIndicator.bind(this)) ++ this._onWorkspaceSwitched.bind(this)) + ]; + + this.actor.connect('scroll-event', this._onScrollEvent.bind(this)); +@@ -61,14 +61,26 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + super.destroy(); + } + +- _updateIndicator() { +- this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE); ++ _onWorkspaceSwitched() { + this._currentWorkspace = global.screen.get_active_workspace_index(); +- this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.DOT); ++ ++ 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 current = global.screen.get_active_workspace_index(); + let total = global.screen.n_workspaces; +-- +2.21.0 + + +From 1fa782c8ab1d3e69f99cbe0a68110a557d828c78 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 5 Jun 2019 02:53:38 +0000 +Subject: [PATCH 20/33] 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/classic.css | 9 +++ + extensions/window-list/stylesheet.css | 29 +++++++++ + extensions/window-list/workspaceIndicator.js | 63 +++++++++++++++++++- + 3 files changed, 100 insertions(+), 1 deletion(-) + +diff --git a/extensions/window-list/classic.css b/extensions/window-list/classic.css +index cc967e0..c533473 100644 +--- a/extensions/window-list/classic.css ++++ b/extensions/window-list/classic.css +@@ -47,3 +47,12 @@ + color: #888; + box-shadow: none; + } ++ ++/* workspace switcher */ ++.window-list-workspace-indicator .workspace { ++ background-color: #ddd; ++} ++ ++.window-list-workspace-indicator .workspace.active { ++ background-color: #ccc; ++} +diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css +index bab8f76..ad5978a 100644 +--- a/extensions/window-list/stylesheet.css ++++ b/extensions/window-list/stylesheet.css +@@ -92,6 +92,35 @@ + 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:last-child:ltr, ++.window-list-workspace-indicator .workspace:first-child:last-child:rtl { ++ border-radius: 4px; ++} ++ ++.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 6352763..77acf8a 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -7,6 +7,24 @@ 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; ++ } ++ ++ on_clicked() { ++ let ws = global.screen.get_workspace_by_index(this._index); ++ if (ws) ++ ws.activate(global.get_current_time()); ++ } ++}); ++ + var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + constructor() { + super(0.0, _('Workspace Indicator'), true); +@@ -32,17 +50,30 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + }); + 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._screenSignals = [ + global.screen.connect('notify::n-workspaces', + this._nWorkspacesChanged.bind(this)), + global.screen.connect_after('workspace-switched', +- this._onWorkspaceSwitched.bind(this)) ++ this._onWorkspaceSwitched.bind(this)), ++ global.screen.connect('notify::layout-rows', ++ this._onWorkspaceOrientationChanged.bind(this)) + ]; + + this.actor.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( +@@ -61,16 +92,26 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + super.destroy(); + } + ++ _onWorkspaceOrientationChanged() { ++ let vertical = global.screen.layout_rows == -1; ++ this.reactive = vertical; ++ ++ this._statusBin.visible = vertical; ++ this._thumbnailsBox.visible = !vertical; ++ } ++ + _onWorkspaceSwitched() { + this._currentWorkspace = global.screen.get_active_workspace_index(); + + this._updateMenuOrnament(); ++ this._updateActiveThumbnail(); + + this._statusLabel.set_text(this._getStatusText()); + } + + _nWorkspacesChanged() { + this._updateMenu(); ++ this._updateThumbnails(); + } + + _updateMenuOrnament() { +@@ -81,6 +122,16 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + } + } + ++ _updateActiveThumbnail() { ++ let thumbs = this._thumbnailsBox.get_children(); ++ for (let i = 0; i < thumbs.length; i++) { ++ if (i == this._currentWorkspace) ++ thumbs[i].add_style_class_name('active'); ++ else ++ thumbs[i].remove_style_class_name('active'); ++ } ++ } ++ + _getStatusText() { + let current = global.screen.get_active_workspace_index(); + let total = global.screen.n_workspaces; +@@ -120,6 +171,16 @@ var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { + this._statusLabel.set_text(this._getStatusText()); + } + ++ _updateThumbnails() { ++ this._thumbnailsBox.destroy_all_children(); ++ ++ for (let i = 0; i < global.screen.n_workspaces; i++) { ++ let thumb = new WorkspaceThumbnail(i); ++ this._thumbnailsBox.add_actor(thumb); ++ } ++ this._updateActiveThumbnail(); ++ } ++ + _activate(index) { + if (index >= 0 && index < global.screen.n_workspaces) { + let metaWorkspace = global.screen.get_workspace_by_index(index); +-- +2.21.0 + + +From 8ab04d7d1fe9807cdde949afa743de97539769cc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 11 Jun 2019 23:01:20 +0000 +Subject: [PATCH 21/33] 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 | 27 ++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js +index 77acf8a..edf176e 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -1,6 +1,8 @@ + /* exported WorkspaceIndicator */ + const { Clutter, Gio, GObject, Meta, St } = imports.gi; + ++const DND = imports.ui.dnd; ++const Main = imports.ui.main; + const PanelMenu = imports.ui.panelMenu; + const PopupMenu = imports.ui.popupMenu; + +@@ -16,6 +18,31 @@ let WorkspaceThumbnail = GObject.registerClass({ + }); + + this._index = index; ++ this._delegate = this; // needed for DND ++ } ++ ++ acceptDrop(source) { ++ if (!source.realWindow) ++ return false; ++ ++ let window = source.realWindow.get_meta_window(); ++ this._moveWindow(window); ++ return true; ++ } ++ ++ handleDragOver(source) { ++ if (source.realWindow) ++ return DND.DragMotionResult.MOVE_DROP; ++ else ++ return DND.DragMotionResult.CONTINUE; ++ } ++ ++ ++ _moveWindow(window) { ++ let monitorIndex = Main.layoutManager.findIndexForActor(this); ++ if (monitorIndex != window.get_monitor()) ++ window.move_to_monitor(monitorIndex); ++ window.change_workspace_by_index(this._index, false); + } + + on_clicked() { +-- +2.21.0 + + +From a4d0ccc3122891f9e164b2b45c9540dbdebba0de Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 26 Jun 2019 23:55:58 +0000 +Subject: [PATCH 22/33] window-list: Show previews in workspace switcher + +Currently the new horizontal workspace switcher only shows a series of +buttons, with no indication of the workspaces' contents. Go full GNOME 2 +and add tiny draggable preview rectangles that represent the windows +on a particular workspace. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/74 +--- + extensions/window-list/classic.css | 10 ++ + extensions/window-list/stylesheet.css | 10 ++ + extensions/window-list/workspaceIndicator.js | 153 ++++++++++++++++++- + 3 files changed, 172 insertions(+), 1 deletion(-) + +diff --git a/extensions/window-list/classic.css b/extensions/window-list/classic.css +index c533473..7079d3e 100644 +--- a/extensions/window-list/classic.css ++++ b/extensions/window-list/classic.css +@@ -56,3 +56,13 @@ + .window-list-workspace-indicator .workspace.active { + background-color: #ccc; + } ++ ++.window-list-window-preview { ++ background-color: #ededed; ++ border: 1px solid #ccc; ++} ++ ++.window-list-window-preview.active { ++ background-color: #f6f5f4; ++ border: 2px solid #888; ++} +diff --git a/extensions/window-list/stylesheet.css b/extensions/window-list/stylesheet.css +index ad5978a..79d56ba 100644 +--- a/extensions/window-list/stylesheet.css ++++ b/extensions/window-list/stylesheet.css +@@ -121,6 +121,16 @@ + background-color: rgba(200, 200, 200, .3); + } + ++.window-list-window-preview { ++ background-color: #252525; ++ border: 1px solid #ccc; ++} ++ ++.window-list-window-preview.active { ++ background-color: #353535; ++ border: 2px solid #ccc; ++} ++ + .notification { + font-weight: normal; + } +diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js +index edf176e..0bcee80 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -9,16 +9,130 @@ const PopupMenu = imports.ui.popupMenu; + const Gettext = imports.gettext.domain('gnome-shell-extensions'); + const _ = Gettext.gettext; + ++let WindowPreview = GObject.registerClass({ ++ GTypeName: 'WindowListWindowPreview' ++}, class WindowPreview extends St.Button { ++ _init(window) { ++ super._init({ ++ style_class: 'window-list-window-preview' ++ }); ++ ++ this._delegate = this; ++ DND.makeDraggable(this, { restoreOnSuccess: true }); ++ ++ this._window = window; ++ ++ this.connect('destroy', this._onDestroy.bind(this)); ++ ++ this._sizeChangedId = this._window.connect('size-changed', ++ this._relayout.bind(this)); ++ this._positionChangedId = this._window.connect('position-changed', ++ this._relayout.bind(this)); ++ this._minimizedChangedId = this._window.connect('notify::minimized', ++ this._relayout.bind(this)); ++ this._monitorEnteredId = global.screen.connect('window-entered-monitor', ++ this._relayout.bind(this)); ++ this._monitorLeftId = global.screen.connect('window-left-monitor', ++ this._relayout.bind(this)); ++ ++ // Do initial layout when we get a parent ++ let id = this.connect('parent-set', () => { ++ this.disconnect(id); ++ if (!this.get_parent()) ++ return; ++ this._laterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { ++ this._laterId = 0; ++ this._relayout(); ++ return false; ++ }); ++ }); ++ ++ this._focusChangedId = global.display.connect('notify::focus-window', ++ this._onFocusChanged.bind(this)); ++ this._onFocusChanged(); ++ } ++ ++ // needed for DND ++ get realWindow() { ++ return this._window.get_compositor_private(); ++ } ++ ++ _onDestroy() { ++ this._window.disconnect(this._sizeChangedId); ++ this._window.disconnect(this._positionChangedId); ++ this._window.disconnect(this._minimizedChangedId); ++ global.screen.disconnect(this._monitorEnteredId); ++ global.screen.disconnect(this._monitorLeftId); ++ global.display.disconnect(this._focusChangedId); ++ if (this._laterId) ++ Meta.later_remove(this._laterId); ++ } ++ ++ _onFocusChanged() { ++ if (global.display.focus_window == this._window) ++ this.add_style_class_name('active'); ++ else ++ this.remove_style_class_name('active'); ++ } ++ ++ _relayout() { ++ let monitor = Main.layoutManager.findIndexForActor(this); ++ this.visible = monitor == this._window.get_monitor() && ++ this._window.showing_on_its_workspace(); ++ ++ if (!this.visible) ++ return; ++ ++ let workArea = Main.layoutManager.getWorkAreaForMonitor(monitor); ++ let hscale = this.get_parent().allocation.get_width() / workArea.width; ++ let vscale = this.get_parent().allocation.get_height() / workArea.height; ++ ++ let frameRect = this._window.get_frame_rect(); ++ this.set_size( ++ Math.round(Math.min(frameRect.width, workArea.width) * hscale), ++ Math.round(Math.min(frameRect.height, workArea.height) * vscale)); ++ this.set_position( ++ Math.round(frameRect.x * hscale), ++ Math.round(frameRect.y * vscale)); ++ } ++}); ++ + let WorkspaceThumbnail = GObject.registerClass({ + GTypeName: 'WindowListWorkspaceThumbnail' + }, class WorkspaceThumbnail extends St.Button { + _init(index) { + super._init({ +- style_class: 'workspace' ++ style_class: 'workspace', ++ child: new Clutter.Actor({ ++ layout_manager: new Clutter.BinLayout(), ++ clip_to_allocation: true ++ }), ++ x_fill: true, ++ y_fill: true + }); + ++ this.connect('destroy', this._onDestroy.bind(this)); ++ + this._index = index; + this._delegate = this; // needed for DND ++ ++ this._windowPreviews = new Map(); ++ ++ this._workspace = global.screen.get_workspace_by_index(index); ++ ++ this._windowAddedId = this._workspace.connect('window-added', ++ (ws, window) => { ++ this._addWindow(window); ++ }); ++ this._windowRemovedId = this._workspace.connect('window-removed', ++ (ws, window) => { ++ this._removeWindow(window); ++ }); ++ this._restackedId = global.screen.connect('restacked', ++ this._onRestacked.bind(this)); ++ ++ this._workspace.list_windows().forEach(w => this._addWindow(w)); ++ this._onRestacked(); + } + + acceptDrop(source) { +@@ -37,6 +151,37 @@ let WorkspaceThumbnail = GObject.registerClass({ + return DND.DragMotionResult.CONTINUE; + } + ++ _addWindow(window) { ++ if (this._windowPreviews.has(window)) ++ return; ++ ++ let preview = new WindowPreview(window); ++ preview.connect('clicked', (a, btn) => this.emit('clicked', btn)); ++ this._windowPreviews.set(window, preview); ++ this.child.add_child(preview); ++ } ++ ++ _removeWindow(window) { ++ let preview = this._windowPreviews.get(window); ++ if (!preview) ++ return; ++ ++ this._windowPreviews.delete(window); ++ preview.destroy(); ++ } ++ ++ _onRestacked() { ++ let lastPreview = null; ++ let windows = global.get_window_actors().map(a => a.meta_window); ++ for (let i = 0; i < windows.length; i++) { ++ let preview = this._windowPreviews.get(windows[i]); ++ if (!preview) ++ continue; ++ ++ this.child.set_child_above_sibling(preview, lastPreview); ++ lastPreview = preview; ++ } ++ } + + _moveWindow(window) { + let monitorIndex = Main.layoutManager.findIndexForActor(this); +@@ -50,6 +195,12 @@ let WorkspaceThumbnail = GObject.registerClass({ + if (ws) + ws.activate(global.get_current_time()); + } ++ ++ _onDestroy() { ++ this._workspace.disconnect(this._windowAddedId); ++ this._workspace.disconnect(this._windowRemovedId); ++ global.screen.disconnect(this._restackedId); ++ } + }); + + var WorkspaceIndicator = class WorkspaceIndicator extends PanelMenu.Button { +-- +2.21.0 + + +From 73e31ec6a601b2f692cf58b5377a1d92fce8d5ca Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 5 Jun 2019 03:31:16 +0000 +Subject: [PATCH 23/33] 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 | 38 +++++++++++++++++++ + extensions/horizontal-workspaces/meson.build | 5 +++ + .../horizontal-workspaces/metadata.json.in | 10 +++++ + .../horizontal-workspaces/stylesheet.css | 1 + + meson.build | 1 + + 5 files changed, 55 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..bade48b +--- /dev/null ++++ b/extensions/horizontal-workspaces/extension.js +@@ -0,0 +1,38 @@ ++/* exported init */ ++const { Meta } = imports.gi; ++ ++const { ThumbnailsBox } = imports.ui.workspaceThumbnail; ++ ++class Extension { ++ constructor() { ++ this._origUpdateSwitcherVisibility = ++ ThumbnailsBox.prototype._updateSwitcherVisibility; ++ } ++ ++ enable() { ++ global.screen.override_workspace_layout( ++ Meta.ScreenCorner.TOPLEFT, ++ false, ++ 1, ++ -1); ++ ++ ThumbnailsBox.prototype._updateSwitcherVisibility = function() { ++ this.hide(); ++ }; ++ } ++ ++ disable() { ++ global.screen.override_workspace_layout( ++ Meta.ScreenCorner.TOPLEFT, ++ false, ++ -1, ++ 1); ++ ++ ThumbnailsBox.prototype._updateSwitcherVisibility = ++ this._origUpdateSwitcherVisibility; ++ } ++} ++ ++function init() { ++ return new Extension(); ++} +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 fa4aa9c..c1581a5 100644 +--- a/meson.build ++++ b/meson.build +@@ -36,6 +36,7 @@ uuid_suffix = '@gnome-shell-extensions.gcampax.github.com' + classic_extensions = [ + 'alternate-tab', + 'apps-menu', ++ 'horizontal-workspaces', + 'places-menu', + 'launch-new-instance', + 'top-icons', +-- +2.21.0 + + +From 1269e9226cf93903957acc58029f4db3e095d5ca Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sat, 29 Jun 2019 01:24:54 +0200 +Subject: [PATCH 24/33] workspace-indicator: Fix whitespace error + +We only want a single space before and after operators, not at least +one. Unfortunately eslint only enforces the latter ... + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/71 +--- + extensions/workspace-indicator/extension.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js +index ace1703..f6d9912 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -106,7 +106,7 @@ class WorkspaceIndicator extends PanelMenu.Button { + } + + _activate(index) { +- if(index >= 0 && index < global.screen.n_workspaces) { ++ if(index >= 0 && index < global.screen.n_workspaces) { + let metaWorkspace = global.screen.get_workspace_by_index(index); + metaWorkspace.activate(global.get_current_time()); + } +-- +2.21.0 + + +From fee1399f6862ca99ed063b795fc3014b0d18414f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sun, 9 Jun 2019 22:58:29 +0000 +Subject: [PATCH 25/33] 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 | 35 +++++++++++---------- + 1 file changed, 18 insertions(+), 17 deletions(-) + +diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js +index f6d9912..67dec14 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -26,12 +26,14 @@ class WorkspaceIndicator extends PanelMenu.Button { + super(0.0, _("Workspace Indicator")); + + this._currentWorkspace = global.screen.get_active_workspace().index(); +- this.statusLabel = new St.Label({ y_align: Clutter.ActorAlign.CENTER, +- text: this._labelText() }); ++ this._statusLabel = new St.Label({ ++ y_align: Clutter.ActorAlign.CENTER, ++ text: this._labelText() ++ }); + +- this.actor.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 +48,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 +69,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.screen.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) { +@@ -84,25 +86,24 @@ class WorkspaceIndicator extends PanelMenu.Button { + + _createWorkspacesSection() { + this._workspaceSection.removeAll(); +- this.workspacesItems = []; ++ this._workspacesItems = []; + this._currentWorkspace = global.screen.get_active_workspace().index(); + + let i = 0; + for(; i < global.screen.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; +- let self = this; +- 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 9f8beea12da86dfa0540fc022ef36de0fa67a932 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sun, 9 Jun 2019 23:03:55 +0000 +Subject: [PATCH 26/33] 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 67dec14..3befe14 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -51,9 +51,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)); + } + + destroy() { +@@ -84,6 +84,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() { + this._workspaceSection.removeAll(); + this._workspacesItems = []; +-- +2.21.0 + + +From 2d2baa3981a3d061c7b32d57ad07da9a3553dfa3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sun, 9 Jun 2019 23:05:00 +0000 +Subject: [PATCH 27/33] 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 3befe14..d341b93 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -25,7 +25,7 @@ class WorkspaceIndicator extends PanelMenu.Button { + constructor() { + super(0.0, _("Workspace Indicator")); + +- this._currentWorkspace = global.screen.get_active_workspace().index(); ++ this._currentWorkspace = global.screen.get_active_workspace_index(); + this._statusLabel = new St.Label({ + y_align: Clutter.ActorAlign.CENTER, + text: this._labelText() +@@ -70,7 +70,7 @@ class WorkspaceIndicator extends PanelMenu.Button { + + _updateIndicator() { + this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE); +- this._currentWorkspace = global.screen.get_active_workspace().index(); ++ this._currentWorkspace = global.screen.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 { + _createWorkspacesSection() { + this._workspaceSection.removeAll(); + this._workspacesItems = []; +- this._currentWorkspace = global.screen.get_active_workspace().index(); ++ this._currentWorkspace = global.screen.get_active_workspace_index(); + + let i = 0; + for(; i < global.screen.n_workspaces; i++) { +@@ -129,7 +129,7 @@ class WorkspaceIndicator extends PanelMenu.Button { + return; + } + +- let newIndex = global.screen.get_active_workspace().index() + diff; ++ let newIndex = global.screen.get_active_workspace_index() + diff; + this._activate(newIndex); + } + }; +-- +2.21.0 + + +From 7c3dfb2c2d5713f84a11d59dba9e34e50a51e204 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sun, 9 Jun 2019 23:09:12 +0000 +Subject: [PATCH 28/33] 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 helper function. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/71 +--- + extensions/workspace-indicator/extension.js | 30 ++++++++++++++------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js +index d341b93..58ac865 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -37,12 +37,12 @@ class WorkspaceIndicator extends PanelMenu.Button { + this._workspaceSection = new PopupMenu.PopupMenuSection(); + this.menu.addMenuItem(this._workspaceSection); + +- this._screenSignals = []; +- this._screenSignals.push(global.screen.connect_after('workspace-added', this._createWorkspacesSection.bind(this))); +- this._screenSignals.push(global.screen.connect_after('workspace-removed', +- this._createWorkspacesSection.bind(this))); +- this._screenSignals.push(global.screen.connect_after('workspace-switched', +- this._updateIndicator.bind(this))); ++ this._screenSignals = [ ++ global.screen.connect_after('notify::n-workspaces', ++ this._nWorkspacesChanged.bind(this)), ++ global.screen.connect_after('workspace-switched', ++ this._onWorkspaceSwitched.bind(this)) ++ ]; + + this.actor.connect('scroll-event', this._onScrollEvent.bind(this)); + this._createWorkspacesSection(); +@@ -68,14 +68,26 @@ class WorkspaceIndicator extends PanelMenu.Button { + super.destroy(); + } + +- _updateIndicator() { +- this._workspacesItems[this._currentWorkspace].setOrnament(PopupMenu.Ornament.NONE); ++ _onWorkspaceSwitched() { + this._currentWorkspace = global.screen.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 e5b8c870ff750973d615eb801e15b56ad8f614db Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sun, 9 Jun 2019 23:17:35 +0000 +Subject: [PATCH 29/33] 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 58ac865..e6fffe4 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -27,6 +27,7 @@ class WorkspaceIndicator extends PanelMenu.Button { + + this._currentWorkspace = global.screen.get_active_workspace_index(); + this._statusLabel = new St.Label({ ++ style_class: 'panel-workspace-indicator', + y_align: Clutter.ActorAlign.CENTER, + text: this._labelText() + }); +@@ -47,9 +48,6 @@ class WorkspaceIndicator extends PanelMenu.Button { + this.actor.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 cce0e2b739cab74a166cfa75631836a7bfd06429 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sun, 9 Jun 2019 23:45:24 +0000 +Subject: [PATCH 30/33] 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 | 74 ++++++++++++++++++- + extensions/workspace-indicator/stylesheet.css | 27 ++++++- + 2 files changed, 98 insertions(+), 3 deletions(-) + +diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js +index e6fffe4..32bb10f 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -1,6 +1,7 @@ + // -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- + + const Gio = imports.gi.Gio; ++const GObject = imports.gi.GObject; + const Meta = imports.gi.Meta; + const Clutter = imports.gi.Clutter; + const St = imports.gi.St; +@@ -21,10 +22,36 @@ const Convenience = Me.imports.convenience; + const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences'; + const WORKSPACE_KEY = 'workspace-names'; + ++let WorkspaceThumbnail = GObject.registerClass({ ++ GTypeName: 'WorkspaceIndicatorWorkspaceThumbnail' ++}, class WorkspaceThumbnail extends St.Button { ++ _init(index) { ++ super._init({ ++ style_class: 'workspace', ++ }); ++ ++ this._index = index; ++ } ++ ++ on_clicked() { ++ let ws = global.screen.get_workspace_by_index(this._index); ++ if (ws) ++ ws.activate(global.get_current_time()); ++ } ++}); ++ ++ + class WorkspaceIndicator extends PanelMenu.Button { + constructor() { + super(0.0, _("Workspace Indicator")); + ++ let container = new St.Widget({ ++ layout_manager: new Clutter.BinLayout(), ++ x_expand: true, ++ y_expand: true ++ }); ++ this.actor.add_actor(container); ++ + this._currentWorkspace = global.screen.get_active_workspace_index(); + this._statusLabel = new St.Label({ + style_class: 'panel-workspace-indicator', +@@ -32,7 +59,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(); +@@ -42,11 +77,16 @@ class WorkspaceIndicator extends PanelMenu.Button { + global.screen.connect_after('notify::n-workspaces', + this._nWorkspacesChanged.bind(this)), + global.screen.connect_after('workspace-switched', +- this._onWorkspaceSwitched.bind(this)) ++ this._onWorkspaceSwitched.bind(this)), ++ global.screen.connect('notify::layout-rows', ++ this._onWorkspaceOrientationChanged.bind(this)) + ]; + + this.actor.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( +@@ -66,16 +106,26 @@ class WorkspaceIndicator extends PanelMenu.Button { + super.destroy(); + } + ++ _onWorkspaceOrientationChanged() { ++ let vertical = global.screen.layout_rows == -1; ++ this.actor.reactive = vertical; ++ ++ this._statusLabel.visible = vertical; ++ this._thumbnailsBox.visible = !vertical; ++ } ++ + _onWorkspaceSwitched() { + this._currentWorkspace = global.screen.get_active_workspace_index(); + + this._updateMenuOrnament(); ++ this._updateActiveThumbnail(); + + this._statusLabel.set_text(this._labelText()); + } + + _nWorkspacesChanged() { + this._createWorkspacesSection(); ++ this._updateThumbnails(); + } + + _updateMenuOrnament() { +@@ -86,6 +136,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; +@@ -121,6 +181,16 @@ class WorkspaceIndicator extends PanelMenu.Button { + this._statusLabel.set_text(this._labelText()); + } + ++ _updateThumbnails() { ++ this._thumbnailsBox.destroy_all_children(); ++ ++ for (let i = 0; i < global.screen.n_workspaces; i++) { ++ let thumb = new WorkspaceThumbnail(i); ++ this._thumbnailsBox.add_actor(thumb); ++ } ++ this._updateActiveThumbnail(); ++ } ++ + _activate(index) { + if(index >= 0 && index < global.screen.n_workspaces) { + let metaWorkspace = global.screen.get_workspace_by_index(index); +diff --git a/extensions/workspace-indicator/stylesheet.css b/extensions/workspace-indicator/stylesheet.css +index 1271f1c..5118194 100644 +--- a/extensions/workspace-indicator/stylesheet.css ++++ b/extensions/workspace-indicator/stylesheet.css +@@ -1,5 +1,30 @@ + .panel-workspace-indicator { + padding: 0 8px; +- background-color: rgba(200, 200, 200, .5); ++} ++ ++.panel-workspace-indicator-box { ++ padding: 2px 0; ++} ++ ++.panel-workspace-indicator-box .workspace { ++ width: 40px; ++} ++ ++.panel-workspace-indicator, ++.panel-workspace-indicator-box .workspace { + 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 + + +From 98df229b8a62e575529ebc28e085df53154ebc42 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 28 Jun 2019 11:33:16 +0200 +Subject: [PATCH 31/33] workspace-indicator: Show previews in workspace + switcher + +Currently the new horizontal workspace switcher only shows a series of +buttons, with no indication of the workspaces' contents. Go full GNOME 2 +and add tiny draggable preview rectangles that represent the windows +on a particular workspace. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/77 +--- + extensions/workspace-indicator/extension.js | 179 +++++++++++++++++- + extensions/workspace-indicator/stylesheet.css | 10 + + 2 files changed, 188 insertions(+), 1 deletion(-) + +diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js +index 32bb10f..6b656c8 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -6,6 +6,8 @@ const Meta = imports.gi.Meta; + const Clutter = imports.gi.Clutter; + const St = imports.gi.St; + const Mainloop = imports.mainloop; ++ ++const DND = imports.ui.dnd; + const PanelMenu = imports.ui.panelMenu; + const PopupMenu = imports.ui.popupMenu; + const Panel = imports.ui.panel; +@@ -22,15 +24,185 @@ const Convenience = Me.imports.convenience; + const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences'; + const WORKSPACE_KEY = 'workspace-names'; + ++let WindowPreview = GObject.registerClass({ ++ GTypeName: 'WorkspaceIndicatorWindowPreview' ++}, class WindowPreview extends St.Button { ++ _init(window) { ++ super._init({ ++ style_class: 'workspace-indicator-window-preview' ++ }); ++ ++ this._delegate = this; ++ DND.makeDraggable(this, { restoreOnSuccess: true }); ++ ++ this._window = window; ++ ++ this.connect('destroy', this._onDestroy.bind(this)); ++ ++ this._sizeChangedId = this._window.connect('size-changed', ++ this._relayout.bind(this)); ++ this._positionChangedId = this._window.connect('position-changed', ++ this._relayout.bind(this)); ++ this._minimizedChangedId = this._window.connect('notify::minimized', ++ this._relayout.bind(this)); ++ this._monitorEnteredId = global.screen.connect('window-entered-monitor', ++ this._relayout.bind(this)); ++ this._monitorLeftId = global.screen.connect('window-left-monitor', ++ this._relayout.bind(this)); ++ ++ // Do initial layout when we get a parent ++ let id = this.connect('parent-set', () => { ++ this.disconnect(id); ++ if (!this.get_parent()) ++ return; ++ this._laterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { ++ this._laterId = 0; ++ this._relayout(); ++ return false; ++ }); ++ }); ++ ++ this._focusChangedId = global.display.connect('notify::focus-window', ++ this._onFocusChanged.bind(this)); ++ this._onFocusChanged(); ++ } ++ ++ // needed for DND ++ get realWindow() { ++ return this._window.get_compositor_private(); ++ } ++ ++ _onDestroy() { ++ this._window.disconnect(this._sizeChangedId); ++ this._window.disconnect(this._positionChangedId); ++ this._window.disconnect(this._minimizedChangedId); ++ global.screen.disconnect(this._monitorEnteredId); ++ global.screen.disconnect(this._monitorLeftId); ++ global.display.disconnect(this._focusChangedId); ++ if (this._laterId) ++ Meta.later_remove(this._laterId); ++ } ++ ++ _onFocusChanged() { ++ if (global.display.focus_window == this._window) ++ this.add_style_class_name('active'); ++ else ++ this.remove_style_class_name('active'); ++ } ++ ++ _relayout() { ++ let monitor = Main.layoutManager.findIndexForActor(this); ++ this.visible = monitor == this._window.get_monitor() && ++ this._window.showing_on_its_workspace(); ++ ++ if (!this.visible) ++ return; ++ ++ let workArea = Main.layoutManager.getWorkAreaForMonitor(monitor); ++ let hscale = this.get_parent().allocation.get_width() / workArea.width; ++ let vscale = this.get_parent().allocation.get_height() / workArea.height; ++ ++ let frameRect = this._window.get_frame_rect(); ++ this.set_size( ++ Math.round(Math.min(frameRect.width, workArea.width) * hscale), ++ Math.round(Math.min(frameRect.height, workArea.height) * vscale)); ++ this.set_position( ++ Math.round(frameRect.x * hscale), ++ Math.round(frameRect.y * vscale)); ++ } ++}); ++ + let WorkspaceThumbnail = GObject.registerClass({ + GTypeName: 'WorkspaceIndicatorWorkspaceThumbnail' + }, class WorkspaceThumbnail extends St.Button { + _init(index) { + super._init({ + style_class: 'workspace', ++ child: new Clutter.Actor({ ++ layout_manager: new Clutter.BinLayout(), ++ clip_to_allocation: true ++ }), ++ x_fill: true, ++ y_fill: true + }); + ++ this.connect('destroy', this._onDestroy.bind(this)); ++ + this._index = index; ++ this._delegate = this; // needed for DND ++ ++ this._windowPreviews = new Map(); ++ ++ this._workspace = global.screen.get_workspace_by_index(index); ++ ++ this._windowAddedId = this._workspace.connect('window-added', ++ (ws, window) => { ++ this._addWindow(window); ++ }); ++ this._windowRemovedId = this._workspace.connect('window-removed', ++ (ws, window) => { ++ this._removeWindow(window); ++ }); ++ this._restackedId = global.screen.connect('restacked', ++ this._onRestacked.bind(this)); ++ ++ this._workspace.list_windows().forEach(w => this._addWindow(w)); ++ this._onRestacked(); ++ } ++ ++ acceptDrop(source) { ++ if (!source.realWindow) ++ return false; ++ ++ let window = source.realWindow.get_meta_window(); ++ this._moveWindow(window); ++ return true; ++ } ++ ++ handleDragOver(source) { ++ if (source.realWindow) ++ return DND.DragMotionResult.MOVE_DROP; ++ else ++ return DND.DragMotionResult.CONTINUE; ++ } ++ ++ _addWindow(window) { ++ if (this._windowPreviews.has(window)) ++ return; ++ ++ let preview = new WindowPreview(window); ++ preview.connect('clicked', (a, btn) => this.emit('clicked', btn)); ++ this._windowPreviews.set(window, preview); ++ this.child.add_child(preview); ++ } ++ ++ _removeWindow(window) { ++ let preview = this._windowPreviews.get(window); ++ if (!preview) ++ return; ++ ++ this._windowPreviews.delete(window); ++ preview.destroy(); ++ } ++ ++ _onRestacked() { ++ let lastPreview = null; ++ let windows = global.get_window_actors().map(a => a.meta_window); ++ for (let i = 0; i < windows.length; i++) { ++ let preview = this._windowPreviews.get(windows[i]); ++ if (!preview) ++ continue; ++ ++ this.child.set_child_above_sibling(preview, lastPreview); ++ lastPreview = preview; ++ } ++ } ++ ++ _moveWindow(window) { ++ let monitorIndex = Main.layoutManager.findIndexForActor(this); ++ if (monitorIndex != window.get_monitor()) ++ window.move_to_monitor(monitorIndex); ++ window.change_workspace_by_index(this._index, false); + } + + on_clicked() { +@@ -38,8 +210,13 @@ let WorkspaceThumbnail = GObject.registerClass({ + if (ws) + ws.activate(global.get_current_time()); + } +-}); + ++ _onDestroy() { ++ this._workspace.disconnect(this._windowAddedId); ++ this._workspace.disconnect(this._windowRemovedId); ++ global.screen.disconnect(this._restackedId); ++ } ++}); + + class WorkspaceIndicator extends PanelMenu.Button { + constructor() { +diff --git a/extensions/workspace-indicator/stylesheet.css b/extensions/workspace-indicator/stylesheet.css +index 5118194..8601c3e 100644 +--- a/extensions/workspace-indicator/stylesheet.css ++++ b/extensions/workspace-indicator/stylesheet.css +@@ -28,3 +28,13 @@ + .panel-workspace-indicator-box .workspace:first-child { + border-left-width: 1px; + } ++ ++.workspace-indicator-window-preview { ++ background-color: #252525; ++ border: 1px solid #ccc; ++} ++ ++.workspace-indicator-window-preview.active { ++ background-color: #353535; ++ border: 2px solid #ccc; ++} +-- +2.21.0 + + +From 9b1fb384f2f46a953c4838991ec58cd3c2872b31 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 6 Sep 2019 20:41:23 +0200 +Subject: [PATCH 32/33] window-list: Exclude DESKTOP windows from window + previews + +While nautilus removed its desktop support a while ago in favor of an +extension, it's still possible that some external X11 desktop icon app +is used. As DESKTOP windows cannot be moved between workspaces or stacked, +and aren't perceived as regular windows, it doesn't make sense to show +them as previews in the workspace switcher. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/93 +--- + extensions/window-list/workspaceIndicator.js | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js +index 0bcee80..c0dd65d 100644 +--- a/extensions/window-list/workspaceIndicator.js ++++ b/extensions/window-list/workspaceIndicator.js +@@ -78,6 +78,7 @@ let WindowPreview = GObject.registerClass({ + _relayout() { + let monitor = Main.layoutManager.findIndexForActor(this); + this.visible = monitor == this._window.get_monitor() && ++ this._window.window_type !== Meta.WindowType.DESKTOP && + this._window.showing_on_its_workspace(); + + if (!this.visible) +-- +2.21.0 + + +From 59d4e3377072507f95562c8516913b1d257651c0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 6 Sep 2019 20:47:56 +0200 +Subject: [PATCH 33/33] workspace-indicator: Exclude DESKTOP windows from + window previews + +While nautilus removed its desktop support a while ago in favor of an +extension, it's still possible that some external X11 desktop icon app +is used. As DESKTOP windows cannot be moved between workspaces or stacked, +and aren't perceived as regular windows, it doesn't make sense to show +them as previews in the workspace switcher. + +https://gitlab.gnome.org/GNOME/gnome-shell-extensions/merge_requests/93 +--- + extensions/workspace-indicator/extension.js | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js +index 6b656c8..6b7947d 100644 +--- a/extensions/workspace-indicator/extension.js ++++ b/extensions/workspace-indicator/extension.js +@@ -93,6 +93,7 @@ let WindowPreview = GObject.registerClass({ + _relayout() { + let monitor = Main.layoutManager.findIndexForActor(this); + this.visible = monitor == this._window.get_monitor() && ++ this._window.window_type !== Meta.WindowType.DESKTOP && + this._window.showing_on_its_workspace(); + + if (!this.visible) +-- +2.21.0 + diff --git a/SPECS/gnome-shell-extensions.spec b/SPECS/gnome-shell-extensions.spec index 98b1372..c7ea5d2 100644 --- a/SPECS/gnome-shell-extensions.spec +++ b/SPECS/gnome-shell-extensions.spec @@ -6,7 +6,7 @@ Name: gnome-shell-extensions Version: 3.28.1 -Release: 7%{?dist} +Release: 11%{?dist} Summary: Modify and extend GNOME Shell functionality and behavior Group: User Interface/Desktops @@ -27,14 +27,17 @@ Requires: gnome-shell >= %{min_gs_version} BuildArch: noarch Patch1: 0001-Update-style.patch -Patch2: 0001-classic-Shade-panel-in-overview.patch -Patch3: 0001-apps-menu-add-logo-icon-to-Applications-menu.patch +Patch2: 0001-apps-menu-add-logo-icon-to-Applications-menu.patch Patch4: add-extra-extensions.patch Patch5: 0001-apps-menu-Explicitly-set-label_actor.patch Patch6: resurrect-system-monitor.patch Patch7: 0001-data-drop-app-icon-styling.patch Patch11: 0001-Include-top-icons-in-classic-session.patch Patch12: 0001-window-list-drop-button-grab-when-leaving-button.patch +Patch13: 0001-Add-extra-osk-keys-extension.patch +Patch14: more-classic-classic-mode.patch +Patch15: 0001-window-list-workspace-indicator-Set-reactiveness-of-.patch +Patch16: 0001-A-couple-of-more-backport-fixes.patch Patch99: 0001-Revert-data-Remove-nautilus-classic.patch @@ -47,6 +50,7 @@ Enabled extensions: * apps-menu * auto-move-windows * drive-menu + * horizontal-workspaces * launch-new-instance * native-window-placement * places-menu @@ -87,6 +91,7 @@ Group: User Interface/Desktops License: GPLv2+ Requires: %{pkg_prefix}-alternate-tab = %{version}-%{release} Requires: %{pkg_prefix}-apps-menu = %{version}-%{release} +Requires: %{pkg_prefix}-horizontal-workspaces = %{version}-%{release} Requires: %{pkg_prefix}-launch-new-instance = %{version}-%{release} Requires: %{pkg_prefix}-places-menu = %{version}-%{release} Requires: %{pkg_prefix}-top-icons = %{version}-%{release} @@ -172,6 +177,27 @@ This GNOME Shell extension provides a panel status menu for accessing and unmounting removable devices. +%package -n %{pkg_prefix}-extra-osk-keys +Summary: Extra Onscreen Keyboard Keys +Group: User Interface/Desktops +License: GPLv2+ +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-extra-osk-keys +Adds extra keys to gnome-shell onscreen keyboard. + + +%package -n %{pkg_prefix}-horizontal-workspaces +Summary: Arrange workspaces in GNOME Shell in a single row +Group: User Interface/Desktops +License: GPLv2+ +Requires: %{pkg_prefix}-common = %{version}-%{release} + +%description -n %{pkg_prefix}-horizontal-workspaces +This GNOME Shell extension changes the workspace layout from a single column +to a single row. + + %package -n %{pkg_prefix}-launch-new-instance Summary: Always launch a new application instance for GNOME Shell Group: User Interface/Desktops @@ -387,6 +413,14 @@ rm $RPM_BUILD_ROOT%{_datadir}/glib-2.0/schemas/org.gnome.shell.extensions.exampl %{_datadir}/gnome-shell/extensions/drive-menu*/ +%files -n %{pkg_prefix}-extra-osk-keys +%{_datadir}/gnome-shell/extensions/extra-osk-keys*/ + + +%files -n %{pkg_prefix}-horizontal-workspaces +%{_datadir}/gnome-shell/extensions/horizontal-workspaces*/ + + %files -n %{pkg_prefix}-launch-new-instance %{_datadir}/gnome-shell/extensions/launch-new-instance*/ @@ -528,6 +562,22 @@ fi %changelog +* Fri Dec 06 2019 Jonas Ådahl - 3.28.1-11 +- A couple of fixes to the classic backports + Resolves: #1778270 + +* Wed Sep 25 2019 Jonas Ådahl - 3.28.1-10 +- Fix unwanted appearance of workspace switcher menu + Resolves: #1752357 + +* Sun Aug 25 2019 Florian Müllner - 3.28.1-9 +- Make classic mode more classic + Resolves: #1720286 + +* Fri Aug 09 2019 Carlos Garnacho - 3.28.1-8 +- Add extra-osk-keys extension + Resolves: #1702417 + * Tue Mar 26 2019 Florian Müllner - 3.28.1-7 - Add window-grouper extension Resolves: #1355845