From 2ac30c34a3512a83daca0d41b8bf53ac22d59480 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 9 Feb 2025 23:15:44 +0100 Subject: [PATCH] Adding upstream version 20231210. Signed-off-by: Daniel Baumann --- CHANGELOG.txt | 22 +- Makefile | 4 +- VERSION.txt | 2 +- .../{ => 44}/disable-workspace-switcher.mk | 0 .../disable-workspace-switcher/LICENSE | 0 .../disable-workspace-switcher/README.md | 0 .../extension.js | 0 .../metadata.json | 0 extensions/{ => 44}/hibernate-status.mk | 0 .../{ => 44}/hibernate-status/.gitignore | 0 extensions/{ => 44}/hibernate-status/LICENSE | 0 extensions/{ => 44}/hibernate-status/Makefile | 0 .../{ => 44}/hibernate-status/README.md | 0 .../hibernate-status/buildforupload.sh | 0 .../hibernate-status/confirmDialog.js | 2 +- .../{ => 44}/hibernate-status/extension.js | 0 .../bg/LC_MESSAGES/hibernate-status-button.po | 0 .../cs/LC_MESSAGES/hibernate-status-button.po | 0 .../de/LC_MESSAGES/hibernate-status-button.po | 0 .../es/LC_MESSAGES/hibernate-status-button.po | 0 .../fa/LC_MESSAGES/hibernate-status-button.po | 0 .../fr/LC_MESSAGES/hibernate-status-button.po | 0 .../hu/LC_MESSAGES/hibernate-status-button.po | 0 .../it/LC_MESSAGES/hibernate-status-button.po | 0 .../nl/LC_MESSAGES/hibernate-status-button.po | 0 .../oc/LC_MESSAGES/hibernate-status-button.po | 0 .../pl/LC_MESSAGES/hibernate-status-button.po | 0 .../pt/LC_MESSAGES/hibernate-status-button.po | 0 .../LC_MESSAGES/hibernate-status-button.po | 0 .../ru/LC_MESSAGES/hibernate-status-button.po | 0 .../uk/LC_MESSAGES/hibernate-status-button.po | 0 .../LC_MESSAGES/hibernate-status-button.po | 0 .../{ => 44}/hibernate-status/metadata.json | 0 extensions/{ => 44}/hibernate-status/prefs.js | 0 ...nsions.hibernate-status-button.gschema.xml | 0 extensions/{ => 44}/middleclickclose.mk | 0 .../{ => 44}/middleclickclose/.gitignore | 0 extensions/{ => 44}/middleclickclose/LICENSE | 0 .../{ => 44}/middleclickclose/README.md | 0 .../middleclickclose/buildforupload.sh | 0 .../extension.js | 0 .../locale/de/LC_MESSAGES/de_DE.mo | Bin .../locale/de/LC_MESSAGES/de_DE.po | 0 .../locale/fr/LC_MESSAGES/fr.mo | Bin .../locale/fr/LC_MESSAGES/fr.po | 0 .../locale/it/LC_MESSAGES/it.mo | Bin .../locale/it/LC_MESSAGES/it.po | 0 .../locale/nl/nl.mo | Bin .../locale/nl/nl.po | 0 .../locale/sk/LC_MESSAGES/sk.mo | Bin .../locale/sk/LC_MESSAGES/sk.po | 0 .../metadata.json | 0 .../prefs.js | 0 ...ll.extensions.middleclickclose.gschema.xml | 0 .../tweak-tool-screenshot.png | Bin extensions/{ => 44}/multi-monitors-add-on.mk | 0 .../{ => 44}/multi-monitors-add-on/.project | 0 .../{ => 44}/multi-monitors-add-on/LICENSE | 0 .../{ => 44}/multi-monitors-add-on/README.md | 0 .../convenience.js | 0 .../multi-monitors-add-on@spin83/extension.js | 0 .../icons/multi-monitors-l-symbolic.svg | 0 .../icons/multi-monitors-r-symbolic.svg | 0 .../multi-monitors-add-on@spin83/indicator.js | 0 .../de/LC_MESSAGES/multi-monitors-add-on.mo | Bin .../de/LC_MESSAGES/multi-monitors-add-on.po | 0 .../es/LC_MESSAGES/multi-monitors-add-on.mo | Bin .../es/LC_MESSAGES/multi-monitors-add-on.po | 0 .../fr/LC_MESSAGES/multi-monitors-add-on.mo | Bin .../fr/LC_MESSAGES/multi-monitors-add-on.po | 0 .../it/LC_MESSAGES/multi-monitors-add-on.mo | Bin .../it/LC_MESSAGES/multi-monitors-add-on.po | 0 .../pl/LC_MESSAGES/multi-monitors-add-on.mo | Bin .../pl/LC_MESSAGES/multi-monitors-add-on.po | 0 .../LC_MESSAGES/multi-monitors-add-on.mo | Bin .../LC_MESSAGES/multi-monitors-add-on.po | 0 .../metadata.json | 0 .../mmcalendar.js | 0 .../multi-monitors-add-on@spin83/mmlayout.js | 0 .../mmoverview.js | 0 .../multi-monitors-add-on@spin83/mmpanel.js | 0 .../multi-monitors-add-on.pot | 0 .../multi-monitors-add-on@spin83/prefs.js | 0 .../schemas/gschemas.compiled | Bin ...tensions.multi-monitors-add-on.gschema.xml | 0 .../stylesheet.css | 0 extensions/{ => 44}/no-overview.mk | 0 extensions/{ => 44}/no-overview/LICENSE | 0 extensions/{ => 44}/no-overview/README.md | 0 extensions/{ => 44}/no-overview/extension.js | 0 extensions/44/no-overview/metadata.json | 18 + extensions/{ => 44}/vertical-workspaces.mk | 0 .../.github/ISSUE_TEMPLATE/bug_report.md | 0 .../.github/ISSUE_TEMPLATE/feature_request.md | 0 .../{ => 44}/vertical-workspaces/CHANGELOG.md | 96 + .../{ => 44}/vertical-workspaces/LICENSE | 0 .../{ => 44}/vertical-workspaces/Makefile | 0 .../{ => 44}/vertical-workspaces/README.md | 18 +- .../44/vertical-workspaces/extension.js | 793 ++++++ .../vertical-workspaces/lib/appDisplay.js | 1249 +++++---- .../vertical-workspaces/lib/appFavorites.js | 79 + extensions/44/vertical-workspaces/lib/dash.js | 1506 +++++++++++ .../lib/extensionsSearchProvider.js | 423 +++ .../vertical-workspaces/lib/iconGrid.js | 133 +- .../vertical-workspaces/lib/layout.js | 219 +- .../44/vertical-workspaces/lib/messageTray.js | 91 + .../vertical-workspaces/lib/optionsFactory.js | 251 +- .../44/vertical-workspaces/lib/osdWindow.js | 118 + .../44/vertical-workspaces/lib/overlayKey.js | 168 ++ .../44/vertical-workspaces/lib/overview.js | 165 ++ .../lib/overviewControls.js | 831 ++++-- .../44/vertical-workspaces/lib/panel.js | 256 ++ .../lib/recentFilesSearchProvider.js | 258 ++ .../44/vertical-workspaces/lib/search.js | 323 +++ .../lib/searchController.js | 94 + .../vertical-workspaces/lib/settings.js | 300 ++- .../vertical-workspaces/lib/swipeTracker.js | 107 +- .../{ => 44}/vertical-workspaces/lib/util.js | 199 +- .../44/vertical-workspaces/lib/winTmb.js | 525 ++++ .../lib/windowAttentionHandler.js | 84 +- .../vertical-workspaces/lib/windowManager.js | 154 +- .../vertical-workspaces/lib/windowPreview.js | 633 +++++ .../lib/windowSearchProvider.js | 223 +- .../vertical-workspaces/lib/workspace.js | 83 +- .../lib/workspaceAnimation.js | 206 ++ .../lib/workspaceSwitcherPopup.js | 109 + .../lib/workspaceThumbnail.js | 258 +- .../vertical-workspaces/lib/workspacesView.js | 363 +-- .../vertical-workspaces/metadata.json | 10 +- .../{ => 44}/vertical-workspaces/po/cs.po | 0 .../vertical-workspaces/po/nl.po} | 664 +++-- .../po/vertical-workspaces.pot | 0 .../{ => 44}/vertical-workspaces/prefs.js | 1109 +++++--- ...extensions.vertical-workspaces.gschema.xml | 408 +++ .../screenshots/screenshot.jpg | Bin .../screenshots/screenshot0.jpg | Bin .../screenshots/screenshot0.png | Bin .../screenshots/screenshot1.png | Bin .../screenshots/screenshot2.png | Bin .../screenshots/screenshot3.png | Bin .../screenshots/screenshot4.png | Bin .../screenshots/screenshot5.png | Bin .../screenshots/vertical-workspaces.gif | Bin .../44/vertical-workspaces/stylesheet.css | 307 +++ .../{ => 45}/disable-workspace-switcher.mk | 0 .../disable-workspace-switcher/LICENSE | 0 .../disable-workspace-switcher/README.md | 0 .../extension.js | 14 + .../metadata.json | 4 +- extensions/{ => 45}/hibernate-status.mk | 0 extensions/45/hibernate-status/.editorconfig | 12 + .../.github/workflows/release.yaml | 32 + .../{ => 45}/hibernate-status/.gitignore | 0 extensions/45/hibernate-status/.prettierrc | 8 + extensions/{ => 45}/hibernate-status/LICENSE | 0 extensions/{ => 45}/hibernate-status/Makefile | 2 +- .../{ => 45}/hibernate-status/README.md | 4 +- .../hibernate-status/buildforupload.sh | 0 extensions/45/hibernate-status/extension.js | 573 +++++ .../bg/LC_MESSAGES/hibernate-status-button.po | 0 .../cs/LC_MESSAGES/hibernate-status-button.po | 0 .../de/LC_MESSAGES/hibernate-status-button.po | 0 .../es/LC_MESSAGES/hibernate-status-button.po | 0 .../fa/LC_MESSAGES/hibernate-status-button.po | 0 .../fr/LC_MESSAGES/hibernate-status-button.po | 0 .../hu/LC_MESSAGES/hibernate-status-button.po | 0 .../it/LC_MESSAGES/hibernate-status-button.po | 0 .../nl/LC_MESSAGES/hibernate-status-button.po | 0 .../oc/LC_MESSAGES/hibernate-status-button.po | 0 .../pl/LC_MESSAGES/hibernate-status-button.po | 0 .../pt/LC_MESSAGES/hibernate-status-button.po | 0 .../LC_MESSAGES/hibernate-status-button.po | 0 .../ru/LC_MESSAGES/hibernate-status-button.po | 0 .../uk/LC_MESSAGES/hibernate-status-button.po | 0 .../LC_MESSAGES/hibernate-status-button.po | 0 extensions/45/hibernate-status/metadata.json | 9 + extensions/45/hibernate-status/prefs.js | 171 ++ ...nsions.hibernate-status-button.gschema.xml | 35 + .../middleclickclose.mk} | 13 +- extensions/45/middleclickclose/.gitignore | 1 + extensions/{ => 45}/middleclickclose/LICENSE | 0 extensions/45/middleclickclose/Makefile | 27 + extensions/45/middleclickclose/README.md | 29 + .../45/middleclickclose/src/extension.js | 103 + .../middleclickclose/src}/metadata.json | 14 +- .../middleclickclose/src/po/de.po} | 44 +- extensions/45/middleclickclose/src/po/el.po | 74 + .../middleclickclose/src/po}/fr.po | 50 +- .../middleclickclose/src/po}/it.po | 52 +- .../nl => 45/middleclickclose/src/po}/nl.po | 48 +- extensions/45/middleclickclose/src/po/pt.po | 72 + .../middleclickclose/src/po}/sk.po | 44 +- extensions/45/middleclickclose/src/po/sr.po | 75 + .../45/middleclickclose/src/po/sr@latin.po | 75 + .../45/middleclickclose/src/po/template.pot | 72 + extensions/45/middleclickclose/src/po/uk.po | 73 + extensions/45/middleclickclose/src/prefs.js | 108 + ...ll.extensions.middleclickclose.gschema.xml | 27 + .../45/middleclickclose/src/settingsWatch.js | 87 + extensions/{ => 45}/no-overview.mk | 0 .../no-overview}/LICENSE | 0 extensions/{ => 45}/no-overview/README.md | 0 extensions/{ => 45}/no-overview/extension.js | 20 +- extensions/45/no-overview/metadata.json | 14 + extensions/{ => 45}/vertical-workspaces.mk | 0 .../.github/ISSUE_TEMPLATE/bug_report.md | 0 .../.github/ISSUE_TEMPLATE/feature_request.md | 0 .../{ => 45}/vertical-workspaces/CHANGELOG.md | 96 + .../vertical-workspaces}/LICENSE | 0 .../{ => 45}/vertical-workspaces/Makefile | 0 .../{ => 45}/vertical-workspaces/README.md | 19 +- .../45/vertical-workspaces/extension.js | 842 ++++++ .../45/vertical-workspaces/lib/appDisplay.js | 1677 ++++++++++++ .../vertical-workspaces/lib/appFavorites.js | 79 + extensions/45/vertical-workspaces/lib/dash.js | 1515 +++++++++++ .../lib/extensionsSearchProvider.js | 406 +++ .../vertical-workspaces/lib/iconGrid.js | 146 +- .../vertical-workspaces/lib/layout.js | 221 +- .../45/vertical-workspaces/lib/messageTray.js | 91 + .../vertical-workspaces/lib/optionsFactory.js | 256 +- .../45/vertical-workspaces/lib/osdWindow.js | 118 + .../45/vertical-workspaces/lib/overlayKey.js | 168 ++ .../45/vertical-workspaces/lib/overview.js | 162 ++ .../lib/overviewControls.js | 882 ++++--- .../45/vertical-workspaces/lib/panel.js | 253 ++ .../lib/recentFilesSearchProvider.js | 308 +++ .../45/vertical-workspaces/lib/search.js | 291 +++ .../lib/searchController.js | 94 + .../vertical-workspaces/lib/settings.js | 300 ++- .../vertical-workspaces/lib/swipeTracker.js | 116 + .../{ => 45}/vertical-workspaces/lib/util.js | 258 +- .../45/vertical-workspaces/lib/winTmb.js | 525 ++++ .../lib/windowAttentionHandler.js | 156 ++ .../vertical-workspaces/lib/windowManager.js | 158 +- .../vertical-workspaces/lib/windowPreview.js | 619 +++++ .../lib/windowSearchProvider.js | 244 +- .../45/vertical-workspaces/lib/workspace.js | 463 ++++ .../lib/workspaceAnimation.js | 262 ++ .../lib/workspaceSwitcherPopup.js | 108 + .../lib/workspaceThumbnail.js | 382 +-- .../vertical-workspaces/lib/workspacesView.js | 349 ++- .../vertical-workspaces/metadata.json | 13 +- .../{ => 45}/vertical-workspaces/po/cs.po | 0 .../vertical-workspaces/po/nl.po} | 664 +++-- .../po/vertical-workspaces.pot | 0 extensions/45/vertical-workspaces/prefs.js | 2262 +++++++++++++++++ ...extensions.vertical-workspaces.gschema.xml | 408 +++ .../screenshots/screenshot.jpg | Bin .../screenshots/screenshot0.jpg | Bin .../screenshots/screenshot0.png | Bin .../screenshots/screenshot1.png | Bin .../screenshots/screenshot2.png | Bin .../screenshots/screenshot3.png | Bin .../screenshots/screenshot4.png | Bin .../screenshots/screenshot5.png | Bin .../screenshots/vertical-workspaces.gif | Bin .../45/vertical-workspaces/stylesheet.css | 308 +++ extensions/current | 1 + extensions/vertical-workspaces/extension.js | 664 ----- .../vertical-workspaces/lib/appFavorites.js | 61 - extensions/vertical-workspaces/lib/dash.js | 1186 --------- .../vertical-workspaces/lib/messageTray.js | 67 - .../vertical-workspaces/lib/osdWindow.js | 93 - .../vertical-workspaces/lib/overlayKey.js | 108 - .../vertical-workspaces/lib/overview.js | 59 - extensions/vertical-workspaces/lib/panel.js | 197 -- .../lib/recentFilesSearchProvider.js | 260 -- extensions/vertical-workspaces/lib/search.js | 206 -- .../vertical-workspaces/lib/windowPreview.js | 379 --- .../lib/workspaceAnimation.js | 184 -- .../lib/workspaceSwitcherPopup.js | 90 - ...extensions.vertical-workspaces.gschema.xml | 351 --- extensions/vertical-workspaces/stylesheet.css | 224 -- 273 files changed, 25892 insertions(+), 8304 deletions(-) copy extensions/{ => 44}/disable-workspace-switcher.mk (100%) copy extensions/{ => 44}/disable-workspace-switcher/LICENSE (100%) copy extensions/{ => 44}/disable-workspace-switcher/README.md (100%) rename extensions/{ => 44}/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js (100%) copy extensions/{ => 44}/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json (100%) copy extensions/{ => 44}/hibernate-status.mk (100%) copy extensions/{ => 44}/hibernate-status/.gitignore (100%) copy extensions/{ => 44}/hibernate-status/LICENSE (100%) copy extensions/{ => 44}/hibernate-status/Makefile (100%) copy extensions/{ => 44}/hibernate-status/README.md (100%) copy extensions/{ => 44}/hibernate-status/buildforupload.sh (100%) rename extensions/{ => 44}/hibernate-status/confirmDialog.js (100%) rename extensions/{ => 44}/hibernate-status/extension.js (100%) copy extensions/{ => 44}/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po (100%) copy extensions/{ => 44}/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 44}/hibernate-status/metadata.json (100%) rename extensions/{ => 44}/hibernate-status/prefs.js (100%) rename extensions/{ => 44}/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml (100%) rename extensions/{ => 44}/middleclickclose.mk (100%) rename extensions/{ => 44}/middleclickclose/.gitignore (100%) copy extensions/{ => 44}/middleclickclose/LICENSE (100%) rename extensions/{ => 44}/middleclickclose/README.md (100%) rename extensions/{ => 44}/middleclickclose/buildforupload.sh (100%) rename extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/extension.js (100%) rename extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.mo (100%) copy extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po (100%) rename extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.mo (100%) copy extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.po (100%) rename extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.mo (100%) copy extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.po (100%) rename extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.mo (100%) copy extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.po (100%) rename extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.mo (100%) copy extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.po (100%) copy extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/metadata.json (100%) rename extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/prefs.js (100%) rename extensions/{ => 44}/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml (100%) rename extensions/{ => 44}/middleclickclose/tweak-tool-screenshot.png (100%) rename extensions/{ => 44}/multi-monitors-add-on.mk (100%) rename extensions/{ => 44}/multi-monitors-add-on/.project (100%) rename extensions/{ => 44}/multi-monitors-add-on/LICENSE (100%) rename extensions/{ => 44}/multi-monitors-add-on/README.md (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/convenience.js (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/extension.js (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/indicator.js (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/metadata.json (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/mmcalendar.js (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/mmlayout.js (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/mmpanel.js (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/multi-monitors-add-on.pot (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/prefs.js (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/gschemas.compiled (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml (100%) rename extensions/{ => 44}/multi-monitors-add-on/multi-monitors-add-on@spin83/stylesheet.css (100%) copy extensions/{ => 44}/no-overview.mk (100%) rename extensions/{ => 44}/no-overview/LICENSE (100%) copy extensions/{ => 44}/no-overview/README.md (100%) copy extensions/{ => 44}/no-overview/extension.js (100%) create mode 100644 extensions/44/no-overview/metadata.json copy extensions/{ => 44}/vertical-workspaces.mk (100%) copy extensions/{ => 44}/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md (100%) copy extensions/{ => 44}/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md (100%) copy extensions/{ => 44}/vertical-workspaces/CHANGELOG.md (73%) rename extensions/{ => 44}/vertical-workspaces/LICENSE (100%) copy extensions/{ => 44}/vertical-workspaces/Makefile (100%) copy extensions/{ => 44}/vertical-workspaces/README.md (86%) create mode 100644 extensions/44/vertical-workspaces/extension.js rename extensions/{ => 44}/vertical-workspaces/lib/appDisplay.js (51%) create mode 100644 extensions/44/vertical-workspaces/lib/appFavorites.js create mode 100644 extensions/44/vertical-workspaces/lib/dash.js create mode 100644 extensions/44/vertical-workspaces/lib/extensionsSearchProvider.js copy extensions/{ => 44}/vertical-workspaces/lib/iconGrid.js (69%) copy extensions/{ => 44}/vertical-workspaces/lib/layout.js (67%) create mode 100644 extensions/44/vertical-workspaces/lib/messageTray.js copy extensions/{ => 44}/vertical-workspaces/lib/optionsFactory.js (67%) create mode 100644 extensions/44/vertical-workspaces/lib/osdWindow.js create mode 100644 extensions/44/vertical-workspaces/lib/overlayKey.js create mode 100644 extensions/44/vertical-workspaces/lib/overview.js copy extensions/{ => 44}/vertical-workspaces/lib/overviewControls.js (65%) create mode 100644 extensions/44/vertical-workspaces/lib/panel.js create mode 100644 extensions/44/vertical-workspaces/lib/recentFilesSearchProvider.js create mode 100644 extensions/44/vertical-workspaces/lib/search.js create mode 100644 extensions/44/vertical-workspaces/lib/searchController.js copy extensions/{ => 44}/vertical-workspaces/lib/settings.js (66%) rename extensions/{ => 44}/vertical-workspaces/lib/swipeTracker.js (51%) copy extensions/{ => 44}/vertical-workspaces/lib/util.js (69%) create mode 100644 extensions/44/vertical-workspaces/lib/winTmb.js rename extensions/{ => 44}/vertical-workspaces/lib/windowAttentionHandler.js (52%) copy extensions/{ => 44}/vertical-workspaces/lib/windowManager.js (55%) create mode 100644 extensions/44/vertical-workspaces/lib/windowPreview.js copy extensions/{ => 44}/vertical-workspaces/lib/windowSearchProvider.js (54%) rename extensions/{ => 44}/vertical-workspaces/lib/workspace.js (75%) create mode 100644 extensions/44/vertical-workspaces/lib/workspaceAnimation.js create mode 100644 extensions/44/vertical-workspaces/lib/workspaceSwitcherPopup.js copy extensions/{ => 44}/vertical-workspaces/lib/workspaceThumbnail.js (87%) copy extensions/{ => 44}/vertical-workspaces/lib/workspacesView.js (76%) copy extensions/{ => 44}/vertical-workspaces/metadata.json (76%) copy extensions/{ => 44}/vertical-workspaces/po/cs.po (100%) copy extensions/{vertical-workspaces/po/vertical-workspaces.pot => 44/vertical-workspaces/po/nl.po} (56%) copy extensions/{ => 44}/vertical-workspaces/po/vertical-workspaces.pot (100%) rename extensions/{ => 44}/vertical-workspaces/prefs.js (68%) create mode 100644 extensions/44/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml copy extensions/{ => 44}/vertical-workspaces/screenshots/screenshot.jpg (100%) copy extensions/{ => 44}/vertical-workspaces/screenshots/screenshot0.jpg (100%) copy extensions/{ => 44}/vertical-workspaces/screenshots/screenshot0.png (100%) copy extensions/{ => 44}/vertical-workspaces/screenshots/screenshot1.png (100%) copy extensions/{ => 44}/vertical-workspaces/screenshots/screenshot2.png (100%) copy extensions/{ => 44}/vertical-workspaces/screenshots/screenshot3.png (100%) copy extensions/{ => 44}/vertical-workspaces/screenshots/screenshot4.png (100%) copy extensions/{ => 44}/vertical-workspaces/screenshots/screenshot5.png (100%) copy extensions/{ => 44}/vertical-workspaces/screenshots/vertical-workspaces.gif (100%) create mode 100644 extensions/44/vertical-workspaces/stylesheet.css copy extensions/{ => 45}/disable-workspace-switcher.mk (100%) copy extensions/{ => 45}/disable-workspace-switcher/LICENSE (100%) rename extensions/{ => 45}/disable-workspace-switcher/README.md (100%) create mode 100644 extensions/45/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js rename extensions/{ => 45}/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json (90%) rename extensions/{ => 45}/hibernate-status.mk (100%) create mode 100644 extensions/45/hibernate-status/.editorconfig create mode 100644 extensions/45/hibernate-status/.github/workflows/release.yaml rename extensions/{ => 45}/hibernate-status/.gitignore (100%) create mode 100644 extensions/45/hibernate-status/.prettierrc rename extensions/{ => 45}/hibernate-status/LICENSE (100%) rename extensions/{ => 45}/hibernate-status/Makefile (96%) rename extensions/{ => 45}/hibernate-status/README.md (91%) rename extensions/{ => 45}/hibernate-status/buildforupload.sh (100%) create mode 100644 extensions/45/hibernate-status/extension.js rename extensions/{ => 45}/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po (100%) rename extensions/{ => 45}/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po (100%) create mode 100644 extensions/45/hibernate-status/metadata.json create mode 100644 extensions/45/hibernate-status/prefs.js create mode 100644 extensions/45/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml rename extensions/{disable-workspace-switcher.mk => 45/middleclickclose.mk} (83%) create mode 100644 extensions/45/middleclickclose/.gitignore rename extensions/{ => 45}/middleclickclose/LICENSE (100%) create mode 100644 extensions/45/middleclickclose/Makefile create mode 100644 extensions/45/middleclickclose/README.md create mode 100644 extensions/45/middleclickclose/src/extension.js rename extensions/{middleclickclose/middleclickclose@paolo.tranquilli.gmail.com => 45/middleclickclose/src}/metadata.json (75%) rename extensions/{middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po => 45/middleclickclose/src/po/de.po} (74%) create mode 100644 extensions/45/middleclickclose/src/po/el.po rename extensions/{middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES => 45/middleclickclose/src/po}/fr.po (65%) rename extensions/{middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES => 45/middleclickclose/src/po}/it.po (69%) rename extensions/{middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl => 45/middleclickclose/src/po}/nl.po (71%) create mode 100644 extensions/45/middleclickclose/src/po/pt.po rename extensions/{middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES => 45/middleclickclose/src/po}/sk.po (74%) create mode 100644 extensions/45/middleclickclose/src/po/sr.po create mode 100644 extensions/45/middleclickclose/src/po/sr@latin.po create mode 100644 extensions/45/middleclickclose/src/po/template.pot create mode 100644 extensions/45/middleclickclose/src/po/uk.po create mode 100644 extensions/45/middleclickclose/src/prefs.js create mode 100644 extensions/45/middleclickclose/src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml create mode 100644 extensions/45/middleclickclose/src/settingsWatch.js rename extensions/{ => 45}/no-overview.mk (100%) copy extensions/{disable-workspace-switcher => 45/no-overview}/LICENSE (100%) rename extensions/{ => 45}/no-overview/README.md (100%) rename extensions/{ => 45}/no-overview/extension.js (61%) create mode 100644 extensions/45/no-overview/metadata.json rename extensions/{ => 45}/vertical-workspaces.mk (100%) rename extensions/{ => 45}/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md (100%) rename extensions/{ => 45}/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md (100%) rename extensions/{ => 45}/vertical-workspaces/CHANGELOG.md (73%) rename extensions/{disable-workspace-switcher => 45/vertical-workspaces}/LICENSE (100%) rename extensions/{ => 45}/vertical-workspaces/Makefile (100%) rename extensions/{ => 45}/vertical-workspaces/README.md (86%) create mode 100644 extensions/45/vertical-workspaces/extension.js create mode 100644 extensions/45/vertical-workspaces/lib/appDisplay.js create mode 100644 extensions/45/vertical-workspaces/lib/appFavorites.js create mode 100644 extensions/45/vertical-workspaces/lib/dash.js create mode 100644 extensions/45/vertical-workspaces/lib/extensionsSearchProvider.js rename extensions/{ => 45}/vertical-workspaces/lib/iconGrid.js (67%) rename extensions/{ => 45}/vertical-workspaces/lib/layout.js (67%) create mode 100644 extensions/45/vertical-workspaces/lib/messageTray.js rename extensions/{ => 45}/vertical-workspaces/lib/optionsFactory.js (65%) create mode 100644 extensions/45/vertical-workspaces/lib/osdWindow.js create mode 100644 extensions/45/vertical-workspaces/lib/overlayKey.js create mode 100644 extensions/45/vertical-workspaces/lib/overview.js rename extensions/{ => 45}/vertical-workspaces/lib/overviewControls.js (62%) create mode 100644 extensions/45/vertical-workspaces/lib/panel.js create mode 100644 extensions/45/vertical-workspaces/lib/recentFilesSearchProvider.js create mode 100644 extensions/45/vertical-workspaces/lib/search.js create mode 100644 extensions/45/vertical-workspaces/lib/searchController.js rename extensions/{ => 45}/vertical-workspaces/lib/settings.js (66%) create mode 100644 extensions/45/vertical-workspaces/lib/swipeTracker.js rename extensions/{ => 45}/vertical-workspaces/lib/util.js (59%) create mode 100644 extensions/45/vertical-workspaces/lib/winTmb.js create mode 100644 extensions/45/vertical-workspaces/lib/windowAttentionHandler.js rename extensions/{ => 45}/vertical-workspaces/lib/windowManager.js (53%) create mode 100644 extensions/45/vertical-workspaces/lib/windowPreview.js rename extensions/{ => 45}/vertical-workspaces/lib/windowSearchProvider.js (50%) create mode 100644 extensions/45/vertical-workspaces/lib/workspace.js create mode 100644 extensions/45/vertical-workspaces/lib/workspaceAnimation.js create mode 100644 extensions/45/vertical-workspaces/lib/workspaceSwitcherPopup.js rename extensions/{ => 45}/vertical-workspaces/lib/workspaceThumbnail.js (80%) rename extensions/{ => 45}/vertical-workspaces/lib/workspacesView.js (77%) rename extensions/{ => 45}/vertical-workspaces/metadata.json (74%) rename extensions/{ => 45}/vertical-workspaces/po/cs.po (100%) copy extensions/{vertical-workspaces/po/vertical-workspaces.pot => 45/vertical-workspaces/po/nl.po} (56%) rename extensions/{ => 45}/vertical-workspaces/po/vertical-workspaces.pot (100%) create mode 100644 extensions/45/vertical-workspaces/prefs.js create mode 100644 extensions/45/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml rename extensions/{ => 45}/vertical-workspaces/screenshots/screenshot.jpg (100%) rename extensions/{ => 45}/vertical-workspaces/screenshots/screenshot0.jpg (100%) rename extensions/{ => 45}/vertical-workspaces/screenshots/screenshot0.png (100%) rename extensions/{ => 45}/vertical-workspaces/screenshots/screenshot1.png (100%) rename extensions/{ => 45}/vertical-workspaces/screenshots/screenshot2.png (100%) rename extensions/{ => 45}/vertical-workspaces/screenshots/screenshot3.png (100%) rename extensions/{ => 45}/vertical-workspaces/screenshots/screenshot4.png (100%) rename extensions/{ => 45}/vertical-workspaces/screenshots/screenshot5.png (100%) rename extensions/{ => 45}/vertical-workspaces/screenshots/vertical-workspaces.gif (100%) create mode 100644 extensions/45/vertical-workspaces/stylesheet.css create mode 120000 extensions/current delete mode 100644 extensions/vertical-workspaces/extension.js delete mode 100644 extensions/vertical-workspaces/lib/appFavorites.js delete mode 100644 extensions/vertical-workspaces/lib/dash.js delete mode 100644 extensions/vertical-workspaces/lib/messageTray.js delete mode 100644 extensions/vertical-workspaces/lib/osdWindow.js delete mode 100644 extensions/vertical-workspaces/lib/overlayKey.js delete mode 100644 extensions/vertical-workspaces/lib/overview.js delete mode 100644 extensions/vertical-workspaces/lib/panel.js delete mode 100644 extensions/vertical-workspaces/lib/recentFilesSearchProvider.js delete mode 100644 extensions/vertical-workspaces/lib/search.js delete mode 100644 extensions/vertical-workspaces/lib/windowPreview.js delete mode 100644 extensions/vertical-workspaces/lib/workspaceAnimation.js delete mode 100644 extensions/vertical-workspaces/lib/workspaceSwitcherPopup.js delete mode 100644 extensions/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml delete mode 100644 extensions/vertical-workspaces/stylesheet.css diff --git a/CHANGELOG.txt b/CHANGELOG.txt index d61f76f..5188f77 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,11 +1,27 @@ +2023-12-10 Daniel Baumann + + * Releasing version 20231210. + + [ Daniel Baumann ] + * Moving current extensions to subdirectory for GNOME 44 as GNOME Shell 45 is backwards incompatible (see Debian #1052112). + * Updating 44/hibernate-status to version 1.13 [d3280b8]. + * Updating 44/no-overview to version 44 [68db01d]. + * Updating 44/vertical-workspaces to version 37+20231208 [0d82192]. + * Setting extension default symlink to 45. + * Adding 45/disable-workspace-switcher version 3+20231017 [e4b8d3e]. + * Adding 45/hibernate-status version 1.14-rc2 [debf9ae]. + * Adding 45/middleclickclose version 29 [c6f86c2]. + * Adding 45/no-overview version 45 [d1268aa]. + * Adding 45/vertical-workspaces version 37+20231208 [0c80cb1]. + 2023-06-18 Daniel Baumann * Releasing version 20230618. [ Daniel Baumann ] * Adding todo file. - * Updating disable-workspace-switcher to version 2 [39fded2]. - * Updating hibernate-status to version 20230505 [d11284d]. + * Updating disable-workspace-switcher to version 3 [39fded2]. + * Updating hibernate-status to version 1.12 [d11284d]. * Updating middleclickclose to version 1.9.1 [f1e773d]. * Updating multi-monitors-add-on to version 26 [d8ea040]. * Updating vertical-workspaces to version 28 [891a8df]. @@ -38,5 +54,5 @@ [ Daniel Baumann ] * Adding disable-workspace-switcher version 2 (8d94260). - * Adding hibernate-status version 20230113 (bfca991). + * Adding hibernate-status version 1.11+20230113 (bfca991). * Adding multi-monitors-add-on version 25 (515dfd0). diff --git a/Makefile b/Makefile index 20376ee..eed2f8b 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,10 @@ SHELL := sh -e SOFTWARE = gnome-shell-extensions-extra VERSION := $(shell cat VERSION.txt) +EXTENSIONS_VERSION := current + extensions-%: - @cd extensions; \ + @cd extensions/$(EXTENSIONS_VERSION); \ for EXTENSION in *.mk; \ do \ echo "################################################################################"; \ diff --git a/VERSION.txt b/VERSION.txt index 8f07d5b..19d30f1 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -20230618 +20231210 diff --git a/extensions/disable-workspace-switcher.mk b/extensions/44/disable-workspace-switcher.mk similarity index 100% copy from extensions/disable-workspace-switcher.mk copy to extensions/44/disable-workspace-switcher.mk diff --git a/extensions/disable-workspace-switcher/LICENSE b/extensions/44/disable-workspace-switcher/LICENSE similarity index 100% copy from extensions/disable-workspace-switcher/LICENSE copy to extensions/44/disable-workspace-switcher/LICENSE diff --git a/extensions/disable-workspace-switcher/README.md b/extensions/44/disable-workspace-switcher/README.md similarity index 100% copy from extensions/disable-workspace-switcher/README.md copy to extensions/44/disable-workspace-switcher/README.md diff --git a/extensions/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js b/extensions/44/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js similarity index 100% rename from extensions/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js rename to extensions/44/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js diff --git a/extensions/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json b/extensions/44/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json similarity index 100% copy from extensions/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json copy to extensions/44/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json diff --git a/extensions/hibernate-status.mk b/extensions/44/hibernate-status.mk similarity index 100% copy from extensions/hibernate-status.mk copy to extensions/44/hibernate-status.mk diff --git a/extensions/hibernate-status/.gitignore b/extensions/44/hibernate-status/.gitignore similarity index 100% copy from extensions/hibernate-status/.gitignore copy to extensions/44/hibernate-status/.gitignore diff --git a/extensions/hibernate-status/LICENSE b/extensions/44/hibernate-status/LICENSE similarity index 100% copy from extensions/hibernate-status/LICENSE copy to extensions/44/hibernate-status/LICENSE diff --git a/extensions/hibernate-status/Makefile b/extensions/44/hibernate-status/Makefile similarity index 100% copy from extensions/hibernate-status/Makefile copy to extensions/44/hibernate-status/Makefile diff --git a/extensions/hibernate-status/README.md b/extensions/44/hibernate-status/README.md similarity index 100% copy from extensions/hibernate-status/README.md copy to extensions/44/hibernate-status/README.md diff --git a/extensions/hibernate-status/buildforupload.sh b/extensions/44/hibernate-status/buildforupload.sh similarity index 100% copy from extensions/hibernate-status/buildforupload.sh copy to extensions/44/hibernate-status/buildforupload.sh diff --git a/extensions/hibernate-status/confirmDialog.js b/extensions/44/hibernate-status/confirmDialog.js similarity index 100% rename from extensions/hibernate-status/confirmDialog.js rename to extensions/44/hibernate-status/confirmDialog.js index 62069e2..8d56ed9 100644 --- a/extensions/hibernate-status/confirmDialog.js +++ b/extensions/44/hibernate-status/confirmDialog.js @@ -164,12 +164,12 @@ class ConfirmDialog extends ModalDialog.ModalDialog { let keys = dialog.confirmButtons[i].key; buttons.push({ action: () => { - this.close(); let signalId = this.connect('closed', () => { this.disconnect(signalId); this._confirm(signal); }); + this.close(); }, label: label, key: keys diff --git a/extensions/hibernate-status/extension.js b/extensions/44/hibernate-status/extension.js similarity index 100% rename from extensions/hibernate-status/extension.js rename to extensions/44/hibernate-status/extension.js diff --git a/extensions/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po b/extensions/44/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po similarity index 100% copy from extensions/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po copy to extensions/44/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/metadata.json b/extensions/44/hibernate-status/metadata.json similarity index 100% rename from extensions/hibernate-status/metadata.json rename to extensions/44/hibernate-status/metadata.json diff --git a/extensions/hibernate-status/prefs.js b/extensions/44/hibernate-status/prefs.js similarity index 100% rename from extensions/hibernate-status/prefs.js rename to extensions/44/hibernate-status/prefs.js diff --git a/extensions/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml b/extensions/44/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml similarity index 100% rename from extensions/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml rename to extensions/44/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml diff --git a/extensions/middleclickclose.mk b/extensions/44/middleclickclose.mk similarity index 100% rename from extensions/middleclickclose.mk rename to extensions/44/middleclickclose.mk diff --git a/extensions/middleclickclose/.gitignore b/extensions/44/middleclickclose/.gitignore similarity index 100% rename from extensions/middleclickclose/.gitignore rename to extensions/44/middleclickclose/.gitignore diff --git a/extensions/middleclickclose/LICENSE b/extensions/44/middleclickclose/LICENSE similarity index 100% copy from extensions/middleclickclose/LICENSE copy to extensions/44/middleclickclose/LICENSE diff --git a/extensions/middleclickclose/README.md b/extensions/44/middleclickclose/README.md similarity index 100% rename from extensions/middleclickclose/README.md rename to extensions/44/middleclickclose/README.md diff --git a/extensions/middleclickclose/buildforupload.sh b/extensions/44/middleclickclose/buildforupload.sh similarity index 100% rename from extensions/middleclickclose/buildforupload.sh rename to extensions/44/middleclickclose/buildforupload.sh diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/extension.js b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/extension.js similarity index 100% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/extension.js rename to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/extension.js diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.mo b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.mo similarity index 100% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.mo rename to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.mo diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po similarity index 100% copy from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po copy to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.mo b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.mo similarity index 100% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.mo rename to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.mo diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.po b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.po similarity index 100% copy from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.po copy to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.po diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.mo b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.mo similarity index 100% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.mo rename to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.mo diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.po b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.po similarity index 100% copy from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.po copy to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.po diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.mo b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.mo similarity index 100% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.mo rename to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.mo diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.po b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.po similarity index 100% copy from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.po copy to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.po diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.mo b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.mo similarity index 100% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.mo rename to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.mo diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.po b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.po similarity index 100% copy from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.po copy to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.po diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/metadata.json b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/metadata.json similarity index 100% copy from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/metadata.json copy to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/metadata.json diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/prefs.js b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/prefs.js similarity index 100% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/prefs.js rename to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/prefs.js diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml b/extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml similarity index 100% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml rename to extensions/44/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml diff --git a/extensions/middleclickclose/tweak-tool-screenshot.png b/extensions/44/middleclickclose/tweak-tool-screenshot.png similarity index 100% rename from extensions/middleclickclose/tweak-tool-screenshot.png rename to extensions/44/middleclickclose/tweak-tool-screenshot.png diff --git a/extensions/multi-monitors-add-on.mk b/extensions/44/multi-monitors-add-on.mk similarity index 100% rename from extensions/multi-monitors-add-on.mk rename to extensions/44/multi-monitors-add-on.mk diff --git a/extensions/multi-monitors-add-on/.project b/extensions/44/multi-monitors-add-on/.project similarity index 100% rename from extensions/multi-monitors-add-on/.project rename to extensions/44/multi-monitors-add-on/.project diff --git a/extensions/multi-monitors-add-on/LICENSE b/extensions/44/multi-monitors-add-on/LICENSE similarity index 100% rename from extensions/multi-monitors-add-on/LICENSE rename to extensions/44/multi-monitors-add-on/LICENSE diff --git a/extensions/multi-monitors-add-on/README.md b/extensions/44/multi-monitors-add-on/README.md similarity index 100% rename from extensions/multi-monitors-add-on/README.md rename to extensions/44/multi-monitors-add-on/README.md diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/convenience.js b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/convenience.js similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/convenience.js rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/convenience.js diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/extension.js b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/extension.js similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/extension.js rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/extension.js diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/indicator.js b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/indicator.js similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/indicator.js rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/indicator.js diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/metadata.json b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/metadata.json similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/metadata.json rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/metadata.json diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmcalendar.js b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/mmcalendar.js similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmcalendar.js rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/mmcalendar.js diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmlayout.js b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/mmlayout.js similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmlayout.js rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/mmlayout.js diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/mmoverview.js diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmpanel.js b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/mmpanel.js similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/mmpanel.js rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/mmpanel.js diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/multi-monitors-add-on.pot b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/multi-monitors-add-on.pot similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/multi-monitors-add-on.pot rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/multi-monitors-add-on.pot diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/prefs.js b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/prefs.js similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/prefs.js rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/prefs.js diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/gschemas.compiled b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/gschemas.compiled similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/gschemas.compiled rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/gschemas.compiled diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml diff --git a/extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/stylesheet.css b/extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/stylesheet.css similarity index 100% rename from extensions/multi-monitors-add-on/multi-monitors-add-on@spin83/stylesheet.css rename to extensions/44/multi-monitors-add-on/multi-monitors-add-on@spin83/stylesheet.css diff --git a/extensions/no-overview.mk b/extensions/44/no-overview.mk similarity index 100% copy from extensions/no-overview.mk copy to extensions/44/no-overview.mk diff --git a/extensions/no-overview/LICENSE b/extensions/44/no-overview/LICENSE similarity index 100% rename from extensions/no-overview/LICENSE rename to extensions/44/no-overview/LICENSE diff --git a/extensions/no-overview/README.md b/extensions/44/no-overview/README.md similarity index 100% copy from extensions/no-overview/README.md copy to extensions/44/no-overview/README.md diff --git a/extensions/no-overview/extension.js b/extensions/44/no-overview/extension.js similarity index 100% copy from extensions/no-overview/extension.js copy to extensions/44/no-overview/extension.js diff --git a/extensions/44/no-overview/metadata.json b/extensions/44/no-overview/metadata.json new file mode 100644 index 0000000..6d158a3 --- /dev/null +++ b/extensions/44/no-overview/metadata.json @@ -0,0 +1,18 @@ +{ + "_generated": "Generated by SweetTooth, do not edit", + "description": "No overview at start-up. For GNOME Shell 40+.", + "name": "No overview at start-up", + "original-authors": [ + "fthx" + ], + "shell-version": [ + "40", + "41", + "42", + "43", + "44" + ], + "url": "https://github.com/fthx/no-overview", + "uuid": "no-overview@fthx", + "version": 13 +} diff --git a/extensions/vertical-workspaces.mk b/extensions/44/vertical-workspaces.mk similarity index 100% copy from extensions/vertical-workspaces.mk copy to extensions/44/vertical-workspaces.mk diff --git a/extensions/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md b/extensions/44/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md similarity index 100% copy from extensions/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md copy to extensions/44/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/extensions/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md b/extensions/44/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md similarity index 100% copy from extensions/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md copy to extensions/44/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md diff --git a/extensions/vertical-workspaces/CHANGELOG.md b/extensions/44/vertical-workspaces/CHANGELOG.md similarity index 73% copy from extensions/vertical-workspaces/CHANGELOG.md copy to extensions/44/vertical-workspaces/CHANGELOG.md index 0beb20c..e8fd632 100644 --- a/extensions/vertical-workspaces/CHANGELOG.md +++ b/extensions/44/vertical-workspaces/CHANGELOG.md @@ -1,4 +1,100 @@ ## Changelog: +### v45.2 for GNOME 45.2+, v44.10 for GNOME 42-44 (2023-12-02) +**Added:** +- *Dash* option *Isolate Workspaces* on *Behavior* tab +- *Brightness for Search View* option allows adjusting background wallpaper brightness in overview search view +- *Extensions Search Provider* module allows to search for extensions from the overview, open their settings and enable or disable them + + +### v45.1 for GNOME 45.1+ (EGO 2023-11-25) v44.9 for GNOME 42-44 (not released) +**Added:** +- *Window Thumbnail - PIP* option in app icon menu and as an click actions for Window Preview +- Workspace switcher options *Wraparound* and *Ignore Last (empty)* +- *Overlay key (Super)* and *Hot Corner* actions offer different overview modes independent on global *Overview Mode* +- Workspace thumbnails background without wallpaper is now semi-transparent to match other overview elements +- App Grid sorting options allow alphabetical order with folders + +**Fixed:** +- App grid has less rows than it should +- Many minor fixes that reflect gnome-shell development and backports +- Centered app folder position on multi-monitor system +- Window can't be activated using touchscreen +- Setting background wallpaper too early on startup can crash Wayland session + +**Other changes:** +- Since GNOME 45 V-Shell has 2 versions - one for GS 45 and the second for GS 42-44. Versioning no longer depends on EGO's upload counter +- Dash light style background opacity is not limited +- Refactored Recent Files Search Provider and other modules +- Settings window - ComboBox replaced with (finally fixed) DropDown + + +### v37 (2023-07-01) +**Fixed:** +- False detections of conflicting extensions + + +### v36 (2023-07-01) (35/34/33/32/31 skipped due to issues with extensions.gnome.org) +**Added:** +- Option *Fix New Windows Not In Focus* on *Misc* tab + +**Fixed:** +- Improved compatibility with Dash to Dock extension - hidden dock in the overview, broken layout after startup +- Compatibility with Hide Top Bar extension +- App search provider ignores non-localized app names +- App folders grid dimensions wrong if set to *Adaptive* and folder icon is set to a fixed size +- Dash and active app folder icons running indicator position +- Blur/Brightness transitions in static overview mode +- Hot corner edge barrier can be active even if hot corner is disabled +- Disabling dash module does not reset dash position to default + +**Other changes:** +- Removed css class reducing Quick Settings buttons height in GNOME 44 + + +### v30 (2023-06-09) +**Added:** +- Dash option - Click Behavior: *Prefer Current Workspace* - opens a new window if app not present on the active workspace +- Window search provider sorting options +- Esc key behavior options +- Window preview - middle and secondary mouse button behavior options, close button can be hidden +- GNOME 3 vertical dash style is now optional +- Window preview title position option +- Light dash background option +- Remove app folder button in folder dialog +- *Updating V-Shell* banner appears during updating V-Shell settings when settings window is irresponsive +- Dutch translation by @Vistaus + +**Fixed:** +- Dash icon scroll action conflicts with Dash to Dock +- Open new window by middle click on app icon or Ctrl+Enter doesn't work +- Dash icon label can extend to the adjacent display +- WindowPreview module not updated when "always-activate-selected-window" changed +- App folder dialog position if secondary monitor connected +- App folder dialog sizing and positioning +- Background brightness in search view reduced independently to avoid unreadable text and consistent style +- Compatibility with Burn My Windows - freeze after screen unlocked, or extensions re-enabled +- Window and Recent files search providers modes not isolated well from results of other providers +- Recent file search provider results sorting +- App grid icons with multi-line label move on hover when label expands +- Search view animation skipped id triggered from app grid state +- DING desktop icons not visible during static background workspace animation + + +**Other changes:** +- Added `unlock-dialog` session mode to avoid unnecessary system load when using screen lock +- App Grid refactored, added transparent app folder dialogs on clean background +- Search view transparency and fixed background brightness in classic overview +- Search view in static workspace overview with full opacity and close to default style +- Settings window - Profiles tab moved at first position, Dash icons position options moved back to layout +- Updated default profiles + + +### v29 (2023-04-11) +**Fixed:** +- Window switcher/highlighter logic when scrolling over an dash icon +- Unhandled promise rejection warnings on GS 43+ + + ### v28 (2023-04-06) **Added:** - App Grid - vertical app folder orientation diff --git a/extensions/vertical-workspaces/LICENSE b/extensions/44/vertical-workspaces/LICENSE similarity index 100% rename from extensions/vertical-workspaces/LICENSE rename to extensions/44/vertical-workspaces/LICENSE diff --git a/extensions/vertical-workspaces/Makefile b/extensions/44/vertical-workspaces/Makefile similarity index 100% copy from extensions/vertical-workspaces/Makefile copy to extensions/44/vertical-workspaces/Makefile diff --git a/extensions/vertical-workspaces/README.md b/extensions/44/vertical-workspaces/README.md similarity index 86% copy from extensions/vertical-workspaces/README.md copy to extensions/44/vertical-workspaces/README.md index ebff9df..449bafa 100644 --- a/extensions/vertical-workspaces/README.md +++ b/extensions/44/vertical-workspaces/README.md @@ -2,7 +2,7 @@ A GNOME Shell extension that lets you customize your GNOME Shell UX to suit your workflow, whether you like horizontally or vertically stacked workspaces. -Currently supported GNOME versions: 42, 43, 44 +Currently supported GNOME versions: 42 - 45 [](https://extensions.gnome.org/extension/5177/vertical-workspaces/) @@ -17,6 +17,7 @@ Currently supported GNOME versions: 42, 43, 44 - Support for secondary monitors, workspace thumbnails can be placed on the opposite side than on the primary monitor - Wallpaper background with adjustable blur effect and brightness in the overview - Custom Dash icon size and on-click/scroll behavior +- Optional workspace isolated Dash - Dash background transparency and corner radius adjustments - Adjustable app grid icon size, number of columns and rows, content, optional active and draggable icons in folder preview in optional 3x3 grid - Custom search view width, app results icons size and number of result lists rows, improved app search @@ -26,12 +27,14 @@ Currently supported GNOME versions: 42, 43, 44 - Control over transition animations, including speed - Window search provider with *Space* hotkey allows quick window navigation - Recent files search provider with *Ctrl + Space* hotkey +- Extensions search provider with *Ctrl + Shift + Space* hotkey allows to search for extensions, open their settings and enable or disable them - Reorder workspaces in overview using *Shift + Scroll* or *Shift + Page Up/Down* - Adds *Force Quit*, *Close Windows on Current Workspace* and *Move Windows to Current Workspace* items to app icon menu. The latter action can be activated using *Shift + click* on app icon - Change notification banners and OSD popups position - Window attention handler options can activate the attention-demanding window immediately or silence its notification - Optional position of the hot corner that can follow the dash and expand to hot edge - Super key double-press options +- Window thumbnail (PIP) options allows you to create scaled down clone of the window by clicking on its preview in the overview (secondary mouse buttons or window preview icon) ## Changelog [CHANGELOG.md](CHANGELOG.md) @@ -51,14 +54,23 @@ Install the extension (`--force` switch needs to be used only if some version of gnome-extensions install --force vertical-workspaces@G-dH.github.com.zip ### Installation from GitHub repository -The most recent version in the repository is the one I'm currently using and developing on my own systems, problems may occur, but usually nothing serious. The repository version may change often and doesn't updates automatically on your system, but once the stable release shows up on extensions.gnome.org, it should be updated automatically from there. If you want to help me, use this latest version and report bugs. -You may need to install `git`, `gettext` and `glib2.0` for successful installation. +The most recent version in the repository is the one I'm currently using and developing on my own systems, problems may occur, but usually nothing serious. The repository version may change often and doesn't updates automatically on your system. If you want to help me, use this latest version and report bugs. +You may need to install `git`, `make`, `gettext` and `glib2.0` for successful installation. Navigate to the directory you want to download the source code and execute following commands in the terminal: +GNOME 45: + git clone https://github.com/G-dH/vertical-workspaces.git cd vertical-workspaces make install +GNOME 42 - 44: + + git clone https://github.com/G-dH/vertical-workspaces.git + cd vertical-workspaces + git checkout gnome-42-44 + make install + If you get `Can't recursively copy directory` error, take a look at issue #51. ### Enabling the extension diff --git a/extensions/44/vertical-workspaces/extension.js b/extensions/44/vertical-workspaces/extension.js new file mode 100644 index 0000000..911d7dd --- /dev/null +++ b/extensions/44/vertical-workspaces/extension.js @@ -0,0 +1,793 @@ +/** + * V-Shell (Vertical Workspaces) + * extension.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const St = imports.gi.St; + +const Main = imports.ui.main; +const Search = imports.ui.search; +const Workspace = imports.ui.workspace; + +const ExtensionUtils = imports.misc.extensionUtils; +const MyExtension = ExtensionUtils.getCurrentExtension(); + +let Me; +// gettext +let _; +let opt; + +function init() { + ExtensionUtils.initTranslations(); + return new Extension(); +} + +class Extension { + _init() { + Me = {}; + + Me.shellVersion = parseFloat(imports.misc.config.PACKAGE_VERSION); + Me.imports = MyExtension.imports; + Me.metadata = MyExtension.metadata; + Me.gSettings = ExtensionUtils.getSettings(Me.metadata['settings-schema']); + Me.Settings = MyExtension.imports.lib.settings; + Me.gettext = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; + Me.Util = MyExtension.imports.lib.util; + + Me.Modules = this._importModules(); + Me.moduleList = this._getModuleList(); + + Me.WSP_PREFIX = Me.Modules.windowSearchProviderModule._PREFIX; + Me.RFSP_PREFIX = Me.Modules.recentFilesSearchProviderModule._PREFIX; + Me.ESP_PREFIX = Me.Modules.extensionsSearchProviderModule._PREFIX; + + Me.opt = new Me.Settings.Options(Me); + _ = Me.gettext; + opt = Me.opt; + + Me.Util.init(Me); + + Me.moduleList.forEach(module => { + Me.Modules[module] = new Me.Modules[module](Me); + }); + + Me.repairOverrides = this._repairOverrides; + } + + _importModules() { + return { + appDisplayModule: Me.imports.lib.appDisplay.AppDisplayModule, + appFavoritesModule: Me.imports.lib.appFavorites.AppFavoritesModule, + dashModule: Me.imports.lib.dash.DashModule, + iconGridModule: Me.imports.lib.iconGrid.IconGridModule, + layoutModule: Me.imports.lib.layout.LayoutModule, + messageTrayModule: Me.imports.lib.messageTray.MessageTrayModule, + osdWindowModule: Me.imports.lib.osdWindow.OsdWindowModule, + overviewModule: Me.imports.lib.overview.OverviewModule, + overlayKeyModule: Me.imports.lib.overlayKey.OverlayKeyModule, + overviewControlsModule: Me.imports.lib.overviewControls.OverviewControlsModule, + panelModule: Me.imports.lib.panel.PanelModule, + searchModule: Me.imports.lib.search.SearchModule, + searchControllerModule: Me.imports.lib.searchController.SearchControllerModule, + swipeTrackerModule: Me.imports.lib.swipeTracker.SwipeTrackerModule, + windowAttentionHandlerModule: Me.imports.lib.windowAttentionHandler.WindowAttentionHandlerModule, + windowManagerModule: Me.imports.lib.windowManager.WindowManagerModule, + windowPreviewModule: Me.imports.lib.windowPreview.WindowPreviewModule, + workspaceAnimationModule: Me.imports.lib.workspaceAnimation.WorkspaceAnimationModule, + workspaceModule: Me.imports.lib.workspace.WorkspaceModule, + workspaceSwitcherPopupModule: Me.imports.lib.workspaceSwitcherPopup.WorkspaceSwitcherPopupModule, + workspaceThumbnailModule: Me.imports.lib.workspaceThumbnail.WorkspaceThumbnailModule, + workspacesViewModule: Me.imports.lib.workspacesView.WorkspacesViewModule, + windowSearchProviderModule: Me.imports.lib.windowSearchProvider.WindowSearchProviderModule, + winTmbModule: Me.imports.lib.winTmb.WinTmbModule, + recentFilesSearchProviderModule: Me.imports.lib.recentFilesSearchProvider.RecentFilesSearchProviderModule, + extensionsSearchProviderModule: Me.imports.lib.extensionsSearchProvider.ExtensionsSearchProviderModule, + }; + } + + _getModuleList() { + return Object.keys(Me.Modules); + } + + _cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + enable() { + this._init(); + // flag for Util.getEnabledExtensions() + Me.extensionsLoadIncomplete = Main.layoutManager._startingUp; + + this._activateVShell(); + Me.extensionsLoadIncomplete = false; + console.debug(`${Me.metadata.name}: enabled`); + } + + // Reason for using "unlock-dialog" session mode: + // Updating the "appDisplay" content every time the screen is locked/unlocked takes quite a lot of time and affects the user experience. + disable() { + this._removeVShell(); + this._disposeModules(); + + // If Dash to Dock is enabled, disabling V-Shell can end in broken overview + Main.overview.hide(); + + console.debug(`${Me.metadata.name}: disabled`); + + this._cleanGlobals(); + } + + _disposeModules() { + Me.opt.destroy(); + Me.opt = null; + + for (let module of Me.moduleList) { + if (!Me.Modules[module].moduleEnabled) + Me.Modules[module].cleanGlobals(); + } + + Me.Util.cleanGlobals(); + Me.Modules = null; + } + + _activateVShell() { + this._enabled = true; + + this._originalGetNeighbor = Meta.Workspace.prototype.get_neighbor; + + this._removeTimeouts(); + this._timeouts = {}; + + // load VShell configuration + this._updateSettings(); + + // activate all enabled VShell modules + this._updateOverrides(); + + // connect signals to help VShell adapt to changes in DE configuration + this._updateConnections(); + + // switch PageUp/PageDown workspace switcher shortcuts + this._switchPageShortcuts(); + + // if Dash to Dock detected force enable "Fix for DtD" option + this._updateFixDashToDockOption(); + + // update overview background wallpaper if enabled, but don't set it too early on session startup + // because it crashes wayland + if (!Main.layoutManager._startingUp || Meta.is_restart()) + Main.overview._overview.controls._setBackground(); + + this._updateSettingsConnection(); + + // store dash _workId so we will be able to detect replacement when entering overview + this._storeDashId(); + + // workaround for upstream bug - overview always shows workspace 1 instead of the active one after restart + this._setInitialWsIndex(); + } + + _removeVShell() { + this._enabled = false; + + const reset = true; + this._removeTimeouts(); + + this._removeConnections(); + Main.overview._overview.controls._setBackground(reset); + + // remove changes mede by VShell modules + this._updateOverrides(reset); + + // switch PageUp/PageDown workspace switcher shortcuts + this._switchPageShortcuts(); + + // remove any position offsets from dash and ws thumbnails + if (!Me.Util.dashNotDefault()) { + Main.overview.dash.translation_x = 0; + Main.overview.dash.translation_y = 0; + } + Main.overview._overview._controls._thumbnailsBox.translation_x = 0; + Main.overview._overview._controls._thumbnailsBox.translation_y = 0; + Main.overview._overview._controls._searchEntryBin.translation_y = 0; + Main.overview._overview._controls.set_child_above_sibling(Main.overview._overview._controls._workspacesDisplay, null); + // restore default animation speed + St.Settings.get().slow_down_factor = 1; + + // restore default dash background style + Main.overview.dash._background.set_style(''); + // hide status message if shown + this._showStatusMessage(false); + this._prevDash = null; + + Meta.Workspace.prototype.get_neighbor = this._originalGetNeighbor; + } + + _removeTimeouts() { + if (this._timeouts) { + Object.values(this._timeouts).forEach(id => { + if (id) + GLib.source_remove(id); + }); + } + this._timeouts = null; + } + + _storeDashId() { + const dash = Main.overview.dash; + this._prevDash = dash._workId; + } + + _setInitialWsIndex() { + if (Main.layoutManager._startingUp) { + GLib.idle_add(GLib.PRIORITY_LOW, () => { + Main.overview._overview.controls._workspaceAdjustment.set_value(global.workspace_manager.get_active_workspace_index()); + }); + } + } + + _updateSettingsConnection() { + if (!opt._extensionUpdateId) + opt._extensionUpdateId = opt.connect('changed', this._updateSettings.bind(this)); + } + + _updateFixDashToDockOption() { + const dtdEnabled = !!(Me.Util.getEnabledExtensions('dash-to-dock').length || + Me.Util.getEnabledExtensions('ubuntu-dock').length); + + // force enable Fix Dash to Dock option if DtD detected + opt._watchDashToDock = dtdEnabled; + // opt.set('fixUbuntuDock', dtdEnabled); + } + + _updateConnections() { + if (!this._monitorsChangedConId) + this._monitorsChangedConId = Main.layoutManager.connect('monitors-changed', () => this._updateVShell(2000)); + + + if (!this._showingOverviewConId) + this._showingOverviewConId = Main.overview.connect('showing', this._onShowingOverview.bind(this)); + + if (!this._sessionModeConId) { + // the panel must be visible when screen is locked + this._sessionModeConId = Main.sessionMode.connect('updated', session => { + if (session.currentMode === 'user' || session.parentMode === 'user') { + this._timeouts.unlock = GLib.idle_add(GLib.PRIORITY_LOW, + () => { + Me.Modules.panelModule.update(); + Me.Modules.overviewControlsModule.update(); + Me.Modules.winTmbModule.showThumbnails(); + + this._timeouts.unlock = 0; + return GLib.SOURCE_REMOVE; + } + ); + } else if (session.currentMode === 'unlock-dialog') { + Me.Modules.panelModule.update(true); + Me.Modules.winTmbModule.hideThumbnails(); + } + }); + } + + if (!this._watchDockSigId) { + this._watchDockSigId = Main.extensionManager.connect('extension-state-changed', + (source, extension) => { + const uuid = extension.uuid; + // ExtensionState = { + // ENABLED: 1, + // DISABLED: 2, + // ERROR: 3, + // OUT_OF_DATE: 4, + // DOWNLOADING: 5, + // INITIALIZED: 6, + // DISABLING: 7, + // ENABLING: 8, + // + // // Used as an error state for operations on unknown extensions, + // // should never be in a real extensionMeta object. + // UNINSTALLED: 99, + // }; + // no need to restart on disable/remove + // - if DtD was enabled before VShell, VShell will be rebased by extensionSystem + // - if DtD was enabled after VShell, the first _showingOverview detect replacement of the dash and repair VShell + const reset = [1, 2].includes(extension.state); + const dashReplacement = uuid.includes('dash-to-dock') || uuid.includes('ubuntu-dock') || uuid.includes('dash-to-panel'); + if (dashReplacement && reset) + opt._watchDashToDock = true; + if (!Main.layoutManager._startingUp && reset && dashReplacement) + this._updateVShell(1999); + } + ); + } + } + + _removeConnections() { + if (this._monitorsChangedConId) { + Main.layoutManager.disconnect(this._monitorsChangedConId); + this._monitorsChangedConId = 0; + } + + if (this._showingOverviewConId) { + Main.overview.disconnect(this._showingOverviewConId); + this._showingOverviewConId = 0; + } + + if (this._sessionModeConId) { + Main.sessionMode.disconnect(this._sessionModeConId); + this._sessionModeConId = 0; + } + + if (this._watchDockSigId) { + Main.extensionManager.disconnect(this._watchDockSigId); + this._watchDockSigId = 0; + } + } + + _updateOverrides(reset = false) { + Me.Modules.workspacesViewModule.update(reset); + Me.Modules.workspaceThumbnailModule.update(reset); + Me.Modules.overviewModule.update(reset); + Me.Modules.overviewControlsModule.update(reset); + + Me.Modules.workspaceModule.update(reset); + Me.Modules.windowPreviewModule.update(reset); + Me.Modules.windowManagerModule.update(reset); + + Me.Modules.layoutModule.update(reset); + Me.Modules.dashModule.update(reset); + // avoid enabling panel module when session is locked + if (reset || (!reset && !Main.sessionMode.isLocked)) + Me.Modules.panelModule.update(reset); + // the panel must be visible when screen is locked + // at startup time, panel will be updated from the startupAnimation after allocation + if (!reset && Main.sessionMode.isLocked && !Main.layoutManager._startingUp) + Me.Modules.panelModule._showPanel(true); + // PanelModule._showPanel(true); + // hide panel so it appears directly on the final place + /* else if (Main.layoutManager._startingUp && !Meta.is_restart()) + Main.panel.opacity = 0;*/ + + Me.Modules.workspaceAnimationModule.update(reset); + Me.Modules.workspaceSwitcherPopupModule.update(reset); + + Me.Modules.swipeTrackerModule.update(reset); + + Me.Modules.searchModule.update(reset); + + Me.Modules.windowSearchProviderModule.update(reset); + Me.Modules.recentFilesSearchProviderModule.update(reset); + Me.Modules.extensionsSearchProviderModule.update(reset); + + // don't rebuild app grid on any screen lock + // even if the extension includes unlock-screen session mode + // disable/enable is called at least once even on GS44 + // when screen lock is activated for the first time + // because every first disable of each extension rebases + // the entire extensions stack that was enabled later + if (Main.sessionMode.isLocked) + this._sessionLockActive = true; + + // This covers unnecessary enable/disable cycles during first screen lock when extensions are rebased, but is not allowed by the EGO rules + if (!this._sessionLockActive || !Main.extensionManager._getEnabledExtensions().includes(Me.metadata.uuid)) { + // iconGridModule will be updated from appDisplayModule + Me.Modules.appDisplayModule.update(reset); + } + + if (!this._sessionLockActive && !Main.layoutManager._startingUp && opt.APP_GRID_PERFORMANCE) { + // Avoid showing status at startup, can cause freeze + this._showStatusMessage(); + } + + if (!Main.sessionMode.isLocked) + this._sessionLockActive = false; + + // iconGridModule will be updated from appDisplayModule + // Me.Modules.appDisplayModule.update(reset); + + Me.Modules.windowAttentionHandlerModule.update(reset); + Me.Modules.appFavoritesModule.update(reset); + Me.Modules.messageTrayModule.update(reset); + Me.Modules.osdWindowModule.update(reset); + Me.Modules.overlayKeyModule.update(reset); + Me.Modules.searchControllerModule.update(reset); + Me.Modules.winTmbModule.update(reset); + + if (!reset && !Main.layoutManager._startingUp) + Main.overview._overview.controls.setInitialTranslations(); + } + + _onShowingOverview() { + if (Main.layoutManager._startingUp) + return; + + Main.overview._overview.controls.opacity = 255; + + // store pointer X coordinate for OVERVIEW_MODE 1 window spread - if mouse pointer is steady, don't spread + opt.showingPointerX = global.get_pointer()[0]; + + if (!Main.overview._overview.controls._bgManagers && (opt.SHOW_BG_IN_OVERVIEW || opt.SHOW_WS_PREVIEW_BG)) + Main.overview._overview.controls._setBackground(); + + if (opt._watchDashToDock) { + // workaround for Dash to Dock (Ubuntu Dock) breaking overview allocations after enabled and changed position + // DtD replaces dock and its _workId on every position change + const dash = Main.overview.dash; + if (this._prevDash !== dash._workId) + this._updateVShell(0); + } + } + + _updateVShell(timeout = 200) { + if (!this._enabled || Main.layoutManager._startingUp) + return; + + if (this._timeouts.reset) + GLib.source_remove(this._timeouts.reset); + this._timeouts.reset = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + timeout, + () => { + if (!this._enabled) + return GLib.SOURCE_REMOVE; + + const dash = Main.overview.dash; + if (timeout < 2000) { // timeout < 2000 for partial update + this._prevDash = dash._workId; + console.warn(`[${Me.metadata.name}]: Dash has been replaced, updating extension ...`); + Me._resetInProgress = true; + // update only necessary modules if dash has been replaced + this._repairOverrides(); + Me._resetInProgress = false; + } else { + console.warn(`[${Me.metadata.name}]: Updating extension ...`); + // for case the monitor configuration has been changed, update all + Me._resetInProgress = true; + this._activateVShell(); + Me._resetInProgress = false; + } + this._timeouts.reset = 0; + return GLib.SOURCE_REMOVE; + } + ); + } + + // the key modules that can be affected by the supported incompatible extensions + _repairOverrides() { + Me.Modules.overviewModule.update(); + Me.Modules.overviewControlsModule.update(); + Me.Modules.workspacesViewModule.update(); + Me.Modules.windowPreviewModule.update(); + Me.Modules.panelModule.update(); + Me.Modules.dashModule.update(); + } + + _updateSettings(settings, key) { + // update settings cache and option variables + opt._updateSettings(); + + // avoid overload while loading profile - update only once + // delayed gsettings writes are processed alphabetically + if (key === 'aaa-loading-profile') { + this._showStatusMessage(); + if (this._timeouts.loadingProfile) + GLib.source_remove(this._timeouts.loadingProfile); + this._timeouts.loadingProfile = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 100, () => { + this._activateVShell(); + this._timeouts.loadingProfile = 0; + return GLib.SOURCE_REMOVE; + }); + } + if (this._timeouts.loadingProfile) + return; + + if (key?.includes('profile-data')) { + const index = key.replace('profile-data-', ''); + Main.notify(`${Me.metadata.name}`, `Profile ${index} has been updated`); + } + + opt.WORKSPACE_MIN_SPACING = Main.overview._overview._controls._thumbnailsBox.get_theme_node().get_length('spacing'); + // update variables that cannot be processed within settings + const dash = Main.overview.dash; + if (Me.Util.dashIsDashToDock()) { + opt.DASH_POSITION = dash._position; + opt.DASH_TOP = opt.DASH_POSITION === 0; + opt.DASH_RIGHT = opt.DASH_POSITION === 1; + opt.DASH_BOTTOM = opt.DASH_POSITION === 2; + opt.DASH_LEFT = opt.DASH_POSITION === 3; + opt.DASH_VERTICAL = opt.DASH_LEFT || opt.DASH_RIGHT; + } + + opt.DASH_VISIBLE = opt.DASH_VISIBLE && !Me.Util.getEnabledExtensions('dash-to-panel@jderose9.github.com').length; + + const monitorWidth = global.display.get_monitor_geometry(global.display.get_primary_monitor()).width; + if (monitorWidth < 1600) { + opt.APP_GRID_ICON_SIZE_DEFAULT = opt.APP_GRID_ACTIVE_PREVIEW && !opt.APP_GRID_USAGE ? 128 : 64; + opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT = 64; + } + + Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = opt.OVERVIEW_MODE === 1 ? 0.1 : 0.95; + + // adjust search entry style for OM2 + if (opt.OVERVIEW_MODE2) + Main.overview.searchEntry.add_style_class_name('search-entry-om2'); + else + Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); + + Main.overview.searchEntry.visible = opt.SHOW_SEARCH_ENTRY; + Main.overview.searchEntry.opacity = 255; + St.Settings.get().slow_down_factor = opt.ANIMATION_TIME_FACTOR; + Search.MAX_LIST_SEARCH_RESULTS_ROWS = opt.SEARCH_MAX_ROWS; + + opt.START_Y_OFFSET = (opt.get('panelModule') && opt.PANEL_OVERVIEW_ONLY && opt.PANEL_POSITION_TOP) || + // better to add unnecessary space than to have a panel overlapping other objects + Me.Util.getEnabledExtensions('hidetopbar').length + ? Main.panel.height + : 0; + + // Options for workspace switcher, apply custom function only if needed + if (opt.WS_WRAPAROUND || opt.WS_IGNORE_LAST) + Meta.Workspace.prototype.get_neighbor = this._getNeighbor; + else + Meta.Workspace.prototype.get_neighbor = this._originalGetNeighbor; + + if (settings) + this._applySettings(key); + } + + _applySettings(key) { + if (key?.endsWith('-module')) { + for (let module of Me.moduleList) { + if (opt.options[module] && key === opt.options[module][1]) { + if (key === 'app-display-module') + this._showStatusMessage(); + Me.Modules[module].update(); + break; + } + } + return; + } + + Main.overview._overview.controls._setBackground(); + this._switchPageShortcuts(); + + if (key?.includes('panel')) + Me.Modules.panelModule.update(); + + if (key?.includes('dash') || key?.includes('icon') || key?.includes('dot-style')) + Me.Modules.dashModule.update(); + + if (key?.includes('hot-corner') || key?.includes('dash')) + Me.Modules.layoutModule.update(); + + switch (key) { + case 'ws-thumbnails-position': + this._updateOverrides(); + break; + case 'workspace-switcher-animation': + Me.Modules.workspaceAnimationModule.update(); + break; + case 'search-width-scale': + Me.Modules.searchModule.update(); + break; + case 'favorites-notify': + Me.Modules.appFavoritesModule.update(); + break; + case 'window-attention-mode': + Me.Modules.windowAttentionHandlerModule.update(); + break; + case 'show-ws-preview-bg': + Me.Modules.panelModule.update(); + break; + case 'notification-position': + Me.Modules.messageTrayModule.update(); + break; + case 'osd-position': + Me.Modules.osdWindowModule.update(); + break; + case 'overlay-key': + Me.Modules.overlayKeyModule.update(); + break; + case 'always-activate-selected-window': + Me.Modules.windowPreviewModule.update(); + break; + } + + if (key?.includes('app-grid') || + key?.includes('app-folder') || + key?.includes('dot-style') || + key === 'show-search-entry' || + key === 'ws-thumbnail-scale' || + key === 'ws-thumbnail-scale-appgrid') { + this._showStatusMessage(); + Me.Modules.appDisplayModule.update(); + } + } + + _switchPageShortcuts() { + // ignore screen lock + if (!opt.get('enablePageShortcuts') || this._sessionLockActive) + return; + + const vertical = global.workspaceManager.layout_rows === -1; + const schema = 'org.gnome.desktop.wm.keybindings'; + const settings = new Gio.Settings({ schema_id: schema }); + + const keyLeft = 'switch-to-workspace-left'; + const keyRight = 'switch-to-workspace-right'; + const keyUp = 'switch-to-workspace-up'; + const keyDown = 'switch-to-workspace-down'; + + const keyMoveLeft = 'move-to-workspace-left'; + const keyMoveRight = 'move-to-workspace-right'; + const keyMoveUp = 'move-to-workspace-up'; + const keyMoveDown = 'move-to-workspace-down'; + + const switchPrevSc = 'Page_Up'; + const switchNextSc = 'Page_Down'; + const movePrevSc = 'Page_Up'; + const moveNextSc = 'Page_Down'; + + let switchLeft = settings.get_strv(keyLeft); + let switchRight = settings.get_strv(keyRight); + let switchUp = settings.get_strv(keyUp); + let switchDown = settings.get_strv(keyDown); + + let moveLeft = settings.get_strv(keyMoveLeft); + let moveRight = settings.get_strv(keyMoveRight); + let moveUp = settings.get_strv(keyMoveUp); + let moveDown = settings.get_strv(keyMoveDown); + + if (vertical) { + if (switchLeft.includes(switchPrevSc)) + switchLeft.splice(switchLeft.indexOf(switchPrevSc), 1); + if (switchRight.includes(switchNextSc)) + switchRight.splice(switchRight.indexOf(switchNextSc), 1); + if (moveLeft.includes(movePrevSc)) + moveLeft.splice(moveLeft.indexOf(movePrevSc), 1); + if (moveRight.includes(moveNextSc)) + moveRight.splice(moveRight.indexOf(moveNextSc), 1); + + if (!switchUp.includes(switchPrevSc)) + switchUp.push(switchPrevSc); + if (!switchDown.includes(switchNextSc)) + switchDown.push(switchNextSc); + if (!moveUp.includes(movePrevSc)) + moveUp.push(movePrevSc); + if (!moveDown.includes(moveNextSc)) + moveDown.push(moveNextSc); + } else { + if (!switchLeft.includes(switchPrevSc)) + switchLeft.push(switchPrevSc); + if (!switchRight.includes(switchNextSc)) + switchRight.push(switchNextSc); + if (!moveLeft.includes(movePrevSc)) + moveLeft.push(movePrevSc); + if (!moveRight.includes(moveNextSc)) + moveRight.push(moveNextSc); + + if (switchUp.includes(switchPrevSc)) + switchUp.splice(switchUp.indexOf(switchPrevSc), 1); + if (switchDown.includes(switchNextSc)) + switchDown.splice(switchDown.indexOf(switchNextSc), 1); + if (moveUp.includes(movePrevSc)) + moveUp.splice(moveUp.indexOf(movePrevSc), 1); + if (moveDown.includes(moveNextSc)) + moveDown.splice(moveDown.indexOf(moveNextSc), 1); + } + + settings.set_strv(keyLeft, switchLeft); + settings.set_strv(keyRight, switchRight); + settings.set_strv(keyUp, switchUp); + settings.set_strv(keyDown, switchDown); + + settings.set_strv(keyMoveLeft, moveLeft); + settings.set_strv(keyMoveRight, moveRight); + settings.set_strv(keyMoveUp, moveUp); + settings.set_strv(keyMoveDown, moveDown); + } + + // Status dialog that appears during updating V-Shell configuration and blocks inputs + _showStatusMessage(show = true) { + if ((show && Me._resetInProgress) || Main.layoutManager._startingUp || !Main.overview._overview.controls._appDisplay._sortOrderedItemsAlphabetically) + return; + + if (Me._vShellMessageTimeoutId) { + GLib.source_remove(Me._vShellMessageTimeoutId); + Me._vShellMessageTimeoutId = 0; + } + + if (Me._vShellStatusMessage && !show) { + Me._vShellStatusMessage.close(); + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + } + + if (!show) + return; + + if (!Me._vShellStatusMessage) { + const sm = new Main.RestartMessage(_('Updating V-Shell...')); + sm.set_style('background-color: rgba(0,0,0,0.3);'); + sm.open(); + Me._vShellStatusMessage = sm; + } + + // just for case the message wasn't removed from appDisplay after App Grid realization + Me._vShellMessageTimeoutId = GLib.timeout_add_seconds( + GLib.PRIORITY_DEFAULT, + 5, + () => { + if (Me._vShellStatusMessage) { + Me._vShellStatusMessage.close(); + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + Me._resetInProgress = false; + } + + Me._vShellMessageTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } + ); + } + + _getNeighbor(direction) { + // workspace matrix is supported + const activeIndex = this.index(); + const ignoreLast = opt.WS_IGNORE_LAST && !Main.overview._shown ? 1 : 0; + const wraparound = opt.WS_WRAPAROUND; + const nWorkspaces = global.workspace_manager.n_workspaces; + const lastIndex = nWorkspaces - 1 - ignoreLast; + const rows = global.workspace_manager.layout_rows > -1 ? global.workspace_manager.layout_rows : nWorkspaces; + const columns = global.workspace_manager.layout_columns > -1 ? global.workspace_manager.layout_columns : nWorkspaces; + + let index = activeIndex; + let neighborExists; + + if (direction === Meta.MotionDirection.LEFT) { + index -= 1; + const currentRow = Math.floor(activeIndex / columns); + const indexRow = Math.floor(index / columns); + neighborExists = index > -1 && indexRow === currentRow; + if (wraparound && !neighborExists) { + index = currentRow * columns + columns - 1; + const maxIndexOnLastRow = lastIndex % columns; + index = index < (lastIndex - ignoreLast) ? index : currentRow * columns + maxIndexOnLastRow; + } + } else if (direction === Meta.MotionDirection.RIGHT) { + index += 1; + const currentRow = Math.floor(activeIndex / columns); + const indexRow = Math.floor(index / columns); + neighborExists = index <= lastIndex && indexRow === currentRow; + if (wraparound && !neighborExists) + index = currentRow * columns; + } else if (direction === Meta.MotionDirection.UP) { + index -= columns; + neighborExists = index > -1; + if (wraparound && !neighborExists) { + index = rows * columns + index; + index = index < nWorkspaces - ignoreLast ? index : index - columns; + } + } else if (direction === Meta.MotionDirection.DOWN) { + index += columns; + neighborExists = index <= lastIndex; + if (wraparound && !neighborExists) + index %= columns; + } + + return global.workspace_manager.get_workspace_by_index(neighborExists || wraparound ? index : activeIndex); + } +} diff --git a/extensions/vertical-workspaces/lib/appDisplay.js b/extensions/44/vertical-workspaces/lib/appDisplay.js similarity index 51% rename from extensions/vertical-workspaces/lib/appDisplay.js rename to extensions/44/vertical-workspaces/lib/appDisplay.js index 2ac70b1..aeb2808 100644 --- a/extensions/vertical-workspaces/lib/appDisplay.js +++ b/extensions/44/vertical-workspaces/lib/appDisplay.js @@ -10,246 +10,501 @@ 'use strict'; -const { Clutter, GLib, GObject, Meta, Shell, St, Graphene, Pango } = imports.gi; +const Clutter = imports.gi.Clutter; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Graphene = imports.gi.Graphene; +const Meta = imports.gi.Meta; +const Pango = imports.gi.Pango; +const Shell = imports.gi.Shell; +const St = imports.gi.St; -const DND = imports.ui.dnd; -const Main = imports.ui.main; const AppDisplay = imports.ui.appDisplay; +const DND = imports.ui.dnd; const IconGrid = imports.ui.iconGrid; +const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const IconGridOverride = Me.imports.lib.iconGrid; -const _Util = Me.imports.lib.util; - -const DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x00000022); -const DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000000); - -// gettext -const _ = Me.imports.lib.settings._; - -let _overrides; - -let _appGridLayoutSettings; -let _appDisplayScrollConId; -let _appSystemStateConId; -let _appGridLayoutConId; -let _origAppViewItemAcceptDrop; -let _updateFolderIcons; - +let Me; let opt; -let shellVersion = _Util.shellVersion; -let _firstRun = true; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('appDisplayModule', true); - reset = reset || !moduleEnabled; +let _timeouts; - // don't even touch this module if disabled - if (_firstRun && reset) - return; +// DIALOG_SHADE_NORMAL = Clutter.Color.from_pixel(0x00000022); +// DIALOG_SHADE_HIGHLIGHT = Clutter.Color.from_pixel(0x00000000); - _firstRun = false; +var AppDisplayModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - if (_overrides) - _overrides.removeAll(); + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; - if (reset) { - _setAppDisplayOrientation(false); - _updateAppGridProperties(reset); - _updateAppGridDND(reset); - _restoreOverviewGroup(); - _overrides = null; + this._appGridLayoutSettings = null; + this._appDisplayScrollConId = 0; + this._appSystemStateConId = 0; + this._appGridLayoutConId = 0; + this._origAppViewItemAcceptDrop = null; + this._updateFolderIcons = 0; + } + + cleanGlobals() { + Me = null; opt = null; - return; } - _overrides = new _Util.Overrides(); + update(reset) { + this._removeTimeouts(); + this.moduleEnabled = opt.get('appDisplayModule'); + const conflict = false; - if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) { - _overrides.addOverride('AppDisplayVertical', AppDisplay.AppDisplay.prototype, AppDisplayVertical); - _overrides.addOverride('BaseAppViewVertical', AppDisplay.BaseAppView.prototype, BaseAppViewVertical); + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + this.moduleEnabled = false; + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) { + this.moduleEnabled = false; + console.debug(' AppDisplayModule - Keeping untouched'); + } } - // Custom App Grid - _overrides.addOverride('AppFolderDialog', AppDisplay.AppFolderDialog.prototype, AppFolderDialog); - if (shellVersion >= 43) { - // const defined class needs to be touched before real access - AppDisplay.BaseAppViewGridLayout; - _overrides.addOverride('BaseAppViewGridLayout', AppDisplay.BaseAppViewGridLayout.prototype, BaseAppViewGridLayout); - } - _overrides.addOverride('FolderView', AppDisplay.FolderView.prototype, FolderView); - _overrides.addOverride('FolderIcon', AppDisplay.FolderIcon.prototype, FolderIcon); - _overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIcon); - _overrides.addOverride('AppDisplay', AppDisplay.AppDisplay.prototype, AppDisplayCommon); - _overrides.addOverride('AppViewItem', AppDisplay.AppViewItem.prototype, AppViewItemCommon); - _overrides.addOverride('BaseAppViewCommon', AppDisplay.BaseAppView.prototype, BaseAppViewCommon); + _activateModule() { + Me.Modules.iconGridModule.update(); - _setAppDisplayOrientation(opt.ORIENTATION === Clutter.Orientation.VERTICAL); - _updateAppGridProperties(); - _updateAppGridDND(); - opt._appGridNeedsRedisplay = true; -} + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); -function _setAppDisplayOrientation(vertical = false) { - const CLUTTER_ORIENTATION = vertical ? Clutter.Orientation.VERTICAL : Clutter.Orientation.HORIZONTAL; - const scroll = vertical ? 'vscroll' : 'hscroll'; - // app display to vertical has issues - page indicator not working - // global appDisplay orientation switch is not built-in - let appDisplay = Main.overview._overview._controls._appDisplay; - // following line itself only changes in which axis will operate overshoot detection which switches appDisplay pages while dragging app icon to vertical - appDisplay._orientation = CLUTTER_ORIENTATION; - appDisplay._grid.layoutManager._orientation = CLUTTER_ORIENTATION; - appDisplay._swipeTracker.orientation = CLUTTER_ORIENTATION; - appDisplay._swipeTracker._reset(); - if (vertical) { - appDisplay._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); + _timeouts = {}; - // move and change orientation of page indicators - // needs corrections in appgrid page calculations, e.g. appDisplay.adaptToSize() fnc, - // which complicates use of super call inside the function - const pageIndicators = appDisplay._pageIndicators; - pageIndicators.vertical = true; - appDisplay._box.vertical = false; - pageIndicators.x_expand = false; - pageIndicators.y_align = Clutter.ActorAlign.CENTER; - pageIndicators.x_align = Clutter.ActorAlign.START; + // Common + this._overrides.addOverride('FolderView', AppDisplay.FolderView.prototype, FolderView); + this._overrides.addOverride('FolderIcon', AppDisplay.FolderIcon.prototype, FolderIcon); + if (opt.APP_GRID_ACTIVE_PREVIEW) + this._overrides.addOverride('ActiveFolderIcon', AppDisplay.FolderIcon, ActiveFolderIcon); + this._overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIcon); + this._overrides.addOverride('AppDisplay', AppDisplay.AppDisplay.prototype, AppDisplayCommon); + this._overrides.addOverride('AppViewItem', AppDisplay.AppViewItem.prototype, AppViewItemCommon); + this._overrides.addOverride('BaseAppViewCommon', AppDisplay.BaseAppView.prototype, BaseAppViewCommon); - const scrollContainer = appDisplay._scrollView.get_parent(); - if (shellVersion < 43) { - // remove touch friendly side navigation bars / arrows - if (appDisplay._hintContainer && appDisplay._hintContainer.get_parent()) - scrollContainer.remove_child(appDisplay._hintContainer); + if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) { + this._overrides.addOverride('AppDisplayVertical', AppDisplay.AppDisplay.prototype, AppDisplayVertical); + this._overrides.addOverride('BaseAppViewVertical', AppDisplay.BaseAppView.prototype, BaseAppViewVertical); + } + + // Custom App Grid + this._overrides.addOverride('AppFolderDialog', AppDisplay.AppFolderDialog.prototype, AppFolderDialog); + if (Me.shellVersion >= 43) { + // const defined class needs to be touched before real access + this._dummy = AppDisplay.AppGrid; + delete this._dummy; + // BaseAppViewGridLayout is not exported, we can only access current instance + this._overrides.addOverride('BaseAppViewGridLayout', Main.overview._overview.controls._appDisplay._appGridLayout, BaseAppViewGridLayout); + this._overrides.addOverride('FolderGrid', AppDisplay.FolderGrid.prototype, FolderGrid); } else { - // moving these bars needs more patching of the appDisplay's code - // for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page - appDisplay._nextPageIndicator.add_style_class_name('nextPageIndicator'); - appDisplay._prevPageIndicator.add_style_class_name('prevPageIndicator'); + this._overrides.addOverride('FolderGrid', AppDisplay.FolderGrid.prototype, FolderGridLegacy); } - // setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them - appDisplay._nextPageArrow.scale_x = 0; - appDisplay._prevPageArrow.scale_x = 0; - } else { - appDisplay._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); - if (_appDisplayScrollConId) { - appDisplay._adjustment.disconnect(_appDisplayScrollConId); - _appDisplayScrollConId = 0; - } + this._setAppDisplayOrientation(opt.ORIENTATION === Clutter.Orientation.VERTICAL); + this._updateDND(); + if (!Main.sessionMode.isGreeter) + this._updateAppDisplayProperties(); - // restore original page indicators - const pageIndicators = appDisplay._pageIndicators; - pageIndicators.vertical = false; - appDisplay._box.vertical = true; - pageIndicators.x_expand = true; - pageIndicators.y_align = Clutter.ActorAlign.END; - pageIndicators.x_align = Clutter.ActorAlign.CENTER; - - // put back touch friendly navigation bars/buttons - const scrollContainer = appDisplay._scrollView.get_parent(); - if (appDisplay._hintContainer && !appDisplay._hintContainer.get_parent()) { - scrollContainer.add_child(appDisplay._hintContainer); - // the hit container covers the entire app grid and added at the top of the stack blocks DND drops - // so it needs to be pushed below - scrollContainer.set_child_below_sibling(appDisplay._hintContainer, null); - } - - appDisplay._nextPageArrow.scale_x = 1; - appDisplay._prevPageArrow.scale_x = 1; - - appDisplay._nextPageIndicator.remove_style_class_name('nextPageIndicator'); - appDisplay._prevPageIndicator.remove_style_class_name('prevPageIndicator'); + console.debug(' AppDisplayModule - Activated'); } - // value for page indicator is calculated from scroll adjustment, horizontal needs to be replaced by vertical - appDisplay._adjustment = appDisplay._scrollView[scroll].adjustment; + _disableModule() { + Me.Modules.iconGridModule.update(true); - // no need to connect already connected signal (wasn't removed the original one before) - if (!vertical) { - // reset used appDisplay properties - Main.overview._overview._controls._appDisplay.scale_y = 1; - Main.overview._overview._controls._appDisplay.scale_x = 1; + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + const reset = true; + this._setAppDisplayOrientation(false); + this._updateAppDisplayProperties(reset); + this._updateDND(reset); + this._restoreOverviewGroup(); + this._removeStatusMessage(); + + console.debug(' AppDisplayModule - Disabled'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } + + _setAppDisplayOrientation(vertical = false) { + const CLUTTER_ORIENTATION = vertical ? Clutter.Orientation.VERTICAL : Clutter.Orientation.HORIZONTAL; + const scroll = vertical ? 'vscroll' : 'hscroll'; + // app display to vertical has issues - page indicator not working + // global appDisplay orientation switch is not built-in + let appDisplay = Main.overview._overview._controls._appDisplay; + // following line itself only changes in which axis will operate overshoot detection which switches appDisplay pages while dragging app icon to vertical + appDisplay._orientation = CLUTTER_ORIENTATION; + appDisplay._grid.layoutManager._orientation = CLUTTER_ORIENTATION; + appDisplay._swipeTracker.orientation = CLUTTER_ORIENTATION; + appDisplay._swipeTracker._reset(); + if (vertical) { + appDisplay._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); + + // move and change orientation of page indicators + const pageIndicators = appDisplay._pageIndicators; + pageIndicators.vertical = true; + appDisplay._box.vertical = false; + pageIndicators.x_expand = false; + pageIndicators.y_align = Clutter.ActorAlign.CENTER; + pageIndicators.x_align = Clutter.ActorAlign.START; + + const scrollContainer = appDisplay._scrollView.get_parent(); + if (Me.shellVersion < 43) { + // remove touch friendly side navigation bars / arrows + if (appDisplay._hintContainer && appDisplay._hintContainer.get_parent()) + scrollContainer.remove_child(appDisplay._hintContainer); + } else { + // moving these bars needs more patching of the appDisplay's code + // for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page + appDisplay._nextPageIndicator.add_style_class_name('nextPageIndicator'); + appDisplay._prevPageIndicator.add_style_class_name('prevPageIndicator'); + } + + // setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them + appDisplay._nextPageArrow.scale_x = 0; + appDisplay._prevPageArrow.scale_x = 0; + } else { + appDisplay._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); + if (this._appDisplayScrollConId) { + appDisplay._adjustment.disconnect(this._appDisplayScrollConId); + this._appDisplayScrollConId = 0; + } + + // restore original page indicators + const pageIndicators = appDisplay._pageIndicators; + pageIndicators.vertical = false; + appDisplay._box.vertical = true; + pageIndicators.x_expand = true; + pageIndicators.y_align = Clutter.ActorAlign.END; + pageIndicators.x_align = Clutter.ActorAlign.CENTER; + + // put back touch friendly navigation bars/buttons + const scrollContainer = appDisplay._scrollView.get_parent(); + if (appDisplay._hintContainer && !appDisplay._hintContainer.get_parent()) { + scrollContainer.add_child(appDisplay._hintContainer); + // the hit container covers the entire app grid and added at the top of the stack blocks DND drops + // so it needs to be pushed below + scrollContainer.set_child_below_sibling(appDisplay._hintContainer, null); + } + + appDisplay._nextPageArrow.scale_x = 1; + appDisplay._prevPageArrow.scale_x = 1; + + appDisplay._nextPageIndicator.remove_style_class_name('nextPageIndicator'); + appDisplay._prevPageIndicator.remove_style_class_name('prevPageIndicator'); + } + + // value for page indicator is calculated from scroll adjustment, horizontal needs to be replaced by vertical + appDisplay._adjustment = appDisplay._scrollView[scroll].adjustment; + + // no need to connect already connected signal (wasn't removed the original one before) + if (!vertical) { + // reset used appDisplay properties + Main.overview._overview._controls._appDisplay.scale_y = 1; + Main.overview._overview._controls._appDisplay.scale_x = 1; + Main.overview._overview._controls._appDisplay.opacity = 255; + return; + } + + // update appGrid dot pages indicators + this._appDisplayScrollConId = appDisplay._adjustment.connect('notify::value', adj => { + const value = adj.value / adj.page_size; + appDisplay._pageIndicators.setCurrentPosition(value); + }); + } + + // Set App Grid columns, rows, icon size, incomplete pages + _updateAppDisplayProperties(reset = false) { + opt._appGridNeedsRedisplay = false; + // columns, rows, icon size + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.visible = true; + if (reset) { + appDisplay._grid.layoutManager.fixedIconSize = -1; + appDisplay._grid.layoutManager.allow_incomplete_pages = true; + appDisplay._grid._currentMode = -1; + appDisplay._grid.setGridModes(); + if (this._appGridLayoutSettings) { + this._appGridLayoutSettings.disconnect(this._appGridLayoutConId); + this._appGridLayoutConId = 0; + this._appGridLayoutSettings = null; + } + appDisplay._redisplay(); + + appDisplay._grid.set_style(''); + this._updateAppGrid(reset); + } else { + // update grid on layout reset + if (!this._appGridLayoutSettings) { + this._appGridLayoutSettings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); + this._appGridLayoutConId = this._appGridLayoutSettings.connect('changed::app-picker-layout', this._updateLayout); + } + + appDisplay._grid.layoutManager.allow_incomplete_pages = opt.APP_GRID_ALLOW_INCOMPLETE_PAGES; + // appDisplay._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`); + // APP_GRID_SPACING constant is used for grid dimensions calculation + // but sometimes the actual grid spacing properties affect/change the calculated size, therefore we set it lower to avoid this problem + // main app grid always use available space and the spacing is optimized for the grid dimensions + appDisplay._grid.set_style('column-spacing: 5px; row-spacing: 5px;'); + + // force redisplay + appDisplay._grid._currentMode = -1; + appDisplay._grid.setGridModes(); + appDisplay._grid.layoutManager.fixedIconSize = opt.APP_GRID_ICON_SIZE; + // avoid resetting appDisplay before startup animation + // x11 shell restart skips startup animation + if (!Main.layoutManager._startingUp) { + this._updateAppGrid(); + } else if (Main.layoutManager._startingUp && (Meta.is_restart() || Me.Util.dashIsDashToDock())) { + _timeouts.three = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._updateAppGrid(); + _timeouts.three = 0; + return GLib.SOURCE_REMOVE; + }); + } + } + } + + _updateDND(reset) { + if (!reset) { + if (!this._appSystemStateConId && opt.APP_GRID_INCLUDE_DASH >= 3) { + this._appSystemStateConId = Shell.AppSystem.get_default().connect( + 'app-state-changed', + () => { + this._updateFolderIcons = true; + Main.overview._overview.controls._appDisplay._redisplay(); + } + ); + } + } else if (this._appSystemStateConId) { + Shell.AppSystem.get_default().disconnect(this._appSystemStateConId); + this._appSystemStateConId = 0; + } + } + + _restoreOverviewGroup() { + Main.overview.dash.showAppsButton.checked = false; + Main.layoutManager.overviewGroup.opacity = 255; + Main.layoutManager.overviewGroup.scale_x = 1; + Main.layoutManager.overviewGroup.scale_y = 1; + Main.layoutManager.overviewGroup.hide(); + Main.overview._overview._controls._appDisplay.translation_x = 0; + Main.overview._overview._controls._appDisplay.translation_y = 0; + Main.overview._overview._controls._appDisplay.visible = true; Main.overview._overview._controls._appDisplay.opacity = 255; - return; } - // update appGrid dot pages indicators - _appDisplayScrollConId = appDisplay._adjustment.connect('notify::value', adj => { - const value = adj.value / adj.page_size; - appDisplay._pageIndicators.setCurrentPosition(value); - }); -} + // update all invalid positions that may be result of grid/icon size change + _updateIconPositions() { + const appDisplay = Main.overview._overview._controls._appDisplay; + const layout = JSON.stringify(global.settings.get_value('app-picker-layout').recursiveUnpack()); + // if app grid layout is empty, sort source alphabetically to avoid misplacing + if (layout === JSON.stringify([]) && appDisplay._sortOrderedItemsAlphabetically) + appDisplay._sortOrderedItemsAlphabetically(); + const icons = [...appDisplay._orderedItems]; + for (let i = 0; i < icons.length; i++) + appDisplay._moveItem(icons[i], -1, -1); + } -// Set App Grid columns, rows, icon size, incomplete pages -function _updateAppGridProperties(reset = false) { - opt._appGridNeedsRedisplay = false; - // columns, rows, icon size - const appDisplay = Main.overview._overview._controls._appDisplay; - appDisplay.visible = true; - - if (reset) { - appDisplay._grid.layoutManager.fixedIconSize = -1; - appDisplay._grid.layoutManager.allow_incomplete_pages = true; - appDisplay._grid.setGridModes(); - if (_appGridLayoutSettings) { - _appGridLayoutSettings.disconnect(_appGridLayoutConId); - _appGridLayoutConId = 0; - _appGridLayoutSettings = null; + _removeIcons() { + const appDisplay = Main.overview._overview._controls._appDisplay; + const icons = [...appDisplay._orderedItems]; + for (let i = 0; i < icons.length; i++) { + const icon = icons[i]; + if (icon._dialog) + Main.layoutManager.overviewGroup.remove_child(icon._dialog); + appDisplay._removeItem(icon); + icon.destroy(); } + appDisplay._folderIcons = []; + } + + _removeStatusMessage() { + if (Me._vShellStatusMessage) { + if (Me._vShellMessageTimeoutId) { + GLib.source_remove(Me._vShellMessageTimeoutId); + Me._vShellMessageTimeoutId = 0; + } + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + } + } + + _updateLayout(settings, key) { + const currentValue = JSON.stringify(settings.get_value(key).deep_unpack()); + const emptyValue = JSON.stringify([]); + const customLayout = currentValue !== emptyValue; + if (!customLayout) { + this._updateAppGrid(); + } + } + + _updateAppGrid(reset = false, callback) { + const appDisplay = Main.overview._overview._controls._appDisplay; + // reset the grid only if called directly without args or if all folders where removed by using reset button in Settings window + // otherwise this function is called every time a user moves icon to another position as a settings callback + + // force update icon size using adaptToSize(), the page size cannot be the same as the current one + appDisplay._grid.layoutManager._pageWidth += 1; + appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); + + // don't delay the first screen lock on GS < 44, removing icons takes a time and with other 15 enabled extensions it can be multiplied by 15 + if (!Main.sessionMode.isLocked) + this._removeIcons(); + appDisplay._redisplay(); - - appDisplay._grid.set_style(''); - _resetAppGrid(); - } else { - // update grid on layout reset - if (!_appGridLayoutSettings) { - _appGridLayoutSettings = ExtensionUtils.getSettings('org.gnome.shell'); - _appGridLayoutConId = _appGridLayoutSettings.connect('changed::app-picker-layout', _resetAppGrid); + // don't realize appDisplay on disable, or at startup if disabled + // always realize appDisplay otherwise to avoid errors while opening folders (that I was unable to trace) + if (reset || (!opt.APP_GRID_PERFORMANCE && callback)) { + this._removeStatusMessage(); + if (callback) + callback(); + return; } - appDisplay._grid.layoutManager.allow_incomplete_pages = opt.APP_GRID_ALLOW_INCOMPLETE_PAGES; - appDisplay._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`); + // workaround - silently realize appDisplay + // appDisplay and its content must be "visible" (opacity > 0) on the screen (within monitor geometry) + // to realize its objects + // this action takes some time and affects animations during the first use + // if we do it invisibly before user needs it, it can improve the user's experience - // force redisplay - appDisplay._grid._currentMode = -1; - appDisplay._grid.setGridModes(); - appDisplay._grid.layoutManager.fixedIconSize = opt.APP_GRID_ICON_SIZE; - // appDisplay._folderIcons.forEach(folder => folder._dialog?._updateFolderSize()); - _resetAppGrid(); + this._exposeAppGrid(); + + // let the main loop process our changes before continuing + _timeouts.one = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._updateIconPositions(); + if (appDisplay._sortOrderedItemsAlphabetically) { + appDisplay._sortOrderedItemsAlphabetically(); + appDisplay._grid.layoutManager._pageWidth += 1; + appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); + appDisplay._setLinearPositions(appDisplay._orderedItems); + } + + appDisplay._redisplay(); + // realize also all app folders (by opening them) so the first popup is as smooth as the second one + // let the main loop process our changes before continuing + _timeouts.two = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._restoreAppGrid(); + Me._resetInProgress = false; + this._removeStatusMessage(); + + if (callback) + callback(); + + _timeouts.two = 0; + return GLib.SOURCE_REMOVE; + }); + _timeouts.one = 0; + return GLib.SOURCE_REMOVE; + }); } -} -function _updateAppGridDND(reset) { - if (!reset) { - if (!_appSystemStateConId && opt.APP_GRID_INCLUDE_DASH >= 3) { - _appSystemStateConId = Shell.AppSystem.get_default().connect( - 'app-state-changed', - () => { - _updateFolderIcons = true; - Main.overview._overview._controls._appDisplay._redisplay(); - } - ); + _exposeAppGrid() { + const overviewGroup = Main.layoutManager.overviewGroup; + if (!overviewGroup.visible) { + // scale down the overviewGroup so it don't cover uiGroup + overviewGroup.scale_y = 0.001; + // make it invisible to the eye, but visible for the renderer + overviewGroup.opacity = 1; + // if overview is hidden, show it + overviewGroup.visible = true; } - } else if (_appSystemStateConId) { - Shell.AppSystem.get_default().disconnect(_appSystemStateConId); - _appSystemStateConId = 0; - } - if (opt.APP_GRID_ORDER && !reset) { - if (!_origAppViewItemAcceptDrop) - _origAppViewItemAcceptDrop = AppDisplay.AppViewItem.prototype.acceptDrop; - AppDisplay.AppViewItem.prototype.acceptDrop = () => false; - } else if (_origAppViewItemAcceptDrop) { - AppDisplay.AppViewItem.prototype.acceptDrop = _origAppViewItemAcceptDrop; - } -} -function _restoreOverviewGroup() { - Main.overview.dash.showAppsButton.checked = false; - Main.layoutManager.overviewGroup.opacity = 255; - Main.layoutManager.overviewGroup.scale_x = 1; - Main.layoutManager.overviewGroup.hide(); -} + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.opacity = 1; + + // find usable value, sometimes it's one, sometime the other... + let [x, y] = appDisplay.get_position(); + let { x1, y1 } = appDisplay.allocation; + x = x === Infinity ? 0 : x; + y = y === Infinity ? 0 : y; + x1 = x1 === Infinity ? 0 : x1; + y1 = y1 === Infinity ? 0 : y1; + appDisplay.translation_x = -(x ? x : x1); + appDisplay.translation_y = -(y ? y : y1); + this._exposeAppFolders(); + } + + _exposeAppFolders() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay._folderIcons.forEach(d => { + d._ensureFolderDialog(); + d._dialog._updateFolderSize(); + d._dialog.scale_y = 0.0001; + d._dialog.show(); + }); + } + + _restoreAppGrid() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.translation_x = 0; + appDisplay.translation_y = 0; + // appDisplay.opacity = 0; + this._hideAppFolders(); + + const overviewGroup = Main.layoutManager.overviewGroup; + if (!Main.overview._shown) + overviewGroup.hide(); + overviewGroup.scale_y = 1; + overviewGroup.opacity = 255; + + this._removeStatusMessage(); + } + + _hideAppFolders() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay._folderIcons.forEach(d => { + if (d._dialog) { + d._dialog._updateFolderSize(); + d._dialog.hide(); + d._dialog.scale_y = 1; + } + }); + } + + _getWindowApp(metaWin) { + const tracker = Shell.WindowTracker.get_default(); + return tracker.get_window_app(metaWin); + } + + _getAppLastUsedWindow(app) { + let recentWin; + global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { + const winApp = this._getWindowApp(metaWin); + if (!recentWin && winApp === app) + recentWin = metaWin; + }); + return recentWin; + } + + _getAppRecentWorkspace(app) { + const recentWin = this._getAppLastUsedWindow(app); + if (recentWin) + return recentWin.get_workspace(); + + return null; + } +}; const AppDisplayVertical = { // correction of the appGrid size when page indicators were moved from the bottom to the right @@ -302,7 +557,7 @@ const AppDisplayCommon = { const appsInsideFolders = new Set(); this._folderIcons = []; - if (!opt.APP_GRID_ORDER) { + if (!opt.APP_GRID_USAGE) { let folders = this._folderSettings.get_strv('folder-children'); folders.forEach(id => { let path = `${this._folderSettings.path}folders/${id}/`; @@ -317,8 +572,8 @@ const AppDisplayCommon = { if (icon.pressed) this.updateDragFocus(icon); }); - } else if (_updateFolderIcons && opt.APP_GRID_EXCLUDE_RUNNING) { - // if any app changed its running state, update folder icon + } else if (this._updateFolderIcons && opt.APP_GRID_EXCLUDE_RUNNING) { + // if any app changed its running state, update folder icon icon.icon.update(); } @@ -334,8 +589,9 @@ const AppDisplayCommon = { icon.getAppIds().forEach(appId => appsInsideFolders.add(appId)); }); } + // reset request to update active icon - _updateFolderIcons = false; + this._updateFolderIcons = false; // Allow dragging of the icon only if the Dash would accept a drop to // change favorite-apps. There are no other possible drop targets from @@ -348,7 +604,7 @@ const AppDisplayCommon = { global.settings.is_writable('app-picker-layout'); apps.forEach(appId => { - if (!opt.APP_GRID_ORDER && appsInsideFolders.has(appId)) + if (!opt.APP_GRID_USAGE && appsInsideFolders.has(appId)) return; let icon = this._items.get(appId); @@ -380,7 +636,7 @@ const AppDisplayCommon = { dragMotion: this._onDragMotion.bind(this), }; DND.addDragMonitor(this._dragMonitor); - if (shellVersion < 43) + if (Me.shellVersion < 43) this._slideSidePages(AppDisplay.SidePages.PREVIOUS | AppDisplay.SidePages.NEXT | AppDisplay.SidePages.DND); else this._appGridLayout.showPageIndicators(); @@ -419,50 +675,16 @@ const AppDisplayCommon = { this._redisplay(); }, - // accept source from active preview + // accept source from active folder preview acceptDrop(source) { - if (opt.APP_GRID_ORDER) + if (opt.APP_GRID_USAGE) return false; if (source._sourceItem) source = source._sourceItem; - let dropTarget = null; - if (shellVersion >= 43) { - dropTarget = this._dropTarget; - delete this._dropTarget; - } - - if (!this._canAccept(source)) + if (!BaseAppViewCommon.acceptDrop.bind(this)(source)) return false; - if ((shellVersion < 43 && this._dropPage) || - (shellVersion >= 43 && (dropTarget === this._prevPageIndicator || - dropTarget === this._nextPageIndicator))) { - let increment; - - if (shellVersion < 43) - increment = this._dropPage === AppDisplay.SidePages.NEXT ? 1 : -1; - else - increment = dropTarget === this._prevPageIndicator ? -1 : 1; - - const { currentPage, nPages } = this._grid; - const page = Math.min(currentPage + increment, nPages); - const position = page < nPages ? -1 : 0; - - this._moveItem(source, page, position); - this.goToPage(page); - } else if (this._delayedMoveData) { - // Dropped before the icon was moved - const { page, position } = this._delayedMoveData; - - try { - this._moveItem(source, page, position); - } catch (e) { - log(`Warning:${e}`); - } - this._removeDelayedMove(); - } - this._savePages(); let view = AppDisplay._getViewFromIcon(source); @@ -493,7 +715,7 @@ const BaseAppViewVertical = { this._pageIndicators.x_align = Clutter.ActorAlign.START; this._pageIndicators.set_style('margin-right: 10px;'); const scrollContainer = this._scrollView.get_parent(); - if (shellVersion < 43) { + if (Me.shellVersion < 43) { // remove touch friendly side navigation bars / arrows if (this._hintContainer && this._hintContainer.get_parent()) scrollContainer.remove_child(this._hintContainer); @@ -536,7 +758,7 @@ const BaseAppViewCommon = { try { this._moveItem(icon, page, position); } catch (e) { - log(`Warning:${e}`); + console.warn(`Warning:${e}`); } }); }, @@ -570,14 +792,19 @@ const BaseAppViewCommon = { } }); - // sort all alphabetically - if (opt.APP_GRID_ORDER > 0) { + // different options for root app grid and app folders + const thisIsFolder = this instanceof AppDisplay.FolderView; + const thisIsAppDisplay = !thisIsFolder; + if ((opt.APP_GRID_ORDER && thisIsAppDisplay) || + (opt.APP_FOLDER_ORDER && thisIsFolder)) { // const { itemsPerPage } = this._grid; let appIcons = this._orderedItems; + // sort all alphabetically this._sortOrderedItemsAlphabetically(appIcons); // appIcons.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())); // then sort used apps by usage - if (opt.APP_GRID_ORDER === 2) + if ((opt.APP_GRID_USAGE && thisIsAppDisplay) || + (opt.APP_FOLDER_USAGE && thisIsFolder)) appIcons.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id)); // sort favorites first @@ -595,9 +822,14 @@ const BaseAppViewCommon = { } // sort running first - if (opt.APP_GRID_DASH_FIRST) + if (opt.APP_GRID_DASH_FIRST && thisIsAppDisplay) appIcons.sort((a, b) => a.app.get_state() !== Shell.AppState.RUNNING && b.app.get_state() === Shell.AppState.RUNNING); + if (opt.APP_GRID_FOLDERS_FIRST) + appIcons.sort((a, b) => b._folder && !a._folder); + else if (opt.APP_GRID_FOLDERS_LAST) + appIcons.sort((a, b) => a._folder && !b._folder); + this._setLinearPositions(appIcons); this._orderedItems = appIcons; @@ -611,7 +843,7 @@ const BaseAppViewCommon = { }, _canAccept(source) { - return opt.APP_GRID_ORDER ? false : source instanceof AppDisplay.AppViewItem; + return source instanceof AppDisplay.AppViewItem; }, // support active preview icons @@ -619,12 +851,25 @@ const BaseAppViewCommon = { if (!this._canAccept(source)) return false; - if (source._sourceItem) - source = source._sourceItem; + let dropTarget = null; + if (Me.shellVersion >= 43) { + dropTarget = this._dropTarget; + delete this._dropTarget; + } + if (!this._canAccept(source)) + return false; + + if ((Me.shellVersion < 43 && this._dropPage) || + (Me.shellVersion >= 43 && (dropTarget === this._prevPageIndicator || + dropTarget === this._nextPageIndicator))) { + let increment; + + if (Me.shellVersion < 43) + increment = this._dropPage === AppDisplay.SidePages.NEXT ? 1 : -1; + else + increment = dropTarget === this._prevPageIndicator ? -1 : 1; - if (this._dropPage) { - const increment = this._dropPage === AppDisplay.SidePages.NEXT ? 1 : -1; const { currentPage, nPages } = this._grid; const page = Math.min(currentPage + increment, nPages); const position = page < nPages ? -1 : 0; @@ -635,7 +880,11 @@ const BaseAppViewCommon = { // Dropped before the icon was moved const { page, position } = this._delayedMoveData; - this._moveItem(source, page, position); + try { + this._moveItem(source, page, position); + } catch (e) { + console.warn(`Warning:${e}`); + } this._removeDelayedMove(); } @@ -652,7 +901,7 @@ const BaseAppViewCommon = { const appIcon = dragEvent.source; - if (shellVersion < 43) { + if (Me.shellVersion < 43) { this._dropPage = this._pageForCoords(dragEvent.x, dragEvent.y); if (this._dropPage && this._dropPage === AppDisplay.SidePages.PREVIOUS && @@ -663,7 +912,7 @@ const BaseAppViewCommon = { } if (appIcon instanceof AppDisplay.AppViewItem) { - if (shellVersion < 44) { + if (Me.shellVersion < 44) { // Handle the drag overshoot. When dragging to above the // icon grid, move to the page above; when dragging below, // move to the page below. @@ -685,7 +934,10 @@ const BaseAppViewCommon = { } } - this._maybeMoveItem(dragEvent); + const thisIsFolder = this instanceof AppDisplay.FolderView; + const thisIsAppDisplay = !thisIsFolder; + if ((!opt.APP_GRID_ORDER && thisIsAppDisplay) || (!opt.APP_FOLDER_ORDER && thisIsFolder)) + this._maybeMoveItem(dragEvent); return DND.DragMotionResult.CONTINUE; }, @@ -771,16 +1023,12 @@ const FolderIcon = { : St.ButtonMask.ONE | St.ButtonMask.TWO; this.button_mask = buttonMask;*/ this.button_mask = St.ButtonMask.ONE | St.ButtonMask.TWO; - - // build the folders now to avoid node errors when dragging active folder preview icons - if (this.visible && opt.APP_GRID_ACTIVE_PREVIEW) - this._ensureFolderDialog(); }, open() { this._ensureFolderDialog(); - if (this._dialog._designCapacity !== this.view._orderedItems.length) - this._dialog._updateFolderSize(); + // if (this._dialog._designCapacity !== this.view._orderedItems.length) + this._dialog._updateFolderSize(); this.view._scrollView.vscroll.adjustment.value = 0; this._dialog.popup(); @@ -789,15 +1037,7 @@ const FolderIcon = { const FolderView = { _createGrid() { - let grid; - if (shellVersion < 43) - grid = new FolderGrid(); - else - grid = new FolderGrid43(); - - // IconGrid algorithm for adaptive icon size - // counts with different default(max) size for folders - grid.layoutManager._isFolder = true; + let grid = new AppDisplay.FolderGrid(); return grid; }, @@ -832,11 +1072,15 @@ const FolderView = { bin.child = this._orderedItems[i].app.create_icon_texture(subSize); } else { const app = this._orderedItems[i].app; - const child = new ActiveFolderIcon(app); + const child = new AppDisplay.AppIcon(app, { + setSizeManually: true, + showLabel: false, + }); child._sourceItem = this._orderedItems[i]; child._sourceFolder = this; child.icon.style_class = ''; child.icon.set_style('margin: 0; padding: 0;'); + child._dot.set_style('margin-bottom: 1px;'); child.icon.setIconSize(subSize); bin.child = child; @@ -863,9 +1107,9 @@ const FolderView = { layout.attach(bin, rtl ? (i + 1) % gridSize : i % gridSize, Math.floor(i / gridSize), 1, 1); } - // if folder content changed, update folder size - if (this._dialog && this._dialog._designCapacity !== this._orderedItems.length) - this._dialog._updateFolderSize(); + // if folder content changed, update folder size, but not if it's empty + /* if (this._dialog && this._dialog._designCapacity !== this._orderedItems.length && this._orderedItems.length) + this._dialog._updateFolderSize();*/ return icon; }, @@ -927,6 +1171,13 @@ const FolderView = { items.push(icon); }); + + if (opt.APP_FOLDER_ORDER) + Main.overview._overview.controls._appDisplay._sortOrderedItemsAlphabetically(items); + + if (opt.APP_FOLDER_USAGE) + items.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id)); + this._appIds = this._apps.map(app => app.get_id()); return items; }, @@ -942,10 +1193,9 @@ const FolderView = { }; // folder columns and rows -const FolderGrid = GObject.registerClass( -class FolderGrid extends IconGrid.IconGrid { +const FolderGridLegacy = { _init() { - super._init({ + IconGrid.IconGrid.prototype._init.bind(this)({ allow_incomplete_pages: false, // For adaptive size (0), set the numbers high enough to fit all the icons // to avoid splitting the icons to pages @@ -954,56 +1204,52 @@ class FolderGrid extends IconGrid.IconGrid { page_halign: Clutter.ActorAlign.CENTER, page_valign: Clutter.ActorAlign.CENTER, }); - + this.layout_manager._isFolder = true; // if (!opt.APP_GRID_FOLDER_DEFAULT) const spacing = opt.APP_GRID_SPACING; this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`); - this.layout_manager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; - } + this.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; + }, adaptToSize(width, height) { this.layout_manager.adaptToSize(width, height); - } -}); + }, +}; +const FolderGrid = { + _init() { + AppDisplay.AppGrid.prototype._init.bind(this)({ + allow_incomplete_pages: false, + columns_per_page: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 20, + rows_per_page: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 20, + page_halign: Clutter.ActorAlign.CENTER, + page_valign: Clutter.ActorAlign.CENTER, + }); + this.layout_manager._isFolder = true; + const spacing = opt.APP_GRID_SPACING; + this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`); + this.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; -let FolderGrid43; -// first reference to constant defined using const in other module returns undefined, the AppGrid const will remain empty and unused -const AppGrid = AppDisplay.AppGrid; -if (AppDisplay.AppGrid) { - FolderGrid43 = GObject.registerClass( - class FolderGrid43 extends AppDisplay.AppGrid { - _init() { - super._init({ - allow_incomplete_pages: false, - columns_per_page: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 20, - rows_per_page: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 20, - page_halign: Clutter.ActorAlign.CENTER, - page_valign: Clutter.ActorAlign.CENTER, - }); + this.setGridModes([ + { + columns: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 3, + rows: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 3, + }, + ]); + }, - const spacing = opt.APP_GRID_SPACING; - this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`); - this.layout_manager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; + adaptToSize(width, height) { + this.layout_manager.adaptToSize(width, height); + }, +}; - this.setGridModes([ - { - columns: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 3, - rows: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 3, - }, - ]); - } - - adaptToSize(width, height) { - this.layout_manager.adaptToSize(width, height); - } - }); -} const FOLDER_DIALOG_ANIMATION_TIME = 200; // AppDisplay.FOLDER_DIALOG_ANIMATION_TIME const AppFolderDialog = { // injection to _init() after__init() { + this._viewBox.add_style_class_name('app-folder-dialog-vshell'); + // delegate this dialog to the FolderIcon._view // so its _createFolderIcon function can update the dialog if folder content changed this._view._dialog = this; @@ -1023,18 +1269,65 @@ const AppFolderDialog = { }); this.child.add_action(clickAction); + + // Adjust empty actor to center the title + this._entryBox.get_first_child().width = 82; + }, + + after__addFolderNameEntry() { + // Edit button + this._removeButton = new St.Button({ + style_class: 'edit-folder-button', + button_mask: St.ButtonMask.ONE, + toggle_mode: false, + reactive: true, + can_focus: true, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + child: new St.Icon({ + icon_name: 'user-trash-symbolic', + icon_size: 16, + }), + }); + + this._removeButton.connect('clicked', () => { + if (Date.now() - this._removeButton._lastClick < Clutter.Settings.get_default().double_click_time) { + this._grabHelper.ungrab({ actor: this }); + // without hiding the dialog, Shell crashes (at least on X11) + this.hide(); + this._view._deletingFolder = true; + + // Resetting all keys deletes the relocatable schema + let keys = this._folder.settings_schema.list_keys(); + for (const key of keys) + this._folder.reset(key); + + let settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' }); + let folders = settings.get_strv('folder-children'); + folders.splice(folders.indexOf(this._view._id), 1); + + // remove all abandoned folders (usually my own garbage and unwanted default folders...) + /* const appFolders = this._appDisplay._folderIcons.map(icon => icon._id); + folders.forEach(folder => { + if (!appFolders.includes(folder)) { + folders.splice(folders.indexOf(folder._id), 1); + } + });*/ + settings.set_strv('folder-children', folders); + + this._view._deletingFolder = false; + return; + } + this._removeButton._lastClick = Date.now(); + }); + + this._entryBox.add_child(this._removeButton); }, popup() { if (this._isOpen) return; - /* if (!this._correctSize) { - // update folder with the precise app item size when the dialog is realized - GLib.idle_add(0, () => this._updateFolderSize(true)); - this._correctSize = true; - }*/ - this._isOpen = this._grabHelper.grab({ actor: this, onUngrab: () => this.popdown(), @@ -1046,26 +1339,48 @@ const AppFolderDialog = { this.get_parent().set_child_above_sibling(this, null); this._needsZoomAndFade = true; - this.show(); + // the first folder dialog realization needs size correction + // so set the folder size, let it realize and then update the folder content + if (!this.realized) { + this._updateFolderSize(); + GLib.idle_add( + GLib.PRIORITY_DEFAULT, + () => { + this._updateFolderSize(); + } + ); + } + + this.show(); this.emit('open-state-changed', true); }, _updateFolderSize() { - // adapt folder size according to the settings and number of icons const view = this._view; + const [firstItem] = view._grid.layoutManager._container; + if (!firstItem) + return; + // adapt folder size according to the settings and number of icons + const appDisplay = this._source._parentView; + if (!appDisplay.width || appDisplay.allocation.x2 === Infinity || appDisplay.allocation.x2 === -Infinity) { + return; + } + view._grid.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; view._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`); const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const dialogMargin = 30; + const itemPadding = 55; // default icon item padding on Fedora 44 + // const dialogMargin = 30; const nItems = view._orderedItems.length; let columns = opt.APP_GRID_FOLDER_COLUMNS; let rows = opt.APP_GRID_FOLDER_ROWS; + const fullAdaptiveGrid = !columns && !rows; let spacing = opt.APP_GRID_SPACING; - const monitor = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + const minItemSize = 48 + itemPadding; - if (!columns && !rows) { + if (fullAdaptiveGrid) { columns = Math.ceil(Math.sqrt(nItems)); rows = columns; if (columns * (columns - 1) >= nItems) { @@ -1081,38 +1396,67 @@ const AppFolderDialog = { } const iconSize = opt.APP_GRID_FOLDER_ICON_SIZE < 0 ? opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT : opt.APP_GRID_FOLDER_ICON_SIZE; - let itemSize = iconSize + 53; // icon padding + view._grid.layoutManager.fixedIconSize = iconSize; + + let itemSize = iconSize + 55; // icon padding // first run sets the grid before we can read the real icon size // so we estimate the size from default properties // and correct it in the second run - if (this._notFirstRun) { - const [firstItem] = view._grid.layoutManager._container; + if (this.realized) { firstItem.icon.setIconSize(iconSize); const [firstItemWidth] = firstItem.get_preferred_size(); const realSize = firstItemWidth / scaleFactor; - if (realSize > iconSize) + // if the preferred item size is smaller than icon plus some padding, ignore it + // (icons that are not yet realized are returning sizes like 45 or 53) + if (realSize > (iconSize + 24)) itemSize = realSize; - } else { - this._needsUpdateSize = true; - this._notFirstRun = true; } - let width = columns * (itemSize + spacing) + /* padding for nav arrows*/64; - width = Math.round(width + (opt.ORIENTATION || !opt.APP_GRID_FOLDER_COLUMNS ? 100 : 160/* space for navigation arrows*/)); - let height = rows * (itemSize + spacing) + /* header*/75 + /* padding*/100; + width = Math.round(width + (opt.ORIENTATION ? 100 : 160/* space for navigation arrows*/)); + let height = rows * (itemSize + spacing) + /* header*/75 + /* padding*/ 2 * 30 + /* padding + ?page indicator*/(!opt.ORIENTATION || !opt.APP_GRID_FOLDER_COLUMNS ? 100 : 70); - // folder must fit the primary monitor + // allocation is more reliable than appDisplay width/height properties + const appDisplayWidth = appDisplay.allocation.x2 - appDisplay.allocation.x1; + const appDisplayHeight = appDisplay.allocation.y2 - appDisplay.allocation.y1 + (opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0); + + // folder must fit the appDisplay area // reduce columns/rows if needed and count with the scaled values - while (width * scaleFactor > monitor.width - 2 * dialogMargin) { - width -= itemSize + spacing; - columns -= 1; + if (!opt.APP_GRID_FOLDER_ROWS) { + while ((height * scaleFactor) > appDisplayHeight) { + height -= itemSize + spacing; + rows -= 1; + } } - while (height * scaleFactor > monitor.height - 2 * dialogMargin) { - height -= itemSize + spacing; - rows -= 1; + + if (!opt.APP_GRID_FOLDER_COLUMNS) { + while ((width * scaleFactor) > appDisplayWidth) { + width -= itemSize + spacing; + columns -= 1; + } } - width = Math.max(540, width); + // try to compensate for the previous reduction if there is a space + if (!opt.APP_GRID_FOLDER_COLUMNS) { + while ((nItems > columns * rows) && ((width * scaleFactor + itemSize + spacing) <= appDisplayWidth)) { + width += itemSize + spacing; + columns += 1; + } + // remove columns that cannot be displayed + if ((columns * minItemSize + (columns - 1) * spacing) > appDisplayWidth) + columns = Math.floor(appDisplayWidth / (minItemSize + spacing)); + } + if (!opt.APP_GRID_FOLDER_ROWS) { + while ((nItems > columns * rows) && ((height * scaleFactor + itemSize + spacing) <= appDisplayHeight)) { + height += itemSize + spacing; + rows += 1; + } + // remove rows that cannot be displayed + if ((rows * minItemSize + (rows - 1) * spacing) > appDisplayHeight) + rows = Math.floor(appDisplayWidth / (minItemSize + spacing)); + } + + width = Math.clamp(width, 640, appDisplayWidth); + height = Math.min(height, appDisplayHeight); const layoutManager = view._grid.layoutManager; layoutManager.rows_per_page = rows; @@ -1127,6 +1471,8 @@ const AppFolderDialog = { padding: 30px; `); + view._grid.layoutManager._pageWidth += 1; + view._grid.layoutManager.adaptToSize(view._grid.layoutManager._pageWidth - 1, view._grid.layoutManager._pageHeight); view._redisplay(); // store original item count @@ -1145,40 +1491,34 @@ const AppFolderDialog = { // this. covers the whole screen let dialogTargetX = dialogX; let dialogTargetY = dialogY; - if (!opt.APP_GRID_FOLDER_CENTER) { - const appDisplay = this._source._parentView; - dialogTargetX = Math.round(sourceCenterX - this.child.width / 2); - dialogTargetY = Math.round(sourceCenterY - this.child.height / 2); + const appDisplay = this._source._parentView; + const [appDisplayX, appDisplayY] = this._source._parentView.get_transformed_position(); + + if (!opt.APP_GRID_FOLDER_CENTER) { + dialogTargetX = sourceCenterX - this.child.width / 2; + dialogTargetY = sourceCenterY - this.child.height / 2; // keep the dialog in appDisplay area if possible dialogTargetX = Math.clamp( dialogTargetX, - this.x + appDisplay.x, - this.x + appDisplay.x + appDisplay.width - this.child.width + appDisplayX, + appDisplayX + appDisplay.width - this.child.width ); dialogTargetY = Math.clamp( dialogTargetY, - this.y + appDisplay.y, - this.y + appDisplay.y + appDisplay.height - this.child.height - ); - // or at least in the monitor area - const monitor = global.display.get_monitor_geometry(global.display.get_primary_monitor()); - dialogTargetX = Math.clamp( - dialogTargetX, - this.x + monitor.x, - this.x + monitor.x + monitor.width - this.child.width - ); - - dialogTargetY = Math.clamp( - dialogTargetY, - this.y + monitor.y, - this.y + monitor.y + monitor.height - this.child.height + appDisplayY, + appDisplayY + appDisplay.height - this.child.height ); + } else { + const searchEntryHeight = opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0; + dialogTargetX = appDisplayX + appDisplay.width / 2 - this.child.width / 2; + dialogTargetY = appDisplayY - searchEntryHeight + ((appDisplay.height + searchEntryHeight) / 2 - this.child.height / 2) / 2; } - const dialogOffsetX = -dialogX + dialogTargetX; - const dialogOffsetY = -dialogY + dialogTargetY; + + const dialogOffsetX = Math.round(dialogTargetX - dialogX); + const dialogOffsetY = Math.round(dialogTargetY - dialogY); this.child.set({ translation_x: sourceX - dialogX, @@ -1188,12 +1528,6 @@ const AppFolderDialog = { opacity: 0, }); - this.ease({ - background_color: DIALOG_SHADE_NORMAL, - duration: FOLDER_DIALOG_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - this.child.ease({ translation_x: dialogOffsetX, translation_y: dialogOffsetY, @@ -1202,17 +1536,22 @@ const AppFolderDialog = { opacity: 255, duration: FOLDER_DIALOG_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - // if the folder grid was build with the estimated icon item size because the real size wasn't available - // rebuild it with the real size now, after the folder was realized - if (this._needsUpdateSize) { - this._updateFolderSize(); - this._view._redisplay(); - this._needsUpdateSize = false; - } - }, }); + appDisplay.ease({ + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + if (opt.SHOW_SEARCH_ENTRY) { + Main.overview.searchEntry.ease({ + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + this._needsZoomAndFade = false; if (this._sourceMappedId === 0) { @@ -1230,17 +1569,20 @@ const AppFolderDialog = { return; } + // if the dialog was shown silently, skip animation + if (this.scale_y < 1) { + this._needsZoomAndFade = false; + this.hide(); + this._popdownCallbacks.forEach(func => func()); + this._popdownCallbacks = []; + return; + } + let [sourceX, sourceY] = this._source.get_transformed_position(); let [dialogX, dialogY] = this.child.get_transformed_position(); - this.ease({ - background_color: Clutter.Color.from_pixel(0x00000000), - duration: FOLDER_DIALOG_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - this.child.ease({ translation_x: sourceX - dialogX + this.child.translation_x, translation_y: sourceY - dialogY + this.child.translation_y, @@ -1264,11 +1606,35 @@ const AppFolderDialog = { }, }); + const appDisplay = this._source._parentView; + appDisplay.ease({ + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + if (opt.SHOW_SEARCH_ENTRY) { + Main.overview.searchEntry.ease({ + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + this._needsZoomAndFade = false; }, _setLighterBackground(lighter) { - const backgroundColor = lighter + if (this._isOpen) { + const appDisplay = this._source._parentView; + appDisplay.ease({ + opacity: lighter ? 20 : 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + /* const backgroundColor = lighter ? DIALOG_SHADE_HIGHLIGHT : DIALOG_SHADE_NORMAL; @@ -1276,91 +1642,10 @@ const AppFolderDialog = { backgroundColor, duration: FOLDER_DIALOG_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); + }); */ }, }; -// just make app grid to update all invalid positions that may be result of grid/icon size change -function _updateIconPositions() { - const appDisplay = Main.overview._overview._controls._appDisplay; - const icons = [...appDisplay._orderedItems]; - for (let i = 0; i < icons.length; i++) - appDisplay._moveItem(icons[i], -1, -1); -} - -function _removeIcons() { - const appDisplay = Main.overview._overview._controls._appDisplay; - const icons = [...appDisplay._orderedItems]; - for (let i = 0; i < icons.length; i++) { - const icon = icons[i]; - if (icon._dialog) - Main.layoutManager.overviewGroup.remove_child(icon._dialog); - appDisplay._removeItem(icon); - icon.destroy(); - } - appDisplay._folderIcons = []; -} - -function _resetAppGrid(settings) { - const appDisplay = Main.overview._overview._controls._appDisplay; - // reset the grid only if called directly without args or if all folders where removed by using reset button in Settings window - // otherwise this function is called every time a user moves icon to another position as a settings callback - if (settings) { - const currentValue = JSON.stringify(global.settings.get_value('app-picker-layout').deep_unpack()); - const emptyValue = JSON.stringify([]); - const customLayout = currentValue !== emptyValue; - // appDisplay._customLayout = customLayout; - if (customLayout) - return; - else - opt._appGridNeedsRedisplay = true; - } - - // force update icon size using adaptToSize(). the page size cannot be the same as the current one - appDisplay._grid.layoutManager._pageWidth += 1; - appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); - _removeIcons(); - appDisplay._redisplay(); - // force appDisplay to move all icons to proper positions and update all properties - GLib.idle_add(0, () => { - _updateIconPositions(); - if (appDisplay._sortOrderedItemsAlphabetically) { - appDisplay._sortOrderedItemsAlphabetically(); - appDisplay._grid.layoutManager._pageWidth += 1; - appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); - appDisplay._setLinearPositions(appDisplay._orderedItems); - } else { - appDisplay._removeItem(appDisplay._orderedItems[0]); - appDisplay._redisplay(); - } - - appDisplay._redisplay(); - }); -} - -function _getWindowApp(metaWin) { - const tracker = Shell.WindowTracker.get_default(); - return tracker.get_window_app(metaWin); -} - -function _getAppLastUsedWindow(app) { - let recentWin; - global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { - const winApp = _getWindowApp(metaWin); - if (!recentWin && winApp === app) - recentWin = metaWin; - }); - return recentWin; -} - -function _getAppRecentWorkspace(app) { - const recentWin = _getAppLastUsedWindow(app); - if (recentWin) - return recentWin.get_workspace(); - - return null; -} - const AppIcon = { after__init() { // update the app label behavior @@ -1377,7 +1662,7 @@ const AppIcon = { return source !== this && (source instanceof this.constructor) && (view instanceof AppDisplay.AppDisplay && - !opt.APP_GRID_ORDER); + !opt.APP_GRID_USAGE); }, }; @@ -1427,7 +1712,7 @@ const AppViewItemCommon = { // support active preview icons acceptDrop(source, _actor, x) { - if (opt.APP_GRID_ORDER) + if (opt.APP_GRID_USAGE) return DND.DragMotionResult.NO_DROP; this._setHoveringByDnd(false); @@ -1449,26 +1734,18 @@ const AppViewItemCommon = { }; -const ActiveFolderIcon = GObject.registerClass( -class ActiveFolderIcon extends AppDisplay.AppIcon { - _init(app) { - super._init(app, { - setSizeManually: true, - showLabel: false, - }); - } - +const ActiveFolderIcon = { handleDragOver() { return DND.DragMotionResult.CONTINUE; - } + }, acceptDrop() { return false; - } + }, _onDragEnd() { this._dragging = false; this.undoScaleAndFade(); Main.overview.endItemDrag(this._sourceItem.icon); - } -}); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/appFavorites.js b/extensions/44/vertical-workspaces/lib/appFavorites.js new file mode 100644 index 0000000..3efb68c --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/appFavorites.js @@ -0,0 +1,79 @@ +/** + * V-Shell (Vertical Workspaces) + * appFavorites.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const AppFavorites = imports.ui.appFavorites; + +let Me; +let opt; + +var AppFavoritesModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('appFavoritesModule'); + + // if notifications are enabled no override is needed + reset = reset || !this.moduleEnabled || opt.SHOW_FAV_NOTIFICATION; + + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) { + this.moduleEnabled = false; + console.debug(' AppFavoritesModule - Keeping untouched'); + } + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + // use actual instance instead of prototype + this._overrides.addOverride('AppFavorites', AppFavorites.getAppFavorites(), AppFavoritesCommon); + + console.debug(' AppFavoritesModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' AppFavoritesModule - Deactivated'); + } +}; + +const AppFavoritesCommon = { + addFavoriteAtPos(appId, pos) { + this._addFavorite(appId, pos); + }, + + removeFavorite(appId) { + this._removeFavorite(appId); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/dash.js b/extensions/44/vertical-workspaces/lib/dash.js new file mode 100644 index 0000000..17d43ea --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/dash.js @@ -0,0 +1,1506 @@ +/** + * V-Shell (Vertical Workspaces) + * dash.js + * + * @author GdH + * @copyright 2022-2023 + * @license GPL-3.0 + * modified dash module of https://github.com/RensAlthuis/vertical-overview extension + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const AltTab = imports.ui.altTab; +const AppFavorites = imports.ui.appFavorites; +const AppDisplay = imports.ui.appDisplay; +const AppMenu = imports.ui.appMenu; +const BoxPointer = imports.ui.boxpointer; +const Dash = imports.ui.dash; +const DND = imports.ui.dnd; +const IconGrid = imports.ui.iconGrid; +const Main = imports.ui.main; +const PopupMenu = imports.ui.popupMenu; + +let Me; +let opt; +// gettext +let _; + +let _moduleEnabled; +let _timeouts; + +// added values to achieve a better ability to scale down according to available space +var BaseIconSizes = [16, 24, 32, 40, 44, 48, 56, 64, 72, 80, 96, 112, 128]; + +const DASH_ITEM_LABEL_SHOW_TIME = 150; + +var DashModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._originalWorkId = null; + this._customWorkId = null; + this._showAppsIconBtnPressId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + this._removeTimeouts(); + + this.moduleEnabled = opt.get('dashModule'); + const conflict = !!(Me.Util.getEnabledExtensions('dash-to-dock').length || + Me.Util.getEnabledExtensions('ubuntu-dock').length || + Me.Util.getEnabledExtensions('dash-to-panel').length); + + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Dash" module disabled due to potential conflict with another extension`); + + reset = reset || !this.moduleEnabled || conflict; + this._conflict = conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' DashModule - Keeping untouched'); + } + + updateStyle(dash) { + if (opt.DASH_BG_LIGHT) + dash._background.add_style_class_name('dash-background-light'); + else + dash._background.remove_style_class_name('dash-background-light'); + + dash._background.opacity = opt.DASH_BG_OPACITY; + let radius = opt.DASH_BG_RADIUS; + if (radius) { + let style; + switch (opt.DASH_POSITION) { + case 1: + style = opt.DASH_BG_GS3_STYLE ? `border-radius: ${radius}px 0 0 ${radius}px;` : `border-radius: ${radius}px;`; + break; + case 3: + style = opt.DASH_BG_GS3_STYLE ? `border-radius: 0 ${radius}px ${radius}px 0;` : `border-radius: ${radius}px;`; + break; + default: + style = `border-radius: ${radius}px;`; + } + dash._background.set_style(style); + } else { + dash._background.set_style(''); + } + } + + _activateModule() { + _moduleEnabled = true; + _timeouts = {}; + const dash = Main.overview._overview._controls.layoutManager._dash; + + if (!this._originalWorkId) + this._originalWorkId = dash._workId; + + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._resetStyle(dash); + this.updateStyle(dash); + + this._overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerCommon); + this._overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommon); + this._overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIconCommon); + this._overrides.addOverride('DashIcon', Dash.DashIcon.prototype, DashIconCommon); + this._overrides.addOverride('AppMenu', AppMenu.AppMenu.prototype, AppMenuCommon); + + if (opt.DASH_VERTICAL) { + dash.add_style_class_name('vertical'); + this._setOrientation(Clutter.Orientation.VERTICAL); + } else { + this._setOrientation(Clutter.Orientation.HORIZONTAL); + } + + if (!this._customWorkId) + this._customWorkId = Main.initializeDeferredWork(dash._box, dash._redisplay.bind(dash)); + dash._workId = this._customWorkId; + + this._updateSearchWindowsIcon(); + this._updateRecentFilesIcon(); + this._updateExtensionsIcon(); + this._moveDashAppGridIcon(); + this._connectShowAppsIcon(); + + dash.visible = opt.DASH_VISIBLE; + dash._background.add_style_class_name('dash-background-reduced'); + dash._queueRedisplay(); + + if (opt.DASH_ISOLATE_WS && !this._wmSwitchWsConId) { + this._wmSwitchWsConId = global.windowManager.connect('switch-workspace', () => dash._queueRedisplay()); + this._newWindowConId = global.display.connect_after('window-created', () => dash._queueRedisplay()); + } + + console.debug(' DashModule - Activated'); + } + + _disableModule() { + const dash = Main.overview._overview._controls.layoutManager._dash; + this._resetStyle(dash); + + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + dash._workId = this._originalWorkId; + + if (this._wmSwitchWsConId) { + global.windowManager.disconnect(this._wmSwitchWsConId); + this._wmSwitchWsConId = 0; + } + if (this._newWindowConId) { + global.windowManager.disconnect(this._newWindowConId); + this._newWindowConId = 0; + } + + const reset = true; + this._setOrientation(Clutter.Orientation.HORIZONTAL); + this._moveDashAppGridIcon(reset); + this._connectShowAppsIcon(reset); + this._updateSearchWindowsIcon(false); + this._updateRecentFilesIcon(false); + this._updateExtensionsIcon(false); + dash.visible = !this._conflict; + dash._background.opacity = 255; + + _moduleEnabled = false; + console.debug(' DashModule - Disabled'); + } + + _resetStyle(dash) { + dash.remove_style_class_name('vertical'); + dash.remove_style_class_name('vertical-gs3-left'); + dash.remove_style_class_name('vertical-gs3-right'); + dash.remove_style_class_name('vertical-left'); + dash.remove_style_class_name('vertical-right'); + dash._background.remove_style_class_name('dash-background-light'); + dash._background.remove_style_class_name('dash-background-reduced'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } + + _setOrientation(orientation, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + + dash._box.layout_manager.orientation = orientation; + dash._dashContainer.layout_manager.orientation = orientation; + dash._dashContainer.y_expand = !orientation; + dash._dashContainer.x_expand = !!orientation; + dash.x_align = orientation ? Clutter.ActorAlign.START : Clutter.ActorAlign.CENTER; + dash.y_align = orientation ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.FILL; + + let sizerBox = dash._background.get_children()[0]; + sizerBox.clear_constraints(); + sizerBox.add_constraint(new Clutter.BindConstraint({ + source: dash._showAppsIcon.icon, + coordinate: orientation ? Clutter.BindCoordinate.WIDTH : Clutter.BindCoordinate.HEIGHT, + })); + sizerBox.add_constraint(new Clutter.BindConstraint({ + source: dash._dashContainer, + coordinate: orientation ? Clutter.BindCoordinate.HEIGHT : Clutter.BindCoordinate.WIDTH, + })); + dash._box.remove_all_children(); + dash._separator = null; + dash._queueRedisplay(); + dash._adjustIconSize(); + + if (orientation && opt.DASH_BG_GS3_STYLE) { + if (opt.DASH_LEFT) + dash.add_style_class_name('vertical-gs3-left'); + else if (opt.DASH_RIGHT) + dash.add_style_class_name('vertical-gs3-right'); + } else { + dash.remove_style_class_name('vertical-gs3-left'); + dash.remove_style_class_name('vertical-gs3-right'); + } + } + + _moveDashAppGridIcon(reset = false, dash) { + // move dash app grid icon to the front + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + + const appIconPosition = opt.get('showAppsIconPosition'); + dash._showAppsIcon.remove_style_class_name('show-apps-icon-vertical-hide'); + dash._showAppsIcon.remove_style_class_name('show-apps-icon-horizontal-hide'); + dash._showAppsIcon.opacity = 255; + if (!reset && appIconPosition === 0) // 0 - start + dash._dashContainer.set_child_at_index(dash._showAppsIcon, 0); + if (reset || appIconPosition === 1) { // 1 - end + const index = dash._dashContainer.get_children().length - 1; + dash._dashContainer.set_child_at_index(dash._showAppsIcon, index); + } + if (!reset && appIconPosition === 2) { // 2 - hide + const style = opt.DASH_VERTICAL ? 'show-apps-icon-vertical-hide' : 'show-apps-icon-horizontal-hide'; + dash._showAppsIcon.add_style_class_name(style); + // for some reason even if the icon height in vertical mode should be set to 0 by the style, it stays visible in full size returning height 1px + dash._showAppsIcon.opacity = 0; + } + } + + _connectShowAppsIcon(reset = false, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + if (!reset) { + if (this._showAppsIconBtnPressId || Me.Util.dashIsDashToDock()) { + // button is already connected || dash is Dash to Dock + return; + } + dash._showAppsIcon.reactive = true; + this._showAppsIconBtnPressId = dash._showAppsIcon.connect('button-press-event', (actor, event) => { + const button = event.get_button(); + if (button === Clutter.BUTTON_MIDDLE) + Me.Util.openPreferences(); + else if (button === Clutter.BUTTON_SECONDARY) + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + else + return Clutter.EVENT_PROPAGATE; + return Clutter.EVENT_STOP; + }); + } else if (this._showAppsIconBtnPressId) { + dash._showAppsIcon.disconnect(this._showAppsIconBtnPressId); + this._showAppsIconBtnPressId = 0; + dash._showAppsIcon.reactive = false; + } + } + + _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; + + if (dash._showWindowsIcon) { + dashContainer.remove_child(dash._showWindowsIcon); + if (dash._showWindowsIconClickedId) { + dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId); + dash._showWindowsIconClickedId = 0; + } + delete dash._showWindowsIconClickedId; + if (dash._showWindowsIcon) + dash._showWindowsIcon.destroy(); + delete dash._showWindowsIcon; + } + + if (!show || !opt.get('windowSearchProviderModule')) + return; + + if (!dash._showWindowsIcon) { + dash._showWindowsIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('showWindowsIcon', dash._showWindowsIcon, ShowWindowsIcon); + dash._showWindowsIcon._afterInit(); + dash._showWindowsIcon.show(false); + dashContainer.add_child(dash._showWindowsIcon); + dash._hookUpLabel(dash._showWindowsIcon); + } + + dash._showWindowsIcon.icon.setIconSize(dash.iconSize); + if (opt.SHOW_WINDOWS_ICON === 1) { + dashContainer.set_child_at_index(dash._showWindowsIcon, 0); + } else if (opt.SHOW_WINDOWS_ICON === 2) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._showWindowsIcon, index); + } + + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); + + if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) { + dash._showWindowsIconClickedId = dash._showWindowsIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + }); + } + } + + _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; + + if (dash._recentFilesIcon) { + dashContainer.remove_child(dash._recentFilesIcon); + if (dash._recentFilesIconClickedId) { + dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId); + dash._recentFilesIconClickedId = 0; + } + delete dash._recentFilesIconClickedId; + if (dash._recentFilesIcon) + dash._recentFilesIcon.destroy(); + delete dash._recentFilesIcon; + } + + if (!show || !opt.get('recentFilesSearchProviderModule')) + return; + + if (!dash._recentFilesIcon) { + dash._recentFilesIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('recentFilesIcon', dash._recentFilesIcon, ShowRecentFilesIcon); + dash._recentFilesIcon._afterInit(); + dash._recentFilesIcon.show(false); + dashContainer.add_child(dash._recentFilesIcon); + dash._hookUpLabel(dash._recentFilesIcon); + } + + dash._recentFilesIcon.icon.setIconSize(dash.iconSize); + if (opt.SHOW_RECENT_FILES_ICON === 1) { + dashContainer.set_child_at_index(dash._recentFilesIcon, 0); + } else if (opt.SHOW_RECENT_FILES_ICON === 2) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._recentFilesIcon, index); + } + + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); + + if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) { + dash._recentFilesIconClickedId = dash._recentFilesIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.RFSP_PREFIX); + }); + } + } + + _updateExtensionsIcon(show = opt.SHOW_EXTENSIONS_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; + + if (dash._extensionsIcon) { + dashContainer.remove_child(dash._extensionsIcon); + if (dash._extensionsIconClickedId) { + dash._extensionsIcon.toggleButton.disconnect(dash._extensionsIconClickedId); + dash._extensionsIconClickedId = 0; + } + delete dash._extensionsIconClickedId; + if (dash._extensionsIcon) + dash._extensionsIcon.destroy(); + delete dash._extensionsIcon; + } + + if (!show || !opt.get('extensionsSearchProviderModule')) + return; + + if (!dash._extensionsIcon) { + dash._extensionsIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('extensionsIcon', dash._extensionsIcon, ShowExtensionsIcon); + dash._extensionsIcon._afterInit(); + dash._extensionsIcon.show(false); + dashContainer.add_child(dash._extensionsIcon); + dash._hookUpLabel(dash._extensionsIcon); + } + + dash._extensionsIcon.icon.setIconSize(dash.iconSize); + if (opt.SHOW_EXTENSIONS_ICON === 1) { + dashContainer.set_child_at_index(dash._extensionsIcon, 0); + } else if (opt.SHOW_EXTENSIONS_ICON === 2) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._extensionsIcon, index); + } + + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); + + if (dash._extensionsIcon && !dash._extensionsIconClickedId) { + dash._extensionsIconClickedId = dash._extensionsIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.ESP_PREFIX); + }); + } + } +}; + +const DashItemContainerCommon = { + // move labels according dash position + showLabel() { + if (!this._labelText) + return; + + const windows = this.child.app?.get_windows(); + const recentWindowTitle = windows && windows.length ? windows[0].get_title() : ''; + const windowCount = this.child.app?.get_windows().length; + let labelSuffix = ''; + if (windowCount > 1) + labelSuffix = ` (${windowCount})`; + if (recentWindowTitle && recentWindowTitle !== this._labelText) + labelSuffix += `\n ${recentWindowTitle}`; + + + this.label.set_text(this._labelText + labelSuffix); + + this.label.opacity = 0; + this.label.show(); + + let [stageX, stageY] = this.get_transformed_position(); + + const itemWidth = this.allocation.get_width(); + const itemHeight = this.allocation.get_height(); + + const labelWidth = this.label.get_width(); + const labelHeight = this.label.get_height(); + let xOffset = Math.floor((itemWidth - labelWidth) / 2); + let x = Math.clamp(stageX + xOffset, 0, global.stage.width - labelWidth); + const primaryMonitor = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + x = Math.clamp(x, primaryMonitor.x, primaryMonitor.x + primaryMonitor.width - labelWidth); + + let node = this.label.get_theme_node(); + let y; + + if (opt.DASH_TOP) { + const yOffset = 0.75 * itemHeight + 3 * node.get_length('-y-offset'); + y = stageY + yOffset; + } else if (opt.DASH_BOTTOM) { + const yOffset = node.get_length('-y-offset'); + y = stageY - this.label.height - yOffset; + } else if (opt.DASH_RIGHT) { + const yOffset = Math.floor((itemHeight - labelHeight) / 2); + xOffset = 4; + + x = stageX - xOffset - this.label.width; + y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); + } else if (opt.DASH_LEFT) { + const yOffset = Math.floor((itemHeight - labelHeight) / 2); + xOffset = 4; + + x = stageX + this.width + xOffset; + y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); + } + + this.label.set_position(x, y); + this.label.ease({ + opacity: 255, + duration: DASH_ITEM_LABEL_SHOW_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + this.label.set_position(x, y); + this.label.ease({ + opacity: 255, + duration: DASH_ITEM_LABEL_SHOW_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + }, +}; + +const DashCommon = { + _redisplay() { + // After disabling V-Shell queueRedisplay() may call this function + // In that case redirect the call to the current _redisplay() + if (!_moduleEnabled) { + this._redisplay(); + return; + } + + let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); + + let running = this._appSystem.get_running(); + + if (opt.DASH_ISOLATE_WS) { + const currentWs = global.workspace_manager.get_active_workspace(); + running = running.filter(app => { + return app.get_windows().filter(w => w.get_workspace() === currentWs).length; + }); + this._box.get_children().forEach(a => a.child?._updateRunningStyle()); + } + + let children = this._box.get_children().filter(actor => { + return actor.child && + actor.child._delegate && + actor.child._delegate.app; + }); + // Apps currently in the dash + let oldApps = children.map(actor => actor.child._delegate.app); + // Apps supposed to be in the dash + let newApps = []; + + for (let id in favorites) + newApps.push(favorites[id]); + + for (let i = 0; i < running.length; i++) { + let app = running[i]; + if (app.get_id() in favorites) + continue; + newApps.push(app); + } + + // Figure out the actual changes to the list of items; we iterate + // over both the list of items currently in the dash and the list + // of items expected there, and collect additions and removals. + // Moves are both an addition and a removal, where the order of + // the operations depends on whether we encounter the position + // where the item has been added first or the one from where it + // was removed. + // There is an assumption that only one item is moved at a given + // time; when moving several items at once, everything will still + // end up at the right position, but there might be additional + // additions/removals (e.g. it might remove all the launchers + // and add them back in the new order even if a smaller set of + // additions and removals is possible). + // If above assumptions turns out to be a problem, we might need + // to use a more sophisticated algorithm, e.g. Longest Common + // Subsequence as used by diff. + let addedItems = []; + let removedActors = []; + + let newIndex = 0; + let oldIndex = 0; + while (newIndex < newApps.length || oldIndex < oldApps.length) { + let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null; + let newApp = newApps.length > newIndex ? newApps[newIndex] : null; + + // No change at oldIndex/newIndex + if (oldApp === newApp) { + oldIndex++; + newIndex++; + continue; + } + + // App removed at oldIndex + if (oldApp && !newApps.includes(oldApp)) { + removedActors.push(children[oldIndex]); + oldIndex++; + continue; + } + + // App added at newIndex + if (newApp && !oldApps.includes(newApp)) { + addedItems.push({ + app: newApp, + item: this._createAppItem(newApp), + pos: newIndex, + }); + newIndex++; + continue; + } + + // App moved + let nextApp = newApps.length > newIndex + 1 + ? newApps[newIndex + 1] : null; + let insertHere = nextApp && nextApp === oldApp; + let alreadyRemoved = removedActors.reduce((result, actor) => { + let removedApp = actor.child._delegate.app; + return result || removedApp === newApp; + }, false); + + if (insertHere || alreadyRemoved) { + let newItem = this._createAppItem(newApp); + addedItems.push({ + app: newApp, + item: newItem, + pos: newIndex + removedActors.length, + }); + newIndex++; + } else { + removedActors.push(children[oldIndex]); + oldIndex++; + } + } + + for (let i = 0; i < addedItems.length; i++) { + this._box.insert_child_at_index( + addedItems[i].item, + addedItems[i].pos); + } + + for (let i = 0; i < removedActors.length; i++) { + let item = removedActors[i]; + + // Don't animate item removal when the overview is transitioning + // or hidden + if (Main.overview.visible && !Main.overview.animationInProgress) + item.animateOutAndDestroy(); + else + item.destroy(); + } + + this._adjustIconSize(); + + // Skip animations on first run when adding the initial set + // of items, to avoid all items zooming in at once + + let animate = this._shownInitially && Main.overview.visible && + !Main.overview.animationInProgress; + + if (!this._shownInitially) + this._shownInitially = true; + + for (let i = 0; i < addedItems.length; i++) + addedItems[i].item.show(animate); + + // Update separator + const nFavorites = Object.keys(favorites).length; + const nIcons = children.length + addedItems.length - removedActors.length; + if (nFavorites > 0 && nFavorites < nIcons) { + // destroy the horizontal separator if it exists. + // this is incredibly janky, but I can't think of a better way atm. + if (this._separator && this._separator.height !== 1) { + this._separator.destroy(); + this._separator = null; + } + + if (!this._separator) { + this._separator = new St.Widget({ + style_class: 'dash-separator', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + width: opt.DASH_VERTICAL ? this.iconSize : 1, + height: opt.DASH_VERTICAL ? 1 : this.iconSize, + }); + this._box.add_child(this._separator); + } + + // FIXME: separator placement is broken (also in original dash) + let pos = nFavorites + this._animatingPlaceholdersCount; + if (this._dragPlaceholder) + pos++; + this._box.set_child_at_index(this._separator, pos); + } else if (this._separator) { + this._separator.destroy(); + this._separator = null; + } + // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 + // Without it, StBoxLayout may use a stale size cache + this._box.queue_relayout(); + }, + + _createAppItem(app) { + let appIcon = new Dash.DashIcon(app); + + let indicator = appIcon._dot; + if (opt.DASH_VERTICAL) { + indicator.x_align = opt.DASH_LEFT ? Clutter.ActorAlign.START : Clutter.ActorAlign.END; + indicator.y_align = Clutter.ActorAlign.CENTER; + } else { + indicator.x_align = Clutter.ActorAlign.CENTER; + indicator.y_align = Clutter.ActorAlign.END; + } + + appIcon.connect('menu-state-changed', + (o, opened) => { + this._itemMenuStateChanged(item, opened); + }); + + let item = new Dash.DashItemContainer(); + item.setChild(appIcon); + + // Override default AppIcon label_actor, now the + // accessible_name is set at DashItemContainer.setLabelText + appIcon.label_actor = null; + item.setLabelText(app.get_name()); + + appIcon.icon.setIconSize(this.iconSize); + this._hookUpLabel(item, appIcon); + + return item; + }, + + // use custom BaseIconSizes and add support for custom icons + _adjustIconSize() { + // if a user launches multiple apps at once, this function may be called again before the previous call has finished + // as a result, new icons will not reach their full size, or will be missing, if adding a new icon and changing the dash size due to lack of space at the same time + if (this._adjustingInProgress) + return; + + // For the icon size, we only consider children which are "proper" + // icons (i.e. ignoring drag placeholders) and which are not + // animating out (which means they will be destroyed at the end of + // the animation) + let iconChildren = this._box.get_children().filter(actor => { + return actor.child && + actor.child._delegate && + actor.child._delegate.icon && + !actor.animatingOut; + }); + + // add new custom icons to the list + if (this._showAppsIcon.visible) + iconChildren.push(this._showAppsIcon); + + if (this._showWindowsIcon) + iconChildren.push(this._showWindowsIcon); + + if (this._recentFilesIcon) + iconChildren.push(this._recentFilesIcon); + + if (this._extensionsIcon) + iconChildren.push(this._extensionsIcon); + + if (!iconChildren.length) + return; + + if (this._maxWidth === -1 || this._maxHeight === -1) + return; + + const dashHorizontal = !opt.DASH_VERTICAL; + + const themeNode = this.get_theme_node(); + const maxAllocation = new Clutter.ActorBox({ + x1: 0, + y1: 0, + x2: dashHorizontal ? this._maxWidth : 42, // not whatever + y2: dashHorizontal ? 42 : this._maxHeight, + }); + + let maxContent = themeNode.get_content_box(maxAllocation); + + let spacing = themeNode.get_length('spacing'); + + let firstButton = iconChildren[0].child; + let firstIcon = firstButton._delegate.icon; + + if (!firstIcon.icon) + return; + + // Enforce valid spacings during the size request + firstIcon.icon.ensure_style(); + const [, , iconWidth, iconHeight] = firstIcon.icon.get_preferred_size(); + const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size(); + let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + + let availWidth, availHeight, maxIconSize; + if (dashHorizontal) { + availWidth = maxContent.x2 - maxContent.x1; + // Subtract icon padding and box spacing from the available width + availWidth -= iconChildren.length * (buttonWidth - iconWidth) + + (iconChildren.length - 1) * spacing + + 2 * this._background.get_theme_node().get_horizontal_padding(); + + availHeight = this._maxHeight; + availHeight -= this.margin_top + this.margin_bottom; + availHeight -= this._background.get_theme_node().get_vertical_padding(); + availHeight -= themeNode.get_vertical_padding(); + availHeight -= buttonHeight - iconHeight; + + maxIconSize = Math.min(availWidth / iconChildren.length, availHeight, opt.MAX_ICON_SIZE * scaleFactor); + } else { + availWidth = this._maxWidth; + availWidth -= this._background.get_theme_node().get_horizontal_padding(); + availWidth -= themeNode.get_horizontal_padding(); + availWidth -= buttonWidth - iconWidth; + + availHeight = maxContent.y2 - maxContent.y1; + availHeight -= iconChildren.length * (buttonHeight - iconHeight) + + (iconChildren.length - 1) * spacing + + 2 * this._background.get_theme_node().get_vertical_padding(); + + maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE * scaleFactor); + } + + let iconSizes = BaseIconSizes.map(s => s * scaleFactor); + + let newIconSize = BaseIconSizes[0]; + for (let i = 0; i < iconSizes.length; i++) { + if (iconSizes[i] <= maxIconSize) + newIconSize = BaseIconSizes[i]; + } + + if (newIconSize === this.iconSize) + return; + + // set the in-progress state here after all the possible cancels + this._adjustingInProgress = true; + + let oldIconSize = this.iconSize; + this.iconSize = newIconSize; + this.emit('icon-size-changed'); + + let scale = oldIconSize / newIconSize; + for (let i = 0; i < iconChildren.length; i++) { + let icon = iconChildren[i].child._delegate.icon; + + // Set the new size immediately, to keep the icons' sizes + // in sync with this.iconSize + icon.setIconSize(this.iconSize); + + // Don't animate the icon size change when the overview + // is transitioning, not visible or when initially filling + // the dash + if (!Main.overview.visible || Main.overview.animationInProgress || + !this._shownInitially) + continue; + + let [targetWidth, targetHeight] = icon.icon.get_size(); + + // Scale the icon's texture to the previous size and + // tween to the new size + icon.icon.set_size(icon.icon.width * scale, + icon.icon.height * scale); + + icon.icon.ease({ + width: targetWidth, + height: targetHeight, + duration: Dash.DASH_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + if (this._separator) { + this._separator.ease({ + width: dashHorizontal ? 1 : this.iconSize, + height: dashHorizontal ? this.iconSize : 1, + duration: Dash.DASH_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + this._adjustingInProgress = false; + }, + + handleDragOver(source, actor, x, y, _time) { + let app = Dash.getAppFromSource(source); + + // Don't allow favoriting of transient apps + if (app === null || app.is_window_backed()) + return DND.DragMotionResult.NO_DROP; + if (!global.settings.is_writable('favorite-apps')) + return DND.DragMotionResult.NO_DROP; + let favorites = AppFavorites.getAppFavorites().getFavorites(); + let numFavorites = favorites.length; + + let favPos = favorites.indexOf(app); + + let children = this._box.get_children(); + let numChildren = children.length; + let boxSize = opt.DASH_VERTICAL ? this._box.height : this._box.width; + + // Keep the placeholder out of the index calculation; assuming that + // the remove target has the same size as "normal" items, we don't + // need to do the same adjustment there. + if (this._dragPlaceholder) { + boxSize -= opt.DASH_VERTICAL ? this._dragPlaceholder.height : this._dragPlaceholder.width; + numChildren--; + } + + // Same with the separator + if (this._separator) { + boxSize -= opt.DASH_VERTICAL ? this._separator.height : this._separator.width; + numChildren--; + } + + let pos; + if (this._emptyDropTarget) + pos = 0; // always insert at the start when dash is empty + else if (this.text_direction === Clutter.TextDirection.RTL) + pos = numChildren - Math.floor((opt.DASH_VERTICAL ? y : x) * numChildren / boxSize); + else + pos = Math.floor((opt.DASH_VERTICAL ? y : x) * numChildren / boxSize); + + // Put the placeholder after the last favorite if we are not + // in the favorites zone + if (pos > numFavorites) + pos = numFavorites; + + if (pos !== this._dragPlaceholderPos && this._animatingPlaceholdersCount === 0) { + this._dragPlaceholderPos = pos; + + // Don't allow positioning before or after self + if (favPos !== -1 && (pos === favPos || pos === favPos + 1)) { + this._clearDragPlaceholder(); + return DND.DragMotionResult.CONTINUE; + } + + // If the placeholder already exists, we just move + // it, but if we are adding it, expand its size in + // an animation + let fadeIn; + if (this._dragPlaceholder) { + this._dragPlaceholder.destroy(); + fadeIn = false; + } else { + fadeIn = true; + } + + this._dragPlaceholder = new Dash.DragPlaceholderItem(); + this._dragPlaceholder.child.set_width(this.iconSize / (opt.DASH_VERTICAL ? 2 : 1)); + this._dragPlaceholder.child.set_height(this.iconSize / (opt.DASH_VERTICAL ? 1 : 2)); + this._box.insert_child_at_index( + this._dragPlaceholder, + this._dragPlaceholderPos); + this._dragPlaceholder.show(fadeIn); + } + + if (!this._dragPlaceholder) + return DND.DragMotionResult.NO_DROP; + + let srcIsFavorite = favPos !== -1; + + if (srcIsFavorite) + return DND.DragMotionResult.MOVE_DROP; + + return DND.DragMotionResult.COPY_DROP; + }, +}; + +const DashIconCommon = { + after__init() { + if (opt.DASH_ICON_SCROLL && !Me.Util.dashNotDefault()) { + this._scrollConId = this.connect('scroll-event', DashExtensions.onScrollEvent.bind(this)); + this._leaveConId = this.connect('leave-event', DashExtensions.onLeaveEvent.bind(this)); + } + }, + + popupMenu() { + const side = opt.DASH_VERTICAL ? St.Side.LEFT : St.Side.BOTTOM; + AppIconCommon.popupMenu.bind(this)(side); + }, + + _updateRunningStyle() { + const currentWs = global.workspace_manager.get_active_workspace(); + const show = opt.DASH_ISOLATE_WS + ? this.app.get_windows().filter(w => w.get_workspace() === currentWs).length + : this.app.state !== Shell.AppState.STOPPED; + + if (show) + this._dot.show(); + else + this._dot.hide(); + }, +}; + +const DashExtensions = { + onScrollEvent(source, event) { + if ((this.app && !opt.DASH_ICON_SCROLL) || (this._isSearchWindowsIcon && !opt.SEARCH_WINDOWS_ICON_SCROLL)) { + if (this._scrollConId) { + this.disconnect(this._scrollConId); + this._scrollConId = 0; + } + if (this._leaveConId) { + this.disconnect(this._leaveConId); + this._leaveConId = 0; + } + return Clutter.EVENT_PROPAGATE; + } + + if (Main.overview._overview.controls._stateAdjustment.value > 1) + return Clutter.EVENT_PROPAGATE; + + let direction = Me.Util.getScrollDirection(event); + if (direction === Clutter.ScrollDirection.UP) + direction = 1; + else if (direction === Clutter.ScrollDirection.DOWN) + direction = -1; + else + return Clutter.EVENT_STOP; + + // avoid uncontrollable switching if smooth scroll wheel or trackpad is used + if (this._lastScroll && Date.now() - this._lastScroll < 160) + return Clutter.EVENT_STOP; + + this._lastScroll = Date.now(); + + DashExtensions.switchWindow.bind(this)(direction); + return Clutter.EVENT_STOP; + }, + + onLeaveEvent() { + if (!this._selectedMetaWin || this.has_pointer || this.toggleButton?.has_pointer) + return; + + this._selectedPreview._activateSelected = false; + this._selectedMetaWin = null; + this._scrolledWindows = null; + DashExtensions.showWindowPreview.bind(this)(null); + }, + + + switchWindow(direction) { + if (!this._scrolledWindows) { + this._initialSelection = true; + // source is app icon + if (this.app) { + this._scrolledWindows = this.app.get_windows(); + if (opt.DASH_ISOLATE_WS) { + const currentWs = global.workspaceManager.get_active_workspace(); + this._scrolledWindows = this._scrolledWindows.filter(w => w.get_workspace() === currentWs); + } + + const wsList = []; + this._scrolledWindows.forEach(w => { + const ws = w.get_workspace(); + if (!wsList.includes(ws)) + wsList.push(ws); + }); + + // sort windows by workspaces in MRU order + this._scrolledWindows.sort((a, b) => wsList.indexOf(a.get_workspace()) > wsList.indexOf(b.get_workspace())); + // source is Search Windows icon + } else if (this._isSearchWindowsIcon) { + if (opt.SEARCH_WINDOWS_ICON_SCROLL === 1) // all windows + this._scrolledWindows = AltTab.getWindows(null); + else + this._scrolledWindows = AltTab.getWindows(global.workspace_manager.get_active_workspace()); + } + } + + let windows = this._scrolledWindows; + + if (!windows.length) + return; + + // if window selection is in the process, the previewed window must be the current one + let currentWin = this._selectedMetaWin ? this._selectedMetaWin : windows[0]; + + const currentIdx = windows.indexOf(currentWin); + let targetIdx = currentIdx; + const focusWindow = AltTab.getWindows(null)[0]; + const appFocused = this._scrolledWindows[0] === focusWindow && this._scrolledWindows[0].get_workspace() === global.workspace_manager.get_active_workspace(); + // only if the app has focus, immediately switch to the previous window + // otherwise just set the current window above others + if (!this._initialSelection || appFocused) + targetIdx += direction; + else + this._initialSelection = false; + + if (targetIdx > windows.length - 1) + targetIdx = 0; + else if (targetIdx < 0) + targetIdx = windows.length - 1; + + const metaWin = windows[targetIdx]; + DashExtensions.showWindowPreview.bind(this)(metaWin); + this._selectedMetaWin = metaWin; + }, + + showWindowPreview(metaWin) { + const views = Main.overview._overview.controls._workspacesDisplay._workspacesViews; + const viewsIter = [views[0]]; + // secondary monitors use different structure + views.forEach(v => { + if (v._workspacesView) + viewsIter.push(v._workspacesView); + }); + + viewsIter.forEach(view => { + // if workspaces are on primary monitor only + if (!view || !view._workspaces) + return; + + view._workspaces.forEach(ws => { + ws._windows.forEach(windowPreview => { + // metaWin === null resets opacity + let opacity = metaWin ? 50 : 255; + windowPreview._activateSelected = false; + + // minimized windows are invisible if windows are not exposed (WORKSPACE_MODE === 0) + if (!windowPreview.opacity) + windowPreview.opacity = 255; + + // app windows set to lower opacity, so they can be recognized + if (this._scrolledWindows && this._scrolledWindows.includes(windowPreview.metaWindow)) { + if (opt.DASH_ICON_SCROLL === 2) + opacity = 254; + } + if (windowPreview.metaWindow === metaWin) { + if (metaWin && metaWin.get_workspace() !== global.workspace_manager.get_active_workspace()) { + Main.wm.actionMoveWorkspace(metaWin.get_workspace()); + if (_timeouts.wsSwitcherAnimation) + GLib.source_remove(_timeouts.wsSwitcherAnimation); + // setting window preview above siblings before workspace switcher animation has no effect + // we need to set the window above after the ws preview become visible on the screen + // the default switcher animation time is 250, 200 ms delay should be enough + _timeouts.wsSwitcherAnimation = GLib.timeout_add(0, 200 * St.Settings.get().slow_down_factor, () => { + windowPreview.get_parent().set_child_above_sibling(windowPreview, null); + _timeouts.wsSwitcherAnimation = 0; + return GLib.SOURCE_REMOVE; + }); + } else { + windowPreview.get_parent().set_child_above_sibling(windowPreview, null); + } + + opacity = 255; + this._selectedPreview = windowPreview; + windowPreview._activateSelected = true; + } + + // if windows are exposed, highlight selected using opacity + if ((opt.OVERVIEW_MODE && opt.WORKSPACE_MODE) || !opt.OVERVIEW_MODE) { + if (metaWin && opacity === 255) + windowPreview.showOverlay(true); + else + windowPreview.hideOverlay(true); + windowPreview.ease({ + duration: 200, + opacity, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + }); + }); + }); + }, +}; + +const AppIconCommon = { + after__init() { + if (this._updateRunningDotStyle) + this._updateRunningDotStyle(); + }, + + _updateRunningDotStyle() { + if (opt.RUNNING_DOT_STYLE) + this._dot.add_style_class_name('app-well-app-running-dot-custom'); + else + this._dot.remove_style_class_name('app-well-app-running-dot-custom'); + }, + + activate(button) { + const event = Clutter.get_current_event(); + const state = event ? event.get_state() : 0; + const isMiddleButton = button && button === Clutter.BUTTON_MIDDLE; + const isCtrlPressed = Me.Util.isCtrlPressed(state); + const isShiftPressed = Me.Util.isShiftPressed(state); + + const currentWS = global.workspace_manager.get_active_workspace(); + const appRecentWorkspace = this._getAppRecentWorkspace(this.app); + // this feature shouldn't affect search results, dash icons don't have labels, so we use them as a condition + const showWidowsBeforeActivation = opt.DASH_CLICK_ACTION === 1 && !this.icon.label; + + let targetWindowOnCurrentWs = false; + if (opt.DASH_FOLLOW_RECENT_WIN) { + targetWindowOnCurrentWs = appRecentWorkspace === currentWS; + } else { + this.app.get_windows().forEach( + w => { + targetWindowOnCurrentWs = targetWindowOnCurrentWs || (w.get_workspace() === currentWS); + } + ); + } + + const openNewWindow = this.app.can_open_new_window() && + this.app.state === Shell.AppState.RUNNING && + (((isCtrlPressed || isMiddleButton) && !opt.DASH_CLICK_OPEN_NEW_WIN) || + (opt.DASH_CLICK_OPEN_NEW_WIN && !this._selectedMetaWin && !isMiddleButton) || + ((opt.DASH_CLICK_PREFER_WORKSPACE || opt.DASH_ISOLATE_WS) && !targetWindowOnCurrentWs)); + + if ((this.app.state === Shell.AppState.STOPPED || openNewWindow) && !isShiftPressed) + this.animateLaunch(); + + if (openNewWindow) { + this.app.open_new_window(-1); + // if DASH_CLICK_ACTION == "SHOW_WINS_BEFORE", the app has more than one window and has no window on the current workspace, + // don't activate the app immediately, only move the overview to the workspace with the app's recent window + } else if (showWidowsBeforeActivation && !isShiftPressed && this.app.get_n_windows() > 1 && !targetWindowOnCurrentWs/* && !(opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE)*/) { + + Main.wm.actionMoveWorkspace(appRecentWorkspace); + Main.overview.dash.showAppsButton.checked = false; + return; + } else if (this._selectedMetaWin) { + this._selectedMetaWin.activate(global.get_current_time()); + } else if (showWidowsBeforeActivation && opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && !isShiftPressed && this.app.get_n_windows() > 1) { + // expose windows + Main.overview._overview._controls._thumbnailsBox._activateThumbnailAtPoint(0, 0, global.get_current_time(), true); + return; + } else if (((opt.DASH_SHIFT_CLICK_MV && isShiftPressed) || ((opt.DASH_CLICK_PREFER_WORKSPACE || opt.DASH_ISOLATE_WS) && !openNewWindow)) && this.app.get_windows().length) { + this._moveAppToCurrentWorkspace(); + if (opt.DASH_ISOLATE_WS) { + this.app.activate(); + // hide the overview after the window is re-created + GLib.idle_add(GLib.PRIORITY_LOW, () => Main.overview.hide()); + } + return; + } else if (isShiftPressed) { + return; + } else { + this.app.activate(); + } + + Main.overview.hide(); + }, + + _moveAppToCurrentWorkspace() { + this.app.get_windows().forEach(w => w.change_workspace(global.workspace_manager.get_active_workspace())); + }, + + popupMenu(side = St.Side.LEFT) { + this.setForcedHighlight(true); + this._removeMenuTimeout(); + this.fake_release(); + + if (!this._getWindowsOnCurrentWs) { + this._getWindowsOnCurrentWs = function () { + const winList = []; + this.app.get_windows().forEach(w => { + if (w.get_workspace() === global.workspace_manager.get_active_workspace()) + winList.push(w); + }); + return winList; + }; + + this._windowsOnOtherWs = function () { + return (this.app.get_windows().length - this._getWindowsOnCurrentWs().length) > 0; + }; + } + + if (!this._menu) { + this._menu = new AppMenu.AppMenu(this, side, { + favoritesSection: true, + showSingleWindows: true, + }); + + this._menu.setApp(this.app); + this._openSigId = this._menu.connect('open-state-changed', (menu, isPoppedUp) => { + if (!isPoppedUp) + this._onMenuPoppedDown(); + }); + // Main.overview.connectObject('hiding', + this._hidingSigId = Main.overview.connect('hiding', + () => this._menu.close(), this); + + Main.uiGroup.add_actor(this._menu.actor); + this._menuManager.addMenu(this._menu); + } + + // once the menu is created, it stays unchanged and we need to modify our items based on current situation + if (this._addedMenuItems && this._addedMenuItems.length) + this._addedMenuItems.forEach(i => i.destroy()); + + + const popupItems = []; + + const separator = new PopupMenu.PopupSeparatorMenuItem(); + this._menu.addMenuItem(separator); + + if (this.app.get_n_windows()) { + // if (/* opt.APP_MENU_FORCE_QUIT*/true) {} + popupItems.push([_('Force Quit'), () => { + this.app.get_windows()[0].kill(); + }]); + + // if (opt.APP_MENU_CLOSE_WS) {} + const nWin = this._getWindowsOnCurrentWs().length; + if (nWin) { + popupItems.push([_(`Close ${nWin} Windows on Current Workspace`), () => { + const windows = this._getWindowsOnCurrentWs(); + let time = global.get_current_time(); + for (let win of windows) { + // increase time by 1 ms for each window to avoid errors from GS + win.delete(time++); + } + }]); + } + + popupItems.push([_('Move App to Current Workspace ( Shift + Click )'), this._moveAppToCurrentWorkspace]); + if (opt.WINDOW_THUMBNAIL_ENABLED) { + popupItems.push([_('Create Window Thumbnail - PIP'), () => { + Me.Modules.winTmbModule.createThumbnail(this.app.get_windows()[0]); + }]); + } + } + + this._addedMenuItems = []; + this._addedMenuItems.push(separator); + popupItems.forEach(i => { + let item = new PopupMenu.PopupMenuItem(i[0]); + this._menu.addMenuItem(item); + item.connect('activate', i[1].bind(this)); + if (i[1] === this._moveAppToCurrentWorkspace && !this._windowsOnOtherWs()) + item.setSensitive(false); + this._addedMenuItems.push(item); + }); + + this.emit('menu-state-changed', true); + + this._menu.open(BoxPointer.PopupAnimation.FULL); + this._menuManager.ignoreRelease(); + this.emit('sync-tooltip'); + + return false; + }, + + _getWindowApp(metaWin) { + const tracker = Shell.WindowTracker.get_default(); + return tracker.get_window_app(metaWin); + }, + + _getAppLastUsedWindow(app) { + let recentWin; + global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { + const winApp = this._getWindowApp(metaWin); + if (!recentWin && winApp === app) + recentWin = metaWin; + }); + return recentWin; + }, + + _getAppRecentWorkspace(app) { + const recentWin = this._getAppLastUsedWindow(app); + if (recentWin) + return recentWin.get_workspace(); + + return null; + }, +}; + +const ShowWindowsIcon = { + _afterInit() { + this._isSearchWindowsIcon = true; + this._labelText = _('Search Open Windows (Hotkey: Space)'); + this.toggleButton = new St.Button({ + style_class: 'show-apps', + track_hover: true, + can_focus: true, + toggle_mode: false, + }); + + this._iconActor = null; + this.icon = new IconGrid.BaseIcon(this.labelText, { + setSizeManually: true, + showLabel: false, + createIcon: this._createIcon.bind(this), + }); + this.icon.y_align = Clutter.ActorAlign.CENTER; + + this.toggleButton.add_actor(this.icon); + this.toggleButton._delegate = this; + + this.setChild(this.toggleButton); + + if (opt.SEARCH_WINDOWS_ICON_SCROLL) { + this.reactive = true; + this._scrollConId = this.connect('scroll-event', DashExtensions.onScrollEvent.bind(this)); + this._leaveConId = this.connect('leave-event', DashExtensions.onLeaveEvent.bind(this)); + } + }, + + _createIcon(size) { + this._iconActor = new St.Icon({ + icon_name: 'focus-windows-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }); + return this._iconActor; + }, +}; + +const ShowRecentFilesIcon = { + _afterInit() { + this._labelText = _('Search Recent Files (Hotkey: Ctrl + Space)'); + this.toggleButton = new St.Button({ + style_class: 'show-apps', + track_hover: true, + can_focus: true, + toggle_mode: false, + }); + + this._iconActor = null; + this.icon = new IconGrid.BaseIcon(this.labelText, { + setSizeManually: true, + showLabel: false, + createIcon: this._createIcon.bind(this), + }); + this.icon.y_align = Clutter.ActorAlign.CENTER; + + this.toggleButton.add_actor(this.icon); + this.toggleButton._delegate = this; + + this.setChild(this.toggleButton); + }, + + _createIcon(size) { + this._iconActor = new St.Icon({ + icon_name: 'document-open-recent-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }); + return this._iconActor; + }, +}; + +const ShowExtensionsIcon = { + _afterInit() { + this._labelText = _('Search Extensions (Hotkey: Ctrl + Shift + Space)'); + this.toggleButton = new St.Button({ + style_class: 'show-apps', + track_hover: true, + can_focus: true, + toggle_mode: false, + }); + + this._iconActor = null; + this.icon = new IconGrid.BaseIcon(this.labelText, { + setSizeManually: true, + showLabel: false, + createIcon: this._createIcon.bind(this), + }); + this.icon.y_align = Clutter.ActorAlign.CENTER; + + this.toggleButton.add_actor(this.icon); + this.toggleButton._delegate = this; + + this.setChild(this.toggleButton); + }, + + _createIcon(size) { + this._iconActor = new St.Icon({ + icon_name: 'application-x-addon-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }); + return this._iconActor; + }, +}; + +const AppMenuCommon = { + _updateWindowsSection() { + if (global.compositor) { + if (this._updateWindowsLaterId) { + const laters = global.compositor.get_laters(); + laters.remove(this._updateWindowsLaterId); + } + } else if (this._updateWindowsLaterId) { + Meta.later_remove(this._updateWindowsLaterId); + } + + this._updateWindowsLaterId = 0; + + this._windowSection.removeAll(); + this._openWindowsHeader.hide(); + + if (!this._app) + return; + + const minWindows = this._showSingleWindows ? 1 : 2; + const currentWs = global.workspaceManager.get_active_workspace(); + const isolateWs = opt.DASH_ISOLATE_WS && !Main.overview.dash.showAppsButton.checked; + const windows = this._app.get_windows().filter(w => !w.skip_taskbar && (isolateWs ? w.get_workspace() === currentWs : true)); + if (windows.length < minWindows) + return; + + this._openWindowsHeader.show(); + + windows.forEach(window => { + const title = window.title || this._app.get_name(); + const item = this._windowSection.addAction(title, event => { + Main.activateWindow(window, event.get_time()); + }); + window.connectObject('notify::title', () => { + item.label.text = window.title || this._app.get_name(); + }, item); + }); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/extensionsSearchProvider.js b/extensions/44/vertical-workspaces/lib/extensionsSearchProvider.js new file mode 100644 index 0000000..5d0f28a --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/extensionsSearchProvider.js @@ -0,0 +1,423 @@ +/** +* V-Shell (Vertical Workspaces) + * extensionsSearchProvider.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + */ + +'use strict'; + +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const St = imports.gi.St; +const Shell = imports.gi.Shell; +const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; + +const Main = imports.ui.main; + +const ExtensionState = { + 1: 'ENABLED', + 2: 'DISABLED', + 3: 'ERROR', + 4: 'INCOMPATIBLE', + 5: 'DOWNLOADING', + 6: 'INITIALIZED', + 7: 'DISABLING', + 8: 'ENABLING', +}; + +let Me; +let opt; +// gettext +let _; +let _toggleTimeout; + +// prefix helps to eliminate results from other search providers +// so it needs to be something less common +// needs to be accessible from vw module +const PREFIX = 'eq//'; + +var ExtensionsSearchProviderModule = class { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._extensionsSearchProvider = null; + this._enableTimeoutId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + if (_toggleTimeout) + GLib.source_remove(_toggleTimeout); + + this.moduleEnabled = opt.get('extensionsSearchProviderModule'); + + reset = reset || !this.moduleEnabled; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' ExtensionsSearchProviderModule - Keeping untouched'); + } + + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._extensionsSearchProvider) { + this._extensionsSearchProvider = new extensionsSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._extensionsSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } + ); + console.debug(' ExtensionsSearchProviderModule - Activated'); + } + + _disableModule() { + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } + + if (this._extensionsSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._extensionsSearchProvider); + this._extensionsSearchProvider = null; + } + + console.debug(' ExtensionsSearchProviderModule - Disabled'); + } + + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +}; + +class extensionsSearchProvider { + constructor() { + this.id = 'extensions'; + const appSystem = Shell.AppSystem.get_default(); + let appInfo = appSystem.lookup_app('com.matjakeman.ExtensionManager.desktop')?.get_app_info(); + if (!appInfo) + appInfo = appSystem.lookup_app('org.gnome.Extensions.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/gnome-extensions-app', 'Extensions', null); + appInfo.get_description = () => _('Search extensions'); + appInfo.get_name = () => _('Extensions'); + appInfo.get_id = () => 'org.gnome.Extensions.desktop'; + appInfo.get_icon = () => Gio.icon_new_for_string('application-x-addon'); + appInfo.should_show = () => true; + + this.appInfo = appInfo; + this.canLaunchSearch = true; + this.isRemoteProvider = false; + } + + getInitialResultSet(terms, callback /* , cancellable = null*/) { + // In GS 43 callback arg has been removed + /* if (Me.shellVersion >= 43) + cancellable = callback; */ + + const extensions = {}; + Main.extensionManager._extensions.forEach( + e => { + extensions[e.uuid] = e; + } + ); + this.extensions = extensions; + + if (Me.shellVersion >= 43) + return new Promise(resolve => resolve(this._getResultSet(terms))); + else + callback(this._getResultSet(terms)); + + return null; + } + + _getResultSet(terms) { + // do not modify original terms + let termsCopy = [...terms]; + // search for terms without prefix + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); + + const candidates = this.extensions; + const _terms = [].concat(termsCopy); + + const term = _terms.join(' '); + + const results = []; + let m; + for (let id in candidates) { + const extension = this.extensions[id]; + const text = extension.metadata.name + (extension.state === 1 ? 'enabled' : '') + ([6, 2].includes(extension.state) ? 'disabled' : ''); + if (opt.SEARCH_FUZZY) + m = Me.Util.fuzzyMatch(term, text); + else + m = Me.Util.strictMatch(term, text); + + if (m !== -1) + results.push({ weight: m, id }); + } + + // sort alphabetically + results.sort((a, b) => this.extensions[a.id].metadata.name.localeCompare(this.extensions[b.id].metadata.name)); + // enabled first + // results.sort((a, b) => this.extensions[a.id].state !== 1 && this.extensions[b.id].state === 1); + // incompatible last + results.sort((a, b) => this.extensions[a.id].state === 4 && this.extensions[b.id].state !== 4); + + this.resultIds = results.map(item => item.id); + return this.resultIds; + } + + getResultMetas(resultIds, callback = null) { + const metas = resultIds.map(id => this.getResultMeta(id)); + if (Me.shellVersion >= 43) + return new Promise(resolve => resolve(metas)); + else if (callback) + callback(metas); + return null; + } + + getResultMeta(resultId) { + const result = this.extensions[resultId]; + + const versionName = result.metadata['version-name'] ?? ''; + let version = result.metadata['version'] ?? ''; + version = versionName && version ? `/${version}` : version; + const versionStr = `${versionName}${version}`; + + return { + 'id': resultId, + 'name': `${result.metadata.name}`, + 'version': versionStr, + 'description': versionStr, // description will be updated in result object + 'createIcon': size => { + let icon = this.getIcon(result, size); + return icon; + }, + }; + } + + getIcon(extension, size) { + let opacity = 0; + let iconName = 'process-stop-symbolic'; + + switch (extension.state) { + case 1: + if (extension.hasUpdate) + iconName = 'software-update-available'; // 'software-update-available-symbolic'; + else + iconName = 'object-select';// 'object-select-symbolic'; + + opacity = 255; + break; + case 3: + if (Main.extensionManager._enabledExtensions.includes(extension.uuid)) + iconName = 'emblem-ok-symbolic'; + else + iconName = 'dialog-error'; + opacity = 100; + break; + case 4: + iconName = 'software-update-urgent'; // 'software-update-urgent-symbolic'; + opacity = 100; + break; + } + + if (extension.hasUpdate) { + iconName = 'software-update-available'; // 'software-update-available-symbolic'; + opacity = 100; + } + + const icon = new St.Icon({ icon_name: iconName, icon_size: size }); + icon.set({ + reactive: true, + opacity, + }); + + return icon; + } + + createResultObject(meta) { + return new ListSearchResult(this, meta, this.extensions[meta.id]); + } + + launchSearch(terms, timeStamp) { + this.appInfo.launch([], global.create_app_launch_context(timeStamp, -1), null); + } + + activateResult(resultId/* terms, timeStamp*/) { + const extension = this.extensions[resultId]; + if (Me.Util.isShiftPressed()) + this._toggleExtension(extension); + else if (extension.hasPrefs) + Me.Util.openPreferences(extension.metadata); + } + + filterResults(results /* , maxResults*/) { + // return results.slice(0, maxResults); + return results; + } + + getSubsearchResultSet(previousResults, terms, callback) { + if (Me.shellVersion < 43) { + this.getSubsearchResultSet42(terms, callback); + return null; + } + return this.getInitialResultSet(terms); + } + + getSubsearchResultSet42(terms, callback) { + callback(this._getResultSet(terms)); + } +} + +const ListSearchResult = GObject.registerClass( +class ListSearchResult extends St.Button { + _init(provider, metaInfo, extension) { + this.provider = provider; + this.metaInfo = metaInfo; + this.extension = extension; + + super._init({ + reactive: true, + can_focus: true, + track_hover: true, + }); + + this.style_class = 'list-search-result'; + + let content = new St.BoxLayout({ + style_class: 'list-search-result-content', + vertical: false, + x_align: Clutter.ActorAlign.START, + x_expand: true, + y_expand: true, + }); + this.set_child(content); + + let titleBox = new St.BoxLayout({ + style_class: 'list-search-result-title', + y_align: Clutter.ActorAlign.CENTER, + }); + + content.add_child(titleBox); + + // An icon for, or thumbnail of, content + let icon = this.metaInfo['createIcon'](this.ICON_SIZE); + let iconBox = new St.Button(); + iconBox.set_child(icon); + titleBox.add(iconBox); + iconBox.set_style('border: 1px solid rgba(200,200,200,0.2); padding: 2px; border-radius: 8px;'); + this._iconBox = iconBox; + this.icon = icon; + + iconBox.connect('clicked', () => { + this._toggleExtension(); + return Clutter.EVENT_STOP; + }); + + let title = new St.Label({ + text: this.metaInfo['name'], + y_align: Clutter.ActorAlign.CENTER, + opacity: extension.hasPrefs ? 255 : 150, + }); + titleBox.add_child(title); + + this.label_actor = title; + + this._descriptionLabel = new St.Label({ + style_class: 'list-search-result-description', + y_align: Clutter.ActorAlign.CENTER, + }); + content.add_child(this._descriptionLabel); + + this._highlightTerms(); + + this.connect('destroy', () => { + if (_toggleTimeout) { + GLib.source_remove(_toggleTimeout); + _toggleTimeout = 0; + } + }); + } + + _toggleExtension() { + const state = this.extension.state; + if (![1, 2, 6, 3].includes(state) || this.extension.metadata.uuid.includes('vertical-workspaces')) + return; + + if ([2, 6].includes(state)) + Main.extensionManager.enableExtension(this.extension.uuid); + else if ([1, 3].includes(state)) + Main.extensionManager.disableExtension(this.extension.uuid); + + if (_toggleTimeout) + GLib.source_remove(_toggleTimeout); + + _toggleTimeout = GLib.timeout_add(GLib.PRIORITY_LOW, 200, + () => { + if ([7, 8].includes(this.extension.state)) + return GLib.SOURCE_CONTINUE; + + this.icon?.destroy(); + this.icon = this.metaInfo['createIcon'](this.ICON_SIZE); + this._iconBox.set_child(this.icon); + this._highlightTerms(); + + _toggleTimeout = 0; + return GLib.SOURCE_REMOVE; + } + ); + } + + get ICON_SIZE() { + return 24; + } + + _highlightTerms() { + const extension = this.extension; + const state = extension.state === 4 ? ExtensionState[this.extension.state] : ''; + const error = extension.state === 3 ? ` ERROR: ${this.extension.error}` : ''; + const update = extension.hasUpdate ? ' | UPDATE PENDING' : ''; + const text = `${this.metaInfo.version} ${state}${error}${update}`; + let markup = text;// this.metaInfo['description'].split('\n')[0]; + this._descriptionLabel.clutter_text.set_markup(markup); + } + + vfunc_clicked() { + this.activate(); + } + + activate() { + this.provider.activateResult(this.metaInfo.id); + + if (this.metaInfo.clipboardText) { + St.Clipboard.get_default().set_text( + St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText); + } + Main.overview.toggle(); + } +}); diff --git a/extensions/vertical-workspaces/lib/iconGrid.js b/extensions/44/vertical-workspaces/lib/iconGrid.js similarity index 69% copy from extensions/vertical-workspaces/lib/iconGrid.js copy to extensions/44/vertical-workspaces/lib/iconGrid.js index 1aa980e..1f7516b 100644 --- a/extensions/vertical-workspaces/lib/iconGrid.js +++ b/extensions/44/vertical-workspaces/lib/iconGrid.js @@ -9,11 +9,13 @@ */ 'use strict'; -const { GLib, St, Meta } = imports.gi; + +const St = imports.gi.St; + const IconGrid = imports.ui.iconGrid; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; -const shellVersion = _Util.shellVersion; + +let Me; +let opt; // added sizes for better scaling const IconSize = { @@ -29,47 +31,58 @@ const IconSize = { LARGE: 96, 80: 80, 64: 64, - 48: 48, - TINY: 32, + TINY: 48, }; const PAGE_WIDTH_CORRECTION = 100; -let opt; -let _overrides; -let _firstRun = true; +var IconGridModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('appDisplayModule', true); - reset = reset || !moduleEnabled; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; } - _overrides = new _Util.Overrides(); + update(reset) { + this.moduleEnabled = opt.get('appDisplayModule'); + // if notifications are enabled no override is needed + reset = reset || !this.moduleEnabled; - if (shellVersion < 43 && IconGridCommon._findBestModeForSize) { - IconGridCommon['findBestModeForSize'] = IconGridCommon._findBestModeForSize; - IconGridCommon['_findBestModeForSize'] = undefined; + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } } - _overrides.addOverride('IconGrid', IconGrid.IconGrid.prototype, IconGridCommon); - _overrides.addOverride('IconGridLayout', IconGrid.IconGridLayout.prototype, IconGridLayoutCommon); -} -// workaround - silence page -2 error on gnome 43 while cleaning app grid + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + if (Me.shellVersion < 43 && IconGridCommon._findBestModeForSize) { + IconGridCommon['findBestModeForSize'] = IconGridCommon._findBestModeForSize; + delete IconGridCommon['_findBestModeForSize']; + } + this._overrides.addOverride('IconGrid', IconGrid.IconGrid.prototype, IconGridCommon); + this._overrides.addOverride('IconGridLayout', IconGrid.IconGridLayout.prototype, IconGridLayoutCommon); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + } +}; const IconGridCommon = { getItemsAtPage(page) { @@ -87,7 +100,7 @@ const IconGridCommon = { return; const { pagePadding } = this.layout_manager; const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const iconPadding = 53 * scaleFactor; + const iconPadding = 51 * scaleFactor; // provided width is usually about 100px wider in horizontal orientation with prev/next page indicators const pageIndicatorCompensation = opt.ORIENTATION ? 0 : PAGE_WIDTH_CORRECTION; @@ -99,8 +112,13 @@ const IconGridCommon = { this.layoutManager._gridWidth = width; this.layoutManager._gridHeight = height; + width -= 80; // compensation for default padding + height -= 80; + const spacing = opt.APP_GRID_SPACING; - const iconSize = (opt.APP_GRID_ICON_SIZE > 0 ? opt.APP_GRID_ICON_SIZE : opt.APP_GRID_ICON_SIZE_DEFAULT) * scaleFactor; + // set the icon size as fixed to avoid changes in size later + const iconSize = opt.APP_GRID_ICON_SIZE > 0 ? opt.APP_GRID_ICON_SIZE : opt.APP_GRID_ICON_SIZE_DEFAULT; + const itemSize = iconSize * scaleFactor + iconPadding; // if this._gridModes.length === 1, custom grid should be used // if (iconSize > 0 && this._gridModes.length > 1) { let columns = opt.APP_GRID_COLUMNS; @@ -109,17 +127,20 @@ const IconGridCommon = { let unusedSpaceH = -1; let unusedSpaceV = -1; if (!columns) { - columns = Math.floor(width / (iconSize + iconPadding)) + 1; + // calculate #columns + 1 without spacing + columns = Math.floor(width / itemSize) + 1; + // check if columns with spacing fits the available width + // and reduce the number until it fits while (unusedSpaceH < 0) { columns -= 1; - unusedSpaceH = width - columns * (iconSize + iconPadding) - (columns - 1) * spacing; + unusedSpaceH = width - columns * itemSize - (columns - 1) * spacing; } } if (!rows) { - rows = Math.floor(height / (iconSize + iconPadding)) + 1; + rows = Math.floor(height / itemSize) + 1; while (unusedSpaceV < 0) { rows -= 1; - unusedSpaceV = height - rows * (iconSize + iconPadding) - (rows - 1) * spacing; + unusedSpaceV = height - rows * itemSize - ((rows - 1) * spacing); } } @@ -135,9 +156,16 @@ const IconGridLayoutCommon = { const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); const nColumns = this.columnsPerPage; const nRows = this.rowsPerPage; + + // if grid is not defined return default icon size + if (nColumns < 1 && nRows < 1) { + return this._isFolder + ? opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT : opt.APP_GRID_ICON_SIZE_DEFAULT; + } + const columnSpacingPerPage = opt.APP_GRID_SPACING * (nColumns - 1); const rowSpacingPerPage = opt.APP_GRID_SPACING * (nRows - 1); - const iconPadding = 53 * scaleFactor; + const iconPadding = 55 * scaleFactor; const paddingH = this._isFolder ? this.pagePadding.left + this.pagePadding.right : 0; const paddingV = this._isFolder ? this.pagePadding.top + this.pagePadding.bottom : 0; @@ -156,10 +184,14 @@ const IconGridLayoutCommon = { return opt.APP_GRID_ICON_SIZE_DEFAULT;*/ let iconSizes = Object.values(IconSize).sort((a, b) => b - a); - - // limit max icon size for folders, the whole range is for the main grid with active folders - if (this._isFolder) + // limit max icon size for folders and fully adaptive folder grids, the whole range is for the main grid with active folders + if (this._isFolder && opt.APP_GRID_FOLDER_ADAPTIVE && opt.APP_GRID_FOLDER_ICON_SIZE < 0) + iconSizes = iconSizes.slice(iconSizes.indexOf(opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT), -1); + else if (this._isFolder) iconSizes = iconSizes.slice(iconSizes.indexOf(IconSize.LARGE), -1); + else if (opt.APP_GRID_ADAPTIVE && opt.APP_GRID_ICON_SIZE < 0) + iconSizes = iconSizes.slice(iconSizes.indexOf(opt.APP_GRID_ICON_SIZE_DEFAULT), -1); + let sizeInvalid = false; for (const size of iconSizes) { @@ -167,10 +199,9 @@ const IconGridLayoutCommon = { if (firstItem) { firstItem.icon.setIconSize(size); - const [firstItemWidth, firstItemHeight] = - firstItem.get_preferred_size(); + const [firstItemWidth] = firstItem.get_preferred_size(); - const itemSize = Math.max(firstItemWidth, firstItemHeight); + const itemSize = firstItemWidth; if (itemSize < size) sizeInvalid = true; @@ -199,7 +230,7 @@ const IconGridLayoutCommon = { removeItem(item) { if (!this._items.has(item)) { - log(`Item ${item} is not part of the IconGridLayout`); + console.error(`Item ${item} is not part of the IconGridLayout`); return; // throw new Error(`Item ${item} is not part of the IconGridLayout`); } @@ -215,13 +246,13 @@ const IconGridLayoutCommon = { addItem(item, page = -1, index = -1) { if (this._items.has(item)) { - log(`iconGrid: Item ${item} already added to IconGridLayout`); + console.error(`iconGrid: Item ${item} already added to IconGridLayout`); return; // throw new Error(`Item ${item} already added to IconGridLayout`); } if (page > this._pages.length) { - log(`iconGrid: Cannot add ${item} to page ${page}`); + console.error(`iconGrid: Cannot add ${item} to page ${page}`); page = -1; index = -1; // throw new Error(`Cannot add ${item} to page ${page}`); @@ -240,7 +271,7 @@ const IconGridLayoutCommon = { moveItem(item, newPage, newPosition) { if (!this._items.has(item)) { - log(`iconGrid: Item ${item} is not part of the IconGridLayout`); + console.error(`iconGrid: Item ${item} is not part of the IconGridLayout`); return; // throw new Error(`Item ${item} is not part of the IconGridLayout`); } diff --git a/extensions/vertical-workspaces/lib/layout.js b/extensions/44/vertical-workspaces/lib/layout.js similarity index 67% copy from extensions/vertical-workspaces/lib/layout.js copy to extensions/44/vertical-workspaces/lib/layout.js index f6562fd..6e72645 100644 --- a/extensions/vertical-workspaces/lib/layout.js +++ b/extensions/44/vertical-workspaces/lib/layout.js @@ -10,67 +10,107 @@ 'use strict'; -const { Meta, GLib, Shell, Clutter, GObject } = imports.gi; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const Gio = imports.gi.Gio; -const Main = imports.ui.main; const Layout = imports.ui.layout; -const Ripples = imports.ui.ripples; -const DND = imports.ui.dnd; +const Main = imports.ui.main; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -let _overrides; -let _timeouts; +let Me; let opt; -let _firstRun = true; -let _originalUpdateHotCorners; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('layoutModule', true); - const conflict = _Util.getEnabledExtensions('custom-hot-corners').length || - _Util.getEnabledExtensions('dash-to-panel').length; - reset = reset || !moduleEnabled; +let _timeouts; - // don't even touch this module if disabled or in conflict - if (_firstRun && (reset || conflict)) - return; +var LayoutModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _timeouts = {}; - _firstRun = false; - - if (!_originalUpdateHotCorners) - _originalUpdateHotCorners = Layout.LayoutManager.prototype._updateHotCorners; - - if (_overrides) - _overrides.removeAll(); - - if (_timeouts) { - Object.values(_timeouts).forEach(t => { - if (t) - GLib.source_remove(t); - }); + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._originalUpdateHotCorners = null; } - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - _timeouts = null; - Main.layoutManager._updateHotCorners = _originalUpdateHotCorners; - Main.layoutManager._updateHotCorners(); - return; } - _timeouts = {}; + update(reset) { + this._removeTimeouts(); - _overrides = new _Util.Overrides(); - _overrides.addOverride('LayoutManager', Layout.LayoutManager.prototype, LayoutManagerCommon); + this.moduleEnabled = opt.get('layoutModule'); + const conflict = Me.Util.getEnabledExtensions('custom-hot-corners').length || + Me.Util.getEnabledExtensions('dash-to-panel').length; - Main.layoutManager._updateHotCorners = LayoutManagerCommon._updateHotCorners.bind(Main.layoutManager); + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Layout" module disabled due to potential conflict with another extension`); - Main.layoutManager._updatePanelBarrier(); - Main.layoutManager._updateHotCorners(); -} + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' LayoutModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + this._overrides.addOverride('LayoutManager', Main.layoutManager, LayoutManagerCommon); + this._overrides.addOverride('HotCorner', Layout.HotCorner.prototype, HotCornerCommon); + + Main.layoutManager._updatePanelBarrier(); + Main.layoutManager._updateHotCorners(); + + if (!this._hotCornersEnabledConId) { + this._interfaceSettings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.interface', + }); + this._hotCornersEnabledConId = this._interfaceSettings.connect('changed::enable-hot-corners', + () => Main.layoutManager._updateHotCorners()); + } + + console.debug(' LayoutModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + Main.layoutManager._updateHotCorners(); + + if (this._hotCornersEnabledConId) { + this._interfaceSettings.disconnect(this._hotCornersEnabledConId); + this._hotCornersEnabledConId = 0; + this._interfaceSettings = null; + } + + console.debug(' LayoutModule - Disabled'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } +}; const LayoutManagerCommon = { _updatePanelBarrier() { @@ -84,7 +124,7 @@ const LayoutManagerCommon = { this._leftPanelBarrier = null; } - if (!this.primaryMonitor || !opt) + if (!this.primaryMonitor || !opt || Me.Util.getEnabledExtensions('hidetopbar')) return; if (this.panelBox.height) { @@ -113,6 +153,7 @@ const LayoutManagerCommon = { // avoid errors if called from foreign override if (!opt) return; + // destroy old hot corners this.hotCorners.forEach(corner => corner?.destroy()); this.hotCorners = []; @@ -122,7 +163,7 @@ const LayoutManagerCommon = { return; } - let size = this.panelBox.height; + let size = this.panelBox.height ? this.panelBox.height : 27; // position 0 - default, 1-TL, 2-TR, 3-BL, 4-BR const position = opt.HOT_CORNER_POSITION; @@ -181,8 +222,8 @@ const LayoutManagerCommon = { } if (haveCorner) { - let corner = new HotCorner(this, monitor, cornerX, cornerY); - corner.setBarrierSize(size); + let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY); + corner.setBarrierSize(size, false); this.hotCorners.push(corner); } else { this.hotCorners.push(null); @@ -193,11 +234,8 @@ const LayoutManagerCommon = { }, }; -var HotCorner = GObject.registerClass( -class HotCorner extends Layout.HotCorner { - _init(layoutManager, monitor, x, y) { - super._init(layoutManager, monitor, x, y); - +const HotCornerCommon = { + after__init() { let angle = 0; switch (opt.HOT_CORNER_POSITION) { case 2: @@ -214,9 +252,14 @@ class HotCorner extends Layout.HotCorner { this._ripples._ripple1.rotation_angle_z = angle; this._ripples._ripple2.rotation_angle_z = angle; this._ripples._ripple3.rotation_angle_z = angle; - } + }, + + setBarrierSize(size, notMyCall = true) { + // ignore calls from the original _updateHotCorners() callback to avoid building barriers outside screen + if (notMyCall && size > 0) { + return; + } - setBarrierSize(size) { if (this._verticalBarrier) { this._pressureBarrier.removeBarrier(this._verticalBarrier); this._verticalBarrier.destroy(); @@ -232,8 +275,8 @@ class HotCorner extends Layout.HotCorner { if (size > 0) { const primaryMonitor = global.display.get_primary_monitor(); const monitor = this._monitor; - const extendV = opt && opt.HOT_CORNER_EDGE && opt.DASH_VERTICAL && monitor.index === primaryMonitor; - const extendH = opt && opt.HOT_CORNER_EDGE && !opt.DASH_VERTICAL && monitor.index === primaryMonitor; + const extendV = opt && opt.HOT_CORNER_ACTION && opt.HOT_CORNER_EDGE && opt.DASH_VERTICAL && monitor.index === primaryMonitor; + const extendH = opt && opt.HOT_CORNER_ACTION && opt.HOT_CORNER_EDGE && !opt.DASH_VERTICAL && monitor.index === primaryMonitor; if (opt.HOT_CORNER_POSITION <= 1) { this._verticalBarrier = new Meta.Barrier({ @@ -284,25 +327,52 @@ class HotCorner extends Layout.HotCorner { this._pressureBarrier.addBarrier(this._verticalBarrier); this._pressureBarrier.addBarrier(this._horizontalBarrier); } - } + }, _toggleOverview() { if (!opt.HOT_CORNER_ACTION || (!opt.HOT_CORNER_FULLSCREEN && this._monitor.inFullscreen && !Main.overview.visible)) return; if (Main.overview.shouldToggleByCornerOrButton()) { - if ((opt.HOT_CORNER_ACTION === 1 && !_Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 2 && _Util.isCtrlPressed())) + if (Main.overview._shown) { this._toggleWindowPicker(true); - else if ((opt.HOT_CORNER_ACTION === 2 && !_Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 1 && _Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 3 && _Util.isCtrlPressed())) + } else if ((opt.HOT_CORNER_ACTION === 2 && !Me.Util.isCtrlPressed()) || ([3, 4, 5, 6].includes(opt.HOT_CORNER_ACTION) && Me.Util.isCtrlPressed())) { + // Default overview + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 1) { + Main.overview.resetOverviewMode(); + this._toggleWindowPicker(true, true); + } else if ((opt.HOT_CORNER_ACTION === 3 && !Me.Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 2 && Me.Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 6 && Me.Util.isCtrlPressed())) { + // Applications this._toggleApplications(true); - else if (opt.HOT_CORNER_ACTION === 3 && !_Util.isCtrlPressed()) + } else if (opt.HOT_CORNER_ACTION === 4 && !Me.Util.isCtrlPressed()) { + // Overview - static ws preview + opt.OVERVIEW_MODE = 1; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 5 && !Me.Util.isCtrlPressed()) { + // Overview - static ws + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 6 && !Me.Util.isCtrlPressed()) { + // Window search provider + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; this._toggleWindowSearchProvider(); + } if (opt.HOT_CORNER_RIPPLES && Main.overview.animationInProgress) this._ripples.playAnimation(this._x, this._y); } - } + }, - _toggleWindowPicker(leaveOverview = false) { + _toggleWindowPicker(leaveOverview = false, customOverviewMode = false) { if (Main.overview._shown && (leaveOverview || !Main.overview.dash.showAppsButton.checked)) { Main.overview.hide(); } else if (Main.overview.dash.showAppsButton.checked) { @@ -320,17 +390,17 @@ class HotCorner extends Layout.HotCorner { // delay cannot be too short 200, () => { - Main.overview.show(); + Main.overview.show(1, customOverviewMode); _timeouts.releaseKeyboardTimeoutId = 0; return GLib.SOURCE_REMOVE; } ); } else { - Main.overview.show(); + Main.overview.show(1, customOverviewMode); } } - } + }, _toggleApplications(leaveOverview = false) { if ((leaveOverview && Main.overview._shown) || Main.overview.dash.showAppsButton.checked) { @@ -360,12 +430,15 @@ class HotCorner extends Layout.HotCorner { Main.overview.show(2); // 2 for App Grid } } - } + }, _toggleWindowSearchProvider() { if (!Main.overview._overview._controls._searchController._searchActive) { - this._toggleWindowPicker(); - const prefix = 'wq// '; + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(false, true); + const prefix = Me.WSP_PREFIX; const position = prefix.length; const searchEntry = Main.overview.searchEntry; searchEntry.set_text(prefix); @@ -376,5 +449,5 @@ class HotCorner extends Layout.HotCorner { // Main.overview.searchEntry.text = ''; Main.overview.hide(); } - } -}); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/messageTray.js b/extensions/44/vertical-workspaces/lib/messageTray.js new file mode 100644 index 0000000..ef7a51b --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/messageTray.js @@ -0,0 +1,91 @@ +/** + * V-Shell (Vertical Workspaces) + * messageTray.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; + +const Main = imports.ui.main; + +let Me; +let opt; + +var MessageTrayModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('messageTrayModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' MessageTrayModule - Keeping untouched'); + } + + _activateModule() { + this._setNotificationPosition(opt.NOTIFICATION_POSITION); + + console.debug(' MessageTrayModule - Activated'); + } + + _disableModule() { + this._setNotificationPosition(1); + + console.debug(' MessageTrayModule - Disabled'); + } + + _setNotificationPosition(position) { + switch (position) { + case 0: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.START; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; + break; + case 1: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.CENTER; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; + break; + case 2: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.END; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; + break; + case 3: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.START; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; + break; + case 4: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.CENTER; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; + break; + case 5: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.END; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; + break; + } + } +}; diff --git a/extensions/vertical-workspaces/lib/optionsFactory.js b/extensions/44/vertical-workspaces/lib/optionsFactory.js similarity index 67% copy from extensions/vertical-workspaces/lib/optionsFactory.js copy to extensions/44/vertical-workspaces/lib/optionsFactory.js index da62dd1..df6c970 100644 --- a/extensions/vertical-workspaces/lib/optionsFactory.js +++ b/extensions/44/vertical-workspaces/lib/optionsFactory.js @@ -9,41 +9,35 @@ 'use strict'; -const { Gtk, Gio, GObject } = imports.gi; +const Adw = imports.gi.Adw; +const Gtk = imports.gi.Gtk; +const Gio = imports.gi.Gio; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; - -const shellVersion = Settings.shellVersion; +let Me; // gettext -const _ = Settings._; +let _; // = Settings._; -const ProfileNames = [ - _('GNOME 3'), - _('GNOME 40+ - Bottom Hot Edge'), - _('Hot Corner Centric - Top Left Hot Corner'), - _('Dock Overview - Bottom Hot Edge'), -]; +function init(me) { + Me = me; + _ = Me.gettext; +} -// libadwaita is available starting with GNOME Shell 42. -let Adw = null; -try { - Adw = imports.gi.Adw; -} catch (e) {} +function cleanGlobals() { + Me = null; + _ = null; +} function _newImageFromIconName(name) { return Gtk.Image.new_from_icon_name(name); } var ItemFactory = class ItemFactory { - constructor(gOptions) { - this._gOptions = gOptions; - this._settings = this._gOptions._gsettings; + constructor() { + this._settings = Me.Opt._gsettings; } - getRowWidget(text, caption, widget, variable, options = []) { + getRowWidget(text, caption, widget, variable, options = [], dependsOn) { let item = []; let label; if (widget) { @@ -81,8 +75,8 @@ var ItemFactory = class ItemFactory { let key; - if (variable && this._gOptions.options[variable]) { - const opt = this._gOptions.options[variable]; + if (variable && Me.Opt.options[variable]) { + const opt = Me.Opt.options[variable]; key = opt[1]; } @@ -95,6 +89,11 @@ var ItemFactory = class ItemFactory { this._connectComboBox(widget, key, variable, options); else if (widget._isDropDown) this._connectDropDown(widget, key, variable, options); + + if (dependsOn) { + const dKey = Me.Opt.options[dependsOn][1]; + this._settings.bind(dKey, widget, 'sensitive', Gio.SettingsBindFlags.GET); + } } return item; @@ -111,7 +110,7 @@ var ItemFactory = class ItemFactory { _connectComboBox(widget, key, variable, options) { let model = widget.get_model(); widget._comboMap = {}; - const currentValue = this._gOptions.get(variable); + const currentValue = Me.Opt.get(variable); for (const [label, value] of options) { let iter; model.set(iter = model.append(), [0, 1], [label, value]); @@ -120,8 +119,8 @@ var ItemFactory = class ItemFactory { widget._comboMap[value] = iter; } - this._gOptions.connect(`changed::${key}`, () => { - widget.set_active_iter(widget._comboMap[this._gOptions.get(variable, true)]); + Me.Opt.connect(`changed::${key}`, () => { + widget.set_active_iter(widget._comboMap[Me.Opt.get(variable, true)]); }); widget.connect('changed', () => { const [success, iter] = widget.get_active_iter(); @@ -129,17 +128,17 @@ var ItemFactory = class ItemFactory { if (!success) return; - this._gOptions.set(variable, model.get_value(iter, 1)); + Me.Opt.set(variable, model.get_value(iter, 1)); }); } _connectDropDown(widget, key, variable, options) { const model = widget.get_model(); - const currentValue = this._gOptions.get(variable); + const currentValue = Me.Opt.get(variable); for (let i = 0; i < options.length; i++) { const text = options[i][0]; const id = options[i][1]; - model.append(new DropDownItem({ text, id })); + model.append(new DropDownItemVW({ text, id })); if (id === currentValue) widget.set_selected(i); } @@ -157,11 +156,11 @@ var ItemFactory = class ItemFactory { widget.connect('notify::selected-item', dropDown => { const item = dropDown.get_selected_item(); - this._gOptions.set(variable, item.id); + Me.Opt.set(variable, item.id); }); - this._gOptions.connect(`changed::${key}`, () => { - const newId = this._gOptions.get(variable, true); + Me.Opt.connect(`changed::${key}`, () => { + const newId = Me.Opt.get(variable, true); for (let i = 0; i < options.length; i++) { const id = options[i][1]; if (id === newId) @@ -214,7 +213,7 @@ var ItemFactory = class ItemFactory { newDropDown() { const dropDown = new Gtk.DropDown({ model: new Gio.ListStore({ - item_type: DropDownItem, + item_type: DropDownItemVW, }), halign: Gtk.Align.END, valign: Gtk.Align.CENTER, @@ -255,11 +254,11 @@ var ItemFactory = class ItemFactory { newLinkButton(uri) { const linkBtn = new Gtk.LinkButton({ - label: shellVersion < 42 ? 'Click Me!' : '', uri, halign: Gtk.Align.END, valign: Gtk.Align.CENTER, hexpand: true, + icon_name: 'emblem-symbolic-link', }); return linkBtn; } @@ -298,18 +297,23 @@ var ItemFactory = class ItemFactory { entry.set_text(opt.get(`profileName${profileIndex}`)); entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, 'edit-clear-symbolic'); entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, true); - entry.connect('icon-press', e => e.set_text('')); - entry.connect('changed', e => opt.set(`profileName${profileIndex}`, e.get_text())); const resetProfile = this.newButton(); resetProfile.set({ tooltip_text: _('Reset profile to defaults'), - icon_name: 'edit-delete-symbolic', + icon_name: 'document-revert-symbolic', hexpand: false, css_classes: ['destructive-action'], }); function setName() { + const ProfileNames = [ + _('GNOME 3'), + _('GNOME 40+ - Bottom Hot Edge'), + _('Hot Corner Centric - Top Left Hot Corner'), + _('Dock Overview - Bottom Hot Edge'), + ]; + let name = opt.get(`profileName${profileIndex}`, true); if (!name) name = ProfileNames[profileIndex - 1]; @@ -317,6 +321,10 @@ var ItemFactory = class ItemFactory { } setName(); + + entry.connect('icon-press', e => e.set_text('')); + entry.connect('changed', e => opt.set(`profileName${profileIndex}`, e.get_text())); + resetProfile.connect('clicked', () => { reset(profileIndex); setName(); @@ -366,7 +374,7 @@ var ItemFactory = class ItemFactory { valign: Gtk.Align.CENTER, hexpand: true, css_classes: ['destructive-action'], - icon_name: 'edit-delete-symbolic', + icon_name: 'document-revert-symbolic', }); btn.connect('clicked', () => { @@ -382,7 +390,7 @@ var ItemFactory = class ItemFactory { var AdwPrefs = class { constructor(gOptions) { - this._gOptions = gOptions; + Me.Opt = gOptions; } getFilledWindow(window, pages) { @@ -457,158 +465,9 @@ var AdwPrefs = class { } }; -var LegacyPrefs = class { - constructor(gOptions) { - this._gOptions = gOptions; - } - - getPrefsWidget(pages) { - const prefsWidget = new Gtk.Box({ - orientation: Gtk.Orientation.VERTICAL, - }); - const stack = new Gtk.Stack({ - hexpand: true, - }); - const stackSwitcher = new Gtk.StackSwitcher({ - halign: Gtk.Align.CENTER, - hexpand: true, - }); - - const context = stackSwitcher.get_style_context(); - context.add_class('caption'); - - stackSwitcher.set_stack(stack); - stack.set_transition_duration(300); - stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT); - - const pageProperties = { - hscrollbar_policy: Gtk.PolicyType.NEVER, - vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, - vexpand: true, - hexpand: true, - visible: true, - }; - - const pagesBtns = []; - - for (let page of pages) { - const name = page.name; - const title = page.title; - const iconName = page.iconName; - const optionList = page.optionList; - - stack.add_named(this._getLegacyPage(optionList, pageProperties), name); - pagesBtns.push( - [new Gtk.Label({ label: title }), _newImageFromIconName(iconName, Gtk.IconSize.BUTTON)] - ); - } - - let stBtn = stackSwitcher.get_first_child ? stackSwitcher.get_first_child() : null; - for (let i = 0; i < pagesBtns.length; i++) { - const box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, spacing: 6, visible: true }); - const icon = pagesBtns[i][1]; - icon.margin_start = 30; - icon.margin_end = 30; - box.append(icon); - box.append(pagesBtns[i][0]); - if (stackSwitcher.get_children) { - stBtn = stackSwitcher.get_children()[i]; - stBtn.add(box); - } else { - stBtn.set_child(box); - stBtn.visible = true; - stBtn = stBtn.get_next_sibling(); - } - } - - if (stack.show_all) - stack.show_all(); - if (stackSwitcher.show_all) - stackSwitcher.show_all(); - - prefsWidget.append(stack); - - if (prefsWidget.show_all) - prefsWidget.show_all(); - - prefsWidget._stackSwitcher = stackSwitcher; - - return prefsWidget; - } - - _getLegacyPage(optionList, pageProperties) { - const page = new Gtk.ScrolledWindow(pageProperties); - const mainBox = new Gtk.Box({ - orientation: Gtk.Orientation.VERTICAL, - spacing: 5, - homogeneous: false, - margin_start: 30, - margin_end: 30, - margin_top: 12, - margin_bottom: 12, - }); - - let context = page.get_style_context(); - context.add_class('background'); - - let frame; - let frameBox; - for (let item of optionList) { - // label can be plain text for Section Title - // or GtkBox for Option - const option = item[0]; - const widget = item[1]; - - if (!widget) { - const lbl = new Gtk.Label({ - label: option, - xalign: 0, - margin_bottom: 4, - }); - - context = lbl.get_style_context(); - context.add_class('heading'); - - mainBox.append(lbl); - - frame = new Gtk.Frame({ - margin_bottom: 16, - }); - - frameBox = new Gtk.ListBox({ - selection_mode: null, - }); - - mainBox.append(frame); - frame.set_child(frameBox); - continue; - } - - const grid = new Gtk.Grid({ - column_homogeneous: false, - column_spacing: 20, - margin_start: 8, - margin_end: 8, - margin_top: 8, - margin_bottom: 8, - hexpand: true, - }); - - grid.attach(option, 0, 0, 5, 1); - - if (widget) - grid.attach(widget, 5, 0, 2, 1); - - frameBox.append(grid); - } - page.set_child(mainBox); - - return page; - } -}; - -const DropDownItem = GObject.registerClass({ - GTypeName: 'DropdownItem', +const { GObject } = imports.gi; +const DropDownItemVW = GObject.registerClass({ + GTypeName: 'DropDownItemVW', Properties: { 'text': GObject.ParamSpec.string( 'text', @@ -622,10 +481,11 @@ const DropDownItem = GObject.registerClass({ 'Id', 'Item id stored in settings', GObject.ParamFlags.READWRITE, - 0, 100, 0 + // min, max, default + -2147483648, 2147483647, 0 ), }, -}, class DropDownItem extends GObject.Object { +}, class DropDownItemVW extends GObject.Object { get text() { return this._text; } @@ -641,5 +501,4 @@ const DropDownItem = GObject.registerClass({ set id(id) { this._id = id; } -} -); +}); diff --git a/extensions/44/vertical-workspaces/lib/osdWindow.js b/extensions/44/vertical-workspaces/lib/osdWindow.js new file mode 100644 index 0000000..4699ddf --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/osdWindow.js @@ -0,0 +1,118 @@ +/** + * V-Shell (Vertical Workspaces) + * osdWindow.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; + +const Main = imports.ui.main; +const OsdWindow = imports.ui.osdWindow; + +let Me; +let opt; + +let OsdPositions; + +var OsdWindowModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + + OsdPositions = { + 1: { + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + }, + 2: { + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.START, + }, + 3: { + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.START, + }, + 4: { + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + }, + 5: { + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.END, + }, + 6: { + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + }, + 7: { + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.END, + }, + }; + } + + cleanGlobals() { + Me = null; + opt = null; + OsdPositions = null; + } + + update(reset) { + this.moduleEnabled = opt.get('osdWindowModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OsdWindowModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('osdWindow', OsdWindow.OsdWindow.prototype, OsdWindowCommon); + console.debug(' OsdWindowModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + this._updateExistingOsdWindows(6); + + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); + } + + _updateExistingOsdWindows(position) { + position = position ? position : opt.OSD_POSITION; + Main.osdWindowManager._osdWindows.forEach(osd => { + osd.set(OsdPositions[position]); + }); + } +}; + +const OsdWindowCommon = { + after_show() { + if (!opt.OSD_POSITION) + this.opacity = 0; + this.set(OsdPositions[opt.OSD_POSITION]); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/overlayKey.js b/extensions/44/vertical-workspaces/lib/overlayKey.js new file mode 100644 index 0000000..815abaa --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/overlayKey.js @@ -0,0 +1,168 @@ +/** + * V-Shell (Vertical Workspaces) + * overlayKey.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const GLib = imports.gi.GLib; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const St = imports.gi.St; + +const Main = imports.ui.main; +const Overview = imports.ui.overview; + +let Me; +let opt; + +var OverlayKeyModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._originalOverlayKeyHandlerId = 0; + this._overlayKeyHandlerId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('overlayKeyModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OverlayKeyModule - Keeping untouched'); + } + + _activateModule() { + if (!this._originalOverlayKeyHandlerId) { + this._originalOverlayKeyHandlerId = GObject.signal_handler_find(global.display, { signalId: 'overlay-key' }); + if (this._originalOverlayKeyHandlerId !== null) { + global.display.block_signal_handler(this._originalOverlayKeyHandlerId); + this._connectOverlayKey(); + } + } + console.debug(' OverlayKeyModule - Activated'); + } + + _disableModule() { + this._restoreOverlayKeyHandler(); + + console.debug(' OverlayKeyModule - Disabled'); + } + + _restoreOverlayKeyHandler() { + // Disconnect modified overlay key handler + if (this._overlayKeyHandlerId) { + global.display.disconnect(this._overlayKeyHandlerId); + this._overlayKeyHandlerId = 0; + } + + // Unblock original overlay key handler + if (this._originalOverlayKeyHandlerId) { + global.display.unblock_signal_handler(this._originalOverlayKeyHandlerId); + this._originalOverlayKeyHandlerId = 0; + } + } + + _connectOverlayKey() { + if (this._overlayKeyHandlerId) + return; + + this._overlayKeyHandlerId = global.display.connect('overlay-key', this._onOverlayKeyPressed.bind(Main.overview._overview.controls)); + } + + _onOverlayKeyPressed() { + if (this._a11ySettings.get_boolean('stickykeys-enable')) + return; + + const { initialState, finalState, transitioning } = + this._stateAdjustment.getStateTransitionParams(); + + const time = GLib.get_monotonic_time() / 1000; + const timeDiff = time - this._lastOverlayKeyTime; + this._lastOverlayKeyTime = time; + + const shouldShift = St.Settings.get().enable_animations + ? transitioning && finalState > initialState + : Main.overview.visible && timeDiff < Overview.ANIMATION_TIME; + + const mode = opt.OVERLAY_KEY_SECONDARY; + if (shouldShift) { + Me.Util.activateSearchProvider(''); + if (mode === 1) { + this._shiftState(Meta.MotionDirection.UP); + } else if (mode === 2) { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + } else if (mode === 3) { + // Changing the overview mode automatically changes the overview transition + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + } + } else { + if (Main.overview._shown) { + Main.overview.hide(); + return; + } + switch (opt.OVERLAY_KEY_PRIMARY) { + case 0: // Disabled + return; + case 1: // Follow global overview mode + Main.overview.resetOverviewMode(); + break; + case 2: // Default overview + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + break; + case 3: // Default overview + if (Main.overview._shown) + Main.overview.hide(); + else + Main.overview.show(2); + return; + case 4: // Static WS preview + opt.OVERVIEW_MODE = 1; + opt.OVERVIEW_MODE2 = false; + if (!Main.overview._shown) + opt.WORKSPACE_MODE = 0; + break; + case 5: // Static WS + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + break; + case 6: // Window Search + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + if (!Main.overview._shown) + opt.WORKSPACE_MODE = 0; + break; + } + const customOverviewMode = !Main.overview._shown; + Main.overview.toggle(customOverviewMode); + if (opt.OVERLAY_KEY_PRIMARY === 6) + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + } + } +}; diff --git a/extensions/44/vertical-workspaces/lib/overview.js b/extensions/44/vertical-workspaces/lib/overview.js new file mode 100644 index 0000000..833fc58 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/overview.js @@ -0,0 +1,165 @@ +/** + * V-Shell (Vertical Workspaces) + * overview.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Main = imports.ui.main; +const Overview = imports.ui.overview; +const OverviewControls = imports.ui.overviewControls; + +let Me; +let opt; + +var OverviewModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = true; + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OverviewModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('Overview', Overview.Overview.prototype, OverviewCommon); + console.debug(' OverviewModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' OverviewModule - Disabled'); + } +}; + +const OverviewCommon = { + show(state = OverviewControls.ControlsState.WINDOW_PICKER, customOverviewMode) { + if (!customOverviewMode) + this.resetOverviewMode(); + + if (state === OverviewControls.ControlsState.HIDDEN) + throw new Error('Invalid state, use hide() to hide'); + + if (this.isDummy) + return; + if (this._shown) + return; + this._shown = true; + + if (!this._syncGrab()) + return; + + Main.layoutManager.showOverview(); + this._animateVisible(state); + }, + + toggle(customOverviewMode) { + if (this.isDummy) + return; + + if (this._visible) + this.hide(); + else + this.show(OverviewControls.ControlsState.WINDOW_PICKER, customOverviewMode); + }, + + resetOverviewMode() { + // reset Overview Mode do default + opt.OVERVIEW_MODE = opt.get('overviewMode'); + opt.OVERVIEW_MODE2 = opt.OVERVIEW_MODE === 2; + opt.WORKSPACE_MODE = opt.OVERVIEW_MODE > 0 ? 0 : 1; + }, + + _showDone() { + this._animationInProgress = false; + this._coverPane.hide(); + + if (Me.shellVersion < 44) + this.emit('shown'); + else if (this._shownState !== 'SHOWN') + this._changeShownState('SHOWN'); + + // Handle any calls to hide* while we were showing + if (!this._shown) + this._animateNotVisible(); + + // if user activates overview during startup animation, transition needs to be shifted to the state 2 here + const controls = this._overview._controls; + if (controls._searchController._searchActive && controls._stateAdjustment.value === 1) { + if (opt.SEARCH_VIEW_ANIMATION) + controls._onSearchChanged(); + else if (!opt.OVERVIEW_MODE2) + controls._stateAdjustment.value = 2; + } + + this._syncGrab(); + }, + + // Workaround - should probably be fixed elsewhere in the upstream code + // If a new window is opened from the overview + // and is realized before the overview animation is complete, + // the new window will not get focus + after__hideDone() { + if (!opt.FIX_NEW_WINDOW_FOCUS) + return; + + const workspace = global.workspace_manager.get_active_workspace(); + const recentDesktopWin = global.display.get_tab_list(1, workspace)[0]; + let recentNormalWin = null; + const tabList = global.display.get_tab_list(0, workspace); + + for (let i = 0; i < tabList.length; i++) { + if (tabList[i].minimized === false) { + recentNormalWin = tabList[i]; + break; + } + } + + let recentWin = recentNormalWin; + if (recentNormalWin && recentDesktopWin) { + recentWin = recentNormalWin.get_user_time() > recentDesktopWin.get_user_time() + ? recentNormalWin + : recentDesktopWin; + } + + const focusedWin = global.display.focus_window; + + if (recentWin && focusedWin !== recentWin) + recentWin.activate(global.get_current_time()); + }, +}; + diff --git a/extensions/vertical-workspaces/lib/overviewControls.js b/extensions/44/vertical-workspaces/lib/overviewControls.js similarity index 65% copy from extensions/vertical-workspaces/lib/overviewControls.js copy to extensions/44/vertical-workspaces/lib/overviewControls.js index 4959b83..7528682 100644 --- a/extensions/vertical-workspaces/lib/overviewControls.js +++ b/extensions/44/vertical-workspaces/lib/overviewControls.js @@ -10,90 +10,148 @@ 'use strict'; -const { Clutter, GLib, GObject, St } = imports.gi; +const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const Background = imports.ui.background; +const Layout = imports.ui.layout; const Main = imports.ui.main; -const Util = imports.misc.util; +const Overview = imports.ui.overview; const OverviewControls = imports.ui.overviewControls; +const Workspace = imports.ui.workspace; const WorkspaceThumbnail = imports.ui.workspaceThumbnail; +const WorkspacesView = imports.ui.workspacesView; +const Util = imports.misc.util; -const ControlsState = imports.ui.overviewControls.ControlsState; -const FitMode = imports.ui.workspacesView.FitMode; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; - -let _overrides; +let Me; let opt; +// gettext +let _; -const ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME; +const ControlsState = OverviewControls.ControlsState; +const FitMode = WorkspacesView.FitMode; + +const ANIMATION_TIME = Overview.ANIMATION_TIME; const DASH_MAX_SIZE_RATIO = 0.25; let _originalSearchControllerSigId; let _searchControllerSigId; let _timeouts; -let _startupInitComplete = false; -function update(reset = false) { - if (_overrides) - _overrides.removeAll(); +var OverviewControlsModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; - if (_timeouts) { - Object.values(_timeouts).forEach(id => { - if (id) - GLib.source_remove(id); - }); + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - _replaceOnSearchChanged(reset); - - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - _timeouts = null; - return; + _ = null; } - _timeouts = {}; + update(reset) { + this._removeTimeouts(); + this.moduleEnabled = true; + const conflict = false; - opt = Me.imports.lib.settings.opt; - _overrides = new _Util.Overrides(); + reset = reset || !this.moduleEnabled || conflict; - _overrides.addOverride('ControlsManager', OverviewControls.ControlsManager.prototype, ControlsManager); - - if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) - _overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutVertical); - else - _overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutHorizontal); -} - -function _replaceOnSearchChanged(reset = false) { - const searchController = Main.overview._overview.controls._searchController; - if (reset) { - if (_searchControllerSigId) { - searchController.disconnect(_searchControllerSigId); - _searchControllerSigId = 0; + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); } - if (_originalSearchControllerSigId) { - searchController.unblock_signal_handler(_originalSearchControllerSigId); - _originalSearchControllerSigId = 0; - } - Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_x = 0; - Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_y = 0; - Main.overview.searchEntry.visible = true; - Main.overview.searchEntry.opacity = 255; - } else { - // reconnect signal to use custom function (callbacks cannot be overridden in class prototype, they are already in memory as a copy for the given callback) - _originalSearchControllerSigId = GObject.signal_handler_find(searchController, { signalId: 'notify', detail: 'search-active' }); - if (_originalSearchControllerSigId) - searchController.block_signal_handler(_originalSearchControllerSigId); - - _searchControllerSigId = searchController.connect('notify::search-active', ControlsManager._onSearchChanged.bind(Main.overview._overview.controls)); + if (reset && this._firstActivation) + console.debug(' OverviewControlsModule - Keeping untouched'); } -} -const ControlsManager = { + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + this._replaceOnSearchChanged(); + + this._overrides.addOverride('ControlsManager', OverviewControls.ControlsManager.prototype, ControlsManagerCommon); + + if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) + this._overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutVertical); + else + this._overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutHorizontal); + + // if DtD is enabled, we need to replace _prepareStartupAnimation() to minimize the mess in the overview + // const dashToDockEnabled = Me.Util.getEnabledExtensions('dash-to-dock').length || + // Me.Util.getEnabledExtensions('ubuntu-dock').length; + // if (dashToDockEnabled) + this._overrides.addOverride('LayoutManagerDtD', Layout.LayoutManager.prototype, LayoutManager); + + console.debug(' OverviewControlsModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + const reset = true; + this._replaceOnSearchChanged(reset); + Main.overview._overview._controls._appDisplay.opacity = 255; + + console.debug(' OverviewControlsModule - Disabled'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } + + _replaceOnSearchChanged(reset) { + const searchController = Main.overview._overview.controls._searchController; + if (reset) { + if (_searchControllerSigId) { + searchController.disconnect(_searchControllerSigId); + _searchControllerSigId = 0; + } + if (_originalSearchControllerSigId) { + searchController.unblock_signal_handler(_originalSearchControllerSigId); + _originalSearchControllerSigId = 0; + } + Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_x = 0; + Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_y = 0; + Main.overview.searchEntry.visible = true; + Main.overview.searchEntry.opacity = 255; + } else { + // reconnect signal to use custom function (callbacks cannot be overridden in class prototype, they are already in memory as a copy for the given callback) + if (!_originalSearchControllerSigId) + _originalSearchControllerSigId = GObject.signal_handler_find(searchController, { signalId: 'notify', detail: 'search-active' }); + if (_originalSearchControllerSigId) + searchController.block_signal_handler(_originalSearchControllerSigId); + + if (!_searchControllerSigId) + _searchControllerSigId = searchController.connect('notify::search-active', ControlsManagerCommon._onSearchChanged.bind(Main.overview._overview.controls)); + } + } +}; + +const ControlsManagerCommon = { // this function is used as a callback by a signal handler, needs to be reconnected after modification as the original callback uses a copy of the original function /* _update: function() { ... @@ -105,16 +163,21 @@ const ControlsManager = { }, _updateThumbnailsBox() { + const { currentState } = this._stateAdjustment.getStateTransitionParams(); const { shouldShow } = this._thumbnailsBox; - const thumbnailsBoxVisible = shouldShow; + const thumbnailsBoxVisible = shouldShow && + ((currentState < ControlsState.APP_GRID && opt.SHOW_WS_TMB) || + (currentState > ControlsState.WINDOW_PICKER && opt.SHOW_WS_TMB_APPGRID) || + (currentState > ControlsState.WINDOW_PICKER && this._searchController.searchActive && opt.SHOW_WS_TMB) + ); this._thumbnailsBox.visible = thumbnailsBoxVisible; // this call should be directly in _update(), but it's used as a callback function and it would require to reconnect the signal - this._updateWorkspacesDisplay(); + this._updateOverview(); }, // this function is pure addition to the original code and handles wsDisp transition to APP_GRID view - _updateWorkspacesDisplay() { + _updateOverview() { this._workspacesDisplay.translation_x = 0; this._workspacesDisplay.translation_y = 0; this._workspacesDisplay.scale_x = 1; @@ -167,18 +230,21 @@ const ControlsManager = { ws._background.opacity = opacity; } + if (opt.WORKSPACE_MODE) + Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95; + // if ws preview background is disabled, animate tmb box and dash const tmbBox = this._thumbnailsBox; const dash = this.dash; const searchEntryBin = this._searchEntryBin; // this dash transition collides with startup animation and freezes GS for good, needs to be delayed (first Main.overview 'hiding' event enables it) - const skipDash = _Util.dashNotDefault(); + const skipDash = Me.Util.dashNotDefault(); // OVERVIEW_MODE 2 should animate dash and wsTmbBox only if WORKSPACE_MODE === 0 (windows not spread) const animateOverviewMode2 = opt.OVERVIEW_MODE2 && !(finalState === 1 && opt.WORKSPACE_MODE); if (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2) || animateOverviewMode2)) { if (!tmbBox._translationOriginal || Math.abs(tmbBox._translationOriginal[0]) > 500) { // swipe gesture can call this calculation before tmbBox is finalized, giving nonsense width - const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin); + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = this._getOverviewTranslations(dash, tmbBox, searchEntryBin); tmbBox._translationOriginal = [tmbTranslationX, tmbTranslationY]; dash._translationOriginal = [dashTranslationX, dashTranslationY]; searchEntryBin._translationOriginal = searchTranslationY; @@ -229,7 +295,7 @@ const ControlsManager = { // set searchEntry above appDisplay this.set_child_above_sibling(this._searchEntryBin, null); // move dash above wsTmb for case that dash and wsTmb animate from the same side - if (!_Util.dashNotDefault()) + if (!Me.Util.dashNotDefault()) this.set_child_above_sibling(dash, null); this.set_child_below_sibling(this._thumbnailsBox, null); this.set_child_below_sibling(this._workspacesDisplay, null); @@ -238,7 +304,7 @@ const ControlsManager = { // set dash above workspace in the overview this.set_child_above_sibling(this._thumbnailsBox, null); this.set_child_above_sibling(this._searchEntryBin, null); - if (!_Util.dashNotDefault()) + if (!Me.Util.dashNotDefault()) this.set_child_above_sibling(this.dash, null); this.dash._isAbove = true; @@ -247,6 +313,7 @@ const ControlsManager = { this.set_child_above_sibling(this._workspacesDisplay, null); this.dash._isAbove = false; } + }, // fix for upstream bug - appGrid.visible after transition from APP_GRID to HIDDEN @@ -258,12 +325,6 @@ const ControlsManager = { if (this.dash.showAppsButton.checked) this._searchTransition = false; - // update App Grid after settings changed - // only if the App Grid is currently visible on the screen, the paging updates correctly - if (currentState === ControlsState.APP_GRID && this._appDisplay.visible && opt._appGridNeedsRedisplay) { - Me.imports.lib.appDisplay._updateAppGridProperties(); - opt._appGridNeedsRedisplay = false; - } // if !APP_GRID_ANIMATION, appGrid needs to be hidden in WINDOW_PICKER mode (1) // but needs to be visible for transition from HIDDEN (0) to APP_GRID (2) this._appDisplay.visible = @@ -305,11 +366,19 @@ const ControlsManager = { this._workspacesDisplay.reactive = true; this._workspacesDisplay.setPrimaryWorkspaceVisible(true); } else { + if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) + this._searchController._searchResults._statusText.add_style_class_name('search-statustext-om2'); + else + this._searchController._searchResults._statusText.remove_style_class_name('search-statustext-om2'); this._searchController.show(); entry.visible = true; entry.opacity = 255; + // avoid awkward ws scale animation during search activation + WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 0; } + if (opt.SHOW_BG_IN_OVERVIEW && this._bgManagers) + this._updateBackground(this._bgManagers[0]); this._searchTransition = true; this._searchController._searchResults.translation_x = 0; @@ -317,34 +386,33 @@ const ControlsManager = { this._searchController.opacity = 255; this._searchController.visible = true; - if (opt.SEARCH_VIEW_ANIMATION && !this.dash.showAppsButton.checked && ![4, 8].includes(opt.WS_TMB_POSITION) /* && !opt.OVERVIEW_MODE2*/) { + if (opt.SEARCH_VIEW_ANIMATION && ![4, 8].includes(opt.WS_TMB_POSITION) /* && !opt.OVERVIEW_MODE2*/) { this._updateAppDisplayVisibility(); + this.layoutManager._searchController._searchResults._statusBin.opacity = 1; this._searchController.opacity = searchActive ? 255 : 0; let translationX = 0; let translationY = 0; const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor()); - if (currentState < ControlsState.APP_GRID) { - switch (opt.SEARCH_VIEW_ANIMATION) { - case 1: - // make it longer to cover the delay before results appears - translationX = geometry.width; - translationY = 0; - break; - case 2: - translationX = -geometry.width; - translationY = 0; - break; - case 3: - translationX = 0; - translationY = geometry.height; - break; - case 5: - translationX = 0; - translationY = -geometry.height; - break; - } + switch (opt.SEARCH_VIEW_ANIMATION) { + case 1: + // make it longer to cover the delay before results appears + translationX = geometry.width; + translationY = 0; + break; + case 2: + translationX = -geometry.width; + translationY = 0; + break; + case 3: + translationX = 0; + translationY = geometry.height; + break; + case 5: + translationX = 0; + translationY = -geometry.height; + break; } if (searchActive) { @@ -363,6 +431,8 @@ const ControlsManager = { onComplete: () => { this._searchController.visible = searchActive; this._searchTransition = false; + this.layoutManager._searchController._searchResults._statusBin.opacity = 255; + WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 200; }, }); @@ -372,7 +442,10 @@ const ControlsManager = { opacity: searchActive || currentState < 2 ? 0 : 255, duration: SIDE_CONTROLS_ANIMATION_TIME / 2, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => this._updateAppDisplayVisibility(), + onComplete: () => { + this._updateAppDisplayVisibility(); + WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 200; + }, }); // this._updateAppDisplayVisibility(); @@ -390,7 +463,7 @@ const ControlsManager = { this._searchController.opacity = searchActive ? 0 : 255; this._searchController.ease({ opacity: searchActive ? 255 : 0, - duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME * 2 : 0, + duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME : 0, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => (this._searchController.visible = searchActive), }); @@ -398,14 +471,16 @@ const ControlsManager = { // reuse already tuned overview transition, just replace APP_GRID with the search view if (!(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) && !Main.overview._animationInProgress && finalState !== ControlsState.HIDDEN && !this.dash.showAppsButton.checked) { - Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg'); Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); + const duration = opt.SEARCH_VIEW_ANIMATION ? 150 : 0; this._stateAdjustment.ease(searchActive ? ControlsState.APP_GRID : ControlsState.WINDOW_PICKER, { // shorter animation time when entering search view can avoid stuttering in transition // collecting search results take some time and the problematic part is the realization of the object on the screen // if the ws animation ends before this event, the whole transition is smoother // removing the ws transition (duration: 0) seems like the best solution here - duration: searchActive || (opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE) ? 80 : SIDE_CONTROLS_ANIMATION_TIME, + duration: searchActive ? duration : SIDE_CONTROLS_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive); @@ -413,18 +488,20 @@ const ControlsManager = { }); } else if (opt.OVERVIEW_MODE2 && !(opt.WORKSPACE_MODE || this.dash.showAppsButton.checked)) { // add background to search results and make searchEntry border thicker for better visibility - Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg-om2'); Main.overview.searchEntry.add_style_class_name('search-entry-om2'); } else { - Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg-om2'); Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); } }, async runStartupAnimation(callback) { this._ignoreShowAppsButtonToggle = true; - this._searchController.prepareToEnterOverview(); - this._workspacesDisplay.prepareToEnterOverview(); + + this.prepareToEnterOverview(); this._stateAdjustment.value = ControlsState.HIDDEN; this._stateAdjustment.ease(ControlsState.WINDOW_PICKER, { @@ -436,16 +513,20 @@ const ControlsManager = { this._ignoreShowAppsButtonToggle = false; // Set the opacity here to avoid a 1-frame flicker - this.opacity = 0; + this.opacity = 1; + this._appDisplay.opacity = 1; // We can't run the animation before the first allocation happens await this.layout_manager.ensureAllocation(); - const { STARTUP_ANIMATION_TIME } = imports.ui.layout; + this._setBackground(); + Main.panel.opacity = 255; + + const { STARTUP_ANIMATION_TIME } = Layout; // Opacity this.ease({ - opacity: 255, + opacity: opt.STARTUP_STATE === 1 ? 0 : 255, duration: STARTUP_ANIMATION_TIME, mode: Clutter.AnimationMode.LINEAR, onComplete: () => { @@ -465,53 +546,24 @@ const ControlsManager = { } const searchEntryBin = this._searchEntryBin; - const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = - _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin); + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = + this._getOverviewTranslations(dash, tmbBox, searchEntryBin); const onComplete = function () { // running init callback again causes issues (multiple connections) - if (!_startupInitComplete) + if (!Main.overview._startupInitComplete) callback(); - _startupInitComplete = true; - // force app grid to build before the first visible animation to remove possible stuttering - this._appDisplay.opacity = 1; + const appDisplayModule = Me.Modules.appDisplayModule; + if (!appDisplayModule.moduleEnabled) + this._finishStartupSequence(); + else + this._realizeAppDisplayAndFinishSequence(); - const [x, y] = this._appDisplay.get_position(); - const translationX = -x; - const translationY = -y; - this._appDisplay.translation_x = translationX; - this._appDisplay.translation_y = translationY; - GLib.idle_add(0, () => { - this._appDisplay._removeItem(this._appDisplay._orderedItems[0]); - this._appDisplay._redisplay(); - }); - - // let the main loop realize previous changes before continuing - _timeouts.startupAnim1 = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 10, - () => { - GLib.idle_add(0, () => { - this._appDisplay._removeItem(this._appDisplay._orderedItems[0]); - this._appDisplay._redisplay(); - }); - this._appDisplay.translation_x = 0; - this._appDisplay.translation_y = 0; - this._appDisplay.visible = false; - if (opt.STARTUP_STATE === 1) { - Main.overview.hide(); - } else if (opt.STARTUP_STATE === 2) { - this._appDisplay.opacity = 255; - this.dash.showAppsButton.checked = true; - } - _timeouts.startupAnim1 = 0; - return GLib.SOURCE_REMOVE; - } - ); + Main.overview._startupInitComplete = true; }.bind(this); - if (dash.visible && !_Util.dashNotDefault()) { + if (dash.visible && !Me.Util.dashNotDefault()) { dash.translation_x = dashTranslationX; dash.translation_y = dashTranslationY; dash.opacity = 255; @@ -521,9 +573,7 @@ const ControlsManager = { delay: STARTUP_ANIMATION_TIME / 2, duration: STARTUP_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - onComplete(); - }, + onComplete, }); } else { // set dash opacity to make it visible if user enable it later @@ -535,6 +585,7 @@ const ControlsManager = { STARTUP_ANIMATION_TIME * 2 * St.Settings.get().slow_down_factor, () => { onComplete(); + Main.overview._startupInitComplete = true; _timeouts.startupAnim2 = 0; return GLib.SOURCE_REMOVE; } @@ -571,7 +622,6 @@ const ControlsManager = { if (view._monitorIndex !== global.display.get_primary_monitor() && view._thumbnails.visible) { const secTmbBox = view._thumbnails; - _Util.getOverviewTranslations(opt, dash, secTmbBox, searchEntryBin); if (opt.SEC_WS_TMB_LEFT) secTmbBox.translation_x = -(secTmbBox.width + 12); // compensate for padding else if (opt.SEC_WS_TMB_RIGHT) @@ -594,13 +644,129 @@ const ControlsManager = { } }, + _realizeAppDisplayAndFinishSequence() { + const appDisplayModule = Me.Modules.appDisplayModule; + // realize app grid for smoother first animation + appDisplayModule._updateAppGrid(false, this._finishStartupSequence.bind(this)); + }, + + _finishStartupSequence(priority = GLib.PRIORITY_LOW) { + if (!this._bgManagers) + this._setBackground(); + + _timeouts.finishStartup = GLib.idle_add( + priority, () => { + this._appDisplay.opacity = 255; + if (opt.STARTUP_STATE === 1) { + Main.overview.hide(); + } else if (opt.STARTUP_STATE === 2) { + Main.overview.show(2); // just because of DtD, because we skipped startup animation + this.dash.showAppsButton.checked = true; + } else if (!opt.STARTUP_STATE && Me.Util.dashNotDefault()) { + Main.overview.show(); + } + + _timeouts.finishStartup = 0; + return GLib.SOURCE_REMOVE; + } + ); + }, + + setInitialTranslations() { + const dash = this.dash; + const tmbBox = this._thumbnailsBox; + const searchEntryBin = this._searchEntryBin; + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = + this._getOverviewTranslations(dash, tmbBox, searchEntryBin); + if (!Me.Util.dashNotDefault()) { + dash.translation_x = dashTranslationX; + dash.translation_y = dashTranslationY; + } + tmbBox.translation_x = tmbTranslationX; + tmbBox.translation_y = tmbTranslationY; + searchEntryBin.translation_y = searchTranslationY; + }, + + _getOverviewTranslations(dash, tmbBox, searchEntryBin) { + // const tmbBox = Main.overview._overview._controls._thumbnailsBox; + const animationsDisabled = !St.Settings.get().enable_animations || (opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2); + if (animationsDisabled) + return [0, 0, 0, 0, 0]; + + let searchTranslationY = 0; + if (searchEntryBin.visible) { + const offset = (dash.visible && (!opt.DASH_VERTICAL ? dash.height + 12 : 0)) + + (opt.WS_TMB_TOP ? tmbBox.height + 12 : 0); + searchTranslationY = -searchEntryBin.height - offset - 30; + } + + let tmbTranslationX = 0; + let tmbTranslationY = 0; + let offset; + if (tmbBox.visible) { + const tmbWidth = tmbBox.width === Infinity ? 0 : tmbBox.width; + const tmbHeight = tmbBox.height === Infinity ? 0 : tmbBox.height; + switch (opt.WS_TMB_POSITION) { + case 3: // left + offset = 10 + (dash?.visible && opt.DASH_LEFT ? dash.width : 0); + tmbTranslationX = -tmbWidth - offset; + tmbTranslationY = 0; + break; + case 1: // right + offset = 10 + (dash?.visible && opt.DASH_RIGHT ? dash.width : 0); + tmbTranslationX = tmbWidth + offset; + tmbTranslationY = 0; + break; + case 0: // top + offset = 10 + (dash?.visible && opt.DASH_TOP ? dash.height : 0) + Main.panel.height; + tmbTranslationX = 0; + tmbTranslationY = -tmbHeight - offset; + break; + case 2: // bottom + offset = 10 + (dash?.visible && opt.DASH_BOTTOM ? dash.height : 0) + Main.panel.height; // just for case the panel is at bottom + tmbTranslationX = 0; + tmbTranslationY = tmbHeight + offset; + break; + } + } + + let dashTranslationX = 0; + let dashTranslationY = 0; + let position = opt.DASH_POSITION; + // if DtD replaced the original Dash, read its position + if (Me.Util.dashIsDashToDock()) + position = dash._position; + + if (dash?.visible) { + const dashWidth = dash.width === Infinity ? 0 : dash.width; + const dashHeight = dash.height === Infinity ? 0 : dash.height; + switch (position) { + case 0: // top + dashTranslationX = 0; + dashTranslationY = -dashHeight - dash.margin_bottom - Main.panel.height; + break; + case 1: // right + dashTranslationX = dashWidth; + dashTranslationY = 0; + break; + case 2: // bottom + dashTranslationX = 0; + dashTranslationY = dashHeight + dash.margin_bottom + Main.panel.height; + break; + case 3: // left + dashTranslationX = -dashWidth; + dashTranslationY = 0; + break; + } + } + + return [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY]; + }, + animateToOverview(state, callback) { this._ignoreShowAppsButtonToggle = true; this._searchTransition = false; - this._searchController.prepareToEnterOverview(); - this._workspacesDisplay.prepareToEnterOverview(); - this._stateAdjustment.value = ControlsState.HIDDEN; // building window thumbnails takes some time and with many windows on the workspace @@ -627,10 +793,151 @@ const ControlsManager = { this._ignoreShowAppsButtonToggle = false; }, + + _setBackground(reset = false) { + if (this._bgManagers) { + this._bgManagers.forEach(bg => { + Main.overview._overview._controls._stateAdjustment.disconnect(bg._fadeSignal); + bg.destroy(); + }); + } + + // if (!SHOW_BG_IN_OVERVIEW && !SHOW_WS_PREVIEW_BG) the background is used for static transition from wallpaper to empty bg in the overview + if (reset || (!opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG)) { + delete this._bgManagers; + return; + } + + this._bgManagers = []; + for (const monitor of Main.layoutManager.monitors) { + const bgManager = new Background.BackgroundManager({ + monitorIndex: monitor.index, + container: Main.layoutManager.overviewGroup, + vignette: true, + }); + + bgManager.backgroundActor.content.vignette_sharpness = 0; + bgManager.backgroundActor.content.brightness = 1; + + + bgManager._fadeSignal = Main.overview._overview._controls._stateAdjustment.connect('notify::value', v => { + this._updateBackground(bgManager, v.value, v); + }); + + if (monitor.index === global.display.get_primary_monitor()) { + bgManager._primary = true; + this._bgManagers.unshift(bgManager); // primary monitor first + } else { + bgManager._primary = false; + this._bgManagers.push(bgManager); + } + } + }, + + _updateBackground(bgManager, stateValue = 2, stateAdjustment = null) { + // Blur My Shell extension destroys all background actors in the overview and doesn't care about consequences + if (this._bgManagers[0] && !Main.layoutManager.overviewGroup.get_children().includes(this._bgManagers[0].backgroundActor)) { + Main.notifyError(`[${Me.metadata.name}]`, _('Overview background crashed!\nIf you are using Blur My Shell, disable overview blur in its settings and re-enable V-Shell Overview Background to avoid visual glitches.')); + // remove and disconnect our destroyed backgrounds to avoid more errors + this._setBackground(true); + return; + } + + const finalState = stateAdjustment?.getStateTransitionParams().finalState; + if (!opt.SHOW_BG_IN_OVERVIEW && !opt.SHOW_WS_PREVIEW_BG) { + // if no bg shown in the overview, fade out the wallpaper + if (!(opt.OVERVIEW_MODE2 && opt.WORKSPACE_MODE && finalState === 1)) + bgManager.backgroundActor.opacity = Util.lerp(255, 0, Math.min(stateValue, 1)); + } else { + let VIGNETTE, BRIGHTNESS, bgValue; + if (opt.OVERVIEW_MODE2 && stateValue <= 1 && !opt.WORKSPACE_MODE) { + VIGNETTE = 0; + BRIGHTNESS = 1; + bgValue = stateValue; + } else { + VIGNETTE = 0.2; + BRIGHTNESS = opt.OVERVIEW_BG_BRIGHTNESS; + if (opt.OVERVIEW_MODE2 && stateValue > 1 && !opt.WORKSPACE_MODE) + bgValue = stateValue - 1; + else + bgValue = stateValue; + } + + let blurEffect = bgManager.backgroundActor.get_effect('blur'); + if (!blurEffect) { + blurEffect = new Shell.BlurEffect({ + brightness: 1, + sigma: 0, + mode: Shell.BlurMode.ACTOR, + }); + bgManager.backgroundActor.add_effect_with_name('blur', blurEffect); + } + + const searchActive = Main.overview._overview.controls._searchController.searchActive; + if (searchActive) + BRIGHTNESS = opt.SEARCH_BG_BRIGHTNESS; + + bgManager.backgroundActor.content.vignette_sharpness = VIGNETTE; + bgManager.backgroundActor.content.brightness = BRIGHTNESS; + + let vignetteInit, brightnessInit;// , sigmaInit; + if (opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG) { + vignetteInit = VIGNETTE; + brightnessInit = BRIGHTNESS; + // sigmaInit = opt.OVERVIEW_BG_BLUR_SIGMA; + } else { + vignetteInit = 0; + brightnessInit = 1; + // sigmaInit = 0; + } + + if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { + bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, bgValue); + bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, bgValue); + } else { + bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, Math.min(stateValue, 1)); + bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, Math.min(stateValue, 1)); + } + + if (opt.OVERVIEW_BG_BLUR_SIGMA || opt.APP_GRID_BG_BLUR_SIGMA) { + // reduce number of steps of blur transition to improve performance + const step = opt.SMOOTH_BLUR_TRANSITIONS ? 0.05 : 0.2; + const progress = stateValue - (stateValue % step); + if (opt.SHOW_WS_PREVIEW_BG && stateValue < 1 && !searchActive) { // no need to animate transition, unless appGrid state is involved, static bg is covered by the ws preview bg + if (blurEffect.sigma !== opt.OVERVIEW_BG_BLUR_SIGMA) + blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; + } else if (stateValue < 1 && !searchActive && !(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue < 1 && !searchActive && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && blurEffect.sigma)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue > 1 && !searchActive && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && finalState === 1)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress % 1)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if ((stateValue > 1 && bgManager._primary) || searchActive) { + const sigma = Math.round(Util.lerp(opt.OVERVIEW_BG_BLUR_SIGMA, opt.APP_GRID_BG_BLUR_SIGMA, progress % 1)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue === 1 && !(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)) { + blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; + } else if (stateValue === 0 || (stateValue === 1 && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE))) { + blurEffect.sigma = 0; + } + } + } + }, }; const ControlsManagerLayoutVertical = { - _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, searchHeight, startY) { + _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, thumbnailsHeight, searchHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const workspaceBox = box.copy(); let [width, height] = workspaceBox.get_size(); // const { x1: startX/* y1: startY*/ } = workAreaBox; @@ -640,7 +947,7 @@ const ControlsManagerLayoutVertical = { const dash = Main.overview.dash; // including Dash to Dock and clones properties for compatibility - if (_Util.dashIsDashToDock()) { + if (Me.Util.dashIsDashToDock()) { // Dash to Dock also always affects workAreaBox Main.layoutManager._trackedActors.forEach(actor => { if (actor.affectsStruts && actor.actor.width === dash.width) { @@ -678,7 +985,7 @@ const ControlsManagerLayoutVertical = { case ControlsState.APP_GRID: if (opt.WS_ANIMATION && opt.SHOW_WS_TMB && state === ControlsState.APP_GRID) { workspaceBox.set_origin(...this._workspacesThumbnails.get_position()); - workspaceBox.set_size(...this._workspacesThumbnails.get_size()); + workspaceBox.set_size(thumbnailsWidth, thumbnailsHeight); } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { if (opt.START_Y_OFFSET) { let [x, y] = workAreaBox.get_origin(); @@ -693,7 +1000,7 @@ const ControlsManagerLayoutVertical = { height = opt.PANEL_POSITION_TOP ? height : height - Main.panel.height; searchHeight = opt.SHOW_SEARCH_ENTRY ? searchHeight : 0; wWidth = width - - (opt.DASH_VERTICAL ? dash.width : 0) - + (opt.DASH_VERTICAL ? dashWidth : 0) - thumbnailsWidth - 4 * spacing; wHeight = height - @@ -739,7 +1046,7 @@ const ControlsManagerLayoutVertical = { } const wsBoxX = /* startX + */xOffset; - wsBoxY = Math.round(startY + yOffset); + wsBoxY = startY + yOffset; workspaceBox.set_origin(Math.round(wsBoxX), Math.round(wsBoxY)); workspaceBox.set_size(Math.round(wWidth), Math.round(wHeight)); } @@ -749,6 +1056,10 @@ const ControlsManagerLayoutVertical = { }, _getAppDisplayBoxForState(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsWidth, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const [width] = box.get_size(); const { x1: startX } = workAreaBox; // const { y1: startY } = workAreaBox; @@ -762,11 +1073,11 @@ const ControlsManagerLayoutVertical = { const xOffsetR = (opt.WS_TMB_RIGHT ? thumbnailsWidth : 0) + (opt.DASH_RIGHT ? dashWidth : 0); const yOffsetT = (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0); const yOffsetB = opt.DASH_BOTTOM ? dashHeight : 0; - const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 4 * spacing : width - xOffsetL - xOffsetR - 4 * spacing; - const adHeight = height - yOffsetT - yOffsetB - 4 * spacing; + const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 2 * spacing : width - xOffsetL - xOffsetR - 2 * spacing; + const adHeight = height - yOffsetT - yOffsetB; const appDisplayX = opt.CENTER_APP_GRID ? (width - adWidth) / 2 : xOffsetL + 2 * spacing; - const appDisplayY = startY + yOffsetT + 2 * spacing; + const appDisplayY = startY + yOffsetT; switch (state) { case ControlsState.HIDDEN: @@ -774,43 +1085,43 @@ const ControlsManagerLayoutVertical = { // 1 - left, 2 - right, 3 - bottom, 5 - top switch (opt.APP_GRID_ANIMATION) { case 0: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; case 1: - appDisplayBox.set_origin(startX + width, appDisplayY); + appDisplayBox.set_origin(Math.round(startX + width), Math.round(appDisplayY)); break; case 2: - appDisplayBox.set_origin(startX - adWidth, appDisplayY); + appDisplayBox.set_origin(Math.round(startX - adWidth), Math.round(appDisplayY)); break; case 3: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y2); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y2)); break; case 5: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y1 - adHeight); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y1 - adHeight)); break; } break; case ControlsState.APP_GRID: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; } - appDisplayBox.set_size(adWidth, adHeight); + appDisplayBox.set_size(Math.round(adWidth), Math.round(adHeight)); return appDisplayBox; }, vfunc_allocate(container, box) { + const transitionParams = this._stateAdjustment.getStateTransitionParams(); + const childBox = new Clutter.ActorBox(); - const { spacing } = this; - + const halfSpacing = spacing / 2; const monitor = Main.layoutManager.findMonitorForActor(this._container); const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index); const startX = workArea.x - monitor.x; // if PANEL_OVERVIEW_ONLY, the affectStruts property is set to false to avoid stuttering // therefore we need to add panel height to startY let startY = workArea.y - monitor.y + opt.START_Y_OFFSET; - const workAreaBox = new Clutter.ActorBox(); workAreaBox.set_origin(startX, startY); workAreaBox.set_size(workArea.width, workArea.height); @@ -818,7 +1129,7 @@ const ControlsManagerLayoutVertical = { box.x1 += startX; let [width, height] = box.get_size(); // if panel is at bottom position, - // compensate the height of the available box (the box size is calculated for top panel) + // compensate for the height of the available box (the box size is calculated for top panel) height = opt.PANEL_POSITION_TOP ? height : height - Main.panel.height; let availableHeight = height; @@ -830,8 +1141,8 @@ const ControlsManagerLayoutVertical = { // dash cloud be overridden by the Dash to Dock clone const dash = Main.overview.dash; - if (_Util.dashIsDashToDock()) { - // if Dash to Dock replaced the default dash and its inteli-hide id disabled we need to compensate for affected startY + if (Me.Util.dashIsDashToDock()) { + // if Dash to Dock replaced the default dash and its inteli-hide is disabled we need to compensate for affected startY if (!Main.overview.dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled) { if (Main.panel.y === monitor.y) startY = Main.panel.height + spacing; @@ -857,48 +1168,48 @@ const ControlsManagerLayoutVertical = { } } - const transitionParams = this._stateAdjustment.getStateTransitionParams(); - // Workspace Thumbnails let wsTmbWidth = 0; let wsTmbHeight = 0; - if (this._workspacesThumbnails.visible) { - // const { expandFraction } = this._workspacesThumbnails; + let maxWsTmbScale = opt.MAX_THUMBNAIL_SCALE; + if (opt.SHOW_WS_TMB) { const dashHeightReservation = !opt.WS_TMB_FULL && !opt.DASH_VERTICAL ? dashHeight : 0; - let maxScale = opt.MAX_THUMBNAIL_SCALE; - if (!opt.MAX_THUMBNAIL_SCALE_STABLE) { + const searchActive = this._searchController.searchActive; + if (!opt.MAX_THUMBNAIL_SCALE_STABLE && !searchActive) { const initState = transitionParams.initialState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE; const finalState = transitionParams.finalState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE; - maxScale = Util.lerp(initState, finalState, transitionParams.progress); + maxWsTmbScale = Util.lerp(initState, finalState, transitionParams.progress); } - wsTmbWidth = width * maxScale; + wsTmbWidth = width * maxWsTmbScale; let totalTmbSpacing; - [totalTmbSpacing, wsTmbHeight] = this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth); + [totalTmbSpacing, wsTmbHeight] = this._workspacesThumbnails.get_preferred_height(wsTmbWidth); wsTmbHeight += totalTmbSpacing; - const wsTmbHeightMax = height - dashHeightReservation; + const wsTmbHeightMax = opt.WS_TMB_FULL + ? height - spacing + : height - dashHeightReservation - 2 * spacing; if (wsTmbHeight > wsTmbHeightMax) { wsTmbHeight = wsTmbHeightMax; - wsTmbWidth = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight)[1]; + wsTmbWidth = Math.round(this._workspacesThumbnails.get_preferred_width(wsTmbHeight)[1]); } let wsTmbX; if (opt.WS_TMB_RIGHT) - wsTmbX = Math.round(startX + width - (opt.DASH_RIGHT ? dashWidth : 0) - wsTmbWidth - spacing / 2); + wsTmbX = Math.round(startX + width - (opt.DASH_RIGHT ? dashWidth : 0) - wsTmbWidth /* - halfSpacing*/); // this halfSpacing is a part od dash style else - wsTmbX = Math.round((opt.DASH_LEFT ? dashWidth : 0) + spacing / 2); + wsTmbX = Math.round(opt.DASH_LEFT ? dashWidth : 0/* + halfSpacing*/); // this halfSpacing is a part od dash style let wstOffset = (height - wsTmbHeight - (opt.DASH_VERTICAL ? 0 : dashHeightReservation)) / 2; - wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2); + wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - halfSpacing); let wsTmbY = Math.round(startY + (dashHeightReservation && opt.DASH_TOP ? dashHeight : 0) + wstOffset); childBox.set_origin(wsTmbX, wsTmbY); - childBox.set_size(Math.round(wsTmbWidth), Math.round(wsTmbHeight)); + childBox.set_size(Math.max(wsTmbWidth, 1), Math.max(wsTmbHeight, 1)); this._workspacesThumbnails.allocate(childBox); } @@ -927,7 +1238,7 @@ const ControlsManagerLayoutVertical = { if (!opt.DASH_VERTICAL) { offset = (width - ((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter ? wsTmbWidth : 0) - dashWidth) / 2; - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashX = offset; if ((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter) { @@ -944,7 +1255,7 @@ const ControlsManagerLayoutVertical = { } } else { offset = (height - dashHeight) / 2; - dashY = startY + (offset - opt.DASH_POSITION_ADJUSTMENT * offset); + dashY = startY + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing)); } childBox.set_origin(Math.round(startX + dashX), Math.round(dashY)); @@ -957,7 +1268,7 @@ const ControlsManagerLayoutVertical = { let [searchHeight] = this._searchEntry.get_preferred_height(width - wsTmbWidth); // Workspaces - let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, searchHeight, startY]; + let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, wsTmbHeight, searchHeight, startY]; // Update cached boxes for (const state of Object.values(ControlsState)) { @@ -983,7 +1294,7 @@ const ControlsManagerLayoutVertical = { // Y position under top Dash let searchEntryX, searchEntryY; if (opt.DASH_TOP) - searchEntryY = startY + dashHeight - spacing; + searchEntryY = startY + dashHeight; else searchEntryY = startY; @@ -1005,7 +1316,11 @@ const ControlsManagerLayoutVertical = { availableHeight -= searchHeight + spacing; // if (this._appDisplay.visible)... ? Can cause problems - params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbWidth, startY]; // send startY, can be corrected + // Calculate appDisplay always for AppGrid state WsTmb scale + let wsTmbWidthAppGrid = opt.MAX_THUMBNAIL_SCALE_APPGRID > 0 + ? Math.round(wsTmbWidth / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE_APPGRID) + : Math.round(wsTmbWidth / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE); + params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbWidthAppGrid, startY]; // send startY, can be corrected let appDisplayBox; if (!transitionParams.transitioning) { appDisplayBox = @@ -1024,9 +1339,9 @@ const ControlsManagerLayoutVertical = { if (opt.CENTER_SEARCH_VIEW) { const dashW = (opt.DASH_VERTICAL ? dashWidth : 0) + spacing; searchWidth = width - 2 * wsTmbWidth - 2 * dashW; - childBox.set_origin(wsTmbWidth + dashW, startY + (opt.DASH_TOP ? dashHeight : spacing) + searchHeight); + childBox.set_origin(wsTmbWidth + dashW, startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + searchHeight); } else { - childBox.set_origin(this._xAlignCenter ? wsTmbWidth + spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight : spacing) + searchHeight); + childBox.set_origin(this._xAlignCenter ? wsTmbWidth + spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + searchHeight); } childBox.set_size(searchWidth, availableHeight); @@ -1037,7 +1352,11 @@ const ControlsManagerLayoutVertical = { }; const ControlsManagerLayoutHorizontal = { - _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsHeight, searchHeight, startY) { + _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, thumbnailsHeight, searchHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const workspaceBox = box.copy(); let [width, height] = workspaceBox.get_size(); // let { x1: startX/* , y1: startY*/ } = workAreaBox; @@ -1046,7 +1365,7 @@ const ControlsManagerLayoutHorizontal = { const dash = Main.overview.dash; // including Dash to Dock and clones properties for compatibility - if (_Util.dashIsDashToDock()) { + if (Me.Util.dashIsDashToDock()) { // Dash to Dock always affects workAreaBox Main.layoutManager._trackedActors.forEach(actor => { if (actor.affectsStruts && actor.actor.width === dash.width) { @@ -1084,7 +1403,7 @@ const ControlsManagerLayoutHorizontal = { case ControlsState.APP_GRID: if (opt.WS_ANIMATION && opt.SHOW_WS_TMB && state === ControlsState.APP_GRID) { workspaceBox.set_origin(...this._workspacesThumbnails.get_position()); - workspaceBox.set_size(...this._workspacesThumbnails.get_size()); + workspaceBox.set_size(thumbnailsWidth, thumbnailsHeight); } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { if (opt.START_Y_OFFSET) { let [x, y] = workAreaBox.get_origin(); @@ -1146,7 +1465,7 @@ const ControlsManagerLayoutHorizontal = { } wsBoxX = /* startX + */xOffset; - wsBoxY = Math.round(startY + yOffset); + wsBoxY = startY + yOffset; workspaceBox.set_origin(Math.round(wsBoxX), Math.round(wsBoxY)); workspaceBox.set_size(Math.round(wWidth), Math.round(wHeight)); } @@ -1156,6 +1475,10 @@ const ControlsManagerLayoutHorizontal = { }, _getAppDisplayBoxForState(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const [width] = box.get_size(); const { x1: startX } = workAreaBox; // const { y1: startY } = workAreaBox; @@ -1163,16 +1486,16 @@ const ControlsManagerLayoutHorizontal = { const appDisplayBox = new Clutter.ActorBox(); const { spacing } = this; - const yOffsetT = (opt.WS_TMB_TOP ? thumbnailsHeight : 0) + (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0) + 2 * spacing; - const yOffsetB = (opt.WS_TMB_BOTTOM ? thumbnailsHeight : 0) + (opt.DASH_BOTTOM ? dashHeight : 0); + const yOffsetT = (opt.WS_TMB_TOP ? thumbnailsHeight + spacing : 0) + (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0); + const yOffsetB = (opt.WS_TMB_BOTTOM ? thumbnailsHeight + spacing : 0) + (opt.DASH_BOTTOM ? dashHeight : 0); const xOffsetL = opt.DASH_LEFT ? dashWidth : 0; const xOffsetR = opt.DASH_RIGHT ? dashWidth : 0; - const hSpacing = xOffsetL + xOffsetR ? 2 * spacing : 0; + const hSpacing = xOffsetL + xOffsetR ? spacing : 0; const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 2 * hSpacing : width - xOffsetL - xOffsetR - 2 * hSpacing; - const adHeight = height - yOffsetT - yOffsetB - 4 * spacing; + const adHeight = height - yOffsetT - yOffsetB; const appDisplayX = opt.CENTER_APP_GRID ? (width - adWidth) / 2 : xOffsetL + hSpacing; - const appDisplayY = startY + yOffsetT + hSpacing; + const appDisplayY = startY + yOffsetT; switch (state) { case ControlsState.HIDDEN: @@ -1180,36 +1503,36 @@ const ControlsManagerLayoutHorizontal = { // 1 - left, 2 - right, 3 - bottom, 5 - top switch (opt.APP_GRID_ANIMATION) { case 0: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; case 1: - appDisplayBox.set_origin(startX + width, appDisplayY); + appDisplayBox.set_origin(Math.round(startX + width), Math.round(appDisplayY)); break; case 2: - appDisplayBox.set_origin(startX - adWidth, appDisplayY); + appDisplayBox.set_origin(Math.round(startX - adWidth), Math.round(appDisplayY)); break; case 3: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y2); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y2)); break; case 5: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y1 - adHeight); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y1 - adHeight)); break; } break; case ControlsState.APP_GRID: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; } - appDisplayBox.set_size(adWidth, adHeight); + appDisplayBox.set_size(Math.round(adWidth), Math.round(adHeight)); return appDisplayBox; }, vfunc_allocate(container, box) { + const transitionParams = this._stateAdjustment.getStateTransitionParams(); const childBox = new Clutter.ActorBox(); - const { spacing } = this; - + const halfSpacing = spacing / 2; const monitor = Main.layoutManager.findMonitorForActor(this._container); const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index); const startX = workArea.x - monitor.x; @@ -1235,7 +1558,7 @@ const ControlsManagerLayoutHorizontal = { // dash cloud be overridden by the Dash to Dock clone const dash = Main.overview.dash; - if (_Util.dashIsDashToDock()) { + if (Me.Util.dashIsDashToDock()) { // if Dash to Dock replaced the default dash and its inteli-hide is disabled we need to compensate for affected startY if (!Main.overview.dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled) { // if (Main.panel.y === monitor.y) @@ -1265,60 +1588,53 @@ const ControlsManagerLayoutHorizontal = { let [searchHeight] = this._searchEntry.get_preferred_height(width); - const transitionParams = this._stateAdjustment.getStateTransitionParams(); - // Workspace Thumbnails let wsTmbWidth = 0; let wsTmbHeight = 0; - if (this._workspacesThumbnails.visible) { - // const { expandFraction } = this._workspacesThumbnails; + let maxWsTmbScale = opt.MAX_THUMBNAIL_SCALE; + if (opt.SHOW_WS_TMB) { const dashWidthReservation = !opt.WS_TMB_FULL && opt.DASH_VERTICAL ? dashWidth : 0; - let maxScale = opt.MAX_THUMBNAIL_SCALE; - if (!opt.MAX_THUMBNAIL_SCALE_STABLE) { + const searchActive = this._searchController.searchActive; + if (!opt.MAX_THUMBNAIL_SCALE_STABLE && !searchActive) { const initState = transitionParams.initialState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE; const finalState = transitionParams.finalState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE; - maxScale = Util.lerp(initState, finalState, transitionParams.progress); + maxWsTmbScale = Util.lerp(initState, finalState, transitionParams.progress); } - wsTmbHeight = height * maxScale; + wsTmbHeight = Math.round(height * maxWsTmbScale); let totalTmbSpacing; - [totalTmbSpacing, wsTmbWidth] = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight); + [totalTmbSpacing, wsTmbWidth] = this._workspacesThumbnails.get_preferred_width(wsTmbHeight); wsTmbWidth += totalTmbSpacing; const wsTmbWidthMax = opt.WS_TMB_FULL - ? width - : width - (opt.DASH_VERTICAL ? 0 : dashWidthReservation); + ? width - spacing + : width - dashWidthReservation - 2 * spacing; if (wsTmbWidth > wsTmbWidthMax) { wsTmbWidth = wsTmbWidthMax; - wsTmbHeight = this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth)[1]; + wsTmbHeight = Math.round(this._workspacesThumbnails.get_preferred_height(wsTmbWidth)[1]); } let wsTmbY; if (opt.WS_TMB_TOP) - wsTmbY = Math.round(startY + /* searchHeight + */(opt.DASH_TOP ? dashHeight : spacing / 2)); + wsTmbY = Math.round(startY + (opt.DASH_TOP ? dashHeight : halfSpacing)); else - wsTmbY = Math.round(startY + height - (opt.DASH_BOTTOM ? dashHeight : 0) - wsTmbHeight); + wsTmbY = Math.round(startY + height - (opt.DASH_BOTTOM ? dashHeight : halfSpacing) - wsTmbHeight); - let wstOffset = (width - wsTmbWidth) / 2; - wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2); - let wsTmbX = Math.round(Math.clamp( - startX + wstOffset, - startX + (opt.DASH_LEFT ? dashWidthReservation : 0), - width - wsTmbWidth - startX - (opt.DASH_RIGHT ? dashWidthReservation : 0) - )); + let wstOffset = (width - wsTmbWidth - dashWidthReservation) / 2; + wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * wstOffset; + let wsTmbX = Math.round(startX + (opt.DASH_LEFT ? dashWidthReservation : 0) + wstOffset); childBox.set_origin(wsTmbX, wsTmbY); - childBox.set_size(Math.round(wsTmbWidth), Math.round(wsTmbHeight)); + childBox.set_size(Math.max(wsTmbWidth, 1), Math.max(wsTmbHeight, 1)); this._workspacesThumbnails.allocate(childBox); availableHeight -= wsTmbHeight + spacing; } - if (this._dash.visible) { if (opt.WS_TMB_FULL && opt.DASH_VERTICAL) { const wMaxHeight = height - spacing - wsTmbHeight; @@ -1339,25 +1655,24 @@ const ControlsManagerLayoutHorizontal = { else dashY = startY + height - dashHeight; - if (opt.DASH_VERTICAL) { if (opt.WS_TMB_FULL) { offset = (height - dashHeight - wsTmbHeight) / 2; if (opt.WS_TMB_TOP) { - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset + wsTmbHeight; } else { - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset; } } else { offset = (height - dashHeight) / 2; - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset; } } else { offset = (width - dashWidth) / 2; - dashX = startX + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing)); + dashX = startX + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing)); } childBox.set_origin(Math.round(startX + dashX), Math.round(dashY)); @@ -1368,7 +1683,7 @@ const ControlsManagerLayoutHorizontal = { availableHeight -= opt.DASH_VERTICAL ? 0 : dashHeight; // Workspaces - let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbHeight, searchHeight, startY]; + let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, wsTmbHeight, searchHeight, startY]; // Update cached boxes for (const state of Object.values(ControlsState)) { @@ -1394,7 +1709,7 @@ const ControlsManagerLayoutHorizontal = { // Y position under top Dash let searchEntryX, searchEntryY; if (opt.DASH_TOP) - searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight : 0) + dashHeight - spacing; + searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight : 0) + dashHeight; else searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0); @@ -1416,7 +1731,11 @@ const ControlsManagerLayoutHorizontal = { availableHeight -= searchHeight + spacing; // if (this._appDisplay.visible)... ? Can cause problems - params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbHeight, startY]; + // Calculate appDisplay always for AppGrid state WsTmb scale + let wsTmbHeightAppGrid = opt.MAX_THUMBNAIL_SCALE_APPGRID > 0 + ? Math.round(wsTmbHeight / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE_APPGRID) + : Math.round(wsTmbHeight / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE); + params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbHeightAppGrid, startY]; let appDisplayBox; if (!transitionParams.transitioning) { appDisplayBox = @@ -1435,9 +1754,9 @@ const ControlsManagerLayoutHorizontal = { if (opt.CENTER_SEARCH_VIEW) { const dashW = (opt.DASH_VERTICAL ? dashWidth : 0) + spacing; searchWidth = width - 2 * dashW; - childBox.set_origin(dashW, startY + (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); + childBox.set_origin(dashW, startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); } else { - childBox.set_origin(this._xAlignCenter ? spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); + childBox.set_origin(this._xAlignCenter ? spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); } childBox.set_size(searchWidth, availableHeight); @@ -1462,3 +1781,21 @@ function _getFitModeForState(state) { return FitMode.SINGLE; } } + +const LayoutManager = { + _startupAnimation() { + if (Me.Util.dashIsDashToDock() && !Meta.is_restart()) { + // DtD breaks overview on startup + // Skip animation to hide the mess + this._startupAnimationComplete(); + const controlsManager = Main.overview._overview.controls; + controlsManager._finishStartupSequence.bind(controlsManager)(); + } else if (Meta.is_restart()) { + this._startupAnimationComplete(); + } else if (Main.sessionMode.isGreeter) { + this._startupAnimationGreeter(); + } else { + this._startupAnimationSession(); + } + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/panel.js b/extensions/44/vertical-workspaces/lib/panel.js new file mode 100644 index 0000000..898407a --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/panel.js @@ -0,0 +1,256 @@ +/** + * V-Shell (Vertical Workspaces) + * panel.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; + +const Main = imports.ui.main; +const Overview = imports.ui.overview; +const Panel = imports.ui.panel; + +let Me; +let opt; + +const ANIMATION_TIME = Overview.ANIMATION_TIME; + +var PanelModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + + this._showingOverviewConId = 0; + this._hidingOverviewConId = 0; + this._styleChangedConId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('panelModule'); + const conflict = Me.Util.getEnabledExtensions('dash-to-panel').length || + Me.Util.getEnabledExtensions('hidetopbar').length; + + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Panel" module disabled due to potential conflict with another extension`); + + reset = reset || !this.moduleEnabled || conflict || Main.sessionMode.isLocked; + + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' PanelModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + const panelBox = Main.layoutManager.panelBox; + + this._setPanelPosition(); + this._updateStyleChangedConnection(); + + if (opt.PANEL_MODE === 0) { + this._updateOverviewConnection(true); + this._reparentPanel(false); + panelBox.translation_y = 0; + Main.panel.opacity = 255; + this._setPanelStructs(true); + } else if (opt.PANEL_MODE === 1) { + if (opt.SHOW_WS_PREVIEW_BG) { + this._reparentPanel(true); + if (opt.OVERVIEW_MODE2) { + // in OM2 if the panel has been moved to the overviewGroup move panel above all + Main.layoutManager.overviewGroup.set_child_above_sibling(panelBox, null); + this._updateOverviewConnection(); + } else { + // otherwise move the panel below overviewGroup so it can get below workspacesDisplay + Main.layoutManager.overviewGroup.set_child_below_sibling(panelBox, Main.overview._overview); + this._updateOverviewConnection(true); + } + this._showPanel(true); + } else { + // if ws preview bg is disabled, panel can stay in uiGroup + this._reparentPanel(false); + this._showPanel(false); + this._updateOverviewConnection(); + } + // _connectPanel(); + } else if (opt.PANEL_MODE === 2) { + this._updateOverviewConnection(true); + this._reparentPanel(false); + this._showPanel(false); + // _connectPanel(); + } + this._setPanelStructs(opt.PANEL_MODE === 0); + Main.layoutManager._updateHotCorners(); + + this._overrides.addOverride('ActivitiesButton', Panel.ActivitiesButton.prototype, ActivitiesButton); + + console.debug(' PanelModule - Activated'); + } + + _disableModule() { + const reset = true; + this._setPanelPosition(reset); + this._updateOverviewConnection(reset); + this._reparentPanel(false); + + this._updateStyleChangedConnection(reset); + + const panelBox = Main.layoutManager.panelBox; + panelBox.translation_y = 0; + Main.panel.opacity = 255; + this._setPanelStructs(true); + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' PanelModule - Disabled'); + } + + _setPanelPosition(reset = false) { + const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + const panelBox = Main.layoutManager.panelBox; + const panelHeight = Main.panel.height; // panelBox height can be 0 after shell start + + if (opt.PANEL_POSITION_TOP || reset) + panelBox.set_position(geometry.x, geometry.y); + else + panelBox.set_position(geometry.x, geometry.y + geometry.height - panelHeight); + } + + _updateStyleChangedConnection(reset = false) { + if (reset) { + if (this._styleChangedConId) { + Main.panel.disconnect(this._styleChangedConId); + this._styleChangedConId = 0; + } + } else if (!this._styleChangedConId) { + this._styleChangedConId = Main.panel.connect('style-changed', () => { + if (opt.PANEL_MODE === 1 && !opt.OVERVIEW_MODE2) + Main.panel.add_style_pseudo_class('overview'); + else if (opt.OVERVIEW_MODE2) + Main.panel.remove_style_pseudo_class('overview'); + }); + } + } + + _updateOverviewConnection(reset = false) { + if (reset) { + if (this._hidingOverviewConId) { + Main.overview.disconnect(this._hidingOverviewConId); + this._hidingOverviewConId = 0; + } + if (this._showingOverviewConId) { + Main.overview.disconnect(this._showingOverviewConId); + this._showingOverviewConId = 0; + } + } else { + if (!this._hidingOverviewConId) { + this._hidingOverviewConId = Main.overview.connect('hiding', () => { + if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2) + this._showPanel(false); + }); + } + if (!this._showingOverviewConId) { + this._showingOverviewConId = Main.overview.connect('showing', () => { + if (Main.layoutManager._startingUp) + return; + if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2 || Main.layoutManager.panelBox.translation_y) + this._showPanel(true); + }); + } + } + } + + _reparentPanel(reparent = false) { + const panel = Main.layoutManager.panelBox; + if (reparent && panel.get_parent() === Main.layoutManager.uiGroup) { + Main.layoutManager.uiGroup.remove_child(panel); + Main.layoutManager.overviewGroup.add_child(panel); + } else if (!reparent && panel.get_parent() === Main.layoutManager.overviewGroup) { + Main.layoutManager.overviewGroup.remove_child(panel); + // return the panel at default position, panel shouldn't cover objects that should be above + Main.layoutManager.uiGroup.insert_child_at_index(panel, 4); + } + } + + _setPanelStructs(state) { + Main.layoutManager._trackedActors.forEach(a => { + if (a.actor === Main.layoutManager.panelBox) + a.affectsStruts = state; + }); + + // workaround to force maximized windows to resize after removing affectsStruts + // simulation of minimal swipe gesture to the opposite direction + // todo - needs better solution!!!!!!!!!!! + // const direction = _getAppGridAnimationDirection() === 2 ? 1 : -1; + // Main.overview._swipeTracker._beginTouchSwipe(null, global.get_current_time(), 1, 1); + // Main.overview._swipeTracker._updateGesture(null, global.get_current_time(), direction, 1); + // GLib.timeout_add(0, 50, () => Main.overview._swipeTracker._endGesture(global.get_current_time(), 1, true));*/ + } + + _showPanel(show = true) { + if (show) { + Main.panel.opacity = 255; + Main.layoutManager.panelBox.ease({ + duration: ANIMATION_TIME, + translation_y: 0, + onComplete: () => { + this._setPanelStructs(opt.PANEL_MODE === 0); + }, + }); + } else { + const panelHeight = Main.panel.height; + Main.layoutManager.panelBox.ease({ + duration: ANIMATION_TIME, + translation_y: opt.PANEL_POSITION_TOP ? -panelHeight + 1 : panelHeight - 1, + onComplete: () => { + Main.panel.opacity = 0; + this._setPanelStructs(opt.PANEL_MODE === 0); + }, + }); + } + } +}; + +const ActivitiesButton = { + vfunc_event(event) { + if (event.type() === Clutter.EventType.TOUCH_END || + event.type() === Clutter.EventType.BUTTON_RELEASE) { + if (Main.overview.shouldToggleByCornerOrButton()) { + if (event.get_button() === Clutter.BUTTON_SECONDARY && !Main.overview.dash.showAppsButton.checked) { + Main.overview.show(2); + Main.overview.dash.showAppsButton.checked = true; + } else { + Main.overview.toggle(); + } + } + } else if (event.type() === Clutter.EventType.SCROLL) { + Main.wm.handleWorkspaceScroll(event); + } + + return Clutter.EVENT_PROPAGATE; + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/recentFilesSearchProvider.js b/extensions/44/vertical-workspaces/lib/recentFilesSearchProvider.js new file mode 100644 index 0000000..b567ff2 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/recentFilesSearchProvider.js @@ -0,0 +1,258 @@ +/** +* V-Shell (Vertical Workspaces) + * recentFilesSearchProvider.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + */ + +'use strict'; + +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const RecentManager = imports.gi.Gtk.RecentManager; +const St = imports.gi.St; +const Shell = imports.gi.Shell; + +const Main = imports.ui.main; + +let Me; +let opt; +// gettext +let _; + +// prefix helps to eliminate results from other search providers +// so it needs to be something less common +// needs to be accessible from vw module +const PREFIX = 'fq//'; + +var RecentFilesSearchProviderModule = class { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._recentFilesSearchProvider = null; + this._enableTimeoutId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + this.moduleEnabled = opt.get('recentFilesSearchProviderModule'); + + reset = reset || !this.moduleEnabled; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' RecentFilesSearchProviderModule - Keeping untouched'); + } + + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._recentFilesSearchProvider) { + this._recentFilesSearchProvider = new RecentFilesSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._recentFilesSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } + ); + + console.debug(' RecentFilesSearchProviderModule - Activated'); + } + + _disableModule() { + if (this._recentFilesSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._recentFilesSearchProvider); + this._recentFilesSearchProvider = null; + } + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } + + console.debug(' RecentFilesSearchProviderModule - Disabled'); + } + + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +}; + +class RecentFilesSearchProvider { + constructor() { + this.id = 'recent-files'; + const appSystem = Shell.AppSystem.get_default(); + let appInfo = appSystem.lookup_app('org.gnome.Nautilus.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -w', _('Recent Files'), null); + appInfo.get_description = () => _('Search recent files'); + appInfo.get_name = () => _('Recent Files'); + appInfo.get_id = () => 'org.gnome.Nautilus.desktop'; + appInfo.get_icon = () => Gio.icon_new_for_string('document-open-recent-symbolic'); + appInfo.should_show = () => true; + + this.appInfo = appInfo; + this.canLaunchSearch = true; + this.isRemoteProvider = false; + } + + getInitialResultSet(terms, callback /* , cancellable = null*/) { + // In GS 43 callback arg has been removed + /* if (Me.shellVersion >= 43) + cancellable = callback; */ + + const filesDict = {}; + let files = []; + if (terms[0].startsWith(PREFIX)) + files = RecentManager.get_default().get_items(); + + // Detect whether time stamps are in int, or in GLib.DateTime object + this._timeNeedsConversion = files[0]?.get_modified().to_unix; + + for (let file of files) + filesDict[file.get_uri()] = file; + + this.files = filesDict; + + if (Me.shellVersion >= 43) + return new Promise(resolve => resolve(this._getResultSet(terms))); + else + callback(this._getResultSet(terms)); + + return null; + } + + _getResultSet(terms) { + if (!terms[0].startsWith(PREFIX)) + return []; + // do not modify original terms + let termsCopy = [...terms]; + // search for terms without prefix + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); + + const candidates = this.files; + const _terms = [].concat(termsCopy); + // let match; + + const term = _terms.join(' '); + /* match = s => { + return fuzzyMatch(term, s); + }; */ + + const results = []; + let m; + for (let id in candidates) { + const file = this.files[id]; + const name = `${file.get_age()}d: ${file.get_display_name()} ${file.get_uri_display().replace(`/${file.get_display_name()}`, '')}`; + if (opt.SEARCH_FUZZY) + m = Me.Util.fuzzyMatch(term, name); + else + m = Me.Util.strictMatch(term, name); + + if (m !== -1) + results.push({ weight: m, id }); + } + + if (this._timeNeedsConversion) + results.sort((a, b) => this.files[a.id].get_modified().to_unix() < this.files[b.id].get_modified().to_unix()); + else + results.sort((a, b) => this.files[a.id].get_modified() < this.files[b.id].get_modified()); + + this.resultIds = results.map(item => item.id); + return this.resultIds; + } + + getResultMetas(resultIds, callback = null) { + const metas = resultIds.map(id => this.getResultMeta(id)); + if (Me.shellVersion >= 43) + return new Promise(resolve => resolve(metas)); + else if (callback) + callback(metas); + return null; + } + + getResultMeta(resultId) { + const result = this.files[resultId]; + return { + 'id': resultId, + 'name': `${result.get_age()}: ${result.get_display_name()}`, + 'description': `${result.get_uri_display().replace(`/${result.get_display_name()}`, '')}`, + 'createIcon': size => { + let icon = this.getIcon(result, size); + return icon; + }, + }; + } + + getIcon(result, size) { + let icon, gicon; + + const appInfo = Gio.AppInfo.get_default_for_type(result.get_mime_type(), false); + if (appInfo) + gicon = appInfo.get_icon(); + + if (gicon) + icon = new St.Icon({ gicon, icon_size: size }); + else + icon = new St.Icon({ icon_name: 'icon-missing', icon_size: size }); + + return icon; + } + + launchSearch(terms, timeStamp) { + const appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -w recent:///', 'Nautilus', null); + appInfo.launch([], global.create_app_launch_context(timeStamp, -1)); + } + + activateResult(resultId, terms, timeStamp) { + const uri = resultId; + const context = global.create_app_launch_context(timeStamp, -1); + if (Me.Util.isShiftPressed()) { + Main.overview.toggle(); + this.appInfo.launch_uris([uri], context); + } else if (Gio.app_info_launch_default_for_uri(uri, context)) { + // update recent list after (hopefully) successful activation + const recentManager = RecentManager.get_default(); + recentManager.add_item(resultId); + } else { + this.appInfo.launch_uris([uri], context); + } + } + + filterResults(results /* , maxResults*/) { + // return results.slice(0, maxResults); + return results.slice(0, 20); + } + + getSubsearchResultSet(previousResults, terms, callback) { + if (Me.shellVersion < 43) { + this.getSubsearchResultSet42(terms, callback); + return null; + } + return this.getInitialResultSet(terms); + } + + getSubsearchResultSet42(terms, callback) { + callback(this._getResultSet(terms)); + } +} diff --git a/extensions/44/vertical-workspaces/lib/search.js b/extensions/44/vertical-workspaces/lib/search.js new file mode 100644 index 0000000..618c5ed --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/search.js @@ -0,0 +1,323 @@ +/** + * V-Shell (Vertical Workspaces) + * search.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const AppDisplay = imports.ui.appDisplay; +const IconGrid = imports.ui.iconGrid; +const Main = imports.ui.main; +const Search = imports.ui.search; + +let Me; +// gettext +let _; +let opt; + + +let SEARCH_MAX_WIDTH; + +var SearchModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + this.moduleEnabled = opt.get('searchModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' SearchModule - Keeping untouched'); + } + + _activateModule() { + this._updateSearchViewWidth(); + + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('AppSearchProvider', AppDisplay.AppSearchProvider.prototype, AppSearchProvider); + this._overrides.addOverride('SearchResult', Search.SearchResult.prototype, SearchResult); + this._overrides.addOverride('SearchResultsView', Search.SearchResultsView.prototype, SearchResultsView); + this._overrides.addOverride('ProviderInfo', Search.ProviderInfo.prototype, ProviderInfo); + + // Don't expand the search view vertically and align it to the top + // this is important in the static workspace mode when the search view bg is not transparent + // also the "Searching..." and "No Results" notifications will be closer to the search entry, with the distance given by margin-top in the stylesheet + Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.START; + console.debug(' SearchModule - Activated'); + } + + _disableModule() { + const reset = true; + this._updateSearchViewWidth(reset); + + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.FILL; + + + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); + } + + _updateSearchViewWidth(reset = false) { + const searchContent = Main.overview._overview._controls.layoutManager._searchController._searchResults._content; + if (!SEARCH_MAX_WIDTH) { // just store original value; + const themeNode = searchContent.get_theme_node(); + const width = themeNode.get_max_width(); + SEARCH_MAX_WIDTH = width; + } + + if (reset) { + searchContent.set_style(''); + } else { + let width = Math.round(SEARCH_MAX_WIDTH * opt.SEARCH_VIEW_SCALE); + searchContent.set_style(`max-width: ${width}px;`); + } + } +}; + +// AppDisplay.AppSearchProvider +const AppSearchProvider = { + getInitialResultSet(terms, callback, cancellable) { + // Defer until the parental controls manager is initialized, so the + // results can be filtered correctly. + if (!this._parentalControlsManager.initialized) { + if (Me.shellVersion < 43) { + let initializedId = this._parentalControlsManager.connect('app-filter-changed', () => { + if (this._parentalControlsManager.initialized) { + this._parentalControlsManager.disconnect(initializedId); + this.getInitialResultSet(terms, callback, cancellable); + } + }); + return null; + } else { + // callback has been removed in 43 + cancellable = callback; + return new Promise(resolve => { + let initializedId = this._parentalControlsManager.connect('app-filter-changed', async () => { + if (this._parentalControlsManager.initialized) { + this._parentalControlsManager.disconnect(initializedId); + resolve(await this.getInitialResultSet(terms, cancellable)); + } + }); + }); + } + } + + const pattern = terms.join(' '); + + let appInfoList = Shell.AppSystem.get_default().get_installed(); + + let weightList = {}; + appInfoList = appInfoList.filter(appInfo => { + try { + appInfo.get_id(); // catch invalid file encodings + } catch (e) { + return false; + } + + let string = ''; + let name; + let shouldShow = false; + if (appInfo.get_display_name) { + // show only launchers that should be visible in this DE + shouldShow = appInfo.should_show() && this._parentalControlsManager.shouldShowApp(appInfo); + + if (shouldShow) { + let id = appInfo.get_id().split('.'); + id = id[id.length - 2] || ''; + let baseName = appInfo.get_string('Name') || ''; + let dispName = appInfo.get_display_name() || ''; + let gName = appInfo.get_generic_name() || ''; + let description = appInfo.get_description() || ''; + let categories = appInfo.get_string('Categories') || ''; + let keywords = appInfo.get_string('Keywords') || ''; + name = `${dispName} ${id}`; + string = `${dispName} ${gName} ${baseName} ${description} ${categories} ${keywords} ${id}`; + } + } + + let m = -1; + if (shouldShow && opt.SEARCH_FUZZY) { + m = Me.Util.fuzzyMatch(pattern, name); + m = (m + Me.Util.strictMatch(pattern, string)) / 2; + } else if (shouldShow) { + m = Me.Util.strictMatch(pattern, string); + } + + if (m !== -1) + weightList[appInfo.get_id()] = m; + + return shouldShow && (m !== -1); + }); + + appInfoList.sort((a, b) => weightList[a.get_id()] > weightList[b.get_id()]); + + const usage = Shell.AppUsage.get_default(); + // sort apps by usage list + appInfoList.sort((a, b) => usage.compare(a.get_id(), b.get_id())); + // prefer apps where any word in their name starts with the pattern + appInfoList.sort((a, b) => Me.Util.isMoreRelevant(a.get_display_name(), b.get_display_name(), pattern)); + + let results = appInfoList.map(app => app.get_id()); + + results = results.concat(this._systemActions.getMatchingActions(terms)); + + if (Me.shellVersion < 43) { + callback(results); + return null; + } else { + return new Promise(resolve => resolve(results)); + } + }, + + // App search result size + createResultObject(resultMeta) { + if (resultMeta.id.endsWith('.desktop')) { + const icon = new AppDisplay.AppIcon(this._appSys.lookup_app(resultMeta['id']), { + expandTitleOnHover: false, + }); + icon.icon.setIconSize(opt.SEARCH_ICON_SIZE); + return icon; + } else { + const icon = new AppDisplay.SystemActionIcon(this, resultMeta); + icon.icon._setSizeManually = true; + icon.icon.setIconSize(opt.SEARCH_ICON_SIZE); + return icon; + } + }, +}; + +const SearchResult = { + activate() { + this.provider.activateResult(this.metaInfo.id, this._resultsView.terms); + + if (this.metaInfo.clipboardText) { + St.Clipboard.get_default().set_text( + St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText); + } + // don't close overview if Shift key is pressed - Shift moves windows to the workspace + if (!Me.Util.isShiftPressed()) + Main.overview.toggle(); + }, +}; + +const SearchResultsView = { + _doSearch() { + if (!this._doProviderSearch) { + this._doSearchLegacy(); + return; + } + this._startingSearch = false; + + let previousResults = this._results; + this._results = {}; + + this._providers.forEach(provider => { + const onlyVShellProviders = this._terms.includes('wq//') || this._terms.includes('fq//'); + if (!onlyVShellProviders || (onlyVShellProviders && (provider.id.includes('open-windows') || provider.id.includes('recent-files')))) { + let previousProviderResults = previousResults[provider.id]; + this._doProviderSearch(provider, previousProviderResults); + } + }); + + this._updateSearchProgress(); + + this._clearSearchTimeout(); + }, + + _doSearchLegacy() { + this._startingSearch = false; + + let previousResults = this._results; + this._results = {}; + + this._providers.forEach(provider => { + const onlyVShellProviders = this._terms.includes('wq//') || this._terms.includes('fq//'); + if (!onlyVShellProviders || (onlyVShellProviders && (provider.id.includes('open-windows') || provider.id.includes('recent-files')))) { + provider.searchInProgress = true; + + let previousProviderResults = previousResults[provider.id]; + if (this._isSubSearch && previousProviderResults) { + provider.getSubsearchResultSet(previousProviderResults, + this._terms, + results => { + this._gotResults(results, provider); + }, + this._cancellable); + } else { + provider.getInitialResultSet(this._terms, + results => { + this._gotResults(results, provider); + }, + this._cancellable); + } + } + }); + + this._updateSearchProgress(); + + this._clearSearchTimeout(); + }, + + _updateSearchProgress() { + let haveResults = this._providers.some(provider => { + let display = provider.display; + return display.getFirstResult() !== null; + }); + + this._scrollView.visible = haveResults; + this._statusBin.visible = !haveResults; + + if (!haveResults) { + if (this.searchInProgress) + this._statusText.set_text(_('Searching…')); + else + this._statusText.set_text(_('No results.')); + } + }, +}; + +// fixes app is null error if search provider id is not a desktop app id. +const ProviderInfo = { + animateLaunch() { + let appSys = Shell.AppSystem.get_default(); + let app = appSys.lookup_app(this.provider.appInfo.get_id()); + if (app && app.state === Shell.AppState.STOPPED) + IconGrid.zoomOutActor(this._content); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/searchController.js b/extensions/44/vertical-workspaces/lib/searchController.js new file mode 100644 index 0000000..76b65e8 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/searchController.js @@ -0,0 +1,94 @@ +/** + * V-Shell (Vertical Workspaces) + * searchController.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; + +const Main = imports.ui.main; + +let Me; +let opt; + +var SearchControllerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._originalOnStageKeyPress = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('searchControllerModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' SearchControllerModule - Keeping untouched'); + } + + _activateModule() { + if (!this._originalOnStageKeyPress) + this._originalOnStageKeyPress = Main.overview._overview.controls._searchController._onStageKeyPress; + + Main.overview._overview.controls._searchController._onStageKeyPress = SearchControllerCommon._onStageKeyPress; + console.debug(' SearchControllerModule - Activated'); + } + + _disableModule() { + if (this._originalOnStageKeyPress) + Main.overview._overview.controls._searchController._onStageKeyPress = this._originalOnStageKeyPress; + this._originalOnStageKeyPress = null; + + console.debug(' SearchControlerModule - Disabled'); + } +}; + +// if opt.ESC_BEHAVIOR > 0 force close the overview +const SearchControllerCommon = { + _onStageKeyPress(actor, event) { + // Ignore events while anything but the overview has + // pushed a modal (system modals, looking glass, ...) + if (Main.modalCount > 1) + return Clutter.EVENT_PROPAGATE; + + let symbol = event.get_key_symbol(); + if (symbol === Clutter.KEY_Escape) { + if (this._searchActive && !opt.ESC_BEHAVIOR) { + this.reset(); + } else if (this._showAppsButton.checked && !opt.ESC_BEHAVIOR) { + this._showAppsButton.checked = false; + } else { + this.reset(); + Main.overview.hide(); + } + + return Clutter.EVENT_STOP; + } else if (this._shouldTriggerSearch(symbol)) { + this.startSearch(event); + } + return Clutter.EVENT_PROPAGATE; + }, +}; diff --git a/extensions/vertical-workspaces/lib/settings.js b/extensions/44/vertical-workspaces/lib/settings.js similarity index 66% copy from extensions/vertical-workspaces/lib/settings.js copy to extensions/44/vertical-workspaces/lib/settings.js index 66f3a45..cbb74f2 100644 --- a/extensions/vertical-workspaces/lib/settings.js +++ b/extensions/44/vertical-workspaces/lib/settings.js @@ -9,25 +9,15 @@ 'use strict'; -const { GLib } = imports.gi; +const GLib = imports.gi.GLib; -const Config = imports.misc.config; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -var shellVersion = parseFloat(Config.PACKAGE_VERSION); - -const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']); -var _ = Gettext.gettext; -const _schema = Me.metadata['settings-schema']; - -// common instance of Options accessible from all modules -var opt; +let Me; var Options = class Options { - constructor() { - this._gsettings = ExtensionUtils.getSettings(_schema); + constructor(me) { + Me = me; + + this._gsettings = Me.gSettings; this._connectionIds = []; this._writeTimeoutId = 0; this._gsettings.delay(); @@ -51,8 +41,8 @@ var Options = class Options { wsMaxSpacing: ['int', 'ws-max-spacing'], wsPreviewScale: ['int', 'ws-preview-scale'], secWsPreviewScale: ['int', 'secondary-ws-preview-scale'], - secWsPreviewShift: ['bool', 'secondary-ws-preview-shift'], - wsThumbnailsFull: ['bool', 'ws-thumbnails-full'], + secWsPreviewShift: ['boolean', 'secondary-ws-preview-shift'], + wsThumbnailsFull: ['boolean', 'ws-thumbnails-full'], secWsThumbnailsPosition: ['int', 'secondary-ws-thumbnails-position'], dashPosition: ['int', 'dash-position'], dashPositionAdjust: ['int', 'dash-position-adjust'], @@ -64,6 +54,7 @@ var Options = class Options { dashMaxIconSize: ['int', 'dash-max-icon-size'], dashShowWindowsIcon: ['int', 'dash-show-windows-icon'], dashShowRecentFilesIcon: ['int', 'dash-show-recent-files-icon'], + dashShowExtensionsIcon: ['int', 'dash-show-extensions-icon'], centerDashToWs: ['boolean', 'center-dash-to-ws'], showAppsIconPosition: ['int', 'show-app-icon-position'], wsThumbnailScale: ['int', 'ws-thumbnail-scale'], @@ -73,13 +64,17 @@ var Options = class Options { centerSearch: ['boolean', 'center-search'], centerAppGrid: ['boolean', 'center-app-grid'], dashBgOpacity: ['int', 'dash-bg-opacity'], + dashBgColor: ['int', 'dash-bg-color'], dashBgRadius: ['int', 'dash-bg-radius'], + dashBgGS3Style: ['boolean', 'dash-bg-gs3-style'], + runningDotStyle: ['int', 'running-dot-style'], enablePageShortcuts: ['boolean', 'enable-page-shortcuts'], showWsSwitcherBg: ['boolean', 'show-ws-switcher-bg'], showWsPreviewBg: ['boolean', 'show-ws-preview-bg'], wsPreviewBgRadius: ['int', 'ws-preview-bg-radius'], showBgInOverview: ['boolean', 'show-bg-in-overview'], overviewBgBrightness: ['int', 'overview-bg-brightness'], + searchBgBrightness: ['int', 'search-bg-brightness'], overviewBgBlurSigma: ['int', 'overview-bg-blur-sigma'], appGridBgBlurSigma: ['int', 'app-grid-bg-blur-sigma'], smoothBlurTransitions: ['boolean', 'smooth-blur-transitions'], @@ -87,9 +82,8 @@ var Options = class Options { searchViewAnimation: ['int', 'search-view-animation'], workspaceAnimation: ['int', 'workspace-animation'], animationSpeedFactor: ['int', 'animation-speed-factor'], - fixUbuntuDock: ['boolean', 'fix-ubuntu-dock'], winPreviewIconSize: ['int', 'win-preview-icon-size'], - alwaysShowWinTitles: ['boolean', 'always-show-win-titles'], + winTitlePosition: ['int', 'win-title-position'], startupState: ['int', 'startup-state'], overviewMode: ['int', 'overview-mode'], workspaceSwitcherAnimation: ['int', 'workspace-switcher-animation'], @@ -105,17 +99,18 @@ var Options = class Options { appGridContent: ['int', 'app-grid-content'], appGridIncompletePages: ['boolean', 'app-grid-incomplete-pages'], appGridOrder: ['int', 'app-grid-order'], + appFolderOrder: ['int', 'app-folder-order'], appGridNamesMode: ['int', 'app-grid-names'], appGridActivePreview: ['boolean', 'app-grid-active-preview'], appGridFolderCenter: ['boolean', 'app-grid-folder-center'], appGridPageWidthScale: ['int', 'app-grid-page-width-scale'], appGridSpacing: ['int', 'app-grid-spacing'], - searchWindowsEnable: ['boolean', 'search-windows-enable'], - searchRecentFilesEnable: ['boolean', 'search-recent-files-enable'], + searchWindowsOrder: ['int', 'search-windows-order'], searchFuzzy: ['boolean', 'search-fuzzy'], searchMaxResultsRows: ['int', 'search-max-results-rows'], dashShowWindowsBeforeActivation: ['int', 'dash-show-windows-before-activation'], dashIconScroll: ['int', 'dash-icon-scroll'], + dashIsolateWorkspaces: ['boolean', 'dash-isolate-workspaces'], searchWindowsIconScroll: ['int', 'search-windows-icon-scroll'], panelVisibility: ['int', 'panel-visibility'], panelPosition: ['int', 'panel-position'], @@ -123,6 +118,8 @@ var Options = class Options { wsSwPopupHPosition: ['int', 'ws-sw-popup-h-position'], wsSwPopupVPosition: ['int', 'ws-sw-popup-v-position'], wsSwPopupMode: ['int', 'ws-sw-popup-mode'], + wsSwitcherWraparound: ['boolean', 'ws-switcher-wraparound'], + wsSwitcherIgnoreLast: ['boolean', 'ws-switcher-ignore-last'], favoritesNotify: ['int', 'favorites-notify'], notificationPosition: ['int', 'notification-position'], osdPosition: ['int', 'osd-position'], @@ -131,17 +128,26 @@ var Options = class Options { hotCornerFullscreen: ['boolean', 'hot-corner-fullscreen'], hotCornerRipples: ['boolean', 'hot-corner-ripples'], alwaysActivateSelectedWindow: ['boolean', 'always-activate-selected-window'], - windowIconClickSearch: ['boolean', 'window-icon-click-search'], + winPreviewSecBtnAction: ['int', 'win-preview-sec-mouse-btn-action'], + winPreviewMidBtnAction: ['int', 'win-preview-mid-mouse-btn-action'], + winPreviewShowCloseButton: ['boolean', 'win-preview-show-close-button'], + windowIconClickAction: ['int', 'window-icon-click-action'], + overlayKeyPrimary: ['int', 'overlay-key-primary'], overlayKeySecondary: ['int', 'overlay-key-secondary'], + overviewEscBehavior: ['int', 'overview-esc-behavior'], + newWindowFocusFix: ['boolean', 'new-window-focus-fix'], + appGridPerformance: ['boolean', 'app-grid-performance'], + windowThumbnailScale: ['int', 'window-thumbnail-scale'], - workspaceThumbnailsModule: ['boolean', 'workspace-thumbnails-module'], workspaceSwitcherPopupModule: ['boolean', 'workspace-switcher-popup-module'], workspaceAnimationModule: ['boolean', 'workspace-animation-module'], workspaceModule: ['boolean', 'workspace-module'], windowManagerModule: ['boolean', 'window-manager-module'], windowPreviewModule: ['boolean', 'window-preview-module'], - winAttentionHandlerModule: ['boolean', 'win-attention-handler-module'], + windowAttentionHandlerModule: ['boolean', 'win-attention-handler-module'], + windowThumbnailModule: ['boolean', 'window-thumbnail-module'], swipeTrackerModule: ['boolean', 'swipe-tracker-module'], + searchControllerModule: ['boolean', 'search-controller-module'], searchModule: ['boolean', 'search-module'], panelModule: ['boolean', 'panel-module'], overlayKeyModule: ['boolean', 'overlay-key-module'], @@ -151,6 +157,9 @@ var Options = class Options { dashModule: ['boolean', 'dash-module'], appFavoritesModule: ['boolean', 'app-favorites-module'], appDisplayModule: ['boolean', 'app-display-module'], + windowSearchProviderModule: ['boolean', 'window-search-provider-module'], + recentFilesSearchProviderModule: ['boolean', 'recent-files-search-provider-module'], + extensionsSearchProviderModule: ['boolean', 'extensions-search-provider-module'], profileName1: ['string', 'profile-name-1'], profileName2: ['string', 'profile-name-2'], @@ -158,9 +167,10 @@ var Options = class Options { profileName4: ['string', 'profile-name-4'], }; this.cachedOptions = {}; + } - this.shellVersion = shellVersion; - // this.storeProfile(0); + cleanGlobals() { + Me = null; } connect(name, callback) { @@ -183,7 +193,7 @@ var Options = class Options { get(option, updateCache = false) { if (!this.options[option]) { - log(`[${Me.metadata.name}] Error: Option ${option} is undefined.`); + console.error(`[${Me.metadata.name}] Error: Option ${option} is undefined.`); return null; } @@ -195,7 +205,6 @@ var Options = class Options { else gSettings = this._gsettings; - this.cachedOptions[option] = gSettings.get_value(key).deep_unpack(); } @@ -242,7 +251,8 @@ var Options = class Options { storeProfile(index) { const profile = {}; Object.keys(this.options).forEach(v => { - profile[v] = this.get(v).toString(); + if (!v.startsWith('profileName')) + profile[v] = this.get(v).toString(); }); this._gsettings.set_value(`profile-data-${index}`, new GLib.Variant('a{ss}', profile)); @@ -250,8 +260,14 @@ var Options = class Options { loadProfile(index) { const options = this._gsettings.get_value(`profile-data-${index}`).deep_unpack(); + // set the aaa-loading-data so extension.js doesn't reset V-Shell after each profile item + // delayed gsettings writes are processed alphabetically, so this key will be processed first this._gsettings.set_boolean('aaa-loading-profile', !this._gsettings.get_boolean('aaa-loading-profile')); for (let o of Object.keys(options)) { + if (!this.options[o]) { + console.error(`[${Me.metadata.name}] Error: "${o}" is not a valid profile key -> Update your profile`); + continue; + } const [type] = this.options[o]; let value = options[o]; switch (type) { @@ -275,7 +291,14 @@ var Options = class Options { } _updateSettings() { - this.DASH_POSITION = this.get('dashPosition', true); + this._updateCachedSettings(); + this.DASH_BG_ALPHA = this.get('dashBgOpacity') / 100; + this.DASH_BG_OPACITY = this.get('dashBgOpacity') * 2.5; + this.DASH_BG_COLOR = this.get('dashBgColor'); + this.DASH_BG_RADIUS = this.get('dashBgRadius'); + this.DASH_BG_LIGHT = this.DASH_BG_COLOR === 1; + this.DASH_BG_GS3_STYLE = this.get('dashBgGS3Style'); + this.DASH_POSITION = this.get('dashModule') ? this.get('dashPosition') : 2; this.DASH_TOP = this.DASH_POSITION === 0; this.DASH_RIGHT = this.DASH_POSITION === 1; this.DASH_BOTTOM = this.DASH_POSITION === 2; @@ -284,26 +307,35 @@ var Options = class Options { this.DASH_VISIBLE = this.DASH_POSITION !== 4; // 4 - disable this.DASH_FOLLOW_RECENT_WIN = false; - this.DASH_CLICK_ACTION = this.get('dashShowWindowsBeforeActivation', true); - this.DASH_ICON_SCROLL = this.get('dashIconScroll', true); + this.DASH_ISOLATE_WS = this.get('dashIsolateWorkspaces'); + + this.DASH_CLICK_ACTION = this.get('dashShowWindowsBeforeActivation'); + this.DASH_CLICK_SWITCH_BEFORE_ACTIVATION = this.DASH_CLICK_ACTION === 1; + this.DASH_CLICK_OPEN_NEW_WIN = this.DASH_CLICK_ACTION === 2; + this.DASH_CLICK_PREFER_WORKSPACE = this.DASH_CLICK_ACTION === 3; + + this.DASH_ICON_SCROLL = this.get('dashIconScroll'); this.DASH_SHIFT_CLICK_MV = true; - this.SEARCH_WINDOWS_ICON_SCROLL = this.get('searchWindowsIconScroll', true); + this.RUNNING_DOT_STYLE = this.get('runningDotStyle'); - this.DASH_POSITION_ADJUSTMENT = this.get('dashPositionAdjust', true); + this.SEARCH_WINDOWS_ICON_SCROLL = this.get('searchWindowsIconScroll'); + + this.DASH_POSITION_ADJUSTMENT = this.get('dashPositionAdjust'); this.DASH_POSITION_ADJUSTMENT = this.DASH_POSITION_ADJUSTMENT * -1 / 100; // range 1 to -1 - this.CENTER_DASH_WS = this.get('centerDashToWs', true); + this.CENTER_DASH_WS = this.get('centerDashToWs'); - this.MAX_ICON_SIZE = 64; // updates from main module - this.SHOW_WINDOWS_ICON = this.get('dashShowWindowsIcon', true); - this.SHOW_RECENT_FILES_ICON = this.get('dashShowRecentFilesIcon', true); + this.MAX_ICON_SIZE = this.get('dashMaxIconSize'); + this.SHOW_WINDOWS_ICON = this.get('dashShowWindowsIcon'); + this.SHOW_RECENT_FILES_ICON = this.get('dashShowRecentFilesIcon'); + this.SHOW_EXTENSIONS_ICON = this.get('dashShowExtensionsIcon'); - this.WS_TMB_POSITION = this.get('workspaceThumbnailsPosition', true); + this.WS_TMB_POSITION = this.get('workspaceThumbnailsPosition'); this.ORIENTATION = this.WS_TMB_POSITION > 4 ? 0 : 1; - this.WORKSPACE_MAX_SPACING = this.get('wsMaxSpacing', true); + this.WORKSPACE_MAX_SPACING = this.get('wsMaxSpacing'); // ORIENTATION || DASH_LEFT || DASH_RIGHT ? 350 : 80; this.SHOW_WS_TMB = ![4, 9].includes(this.WS_TMB_POSITION); // 4, 9 - disable - this.WS_TMB_FULL = this.get('wsThumbnailsFull', true); + this.WS_TMB_FULL = this.get('wsThumbnailsFull'); // translate ws tmb position to 0 top, 1 right, 2 bottom, 3 left // 0L 1R, 2LF, 3RF, 4DV, 5T, 6B, 7TF, 8BF, 9DH this.WS_TMB_POSITION = [3, 1, 3, 1, 4, 0, 2, 0, 2, 8][this.WS_TMB_POSITION]; @@ -311,81 +343,104 @@ var Options = class Options { this.WS_TMB_RIGHT = this.WS_TMB_POSITION === 1; this.WS_TMB_BOTTOM = this.WS_TMB_POSITION === 2; this.WS_TMB_LEFT = this.WS_TMB_POSITION === 3; - this.WS_TMB_POSITION_ADJUSTMENT = this.get('wsTmbPositionAdjust', true) * -1 / 100; // range 1 to -1 - this.SEC_WS_TMB_POSITION = this.get('secWsThumbnailsPosition', true); + this.WS_TMB_POSITION_ADJUSTMENT = this.get('wsTmbPositionAdjust') * -1 / 100; // range 1 to -1 + this.SEC_WS_TMB_POSITION = this.get('secWsThumbnailsPosition'); this.SHOW_SEC_WS_TMB = this.SEC_WS_TMB_POSITION !== 3 && this.SHOW_WS_TMB; this.SEC_WS_TMB_TOP = (this.SEC_WS_TMB_POSITION === 0 && !this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_TOP); this.SEC_WS_TMB_RIGHT = (this.SEC_WS_TMB_POSITION === 1 && this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_RIGHT); this.SEC_WS_TMB_BOTTOM = (this.SEC_WS_TMB_POSITION === 1 && !this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_BOTTOM); this.SEC_WS_TMB_LEFT = (this.SEC_WS_TMB_POSITION === 0 && this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_LEFT); - this.SEC_WS_TMB_POSITION_ADJUSTMENT = this.get('secWsTmbPositionAdjust', true) * -1 / 100; // range 1 to -1 - this.SEC_WS_PREVIEW_SHIFT = this.get('secWsPreviewShift', true); - this.SHOW_WST_LABELS = this.get('showWsTmbLabels', true); - this.SHOW_WST_LABELS_ON_HOVER = this.get('showWsTmbLabelsOnHover', true); - this.CLOSE_WS_BUTTON_MODE = this.get('closeWsButtonMode', true); + this.SEC_WS_TMB_POSITION_ADJUSTMENT = this.get('secWsTmbPositionAdjust') * -1 / 100; // range 1 to -1 + this.SEC_WS_PREVIEW_SHIFT = this.get('secWsPreviewShift'); + this.SHOW_WST_LABELS = this.get('showWsTmbLabels'); + this.SHOW_WST_LABELS_ON_HOVER = this.get('showWsTmbLabelsOnHover'); + this.CLOSE_WS_BUTTON_MODE = this.get('closeWsButtonMode'); - this.MAX_THUMBNAIL_SCALE = this.get('wsThumbnailScale', true) / 100; - this.MAX_THUMBNAIL_SCALE_APPGRID = this.get('wsThumbnailScaleAppGrid', true) / 100; - if (this.MAX_THUMBNAIL_SCALE_APPGRID === 0) - this.MAX_THUMBNAIL_SCALE_APPGRID = this.MAX_THUMBNAIL_SCALE; + this.MAX_THUMBNAIL_SCALE = this.get('wsThumbnailScale') / 100; + if (this.MAX_THUMBNAIL_SCALE === 0) { + this.MAX_THUMBNAIL_SCALE = 0.01; + this.SHOW_WS_TMB = false; + } + this.MAX_THUMBNAIL_SCALE_APPGRID = this.get('wsThumbnailScaleAppGrid') / 100; + this.SHOW_WS_TMB_APPGRID = true; + if (this.MAX_THUMBNAIL_SCALE_APPGRID === 0) { + this.MAX_THUMBNAIL_SCALE_APPGRID = 0.01; + this.SHOW_WS_TMB_APPGRID = false; + } this.MAX_THUMBNAIL_SCALE_STABLE = this.MAX_THUMBNAIL_SCALE === this.MAX_THUMBNAIL_SCALE_APPGRID; - this.SEC_MAX_THUMBNAIL_SCALE = this.get('secWsThumbnailScale', true) / 100; - this.WS_PREVIEW_SCALE = this.get('wsPreviewScale', true) / 100; - this.SEC_WS_PREVIEW_SCALE = this.get('secWsPreviewScale', true) / 100; + this.SEC_MAX_THUMBNAIL_SCALE = this.get('secWsThumbnailScale') / 100; + if (this.SEC_MAX_THUMBNAIL_SCALE === 0) { + this.SEC_MAX_THUMBNAIL_SCALE = 0.01; + this.SHOW_SEC_WS_TMB = false; + } + + this.WS_PREVIEW_SCALE = this.get('wsPreviewScale') / 100; + this.SEC_WS_PREVIEW_SCALE = this.get('secWsPreviewScale') / 100; // calculate number of possibly visible neighbor previews according to ws scale this.NUMBER_OF_VISIBLE_NEIGHBORS = Math.round(1 + (1 - this.WS_PREVIEW_SCALE) / 4); - this.SHOW_WS_TMB_BG = this.get('showWsSwitcherBg', true) && this.SHOW_WS_TMB; - this.WS_PREVIEW_BG_RADIUS = this.get('wsPreviewBgRadius', true); - this.SHOW_WS_PREVIEW_BG = this.get('showWsPreviewBg', true); + this.SHOW_WS_TMB_BG = this.get('showWsSwitcherBg') && this.SHOW_WS_TMB; + this.WS_PREVIEW_BG_RADIUS = this.get('wsPreviewBgRadius'); + this.SHOW_WS_PREVIEW_BG = this.get('showWsPreviewBg'); - this.CENTER_APP_GRID = this.get('centerAppGrid', true); + this.CENTER_APP_GRID = this.get('centerAppGrid'); - this.SHOW_SEARCH_ENTRY = this.get('showSearchEntry', true); - this.CENTER_SEARCH_VIEW = this.get('centerSearch', true); - this.APP_GRID_ANIMATION = this.get('appGridAnimation', true); + this.SHOW_SEARCH_ENTRY = this.get('showSearchEntry'); + this.CENTER_SEARCH_VIEW = this.get('centerSearch'); + this.APP_GRID_ANIMATION = this.get('appGridAnimation'); if (this.APP_GRID_ANIMATION === 4) this.APP_GRID_ANIMATION = this._getAnimationDirection(); - this.SEARCH_VIEW_ANIMATION = this.get('searchViewAnimation', true); + this.SEARCH_VIEW_ANIMATION = this.get('searchViewAnimation'); if (this.SEARCH_VIEW_ANIMATION === 4) this.SEARCH_VIEW_ANIMATION = 3; - this.WS_ANIMATION = this.get('workspaceAnimation', true); + this.WS_ANIMATION = this.get('workspaceAnimation'); - this.WIN_PREVIEW_ICON_SIZE = [64, 48, 32, 22, 8][this.get('winPreviewIconSize', true)]; - this.ALWAYS_SHOW_WIN_TITLES = this.get('alwaysShowWinTitles', true); + this.WIN_PREVIEW_ICON_SIZE = [64, 48, 32, 22, 8][this.get('winPreviewIconSize')]; + this.WIN_TITLES_POSITION = this.get('winTitlePosition'); + this.ALWAYS_SHOW_WIN_TITLES = this.WIN_TITLES_POSITION === 1; - this.STARTUP_STATE = this.get('startupState', true); - this.SHOW_BG_IN_OVERVIEW = this.get('showBgInOverview', true); - this.OVERVIEW_BG_BRIGHTNESS = this.get('overviewBgBrightness', true) / 100; - this.OVERVIEW_BG_BLUR_SIGMA = this.get('overviewBgBlurSigma', true); - this.APP_GRID_BG_BLUR_SIGMA = this.get('appGridBgBlurSigma', true); - this.SMOOTH_BLUR_TRANSITIONS = this.get('smoothBlurTransitions', true); + this.STARTUP_STATE = this.get('startupState'); + this.SHOW_BG_IN_OVERVIEW = this.get('showBgInOverview'); + this.OVERVIEW_BG_BRIGHTNESS = this.get('overviewBgBrightness') / 100; + this.SEARCH_BG_BRIGHTNESS = this.get('searchBgBrightness') / 100; + this.OVERVIEW_BG_BLUR_SIGMA = this.get('overviewBgBlurSigma'); + this.APP_GRID_BG_BLUR_SIGMA = this.get('appGridBgBlurSigma'); + this.SMOOTH_BLUR_TRANSITIONS = this.get('smoothBlurTransitions'); - this.OVERVIEW_MODE = this.get('overviewMode', true); + this.OVERVIEW_MODE = this.get('overviewMode'); this.OVERVIEW_MODE2 = this.OVERVIEW_MODE === 2; this.WORKSPACE_MODE = this.OVERVIEW_MODE ? 0 : 1; - this.STATIC_WS_SWITCHER_BG = this.get('workspaceSwitcherAnimation', true); + this.STATIC_WS_SWITCHER_BG = this.get('workspaceSwitcherAnimation'); - this.ANIMATION_TIME_FACTOR = this.get('animationSpeedFactor', true) / 100; + this.ANIMATION_TIME_FACTOR = this.get('animationSpeedFactor') / 100; - this.SEARCH_ICON_SIZE = this.get('searchIconSize', true); - this.SEARCH_VIEW_SCALE = this.get('searchViewScale', true) / 100; - this.SEARCH_MAX_ROWS = this.get('searchMaxResultsRows', true); - this.SEARCH_FUZZY = this.get('searchFuzzy', true); + this.SEARCH_ICON_SIZE = this.get('searchIconSize'); + this.SEARCH_VIEW_SCALE = this.get('searchViewScale') / 100; + this.SEARCH_MAX_ROWS = this.get('searchMaxResultsRows'); + this.SEARCH_FUZZY = this.get('searchFuzzy'); - this.APP_GRID_ALLOW_INCOMPLETE_PAGES = this.get('appGridIncompletePages', true); - this.APP_GRID_ICON_SIZE = this.get('appGridIconSize', true); - this.APP_GRID_COLUMNS = this.get('appGridColumns', true); - this.APP_GRID_ROWS = this.get('appGridRows', true); + this.APP_GRID_ALLOW_INCOMPLETE_PAGES = this.get('appGridIncompletePages'); + this.APP_GRID_ICON_SIZE = this.get('appGridIconSize'); + this.APP_GRID_COLUMNS = this.get('appGridColumns'); + this.APP_GRID_ROWS = this.get('appGridRows'); this.APP_GRID_ADAPTIVE = !this.APP_GRID_COLUMNS && !this.APP_GRID_ROWS; - this.APP_GRID_ORDER = this.get('appGridOrder', true); - this.APP_GRID_INCLUDE_DASH = this.get('appGridContent', true); + this.APP_GRID_ORDER = this.get('appGridOrder'); + this.APP_GRID_ALPHABET = [1, 2].includes(this.APP_GRID_ORDER); + this.APP_GRID_FOLDERS_FIRST = this.APP_GRID_ORDER === 1; + this.APP_GRID_FOLDERS_LAST = this.APP_GRID_ORDER === 2; + this.APP_GRID_USAGE = this.APP_GRID_ORDER === 3; + + this.APP_FOLDER_ORDER = this.get('appFolderOrder'); + this.APP_FOLDER_ALPHABET = this.APP_FOLDER_ORDER === 1; + this.APP_FOLDER_USAGE = this.APP_FOLDER_ORDER === 2; + + this.APP_GRID_INCLUDE_DASH = this.get('appGridContent'); /* APP_GRID_INCLUDE_DASH 0 - Include All 1 - Include All - Favorites and Runnings First @@ -397,46 +452,50 @@ var Options = class Options { this.APP_GRID_EXCLUDE_RUNNING = this.APP_GRID_INCLUDE_DASH === 3 || this.APP_GRID_INCLUDE_DASH === 4; this.APP_GRID_DASH_FIRST = this.APP_GRID_INCLUDE_DASH === 1; - this.APP_GRID_NAMES_MODE = this.get('appGridNamesMode', true); + this.APP_GRID_NAMES_MODE = this.get('appGridNamesMode'); - this.APP_GRID_FOLDER_ICON_SIZE = this.get('appGridFolderIconSize', true); - this.APP_GRID_FOLDER_ICON_GRID = this.get('appGridFolderIconGrid', true); - this.APP_GRID_FOLDER_COLUMNS = this.get('appGridFolderColumns', true); - this.APP_GRID_FOLDER_ROWS = this.get('appGridFolderRows', true); - this.APP_GRID_SPACING = this.get('appGridSpacing', true); + this.APP_GRID_FOLDER_ICON_SIZE = this.get('appGridFolderIconSize'); + this.APP_GRID_FOLDER_ICON_GRID = this.get('appGridFolderIconGrid'); + this.APP_GRID_FOLDER_COLUMNS = this.get('appGridFolderColumns'); + this.APP_GRID_FOLDER_ROWS = this.get('appGridFolderRows'); + this.APP_GRID_SPACING = this.get('appGridSpacing'); this.APP_GRID_FOLDER_DEFAULT = this.APP_GRID_FOLDER_ROWS === 3 && this.APP_GRID_FOLDER_COLUMNS === 3; - this.APP_GRID_ACTIVE_PREVIEW = this.get('appGridActivePreview', true); - this.APP_GRID_FOLDER_CENTER = this.get('appGridFolderCenter', true); - this.APP_GRID_PAGE_WIDTH_SCALE = this.get('appGridPageWidthScale', true) / 100; + this.APP_GRID_FOLDER_ADAPTIVE = !this.APP_GRID_FOLDER_COLUMNS && !this.APP_GRID_FOLDER_ROWS; + this.APP_GRID_ACTIVE_PREVIEW = this.get('appGridActivePreview'); + this.APP_GRID_FOLDER_CENTER = this.get('appGridFolderCenter'); + this.APP_GRID_PAGE_WIDTH_SCALE = this.get('appGridPageWidthScale') / 100; - this.APP_GRID_ICON_SIZE_DEFAULT = this.APP_GRID_ACTIVE_PREVIEW && !this.APP_GRID_ORDER ? 176 : 96; + this.APP_GRID_ICON_SIZE_DEFAULT = this.APP_GRID_ACTIVE_PREVIEW && !this.APP_GRID_USAGE ? 176 : 96; this.APP_GRID_FOLDER_ICON_SIZE_DEFAULT = 96; - this.WINDOW_SEARCH_PROVIDER_ENABLED = this.get('searchWindowsEnable', true); - this.RECENT_FILES_SEARCH_PROVIDER_ENABLED = this.get('searchRecentFilesEnable', true); + this.APP_GRID_PERFORMANCE = this.get('appGridPerformance'); - this.PANEL_POSITION_TOP = this.get('panelPosition', true) === 0; - this.PANEL_MODE = this.get('panelVisibility', true); + this.WINDOW_SEARCH_ORDER = this.get('searchWindowsOrder'); + + this.PANEL_POSITION_TOP = this.get('panelPosition') === 0; + this.PANEL_MODE = this.get('panelVisibility'); this.PANEL_DISABLED = this.PANEL_MODE === 2; this.PANEL_OVERVIEW_ONLY = this.PANEL_MODE === 1; this.START_Y_OFFSET = 0; // set from main module - this.FIX_UBUNTU_DOCK = this.get('fixUbuntuDock', true); - this.WINDOW_ATTENTION_MODE = this.get('windowAttentionMode', true); + this.WINDOW_ATTENTION_MODE = this.get('windowAttentionMode'); this.WINDOW_ATTENTION_DISABLE_NOTIFICATIONS = this.WINDOW_ATTENTION_MODE === 1; this.WINDOW_ATTENTION_FOCUS_IMMEDIATELY = this.WINDOW_ATTENTION_MODE === 2; - this.WS_SW_POPUP_H_POSITION = this.get('wsSwPopupHPosition', true) / 100; - this.WS_SW_POPUP_V_POSITION = this.get('wsSwPopupVPosition', true) / 100; - this.WS_SW_POPUP_MODE = this.get('wsSwPopupMode', true); + this.WS_SW_POPUP_H_POSITION = this.get('wsSwPopupHPosition') / 100; + this.WS_SW_POPUP_V_POSITION = this.get('wsSwPopupVPosition') / 100; + this.WS_SW_POPUP_MODE = this.get('wsSwPopupMode'); - this.SHOW_FAV_NOTIFICATION = this.get('favoritesNotify', true); - this.NOTIFICATION_POSITION = this.get('notificationPosition', true); + this.WS_WRAPAROUND = this.get('wsSwitcherWraparound'); + this.WS_IGNORE_LAST = this.get('wsSwitcherIgnoreLast'); - this.OSD_POSITION = this.get('osdPosition', true); + this.SHOW_FAV_NOTIFICATION = this.get('favoritesNotify'); + this.NOTIFICATION_POSITION = this.get('notificationPosition'); - this.HOT_CORNER_ACTION = this.get('hotCornerAction', true); - this.HOT_CORNER_POSITION = this.get('hotCornerPosition', true); + this.OSD_POSITION = this.get('osdPosition'); + + this.HOT_CORNER_ACTION = this.get('hotCornerAction'); + this.HOT_CORNER_POSITION = this.get('hotCornerPosition'); if (this.HOT_CORNER_POSITION === 6 && this.DASH_VISIBLE) this.HOT_CORNER_EDGE = true; else @@ -451,13 +510,24 @@ var Options = class Options { else this.HOT_CORNER_POSITION = 0; } - this.HOT_CORNER_FULLSCREEN = this.get('hotCornerFullscreen', true); - this.HOT_CORNER_RIPPLES = this.get('hotCornerRipples', true); + this.HOT_CORNER_FULLSCREEN = this.get('hotCornerFullscreen'); + this.HOT_CORNER_RIPPLES = this.get('hotCornerRipples'); - this.ALWAYS_ACTIVATE_SELECTED_WINDOW = this.get('alwaysActivateSelectedWindow', true); - this.WINDOW_ICON_CLICK_SEARCH = this.get('windowIconClickSearch', true); + this.ALWAYS_ACTIVATE_SELECTED_WINDOW = this.get('alwaysActivateSelectedWindow'); + this.WIN_PREVIEW_SEC_BTN_ACTION = this.get('winPreviewSecBtnAction'); + this.WIN_PREVIEW_MID_BTN_ACTION = this.get('winPreviewMidBtnAction'); + this.SHOW_CLOSE_BUTTON = this.get('winPreviewShowCloseButton'); + this.WINDOW_ICON_CLICK_ACTION = this.get('windowIconClickAction'); - this.OVERLAY_KEY_SECONDARY = this.get('overlayKeySecondary', true); + this.OVERLAY_KEY_PRIMARY = this.get('overlayKeyPrimary'); + this.OVERLAY_KEY_SECONDARY = this.get('overlayKeySecondary'); + + this.ESC_BEHAVIOR = this.get('overviewEscBehavior'); + + this.WINDOW_THUMBNAIL_ENABLED = this.get('windowThumbnailModule'); + this.WINDOW_THUMBNAIL_SCALE = this.get('windowThumbnailScale') / 100; + + this.FIX_NEW_WINDOW_FOCUS = this.get('newWindowFocusFix'); } _getAnimationDirection() { diff --git a/extensions/vertical-workspaces/lib/swipeTracker.js b/extensions/44/vertical-workspaces/lib/swipeTracker.js similarity index 51% rename from extensions/vertical-workspaces/lib/swipeTracker.js rename to extensions/44/vertical-workspaces/lib/swipeTracker.js index d9c3407..7122ead 100644 --- a/extensions/vertical-workspaces/lib/swipeTracker.js +++ b/extensions/44/vertical-workspaces/lib/swipeTracker.js @@ -10,61 +10,90 @@ 'use strict'; -const { Clutter, GObject } = imports.gi; +const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; + const Main = imports.ui.main; const SwipeTracker = imports.ui.swipeTracker; -const Me = imports.misc.extensionUtils.getCurrentExtension(); - +let Me; let opt; -let _firstRun = true; -let _vwGestureUpdateId; -let _originalGestureUpdateId; +var SwipeTrackerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('swipeTrackerModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (reset || !opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL - // original swipeTrackers' orientation and updateGesture function - Main.overview._swipeTracker.orientation = Clutter.Orientation.VERTICAL; - Main.wm._workspaceAnimation._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL; - Main.overview._swipeTracker._updateGesture = SwipeTracker.SwipeTracker.prototype._updateGesture; - if (_vwGestureUpdateId) { - Main.overview._swipeTracker._touchpadGesture.disconnect(_vwGestureUpdateId); - _vwGestureUpdateId = 0; - } - if (_originalGestureUpdateId) { - Main.overview._swipeTracker._touchpadGesture.unblock_signal_handler(_originalGestureUpdateId); - _originalGestureUpdateId = 0; - } - - opt = null; - return; + this._firstActivation = true; + this.moduleEnabled = false; } - if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('swipeTrackerModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' SwipeTrackerModule - Keeping untouched'); + } + + _activateModule() { + if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL + this._setVertical(); + } else { + this._setHorizontal(); + } + console.debug(' SwipeTrackerModule - Activated'); + } + + _disableModule() { + this._setHorizontal(); + + console.debug(' SwipeTrackerModule - Disabled'); + } + + _setVertical() { // reverse swipe gestures for enter/leave overview and ws switching Main.overview._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL; Main.wm._workspaceAnimation._swipeTracker.orientation = Clutter.Orientation.VERTICAL; // overview's updateGesture() function should reflect ws tmb position to match appGrid/ws animation direction // function in connection cannot be overridden in prototype of its class because connected is actually another copy of the original function - if (!_originalGestureUpdateId) { - _originalGestureUpdateId = GObject.signal_handler_find(Main.overview._swipeTracker._touchpadGesture, { signalId: 'update' }); - Main.overview._swipeTracker._touchpadGesture.block_signal_handler(_originalGestureUpdateId); + if (!this._originalGestureUpdateId) { + this._originalGestureUpdateId = GObject.signal_handler_find(Main.overview._swipeTracker._touchpadGesture, { signalId: 'update' }); + Main.overview._swipeTracker._touchpadGesture.block_signal_handler(this._originalGestureUpdateId); Main.overview._swipeTracker._updateGesture = SwipeTrackerVertical._updateGesture; - _vwGestureUpdateId = Main.overview._swipeTracker._touchpadGesture.connect('update', SwipeTrackerVertical._updateGesture.bind(Main.overview._swipeTracker)); + this._vwGestureUpdateId = Main.overview._swipeTracker._touchpadGesture.connect('update', SwipeTrackerVertical._updateGesture.bind(Main.overview._swipeTracker)); } } -} + + _setHorizontal() { + // original swipeTrackers' orientation and updateGesture function + Main.overview._swipeTracker.orientation = Clutter.Orientation.VERTICAL; + Main.wm._workspaceAnimation._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL; + Main.overview._swipeTracker._updateGesture = SwipeTracker.SwipeTracker.prototype._updateGesture; + if (this._vwGestureUpdateId) { + Main.overview._swipeTracker._touchpadGesture.disconnect(this._vwGestureUpdateId); + this._vwGestureUpdateId = 0; + } + if (this._originalGestureUpdateId) { + Main.overview._swipeTracker._touchpadGesture.unblock_signal_handler(this._originalGestureUpdateId); + this._originalGestureUpdateId = 0; + } + } +}; const SwipeTrackerVertical = { _updateGesture(gesture, time, delta, distance) { diff --git a/extensions/vertical-workspaces/lib/util.js b/extensions/44/vertical-workspaces/lib/util.js similarity index 69% copy from extensions/vertical-workspaces/lib/util.js copy to extensions/44/vertical-workspaces/lib/util.js index 5f5c069..9bc4365 100644 --- a/extensions/vertical-workspaces/lib/util.js +++ b/extensions/44/vertical-workspaces/lib/util.js @@ -10,16 +10,27 @@ 'use strict'; +const Clutter = imports.gi.Clutter; +const Meta = imports.gi.Meta; const Gi = imports._gi; -const { Shell, Meta, Clutter } = imports.gi; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Shell = imports.gi.Shell; -const Config = imports.misc.config; -const Main = imports.ui.main; +const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +let Me; -var shellVersion = parseFloat(Config.PACKAGE_VERSION); +let _installedExtensions; + +function init(me) { + Me = me; +} + +function cleanGlobals() { + Me = null; + _installedExtensions = null; +} var Overrides = class { constructor() { @@ -27,8 +38,13 @@ var Overrides = class { } addOverride(name, prototype, overrideList) { + const backup = this.overrideProto(prototype, overrideList, name); + // don't update originals when override's just refreshing, keep initial content + let originals = this._overrides[name]?.originals; + if (!originals) + originals = backup; this._overrides[name] = { - originals: this.overrideProto(prototype, overrideList), + originals, prototype, }; } @@ -38,15 +54,15 @@ var Overrides = class { if (!override) return false; - this.overrideProto(override.prototype, override.originals); - this._overrides[name] = undefined; + this.overrideProto(override.prototype, override.originals, name); + delete this._overrides[name]; return true; } removeAll() { for (let name in this._overrides) { this.removeOverride(name); - this._overrides[name] = undefined; + delete this._overrides[name]; } } @@ -54,13 +70,17 @@ var Overrides = class { proto[Gi.hook_up_vfunc_symbol](symbol, func); } - overrideProto(proto, overrides) { + overrideProto(proto, overrides, name) { const backup = {}; - + const originals = this._overrides[name]?.originals; for (let symbol in overrides) { if (symbol.startsWith('after_')) { const actualSymbol = symbol.slice('after_'.length); - const fn = proto[actualSymbol]; + let fn; + if (originals && originals[actualSymbol]) + fn = originals[actualSymbol]; + else + fn = proto[actualSymbol]; const afterFn = overrides[symbol]; proto[actualSymbol] = function (...args) { args = Array.prototype.slice.call(args); @@ -72,11 +92,11 @@ var Overrides = class { } else { backup[symbol] = proto[symbol]; if (symbol.startsWith('vfunc')) { - if (shellVersion < 42) + if (Me.shellVersion < 42) this.hookVfunc(proto, symbol.slice(6), overrides[symbol]); else this.hookVfunc(proto[Gi.gobject_prototype_symbol], symbol.slice(6), overrides[symbol]); - } else { + } else if (overrides[symbol] !== null) { proto[symbol] = overrides[symbol]; } } @@ -85,87 +105,21 @@ var Overrides = class { } }; -function getOverviewTranslations(opt, dash, tmbBox, searchEntryBin) { - // const tmbBox = Main.overview._overview._controls._thumbnailsBox; - let searchTranslationY = 0; - if (searchEntryBin.visible) { - const offset = (dash.visible && (!opt.DASH_VERTICAL ? dash.height + 12 : 0)) + - (opt.WS_TMB_TOP ? tmbBox.height + 12 : 0); - searchTranslationY = -searchEntryBin.height - offset - 30; - } - - let tmbTranslationX = 0; - let tmbTranslationY = 0; - let offset; - if (tmbBox.visible) { - switch (opt.WS_TMB_POSITION) { - case 3: // left - offset = 10 + (dash?.visible && opt.DASH_LEFT ? dash.width : 0); - tmbTranslationX = -tmbBox.width - offset; - tmbTranslationY = 0; - break; - case 1: // right - offset = 10 + (dash?.visible && opt.DASH_RIGHT ? dash.width : 0); - tmbTranslationX = tmbBox.width + offset; - tmbTranslationY = 0; - break; - case 0: // top - offset = 10 + (dash?.visible && opt.DASH_TOP ? dash.height : 0) + Main.panel.height; - tmbTranslationX = 0; - tmbTranslationY = -tmbBox.height - offset; - break; - case 2: // bottom - offset = 10 + (dash?.visible && opt.DASH_BOTTOM ? dash.height : 0) + Main.panel.height; // just for case the panel is at bottom - tmbTranslationX = 0; - tmbTranslationY = tmbBox.height + offset; - break; - } - } - - let dashTranslationX = 0; - let dashTranslationY = 0; - let position = opt.DASH_POSITION; - // if DtD replaced the original Dash, read its position - if (dashIsDashToDock()) - position = dash._position; - - if (dash?.visible) { - switch (position) { - case 0: // top - dashTranslationX = 0; - dashTranslationY = -dash.height - dash.margin_bottom - Main.panel.height; - break; - case 1: // right - dashTranslationX = dash.width; - dashTranslationY = 0; - break; - case 2: // bottom - dashTranslationX = 0; - dashTranslationY = dash.height + dash.margin_bottom + Main.panel.height; - break; - case 3: // left - dashTranslationX = -dash.width; - dashTranslationY = 0; - break; - } - } - - return [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY]; -} - -function openPreferences() { +function openPreferences(metadata) { + if (!metadata) + metadata = Me.metadata; const windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null); let tracker = Shell.WindowTracker.get_default(); let metaWin, isVW = null; for (let win of windows) { const app = tracker.get_window_app(win); - if (win.get_title().includes(Me.metadata.name) && app.get_name() === 'Extensions') { + if (win.get_title()?.includes(metadata.name) && app.get_name() === 'Extensions') { // this is our existing window metaWin = win; isVW = true; break; - } else if (win.wm_class.includes('org.gnome.Shell.Extensions')) { + } else if (win.wm_class?.includes('org.gnome.Shell.Extensions')) { // this is prefs window of another extension metaWin = win; isVW = false; @@ -182,17 +136,20 @@ function openPreferences() { } if (!metaWin || (metaWin && !isVW)) { - try { - Main.extensionManager.openExtensionPrefs(Me.metadata.uuid, '', {}); - } catch (e) { - log(e); - } + GLib.idle_add(GLib.PRIORITY_LOW, () => { + try { + Main.extensionManager.openExtensionPrefs(metadata.uuid, '', {}); + } catch (e) { + console.error(e); + } + }); } } function activateSearchProvider(prefix = '') { const searchEntry = Main.overview.searchEntry; - if (!searchEntry.get_text() || !searchEntry.get_text().startsWith(prefix)) { + const searchEntryText = searchEntry.get_text(); + if (!searchEntryText || (searchEntryText && !searchEntry.get_text().startsWith(prefix))) { prefix = `${prefix} `; const position = prefix.length; searchEntry.set_text(prefix); @@ -220,9 +177,16 @@ function reorderWorkspace(direction = 0) { global.workspace_manager.reorder_workspace(activeWs, targetIdx); } +function activateKeyboardForWorkspaceView() { + Main.ctrlAltTabManager._items.forEach(i => { + if (i.sortGroup === 1 && i.name === 'Windows') + Main.ctrlAltTabManager.focusGroup(i); + }); +} + function exposeWindows(adjustment, activateKeyboard) { // expose windows for static overview modes - if (!adjustment.value && !Main.overview._animationInProgress) { + if (!adjustment.value/* && !Main.overview._animationInProgress*/) { if (adjustment.value === 0) { adjustment.value = 0; adjustment.ease(1, { @@ -330,13 +294,48 @@ function isMoreRelevant(stringA, stringB, pattern) { return !aAny && bAny; } -function getEnabledExtensions(uuid = '') { - let extensions = []; - Main.extensionManager._extensions.forEach(e => { - if (e.state === 1 && e.uuid.includes(uuid)) - extensions.push(e); - }); - return extensions; +function getEnabledExtensions(pattern = '') { + let result = []; + // extensionManager is unreliable at startup (if not all extensions were loaded) + // but gsettings key can contain removed extensions... + // therefore we have to look into filesystem, what's really installed + if (!_installedExtensions) { + const extensionFiles = [...collectFromDatadirs('extensions', true)]; + _installedExtensions = extensionFiles.map(({ info }) => { + let fileType = info.get_file_type(); + if (fileType !== Gio.FileType.DIRECTORY) + return null; + const uuid = info.get_name(); + return uuid; + }); + } + const enabled = Main.extensionManager._enabledExtensions; + result = _installedExtensions.filter(ext => enabled.includes(ext)); + return result.filter(uuid => uuid !== null && uuid.includes(pattern)); +} + +function* collectFromDatadirs(subdir, includeUserDir) { + let dataDirs = GLib.get_system_data_dirs(); + if (includeUserDir) + dataDirs.unshift(GLib.get_user_data_dir()); + + for (let i = 0; i < dataDirs.length; i++) { + let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]); + let dir = Gio.File.new_for_path(path); + + let fileEnum; + try { + fileEnum = dir.enumerate_children('standard::name,standard::type', + Gio.FileQueryInfoFlags.NONE, null); + } catch (e) { + fileEnum = null; + } + if (fileEnum !== null) { + let info; + while ((info = fileEnum.next_file(null))) + yield { dir: fileEnum.get_child(info), info }; + } + } } function getScrollDirection(event) { diff --git a/extensions/44/vertical-workspaces/lib/winTmb.js b/extensions/44/vertical-workspaces/lib/winTmb.js new file mode 100644 index 0000000..b18ea18 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/winTmb.js @@ -0,0 +1,525 @@ +/** + * V-Shell (Vertical Workspaces) + * WinTmb + * + * @author GdH + * @copyright 2021-2023 + * @license GPL-3.0 + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const St = imports.gi.St; + +const AltTab = imports.ui.altTab; +const DND = imports.ui.dnd; +const Main = imports.ui.main; + +let Me; +let opt; + +const SCROLL_ICON_OPACITY = 240; +const DRAG_OPACITY = 200; +const CLOSE_BTN_OPACITY = 240; + + +var WinTmbModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this._removeTimeouts(); + + this.moduleEnabled = opt.get('windowThumbnailModule'); + + reset = reset || !this.moduleEnabled; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WinTmb - Keeping untouched'); + } + + _activateModule() { + this._timeouts = {}; + if (!this._windowThumbnails) + this._windowThumbnails = []; + + Main.overview.connectObject('hidden', () => this.showThumbnails(), this); + console.debug(' WinTmb - Activated'); + } + + _disableModule() { + Main.overview.disconnectObject(this); + this._disconnectStateAdjustment(); + this.removeAllThumbnails(); + console.debug(' WinTmb - Disabled'); + } + + _removeTimeouts() { + if (this._timeouts) { + Object.values(this._timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + this._timeouts = null; + } + } + + createThumbnail(metaWin) { + const thumbnail = new WindowThumbnail(metaWin, { + 'height': Math.floor(opt.WINDOW_THUMBNAIL_SCALE * global.display.get_monitor_geometry(global.display.get_current_monitor()).height), + 'thumbnailsOnScreen': this._windowThumbnails.length, + }); + + this._windowThumbnails.push(thumbnail); + thumbnail.connect('removed', tmb => { + this._windowThumbnails.splice(this._windowThumbnails.indexOf(tmb), 1); + tmb.destroy(); + if (!this._windowThumbnails.length) + this._disconnectStateAdjustment(); + }); + + if (!this._stateAdjustmentConId) { + this._stateAdjustmentConId = Main.overview._overview.controls._stateAdjustment.connectObject('notify::value', () => { + if (!this._thumbnailsHidden && (!opt.OVERVIEW_MODE2 || opt.WORKSPACE_MODE)) + this.hideThumbnails(); + }, this); + } + } + + hideThumbnails() { + this._windowThumbnails.forEach(tmb => { + tmb.ease({ + opacity: 0, + duration: 200, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => tmb.hide(), + }); + }); + this._thumbnailsHidden = true; + } + + showThumbnails() { + this._windowThumbnails.forEach(tmb => { + tmb.show(); + tmb.ease({ + opacity: 255, + duration: 100, + mode: Clutter.AnimationMode.LINEAR, + }); + }); + this._thumbnailsHidden = false; + } + + removeAllThumbnails() { + this._windowThumbnails.forEach(tmb => tmb.remove()); + this._windowThumbnails = []; + } + + _disconnectStateAdjustment() { + Main.overview._overview.controls._stateAdjustment.disconnectObject(this); + } +}; + +const WindowThumbnail = GObject.registerClass({ + Signals: { 'removed': {} }, +}, class WindowThumbnail extends St.Widget { + _init(metaWin, args) { + this._hoverShowsPreview = false; + this._customOpacity = 255; + this._initTmbHeight = args.height; + this._minimumHeight = Math.floor(5 / 100 * global.display.get_monitor_geometry(global.display.get_current_monitor()).height); + this._scrollTimeout = 100; + this._positionOffset = args.thumbnailsOnScreen; + this._reverseTmbWheelFunc = false; + this._click_count = 1; + this._prevBtnPressTime = 0; + this.w = metaWin; + super._init({ + layout_manager: new Clutter.BinLayout(), + visible: true, + reactive: true, + can_focus: true, + track_hover: true, + }); + this.connect('button-release-event', this._onBtnReleased.bind(this)); + this.connect('scroll-event', this._onScrollEvent.bind(this)); + // this.connect('motion-event', this._onMouseMove.bind(this)); // may be useful in the future.. + + this._delegate = this; + this._draggable = DND.makeDraggable(this, { dragActorOpacity: DRAG_OPACITY }); + this._draggable.connect('drag-end', this._end_drag.bind(this)); + this._draggable.connect('drag-cancelled', this._end_drag.bind(this)); + this._draggable._animateDragEnd = eventTime => { + this._draggable._animationInProgress = true; + this._draggable._onAnimationComplete(this._draggable._dragActor, eventTime); + this.opacity = this._customOpacity; + }; + + this.clone = new Clutter.Clone({ reactive: true }); + Main.layoutManager.addChrome(this); + + this.window = this.w.get_compositor_private(); + + this.clone.set_source(this.window); + + this.add_child(this.clone); + this._addCloseButton(); + this._addScrollModeIcon(); + + this.connect('enter-event', () => { + global.display.set_cursor(Meta.Cursor.POINTING_HAND); + this._closeButton.opacity = CLOSE_BTN_OPACITY; + this._scrollModeBin.opacity = SCROLL_ICON_OPACITY; + if (this._hoverShowsPreview && !Main.overview._shown) { + this._closeButton.opacity = 50; + this._showWindowPreview(false, true); + } + }); + + this.connect('leave-event', () => { + global.display.set_cursor(Meta.Cursor.DEFAULT); + this._closeButton.opacity = 0; + this._scrollModeBin.opacity = 0; + if (this._winPreview) + this._destroyWindowPreview(); + }); + + this._setSize(true); + this.set_position(...this._getInitialPosition()); + this.show(); + this.window_id = this.w.get_id(); + this.tmbRedrawDirection = true; + + // remove thumbnail content and hide thumbnail if its window is destroyed + this.windowConnect = this.window.connect('destroy', () => { + if (this) + this.remove(); + }); + } + + _getInitialPosition() { + const offset = 20; + let monitor = Main.layoutManager.monitors[global.display.get_current_monitor()]; + let x = Math.min(monitor.x + monitor.width - (this.window.width * this.scale) - offset); + let y = Math.min(monitor.y + monitor.height - (this.window.height * this.scale) - offset - ((this._positionOffset * this._initTmbHeight) % (monitor.height - this._initTmbHeight))); + return [x, y]; + } + + _setSize(resetScale = false) { + if (resetScale) + this.scale = Math.min(1.0, this._initTmbHeight / this.window.height); + + const width = this.window.width * this.scale; + const height = this.window.height * this.scale; + this.set_size(width, height); + if (this.icon) { + this.icon.scale_x = this.scale; + this.icon.scale_y = this.scale; + } + + // when the scale of this. actor change, this.clone resize accordingly, + // but the reactive area of the actor doesn't change until the actor is redrawn + // this updates the actor's input region area: + Main.layoutManager._queueUpdateRegions(); + } + + /* _onMouseMove(actor, event) { + let [pos_x, pos_y] = event.get_coords(); + let state = event.get_state(); + if (this._ctrlPressed(state)) { + } + }*/ + + _onBtnReleased(actor, event) { + // Clutter.Event.click_count property in no longer available, since GS42 + if ((event.get_time() - this._prevBtnPressTime) < Clutter.Settings.get_default().double_click_time) + this._click_count += 1; + else + this._click_count = 1; + + this._prevBtnPressTime = event.get_time(); + + if (this._click_count === 2 && event.get_button() === Clutter.BUTTON_PRIMARY) + this.w.activate(global.get_current_time()); + + + const button = event.get_button(); + const state = event.get_state(); + switch (button) { + case Clutter.BUTTON_PRIMARY: + if (this._ctrlPressed(state)) { + this._setSize(); + } else { + this._reverseTmbWheelFunc = !this._reverseTmbWheelFunc; + this._scrollModeBin.set_child(this._reverseTmbWheelFunc ? this._scrollModeSourceIcon : this._scrollModeResizeIcon); + } + return Clutter.EVENT_STOP; + case Clutter.BUTTON_SECONDARY: + if (this._ctrlPressed(state)) { + this.remove(); + } else { + this._hoverShowsPreview = !this._hoverShowsPreview; + this._showWindowPreview(); + } + return Clutter.EVENT_STOP; + case Clutter.BUTTON_MIDDLE: + if (this._ctrlPressed(state)) + this.w.delete(global.get_current_time()); + return Clutter.EVENT_STOP; + default: + return Clutter.EVENT_PROPAGATE; + } + } + + _onScrollEvent(actor, event) { + let direction = Me.Util.getScrollDirection(event); + + if (this._actionTimeoutActive()) + return Clutter.EVENT_PROPAGATE; + let state = event.get_state(); + switch (direction) { + case Clutter.ScrollDirection.UP: + if (this._shiftPressed(state)) { + this.opacity = Math.min(255, this.opacity + 24); + this._customOpacity = this.opacity; + } else if (this._reverseTmbWheelFunc !== this._ctrlPressed(state)) { + this._switchSourceWin(-1); + } else if (this._reverseTmbWheelFunc === this._ctrlPressed(state)) { + this.scale = Math.max(0.05, this.scale - 0.025); + } + break; + case Clutter.ScrollDirection.DOWN: + if (this._shiftPressed(state)) { + this.opacity = Math.max(48, this.opacity - 24); + this._customOpacity = this.opacity; + } else if (this._reverseTmbWheelFunc !== this._ctrlPressed(state)) { + this._switchSourceWin(+1); + } else if (this._reverseTmbWheelFunc === this._ctrlPressed(state)) { + this.scale = Math.min(1, this.scale + 0.025); + } + break; + default: + return Clutter.EVENT_PROPAGATE; + } + this._setSize(); + return Clutter.EVENT_STOP; + } + + remove() { + if (this.clone) { + this.window.disconnect(this.windowConnect); + this.clone.set_source(null); + } + if (this._winPreview) + this._destroyWindowPreview(); + + this.emit('removed'); + } + + _end_drag() { + this.set_position(this._draggable._dragOffsetX + this._draggable._dragX, this._draggable._dragOffsetY + this._draggable._dragY); + this._setSize(); + } + + _ctrlPressed(state) { + return (state & Clutter.ModifierType.CONTROL_MASK) !== 0; + } + + _shiftPressed(state) { + return (state & Clutter.ModifierType.SHIFT_MASK) !== 0; + } + + _switchSourceWin(direction) { + let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null); + windows = windows.filter(w => !(w.skip_taskbar || w.minimized)); + let idx = -1; + for (let i = 0; i < windows.length; i++) { + if (windows[i] === this.w) { + idx = i + direction; + break; + } + } + idx = idx >= windows.length ? 0 : idx; + idx = idx < 0 ? windows.length - 1 : idx; + let w = windows[idx]; + let win = w.get_compositor_private(); + this.clone.set_source(win); + this.window.disconnect(this.windowConnect); + // the new thumbnail should be the same height as the previous one + this.scale = (this.scale * this.window.height) / win.height; + this.window = win; + this.windowConnect = this.window.connect('destroy', () => { + if (this) + this.remove(); + }); + this.w = w; + + if (this._winPreview) + this._showWindowPreview(true); + } + + _actionTimeoutActive() { + const timeout = this._reverseTmbWheelFunc ? this._scrollTimeout : this._scrollTimeout / 4; + if (!this._lastActionTime || Date.now() - this._lastActionTime > timeout) { + this._lastActionTime = Date.now(); + return false; + } + return true; + } + + /* _setIcon() { + let tracker = Shell.WindowTracker.get_default(); + let app = tracker.get_window_app(this.w); + let icon = app + ? app.create_icon_texture(this.height) + : new St.Icon({ icon_name: 'icon-missing', icon_size: this.height }); + icon.x_expand = icon.y_expand = true; + if (this.icon) + this.icon.destroy(); + this.icon = icon; + }*/ + + _addCloseButton() { + const closeButton = new St.Button({ + opacity: 0, + style_class: 'window-close', + child: new St.Icon({ icon_name: 'preview-close-symbolic' }), + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.START, + x_expand: true, + y_expand: true, + }); + + closeButton.set_style(` + margin: 3px; + background-color: rgba(200, 0, 0, 0.9); + `); + + closeButton.connect('clicked', () => { + this.remove(); + return Clutter.EVENT_STOP; + }); + + this._closeButton = closeButton; + this.add_child(this._closeButton); + } + + _addScrollModeIcon() { + this._scrollModeBin = new St.Bin({ + x_expand: true, + y_expand: true, + }); + this._scrollModeResizeIcon = new St.Icon({ + icon_name: 'view-fullscreen-symbolic', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + x_expand: true, + y_expand: true, + opacity: SCROLL_ICON_OPACITY, + style_class: 'icon-dropshadow', + scale_x: 0.5, + scale_y: 0.5, + }); + this._scrollModeResizeIcon.set_style(` + margin: 13px; + color: rgb(255, 255, 255); + box-shadow: 0 0 40px 40px rgba(0,0,0,0.7); + `); + this._scrollModeSourceIcon = new St.Icon({ + icon_name: 'media-skip-forward-symbolic', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + x_expand: true, + y_expand: true, + opacity: SCROLL_ICON_OPACITY, + style_class: 'icon-dropshadow', + scale_x: 0.5, + scale_y: 0.5, + }); + this._scrollModeSourceIcon.set_style(` + margin: 13px; + color: rgb(255, 255, 255); + box-shadow: 0 0 40px 40px rgba(0,0,0,0.7); + `); + this._scrollModeBin.set_child(this._scrollModeResizeIcon); + this.add_child(this._scrollModeBin); + this._scrollModeBin.opacity = 0; + } + + _showWindowPreview(update = false, dontDestroy = false) { + if (this._winPreview && !dontDestroy) { + this._destroyWindowPreview(); + this._previewCreationTime = 0; + this._closeButton.opacity = CLOSE_BTN_OPACITY; + if (!update) + return; + } + + if (!this._winPreview) { + this._winPreview = new AltTab.CyclerHighlight(); + global.window_group.add_actor(this._winPreview); + [this._winPreview._xPointer, this._winPreview._yPointer] = global.get_pointer(); + } + + if (!update) { + this._winPreview.opacity = 0; + this._winPreview.ease({ + opacity: 255, + duration: 70, + mode: Clutter.AnimationMode.LINEAR, + /* onComplete: () => { + this._closeButton.opacity = 50; + },*/ + }); + + this.ease({ + opacity: Math.min(50, this._customOpacity), + duration: 70, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => { + }, + }); + } else { + this._winPreview.opacity = 255; + } + this._winPreview.window = this.w; + this._winPreview._window = this.w; + global.window_group.set_child_above_sibling(this._winPreview, null); + } + + _destroyWindowPreview() { + if (this._winPreview) { + this._winPreview.ease({ + opacity: 0, + duration: 100, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => { + this._winPreview.destroy(); + this._winPreview = null; + this.opacity = this._customOpacity; + }, + }); + } + } +}); diff --git a/extensions/vertical-workspaces/lib/windowAttentionHandler.js b/extensions/44/vertical-workspaces/lib/windowAttentionHandler.js similarity index 52% rename from extensions/vertical-workspaces/lib/windowAttentionHandler.js rename to extensions/44/vertical-workspaces/lib/windowAttentionHandler.js index 10703c2..a3db986 100644 --- a/extensions/vertical-workspaces/lib/windowAttentionHandler.js +++ b/extensions/44/vertical-workspaces/lib/windowAttentionHandler.js @@ -11,47 +11,69 @@ 'use strict'; const Main = imports.ui.main; -const WindowAttentionHandler = imports.ui.windowAttentionHandler; const MessageTray = imports.ui.messageTray; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; +const WindowAttentionHandler = imports.ui.windowAttentionHandler; +let Me; let opt; -let _firstRun = false; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('winAttentionHandlerModule', true); - reset = reset || !moduleEnabled; +var WindowAttentionHandlerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - if (_firstRun && reset) - return; - - _firstRun = false; - if (reset) { - reset = true; - _updateConnections(reset); - opt = null; - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - _updateConnections(); -} + cleanGlobals() { + Me = null; + opt = null; + } -function _updateConnections(reset) { - global.display.disconnectObject(Main.windowAttentionHandler); + update(reset) { + this.moduleEnabled = opt.get('windowAttentionHandlerModule'); + const conflict = false; - const handlerFnc = reset - ? Main.windowAttentionHandler._onWindowDemandsAttention - : WindowAttentionHandlerCommon._onWindowDemandsAttention; + reset = reset || !this.moduleEnabled || conflict; - global.display.connectObject( - 'window-demands-attention', handlerFnc.bind(Main.windowAttentionHandler), - 'window-marked-urgent', handlerFnc.bind(Main.windowAttentionHandler), - Main.windowAttentionHandler); -} + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WindowAttentionHandlerModule - Keeping untouched'); + } + + _activateModule() { + this._updateConnections(); + console.debug(' WindowAttentionHandlerModule - Activated'); + } + + _disableModule() { + const reset = true; + this._updateConnections(reset); + + console.debug(' WindowAttentionHandlerModule - Disabled'); + } + + _updateConnections(reset) { + global.display.disconnectObject(Main.windowAttentionHandler); + + const handlerFnc = reset + ? Main.windowAttentionHandler._onWindowDemandsAttention + : WindowAttentionHandlerCommon._onWindowDemandsAttention; + + global.display.connectObject( + 'window-demands-attention', handlerFnc.bind(Main.windowAttentionHandler), + 'window-marked-urgent', handlerFnc.bind(Main.windowAttentionHandler), + Main.windowAttentionHandler); + } +}; const WindowAttentionHandlerCommon = { _onWindowDemandsAttention(display, window) { diff --git a/extensions/vertical-workspaces/lib/windowManager.js b/extensions/44/vertical-workspaces/lib/windowManager.js similarity index 55% copy from extensions/vertical-workspaces/lib/windowManager.js copy to extensions/44/vertical-workspaces/lib/windowManager.js index 2d46b0b..0cae6aa 100644 --- a/extensions/vertical-workspaces/lib/windowManager.js +++ b/extensions/44/vertical-workspaces/lib/windowManager.js @@ -10,81 +10,101 @@ 'use strict'; -const { GObject, Clutter, Meta } = imports.gi; +const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; const Main = imports.ui.main; const WindowManager = imports.ui.windowManager; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; -let _overrides; - -const MINIMIZE_WINDOW_ANIMATION_TIME = WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME; -const MINIMIZE_WINDOW_ANIMATION_MODE = WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE; +let Me; let opt; -let _firstRun = true; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('windowManagerModule', true); - reset = reset || !moduleEnabled; +var WindowManagerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // don't even touch this module if disabled - if (_firstRun && reset) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; - _firstRun = false; + this._originalMinimizeSigId = 0; + this._minimizeSigId = 0; + this._originalUnminimizeSigId = 0; + this._unminimizeSigId = 0; + } - if (_overrides) - _overrides.removeAll(); - - - _replaceMinimizeFunction(reset); - - - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; } - _overrides = new _Util.Overrides(); + update(reset) { + this.moduleEnabled = opt.get('windowManagerModule'); + const conflict = false; - _overrides.addOverride('WindowManager', WindowManager.WindowManager.prototype, WindowManagerCommon); -} + reset = reset || !this.moduleEnabled || conflict; -// ------------- Fix and adapt minimize/unminimize animations -------------------------------------- - -let _originalMinimizeSigId; -let _minimizeSigId; -let _originalUnminimizeSigId; -let _unminimizeSigId; - -function _replaceMinimizeFunction(reset = false) { - if (reset) { - Main.wm._shellwm.disconnect(_minimizeSigId); - _minimizeSigId = 0; - Main.wm._shellwm.unblock_signal_handler(_originalMinimizeSigId); - _originalMinimizeSigId = 0; - - Main.wm._shellwm.disconnect(_unminimizeSigId); - _unminimizeSigId = 0; - Main.wm._shellwm.unblock_signal_handler(_originalUnminimizeSigId); - _originalUnminimizeSigId = 0; - } else if (!_minimizeSigId) { - _originalMinimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'minimize' }); - if (_originalMinimizeSigId) { - Main.wm._shellwm.block_signal_handler(_originalMinimizeSigId); - _minimizeSigId = Main.wm._shellwm.connect('minimize', WindowManagerCommon._minimizeWindow.bind(Main.wm)); - } - - _originalUnminimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'unminimize' }); - if (_originalUnminimizeSigId) { - Main.wm._shellwm.block_signal_handler(_originalUnminimizeSigId); - _unminimizeSigId = Main.wm._shellwm.connect('unminimize', WindowManagerCommon._unminimizeWindow.bind(Main.wm)); + // don't even touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); } + if (reset && this._firstActivation) + console.debug(' WindowManagerModule - Keeping untouched'); } -} + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WindowManager', WindowManager.WindowManager.prototype, WindowManagerCommon); + + if (!this._minimizeSigId) { + this._originalMinimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'minimize' }); + if (this._originalMinimizeSigId) { + Main.wm._shellwm.block_signal_handler(this._originalMinimizeSigId); + this._minimizeSigId = Main.wm._shellwm.connect('minimize', WindowManagerCommon._minimizeWindow.bind(Main.wm)); + } + + this._originalUnminimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'unminimize' }); + if (this._originalUnminimizeSigId) { + Main.wm._shellwm.block_signal_handler(this._originalUnminimizeSigId); + this._unminimizeSigId = Main.wm._shellwm.connect('unminimize', WindowManagerCommon._unminimizeWindow.bind(Main.wm)); + } + } + console.debug(' WindowManagerModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + if (this._minimizeSigId) { + Main.wm._shellwm.disconnect(this._minimizeSigId); + this._minimizeSigId = 0; + } + if (this._originalMinimizeSigId) { + Main.wm._shellwm.unblock_signal_handler(this._originalMinimizeSigId); + this._originalMinimizeSigId = 0; + } + + if (this._unminimizeSigId) { + Main.wm._shellwm.disconnect(this._unminimizeSigId); + this._unminimizeSigId = 0; + } + if (this._originalUnminimizeSigId) { + Main.wm._shellwm.unblock_signal_handler(this._originalUnminimizeSigId); + this._originalUnminimizeSigId = 0; + } + + console.debug(' WindowManagerModule - Disabled'); + } +}; // fix for mainstream bug - fullscreen windows should minimize using opacity transition // but its being applied directly on window actor and that doesn't work @@ -109,8 +129,8 @@ const WindowManagerCommon = { /* if (actor.meta_window.is_monitor_sized()) { actor.get_first_child().ease({ opacity: 0, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._minimizeWindowDone(shellwm, actor), }); } else { */ @@ -140,8 +160,8 @@ const WindowManagerCommon = { scale_y: yScale, x: xDest, y: yDest, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._minimizeWindowDone(shellwm, actor), }); // } @@ -176,8 +196,8 @@ const WindowManagerCommon = { actor.set_scale(1.0, 1.0); actor.ease({ opacity: 255, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._unminimizeWindowDone(shellwm, actor), }); } else { */ @@ -208,8 +228,8 @@ const WindowManagerCommon = { scale_y: 1, x: xDest, y: yDest, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._unminimizeWindowDone(shellwm, actor), }); // } diff --git a/extensions/44/vertical-workspaces/lib/windowPreview.js b/extensions/44/vertical-workspaces/lib/windowPreview.js new file mode 100644 index 0000000..2766138 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/windowPreview.js @@ -0,0 +1,633 @@ +/** + * V-Shell (Vertical Workspaces) + * windowPreview.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Clutter = imports.gi.Clutter; +const Atk = imports.gi.Atk; +const GLib = imports.gi.GLib; +const Graphene = imports.gi.Graphene; +const Meta = imports.gi.Meta; +const Pango = imports.gi.Pango; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const DND = imports.ui.dnd; +const Main = imports.ui.main; +const OverviewControls = imports.ui.overviewControls; +const WindowPreview = imports.ui.windowPreview; + +let Me; +let opt; + +const WINDOW_SCALE_TIME = 200; +const WINDOW_ACTIVE_SIZE_INC = 5; +const WINDOW_OVERLAY_FADE_TIME = 200; +const WINDOW_DND_SIZE = 256; +const DRAGGING_WINDOW_OPACITY = 100; + +const ControlsState = OverviewControls.ControlsState; + +var WindowPreviewModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('windowPreviewModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WindowPreviewModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WindowPreview', WindowPreview.WindowPreview.prototype, WindowPreviewCommon); + // A shorter timeout allows user to quickly cancel the selection by leaving the preview with the mouse pointer + if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW) + WindowPreview.WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 150; + console.debug(' WindowPreviewModule - Activated'); + } + + _disableModule() { + // If WindowPreview._init was injected by another extension (like Burn My Windows) + // which enables/disables before V-Shell + // don't restore the original if it's not injected, + // because it would restore injected _init and recursion would freeze GS when extensions are enabled again. + // This can happen when all extension re-enabled, not only when screen is locked/unlocked + // If _init doesn't include "fn.apply(this, args)" when reset === true, some extension already restored the original + const skipReset = WindowPreview.WindowPreview.prototype._init.toString().includes('fn.apply(this, args)'); + if (this._overrides && skipReset) { + // skip restoring original _init() + this._overrides['_init'] = null; + } + + if (this._overrides) + this._overrides.removeAll(); + + this._overrides = null; + + console.debug(' WindowPreviewModule - Disabled'); + } +}; + +const WindowPreviewCommon = { + _init(metaWindow, workspace, overviewAdjustment) { + this.metaWindow = metaWindow; + this.metaWindow._delegate = this; + this._windowActor = metaWindow.get_compositor_private(); + this._workspace = workspace; + this._overviewAdjustment = overviewAdjustment; + + const ICON_SIZE = opt.WIN_PREVIEW_ICON_SIZE; + const ICON_OVERLAP = 0.7; + + Shell.WindowPreview.prototype._init.bind(this)({ + reactive: true, + can_focus: true, + accessible_role: Atk.Role.PUSH_BUTTON, + offscreen_redirect: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY, + }); + + const windowContainer = new Clutter.Actor({ + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + }); + this.window_container = windowContainer; + + windowContainer.connect('notify::scale-x', + () => this._adjustOverlayOffsets()); + // gjs currently can't handle setting an actors layout manager during + // the initialization of the actor if that layout manager keeps track + // of its container, so set the layout manager after creating the + // container + windowContainer.layout_manager = new Shell.WindowPreviewLayout(); + this.add_child(windowContainer); + + this._addWindow(metaWindow); + + this._delegate = this; + + this._stackAbove = null; + + this._cachedBoundingBox = { + x: windowContainer.layout_manager.bounding_box.x1, + y: windowContainer.layout_manager.bounding_box.y1, + width: windowContainer.layout_manager.bounding_box.get_width(), + height: windowContainer.layout_manager.bounding_box.get_height(), + }; + + windowContainer.layout_manager.connect( + 'notify::bounding-box', layout => { + this._cachedBoundingBox = { + x: layout.bounding_box.x1, + y: layout.bounding_box.y1, + width: layout.bounding_box.get_width(), + height: layout.bounding_box.get_height(), + }; + + // A bounding box of 0x0 means all windows were removed + if (layout.bounding_box.get_area() > 0) + this.emit('size-changed'); + }); + + this._windowActor.connectObject('destroy', () => this.destroy(), this); + + this._updateAttachedDialogs(); + + let clickAction = new Clutter.ClickAction(); + clickAction.connect('clicked', act => { + const button = act.get_button(); + if (button === Clutter.BUTTON_SECONDARY) { + if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 1) { + this._closeWinAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 2) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 3 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } + } else if (button === Clutter.BUTTON_MIDDLE) { + if (opt.WIN_PREVIEW_MID_BTN_ACTION === 1) { + this._closeWinAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_MID_BTN_ACTION === 2) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_MID_BTN_ACTION === 3 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } + } + return this._activate(); + }); + + + if (this._onLongPress) { + clickAction.connect('long-press', this._onLongPress.bind(this)); + } else { + clickAction.connect('long-press', (action, actor, state) => { + if (state === Clutter.LongPressState.ACTIVATE) + this.showOverlay(true); + return true; + }); + } + + this.connect('destroy', this._onDestroy.bind(this)); + + this._draggable = DND.makeDraggable(this, { + restoreOnSuccess: true, + manualMode: !!this._onLongPress, + dragActorMaxSize: WINDOW_DND_SIZE, + dragActorOpacity: DRAGGING_WINDOW_OPACITY, + }); + + // _draggable.addClickAction is new in GS45 + if (this._draggable.addClickAction) + this._draggable.addClickAction(clickAction); + else + this.add_action(clickAction); + + this._draggable.connect('drag-begin', this._onDragBegin.bind(this)); + this._draggable.connect('drag-cancelled', this._onDragCancelled.bind(this)); + this._draggable.connect('drag-end', this._onDragEnd.bind(this)); + this.inDrag = false; + + this._selected = false; + this._overlayEnabled = true; + this._overlayShown = false; + this._closeRequested = false; + this._idleHideOverlayId = 0; + + const tracker = Shell.WindowTracker.get_default(); + const app = tracker.get_window_app(this.metaWindow); + this._icon = app.create_icon_texture(ICON_SIZE); + this._icon.add_style_class_name('icon-dropshadow'); + this._icon.set({ + reactive: true, + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + }); + this._icon.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.POSITION, + })); + this._icon.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + factor: 0.5, + })); + this._icon.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: ICON_OVERLAP }), + factor: 1, + })); + + if (opt.WINDOW_ICON_CLICK_ACTION) { + const iconClickAction = new Clutter.ClickAction(); + iconClickAction.connect('clicked', act => { + if (act.get_button() === Clutter.BUTTON_PRIMARY) { + if (opt.WINDOW_ICON_CLICK_ACTION === 1) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WINDOW_ICON_CLICK_ACTION === 2 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } + } /* else if (act.get_button() === Clutter.BUTTON_SECONDARY) { + return Clutter.EVENT_STOP; + }*/ + return Clutter.EVENT_PROPAGATE; + }); + this._icon.add_action(iconClickAction); + } + const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); + this._title = new St.Label({ + visible: false, + style_class: 'window-caption', + text: this._getCaption(), + reactive: true, + }); + this._title.clutter_text.single_line_mode = true; + this._title.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.X, + })); + + let offset; + if (opt.WIN_TITLES_POSITION < 2) { + // we cannot get proper title height before it gets to the stage, so 35 is estimated height + spacing + offset = -scaleFactor * (ICON_SIZE * ICON_OVERLAP + 35); + } else { + offset = scaleFactor * (ICON_SIZE * (1 - ICON_OVERLAP) + 4); + } + this._title.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.Y, + offset, + })); + this._title.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + factor: 0.5, + })); + this._title.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: 0 }), + factor: 1, + })); + this._title.clutter_text.ellipsize = Pango.EllipsizeMode.END; + this.label_actor = this._title; + this.metaWindow.connectObject( + 'notify::title', () => (this._title.text = this._getCaption()), + this); + + const layout = Meta.prefs_get_button_layout(); + this._closeButtonSide = + layout.left_buttons.includes(Meta.ButtonFunction.CLOSE) + ? St.Side.LEFT : St.Side.RIGHT; + if (Me.shellVersion < 43) { + this._closeButton = new St.Button({ + visible: false, + style_class: 'window-close', + child: new St.Icon({ icon_name: 'preview-close-symbolic' }), + }); + } else { + this._closeButton = new St.Button({ + visible: false, + style_class: 'window-close', + icon_name: 'preview-close-symbolic', + }); + } + this._closeButton.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.POSITION, + })); + this._closeButton.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + pivot_point: new Graphene.Point({ x: 0.5, y: -1 }), + factor: this._closeButtonSide === St.Side.LEFT ? 0 : 1, + })); + this._closeButton.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: 0.5 }), + factor: 0, + })); + this._closeButton.connect('clicked', () => this._deleteAll()); + + this.add_child(this._title); + this.add_child(this._icon); + this.add_child(this._closeButton); + + this._overviewAdjustment.connectObject( + 'notify::value', () => this._updateIconScale(), this); + this._updateIconScale(); + + this.connect('notify::realized', () => { + if (!this.realized) + return; + + this._title.ensure_style(); + this._icon.ensure_style(); + }); + + if (ICON_SIZE < 22) { + // disable app icon + this._icon.hide(); + } else { + this._updateIconScale(); + } + + + + // if window is created while the overview is shown, icon and title should be visible immediately + if (Main.overview._overview._controls._stateAdjustment.value < 1) { + this._icon.scale_x = 0; + this._icon.scale_y = 0; + this._title.opacity = 0; + } + + if (opt.ALWAYS_SHOW_WIN_TITLES) + this._title.show(); + + if (opt.OVERVIEW_MODE === 1) { + // spread windows on hover + this._wsStateConId = this.connect('enter-event', () => { + // don't spread windows if user don't use pointer device at this moment + if (global.get_pointer()[0] === opt.showingPointerX || Main.overview._overview._controls._stateAdjustment.value < 1) + return; + + opt.WORKSPACE_MODE = 1; + const view = this._workspace.get_parent(); + view.exposeWindows(this._workspace.metaWorkspace.index()); + this.disconnect(this._wsStateConId); + }); + } + + if (opt.OVERVIEW_MODE) { + // show window icon and title on ws windows spread + this._stateAdjustmentSigId = this._workspace.stateAdjustment.connect('notify::value', this._updateIconScale.bind(this)); + } + + const metaWin = this.metaWindow; + if (opt.DASH_ISOLATE_WS && !metaWin._wsChangedConId) { + metaWin._wsChangedConId = metaWin.connect('workspace-changed', + () => Main.overview.dash._queueRedisplay()); + } else if (!opt.DASH_ISOLATE_WS && metaWin._wsChangedConId) { + metaWin.disconnect(metaWin._wsChangedConId); + } + }, + + _closeWinAction() { + this.hide(); + this._deleteAll(); + }, + + _removeLaters() { + // this action cancels long-press event and the 'long-press-cancel' event is used by the Shell to actually initiate DnD + // so the dnd initiation needs to be removed + if (this._longPressLater) { + if (Meta.later_remove) + Meta.later_remove(this._longPressLater); + else + global.compositor.get_laters().remove(this._longPressLater); + delete this._longPressLater; + } + }, + + _searchAppWindowsAction() { + this._removeLaters(); + const tracker = Shell.WindowTracker.get_default(); + const appName = tracker.get_window_app(this.metaWindow).get_name(); + Me.Util.activateSearchProvider(`${Me.WSP_PREFIX} ${appName}`); + }, + + _updateIconScale() { + let { currentState, initialState, finalState } = + this._overviewAdjustment.getStateTransitionParams(); + + // Current state - 0 - HIDDEN, 1 - WINDOW_PICKER, 2 - APP_GRID + const primaryMonitor = this.metaWindow.get_monitor() === global.display.get_primary_monitor(); + + const visible = + (initialState > ControlsState.HIDDEN || finalState > ControlsState.HIDDEN) && + !(finalState === ControlsState.APP_GRID && opt.WS_ANIMATION && primaryMonitor); + + let scale = 0; + if (visible) + scale = currentState >= 1 ? 1 : currentState % 1; + + if (!primaryMonitor && opt.WORKSPACE_MODE && + ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || + (initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER)) + ) + scale = 1; + else if (!primaryMonitor && opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE) + scale = 0; + /* } else if (primaryMonitor && ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || + initialState === ControlsState.APP_GRID && finalState === ControlsState.HIDDEN)) {*/ + else if (primaryMonitor && currentState > ControlsState.WINDOW_PICKER) + scale = 0; + + // in static workspace mode show icon and title on windows expose + if (opt.OVERVIEW_MODE) { + if (currentState === 1) + scale = opt.WORKSPACE_MODE; + else if (finalState === 1 || (finalState === 0 && !opt.WORKSPACE_MODE)) + return; + } + + if (!opt.WS_ANIMATION && (Main.overview._overview.controls._searchController.searchActive || + ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || + (initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER))) + ) + return; + + // if titles are in 'always show' mode, we need to add transition between visible/invisible state + // but the transition is quite expensive, + // showing the titles at the end of the transition is good enough and workspace preview transition is much smoother + if (scale === 1) { + this._icon.set({ + scale_x: 1, + scale_y: 1, + }); + this._title.ease({ + duration: 100, + opacity: 255, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } else { + this._title.opacity = 0; + this._icon.set({ + scale_x: scale, + scale_y: scale, + }); + } + }, + + showOverlay(animate) { + if (!this._overlayEnabled) + return; + + if (this._overlayShown) + return; + + this._overlayShown = true; + if (opt.WIN_TITLES_POSITION === 2) + this._restack(); + + // If we're supposed to animate and an animation in our direction + // is already happening, let that one continue + const ongoingTransition = this._title.get_transition('opacity'); + if (animate && + ongoingTransition && + ongoingTransition.get_interval().peek_final_value() === 255) + return; + + const toShow = this._windowCanClose() && opt.SHOW_CLOSE_BUTTON + ? [this._closeButton] + : []; + + if (!opt.ALWAYS_SHOW_WIN_TITLES) + toShow.push(this._title); + + + toShow.forEach(a => { + a.opacity = 0; + a.show(); + a.ease({ + opacity: 255, + duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + }); + + const [width, height] = this.window_container.get_size(); + const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); + const activeExtraSize = WINDOW_ACTIVE_SIZE_INC * 2 * scaleFactor; + const origSize = Math.max(width, height); + const scale = (origSize + activeExtraSize) / origSize; + + this.window_container.ease({ + scale_x: scale, + scale_y: scale, + duration: animate ? WINDOW_SCALE_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + this.emit('show-chrome'); + }, + + hideOverlay(animate) { + if (!this._overlayShown) + return; + this._overlayShown = false; + + if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW && Main.overview._overview.controls._stateAdjustment.value < 1) + this._activateSelected = true; + + + if (opt.WIN_TITLES_POSITION === 2) + this._restack(); + + // If we're supposed to animate and an animation in our direction + // is already happening, let that one continue + const ongoingTransition = this._title.get_transition('opacity'); + if (animate && + ongoingTransition && + ongoingTransition.get_interval().peek_final_value() === 0) + return; + + const toHide = [this._closeButton]; + + if (!opt.ALWAYS_SHOW_WIN_TITLES) + toHide.push(this._title); + + toHide.forEach(a => { + a.opacity = 255; + a.ease({ + opacity: 0, + duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => a.hide(), + }); + }); + + if (this.window_container) { + this.window_container.ease({ + scale_x: 1, + scale_y: 1, + duration: animate ? WINDOW_SCALE_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + }, + + _onDestroy() { + // workaround for upstream bug - hideOverlay is called after windowPreview is destroyed, from the leave event callback + // hiding the preview now avoids firing the post-mortem leave event + this.hide(); + if (this._activateSelected) + this._activate(); + + this.metaWindow._delegate = null; + this._delegate = null; + + if (this._longPressLater) { + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, this._longPressLater); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, this._longPressLater); + delete this._longPressLater; + } + + if (this._idleHideOverlayId > 0) { + GLib.source_remove(this._idleHideOverlayId); + this._idleHideOverlayId = 0; + } + + if (this.inDrag) { + this.emit('drag-end'); + this.inDrag = false; + } + + if (this._stateAdjustmentSigId) + this._workspace.stateAdjustment.disconnect(this._stateAdjustmentSigId); + }, +}; diff --git a/extensions/vertical-workspaces/lib/windowSearchProvider.js b/extensions/44/vertical-workspaces/lib/windowSearchProvider.js similarity index 54% copy from extensions/vertical-workspaces/lib/windowSearchProvider.js copy to extensions/44/vertical-workspaces/lib/windowSearchProvider.js index 5f90784..b82f365 100644 --- a/extensions/vertical-workspaces/lib/windowSearchProvider.js +++ b/extensions/44/vertical-workspaces/lib/windowSearchProvider.js @@ -9,30 +9,23 @@ 'use strict'; -const { GLib, Gio, Meta, St, Shell } = imports.gi; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const St = imports.gi.St; const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; -const _Util = Me.imports.lib.util; +let Me; +let opt; // gettext -const _ = Settings._; - -const shellVersion = Settings.shellVersion; - -const ModifierType = imports.gi.Clutter.ModifierType; - -let windowSearchProvider; -let _enableTimeoutId = 0; +let _; // prefix helps to eliminate results from other search providers // so it needs to be something less common // needs to be accessible from vw module -var prefix = 'wq//'; - -let opt; +const PREFIX = 'wq//'; const Action = { NONE: 0, @@ -42,66 +35,77 @@ const Action = { MOVE_ALL_TO_WS: 4, }; -function getOverviewSearchResult() { - return Main.overview._overview.controls._searchController._searchResults; -} +var WindowSearchProviderModule = class { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - if (!reset && opt.WINDOW_SEARCH_PROVIDER_ENABLED && !windowSearchProvider) { - enable(); - } else if (reset || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) { - disable(); + this._firstActivation = true; + this.moduleEnabled = false; + // export for other modules + + this._windowSearchProvider = null; + this._enableTimeoutId = 0; + } + + cleanGlobals() { + Me = null; opt = null; + _ = null; } -} -function enable() { - // delay because Fedora had problem to register a new provider soon after Shell restarts - _enableTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 2000, - () => { - if (!windowSearchProvider) { - windowSearchProvider = new WindowSearchProvider(opt); - getOverviewSearchResult()._registerProvider( - windowSearchProvider - ); - } - _enableTimeoutId = 0; - return GLib.SOURCE_REMOVE; + update(reset) { + this.moduleEnabled = opt.get('windowSearchProviderModule'); + + reset = reset || !this.moduleEnabled; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); } - ); -} + if (reset && this._firstActivation) + console.debug(' WindowSearchProviderModule - Keeping untouched'); + } -function disable() { - if (windowSearchProvider) { - getOverviewSearchResult()._unregisterProvider( - windowSearchProvider + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._windowSearchProvider) { + this._windowSearchProvider = new WindowSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._windowSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } ); - windowSearchProvider = null; + console.debug(' WindowSearchProviderModule - Activated'); } - if (_enableTimeoutId) { - GLib.source_remove(_enableTimeoutId); - _enableTimeoutId = 0; + + _disableModule() { + if (this._windowSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._windowSearchProvider); + this._windowSearchProvider = null; + } + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } + + console.debug(' WindowSearchProviderModule - Disabled'); } -} -function makeResult(window, i) { - const app = Shell.WindowTracker.get_default().get_window_app(window); - const appName = app ? app.get_name() : 'Unknown'; - const windowTitle = window.get_title(); - const wsIndex = window.get_workspace().index(); - - return { - 'id': i, - // convert all accented chars to their basic form and lower case for search - 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(), - appName, - windowTitle, - window, - }; -} + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +}; const closeSelectedRegex = /^\/x!$/; const closeAllResultsRegex = /^\/xa!$/; @@ -110,15 +114,22 @@ const moveAllToWsRegex = /^\/ma[0-9]+$/; const WindowSearchProvider = class WindowSearchProvider { constructor() { - this.id = `open-windows@${Me.metadata.uuid}`; - this.appInfo = Gio.AppInfo.create_from_commandline('true', _('Open Windows'), null); - this.appInfo.get_description = () => _('List of open windows'); - this.appInfo.get_name = () => _('Open Windows'); - this.appInfo.get_id = () => this.id; - this.appInfo.get_icon = () => Gio.icon_new_for_string('focus-windows-symbolic'); - this.appInfo.should_show = () => true; + this.id = 'open-windows'; + const appSystem = Shell.AppSystem.get_default(); + // use arbitrary app to get complete appInfo object + let appInfo = appSystem.lookup_app('com.matjakeman.ExtensionManager.desktop')?.get_app_info(); + if (!appInfo) + appInfo = appSystem.lookup_app('org.gnome.Extensions.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('true', _('Open Windows'), null); + appInfo.get_description = () => _('Search open windows'); + appInfo.get_name = () => _('Open Windows'); + appInfo.get_id = () => this.id; + appInfo.get_icon = () => Gio.icon_new_for_string('focus-windows-symbolic'); + appInfo.should_show = () => true; - this.canLaunchSearch = true; + this.appInfo = appInfo; + this.canLaunchSearch = false; this.isRemoteProvider = false; this.action = 0; @@ -128,7 +139,7 @@ const WindowSearchProvider = class WindowSearchProvider { // do not modify original terms let termsCopy = [...terms]; // search for terms without prefix - termsCopy[0] = termsCopy[0].replace(prefix, ''); + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); /* if (gOptions.get('searchWindowsCommands')) { this.action = 0; @@ -167,9 +178,9 @@ const WindowSearchProvider = class WindowSearchProvider { let m; for (let key in candidates) { if (opt.SEARCH_FUZZY) - m = _Util.fuzzyMatch(term, candidates[key].name); + m = Me.Util.fuzzyMatch(term, candidates[key].name); else - m = _Util.strictMatch(term, candidates[key].name); + m = Me.Util.strictMatch(term, candidates[key].name); if (m !== -1) results.push({ weight: m, id: key }); @@ -178,7 +189,19 @@ const WindowSearchProvider = class WindowSearchProvider { results.sort((a, b) => a.weight > b.weight); const currentWs = global.workspace_manager.get_active_workspace_index(); // prefer current workspace - results.sort((a, b) => (this.windows[a.id].window.get_workspace().index() !== currentWs) && (this.windows[b.id].window.get_workspace().index() === currentWs)); + switch (opt.WINDOW_SEARCH_ORDER) { + case 1: // MRU - current ws first + results.sort((a, b) => (this.windows[a.id].window.get_workspace().index() !== currentWs) && (this.windows[b.id].window.get_workspace().index() === currentWs)); + break; + case 2: // MRU - by workspace + results.sort((a, b) => this.windows[a.id].window.get_workspace().index() > this.windows[b.id].window.get_workspace().index()); + break; + case 3: // Stable sequence - by workspace + results.sort((a, b) => this.windows[a.id].window.get_stable_sequence() > this.windows[b.id].window.get_stable_sequence()); + results.sort((a, b) => this.windows[a.id].window.get_workspace().index() > this.windows[b.id].window.get_workspace().index()); + break; + } + results.sort((a, b) => (_terms !== ' ') && (a.weight > 0 && b.weight === 0)); this.resultIds = results.map(item => item.id); @@ -187,7 +210,7 @@ const WindowSearchProvider = class WindowSearchProvider { getResultMetas(resultIds, callback = null) { const metas = resultIds.map(id => this.getResultMeta(id)); - if (shellVersion >= 43) + if (Me.shellVersion >= 43) return new Promise(resolve => resolve(metas)); else callback(metas); @@ -210,12 +233,29 @@ const WindowSearchProvider = class WindowSearchProvider { }; } + makeResult(window, i) { + const app = Shell.WindowTracker.get_default().get_window_app(window); + const appName = app ? app.get_name() : 'Unknown'; + const windowTitle = window.get_title(); + const wsIndex = window.get_workspace().index(); + + return { + 'id': i, + // convert all accented chars to their basic form and lower case for search + 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(), + appName, + windowTitle, + window, + }; + } + launchSearch(/* terms, timeStamp*/) { + } activateResult(resultId/* , terms, timeStamp*/) { - const isCtrlPressed = _Util.isCtrlPressed(); - const isShiftPressed = _Util.isShiftPressed(); + const isCtrlPressed = Me.Util.isCtrlPressed(); + const isShiftPressed = Me.Util.isShiftPressed(); this.action = 0; this.targetWs = 0; @@ -276,15 +316,16 @@ const WindowSearchProvider = class WindowSearchProvider { this.windows = windows = {}; global.display.get_tab_list(Meta.TabList.NORMAL, null).filter(w => w.get_workspace() !== null).map( (v, i) => { - windows[`${i}-${v.get_id()}`] = makeResult(v, `${i}-${v.get_id()}`); + windows[`${i}-${v.get_id()}`] = this.makeResult(v, `${i}-${v.get_id()}`); return windows[`${i}-${v.get_id()}`]; } ); - if (shellVersion >= 43) + if (Me.shellVersion >= 43) return new Promise(resolve => resolve(this._getResultSet(terms))); else callback(this._getResultSet(terms)); + return null; } @@ -293,13 +334,15 @@ const WindowSearchProvider = class WindowSearchProvider { return results; } - getSubsearchResultSet(previousResults, terms, callback/* , cancellable*/) { - // if we return previous results, quick typers get non-actual results - callback(this._getResultSet(terms)); + getSubsearchResultSet(previousResults, terms, callback) { + if (Me.shellVersion < 43) { + this.getSubsearchResultSet42(terms, callback); + return null; + } + return this.getInitialResultSet(terms); } - /* createResultObject(resultMeta) { - const app = Shell.WindowTracker.get_default().get_window_app(resultMeta.id); - return new AppIcon(app); - }*/ + getSubsearchResultSet42(terms, callback) { + callback(this._getResultSet(terms)); + } }; diff --git a/extensions/vertical-workspaces/lib/workspace.js b/extensions/44/vertical-workspaces/lib/workspace.js similarity index 75% rename from extensions/vertical-workspaces/lib/workspace.js rename to extensions/44/vertical-workspaces/lib/workspace.js index 3b61a6d..be60403 100644 --- a/extensions/vertical-workspaces/lib/workspace.js +++ b/extensions/44/vertical-workspaces/lib/workspace.js @@ -10,51 +10,68 @@ 'use strict'; -const { St, Graphene } = imports.gi; +const Graphene = imports.gi.Graphene; +const St = imports.gi.St; const Main = imports.ui.main; -const Util = imports.misc.util; const Workspace = imports.ui.workspace; +const Util = imports.misc.util; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; - -let _overrides; +let Me; let opt; -let _firstRun = true; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('workspaceModule', true); - reset = reset || !moduleEnabled; +var WorkspaceModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - - if (reset) { - Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95; - _overrides = null; - opt = null; - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - _overrides = new _Util.Overrides(); + cleanGlobals() { + Me = null; + opt = null; + } - _overrides.addOverride('WorkspaceBackground', Workspace.WorkspaceBackground.prototype, WorkspaceBackground); + update(reset) { + this.moduleEnabled = opt.get('workspaceModule'); + const conflict = false; - // fix overlay base for Vertical Workspaces - _overrides.addOverride('WorkspaceLayout', Workspace.WorkspaceLayout.prototype, WorkspaceLayout); -} + reset = reset || !this.moduleEnabled || conflict; + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WorkspaceBackground', Workspace.WorkspaceBackground.prototype, WorkspaceBackground); + + // fix overlay base for Vertical Workspaces + this._overrides.addOverride('WorkspaceLayout', Workspace.WorkspaceLayout.prototype, WorkspaceLayout); + console.debug(' WorkspaceModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + Workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95; + + console.debug(' WorkspaceModule - Disabled'); + } +}; // workaround for upstream bug (that is not that invisible in default shell) // smaller window cannot be scaled below 0.95 (WINDOW_PREVIEW_MAXIMUM_SCALE) diff --git a/extensions/44/vertical-workspaces/lib/workspaceAnimation.js b/extensions/44/vertical-workspaces/lib/workspaceAnimation.js new file mode 100644 index 0000000..d790895 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/workspaceAnimation.js @@ -0,0 +1,206 @@ +/** + * V-Shell (Vertical Workspaces) + * workspacesAnimation.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Main = imports.ui.main; +const WorkspaceAnimation = imports.ui.workspaceAnimation; +const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; + +let Me; +let opt; + +var WorkspaceAnimationModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + // first reference to constant defined using const in other module returns undefined, the MonitorGroup const will remain empty and unused + this.dummy = WorkspaceAnimation.MonitorGroup; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._origBaseDistance = null; + this._wsAnimationSwipeBeginId = 0; + this._wsAnimationSwipeUpdateId = 0; + this._wsAnimationSwipeEndId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('workspaceAnimationModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceAnimationModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + if (opt.STATIC_WS_SWITCHER_BG) { + this._overrideMonitorGroupProperty(); + this._overrides.addOverride('WorkspaceAnimationMonitorGroup', WorkspaceAnimation.MonitorGroup.prototype, MonitorGroup); + } + + this._connectWsAnimationSwipeTracker(); + console.debug(' WorkspaceAnimationModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + const reset = true; + this._connectWsAnimationSwipeTracker(reset); + this._overrideMonitorGroupProperty(reset); + console.debug(' WorkspaceAnimationModule - Disabled'); + } + + _overrideMonitorGroupProperty(reset = false) { + if (!this._origBaseDistance) + this._origBaseDistance = Object.getOwnPropertyDescriptor(WorkspaceAnimation.MonitorGroup.prototype, 'baseDistance').get; + + let getter; + if (reset) { + if (this._origBaseDistance) + getter = { get: this._origBaseDistance }; + } else { + getter = { + get() { + // const spacing = 100 * imports.gi.St.ThemeContext.get_for_stage(global.stage).scale_factor; + const spacing = 0; + if (global.workspace_manager.layout_rows === -1) + return this._monitor.height + spacing + (opt.PANEL_MODE ? Main.panel.height : 0); // compensation for hidden panel + else + return this._monitor.width + spacing; + }, + }; + } + + if (getter) + Object.defineProperty(WorkspaceAnimation.MonitorGroup.prototype, 'baseDistance', getter); + } + + _connectWsAnimationSwipeTracker(reset = false) { + if (reset) { + if (this._wsAnimationSwipeBeginId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeBeginId); + this._wsAnimationSwipeBeginId = 0; + } + if (this._wsAnimationSwipeEndId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeEndId); + this._wsAnimationSwipeEndId = 0; + } + } else if (!this._wsAnimationSwipeBeginId) { + // display ws switcher popup when gesture begins and connect progress + this._wsAnimationSwipeBeginId = Main.wm._workspaceAnimation._swipeTracker.connect('begin', () => this._connectWsAnimationProgress(true)); + // we want to be sure that popup with the final ws index show up when gesture ends + this._wsAnimationSwipeEndId = Main.wm._workspaceAnimation._swipeTracker.connect('end', (tracker, duration, endProgress) => this._connectWsAnimationProgress(false, endProgress)); + } + } + + _connectWsAnimationProgress(connect, endProgress = null) { + if (Main.overview.visible) + return; + + if (connect && !this._wsAnimationSwipeUpdateId) { + this._wsAnimationSwipeUpdateId = Main.wm._workspaceAnimation._swipeTracker.connect('update', (tracker, progress) => this._showWsSwitcherPopup(progress)); + } else if (!connect && this._wsAnimationSwipeUpdateId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeUpdateId); + this._wsAnimationSwipeUpdateId = 0; + this._showWsSwitcherPopup(Math.round(endProgress)); + } + } + + _showWsSwitcherPopup(progress) { + if (Main.overview.visible) + return; + + const wsIndex = Math.round(progress); + if (Main.wm._workspaceSwitcherPopup === null) { + Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); + Main.wm._workspaceSwitcherPopup.connect('destroy', () => { + Main.wm._workspaceSwitcherPopup = null; + }); + } + + Main.wm._workspaceSwitcherPopup.display(wsIndex); + } +}; + +const MonitorGroup = { + // injection to _init() + after__init() { + // we have two options to implement static bg feature + // one is adding background to monitorGroup + // but this one has disadvantage - sticky windows will be always on top of animated windows + // which is bad for conky, for example, that window should be always below + /* this._bgManager = new Background.BackgroundManager({ + container: this, + monitorIndex: this._monitor.index, + controlPosition: false, + });*/ + + // the second option is to make background of the monitorGroup transparent so the real desktop content will stay visible, + // hide windows that should be animated and keep only sticky windows + // we can keep certain sticky windows bellow and also extensions like DING (icons on desktop) will stay visible + this.set_style('background-color: transparent;'); + // stickyGroup holds the Always on Visible Workspace windows to keep them static and above other windows during animation + const stickyGroup = this.get_children()[1]; + stickyGroup._windowRecords.forEach(r => { + const metaWin = r.windowActor.metaWindow; + // conky is sticky but should never get above other windows during ws animation + // so we hide it from the overlay group, we will see the original if not covered by other windows + if (metaWin.wm_class === 'conky') + r.clone.opacity = 0; + }); + this._hiddenWindows = []; + // remove (hide) background wallpaper from the animation, we will see the original one + this._workspaceGroups.forEach(w => { + w._background.opacity = 0; + }); + // hide (scale to 0) all non-sticky windows, their clones will be animated + global.get_window_actors().forEach(actor => { + const metaWin = actor.metaWindow; + if (metaWin?.get_monitor() === this._monitor.index && + !(metaWin?.wm_class === 'conky' && metaWin?.is_on_all_workspaces()) && + !(metaWin?.wm_class === 'Gjs' && metaWin?.is_on_all_workspaces())) { // DING extension uses window with Gjs class + // hide original window. we cannot use opacity since it also affects clones. + // scaling them to 0 works well + actor.scale_x = 0; + this._hiddenWindows.push(actor); + } + }); + + // restore all hidden windows at the end of animation + // todo - actors removed during transition need to be removed from the list to avoid access to destroyed actor + this.connect('destroy', () => { + this._hiddenWindows.forEach(actor => { + actor.scale_x = 1; + }); + }); + }, +}; diff --git a/extensions/44/vertical-workspaces/lib/workspaceSwitcherPopup.js b/extensions/44/vertical-workspaces/lib/workspaceSwitcherPopup.js new file mode 100644 index 0000000..5bde6d0 --- /dev/null +++ b/extensions/44/vertical-workspaces/lib/workspaceSwitcherPopup.js @@ -0,0 +1,109 @@ +/** + * V-Shell (Vertical Workspaces) + * workspacesSwitcherPopup.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +const Main = imports.ui.main; +const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; + +let Me; +let opt; + +var WorkspaceSwitcherPopupModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('workspaceSwitcherPopupModule'); + const conflict = Me.Util.getEnabledExtensions('workspace-switcher-manager').length || + Me.Util.getEnabledExtensions('WsSwitcherPopupManager').length; + + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "WorkspaceSwitcherPopup" module disabled due to potential conflict with another extension`); + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceSwitcherPopupModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WorkspaceSwitcherPopup', WorkspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype, WorkspaceSwitcherPopupCommon); + console.debug(' WorkspaceSwitcherPopupModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); + } +}; + +const WorkspaceSwitcherPopupCommon = { + // injection to _init() + after__init() { + if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL + this._list.vertical = true; + } + this._list.set_style('margin: 0;'); + if (this.get_constraints()[0]) + this.remove_constraint(this.get_constraints()[0]); + }, + + // injection to display() + after_display() { + if (opt.WS_SW_POPUP_MODE) + this._setPopupPosition(); + else + this.opacity = 0; + }, + + _setPopupPosition() { + let workArea; + if (opt.WS_SW_POPUP_MODE === 1) { + // workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);*/ + workArea = global.display.get_monitor_geometry(Main.layoutManager.primaryIndex); + } else { + // workArea = Main.layoutManager.getWorkAreaForMonitor(global.display.get_current_monitor()); + workArea = global.display.get_monitor_geometry(global.display.get_current_monitor()); + } + + let [, natHeight] = this.get_preferred_height(global.screen_width); + let [, natWidth] = this.get_preferred_width(natHeight); + let h = opt.WS_SW_POPUP_H_POSITION; + let v = opt.WS_SW_POPUP_V_POSITION; + this.x = workArea.x + Math.floor((workArea.width - natWidth) * h); + this.y = workArea.y + Math.floor((workArea.height - natHeight) * v); + this.set_position(this.x, this.y); + }, +}; diff --git a/extensions/vertical-workspaces/lib/workspaceThumbnail.js b/extensions/44/vertical-workspaces/lib/workspaceThumbnail.js similarity index 87% copy from extensions/vertical-workspaces/lib/workspaceThumbnail.js copy to extensions/44/vertical-workspaces/lib/workspaceThumbnail.js index d0bc206..844c224 100644 --- a/extensions/vertical-workspaces/lib/workspaceThumbnail.js +++ b/extensions/44/vertical-workspaces/lib/workspaceThumbnail.js @@ -10,56 +10,90 @@ 'use strict'; -const { GLib, Clutter, Graphene, Meta, Shell, St } = imports.gi; +const Clutter = imports.gi.Clutter; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const St = imports.gi.St; + +const AppDisplay = imports.ui.appDisplay; +const Background = imports.ui.background; const DND = imports.ui.dnd; const Main = imports.ui.main; -const Background = imports.ui.background; +const OverviewControls = imports.ui.overviewControls; const WorkspaceThumbnail = imports.ui.workspaceThumbnail; + +let Me; +let opt; + const ThumbnailState = WorkspaceThumbnail.ThumbnailState; - -const ControlsState = imports.ui.overviewControls.ControlsState; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -// gettext -const _ = Me.imports.lib.settings._; - -const _Util = Me.imports.lib.util; -const shellVersion = _Util.shellVersion; - -let _overrides; +const ControlsState = OverviewControls.ControlsState; const WORKSPACE_CUT_SIZE = 10; -const _originalMaxThumbnailScale = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE; -let opt = null; +var WorkspaceThumbnailModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -function update(reset = false) { - if (_overrides) - _overrides.removeAll(); - - - if (reset) { - if (_originalMaxThumbnailScale) - WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = _originalMaxThumbnailScale; - _overrides = null; - opt = null; - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - opt = Me.imports.lib.settings.opt; - _overrides = new _Util.Overrides(); + cleanGlobals() { + Me = null; + opt = null; + } - // don't limit max thumbnail scale for other clients than overview, for example AATWS. - WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = 1; + update(reset) { + this.moduleEnabled = true; + const conflict = false; - _overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon); - _overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon); + reset = reset || !this.moduleEnabled || conflict; - // replacing opt.ORIENTATION local constant with boxOrientation internal variable allows external customers such as the AATWS extension to control the box orientation. - Main.overview._overview.controls._thumbnailsBox._boxOrientation = opt.ORIENTATION; -} + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceThumbnailModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + if (!this._originalMaxThumbnailScale) + this._originalMaxThumbnailScale = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE; + + // don't limit max thumbnail scale for other clients than overview, specifically AATWS. + WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = 1; + // WorkspaceThumbnail.ThumbnailsBox._MAX_THUMBNAIL_SCALE = 1; + + this._overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon); + this._overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon); + + // replacing opt.ORIENTATION local constant with boxOrientation internal variable allows external customers such as the AATWS extension to control the box orientation. + Main.overview._overview.controls._thumbnailsBox._boxOrientation = opt.ORIENTATION; + + console.debug(' WorkspaceThumbnailModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = this._originalMaxThumbnailScale; + + console.debug(' WorkspaceThumbnailModule - Disabled'); + } +}; const WorkspaceThumbnailCommon = { // injection to _init() @@ -70,6 +104,8 @@ const WorkspaceThumbnailCommon = { // unless border is removed if (opt.SHOW_WS_TMB_BG) this.add_style_class_name('ws-tmb-labeled'); + else + this.add_style_class_name('ws-tmb-transparent'); // add workspace thumbnails labels if enabled if (opt.SHOW_WST_LABELS) { // 0 - disable @@ -77,7 +113,7 @@ const WorkspaceThumbnailCommon = { const wsIndex = this.metaWorkspace.index(); let label = `${wsIndex + 1}`; if (opt.SHOW_WST_LABELS === 2) { // 2 - index + workspace name - const settings = ExtensionUtils.getSettings('org.gnome.desktop.wm.preferences'); + const settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.wm.preferences' }); const wsLabels = settings.get_strv('workspace-names'); if (wsLabels.length > wsIndex && wsLabels[wsIndex]) label += `: ${wsLabels[wsIndex]}`; @@ -129,7 +165,9 @@ const WorkspaceThumbnailCommon = { } }); this._nWindowsConId = this.metaWorkspace.connect('notify::n-windows', () => { - // wait for new information + if (this._updateLabelTimeout) + return; + // wait for new data this._updateLabelTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => { const newLabel = getLabel(); this._wsLabel.text = newLabel; @@ -168,7 +206,7 @@ const WorkspaceThumbnailCommon = { closeButton.opacity = 255; if (!Meta.prefs_get_dynamic_workspaces() || (Meta.prefs_get_dynamic_workspaces() && global.workspace_manager.get_n_workspaces() - 1 !== this.metaWorkspace.index())) { // color the button red if ready to react on clicks - if (opt.CLOSE_WS_BUTTON_MODE < 3 || (opt.CLOSE_WS_BUTTON_MODE === 3 && _Util.isCtrlPressed())) + if (opt.CLOSE_WS_BUTTON_MODE < 3 || (opt.CLOSE_WS_BUTTON_MODE === 3 && Me.Util.isCtrlPressed())) closeButton.add_style_class_name('workspace-close-button-hover'); } }); @@ -220,12 +258,6 @@ const WorkspaceThumbnailCommon = { this._viewport.set_child_below_sibling(this._bgManager.backgroundActor, null); - this.connect('destroy', () => { - if (this._bgManager) - this._bgManager.destroy(); - this._bgManager = null; - }); - // full brightness of the thumbnail bg draws unnecessary attention // there is a grey bg under the wallpaper this._bgManager.backgroundActor.opacity = 220; @@ -256,7 +288,7 @@ const WorkspaceThumbnailCommon = { this._lastCloseClickTime = Date.now(); return; } - } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !_Util.isCtrlPressed()) { + } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !Me.Util.isCtrlPressed()) { return; } @@ -335,7 +367,7 @@ const WorkspaceThumbnailCommon = { if (!source.app && source.shellWorkspaceLaunch) return DND.DragMotionResult.COPY_DROP; - if (source instanceof imports.ui.appDisplay.FolderIcon) + if (source instanceof AppDisplay.FolderIcon) return DND.DragMotionResult.COPY_DROP; @@ -369,8 +401,8 @@ const WorkspaceThumbnailCommon = { timestamp: time, }); return true; - } else if (source instanceof imports.ui.appDisplay.FolderIcon) { - if (shellVersion >= 44) { + } else if (source instanceof AppDisplay.FolderIcon) { + if (Me.shellVersion >= 44) { for (let app of source.view._apps) { // const app = Shell.AppSystem.get_default().lookup_app(id); app.open_new_window(this.metaWorkspace.index()); @@ -419,7 +451,7 @@ const ThumbnailsBoxCommon = { if (!source.metaWindow && (!source.app || !source.app.can_open_new_window()) && (source.app || !source.shellWorkspaceLaunch) && - !(source instanceof imports.ui.appDisplay.FolderIcon)) + !(source instanceof AppDisplay.FolderIcon)) return false; @@ -448,8 +480,8 @@ const ThumbnailsBoxCommon = { workspace: newWorkspaceIndex, timestamp: time, }); - } else if (source instanceof imports.ui.appDisplay.FolderIcon) { - if (shellVersion >= 44) { + } else if (source instanceof AppDisplay.FolderIcon) { + if (Me.shellVersion >= 44) { for (let app of source.view._apps) { // const app = Shell.AppSystem.get_default().lookup_app(id); app.open_new_window(newWorkspaceIndex); @@ -495,7 +527,7 @@ const ThumbnailsBoxCommon = { if (!source.metaWindow && (!source.app || !source.app.can_open_new_window()) && (source.app || !source.shellWorkspaceLaunch) && - source !== Main.xdndHandler && !(source instanceof imports.ui.appDisplay.FolderIcon)) + source !== Main.xdndHandler && !(source instanceof AppDisplay.FolderIcon)) return DND.DragMotionResult.CONTINUE; const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; @@ -551,18 +583,18 @@ const ThumbnailsBoxCommon = { return ThumbnailsBoxHorizontal._withinWorkspace.bind(this)(...args); }, - get_preferred_custom_width(...args) { + vfunc_get_preferred_width(...args) { if (this._boxOrientation) - return ThumbnailsBoxVertical.get_preferred_custom_width.bind(this)(...args); + return ThumbnailsBoxVertical.vfunc_get_preferred_width.bind(this)(...args); else - return ThumbnailsBoxHorizontal.get_preferred_custom_width.bind(this)(...args); + return ThumbnailsBoxHorizontal.vfunc_get_preferred_width.bind(this)(...args); }, - get_preferred_custom_height(...args) { + vfunc_get_preferred_height(...args) { if (this._boxOrientation) - return ThumbnailsBoxVertical.get_preferred_custom_height.bind(this)(...args); + return ThumbnailsBoxVertical.vfunc_get_preferred_height.bind(this)(...args); else - return ThumbnailsBoxHorizontal.get_preferred_custom_height.bind(this)(...args); + return ThumbnailsBoxHorizontal.vfunc_get_preferred_height.bind(this)(...args); }, vfunc_allocate(...args) { @@ -632,14 +664,9 @@ const ThumbnailsBoxVertical = { return y > workspaceY1 && y <= workspaceY2; }, - // vfunc_get_preferred_width: function(forHeight) { - // override of this vfunc doesn't work for some reason (tested on Ubuntu and Fedora), it's not reachable - get_preferred_custom_width(forHeight) { - if (!this.visible) - return [0, 0]; - - if (forHeight === -1) - return this.get_preferred_custom_height(forHeight); + vfunc_get_preferred_width(forHeight) { + if (forHeight < 10) + return [this._porthole.width, this._porthole.width]; let themeNode = this.get_theme_node(); @@ -652,19 +679,14 @@ const ThumbnailsBoxVertical = { const avail = forHeight - totalSpacing; let scale = (avail / nWorkspaces) / this._porthole.height; - // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE); const width = Math.round(this._porthole.width * scale); return themeNode.adjust_preferred_height(width, width); }, - get_preferred_custom_height(_forWidth) { - if (!this.visible) - return [0, 0]; - - // Note that for getPreferredHeight/Width we cheat a bit and skip propagating - // the size request to our children because we know how big they are and know - // that the actors aren't depending on the virtual functions being called. + vfunc_get_preferred_height(forWidth) { + if (forWidth < 10) + return [0, this._porthole.height]; let themeNode = this.get_theme_node(); let spacing = themeNode.get_length('spacing'); @@ -674,15 +696,14 @@ const ThumbnailsBoxVertical = { let totalSpacing = (nWorkspaces - 3) * spacing; const ratio = this._porthole.width / this._porthole.height; - const tmbHeight = themeNode.adjust_for_width(_forWidth) / ratio; + const tmbHeight = themeNode.adjust_for_width(forWidth) / ratio; const naturalheight = this._thumbnails.reduce((accumulator, thumbnail/* , index*/) => { const progress = 1 - thumbnail.collapse_fraction; const height = tmbHeight * progress; return accumulator + height; }, 0); - - return themeNode.adjust_preferred_width(totalSpacing, naturalheight); + return themeNode.adjust_preferred_width(totalSpacing, Math.round(naturalheight)); }, // removes extra space (extraWidth in the original function), we need the box as accurate as possible @@ -759,16 +780,11 @@ const ThumbnailsBoxVertical = { this._dropPlaceholder.allocate_preferred_size( ...this._dropPlaceholder.get_position()); - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); - } else { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); - } + const laterFunc = () => this._dropPlaceholder.hide(); + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, laterFunc); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, laterFunc); } let childBox = new Clutter.ActorBox(); @@ -796,16 +812,11 @@ const ThumbnailsBoxVertical = { this._dropPlaceholder.allocate(childBox); - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); - } else { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); - } + const laterFunc = () => this._dropPlaceholder.show(); + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, laterFunc); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, laterFunc); y += placeholderHeight + spacing; } @@ -925,7 +936,10 @@ const ThumbnailsBoxHorizontal = { return x > workspaceX1 && x <= workspaceX2; }, - get_preferred_custom_height(forWidth) { + vfunc_get_preferred_height(forWidth) { + if (forWidth < 10) + return [this._porthole.height, this._porthole.height]; + let themeNode = this.get_theme_node(); forWidth = themeNode.adjust_for_width(forWidth); @@ -937,18 +951,15 @@ const ThumbnailsBoxHorizontal = { const avail = forWidth - totalSpacing; let scale = (avail / nWorkspaces) / this._porthole.width; - // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE); const height = Math.round(this._porthole.height * scale); + return themeNode.adjust_preferred_height(height, height); }, - get_preferred_custom_width(_forHeight) { - // Note that for getPreferredHeight/Width we cheat a bit and skip propagating - // the size request to our children because we know how big they are and know - // that the actors aren't depending on the virtual functions being called. - if (!this.visible) - return [0, 0]; + vfunc_get_preferred_width(forHeight) { + if (forHeight < 10) + return [0, this._porthole.width]; let themeNode = this.get_theme_node(); @@ -959,13 +970,14 @@ const ThumbnailsBoxHorizontal = { const ratio = this._porthole.height / this._porthole.width; - const tmbWidth = themeNode.adjust_for_height(_forHeight) / ratio; + const tmbWidth = themeNode.adjust_for_height(forHeight) / ratio; const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail) => { const progress = 1 - thumbnail.collapse_fraction; const width = tmbWidth * progress; return accumulator + width; }, 0); + return themeNode.adjust_preferred_width(totalSpacing, naturalWidth); }, @@ -1041,16 +1053,11 @@ const ThumbnailsBoxHorizontal = { this._dropPlaceholder.allocate_preferred_size( ...this._dropPlaceholder.get_position()); - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); - } else { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); - } + const laterFunc = () => this._dropPlaceholder.hide(); + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, laterFunc); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, laterFunc); } let childBox = new Clutter.ActorBox(); @@ -1078,16 +1085,11 @@ const ThumbnailsBoxHorizontal = { this._dropPlaceholder.allocate(childBox); - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); - } else { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); - } + const laterFunc = () => this._dropPlaceholder.show(); + if (Meta.later_add) + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, laterFunc); + else + global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, laterFunc); x += placeholderWidth + spacing; } diff --git a/extensions/vertical-workspaces/lib/workspacesView.js b/extensions/44/vertical-workspaces/lib/workspacesView.js similarity index 76% copy from extensions/vertical-workspaces/lib/workspacesView.js copy to extensions/44/vertical-workspaces/lib/workspacesView.js index e3575f1..3df7b22 100644 --- a/extensions/vertical-workspaces/lib/workspacesView.js +++ b/extensions/44/vertical-workspaces/lib/workspacesView.js @@ -10,62 +10,92 @@ 'use strict'; -const { GObject, Clutter, Meta, St } = imports.gi; +const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const St = imports.gi.St; const Main = imports.ui.main; -const Util = imports.misc.util; +const OverviewControls = imports.ui.overviewControls; const WorkspacesView = imports.ui.workspacesView; -// first reference to constant defined using const in other module returns undefined, the SecondaryMonitorDisplay const will remain empty and unused -const SecondaryMonitorDisplay = WorkspacesView.SecondaryMonitorDisplay; -const ControlsState = imports.ui.overviewControls.ControlsState; -const FitMode = imports.ui.workspacesView.FitMode; - -const SIDE_CONTROLS_ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const SEARCH_WINDOWS_PREFIX = Me.imports.lib.windowSearchProvider.prefix; -const SEARCH_RECENT_FILES_PREFIX = Me.imports.lib.recentFilesSearchProvider.prefix; - -const _Util = Me.imports.lib.util; -let _overrides; +const Util = imports.misc.util; +let Me; let opt; +const ControlsState = OverviewControls.ControlsState; +const FitMode = WorkspacesView.FitMode; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - opt.DESKTOP_CUBE_ENABLED = Main.extensionManager._enabledExtensions.includes('desktop-cube@schneegans.github.com'); - const cubeSupported = opt.DESKTOP_CUBE_ENABLED && !opt.ORIENTATION && !opt.OVERVIEW_MODE; +var WorkspacesViewModule = class { + constructor(me) { + // first reference to constant defined using const in other module returns undefined, the SecondaryMonitorDisplay const will remain empty and unused + this.dummy = WorkspacesView.SecondaryMonitorDisplay; - // if desktop cube extension is enabled while V-Shell is loaded, removeAll() would override its code - if (_overrides && !cubeSupported) { - _overrides.removeAll(); - global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1); + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; } + update(reset) { + this.moduleEnabled = true; + const conflict = false; - _overrides = new _Util.Overrides(); + reset = reset || !this.moduleEnabled || conflict; - if (!cubeSupported) - _overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon); - - _overrides.addOverride('WorkspacesDisplay', WorkspacesView.WorkspacesDisplay.prototype, WorkspacesDisplay); - _overrides.addOverride('ExtraWorkspaceView', WorkspacesView.ExtraWorkspaceView.prototype, ExtraWorkspaceView); - - if (opt.ORIENTATION) { - // switch internal workspace orientation in GS - global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1); - _overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayVertical); - } else { - _overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayHorizontal); + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspacesViewModule - Keeping untouched'); } -} + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + const desktopCubeEnabled = Me.Util.getEnabledExtensions('desktop-cube@schneegans.github.com').length; + const desktopCubeConflict = desktopCubeEnabled && !opt.ORIENTATION && !opt.OVERVIEW_MODE; + + if (!desktopCubeConflict) + this._overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon); + + this._overrides.addOverride('WorkspacesDisplay', WorkspacesView.WorkspacesDisplay.prototype, WorkspacesDisplayCommon); + this._overrides.addOverride('ExtraWorkspaceView', WorkspacesView.ExtraWorkspaceView.prototype, ExtraWorkspaceViewCommon); + this._overrides.addOverride('SecondaryMonitorDisplayCommon', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayCommon); + + if (opt.ORIENTATION) { + // switch internal workspace orientation in GS + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1); + this._overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayVertical); + } else { + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1); + this._overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayHorizontal); + } + + console.debug(' WorkspacesViewModule - Activated'); + } + + _disableModule() { + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1); + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' WorkspacesViewModule - Disabled'); + } +}; const WorkspacesViewCommon = { _getFirstFitSingleWorkspaceBox(box, spacing, vertical) { @@ -91,7 +121,6 @@ const WorkspacesViewCommon = { } const fitSingleBox = new Clutter.ActorBox({ x1, y1 }); - fitSingleBox.set_size(workspaceWidth, workspaceHeight); return fitSingleBox; @@ -143,21 +172,7 @@ const WorkspacesViewCommon = { }, _updateVisibility() { - // replaced in _updateWorkspacesState - /* let workspaceManager = global.workspace_manager; - let active = workspaceManager.get_active_workspace_index(); - - const fitMode = this._fitModeAdjustment.value; - const singleFitMode = fitMode === FitMode.SINGLE; - - for (let w = 0; w < this._workspaces.length; w++) { - let workspace = this._workspaces[w]; - - if (this._animating || this._gestureActive || !singleFitMode) - workspace.show(); - else - workspace.visible = Math.abs(w - active) <= opt.NUMBER_OF_VISIBLE_NEIGHBORS; - }*/ + // visibility handles _updateWorkspacesState() }, // disable scaling and hide inactive workspaces @@ -175,27 +190,21 @@ const WorkspacesViewCommon = { const primaryMonitor = Main.layoutManager.primaryMonitor.index; - // define the transition values here to save time in each ws - let scaleX, scaleY; - if (opt.ORIENTATION) { // vertical 1 / horizontal 0 - scaleX = 1; - scaleY = 0.1; - } else { - scaleX = 0.1; - scaleY = 1; - } - const wsScrollProgress = adj.value % 1; const secondaryMonitor = this._monitorIndex !== global.display.get_primary_monitor(); const blockSecondaryAppGrid = opt.OVERVIEW_MODE && currentState > 1; + // Hide inactive workspaces this._workspaces.forEach((w, index) => { if (!(blockSecondaryAppGrid && secondaryMonitor)) w.stateAdjustment.value = workspaceMode; - const distanceToCurrentWorkspace = Math.abs(adj.value - index); + let distance = adj.value - index; + const distanceToCurrentWorkspace = Math.abs(distance); const scaleProgress = 1 - Math.clamp(distanceToCurrentWorkspace, 0, 1); + // const scale = Util.lerp(0.94, 1, scaleProgress); + // w.set_scale(scale, scale); // if we disable workspaces that we can't or don't need to see, transition animations will be noticeably smoother // only the current ws needs to be visible during overview transition animations @@ -210,22 +219,28 @@ const WorkspacesViewCommon = { // after transition from APP_GRID to WINDOW_PICKER state, // adjacent workspaces are hidden and we need them to show up // make them visible during animation can impact smoothness of the animation - // so we show them after the animation finished, scaling animation will make impression that they move in from outside the monitor + // so we show them after the animation finished, move them to their position from outside of the monitor if (!w.visible && distanceToCurrentWorkspace === 1 && initialState === ControlsState.APP_GRID && currentState === ControlsState.WINDOW_PICKER) { - w.scale_x = scaleX; - w.scale_y = scaleY; w.visible = true; + const directionNext = distance > 0; + if (!opt.ORIENTATION) { + const width = w.width * 0.6 * opt.WS_PREVIEW_SCALE; + w.translation_x = directionNext ? -width : width; + } + if (opt.ORIENTATION) { + const height = w.height * 0.6 * opt.WS_PREVIEW_SCALE; + w.translation_y = directionNext ? -height : height; + } + + w.opacity = 10; + w.get_parent().set_child_below_sibling(w, null); w.ease({ - duration: 100, - scale_x: 1, - scale_y: 1, + duration: 300, + translation_x: 0, + translation_y: 0, + opacity: 255, mode: Clutter.AnimationMode.EASE_OUT_QUAD, }); - } else if (!w.visible && distanceToCurrentWorkspace <= opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState === ControlsState.WINDOW_PICKER) { - w.set({ - scale_x: 1, - scale_y: 1, - }); } // force ws preview bg corner radiuses where GS doesn't do it @@ -238,9 +253,40 @@ const WorkspacesViewCommon = { w._background.opacity = 0; }); }, + + exposeWindows(workspaceIndex = null, callback) { + let adjustments = []; + if (workspaceIndex === null) { + this._workspaces.forEach(ws => { + adjustments.push(ws._background._stateAdjustment); + }); + } else { + adjustments.push(this._workspaces[workspaceIndex]._background._stateAdjustment); + } + + adjustments.forEach(adj => { + if (adj.value === 0) { + adj.value = 0; + adj.ease(1, { + duration: 200, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + opt.WORKSPACE_MODE = 1; + if (callback) + callback(); + }, + }); + } + }); + }, +}; + +const SecondaryMonitorDisplayCommon = { + exposeWindows(...args) { + this._workspacesView.exposeWindows(...args); + }, }; -// SecondaryMonitorDisplay Vertical const SecondaryMonitorDisplayVertical = { _getThumbnailParamsForState(state) { @@ -270,24 +316,6 @@ const SecondaryMonitorDisplayVertical = { return { opacity, scale, translationX }; }, - _getThumbnailsWidth(box, spacing) { - if (opt.SEC_WS_TMB_HIDDEN) - return 0; - - const [width, height] = box.get_size(); - const { expandFraction } = this._thumbnails; - const [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(height - 2 * spacing); - let scaledWidth; - if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED) - scaledWidth = ((height - Main.panel.height) * opt.SEC_MAX_THUMBNAIL_SCALE) * (width / height); - else - scaledWidth = width * opt.SEC_MAX_THUMBNAIL_SCALE; - - return Math.min( - thumbnailsWidth * expandFraction, - Math.round(scaledWidth)); - }, - _getWorkspacesBoxForState(state, box, padding, thumbnailsWidth, spacing) { // const { ControlsState } = OverviewControls; const workspaceBox = box.copy(); @@ -303,7 +331,7 @@ const SecondaryMonitorDisplayVertical = { break; yShift = 0; - if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED) { + if (opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible) { if (opt.PANEL_POSITION_TOP) yShift = Main.panel.height; else @@ -341,26 +369,40 @@ const SecondaryMonitorDisplayVertical = { const spacing = themeNode.get_length('spacing') * expandFraction; const padding = Math.round(0.1 * height); - let thumbnailsWidth = this._getThumbnailsWidth(contentBox, spacing); - let [, thumbnailsHeight] = this._thumbnails.get_preferred_custom_height(thumbnailsWidth); - thumbnailsHeight = Math.min(thumbnailsHeight, height - 2 * spacing); - + let thumbnailsWidth = 0; + let thumbnailsHeight = 0; this._thumbnails.visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible) { + const reduceBoxHeight = opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; + + thumbnailsWidth = width * opt.SEC_MAX_THUMBNAIL_SCALE; + + let totalTmbSpacing; + [totalTmbSpacing, thumbnailsHeight] = this._thumbnails.get_preferred_height(thumbnailsWidth); + thumbnailsHeight = Math.round(thumbnailsHeight + totalTmbSpacing); + + const thumbnailsHeightMax = height - spacing - reduceBoxHeight; + + if (thumbnailsHeight > thumbnailsHeightMax) { + thumbnailsHeight = thumbnailsHeightMax; + thumbnailsWidth = Math.round(this._thumbnails.get_preferred_width(thumbnailsHeight)[1]); + } + let wsTmbX; if (opt.SEC_WS_TMB_LEFT) { // left - wsTmbX = Math.round(spacing / 4); + wsTmbX = spacing / 2; this._thumbnails._positionLeft = true; } else { - wsTmbX = Math.round(width - spacing / 4 - thumbnailsWidth); + wsTmbX = width - spacing / 2 - thumbnailsWidth; this._thumbnails._positionLeft = false; } const childBox = new Clutter.ActorBox(); - const availSpace = height - thumbnailsHeight - 2 * spacing; + const availSpace = height - thumbnailsHeight; let wsTmbY = availSpace / 2; - wsTmbY -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbY - spacing; + wsTmbY -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbY; + wsTmbY += opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; childBox.set_origin(Math.round(wsTmbX), Math.round(wsTmbY)); childBox.set_size(thumbnailsWidth, thumbnailsHeight); @@ -390,22 +432,14 @@ const SecondaryMonitorDisplayVertical = { if (opt.OVERVIEW_MODE2) this.set_child_above_sibling(this._thumbnails, null); - const visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible === visible) return; this._thumbnails.show(); + this._thumbnails.visible = visible; this._updateThumbnailParams(); - this._thumbnails.ease_property('expand-fraction', visible ? 1 : 0, { - duration: SIDE_CONTROLS_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - this._thumbnails.visible = visible; - this._thumbnails._indicator.visible = visible; - }, - }); }, _updateThumbnailParams() { @@ -417,7 +451,6 @@ const SecondaryMonitorDisplayVertical = { if (!this._thumbnails._thumbnails.length) this._thumbnails._createThumbnails(); - const { initialState, finalState, progress } = this._overviewAdjustment.getStateTransitionParams(); @@ -474,7 +507,6 @@ const SecondaryMonitorDisplayVertical = { }, }; -// SecondaryMonitorDisplay Horizontal const SecondaryMonitorDisplayHorizontal = { _getThumbnailParamsForState(state) { // const { ControlsState } = OverviewControls; @@ -520,7 +552,7 @@ const SecondaryMonitorDisplayHorizontal = { break; yShift = 0; - if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED) { + if (opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible) { if (opt.PANEL_POSITION_TOP) yShift = Main.panel.height; else @@ -571,24 +603,36 @@ const SecondaryMonitorDisplayHorizontal = { const spacing = themeNode.get_length('spacing') * expandFraction; const padding = Math.round(0.1 * height); - let thumbnailsHeight = this._getThumbnailsHeight(contentBox); - let [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(thumbnailsHeight); - thumbnailsWidth = Math.min(thumbnailsWidth, width - 2 * spacing); - + let thumbnailsWidth = 0; + let thumbnailsHeight = 0; this._thumbnails.visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible) { + const reservedHeight = opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; + + thumbnailsHeight = height * opt.SEC_MAX_THUMBNAIL_SCALE; + + let totalTmbSpacing; + [totalTmbSpacing, thumbnailsWidth] = this._thumbnails.get_preferred_width(thumbnailsHeight); + thumbnailsWidth = Math.round(thumbnailsWidth + totalTmbSpacing); + + const thumbnailsWidthMax = width - spacing; + + if (thumbnailsWidth > thumbnailsWidthMax) { + thumbnailsWidth = thumbnailsWidthMax; + thumbnailsHeight = Math.round(this._thumbnails.get_preferred_height(thumbnailsWidth)[1]); + } + let wsTmbY; if (opt.SEC_WS_TMB_TOP) - wsTmbY = Math.round(spacing / 4); + wsTmbY = spacing / 2 + reservedHeight; else - wsTmbY = Math.round(height - spacing / 4 - thumbnailsHeight); - + wsTmbY = height - spacing / 2 - thumbnailsHeight; const childBox = new Clutter.ActorBox(); - const availSpace = width - thumbnailsWidth - 2 * spacing; + const availSpace = width - thumbnailsWidth; let wsTmbX = availSpace / 2; - wsTmbX -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbX - spacing; + wsTmbX -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbX; childBox.set_origin(Math.round(wsTmbX), Math.round(wsTmbY)); childBox.set_size(thumbnailsWidth, thumbnailsHeight); @@ -625,7 +669,6 @@ const SecondaryMonitorDisplayHorizontal = { if (!this._thumbnails._thumbnails.length) this._thumbnails._createThumbnails(); - const { initialState, finalState, progress } = this._overviewAdjustment.getStateTransitionParams(); @@ -682,7 +725,7 @@ const SecondaryMonitorDisplayHorizontal = { }, }; -const ExtraWorkspaceView = { +const ExtraWorkspaceViewCommon = { _updateWorkspaceMode() { const overviewState = this._overviewAdjustment.value; @@ -701,9 +744,23 @@ const ExtraWorkspaceView = { if (!opt.SHOW_WS_PREVIEW_BG && this._workspace._background.opacity) this._workspace._background.opacity = 0; }, + + exposeWindows() { + const adjustment = this._workspace._background._stateAdjustment; + if (adjustment.value === 0) { + adjustment.value = 0; + adjustment.ease(1, { + duration: 200, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + opt.WORKSPACE_MODE = 1; + }, + }); + } + }, }; -const WorkspacesDisplay = { +const WorkspacesDisplayCommon = { _updateWorkspacesViews() { for (let i = 0; i < this._workspacesViews.length; i++) this._workspacesViews[i].destroy(); @@ -761,8 +818,8 @@ const WorkspacesDisplay = { return Clutter.EVENT_STOP; } - if (_Util.isShiftPressed()) { - let direction = _Util.getScrollDirection(event); + if (Me.Util.isShiftPressed()) { + let direction = Me.Util.getScrollDirection(event); if (direction === null || (Date.now() - this._lastScrollTime) < 150) return Clutter.EVENT_STOP; this._lastScrollTime = Date.now(); @@ -775,11 +832,11 @@ const WorkspacesDisplay = { else direction = 0; - if (direction) { - _Util.reorderWorkspace(direction); + Me.Util.reorderWorkspace(direction); // make all workspaces on primary monitor visible for case the new position is hidden - Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => { + const primaryMonitorIndex = global.display.get_primary_monitor(); + Main.overview._overview._controls._workspacesDisplay._workspacesViews[primaryMonitorIndex]._workspaces.forEach(w => { w.visible = true; }); return Clutter.EVENT_STOP; @@ -806,7 +863,7 @@ const WorkspacesDisplay = { switch (symbol) { case Clutter.KEY_Return: case Clutter.KEY_KP_Enter: - if (_Util.isCtrlPressed()) { + if (Me.Util.isCtrlPressed()) { Main.ctrlAltTabManager._items.forEach(i => { if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i); @@ -836,17 +893,17 @@ const WorkspacesDisplay = { which = workspaceManager.n_workspaces - 1; break; case Clutter.KEY_space: - if (_Util.isCtrlPressed() && _Util.isShiftPressed()) { - _Util.openPreferences(); - } else if (_Util.isAltPressed()) { + if (Me.Util.isCtrlPressed() && Me.Util.isShiftPressed()) { + Me.Util.activateSearchProvider(Me.ESP_PREFIX); + } else if (Me.Util.isAltPressed()) { Main.ctrlAltTabManager._items.forEach(i => { if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i); }); - } else if (opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED && _Util.isCtrlPressed()) { - _Util.activateSearchProvider(SEARCH_RECENT_FILES_PREFIX); - } else if (opt.WINDOW_SEARCH_PROVIDER_ENABLED) { - _Util.activateSearchProvider(SEARCH_WINDOWS_PREFIX); + } else if (opt.get('recentFilesSearchProviderModule') && Me.Util.isCtrlPressed()) { + Me.Util.activateSearchProvider(Me.RFSP_PREFIX); + } else if (opt.get('windowSearchProviderModule')) { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); } return Clutter.EVENT_STOP; @@ -867,16 +924,18 @@ const WorkspacesDisplay = { }); } else if (opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE && state === 1) { // expose windows for OVERVIEW_MODE 1 - const adjustment = this._workspacesViews[0]._workspaces[global.workspace_manager.get_active_workspace().index()]._background._stateAdjustment; - opt.WORKSPACE_MODE = 1; - _Util.exposeWindows(adjustment, true); + const wsIndex = global.workspace_manager.get_active_workspace().index(); + // after expose animation activate keyboard for window selection + const callback = Me.Util.activateKeyboardForWorkspaceView; + this._workspacesViews.forEach( + view => { + view.exposeWindows(wsIndex, callback); + } + ); } else { if (state === 2) return Clutter.EVENT_PROPAGATE; - Main.ctrlAltTabManager._items.forEach(i => { - if (i.sortGroup === 1 && i.name === 'Windows') - Main.ctrlAltTabManager.focusGroup(i); - }); + Me.Util.activateKeyboardForWorkspaceView(); } return Clutter.EVENT_STOP; @@ -895,14 +954,14 @@ const WorkspacesDisplay = { // Otherwise it is a workspace index ws = workspaceManager.get_workspace_by_index(which); - if (_Util.isShiftPressed()) { + if (Me.Util.isShiftPressed()) { let direction; if (which === Meta.MotionDirection.UP || which === Meta.MotionDirection.LEFT) direction = -1; else if (which === Meta.MotionDirection.DOWN || which === Meta.MotionDirection.RIGHT) direction = 1; if (direction) - _Util.reorderWorkspace(direction); + Me.Util.reorderWorkspace(direction); // make all workspaces on primary monitor visible for case the new position is hidden Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => { w.visible = true; diff --git a/extensions/vertical-workspaces/metadata.json b/extensions/44/vertical-workspaces/metadata.json similarity index 76% copy from extensions/vertical-workspaces/metadata.json copy to extensions/44/vertical-workspaces/metadata.json index dbfaa91..fa62b14 100644 --- a/extensions/vertical-workspaces/metadata.json +++ b/extensions/44/vertical-workspaces/metadata.json @@ -7,8 +7,16 @@ "43", "44" ], + "session-modes": [ + "user", + "unlock-dialog" + ], "url": "https://github.com/G-dH/vertical-workspaces", + "donations": { + "buymeacoffee": "georgdh" + }, "gettext-domain": "vertical-workspaces", "settings-schema": "org.gnome.shell.extensions.vertical-workspaces", - "version": 28 + "version-name": "44.10" } + diff --git a/extensions/vertical-workspaces/po/cs.po b/extensions/44/vertical-workspaces/po/cs.po similarity index 100% copy from extensions/vertical-workspaces/po/cs.po copy to extensions/44/vertical-workspaces/po/cs.po diff --git a/extensions/vertical-workspaces/po/vertical-workspaces.pot b/extensions/44/vertical-workspaces/po/nl.po similarity index 56% copy from extensions/vertical-workspaces/po/vertical-workspaces.pot copy to extensions/44/vertical-workspaces/po/nl.po index 44ccddb..2d9613e 100644 --- a/extensions/vertical-workspaces/po/vertical-workspaces.pot +++ b/extensions/44/vertical-workspaces/po/nl.po @@ -3,95 +3,96 @@ # This file is distributed under the same license as the vertical-workspaces package. # FIRST AUTHOR , YEAR. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: vertical-workspaces\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-03-08 12:37+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"PO-Revision-Date: 2023-05-04 14:03+0200\n" +"Last-Translator: Heimen Stoffels \n" +"Language-Team: \n" +"Language: nl\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.2.2\n" #: dash.js:743 msgid "Search Open Windows (Hotkey: Space)" -msgstr "" +msgstr "Geopende vensters doorzoeken (sneltoets: spatiebalk)" #: dash.js:816 msgid "Search Recent Files (Hotkey: Ctrl + Space)" -msgstr "" +msgstr "Onlangs gebruikte bestanden doorzoeken (sneltoets: Ctrl+spatiebalk)" #: windowSearchProvider.js:117 windowSearchProvider.js:119 msgid "Open Windows" -msgstr "" +msgstr "Geopende vensters" #: windowSearchProvider.js:118 msgid "List of open windows" -msgstr "" +msgstr "Een lijst met geopende vensters" #: appDisplay.js:1317 msgid "Force Quit" -msgstr "" +msgstr "Toepassing gedwongen afsluiten" #: appDisplay.js:1335 msgid "Move App to Current Workspace ( Shift + Click )" -msgstr "" +msgstr "Toepassing verplaatsen naar huidig werkblad (Shift+muisklik)" #: prefs.js:47 prefs.js:1465 msgid "Layout" -msgstr "" +msgstr "Indeling" #: prefs.js:53 msgid "Appearance" -msgstr "" +msgstr "Vormgeving" #: prefs.js:59 msgid "Behavior" -msgstr "" +msgstr "Gedrag" #: prefs.js:65 msgid "Misc" -msgstr "" +msgstr "Overig" #: prefs.js:71 msgid "About" -msgstr "" +msgstr "Over" #: prefs.js:119 prefs.js:517 prefs.js:1030 prefs.js:1456 msgid "Dash" -msgstr "" +msgstr "Dash" #: prefs.js:125 msgid "Dash Position" -msgstr "" +msgstr "Dashlocatie" #: prefs.js:131 msgid "Top" -msgstr "" +msgstr "Bovenaan" #: prefs.js:132 msgid "Right" -msgstr "" +msgstr "Rechts" #: prefs.js:133 prefs.js:355 msgid "Bottom" -msgstr "" +msgstr "Onderaan" #: prefs.js:134 msgid "Left" -msgstr "" +msgstr "Links" #: prefs.js:135 prefs.js:393 prefs.js:557 prefs.js:572 prefs.js:587 msgid "Hide" -msgstr "" +msgstr "Verbergen" #: prefs.js:142 msgid "Center Horizontal Dash to Workspace" -msgstr "" +msgstr "Horizontale dash centreren op werkblad" #: prefs.js:143 msgid "" @@ -99,24 +100,29 @@ msgid "" "recalculated relative to the workspace preview instead of the screen. Works " "only with the default Dash." msgstr "" +"Als de dashlocatie boven- of onderaan is, dan wordt de locatie herberekend " +"op basis van de werkbladvoorvertoning in plaats van het scherm. Werkt alleen " +"met de standaarddash." #: prefs.js:160 msgid "Fine Tune Dash Position" -msgstr "" +msgstr "Dashlocatie aanpassen" #: prefs.js:161 msgid "" "Adjusts position of the dock on chosen axis. Works only with the default " "Dash." msgstr "" +"Pas de locatie van het dock op de gekozen as aan. Werkt alleen met de " +"standaarddash." #: prefs.js:170 msgid "Workspace Thumbnails / Orientation" -msgstr "" +msgstr "Werkbladminiaturen/-oriëntatie" #: prefs.js:176 msgid "Thumbnails Position / Workspaces Orientation" -msgstr "" +msgstr "Miniatuurlocatie/Werkbladoriëntatie" #: prefs.js:177 msgid "" @@ -125,42 +131,46 @@ msgid "" "workspace thumbnails, one sets workspaces to the vertical orientation, the " "second one to horizontal." msgstr "" +"De locatie van werkbladminiaturen op het scherm stelt tevens de oriëntatie " +"van werkbladen in op verticaal of horizontaal. U heeft twee mogelijkheden om " +"miniaturen uit te schakelen: u kunt de werkbladen instellen op verticaal of " +"horizontaal." #: prefs.js:183 msgid "Left \t Vertical Orientation" -msgstr "" +msgstr "Links \t Verticale oriëntatie" #: prefs.js:184 msgid "Right \t Vertical Orientation" -msgstr "" +msgstr "Rechts \t Horizontale oriëntatie" #: prefs.js:185 msgid "Hide \t Set Vertical Orientation" -msgstr "" +msgstr "Verbergen \t Verticale oriëntatie instellen" #: prefs.js:186 msgid "Top \t Horizontal Orientation" -msgstr "" +msgstr "Bovenaan \t Horizontale oriëntatie" #: prefs.js:187 msgid "Bottom \t Horizontal Orientation" -msgstr "" +msgstr "Onderaan \t Horizontale oriëntatie" #: prefs.js:188 msgid "Hide \t Set Horizontal Orientation" -msgstr "" +msgstr "Verbergen \t Horizontale oriëntatie instellen" #: prefs.js:204 prefs.js:409 msgid "Fine Tune Workspace Thumbnails Position" -msgstr "" +msgstr "Werkbladminiatuurlocatie instellen" #: prefs.js:205 msgid "Adjusts workspace thumbnails vertical position." -msgstr "" +msgstr "Pas de verticale positie van werkbladminiaturen aan." #: prefs.js:213 msgid "Reserve Full Screen Height/Width for Thumbnails" -msgstr "" +msgstr "Gehele scherm gebruiken voor miniaturen" #: prefs.js:214 msgid "" @@ -168,24 +178,29 @@ msgid "" "the expense of space available for Dash (if the Dash is oriented in a " "different axis)." msgstr "" +"De gehele schermhoogte en -breedte zal worden gebruikt voor " +"werkbladminiaturen. Let op: dit gaat ten koste van de beschikbare dashruimte " +"(als de dash op een andere oriëntatie is ingesteld)." #: prefs.js:231 prefs.js:427 msgid "Workspace Thumbnails Max Scale" -msgstr "" +msgstr "Max. omvang van werkbladminiaturen" #: prefs.js:232 msgid "" "Adjusts maximum size of the workspace thumbnails (% relative to display " "width)." msgstr "" +"Pas de maximale omvang van werkbladminiaturen aan (% relatief in verhouding " +"tot de schermbreedte)." #: prefs.js:241 prefs.js:697 msgid "Workspace Preview" -msgstr "" +msgstr "Werkbladvoorvertoning" #: prefs.js:256 msgid "Workspaces Scale" -msgstr "" +msgstr "Werkbladomvang verkleinen" #: prefs.js:257 msgid "" @@ -193,10 +208,12 @@ msgid "" "workspaces on the screen. Default size is calculated to use all available " "space." msgstr "" +"Maak de werkbladvoorvertoningen kleiner om meer werkbladen op het scherm te " +"tonen. De standaardomvang maakt gebruik van alle beschikbare ruimte." #: prefs.js:274 msgid "Workspaces Spacing" -msgstr "" +msgstr "Ruimte tussen werkbladen" #: prefs.js:275 msgid "" @@ -204,14 +221,17 @@ msgid "" "the adjacent workspaces overlap to the current workspace overview. Default " "value should set the adjacent workspaces out of the screen." msgstr "" +"Pas de ruimte tussen werkbladvoorvertoningen aan zodat u zelf kunt bepalen " +"hoeveel werkbladen mogen overlappen. De standaardwaarde ligt buiten het " +"scherm." #: prefs.js:284 prefs.js:712 prefs.js:1072 msgid "App Grid" -msgstr "" +msgstr "Toepassingsrooster" #: prefs.js:290 msgid "Center App Grid" -msgstr "" +msgstr "Toepassingsrooster centreren" #: prefs.js:291 msgid "" @@ -220,137 +240,152 @@ msgid "" "for narrower and small resolution displays, especially if workspace " "thumbnails are bigger." msgstr "" +"Toon het toepassingsrooster op het midden van het scherm in plaats van over " +"de gehele ruimte. Let op: deze optie kan van invloed zijn op de " +"roosteromvang. Op kleine schermen wordt het rooster smaller, vooral als de " +"werkbladminiaturen groter zijn." #: prefs.js:300 msgid "Search View" -msgstr "" +msgstr "Zoekweergave" #: prefs.js:306 msgid "Center Search View" -msgstr "" +msgstr "Zoekweergave centreren" #: prefs.js:307 msgid "" "Search view will be centered to the display instead of the available space." msgstr "" +"Toon de zoekweergave op het midden van het scherm in plaats van over de " +"gehele ruimte." #: prefs.js:315 msgid "Always Show Search Entry" -msgstr "" +msgstr "Zoekvak altijd tonen" #: prefs.js:316 msgid "" "If disabled, the search entry field will be hidden when not in use, so the " "workspace preview and app grid may take up more space." msgstr "" +"Schakel uit om het zoekvak te verbergen als het niet wordt gebruikt, zodat " +"er meer ruimte voor de werkbladvoorvertoningen en het toepassingsrooster is." #: prefs.js:333 msgid "Search Results Width" -msgstr "" +msgstr "Breedte van zoekresultaten" #: prefs.js:334 msgid "" "Adjusts maximum width of search results view (% relative to default). This " "allows you to fit more (or less) app icons into the app search result." msgstr "" +"Pas de breedte van zoekresultaten aan (% relatief ten opzichte van " +"standaard). Hierdoor kunt u meer of minder pictogrammen in de resultaten " +"tonen." #: prefs.js:342 prefs.js:1483 msgid "Panel" -msgstr "" +msgstr "Bovenbalk" #: prefs.js:348 msgid "Main Panel Position" -msgstr "" +msgstr "Bovenbalklocatie" #: prefs.js:349 msgid "Allows you to place the main panel at the bottom of your monitor." -msgstr "" +msgstr "Verplaats de bovenbalk naar de onderkant van het scherm." #: prefs.js:354 msgid "Top (Default)" -msgstr "" +msgstr "Bovenaan (standaard)" #: prefs.js:362 msgid "Main Panel Visibility" -msgstr "" +msgstr "Zichtbaarheid van bovenbalk" #: prefs.js:363 msgid "Main panel can be visible always, only in the overview or never." -msgstr "" +msgstr "Toon de bovenbalk altijd, alleen op het overzicht of nooit." #: prefs.js:368 msgid "Always Visible (Default)" -msgstr "" +msgstr "Altijd tonen (standaard)" #: prefs.js:369 msgid "Overview Only" -msgstr "" +msgstr "Alleen op het overzicht" #: prefs.js:370 msgid "Always Hidden" -msgstr "" +msgstr "Altijd verbergen" #: prefs.js:378 msgid "Secondary Monitors" -msgstr "" +msgstr "Meerdere beeldschermen" #: prefs.js:384 msgid "Workspace Thumbnails Position" -msgstr "" +msgstr "Werkbladminiatuurlocaties" #: prefs.js:385 msgid "" "Allows you to place workspace thumbnails of secondary monitors on the " "opposite side than on the primary monitor." -msgstr "" +msgstr "Plaats werkbladminiaturen op andere beeldschermen dan het hoofdscherm." #: prefs.js:390 msgid "Same as Primary" -msgstr "" +msgstr "Gelijk aan hoofdscherm" #: prefs.js:391 msgid "Left / Top" -msgstr "" +msgstr "Links/Bovenaan" #: prefs.js:392 msgid "Right / Bottom" -msgstr "" +msgstr "Rechts/Onderaan" #: prefs.js:410 msgid "Adjusts secondary monitors workspace thumbnails vertical position." -msgstr "" +msgstr "Pas de verticale miniatuurpositie op andere beeldschermen aan." #: prefs.js:428 msgid "" "Adjusts maximum size of the workspace thumbnails (% relative to display " "width) for secondary monitors." msgstr "" +"Pas de maximale omvang van werkbladminiaturen op andere beeldschermen aan (% " +"relatief in verhouding tot de schermbreedte)." #: prefs.js:445 msgid "Workspace Preview Scale" -msgstr "" +msgstr "Werkbladvoorvertoningen verkleinen" #: prefs.js:446 msgid "Scales down workspace previews on secondary monitors." -msgstr "" +msgstr "Verklein de voorvertoningen op andere beeldschermen." #: prefs.js:454 msgid "Shift Workspace Preview by Panel Height" -msgstr "" +msgstr "Werkbladvoorvertoningen aanpassen aan bovenbalkhoogte" #: prefs.js:455 msgid "" "This option can help align overview of the secondary monitor with the " "primary monitor." msgstr "" +"Met deze optie kunt u het overzicht op een ander beeldscherm gelijkstellen " +"aan die op het hoofdscherm." #: prefs.js:464 msgid "Workspace Switcher Popup" -msgstr "" +msgstr "Werkbladwisselaarpop-up" #: prefs.js:480 msgid "Horizontal Position (% from left)" -msgstr "" +msgstr "Horizontale positie (in % vanaf linkerzijde)" #: prefs.js:481 msgid "" @@ -359,74 +394,80 @@ msgid "" "you want more control over the popup, try Workspace Switcher Manager " "extension." msgstr "" +"Deze pop-up wordt getoond als u van werkblad wisselt met behulp van een " +"sneltoets of gebaar buiten het overzicht om. U kunt de pop-up uitschakelen " +"op het tabblad ‘Gedrag’. Als u de pop-up naar eigen hand wilt zetten, " +"probeer dan de Workspace Switcher Manager-uitbreiding." #: prefs.js:499 msgid "Vertical Position (% from top)" -msgstr "" +msgstr "Verticale positie (in % vanaf bovenzijde)" #: prefs.js:523 msgid "Dash Max Icon Size" -msgstr "" +msgstr "Max. pictogramgrootte in dash" #: prefs.js:524 msgid "Maximum size of Dash icons in pixels. Works only with default Dash." msgstr "" +"De maximale pictogramgrootte, in pixels. Let op: dit werkt alleen in " +"combinatie met de standaarddash." #: prefs.js:529 prefs.js:732 prefs.js:752 prefs.js:853 msgid "128" -msgstr "" +msgstr "128" #: prefs.js:530 prefs.js:733 prefs.js:753 prefs.js:854 msgid "112" -msgstr "" +msgstr "112" #: prefs.js:531 prefs.js:734 prefs.js:754 prefs.js:855 msgid "96" -msgstr "" +msgstr "96" #: prefs.js:532 prefs.js:735 prefs.js:755 prefs.js:856 msgid "80" -msgstr "" +msgstr "80" #: prefs.js:533 prefs.js:676 prefs.js:736 prefs.js:756 prefs.js:857 msgid "64" -msgstr "" +msgstr "64" #: prefs.js:534 prefs.js:677 prefs.js:737 prefs.js:757 prefs.js:858 msgid "48" -msgstr "" +msgstr "48" #: prefs.js:535 prefs.js:678 prefs.js:758 prefs.js:859 msgid "32" -msgstr "" +msgstr "32" #: prefs.js:536 msgid "24" -msgstr "" +msgstr "24" #: prefs.js:537 msgid "16" -msgstr "" +msgstr "16" #: prefs.js:551 msgid "Show Apps Icon Position" -msgstr "" +msgstr "Locatie van ‘Toepassingen tonen’" #: prefs.js:552 msgid "Sets the position of the \"Show Applications\" icon in the Dash." -msgstr "" +msgstr "Stel de locatie van het pictogram ‘Toepassingen tonen’ in de dash in." #: prefs.js:558 prefs.js:573 prefs.js:588 msgid "Start" -msgstr "" +msgstr "Begin" #: prefs.js:559 prefs.js:574 prefs.js:589 msgid "End" -msgstr "" +msgstr "Einde" #: prefs.js:566 msgid "Open Windows Icon Position" -msgstr "" +msgstr "Locatie van ‘Geopende vensters’" #: prefs.js:567 msgid "" @@ -436,10 +477,15 @@ msgid "" "secondary mouse button click on the Show Apps Icon, or the Space hotkey to " "access this feature." msgstr "" +"Deze optie voegt een pictogram ‘Geopende vensters doorzoeken’ toe aan de " +"dash (als de vensterzoekdienst is ingeschakeld op het tabblad ‘Gedrag’), " +"zodat u direct kunt zoeken. Als u dit pictogram uitschakelt, dan kunt u deze " +"functie alsnog aanroepen met behulp van een rechtermuisklik op ‘Toepassingen " +"tonen’ of door te drukken op de spatiebalk." #: prefs.js:581 msgid "Recent Files Icon Position" -msgstr "" +msgstr "Locatie van ‘Onlangs gebruikte bestanden’" #: prefs.js:582 msgid "" @@ -448,164 +494,178 @@ msgid "" "recent files search provider results. Even if you disable this icon, you can " "use Ctrl + Space hotkey to access this feature." msgstr "" +"Deze optie voegt een pictogram ‘Onlangs gebruikte bestanden doorzoeken’ toe " +"aan de dash (als de bestandszoekdienst is ingeschakeld op het tabblad " +"‘Gedrag’), zodat u direct kunt zoeken. Als u dit pictogram uitschakelt, dan " +"kunt u deze functie alsnog aanroepen met behulp van de sneltoets Ctrl + " +"spatiebalk." #: prefs.js:597 msgid "Dash Background Opacity" -msgstr "" +msgstr "Doorzichtigheid van dashachtergrond" #: prefs.js:598 msgid "Adjusts the opacity of the dash background." -msgstr "" +msgstr "Pas de doorzichtigheid van de dashachtergrond aan." #: prefs.js:614 msgid "Dash Background Radius" -msgstr "" +msgstr "Dash-achtergrondstraal" #: prefs.js:615 msgid "" "Adjusts the border radius of the dash background in pixels. 0 means default " "value." msgstr "" +"Pas de straal van de dashachtergrond aan (in pixels). 0 = standaardwaarde." #: prefs.js:623 prefs.js:1050 msgid "Workspace Thumbnails" -msgstr "" +msgstr "Werkbladminiaturen" #: prefs.js:629 msgid "Show Workspace Thumbnail Labels" -msgstr "" +msgstr "Werkbladminiatuurlabels tonen" #: prefs.js:630 msgid "" "Each workspace thumbnail can show its index and name (if defined in the " "system settings) or name/title of its most recently used app/window." msgstr "" +"Toon de namen van werkbladen (indien ingesteld in de systeemvoorkeuren) of " +"naam+titel van onlangs gebruikte toepassing/venster." #: prefs.js:635 prefs.js:680 prefs.js:1001 prefs.js:1062 prefs.js:1260 #: prefs.js:1278 prefs.js:1296 prefs.js:1331 msgid "Disable" -msgstr "" +msgstr "Uitschakelen" #: prefs.js:636 msgid "Index" -msgstr "" +msgstr "Index" #: prefs.js:637 msgid "Index + WS Name" -msgstr "" +msgstr "Index + Werkbladnaam" #: prefs.js:638 msgid "Index + App Name" -msgstr "" +msgstr "Index + Toepassingsnaam" #: prefs.js:639 msgid "Index + Window Title" -msgstr "" +msgstr "Index + Venstertitel" #: prefs.js:646 msgid "Show WS Thumbnail Label on Hover" -msgstr "" +msgstr "Miniatuurlabels tonen na aanwijzen" #: prefs.js:647 msgid "Show label only when the mouse pointer hovers over a thumbnail" -msgstr "" +msgstr "Toon de labels alleen als u met de aanwijzer een miniatuur aanwijst." #: prefs.js:655 msgid "Show Wallpaper in Workspace Thumbnails" -msgstr "" +msgstr "Werkbladminiaturen voorzien van achtergrond" #: prefs.js:656 msgid "All workspace thumbnails will include the current desktop background." -msgstr "" +msgstr "Toon de huidige bureaubladachtergrond op werkbladminiaturen." #: prefs.js:664 msgid "Window Preview" -msgstr "" +msgstr "Venstervoorvertoning" #: prefs.js:670 msgid "Window Preview App Icon Size" -msgstr "" +msgstr "Pictogramgrootte in venstervoorvertoning" #: prefs.js:671 msgid "Default size is 64." -msgstr "" +msgstr "Standaard: 64." #: prefs.js:679 msgid "22" -msgstr "" +msgstr "22" #: prefs.js:687 msgid "Always Show Window Titles" -msgstr "" +msgstr "Venstertitels altijd tonen" #: prefs.js:688 msgid "" "All windows on the workspace preview will show their titles, not only the " "one with the mouse pointer." msgstr "" +"Toon venstertitels van alle vensters op de voorvertoning, niet alleen van " +"het venster onder de aanwijzer." #: prefs.js:703 msgid "Show Workspace Preview Background" -msgstr "" +msgstr "Werkbladvoorvertoning voorzien van achtergrond" #: prefs.js:704 msgid "Allows you to hide the scaling background of the workspace preview." -msgstr "" +msgstr "Toon of verberg de achtergrond op de werkbladvoorvertoning." #: prefs.js:718 msgid "Icon Size" -msgstr "" +msgstr "Pictogramgrootte" #: prefs.js:719 msgid "" "Allows to force fixed icon size and bypass the default adaptive algorithm." msgstr "" +"Stel een vaste pictogramgrootte in ter vervanging van de standaard " +"aanpasbare grootte." #: prefs.js:724 prefs.js:751 msgid "Adaptive (Default)" -msgstr "" +msgstr "Aanpasbaar (standaard)" #: prefs.js:725 msgid "256" -msgstr "" +msgstr "256" #: prefs.js:726 msgid "224" -msgstr "" +msgstr "224" #: prefs.js:727 msgid "208" -msgstr "" +msgstr "208" #: prefs.js:728 msgid "192" -msgstr "" +msgstr "192" #: prefs.js:729 msgid "176" -msgstr "" +msgstr "176" #: prefs.js:730 msgid "160" -msgstr "" +msgstr "160" #: prefs.js:731 msgid "144" -msgstr "" +msgstr "144" #: prefs.js:745 msgid "Folder Icon Size" -msgstr "" +msgstr "Pictogramgrootte van mappen" #: prefs.js:746 msgid "" "Allows to disable the default adaptive algorithm and set a fixed size of " "icons inside folders." msgstr "" +"Stel een vaste pictogramgrootte in ter vervanging van de standaard " +"aanpasbare grootte." #: prefs.js:766 msgid "Max App Folder Icon Grid Size" -msgstr "" +msgstr "Max. grootte van mappen op rooster" #: prefs.js:767 msgid "" @@ -613,128 +673,145 @@ msgid "" "content, this option allows you to increase the number to 9 icons if folder " "contains more than 4 or 8 apps. The latter avoids half empty folder icons." msgstr "" +"Elk mappictogram toont maximaal 4 pictogrammen als voorvertoning. Met behulp " +"van deze optie kunt u het aantal verhogen naar maximaal 9 als de map 4 of 8 " +"toepassingen bevat. De laatste optie voorkomt halflege mappictogrammen." #: prefs.js:772 msgid "2x2 (Default)" -msgstr "" +msgstr "2x2 (standaard)" #: prefs.js:773 msgid "3x3 for 5+ apps" -msgstr "" +msgstr "3x3 (5 of meer toepassingen)" #: prefs.js:774 msgid "3x3 for 9+ apps" -msgstr "" +msgstr "3x3 (9 of meer toepassingen)" #: prefs.js:788 msgid "Columns per Page (0 for adaptive grid)" -msgstr "" +msgstr "Aantal kolommen per pagina (0 = aanpasbaar rooster)" #: prefs.js:789 msgid "" "Number of columns in application grid. If set to 0 (default setting) the " "number will be set automatically to fit available height." msgstr "" +"Het aantal kolommen van het toepassingsrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare hoogte." #: prefs.js:803 msgid "Rows per Page (0 for adaptive grid)" -msgstr "" +msgstr "Aantal rijen per pagina (0 = aanpasbaar rooster)" #: prefs.js:804 msgid "" "Number of rows in application grid. If set to 0 (default setting) the number " "will be set automatically to fit available height." msgstr "" +"Het aantal rijen van het toepassingsrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare hoogte." #: prefs.js:818 msgid "Folder Columns per Page (0 for adaptive grid)" -msgstr "" +msgstr "Aantal mapkolommen per pagina (0 = aanpasbaar rooster)" #: prefs.js:819 msgid "" "Number of columns in folder grid. If you leave the value on 0, the number of " "columns will be calculated to fit all folder icons." msgstr "" +"Het aantal kolommen van het mappenrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare pictogrammen." #: prefs.js:833 msgid "Folder Rows per Page (0 for adaptive grid)" -msgstr "" +msgstr "Aantal maprijen per pagina (0 = aanpasbaar rooster)" #: prefs.js:834 msgid "" "Number of rows in folder grid. If you leave the value on 0, the number of " "rows will be calculated to fit all folder icons." msgstr "" +"Het aantal rijen van het mappenrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare pictogrammen." #: prefs.js:841 prefs.js:1174 prefs.js:1492 msgid "Search" -msgstr "" +msgstr "Zoeken" #: prefs.js:847 msgid "App Search Icon Size" -msgstr "" +msgstr "Omvang van zoekresultaten" #: prefs.js:848 msgid "Size of results provided by the App Search Provider." -msgstr "" +msgstr "De omvang van toepassingszoekresultaten." #: prefs.js:878 msgid "Max Search Results Rows" -msgstr "" +msgstr "Aantal zoekresultaatrijen" #: prefs.js:879 msgid "" "Sets the maximum number of rows for result lists of all search providers " "except window search provider which always lists all results." msgstr "" +"Stel het maximumaantal rijen van resultaatlijsten van alle zoekdiensten (met " +"uitzondering van vensters) in." #: prefs.js:888 msgid "Overview Background" -msgstr "" +msgstr "Overzichtsachtergrond" #: prefs.js:894 msgid "Show Static Background" -msgstr "" +msgstr "Eigen achtergrond tonen" #: prefs.js:895 msgid "Show static background wallpaper instead of the solid grey color." -msgstr "" +msgstr "Stel een eigen achtergrond in in plaats van de grijze kleur." #: prefs.js:911 msgid "Blur Window Picker Background" -msgstr "" +msgstr "Vensterkiezerachtergrond vervagen" #: prefs.js:912 msgid "Blur background wallpaper (if enabled) in the window picker view." -msgstr "" +msgstr "Vervaag de achtergrond (indien ingeschakeld) van de vensterkiezer." #: prefs.js:928 msgid "Blur App Grid/Search View Background" -msgstr "" +msgstr "Toepassingsrooster-/Zoekweergave-achtergrond vervagen" #: prefs.js:929 msgid "" "Blur background wallpaper (if enabled) in the app grid and search results " "views." msgstr "" +"Vervaag de achtergrond (indien ingeschakeld) van het toepassingsrooster en " +"de zoekweergave." #: prefs.js:937 msgid "Smooth Blur Transitions" -msgstr "" +msgstr "Vloeiende vervaagovergangen" #: prefs.js:938 msgid "" "Makes blur transitions smoother but can impact overall smoothness of " "overview animations." msgstr "" +"Maak vervaagovergangen vloeiender. Let op: dit kan van invloed zijn op de " +"overige animaties." #: prefs.js:953 prefs.js:980 prefs.js:1002 msgid "Overview" -msgstr "" +msgstr "Overzicht" #: prefs.js:959 msgid "Overview Mode" -msgstr "" +msgstr "Overzichtsmodus" #: prefs.js:960 msgid "" @@ -745,71 +822,84 @@ msgid "" "workspace thumbnail scales the ws preview and exposes its windows like in " "the default overview mode." msgstr "" +"De modus ‘Vensters tonen na aanwijzen’ toont geen werkbladminiaturen totdat " +"de aanwijzer een venster aanwijst.\n" +"De statische modus toont geen verkleinde werkbladminiaturen, maar alleen de " +"dash en miniaturen boven het bureaublad. Klik op een werkbladminiatuur om de " +"voorvertoning te verkleinen en alle vensters te tonen, net als in de " +"standaard overzichtsmodus." #: prefs.js:965 prefs.js:1259 prefs.js:1277 prefs.js:1317 msgid "Default" -msgstr "" +msgstr "Standaard" #: prefs.js:966 msgid "Expose Windows on Hover" -msgstr "" +msgstr "Vensters tonen na aanwijzen" #: prefs.js:967 msgid "Static Workspace" -msgstr "" +msgstr "Statische werkbladen" #: prefs.js:974 msgid "Startup State" -msgstr "" +msgstr "Opstartmodus" #: prefs.js:975 msgid "Allows to change the state in which GNOME Shell starts a session." -msgstr "" +msgstr "Geef aan in welke modus GNOME Shell een sessie dient te starten." #: prefs.js:981 msgid "Desktop" -msgstr "" +msgstr "Bureaublad" #: prefs.js:982 prefs.js:1003 msgid "Applications" -msgstr "" +msgstr "Toepassingen" #: prefs.js:989 msgid "" "Hot Corner (Install Custom Hot Corners - Extended extension for more options)" msgstr "" +"Snelhoek (installeer voor meer mogelijkheden de uitbreiding ‘Custom Hot " +"Corners - Extended)" #: prefs.js:995 msgid "Hot corner Action" -msgstr "" +msgstr "Snelhoekactie" #: prefs.js:996 msgid "" "Disable or change behavior of hot corner. Holding down the Ctrl key while " "hitting the hot corner switches between Overview/Applications actions." msgstr "" +"Pas het gedrag van de snelhoek aan of schakel deze uit. Houd Ctrl ingedrukt " +"tijdens het aanwijzen van de snelhoek om te schakelen tussen het overzicht " +"en de toepassingen." #: prefs.js:1010 msgid "Enable Hot Corner in Full-Screen mode" -msgstr "" +msgstr "Snelhoek tonen in schermvullende modus" #: prefs.js:1011 msgid "" "If you often work with full-screen applications and want the hot corner to " "be usable." msgstr "" +"Als u regelmatig met schermvullende toepassingen werkt en de snelhoek wilt " +"blijven gebruiken." #: prefs.js:1020 msgid "Show Ripples Animation" -msgstr "" +msgstr "Golfanimatie tonen" #: prefs.js:1021 msgid "Ripples animation shows up when you trigger hot corner." -msgstr "" +msgstr "Toon een golfanimatie zodra u de snelhoek activeert." #: prefs.js:1036 msgid "Dash Icon Click" -msgstr "" +msgstr "Klikken op dashpictogram" #: prefs.js:1037 msgid "" @@ -817,18 +907,21 @@ msgid "" "window is not on the current workspace, the overview can switch to the " "workspace with the recent window." msgstr "" +"Als de aangeklikte toepassing meer dan dan één venster bevat en het onlangs " +"gebruikte venster niet op het huidige werkblad staat, dan kan worden " +"overgeschakeld naar het juiste werkblad." #: prefs.js:1042 msgid "Activate Last Used Window Immediately" -msgstr "" +msgstr "Laatstgebruikte venster focussen" #: prefs.js:1043 msgid "Switch to Workspace with Recently Used Window" -msgstr "" +msgstr "Overschakelen naar werkblad met laatstgebruikte venster" #: prefs.js:1056 msgid "Close Workspace Button" -msgstr "" +msgstr "Werkbladsluitknop" #: prefs.js:1057 msgid "" @@ -836,44 +929,49 @@ msgid "" "over it and allows you to close all windows on the workspace. You can choose " "a safety lock to prevent accidental use." msgstr "" +"De werkbladsluitknop wordt getoond zodra u een miniatuur aanwijst. Hiermee " +"kunt u alle vensters op het desbetreffende werkblad sluiten. U kunt " +"instellen dat u sluiten wilt voorkomen." #: prefs.js:1063 msgid "Single Click" -msgstr "" +msgstr "Eenmaal klikken" #: prefs.js:1064 msgid "Double Click" -msgstr "" +msgstr "Dubbelklikken" #: prefs.js:1065 msgid "Ctrl Key + Click" -msgstr "" +msgstr "Ctlr+klik" #: prefs.js:1078 msgid "Apps Order" -msgstr "" +msgstr "Toepassingsvolgorde" #: prefs.js:1079 msgid "" "Choose sorting method for the app grid. Note that sorting by alphabet and " "usage ignores folders." msgstr "" +"Kies de sorteermethode van het toepassingsrooster. Let op: sorteren op " +"alfabet of gebruik negeert mappen." #: prefs.js:1084 msgid "Custom (Default)" -msgstr "" +msgstr "Aangepast (standaard)" #: prefs.js:1085 msgid "Alphabet" -msgstr "" +msgstr "Alfabet" #: prefs.js:1086 msgid "Usage" -msgstr "" +msgstr "Gebruik" #: prefs.js:1093 msgid "App Grid Content" -msgstr "" +msgstr "Inhoud van toepassingsrooster" #: prefs.js:1094 msgid "" @@ -881,30 +979,34 @@ msgid "" "in the grid or remove also running applications. Option \"Favorites and " "Running First\" only works with the Alphabet and Usage sorting." msgstr "" +"Standaard worden favoriete toepassingen verborgen. Met behulp van deze optie " +"kunt u ze klonen op het rooster of ook actieve toepassingen verbergen. Let " +"op: de optie ‘Favorieten en actieve bovenaan’ werkt alleen in combinatie met " +"sorteren op alfabet of gebruik." #: prefs.js:1099 msgid "Include All" -msgstr "" +msgstr "Alles" #: prefs.js:1100 msgid "Include All - Favorites and Running First" -msgstr "" +msgstr "Alles - Favorieten en actieve bovenaan" #: prefs.js:1101 msgid "Exclude Favorites (Default)" -msgstr "" +msgstr "Favorieten verbergen (standaard)" #: prefs.js:1102 msgid "Exclude Running" -msgstr "" +msgstr "Actieve verbergen" #: prefs.js:1103 msgid "Exclude Favorites and Running" -msgstr "" +msgstr "Favorieten en actieve verbergen" #: prefs.js:1110 msgid "Active Icons in Folder Preview" -msgstr "" +msgstr "Actieve pictogrammen op mapvoorvertoning" #: prefs.js:1111 msgid "" @@ -913,20 +1015,27 @@ msgid "" "the app without closing the overview, so you can open multiple apps in a row " "on the current workspace and secondary button opens the folder." msgstr "" +"Schakel in om op een toepassingspictogram te kunnen klikken vanuit een " +"mapvoorvertoning. Hierdoor hoeft niet eerst de map te worden geopend. Met " +"een middelklik opent u een nieuw venster zonder het overzicht te sluiten, " +"zodat u meerdere toepassingen per rij kunt openen. Met een rechtermuisklik " +"wordt de map geopend." #: prefs.js:1120 msgid "Center Open Folders" -msgstr "" +msgstr "Geopende mappen centreren" #: prefs.js:1121 msgid "" "App folder may open in the center of the screen or above the source folder " "icon." msgstr "" +"Toon mappen op het midden van het scherm in plaats van boven het " +"mappictogram." #: prefs.js:1130 msgid "Allow Incomplete Pages" -msgstr "" +msgstr "Onvolledige pagina's tonen" #: prefs.js:1131 msgid "" @@ -934,48 +1043,51 @@ msgid "" "fill any empty slot left after an icon was (re)moved (to a folder for " "example)." msgstr "" +"Schakel uit om pictogrammen van de volgende pagina (indien beschikbaar) " +"automatisch te verplaatsen om gaten op te vullen als er pictogrammen " +"verplaatst of verwijderd zijn." #: prefs.js:1140 msgid "App Labels Behavior" -msgstr "" +msgstr "Toepassingslabels" #: prefs.js:1141 msgid "Choose how and when to display app names." -msgstr "" +msgstr "Geef aan hoe en wanneer er toepassingsnamen dienen te worden getoond." #: prefs.js:1146 msgid "Ellipsized - Expand Selected (Default)" -msgstr "" +msgstr "Ingekort - Selectie volledig tonen (standaard)" #: prefs.js:1147 msgid "Always Expanded" -msgstr "" +msgstr "Altijd volledig tonen" #: prefs.js:1148 msgid "Hidden - Show Selected Only" -msgstr "" +msgstr "Verborgen - Alleen selectie tonen" #: prefs.js:1154 msgid "Reset App Grid Layout" -msgstr "" +msgstr "Standaardindeling herstellen" #: prefs.js:1155 msgid "" "Removes all stored app grid icons positions, after the reset icons will be " "ordered alphabetically." -msgstr "" +msgstr "Wis alle pictogramlocaties en sorteer alle pictogrammen op alfabet." #: prefs.js:1163 msgid "Remove App Grid Folders" -msgstr "" +msgstr "Mappen verwijderen" #: prefs.js:1164 msgid "Removes all folders, folder apps move to root grid." -msgstr "" +msgstr "Verwijder alle mappen en verplaats toepassingen naar het hoofdrooster." #: prefs.js:1186 msgid "Enable Window Search Provider" -msgstr "" +msgstr "Vensters doorzoeken" #: prefs.js:1187 msgid "" @@ -983,10 +1095,13 @@ msgid "" "results. You can search app names and window titles. You can also use \"wq/" "\" prefix to suppress results from other search providers." msgstr "" +"Schakel de dienst om geopende vensters te zoeken in. U kunt zoeken op " +"toepassingsnamen en venstertitels. Ook kunt u ‘wq/’ voorafgaand aan een " +"zoekopdracht toevoegen om resultaten van andere diensten te verbergen." #: prefs.js:1195 msgid "Enable Recent Files Search Provider" -msgstr "" +msgstr "Onlangs gebruikte bestanden doorzoeken" #: prefs.js:1196 msgid "" @@ -995,10 +1110,15 @@ msgid "" "field. This option needs File History option enabled in the GNOME Privacy " "settings." msgstr "" +"Schakel de dienst in om onlangs gebruikte bestanden te doorzoeken. Dit kan " +"met behulp van een dashpictogram, Ctrl+spatiebalk of door ‘fq//’ voorafgaand " +"aan een zoekopdracht in te vullen. Let op: voor deze optie is " +"bestandsgeschiedenis vereist. Die optie vindt u in de GNOME-" +"privacyvoorkeuren." #: prefs.js:1204 msgid "Enable Fuzzy Match" -msgstr "" +msgstr "Onduidelijke overeenkomsten inschakelen" #: prefs.js:1205 msgid "" @@ -1006,28 +1126,34 @@ msgid "" "searching for and find \"Firefox\" even if you type \"ffx\". Works only for " "the App, Window and Recent files search providers." msgstr "" +"Schakel onduidelijke overeenkomsten in om letters over te slaan. Voorbeeld: " +"‘Firefox’ wordt gevonden als u ‘ffx’ typt. Let op: dit werkt alleen in " +"combinatie met de diensten toepassingen, vensters en onlangs gebruikte " +"bestanden." #: prefs.js:1223 msgid "Animations - General" -msgstr "" +msgstr "Animaties - Algemeen" #: prefs.js:1238 msgid "Animation Speed" -msgstr "" +msgstr "Animatiesnelheid" #: prefs.js:1239 msgid "" "Adjusts the global animation speed in % of the default duration - higher " "value means slower animation." msgstr "" +"Pas de algemene animatiesnelheid aan (in %). Hogere waarde = langzamere " +"animatie." #: prefs.js:1247 msgid "Animations - Overview" -msgstr "" +msgstr "Animaties - Overzicht" #: prefs.js:1253 msgid "App Grid Animation" -msgstr "" +msgstr "Animatie van toepassingsrooster" #: prefs.js:1254 msgid "" @@ -1035,26 +1161,29 @@ msgid "" "screen. You can choose direction, keep it Default (direction will be chosen " "automatically) or disable the animation if you don't like it." msgstr "" +"Toon een animatie vanaf de zijkant na het openen van het toepassingsrooster. " +"U kunt aangeven vanuit welke richting de animatie dient te komen (standaard: " +"automatisch) of de animatie in zijn geheel uitschakelen." #: prefs.js:1261 prefs.js:1279 msgid "Right to Left" -msgstr "" +msgstr "Van rechts naar links" #: prefs.js:1262 prefs.js:1280 msgid "Left to Right" -msgstr "" +msgstr "Van links naar rechts" #: prefs.js:1263 prefs.js:1281 msgid "Bottom to Top" -msgstr "" +msgstr "Van boven naar onder" #: prefs.js:1264 prefs.js:1282 msgid "Top to Bottom" -msgstr "" +msgstr "Van onder naar boven" #: prefs.js:1271 msgid "Search View Animation" -msgstr "" +msgstr "Animatie van zoekweergave" #: prefs.js:1272 msgid "" @@ -1062,28 +1191,33 @@ msgid "" "from the edge of the screen. You can choose direction, keep it Default " "(currently Bottom to Top) or disable the animation if you don't like it." msgstr "" +"Toon een animatie vanaf de zijkant na het openen van de zoekweergave. U kunt " +"aangeven vanuit welke richting de animatie dient te komen (standaard: van " +"onder naar boven) of de animatie in zijn geheel uitschakelen." #: prefs.js:1290 msgid "Workspace Preview Animation" -msgstr "" +msgstr "Animatie van werkbladminiaturen" #: prefs.js:1291 msgid "" "When entering / leaving the App Grid / Search view, the workspace preview " "can animate to/from workspace thumbnail." msgstr "" +"Toon een animatie na het openen en sluiten van het toepassingsrooster en de " +"zoekweergave." #: prefs.js:1297 msgid "Enable" -msgstr "" +msgstr "Inschakelen" #: prefs.js:1305 msgid "Workspace Switcher" -msgstr "" +msgstr "Werkbladwisselaar" #: prefs.js:1311 msgid "Workspace Switcher Animation" -msgstr "" +msgstr "Animatie van werkbladwisselaar" #: prefs.js:1312 msgid "" @@ -1091,14 +1225,17 @@ msgid "" "switcher animation outside of the overview. The Static Background mode also " "keeps Conky and desktop icons on their place during switching." msgstr "" +"Hiermee kunt u de animatie van de bureaubladachtergrond op de " +"werkbladwisselaar buiten het overzicht uitschakelen. De statische modus laat " +"Conky en pictogrammen op hun plaats staan tijdens het wisselen." #: prefs.js:1318 msgid "Static Background" -msgstr "" +msgstr "Statische achtergrond" #: prefs.js:1325 msgid "Workspace Switcher Popup Mode" -msgstr "" +msgstr "Werkbladwisselaarpop-upmodus" #: prefs.js:1326 msgid "" @@ -1107,54 +1244,58 @@ msgid "" "show it on the current monitor (the one with mouse pointer) instead of the " "primary." msgstr "" +"Deze pop-up wordt getoond als u van werkblad wisselt met behulp van een " +"sneltoets of gebaar buiten het overzicht. U kunt de pop-up in zijn geheel " +"uitschakelen of alleen op het huidige beeldscherm (het scherm met de " +"aanwijzer) tonen." #: prefs.js:1332 msgid "Show on Primary Monitor (Default)" -msgstr "" +msgstr "Tonen op hoofdscherm (standaard)" #: prefs.js:1333 msgid "Show on Current Monitor" -msgstr "" +msgstr "Tonen op huidig beeldscherm" #: prefs.js:1341 msgid "Notifications" -msgstr "" +msgstr "Meldingen" #: prefs.js:1347 msgid "Notification Banner Position" -msgstr "" +msgstr "Meldingslocatie" #: prefs.js:1348 msgid "Choose where the notification pop-ups appear on the screen." -msgstr "" +msgstr "Geef aan op welke locatie meldingen dienen te worden getoond." #: prefs.js:1353 msgid "Top Left" -msgstr "" +msgstr "Linksboven" #: prefs.js:1354 msgid "Top Middle" -msgstr "" +msgstr "Linksmidden" #: prefs.js:1355 msgid "Top Right (Default)" -msgstr "" +msgstr "Rechtsboven (standaard)" #: prefs.js:1356 msgid "Bottom Left" -msgstr "" +msgstr "Linksonder" #: prefs.js:1357 msgid "Bottom Middle" -msgstr "" +msgstr "Middenonder" #: prefs.js:1358 msgid "Bottom Right" -msgstr "" +msgstr "Rechtsonder" #: prefs.js:1365 msgid "Window Attention Handler" -msgstr "" +msgstr "Vensterfocusafhandeling" #: prefs.js:1366 msgid "" @@ -1163,38 +1304,41 @@ msgid "" "(notification will be pushed into the message tray silently) or focus the " "source window immediately instead." msgstr "" +"Als een venster om focus vraagt (doorgaans een nieuw venster), dan toont " +"GNOME Shell een melding. U kunt deze meldingen uitschakelen om ze direct " +"naar het berichtenvak te sturen of het venster direct focussen." #: prefs.js:1371 prefs.js:1386 msgid "Show Notifications (Default)" -msgstr "" +msgstr "Meldingen tonen (standaard)" #: prefs.js:1372 msgid "Disable Notification Popups" -msgstr "" +msgstr "Meldingen uitschakelen" #: prefs.js:1373 msgid "Immediately Focus Window" -msgstr "" +msgstr "Venster direct focussen" #: prefs.js:1380 msgid "Favorites" -msgstr "" +msgstr "Favorieten" #: prefs.js:1381 msgid "Disable pin/unpin app notifications." -msgstr "" +msgstr "Schakel meldingen omtrent favorieten uit." #: prefs.js:1387 msgid "Disable Notifications" -msgstr "" +msgstr "Meldingen uitschakelen" #: prefs.js:1402 msgid "Keyboard" -msgstr "" +msgstr "Toetsenbord" #: prefs.js:1408 msgid "Override Page Up/Down Shortcuts" -msgstr "" +msgstr "Page Up-/Down-sneltoetsgedrag aanpassen" #: prefs.js:1409 msgid "" @@ -1202,14 +1346,17 @@ msgid "" "keyboard shortcuts for the current workspace orientation. If you encounter " "any issues, check the configuration in the dconf editor." msgstr "" +"Met deze optie kunt u het gedrag van de sneltoetsen (Shift +) Super + Page " +"Up/Down binnen de huidige werkbladoriëntatie aanpassen. Let op: als u " +"problemen ervaart, bekijk dan de configuratie in dconf-bewerker." #: prefs.js:1417 msgid "Compatibility" -msgstr "" +msgstr "Compatibiliteit" #: prefs.js:1423 msgid "Fix for Dash to Dock" -msgstr "" +msgstr "Dash-to-Dock-oplossing" #: prefs.js:1424 msgid "" @@ -1218,82 +1365,90 @@ msgid "" "change monitors configuration. If you are experiencing such issues, try to " "enable this option, or (better) disable/replace the dock extension." msgstr "" +"Met het standaard Ubuntu-dock en andere Dash-to-Dock-varianten kunt u " +"problemen met het activiteitenoverzicht ervaren als u de docklocatie of " +"beeldschermvoorkeuren aanpast. In dat geval kunt u deze optie proberen of de " +"dockuitbreiding in kwestie uit te schakelen/te vervangen." #: prefs.js:1432 msgid "" "V-Shell Modules that can be disabled in case of conflict or misbehavior." msgstr "" +"V-Shellmodules die kunnen worden uitgeschakeld bij problemen of conflicten." #: prefs.js:1438 msgid "AppFavorites" -msgstr "" +msgstr "Favoriete toepassingen" #: prefs.js:1439 msgid "Pin/unpin app notification options." -msgstr "" +msgstr "Opties omtrent favorietenmeldingen." #: prefs.js:1447 msgid "AppDisplay / IconGrid" -msgstr "" +msgstr "Toepassingsweergave/-rooster" #: prefs.js:1448 msgid "App grid customization and options." -msgstr "" +msgstr "Opties omtrent het aanpassen van het toepassingsrooster." #: prefs.js:1457 msgid "Dash configuration options and support for vertical orientation." -msgstr "" +msgstr "Dashopties en ondersteuning voor verticale oriëntatie." #: prefs.js:1466 msgid "" "Hot corner options, removes right panel barrier that collides with CHC-E " "extension." msgstr "" +"Snelhoekopties. Verwijder de barrière van de rechterbalk die botst met de " +"CHC-E-uitbreiding." #: prefs.js:1474 msgid "MessageTray" -msgstr "" +msgstr "Berichtenvak" #: prefs.js:1475 msgid "Notification position options." -msgstr "" +msgstr "Opties omtrent meldingslocaties." #: prefs.js:1484 msgid "Panel options." -msgstr "" +msgstr "Bovenbalkopties." #: prefs.js:1493 msgid "Search view and app search provider customization and options." -msgstr "" +msgstr "Zoekweergave- en zoekdienstopties." #: prefs.js:1501 msgid "SwipeTracker" -msgstr "" +msgstr "Gebaar-volgsysteem" #: prefs.js:1502 msgid "Gestures for vertical workspace orientation." -msgstr "" +msgstr "Gebaren in de verticale werkbladoriëntatie." #: prefs.js:1510 msgid "WindowAttentionHandler" -msgstr "" +msgstr "Vensterfocusafhandeling" #: prefs.js:1511 msgid "Window attention handler options." -msgstr "" +msgstr "Vensterfocusafhandelingsopties." #: prefs.js:1519 msgid "WindowManager" -msgstr "" +msgstr "Vensterbeheer" #: prefs.js:1520 msgid "" "Fixes an upstream bug in the minimization animation of a full-screen window." msgstr "" +"Lost een bug omtrent de minimaliseeranimatie van schermvullende vensters op." #: prefs.js:1528 msgid "WindowPreview" -msgstr "" +msgstr "Venstervoorvertoning" #: prefs.js:1529 msgid "" @@ -1301,89 +1456,96 @@ msgid "" "errors when you close a window from an overview or exit the overview with a " "gesture when any window is selected." msgstr "" +"Venstervoorvertoningsopties. Lost een bug op omtrent het vastleggen van " +"foutmeldingen in het systeemlogboek na het sluiten van een venster vanuit " +"het overzicht of sluiten van het overzicht met een gebaar." #: prefs.js:1537 msgid "Workspace" -msgstr "" +msgstr "Werkblad" #: prefs.js:1538 msgid "" "Fixes workspace preview allocations for vertical workspaces orientation and " "window scaling in static overview modes." msgstr "" +"Lost een bug omtrent het toewijzen van werkbladvoorvertoningen in verticale " +"oriëntatie op, alsmede vensterschaling in de statische modus." #: prefs.js:1546 msgid "WorkspaceAnimation" -msgstr "" +msgstr "Werkbladanimatie" #: prefs.js:1547 msgid "Static workspace animation option." -msgstr "" +msgstr "Statische werkbladanimatie." #: prefs.js:1555 msgid "WorkspaceSwitcherPopup" -msgstr "" +msgstr "Werkbladwisselaarpop-up" #: prefs.js:1556 msgid "Workspace switcher popup position options." -msgstr "" +msgstr "Opties omtrent de werkbladwisselaarpop-up." #: prefs.js:1573 msgid "Version" -msgstr "" +msgstr "Versie" #: prefs.js:1579 msgid "Reset all options" -msgstr "" +msgstr "Standaardwaarden herstellen" #: prefs.js:1580 msgid "Set all options to default values." -msgstr "" +msgstr "Herstel alle standaardvoorkeuren." #: prefs.js:1586 msgid "Links" -msgstr "" +msgstr "Links" #: prefs.js:1590 msgid "Homepage" -msgstr "" +msgstr "Website" #: prefs.js:1591 msgid "Source code and more info about this extension" -msgstr "" +msgstr "Broncode en meer informatie over deze uitbreiding" #: prefs.js:1596 msgid "Changelog" -msgstr "" +msgstr "Wijzigingslog" #: prefs.js:1597 msgid "See what's changed." -msgstr "" +msgstr "Bekijk alle wijzigingen." #: prefs.js:1602 msgid "GNOME Extensions" -msgstr "" +msgstr "GNOME-uitbreidingen" #: prefs.js:1603 msgid "Rate and comment the extension on GNOME Extensions site." -msgstr "" +msgstr "Beoordeel deze uitbreiding op de GNOME-uitbreidingensite." #: prefs.js:1608 msgid "Report a bug or suggest new feature" -msgstr "" +msgstr "Bug melden of idee delen" #: prefs.js:1614 msgid "Buy Me a Coffee" -msgstr "" +msgstr "Trakteer me op koffie" #: prefs.js:1615 msgid "If you like this extension, you can help me with my coffee expenses." msgstr "" +"Als u deze uitbreiding graag gebruikt, dan kunt u me trakteren op een kopje " +"koffie." #: recentFilesSearchProvider.js:109 msgid "Search recent files" -msgstr "" +msgstr "Onlangs gebruikte bestanden doorzoeken" #: recentFilesSearchProvider.js:110 msgid "Recent Files" -msgstr "" +msgstr "Onlangs gebruikte bestanden" diff --git a/extensions/vertical-workspaces/po/vertical-workspaces.pot b/extensions/44/vertical-workspaces/po/vertical-workspaces.pot similarity index 100% copy from extensions/vertical-workspaces/po/vertical-workspaces.pot copy to extensions/44/vertical-workspaces/po/vertical-workspaces.pot diff --git a/extensions/vertical-workspaces/prefs.js b/extensions/44/vertical-workspaces/prefs.js similarity index 68% rename from extensions/vertical-workspaces/prefs.js rename to extensions/44/vertical-workspaces/prefs.js index 6cea321..b757b91 100644 --- a/extensions/vertical-workspaces/prefs.js +++ b/extensions/44/vertical-workspaces/prefs.js @@ -9,115 +9,142 @@ 'use strict'; -const { Gtk, GLib } = imports.gi; +const Gtk = imports.gi.Gtk; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; +const MyExtension = ExtensionUtils.getCurrentExtension(); +const OptionsFactory = MyExtension.imports.lib.optionsFactory; +const GObject = imports.gi.GObject; -const ItemFactory = Me.imports.lib.optionsFactory.ItemFactory; -const AdwPrefs = Me.imports.lib.optionsFactory.AdwPrefs; -const LegacyPrefs = Me.imports.lib.optionsFactory.LegacyPrefs; - -const shellVersion = Settings.shellVersion; - -// gettext -const _ = Settings._; - -// libadwaita is available starting with GNOME Shell 42. -let Adw = null; -try { - Adw = imports.gi.Adw; -} catch (e) {} - -let gOptions; -let pageList; -let itemFactory; +let Me; +let _; +let opt; function init() { - ExtensionUtils.initTranslations(Me.metadata['gettext-domain']); - gOptions = new Settings.Options(); + Me = {}; - itemFactory = new ItemFactory(gOptions); + Me.shellVersion = parseFloat(imports.misc.config.PACKAGE_VERSION); + Me.imports = MyExtension.imports; + Me.metadata = MyExtension.metadata; + Me.gSettings = ExtensionUtils.getSettings(Me.metadata['settings-schema']); + Me.Settings = MyExtension.imports.lib.settings; + Me.gettext = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; - pageList = [ + Me.opt = new Me.Settings.Options(Me); + _ = Me.gettext; + opt = Me.opt; + + OptionsFactory.init(Me); +} + +function _getPageList() { + const itemFactory = new OptionsFactory.ItemFactory(); + const pageList = [ + { + name: 'profiles', + title: _('Profiles'), + iconName: 'open-menu-symbolic', + optionList: _getProfilesOptionList(itemFactory), + }, { name: 'layout', title: _('Layout'), iconName: 'view-grid-symbolic', - optionList: _getLayoutOptionList(), + optionList: _getLayoutOptionList(itemFactory), }, { name: 'appearance', title: _('Appearance'), iconName: 'view-reveal-symbolic', - optionList: _getAppearanceOptionList(), + optionList: _getAppearanceOptionList(itemFactory), }, { name: 'behavior', title: _('Behavior'), iconName: 'system-run-symbolic', - optionList: _getBehaviorOptionList(), + optionList: _getBehaviorOptionList(itemFactory), + }, + { + name: 'modules', + title: _('Modules'), + iconName: 'application-x-addon-symbolic', + optionList: _getModulesOptionList(itemFactory), }, { name: 'misc', title: _('Misc'), iconName: 'preferences-other-symbolic', - optionList: _getMiscOptionList(), - }, - { - name: 'profiles', - title: _('Profiles'), - iconName: 'open-menu-symbolic', - optionList: _getProfilesOptionList(), + optionList: _getMiscOptionList(itemFactory), }, { name: 'about', title: _('About'), iconName: 'preferences-system-details-symbolic', - optionList: _getAboutOptionList(), + optionList: _getAboutOptionList(itemFactory), }, ]; + + return pageList; } function fillPreferencesWindow(window) { - window = new AdwPrefs(gOptions).getFilledWindow(window, pageList); + window = new OptionsFactory.AdwPrefs(opt).getFilledWindow(window, _getPageList()); window.connect('close-request', () => { - gOptions.destroy(); - gOptions = null; - itemFactory = null; - pageList = null; + opt.destroy(); + opt = null; + Me = null; + _ = null; }); window.set_default_size(800, 800); } -function buildPrefsWidget() { - const prefsWidget = new LegacyPrefs(gOptions).getPrefsWidget(pageList); - - prefsWidget.connect('realize', widget => { - const window = widget.get_root ? widget.get_root() : widget.get_toplevel(); - const width = 800; - const height = 800; - window.set_default_size(width, height); - const headerbar = window.get_titlebar(); - headerbar.title_widget = prefsWidget._stackSwitcher; - - const signal = Gtk.get_major_version() === 3 ? 'destroy' : 'close-request'; - window.connect(signal, () => { - gOptions.destroy(); - gOptions = null; - }); - }); - - return prefsWidget; -} - // //////////////////////////////////////////////////////////////////// -function _getLayoutOptionList() { +function _getProfilesOptionList(itemFactory) { const optionList = []; // options item format: - // [text, caption, widget, settings-variable, options for combo] + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + + optionList.push( + itemFactory.getRowWidget( + _('Custom Profiles'), + _('Sets of settings that can help you with the initial customization') + ) + ); + + optionList.push(itemFactory.getRowWidget( + _('Profile 1'), + null, + itemFactory.newPresetButton(opt, 1) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 2'), + null, + itemFactory.newPresetButton(opt, 2) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 3'), + null, + itemFactory.newPresetButton(opt, 3) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 4'), + null, + itemFactory.newPresetButton(opt, 4) + )); + + return optionList; +} + +function _getLayoutOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) optionList.push( itemFactory.getRowWidget( @@ -129,8 +156,8 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Dash Position'), null, - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'dashPosition', [ [_('Top'), 0], @@ -138,7 +165,8 @@ function _getLayoutOptionList() { [_('Bottom'), 2], [_('Left'), 3], [_('Hide'), 4], - ] + ], + 'dashModule' ) ); @@ -147,7 +175,9 @@ function _getLayoutOptionList() { _('Center Horizontal Dash to Workspace'), _('If the Dash Position is set to Top or Bottom, the position will be recalculated relative to the workspace preview instead of the screen'), itemFactory.newSwitch(), - 'centerDashToWs' + 'centerDashToWs', + null, + 'dashModule' ) ); @@ -165,7 +195,73 @@ function _getLayoutOptionList() { _('Fine Tune Dash Position'), _('Adjusts the position of the dash on the axis given by the orientation of the workspaces'), dashPositionScale, - 'dashPositionAdjust' + 'dashPositionAdjust', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Apps Icon Position'), + _('Sets the position of the "Show Applications" icon in the Dash'), + // // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'showAppsIconPosition', + [ + [_('Hide'), 2], + [_('Start'), 0], + [_('End'), 1], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Open Windows Icon Position'), + _('Allows to add "Search Open Windows" icon into Dash (if window search provider enabled on the Modules tab) so you can directly toggle window search provider results. You can also use the secondary mouse button click on the Show Apps Icon, or the Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowWindowsIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Recent Files Icon Position'), + _('Allows to add "Search Recent Files" icon into Dash (if recent files search provider enabled on the Modules tab) so you can directly toggle recent files search provider results. You can also use Ctrl + Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowRecentFilesIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Extensions Icon Position'), + _('Allows to add "Search Extensions" icon into Dash (if extensions search provider enabled on the Module tab) so you can directly toggle extensions search provider results. You can also use the Ctrl + Shift + Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowExtensionsIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' ) ); @@ -180,17 +276,17 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Thumbnails Position / Workspaces Orientation'), _('Position of the workspace thumbnails on the screen also sets orientation of the workspaces to vertical or horizontal. You have two options to disable workspace thumbnails, one sets workspaces to vertical orientation, the second one to horizontal.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'workspaceThumbnailsPosition', // this mess is just because of backward compatibility [ - [_('Left \t Vertical Orientation'), 0], - [_('Right \t Vertical Orientation'), 1], - [_('Hide \t Set Vertical Orientation'), 4], - [_('Top \t Horizontal Orientation'), 5], + [_('Left \t Vertical Orientation'), 0], + [_('Right \t Vertical Orientation'), 1], + [_('Hide \t Vertical Orientation'), 4], + [_('Top \t Horizontal Orientation'), 5], [_('Bottom \t Horizontal Orientation'), 6], - [_('Hide \t Set Horizontal Orientation'), 9], + [_('Hide \t Horizontal Orientation'), 9], ] ) ); @@ -233,7 +329,7 @@ function _getLayoutOptionList() { wsThumbnailScale.add_mark(13, Gtk.PositionType.TOP, null); optionList.push( itemFactory.getRowWidget( - _('Workspace Thumbnails Max Scale'), + _('Workspace Thumbnails Max Scale - Window Picker'), _('Adjusts maximum size of the workspace thumbnails in the overview (% relative to display width)'), wsThumbnailScale, 'wsThumbnailScale' @@ -248,11 +344,11 @@ function _getLayoutOptionList() { }); const wsThumbnailAppScale = itemFactory.newScale(wsThumbnailAppScaleAdjustment); - wsThumbnailAppScale.add_mark(0, Gtk.PositionType.TOP, null); + wsThumbnailAppScale.add_mark(13, Gtk.PositionType.TOP, null); optionList.push( itemFactory.getRowWidget( _('Workspace Thumbnails Max Scale - App View'), - _('Set to 0 to follow "Workspace Thumbnails Max Scale" scale. Allows you to set different thumbnails scale for the Applications view'), + _('Allows you to set different thumbnails scale for the Applications view'), wsThumbnailAppScale, 'wsThumbnailScaleAppGrid' ) @@ -330,7 +426,9 @@ function _getLayoutOptionList() { _('App Grid Page Width Scale'), _('Adjusts max app grid page width relative to the available space.'), agPageWidthScale, - 'appGridPageWidthScale' + 'appGridPageWidthScale', + null, + 'appDisplayModule' ) ); @@ -372,7 +470,9 @@ function _getLayoutOptionList() { _('Search Results Width'), _('Adjusts maximum width of search results view (% relative to default). This allows to fit more (or less) app icons into the app search result'), searchViewScale, - 'searchViewScale' + 'searchViewScale', + null, + 'searchModule' ) ); @@ -386,13 +486,14 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Main Panel Position'), _('Allows to place the main panel at the bottom of the primary display'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'panelPosition', [ [_('Top (Default)'), 0], [_('Bottom'), 1], - ] + ], + 'panelModule' ) ); @@ -400,15 +501,16 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Main Panel Visibility'), _('Allows to hide main panel when not needed'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'panelVisibility', [ [_('Always Visible (Default)'), 0], [_('Overview Only'), 1], [_('Always Hidden'), 2], // [_('Desktop View Only'), 3], - ] + ], + 'panelModule' ) ); @@ -434,7 +536,9 @@ function _getLayoutOptionList() { _('Horizontal Position (% from left)'), _('This popup shows up when you switch workspace using a keyboard shortcut or gesture outside of the overview. You can disable it on the "Behavior" tab. If you want more control over the popup, try the "Workspace Switcher Manager" extension'), hScale, - 'wsSwPopupHPosition' + 'wsSwPopupHPosition', + null, + 'workspaceSwitcherPopupModule' ) ); @@ -453,7 +557,9 @@ function _getLayoutOptionList() { _('Vertical Position (% from top)'), null, vScale, - 'wsSwPopupVPosition' + 'wsSwPopupVPosition', + null, + 'workspaceSwitcherPopupModule' ) ); @@ -467,17 +573,18 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Notification Banner Position'), _('Choose where the notification banners appear on the screen'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'notificationPosition', [ [_('Top Left'), 0], - [_('Top Center'), 1], - [_('Top Right (Default)'), 2], + [_('Top Center (Default)'), 1], + [_('Top Right'), 2], [_('Bottom Left'), 3], [_('Bottom Center'), 4], [_('Bottom Right'), 5], - ] + ], + 'messageTrayModule' ) ); @@ -485,8 +592,8 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('OSD Popup Position'), _('Choose where the OSD pop-ups (like sound volume level) appear on the screen'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'osdPosition', [ [_('Disable'), 0], @@ -497,7 +604,8 @@ function _getLayoutOptionList() { [_('Bottom Left'), 5], [_('Bottom Center (Default)'), 6], [_('Bottom Right'), 7], - ] + ], + 'osdWindowModule' ) ); @@ -511,8 +619,8 @@ function _getLayoutOptionList() { itemFactory.getRowWidget( _('Workspace Thumbnails Position'), _('Allows to place workspace thumbnails of secondary monitors on the opposite side than on the primary monitor'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'secWsThumbnailsPosition', [ [_('Same as Primary'), 2], @@ -543,7 +651,7 @@ function _getLayoutOptionList() { const secWsThumbnailScaleAdjustment = new Gtk.Adjustment({ upper: 30, - lower: 5, + lower: 0, step_increment: 1, page_increment: 1, }); @@ -591,10 +699,10 @@ function _getLayoutOptionList() { return optionList; } -function _getAppearanceOptionList() { +function _getAppearanceOptionList(itemFactory) { const optionList = []; // options item format: - // [text, caption, widget, settings-variable, options for combo] + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) // ---------------------------------------------------------------- optionList.push( @@ -607,8 +715,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Dash Max Icon Size'), _('Maximum size of Dash icons in pixels'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'dashMaxIconSize', [ [_('128'), 128], @@ -618,54 +726,23 @@ function _getAppearanceOptionList() { [_('64'), 64], [_('48'), 48], [_('32'), 32], - [_('24'), 24], - [_('16'), 16], - ] + ], + 'dashModule' ) ); optionList.push( itemFactory.getRowWidget( - _('Show Apps Icon Position'), - _('Sets the position of the "Show Applications" icon in the Dash'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), - 'showAppsIconPosition', + _('Dash Background Style'), + _('Allows you to change the background color of the dash to match the search results an app folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashBgColor', [ - [_('Hide'), 2], - [_('Start'), 0], - [_('End'), 1], - ] - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('Open Windows Icon Position'), - _('Allows to add "Search Open Windows" icon into Dash (if window search provider enabled on the Behavior tab) so you can directly toggle window search provider results. You can also use the secondary mouse button click on the Show Apps Icon, or the Space hotkey'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), - 'dashShowWindowsIcon', - [ - [_('Hide'), 0], - [_('Start'), 1], - [_('End'), 2], - ] - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('Recent Files Icon Position'), - _('Allows to add "Search Recent Files" icon into Dash (if recent files search provider enabled on the Behavior tab) so you can directly toggle recent files search provider results. You can also use Ctrl + Space hotkey'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), - 'dashShowRecentFilesIcon', - [ - [_('Hide'), 0], - [_('Start'), 1], - [_('End'), 2], - ] + [_('Default'), 0], + [_('Light'), 1], + ], + 'dashModule' ) ); @@ -682,7 +759,9 @@ function _getAppearanceOptionList() { _('Dash Background Opacity'), _('Adjusts the opacity of the Dash background'), dashBgOpacityScale, - 'dashBgOpacity' + 'dashBgOpacity', + null, + 'dashModule' ) ); @@ -699,7 +778,35 @@ function _getAppearanceOptionList() { _('Dash Background Radius'), _('Adjusts the border radius of the Dash background in pixels. 0 means the default value given by the current theme style'), dashBgRadiusScale, - 'dashBgRadius' + 'dashBgRadius', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Dash Background GNOME 3 Style'), + _('Background of the vertically oriented dash will imitate the GNOME 3 style'), + itemFactory.newSwitch(), + 'dashBgGS3Style', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Running App Indicator'), + _('Allows you to change style of the running app indicator under the app icon'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'runningDotStyle', + [ + [_('Dot (Default)'), 0], + [_('Line'), 1], + ], + 'dashModule' ) ); @@ -713,8 +820,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Show Workspace Thumbnail Labels'), _('Each workspace thumbnail can show label with its index and name (if defined in the system settings) or name/title of its most recently used app/window'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'showWsTmbLabels', [ [_('Disable'), 0], @@ -754,8 +861,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Window Preview App Icon Size'), null, - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'winPreviewIconSize', [ [_('64 (Default)'), 0], @@ -763,16 +870,35 @@ function _getAppearanceOptionList() { [_('32'), 2], [_('22'), 3], [_('Disable'), 4], - ] + ], + 'windowPreviewModule' ) ); optionList.push( itemFactory.getRowWidget( - _('Always Show Window Titles'), - _('All windows on the workspace preview will still show their titles, not only the one with the mouse pointer'), + _('Window Title Position / Visibility'), + _('Sets the position of the window title that is displayed when the mouse hovers over the window or can always be visible'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winTitlePosition', + [ + [_('Inside Window'), 0], + [_('Inside Window Always Visible'), 1], + [_('Below Window (Default)'), 2], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Close Window Button'), + _('Allows you to hide close window button'), itemFactory.newSwitch(), - 'alwaysShowWinTitles' + 'winPreviewShowCloseButton', + null, + 'windowPreviewModule' ) ); @@ -820,8 +946,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Icon Size'), _('Allows to set a fixed app grid icon size and bypass the default adaptive algorithm'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridIconSize', [ [_('Adaptive (Default)'), -1], @@ -839,7 +965,8 @@ function _getAppearanceOptionList() { [_('64'), 64], [_('48'), 48], // [_('32'), 32], - ] + ], + 'appDisplayModule' ) ); @@ -847,8 +974,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('Folder Icon Size'), _('Allows to set a fixed icon size and bypass the default adaptive algorithm in the open folder dialog'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridFolderIconSize', [ [_('Adaptive (Default)'), -1], @@ -859,23 +986,24 @@ function _getAppearanceOptionList() { [_('64'), 64], [_('48'), 48], [_('32'), 32], - ] + ], + 'appDisplayModule' ) ); - const folderIconGridCombo = itemFactory.newComboBox(); + const folderIconGridCombo = itemFactory.newDropDown(); optionList.push( itemFactory.getRowWidget( _('Max App Folder Icon Grid Size'), _('Sets a grid size (number of icons) in the folder preview. 3x3 options automatically switches between 2x2 and 3x3 grid depending on the number of icons in the folder'), folderIconGridCombo, - // itemFactory.newDropDown(), 'appGridFolderIconGrid', [ [_('2x2 (Default)'), 2], [_('3x3 for 5+ apps'), 3], [_('3x3 for 9+ apps'), 4], - ] + ], + 'appDisplayModule' ) ); @@ -891,7 +1019,9 @@ function _getAppearanceOptionList() { _('Columns per Page (0 for adaptive grid)'), _('Number of columns in the application grid. If set to 0 (the default), the number will be set automatically to fit the available width'), columnsSpinBtn, - 'appGridColumns' + 'appGridColumns', + null, + 'appDisplayModule' )); const rowsAdjustment = new Gtk.Adjustment({ @@ -906,11 +1036,13 @@ function _getAppearanceOptionList() { _('Rows per Page (0 for adaptive grid)'), _('Number of rows in the application grid. If set to 0 (the default), the number will be set automatically to fit the available height'), rowsSpinBtn, - 'appGridRows' + 'appGridRows', + null, + 'appDisplayModule' )); const folderColumnsAdjustment = new Gtk.Adjustment({ - upper: 8, + upper: 15, lower: 0, step_increment: 1, page_increment: 1, @@ -921,11 +1053,13 @@ function _getAppearanceOptionList() { _('Folder Columns per Page (0 for adaptive grid)'), _('Number of columns in folder grid. If you leave the value at 0, the number of columns will be calculated to fit all the folder icons on one page'), folderColumnsSpinBtn, - 'appGridFolderColumns' + 'appGridFolderColumns', + null, + 'appDisplayModule' )); const folderRowsAdjustment = new Gtk.Adjustment({ - upper: 8, + upper: 15, lower: 0, step_increment: 1, page_increment: 1, @@ -936,7 +1070,9 @@ function _getAppearanceOptionList() { _('Folder Rows per Page (0 for adaptive grid)'), _('Number of rows in folder grid. If you leave the value at 0, the number of rows will be calculated to fit all the folder icons on one page'), folderRowsSpinBtn, - 'appGridFolderRows' + 'appGridFolderRows', + null, + 'appDisplayModule' )); const appGridSpacingAdjustment = new Gtk.Adjustment({ @@ -951,9 +1087,11 @@ function _getAppearanceOptionList() { optionList.push( itemFactory.getRowWidget( _('Grid Spacing'), - _('Adjusts spacing between icons.'), + _('Adjusts the spacing between icons in a grid, the real impact is on folders'), appGridSpacingScale, - 'appGridSpacing' + 'appGridSpacing', + null, + 'appDisplayModule' ) ); @@ -967,8 +1105,8 @@ function _getAppearanceOptionList() { itemFactory.getRowWidget( _('App Search Icon Size'), _('Size of results provided by the App Search Provider - smaller size allows to fit more results'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'searchIconSize', [ [_('128'), 128], @@ -978,7 +1116,8 @@ function _getAppearanceOptionList() { [_('64'), 64], [_('48'), 48], [_('32'), 32], - ] + ], + 'searchModule' ) ); @@ -999,7 +1138,9 @@ function _getAppearanceOptionList() { _('Max Search Results Rows'), _('Sets the maximum number of rows for result lists of all search providers except the window search provider which always lists all results'), maxSearchResultsSpinButton, - 'searchMaxResultsRows' + 'searchMaxResultsRows', + null, + 'searchModule' ) ); @@ -1036,6 +1177,23 @@ function _getAppearanceOptionList() { ) ); + const searchBrightnessBgAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 0, + step_increment: 1, + page_increment: 10, + }); + + const searchBgBrightnessScale = itemFactory.newScale(searchBrightnessBgAdjustment); + optionList.push( + itemFactory.getRowWidget( + _('Brightness for Search View'), + _('Allows you to set a lower background brightness for search view mode where text visibility is more important'), + searchBgBrightnessScale, + 'searchBgBrightness' + ) + ); + const blurBgAdjustment = new Gtk.Adjustment({ upper: 100, lower: 0, @@ -1079,11 +1237,36 @@ function _getAppearanceOptionList() { ) ); + + optionList.push( + itemFactory.getRowWidget( + _('Window Thumbnails (PIP)') + ) + ); + + const winTmbAdjustment = new Gtk.Adjustment({ + upper: 50, + lower: 5, + step_increment: 1, + page_increment: 1, + }); + + const winTmbScale = itemFactory.newScale(winTmbAdjustment); + winTmbScale.add_mark(15, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Default Window Thumbnail Scale (% of screen height)'), + _('Default scale of window thumbnail (like Picture In Picture) that you can create using the app icon menu or window preview action'), + winTmbScale, + 'windowThumbnailScale' + ) + ); + return optionList; } // ---------------------------------------------------------------- -function _getBehaviorOptionList() { +function _getBehaviorOptionList(itemFactory) { const optionList = []; optionList.push( @@ -1096,8 +1279,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Overview Mode'), _('The Expose Windows on Hover mode does not expose the workspace preview windows until the mouse pointer enters any window\nThe Static Workspace mode keeps the workspace static when you activate the overview, it only shows Dash, workspace thumbnails and search entry over the workspace and only clicking on an active workspace thumbnail activates the default overview'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'overviewMode', [ [_('Default'), 0], @@ -1111,8 +1294,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Startup State'), _('Allows to change the state in which GNOME Shell starts a session'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'startupState', [ [_('Overview (Default)'), 0], @@ -1122,25 +1305,63 @@ function _getBehaviorOptionList() { ) ); + optionList.push( + itemFactory.getRowWidget( + _('Escape Key Behavior'), + _('Allows you to close the overview with a single press of the Escape key, even from the application grid or from search, if the search entry field does not have focus'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'overviewEscBehavior', + [ + [_('Default'), 0], + [_('Close Overview'), 1], + ], + 'searchControllerModule' + ) + ); + optionList.push( itemFactory.getRowWidget( _('Overlay Key (Super/Windows)') ) ); + optionList.push( + itemFactory.getRowWidget( + _('Single-Press Action'), + _('Disable or change behavior when you press and release the Super key. The "Search Windows" options requires the WindowSearchProvider module to be activated'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'overlayKeyPrimary', + [ + [_('Disable'), 0], + [_('Follow Global Overview Mode'), 1], + [_('Overview (Default)'), 2], + [_('Applications'), 3], + [_('Overview - Static WS Preview'), 4], + [_('Overview - Static Workspace'), 5], + [_('Search Windows'), 6], + // [_('Search Recent Files'), 7], + ], + 'overlayKeyModule' + ) + ); + optionList.push( itemFactory.getRowWidget( _('Double-Press Action'), - _('Disable or change behavior when you double-press the Super key. The "Search" options require the respective search provider to be activated'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + _('Disable or change behavior when you double-press the Super key. The "Search Windows" option requires the WindowSearchProvider module to be activated. The "Static WS Overview - Expose Windows" option allows you to switch to default Activities Overview window picker view if you set static workspace (preview) for the single press/release Super key action'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'overlayKeySecondary', [ [_('Disable'), 0], [_('Applications (Default)'), 1], [_('Search Windows'), 2], - [_('Search Recent Files'), 3], - ] + [_('Overview - Window Picker'), 3], + // [_('Search Recent Files'), 4], + ], + 'overlayKeyModule' ) ); @@ -1154,24 +1375,28 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Hot Corner Action'), _('Disable or change behavior of the hot corner. Holding down the Ctrl key while hitting the hot corner switches between Overview/Applications actions'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'hotCornerAction', [ [_('Disable'), 0], - [_('Overview'), 1], - [_('Applications'), 2], - [_('Search Windows'), 3], - ] + [_('Follow Global Overview Mode'), 1], + [_('Overview - Window Picker'), 2], + [_('Applications'), 3], + [_('Overview - Static WS Preview'), 4], + [_('Overview - Static Workspace'), 5], + [_('Search Windows'), 6], + ], + 'layoutModule' ) ); optionList.push( itemFactory.getRowWidget( _('Hot Corner Position'), - _('Choose which corner of your monitors will be active. If you choose "Follow Dash" option, the corner will be placed near the left or top edge of the Dash. The last option extends the hot corner barrier to cover the entire ege of the monitor where Dash is located'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + _('Choose which corner of your monitors will be active. If you choose "Follow Dash" option, the corner will be placed near the left or top edge of the Dash. The last option extends the hot corner trigger to cover the entire ege of the monitor where Dash is located'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'hotCornerPosition', [ [_('Default'), 0], @@ -1181,7 +1406,8 @@ function _getBehaviorOptionList() { [_('Bottom Right'), 4], [_('Follow Dash'), 5], [_('Follow Dash - Hot Edge'), 6], - ] + ], + 'layoutModule' ) ); @@ -1190,8 +1416,9 @@ function _getBehaviorOptionList() { _('Enable Hot Corner in Full-Screen Mode'), _('If you often work with full-screen applications and want the hot corner to be usable'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'hotCornerFullscreen' + 'hotCornerFullscreen', + null, + 'layoutModule' ) ); @@ -1200,8 +1427,9 @@ function _getBehaviorOptionList() { _('Show Ripples Animation'), _('The ripple animation is played when the hot corner is activated. The ripple size has been reduced to be less distracting'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'hotCornerRipples' + 'hotCornerRipples', + null, + 'layoutModule' ) ); @@ -1211,18 +1439,31 @@ function _getBehaviorOptionList() { ) ); + optionList.push( + itemFactory.getRowWidget( + _('Isolate Workspaces'), + _('Dash will only show apps and windows from the current workspace'), + itemFactory.newSwitch(), + 'dashIsolateWorkspaces', + null, + 'dashModule' + ) + ); + optionList.push( itemFactory.getRowWidget( _('App Icon - Click Behavior'), - _('if the app you clicked on has more than one window and the recently used window is not on the current workspace, the overview can switch to the workspace with the recent window'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + _('Choose your preferred behavior when clicking on an app icon. The "Prefer Current Workspace" option opens a new app window if not present in the current workspace. The "Open New Window" option also switches behavior of the middle click to "Activate" since its default behavior is to open a new window'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'dashShowWindowsBeforeActivation', [ [_('Activate App Immediately'), 0], [_('First Switch to Workspace'), 1], [_('Open New Window (if supported)'), 2], - ] + [_('Prefer Current Workspace'), 3], + ], + 'dashModule' ) ); @@ -1230,14 +1471,15 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('App Icon - Scroll Action'), _('Choose the behavior when scrolling over an app icon. The window cycler works with a list of windows sorted by the "Most Recently Used" and grouped by workspaces. Scrolling up cycles through previously used windows on the same workspace and then switches to another workspace, if any'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'dashIconScroll', [ [_('Default'), 0], [_('Cycle App Windows - Highlight Selected'), 1], [_('Cycle App Windows - Highlight App'), 2], - ] + ], + 'dashModule' ) ); @@ -1245,14 +1487,15 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Search Windows Icon - Scroll Action'), _('Choose the behavior when scrolling over the Search Windows icon. The window cycler works with a list of windows sorted by "Most Recently Used" of the current workspace or all workspaces. Scrolling up cycles through previously used windows on the same workspace, or all windows regardless workspace. This option is mainly useful for the static workspace overview mode.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'searchWindowsIconScroll', [ [_('Default'), 0], [_('Cycle All Windows'), 1], [_('Cycle Windows On Current WS'), 2], - ] + ], + 'dashModule' ) ); @@ -1266,8 +1509,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Close Workspace Button'), _('The Close Workspace button appears on the workspace thumbnail when you hover over it and allows you to close all windows on the workspace. You can choose a "safety lock" to prevent accidental use'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'closeWsButtonMode', [ [_('Disable'), 0], @@ -1286,23 +1529,64 @@ function _getBehaviorOptionList() { optionList.push( itemFactory.getRowWidget( - _('Always Activate Selected'), - _('If enabled, the currently selected window will be activated when leaving the Overview even without clicking. Usage example - press Super to open the Overview, place mouse pointer over a window, press Super again to activate the window'), - itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'alwaysActivateSelectedWindow' + _('Secondary Button Click Action'), + _('Allows you to add a secondary mouse click action to the window preview'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winPreviewSecBtnAction', + [ + [_('Activate Window (Default)'), 0], + [_('Close Window'), 1], + [_('Search For Same App Windows'), 2], + [_('Create Window Thumbnail - PIP'), 3], + ], + 'windowPreviewModule' ) ); - /* optionList.push( + optionList.push( itemFactory.getRowWidget( - _('App Icon Activates Window Search'), - _('If enabled, clicking a window preview icon will activate a search view with the application name as the search term, so you can list all app windows from all workspaces and filter them by typing.'), - itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'windowIconClickSearch' + _('Middle Button Click Action'), + _('Allows you to add a middle mouse click action to the window preview'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winPreviewMidBtnAction', + [ + [_('Activate Window (Default)'), 0], + [_('Close Window'), 1], + [_('Search For Same App Windows'), 2], + [_('Create Window Thumbnail - PIP'), 3], + ], + 'windowPreviewModule' ) - );*/ + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Icon Click Action'), + _('Select the action to take when the application icon on the window preview is clicked'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'windowIconClickAction', + [ + [_('Activate Window (Default)'), 0], + [_('Search For Same App Windows'), 1], + [_('Create Window Thumbnail - PIP'), 2], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Always Activate Selected'), + _('If enabled, the currently selected window will be activated when leaving the Overview even without clicking. Usage example - press Super to open the Overview, place mouse pointer over a window, press Super again to activate the window'), + itemFactory.newSwitch(), + 'alwaysActivateSelectedWindow', + null, + 'windowPreviewModule' + ) + ); optionList.push( itemFactory.getRowWidget( @@ -1312,16 +1596,34 @@ function _getBehaviorOptionList() { optionList.push( itemFactory.getRowWidget( - _('Apps Order'), - _('Choose sorting method for the app grid. Note that sorting by alphabet and usage ignores folders'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + _('App Grid Order'), + _('Choose sorting method for the app grid. Note that sorting by usage ignores folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridOrder', + [ + [_('Custom (Default)'), 0], + [_('Alphabet - Folders First'), 1], + [_('Alphabet - Folders Last'), 2], + [_('Usage - No Folders'), 3], + ], + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Folder Order'), + _('Choose sorting method for app folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appFolderOrder', [ [_('Custom (Default)'), 0], [_('Alphabet'), 1], [_('Usage'), 2], - ] + ], + 'appDisplayModule' ) ); @@ -1329,8 +1631,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('App Grid Content'), _('The default Shell removes favorite apps, this option allows to duplicate them in the grid or remove also running applications. Option "Favorites and Running First" only works with the Alphabet and Usage sorting'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridContent', [ [_('Include All'), 0], @@ -1338,7 +1640,8 @@ function _getBehaviorOptionList() { [_('Exclude Favorites (Default)'), 2], [_('Exclude Running'), 3], [_('Exclude Favorites and Running'), 4], - ] + ], + 'appDisplayModule' ) ); @@ -1347,8 +1650,9 @@ function _getBehaviorOptionList() { _('Active Icons in Folder Preview'), _('If enabled, icons in the folder review behaves like normal icons, you can activate or even drag them directly, without having to open the folder first'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'appGridActivePreview' + 'appGridActivePreview', + null, + 'appDisplayModule' ) ); @@ -1357,8 +1661,9 @@ function _getBehaviorOptionList() { _('Center Open Folders'), _('App folder may open in the center of the screen or above the source folder icon'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'appGridFolderCenter' + 'appGridFolderCenter', + null, + 'appDisplayModule' ) ); @@ -1367,8 +1672,9 @@ function _getBehaviorOptionList() { _('Allow Incomplete Pages'), _('If disabled, icons from the next page (if any) are automatically moved to fill any empty slot left after an icon was (re)moved (to a folder for example)'), itemFactory.newSwitch(), - // itemFactory.newDropDown(), - 'appGridIncompletePages' + 'appGridIncompletePages', + null, + 'appDisplayModule' ) ); @@ -1376,14 +1682,15 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('App Labels Behavior'), _('Choose how and when to display app names'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridNamesMode', [ [_('Ellipsized - Expand Selected (Default)'), 0], [_('Always Expanded'), 1], [_('Hidden - Show Selected Only'), 2], - ] + ], + 'appDisplayModule' ) ); @@ -1391,7 +1698,7 @@ function _getBehaviorOptionList() { _('Reset App Grid Layout'), _('Removes all stored app grid positions, after reset icons will be sorted alphabetically, except folder contents'), itemFactory.newResetButton(() => { - const settings = ExtensionUtils.getSettings('org.gnome.shell'); + const settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); settings.set_value('app-picker-layout', new GLib.Variant('aa{sv}', [])); }) )); @@ -1400,7 +1707,7 @@ function _getBehaviorOptionList() { _('Remove App Grid Folders'), _('Removes all folders, folder apps will move to the root grid'), itemFactory.newResetButton(() => { - const settings = ExtensionUtils.getSettings('org.gnome.desktop.app-folders'); + const settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' }); settings.set_strv('folder-children', []); }) )); @@ -1418,21 +1725,29 @@ function _getBehaviorOptionList() { ) );*/ - optionList.push( + /* optionList.push( itemFactory.getRowWidget( _('Enable Window Search Provider'), _('Activates the window search provider that adds open windows to the search results. You can search app names and window titles. You can also use "wq//" prefix (also by pressing the Space hotkey in the overview, or clicking dash icon) to suppress results from other search providers'), itemFactory.newSwitch(), 'searchWindowsEnable' ) - ); + );*/ optionList.push( itemFactory.getRowWidget( - _('Enable Recent Files Search Provider'), - _('Activates the recent files search provider that can be triggered by a dash icon, Ctrl + Space hotkey or by typing "fq//" prefix in the search entry field. This option needs File History option enabled in the GNOME Privacy settings'), - itemFactory.newSwitch(), - 'searchRecentFilesEnable' + _('Window Search Provider - Sorting'), + _('Choose the window sorting method'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'searchWindowsOrder', + [ + [_('Most Recently Used (MRU)'), 0], + [_('MRU - Current Workspace First'), 1], + [_('MRU - By Workspaces'), 2], + [_('Stable Sequence - By Workspaces'), 3], + ], + 'windowSearchProviderModule' ) ); @@ -1489,8 +1804,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('App Grid Animation'), _('When entering the App Grid view, the app grid animates from the edge of the screen. You can choose the direction, keep the Default (direction will be selected automatically) or disable the animation if you don\'t like it'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'appGridAnimation', [ [_('Default'), 4], @@ -1507,8 +1822,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Search View Animation'), _('When search is activated the search view with search results can animate from the edge of the screen. You can choose the direction, keep the Default (currently Bottom to Top) or disable the animation if you don\'t like it.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'searchViewAnimation', [ [_('Default'), 4], @@ -1526,8 +1841,8 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Workspace Preview Animation'), _('When entering / leaving the App Grid / Search view, the workspace preview can animate to/from workspace thumbnail.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'workspaceAnimation', [ [_('Disable'), 0], @@ -1543,17 +1858,36 @@ function _getBehaviorOptionList() { ) ); + optionList.push( + itemFactory.getRowWidget( + _('Wraparound'), + _('Continue from the last workspace to the first and vice versa'), + itemFactory.newSwitch(), + 'wsSwitcherWraparound' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Ignore Last (empty) Workspace'), + _('In Dynamic workspaces mode, there is always one empty workspace at the end. Switcher can ignore this last workspace'), + itemFactory.newSwitch(), + 'wsSwitcherIgnoreLast' + ) + ); + optionList.push( itemFactory.getRowWidget( _('Workspace Switcher Animation'), _('Allows you to disable movement of the desktop background during workspace switcher animation outside of the overview. The Static Background mode also keeps Conky and desktop icons on their place during switching.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'workspaceSwitcherAnimation', [ [_('Default'), 0], [_('Static Background'), 1], - ] + ], + 'workspaceAnimationModule' ) ); @@ -1561,18 +1895,18 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Workspace Switcher Popup Mode'), _('This popup shows up when you switch workspace using a keyboard shortcut or gesture outside of the overview. You can to disable the popup at all, or show it on the current monitor (the one with mouse pointer) instead of the primary.'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'wsSwPopupMode', [ [_('Disable'), 0], [_('Show on Primary Monitor (Default)'), 1], [_('Show on Current Monitor'), 2], - ] + ], + 'workspaceSwitcherPopupModule' ) ); - optionList.push( itemFactory.getRowWidget( _('Notifications') @@ -1583,14 +1917,15 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Window Attention Handler'), _('When a window requires attention (often a new window), GNOME Shell shows you a notification about it. You can disable popups of these messages (notification will be pushed into the message tray silently) or focus the source window immediately instead'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'windowAttentionMode', [ [_('Show Notifications (Default)'), 0], [_('Disable Notification Popups'), 1], [_('Immediately Focus Window'), 2], - ] + ], + 'windowAttentionHandlerModule' ) ); @@ -1598,108 +1933,57 @@ function _getBehaviorOptionList() { itemFactory.getRowWidget( _('Favorites'), _('Disable pin/unpin app notifications'), - itemFactory.newComboBox(), - // itemFactory.newDropDown(), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), 'favoritesNotify', [ [_('Show Notifications (Default)'), 1], [_('Disable Notifications'), 0], - ] - ) - ); - - return optionList; -} - -function _getProfilesOptionList() { - const optionList = []; - // options item format: - // [text, caption, widget, settings-variable, options for combo] - - optionList.push( - itemFactory.getRowWidget( - _('Custom Profiles'), - _('Sets of settings that can help you with the initial customization') - ) - ); - - optionList.push(itemFactory.getRowWidget( - _('Profile 1'), - null, - itemFactory.newPresetButton(gOptions, 1) - )); - - optionList.push(itemFactory.getRowWidget( - _('Profile 2'), - null, - itemFactory.newPresetButton(gOptions, 2) - )); - - optionList.push(itemFactory.getRowWidget( - _('Profile 3'), - null, - itemFactory.newPresetButton(gOptions, 3) - )); - - optionList.push(itemFactory.getRowWidget( - _('Profile 4'), - null, - itemFactory.newPresetButton(gOptions, 4) - )); - - return optionList; -} - -function _getMiscOptionList() { - const optionList = []; - // options item format: - // [text, caption, widget, settings-variable, options for combo] - - optionList.push( - itemFactory.getRowWidget( - _('Keyboard') - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('Override Page Up/Down Shortcuts'), - _('This option automatically overrides the (Shift +) Super + Page Up/Down keyboard shortcuts for the current workspace orientation. If you encounter any issues, check the configuration in the dconf editor'), - itemFactory.newSwitch(), - 'enablePageShortcuts' - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('Compatibility') - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('Fix for Dash to Dock'), - _('With the default Ubuntu Dock and other Dash To Dock forks, you may experience issues with Activities overview after you change Dock position or change monitors configuration. This option is enabled automatically if a replacement for the Dash is detected'), - itemFactory.newSwitch(), - 'fixUbuntuDock' - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('V-Shell Modules that can be disabled in case of conflict or misbehavior:') - ) - ); - - optionList.push( - itemFactory.getRowWidget( - _('AppFavorites'), - _('Pin/unpin app notification options'), - itemFactory.newSwitch(), + ], 'appFavoritesModule' ) ); + return optionList; +} + +function _getModulesOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + optionList.push( + itemFactory.getRowWidget( + _('V-Shell Modules (allows you to disable modules that conflict with another extension)') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('WindowSearchProvider'), + _('Activates the window search provider that adds open windows to the search results. You can search app names and window titles. You can also use "wq//" prefix (also by pressing the Space hotkey in the overview, or clicking dash icon) to suppress results from other search providers'), + itemFactory.newSwitch(), + 'windowSearchProviderModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('RecentFilesSearchProvider'), + _('Activates the recent files search provider that can be triggered by a dash icon, Ctrl + Space hotkey or by typing "fq//" prefix in the search entry field. This option needs File History option enabled in the GNOME Privacy settings'), + itemFactory.newSwitch(), + 'recentFilesSearchProviderModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('ExtensionsSearchProvider'), + _('Activates the extensions search provider that adds extensions to the search results. You can also use "eq//" prefix (also by pressing the Ctrl + Shift + Space hotkey in the overview, or clicking dash icon) to suppress results from other search providers'), + itemFactory.newSwitch(), + 'extensionsSearchProviderModule' + ) + ); + optionList.push( itemFactory.getRowWidget( _('AppDisplay / IconGrid'), @@ -1709,6 +1993,15 @@ function _getMiscOptionList() { ) ); + optionList.push( + itemFactory.getRowWidget( + _('AppFavorites'), + _('Pin/unpin app notification options'), + itemFactory.newSwitch(), + 'appFavoritesModule' + ) + ); + optionList.push( itemFactory.getRowWidget( _('Dash'), @@ -1772,6 +2065,15 @@ function _getMiscOptionList() { ) ); + optionList.push( + itemFactory.getRowWidget( + _('SearchController'), + _('Escape key behavior options in the overview'), + itemFactory.newSwitch(), + 'searchControllerModule' + ) + ); + optionList.push( itemFactory.getRowWidget( _('SwipeTracker'), @@ -1786,7 +2088,7 @@ function _getMiscOptionList() { _('WindowAttentionHandler'), _('Window attention handler options'), itemFactory.newSwitch(), - 'winAttentionHandlerModule' + 'windowAttentionHandlerModule' ) ); @@ -1808,6 +2110,15 @@ function _getMiscOptionList() { ) ); + optionList.push( + itemFactory.getRowWidget( + _('WindowThumbnail'), + _('Create Window Thumbnail (PIP) option in the app icon menu and window preview actions'), + itemFactory.newSwitch(), + 'windowThumbnailModule' + ) + ); + optionList.push( itemFactory.getRowWidget( _('Workspace'), @@ -1838,17 +2149,89 @@ function _getMiscOptionList() { return optionList; } -function _getAboutOptionList() { +function _getMiscOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + + optionList.push( + itemFactory.getRowWidget( + _('Keyboard') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Override Page Up/Down Shortcuts'), + _('This option automatically overrides the (Shift +) Super + Page Up/Down keyboard shortcuts for the current workspace orientation. If you encounter any issues, check the configuration in the dconf editor'), + itemFactory.newSwitch(), + 'enablePageShortcuts' + ) + ); + + /* optionList.push( + itemFactory.getRowWidget( + _('Compatibility') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Improve compatibility with Dash to Dock'), + _('With the default Ubuntu Dock and other Dash To Dock forks, you may experience issues with Activities overview after you change Dock position or re-enable the extension. This option is enabled automatically if a replacement for the Dash is detected. In any case, using Dash to Dock extension with V-Shell is problematic and not recommended.'), + itemFactory.newSwitch(), + 'fixUbuntuDock' + ) + );*/ + + optionList.push( + itemFactory.getRowWidget( + _('Performance') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Smooth App Grid Animations'), + _('This option allows V-Shell to pre-realize app grid and app folders during session startup in order to avoid stuttering animations when using them for the first time. If enabled, the session startup needs a little bit more time to finish and necessary memory will be allocated at this time'), + itemFactory.newSwitch(), + 'appGridPerformance' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workarounds') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Fix New Window Not In Focus'), + _('If you often find that the app window you open from the Activities overview does not get focus, try enabling this option.'), + itemFactory.newSwitch(), + 'newWindowFocusFix' + ) + ); + + return optionList; +} + +function _getAboutOptionList(itemFactory) { const optionList = []; optionList.push(itemFactory.getRowWidget( Me.metadata.name )); + const versionName = Me.metadata['version-name'] ?? ''; + let version = Me.metadata['version'] ?? ''; + version = versionName && version ? `/${version}` : version; + const versionStr = `${versionName}${version}`; optionList.push(itemFactory.getRowWidget( _('Version'), null, - itemFactory.newLabel(Me.metadata.version.toString()) + itemFactory.newLabel(versionStr) )); optionList.push(itemFactory.getRowWidget( diff --git a/extensions/44/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml b/extensions/44/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml new file mode 100644 index 0000000..0a2ae51 --- /dev/null +++ b/extensions/44/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml @@ -0,0 +1,408 @@ + + + + + 0 + + + false + + + 2 + + + 350 + + + 95 + + + 100 + + + false + + + 2 + + + 0 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + 3 + + + false + + + 0 + + + 2 + + + false + + + false + + + true + + + false + + + 13 + + + 13 + + + 13 + + + 48 + + + 85 + + + 0 + + + 18 + + + true + + + 1 + + + false + + + true + + + false + + + 30 + + + true + + + 0 + + + 40 + + + 60 + + + 30 + + + false + + + 4 + + + 0 + + + 1 + + + 1 + + + 100 + + + 1 + + + 0 + + + 2 + + + 0 + + + 96 + + + 104 + + + -1 + + + 0 + + + 0 + + + 0 + + + 0 + + + 2 + + + -1 + + + 0 + + + 0 + + + 12 + + + true + + + 1 + + + 3 + + + false + + + false + + + 90 + + + 1 + + + 1 + + + false + + + 1 + + + true + + + 1 + + + false + + + 5 + + + 0 + + + 0 + + + 0 + + + 50 + + + 95 + + + 1 + + + false + + + false + + + 1 + + + 2 + + + 6 + + + 1 + + + 6 + + + true + + + true + + + false + + + 2 + + + 0 + + + true + + + 1 + + + 1 + + + 1 + + + 0 + + + false + + + true + + + 15 + + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + + false + + + + "" + + + {'workspaceThumbnailsPosition': '1', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '3', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '-80', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '-80', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'dashShowExtensionsIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '13', 'secWsThumbnailScale': '13', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'dashBgOpacity': '80', 'dashBgColor': '0', 'dashBgRadius': '0', 'dashBgGS3Style': 'true', 'runningDotStyle': '1', 'enablePageShortcuts': 'false', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'false', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '30', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '40', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '0', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '100', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '1', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '0', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '3', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'} + + + + "" + + + {'workspaceThumbnailsPosition': '5', 'wsMaxSpacing': '80', 'wsPreviewScale': '100', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '0', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'dashShowExtensionsIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '5', 'wsThumbnailScaleAppGrid': '15', 'secWsThumbnailScale': '5', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'dashBgOpacity': '20', 'dashBgColor': '1', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'false', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '50', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '50', 'appGridBgBlurSigma': '40', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '0', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'true', 'appGridPageWidthScale': '90', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '1', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'} + + + + "" + + + {'workspaceThumbnailsPosition': '0', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '0', 'dashPositionAdjust': '-100', 'wsTmbPositionAdjust': '-100', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'dashShowExtensionsIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '0', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '13', 'secWsThumbnailScale': '13', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'dashBgOpacity': '20', 'dashBgColor': '1', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'false', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '60', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '30', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '2', 'overviewMode': '1', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '90', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '0', 'notificationPosition': '2', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '1', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'} + + + + "" + + + {'workspaceThumbnailsPosition': '6', 'wsMaxSpacing': '65', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'dashShowExtensionsIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '10', 'wsThumbnailScaleAppGrid': '10', 'secWsThumbnailScale': '10', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'dashBgOpacity': '100', 'dashBgColor': '0', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '60', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '80', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '2', 'overviewMode': '2', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '90', 'appGridSpacing': '5', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '0', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'} + + + + diff --git a/extensions/vertical-workspaces/screenshots/screenshot.jpg b/extensions/44/vertical-workspaces/screenshots/screenshot.jpg similarity index 100% copy from extensions/vertical-workspaces/screenshots/screenshot.jpg copy to extensions/44/vertical-workspaces/screenshots/screenshot.jpg diff --git a/extensions/vertical-workspaces/screenshots/screenshot0.jpg b/extensions/44/vertical-workspaces/screenshots/screenshot0.jpg similarity index 100% copy from extensions/vertical-workspaces/screenshots/screenshot0.jpg copy to extensions/44/vertical-workspaces/screenshots/screenshot0.jpg diff --git a/extensions/vertical-workspaces/screenshots/screenshot0.png b/extensions/44/vertical-workspaces/screenshots/screenshot0.png similarity index 100% copy from extensions/vertical-workspaces/screenshots/screenshot0.png copy to extensions/44/vertical-workspaces/screenshots/screenshot0.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot1.png b/extensions/44/vertical-workspaces/screenshots/screenshot1.png similarity index 100% copy from extensions/vertical-workspaces/screenshots/screenshot1.png copy to extensions/44/vertical-workspaces/screenshots/screenshot1.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot2.png b/extensions/44/vertical-workspaces/screenshots/screenshot2.png similarity index 100% copy from extensions/vertical-workspaces/screenshots/screenshot2.png copy to extensions/44/vertical-workspaces/screenshots/screenshot2.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot3.png b/extensions/44/vertical-workspaces/screenshots/screenshot3.png similarity index 100% copy from extensions/vertical-workspaces/screenshots/screenshot3.png copy to extensions/44/vertical-workspaces/screenshots/screenshot3.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot4.png b/extensions/44/vertical-workspaces/screenshots/screenshot4.png similarity index 100% copy from extensions/vertical-workspaces/screenshots/screenshot4.png copy to extensions/44/vertical-workspaces/screenshots/screenshot4.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot5.png b/extensions/44/vertical-workspaces/screenshots/screenshot5.png similarity index 100% copy from extensions/vertical-workspaces/screenshots/screenshot5.png copy to extensions/44/vertical-workspaces/screenshots/screenshot5.png diff --git a/extensions/vertical-workspaces/screenshots/vertical-workspaces.gif b/extensions/44/vertical-workspaces/screenshots/vertical-workspaces.gif similarity index 100% copy from extensions/vertical-workspaces/screenshots/vertical-workspaces.gif copy to extensions/44/vertical-workspaces/screenshots/vertical-workspaces.gif diff --git a/extensions/44/vertical-workspaces/stylesheet.css b/extensions/44/vertical-workspaces/stylesheet.css new file mode 100644 index 0000000..2febbb7 --- /dev/null +++ b/extensions/44/vertical-workspaces/stylesheet.css @@ -0,0 +1,307 @@ +/* +* V-Shell (Vertical Workspaces) +* stylesheet.css +*/ + +/* General dash */ +#dash.vertical { + margin: 0px; + padding: 0px; +} + +#dash.vertical .app-well-app, +#dash.vertical .show-apps { + /* left/right padding exceeds dash bg by 6px to + cover spacing between dash and the edge of the screen + so the icons will be selectable even at the edge + this spacing must be accounted for in overview allocate() */ + padding-right: 16px; + padding-left: 16px; + /*spacing between icons*/ + padding-top: 1px; + padding-bottom: 1px; + margin: 0px; +} + +#dash.vertical .dash-separator { + height: 1px; + margin-right: 2px; + margin-left: 2px; + margin-top: 4px; + margin-bottom: 4px; + background-color: transparentize(#eeeeec, 0.7); +} + +#dash.vertical .overview-icon { + padding: 5px 0; +} + +#dash.vertical .app-well-app-running-dot { + margin: 4px 0px; +} + +#dash.vertical .app-well-app-running-dot-custom { + margin: 4px 0px; + width: 2px; + height: 16px; +} + +#dash.vertical .dash-background { + margin: 0px; +} + +#dash.vertical-gs3-left .dash-background { + border-radius: 0 18px 18px 0; + border-left: 0px; + padding: 8px 12px 8px 4px; + margin-left: 0; +} + +#dash.vertical-gs3-right .dash-background { + border-radius: 18px 0 0 18px; + border-right: 0px; + padding: 8px 4px 8px 12px; + margin-right: 0; +} + +#dash.vertical-gs3-left { + margin-right: 6px; + margin-left: 0px; + padding: 0px; +} + +#dash.vertical-gs3-right { + margin-right: 0px; + margin-left: 6px; + padding: 0px; +} + +#dash.vertical-gs3-left .app-well-app, +#dash.vertical-gs3-left .show-apps, +#dash.vertical-gs3-right .app-well-app, +#dash.vertical-gs3-right .show-apps { + /* left/right padding exceeds dash bg by 6px to + cover spacing between dash and the edge of the screen + so the icons will be selectable even at the edge + this spacing must be accounted for in overview allocate() */ + padding-right: 9px; + padding-left: 9px; +} + +.dash-background-light { + background-color: rgb(200, 200, 200); + border-color: rgba(150, 150, 150, 0.4); +} + +.dash-background-reduced { + padding: 10px; +} + +.app-well-app-running-dot { + margin-bottom: 12px; +} + +.app-well-app-running-dot-custom { + margin-bottom: 12px; + width: 16px; + height: 2px; +} + +/* add shadow to the app grid app label to be readable if it overlaps light icon below */ +.overview-icon-with-label, .folder-name-label { + text-shadow: 1px 1px 3px rgba(33, 33, 33, 0.5); +} + +/* adjustment for the vertical ws switcher indicator popup*/ +.ws-switcher-indicator { + padding: 3px; + margin: 5px; +} + +.ws-switcher-indicator:active { + padding: 5px; + margin: 3px; +} + +/* ws thumbnails captions */ +.ws-tmb-label { + padding: 2px; + color: rgb(255, 255, 255); + background-color: rgba(40,40,40,0.8); + text-align: center; +} + +.workspace-thumbnail-indicator { + border-radius: 6px; +} + +.ws-tmb-labeled { + border: 0px; +} + +.ws-tmb-transparent { + border: 0px; + background-color: rgba(200, 200, 200, 0.2); +} + +/* app grid page indicatos */ +.page-indicator-icon { + margin: 10px 10px 10px 10px; +} + +.page-indicator { + padding: 0px; +} + +/* GS 43 App Grid - indicators that show up when dragging icon */ +.prevPageIndicator { + background: rgba(255, 255, 255, 0.1); + background-gradient-start: rgba(255, 255, 255, 0.1); + background-gradient-end: transparent; + background-gradient-direction: vertical; + border-radius: 100px 100px 0px 0px; +} +.nextPageIndicator { + background: rgba(255, 255, 255, 0.1); + background-gradient-start: transparent; + background-gradient-end: rgba(255, 255, 255, 0.1); + background-gradient-direction: vertical; + border-radius: 0px 0px 100px 100px; + +} +/* +.search-entry{ + background-color: rgba(200, 200, 200, 0.1); + color: white; + border-color: rgba(160, 160, 160, 0.4); +} + +.search-entry:hover, +.search-entry:focus { + background-color: rgba(200, 200, 200, 0.2); +}*/ + +.search-entry-om2 { + color: white; + background-color: rgba(40, 40, 40, 1); +} + +.search-entry-om2:hover, +.search-entry-om2:focus { + background-color: rgba(50, 50, 50, 1); +} + +/* for static ws mode */ +/*.search-section-content-bg,*/ +.search-section-content-bg-om2 { + border-radius: 26px; + border: 1px, rgb(60, 60, 60); + padding-top: 15px; +} + +.search-section-content-bg-om2 { + background-color: rgb(40, 40, 40); +} + +.search-section-content { + background-color: rgba(200, 200, 200, 0.1); +} + +/* "no results" / "searching..." text*/ +.search-statustext, .search-statustext-om2 { + background-color: rgba(200, 200, 200, 0.1); + color: white; + margin-top: 50px; + padding: 30px; + border-radius: 20px; + text-shadow: 0px 0px 5px rgb(23, 23, 23); +} + +.search-statustext-om2 { + background-color: rgb(40, 40, 40); + border: 1px rgba(200, 200, 200, 0.1); +} + +#panel:overview, .transparent-panel { + background-color: transparent; +} + +/* reduce spacing between app icons in search results */ +.grid-search-results { + spacing: 4px; +} + +/* hide vertical scroll bar, it's distracting in the search results */ +StButton#vhandle { + background-color: transparent; +} + +.show-apps-icon-horizontal-hide { + width: 0; + margin: 0; + spacing: 0; +} + +.show-apps-icon-vertical-hide { + height: 0; + margin: 0; + spacing: 0; +} + +.workspace-close-button { + color: white; + background-color: dimgrey; + width: 18px; + height: 18px; + padding: 2px; + margin: 2px; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); + border: 0px; + border-radius: 35px; +} + +.workspace-close-button-hover { + background-color: rgb(255, 0, 0); +} + +/* ws tmb placeholder */ +.placeholder-vertical { + background-size: contain; + height: 18px; +} + +/* reduce size of hot corner ripples to half */ +.ripple-box { + width: 26px; + height: 26px; +} + +.osd-window { + margin: 4em; +} + +.app-folder .overview-icon, +.edit-folder-button, +.folder-name-entry { + background-color: rgba(200, 200, 200, 0.08); +} + +.app-folder-dialog-vshell { + background-color: rgba(200, 200, 200, 0.08); +} + +.app-folder-dialog { + border-color: rgba(160, 160, 160, 0.3); +} + +.edit-folder-button:hover, +.app-folder:hover .overview-icon, +.app-folder:focus .overview-icon { + background-color: rgba(200, 200, 200, 0.15); +} + +/* reduce quick menu buttons height in GS44 */ +/*.quick-toggle, +.quick-menu-toggle { + min-height: 42px; +}*/ diff --git a/extensions/disable-workspace-switcher.mk b/extensions/45/disable-workspace-switcher.mk similarity index 100% copy from extensions/disable-workspace-switcher.mk copy to extensions/45/disable-workspace-switcher.mk diff --git a/extensions/disable-workspace-switcher/LICENSE b/extensions/45/disable-workspace-switcher/LICENSE similarity index 100% copy from extensions/disable-workspace-switcher/LICENSE copy to extensions/45/disable-workspace-switcher/LICENSE diff --git a/extensions/disable-workspace-switcher/README.md b/extensions/45/disable-workspace-switcher/README.md similarity index 100% rename from extensions/disable-workspace-switcher/README.md rename to extensions/45/disable-workspace-switcher/README.md diff --git a/extensions/45/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js b/extensions/45/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js new file mode 100644 index 0000000..a814f54 --- /dev/null +++ b/extensions/45/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/extension.js @@ -0,0 +1,14 @@ +import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'; +import { WorkspaceSwitcherPopup } from 'resource:///org/gnome/shell/ui/workspaceSwitcherPopup.js'; + +export default class extends Extension { + enable() { + this._originalWorkspaceSwitcherPopup_display = WorkspaceSwitcherPopup.prototype.display; + WorkspaceSwitcherPopup.prototype.display = () => {}; + } + + disable() { + WorkspaceSwitcherPopup.prototype.display = this._originalWorkspaceSwitcherPopup_display; + this._originalWorkspaceSwitcherPopup_display = null; + } +} diff --git a/extensions/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json b/extensions/45/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json similarity index 90% rename from extensions/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json rename to extensions/45/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json index 68f10c5..6c83096 100644 --- a/extensions/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json +++ b/extensions/45/disable-workspace-switcher/disable-workspace-switcher@jbradaric.me/metadata.json @@ -5,8 +5,6 @@ "uuid": "disable-workspace-switcher@jbradaric.me", "url": "https://github.com/jbradaric/disable-workspace-switcher", "shell-version": [ - "42", - "43", - "44" + "45" ] } diff --git a/extensions/hibernate-status.mk b/extensions/45/hibernate-status.mk similarity index 100% rename from extensions/hibernate-status.mk rename to extensions/45/hibernate-status.mk diff --git a/extensions/45/hibernate-status/.editorconfig b/extensions/45/hibernate-status/.editorconfig new file mode 100644 index 0000000..bf58f2f --- /dev/null +++ b/extensions/45/hibernate-status/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +end_of_line = lf +insert_final_newline = true + +[*.js] +quote_type = single diff --git a/extensions/45/hibernate-status/.github/workflows/release.yaml b/extensions/45/hibernate-status/.github/workflows/release.yaml new file mode 100644 index 0000000..80138f9 --- /dev/null +++ b/extensions/45/hibernate-status/.github/workflows/release.yaml @@ -0,0 +1,32 @@ +name: "Prepare GitHub release" + +on: + push: + +jobs: + zip: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: zip + run: | + sudo apt-get install make gettext + make zip-file + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: zip + path: "*.zip" + - name: Release (pre) + uses: softprops/action-gh-release@v1 + with: + files: "*.zip" + prerelease: true + if: contains(github.ref_name,'rc') && github.ref_type == 'tag' + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: "*.zip" + if: "!contains(github.ref_name,'rc') && github.ref_type == 'tag'" diff --git a/extensions/hibernate-status/.gitignore b/extensions/45/hibernate-status/.gitignore similarity index 100% rename from extensions/hibernate-status/.gitignore rename to extensions/45/hibernate-status/.gitignore diff --git a/extensions/45/hibernate-status/.prettierrc b/extensions/45/hibernate-status/.prettierrc new file mode 100644 index 0000000..8da2b4c --- /dev/null +++ b/extensions/45/hibernate-status/.prettierrc @@ -0,0 +1,8 @@ +tabWidth: 4 +useTabs: false +semi: true +singleQuote: true +quoteProps: 'as-needed' +trailingComma: 'es5' +bracketSpacing: false +arrowParens: 'avoid' diff --git a/extensions/hibernate-status/LICENSE b/extensions/45/hibernate-status/LICENSE similarity index 100% rename from extensions/hibernate-status/LICENSE rename to extensions/45/hibernate-status/LICENSE diff --git a/extensions/hibernate-status/Makefile b/extensions/45/hibernate-status/Makefile similarity index 96% rename from extensions/hibernate-status/Makefile rename to extensions/45/hibernate-status/Makefile index 80f5796..f74ed54 100644 --- a/extensions/hibernate-status/Makefile +++ b/extensions/45/hibernate-status/Makefile @@ -1,7 +1,7 @@ # Basic Makefile UUID = hibernate-status@dromi -BASE_MODULES = extension.js metadata.json confirmDialog.js LICENSE README.md +BASE_MODULES = extension.js metadata.json LICENSE README.md EXTRA_MODULES = prefs.js TOLOCALIZE = confirmDialog.js prefs.js PO_FILES := $(wildcard ./locale/*/*/*.po) diff --git a/extensions/hibernate-status/README.md b/extensions/45/hibernate-status/README.md similarity index 91% rename from extensions/hibernate-status/README.md rename to extensions/45/hibernate-status/README.md index ff88abd..54d5fad 100644 --- a/extensions/hibernate-status/README.md +++ b/extensions/45/hibernate-status/README.md @@ -2,9 +2,9 @@ Gnome Shell extension that adds a hibernate/hybrid suspend button in Status menu. -Originally developed by [@arelange](https://github.com/arelange); now maintained by [@p91paul](https://github.com/p91paul). +Originally developed by [@arelange](https://github.com/arelange) and then [@p91paul](https://github.com/p91paul). Now maintained by [@slaclau](https://github.com/slaclau). -Supports GNOME 3.36. +Supports GNOME 45. ## FAQ diff --git a/extensions/hibernate-status/buildforupload.sh b/extensions/45/hibernate-status/buildforupload.sh similarity index 100% rename from extensions/hibernate-status/buildforupload.sh rename to extensions/45/hibernate-status/buildforupload.sh diff --git a/extensions/45/hibernate-status/extension.js b/extensions/45/hibernate-status/extension.js new file mode 100644 index 0000000..d0c7708 --- /dev/null +++ b/extensions/45/hibernate-status/extension.js @@ -0,0 +1,573 @@ +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; +import Clutter from 'gi://Clutter'; + +import * as LoginManager from 'resource:///org/gnome/shell/misc/loginManager.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as StatusSystem from 'resource:///org/gnome/shell/ui/status/system.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; +import * as ExtensionSystem from 'resource:///org/gnome/shell/ui/extensionSystem.js'; +import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js'; +import * as Dialog from 'resource:///org/gnome/shell/ui/dialog.js'; +import * as CheckBoxImport from 'resource:///org/gnome/shell/ui/checkBox.js'; +import {loadInterfaceXML} from 'resource:///org/gnome/shell/misc/fileUtils.js'; + +const CheckBox = CheckBoxImport.CheckBox; +// Use __ () and N__() for the extension gettext domain, and reuse +// the shell domain with the default _() and N_() +import {Extension, gettext as __} from 'resource:///org/gnome/shell/extensions/extension.js'; +export {__}; +const N__ = function (e) { + return e; +}; + +const HIBERNATE_CHECK_TIMEOUT = 20000; + +export default class HibernateButtonExtension extends Extension { + _loginManagerCanHibernate(asyncCallback) { + if (this._loginManager._proxy) { + // systemd path + this._loginManager._proxy.call( + 'CanHibernate', + null, + Gio.DBusCallFlags.NONE, + -1, + null, + function (proxy, asyncResult) { + let result, error; + + try { + result = proxy.call_finish(asyncResult).deep_unpack(); + } catch (e) { + error = e; + } + + if (error) asyncCallback(false); + else asyncCallback(!['no', 'na'].includes(result[0])); + } + ); + } else { + this.can_hibernate_sourceID = GLib.idle_add(() => { + asyncCallback(false); + return false; + }); + } + } + + _loginManagerHibernate() { + if (this._setting.get_boolean('hibernate-works-check')) { + this._hibernateStarted = new Date(); + this.hibernate_sourceID = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + HIBERNATE_CHECK_TIMEOUT, + () => this._checkDidHibernate() + ); + } + if (this._loginManager._proxy) { + // systemd path + this._loginManager._proxy.call( + 'Hibernate', + GLib.Variant.new('(b)', [true]), + Gio.DBusCallFlags.NONE, + -1, + null, + null + ); + } else { + // Can't do in ConsoleKit + this._loginManager.emit('prepare-for-sleep', true); + this._loginManager.emit('prepare-for-sleep', false); + } + } + + _loginManagerCanHybridSleep(asyncCallback) { + if (this._loginManager._proxy) { + // systemd path + this._loginManager._proxy.call( + 'CanHybridSleep', + null, + Gio.DBusCallFlags.NONE, + -1, + null, + function (proxy, asyncResult) { + let result, error; + + try { + result = proxy.call_finish(asyncResult).deep_unpack(); + } catch (e) { + error = e; + } + + if (error) asyncCallback(false); + else asyncCallback(!['no', 'na'].includes(result[0])); + } + ); + } else { + this.can_hybrid_sleep_sourceID = GLib.idle_add(() => { + asyncCallback(false); + return false; + }); + } + } + + _loginManagerHybridSleep() { + if (this._loginManager._proxy) { + // systemd path + this._loginManager._proxy.call( + 'HybridSleep', + GLib.Variant.new('(b)', [true]), + Gio.DBusCallFlags.NONE, + -1, + null, + null + ); + } else { + // Can't do in ConsoleKit + this._loginManager.emit('prepare-for-sleep', true); + this._loginManager.emit('prepare-for-sleep', false); + } + } + + _loginManagerCanSuspendThenHibernate(asyncCallback) { + if (this._loginManager._proxy) { + // systemd path + this._loginManager._proxy.call( + 'CanSuspendThenHibernate', + null, + Gio.DBusCallFlags.NONE, + -1, + null, + function (proxy, asyncResult) { + let result, error; + + try { + result = proxy.call_finish(asyncResult).deep_unpack(); + } catch (e) { + error = e; + } + + if (error) asyncCallback(false); + else asyncCallback(!['no', 'na'].includes(result[0])); + } + ); + } else { + this.can_suspend_then_hibernate_sourceID = GLib.idle_add(() => { + asyncCallback(false); + return false; + }); + } + } + + _loginManagerSuspendThenHibernate() { + if (this._loginManager._proxy) { + // systemd path + this._loginManager._proxy.call( + 'SuspendThenHibernate', + GLib.Variant.new('(b)', [true]), + Gio.DBusCallFlags.NONE, + -1, + null, + null + ); + } else { + // Can't do in ConsoleKit + this._loginManager.emit('prepare-for-sleep', true); + this._loginManager.emit('prepare-for-sleep', false); + } + } + + _updateHaveHibernate() { + this._loginManagerCanHibernate(result => { + log(`Able to hibernate: ${result}`); + this._haveHibernate = result; + this._updateHibernate(); + }); + } + + _updateHibernate() { + this._hibernateMenuItem.visible = + this._haveHibernate && !Main.sessionMode.isLocked && this._setting.get_boolean('show-hibernate'); + } + + _updateHaveHybridSleep() { + this._loginManagerCanHybridSleep(result => { + log(`Able to hybrid-sleep: ${result}`); + this._haveHybridSleep = result; + this._updateHybridSleep(); + }); + } + + _updateHybridSleep() { + this._hybridSleepMenuItem.visible = + this._haveHybridSleep && !Main.sessionMode.isLocked && this._setting.get_boolean('show-hybrid-sleep'); + } + + _updateHaveSuspendThenHibernate() { + this._loginManagerCanSuspendThenHibernate(result => { + log(`Able to suspend then hibernate: ${result}`); + this._haveSuspendThenHibernate = result; + this._updateSuspendThenHibernate(); + }); + } + + _updateSuspendThenHibernate() { + this._suspendThenHibernateMenuItem.visible = + this._haveSuspendThenHibernate && !Main.sessionMode.isLocked && this._setting.get_boolean('show-suspend-then-hibernate'); + } + + _updateDefaults() { + console.log("Update defaults"); + } + + _onHibernateClicked() { + this.systemMenu._systemItem.menu.itemActivated(); + + if (this._setting.get_boolean('show-hibernate-dialog')) { + let HibernateDialogContent = { + subject: C_('title', __('Hibernate')), + description: __('Do you really want to hibernate the system?'), + confirmButtons: [ + { + signal: 'Cancel', + label: C_('button', __('Cancel')), + key: Clutter.Escape, + }, + { + signal: 'ConfirmedHibernate', + label: C_('button', __('Hibernate')), + default: true, + }, + ], + }; + + this._dialog = new ConfirmDialog( + HibernateDialogContent + ); + this._dialog.connect('ConfirmedHibernate', () => + this._loginManagerHibernate() + ); + this._dialog.open(); + } else { + this._loginManagerHibernate() + } + } + + _onHybridSleepClicked() { + this.systemMenu._systemItem.menu.itemActivated(); + this._loginManagerHybridSleep(); + } + + _onSuspendThenHibernateClicked() { + this.systemMenu._systemItem.menu.itemActivated(); + this._loginManagerSuspendThenHibernate(); + } + + _disableExtension() { + Main.extensionManager.disableExtension('hibernate-status@dromi') + console.log('Disabled') + } + + _cancelDisableExtension(notAgain) { + if (notAgain) this.setHibernateWorksCheckEnabled(false); + } + + _checkRequirements() { + if (GLib.access('/run/systemd/seats', 0) < 0) { + let SystemdMissingDialogContent = { + subject: C_('title', __('Hibernate button: Systemd Missing')), + description: __('Systemd seems to be missing and is required.'), + confirmButtons: [ + { + signal: 'Cancel', + label: C_('button', __('Cancel')), + key: Clutter.Escape, + }, + { + signal: 'DisableExtension', + label: C_('button', __('Disable Extension')), + default: true, + }, + ], + iconName: 'document-save-symbolic', + iconStyleClass: 'end-session-dialog-shutdown-icon', + }; + + this._dialog = new ConfirmDialog( + SystemdMissingDialogContent + ); + this._dialog.connect('DisableExtension', this._disableExtension); + this._dialog.open(); + } + } + + _checkDidHibernate() { + /* This function is called HIBERNATE_CHECK_TIMEOUT ms after + * hibernate started. If it is successful, at that point the GS + * process is already frozen; so when this function is actually + * called, way more than HIBERNATE_CHECK_TIMEOUT ms are passed*/ + if ( + new Date() - this._hibernateStarted > + HIBERNATE_CHECK_TIMEOUT + 5000 + ) { + // hibernate succeeded + return; + } + // hibernate failed + + let HibernateFailedDialogContent = { + subject: C_('title', __('Hibernate button: Hibernate failed')), + description: __( + 'Looks like hibernation failed. On some linux distributions hibernation is disabled ' + + 'because not all hardware supports it well; ' + + 'please check your distribution documentation ' + + 'on how to enable it.' + ), + checkBox: __("You are wrong, don't check this anymore!"), + confirmButtons: [ + { + signal: 'Cancel', + label: C_('button', __('Cancel')), + key: Clutter.Escape, + }, + { + signal: 'DisableExtension', + label: C_('button', __('Disable Extension')), + default: true, + }, + ], + iconName: 'document-save-symbolic', + iconStyleClass: 'end-session-dialog-shutdown-icon', + } + this._dialog = new ConfirmDialog( + HibernateFailedDialogContent + ); + this._dialog.connect('DisableExtension', this._disableExtension); + this._dialog.connect('Cancel', this._cancelDisableExtension); + this._dialog.open(); + } + + setHibernateWorksCheckEnabled(enabled) { + let key = 'hibernate-works-check'; + if (this._setting.is_writable(key)) { + if (this._setting.set_boolean(key, enabled)) { + Gio.Settings.sync(); + } else { + throw this._errorSet(key); + } + } else { + throw this._errorWritable(key); + } + } + + _modifySystemItem() { + this._setting = this.getSettings() + this._checkRequirements(); + this._loginManager = LoginManager.getLoginManager(); + this.systemMenu = Main.panel.statusArea.quickSettings._system; + this._hibernateMenuItem = new PopupMenu.PopupMenuItem(__('Hibernate')); + this._hibernateMenuItemId = this._hibernateMenuItem.connect( + 'activate', + () => this._onHibernateClicked() + ); + + this._hybridSleepMenuItem = new PopupMenu.PopupMenuItem( + __('Hybrid Sleep') + ); + this._hybridSleepMenuItemId = this._hybridSleepMenuItem.connect( + 'activate', + () => this._onHybridSleepClicked() + ); + + this._suspendThenHibernateMenuItem = new PopupMenu.PopupMenuItem( + __('Suspend then Hibernate') + ); + this._suspendThenHibernateMenuItemId = this._suspendThenHibernateMenuItem.connect( + 'activate', + () => this._onSuspendThenHibernateClicked() + ); + + let afterSuspendPosition = + this.systemMenu._systemItem.menu.numMenuItems - 5; + + this.systemMenu._systemItem.menu.addMenuItem( + this._hybridSleepMenuItem, + afterSuspendPosition + ); + this.systemMenu._systemItem.menu.addMenuItem( + this._hibernateMenuItem, + afterSuspendPosition + ); + this.systemMenu._systemItem.menu.addMenuItem( + this._suspendThenHibernateMenuItem, + afterSuspendPosition + ); + + this._menuOpenStateChangedId = this.systemMenu._systemItem.menu.connect( + 'open-state-changed', + (menu, open) => { + if (!open) return; + this._updateDefaults(); + this._updateHaveHibernate(); + this._updateHaveHybridSleep(); + this._updateHaveSuspendThenHibernate(); + } + ); + } + + _queueModifySystemItem() { + this.sourceId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { + if (!Main.panel.statusArea.quickSettings._system) + return GLib.SOURCE_CONTINUE; + + this._modifySystemItem(); + return GLib.SOURCE_REMOVE; + }); + } + + enable() { + if (!Main.panel.statusArea.quickSettings._system) { + this._queueModifySystemItem(); + } else { + this._modifySystemItem(); + } + } + + disable() { + this._setting = null; + if (this._menuOpenStateChangedId) { + this.systemMenu._systemItem.menu.disconnect( + this._menuOpenStateChangedId + ); + this._menuOpenStateChangedId = 0; + } + + if (this._suspendThenHibernateMenuItemId) { + this._suspendThenHibernateMenuItem.disconnect(this._suspendThenHibernateMenuItemId); + this._suspendThenHibernateMenuItemId = 0; + } + + if (this._hybridSleepMenuItemId) { + this._hybridSleepMenuItem.disconnect(this._hybridSleepMenuItemId); + this._hybridSleepMenuItemId = 0; + } + + if (this._hibernateMenuItemId) { + this._hibernateMenuItem.disconnect(this._hibernateMenuItemId); + this._hibernateMenuItemId = 0; + } + + if (this._suspendThenHibernateMenuItem) { + this._suspendThenHibernateMenuItem.destroy(); + this._suspendThenHibernateMenuItem = 0; + } + + if (this._hybridSleepMenuItem) { + this._hybridSleepMenuItem.destroy(); + this._hybridSleepMenuItem = 0; + } + + if (this._hibernateMenuItem) { + this._hibernateMenuItem.destroy(); + this._hibernateMenuItem = 0; + } + + if (this.sourceId) { + GLib.Source.remove(this.sourceId); + this.sourceId = null; + } + + if (this.can_suspend_then_hibernate_sourceID) { + GLib.Source.remove(this.can_suspend_then_hibernate_sourceID); + this.can_suspend_then_hibernate_sourceID = null; + } + + if (this.can_hybrid_sleep_sourceID) { + GLib.Source.remove(this.can_hybrid_sleep_sourceID); + this.can_hybrid_sleep_sourceID = null; + } + + if (this.can_hibernate_sourceID) { + GLib.Source.remove(this.can_hibernate_sourceID); + this.can_hibernate_sourceID = null; + } + + if (this.hibernate_sourceID) { + GLib.Source.remove(this.hibernate_sourceID); + this.hibernate_sourceID = null; + } + }; +} + +var ConfirmDialog = GObject.registerClass( + { + Signals: { + ConfirmedHibernate: {param_types: [GObject.TYPE_BOOLEAN]}, + DisableExtension: {param_types: [GObject.TYPE_BOOLEAN]}, + Cancel: {param_types: [GObject.TYPE_BOOLEAN]}, + }, + }, + class ConfirmDialog extends ModalDialog.ModalDialog { + _init(dialog) { + super._init({ + styleClass: 'end-session-dialog', + destroyOnClose: true, + }); + + + this._messageDialogContent = new Dialog.MessageDialogContent(); + + + this._messageDialogContent.description = dialog.description; + this._messageDialogContent.title = dialog.subject; + + if (dialog.iconName) { + this._icon = new St.Icon({ + icon_name: dialog.iconName, + icon_size: _DIALOG_ICON_SIZE, + style_class: dialog.iconStyleClass, + }); + } + + if (dialog.checkBox) { + this._checkBox = new CheckBox(dialog.checkBox); + this._messageDialogContent.add(this._checkBox.actor); + } + + this.contentLayout.add_child(this._messageDialogContent); + + let buttons = []; + for (let i = 0; i < dialog.confirmButtons.length; i++) { + let signal = dialog.confirmButtons[i].signal; + let label = dialog.confirmButtons[i].label; + let keys = dialog.confirmButtons[i].key; + buttons.push({ + action: () => { + let signalId = this.connect('closed', () => { + this.disconnect(signalId); + this._confirm(signal); + }); + this.close(); + }, + label: label, + key: keys, + }); + } + + this.setButtons(buttons); + } + + _confirm(signal) { + var checked; + if (this._checkBox) checked = this._checkBox.actor.get_checked(); + this.emit(signal, checked); + } + + cancel() { + this.close(); + } + } +); + +const _DIALOG_ICON_SIZE = 32; diff --git a/extensions/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/bg/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/cs/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/de/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/es/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/fa/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/fr/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/hu/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/it/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/nl/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/oc/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/pl/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/pt/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/pt_BR/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/ru/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/uk/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po b/extensions/45/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po similarity index 100% rename from extensions/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po rename to extensions/45/hibernate-status/locale/zh_CN/LC_MESSAGES/hibernate-status-button.po diff --git a/extensions/45/hibernate-status/metadata.json b/extensions/45/hibernate-status/metadata.json new file mode 100644 index 0000000..9947c4b --- /dev/null +++ b/extensions/45/hibernate-status/metadata.json @@ -0,0 +1,9 @@ +{ + "uuid": "hibernate-status@dromi", + "name": "Hibernate Status Button", + "url": "https://github.com/arelange/gnome-shell-extension-hibernate-status", + "description": "Adds a Hibernate button in Status menu. Using Alt modifier, you can also select Hybrid Sleep instead.", + "shell-version": ["45"], + "gettext-domain": "hibernate-status-button", + "settings-schema": "org.gnome.shell.extensions.hibernate-status-button" +} diff --git a/extensions/45/hibernate-status/prefs.js b/extensions/45/hibernate-status/prefs.js new file mode 100644 index 0000000..1407a6b --- /dev/null +++ b/extensions/45/hibernate-status/prefs.js @@ -0,0 +1,171 @@ +import Gio from 'gi://Gio'; +import Gtk from 'gi://Gtk'; +import Adw from 'gi://Adw'; +// Use __() and N__() for the extension gettext domain, and reuse +// the shell domain with the default _() and N_() + +import { + ExtensionPreferences, + gettext as __, +} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; +const N__ = function (e) { + return e; +}; + +export default class Prefs extends ExtensionPreferences { + /** + * Creates a new Settings-object to access the settings of this extension. + * @private + */ + constructor(metadata) { + super(metadata); + this.KEY_HIBERNATE_WORKS_CHECK = 'hibernate-works-check'; + this._schemaName = 'org.gnome.shell.extensions.hibernate-status-button'; + this._setting = this.getSettings() + } + /** + *

Binds the given 'callback'-function to the "changed"-signal on the given + * key.

+ *

The 'callback'-function is passed an argument which holds the new + * value of 'key'. The argument is of type "GLib.Variant". Given that the + * receiver knows the internal type, use one of the get_XX()-methods to get + * it's actual value.

+ * @see http://www.roojs.com/seed/gir-1.2-gtk-3.0/gjs/GLib.Variant.html + * @param key the key to watch for changes. + * @param callback the callback-function to call. + */ + bindKey(key, callback) { + // Validate: + if (key === undefined || key === null || typeof key !== 'string') { + throw TypeError("The 'key' should be a string. Got: '" + key + "'"); + } + if ( + callback === undefined || + callback === null || + typeof callback !== 'function' + ) { + throw TypeError( + "'callback' needs to be a function. Got: " + callback + ); + } + // Bind: + this._setting.connect('changed::' + key, function (source, key) { + callback(source.get_value(key)); + }); + } + /** + * Get if check for working hibernation is enabled. The user might + * choose to disable it if we happen to be wrong. + * + * @returns bool true if we need to check if hibernation works. + */ + getHibernateWorksCheckEnabled() { + return this._setting.get_boolean(this.KEY_HIBERNATE_WORKS_CHECK); + } + /** + * Set if check for working hibernation is enabled. The user might + * choose to disable it if we happen to be wrong. + * + * @returns bool true if we need to check if hibernation works. + */ + setHibernateWorksCheckEnabled(enabled) { + let key = this.KEY_HIBERNATE_WORKS_CHECK; + if (this._setting.is_writable(key)) { + if (this._setting.set_boolean(key, enabled)) { + Gio.Settings.sync(); + } else { + throw this._errorSet(key); + } + } else { + throw this._errorWritable(key); + } + } + _errorWritable(key) { + return "The key '" + key + "' is not writable."; + } + _errorSet(key) { + return "Couldn't set the key '" + key + "'"; + } + fillPreferencesWindow(window) { + const page = new Adw.PreferencesPage({ + title: __('General'), + icon_name: 'dialog-information-symbolic', + }); + window.add(page); + + const modes_group = new Adw.PreferencesGroup({ + title: __('Modes'), + description: __('Which buttons should be enabled'), + }); + page.add(modes_group); + + const suspend_row = new Adw.SwitchRow({ + title: __('Suspend'), + subtitle: __('Not implemented yet'), + }); + modes_group.add(suspend_row); + const hibernate_row = new Adw.SwitchRow({ + title: __('Hibernate'), + }); + modes_group.add(hibernate_row); + const hybrid_row = new Adw.SwitchRow({ + title: __('Hybrid sleep'), + }); + modes_group.add(hybrid_row); + const suspend_then_hibernate_row = new Adw.SwitchRow({ + title: __('Suspend then hibernate'), + }); + modes_group.add(suspend_then_hibernate_row); + const restart_row = new Adw.SwitchRow({ + title: __('Restart...'), + subtitle: __('Not implemented yet'), + }); + modes_group.add(restart_row); + const shutdown_row = new Adw.SwitchRow({ + title: __('Shutdown...'), + subtitle: __('Not implemented yet'), + }); + modes_group.add(shutdown_row); + + const dialog_group = new Adw.PreferencesGroup({ + title: __('Dialogs'), + description: __('Which dialogs should be enabled'), + }); + page.add(dialog_group); + + const hibernate_dialog_row = new Adw.SwitchRow({ + title: __('Hibernate'), + }); + dialog_group.add(hibernate_dialog_row); + const hybrid_dialog_row = new Adw.SwitchRow({ + title: __('Hybrid sleep'), + subtitle: __('Not implemented yet'), + }); + dialog_group.add(hybrid_dialog_row); + const suspend_then_hibernate_dialog_row = new Adw.SwitchRow({ + title: __('Suspend then hibernate'), + subtitle: __('Not implemented yet'), + }); + dialog_group.add(suspend_then_hibernate_dialog_row); + + window._settings = this.getSettings(); + window._settings.bind('show-suspend', suspend_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + window._settings.bind('show-hibernate', hibernate_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + window._settings.bind('show-hybrid-sleep', hybrid_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + window._settings.bind('show-suspend-then-hibernate', suspend_then_hibernate_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + window._settings.bind('show-restart', restart_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + window._settings.bind('show-shutdown', shutdown_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + window._settings.bind('show-hibernate-dialog', hibernate_dialog_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + window._settings.bind('show-hybrid-sleep-dialog', hybrid_dialog_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + window._settings.bind('show-suspend-then-hibernate-dialog', suspend_then_hibernate_dialog_row, 'active', + Gio.SettingsBindFlags.DEFAULT); + } +} diff --git a/extensions/45/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml b/extensions/45/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml new file mode 100644 index 0000000..242d9cc --- /dev/null +++ b/extensions/45/hibernate-status/schemas/org.gnome.shell.extensions.hibernate-status-button.gschema.xml @@ -0,0 +1,35 @@ + + + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + false + + + false + + + diff --git a/extensions/disable-workspace-switcher.mk b/extensions/45/middleclickclose.mk similarity index 83% rename from extensions/disable-workspace-switcher.mk rename to extensions/45/middleclickclose.mk index f522857..38a3c2b 100644 --- a/extensions/disable-workspace-switcher.mk +++ b/extensions/45/middleclickclose.mk @@ -18,22 +18,23 @@ # along with this program. If not, see . # Build-Depends: -# none +# gettext libglib2.0-bin SHELL := sh -e -EXTENSION := disable-workspace-switcher -UUID := $(shell awk -F\" '/"uuid": / { print $$4 }' $(EXTENSION)/*@*/metadata.json) +EXTENSION := middleclickclose +UUID := $(shell awk -F\" '/"uuid": / { print $$4 }' $(EXTENSION)/*/metadata.json) all: test: build: + $(MAKE) -C $(EXTENSION) -install: +install: build mkdir -p $(DESTDIR)/usr/share/gnome-shell/extensions - cp -a $(EXTENSION)/$(UUID) $(DESTDIR)/usr/share/gnome-shell/extensions + unzip $(EXTENSION)/*.zip -d $(DESTDIR)/usr/share/gnome-shell/extensions/$(UUID) uninstall: rm -rf $(DESTDIR)/usr/share/gnome-shell/extensions/$(UUID) @@ -47,5 +48,5 @@ reinstall: uninstall install upstream: rm -rf $(EXTENSION) - git clone https://github.com/jbradaric/$(EXTENSION) + git clone https://github.com/p91paul/$(EXTENSION) rm -rf $(EXTENSION)/.git diff --git a/extensions/45/middleclickclose/.gitignore b/extensions/45/middleclickclose/.gitignore new file mode 100644 index 0000000..cf09213 --- /dev/null +++ b/extensions/45/middleclickclose/.gitignore @@ -0,0 +1 @@ +/*.shell-extension.zip diff --git a/extensions/middleclickclose/LICENSE b/extensions/45/middleclickclose/LICENSE similarity index 100% rename from extensions/middleclickclose/LICENSE rename to extensions/45/middleclickclose/LICENSE diff --git a/extensions/45/middleclickclose/Makefile b/extensions/45/middleclickclose/Makefile new file mode 100644 index 0000000..8960c95 --- /dev/null +++ b/extensions/45/middleclickclose/Makefile @@ -0,0 +1,27 @@ +.PHONY: clean all install po pot pack + +POT_SOURCE_FILES = $(wildcard src/schemas/*.gschema.xml src/*.js) +EXTRA_SOURCE_FILES = settingsWatch.js + +all: pack + +pack: + gnome-extensions pack --force src/ $(addprefix --extra-source=, $(EXTRA_SOURCE_FILES)) + +install: pack + gnome-extensions install --force middleclickclose@paolo.tranquilli.gmail.com.shell-extension.zip + +po: $(wildcard src/po/*.po) +pot: src/po/template.pot + +clean: + rm -f middleclickclose@paolo.tranquilli.gmail.com.shell-extension.zip + rm -f src/po/template.pot + +# --- + +src/po/template.pot: $(POT_SOURCE_FILES) + xgettext -F --from-code=UTF-8 --output=src/po/template.pot $(POT_SOURCE_FILES) + +src/po/%.po: pot + msgmerge --quiet --backup off --update $@ src/po/template.pot diff --git a/extensions/45/middleclickclose/README.md b/extensions/45/middleclickclose/README.md new file mode 100644 index 0000000..bfb193b --- /dev/null +++ b/extensions/45/middleclickclose/README.md @@ -0,0 +1,29 @@ +Quick Close in Overview +================ + +Gnome shell extension for closing apps in overview with a middle (or other) click. + +All credit goes to Paolo Tranquilli (http://cs.unibo.it/~tranquil/en/hacking.html), I've merely +copied its code here to provide Gnome Shell 3.10+ compatibility + +This extension is installable from +https://extensions.gnome.org/extension/352/middle-click-to-close-in-overview/. + +Otherwise you may + +* download a [zip](https://github.com/p91paul/middleclickclose/archive/master.zip) of this extension +* extract it +* run the following command + ``` + make install +``` +* reload gnome-shell (Alt-F2, r, Enter) -> on Wayland you need to log out and log in again, there is no in-place reload +* activate it through the Gnome Extensions application. + +## Translations + +If you're interested in contributing a translation, import the translation template file under +`src/po/template.pot` to your favourite po-editing software and create a `*.po` file under `src/po`. + +To update all existing translations after changing the code, run `make po`. To regenerate only the +`template.pot` file, run `make pot` diff --git a/extensions/45/middleclickclose/src/extension.js b/extensions/45/middleclickclose/src/extension.js new file mode 100644 index 0000000..0b0d562 --- /dev/null +++ b/extensions/45/middleclickclose/src/extension.js @@ -0,0 +1,103 @@ +/* This extension is a derived work of the Gnome Shell. +* +* Copyright (c) 2013 Paolo Tranquilli +* +* This extension is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This extension is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this extension; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; + +import { Extension, InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js'; +import { Workspace } from 'resource:///org/gnome/shell/ui/workspace.js'; + +import { SettingsWatch } from './settingsWatch.js'; + +export default class MiddleClickClose extends Extension { + #settings; + #injectionManager; + + enable() { + this.#settings = new SettingsWatch(this.getSettings(), { + close_button: { get: v => v.value, }, + rearrange_delay: {}, + }); + + this.#injectionManager = new InjectionManager(); + this.#patchClickHandler(); + this.#patchWindowRepositioningDelay(); + } + + disable() { + this.#injectionManager.clear(); + this.#injectionManager = null; + + this.#settings.clear(); + this.#settings = null; + } + + #patchClickHandler() { + // Patch _addWindowClone() to override the clicked signal handler for window clones (which + // is what gnome calls window previews). + const settings = this.#settings; + this.#injectionManager.overrideMethod(Workspace.prototype, '_addWindowClone', + original => function () { + let clone = original.apply(this, arguments); + + // This relies on implementation details of both gnome and gobject. Mainly the order + // the clone's actions are defined and the order with which signal handlers are + // connected on the click action. Just pray this never breaks... Or that gnome moves + // the click handler into a named function. That'd be nice too :) + let [clickAction] = clone.get_actions(); + let id = GObject.signal_handler_find(clickAction, { signalId: 'clicked' }); + clickAction.disconnect(id); + clickAction.connect('clicked', action => { + if (action.get_button() == settings.close_button) { + clone._deleteAll(); + } else { + clone._activate(); + } + }); + + return clone; + } + ); + } + + #patchWindowRepositioningDelay() { + // It'd be nice to just change the WINDOW_REPOSITIONING_DELAY in workspace.js, but + // apparently that is impossible with the switch to ESM. Instead, we'll monkey-patch + // _doRemoveWindow() and change the timeout after the fact. + const settings = this.#settings; + const lastLayoutFrozenIds = new WeakMap(); + this.#injectionManager.overrideMethod(Workspace.prototype, '_doRemoveWindow', + original => function () { + const ret = original.apply(this, arguments); + + // Adjust the freeze delay. + if (this._layoutFrozenId > 0 + && this._layoutFrozenId != lastLayoutFrozenIds.get(this) + ) { + const source = GLib.MainContext.default().find_source_by_id(this._layoutFrozenId); + source.set_ready_time(source.get_time() + settings.rearrange_delay * 1000); + } + + // Need to keep the last id to avoid adjusting the layout freeze delay more than once. + lastLayoutFrozenIds.set(this, this._layoutFrozenId); + + return ret; + }) + } +}; diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/metadata.json b/extensions/45/middleclickclose/src/metadata.json similarity index 75% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/metadata.json rename to extensions/45/middleclickclose/src/metadata.json index bc177b3..01234b9 100644 --- a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/metadata.json +++ b/extensions/45/middleclickclose/src/metadata.json @@ -1,11 +1,11 @@ { - "shell-version": ["42","43","44"], - "settings-schema": "org.gnome.shell.extensions.middleclickclose", - "gettext-domain": "org.gnome.shell.extensions.middleclickclose", - "uuid": "middleclickclose@paolo.tranquilli.gmail.com", "name": "Quick Close in Overview", "description": "Close windows with a button click (the middle one by default) when in overview mode", "url": "https://github.com/p91paul/middleclickclose", - "original-authors": [ "Paolo Tranquilli" ], - "locale": "/usr/local/share/locale" -} + "shell-version": [ + "45" + ], + "uuid": "middleclickclose@paolo.tranquilli.gmail.com", + "settings-schema": "org.gnome.shell.extensions.middleclickclose", + "gettext-domain": "org.gnome.shell.extensions.middleclickclose" +} \ No newline at end of file diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po b/extensions/45/middleclickclose/src/po/de.po similarity index 74% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po rename to extensions/45/middleclickclose/src/po/de.po index aed9f54..4fb4821 100644 --- a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/de/LC_MESSAGES/de_DE.po +++ b/extensions/45/middleclickclose/src/po/de.po @@ -7,66 +7,66 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell-extensions-middleclickclose\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-12-30 07:06+0100\n" +"POT-Creation-Date: 2023-08-30 18:29+0300\n" "PO-Revision-Date: 2019-06-17 20:44+0200\n" +"Last-Translator: Onno Giesmann \n" "Language-Team: German <--->\n" +"Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Gtranslator 3.32.1\n" -"Last-Translator: Onno Giesmann \n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"Language: de\n" -#: ../../../prefs.js:33 -msgid "Mouse button to close" -msgstr "Maustaste zum Schließen" - -#: ../../../prefs.js:34 -msgid "Which mouse button triggers closing in overview." -msgstr "Gibt an, welche Maustaste das Schließen in der Übersicht auslöst." - -#: ../../../prefs.js:36 +#: src/prefs.js:30 msgid "Left" msgstr "Linke" -#: ../../../prefs.js:37 +#: src/prefs.js:31 msgid "Middle" msgstr "Mittlere" -#: ../../../prefs.js:38 +#: src/prefs.js:32 msgid "Right" msgstr "Rechte" -#: ../../../prefs.js:39 +#: src/prefs.js:33 msgid "Button 4" msgstr "Taste 4" -#: ../../../prefs.js:40 +#: src/prefs.js:34 msgid "Button 5" msgstr "Taste 5" -#: ../../../prefs.js:41 +#: src/prefs.js:35 msgid "Button 6" msgstr "Taste 6" -#: ../../../prefs.js:42 +#: src/prefs.js:36 msgid "Button 7" msgstr "Taste 7" -#: ../../../prefs.js:43 +#: src/prefs.js:37 msgid "Button 8" msgstr "Taste 8" -#: ../../../prefs.js:44 +#: src/prefs.js:38 msgid "Button 9" msgstr "Taste 9" -#: ../../../prefs.js:50 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Maustaste zum Schließen" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Gibt an, welche Maustaste das Schließen in der Übersicht auslöst." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 msgid "Rearrange delay" msgstr "Verzögerung bis zur Neuanordnung" -#: ../../../prefs.js:51 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 msgid "" "How much time must pass with the pointer not moving for windows in overview " "to rearrange after one was closed." diff --git a/extensions/45/middleclickclose/src/po/el.po b/extensions/45/middleclickclose/src/po/el.po new file mode 100644 index 0000000..d032228 --- /dev/null +++ b/extensions/45/middleclickclose/src/po/el.po @@ -0,0 +1,74 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# George Tsiamasiotis , 2023. +# +msgid "" +msgstr "" +"Project-Id-Version: gnome-shell-extensions-middleclickclose\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-30 18:33+0300\n" +"PO-Revision-Date: 2023-08-30 18:41+0300\n" +"Last-Translator: George Tsiamasiotis \n" +"Language-Team: \n" +"Language: el\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.3.2\n" + +#: src/prefs.js:30 +msgid "Left" +msgstr "Αριστερό" + +#: src/prefs.js:31 +msgid "Middle" +msgstr "Μεσαίο" + +#: src/prefs.js:32 +msgid "Right" +msgstr "Δεξί" + +#: src/prefs.js:33 +msgid "Button 4" +msgstr "Κουμπί 4" + +#: src/prefs.js:34 +msgid "Button 5" +msgstr "Κουμπί 5" + +#: src/prefs.js:35 +msgid "Button 6" +msgstr "Κουμπί 6" + +#: src/prefs.js:36 +msgid "Button 7" +msgstr "Κουμπί 7" + +#: src/prefs.js:37 +msgid "Button 8" +msgstr "Κουμπί 8" + +#: src/prefs.js:38 +msgid "Button 9" +msgstr "Κουμπί 9" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Κουμπί ποντικιού για κλείσιμο" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Ποιό κουμπί του ποντικιού κλείνει παράθυρα στην επισκόπηση." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 +msgid "Rearrange delay" +msgstr "Καθυστέρηση ανακατονομής" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 +msgid "" +"How much time must pass with the pointer not moving for windows in overview " +"to rearrange after one was closed." +msgstr "" +"Πόσος χρόνος πρέπει να περάσει με τον κέρσορα αδρανή στην επισκόπηση μετά " +"το κλείσιμο ενός παραθύρου για να γίνει ανακατανομή." diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.po b/extensions/45/middleclickclose/src/po/fr.po similarity index 65% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.po rename to extensions/45/middleclickclose/src/po/fr.po index b842115..e762a40 100644 --- a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/fr/LC_MESSAGES/fr.po +++ b/extensions/45/middleclickclose/src/po/fr.po @@ -7,69 +7,71 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell-extensions-middleclickclose\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-12-30 07:06+0100\n" +"POT-Creation-Date: 2023-08-30 18:29+0300\n" "PO-Revision-Date: 2015-12-30 07:08+0100\n" +"Last-Translator: DAEM Q.\n" "Language-Team: \n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.8.5\n" -"Last-Translator: DAEM Q.\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"Language: fr\n" -#: ../../../prefs.js:33 -msgid "Mouse button to close" -msgstr "Bouton de souris pour fermer la fenêtre" - -#: ../../../prefs.js:34 -msgid "Which mouse button triggers closing in overview." -msgstr "Le bouton de la souris qui déclenche la fermeture d'une fenêtre dans la Vue d'ensemble." - -#: ../../../prefs.js:36 +#: src/prefs.js:30 msgid "Left" msgstr "Gauche" -#: ../../../prefs.js:37 +#: src/prefs.js:31 msgid "Middle" msgstr "Milieu" -#: ../../../prefs.js:38 +#: src/prefs.js:32 msgid "Right" msgstr "Droit" -#: ../../../prefs.js:39 +#: src/prefs.js:33 msgid "Button 4" msgstr "Bouton 4" -#: ../../../prefs.js:40 +#: src/prefs.js:34 msgid "Button 5" msgstr "Bouton 5" -#: ../../../prefs.js:41 +#: src/prefs.js:35 msgid "Button 6" msgstr "Bouton 6" -#: ../../../prefs.js:42 +#: src/prefs.js:36 msgid "Button 7" msgstr "Bouton 7" -#: ../../../prefs.js:43 +#: src/prefs.js:37 msgid "Button 8" msgstr "Bouton 8" -#: ../../../prefs.js:44 +#: src/prefs.js:38 msgid "Button 9" msgstr "Bouton 9" -#: ../../../prefs.js:50 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Bouton de souris pour fermer la fenêtre" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "" +"Le bouton de la souris qui déclenche la fermeture d'une fenêtre dans la Vue " +"d'ensemble." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 msgid "Rearrange delay" msgstr "Délai avant réarrangement" -#: ../../../prefs.js:51 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 msgid "" "How much time must pass with the pointer not moving for windows in overview " "to rearrange after one was closed." msgstr "" -"Temps qu'il doit se passer sans que le pointeur de la souris ne bouge dans la Vue d'ensemble " -"avant réarrangement des fenêtres." +"Temps qu'il doit se passer sans que le pointeur de la souris ne bouge dans " +"la Vue d'ensemble avant réarrangement des fenêtres." diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.po b/extensions/45/middleclickclose/src/po/it.po similarity index 69% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.po rename to extensions/45/middleclickclose/src/po/it.po index bcc71c4..4b20c55 100644 --- a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/it/LC_MESSAGES/it.po +++ b/extensions/45/middleclickclose/src/po/it.po @@ -7,69 +7,69 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell-extensions-middleclickclose\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-12-30 07:06+0100\n" +"POT-Creation-Date: 2023-08-30 18:29+0300\n" "PO-Revision-Date: 2017-04-21 10:06+0200\n" +"Last-Translator: Jimmy Scionti \n" "Language-Team: Jimmy Scionti \n" +"Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.8.7.1\n" -"Last-Translator: Jimmy Scionti \n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"Language: it\n" -#: ../../../prefs.js:33 -msgid "Mouse button to close" -msgstr "Pulsante del mouse per chiudere la finestra" - -#: ../../../prefs.js:34 -msgid "Which mouse button triggers closing in overview." -msgstr "Seleziona quale pulsante del mouse premere per chiudere una finestra." - -#: ../../../prefs.js:36 +#: src/prefs.js:30 msgid "Left" msgstr "Sinistro" -#: ../../../prefs.js:37 +#: src/prefs.js:31 msgid "Middle" msgstr "Centrale" -#: ../../../prefs.js:38 +#: src/prefs.js:32 msgid "Right" msgstr "Destro" -#: ../../../prefs.js:39 +#: src/prefs.js:33 msgid "Button 4" msgstr "Pulsante 4" -#: ../../../prefs.js:40 +#: src/prefs.js:34 msgid "Button 5" msgstr "Pulsante 5" -#: ../../../prefs.js:41 +#: src/prefs.js:35 msgid "Button 6" msgstr "Pulsante 6" -#: ../../../prefs.js:42 +#: src/prefs.js:36 msgid "Button 7" msgstr "Pulsante 7" -#: ../../../prefs.js:43 +#: src/prefs.js:37 msgid "Button 8" msgstr "Pulsante 8" -#: ../../../prefs.js:44 +#: src/prefs.js:38 msgid "Button 9" msgstr "Pulsante 9" -#: ../../../prefs.js:50 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Pulsante del mouse per chiudere la finestra" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Seleziona quale pulsante del mouse premere per chiudere una finestra." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 msgid "Rearrange delay" msgstr "Ritardo del riordinamento" -#: ../../../prefs.js:51 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 msgid "" -"How much time must pass with the pointer not moving for windows in overview to rearrange " -"after one was closed." +"How much time must pass with the pointer not moving for windows in overview " +"to rearrange after one was closed." msgstr "" -"Seleziona il tempo di attesa tra la chiusura di una finestra e il riordinamento delle altre " -"finestre nell'anteprima." +"Seleziona il tempo di attesa tra la chiusura di una finestra e il " +"riordinamento delle altre finestre nell'anteprima." diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.po b/extensions/45/middleclickclose/src/po/nl.po similarity index 71% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.po rename to extensions/45/middleclickclose/src/po/nl.po index 62fc210..fe9c6e9 100644 --- a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/nl/nl.po +++ b/extensions/45/middleclickclose/src/po/nl.po @@ -6,69 +6,69 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell-extensions-middleclickclose\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-12-30 07:06+0100\n" +"POT-Creation-Date: 2023-08-30 18:29+0300\n" "PO-Revision-Date: 2019-09-15 20:17+0200\n" +"Last-Translator: Heimen Stoffels \n" "Language-Team: Dutch \n" +"Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.2.1\n" -"Last-Translator: Heimen Stoffels \n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"Language: it\n" -#: ../../../prefs.js:33 -msgid "Mouse button to close" -msgstr "Muisknop om vensters mee te sluiten" - -#: ../../../prefs.js:34 -msgid "Which mouse button triggers closing in overview." -msgstr "Met welke muisknop je vensters kunt sluiten op het overzicht." - -#: ../../../prefs.js:36 +#: src/prefs.js:30 msgid "Left" msgstr "Links" -#: ../../../prefs.js:37 +#: src/prefs.js:31 msgid "Middle" msgstr "Scrollwiel" -#: ../../../prefs.js:38 +#: src/prefs.js:32 msgid "Right" msgstr "Rechts" -#: ../../../prefs.js:39 +#: src/prefs.js:33 msgid "Button 4" msgstr "Knop 4" -#: ../../../prefs.js:40 +#: src/prefs.js:34 msgid "Button 5" msgstr "Knop 5" -#: ../../../prefs.js:41 +#: src/prefs.js:35 msgid "Button 6" msgstr "Knop 6" -#: ../../../prefs.js:42 +#: src/prefs.js:36 msgid "Button 7" msgstr "Knop 7" -#: ../../../prefs.js:43 +#: src/prefs.js:37 msgid "Button 8" msgstr "Knop 8" -#: ../../../prefs.js:44 +#: src/prefs.js:38 msgid "Button 9" msgstr "Knop 9" -#: ../../../prefs.js:50 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Muisknop om vensters mee te sluiten" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Met welke muisknop je vensters kunt sluiten op het overzicht." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 msgid "Rearrange delay" msgstr "Vertraging bij herschikken" -#: ../../../prefs.js:51 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 msgid "" -"How much time must pass with the pointer not moving for windows in " -"overview to rearrange after one was closed." +"How much time must pass with the pointer not moving for windows in overview " +"to rearrange after one was closed." msgstr "" "Hoeveel tijd er moet verstrijken na het sluiten van een venster voordat " "vensters worden herschikt." diff --git a/extensions/45/middleclickclose/src/po/pt.po b/extensions/45/middleclickclose/src/po/pt.po new file mode 100644 index 0000000..7f35be1 --- /dev/null +++ b/extensions/45/middleclickclose/src/po/pt.po @@ -0,0 +1,72 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Tomás Marques , 2023 +# +msgid "" +msgstr "" +"Project-Id-Version: gnome-shell-extensions-middleclickclose\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-10-11 23:56+0100\n" +"PO-Revision-Date: 2023-10-11 23:56+0100\n" +"Last-Translator: Tomás Marques \n" +"Language-Team: \n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/prefs.js:30 +msgid "Left" +msgstr "Esquerda" + +#: src/prefs.js:31 +msgid "Middle" +msgstr "Meio" + +#: src/prefs.js:32 +msgid "Right" +msgstr "Direita" + +#: src/prefs.js:33 +msgid "Button 4" +msgstr "Botão 4" + +#: src/prefs.js:34 +msgid "Button 5" +msgstr "Botão 5" + +#: src/prefs.js:35 +msgid "Button 6" +msgstr "Botão 6" + +#: src/prefs.js:36 +msgid "Button 7" +msgstr "Botão 7" + +#: src/prefs.js:37 +msgid "Button 8" +msgstr "Botão 8" + +#: src/prefs.js:38 +msgid "Button 9" +msgstr "Botão 9" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Botão do meio do rato" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Qual o botão do rato que fecha uma janela na vista geral." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 +msgid "Rearrange delay" +msgstr "Duração de reorganizar" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 +msgid "" +"How much time must pass with the pointer not moving for windows in overview " +"to rearrange after one was closed." +msgstr "" +"Quanto tempo tem de passar sem que o cursor se mova para que as janelas na visão geral " +"se reorganizem depois de uma ter sido fechada." diff --git a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.po b/extensions/45/middleclickclose/src/po/sk.po similarity index 74% rename from extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.po rename to extensions/45/middleclickclose/src/po/sk.po index 801f9bc..d9c250a 100644 --- a/extensions/middleclickclose/middleclickclose@paolo.tranquilli.gmail.com/locale/sk/LC_MESSAGES/sk.po +++ b/extensions/45/middleclickclose/src/po/sk.po @@ -7,66 +7,66 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell-extensions-middleclickclose\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 07:06+0100\n" +"POT-Creation-Date: 2023-08-30 18:29+0300\n" "PO-Revision-Date: 2015-10-30 07:08+0100\n" +"Last-Translator: Juraj Fiala \n" "Language-Team: \n" +"Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.8.5\n" -"Last-Translator: Juraj Fiala \n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -"Language: sk\n" -#: ../../../prefs.js:33 -msgid "Mouse button to close" -msgstr "Tlačidlo myši na zavretie" - -#: ../../../prefs.js:34 -msgid "Which mouse button triggers closing in overview." -msgstr "Ktoré tlačidlo myši spustí zavretie v prehľade aktivít." - -#: ../../../prefs.js:36 +#: src/prefs.js:30 msgid "Left" msgstr "Ľavé" -#: ../../../prefs.js:37 +#: src/prefs.js:31 msgid "Middle" msgstr "Stredné" -#: ../../../prefs.js:38 +#: src/prefs.js:32 msgid "Right" msgstr "Pravé" -#: ../../../prefs.js:39 +#: src/prefs.js:33 msgid "Button 4" msgstr "Tlačidlo 4" -#: ../../../prefs.js:40 +#: src/prefs.js:34 msgid "Button 5" msgstr "Tlačidlo 5" -#: ../../../prefs.js:41 +#: src/prefs.js:35 msgid "Button 6" msgstr "Tlačidlo 6" -#: ../../../prefs.js:42 +#: src/prefs.js:36 msgid "Button 7" msgstr "Tlačidlo 7" -#: ../../../prefs.js:43 +#: src/prefs.js:37 msgid "Button 8" msgstr "Tlačidlo 8" -#: ../../../prefs.js:44 +#: src/prefs.js:38 msgid "Button 9" msgstr "Tlačidlo 9" -#: ../../../prefs.js:50 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Tlačidlo myši na zavretie" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Ktoré tlačidlo myši spustí zavretie v prehľade aktivít." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 msgid "Rearrange delay" msgstr "Oneskorenie preskúpenia" -#: ../../../prefs.js:51 +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 msgid "" "How much time must pass with the pointer not moving for windows in overview " "to rearrange after one was closed." diff --git a/extensions/45/middleclickclose/src/po/sr.po b/extensions/45/middleclickclose/src/po/sr.po new file mode 100644 index 0000000..514194c --- /dev/null +++ b/extensions/45/middleclickclose/src/po/sr.po @@ -0,0 +1,75 @@ +# Translation for gnome-shell-extension-middleclickclose in Serbian (Cyrillic). +# Copyright (C) 2023 +# This file is distributed under the same license as the gnome-shell-extension-middleclickclose package. +# Fifty Dinar , 2023. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-09-02 18:28+0300\n" +"PO-Revision-Date: 2023-10-15 18:51+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: sr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4\n" + +#: src/prefs.js:30 +msgid "Left" +msgstr "Лево" + +#: src/prefs.js:31 +msgid "Middle" +msgstr "Средње" + +#: src/prefs.js:32 +msgid "Right" +msgstr "Десно" + +#: src/prefs.js:33 +msgid "Button 4" +msgstr "Дугме 4" + +#: src/prefs.js:34 +msgid "Button 5" +msgstr "Дугме 5" + +#: src/prefs.js:35 +msgid "Button 6" +msgstr "Дугме 6" + +#: src/prefs.js:36 +msgid "Button 7" +msgstr "Дугме 7" + +#: src/prefs.js:37 +msgid "Button 8" +msgstr "Дугме 8" + +#: src/prefs.js:38 +msgid "Button 9" +msgstr "Дугме 9" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Дугме на мишу за затварање апликација" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Које дугме на мишу затвара апликацију у прегледнику." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 +msgid "Rearrange delay" +msgstr "Задршка у распоређивању" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 +msgid "" +"How much time must pass with the pointer not moving for windows in overview " +"to rearrange after one was closed." +msgstr "" +"Колико времена мора да прође приликом непомерања показивача миша за прозоре " +"апликација у прегледнику, како би се прозори распоредили након затварања " +"апликације." diff --git a/extensions/45/middleclickclose/src/po/sr@latin.po b/extensions/45/middleclickclose/src/po/sr@latin.po new file mode 100644 index 0000000..7ec468e --- /dev/null +++ b/extensions/45/middleclickclose/src/po/sr@latin.po @@ -0,0 +1,75 @@ +# Translation for gnome-shell-extension-middleclickclose in Serbian (Latin). +# Copyright (C) 2023 +# This file is distributed under the same license as the gnome-shell-extension-middleclickclose package. +# Fifty Dinar , 2023. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-09-02 18:28+0300\n" +"PO-Revision-Date: 2023-10-15 18:52+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: sr@latin\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4\n" + +#: src/prefs.js:30 +msgid "Left" +msgstr "Levo" + +#: src/prefs.js:31 +msgid "Middle" +msgstr "Srednje" + +#: src/prefs.js:32 +msgid "Right" +msgstr "Desno" + +#: src/prefs.js:33 +msgid "Button 4" +msgstr "Dugme 4" + +#: src/prefs.js:34 +msgid "Button 5" +msgstr "Dugme 5" + +#: src/prefs.js:35 +msgid "Button 6" +msgstr "Dugme 6" + +#: src/prefs.js:36 +msgid "Button 7" +msgstr "Dugme 7" + +#: src/prefs.js:37 +msgid "Button 8" +msgstr "Dugme 8" + +#: src/prefs.js:38 +msgid "Button 9" +msgstr "Dugme 9" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Dugme na mišu za zatvaranje aplikacija" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Koje dugme na mišu zatvara aplikaciju u pregledniku." + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 +msgid "Rearrange delay" +msgstr "Zadrška u raspoređivanju" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 +msgid "" +"How much time must pass with the pointer not moving for windows in overview " +"to rearrange after one was closed." +msgstr "" +"Koliko vremena mora da prođe prilikom nepomeranja pokazivača miša za " +"prozore aplikacija u pregledniku, kako bi se prozori rasporedili nakon " +"zatvaranja aplikacije." diff --git a/extensions/45/middleclickclose/src/po/template.pot b/extensions/45/middleclickclose/src/po/template.pot new file mode 100644 index 0000000..02cdf11 --- /dev/null +++ b/extensions/45/middleclickclose/src/po/template.pot @@ -0,0 +1,72 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-09-02 18:28+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/prefs.js:30 +msgid "Left" +msgstr "" + +#: src/prefs.js:31 +msgid "Middle" +msgstr "" + +#: src/prefs.js:32 +msgid "Right" +msgstr "" + +#: src/prefs.js:33 +msgid "Button 4" +msgstr "" + +#: src/prefs.js:34 +msgid "Button 5" +msgstr "" + +#: src/prefs.js:35 +msgid "Button 6" +msgstr "" + +#: src/prefs.js:36 +msgid "Button 7" +msgstr "" + +#: src/prefs.js:37 +msgid "Button 8" +msgstr "" + +#: src/prefs.js:38 +msgid "Button 9" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 +msgid "Rearrange delay" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 +msgid "" +"How much time must pass with the pointer not moving for windows in overview " +"to rearrange after one was closed." +msgstr "" diff --git a/extensions/45/middleclickclose/src/po/uk.po b/extensions/45/middleclickclose/src/po/uk.po new file mode 100644 index 0000000..dedc32b --- /dev/null +++ b/extensions/45/middleclickclose/src/po/uk.po @@ -0,0 +1,73 @@ +# StandWithUkraine +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Artem Prokop , 2023. +# +msgid "" +msgstr "" +"Project-Id-Version: gnome-shell-extensions-middleclickclose\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-10-22 00:00+0300\n" +"PO-Revision-Date: 2023-10-22 00:00+0300\n" +"Last-Translator: Artem Prokop \n" +"Language-Team: \n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/prefs.js:30 +msgid "Left" +msgstr "Ліва" + +#: src/prefs.js:31 +msgid "Middle" +msgstr "Середня" + +#: src/prefs.js:32 +msgid "Right" +msgstr "Права" + +#: src/prefs.js:33 +msgid "Button 4" +msgstr "Кнопка 4" + +#: src/prefs.js:34 +msgid "Button 5" +msgstr "Кнопка 5" + +#: src/prefs.js:35 +msgid "Button 6" +msgstr "Кнопка 6" + +#: src/prefs.js:36 +msgid "Button 7" +msgstr "Кнопка 7" + +#: src/prefs.js:37 +msgid "Button 8" +msgstr "Кнопка 8" + +#: src/prefs.js:38 +msgid "Button 9" +msgstr "Кнопка 9" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:17 +msgid "Mouse button to close" +msgstr "Кнопка миші для закриття" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:18 +msgid "Which mouse button triggers closing in overview." +msgstr "Яка кнопка миші закриває вікно в оверв'ю" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:23 +msgid "Rearrange delay" +msgstr "Затримка перерозташування" + +#: src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml:24 +msgid "" +"How much time must pass with the pointer not moving for windows in overview " +"to rearrange after one was closed." +msgstr "" +"Скільки часу курсор має не рухатись щоб вікна в оверв'ю перерозташувалися" +" після закриття вікна." diff --git a/extensions/45/middleclickclose/src/prefs.js b/extensions/45/middleclickclose/src/prefs.js new file mode 100644 index 0000000..91d9f13 --- /dev/null +++ b/extensions/45/middleclickclose/src/prefs.js @@ -0,0 +1,108 @@ +/** + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +**/ + +import Adw from 'gi://Adw'; +import Gtk from 'gi://Gtk'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; + +import { ExtensionPreferences, gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export default class MiddleClickClosePreferences extends ExtensionPreferences { + getPreferencesWidget() { + let page = new Adw.PreferencesPage(); + let group = new Adw.PreferencesGroup(); + + group.add(this.buildPreference("close-button", { + nicks: { + left: _("Left"), + middle: _("Middle"), + right: _("Right"), + 'button 4': _("Button 4"), + 'button 5': _("Button 5"), + 'button 6': _("Button 6"), + 'button 7': _("Button 7"), + 'button 8': _("Button 8"), + 'button 9': _("Button 9"), + }, + })); + + group.add(this.buildPreference("rearrange-delay", { + step: 50, + })); + + page.add(group); + return page; + } + + buildPreference(key, opts) { + opts ??= {} + + const settings = this.getSettings(); + const setting = settings.create_action(key); + const schema = settings.settings_schema.get_key(key); + + opts.title ??= schema.get_summary() || schema.get_name(); + opts.subtitle ??= schema.get_description(); + + const ty = schema.get_value_type().dup_string(); + const [range_ty, range] = schema.get_range().recursiveUnpack(); + + if (range_ty == "enum") { + opts.nicks ??= {}; + let row = new Adw.ComboRow({ + title: opts.title, + subtitle: opts.subtitle, + model: new Gtk.StringList({ strings: range.map(nick => opts.nicks[nick]) }), + selected: range.indexOf(setting.state.unpack()), + }); + + row.connect('notify::selected', () => { + setting.activate(GLib.Variant.new_string(range[row.selected])); + }); + + return row; + } else if (range_ty == "range") { + opts.lower ??= range[0] + opts.upper ??= range[1] + } + + if (["i"].includes(ty)) { + let adjustment = new Gtk.Adjustment({ + lower: opts.lower, + upper: opts.upper, + step_increment: opts.step, + value: setting.state.unpack(), + }); + + let row = new Adw.SpinRow({ + title: opts.title, + subtitle: opts.subtitle, + numeric: true, + adjustment, + }); + + adjustment.connect("value-changed", adj => { + setting.activate(GLib.Variant.new_int32(adj.value)); + }); + + return row; + } + + // Yeah... I'm not gonna implement a full introspector here. Just add whatever is required + // when needed. + throw new GObject.NotImplementedError(); + } +}; diff --git a/extensions/45/middleclickclose/src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml b/extensions/45/middleclickclose/src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml new file mode 100644 index 0000000..ebe6dc9 --- /dev/null +++ b/extensions/45/middleclickclose/src/schemas/org.gnome.shell.extensions.middleclickclose.gschema.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + 'middle' + Mouse button to close + Which mouse button triggers closing in overview. + + + 750 + + Rearrange delay + How much time must pass with the pointer not moving for windows in overview to rearrange after one was closed. + + + diff --git a/extensions/45/middleclickclose/src/settingsWatch.js b/extensions/45/middleclickclose/src/settingsWatch.js new file mode 100644 index 0000000..fd8b8bd --- /dev/null +++ b/extensions/45/middleclickclose/src/settingsWatch.js @@ -0,0 +1,87 @@ +export class SettingsWatch { + #settings; + #key_data = new Map(); + + constructor(settings, ...opts) { + this.#settings = settings; + + for (const opt of opts) { + if (typeof opt == 'string') { + this.#addSetting(opt, {}); + } else { + for (const [prop_name, opts] of Object.entries(opt)) { + this.#addSetting(prop_name, opts) + } + } + } + } + + #addSetting(prop_name, opts) { + let key; + if (typeof opts == 'string') { + key = opts; + opts = {}; + } else { + key = opts.key ?? prop_name.replace('_', '-'); + } + + const settings = this.#settings; + const raw_getter = opts.get_raw ?? this.#defaultRawGetter(settings, key); + const getter = opts.get ?? (x => x); + + const update_cb = () => { + let value = getter(raw_getter(settings.get_value(key))); + this.#key_data.get(prop_name).value = value; + + return value; + }; + + this.#key_data.set(prop_name, {}); + Object.defineProperty(this, prop_name, { + enumerable: true, + configurable: true, + get() { + let data = this.#key_data.get(prop_name); + if (data.value === undefined) { + data.handler_id = settings.connect('changed::' + key, update_cb); + data.value = update_cb(); + } + + return data.value; + } + }); + } + + #defaultRawGetter(settings, key) { + const schema = settings.settings_schema.get_key(key); + const [range_ty, _range] = schema.get_range().recursiveUnpack(); + + if (range_ty == "enum") { + return value => { + return { + nick: value.unpack(), + + // FIXME: remove unnecessary lookup if/when g_settings_schema_key_to_enum() + // becomes available. But for now, an extra lookup will have to do. + value: settings.get_enum(key), + }; + }; + } + + return value => value.recursiveUnpack(); + } + + clear() { + for (const [key, data] of this.#key_data.entries()) { + if (data.handler_id !== undefined) + this.#settings.disconnect(data.handler_id); + + delete this[key]; + } + + this.#key_data.clear(); + } +} + +// Allow calling clear as a static function. +SettingsWatch.clear = obj => SettingsWatch.prototype.clear.apply(obj); diff --git a/extensions/no-overview.mk b/extensions/45/no-overview.mk similarity index 100% rename from extensions/no-overview.mk rename to extensions/45/no-overview.mk diff --git a/extensions/disable-workspace-switcher/LICENSE b/extensions/45/no-overview/LICENSE similarity index 100% copy from extensions/disable-workspace-switcher/LICENSE copy to extensions/45/no-overview/LICENSE diff --git a/extensions/no-overview/README.md b/extensions/45/no-overview/README.md similarity index 100% rename from extensions/no-overview/README.md rename to extensions/45/no-overview/README.md diff --git a/extensions/no-overview/extension.js b/extensions/45/no-overview/extension.js similarity index 61% rename from extensions/no-overview/extension.js rename to extensions/45/no-overview/extension.js index 32a9090..63c914a 100644 --- a/extensions/no-overview/extension.js +++ b/extensions/45/no-overview/extension.js @@ -1,14 +1,14 @@ /* No overview at start-up + GNOME Shell 45+ extension Contributors: @fthx, @fmuellner License: GPL v3 */ -const Main = imports.ui.main; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; - -class Extension { +export default class NoOverviewExtension { constructor() { this._realHasOverview = Main.sessionMode.hasOverview; } @@ -19,20 +19,14 @@ class Extension { } Main.sessionMode.hasOverview = false; - Main.layoutManager.connect('startup-complete', () => { - Main.sessionMode.hasOverview = this._realHasOverview + + this._startup_complete = Main.layoutManager.connect('startup-complete', () => { + Main.sessionMode.hasOverview = this._realHasOverview; }); - // handle Ubuntu's method - if (Main.layoutManager.startInOverview) { - Main.layoutManager.startInOverview = false; - } } disable() { Main.sessionMode.hasOverview = this._realHasOverview; + Main.layoutManager.disconnect(this._startup_complete); } } - -function init() { - return new Extension(); -} \ No newline at end of file diff --git a/extensions/45/no-overview/metadata.json b/extensions/45/no-overview/metadata.json new file mode 100644 index 0000000..0c95c9e --- /dev/null +++ b/extensions/45/no-overview/metadata.json @@ -0,0 +1,14 @@ +{ + "_generated": "Generated by SweetTooth, do not edit", + "description": "No overview at start-up. Nothing more.", + "name": "No overview at start-up", + "original-authors": [ + "fthx" + ], + "shell-version": [ + "45" + ], + "url": "https://github.com/fthx/no-overview", + "uuid": "no-overview@fthx", + "version": 999 +} \ No newline at end of file diff --git a/extensions/vertical-workspaces.mk b/extensions/45/vertical-workspaces.mk similarity index 100% rename from extensions/vertical-workspaces.mk rename to extensions/45/vertical-workspaces.mk diff --git a/extensions/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md b/extensions/45/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md similarity index 100% rename from extensions/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md rename to extensions/45/vertical-workspaces/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/extensions/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md b/extensions/45/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md similarity index 100% rename from extensions/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md rename to extensions/45/vertical-workspaces/.github/ISSUE_TEMPLATE/feature_request.md diff --git a/extensions/vertical-workspaces/CHANGELOG.md b/extensions/45/vertical-workspaces/CHANGELOG.md similarity index 73% rename from extensions/vertical-workspaces/CHANGELOG.md rename to extensions/45/vertical-workspaces/CHANGELOG.md index 0beb20c..0ea09cc 100644 --- a/extensions/vertical-workspaces/CHANGELOG.md +++ b/extensions/45/vertical-workspaces/CHANGELOG.md @@ -1,4 +1,100 @@ ## Changelog: +### v45.2 for GNOME 45.2+, v44.10 for GNOME 42-44 (2023-12-03) +**Added:** +- *Dash* option *Isolate Workspaces* on *Behavior* tab +- *Brightness for Search View* option allows adjusting background wallpaper brightness in overview search view +- *Extensions Search Provider* module allows to search for extensions from the overview, open their settings and enable or disable them + + +### v45.1 for GNOME 45.1+ (EGO 2023-11-25) v44.9 for GNOME 42-44 (not released) +**Added:** +- *Window Thumbnail - PIP* option in app icon menu and as an click actions for Window Preview +- Workspace switcher options *Wraparound* and *Ignore Last (empty)* +- *Overlay key (Super)* and *Hot Corner* actions offer different overview modes independent on global *Overview Mode* +- Workspace thumbnails background without wallpaper is now semi-transparent to match other overview elements +- App Grid sorting options allow alphabetical order with folders + +**Fixed:** +- App grid has less rows than it should +- Many minor fixes that reflect gnome-shell development and backports +- Centered app folder position on multi-monitor system +- Window can't be activated using touchscreen +- Setting background wallpaper too early on startup can crash Wayland session + +**Other changes:** +- Since GNOME 45 V-Shell has 2 versions - one for GS 45 and the second for GS 42-44. Versioning no longer depends on EGO's upload counter +- Dash light style background opacity is not limited +- Refactored Recent Files Search Provider and other modules +- Settings window - ComboBox replaced with (finally fixed) DropDown + + +### v37 (2023-07-01) +**Fixed:** +- False detections of conflicting extensions + + +### v36 (2023-07-01) (35/34/33/32/31 skipped due to issues with extensions.gnome.org) +**Added:** +- Option *Fix New Windows Not In Focus* on *Misc* tab + +**Fixed:** +- Improved compatibility with Dash to Dock extension - hidden dock in the overview, broken layout after startup +- Compatibility with Hide Top Bar extension +- App search provider ignores non-localized app names +- App folders grid dimensions wrong if set to *Adaptive* and folder icon is set to a fixed size +- Dash and active app folder icons running indicator position +- Blur/Brightness transitions in static overview mode +- Hot corner edge barrier can be active even if hot corner is disabled +- Disabling dash module does not reset dash position to default + +**Other changes:** +- Removed css class reducing Quick Settings buttons height in GNOME 44 + + +### v30 (2023-06-09) +**Added:** +- Dash option - Click Behavior: *Prefer Current Workspace* - opens a new window if app not present on the active workspace +- Window search provider sorting options +- Esc key behavior options +- Window preview - middle and secondary mouse button behavior options, close button can be hidden +- GNOME 3 vertical dash style is now optional +- Window preview title position option +- Light dash background option +- Remove app folder button in folder dialog +- *Updating V-Shell* banner appears during updating V-Shell settings when settings window is irresponsive +- Dutch translation by @Vistaus + +**Fixed:** +- Dash icon scroll action conflicts with Dash to Dock +- Open new window by middle click on app icon or Ctrl+Enter doesn't work +- Dash icon label can extend to the adjacent display +- WindowPreview module not updated when "always-activate-selected-window" changed +- App folder dialog position if secondary monitor connected +- App folder dialog sizing and positioning +- Background brightness in search view reduced independently to avoid unreadable text and consistent style +- Compatibility with Burn My Windows - freeze after screen unlocked, or extensions re-enabled +- Window and Recent files search providers modes not isolated well from results of other providers +- Recent file search provider results sorting +- App grid icons with multi-line label move on hover when label expands +- Search view animation skipped id triggered from app grid state +- DING desktop icons not visible during static background workspace animation + + +**Other changes:** +- Added `unlock-dialog` session mode to avoid unnecessary system load when using screen lock +- App Grid refactored, added transparent app folder dialogs on clean background +- Search view transparency and fixed background brightness in classic overview +- Search view in static workspace overview with full opacity and close to default style +- Settings window - Profiles tab moved at first position, Dash icons position options moved back to layout +- Updated default profiles + + +### v29 (2023-04-11) +**Fixed:** +- Window switcher/highlighter logic when scrolling over an dash icon +- Unhandled promise rejection warnings on GS 43+ + + ### v28 (2023-04-06) **Added:** - App Grid - vertical app folder orientation diff --git a/extensions/disable-workspace-switcher/LICENSE b/extensions/45/vertical-workspaces/LICENSE similarity index 100% rename from extensions/disable-workspace-switcher/LICENSE rename to extensions/45/vertical-workspaces/LICENSE diff --git a/extensions/vertical-workspaces/Makefile b/extensions/45/vertical-workspaces/Makefile similarity index 100% rename from extensions/vertical-workspaces/Makefile rename to extensions/45/vertical-workspaces/Makefile diff --git a/extensions/vertical-workspaces/README.md b/extensions/45/vertical-workspaces/README.md similarity index 86% rename from extensions/vertical-workspaces/README.md rename to extensions/45/vertical-workspaces/README.md index ebff9df..9ff690c 100644 --- a/extensions/vertical-workspaces/README.md +++ b/extensions/45/vertical-workspaces/README.md @@ -2,7 +2,7 @@ A GNOME Shell extension that lets you customize your GNOME Shell UX to suit your workflow, whether you like horizontally or vertically stacked workspaces. -Currently supported GNOME versions: 42, 43, 44 +Currently supported GNOME versions: 42 - 45 [](https://extensions.gnome.org/extension/5177/vertical-workspaces/) @@ -17,6 +17,7 @@ Currently supported GNOME versions: 42, 43, 44 - Support for secondary monitors, workspace thumbnails can be placed on the opposite side than on the primary monitor - Wallpaper background with adjustable blur effect and brightness in the overview - Custom Dash icon size and on-click/scroll behavior +- Optional workspace isolated Dash - Dash background transparency and corner radius adjustments - Adjustable app grid icon size, number of columns and rows, content, optional active and draggable icons in folder preview in optional 3x3 grid - Custom search view width, app results icons size and number of result lists rows, improved app search @@ -26,12 +27,15 @@ Currently supported GNOME versions: 42, 43, 44 - Control over transition animations, including speed - Window search provider with *Space* hotkey allows quick window navigation - Recent files search provider with *Ctrl + Space* hotkey +- Extensions search provider with *Ctrl + Shift + Space* hotkey allows to search for extensions, open their settings and enable or disable them - Reorder workspaces in overview using *Shift + Scroll* or *Shift + Page Up/Down* - Adds *Force Quit*, *Close Windows on Current Workspace* and *Move Windows to Current Workspace* items to app icon menu. The latter action can be activated using *Shift + click* on app icon - Change notification banners and OSD popups position - Window attention handler options can activate the attention-demanding window immediately or silence its notification - Optional position of the hot corner that can follow the dash and expand to hot edge - Super key double-press options +- Window thumbnail (PIP) options allows you to create scaled down clone of the window by clicking on its preview in the overview (secondary mouse buttons or window preview icon) + ## Changelog [CHANGELOG.md](CHANGELOG.md) @@ -51,14 +55,23 @@ Install the extension (`--force` switch needs to be used only if some version of gnome-extensions install --force vertical-workspaces@G-dH.github.com.zip ### Installation from GitHub repository -The most recent version in the repository is the one I'm currently using and developing on my own systems, problems may occur, but usually nothing serious. The repository version may change often and doesn't updates automatically on your system, but once the stable release shows up on extensions.gnome.org, it should be updated automatically from there. If you want to help me, use this latest version and report bugs. -You may need to install `git`, `gettext` and `glib2.0` for successful installation. +The most recent version in the repository is the one I'm currently using and developing on my own systems, problems may occur, but usually nothing serious. The repository version may change often and doesn't updates automatically on your system. If you want to help me, use this latest version and report bugs. +You may need to install `git`, `make`, `gettext` and `glib2.0` for successful installation. Navigate to the directory you want to download the source code and execute following commands in the terminal: +GNOME 45: + git clone https://github.com/G-dH/vertical-workspaces.git cd vertical-workspaces make install +GNOME 42 - 44: + + git clone https://github.com/G-dH/vertical-workspaces.git + cd vertical-workspaces + git checkout gnome-42-44 + make install + If you get `Can't recursively copy directory` error, take a look at issue #51. ### Enabling the extension diff --git a/extensions/45/vertical-workspaces/extension.js b/extensions/45/vertical-workspaces/extension.js new file mode 100644 index 0000000..44ea42b --- /dev/null +++ b/extensions/45/vertical-workspaces/extension.js @@ -0,0 +1,842 @@ +/** + * V-Shell (Vertical Workspaces) + * extension.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Meta from 'gi://Meta'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js'; + +import * as Config from 'resource:///org/gnome/shell/misc/config.js'; + +import * as Extension from 'resource:///org/gnome/shell/extensions/extension.js'; + +// Me imports +import * as Settings from './lib/settings.js'; +import * as _Util from './lib/util.js'; + +// Me Modules import +import { LayoutModule } from './lib/layout.js'; +import { WorkspacesViewModule } from './lib/workspacesView.js'; +import { WorkspaceThumbnailModule } from './lib/workspaceThumbnail.js'; +import { AppDisplayModule } from './lib/appDisplay.js'; +import { AppFavoritesModule } from './lib/appFavorites.js'; +import { DashModule } from './lib/dash.js'; +import { IconGridModule } from './lib/iconGrid.js'; +import { MessageTrayModule } from './lib/messageTray.js'; +import { OsdWindowModule } from './lib/osdWindow.js'; +import { OverlayKeyModule } from './lib/overlayKey.js'; +import { OverviewModule } from './lib/overview.js'; +import { OverviewControlsModule } from './lib/overviewControls.js'; +import { PanelModule } from './lib/panel.js'; +import { SearchControllerModule } from './lib/searchController.js'; +import { SearchModule } from './lib/search.js'; +import { SwipeTrackerModule } from './lib/swipeTracker.js'; +import { WindowAttentionHandlerModule } from './lib/windowAttentionHandler.js'; +import { WindowManagerModule } from './lib/windowManager.js'; +import { WindowPreviewModule } from './lib/windowPreview.js'; +import { WorkspaceAnimationModule } from './lib/workspaceAnimation.js'; +import { WorkspaceModule } from './lib/workspace.js'; +import { WorkspaceSwitcherPopupModule } from './lib/workspaceSwitcherPopup.js'; +import { WindowSearchProviderModule } from './lib/windowSearchProvider.js'; +import { RecentFilesSearchProviderModule } from './lib/recentFilesSearchProvider.js'; +import { ExtensionsSearchProviderModule } from './lib/extensionsSearchProvider.js'; +import { WinTmbModule } from './lib/winTmb.js'; + +let Me; +// gettext +let _; +let opt; + +export default class VShell extends Extension.Extension { + _init() { + Me = {}; + + Me.getSettings = this.getSettings.bind(this); + Me.shellVersion = parseFloat(Config.PACKAGE_VERSION); + Me.metadata = this.metadata; + Me.gSettings = this.getSettings(); + Me.Settings = Settings; + Me.Util = _Util; + Me.gettext = this.gettext.bind(this); + _ = Me.gettext; + + Me.WSP_PREFIX = WindowSearchProviderModule._PREFIX; + Me.RFSP_PREFIX = RecentFilesSearchProviderModule._PREFIX; + Me.ESP_PREFIX = ExtensionsSearchProviderModule._PREFIX; + + Me.opt = new Me.Settings.Options(Me); + + Me.Util.init(Me); + } + + _cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + enable() { + this._init(); + // flag for Util.getEnabledExtensions() + Me.extensionsLoadIncomplete = Main.layoutManager._startingUp; + opt = Me.opt; + + this._initModules(); + this.activateVShell(); + + Me.extensionsLoadIncomplete = false; + + console.debug(`${Me.metadata.name}: enabled`); + } + + // Reason for using "unlock-dialog" session mode: + // Updating the "appDisplay" content every time the screen is locked/unlocked takes quite a lot of time and affects the user experience. + disable() { + this.removeVShell(); + this._disposeModules(); + + // If Dash to Dock is enabled, disabling V-Shell can end in broken overview + Main.overview.hide(); + + console.debug(`${Me.metadata.name}: disabled`); + + this._cleanGlobals(); + } + + _getModuleList() { + return Object.keys(Me.Modules); + } + + _initModules() { + Me.Modules = {}; + Me.Modules.appDisplayModule = new AppDisplayModule(Me); + Me.Modules.appFavoritesModule = new AppFavoritesModule(Me); + Me.Modules.dashModule = new DashModule(Me); + Me.Modules.iconGridModule = new IconGridModule(Me); + Me.Modules.layoutModule = new LayoutModule(Me); + Me.Modules.messageTrayModule = new MessageTrayModule(Me); + Me.Modules.overviewModule = new OverviewModule(Me); + Me.Modules.overviewControlsModule = new OverviewControlsModule(Me); + Me.Modules.osdWindowModule = new OsdWindowModule(Me); + Me.Modules.overlayKeyModule = new OverlayKeyModule(Me); + Me.Modules.panelModule = new PanelModule(Me); + Me.Modules.searchModule = new SearchModule(Me); + Me.Modules.searchControllerModule = new SearchControllerModule(Me); + Me.Modules.swipeTrackerModule = new SwipeTrackerModule(Me); + Me.Modules.windowAttentionHandlerModule = new WindowAttentionHandlerModule(Me); + Me.Modules.windowPreviewModule = new WindowPreviewModule(Me); + Me.Modules.windowManagerModule = new WindowManagerModule(Me); + Me.Modules.workspaceModule = new WorkspaceModule(Me); + Me.Modules.workspaceAnimationModule = new WorkspaceAnimationModule(Me); + Me.Modules.workspaceSwitcherPopupModule = new WorkspaceSwitcherPopupModule(Me); + Me.Modules.workspaceThumbnailModule = new WorkspaceThumbnailModule(Me); + Me.Modules.workspacesViewModule = new WorkspacesViewModule(Me); + Me.Modules.windowSearchProviderModule = new WindowSearchProviderModule(Me); + Me.Modules.recentFilesSearchProviderModule = new RecentFilesSearchProviderModule(Me); + Me.Modules.extensionsSearchProviderModule = new ExtensionsSearchProviderModule(Me); + Me.Modules.winTmbModule = new WinTmbModule(Me); + } + + _disposeModules() { + Me.opt.destroy(); + Me.opt = null; + + for (let module of this._getModuleList()) { + if (!Me.Modules[module].moduleEnabled) + Me.Modules[module].cleanGlobals(); + } + + Me.Util.cleanGlobals(); + + Me.Modules = null; + opt = null; + } + + activateVShell() { + this._enabled = true; + + this._originalGetNeighbor = Meta.Workspace.prototype.get_neighbor; + + this._removeTimeouts(); + this._timeouts = {}; + + // load VShell configuration + this._updateSettings(); + + // activate all enabled VShell modules + this._updateOverrides(); + + // connect signals to help VShell adapt to changes in DE configuration + this._updateConnections(); + + // switch PageUp/PageDown workspace switcher shortcuts + this._switchPageShortcuts(); + + // if Dash to Dock detected force enable "Fix for DtD" option + this._updateFixDashToDockOption(); + + // update overview background wallpaper if enabled, but don't set it too early on session startup + // because it crashes wayland + if (!Main.layoutManager._startingUp || Meta.is_restart()) + Main.overview._overview.controls._setBackground(); + + this._updateSettingsConnection(); + + // store dash _workId so we will be able to detect replacement when entering overview + this._storeDashId(); + + // workaround for upstream bug - overview always shows workspace 1 instead of the active one after restart + this._setInitialWsIndex(); + } + + removeVShell() { + this._enabled = false; + + const reset = true; + this._removeTimeouts(); + + this._removeConnections(); + Main.overview._overview.controls._setBackground(reset); + + // remove changes mede by VShell modules + this._updateOverrides(reset); + + // switch PageUp/PageDown workspace switcher shortcuts + this._switchPageShortcuts(); + + // remove any position offsets from dash and ws thumbnails + if (!Me.Util.dashNotDefault()) { + Main.overview.dash.translation_x = 0; + Main.overview.dash.translation_y = 0; + } + Main.overview._overview._controls._thumbnailsBox.translation_x = 0; + Main.overview._overview._controls._thumbnailsBox.translation_y = 0; + Main.overview._overview._controls._searchEntryBin.translation_y = 0; + Main.overview._overview._controls.set_child_above_sibling(Main.overview._overview._controls._workspacesDisplay, null); + // restore default animation speed + St.Settings.get().slow_down_factor = 1; + + // restore default dash background style + Main.overview.dash._background.set_style(''); + // hide status message if shown + this._showStatusMessage(false); + this._prevDash = null; + + Meta.Workspace.prototype.get_neighbor = this._originalGetNeighbor; + } + + _removeTimeouts() { + if (this._timeouts) { + Object.values(this._timeouts).forEach(id => { + if (id) + GLib.source_remove(id); + }); + } + this._timeouts = null; + } + + _storeDashId() { + this._prevDash = Main.overview.dash._workId; + } + + _setInitialWsIndex() { + if (Main.layoutManager._startingUp) { + GLib.idle_add(GLib.PRIORITY_LOW, () => { + Main.overview._overview.controls._workspaceAdjustment.set_value(global.workspace_manager.get_active_workspace_index()); + }); + } + } + + _updateSettingsConnection() { + if (!opt._extensionUpdateId) + opt._extensionUpdateId = opt.connect('changed', this._updateSettings.bind(this)); + } + + _updateFixDashToDockOption() { + const dtdEnabled = !!(Me.Util.getEnabledExtensions('dash-to-dock').length || + Me.Util.getEnabledExtensions('ubuntu-dock').length); + + // force enable Fix Dash to Dock option if DtD detected + opt._watchDashToDock = dtdEnabled; + } + + _updateConnections() { + if (!this._monitorsChangedConId) + this._monitorsChangedConId = Main.layoutManager.connect('monitors-changed', () => this._updateVShell(2000)); + + + if (!this._showingOverviewConId) + this._showingOverviewConId = Main.overview.connect('showing', this._onShowingOverview.bind(this)); + + if (!this._sessionModeConId) { + // the panel must be visible when screen is locked + this._sessionModeConId = Main.sessionMode.connect('updated', session => { + if (session.currentMode === 'user' || session.parentMode === 'user') { + this._timeouts.unlock = GLib.idle_add(GLib.PRIORITY_LOW, + () => { + Me.Modules.panelModule.update(); + Me.Modules.overviewControlsModule.update(); + Me.Modules.winTmbModule.showThumbnails(); + + this._timeouts.unlock = 0; + return GLib.SOURCE_REMOVE; + } + ); + } else if (session.currentMode === 'unlock-dialog') { + Me.Modules.panelModule.update(true); + Me.Modules.winTmbModule.hideThumbnails(); + } + }); + } + + if (!this._watchDockSigId) { + this._watchDockSigId = Main.extensionManager.connect('extension-state-changed', + (source, extension) => { + const uuid = extension.uuid; + // ExtensionState = { + // ENABLED: 1, + // DISABLED: 2, + // ERROR: 3, + // OUT_OF_DATE: 4, + // DOWNLOADING: 5, + // INITIALIZED: 6, + // DISABLING: 7, + // ENABLING: 8, + // + // // Used as an error state for operations on unknown extensions, + // // should never be in a real extensionMeta object. + // UNINSTALLED: 99, + // }; + // no need to restart on disable/remove + // - if DtD was enabled before VShell, VShell will be rebased by extensionSystem + // - if DtD was enabled after VShell, the first _showingOverview detect replacement of the dash and repair VShell + const reset = [1, 2].includes(extension.state); + const dashReplacement = uuid.includes('dash-to-dock') || uuid.includes('ubuntu-dock') || uuid.includes('dash-to-panel'); + if (dashReplacement && reset) + opt._watchDashToDock = true; + if (!Main.layoutManager._startingUp && reset && dashReplacement) + this._updateVShell(1999); + } + ); + } + } + + _removeConnections() { + if (this._monitorsChangedConId) { + Main.layoutManager.disconnect(this._monitorsChangedConId); + this._monitorsChangedConId = 0; + } + + if (this._showingOverviewConId) { + Main.overview.disconnect(this._showingOverviewConId); + this._showingOverviewConId = 0; + } + + if (this._sessionModeConId) { + Main.sessionMode.disconnect(this._sessionModeConId); + this._sessionModeConId = 0; + } + + if (this._watchDockSigId) { + Main.extensionManager.disconnect(this._watchDockSigId); + this._watchDockSigId = 0; + } + } + + _updateOverrides(reset = false) { + Me.Modules.workspacesViewModule.update(reset); + Me.Modules.workspaceThumbnailModule.update(reset); + Me.Modules.overviewModule.update(reset); + Me.Modules.overviewControlsModule.update(reset); + + Me.Modules.workspaceModule.update(reset); + Me.Modules.windowPreviewModule.update(reset); + Me.Modules.windowManagerModule.update(reset); + + Me.Modules.layoutModule.update(reset); + Me.Modules.dashModule.update(reset); + // avoid enabling panel module when session is locked + if (reset || (!reset && !Main.sessionMode.isLocked)) + Me.Modules.panelModule.update(reset); + // the panel must be visible when screen is locked + // at startup time, panel will be updated from the startupAnimation after allocation + if (!reset && Main.sessionMode.isLocked && !Main.layoutManager._startingUp) + Me.Modules.panelModule._showPanel(true); + // PanelModule._showPanel(true); + // hide panel so it appears directly on the final place + /* else if (Main.layoutManager._startingUp && !Meta.is_restart()) + Main.panel.opacity = 0;*/ + + Me.Modules.workspaceAnimationModule.update(reset); + Me.Modules.workspaceSwitcherPopupModule.update(reset); + + Me.Modules.swipeTrackerModule.update(reset); + + Me.Modules.searchModule.update(reset); + + Me.Modules.windowSearchProviderModule.update(reset); + Me.Modules.recentFilesSearchProviderModule.update(reset); + Me.Modules.extensionsSearchProviderModule.update(reset); + + // don't rebuild app grid on any screen lock + // even if the extension includes unlock-screen session mode + // disable/enable is called at least once even on GS44 + // when screen lock is activated for the first time + // because every first disable of each extension rebases + // the entire extensions stack that was enabled later + if (Main.sessionMode.isLocked) + this._sessionLockActive = true; + + // This covers unnecessary enable/disable cycles during first screen lock when extensions are rebased, but is not allowed by the EGO rules + if (!this._sessionLockActive || !Main.extensionManager._getEnabledExtensions().includes(Me.metadata.uuid)) { + // iconGridModule will be updated from appDisplayModule + Me.Modules.appDisplayModule.update(reset); + } + + if (!this._sessionLockActive && !Main.layoutManager._startingUp && opt.APP_GRID_PERFORMANCE) { + // Avoid showing status at startup, can cause freeze + this._showStatusMessage(); + } + + if (!Main.sessionMode.isLocked) + this._sessionLockActive = false; + + // iconGridModule will be updated from appDisplayModule + // Me.Modules.appDisplayModule.update(reset); + + Me.Modules.windowAttentionHandlerModule.update(reset); + Me.Modules.appFavoritesModule.update(reset); + Me.Modules.messageTrayModule.update(reset); + Me.Modules.osdWindowModule.update(reset); + Me.Modules.overlayKeyModule.update(reset); + Me.Modules.searchControllerModule.update(reset); + Me.Modules.winTmbModule.update(reset); + + if (!reset && !Main.layoutManager._startingUp) + Main.overview._overview.controls.setInitialTranslations(); + } + + _onShowingOverview() { + if (Main.layoutManager._startingUp) + return; + + Main.overview._overview.controls.opacity = 255; + + // store pointer X coordinate for OVERVIEW_MODE 1 window spread - if mouse pointer is steady, don't spread + opt.showingPointerX = global.get_pointer()[0]; + + if (!Main.overview._overview.controls._bgManagers && (opt.SHOW_BG_IN_OVERVIEW || opt.SHOW_WS_PREVIEW_BG) && !Me.Util.getEnabledExtensions('blur-my-shell').length) + Main.overview._overview.controls._setBackground(); + + if (opt._watchDashToDock) { + // workaround for Dash to Dock (Ubuntu Dock) breaking overview allocations after enabled and changed position + // DtD replaces dock and its _workId on every position change + const dash = Main.overview.dash; + if (this._prevDash !== dash._workId) + this._updateVShell(0); + } + } + + _updateVShell(timeout = 200) { + if (!this._enabled || Main.layoutManager._startingUp) + return; + + if (this._timeouts.reset) + GLib.source_remove(this._timeouts.reset); + this._timeouts.reset = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + timeout, + () => { + if (!this._enabled) + return GLib.SOURCE_REMOVE; + + const dash = Main.overview.dash; + if (timeout < 2000) { // timeout < 2000 for partial update + this._prevDash = dash._workId; + console.warn(`[${Me.metadata.name}]: Dash has been replaced, updating extension ...`); + Me._resetInProgress = true; + // update only necessary modules if dash has been replaced + this._repairOverrides(); + Me._resetInProgress = false; + } else { + console.warn(`[${Me.metadata.name}]: Updating extension ...`); + // for case the monitor configuration has been changed, update all + Me._resetInProgress = true; + this.activateVShell(); + Me._resetInProgress = false; + } + this._timeouts.reset = 0; + return GLib.SOURCE_REMOVE; + } + ); + } + + // the key modules that can be affected by the supported incompatible extensions + _repairOverrides() { + Me.Modules.overviewModule.update(); + Me.Modules.overviewControlsModule.update(); + Me.Modules.layoutModule.update(); + Me.Modules.workspacesViewModule.update(); + Me.Modules.windowPreviewModule.update(); + Me.Modules.panelModule.update(); + Me.Modules.dashModule.update(); + this._updateSettings(); + } + + _updateSettings(settings, key) { + // update settings cache and option variables + opt._updateSettings(); + + // avoid overload while loading profile - update only once + // delayed gsettings writes are processed alphabetically + if (key === 'aaa-loading-profile') { + this._showStatusMessage(); + if (this._timeouts.loadingProfile) + GLib.source_remove(this._timeouts.loadingProfile); + this._timeouts.loadingProfile = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 100, () => { + this.activateVShell(); + this._timeouts.loadingProfile = 0; + return GLib.SOURCE_REMOVE; + }); + } + if (this._timeouts.loadingProfile) + return; + + if (key?.includes('profile-data')) { + const index = key.replace('profile-data-', ''); + Main.notify(`${Me.metadata.name}`, `Profile ${index} has been updated`); + } + + opt.WORKSPACE_MIN_SPACING = Main.overview._overview._controls._thumbnailsBox.get_theme_node().get_length('spacing'); + // update variables that cannot be processed within settings + const dash = Main.overview.dash; + if (Me.Util.dashIsDashToDock()) { + opt.DASH_POSITION = dash._position; + opt.DASH_TOP = opt.DASH_POSITION === 0; + opt.DASH_RIGHT = opt.DASH_POSITION === 1; + opt.DASH_BOTTOM = opt.DASH_POSITION === 2; + opt.DASH_LEFT = opt.DASH_POSITION === 3; + opt.DASH_VERTICAL = opt.DASH_LEFT || opt.DASH_RIGHT; + } + + opt.DASH_VISIBLE = opt.DASH_VISIBLE && !Me.Util.getEnabledExtensions('dash-to-panel@jderose9.github.com').length; + + const monitorWidth = global.display.get_monitor_geometry(global.display.get_primary_monitor()).width; + if (monitorWidth < 1600) { + opt.APP_GRID_ICON_SIZE_DEFAULT = opt.APP_GRID_ACTIVE_PREVIEW && !opt.APP_GRID_USAGE ? 128 : 64; + opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT = 64; + } + + /* if (!Me.Util.dashIsDashToDock()) { // DtD has its own opacity control + Me.Modules.dashModule.updateStyle(dash); + }*/ + + // adjust search entry style for OM2 + if (opt.OVERVIEW_MODE2) + Main.overview.searchEntry.add_style_class_name('search-entry-om2'); + else + Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); + + if (opt.OVERVIEW_MODE === 1) + Me.Modules.workspaceModule.setWindowPreviewMaxScale(0.1); + else + Me.Modules.workspaceModule.setWindowPreviewMaxScale(0.95); + + Main.overview.searchEntry.visible = opt.SHOW_SEARCH_ENTRY; + Main.overview.searchEntry.opacity = 255; + St.Settings.get().slow_down_factor = opt.ANIMATION_TIME_FACTOR; + + opt.START_Y_OFFSET = (opt.get('panelModule') && opt.PANEL_OVERVIEW_ONLY && opt.PANEL_POSITION_TOP) || + // better to add unnecessary space than to have a panel overlapping other objects + Me.Util.getEnabledExtensions('hidetopbar').length + ? Main.panel.height + : 0; + + // Options for workspace switcher, apply custom function only if needed + if (opt.WS_WRAPAROUND || opt.WS_IGNORE_LAST) + Meta.Workspace.prototype.get_neighbor = this._getNeighbor; + else + Meta.Workspace.prototype.get_neighbor = this._originalGetNeighbor; + + if (settings) + this._applySettings(key); + } + + _applySettings(key) { + if (key?.endsWith('-module')) { + for (let module of this._getModuleList()) { + if (opt.options[module] && key === opt.options[module][1]) { + if (key === 'app-display-module') + this._showStatusMessage(); + Me.Modules[module].update(); + break; + } + } + } + + Main.overview._overview.controls._setBackground(); + this._switchPageShortcuts(); + + if (key?.includes('panel')) + Me.Modules.panelModule.update(); + + if (key?.includes('dash') || key?.includes('icon') || key?.includes('dot-style') || key?.includes('provider')) + Me.Modules.dashModule.update(); + + if (key?.includes('hot-corner') || key?.includes('dash')) + Me.Modules.layoutModule.update(); + + switch (key) { + case 'ws-thumbnails-position': + this._updateOverrides(); + break; + case 'workspace-switcher-animation': + Me.Modules.workspaceAnimationModule.update(); + break; + case 'search-width-scale': + Me.Modules.searchModule.update(); + break; + case 'favorites-notify': + Me.Modules.appFavoritesModule.update(); + break; + case 'window-attention-mode': + Me.Modules.windowAttentionHandlerModule.update(); + break; + case 'show-ws-preview-bg': + Me.Modules.panelModule.update(); + break; + case 'notification-position': + Me.Modules.messageTrayModule.update(); + break; + case 'osd-position': + Me.Modules.osdWindowModule.update(); + break; + case 'overlay-key': + Me.Modules.overlayKeyModule.update(); + break; + case 'always-activate-selected-window': + Me.Modules.windowPreviewModule.update(); + break; + } + + if (key?.includes('app-grid') || + key?.includes('app-folder') || + key?.includes('dot-style') || + key === 'show-search-entry' || + key === 'ws-thumbnail-scale' || + key === 'ws-thumbnail-scale-appgrid') { + this._showStatusMessage(); + Me.Modules.appDisplayModule.update(); + } + } + + _switchPageShortcuts() { + // ignore screen lock + if (!opt.get('enablePageShortcuts') || this._sessionLockActive) + return; + + const vertical = global.workspaceManager.layout_rows === -1; + const schema = 'org.gnome.desktop.wm.keybindings'; + const settings = Me.getSettings(schema); + + const keyLeft = 'switch-to-workspace-left'; + const keyRight = 'switch-to-workspace-right'; + const keyUp = 'switch-to-workspace-up'; + const keyDown = 'switch-to-workspace-down'; + + const keyMoveLeft = 'move-to-workspace-left'; + const keyMoveRight = 'move-to-workspace-right'; + const keyMoveUp = 'move-to-workspace-up'; + const keyMoveDown = 'move-to-workspace-down'; + + const switchPrevSc = 'Page_Up'; + const switchNextSc = 'Page_Down'; + const movePrevSc = 'Page_Up'; + const moveNextSc = 'Page_Down'; + + let switchLeft = settings.get_strv(keyLeft); + let switchRight = settings.get_strv(keyRight); + let switchUp = settings.get_strv(keyUp); + let switchDown = settings.get_strv(keyDown); + + let moveLeft = settings.get_strv(keyMoveLeft); + let moveRight = settings.get_strv(keyMoveRight); + let moveUp = settings.get_strv(keyMoveUp); + let moveDown = settings.get_strv(keyMoveDown); + + if (vertical) { + if (switchLeft.includes(switchPrevSc)) + switchLeft.splice(switchLeft.indexOf(switchPrevSc), 1); + if (switchRight.includes(switchNextSc)) + switchRight.splice(switchRight.indexOf(switchNextSc), 1); + if (moveLeft.includes(movePrevSc)) + moveLeft.splice(moveLeft.indexOf(movePrevSc), 1); + if (moveRight.includes(moveNextSc)) + moveRight.splice(moveRight.indexOf(moveNextSc), 1); + + if (!switchUp.includes(switchPrevSc)) + switchUp.push(switchPrevSc); + if (!switchDown.includes(switchNextSc)) + switchDown.push(switchNextSc); + if (!moveUp.includes(movePrevSc)) + moveUp.push(movePrevSc); + if (!moveDown.includes(moveNextSc)) + moveDown.push(moveNextSc); + } else { + if (!switchLeft.includes(switchPrevSc)) + switchLeft.push(switchPrevSc); + if (!switchRight.includes(switchNextSc)) + switchRight.push(switchNextSc); + if (!moveLeft.includes(movePrevSc)) + moveLeft.push(movePrevSc); + if (!moveRight.includes(moveNextSc)) + moveRight.push(moveNextSc); + + if (switchUp.includes(switchPrevSc)) + switchUp.splice(switchUp.indexOf(switchPrevSc), 1); + if (switchDown.includes(switchNextSc)) + switchDown.splice(switchDown.indexOf(switchNextSc), 1); + if (moveUp.includes(movePrevSc)) + moveUp.splice(moveUp.indexOf(movePrevSc), 1); + if (moveDown.includes(moveNextSc)) + moveDown.splice(moveDown.indexOf(moveNextSc), 1); + } + + settings.set_strv(keyLeft, switchLeft); + settings.set_strv(keyRight, switchRight); + settings.set_strv(keyUp, switchUp); + settings.set_strv(keyDown, switchDown); + + settings.set_strv(keyMoveLeft, moveLeft); + settings.set_strv(keyMoveRight, moveRight); + settings.set_strv(keyMoveUp, moveUp); + settings.set_strv(keyMoveDown, moveDown); + } + + // Status dialog that appears during updating V-Shell configuration and blocks inputs + _showStatusMessage(show = true) { + if ((show && Me._resetInProgress) || Main.layoutManager._startingUp || !Main.overview._overview.controls._appDisplay._sortOrderedItemsAlphabetically) + return; + + if (Me._vShellMessageTimeoutId) { + GLib.source_remove(Me._vShellMessageTimeoutId); + Me._vShellMessageTimeoutId = 0; + } + + if (Me._vShellStatusMessage && !show) { + Me._vShellStatusMessage.close(); + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + } + + if (!show) + return; + + if (!Me._vShellStatusMessage) { + const sm = new /* Main.*/RestartMessage(_('Updating V-Shell...')); + sm.set_style('background-color: rgba(0,0,0,0.3);'); + sm.open(); + Me._vShellStatusMessage = sm; + } + + // just for case the message wasn't removed from appDisplay after App Grid realization + Me._vShellMessageTimeoutId = GLib.timeout_add_seconds( + GLib.PRIORITY_DEFAULT, + 5, + () => { + if (Me._vShellStatusMessage) { + Me._vShellStatusMessage.close(); + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + Me._resetInProgress = false; + } + + Me._vShellMessageTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } + ); + } + + _getNeighbor(direction) { + // workspace matrix is supported + const activeIndex = this.index(); + const ignoreLast = opt.WS_IGNORE_LAST && !Main.overview._shown ? 1 : 0; + const wraparound = opt.WS_WRAPAROUND; + const nWorkspaces = global.workspace_manager.n_workspaces; + const lastIndex = nWorkspaces - 1 - ignoreLast; + const rows = global.workspace_manager.layout_rows > -1 ? global.workspace_manager.layout_rows : nWorkspaces; + const columns = global.workspace_manager.layout_columns > -1 ? global.workspace_manager.layout_columns : nWorkspaces; + + let index = activeIndex; + let neighborExists; + + if (direction === Meta.MotionDirection.LEFT) { + index -= 1; + const currentRow = Math.floor(activeIndex / columns); + const indexRow = Math.floor(index / columns); + neighborExists = index > -1 && indexRow === currentRow; + if (wraparound && !neighborExists) { + index = currentRow * columns + columns - 1; + const maxIndexOnLastRow = lastIndex % columns; + index = index < (lastIndex - ignoreLast) ? index : currentRow * columns + maxIndexOnLastRow; + } + } else if (direction === Meta.MotionDirection.RIGHT) { + index += 1; + const currentRow = Math.floor(activeIndex / columns); + const indexRow = Math.floor(index / columns); + neighborExists = index <= lastIndex && indexRow === currentRow; + if (wraparound && !neighborExists) + index = currentRow * columns; + } else if (direction === Meta.MotionDirection.UP) { + index -= columns; + neighborExists = index > -1; + if (wraparound && !neighborExists) { + index = rows * columns + index; + index = index < nWorkspaces - ignoreLast ? index : index - columns; + } + } else if (direction === Meta.MotionDirection.DOWN) { + index += columns; + neighborExists = index <= lastIndex; + if (wraparound && !neighborExists) + index %= columns; + } + + return global.workspace_manager.get_workspace_by_index(neighborExists || wraparound ? index : activeIndex); + } +} + +const RestartMessage = GObject.registerClass( +class RestartMessage extends ModalDialog.ModalDialog { + _init(message) { + super._init({ + shellReactive: true, + styleClass: 'restart-message headline', + shouldFadeIn: false, + destroyOnClose: true, + }); + + let label = new St.Label({ + text: message, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + }); + + this.contentLayout.add_child(label); + this.buttonLayout.hide(); + } +}); diff --git a/extensions/45/vertical-workspaces/lib/appDisplay.js b/extensions/45/vertical-workspaces/lib/appDisplay.js new file mode 100644 index 0000000..22640c6 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/appDisplay.js @@ -0,0 +1,1677 @@ +/** + * V-Shell (Vertical Workspaces) + * appDisplay.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Graphene from 'gi://Graphene'; +import Meta from 'gi://Meta'; +import Pango from 'gi://Pango'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js'; +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; + +let Me; +let opt; +// gettext +let _; + +let _timeouts; + +const APP_ICON_TITLE_EXPAND_TIME = 200; +const APP_ICON_TITLE_COLLAPSE_TIME = 100; + +function _getCategories(info) { + let categoriesStr = info.get_categories(); + if (!categoriesStr) + return []; + return categoriesStr.split(';'); +} + +function _listsIntersect(a, b) { + for (let itemA of a) { + if (b.includes(itemA)) + return true; + } + return false; +} + +export const AppDisplayModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + + this._appGridLayoutSettings = null; + this._appDisplayScrollConId = 0; + this._appSystemStateConId = 0; + this._appGridLayoutConId = 0; + this._origAppViewItemAcceptDrop = null; + this._updateFolderIcons = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + this._removeTimeouts(); + this.moduleEnabled = opt.get('appDisplayModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + this.moduleEnabled = false; + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) { + this.moduleEnabled = false; + console.debug(' AppDisplayModule - Keeping untouched'); + } + } + + _activateModule() { + Me.Modules.iconGridModule.update(); + + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + // Common + // this._overrides.addOverride('BaseAppViewCommon', AppDisplay.BaseAppView.prototype, BaseAppViewCommon); + // instead of overriding inaccessible BaseAppView class, we override its children - AppDisplay and FolderView + this._overrides.addOverride('BaseAppViewCommonApp', AppDisplay.AppDisplay.prototype, BaseAppViewCommon); + this._overrides.addOverride('BaseAppViewCommonFolder', AppDisplay.FolderView.prototype, BaseAppViewCommon); + this._overrides.addOverride('FolderView', AppDisplay.FolderView.prototype, FolderView); + this._overrides.addOverride('AppDisplay', AppDisplay.AppDisplay.prototype, AppDisplayCommon); + this._overrides.addOverride('AppViewItem', AppDisplay.AppViewItem.prototype, AppViewItemCommon); + this._overrides.addOverride('FolderIcon', AppDisplay.FolderIcon.prototype, FolderIcon); + if (opt.APP_GRID_ACTIVE_PREVIEW) + this._overrides.addOverride('ActiveFolderIcon', AppDisplay.FolderIcon, ActiveFolderIcon); + this._overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIcon); + + if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) { + // this._overrides.addOverride('BaseAppViewVertical', AppDisplay.BaseAppView.prototype, BaseAppViewVertical); + this._overrides.addOverride('BaseAppViewVerticalApp', AppDisplay.AppDisplay.prototype, BaseAppViewVertical); + this._overrides.addOverride('BaseAppViewVerticalFolder', AppDisplay.FolderView.prototype, BaseAppViewVertical); + this._overrides.addOverride('AppDisplayVertical', AppDisplay.AppDisplay.prototype, AppDisplayVertical); + } + + // Custom App Grid + this._overrides.addOverride('AppFolderDialog', AppDisplay.AppFolderDialog.prototype, AppFolderDialog); + + // BaseAppViewGridLayout is not exported, we can only access current instance + this._overrides.addOverride('BaseAppViewGridLayout', Main.overview._overview.controls._appDisplay._appGridLayout, BaseAppViewGridLayout); + // this._overrides.addOverride('FolderGrid', AppDisplay.FolderGrid.prototype, FolderGrid); + + this._setAppDisplayOrientation(opt.ORIENTATION === Clutter.Orientation.VERTICAL); + this._updateDND(); + + const appDisplay = Main.overview._overview.controls._appDisplay; + + if (!this._originalWorkId) + this._originalWorkId = appDisplay._redisplayWorkId; + if (!this._newWorkId) { + appDisplay._redisplayWorkId = Main.initializeDeferredWork(appDisplay, () => { + appDisplay._redisplay(); + if (appDisplay._overviewHiddenId === 0) + appDisplay._overviewHiddenId = Main.overview.connect('hidden', () => appDisplay.goToPage(0)); + }); + this._newWorkId = appDisplay._redisplayWorkId; + } else { + appDisplay._redisplayWorkId = this._newWorkId; + } + + + if (!Main.sessionMode.isGreeter) + this._updateAppDisplayProperties(); + + console.debug(' AppDisplayModule - Activated'); + } + + _disableModule() { + Me.Modules.iconGridModule.update(true); + + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + const reset = true; + this._setAppDisplayOrientation(false); + this._updateAppDisplayProperties(reset); + this._updateDND(reset); + this._restoreOverviewGroup(); + this._removeStatusMessage(); + + // register a new appDisplay workId so the original code will be called from the callback + const appDisplay = Main.overview._overview.controls._appDisplay; + appDisplay._redisplayWorkId = this._originalWorkId; + + console.debug(' AppDisplayModule - Disabled'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } + + _setAppDisplayOrientation(vertical = false) { + const CLUTTER_ORIENTATION = vertical ? Clutter.Orientation.VERTICAL : Clutter.Orientation.HORIZONTAL; + const scroll = vertical ? 'vscroll' : 'hscroll'; + // app display to vertical has issues - page indicator not working + // global appDisplay orientation switch is not built-in + let appDisplay = Main.overview._overview._controls._appDisplay; + // following line itself only changes in which axis will operate overshoot detection which switches appDisplay pages while dragging app icon to vertical + appDisplay._orientation = CLUTTER_ORIENTATION; + appDisplay._grid.layoutManager._orientation = CLUTTER_ORIENTATION; + appDisplay._swipeTracker.orientation = CLUTTER_ORIENTATION; + appDisplay._swipeTracker._reset(); + if (vertical) { + appDisplay._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); + + // move and change orientation of page indicators + const pageIndicators = appDisplay._pageIndicators; + pageIndicators.vertical = true; + appDisplay._box.vertical = false; + pageIndicators.x_expand = false; + pageIndicators.y_align = Clutter.ActorAlign.CENTER; + pageIndicators.x_align = Clutter.ActorAlign.START; + + // moving these bars needs more patching of the appDisplay's code + // for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page + appDisplay._nextPageIndicator.add_style_class_name('nextPageIndicator'); + appDisplay._prevPageIndicator.add_style_class_name('prevPageIndicator'); + + // setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them + appDisplay._nextPageArrow.scale_x = 0; + appDisplay._prevPageArrow.scale_x = 0; + } else { + appDisplay._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER); + if (this._appDisplayScrollConId) { + appDisplay._adjustment.disconnect(this._appDisplayScrollConId); + this._appDisplayScrollConId = 0; + } + + // restore original page indicators + const pageIndicators = appDisplay._pageIndicators; + pageIndicators.vertical = false; + appDisplay._box.vertical = true; + pageIndicators.x_expand = true; + pageIndicators.y_align = Clutter.ActorAlign.END; + pageIndicators.x_align = Clutter.ActorAlign.CENTER; + + // put back touch friendly navigation buttons + const scrollContainer = appDisplay._scrollView.get_parent(); + if (appDisplay._hintContainer && !appDisplay._hintContainer.get_parent()) { + scrollContainer.add_child(appDisplay._hintContainer); + // the hit container covers the entire app grid and added at the top of the stack blocks DND drops + // so it needs to be pushed below + scrollContainer.set_child_below_sibling(appDisplay._hintContainer, null); + } + + appDisplay._nextPageArrow.scale_x = 1; + appDisplay._prevPageArrow.scale_x = 1; + + appDisplay._nextPageIndicator.remove_style_class_name('nextPageIndicator'); + appDisplay._prevPageIndicator.remove_style_class_name('prevPageIndicator'); + } + + // value for page indicator is calculated from scroll adjustment, horizontal needs to be replaced by vertical + appDisplay._adjustment = appDisplay._scrollView[scroll].adjustment; + + // no need to connect already connected signal (wasn't removed the original one before) + if (!vertical) { + // reset used appDisplay properties + Main.overview._overview._controls._appDisplay.scale_y = 1; + Main.overview._overview._controls._appDisplay.scale_x = 1; + Main.overview._overview._controls._appDisplay.opacity = 255; + return; + } + + // update appGrid dot pages indicators + this._appDisplayScrollConId = appDisplay._adjustment.connect('notify::value', adj => { + const value = adj.value / adj.page_size; + appDisplay._pageIndicators.setCurrentPosition(value); + }); + } + + // Set App Grid columns, rows, icon size, incomplete pages + _updateAppDisplayProperties(reset = false) { + opt._appGridNeedsRedisplay = false; + // columns, rows, icon size + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.visible = true; + if (reset) { + appDisplay._grid.layoutManager.fixedIconSize = -1; + appDisplay._grid.layoutManager.allow_incomplete_pages = true; + appDisplay._grid._currentMode = -1; + appDisplay._grid.setGridModes(); + if (this._appGridLayoutSettings) { + this._appGridLayoutSettings.disconnect(this._appGridLayoutConId); + this._appGridLayoutConId = 0; + this._appGridLayoutSettings = null; + } + appDisplay._redisplay(); + + appDisplay._grid.set_style(''); + this._updateAppGrid(reset); + } else { + // update grid on layout reset + if (!this._appGridLayoutSettings) { + this._appGridLayoutSettings = Me.getSettings('org.gnome.shell'); + this._appGridLayoutConId = this._appGridLayoutSettings.connect('changed::app-picker-layout', this._updateLayout); + } + + appDisplay._grid.layoutManager.allow_incomplete_pages = opt.APP_GRID_ALLOW_INCOMPLETE_PAGES; + // appDisplay._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`); + // APP_GRID_SPACING constant is used for grid dimensions calculation + // but sometimes the actual grid spacing properties affect/change the calculated size, therefore we set it lower to avoid this problem + // main app grid always use available space and the spacing is optimized for the grid dimensions + appDisplay._grid.set_style('column-spacing: 5px; row-spacing: 5px;'); + + // force redisplay + appDisplay._grid._currentMode = -1; + appDisplay._grid.setGridModes(); + appDisplay._grid.layoutManager.fixedIconSize = opt.APP_GRID_ICON_SIZE; + // avoid resetting appDisplay before startup animation + // x11 shell restart skips startup animation + if (!Main.layoutManager._startingUp) { + this._updateAppGrid(); + } else if (Main.layoutManager._startingUp && (Meta.is_restart() || Me.Util.dashIsDashToDock())) { + _timeouts.three = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._updateAppGrid(); + _timeouts.three = 0; + return GLib.SOURCE_REMOVE; + }); + } + } + } + + _updateDND(reset) { + if (!reset) { + if (!this._appSystemStateConId && opt.APP_GRID_INCLUDE_DASH >= 3) { + this._appSystemStateConId = Shell.AppSystem.get_default().connect( + 'app-state-changed', + () => { + this._updateFolderIcons = true; + Main.overview._overview.controls._appDisplay._redisplay(); + } + ); + } + } else if (this._appSystemStateConId) { + Shell.AppSystem.get_default().disconnect(this._appSystemStateConId); + this._appSystemStateConId = 0; + } + } + + _restoreOverviewGroup() { + Main.overview.dash.showAppsButton.checked = false; + Main.layoutManager.overviewGroup.opacity = 255; + Main.layoutManager.overviewGroup.scale_x = 1; + Main.layoutManager.overviewGroup.scale_y = 1; + Main.layoutManager.overviewGroup.hide(); + Main.overview._overview._controls._appDisplay.translation_x = 0; + Main.overview._overview._controls._appDisplay.translation_y = 0; + Main.overview._overview._controls._appDisplay.visible = true; + Main.overview._overview._controls._appDisplay.opacity = 255; + } + + // update all invalid positions that may be result of grid/icon size change + _updateIconPositions() { + const appDisplay = Main.overview._overview._controls._appDisplay; + const layout = JSON.stringify(global.settings.get_value('app-picker-layout').recursiveUnpack()); + // if app grid layout is empty, sort source alphabetically to avoid misplacing + if (layout === JSON.stringify([]) && appDisplay._sortOrderedItemsAlphabetically) + appDisplay._sortOrderedItemsAlphabetically(); + const icons = [...appDisplay._orderedItems]; + for (let i = 0; i < icons.length; i++) + appDisplay._moveItem(icons[i], -1, -1); + } + + _removeIcons() { + const appDisplay = Main.overview._overview._controls._appDisplay; + const icons = [...appDisplay._orderedItems]; + for (let i = 0; i < icons.length; i++) { + const icon = icons[i]; + if (icon._dialog) + Main.layoutManager.overviewGroup.remove_child(icon._dialog); + appDisplay._removeItem(icon); + icon.destroy(); + } + appDisplay._folderIcons = []; + } + + _removeStatusMessage() { + if (Me._vShellStatusMessage) { + if (Me._vShellMessageTimeoutId) { + GLib.source_remove(Me._vShellMessageTimeoutId); + Me._vShellMessageTimeoutId = 0; + } + Me._vShellStatusMessage.destroy(); + Me._vShellStatusMessage = null; + } + } + + _updateLayout(settings, key) { + const currentValue = JSON.stringify(settings.get_value(key).deep_unpack()); + const emptyValue = JSON.stringify([]); + const customLayout = currentValue !== emptyValue; + if (!customLayout) { + this._updateAppGrid(); + } + } + + _updateAppGrid(reset = false, callback) { + const appDisplay = Main.overview._overview._controls._appDisplay; + // reset the grid only if called directly without args or if all folders where removed by using reset button in Settings window + // otherwise this function is called every time a user moves icon to another position as a settings callback + + // force update icon size using adaptToSize(), the page size cannot be the same as the current one + appDisplay._grid.layoutManager._pageWidth += 1; + appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); + + // don't delay the first screen lock whe extensions are rebased + // removing icons takes time and with other + if (!Main.sessionMode.isLocked) + this._removeIcons(); + + appDisplay._redisplay(); + + // don't realize appDisplay on disable, or at startup if disabled + // always realize appDisplay otherwise to avoid errors while opening folders (that I was unable to trace) + if (reset || (!opt.APP_GRID_PERFORMANCE && callback)) { + this._removeStatusMessage(); + if (callback) + callback(); + return; + } + + // workaround - silently realize appDisplay + // appDisplay and its content must be "visible" (opacity > 0) on the screen (within monitor geometry) + // to realize its objects + // this action takes some time and affects animations during the first use + // if we do it invisibly before user needs it, it can improve the user's experience + + this._exposeAppGrid(); + + // let the main loop process our changes before continuing + _timeouts.one = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._updateIconPositions(); + if (appDisplay._sortOrderedItemsAlphabetically) { + appDisplay._sortOrderedItemsAlphabetically(); + appDisplay._grid.layoutManager._pageWidth += 1; + appDisplay._grid.layoutManager.adaptToSize(appDisplay._grid.layoutManager._pageWidth - 1, appDisplay._grid.layoutManager._pageHeight); + appDisplay._setLinearPositions(appDisplay._orderedItems); + } + + appDisplay._redisplay(); + // realize also all app folders (by opening them) so the first popup is as smooth as the second one + // let the main loop process our changes before continuing + _timeouts.two = GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._restoreAppGrid(); + Me._resetInProgress = false; + this._removeStatusMessage(); + + if (callback) + callback(); + + _timeouts.two = 0; + return GLib.SOURCE_REMOVE; + }); + _timeouts.one = 0; + return GLib.SOURCE_REMOVE; + }); + } + + _exposeAppGrid() { + const overviewGroup = Main.layoutManager.overviewGroup; + if (!overviewGroup.visible) { + // scale down the overviewGroup so it don't cover uiGroup + overviewGroup.scale_y = 0.001; + // make it invisible to the eye, but visible for the renderer + overviewGroup.opacity = 1; + // if overview is hidden, show it + overviewGroup.visible = true; + } + + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.opacity = 1; + + // find usable value, sometimes it's one, sometime the other... + let [x, y] = appDisplay.get_position(); + let { x1, y1 } = appDisplay.allocation; + x = x === Infinity ? 0 : x; + y = y === Infinity ? 0 : y; + x1 = x1 === Infinity ? 0 : x1; + y1 = y1 === Infinity ? 0 : y1; + appDisplay.translation_x = -(x ? x : x1); + appDisplay.translation_y = -(y ? y : y1); + this._exposeAppFolders(); + } + + _exposeAppFolders() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay._folderIcons.forEach(d => { + d._ensureFolderDialog(); + d._dialog._updateFolderSize(); + d._dialog.scale_y = 0.0001; + d._dialog.show(); + }); + } + + _restoreAppGrid() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay.translation_x = 0; + appDisplay.translation_y = 0; + // appDisplay.opacity = 0; + this._hideAppFolders(); + + const overviewGroup = Main.layoutManager.overviewGroup; + if (!Main.overview._shown) + overviewGroup.hide(); + overviewGroup.scale_y = 1; + overviewGroup.opacity = 255; + + this._removeStatusMessage(); + } + + _hideAppFolders() { + const appDisplay = Main.overview._overview._controls._appDisplay; + appDisplay._folderIcons.forEach(d => { + if (d._dialog) { + d._dialog._updateFolderSize(); + d._dialog.hide(); + d._dialog.scale_y = 1; + } + }); + } + + _getWindowApp(metaWin) { + const tracker = Shell.WindowTracker.get_default(); + return tracker.get_window_app(metaWin); + } + + _getAppLastUsedWindow(app) { + let recentWin; + global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { + const winApp = this._getWindowApp(metaWin); + if (!recentWin && winApp === app) + recentWin = metaWin; + }); + return recentWin; + } + + _getAppRecentWorkspace(app) { + const recentWin = this._getAppLastUsedWindow(app); + if (recentWin) + return recentWin.get_workspace(); + + return null; + } +}; + +const AppDisplayVertical = { + // correction of the appGrid size when page indicators were moved from the bottom to the right + adaptToSize(width, height) { + const [, indicatorWidth] = this._pageIndicators.get_preferred_width(-1); + width -= indicatorWidth; + + this._grid.findBestModeForSize(width, height); + + const adaptToSize = AppDisplay.BaseAppView.prototype.adaptToSize.bind(this); + adaptToSize(width, height); + }, +}; + +function _getViewFromIcon(icon) { + for (let parent = icon.get_parent(); parent; parent = parent.get_parent()) { + if (parent instanceof AppDisplay.AppDisplay || parent instanceof AppDisplay.FolderView) { + return parent; + } + } + return null; +} + +const AppDisplayCommon = { + _ensureDefaultFolders() { + // disable creation of default folders if user deleted them + }, + + // apps load adapted for custom sorting and including dash items + _loadApps() { + let appIcons = []; + const runningApps = Shell.AppSystem.get_default().get_running().map(a => a.id); + + this._appInfoList = Shell.AppSystem.get_default().get_installed().filter(appInfo => { + try { + appInfo.get_id(); // catch invalid file encodings + } catch (e) { + return false; + } + + const appIsRunning = runningApps.includes(appInfo.get_id()); + const appIsFavorite = this._appFavorites.isFavorite(appInfo.get_id()); + const excludeApp = (opt.APP_GRID_EXCLUDE_RUNNING && appIsRunning) || (opt.APP_GRID_EXCLUDE_FAVORITES && appIsFavorite); + + return this._parentalControlsManager.shouldShowApp(appInfo) && !excludeApp; + }); + + let apps = this._appInfoList.map(app => app.get_id()); + + let appSys = Shell.AppSystem.get_default(); + + const appsInsideFolders = new Set(); + this._folderIcons = []; + if (!opt.APP_GRID_USAGE) { + let folders = this._folderSettings.get_strv('folder-children'); + folders.forEach(id => { + let path = `${this._folderSettings.path}folders/${id}/`; + let icon = this._items.get(id); + if (!icon) { + icon = new AppDisplay.FolderIcon(id, path, this); + icon.connect('apps-changed', () => { + this._redisplay(); + this._savePages(); + }); + icon.connect('notify::pressed', () => { + if (icon.pressed) + this.updateDragFocus(icon); + }); + } else if (this._updateFolderIcons && opt.APP_GRID_EXCLUDE_RUNNING) { + // if any app changed its running state, update folder icon + icon.icon.update(); + } + + // remove empty folder icons + if (!icon.visible) { + icon.destroy(); + return; + } + + appIcons.push(icon); + this._folderIcons.push(icon); + + icon.getAppIds().forEach(appId => appsInsideFolders.add(appId)); + }); + } + + // reset request to update active icon + this._updateFolderIcons = false; + + // Allow dragging of the icon only if the Dash would accept a drop to + // change favorite-apps. There are no other possible drop targets from + // the app picker, so there's no other need for a drag to start, + // at least on single-monitor setups. + // This also disables drag-to-launch on multi-monitor setups, + // but we hope that is not used much. + const isDraggable = + global.settings.is_writable('favorite-apps') || + global.settings.is_writable('app-picker-layout'); + + apps.forEach(appId => { + if (!opt.APP_GRID_USAGE && appsInsideFolders.has(appId)) + return; + + let icon = this._items.get(appId); + if (!icon) { + let app = appSys.lookup_app(appId); + icon = new AppDisplay.AppIcon(app, { isDraggable }); + icon.connect('notify::pressed', () => { + if (icon.pressed) + this.updateDragFocus(icon); + }); + } + + appIcons.push(icon); + }); + + // At last, if there's a placeholder available, add it + if (this._placeholder) + appIcons.push(this._placeholder); + + return appIcons; + }, + + // support active preview icons + _onDragBegin(overview, source) { + if (source._sourceItem) + source = source._sourceItem; + + this._dragMonitor = { + dragMotion: this._onDragMotion.bind(this), + }; + DND.addDragMonitor(this._dragMonitor); + + this._appGridLayout.showPageIndicators(); + this._dragFocus = null; + this._swipeTracker.enabled = false; + + // When dragging from a folder dialog, the dragged app icon doesn't + // exist in AppDisplay. We work around that by adding a placeholder + // icon that is either destroyed on cancel, or becomes the effective + // new icon when dropped. + if (/* AppDisplay.*/_getViewFromIcon(source) instanceof AppDisplay.FolderView || + (opt.APP_GRID_EXCLUDE_FAVORITES && this._appFavorites.isFavorite(source.id))) + this._ensurePlaceholder(source); + }, + + _ensurePlaceholder(source) { + if (this._placeholder) + return; + + if (source._sourceItem) + source = source._sourceItem; + + const appSys = Shell.AppSystem.get_default(); + const app = appSys.lookup_app(source.id); + + const isDraggable = + global.settings.is_writable('favorite-apps') || + global.settings.is_writable('app-picker-layout'); + + this._placeholder = new AppDisplay.AppIcon(app, { isDraggable }); + this._placeholder.connect('notify::pressed', () => { + if (this._placeholder?.pressed) + this.updateDragFocus(this._placeholder); + }); + this._placeholder.scaleAndFade(); + this._redisplay(); + }, + + // accept source from active folder preview + acceptDrop(source) { + if (opt.APP_GRID_USAGE) + return false; + if (source._sourceItem) + source = source._sourceItem; + + if (!this._acceptDropCommon(source)) + return false; + + let view = /* AppDisplay.*/_getViewFromIcon(source); + if (view instanceof AppDisplay.FolderView) + view.removeApp(source.app); + + if (this._currentDialog) + this._currentDialog.popdown(); + + if (opt.APP_GRID_EXCLUDE_FAVORITES && this._appFavorites.isFavorite(source.id)) + this._appFavorites.removeFavorite(source.id); + + return true; + }, +}; + +const BaseAppViewVertical = { + after__init() { + this._grid.layoutManager._orientation = Clutter.Orientation.VERTICAL; + this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); + this._orientation = Clutter.Orientation.VERTICAL; + this._swipeTracker.orientation = Clutter.Orientation.VERTICAL; + this._swipeTracker._reset(); + this._pageIndicators.vertical = true; + this._box.vertical = false; + this._pageIndicators.x_expand = false; + this._pageIndicators.y_align = Clutter.ActorAlign.CENTER; + this._pageIndicators.x_align = Clutter.ActorAlign.START; + this._pageIndicators.set_style('margin-right: 10px;'); + // moving these bars needs more patching of the this's code + // for now we just change bars style to be more like vertically oriented arrows indicating direction to prev/next page + this._nextPageIndicator.add_style_class_name('nextPageIndicator'); + this._prevPageIndicator.add_style_class_name('prevPageIndicator'); + + // setting their x_scale to 0 removes the arrows and avoid allocation issues compared to .hide() them + this._nextPageArrow.scale_x = 0; + this._prevPageArrow.scale_x = 0; + + this._adjustment = this._scrollView.vscroll.adjustment; + + this._adjustment.connect('notify::value', adj => { + const value = adj.value / adj.page_size; + this._pageIndicators.setCurrentPosition(value); + }); + }, +}; + +const BaseAppViewCommon = { + _sortOrderedItemsAlphabetically(icons = null) { + if (!icons) + icons = this._orderedItems; + icons.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())); + }, + + _setLinearPositions(icons) { + const { itemsPerPage } = this._grid; + icons.forEach((icon, i) => { + const page = Math.floor(i / itemsPerPage); + const position = i % itemsPerPage; + try { + this._moveItem(icon, page, position); + } catch (e) { + console.warn(`Warning:${e}`); + } + }); + }, + + // adds sorting options and option to add favorites and running apps + _redisplay() { + if (this._folderIcons) { + this._folderIcons.forEach(icon => { + icon.view._redisplay(); + }); + } + let oldApps = this._orderedItems.slice(); + let oldAppIds = oldApps.map(icon => icon.id); + + let newApps = this._loadApps().sort(this._compareItems.bind(this)); + let newAppIds = newApps.map(icon => icon.id); + + let addedApps = newApps.filter(icon => !oldAppIds.includes(icon.id)); + let removedApps = oldApps.filter(icon => !newAppIds.includes(icon.id)); + + // Remove old app icons + removedApps.forEach(icon => { + this._removeItem(icon); + icon.destroy(); + }); + + // Add new app icons, or move existing ones + newApps.forEach(icon => { + const [page, position] = this._getItemPosition(icon); + if (addedApps.includes(icon)) { + this._addItem(icon, page, position); + } else if (page !== -1 && position !== -1) { + this._moveItem(icon, page, position); + } else { + // App is part of a folder + } + }); + + // different options for root app grid and app folders + const thisIsFolder = this instanceof AppDisplay.FolderView; + const thisIsAppDisplay = !thisIsFolder; + if ((opt.APP_GRID_ORDER && thisIsAppDisplay) || + (opt.APP_FOLDER_ORDER && thisIsFolder)) { + // const { itemsPerPage } = this._grid; + let appIcons = this._orderedItems; + // sort all alphabetically + this._sortOrderedItemsAlphabetically(appIcons); + // appIcons.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())); + // then sort used apps by usage + if ((opt.APP_GRID_USAGE && thisIsAppDisplay) || + (opt.APP_FOLDER_USAGE && thisIsFolder)) + appIcons.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id)); + + // sort favorites first + if (opt.APP_GRID_DASH_FIRST) { + const fav = Object.keys(this._appFavorites._favorites); + appIcons.sort((a, b) => { + let aFav = fav.indexOf(a.id); + if (aFav < 0) + aFav = 999; + let bFav = fav.indexOf(b.id); + if (bFav < 0) + bFav = 999; + return bFav < aFav; + }); + } + + // sort running first + if (opt.APP_GRID_DASH_FIRST && thisIsAppDisplay) + appIcons.sort((a, b) => a.app.get_state() !== Shell.AppState.RUNNING && b.app.get_state() === Shell.AppState.RUNNING); + + if (opt.APP_GRID_FOLDERS_FIRST) + appIcons.sort((a, b) => b._folder && !a._folder); + else if (opt.APP_GRID_FOLDERS_LAST) + appIcons.sort((a, b) => a._folder && !b._folder); + + this._setLinearPositions(appIcons); + + this._orderedItems = appIcons; + } + + this.emit('view-loaded'); + if (!opt.APP_GRID_ALLOW_INCOMPLETE_PAGES) { + for (let i = 0; i < this._grid.nPages; i++) + this._grid.layoutManager._fillItemVacancies(i); + } + }, + + _canAccept(source) { + return source instanceof AppDisplay.AppViewItem; + }, + + // this method is replacing BaseAppVew.acceptDrop which can't be overridden directly + _acceptDropCommon(source) { + const dropTarget = this._dropTarget; + delete this._dropTarget; + + if (!this._canAccept(source)) + return false; + + if (dropTarget === this._prevPageIndicator || + dropTarget === this._nextPageIndicator) { + let increment; + + increment = dropTarget === this._prevPageIndicator ? -1 : 1; + + const { currentPage, nPages } = this._grid; + const page = Math.min(currentPage + increment, nPages); + const position = page < nPages ? -1 : 0; + + this._moveItem(source, page, position); + this.goToPage(page); + } else if (this._delayedMoveData) { + // Dropped before the icon was moved + const { page, position } = this._delayedMoveData; + + try { + this._moveItem(source, page, position); + } catch (e) { + console.warn(`Warning:${e}`); + } + this._removeDelayedMove(); + } + + return true; + }, + + // support active preview icons + _onDragMotion(dragEvent) { + if (!(dragEvent.source instanceof AppDisplay.AppViewItem)) + return DND.DragMotionResult.CONTINUE; + + if (dragEvent.source._sourceItem) + dragEvent.source = dragEvent.source._sourceItem; + + const appIcon = dragEvent.source; + + if (appIcon instanceof AppDisplay.AppViewItem) { + if (!this._dragMaybeSwitchPageImmediately(dragEvent)) { + // Two ways of switching pages during DND: + // 1) When "bumping" the cursor against the monitor edge, we switch + // page immediately. + // 2) When hovering over the next-page indicator for a certain time, + // we also switch page. + + const { targetActor } = dragEvent; + + if (targetActor === this._prevPageIndicator || + targetActor === this._nextPageIndicator) + this._maybeSetupDragPageSwitchInitialTimeout(dragEvent); + else + this._resetDragPageSwitch(); + } + } + + const thisIsFolder = this instanceof AppDisplay.FolderView; + const thisIsAppDisplay = !thisIsFolder; + if ((!opt.APP_GRID_ORDER && thisIsAppDisplay) || (!opt.APP_FOLDER_ORDER && thisIsFolder)) + this._maybeMoveItem(dragEvent); + + return DND.DragMotionResult.CONTINUE; + }, +}; + +const BaseAppViewGridLayout = { + _getIndicatorsWidth(box) { + const [width, height] = box.get_size(); + const arrows = [ + this._nextPageArrow, + this._previousPageArrow, + ]; + + const minArrowsWidth = arrows.reduce( + (previousWidth, accessory) => { + const [min] = accessory.get_preferred_width(height); + return Math.max(previousWidth, min); + }, 0); + + const idealIndicatorWidth = (width * 0.1/* PAGE_PREVIEW_RATIO*/) / 2; + + return Math.max(idealIndicatorWidth, minArrowsWidth); + }, +}; + +const FolderIcon = { + after__init() { + /* // If folder preview icons are clickable, + // disable opening the folder with primary mouse button and enable the secondary one + const buttonMask = opt.APP_GRID_ACTIVE_PREVIEW + ? St.ButtonMask.TWO | St.ButtonMask.THREE + : St.ButtonMask.ONE | St.ButtonMask.TWO; + this.button_mask = buttonMask;*/ + this.button_mask = St.ButtonMask.ONE | St.ButtonMask.TWO; + }, + + open() { + this._ensureFolderDialog(); + // always open folder with the first page + this.view._scrollView.vscroll.adjustment.value = 0; + this._dialog.popup(); + }, +}; + +const ActiveFolderIcon = { + handleDragOver() { + return DND.DragMotionResult.CONTINUE; + }, + + acceptDrop() { + return false; + }, + + _onDragEnd() { + this._dragging = false; + this.undoScaleAndFade(); + Main.overview.endItemDrag(this._sourceItem.icon); + }, +}; + +const FolderView = { + _createGrid() { + let grid = new FolderGrid(); + return grid; + }, + + createFolderIcon(size) { + const layout = new Clutter.GridLayout({ + row_homogeneous: true, + column_homogeneous: true, + }); + + let icon = new St.Widget({ + layout_manager: layout, + x_align: Clutter.ActorAlign.CENTER, + style: `width: ${size}px; height: ${size}px;`, + }); + + const numItems = this._orderedItems.length; + // decide what number of icons switch to 3x3 grid + // APP_GRID_FOLDER_ICON_GRID: 3 -> more than 4 + // : 4 -> more than 8 + const threshold = opt.APP_GRID_FOLDER_ICON_GRID % 3 ? 8 : 4; + const gridSize = opt.APP_GRID_FOLDER_ICON_GRID > 2 && numItems > threshold ? 3 : 2; + const FOLDER_SUBICON_FRACTION = gridSize === 2 ? 0.4 : 0.27; + + let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size); + let rtl = icon.get_text_direction() === Clutter.TextDirection.RTL; + for (let i = 0; i < gridSize * gridSize; i++) { + const style = `width: ${subSize}px; height: ${subSize}px;`; + let bin = new St.Bin({ style, reactive: true }); + bin.pivot_point = new Graphene.Point({ x: 0.5, y: 0.5 }); + if (i < numItems) { + if (!opt.APP_GRID_ACTIVE_PREVIEW) { + bin.child = this._orderedItems[i].app.create_icon_texture(subSize); + } else { + const app = this._orderedItems[i].app; + const child = new AppDisplay.AppIcon(app, { + setSizeManually: true, + showLabel: false, + }); + + child._sourceItem = this._orderedItems[i]; + child._sourceFolder = this; + child.icon.style_class = ''; + child.icon.set_style('margin: 0; padding: 0;'); + child._dot.set_style('margin-bottom: 1px;'); + child.icon.setIconSize(subSize); + + bin.child = child; + + bin.connect('enter-event', () => { + bin.ease({ + duration: 100, + scale_x: 1.14, + scale_y: 1.14, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + }); + bin.connect('leave-event', () => { + bin.ease({ + duration: 100, + scale_x: 1, + scale_y: 1, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + }); + } + } + + layout.attach(bin, rtl ? (i + 1) % gridSize : i % gridSize, Math.floor(i / gridSize), 1, 1); + } + + // if folder content changed, update folder size, but not if it's empty + if (this._dialog && this._dialog._designCapacity !== this._orderedItems.length && this._orderedItems.length) + this._dialog._updateFolderSize(); + + return icon; + }, + + _loadApps() { + this._apps = []; + const excludedApps = this._folder.get_strv('excluded-apps'); + const appSys = Shell.AppSystem.get_default(); + const addAppId = appId => { + if (excludedApps.includes(appId)) + return; + + if (opt.APP_GRID_EXCLUDE_FAVORITES && this._appFavorites.isFavorite(appId)) + return; + + const app = appSys.lookup_app(appId); + if (!app) + return; + + if (opt.APP_GRID_EXCLUDE_RUNNING) { + const runningApps = Shell.AppSystem.get_default().get_running().map(a => a.id); + if (runningApps.includes(appId)) + return; + } + + if (!this._parentalControlsManager.shouldShowApp(app.get_app_info())) + return; + + if (this._apps.indexOf(app) !== -1) + return; + + this._apps.push(app); + }; + + const folderApps = this._folder.get_strv('apps'); + folderApps.forEach(addAppId); + + const folderCategories = this._folder.get_strv('categories'); + const appInfos = this._parentView.getAppInfos(); + appInfos.forEach(appInfo => { + let appCategories = /* AppDisplay.*/_getCategories(appInfo); + if (!_listsIntersect(folderCategories, appCategories)) + return; + + addAppId(appInfo.get_id()); + }); + + let items = []; + this._apps.forEach(app => { + let icon = this._items.get(app.get_id()); + if (!icon) + icon = new AppDisplay.AppIcon(app); + + items.push(icon); + }); + + if (opt.APP_FOLDER_ORDER) + Main.overview._overview.controls._appDisplay._sortOrderedItemsAlphabetically(items); + + if (opt.APP_FOLDER_USAGE) + items.sort((a, b) => Shell.AppUsage.get_default().compare(a.app.id, b.app.id)); + + this._appIds = this._apps.map(app => app.get_id()); + return items; + }, + + // 42 only - don't apply appGrid scale on folders + adaptToSize(width, height) { + if (!opt.ORIENTATION) { + const [, indicatorHeight] = this._pageIndicators.get_preferred_height(-1); + height -= indicatorHeight; + } + BaseAppViewCommon.adaptToSize.bind(this)(width, height, true); + }, + + acceptDrop(source) { + /* if (!BaseAppViewCommon.acceptDrop.bind(this)(source)) + return false;*/ + if (opt.APP_FOLDER_ORDER) + return false; + if (source._sourceItem) + source = source._sourceItem; + + if (!this._acceptDropCommon(source)) + return false; + + const folderApps = this._orderedItems.map(item => item.id); + this._folder.set_strv('apps', folderApps); + + return true; + }, +}; + +const FolderGrid = GObject.registerClass( +class FolderGrid extends AppDisplay.AppGrid { + _init() { + super._init({ + allow_incomplete_pages: false, + columns_per_page: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 20, + rows_per_page: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 20, + page_halign: Clutter.ActorAlign.CENTER, + page_valign: Clutter.ActorAlign.CENTER, + }); + this.layout_manager._isFolder = true; + const spacing = opt.APP_GRID_SPACING; + this.set_style(`column-spacing: ${spacing}px; row-spacing: ${spacing}px;`); + this.layoutManager.fixedIconSize = opt.APP_GRID_FOLDER_ICON_SIZE; + + this.setGridModes([ + { + columns: opt.APP_GRID_FOLDER_COLUMNS ? opt.APP_GRID_FOLDER_COLUMNS : 3, + rows: opt.APP_GRID_FOLDER_ROWS ? opt.APP_GRID_FOLDER_ROWS : 3, + }, + ]); + } + + adaptToSize(width, height) { + this.layout_manager.adaptToSize(width, height); + } +}); + + +const FOLDER_DIALOG_ANIMATION_TIME = 200; // AppDisplay.FOLDER_DIALOG_ANIMATION_TIME +const AppFolderDialog = { + // injection to _init() + after__init() { + this._viewBox.add_style_class_name('app-folder-dialog-vshell'); + + // delegate this dialog to the FolderIcon._view + // so its _createFolderIcon function can update the dialog if folder content changed + this._view._dialog = this; + + // right click into the folder popup should close it + this.child.reactive = true; + const clickAction = new Clutter.ClickAction(); + clickAction.connect('clicked', act => { + if (act.get_button() === Clutter.BUTTON_PRIMARY) + return Clutter.EVENT_STOP; + const [x, y] = clickAction.get_coords(); + const actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y); + // if it's not entry for editing folder title + if (actor !== this._entry) + this.popdown(); + return Clutter.EVENT_STOP; + }); + + this.child.add_action(clickAction); + }, + + after__addFolderNameEntry() { + // Edit button + this._removeButton = new St.Button({ + style_class: 'edit-folder-button', + button_mask: St.ButtonMask.ONE, + toggle_mode: false, + reactive: true, + can_focus: true, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + child: new St.Icon({ + icon_name: 'user-trash-symbolic', + icon_size: 16, + }), + }); + + this._removeButton.connect('clicked', () => { + if (Date.now() - this._removeButton._lastClick < Clutter.Settings.get_default().double_click_time) { + this._grabHelper.ungrab({ actor: this }); + // without hiding the dialog, Shell crashes (at least on X11) + this.hide(); + this._view._deletingFolder = true; + + // Resetting all keys deletes the relocatable schema + let keys = this._folder.settings_schema.list_keys(); + for (const key of keys) + this._folder.reset(key); + + let settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' }); + let folders = settings.get_strv('folder-children'); + folders.splice(folders.indexOf(this._view._id), 1); + + // remove all abandoned folders (usually my own garbage and unwanted default folders...) + /* const appFolders = this._appDisplay._folderIcons.map(icon => icon._id); + folders.forEach(folder => { + if (!appFolders.includes(folder)) { + folders.splice(folders.indexOf(folder._id), 1); + } + });*/ + settings.set_strv('folder-children', folders); + + this._view._deletingFolder = false; + return; + } + this._removeButton._lastClick = Date.now(); + }); + + this._entryBox.add_child(this._removeButton); + + // Adjust empty actor to center the title + this._entryBox.get_first_child().width = 82; + }, + + popup() { + if (this._isOpen) + return; + + this._isOpen = this._grabHelper.grab({ + actor: this, + onUngrab: () => this.popdown(), + }); + + if (!this._isOpen) + return; + + this.get_parent().set_child_above_sibling(this, null); + + this._needsZoomAndFade = true; + + // the first folder dialog realization needs size correction + // so set the folder size, let it realize and then update the folder content + if (!this.realized) { + this._updateFolderSize(); + GLib.idle_add( + GLib.PRIORITY_DEFAULT, + () => { + this._updateFolderSize(); + } + ); + } + + this.show(); + this.emit('open-state-changed', true); + }, + + _updateFolderSize() { + const view = this._view; + const [firstItem] = view._grid.layoutManager._container; + if (!firstItem) + return; + // adapt folder size according to the settings and number of icons + const appDisplay = this._source._parentView; + if (!appDisplay.width || appDisplay.allocation.x2 === Infinity || appDisplay.allocation.x2 === -Infinity) { + return; + } + + const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); + const itemPadding = 55; // default icon item padding on Fedora 44 + // const dialogMargin = 30; + const nItems = view._orderedItems.length; + let columns = opt.APP_GRID_FOLDER_COLUMNS; + let rows = opt.APP_GRID_FOLDER_ROWS; + const fullAdaptiveGrid = !columns && !rows; + let spacing = opt.APP_GRID_SPACING; + const minItemSize = 48 + itemPadding; + + if (fullAdaptiveGrid) { + columns = Math.ceil(Math.sqrt(nItems)); + rows = columns; + if (columns * (columns - 1) >= nItems) { + rows = columns - 1; + } else if ((columns + 1) * (columns - 1) >= nItems) { + rows = columns - 1; + columns += 1; + } + } else if (!columns && rows) { + columns = Math.ceil(nItems / rows); + } else if (columns && !rows) { + rows = Math.ceil(nItems / columns); + } + + const iconSize = opt.APP_GRID_FOLDER_ICON_SIZE < 0 ? opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT : opt.APP_GRID_FOLDER_ICON_SIZE; + view._grid.layoutManager.fixedIconSize = iconSize; + view._grid.set_style(`column-spacing: ${opt.APP_GRID_SPACING}px; row-spacing: ${opt.APP_GRID_SPACING}px;`); + view._grid.layoutManager._pageWidth += 1; + view._grid.layoutManager.adaptToSize(view._grid.layoutManager._pageWidth - 1, view._grid.layoutManager._pageHeight); + + let itemSize = iconSize + 55; // icon padding + // first run sets the grid before we can read the real icon size + // so we estimate the size from default properties + // and correct it in the second run + if (this.realized) { + firstItem.icon.setIconSize(iconSize); + const [firstItemWidth] = firstItem.get_preferred_size(); + const realSize = firstItemWidth / scaleFactor; + // if the preferred item size is smaller than icon plus some padding, ignore it + // (icons that are not yet realized are returning sizes like 45 or 53) + if (realSize > (iconSize + 24)) + itemSize = realSize; + } + + let width = columns * (itemSize + spacing) + /* padding for nav arrows*/64; + width = Math.round(width + (opt.ORIENTATION ? 100 : 160/* space for navigation arrows*/)); + let height = rows * (itemSize + spacing) + /* header*/75 + /* padding*/ 2 * 30 + /* padding + ?page indicator*/(!opt.ORIENTATION || !opt.APP_GRID_FOLDER_COLUMNS ? 100 : 70); + + // allocation is more reliable than appDisplay width/height properties + const appDisplayWidth = appDisplay.allocation.x2 - appDisplay.allocation.x1; + const appDisplayHeight = appDisplay.allocation.y2 - appDisplay.allocation.y1 + (opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0); + + // folder must fit the appDisplay area + // reduce columns/rows if needed and count with the scaled values + if (!opt.APP_GRID_FOLDER_ROWS) { + while ((height * scaleFactor) > appDisplayHeight) { + height -= itemSize + spacing; + rows -= 1; + } + } + + if (!opt.APP_GRID_FOLDER_COLUMNS) { + while ((width * scaleFactor) > appDisplayWidth) { + width -= itemSize + spacing; + columns -= 1; + } + } + // try to compensate for the previous reduction if there is a space + if (!opt.APP_GRID_FOLDER_COLUMNS) { + while ((nItems > columns * rows) && ((width * scaleFactor + itemSize + spacing) <= appDisplayWidth)) { + width += itemSize + spacing; + columns += 1; + } + // remove columns that cannot be displayed + if ((columns * minItemSize + (columns - 1) * spacing) > appDisplayWidth) + columns = Math.floor(appDisplayWidth / (minItemSize + spacing)); + } + if (!opt.APP_GRID_FOLDER_ROWS) { + while ((nItems > columns * rows) && ((height * scaleFactor + itemSize + spacing) <= appDisplayHeight)) { + height += itemSize + spacing; + rows += 1; + } + // remove rows that cannot be displayed + if ((rows * minItemSize + (rows - 1) * spacing) > appDisplayHeight) + rows = Math.floor(appDisplayWidth / (minItemSize + spacing)); + } + + width = Math.clamp(width, 640, appDisplayWidth); + height = Math.min(height, appDisplayHeight); + + const layoutManager = view._grid.layoutManager; + layoutManager.rows_per_page = rows; + layoutManager.columns_per_page = columns; + + // this line is required by GS 43 + // view._grid.setGridModes([{ columns, rows }]); + + this.child.set_style(` + width: ${width}px; + height: ${height}px; + padding: 30px; + `); + + view._redisplay(); + // store original item count + this._designCapacity = nItems; + }, + + _zoomAndFadeIn() { + let [sourceX, sourceY] = + this._source.get_transformed_position(); + let [dialogX, dialogY] = + this.child.get_transformed_position(); + + const sourceCenterX = sourceX + this._source.width / 2; + const sourceCenterY = sourceY + this._source.height / 2; + + // this. covers the whole screen + let dialogTargetX = dialogX; + let dialogTargetY = dialogY; + + const appDisplay = this._source._parentView; + + const [appDisplayX, appDisplayY] = this._source._parentView.get_transformed_position(); + if (!opt.APP_GRID_FOLDER_CENTER) { + dialogTargetX = sourceCenterX - this.child.width / 2; + dialogTargetY = sourceCenterY - this.child.height / 2; + + // keep the dialog in appDisplay area if possible + dialogTargetX = Math.clamp( + dialogTargetX, + appDisplayX, + appDisplayX + appDisplay.width - this.child.width + ); + + dialogTargetY = Math.clamp( + dialogTargetY, + appDisplayY, + appDisplayY + appDisplay.height - this.child.height + ); + } else { + const searchEntryHeight = opt.SHOW_SEARCH_ENTRY ? Main.overview._overview.controls._searchEntryBin.height : 0; + dialogTargetX = appDisplayX + appDisplay.width / 2 - this.child.width / 2; + dialogTargetY = appDisplayY - searchEntryHeight + ((appDisplay.height + searchEntryHeight) / 2 - this.child.height / 2) / 2; + } + + const dialogOffsetX = Math.round(dialogTargetX - dialogX); + const dialogOffsetY = Math.round(dialogTargetY - dialogY); + + this.child.set({ + translation_x: sourceX - dialogX, + translation_y: sourceY - dialogY, + scale_x: this._source.width / this.child.width, + scale_y: this._source.height / this.child.height, + opacity: 0, + }); + + this.child.ease({ + translation_x: dialogOffsetX, + translation_y: dialogOffsetY, + scale_x: 1, + scale_y: 1, + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + appDisplay.ease({ + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + if (opt.SHOW_SEARCH_ENTRY) { + Main.overview.searchEntry.ease({ + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + this._needsZoomAndFade = false; + + if (this._sourceMappedId === 0) { + this._sourceMappedId = this._source.connect( + 'notify::mapped', this._zoomAndFadeOut.bind(this)); + } + }, + + _zoomAndFadeOut() { + if (!this._isOpen) + return; + + if (!this._source.mapped) { + this.hide(); + return; + } + + // if the dialog was shown silently, skip animation + if (this.scale_y < 1) { + this._needsZoomAndFade = false; + this.hide(); + this._popdownCallbacks.forEach(func => func()); + this._popdownCallbacks = []; + return; + } + + let [sourceX, sourceY] = + this._source.get_transformed_position(); + let [dialogX, dialogY] = + this.child.get_transformed_position(); + + this.child.ease({ + translation_x: sourceX - dialogX + this.child.translation_x, + translation_y: sourceY - dialogY + this.child.translation_y, + scale_x: this._source.width / this.child.width, + scale_y: this._source.height / this.child.height, + opacity: 0, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + this.child.set({ + translation_x: 0, + translation_y: 0, + scale_x: 1, + scale_y: 1, + opacity: 255, + }); + this.hide(); + + this._popdownCallbacks.forEach(func => func()); + this._popdownCallbacks = []; + }, + }); + + const appDisplay = this._source._parentView; + appDisplay.ease({ + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + if (opt.SHOW_SEARCH_ENTRY) { + Main.overview.searchEntry.ease({ + opacity: 255, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + this._needsZoomAndFade = false; + }, + + _setLighterBackground(lighter) { + if (this._isOpen) + Main.overview._overview._controls._appDisplay.opacity = lighter ? 20 : 0; + /* const backgroundColor = lighter + ? this.DIALOG_SHADE_HIGHLIGHT + : this.DIALOG_SHADE_NORMAL; + + this.ease({ + backgroundColor, + duration: FOLDER_DIALOG_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); */ + }, +}; + +const AppIcon = { + after__init() { + // update the app label behavior + this._updateMultiline(); + }, + + // avoid accepting by placeholder when dragging active preview + // and also by icon if alphabet or usage sorting are used + _canAccept(source) { + if (source._sourceItem) + source = source._sourceItem; + let view = /* AppDisplay.*/_getViewFromIcon(source); + + return source !== this && + (source instanceof this.constructor) && + (view instanceof AppDisplay.AppDisplay && + !opt.APP_GRID_USAGE); + }, +}; + +const AppViewItemCommon = { + _updateMultiline() { + const { label } = this.icon; + if (label) + label.opacity = 255; + if (!this._expandTitleOnHover || !this.icon.label) + return; + + const { clutterText } = label; + + const isHighlighted = this.has_key_focus() || this.hover || this._forcedHighlight; + + if (opt.APP_GRID_NAMES_MODE === 2 && this._expandTitleOnHover) { // !_expandTitleOnHover indicates search result icon + label.opacity = isHighlighted || !this.app ? 255 : 0; + } + if (isHighlighted) + this.get_parent()?.set_child_above_sibling(this, null); + + if (!opt.APP_GRID_NAMES_MODE) { + const layout = clutterText.get_layout(); + if (!layout.is_wrapped() && !layout.is_ellipsized()) + return; + } + + label.remove_transition('allocation'); + + const id = label.connect('notify::allocation', () => { + label.restore_easing_state(); + label.disconnect(id); + }); + + const expand = opt.APP_GRID_NAMES_MODE === 1 || this._forcedHighlight || this.hover || this.has_key_focus(); + + label.save_easing_state(); + label.set_easing_duration(expand + ? APP_ICON_TITLE_EXPAND_TIME + : APP_ICON_TITLE_COLLAPSE_TIME); + clutterText.set({ + line_wrap: expand, + line_wrap_mode: expand ? Pango.WrapMode.WORD_CHAR : Pango.WrapMode.NONE, + ellipsize: expand ? Pango.EllipsizeMode.NONE : Pango.EllipsizeMode.END, + }); + }, + + // support active preview icons + acceptDrop(source, _actor, x) { + if (opt.APP_GRID_USAGE) + return DND.DragMotionResult.NO_DROP; + + this._setHoveringByDnd(false); + + if (!this._canAccept(source)) + return false; + + if (this._withinLeeways(x)) + return false; + + // added - remove app from the source folder after dnd to other folder + if (source._sourceItem) { + const app = source._sourceItem.app; + source._sourceFolder.removeApp(app); + } + + return true; + }, + +}; diff --git a/extensions/45/vertical-workspaces/lib/appFavorites.js b/extensions/45/vertical-workspaces/lib/appFavorites.js new file mode 100644 index 0000000..94f67e2 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/appFavorites.js @@ -0,0 +1,79 @@ +/** + * V-Shell (Vertical Workspaces) + * appFavorites.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js'; + +let Me; +let opt; + +export const AppFavoritesModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('appFavoritesModule'); + + // if notifications are enabled no override is needed + reset = reset || !this.moduleEnabled || opt.SHOW_FAV_NOTIFICATION; + + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) { + this.moduleEnabled = false; + console.debug(' AppFavoritesModule - Keeping untouched'); + } + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + // use actual instance instead of prototype + this._overrides.addOverride('AppFavorites', AppFavorites.getAppFavorites(), AppFavoritesCommon); + + console.debug(' AppFavoritesModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' AppFavoritesModule - Deactivated'); + } +}; + +const AppFavoritesCommon = { + addFavoriteAtPos(appId, pos) { + this._addFavorite(appId, pos); + }, + + removeFavorite(appId) { + this._removeFavorite(appId); + }, +}; diff --git a/extensions/45/vertical-workspaces/lib/dash.js b/extensions/45/vertical-workspaces/lib/dash.js new file mode 100644 index 0000000..f26151d --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/dash.js @@ -0,0 +1,1515 @@ +/** + * V-Shell (Vertical Workspaces) + * dash.js + * + * @author GdH + * @copyright 2022-2023 + * @license GPL-3.0 + * modified dash module of https://github.com/RensAlthuis/vertical-overview extension + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; +import GLib from 'gi://GLib'; +import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Dash from 'resource:///org/gnome/shell/ui/dash.js'; +import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js'; +import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js'; +import * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js'; +import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js'; +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; +import * as IconGrid from 'resource:///org/gnome/shell/ui/iconGrid.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; + +let Me; +let opt; +// gettext +let _; + +let _moduleEnabled; +let _timeouts; + +// added values to achieve a better ability to scale down according to available space +export const BaseIconSizes = [16, 24, 32, 40, 44, 48, 56, 64, 72, 80, 96, 112, 128]; + +const DASH_ITEM_LABEL_SHOW_TIME = 150; + +export const DashModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._horizontalWorkId = null; + this._verticalWorkId = null; + this._showAppsIconBtnPressId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + this._removeTimeouts(); + + this.moduleEnabled = opt.get('dashModule'); + const conflict = !!(Me.Util.getEnabledExtensions('dash-to-dock').length || + Me.Util.getEnabledExtensions('ubuntu-dock').length || + Me.Util.getEnabledExtensions('dash-to-panel').length); + + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Dash" module disabled due to potential conflict with another extension`); + + reset = reset || !this.moduleEnabled || conflict; + this._conflict = conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' DashModule - Keeping untouched'); + } + + updateStyle(dash) { + if (opt.DASH_BG_LIGHT) + dash._background.add_style_class_name('dash-background-light'); + else + dash._background.remove_style_class_name('dash-background-light'); + + dash._background.opacity = opt.DASH_BG_OPACITY; + let radius = opt.DASH_BG_RADIUS; + if (radius) { + let style; + switch (opt.DASH_POSITION) { + case 1: + style = opt.DASH_BG_GS3_STYLE ? `border-radius: ${radius}px 0 0 ${radius}px;` : `border-radius: ${radius}px;`; + break; + case 3: + style = opt.DASH_BG_GS3_STYLE ? `border-radius: 0 ${radius}px ${radius}px 0;` : `border-radius: ${radius}px;`; + break; + default: + style = `border-radius: ${radius}px;`; + } + dash._background.set_style(style); + } else { + dash._background.set_style(''); + } + } + + _activateModule() { + _moduleEnabled = true; + _timeouts = {}; + const dash = Main.overview._overview._controls.layoutManager._dash; + + if (!this._originalWorkId) + this._originalWorkId = dash._workId; + + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._resetStyle(dash); + this.updateStyle(dash); + + this._overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerCommon); + this._overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommon); + this._overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIconCommon); + this._overrides.addOverride('DashIcon', Dash.DashIcon.prototype, DashIconCommon); + this._overrides.addOverride('AppMenu', AppMenu.AppMenu.prototype, AppMenuCommon); + + if (opt.DASH_VERTICAL) { + // this._overrides.addOverride('Dash', Dash.Dash.prototype, DashVerticalOverride); + dash.add_style_class_name('vertical'); + this._setOrientation(Clutter.Orientation.VERTICAL); + } else { + this._setOrientation(Clutter.Orientation.HORIZONTAL); + } + + if (!this._customWorkId) + this._customWorkId = Main.initializeDeferredWork(dash._box, dash._redisplay.bind(dash)); + dash._workId = this._customWorkId; + + this._updateSearchWindowsIcon(); + this._updateRecentFilesIcon(); + this._updateExtensionsIcon(); + this._moveDashAppGridIcon(); + this._connectShowAppsIcon(); + + dash.visible = opt.DASH_VISIBLE; + dash._background.add_style_class_name('dash-background-reduced'); + dash._queueRedisplay(); + + if (opt.DASH_ISOLATE_WS && !this._wmSwitchWsConId) { + this._wmSwitchWsConId = global.windowManager.connect('switch-workspace', () => dash._queueRedisplay()); + this._newWindowConId = global.display.connect_after('window-created', () => dash._queueRedisplay()); + } + console.debug(' DashModule - Activated'); + } + + _disableModule() { + const dash = Main.overview._overview._controls.layoutManager._dash; + + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + dash._workId = this._originalWorkId; + + if (this._wmSwitchWsConId) { + global.windowManager.disconnect(this._wmSwitchWsConId); + this._wmSwitchWsConId = 0; + } + if (this._newWindowConId) { + global.windowManager.disconnect(this._newWindowConId); + this._newWindowConId = 0; + } + + const reset = true; + this._setOrientation(Clutter.Orientation.HORIZONTAL); + this._moveDashAppGridIcon(reset); + this._connectShowAppsIcon(reset); + this._updateSearchWindowsIcon(false); + this._updateRecentFilesIcon(false); + this._updateExtensionsIcon(false); + this._resetStyle(dash); + dash.visible = !this._conflict; + dash._background.opacity = 255; + + _moduleEnabled = false; + console.debug(' DashModule - Disabled'); + } + + _resetStyle(dash) { + dash.remove_style_class_name('vertical'); + dash.remove_style_class_name('vertical-gs3-left'); + dash.remove_style_class_name('vertical-gs3-right'); + dash.remove_style_class_name('vertical-left'); + dash.remove_style_class_name('vertical-right'); + dash._background.remove_style_class_name('dash-background-light'); + dash._background.remove_style_class_name('dash-background-reduced'); + dash._background.set_style(''); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } + + _setOrientation(orientation, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + + dash._box.layout_manager.orientation = orientation; + dash._dashContainer.layout_manager.orientation = orientation; + dash._dashContainer.y_expand = !orientation; + dash._dashContainer.x_expand = !!orientation; + dash.x_align = orientation ? Clutter.ActorAlign.START : Clutter.ActorAlign.CENTER; + dash.y_align = orientation ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.FILL; + + let sizerBox = dash._background.get_children()[0]; + sizerBox.clear_constraints(); + sizerBox.add_constraint(new Clutter.BindConstraint({ + source: dash._showAppsIcon.icon, + coordinate: orientation ? Clutter.BindCoordinate.WIDTH : Clutter.BindCoordinate.HEIGHT, + })); + sizerBox.add_constraint(new Clutter.BindConstraint({ + source: dash._dashContainer, + coordinate: orientation ? Clutter.BindCoordinate.HEIGHT : Clutter.BindCoordinate.WIDTH, + })); + dash._box.remove_all_children(); + dash._separator = null; + dash._queueRedisplay(); + dash._adjustIconSize(); + + if (orientation && opt.DASH_BG_GS3_STYLE) { + if (opt.DASH_LEFT) + dash.add_style_class_name('vertical-gs3-left'); + else if (opt.DASH_RIGHT) + dash.add_style_class_name('vertical-gs3-right'); + } else { + dash.remove_style_class_name('vertical-gs3-left'); + dash.remove_style_class_name('vertical-gs3-right'); + } + } + + _moveDashAppGridIcon(reset = false) { + // move dash app grid icon to the front + const dash = Main.overview._overview._controls.layoutManager._dash; + + const appIconPosition = opt.get('showAppsIconPosition'); + dash._showAppsIcon.remove_style_class_name('show-apps-icon-vertical-hide'); + dash._showAppsIcon.remove_style_class_name('show-apps-icon-horizontal-hide'); + dash._showAppsIcon.opacity = 255; + if (!reset && appIconPosition === 0) // 0 - start + dash._dashContainer.set_child_at_index(dash._showAppsIcon, 0); + if (reset || appIconPosition === 1) { // 1 - end + const index = dash._dashContainer.get_children().length - 1; + dash._dashContainer.set_child_at_index(dash._showAppsIcon, index); + } + if (!reset && appIconPosition === 2) { // 2 - hide + const style = opt.DASH_VERTICAL ? 'show-apps-icon-vertical-hide' : 'show-apps-icon-horizontal-hide'; + dash._showAppsIcon.add_style_class_name(style); + // for some reason even if the icon height in vertical mode should be set to 0 by the style, it stays visible in full size returning height 1px + dash._showAppsIcon.opacity = 0; + } + } + + _connectShowAppsIcon(reset = false, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + if (!reset) { + if (this._showAppsIconBtnPressId || Me.Util.dashIsDashToDock()) { + // button is already connected || dash is Dash to Dock + return; + } + dash._showAppsIcon.reactive = true; + this._showAppsIconBtnPressId = dash._showAppsIcon.connect('button-press-event', (actor, event) => { + const button = event.get_button(); + if (button === Clutter.BUTTON_MIDDLE) + Me.Util.openPreferences(); + else if (button === Clutter.BUTTON_SECONDARY) + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + else + return Clutter.EVENT_PROPAGATE; + return Clutter.EVENT_STOP; + }); + } else if (this._showAppsIconBtnPressId) { + dash._showAppsIcon.disconnect(this._showAppsIconBtnPressId); + this._showAppsIconBtnPressId = 0; + dash._showAppsIcon.reactive = false; + } + } + + _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; + + if (dash._showWindowsIcon) { + dashContainer.remove_child(dash._showWindowsIcon); + if (dash._showWindowsIconClickedId) { + dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId); + dash._showWindowsIconClickedId = 0; + } + delete dash._showWindowsIconClickedId; + if (dash._showWindowsIcon) + dash._showWindowsIcon.destroy(); + delete dash._showWindowsIcon; + } + + if (!show || !opt.get('windowSearchProviderModule')) + return; + + if (!dash._showWindowsIcon) { + dash._showWindowsIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('showWindowsIcon', dash._showWindowsIcon, ShowWindowsIcon); + dash._showWindowsIcon._afterInit(); + dash._showWindowsIcon.show(false); + dashContainer.add_child(dash._showWindowsIcon); + dash._hookUpLabel(dash._showWindowsIcon); + } + + dash._showWindowsIcon.icon.setIconSize(dash.iconSize); + if (opt.SHOW_WINDOWS_ICON === 1) { + dashContainer.set_child_at_index(dash._showWindowsIcon, 0); + } else if (opt.SHOW_WINDOWS_ICON === 2) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._showWindowsIcon, index); + } + + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); + + if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) { + dash._showWindowsIconClickedId = dash._showWindowsIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + }); + } + } + + _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; + + if (dash._recentFilesIcon) { + dashContainer.remove_child(dash._recentFilesIcon); + if (dash._recentFilesIconClickedId) { + dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId); + dash._recentFilesIconClickedId = 0; + } + delete dash._recentFilesIconClickedId; + if (dash._recentFilesIcon) + dash._recentFilesIcon.destroy(); + delete dash._recentFilesIcon; + } + + if (!show || !opt.get('recentFilesSearchProviderModule')) + return; + + if (!dash._recentFilesIcon) { + dash._recentFilesIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('recentFilesIcon', dash._recentFilesIcon, ShowRecentFilesIcon); + dash._recentFilesIcon._afterInit(); + dash._recentFilesIcon.show(false); + dashContainer.add_child(dash._recentFilesIcon); + dash._hookUpLabel(dash._recentFilesIcon); + } + + dash._recentFilesIcon.icon.setIconSize(dash.iconSize); + if (opt.SHOW_RECENT_FILES_ICON === 1) { + dashContainer.set_child_at_index(dash._recentFilesIcon, 0); + } else if (opt.SHOW_RECENT_FILES_ICON === 2) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._recentFilesIcon, index); + } + + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); + + if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) { + dash._recentFilesIconClickedId = dash._recentFilesIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.RFSP_PREFIX); + }); + } + } + + _updateExtensionsIcon(show = opt.SHOW_EXTENSIONS_ICON, dash) { + dash = dash ?? Main.overview._overview._controls.layoutManager._dash; + const dashContainer = dash._dashContainer; + + if (dash._extensionsIcon) { + dashContainer.remove_child(dash._extensionsIcon); + if (dash._extensionsIconClickedId) { + dash._extensionsIcon.toggleButton.disconnect(dash._extensionsIconClickedId); + dash._extensionsIconClickedId = 0; + } + delete dash._extensionsIconClickedId; + if (dash._extensionsIcon) + dash._extensionsIcon.destroy(); + delete dash._extensionsIcon; + } + + if (!show || !opt.get('extensionsSearchProviderModule')) + return; + + if (!dash._extensionsIcon) { + dash._extensionsIcon = new Dash.DashItemContainer(); + new Me.Util.Overrides().addOverride('extensionsIcon', dash._extensionsIcon, ShowExtensionsIcon); + dash._extensionsIcon._afterInit(); + dash._extensionsIcon.show(false); + dashContainer.add_child(dash._extensionsIcon); + dash._hookUpLabel(dash._extensionsIcon); + } + + dash._extensionsIcon.icon.setIconSize(dash.iconSize); + if (opt.SHOW_EXTENSIONS_ICON === 1) { + dashContainer.set_child_at_index(dash._extensionsIcon, 0); + } else if (opt.SHOW_EXTENSIONS_ICON === 2) { + const index = dashContainer.get_children().length - 1; + dashContainer.set_child_at_index(dash._extensionsIcon, index); + } + + Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); + + if (dash._extensionsIcon && !dash._extensionsIconClickedId) { + dash._extensionsIconClickedId = dash._extensionsIcon.toggleButton.connect('clicked', () => { + Me.Util.activateSearchProvider(Me.ESP_PREFIX); + }); + } + } +}; + +function getAppFromSource(source) { + if (source instanceof AppDisplay.AppIcon) + return source.app; + else + return null; +} + +const DashItemContainerCommon = { + // move labels according dash position + showLabel() { + if (!this._labelText) + return; + + const windows = this.child.app?.get_windows(); + const recentWindowTitle = windows && windows.length ? windows[0].get_title() : ''; + const windowCount = this.child.app?.get_windows().length; + let labelSuffix = ''; + if (windowCount > 1) + labelSuffix = ` (${windowCount})`; + if (recentWindowTitle && recentWindowTitle !== this._labelText) + labelSuffix += `\n ${recentWindowTitle}`; + + this.label.set_text(this._labelText + labelSuffix); + + this.label.opacity = 0; + this.label.show(); + + let [stageX, stageY] = this.get_transformed_position(); + + const itemWidth = this.allocation.get_width(); + const itemHeight = this.allocation.get_height(); + + const labelWidth = this.label.get_width(); + const labelHeight = this.label.get_height(); + let xOffset = Math.floor((itemWidth - labelWidth) / 2); + let x = Math.clamp(stageX + xOffset, 0, global.stage.width - labelWidth); + const primaryMonitor = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + x = Math.clamp(x, primaryMonitor.x, primaryMonitor.x + primaryMonitor.width - labelWidth); + + let node = this.label.get_theme_node(); + let y; + + if (opt.DASH_TOP) { + const yOffset = 0.75 * itemHeight + 3 * node.get_length('-y-offset'); + y = stageY + yOffset; + } else if (opt.DASH_BOTTOM) { + const yOffset = node.get_length('-y-offset'); + y = stageY - this.label.height - yOffset; + } else if (opt.DASH_RIGHT) { + const yOffset = Math.floor((itemHeight - labelHeight) / 2); + xOffset = 4; + + x = stageX - xOffset - this.label.width; + y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); + } else if (opt.DASH_LEFT) { + const yOffset = Math.floor((itemHeight - labelHeight) / 2); + xOffset = 4; + + x = stageX + this.width + xOffset; + y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); + } + + this.label.set_position(x, y); + this.label.ease({ + opacity: 255, + duration: DASH_ITEM_LABEL_SHOW_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + this.label.set_position(x, y); + this.label.ease({ + opacity: 255, + duration: DASH_ITEM_LABEL_SHOW_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + }, +}; + +const DashCommon = { + _redisplay() { + // After disabling V-Shell queueRedisplay() may call this function + // In that case redirect the call to the current _redisplay() + if (!_moduleEnabled) { + this._redisplay(); + return; + } + + let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); + + let running = this._appSystem.get_running(); + + if (opt.DASH_ISOLATE_WS) { + const currentWs = global.workspace_manager.get_active_workspace(); + running = running.filter(app => { + return app.get_windows().filter(w => w.get_workspace() === currentWs).length; + }); + this._box.get_children().forEach(a => a.child?._updateRunningStyle()); + } + + let children = this._box.get_children().filter(actor => { + return actor.child && + actor.child._delegate && + actor.child._delegate.app; + }); + // Apps currently in the dash + let oldApps = children.map(actor => actor.child._delegate.app); + // Apps supposed to be in the dash + let newApps = []; + + for (let id in favorites) + newApps.push(favorites[id]); + + for (let i = 0; i < running.length; i++) { + let app = running[i]; + if (app.get_id() in favorites) + continue; + newApps.push(app); + } + + // Figure out the actual changes to the list of items; we iterate + // over both the list of items currently in the dash and the list + // of items expected there, and collect additions and removals. + // Moves are both an addition and a removal, where the order of + // the operations depends on whether we encounter the position + // where the item has been added first or the one from where it + // was removed. + // There is an assumption that only one item is moved at a given + // time; when moving several items at once, everything will still + // end up at the right position, but there might be additional + // additions/removals (e.g. it might remove all the launchers + // and add them back in the new order even if a smaller set of + // additions and removals is possible). + // If above assumptions turns out to be a problem, we might need + // to use a more sophisticated algorithm, e.g. Longest Common + // Subsequence as used by diff. + let addedItems = []; + let removedActors = []; + + let newIndex = 0; + let oldIndex = 0; + while (newIndex < newApps.length || oldIndex < oldApps.length) { + let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null; + let newApp = newApps.length > newIndex ? newApps[newIndex] : null; + + // No change at oldIndex/newIndex + if (oldApp === newApp) { + oldIndex++; + newIndex++; + continue; + } + + // App removed at oldIndex + if (oldApp && !newApps.includes(oldApp)) { + removedActors.push(children[oldIndex]); + oldIndex++; + continue; + } + + // App added at newIndex + if (newApp && !oldApps.includes(newApp)) { + addedItems.push({ + app: newApp, + item: this._createAppItem(newApp), + pos: newIndex, + }); + newIndex++; + continue; + } + + // App moved + let nextApp = newApps.length > newIndex + 1 + ? newApps[newIndex + 1] : null; + let insertHere = nextApp && nextApp === oldApp; + let alreadyRemoved = removedActors.reduce((result, actor) => { + let removedApp = actor.child._delegate.app; + return result || removedApp === newApp; + }, false); + + if (insertHere || alreadyRemoved) { + let newItem = this._createAppItem(newApp); + addedItems.push({ + app: newApp, + item: newItem, + pos: newIndex + removedActors.length, + }); + newIndex++; + } else { + removedActors.push(children[oldIndex]); + oldIndex++; + } + } + + for (let i = 0; i < addedItems.length; i++) { + this._box.insert_child_at_index( + addedItems[i].item, + addedItems[i].pos); + } + + for (let i = 0; i < removedActors.length; i++) { + let item = removedActors[i]; + + // Don't animate item removal when the overview is transitioning + // or hidden + if (Main.overview.visible && !Main.overview.animationInProgress) + item.animateOutAndDestroy(); + else + item.destroy(); + } + + this._adjustIconSize(); + + // Skip animations on first run when adding the initial set + // of items, to avoid all items zooming in at once + + let animate = this._shownInitially && Main.overview.visible && + !Main.overview.animationInProgress; + + if (!this._shownInitially) + this._shownInitially = true; + + for (let i = 0; i < addedItems.length; i++) + addedItems[i].item.show(animate); + + // Update separator + const nFavorites = Object.keys(favorites).length; + const nIcons = children.length + addedItems.length - removedActors.length; + if (nFavorites > 0 && nFavorites < nIcons) { + // destroy the horizontal separator if it exists. + // this is incredibly janky, but I can't think of a better way atm. + if (this._separator && this._separator.height !== 1) { + this._separator.destroy(); + this._separator = null; + } + + if (!this._separator) { + this._separator = new St.Widget({ + style_class: 'dash-separator', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + width: opt.DASH_VERTICAL ? this.iconSize : 1, + height: opt.DASH_VERTICAL ? 1 : this.iconSize, + }); + this._box.add_child(this._separator); + } + + // FIXME: separator placement is broken (also in original dash) + let pos = nFavorites + this._animatingPlaceholdersCount; + if (this._dragPlaceholder) + pos++; + this._box.set_child_at_index(this._separator, pos); + } else if (this._separator) { + this._separator.destroy(); + this._separator = null; + } + // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 + // Without it, StBoxLayout may use a stale size cache + this._box.queue_relayout(); + }, + + _createAppItem(app) { + let appIcon = new Dash.DashIcon(app); + + let indicator = appIcon._dot; + if (opt.DASH_VERTICAL) { + indicator.x_align = opt.DASH_LEFT ? Clutter.ActorAlign.START : Clutter.ActorAlign.END; + indicator.y_align = Clutter.ActorAlign.CENTER; + } else { + indicator.x_align = Clutter.ActorAlign.CENTER; + indicator.y_align = Clutter.ActorAlign.END; + } + + appIcon.connect('menu-state-changed', + (o, opened) => { + this._itemMenuStateChanged(item, opened); + }); + + let item = new Dash.DashItemContainer(); + item.setChild(appIcon); + + // Override default AppIcon label_actor, now the + // accessible_name is set at DashItemContainer.setLabelText + appIcon.label_actor = null; + item.setLabelText(app.get_name()); + + appIcon.icon.setIconSize(this.iconSize); + this._hookUpLabel(item, appIcon); + + return item; + }, + + // use custom BaseIconSizes and add support for custom icons + _adjustIconSize() { + // if a user launches multiple apps at once, this function may be called again before the previous call has finished + // as a result, new icons will not reach their full size, or will be missing, if adding a new icon and changing the dash size due to lack of space at the same time + if (this._adjustingInProgress) + return; + + // For the icon size, we only consider children which are "proper" + // icons (i.e. ignoring drag placeholders) and which are not + // animating out (which means they will be destroyed at the end of + // the animation) + let iconChildren = this._box.get_children().filter(actor => { + return actor.child && + actor.child._delegate && + actor.child._delegate.icon && + !actor.animatingOut; + }); + + // add new custom icons to the list + if (this._showAppsIcon.visible) + iconChildren.push(this._showAppsIcon); + + if (this._showWindowsIcon) + iconChildren.push(this._showWindowsIcon); + + if (this._recentFilesIcon) + iconChildren.push(this._recentFilesIcon); + + if (this._extensionsIcon) + iconChildren.push(this._extensionsIcon); + + if (!iconChildren.length) + return; + + if (this._maxWidth === -1 || this._maxHeight === -1) + return; + + const dashHorizontal = !opt.DASH_VERTICAL; + + const themeNode = this.get_theme_node(); + const maxAllocation = new Clutter.ActorBox({ + x1: 0, + y1: 0, + x2: dashHorizontal ? this._maxWidth : 42, // not whatever + y2: dashHorizontal ? 42 : this._maxHeight, + }); + + let maxContent = themeNode.get_content_box(maxAllocation); + + let spacing = themeNode.get_length('spacing'); + + let firstButton = iconChildren[0].child; + let firstIcon = firstButton._delegate.icon; + + if (!firstIcon.icon) + return; + + // Enforce valid spacings during the size request + firstIcon.icon.ensure_style(); + const [, , iconWidth, iconHeight] = firstIcon.icon.get_preferred_size(); + const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size(); + let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + + let availWidth, availHeight, maxIconSize; + if (dashHorizontal) { + availWidth = maxContent.x2 - maxContent.x1; + // Subtract icon padding and box spacing from the available width + availWidth -= iconChildren.length * (buttonWidth - iconWidth) + + (iconChildren.length - 1) * spacing + + 2 * this._background.get_theme_node().get_horizontal_padding(); + + availHeight = this._maxHeight; + availHeight -= this.margin_top + this.margin_bottom; + availHeight -= this._background.get_theme_node().get_vertical_padding(); + availHeight -= themeNode.get_vertical_padding(); + availHeight -= buttonHeight - iconHeight; + + maxIconSize = Math.min(availWidth / iconChildren.length, availHeight, opt.MAX_ICON_SIZE * scaleFactor); + } else { + availWidth = this._maxWidth; + availWidth -= this._background.get_theme_node().get_horizontal_padding(); + availWidth -= themeNode.get_horizontal_padding(); + availWidth -= buttonWidth - iconWidth; + + availHeight = maxContent.y2 - maxContent.y1; + availHeight -= iconChildren.length * (buttonHeight - iconHeight) + + (iconChildren.length - 1) * spacing + + 2 * this._background.get_theme_node().get_vertical_padding(); + + maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE * scaleFactor); + } + + let iconSizes = BaseIconSizes.map(s => s * scaleFactor); + + let newIconSize = BaseIconSizes[0]; + for (let i = 0; i < iconSizes.length; i++) { + if (iconSizes[i] <= maxIconSize) + newIconSize = BaseIconSizes[i]; + } + + if (newIconSize === this.iconSize) + return; + + // set the in-progress state here after all the possible cancels + this._adjustingInProgress = true; + + let oldIconSize = this.iconSize; + this.iconSize = newIconSize; + this.emit('icon-size-changed'); + + let scale = oldIconSize / newIconSize; + for (let i = 0; i < iconChildren.length; i++) { + let icon = iconChildren[i].child._delegate.icon; + + // Set the new size immediately, to keep the icons' sizes + // in sync with this.iconSize + icon.setIconSize(this.iconSize); + + // Don't animate the icon size change when the overview + // is transitioning, not visible or when initially filling + // the dash + if (!Main.overview.visible || Main.overview.animationInProgress || + !this._shownInitially) + continue; + + let [targetWidth, targetHeight] = icon.icon.get_size(); + + // Scale the icon's texture to the previous size and + // tween to the new size + icon.icon.set_size(icon.icon.width * scale, + icon.icon.height * scale); + + icon.icon.ease({ + width: targetWidth, + height: targetHeight, + duration: Dash.DASH_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + if (this._separator) { + this._separator.ease({ + width: dashHorizontal ? 1 : this.iconSize, + height: dashHorizontal ? this.iconSize : 1, + duration: Dash.DASH_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + this._adjustingInProgress = false; + }, + + handleDragOver(source, actor, x, y, _time) { + let app = getAppFromSource(source); + + // Don't allow favoriting of transient apps + if (app === null || app.is_window_backed()) + return DND.DragMotionResult.NO_DROP; + if (!global.settings.is_writable('favorite-apps')) + return DND.DragMotionResult.NO_DROP; + let favorites = AppFavorites.getAppFavorites().getFavorites(); + let numFavorites = favorites.length; + + let favPos = favorites.indexOf(app); + + let children = this._box.get_children(); + let numChildren = children.length; + let boxSize = opt.DASH_VERTICAL ? this._box.height : this._box.width; + + // Keep the placeholder out of the index calculation; assuming that + // the remove target has the same size as "normal" items, we don't + // need to do the same adjustment there. + if (this._dragPlaceholder) { + boxSize -= opt.DASH_VERTICAL ? this._dragPlaceholder.height : this._dragPlaceholder.width; + numChildren--; + } + + // Same with the separator + if (this._separator) { + boxSize -= opt.DASH_VERTICAL ? this._separator.height : this._separator.width; + numChildren--; + } + + let pos; + if (this._emptyDropTarget) + pos = 0; // always insert at the start when dash is empty + else if (this.text_direction === Clutter.TextDirection.RTL) + pos = numChildren - Math.floor((opt.DASH_VERTICAL ? y : x) * numChildren / boxSize); + else + pos = Math.floor((opt.DASH_VERTICAL ? y : x) * numChildren / boxSize); + + // Put the placeholder after the last favorite if we are not + // in the favorites zone + if (pos > numFavorites) + pos = numFavorites; + + if (pos !== this._dragPlaceholderPos && this._animatingPlaceholdersCount === 0) { + this._dragPlaceholderPos = pos; + + // Don't allow positioning before or after self + if (favPos !== -1 && (pos === favPos || pos === favPos + 1)) { + this._clearDragPlaceholder(); + return DND.DragMotionResult.CONTINUE; + } + + // If the placeholder already exists, we just move + // it, but if we are adding it, expand its size in + // an animation + let fadeIn; + if (this._dragPlaceholder) { + this._dragPlaceholder.destroy(); + fadeIn = false; + } else { + fadeIn = true; + } + + // this._dragPlaceholder = new Dash.DragPlaceholderItem(); // not exported in 45 + this._dragPlaceholder = new Dash.DashItemContainer(); + this._dragPlaceholder.setChild(new St.Bin({ style_class: 'placeholder' })); + this._dragPlaceholder.child.set_width(this.iconSize / (opt.DASH_VERTICAL ? 2 : 1)); + this._dragPlaceholder.child.set_height(this.iconSize / (opt.DASH_VERTICAL ? 1 : 2)); + this._box.insert_child_at_index( + this._dragPlaceholder, + this._dragPlaceholderPos); + this._dragPlaceholder.show(fadeIn); + } + + if (!this._dragPlaceholder) + return DND.DragMotionResult.NO_DROP; + + let srcIsFavorite = favPos !== -1; + + if (srcIsFavorite) + return DND.DragMotionResult.MOVE_DROP; + + return DND.DragMotionResult.COPY_DROP; + }, +}; + +const DashIconCommon = { + after__init() { + if (opt.DASH_ICON_SCROLL && !Me.Util.dashNotDefault()) { + this._scrollConId = this.connect('scroll-event', DashExtensions.onScrollEvent.bind(this)); + this._leaveConId = this.connect('leave-event', DashExtensions.onLeaveEvent.bind(this)); + } + }, + + popupMenu() { + const side = opt.DASH_VERTICAL ? St.Side.LEFT : St.Side.BOTTOM; + AppIconCommon.popupMenu.bind(this)(side); + }, + + _updateRunningStyle() { + const currentWs = global.workspace_manager.get_active_workspace(); + const show = opt.DASH_ISOLATE_WS + ? this.app.get_windows().filter(w => w.get_workspace() === currentWs).length + : this.app.state !== Shell.AppState.STOPPED; + + if (show) + this._dot.show(); + else + this._dot.hide(); + }, +}; + +const DashExtensions = { + onScrollEvent(source, event) { + if ((this.app && !opt.DASH_ICON_SCROLL) || (this._isSearchWindowsIcon && !opt.SEARCH_WINDOWS_ICON_SCROLL)) { + if (this._scrollConId) { + this.disconnect(this._scrollConId); + this._scrollConId = 0; + } + if (this._leaveConId) { + this.disconnect(this._leaveConId); + this._leaveConId = 0; + } + return Clutter.EVENT_PROPAGATE; + } + + if (Main.overview._overview.controls._stateAdjustment.value > 1) + return Clutter.EVENT_PROPAGATE; + + let direction = Me.Util.getScrollDirection(event); + if (direction === Clutter.ScrollDirection.UP) + direction = 1; + else if (direction === Clutter.ScrollDirection.DOWN) + direction = -1; + else + return Clutter.EVENT_STOP; + + // avoid uncontrollable switching if smooth scroll wheel or trackpad is used + if (this._lastScroll && Date.now() - this._lastScroll < 160) + return Clutter.EVENT_STOP; + + this._lastScroll = Date.now(); + + DashExtensions.switchWindow.bind(this)(direction); + return Clutter.EVENT_STOP; + }, + + onLeaveEvent() { + if (!this._selectedMetaWin || this.has_pointer || this.toggleButton?.has_pointer) + return; + + this._selectedPreview._activateSelected = false; + this._selectedMetaWin = null; + this._scrolledWindows = null; + DashExtensions.showWindowPreview.bind(this)(null); + }, + + + switchWindow(direction) { + if (!this._scrolledWindows) { + this._initialSelection = true; + // source is app icon + if (this.app) { + this._scrolledWindows = this.app.get_windows(); + if (opt.DASH_ISOLATE_WS) { + const currentWs = global.workspaceManager.get_active_workspace(); + this._scrolledWindows = this._scrolledWindows.filter(w => w.get_workspace() === currentWs); + } + + const wsList = []; + this._scrolledWindows.forEach(w => { + const ws = w.get_workspace(); + if (!wsList.includes(ws)) + wsList.push(ws); + }); + + // sort windows by workspaces in MRU order + this._scrolledWindows.sort((a, b) => wsList.indexOf(a.get_workspace()) > wsList.indexOf(b.get_workspace())); + // source is Search Windows icon + } else if (this._isSearchWindowsIcon) { + if (opt.SEARCH_WINDOWS_ICON_SCROLL === 1) // all windows + this._scrolledWindows = Me.Util.getWindows(null); + else + this._scrolledWindows = Me.Util.getWindows(global.workspace_manager.get_active_workspace()); + } + } + + let windows = this._scrolledWindows; + + if (!windows.length) + return; + + // if window selection is in the process, the previewed window must be the current one + let currentWin = this._selectedMetaWin ? this._selectedMetaWin : windows[0]; + + const currentIdx = windows.indexOf(currentWin); + let targetIdx = currentIdx; + // const focusWindow = Me.Util.getWindows(null)[0]; // incompatible 45 + const focusWindow = Me.Util.getWindows(null)[0]; + const appFocused = this._scrolledWindows[0] === focusWindow && this._scrolledWindows[0].get_workspace() === global.workspace_manager.get_active_workspace(); + // only if the app has focus, immediately switch to the previous window + // otherwise just set the current window above others + if (!this._initialSelection || appFocused) + targetIdx += direction; + else + this._initialSelection = false; + + if (targetIdx > windows.length - 1) + targetIdx = 0; + else if (targetIdx < 0) + targetIdx = windows.length - 1; + + const metaWin = windows[targetIdx]; + DashExtensions.showWindowPreview.bind(this)(metaWin); + this._selectedMetaWin = metaWin; + }, + + showWindowPreview(metaWin) { + const views = Main.overview._overview.controls._workspacesDisplay._workspacesViews; + const viewsIter = [views[0]]; + // secondary monitors use different structure + views.forEach(v => { + if (v._workspacesView) + viewsIter.push(v._workspacesView); + }); + + viewsIter.forEach(view => { + // if workspaces are on primary monitor only + if (!view || !view._workspaces) + return; + + view._workspaces.forEach(ws => { + ws._windows.forEach(windowPreview => { + // metaWin === null resets opacity + let opacity = metaWin ? 50 : 255; + windowPreview._activateSelected = false; + + // minimized windows are invisible if windows are not exposed (WORKSPACE_MODE === 0) + if (!windowPreview.opacity) + windowPreview.opacity = 255; + + // app windows set to lower opacity, so they can be recognized + if (this._scrolledWindows && this._scrolledWindows.includes(windowPreview.metaWindow)) { + if (opt.DASH_ICON_SCROLL === 2) + opacity = 254; + } + if (windowPreview.metaWindow === metaWin) { + if (metaWin && metaWin.get_workspace() !== global.workspace_manager.get_active_workspace()) { + Main.wm.actionMoveWorkspace(metaWin.get_workspace()); + if (_timeouts.wsSwitcherAnimation) + GLib.source_remove(_timeouts.wsSwitcherAnimation); + // setting window preview above siblings before workspace switcher animation has no effect + // we need to set the window above after the ws preview become visible on the screen + // the default switcher animation time is 250, 200 ms delay should be enough + _timeouts.wsSwitcherAnimation = GLib.timeout_add(0, 200 * St.Settings.get().slow_down_factor, () => { + windowPreview.get_parent().set_child_above_sibling(windowPreview, null); + _timeouts.wsSwitcherAnimation = 0; + return GLib.SOURCE_REMOVE; + }); + } else { + windowPreview.get_parent().set_child_above_sibling(windowPreview, null); + } + + opacity = 255; + this._selectedPreview = windowPreview; + windowPreview._activateSelected = true; + } + + // if windows are exposed, highlight selected using opacity + if ((opt.OVERVIEW_MODE && opt.WORKSPACE_MODE) || !opt.OVERVIEW_MODE) { + if (metaWin && opacity === 255) + windowPreview.showOverlay(true); + else + windowPreview.hideOverlay(true); + windowPreview.ease({ + duration: 200, + opacity, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + }); + }); + }); + }, +}; + +const AppIconCommon = { + after__init() { + if (this._updateRunningDotStyle) + this._updateRunningDotStyle(); + }, + + _updateRunningDotStyle() { + if (opt.RUNNING_DOT_STYLE) + this._dot.add_style_class_name('app-well-app-running-dot-custom'); + else + this._dot.remove_style_class_name('app-well-app-running-dot-custom'); + }, + + activate(button) { + const event = Clutter.get_current_event(); + const state = event ? event.get_state() : 0; + const isMiddleButton = button && button === Clutter.BUTTON_MIDDLE; + const isCtrlPressed = Me.Util.isCtrlPressed(state); + const isShiftPressed = Me.Util.isShiftPressed(state); + + const currentWS = global.workspace_manager.get_active_workspace(); + const appRecentWorkspace = this._getAppRecentWorkspace(this.app); + // this feature shouldn't affect search results, dash icons don't have labels, so we use them as a condition + const showWidowsBeforeActivation = opt.DASH_CLICK_ACTION === 1 && !this.icon.label; + + let targetWindowOnCurrentWs = false; + if (opt.DASH_FOLLOW_RECENT_WIN) { + targetWindowOnCurrentWs = appRecentWorkspace === currentWS; + } else { + this.app.get_windows().forEach( + w => { + targetWindowOnCurrentWs = targetWindowOnCurrentWs || (w.get_workspace() === currentWS); + } + ); + } + + const openNewWindow = this.app.can_open_new_window() && + this.app.state === Shell.AppState.RUNNING && + (((isCtrlPressed || isMiddleButton) && !opt.DASH_CLICK_OPEN_NEW_WIN) || + (opt.DASH_CLICK_OPEN_NEW_WIN && !this._selectedMetaWin && !isMiddleButton) || + ((opt.DASH_CLICK_PREFER_WORKSPACE || opt.DASH_ISOLATE_WS) && !targetWindowOnCurrentWs)); + + if ((this.app.state === Shell.AppState.STOPPED || openNewWindow) && !isShiftPressed) + this.animateLaunch(); + + if (openNewWindow) { + this.app.open_new_window(-1); + // if DASH_CLICK_ACTION == "SHOW_WINS_BEFORE", the app has more than one window and has no window on the current workspace, + // don't activate the app immediately, only move the overview to the workspace with the app's recent window + } else if (showWidowsBeforeActivation && !isShiftPressed && this.app.get_n_windows() > 1 && !targetWindowOnCurrentWs/* && !(opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE)*/) { + + Main.wm.actionMoveWorkspace(appRecentWorkspace); + Main.overview.dash.showAppsButton.checked = false; + return; + } else if (this._selectedMetaWin) { + this._selectedMetaWin.activate(global.get_current_time()); + } else if (showWidowsBeforeActivation && opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && !isShiftPressed && this.app.get_n_windows() > 1) { + // expose windows + Main.overview._overview._controls._thumbnailsBox._activateThumbnailAtPoint(0, 0, global.get_current_time(), true); + return; + } else if (((opt.DASH_SHIFT_CLICK_MV && isShiftPressed) || ((opt.DASH_CLICK_PREFER_WORKSPACE || opt.DASH_ISOLATE_WS) && !openNewWindow)) && this.app.get_windows().length) { + this._moveAppToCurrentWorkspace(); + if (opt.DASH_ISOLATE_WS) { + this.app.activate(); + // hide the overview after the window is re-created + GLib.idle_add(GLib.PRIORITY_LOW, () => Main.overview.hide()); + } + return; + } else if (isShiftPressed) { + return; + } else { + this.app.activate(); + } + + Main.overview.hide(); + }, + + _moveAppToCurrentWorkspace() { + this.app.get_windows().forEach(w => w.change_workspace(global.workspace_manager.get_active_workspace())); + }, + + popupMenu(side = St.Side.LEFT) { + this.setForcedHighlight(true); + this._removeMenuTimeout(); + this.fake_release(); + + if (!this._getWindowsOnCurrentWs) { + this._getWindowsOnCurrentWs = function () { + const winList = []; + this.app.get_windows().forEach(w => { + if (w.get_workspace() === global.workspace_manager.get_active_workspace()) + winList.push(w); + }); + return winList; + }; + + this._windowsOnOtherWs = function () { + return (this.app.get_windows().length - this._getWindowsOnCurrentWs().length) > 0; + }; + } + + if (!this._menu) { + this._menu = new AppMenu.AppMenu(this, side, { + favoritesSection: true, + showSingleWindows: true, + }); + + this._menu.setApp(this.app); + this._openSigId = this._menu.connect('open-state-changed', (menu, isPoppedUp) => { + if (!isPoppedUp) + this._onMenuPoppedDown(); + }); + // Main.overview.connectObject('hiding', + this._hidingSigId = Main.overview.connect('hiding', + () => this._menu.close(), this); + + Main.uiGroup.add_actor(this._menu.actor); + this._menuManager.addMenu(this._menu); + } + + // once the menu is created, it stays unchanged and we need to modify our items based on current situation + if (this._addedMenuItems && this._addedMenuItems.length) + this._addedMenuItems.forEach(i => i.destroy()); + + + const popupItems = []; + + const separator = new PopupMenu.PopupSeparatorMenuItem(); + this._menu.addMenuItem(separator); + + if (this.app.get_n_windows()) { + // if (/* opt.APP_MENU_FORCE_QUIT*/true) {} + popupItems.push([_('Force Quit'), () => { + this.app.get_windows()[0].kill(); + }]); + + // if (opt.APP_MENU_CLOSE_WS) {} + const nWin = this._getWindowsOnCurrentWs().length; + if (nWin) { + popupItems.push([_(`Close ${nWin} Windows on Current Workspace`), () => { + const windows = this._getWindowsOnCurrentWs(); + let time = global.get_current_time(); + for (let win of windows) { + // increase time by 1 ms for each window to avoid errors from GS + win.delete(time++); + } + }]); + } + + popupItems.push([_('Move App to Current Workspace ( Shift + Click )'), this._moveAppToCurrentWorkspace]); + if (opt.WINDOW_THUMBNAIL_ENABLED) { + popupItems.push([_('Create Window Thumbnail - PIP'), () => { + Me.Modules.winTmbModule.createThumbnail(this.app.get_windows()[0]); + }]); + } + } + + this._addedMenuItems = []; + this._addedMenuItems.push(separator); + popupItems.forEach(i => { + let item = new PopupMenu.PopupMenuItem(i[0]); + this._menu.addMenuItem(item); + item.connect('activate', i[1].bind(this)); + if (i[1] === this._moveAppToCurrentWorkspace && !this._windowsOnOtherWs()) + item.setSensitive(false); + this._addedMenuItems.push(item); + }); + + this.emit('menu-state-changed', true); + + this._menu.open(BoxPointer.PopupAnimation.FULL); + this._menuManager.ignoreRelease(); + this.emit('sync-tooltip'); + + return false; + }, + + _getWindowApp(metaWin) { + const tracker = Shell.WindowTracker.get_default(); + return tracker.get_window_app(metaWin); + }, + + _getAppLastUsedWindow(app) { + let recentWin; + global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { + const winApp = this._getWindowApp(metaWin); + if (!recentWin && winApp === app) + recentWin = metaWin; + }); + return recentWin; + }, + + _getAppRecentWorkspace(app) { + const recentWin = this._getAppLastUsedWindow(app); + if (recentWin) + return recentWin.get_workspace(); + + return null; + }, +}; + +const ShowWindowsIcon = { + _afterInit() { + this._isSearchWindowsIcon = true; + this._labelText = _('Search Open Windows (Hotkey: Space)'); + this.toggleButton = new St.Button({ + style_class: 'show-apps', + track_hover: true, + can_focus: true, + toggle_mode: false, + }); + + this._iconActor = null; + this.icon = new IconGrid.BaseIcon(this.labelText, { + setSizeManually: true, + showLabel: false, + createIcon: this._createIcon.bind(this), + }); + this.icon.y_align = Clutter.ActorAlign.CENTER; + + this.toggleButton.add_actor(this.icon); + this.toggleButton._delegate = this; + + this.setChild(this.toggleButton); + + if (opt.SEARCH_WINDOWS_ICON_SCROLL) { + this.reactive = true; + this._scrollConId = this.connect('scroll-event', DashExtensions.onScrollEvent.bind(this)); + this._leaveConId = this.connect('leave-event', DashExtensions.onLeaveEvent.bind(this)); + } + }, + + _createIcon(size) { + this._iconActor = new St.Icon({ + icon_name: 'focus-windows-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }); + return this._iconActor; + }, +}; + +const ShowRecentFilesIcon = { + _afterInit() { + this._labelText = _('Search Recent Files (Hotkey: Ctrl + Space)'); + this.toggleButton = new St.Button({ + style_class: 'show-apps', + track_hover: true, + can_focus: true, + toggle_mode: false, + }); + + this._iconActor = null; + this.icon = new IconGrid.BaseIcon(this.labelText, { + setSizeManually: true, + showLabel: false, + createIcon: this._createIcon.bind(this), + }); + this.icon.y_align = Clutter.ActorAlign.CENTER; + + this.toggleButton.add_actor(this.icon); + this.toggleButton._delegate = this; + + this.setChild(this.toggleButton); + }, + + _createIcon(size) { + this._iconActor = new St.Icon({ + icon_name: 'document-open-recent-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }); + return this._iconActor; + }, +}; + +const ShowExtensionsIcon = { + _afterInit() { + this._labelText = _('Search Extensions (Hotkey: Ctrl + Shift + Space)'); + this.toggleButton = new St.Button({ + style_class: 'show-apps', + track_hover: true, + can_focus: true, + toggle_mode: false, + }); + + this._iconActor = null; + this.icon = new IconGrid.BaseIcon(this.labelText, { + setSizeManually: true, + showLabel: false, + createIcon: this._createIcon.bind(this), + }); + this.icon.y_align = Clutter.ActorAlign.CENTER; + + this.toggleButton.add_actor(this.icon); + this.toggleButton._delegate = this; + + this.setChild(this.toggleButton); + }, + + _createIcon(size) { + this._iconActor = new St.Icon({ + icon_name: 'application-x-addon-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }); + return this._iconActor; + }, +}; + +const AppMenuCommon = { + _updateWindowsSection() { + if (global.compositor) { + if (this._updateWindowsLaterId) { + const laters = global.compositor.get_laters(); + laters.remove(this._updateWindowsLaterId); + } + } else if (this._updateWindowsLaterId) { + Meta.later_remove(this._updateWindowsLaterId); + } + + this._updateWindowsLaterId = 0; + + this._windowSection.removeAll(); + this._openWindowsHeader.hide(); + + if (!this._app) + return; + + const minWindows = this._showSingleWindows ? 1 : 2; + const currentWs = global.workspaceManager.get_active_workspace(); + const isolateWs = opt.DASH_ISOLATE_WS && !Main.overview.dash.showAppsButton.checked; + const windows = this._app.get_windows().filter(w => !w.skip_taskbar && (isolateWs ? w.get_workspace() === currentWs : true)); + if (windows.length < minWindows) + return; + + this._openWindowsHeader.show(); + + windows.forEach(window => { + const title = window.title || this._app.get_name(); + const item = this._windowSection.addAction(title, event => { + Main.activateWindow(window, event.get_time()); + }); + window.connectObject('notify::title', () => { + item.label.text = window.title || this._app.get_name(); + }, item); + }); + }, +}; diff --git a/extensions/45/vertical-workspaces/lib/extensionsSearchProvider.js b/extensions/45/vertical-workspaces/lib/extensionsSearchProvider.js new file mode 100644 index 0000000..30d9960 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/extensionsSearchProvider.js @@ -0,0 +1,406 @@ +/** +* V-Shell (Vertical Workspaces) + * extensionsSearchProvider.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + */ + +'use strict'; + +import GLib from 'gi://GLib'; +import St from 'gi://St'; +import Gio from 'gi://Gio'; +import Shell from 'gi://Shell'; +import GObject from 'gi://GObject'; +import Clutter from 'gi://Clutter'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +const ExtensionState = { + 1: 'ENABLED', + 2: 'DISABLED', + 3: 'ERROR', + 4: 'INCOMPATIBLE', + 5: 'DOWNLOADING', + 6: 'INITIALIZED', + 7: 'DISABLING', + 8: 'ENABLING', +}; + +let Me; +let opt; +// gettext +let _; +let _toggleTimeout; + +// prefix helps to eliminate results from other search providers +// so it needs to be something less common +// needs to be accessible from vw module +export const PREFIX = 'eq//'; + +export class ExtensionsSearchProviderModule { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._extensionsSearchProvider = null; + this._enableTimeoutId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + if (_toggleTimeout) + GLib.source_remove(_toggleTimeout); + + this.moduleEnabled = opt.get('extensionsSearchProviderModule'); + + reset = reset || !this.moduleEnabled; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' ExtensionsSearchProviderModule - Keeping untouched'); + } + + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._extensionsSearchProvider) { + this._extensionsSearchProvider = new extensionsSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._extensionsSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } + ); + console.debug(' ExtensionsSearchProviderModule - Activated'); + } + + _disableModule() { + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } + + if (this._extensionsSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._extensionsSearchProvider); + this._extensionsSearchProvider = null; + } + + + console.debug(' ExtensionsSearchProviderModule - Disabled'); + } + + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +} + +class extensionsSearchProvider { + constructor() { + this.id = 'extensions'; + const appSystem = Shell.AppSystem.get_default(); + let appInfo = appSystem.lookup_app('com.matjakeman.ExtensionManager.desktop')?.get_app_info(); + if (!appInfo) + appInfo = appSystem.lookup_app('org.gnome.Extensions.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/gnome-extensions-app', 'Extensions', null); + appInfo.get_description = () => _('Search extensions'); + appInfo.get_name = () => _('Extensions'); + appInfo.get_id = () => 'org.gnome.Extensions.desktop'; + appInfo.get_icon = () => Gio.icon_new_for_string('application-x-addon'); + appInfo.should_show = () => true; + + this.appInfo = appInfo; + this.canLaunchSearch = true; + this.isRemoteProvider = false; + } + + getInitialResultSet(terms/* , callback*/) { + const extensions = {}; + Main.extensionManager._extensions.forEach( + e => { + extensions[e.uuid] = e; + } + ); + this.extensions = extensions; + + return new Promise(resolve => resolve(this._getResultSet(terms))); + } + + _getResultSet(terms) { + // do not modify original terms + let termsCopy = [...terms]; + // search for terms without prefix + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); + + const candidates = this.extensions; + const _terms = [].concat(termsCopy); + + const term = _terms.join(' '); + + const results = []; + let m; + for (let id in candidates) { + const extension = this.extensions[id]; + const text = extension.metadata.name + (extension.state === 1 ? 'enabled' : '') + ([6, 2].includes(extension.state) ? 'disabled' : ''); + if (opt.SEARCH_FUZZY) + m = Me.Util.fuzzyMatch(term, text); + else + m = Me.Util.strictMatch(term, text); + + if (m !== -1) + results.push({ weight: m, id }); + } + + // sort alphabetically + results.sort((a, b) => this.extensions[a.id].metadata.name.localeCompare(this.extensions[b.id].metadata.name)); + // enabled first + // results.sort((a, b) => this.extensions[a.id].state !== 1 && this.extensions[b.id].state === 1); + // incompatible last + results.sort((a, b) => this.extensions[a.id].state === 4 && this.extensions[b.id].state !== 4); + + const resultIds = results.map(item => item.id); + return resultIds; + } + + getResultMetas(resultIds/* , callback = null*/) { + const metas = resultIds.map(id => this.getResultMeta(id)); + return new Promise(resolve => resolve(metas)); + } + + getResultMeta(resultId) { + const result = this.extensions[resultId]; + + const versionName = result.metadata['version-name'] ?? ''; + let version = result.metadata['version'] ?? ''; + version = versionName && version ? `/${version}` : version; + const versionStr = `${versionName}${version}`; + + return { + 'id': resultId, + 'name': `${result.metadata.name}`, + 'version': versionStr, + 'description': versionStr, // description will be updated in result object + 'createIcon': size => { + let icon = this.getIcon(result, size); + return icon; + }, + }; + } + + getIcon(extension, size) { + let opacity = 0; + let iconName = 'process-stop-symbolic'; + + switch (extension.state) { + case 1: + if (extension.hasUpdate) + iconName = 'software-update-available'; // 'software-update-available-symbolic'; + else + iconName = 'object-select-symbolic';// 'object-select-symbolic'; + + opacity = 255; + break; + case 3: + if (Main.extensionManager._enabledExtensions.includes(extension.uuid)) + iconName = 'emblem-ok-symbolic'; + else + iconName = 'dialog-error'; + opacity = 100; + break; + case 4: + iconName = 'software-update-urgent'; // 'software-update-urgent-symbolic'; + opacity = 100; + break; + } + + if (extension.hasUpdate) { + iconName = 'software-update-available'; // 'software-update-available-symbolic'; + opacity = 100; + } + + const icon = new St.Icon({ icon_name: iconName, icon_size: size }); + icon.set({ + opacity, + }); + + return icon; + } + + createResultObject(meta) { + return new ListSearchResult(this, meta, this.extensions[meta.id]); + } + + launchSearch(terms, timeStamp) { + this.appInfo.launch([], global.create_app_launch_context(timeStamp, -1), null); + } + + activateResult(resultId/* terms, timeStamp*/) { + const extension = this.extensions[resultId]; + if (Me.Util.isShiftPressed()) + this._toggleExtension(extension); + else if (extension.hasPrefs) + Me.Util.openPreferences(extension.metadata); + } + + filterResults(results /* , maxResults*/) { + // return results.slice(0, maxResults); + return results; + } + + getSubsearchResultSet(previousResults, terms/* , callback*/) { + return this.getInitialResultSet(terms); + } + + getSubsearchResultSet42(terms, callback) { + callback(this._getResultSet(terms)); + } +} + +const ListSearchResult = GObject.registerClass( +class ListSearchResult extends St.Button { + _init(provider, metaInfo, extension) { + this.provider = provider; + this.metaInfo = metaInfo; + this.extension = extension; + + super._init({ + reactive: true, + can_focus: true, + track_hover: true, + }); + + this.style_class = 'list-search-result'; + + let content = new St.BoxLayout({ + style_class: 'list-search-result-content', + vertical: false, + x_align: Clutter.ActorAlign.START, + x_expand: true, + y_expand: true, + }); + this.set_child(content); + + let titleBox = new St.BoxLayout({ + style_class: 'list-search-result-title', + y_align: Clutter.ActorAlign.CENTER, + }); + + content.add_child(titleBox); + + // An icon for, or thumbnail of, content + let icon = this.metaInfo['createIcon'](this.ICON_SIZE); + let iconBox = new St.Button(); + iconBox.set_child(icon); + titleBox.add(iconBox); + iconBox.set_style('border: 1px solid rgba(200,200,200,0.2); padding: 2px; border-radius: 8px;'); + this._iconBox = iconBox; + this.icon = icon; + + iconBox.connect('clicked', () => { + this._toggleExtension(); + return Clutter.EVENT_STOP; + }); + + let title = new St.Label({ + text: this.metaInfo['name'], + y_align: Clutter.ActorAlign.CENTER, + opacity: extension.hasPrefs ? 255 : 150, + }); + titleBox.add_child(title); + + this.label_actor = title; + + this._descriptionLabel = new St.Label({ + style_class: 'list-search-result-description', + y_align: Clutter.ActorAlign.CENTER, + }); + content.add_child(this._descriptionLabel); + + this._highlightTerms(); + + this.connect('destroy', () => { + if (_toggleTimeout) { + GLib.source_remove(_toggleTimeout); + _toggleTimeout = 0; + } + }); + } + + _toggleExtension() { + const state = this.extension.state; + if (![1, 2, 6, 3].includes(state) || this.extension.metadata.name.includes('vertical-workspaces')) + return; + + if ([2, 6].includes(state)) + Main.extensionManager.enableExtension(this.extension.uuid); + else if ([1, 3].includes(state)) + Main.extensionManager.disableExtension(this.extension.uuid); + + if (_toggleTimeout) + GLib.source_remove(_toggleTimeout); + + _toggleTimeout = GLib.timeout_add(GLib.PRIORITY_LOW, 200, + () => { + if ([7, 8].includes(this.extension.state)) + return GLib.SOURCE_CONTINUE; + + this.icon?.destroy(); + this.icon = this.metaInfo['createIcon'](this.ICON_SIZE); + this._iconBox.set_child(this.icon); + this._highlightTerms(); + + _toggleTimeout = 0; + return GLib.SOURCE_REMOVE; + } + ); + } + + get ICON_SIZE() { + return 24; + } + + _highlightTerms() { + const extension = this.extension; + const state = extension.state === 4 ? ExtensionState[this.extension.state] : ''; + const error = extension.state === 3 ? ` ERROR: ${this.extension.error}` : ''; + const update = extension.hasUpdate ? ' | UPDATE PENDING' : ''; + const text = `${this.metaInfo.version} ${state}${error}${update}`; + let markup = text;// this.metaInfo['description'].split('\n')[0]; + this._descriptionLabel.clutter_text.set_markup(markup); + } + + vfunc_clicked() { + this.activate(); + } + + activate() { + this.provider.activateResult(this.metaInfo.id); + + if (this.metaInfo.clipboardText) { + St.Clipboard.get_default().set_text( + St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText); + } + Main.overview.toggle(); + } +}); diff --git a/extensions/vertical-workspaces/lib/iconGrid.js b/extensions/45/vertical-workspaces/lib/iconGrid.js similarity index 67% rename from extensions/vertical-workspaces/lib/iconGrid.js rename to extensions/45/vertical-workspaces/lib/iconGrid.js index 1aa980e..09ec25e 100644 --- a/extensions/vertical-workspaces/lib/iconGrid.js +++ b/extensions/45/vertical-workspaces/lib/iconGrid.js @@ -9,11 +9,13 @@ */ 'use strict'; -const { GLib, St, Meta } = imports.gi; -const IconGrid = imports.ui.iconGrid; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; -const shellVersion = _Util.shellVersion; + +import St from 'gi://St'; + +import * as IconGrid from 'resource:///org/gnome/shell/ui/iconGrid.js'; + +let Me; +let opt; // added sizes for better scaling const IconSize = { @@ -29,47 +31,54 @@ const IconSize = { LARGE: 96, 80: 80, 64: 64, - 48: 48, - TINY: 32, + TINY: 48, }; const PAGE_WIDTH_CORRECTION = 100; -let opt; -let _overrides; -let _firstRun = true; +export const IconGridModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('appDisplayModule', true); - reset = reset || !moduleEnabled; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; } - _overrides = new _Util.Overrides(); + update(reset) { + this.moduleEnabled = opt.get('appDisplayModule'); + // if notifications are enabled no override is needed + reset = reset || !this.moduleEnabled; - if (shellVersion < 43 && IconGridCommon._findBestModeForSize) { - IconGridCommon['findBestModeForSize'] = IconGridCommon._findBestModeForSize; - IconGridCommon['_findBestModeForSize'] = undefined; + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } } - _overrides.addOverride('IconGrid', IconGrid.IconGrid.prototype, IconGridCommon); - _overrides.addOverride('IconGridLayout', IconGrid.IconGridLayout.prototype, IconGridLayoutCommon); -} -// workaround - silence page -2 error on gnome 43 while cleaning app grid + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('IconGrid', IconGrid.IconGrid.prototype, IconGridCommon); + this._overrides.addOverride('IconGridLayout', IconGrid.IconGridLayout.prototype, IconGridLayoutCommon); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + } +}; const IconGridCommon = { getItemsAtPage(page) { @@ -87,7 +96,7 @@ const IconGridCommon = { return; const { pagePadding } = this.layout_manager; const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const iconPadding = 53 * scaleFactor; + const iconPadding = 51 * scaleFactor; // provided width is usually about 100px wider in horizontal orientation with prev/next page indicators const pageIndicatorCompensation = opt.ORIENTATION ? 0 : PAGE_WIDTH_CORRECTION; @@ -99,8 +108,14 @@ const IconGridCommon = { this.layoutManager._gridWidth = width; this.layoutManager._gridHeight = height; + width -= 80; // compensation for default padding + height -= 80; + const spacing = opt.APP_GRID_SPACING; - const iconSize = (opt.APP_GRID_ICON_SIZE > 0 ? opt.APP_GRID_ICON_SIZE : opt.APP_GRID_ICON_SIZE_DEFAULT) * scaleFactor; + // set the icon size as fixed to avoid changes in size later + const iconSize = opt.APP_GRID_ICON_SIZE > 0 ? opt.APP_GRID_ICON_SIZE : opt.APP_GRID_ICON_SIZE_DEFAULT; + // this.layout_manager.fixedIconSize = iconSize; + const itemSize = iconSize * scaleFactor + iconPadding; // if this._gridModes.length === 1, custom grid should be used // if (iconSize > 0 && this._gridModes.length > 1) { let columns = opt.APP_GRID_COLUMNS; @@ -109,17 +124,20 @@ const IconGridCommon = { let unusedSpaceH = -1; let unusedSpaceV = -1; if (!columns) { - columns = Math.floor(width / (iconSize + iconPadding)) + 1; + // calculate #columns + 1 without spacing + columns = Math.floor(width / itemSize) + 1; + // check if columns with spacing fits the available width + // and reduce the number until it fits while (unusedSpaceH < 0) { columns -= 1; - unusedSpaceH = width - columns * (iconSize + iconPadding) - (columns - 1) * spacing; + unusedSpaceH = width - columns * itemSize - (columns - 1) * spacing; } } if (!rows) { - rows = Math.floor(height / (iconSize + iconPadding)) + 1; + rows = Math.floor(height / itemSize) + 1; while (unusedSpaceV < 0) { rows -= 1; - unusedSpaceV = height - rows * (iconSize + iconPadding) - (rows - 1) * spacing; + unusedSpaceV = height - rows * itemSize - ((rows - 1) * spacing); } } @@ -132,12 +150,27 @@ const IconGridCommon = { const IconGridLayoutCommon = { _findBestIconSize() { + if (this.fixedIconSize !== -1) + return this.fixedIconSize; + const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); const nColumns = this.columnsPerPage; const nRows = this.rowsPerPage; - const columnSpacingPerPage = opt.APP_GRID_SPACING * (nColumns - 1); - const rowSpacingPerPage = opt.APP_GRID_SPACING * (nRows - 1); - const iconPadding = 53 * scaleFactor; + + // if grid is not defined return default icon size + if (nColumns < 1 && nRows < 1) { + let iconSize; + if (this._isFolder) + iconSize = opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT; + else + iconSize = opt.APP_GRID_ICON_SIZE_DEFAULT; + + return iconSize; + } + + const columnSpacingPerPage = /* opt.APP_GRID_SPACING*/ 1 * (nColumns - 1); + const rowSpacingPerPage = /* opt.APP_GRID_SPACING*/ 1 * (nRows - 1); + const iconPadding = 55 * scaleFactor; const paddingH = this._isFolder ? this.pagePadding.left + this.pagePadding.right : 0; const paddingV = this._isFolder ? this.pagePadding.top + this.pagePadding.bottom : 0; @@ -149,17 +182,17 @@ const IconGridLayoutCommon = { const [firstItem] = this._container; - if (this.fixedIconSize !== -1) - return this.fixedIconSize; - /* if (opt.APP_GRID_ADAPTIVE && !this._isFolder) return opt.APP_GRID_ICON_SIZE_DEFAULT;*/ let iconSizes = Object.values(IconSize).sort((a, b) => b - a); - - // limit max icon size for folders, the whole range is for the main grid with active folders - if (this._isFolder) + // limit max icon size for folders and fully adaptive folder grids, the whole range is for the main grid with active folders + if (this._isFolder && opt.APP_GRID_FOLDER_ADAPTIVE && opt.APP_GRID_FOLDER_ICON_SIZE < 0) + iconSizes = iconSizes.slice(iconSizes.indexOf(opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT), -1); + else if (this._isFolder) iconSizes = iconSizes.slice(iconSizes.indexOf(IconSize.LARGE), -1); + else if (opt.APP_GRID_ADAPTIVE && opt.APP_GRID_ICON_SIZE < 0) + iconSizes = iconSizes.slice(iconSizes.indexOf(opt.APP_GRID_ICON_SIZE_DEFAULT), -1); let sizeInvalid = false; for (const size of iconSizes) { @@ -167,10 +200,9 @@ const IconGridLayoutCommon = { if (firstItem) { firstItem.icon.setIconSize(size); - const [firstItemWidth, firstItemHeight] = - firstItem.get_preferred_size(); + const [firstItemWidth] = firstItem.get_preferred_size(); - const itemSize = Math.max(firstItemWidth, firstItemHeight); + const itemSize = firstItemWidth; if (itemSize < size) sizeInvalid = true; @@ -199,7 +231,7 @@ const IconGridLayoutCommon = { removeItem(item) { if (!this._items.has(item)) { - log(`Item ${item} is not part of the IconGridLayout`); + console.error(`iconGrid: Item ${item} is not part of the IconGridLayout`); return; // throw new Error(`Item ${item} is not part of the IconGridLayout`); } @@ -215,13 +247,13 @@ const IconGridLayoutCommon = { addItem(item, page = -1, index = -1) { if (this._items.has(item)) { - log(`iconGrid: Item ${item} already added to IconGridLayout`); + console.error(`iconGrid: Item ${item} already added to IconGridLayout`); return; // throw new Error(`Item ${item} already added to IconGridLayout`); } if (page > this._pages.length) { - log(`iconGrid: Cannot add ${item} to page ${page}`); + console.error(`iconGrid: Cannot add ${item} to page ${page}`); page = -1; index = -1; // throw new Error(`Cannot add ${item} to page ${page}`); @@ -240,7 +272,7 @@ const IconGridLayoutCommon = { moveItem(item, newPage, newPosition) { if (!this._items.has(item)) { - log(`iconGrid: Item ${item} is not part of the IconGridLayout`); + console.error(`iconGrid: Item ${item} is not part of the IconGridLayout`); return; // throw new Error(`Item ${item} is not part of the IconGridLayout`); } diff --git a/extensions/vertical-workspaces/lib/layout.js b/extensions/45/vertical-workspaces/lib/layout.js similarity index 67% rename from extensions/vertical-workspaces/lib/layout.js rename to extensions/45/vertical-workspaces/lib/layout.js index f6562fd..d8f8fdc 100644 --- a/extensions/vertical-workspaces/lib/layout.js +++ b/extensions/45/vertical-workspaces/lib/layout.js @@ -10,67 +10,106 @@ 'use strict'; -const { Meta, GLib, Shell, Clutter, GObject } = imports.gi; +import GLib from 'gi://GLib'; +import Meta from 'gi://Meta'; +import Gio from 'gi://Gio'; -const Main = imports.ui.main; -const Layout = imports.ui.layout; -const Ripples = imports.ui.ripples; -const DND = imports.ui.dnd; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Layout from 'resource:///org/gnome/shell/ui/layout.js'; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -let _overrides; -let _timeouts; +let Me; let opt; -let _firstRun = true; -let _originalUpdateHotCorners; +let _timeouts; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('layoutModule', true); - const conflict = _Util.getEnabledExtensions('custom-hot-corners').length || - _Util.getEnabledExtensions('dash-to-panel').length; - reset = reset || !moduleEnabled; +export const LayoutModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _timeouts = {}; - // don't even touch this module if disabled or in conflict - if (_firstRun && (reset || conflict)) - return; - - _firstRun = false; - - if (!_originalUpdateHotCorners) - _originalUpdateHotCorners = Layout.LayoutManager.prototype._updateHotCorners; - - if (_overrides) - _overrides.removeAll(); - - if (_timeouts) { - Object.values(_timeouts).forEach(t => { - if (t) - GLib.source_remove(t); - }); + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._originalUpdateHotCorners = null; } - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - _timeouts = null; - Main.layoutManager._updateHotCorners = _originalUpdateHotCorners; - Main.layoutManager._updateHotCorners(); - return; } - _timeouts = {}; + update(reset) { + this._removeTimeouts(); - _overrides = new _Util.Overrides(); - _overrides.addOverride('LayoutManager', Layout.LayoutManager.prototype, LayoutManagerCommon); + this.moduleEnabled = opt.get('layoutModule'); + const conflict = Me.Util.getEnabledExtensions('custom-hot-corners').length || + Me.Util.getEnabledExtensions('dash-to-panel').length; - Main.layoutManager._updateHotCorners = LayoutManagerCommon._updateHotCorners.bind(Main.layoutManager); + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Layout" module disabled due to potential conflict with another extension`); - Main.layoutManager._updatePanelBarrier(); - Main.layoutManager._updateHotCorners(); -} + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' LayoutModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + this._overrides.addOverride('LayoutManager', Main.layoutManager, LayoutManagerCommon); + this._overrides.addOverride('HotCorner', Layout.HotCorner.prototype, HotCornerCommon); + + Main.layoutManager._updatePanelBarrier(); + Main.layoutManager._updateHotCorners(); + + if (!this._hotCornersEnabledConId) { + this._interfaceSettings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.interface', + }); + this._hotCornersEnabledConId = this._interfaceSettings.connect('changed::enable-hot-corners', + () => Main.layoutManager._updateHotCorners()); + } + + console.debug(' LayoutModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + Main.layoutManager._updateHotCorners(); + + if (this._hotCornersEnabledConId) { + this._interfaceSettings.disconnect(this._hotCornersEnabledConId); + this._hotCornersEnabledConId = 0; + this._interfaceSettings = null; + } + + console.debug(' LayoutModule - Disabled'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } +}; const LayoutManagerCommon = { _updatePanelBarrier() { @@ -84,7 +123,7 @@ const LayoutManagerCommon = { this._leftPanelBarrier = null; } - if (!this.primaryMonitor || !opt) + if (!this.primaryMonitor || !opt || Me.Util.getEnabledExtensions('hidetopbar')) return; if (this.panelBox.height) { @@ -113,6 +152,7 @@ const LayoutManagerCommon = { // avoid errors if called from foreign override if (!opt) return; + // destroy old hot corners this.hotCorners.forEach(corner => corner?.destroy()); this.hotCorners = []; @@ -122,7 +162,7 @@ const LayoutManagerCommon = { return; } - let size = this.panelBox.height; + let size = this.panelBox.height ? this.panelBox.height : 27; // position 0 - default, 1-TL, 2-TR, 3-BL, 4-BR const position = opt.HOT_CORNER_POSITION; @@ -181,8 +221,8 @@ const LayoutManagerCommon = { } if (haveCorner) { - let corner = new HotCorner(this, monitor, cornerX, cornerY); - corner.setBarrierSize(size); + let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY); + corner.setBarrierSize(size, false); this.hotCorners.push(corner); } else { this.hotCorners.push(null); @@ -193,11 +233,8 @@ const LayoutManagerCommon = { }, }; -var HotCorner = GObject.registerClass( -class HotCorner extends Layout.HotCorner { - _init(layoutManager, monitor, x, y) { - super._init(layoutManager, monitor, x, y); - +const HotCornerCommon = { + after__init() { let angle = 0; switch (opt.HOT_CORNER_POSITION) { case 2: @@ -214,9 +251,13 @@ class HotCorner extends Layout.HotCorner { this._ripples._ripple1.rotation_angle_z = angle; this._ripples._ripple2.rotation_angle_z = angle; this._ripples._ripple3.rotation_angle_z = angle; - } + }, + + setBarrierSize(size, notMyCall = true) { + // ignore calls from the original _updateHotCorners() callback to avoid building barriers outside screen + if (notMyCall && size > 0) + return; - setBarrierSize(size) { if (this._verticalBarrier) { this._pressureBarrier.removeBarrier(this._verticalBarrier); this._verticalBarrier.destroy(); @@ -232,8 +273,8 @@ class HotCorner extends Layout.HotCorner { if (size > 0) { const primaryMonitor = global.display.get_primary_monitor(); const monitor = this._monitor; - const extendV = opt && opt.HOT_CORNER_EDGE && opt.DASH_VERTICAL && monitor.index === primaryMonitor; - const extendH = opt && opt.HOT_CORNER_EDGE && !opt.DASH_VERTICAL && monitor.index === primaryMonitor; + const extendV = opt && opt.HOT_CORNER_ACTION && opt.HOT_CORNER_EDGE && opt.DASH_VERTICAL && monitor.index === primaryMonitor; + const extendH = opt && opt.HOT_CORNER_ACTION && opt.HOT_CORNER_EDGE && !opt.DASH_VERTICAL && monitor.index === primaryMonitor; if (opt.HOT_CORNER_POSITION <= 1) { this._verticalBarrier = new Meta.Barrier({ @@ -284,25 +325,52 @@ class HotCorner extends Layout.HotCorner { this._pressureBarrier.addBarrier(this._verticalBarrier); this._pressureBarrier.addBarrier(this._horizontalBarrier); } - } + }, _toggleOverview() { if (!opt.HOT_CORNER_ACTION || (!opt.HOT_CORNER_FULLSCREEN && this._monitor.inFullscreen && !Main.overview.visible)) return; if (Main.overview.shouldToggleByCornerOrButton()) { - if ((opt.HOT_CORNER_ACTION === 1 && !_Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 2 && _Util.isCtrlPressed())) + if (Main.overview._shown) { this._toggleWindowPicker(true); - else if ((opt.HOT_CORNER_ACTION === 2 && !_Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 1 && _Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 3 && _Util.isCtrlPressed())) + } else if ((opt.HOT_CORNER_ACTION === 2 && !Me.Util.isCtrlPressed()) || ([3, 4, 5, 6].includes(opt.HOT_CORNER_ACTION) && Me.Util.isCtrlPressed())) { + // Default overview + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 1) { + Main.overview.resetOverviewMode(); + this._toggleWindowPicker(true, true); + } else if ((opt.HOT_CORNER_ACTION === 3 && !Me.Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 2 && Me.Util.isCtrlPressed()) || (opt.HOT_CORNER_ACTION === 6 && Me.Util.isCtrlPressed())) { + // Applications this._toggleApplications(true); - else if (opt.HOT_CORNER_ACTION === 3 && !_Util.isCtrlPressed()) + } else if (opt.HOT_CORNER_ACTION === 4 && !Me.Util.isCtrlPressed()) { + // Overview - static ws preview + opt.OVERVIEW_MODE = 1; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 5 && !Me.Util.isCtrlPressed()) { + // Overview - static ws + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(true, true); + } else if (opt.HOT_CORNER_ACTION === 6 && !Me.Util.isCtrlPressed()) { + // Window search provider + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; this._toggleWindowSearchProvider(); + } if (opt.HOT_CORNER_RIPPLES && Main.overview.animationInProgress) this._ripples.playAnimation(this._x, this._y); } - } + }, - _toggleWindowPicker(leaveOverview = false) { + _toggleWindowPicker(leaveOverview = false, customOverviewMode = false) { if (Main.overview._shown && (leaveOverview || !Main.overview.dash.showAppsButton.checked)) { Main.overview.hide(); } else if (Main.overview.dash.showAppsButton.checked) { @@ -320,17 +388,17 @@ class HotCorner extends Layout.HotCorner { // delay cannot be too short 200, () => { - Main.overview.show(); + Main.overview.show(1, customOverviewMode); _timeouts.releaseKeyboardTimeoutId = 0; return GLib.SOURCE_REMOVE; } ); } else { - Main.overview.show(); + Main.overview.show(1, customOverviewMode); } } - } + }, _toggleApplications(leaveOverview = false) { if ((leaveOverview && Main.overview._shown) || Main.overview.dash.showAppsButton.checked) { @@ -360,12 +428,15 @@ class HotCorner extends Layout.HotCorner { Main.overview.show(2); // 2 for App Grid } } - } + }, _toggleWindowSearchProvider() { if (!Main.overview._overview._controls._searchController._searchActive) { - this._toggleWindowPicker(); - const prefix = 'wq// '; + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + this._toggleWindowPicker(false, true); + const prefix = Me.WSP_PREFIX; const position = prefix.length; const searchEntry = Main.overview.searchEntry; searchEntry.set_text(prefix); @@ -376,5 +447,5 @@ class HotCorner extends Layout.HotCorner { // Main.overview.searchEntry.text = ''; Main.overview.hide(); } - } -}); + }, +}; diff --git a/extensions/45/vertical-workspaces/lib/messageTray.js b/extensions/45/vertical-workspaces/lib/messageTray.js new file mode 100644 index 0000000..07f9541 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/messageTray.js @@ -0,0 +1,91 @@ +/** + * V-Shell (Vertical Workspaces) + * messageTray.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +let Me; +let opt; + +export const MessageTrayModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('messageTrayModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' MessageTrayModule - Keeping untouched'); + } + + _activateModule() { + this._setNotificationPosition(opt.NOTIFICATION_POSITION); + + console.debug(' MessageTrayModule - Activated'); + } + + _disableModule() { + this._setNotificationPosition(1); + + console.debug(' MessageTrayModule - Disabled'); + } + + _setNotificationPosition(position) { + switch (position) { + case 0: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.START; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; + break; + case 1: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.CENTER; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; + break; + case 2: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.END; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; + break; + case 3: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.START; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; + break; + case 4: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.CENTER; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; + break; + case 5: + Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.END; + Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; + break; + } + } +}; diff --git a/extensions/vertical-workspaces/lib/optionsFactory.js b/extensions/45/vertical-workspaces/lib/optionsFactory.js similarity index 65% rename from extensions/vertical-workspaces/lib/optionsFactory.js rename to extensions/45/vertical-workspaces/lib/optionsFactory.js index da62dd1..c20885e 100644 --- a/extensions/vertical-workspaces/lib/optionsFactory.js +++ b/extensions/45/vertical-workspaces/lib/optionsFactory.js @@ -9,41 +9,27 @@ 'use strict'; -const { Gtk, Gio, GObject } = imports.gi; +import Adw from 'gi://Adw'; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; - -const shellVersion = Settings.shellVersion; +let Me; // gettext -const _ = Settings._; +let _; -const ProfileNames = [ - _('GNOME 3'), - _('GNOME 40+ - Bottom Hot Edge'), - _('Hot Corner Centric - Top Left Hot Corner'), - _('Dock Overview - Bottom Hot Edge'), -]; - -// libadwaita is available starting with GNOME Shell 42. -let Adw = null; -try { - Adw = imports.gi.Adw; -} catch (e) {} - -function _newImageFromIconName(name) { - return Gtk.Image.new_from_icon_name(name); +export function init(me) { + Me = me; + _ = Me.gettext; } -var ItemFactory = class ItemFactory { - constructor(gOptions) { - this._gOptions = gOptions; - this._settings = this._gOptions._gsettings; +export const ItemFactory = class ItemFactory { + constructor() { + this._settings = Me.Opt._gsettings; } - getRowWidget(text, caption, widget, variable, options = []) { + getRowWidget(text, caption, widget, variable, options = [], dependsOn) { let item = []; let label; if (widget) { @@ -81,8 +67,8 @@ var ItemFactory = class ItemFactory { let key; - if (variable && this._gOptions.options[variable]) { - const opt = this._gOptions.options[variable]; + if (variable && Me.Opt.options[variable]) { + const opt = Me.Opt.options[variable]; key = opt[1]; } @@ -95,6 +81,11 @@ var ItemFactory = class ItemFactory { this._connectComboBox(widget, key, variable, options); else if (widget._isDropDown) this._connectDropDown(widget, key, variable, options); + + if (dependsOn) { + const dKey = Me.Opt.options[dependsOn][1]; + this._settings.bind(dKey, widget, 'sensitive', Gio.SettingsBindFlags.GET); + } } return item; @@ -111,7 +102,7 @@ var ItemFactory = class ItemFactory { _connectComboBox(widget, key, variable, options) { let model = widget.get_model(); widget._comboMap = {}; - const currentValue = this._gOptions.get(variable); + const currentValue = Me.Opt.get(variable); for (const [label, value] of options) { let iter; model.set(iter = model.append(), [0, 1], [label, value]); @@ -120,8 +111,8 @@ var ItemFactory = class ItemFactory { widget._comboMap[value] = iter; } - this._gOptions.connect(`changed::${key}`, () => { - widget.set_active_iter(widget._comboMap[this._gOptions.get(variable, true)]); + Me.Opt.connect(`changed::${key}`, () => { + widget.set_active_iter(widget._comboMap[Me.Opt.get(variable, true)]); }); widget.connect('changed', () => { const [success, iter] = widget.get_active_iter(); @@ -129,17 +120,17 @@ var ItemFactory = class ItemFactory { if (!success) return; - this._gOptions.set(variable, model.get_value(iter, 1)); + Me.Opt.set(variable, model.get_value(iter, 1)); }); } _connectDropDown(widget, key, variable, options) { const model = widget.get_model(); - const currentValue = this._gOptions.get(variable); + const currentValue = Me.Opt.get(variable); for (let i = 0; i < options.length; i++) { const text = options[i][0]; const id = options[i][1]; - model.append(new DropDownItem({ text, id })); + model.append(new DropDownItemVW({ text, id })); if (id === currentValue) widget.set_selected(i); } @@ -157,11 +148,11 @@ var ItemFactory = class ItemFactory { widget.connect('notify::selected-item', dropDown => { const item = dropDown.get_selected_item(); - this._gOptions.set(variable, item.id); + Me.Opt.set(variable, item.id); }); - this._gOptions.connect(`changed::${key}`, () => { - const newId = this._gOptions.get(variable, true); + Me.Opt.connect(`changed::${key}`, () => { + const newId = Me.Opt.get(variable, true); for (let i = 0; i < options.length; i++) { const id = options[i][1]; if (id === newId) @@ -214,7 +205,7 @@ var ItemFactory = class ItemFactory { newDropDown() { const dropDown = new Gtk.DropDown({ model: new Gio.ListStore({ - item_type: DropDownItem, + item_type: DropDownItemVW, }), halign: Gtk.Align.END, valign: Gtk.Align.CENTER, @@ -255,11 +246,11 @@ var ItemFactory = class ItemFactory { newLinkButton(uri) { const linkBtn = new Gtk.LinkButton({ - label: shellVersion < 42 ? 'Click Me!' : '', uri, halign: Gtk.Align.END, valign: Gtk.Align.CENTER, hexpand: true, + icon_name: 'emblem-symbolic-link', }); return linkBtn; } @@ -298,18 +289,23 @@ var ItemFactory = class ItemFactory { entry.set_text(opt.get(`profileName${profileIndex}`)); entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, 'edit-clear-symbolic'); entry.set_icon_activatable(Gtk.EntryIconPosition.SECONDARY, true); - entry.connect('icon-press', e => e.set_text('')); - entry.connect('changed', e => opt.set(`profileName${profileIndex}`, e.get_text())); const resetProfile = this.newButton(); resetProfile.set({ tooltip_text: _('Reset profile to defaults'), - icon_name: 'edit-delete-symbolic', + icon_name: 'document-revert-symbolic', hexpand: false, css_classes: ['destructive-action'], }); function setName() { + const ProfileNames = [ + _('GNOME 3'), + _('GNOME 40+ - Bottom Hot Edge'), + _('Hot Corner Centric - Top Left Hot Corner'), + _('Dock Overview - Bottom Hot Edge'), + ]; + let name = opt.get(`profileName${profileIndex}`, true); if (!name) name = ProfileNames[profileIndex - 1]; @@ -317,6 +313,10 @@ var ItemFactory = class ItemFactory { } setName(); + + entry.connect('icon-press', e => e.set_text('')); + entry.connect('changed', e => opt.set(`profileName${profileIndex}`, e.get_text())); + resetProfile.connect('clicked', () => { reset(profileIndex); setName(); @@ -366,7 +366,7 @@ var ItemFactory = class ItemFactory { valign: Gtk.Align.CENTER, hexpand: true, css_classes: ['destructive-action'], - icon_name: 'edit-delete-symbolic', + icon_name: 'document-revert-symbolic', }); btn.connect('clicked', () => { @@ -380,9 +380,9 @@ var ItemFactory = class ItemFactory { } }; -var AdwPrefs = class { +export const AdwPrefs = class { constructor(gOptions) { - this._gOptions = gOptions; + Me.Opt = gOptions; } getFilledWindow(window, pages) { @@ -405,7 +405,7 @@ var AdwPrefs = class { } _getAdwPage(optionList, pageProperties = {}) { - pageProperties.width_request = 840; + // pageProperties.width_request = 740; const page = new Adw.PreferencesPage(pageProperties); let group; for (let item of optionList) { @@ -457,158 +457,8 @@ var AdwPrefs = class { } }; -var LegacyPrefs = class { - constructor(gOptions) { - this._gOptions = gOptions; - } - - getPrefsWidget(pages) { - const prefsWidget = new Gtk.Box({ - orientation: Gtk.Orientation.VERTICAL, - }); - const stack = new Gtk.Stack({ - hexpand: true, - }); - const stackSwitcher = new Gtk.StackSwitcher({ - halign: Gtk.Align.CENTER, - hexpand: true, - }); - - const context = stackSwitcher.get_style_context(); - context.add_class('caption'); - - stackSwitcher.set_stack(stack); - stack.set_transition_duration(300); - stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT); - - const pageProperties = { - hscrollbar_policy: Gtk.PolicyType.NEVER, - vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, - vexpand: true, - hexpand: true, - visible: true, - }; - - const pagesBtns = []; - - for (let page of pages) { - const name = page.name; - const title = page.title; - const iconName = page.iconName; - const optionList = page.optionList; - - stack.add_named(this._getLegacyPage(optionList, pageProperties), name); - pagesBtns.push( - [new Gtk.Label({ label: title }), _newImageFromIconName(iconName, Gtk.IconSize.BUTTON)] - ); - } - - let stBtn = stackSwitcher.get_first_child ? stackSwitcher.get_first_child() : null; - for (let i = 0; i < pagesBtns.length; i++) { - const box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, spacing: 6, visible: true }); - const icon = pagesBtns[i][1]; - icon.margin_start = 30; - icon.margin_end = 30; - box.append(icon); - box.append(pagesBtns[i][0]); - if (stackSwitcher.get_children) { - stBtn = stackSwitcher.get_children()[i]; - stBtn.add(box); - } else { - stBtn.set_child(box); - stBtn.visible = true; - stBtn = stBtn.get_next_sibling(); - } - } - - if (stack.show_all) - stack.show_all(); - if (stackSwitcher.show_all) - stackSwitcher.show_all(); - - prefsWidget.append(stack); - - if (prefsWidget.show_all) - prefsWidget.show_all(); - - prefsWidget._stackSwitcher = stackSwitcher; - - return prefsWidget; - } - - _getLegacyPage(optionList, pageProperties) { - const page = new Gtk.ScrolledWindow(pageProperties); - const mainBox = new Gtk.Box({ - orientation: Gtk.Orientation.VERTICAL, - spacing: 5, - homogeneous: false, - margin_start: 30, - margin_end: 30, - margin_top: 12, - margin_bottom: 12, - }); - - let context = page.get_style_context(); - context.add_class('background'); - - let frame; - let frameBox; - for (let item of optionList) { - // label can be plain text for Section Title - // or GtkBox for Option - const option = item[0]; - const widget = item[1]; - - if (!widget) { - const lbl = new Gtk.Label({ - label: option, - xalign: 0, - margin_bottom: 4, - }); - - context = lbl.get_style_context(); - context.add_class('heading'); - - mainBox.append(lbl); - - frame = new Gtk.Frame({ - margin_bottom: 16, - }); - - frameBox = new Gtk.ListBox({ - selection_mode: null, - }); - - mainBox.append(frame); - frame.set_child(frameBox); - continue; - } - - const grid = new Gtk.Grid({ - column_homogeneous: false, - column_spacing: 20, - margin_start: 8, - margin_end: 8, - margin_top: 8, - margin_bottom: 8, - hexpand: true, - }); - - grid.attach(option, 0, 0, 5, 1); - - if (widget) - grid.attach(widget, 5, 0, 2, 1); - - frameBox.append(grid); - } - page.set_child(mainBox); - - return page; - } -}; - -const DropDownItem = GObject.registerClass({ - GTypeName: 'DropdownItem', +const DropDownItemVW = GObject.registerClass({ + GTypeName: 'DropDownItemVW', Properties: { 'text': GObject.ParamSpec.string( 'text', @@ -622,10 +472,11 @@ const DropDownItem = GObject.registerClass({ 'Id', 'Item id stored in settings', GObject.ParamFlags.READWRITE, - 0, 100, 0 + // min, max, default + -2147483648, 2147483647, 0 ), }, -}, class DropDownItem extends GObject.Object { +}, class DropDownItemVW extends GObject.Object { get text() { return this._text; } @@ -641,5 +492,4 @@ const DropDownItem = GObject.registerClass({ set id(id) { this._id = id; } -} -); +}); diff --git a/extensions/45/vertical-workspaces/lib/osdWindow.js b/extensions/45/vertical-workspaces/lib/osdWindow.js new file mode 100644 index 0000000..2298c34 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/osdWindow.js @@ -0,0 +1,118 @@ +/** + * V-Shell (Vertical Workspaces) + * osdWindow.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as OsdWindow from 'resource:///org/gnome/shell/ui/osdWindow.js'; + +let Me; +let opt; + +let OsdPositions; + +export const OsdWindowModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + + OsdPositions = { + 1: { + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + }, + 2: { + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.START, + }, + 3: { + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.START, + }, + 4: { + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + }, + 5: { + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.END, + }, + 6: { + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + }, + 7: { + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.END, + }, + }; + } + + cleanGlobals() { + Me = null; + opt = null; + OsdPositions = null; + } + + update(reset) { + this.moduleEnabled = opt.get('osdWindowModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OsdWindowModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('osdWindow', OsdWindow.OsdWindow.prototype, OsdWindowCommon); + console.debug(' OsdWindowModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + this._updateExistingOsdWindows(6); + + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); + } + + _updateExistingOsdWindows(position) { + position = position ? position : opt.OSD_POSITION; + Main.osdWindowManager._osdWindows.forEach(osd => { + osd.set(OsdPositions[position]); + }); + } +}; + +const OsdWindowCommon = { + after_show() { + if (!opt.OSD_POSITION) + this.opacity = 0; + this.set(OsdPositions[opt.OSD_POSITION]); + }, +}; diff --git a/extensions/45/vertical-workspaces/lib/overlayKey.js b/extensions/45/vertical-workspaces/lib/overlayKey.js new file mode 100644 index 0000000..77264c1 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/overlayKey.js @@ -0,0 +1,168 @@ +/** + * V-Shell (Vertical Workspaces) + * overlayKey.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import GLib from 'gi://GLib'; +import St from 'gi://St'; +import Meta from 'gi://Meta'; +import GObject from 'gi://GObject'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Overview from 'resource:///org/gnome/shell/ui/overview.js'; + +let Me; +let opt; + +export const OverlayKeyModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._originalOverlayKeyHandlerId = 0; + this._overlayKeyHandlerId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('overlayKeyModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OverlayKeyModule - Keeping untouched'); + } + + _activateModule() { + if (!this._originalOverlayKeyHandlerId) { + this._originalOverlayKeyHandlerId = GObject.signal_handler_find(global.display, { signalId: 'overlay-key' }); + if (this._originalOverlayKeyHandlerId !== null) { + global.display.block_signal_handler(this._originalOverlayKeyHandlerId); + this._connectOverlayKey(); + } + } + console.debug(' OverlayKeyModule - Activated'); + } + + _disableModule() { + this._restoreOverlayKeyHandler(); + + console.debug(' OverlayKeyModule - Disabled'); + } + + _restoreOverlayKeyHandler() { + // Disconnect modified overlay key handler + if (this._overlayKeyHandlerId) { + global.display.disconnect(this._overlayKeyHandlerId); + this._overlayKeyHandlerId = 0; + } + + // Unblock original overlay key handler + if (this._originalOverlayKeyHandlerId) { + global.display.unblock_signal_handler(this._originalOverlayKeyHandlerId); + this._originalOverlayKeyHandlerId = 0; + } + } + + _connectOverlayKey() { + if (this._overlayKeyHandlerId) + return; + + this._overlayKeyHandlerId = global.display.connect('overlay-key', this._onOverlayKeyPressed.bind(Main.overview._overview.controls)); + } + + _onOverlayKeyPressed() { + if (this._a11ySettings.get_boolean('stickykeys-enable')) + return; + + const { initialState, finalState, transitioning } = + this._stateAdjustment.getStateTransitionParams(); + + const time = GLib.get_monotonic_time() / 1000; + const timeDiff = time - this._lastOverlayKeyTime; + this._lastOverlayKeyTime = time; + + const shouldShift = St.Settings.get().enable_animations + ? transitioning && finalState > initialState + : Main.overview.visible && timeDiff < Overview.ANIMATION_TIME; + + const mode = opt.OVERLAY_KEY_SECONDARY; + if (shouldShift) { + Me.Util.activateSearchProvider(''); + if (mode === 1) { + this._shiftState(Meta.MotionDirection.UP); + } else if (mode === 2) { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + } else if (mode === 3) { + // Changing the overview mode automatically changes the overview transition + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + } + } else { + if (Main.overview._shown) { + Main.overview.hide(); + return; + } + switch (opt.OVERLAY_KEY_PRIMARY) { + case 0: // Disabled + return; + case 1: // Follow global overview mode + Main.overview.resetOverviewMode(); + break; + case 2: // Default overview + opt.OVERVIEW_MODE = 0; + opt.OVERVIEW_MODE2 = false; + opt.WORKSPACE_MODE = 1; + break; + case 3: // Default overview + if (Main.overview._shown) + Main.overview.hide(); + else + Main.overview.show(2); + return; + case 4: // Static WS preview + opt.OVERVIEW_MODE = 1; + opt.OVERVIEW_MODE2 = false; + if (!Main.overview._shown) + opt.WORKSPACE_MODE = 0; + break; + case 5: // Static WS + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + opt.WORKSPACE_MODE = 0; + break; + case 6: // Window Search + opt.OVERVIEW_MODE = 2; + opt.OVERVIEW_MODE2 = true; + if (!Main.overview._shown) + opt.WORKSPACE_MODE = 0; + break; + } + const customOverviewMode = !Main.overview._shown; + Main.overview.toggle(customOverviewMode); + if (opt.OVERLAY_KEY_PRIMARY === 6) + Me.Util.activateSearchProvider(Me.WSP_PREFIX); + } + } +}; diff --git a/extensions/45/vertical-workspaces/lib/overview.js b/extensions/45/vertical-workspaces/lib/overview.js new file mode 100644 index 0000000..449e9c8 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/overview.js @@ -0,0 +1,162 @@ +/** + * V-Shell (Vertical Workspaces) + * overview.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Overview from 'resource:///org/gnome/shell/ui/overview.js'; +import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; + +let Me; +let opt; + +export const OverviewModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = true; + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' OverviewModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('Overview', Overview.Overview.prototype, OverviewCommon); + console.debug(' OverviewModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' OverviewModule - Disabled'); + } +}; + +const OverviewCommon = { + show(state = OverviewControls.ControlsState.WINDOW_PICKER, customOverviewMode) { + if (!customOverviewMode) + this.resetOverviewMode(); + + if (state === OverviewControls.ControlsState.HIDDEN) + throw new Error('Invalid state, use hide() to hide'); + + if (this.isDummy) + return; + if (this._shown) + return; + this._shown = true; + + if (!this._syncGrab()) + return; + + Main.layoutManager.showOverview(); + this._animateVisible(state); + }, + + toggle(customOverviewMode) { + if (this.isDummy) + return; + + if (this._visible) + this.hide(); + else + this.show(OverviewControls.ControlsState.WINDOW_PICKER, customOverviewMode); + }, + + resetOverviewMode() { + // reset Overview Mode do default + opt.OVERVIEW_MODE = opt.get('overviewMode'); + opt.OVERVIEW_MODE2 = opt.OVERVIEW_MODE === 2; + opt.WORKSPACE_MODE = opt.OVERVIEW_MODE > 0 ? 0 : 1; + }, + + _showDone() { + this._animationInProgress = false; + this._coverPane.hide(); + + if (this._shownState !== 'SHOWN') + this._changeShownState('SHOWN'); + + // Handle any calls to hide* while we were showing + if (!this._shown) + this._animateNotVisible(); + + // if user activates overview during startup animation, transition needs to be shifted to the state 2 here + const controls = this._overview._controls; + if (controls._searchController._searchActive && controls._stateAdjustment.value === 1) { + if (opt.SEARCH_VIEW_ANIMATION) + controls._onSearchChanged(); + else if (!opt.OVERVIEW_MODE2) + controls._stateAdjustment.value = 2; + } + + this._syncGrab(); + }, + + // Workaround - should probably be fixed elsewhere in the upstream code + // If a new window is opened from the overview + // and is realized before the overview animation is complete, + // the new window will not get focus + after__hideDone() { + if (!opt.FIX_NEW_WINDOW_FOCUS) + return; + + const workspace = global.workspace_manager.get_active_workspace(); + const recentDesktopWin = global.display.get_tab_list(1, workspace)[0]; + let recentNormalWin = null; + const tabList = global.display.get_tab_list(0, workspace); + + for (let i = 0; i < tabList.length; i++) { + if (tabList[i].minimized === false) { + recentNormalWin = tabList[i]; + break; + } + } + + let recentWin = recentNormalWin; + if (recentNormalWin && recentDesktopWin) { + recentWin = recentNormalWin.get_user_time() > recentDesktopWin.get_user_time() + ? recentNormalWin + : recentDesktopWin; + } + + const focusedWin = global.display.focus_window; + + if (recentWin && focusedWin !== recentWin) + recentWin.activate(global.get_current_time()); + }, +}; diff --git a/extensions/vertical-workspaces/lib/overviewControls.js b/extensions/45/vertical-workspaces/lib/overviewControls.js similarity index 62% rename from extensions/vertical-workspaces/lib/overviewControls.js rename to extensions/45/vertical-workspaces/lib/overviewControls.js index 4959b83..df5b371 100644 --- a/extensions/vertical-workspaces/lib/overviewControls.js +++ b/extensions/45/vertical-workspaces/lib/overviewControls.js @@ -10,90 +10,142 @@ 'use strict'; -const { Clutter, GLib, GObject, St } = imports.gi; -const Main = imports.ui.main; -const Util = imports.misc.util; -const OverviewControls = imports.ui.overviewControls; -const WorkspaceThumbnail = imports.ui.workspaceThumbnail; +import GLib from 'gi://GLib'; +import Clutter from 'gi://Clutter'; +import St from 'gi://St'; +import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; +import GObject from 'gi://GObject'; -const ControlsState = imports.ui.overviewControls.ControlsState; -const FitMode = imports.ui.workspacesView.FitMode; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Overview from 'resource:///org/gnome/shell/ui/overview.js'; +import * as Layout from 'resource:///org/gnome/shell/ui/layout.js'; +import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; +import * as WorkspacesView from 'resource:///org/gnome/shell/ui/workspacesView.js'; +import * as Background from 'resource:///org/gnome/shell/ui/background.js'; +import * as Util from 'resource:///org/gnome/shell/misc/util.js'; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; - -let _overrides; +let Me; let opt; +// gettext +let _; -const ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME; +const ControlsState = OverviewControls.ControlsState; +const FitMode = WorkspacesView.FitMode; + +const ANIMATION_TIME = Overview.ANIMATION_TIME; const DASH_MAX_SIZE_RATIO = 0.25; let _originalSearchControllerSigId; let _searchControllerSigId; let _timeouts; -let _startupInitComplete = false; -function update(reset = false) { - if (_overrides) - _overrides.removeAll(); +export const OverviewControlsModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; - if (_timeouts) { - Object.values(_timeouts).forEach(id => { - if (id) - GLib.source_remove(id); - }); + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - _replaceOnSearchChanged(reset); - - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - _timeouts = null; - return; + _ = null; } - _timeouts = {}; + update(reset) { + this._removeTimeouts(); + this.moduleEnabled = true; + const conflict = false; - opt = Me.imports.lib.settings.opt; - _overrides = new _Util.Overrides(); + reset = reset || !this.moduleEnabled || conflict; - _overrides.addOverride('ControlsManager', OverviewControls.ControlsManager.prototype, ControlsManager); - - if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) - _overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutVertical); - else - _overrides.addOverride('ControlsManagerLayout', OverviewControls.ControlsManagerLayout.prototype, ControlsManagerLayoutHorizontal); -} - -function _replaceOnSearchChanged(reset = false) { - const searchController = Main.overview._overview.controls._searchController; - if (reset) { - if (_searchControllerSigId) { - searchController.disconnect(_searchControllerSigId); - _searchControllerSigId = 0; + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); } - if (_originalSearchControllerSigId) { - searchController.unblock_signal_handler(_originalSearchControllerSigId); - _originalSearchControllerSigId = 0; - } - Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_x = 0; - Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_y = 0; - Main.overview.searchEntry.visible = true; - Main.overview.searchEntry.opacity = 255; - } else { - // reconnect signal to use custom function (callbacks cannot be overridden in class prototype, they are already in memory as a copy for the given callback) - _originalSearchControllerSigId = GObject.signal_handler_find(searchController, { signalId: 'notify', detail: 'search-active' }); - if (_originalSearchControllerSigId) - searchController.block_signal_handler(_originalSearchControllerSigId); - - _searchControllerSigId = searchController.connect('notify::search-active', ControlsManager._onSearchChanged.bind(Main.overview._overview.controls)); + if (reset && this._firstActivation) + console.debug(' OverviewControlsModule - Keeping untouched'); } -} -const ControlsManager = { + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + _timeouts = {}; + + this._replaceOnSearchChanged(); + + this._overrides.addOverride('ControlsManager', OverviewControls.ControlsManager.prototype, ControlsManagerCommon); + + if (opt.ORIENTATION === Clutter.Orientation.VERTICAL) + this._overrides.addOverride('ControlsManagerLayout', Main.overview._overview.controls.layoutManager, ControlsManagerLayoutVertical); + else + this._overrides.addOverride('ControlsManagerLayout', Main.overview._overview.controls.layoutManager, ControlsManagerLayoutHorizontal); + + this._overrides.addOverride('LayoutManager', Layout.LayoutManager.prototype, LayoutManager); + + console.debug(' OverviewControlsModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + const reset = true; + this._replaceOnSearchChanged(reset); + Main.overview._overview._controls._appDisplay.opacity = 255; + + console.debug(' OverviewControlsModule - Disabled'); + } + + _removeTimeouts() { + if (_timeouts) { + Object.values(_timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + _timeouts = null; + } + } + + _replaceOnSearchChanged(reset) { + const searchController = Main.overview._overview.controls._searchController; + if (reset) { + if (_searchControllerSigId) { + searchController.disconnect(_searchControllerSigId); + _searchControllerSigId = 0; + } + if (_originalSearchControllerSigId) { + searchController.unblock_signal_handler(_originalSearchControllerSigId); + _originalSearchControllerSigId = 0; + } + Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_x = 0; + Main.overview._overview._controls.layoutManager._searchController._searchResults.translation_y = 0; + Main.overview.searchEntry.visible = true; + Main.overview.searchEntry.opacity = 255; + } else { + // reconnect signal to use custom function (callbacks cannot be overridden in class prototype, they are already in memory as a copy for the given callback) + if (!_originalSearchControllerSigId) + _originalSearchControllerSigId = GObject.signal_handler_find(searchController, { signalId: 'notify', detail: 'search-active' }); + if (_originalSearchControllerSigId) + searchController.block_signal_handler(_originalSearchControllerSigId); + + if (!_searchControllerSigId) + _searchControllerSigId = searchController.connect('notify::search-active', ControlsManagerCommon._onSearchChanged.bind(Main.overview._overview.controls)); + } + } +}; + +const ControlsManagerCommon = { // this function is used as a callback by a signal handler, needs to be reconnected after modification as the original callback uses a copy of the original function /* _update: function() { ... @@ -105,16 +157,21 @@ const ControlsManager = { }, _updateThumbnailsBox() { + const { currentState } = this._stateAdjustment.getStateTransitionParams(); const { shouldShow } = this._thumbnailsBox; - const thumbnailsBoxVisible = shouldShow; + const thumbnailsBoxVisible = shouldShow && + ((currentState < ControlsState.APP_GRID && opt.SHOW_WS_TMB) || + (currentState > ControlsState.WINDOW_PICKER && opt.SHOW_WS_TMB_APPGRID) || + (currentState > ControlsState.WINDOW_PICKER && this._searchController.searchActive && opt.SHOW_WS_TMB) + ); this._thumbnailsBox.visible = thumbnailsBoxVisible; // this call should be directly in _update(), but it's used as a callback function and it would require to reconnect the signal - this._updateWorkspacesDisplay(); + this._updateOverview(); }, // this function is pure addition to the original code and handles wsDisp transition to APP_GRID view - _updateWorkspacesDisplay() { + _updateOverview() { this._workspacesDisplay.translation_x = 0; this._workspacesDisplay.translation_y = 0; this._workspacesDisplay.scale_x = 1; @@ -143,21 +200,16 @@ const ControlsManager = { let opacity = Math.round(Util.lerp(initialParams.opacity, finalParams.opacity, progress)); - let workspacesDisplayVisible = opacity !== 0/* && !(searchActive)*/; + let workspacesDisplayVisible = opacity !== 0; // improve transition from search results to desktop if (finalState === 0 && this._searchController._searchResults.visible) this._searchController.hide(); // reset Static Workspace window picker mode - if (currentState === 0/* finalState === 0 && progress === 1*/ && opt.OVERVIEW_MODE && opt.WORKSPACE_MODE) + if (currentState === 0 && opt.OVERVIEW_MODE && opt.WORKSPACE_MODE) opt.WORKSPACE_MODE = 0; - if (currentState < 2 && currentState > 1) - WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 0; - else - WorkspaceThumbnail.RESCALE_ANIMATION_TIME = 200; - if (!opt.WS_ANIMATION || !opt.SHOW_WS_TMB) { this._workspacesDisplay.opacity = opacity; } else if (!opt.SHOW_WS_TMB_BG) { @@ -172,13 +224,13 @@ const ControlsManager = { const dash = this.dash; const searchEntryBin = this._searchEntryBin; // this dash transition collides with startup animation and freezes GS for good, needs to be delayed (first Main.overview 'hiding' event enables it) - const skipDash = _Util.dashNotDefault(); + const skipDash = Me.Util.dashNotDefault(); // OVERVIEW_MODE 2 should animate dash and wsTmbBox only if WORKSPACE_MODE === 0 (windows not spread) const animateOverviewMode2 = opt.OVERVIEW_MODE2 && !(finalState === 1 && opt.WORKSPACE_MODE); if (!Main.layoutManager._startingUp && ((!opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2) || animateOverviewMode2)) { if (!tmbBox._translationOriginal || Math.abs(tmbBox._translationOriginal[0]) > 500) { // swipe gesture can call this calculation before tmbBox is finalized, giving nonsense width - const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin); + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = this._getOverviewTranslations(dash, tmbBox, searchEntryBin); tmbBox._translationOriginal = [tmbTranslationX, tmbTranslationY]; dash._translationOriginal = [dashTranslationX, dashTranslationY]; searchEntryBin._translationOriginal = searchTranslationY; @@ -229,7 +281,7 @@ const ControlsManager = { // set searchEntry above appDisplay this.set_child_above_sibling(this._searchEntryBin, null); // move dash above wsTmb for case that dash and wsTmb animate from the same side - if (!_Util.dashNotDefault()) + if (!Me.Util.dashNotDefault()) this.set_child_above_sibling(dash, null); this.set_child_below_sibling(this._thumbnailsBox, null); this.set_child_below_sibling(this._workspacesDisplay, null); @@ -238,7 +290,7 @@ const ControlsManager = { // set dash above workspace in the overview this.set_child_above_sibling(this._thumbnailsBox, null); this.set_child_above_sibling(this._searchEntryBin, null); - if (!_Util.dashNotDefault()) + if (!Me.Util.dashNotDefault()) this.set_child_above_sibling(this.dash, null); this.dash._isAbove = true; @@ -258,12 +310,6 @@ const ControlsManager = { if (this.dash.showAppsButton.checked) this._searchTransition = false; - // update App Grid after settings changed - // only if the App Grid is currently visible on the screen, the paging updates correctly - if (currentState === ControlsState.APP_GRID && this._appDisplay.visible && opt._appGridNeedsRedisplay) { - Me.imports.lib.appDisplay._updateAppGridProperties(); - opt._appGridNeedsRedisplay = false; - } // if !APP_GRID_ANIMATION, appGrid needs to be hidden in WINDOW_PICKER mode (1) // but needs to be visible for transition from HIDDEN (0) to APP_GRID (2) this._appDisplay.visible = @@ -305,11 +351,17 @@ const ControlsManager = { this._workspacesDisplay.reactive = true; this._workspacesDisplay.setPrimaryWorkspaceVisible(true); } else { + if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) + this._searchController._searchResults._statusText.add_style_class_name('search-statustext-om2'); + else + this._searchController._searchResults._statusText.remove_style_class_name('search-statustext-om2'); this._searchController.show(); entry.visible = true; entry.opacity = 255; } + if (opt.SHOW_BG_IN_OVERVIEW && this._bgManagers) + this._updateBackground(this._bgManagers[0]); this._searchTransition = true; this._searchController._searchResults.translation_x = 0; @@ -317,34 +369,33 @@ const ControlsManager = { this._searchController.opacity = 255; this._searchController.visible = true; - if (opt.SEARCH_VIEW_ANIMATION && !this.dash.showAppsButton.checked && ![4, 8].includes(opt.WS_TMB_POSITION) /* && !opt.OVERVIEW_MODE2*/) { + if (opt.SEARCH_VIEW_ANIMATION && ![4, 8].includes(opt.WS_TMB_POSITION)) { this._updateAppDisplayVisibility(); + this.layoutManager._searchController._searchResults._statusBin.opacity = 1; this._searchController.opacity = searchActive ? 255 : 0; let translationX = 0; let translationY = 0; const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor()); - if (currentState < ControlsState.APP_GRID) { - switch (opt.SEARCH_VIEW_ANIMATION) { - case 1: - // make it longer to cover the delay before results appears - translationX = geometry.width; - translationY = 0; - break; - case 2: - translationX = -geometry.width; - translationY = 0; - break; - case 3: - translationX = 0; - translationY = geometry.height; - break; - case 5: - translationX = 0; - translationY = -geometry.height; - break; - } + switch (opt.SEARCH_VIEW_ANIMATION) { + case 1: + // make it longer to cover the delay before results appears + translationX = geometry.width; + translationY = 0; + break; + case 2: + translationX = -geometry.width; + translationY = 0; + break; + case 3: + translationX = 0; + translationY = geometry.height; + break; + case 5: + translationX = 0; + translationY = -geometry.height; + break; } if (searchActive) { @@ -363,6 +414,7 @@ const ControlsManager = { onComplete: () => { this._searchController.visible = searchActive; this._searchTransition = false; + this.layoutManager._searchController._searchResults._statusBin.opacity = 255; }, }); @@ -372,25 +424,17 @@ const ControlsManager = { opacity: searchActive || currentState < 2 ? 0 : 255, duration: SIDE_CONTROLS_ANIMATION_TIME / 2, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => this._updateAppDisplayVisibility(), + onComplete: () => { + this._updateAppDisplayVisibility(); + }, }); - // this._updateAppDisplayVisibility(); this._workspacesDisplay.setPrimaryWorkspaceVisible(true); - /* this._workspacesDisplay.ease({ - opacity: searchActive ? 0 : 255, - duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME / 2 : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - this._workspacesDisplay.reactive = !searchActive; - this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive); - }, - });*/ this._searchController.opacity = searchActive ? 0 : 255; this._searchController.ease({ opacity: searchActive ? 255 : 0, - duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME * 2 : 0, + duration: searchActive ? SIDE_CONTROLS_ANIMATION_TIME : 0, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => (this._searchController.visible = searchActive), }); @@ -398,14 +442,16 @@ const ControlsManager = { // reuse already tuned overview transition, just replace APP_GRID with the search view if (!(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) && !Main.overview._animationInProgress && finalState !== ControlsState.HIDDEN && !this.dash.showAppsButton.checked) { - Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg'); Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); + const duration = opt.SEARCH_VIEW_ANIMATION ? 150 : 0; this._stateAdjustment.ease(searchActive ? ControlsState.APP_GRID : ControlsState.WINDOW_PICKER, { // shorter animation time when entering search view can avoid stuttering in transition // collecting search results take some time and the problematic part is the realization of the object on the screen // if the ws animation ends before this event, the whole transition is smoother // removing the ws transition (duration: 0) seems like the best solution here - duration: searchActive || (opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE) ? 80 : SIDE_CONTROLS_ANIMATION_TIME, + duration: searchActive ? duration : SIDE_CONTROLS_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { this._workspacesDisplay.setPrimaryWorkspaceVisible(!searchActive); @@ -413,18 +459,20 @@ const ControlsManager = { }); } else if (opt.OVERVIEW_MODE2 && !(opt.WORKSPACE_MODE || this.dash.showAppsButton.checked)) { // add background to search results and make searchEntry border thicker for better visibility - Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg-om2'); Main.overview.searchEntry.add_style_class_name('search-entry-om2'); } else { - Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-om2'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.add_style_class_name('search-section-content-bg'); + Main.overview._overview._controls.layoutManager._searchController._searchResults._content.remove_style_class_name('search-section-content-bg-om2'); Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); } }, async runStartupAnimation(callback) { this._ignoreShowAppsButtonToggle = true; - this._searchController.prepareToEnterOverview(); - this._workspacesDisplay.prepareToEnterOverview(); + + this.prepareToEnterOverview(); this._stateAdjustment.value = ControlsState.HIDDEN; this._stateAdjustment.ease(ControlsState.WINDOW_PICKER, { @@ -436,16 +484,19 @@ const ControlsManager = { this._ignoreShowAppsButtonToggle = false; // Set the opacity here to avoid a 1-frame flicker - this.opacity = 0; + this.opacity = 1; + this._appDisplay.opacity = 1; // We can't run the animation before the first allocation happens await this.layout_manager.ensureAllocation(); - const { STARTUP_ANIMATION_TIME } = imports.ui.layout; + this._setBackground(); + Main.panel.opacity = 255; + const STARTUP_ANIMATION_TIME = 500; // Opacity this.ease({ - opacity: 255, + opacity: opt.STARTUP_STATE === 1 ? 0 : 255, duration: STARTUP_ANIMATION_TIME, mode: Clutter.AnimationMode.LINEAR, onComplete: () => { @@ -465,53 +516,24 @@ const ControlsManager = { } const searchEntryBin = this._searchEntryBin; - const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = - _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin); + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = + this._getOverviewTranslations(dash, tmbBox, searchEntryBin); const onComplete = function () { // running init callback again causes issues (multiple connections) - if (!_startupInitComplete) + if (!Main.overview._startupInitComplete) callback(); - _startupInitComplete = true; - // force app grid to build before the first visible animation to remove possible stuttering - this._appDisplay.opacity = 1; + const appDisplayModule = Me.Modules.appDisplayModule; + if (!appDisplayModule.moduleEnabled) + this._finishStartupSequence(); + else + this._realizeAppDisplayAndFinishSequence(); - const [x, y] = this._appDisplay.get_position(); - const translationX = -x; - const translationY = -y; - this._appDisplay.translation_x = translationX; - this._appDisplay.translation_y = translationY; - GLib.idle_add(0, () => { - this._appDisplay._removeItem(this._appDisplay._orderedItems[0]); - this._appDisplay._redisplay(); - }); - - // let the main loop realize previous changes before continuing - _timeouts.startupAnim1 = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 10, - () => { - GLib.idle_add(0, () => { - this._appDisplay._removeItem(this._appDisplay._orderedItems[0]); - this._appDisplay._redisplay(); - }); - this._appDisplay.translation_x = 0; - this._appDisplay.translation_y = 0; - this._appDisplay.visible = false; - if (opt.STARTUP_STATE === 1) { - Main.overview.hide(); - } else if (opt.STARTUP_STATE === 2) { - this._appDisplay.opacity = 255; - this.dash.showAppsButton.checked = true; - } - _timeouts.startupAnim1 = 0; - return GLib.SOURCE_REMOVE; - } - ); + Main.overview._startupInitComplete = true; }.bind(this); - if (dash.visible && !_Util.dashNotDefault()) { + if (dash.visible && !Me.Util.dashNotDefault()) { dash.translation_x = dashTranslationX; dash.translation_y = dashTranslationY; dash.opacity = 255; @@ -521,9 +543,7 @@ const ControlsManager = { delay: STARTUP_ANIMATION_TIME / 2, duration: STARTUP_ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => { - onComplete(); - }, + onComplete, }); } else { // set dash opacity to make it visible if user enable it later @@ -535,6 +555,7 @@ const ControlsManager = { STARTUP_ANIMATION_TIME * 2 * St.Settings.get().slow_down_factor, () => { onComplete(); + Main.overview._startupInitComplete = true; _timeouts.startupAnim2 = 0; return GLib.SOURCE_REMOVE; } @@ -571,7 +592,6 @@ const ControlsManager = { if (view._monitorIndex !== global.display.get_primary_monitor() && view._thumbnails.visible) { const secTmbBox = view._thumbnails; - _Util.getOverviewTranslations(opt, dash, secTmbBox, searchEntryBin); if (opt.SEC_WS_TMB_LEFT) secTmbBox.translation_x = -(secTmbBox.width + 12); // compensate for padding else if (opt.SEC_WS_TMB_RIGHT) @@ -594,13 +614,132 @@ const ControlsManager = { } }, + _realizeAppDisplayAndFinishSequence() { + const appDisplayModule = Me.Modules.appDisplayModule; + // realize app grid for smoother first animation + appDisplayModule._updateAppGrid(false, this._finishStartupSequence.bind(this)); + }, + + _finishStartupSequence() { + if (!this._bgManagers) + this._setBackground(); + + /* if (Me.Util.dashIsDashToDock()) + return;*/ + + _timeouts.finishStartup = GLib.idle_add( + GLib.PRIORITY_LOW, () => { + this._appDisplay.opacity = 255; + if (opt.STARTUP_STATE === 1) { + Main.overview.hide(); + } else if (opt.STARTUP_STATE === 2) { + Main.overview.show(2); // just because of DtD, because we skipped startup animation + this.dash.showAppsButton.checked = true; + } else if (!opt.STARTUP_STATE && Me.Util.dashNotDefault()) { + Main.overview.show(); + } + + _timeouts.finishStartup = 0; + return GLib.SOURCE_REMOVE; + } + ); + }, + + setInitialTranslations() { + const dash = this.dash; + const tmbBox = this._thumbnailsBox; + const searchEntryBin = this._searchEntryBin; + const [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY] = + this._getOverviewTranslations(dash, tmbBox, searchEntryBin); + if (!Me.Util.dashNotDefault()) { + dash.translation_x = dashTranslationX; + dash.translation_y = dashTranslationY; + } + tmbBox.translation_x = tmbTranslationX; + tmbBox.translation_y = tmbTranslationY; + searchEntryBin.translation_y = searchTranslationY; + }, + + _getOverviewTranslations(dash, tmbBox, searchEntryBin) { + // const tmbBox = Main.overview._overview._controls._thumbnailsBox; + const animationsDisabled = !St.Settings.get().enable_animations || (opt.SHOW_WS_PREVIEW_BG && !opt.OVERVIEW_MODE2); + if (animationsDisabled) + return [0, 0, 0, 0, 0]; + + let searchTranslationY = 0; + if (searchEntryBin.visible) { + const offset = (dash.visible && (!opt.DASH_VERTICAL ? dash.height + 12 : 0)) + + (opt.WS_TMB_TOP ? tmbBox.height + 12 : 0); + searchTranslationY = -searchEntryBin.height - offset - 30; + } + + let tmbTranslationX = 0; + let tmbTranslationY = 0; + let offset; + if (tmbBox.visible) { + const tmbWidth = tmbBox.width === Infinity ? 0 : tmbBox.width; + const tmbHeight = tmbBox.height === Infinity ? 0 : tmbBox.height; + switch (opt.WS_TMB_POSITION) { + case 3: // left + offset = 10 + (dash?.visible && opt.DASH_LEFT ? dash.width : 0); + tmbTranslationX = -tmbWidth - offset; + tmbTranslationY = 0; + break; + case 1: // right + offset = 10 + (dash?.visible && opt.DASH_RIGHT ? dash.width : 0); + tmbTranslationX = tmbWidth + offset; + tmbTranslationY = 0; + break; + case 0: // top + offset = 10 + (dash?.visible && opt.DASH_TOP ? dash.height : 0) + Main.panel.height; + tmbTranslationX = 0; + tmbTranslationY = -tmbHeight - offset; + break; + case 2: // bottom + offset = 10 + (dash?.visible && opt.DASH_BOTTOM ? dash.height : 0) + Main.panel.height; // just for case the panel is at bottom + tmbTranslationX = 0; + tmbTranslationY = tmbHeight + offset; + break; + } + } + + let dashTranslationX = 0; + let dashTranslationY = 0; + let position = opt.DASH_POSITION; + // if DtD replaced the original Dash, read its position + if (Me.Util.dashIsDashToDock()) + position = dash._position; + + if (dash?.visible) { + const dashWidth = dash.width === Infinity ? 0 : dash.width; + const dashHeight = dash.height === Infinity ? 0 : dash.height; + switch (position) { + case 0: // top + dashTranslationX = 0; + dashTranslationY = -dashHeight - dash.margin_bottom - Main.panel.height; + break; + case 1: // right + dashTranslationX = dashWidth; + dashTranslationY = 0; + break; + case 2: // bottom + dashTranslationX = 0; + dashTranslationY = dashHeight + dash.margin_bottom + Main.panel.height; + break; + case 3: // left + dashTranslationX = -dashWidth; + dashTranslationY = 0; + break; + } + } + + return [dashTranslationX, dashTranslationY, tmbTranslationX, tmbTranslationY, searchTranslationY]; + }, + animateToOverview(state, callback) { this._ignoreShowAppsButtonToggle = true; this._searchTransition = false; - this._searchController.prepareToEnterOverview(); - this._workspacesDisplay.prepareToEnterOverview(); - this._stateAdjustment.value = ControlsState.HIDDEN; // building window thumbnails takes some time and with many windows on the workspace @@ -627,20 +766,159 @@ const ControlsManager = { this._ignoreShowAppsButtonToggle = false; }, + + _setBackground(reset = false) { + if (this._bgManagers) { + this._bgManagers.forEach(bg => { + Main.overview._overview._controls._stateAdjustment.disconnect(bg._fadeSignal); + bg.destroy(); + }); + } + + // if (!SHOW_BG_IN_OVERVIEW && !SHOW_WS_PREVIEW_BG) the background is used for static transition from wallpaper to empty bg in the overview + if (reset || (!opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG)) { + delete this._bgManagers; + return; + } + + this._bgManagers = []; + for (const monitor of Main.layoutManager.monitors) { + const bgManager = new Background.BackgroundManager({ + monitorIndex: monitor.index, + container: Main.layoutManager.overviewGroup, + vignette: true, + }); + + bgManager.backgroundActor.content.vignette_sharpness = 0; + bgManager.backgroundActor.content.brightness = 1; + + + bgManager._fadeSignal = Main.overview._overview._controls._stateAdjustment.connect('notify::value', v => { + this._updateBackground(bgManager, v.value, v); + }); + + if (monitor.index === global.display.get_primary_monitor()) { + bgManager._primary = true; + this._bgManagers.unshift(bgManager); // primary monitor first + } else { + bgManager._primary = false; + this._bgManagers.push(bgManager); + } + } + }, + + _updateBackground(bgManager, stateValue = 2, stateAdjustment = null) { + // Blur My Shell extension destroys all background actors in the overview and doesn't care about consequences + if (this._bgManagers[0] && !Main.layoutManager.overviewGroup.get_children().includes(this._bgManagers[0].backgroundActor)) { + Main.notifyError(`[${Me.metadata.name}]`, _('Overview background crashed!\nIf you are using Blur My Shell, disable overview blur in its settings and re-enable V-Shell Overview Background to avoid visual glitches.')); + // remove and disconnect our destroyed backgrounds to avoid more errors + this._setBackground(true); + return; + } + + const finalState = stateAdjustment?.getStateTransitionParams().finalState; + if (!opt.SHOW_BG_IN_OVERVIEW && !opt.SHOW_WS_PREVIEW_BG) { + // if no bg shown in the overview, fade out the wallpaper + if (!(opt.OVERVIEW_MODE2 && opt.WORKSPACE_MODE && finalState === 1)) + bgManager.backgroundActor.opacity = Util.lerp(255, 0, Math.min(stateValue, 1)); + } else { + let VIGNETTE, BRIGHTNESS, bgValue; + if (opt.OVERVIEW_MODE2 && stateValue <= 1 && !opt.WORKSPACE_MODE) { + VIGNETTE = 0; + BRIGHTNESS = 1; + bgValue = stateValue; + } else { + VIGNETTE = 0.2; + BRIGHTNESS = opt.OVERVIEW_BG_BRIGHTNESS; + if (opt.OVERVIEW_MODE2 && stateValue > 1 && !opt.WORKSPACE_MODE) + bgValue = stateValue - 1; + else + bgValue = stateValue; + } + + let blurEffect = bgManager.backgroundActor.get_effect('blur'); + if (!blurEffect) { + blurEffect = new Shell.BlurEffect({ + brightness: 1, + sigma: 0, + mode: Shell.BlurMode.ACTOR, + }); + bgManager.backgroundActor.add_effect_with_name('blur', blurEffect); + } + + const searchActive = Main.overview._overview.controls._searchController.searchActive; + if (searchActive) + BRIGHTNESS = opt.SEARCH_BG_BRIGHTNESS; + + bgManager.backgroundActor.content.vignette_sharpness = VIGNETTE; + bgManager.backgroundActor.content.brightness = BRIGHTNESS; + + let vignetteInit, brightnessInit;// , sigmaInit; + if (opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG) { + vignetteInit = VIGNETTE; + brightnessInit = BRIGHTNESS; + // sigmaInit = opt.OVERVIEW_BG_BLUR_SIGMA; + } else { + vignetteInit = 0; + brightnessInit = 1; + // sigmaInit = 0; + } + + if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { + bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, bgValue); + bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, bgValue); + } else { + bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, Math.min(stateValue, 1)); + bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, Math.min(stateValue, 1)); + } + + if (opt.OVERVIEW_BG_BLUR_SIGMA || opt.APP_GRID_BG_BLUR_SIGMA) { + // reduce number of steps of blur transition to improve performance + const step = opt.SMOOTH_BLUR_TRANSITIONS ? 0.05 : 0.2; + const progress = stateValue - (stateValue % step); + if (opt.SHOW_WS_PREVIEW_BG && stateValue < 1 && !searchActive) { // no need to animate transition, unless appGrid state is involved, static bg is covered by the ws preview bg + if (blurEffect.sigma !== opt.OVERVIEW_BG_BLUR_SIGMA) + blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; + } else if (stateValue < 1 && !searchActive && !(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue < 1 && !searchActive && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && blurEffect.sigma)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue > 1 && !searchActive && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE && finalState === 1)) { + const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress % 1)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if ((stateValue > 1 && bgManager._primary) || searchActive) { + const sigma = Math.round(Util.lerp(opt.OVERVIEW_BG_BLUR_SIGMA, opt.APP_GRID_BG_BLUR_SIGMA, progress % 1)); + if (sigma !== blurEffect.sigma) + blurEffect.sigma = sigma; + } else if (stateValue === 1 && !(opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE)) { + blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; + } else if (stateValue === 0 || (stateValue === 1 && (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE))) { + blurEffect.sigma = 0; + } + } + } + }, }; const ControlsManagerLayoutVertical = { - _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, searchHeight, startY) { + _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsWidth, thumbnailsHeight, searchHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const workspaceBox = box.copy(); let [width, height] = workspaceBox.get_size(); - // const { x1: startX/* y1: startY*/ } = workAreaBox; const { spacing } = this; - // const { expandFraction } = this._workspacesThumbnails; const dash = Main.overview.dash; // including Dash to Dock and clones properties for compatibility - if (_Util.dashIsDashToDock()) { + if (Me.Util.dashIsDashToDock()) { // Dash to Dock also always affects workAreaBox Main.layoutManager._trackedActors.forEach(actor => { if (actor.affectsStruts && actor.actor.width === dash.width) { @@ -678,7 +956,7 @@ const ControlsManagerLayoutVertical = { case ControlsState.APP_GRID: if (opt.WS_ANIMATION && opt.SHOW_WS_TMB && state === ControlsState.APP_GRID) { workspaceBox.set_origin(...this._workspacesThumbnails.get_position()); - workspaceBox.set_size(...this._workspacesThumbnails.get_size()); + workspaceBox.set_size(thumbnailsWidth, thumbnailsHeight); } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { if (opt.START_Y_OFFSET) { let [x, y] = workAreaBox.get_origin(); @@ -693,7 +971,7 @@ const ControlsManagerLayoutVertical = { height = opt.PANEL_POSITION_TOP ? height : height - Main.panel.height; searchHeight = opt.SHOW_SEARCH_ENTRY ? searchHeight : 0; wWidth = width - - (opt.DASH_VERTICAL ? dash.width : 0) - + (opt.DASH_VERTICAL ? dashWidth : 0) - thumbnailsWidth - 4 * spacing; wHeight = height - @@ -739,7 +1017,7 @@ const ControlsManagerLayoutVertical = { } const wsBoxX = /* startX + */xOffset; - wsBoxY = Math.round(startY + yOffset); + wsBoxY = startY + yOffset; workspaceBox.set_origin(Math.round(wsBoxX), Math.round(wsBoxY)); workspaceBox.set_size(Math.round(wWidth), Math.round(wHeight)); } @@ -749,24 +1027,27 @@ const ControlsManagerLayoutVertical = { }, _getAppDisplayBoxForState(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsWidth, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const [width] = box.get_size(); const { x1: startX } = workAreaBox; // const { y1: startY } = workAreaBox; let height = workAreaBox.get_height(); const appDisplayBox = new Clutter.ActorBox(); const { spacing } = this; - searchHeight = opt.SHOW_SEARCH_ENTRY ? searchHeight : 0; const xOffsetL = (opt.WS_TMB_LEFT ? thumbnailsWidth : 0) + (opt.DASH_LEFT ? dashWidth : 0); const xOffsetR = (opt.WS_TMB_RIGHT ? thumbnailsWidth : 0) + (opt.DASH_RIGHT ? dashWidth : 0); const yOffsetT = (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0); const yOffsetB = opt.DASH_BOTTOM ? dashHeight : 0; - const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 4 * spacing : width - xOffsetL - xOffsetR - 4 * spacing; - const adHeight = height - yOffsetT - yOffsetB - 4 * spacing; + const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 2 * spacing : width - xOffsetL - xOffsetR - 2 * spacing; + const adHeight = height - yOffsetT - yOffsetB; const appDisplayX = opt.CENTER_APP_GRID ? (width - adWidth) / 2 : xOffsetL + 2 * spacing; - const appDisplayY = startY + yOffsetT + 2 * spacing; + const appDisplayY = startY + yOffsetT; switch (state) { case ControlsState.HIDDEN: @@ -774,43 +1055,42 @@ const ControlsManagerLayoutVertical = { // 1 - left, 2 - right, 3 - bottom, 5 - top switch (opt.APP_GRID_ANIMATION) { case 0: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; case 1: - appDisplayBox.set_origin(startX + width, appDisplayY); + appDisplayBox.set_origin(Math.round(startX + width), Math.round(appDisplayY)); break; case 2: - appDisplayBox.set_origin(startX - adWidth, appDisplayY); + appDisplayBox.set_origin(Math.round(startX - adWidth), Math.round(appDisplayY)); break; case 3: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y2); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y2)); break; case 5: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y1 - adHeight); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y1 - adHeight)); break; } break; case ControlsState.APP_GRID: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; } - appDisplayBox.set_size(adWidth, adHeight); + appDisplayBox.set_size(Math.round(adWidth), Math.round(adHeight)); return appDisplayBox; }, vfunc_allocate(container, box) { const childBox = new Clutter.ActorBox(); - + const transitionParams = this._stateAdjustment.getStateTransitionParams(); const { spacing } = this; - + const halfSpacing = spacing / 2; const monitor = Main.layoutManager.findMonitorForActor(this._container); const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index); const startX = workArea.x - monitor.x; // if PANEL_OVERVIEW_ONLY, the affectStruts property is set to false to avoid stuttering // therefore we need to add panel height to startY let startY = workArea.y - monitor.y + opt.START_Y_OFFSET; - const workAreaBox = new Clutter.ActorBox(); workAreaBox.set_origin(startX, startY); workAreaBox.set_size(workArea.width, workArea.height); @@ -823,15 +1103,15 @@ const ControlsManagerLayoutVertical = { let availableHeight = height; // Dash - const maxDashHeight = Math.round(box.get_height() * DASH_MAX_SIZE_RATIO); + const maxDashHeight = box.get_height() * DASH_MAX_SIZE_RATIO; const maxDashWidth = maxDashHeight * 0.8; let dashHeight = 0; let dashWidth = 0; // dash cloud be overridden by the Dash to Dock clone const dash = Main.overview.dash; - if (_Util.dashIsDashToDock()) { - // if Dash to Dock replaced the default dash and its inteli-hide id disabled we need to compensate for affected startY + if (Me.Util.dashIsDashToDock()) { + // if Dash to Dock replaced the default dash and its inteli-hide is disabled we need to compensate for affected startY if (!Main.overview.dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled) { if (Main.panel.y === monitor.y) startY = Main.panel.height + spacing; @@ -857,61 +1137,59 @@ const ControlsManagerLayoutVertical = { } } - const transitionParams = this._stateAdjustment.getStateTransitionParams(); - // Workspace Thumbnails let wsTmbWidth = 0; let wsTmbHeight = 0; - if (this._workspacesThumbnails.visible) { - // const { expandFraction } = this._workspacesThumbnails; + let maxWsTmbScale = opt.MAX_THUMBNAIL_SCALE; + if (opt.SHOW_WS_TMB) { const dashHeightReservation = !opt.WS_TMB_FULL && !opt.DASH_VERTICAL ? dashHeight : 0; - let maxScale = opt.MAX_THUMBNAIL_SCALE; - if (!opt.MAX_THUMBNAIL_SCALE_STABLE) { + const searchActive = this._searchController.searchActive; + if (!opt.MAX_THUMBNAIL_SCALE_STABLE && !searchActive) { const initState = transitionParams.initialState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE; const finalState = transitionParams.finalState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE; - maxScale = Util.lerp(initState, finalState, transitionParams.progress); + maxWsTmbScale = Util.lerp(initState, finalState, transitionParams.progress); } - wsTmbWidth = width * maxScale; + wsTmbWidth = width * maxWsTmbScale; let totalTmbSpacing; - [totalTmbSpacing, wsTmbHeight] = this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth); + [totalTmbSpacing, wsTmbHeight] = this._workspacesThumbnails.get_preferred_height(wsTmbWidth); wsTmbHeight += totalTmbSpacing; - const wsTmbHeightMax = height - dashHeightReservation; + const wsTmbHeightMax = opt.WS_TMB_FULL + ? height - spacing + : height - dashHeightReservation - 2 * spacing; if (wsTmbHeight > wsTmbHeightMax) { wsTmbHeight = wsTmbHeightMax; - wsTmbWidth = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight)[1]; + wsTmbWidth = Math.round(this._workspacesThumbnails.get_preferred_width(wsTmbHeight)[1]); } let wsTmbX; if (opt.WS_TMB_RIGHT) - wsTmbX = Math.round(startX + width - (opt.DASH_RIGHT ? dashWidth : 0) - wsTmbWidth - spacing / 2); + wsTmbX = Math.round(startX + width - (opt.DASH_RIGHT ? dashWidth : 0) - wsTmbWidth /* - halfSpacing*/); // this halfSpacing is a part od dash style else - wsTmbX = Math.round((opt.DASH_LEFT ? dashWidth : 0) + spacing / 2); - + wsTmbX = Math.round(opt.DASH_LEFT ? dashWidth : 0/* + halfSpacing*/); // this halfSpacing is a part od dash style let wstOffset = (height - wsTmbHeight - (opt.DASH_VERTICAL ? 0 : dashHeightReservation)) / 2; - wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2); + wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - halfSpacing); let wsTmbY = Math.round(startY + (dashHeightReservation && opt.DASH_TOP ? dashHeight : 0) + wstOffset); childBox.set_origin(wsTmbX, wsTmbY); - childBox.set_size(Math.round(wsTmbWidth), Math.round(wsTmbHeight)); + childBox.set_size(Math.max(wsTmbWidth, 1), Math.max(wsTmbHeight, 1)); this._workspacesThumbnails.allocate(childBox); } - if (this._dash.visible) { const wMaxWidth = width - spacing - wsTmbWidth - 2 * spacing - (opt.DASH_VERTICAL ? dashWidth + spacing : 0); if (opt.WS_TMB_FULL && !opt.DASH_VERTICAL) { this._dash.setMaxSize(wMaxWidth, maxDashHeight); [, dashHeight] = this._dash.get_preferred_height(wMaxWidth); [, dashWidth] = this._dash.get_preferred_width(dashHeight); - dashHeight = Math.round(Math.min(dashHeight, maxDashHeight)); - dashWidth = Math.round(Math.min(dashWidth, wMaxWidth)); + dashHeight = Math.min(dashHeight, maxDashHeight); + dashWidth = Math.min(dashWidth, wMaxWidth); } let dashX, dashY, offset; @@ -927,7 +1205,7 @@ const ControlsManagerLayoutVertical = { if (!opt.DASH_VERTICAL) { offset = (width - ((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter ? wsTmbWidth : 0) - dashWidth) / 2; - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashX = offset; if ((opt.WS_TMB_FULL || opt.CENTER_DASH_WS) && !this._xAlignCenter) { @@ -944,11 +1222,11 @@ const ControlsManagerLayoutVertical = { } } else { offset = (height - dashHeight) / 2; - dashY = startY + (offset - opt.DASH_POSITION_ADJUSTMENT * offset); + dashY = startY + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing)); } childBox.set_origin(Math.round(startX + dashX), Math.round(dashY)); - childBox.set_size(dashWidth, dashHeight); + childBox.set_size(Math.round(dashWidth), Math.round(dashHeight)); this._dash.allocate(childBox); } @@ -957,7 +1235,7 @@ const ControlsManagerLayoutVertical = { let [searchHeight] = this._searchEntry.get_preferred_height(width - wsTmbWidth); // Workspaces - let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, searchHeight, startY]; + let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, wsTmbHeight, searchHeight, startY]; // Update cached boxes for (const state of Object.values(ControlsState)) { @@ -983,21 +1261,20 @@ const ControlsManagerLayoutVertical = { // Y position under top Dash let searchEntryX, searchEntryY; if (opt.DASH_TOP) - searchEntryY = startY + dashHeight - spacing; + searchEntryY = startY + dashHeight; else searchEntryY = startY; - searchEntryX = searchXoffset; let searchWidth = width - 2 * spacing - wsTmbWidth - (opt.DASH_VERTICAL ? dashWidth : 0); // xAlignCenter is given by wsBox searchWidth = this._xAlignCenter ? width - 2 * (wsTmbWidth + spacing) : searchWidth; if (opt.CENTER_SEARCH_VIEW) { - childBox.set_origin(0, searchEntryY); - childBox.set_size(width, searchHeight); + childBox.set_origin(0, Math.round(searchEntryY)); + childBox.set_size(Math.round(width), Math.round(searchHeight)); } else { - childBox.set_origin(this._xAlignCenter ? 0 : searchEntryX, searchEntryY); - childBox.set_size(this._xAlignCenter ? width : searchWidth - spacing, searchHeight); + childBox.set_origin(Math.round(this._xAlignCenter ? 0 : searchEntryX), Math.round(searchEntryY)); + childBox.set_size(Math.round(this._xAlignCenter ? width : searchWidth - spacing), Math.round(searchHeight)); } this._searchEntry.allocate(childBox); @@ -1005,7 +1282,11 @@ const ControlsManagerLayoutVertical = { availableHeight -= searchHeight + spacing; // if (this._appDisplay.visible)... ? Can cause problems - params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbWidth, startY]; // send startY, can be corrected + // Calculate appDisplay always for AppGrid state WsTmb scale + let wsTmbWidthAppGrid = opt.MAX_THUMBNAIL_SCALE_APPGRID > 0 + ? wsTmbWidth / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE_APPGRID + : wsTmbWidth / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE; + params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbWidthAppGrid, startY]; // send startY, can be corrected let appDisplayBox; if (!transitionParams.transitioning) { appDisplayBox = @@ -1024,12 +1305,12 @@ const ControlsManagerLayoutVertical = { if (opt.CENTER_SEARCH_VIEW) { const dashW = (opt.DASH_VERTICAL ? dashWidth : 0) + spacing; searchWidth = width - 2 * wsTmbWidth - 2 * dashW; - childBox.set_origin(wsTmbWidth + dashW, startY + (opt.DASH_TOP ? dashHeight : spacing) + searchHeight); + childBox.set_origin(Math.round(wsTmbWidth + dashW), Math.round(startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + searchHeight)); } else { - childBox.set_origin(this._xAlignCenter ? wsTmbWidth + spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight : spacing) + searchHeight); + childBox.set_origin(Math.round(this._xAlignCenter ? wsTmbWidth + spacing : searchXoffset), Math.round(startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + searchHeight)); } - childBox.set_size(searchWidth, availableHeight); + childBox.set_size(Math.round(searchWidth), Math.round(availableHeight)); this._searchController.allocate(childBox); this._runPostAllocation(); @@ -1037,16 +1318,18 @@ const ControlsManagerLayoutVertical = { }; const ControlsManagerLayoutHorizontal = { - _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailsHeight, searchHeight, startY) { + _computeWorkspacesBoxForState(state, box, workAreaBox, dashWidth, dashHeight, thumbnailWidth, thumbnailsHeight, searchHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const workspaceBox = box.copy(); let [width, height] = workspaceBox.get_size(); - // let { x1: startX/* , y1: startY*/ } = workAreaBox; const { spacing } = this; - // const { expandFraction } = this._workspacesThumbnails; const dash = Main.overview.dash; // including Dash to Dock and clones properties for compatibility - if (_Util.dashIsDashToDock()) { + if (Me.Util.dashIsDashToDock()) { // Dash to Dock always affects workAreaBox Main.layoutManager._trackedActors.forEach(actor => { if (actor.affectsStruts && actor.actor.width === dash.width) { @@ -1084,7 +1367,7 @@ const ControlsManagerLayoutHorizontal = { case ControlsState.APP_GRID: if (opt.WS_ANIMATION && opt.SHOW_WS_TMB && state === ControlsState.APP_GRID) { workspaceBox.set_origin(...this._workspacesThumbnails.get_position()); - workspaceBox.set_size(...this._workspacesThumbnails.get_size()); + workspaceBox.set_size(thumbnailWidth, thumbnailsHeight); } else if (opt.OVERVIEW_MODE2 && !opt.WORKSPACE_MODE) { if (opt.START_Y_OFFSET) { let [x, y] = workAreaBox.get_origin(); @@ -1146,7 +1429,7 @@ const ControlsManagerLayoutHorizontal = { } wsBoxX = /* startX + */xOffset; - wsBoxY = Math.round(startY + yOffset); + wsBoxY = startY + yOffset; workspaceBox.set_origin(Math.round(wsBoxX), Math.round(wsBoxY)); workspaceBox.set_size(Math.round(wWidth), Math.round(wHeight)); } @@ -1156,6 +1439,10 @@ const ControlsManagerLayoutHorizontal = { }, _getAppDisplayBoxForState(state, box, workAreaBox, searchHeight, dashWidth, dashHeight, thumbnailsHeight, startY) { + // in case the function is called from the DtD + if (startY === undefined) { + workAreaBox = box; + } const [width] = box.get_size(); const { x1: startX } = workAreaBox; // const { y1: startY } = workAreaBox; @@ -1163,16 +1450,16 @@ const ControlsManagerLayoutHorizontal = { const appDisplayBox = new Clutter.ActorBox(); const { spacing } = this; - const yOffsetT = (opt.WS_TMB_TOP ? thumbnailsHeight : 0) + (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0) + 2 * spacing; - const yOffsetB = (opt.WS_TMB_BOTTOM ? thumbnailsHeight : 0) + (opt.DASH_BOTTOM ? dashHeight : 0); + const yOffsetT = (opt.WS_TMB_TOP ? thumbnailsHeight + spacing : 0) + (opt.DASH_TOP ? dashHeight : 0) + (opt.SHOW_SEARCH_ENTRY ? searchHeight : 0); + const yOffsetB = (opt.WS_TMB_BOTTOM ? thumbnailsHeight + spacing : 0) + (opt.DASH_BOTTOM ? dashHeight : 0); const xOffsetL = opt.DASH_LEFT ? dashWidth : 0; const xOffsetR = opt.DASH_RIGHT ? dashWidth : 0; - const hSpacing = xOffsetL + xOffsetR ? 2 * spacing : 0; + const hSpacing = xOffsetL + xOffsetR ? spacing : 0; const adWidth = opt.CENTER_APP_GRID ? width - 2 * Math.max(xOffsetL, xOffsetR) - 2 * hSpacing : width - xOffsetL - xOffsetR - 2 * hSpacing; - const adHeight = height - yOffsetT - yOffsetB - 4 * spacing; + const adHeight = height - yOffsetT - yOffsetB; const appDisplayX = opt.CENTER_APP_GRID ? (width - adWidth) / 2 : xOffsetL + hSpacing; - const appDisplayY = startY + yOffsetT + hSpacing; + const appDisplayY = startY + yOffsetT; switch (state) { case ControlsState.HIDDEN: @@ -1180,36 +1467,36 @@ const ControlsManagerLayoutHorizontal = { // 1 - left, 2 - right, 3 - bottom, 5 - top switch (opt.APP_GRID_ANIMATION) { case 0: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; case 1: - appDisplayBox.set_origin(startX + width, appDisplayY); + appDisplayBox.set_origin(Math.round(startX + width), Math.round(appDisplayY)); break; case 2: - appDisplayBox.set_origin(startX - adWidth, appDisplayY); + appDisplayBox.set_origin(Math.round(startX - adWidth), Math.round(appDisplayY)); break; case 3: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y2); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y2)); break; case 5: - appDisplayBox.set_origin(appDisplayX, workAreaBox.y1 - adHeight); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(workAreaBox.y1 - adHeight)); break; } break; case ControlsState.APP_GRID: - appDisplayBox.set_origin(appDisplayX, appDisplayY); + appDisplayBox.set_origin(Math.round(appDisplayX), Math.round(appDisplayY)); break; } - appDisplayBox.set_size(adWidth, adHeight); + appDisplayBox.set_size(Math.round(adWidth), Math.round(adHeight)); return appDisplayBox; }, vfunc_allocate(container, box) { + const transitionParams = this._stateAdjustment.getStateTransitionParams(); const childBox = new Clutter.ActorBox(); - const { spacing } = this; - + const halfSpacing = spacing / 2; const monitor = Main.layoutManager.findMonitorForActor(this._container); const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index); const startX = workArea.x - monitor.x; @@ -1228,14 +1515,14 @@ const ControlsManagerLayoutHorizontal = { let availableHeight = height; // Dash - const maxDashHeight = Math.round(box.get_height() * DASH_MAX_SIZE_RATIO); + const maxDashHeight = box.get_height() * DASH_MAX_SIZE_RATIO; const maxDashWidth = maxDashHeight * 0.8; let dashHeight = 0; let dashWidth = 0; // dash cloud be overridden by the Dash to Dock clone const dash = Main.overview.dash; - if (_Util.dashIsDashToDock()) { + if (Me.Util.dashIsDashToDock()) { // if Dash to Dock replaced the default dash and its inteli-hide is disabled we need to compensate for affected startY if (!Main.overview.dash.get_parent()?.get_parent()?.get_parent()?._intellihideIsEnabled) { // if (Main.panel.y === monitor.y) @@ -1265,68 +1552,61 @@ const ControlsManagerLayoutHorizontal = { let [searchHeight] = this._searchEntry.get_preferred_height(width); - const transitionParams = this._stateAdjustment.getStateTransitionParams(); - // Workspace Thumbnails let wsTmbWidth = 0; let wsTmbHeight = 0; - if (this._workspacesThumbnails.visible) { - // const { expandFraction } = this._workspacesThumbnails; + let maxWsTmbScale = opt.MAX_THUMBNAIL_SCALE; + if (opt.SHOW_WS_TMB) { const dashWidthReservation = !opt.WS_TMB_FULL && opt.DASH_VERTICAL ? dashWidth : 0; - let maxScale = opt.MAX_THUMBNAIL_SCALE; - if (!opt.MAX_THUMBNAIL_SCALE_STABLE) { + const searchActive = this._searchController.searchActive; + if (!opt.MAX_THUMBNAIL_SCALE_STABLE && !searchActive) { const initState = transitionParams.initialState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE; const finalState = transitionParams.finalState === ControlsState.APP_GRID ? opt.MAX_THUMBNAIL_SCALE_APPGRID : opt.MAX_THUMBNAIL_SCALE; - maxScale = Util.lerp(initState, finalState, transitionParams.progress); + maxWsTmbScale = Util.lerp(initState, finalState, transitionParams.progress); } - wsTmbHeight = height * maxScale; + wsTmbHeight = Math.round(height * maxWsTmbScale); let totalTmbSpacing; - [totalTmbSpacing, wsTmbWidth] = this._workspacesThumbnails.get_preferred_custom_width(wsTmbHeight); + [totalTmbSpacing, wsTmbWidth] = this._workspacesThumbnails.get_preferred_width(wsTmbHeight); wsTmbWidth += totalTmbSpacing; const wsTmbWidthMax = opt.WS_TMB_FULL - ? width - : width - (opt.DASH_VERTICAL ? 0 : dashWidthReservation); + ? width - spacing + : width - dashWidthReservation - 2 * spacing; if (wsTmbWidth > wsTmbWidthMax) { wsTmbWidth = wsTmbWidthMax; - wsTmbHeight = this._workspacesThumbnails.get_preferred_custom_height(wsTmbWidth)[1]; + wsTmbHeight = Math.round(this._workspacesThumbnails.get_preferred_height(wsTmbWidth)[1]); } let wsTmbY; if (opt.WS_TMB_TOP) - wsTmbY = Math.round(startY + /* searchHeight + */(opt.DASH_TOP ? dashHeight : spacing / 2)); + wsTmbY = Math.round(startY + (opt.DASH_TOP ? dashHeight : halfSpacing)); else - wsTmbY = Math.round(startY + height - (opt.DASH_BOTTOM ? dashHeight : 0) - wsTmbHeight); + wsTmbY = Math.round(startY + height - (opt.DASH_BOTTOM ? dashHeight : halfSpacing) - wsTmbHeight); - let wstOffset = (width - wsTmbWidth) / 2; - wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * (wstOffset - spacing / 2); - let wsTmbX = Math.round(Math.clamp( - startX + wstOffset, - startX + (opt.DASH_LEFT ? dashWidthReservation : 0), - width - wsTmbWidth - startX - (opt.DASH_RIGHT ? dashWidthReservation : 0) - )); + let wstOffset = (width - wsTmbWidth - dashWidthReservation) / 2; + wstOffset -= opt.WS_TMB_POSITION_ADJUSTMENT * wstOffset; + let wsTmbX = Math.round(startX + (opt.DASH_LEFT ? dashWidthReservation : 0) + wstOffset); childBox.set_origin(wsTmbX, wsTmbY); - childBox.set_size(Math.round(wsTmbWidth), Math.round(wsTmbHeight)); + childBox.set_size(Math.max(wsTmbWidth, 1), Math.max(wsTmbHeight, 1)); this._workspacesThumbnails.allocate(childBox); availableHeight -= wsTmbHeight + spacing; } - if (this._dash.visible) { if (opt.WS_TMB_FULL && opt.DASH_VERTICAL) { const wMaxHeight = height - spacing - wsTmbHeight; this._dash.setMaxSize(maxDashWidth, wMaxHeight); [, dashWidth] = this._dash.get_preferred_width(wMaxHeight); [, dashHeight] = this._dash.get_preferred_height(dashWidth); - dashWidth = Math.round(Math.min(dashWidth, maxDashWidth)); - dashHeight = Math.round(Math.min(dashHeight, wMaxHeight)); + dashWidth = Math.min(dashWidth, maxDashWidth); + dashHeight = Math.min(dashHeight, wMaxHeight); } let dashX, dashY, offset; @@ -1339,36 +1619,35 @@ const ControlsManagerLayoutHorizontal = { else dashY = startY + height - dashHeight; - if (opt.DASH_VERTICAL) { if (opt.WS_TMB_FULL) { offset = (height - dashHeight - wsTmbHeight) / 2; if (opt.WS_TMB_TOP) { - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset + wsTmbHeight; } else { - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset; } } else { offset = (height - dashHeight) / 2; - offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - spacing / 2); + offset -= opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing); dashY = startY + offset; } } else { offset = (width - dashWidth) / 2; - dashX = startX + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - spacing)); + dashX = startX + (offset - opt.DASH_POSITION_ADJUSTMENT * (offset - halfSpacing)); } childBox.set_origin(Math.round(startX + dashX), Math.round(dashY)); - childBox.set_size(dashWidth, dashHeight); + childBox.set_size(Math.round(dashWidth), Math.round(dashHeight)); this._dash.allocate(childBox); } availableHeight -= opt.DASH_VERTICAL ? 0 : dashHeight; // Workspaces - let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbHeight, searchHeight, startY]; + let params = [box, workAreaBox, dashWidth, dashHeight, wsTmbWidth, wsTmbHeight, searchHeight, startY]; // Update cached boxes for (const state of Object.values(ControlsState)) { @@ -1394,7 +1673,7 @@ const ControlsManagerLayoutHorizontal = { // Y position under top Dash let searchEntryX, searchEntryY; if (opt.DASH_TOP) - searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight : 0) + dashHeight - spacing; + searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight : 0) + dashHeight; else searchEntryY = startY + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0); @@ -1404,11 +1683,11 @@ const ControlsManagerLayoutHorizontal = { searchWidth = this._xAlignCenter ? width : searchWidth; if (opt.CENTER_SEARCH_VIEW) { - childBox.set_origin(0, searchEntryY); - childBox.set_size(width, searchHeight); + childBox.set_origin(0, Math.round(searchEntryY)); + childBox.set_size(width, Math.round(searchHeight)); } else { - childBox.set_origin(this._xAlignCenter ? 0 : searchEntryX, searchEntryY); - childBox.set_size(this._xAlignCenter ? width : searchWidth - spacing, searchHeight); + childBox.set_origin(Math.round(this._xAlignCenter ? 0 : searchEntryX), Math.round(searchEntryY)); + childBox.set_size(Math.round(this._xAlignCenter ? width : searchWidth - spacing), Math.round(searchHeight)); } this._searchEntry.allocate(childBox); @@ -1416,7 +1695,11 @@ const ControlsManagerLayoutHorizontal = { availableHeight -= searchHeight + spacing; // if (this._appDisplay.visible)... ? Can cause problems - params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbHeight, startY]; + // Calculate appDisplay always for AppGrid state WsTmb scale + let wsTmbHeightAppGrid = opt.MAX_THUMBNAIL_SCALE_APPGRID > 0 + ? wsTmbHeight / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE_APPGRID + : wsTmbHeight / maxWsTmbScale * opt.MAX_THUMBNAIL_SCALE; + params = [box, workAreaBox, searchHeight, dashWidth, dashHeight, wsTmbHeightAppGrid, startY]; let appDisplayBox; if (!transitionParams.transitioning) { appDisplayBox = @@ -1435,12 +1718,12 @@ const ControlsManagerLayoutHorizontal = { if (opt.CENTER_SEARCH_VIEW) { const dashW = (opt.DASH_VERTICAL ? dashWidth : 0) + spacing; searchWidth = width - 2 * dashW; - childBox.set_origin(dashW, startY + (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); + childBox.set_origin(Math.round(dashW), Math.round(startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight)); } else { - childBox.set_origin(this._xAlignCenter ? spacing : searchXoffset, startY + (opt.DASH_TOP ? dashHeight : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight); + childBox.set_origin(Math.round(this._xAlignCenter ? spacing : searchXoffset), Math.round(startY + (opt.DASH_TOP ? dashHeight + spacing : spacing) + (opt.WS_TMB_TOP ? wsTmbHeight + spacing : 0) + searchHeight)); } - childBox.set_size(searchWidth, availableHeight); + childBox.set_size(Math.round(searchWidth), Math.round(availableHeight)); this._searchController.allocate(childBox); this._runPostAllocation(); @@ -1462,3 +1745,20 @@ function _getFitModeForState(state) { return FitMode.SINGLE; } } + +const LayoutManager = { + _startupAnimation() { + if (Me.Util.dashIsDashToDock() && !Meta.is_restart()) { + // DtD breaks overview on startup + // Skip animation to hide the mess + this._startupAnimationComplete(); + Main.overview._overview.controls._finishStartupSequence(); + } else if (Meta.is_restart()) { + this._startupAnimationComplete(); + } else if (Main.sessionMode.isGreeter) { + this._startupAnimationGreeter(); + } else { + this._startupAnimationSession(); + } + }, +}; diff --git a/extensions/45/vertical-workspaces/lib/panel.js b/extensions/45/vertical-workspaces/lib/panel.js new file mode 100644 index 0000000..a148612 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/panel.js @@ -0,0 +1,253 @@ +/** + * V-Shell (Vertical Workspaces) + * panel.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Overview from 'resource:///org/gnome/shell/ui/overview.js'; + +let Me; +let opt; + +const ANIMATION_TIME = Overview.ANIMATION_TIME; + +export const PanelModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + + this._showingOverviewConId = 0; + this._hidingOverviewConId = 0; + this._styleChangedConId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('panelModule'); + const conflict = Me.Util.getEnabledExtensions('dash-to-panel').length || + Me.Util.getEnabledExtensions('hidetopbar').length; + + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "Panel" module disabled due to potential conflict with another extension`); + + reset = reset || !this.moduleEnabled || conflict || Main.sessionMode.isLocked; + + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' PanelModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + const panelBox = Main.layoutManager.panelBox; + + this._setPanelPosition(); + this._updateStyleChangedConnection(); + + if (opt.PANEL_MODE === 0) { + this._updateOverviewConnection(true); + this._reparentPanel(false); + panelBox.translation_y = 0; + Main.panel.opacity = 255; + this._setPanelStructs(true); + } else if (opt.PANEL_MODE === 1) { + if (opt.SHOW_WS_PREVIEW_BG) { + this._reparentPanel(true); + if (opt.OVERVIEW_MODE2) { + // in OM2 if the panel has been moved to the overviewGroup move panel above all + Main.layoutManager.overviewGroup.set_child_above_sibling(panelBox, null); + this._updateOverviewConnection(); + } else { + // otherwise move the panel below overviewGroup so it can get below workspacesDisplay + Main.layoutManager.overviewGroup.set_child_below_sibling(panelBox, Main.overview._overview); + this._updateOverviewConnection(true); + } + this._showPanel(true); + } else { + // if ws preview bg is disabled, panel can stay in uiGroup + this._reparentPanel(false); + this._showPanel(false); + this._updateOverviewConnection(); + } + // _connectPanel(); + } else if (opt.PANEL_MODE === 2) { + this._updateOverviewConnection(true); + this._reparentPanel(false); + this._showPanel(false); + // _connectPanel(); + } + this._setPanelStructs(opt.PANEL_MODE === 0); + Main.layoutManager._updateHotCorners(); + + this._overrides.addOverride('ActivitiesButton', Main.panel.statusArea.activities, ActivitiesButton); + + console.debug(' PanelModule - Activated'); + } + + _disableModule() { + const reset = true; + this._setPanelPosition(reset); + this._updateOverviewConnection(reset); + this._reparentPanel(false); + + this._updateStyleChangedConnection(reset); + + const panelBox = Main.layoutManager.panelBox; + panelBox.translation_y = 0; + Main.panel.opacity = 255; + this._setPanelStructs(true); + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' PanelModule - Disabled'); + } + + _setPanelPosition(reset = false) { + const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor()); + const panelBox = Main.layoutManager.panelBox; + const panelHeight = Main.panel.height; // panelBox height can be 0 after shell start + + if (opt.PANEL_POSITION_TOP || reset) + panelBox.set_position(geometry.x, geometry.y); + else + panelBox.set_position(geometry.x, geometry.y + geometry.height - panelHeight); + } + + _updateStyleChangedConnection(reset = false) { + if (reset) { + if (this._styleChangedConId) { + Main.panel.disconnect(this._styleChangedConId); + this._styleChangedConId = 0; + } + } else if (!this._styleChangedConId) { + this._styleChangedConId = Main.panel.connect('style-changed', () => { + if (opt.PANEL_MODE === 1 && !opt.OVERVIEW_MODE2) + Main.panel.add_style_pseudo_class('overview'); + else if (opt.OVERVIEW_MODE2) + Main.panel.remove_style_pseudo_class('overview'); + }); + } + } + + _updateOverviewConnection(reset = false) { + if (reset) { + if (this._hidingOverviewConId) { + Main.overview.disconnect(this._hidingOverviewConId); + this._hidingOverviewConId = 0; + } + if (this._showingOverviewConId) { + Main.overview.disconnect(this._showingOverviewConId); + this._showingOverviewConId = 0; + } + } else { + if (!this._hidingOverviewConId) { + this._hidingOverviewConId = Main.overview.connect('hiding', () => { + if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2) + this._showPanel(false); + }); + } + if (!this._showingOverviewConId) { + this._showingOverviewConId = Main.overview.connect('showing', () => { + if (Main.layoutManager._startingUp) + return; + if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2 || Main.layoutManager.panelBox.translation_y) + this._showPanel(true); + }); + } + } + } + + _reparentPanel(reparent = false) { + const panel = Main.layoutManager.panelBox; + if (reparent && panel.get_parent() === Main.layoutManager.uiGroup) { + Main.layoutManager.uiGroup.remove_child(panel); + Main.layoutManager.overviewGroup.add_child(panel); + } else if (!reparent && panel.get_parent() === Main.layoutManager.overviewGroup) { + Main.layoutManager.overviewGroup.remove_child(panel); + // return the panel at default position, panel shouldn't cover objects that should be above + Main.layoutManager.uiGroup.insert_child_at_index(panel, 4); + } + } + + _setPanelStructs(state) { + Main.layoutManager._trackedActors.forEach(a => { + if (a.actor === Main.layoutManager.panelBox) + a.affectsStruts = state; + }); + + // workaround to force maximized windows to resize after removing affectsStruts + // simulation of minimal swipe gesture to the opposite direction + // todo - needs better solution!!!!!!!!!!! + // const direction = _getAppGridAnimationDirection() === 2 ? 1 : -1; + // Main.overview._swipeTracker._beginTouchSwipe(null, global.get_current_time(), 1, 1); + // Main.overview._swipeTracker._updateGesture(null, global.get_current_time(), direction, 1); + // GLib.timeout_add(0, 50, () => Main.overview._swipeTracker._endGesture(global.get_current_time(), 1, true));*/ + } + + _showPanel(show = true) { + if (show) { + Main.panel.opacity = 255; + Main.layoutManager.panelBox.ease({ + duration: ANIMATION_TIME, + translation_y: 0, + onComplete: () => { + this._setPanelStructs(opt.PANEL_MODE === 0); + }, + }); + } else { + const panelHeight = Main.panel.height; + Main.layoutManager.panelBox.ease({ + duration: ANIMATION_TIME, + translation_y: opt.PANEL_POSITION_TOP ? -panelHeight + 1 : panelHeight - 1, + onComplete: () => { + Main.panel.opacity = 0; + this._setPanelStructs(opt.PANEL_MODE === 0); + }, + }); + } + } +}; + +const ActivitiesButton = { + vfunc_event(event) { + if (event.type() === Clutter.EventType.TOUCH_END || + event.type() === Clutter.EventType.BUTTON_RELEASE) { + if (Main.overview.shouldToggleByCornerOrButton()) { + if (event.get_button() === Clutter.BUTTON_SECONDARY && !Main.overview.dash.showAppsButton.checked) { + Main.overview.show(2); + Main.overview.dash.showAppsButton.checked = true; + } else { + Main.overview.toggle(); + } + } + } + + return Main.wm.handleWorkspaceScroll(event); + }, +}; diff --git a/extensions/45/vertical-workspaces/lib/recentFilesSearchProvider.js b/extensions/45/vertical-workspaces/lib/recentFilesSearchProvider.js new file mode 100644 index 0000000..6dfec0a --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/recentFilesSearchProvider.js @@ -0,0 +1,308 @@ +/** + * Vertical Workspaces + * recentFilesSearchProvider.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + */ + +'use strict'; + +import GLib from 'gi://GLib'; +import St from 'gi://St'; +import Gio from 'gi://Gio'; +import Shell from 'gi://Shell'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +let Me; +let opt; +// gettext +let _; + +// prefix helps to eliminate results from other search providers +// so it needs to be something less common +// needs to be accessible from vw module +export const PREFIX = 'fq//'; + +export const RecentFilesSearchProviderModule = class { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._recentFilesSearchProvider = null; + this._enableTimeoutId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + this.moduleEnabled = opt.get('recentFilesSearchProviderModule'); + + reset = reset || !this.moduleEnabled; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' RecentFilesSearchProviderModule - Keeping untouched'); + } + + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._recentFilesSearchProvider) { + this._recentFilesSearchProvider = new RecentFilesSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._recentFilesSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } + ); + + console.debug(' RecentFilesSearchProviderModule - Activated'); + } + + _disableModule() { + if (this._recentFilesSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._recentFilesSearchProvider); + this._recentFilesSearchProvider = null; + } + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } + + console.debug(' RecentFilesSearchProviderModule - Disabled'); + } + + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +}; + +class RecentFilesSearchProvider { + constructor() { + this.id = 'recent-files'; + const appSystem = Shell.AppSystem.get_default(); + let appInfo = appSystem.lookup_app('org.gnome.Nautilus.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -w', _('Recent Files'), null); + appInfo.get_description = () => _('Search recent files'); + appInfo.get_name = () => _('Recent Files'); + appInfo.get_id = () => 'org.gnome.Nautilus.desktop'; + appInfo.get_icon = () => Gio.icon_new_for_string('document-open-recent-symbolic'); + appInfo.should_show = () => true; + + this.appInfo = appInfo; + this.canLaunchSearch = true; + this.isRemoteProvider = false; + + this._recentFilesManager = new RecentFilesManager(); + } + + getInitialResultSet(terms/* , callback*/) { + const rfm = this._recentFilesManager; + rfm.loadFromFile(); + + const uris = rfm.getUris(); + const dict = {}; + for (let uri of uris) { + dict[uri] = {}; + dict[uri]['uri'] = uri; + dict[uri]['path'] = rfm.getPath(uri); + dict[uri]['filename'] = rfm.getDisplayName(uri); + dict[uri]['dir'] = rfm.getDirPath(uri); + dict[uri]['age'] = rfm.getAge(uri); + dict[uri]['appInfo'] = rfm.getDefaultAppAppInfo(uri); + } + this.files = dict; + + return new Promise(resolve => resolve(this._getResultSet(terms))); + } + + _getResultSet(terms) { + if (!terms[0].startsWith(PREFIX)) + return []; + // do not modify original terms + let termsCopy = [...terms]; + // search for terms without prefix + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); + + const candidates = Object.values(this.files); + const _terms = [].concat(termsCopy); + + const term = _terms.join(' '); + + const results = []; + let m; + for (let file of candidates) { + if (opt.SEARCH_FUZZY) + m = Me.Util.fuzzyMatch(term, file.filename); + else + m = Me.Util.strictMatch(term, file.filename); + + if (m !== -1) + results.push(file); + } + + results.sort((a, b) => a.age > b.age); + + const resultIds = results.map(item => item.uri); + return resultIds; + } + + getResultMetas(resultIds/* , callback = null*/) { + const metas = resultIds.map(id => this.getResultMeta(id)); + return new Promise(resolve => resolve(metas)); + } + + getResultMeta(resultId) { + const result = this.files[resultId]; + return { + 'id': resultId, + 'name': `${Math.floor(result.age)}: ${result.filename}`, + 'description': `${result.dir}`, + 'createIcon': size => + this._recentFilesManager.getDefaultAppIcon(resultId, size), + }; + } + + launchSearch(terms, timeStamp) { + const appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -w recent:///', 'Nautilus', null); + appInfo.launch([], global.create_app_launch_context(timeStamp, -1)); + + // unlike on 42, on 44 if a window with the same uri is already open it will not get focus/activation + // Gio.app_info_launch_default_for_uri('recent:///', global.create_app_launch_context(timeStamp, -1)); + + // following solution for some reason ignores the recent:/// uri + // this.appInfo.launch_uris(['recent:///'], global.create_app_launch_context(timeStamp, -1)); + } + + activateResult(resultId, terms, timeStamp) { + const uri = resultId; + const context = global.create_app_launch_context(timeStamp, -1); + if (Me.Util.isShiftPressed()) { + Main.overview.toggle(); + this.appInfo.launch_uris([uri], context); + } else if (Gio.app_info_launch_default_for_uri(uri, context)) { + // update recent list after successful activation + this._recentFilesManager.updateAdded(resultId); + this._recentFilesManager.saveToFile(); + } else { + this.appInfo.launch_uris([uri], context); + } + } + + filterResults(results /* , maxResults*/) { + // return results.slice(0, maxResults); + return results.slice(0, 20); + } + + getSubsearchResultSet(previousResults, terms/* , callback*/) { + return this.getInitialResultSet(terms); + } + + getSubsearchResultSet42(terms, callback) { + callback(this._getResultSet(terms)); + } +} + +class RecentFilesManager { + constructor(path) { + path = path ?? GLib.build_filenamev([GLib.get_user_data_dir(), 'recently-used.xbel']); + this._recentlyUsedPath = path; + this._bookmarks = new GLib.BookmarkFile(); + } + + loadFromFile() { + try { + this._bookmarks.load_from_file(this._recentlyUsedPath); + } catch (e) { + if (!e.matches(GLib.BookmarkFileError, GLib.BookmarkFileError.FILE_NOT_FOUND)) + console.error(`Could not open recent files: ${e.message}`); + } + } + + saveToFile() { + try { + this._bookmarks.to_file(this._recentlyUsedPath); + } catch (e) { + if (!e.matches(GLib.BookmarkFileError, GLib.BookmarkFileError.FILE_NOT_FOUND)) + console.error(`Could not open recent files to save data: ${e.message}`); + } + } + + getUris() { + return this._bookmarks.get_uris(); + } + + getPath(uri) { + // GLib.filename_from_uri() removes uri schema and converts string to utf-8 + return GLib.filename_from_uri(uri)[0]; // result is array + } + + getDisplayName(uri) { + const path = this.getPath(uri); + return GLib.filename_display_basename(path); + } + + getDirPath(uri) { + const path = this.getPath(uri); + const filename = this.getDisplayName(uri); + return path.replace(`${filename}`, ''); + } + + getMimeType(uri) { + return this._bookmarks.get_mime_type(uri); + } + + getAdded(uri) { + return this._bookmarks.get_added(uri); + } + + updateAdded(uri) { + this._bookmarks.set_added_date_time(uri, GLib.DateTime.new_now_local()); + } + + // age in days (float) + getAge(uri) { + return (Date.now() / 1000 - this._bookmarks.get_added(uri)) / 60 / 60 / 24; + } + + getDefaultAppAppInfo(uri) { + const mimeType = this.getMimeType(uri); + return Gio.AppInfo.get_default_for_type(mimeType, false); + } + + getDefaultAppIcon(uri, size) { + let icon, gicon; + + const appInfo = this.getDefaultAppAppInfo(uri); + if (appInfo) + gicon = appInfo.get_icon(); + + if (gicon) + icon = new St.Icon({ gicon, icon_size: size }); + else + icon = new St.Icon({ icon_name: 'icon-missing', icon_size: size }); + + return icon; + } +} diff --git a/extensions/45/vertical-workspaces/lib/search.js b/extensions/45/vertical-workspaces/lib/search.js new file mode 100644 index 0000000..e961893 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/search.js @@ -0,0 +1,291 @@ +/** + * V-Shell (Vertical Workspaces) + * search.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; +import St from 'gi://St'; +import Shell from 'gi://Shell'; +import GObject from 'gi://GObject'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Search from 'resource:///org/gnome/shell/ui/search.js'; +import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js'; + +import * as SystemActions from 'resource:///org/gnome/shell/misc/systemActions.js'; + +let Me; +// gettext +let _; +let opt; + +let SEARCH_MAX_WIDTH; + +export const SearchModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + _ = null; + } + + update(reset) { + this.moduleEnabled = opt.get('searchModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' SearchModule - Keeping untouched'); + } + + _activateModule() { + this._updateSearchViewWidth(); + + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('AppSearchProvider', AppDisplay.AppSearchProvider.prototype, AppSearchProvider); + this._overrides.addOverride('SearchResult', Search.SearchResult.prototype, SearchResult); + this._overrides.addOverride('SearchResultsView', Search.SearchResultsView.prototype, SearchResultsView); + this._overrides.addOverride('ListSearchResults', Search.ListSearchResults.prototype, ListSearchResults); + // this._overrides.addOverride('ProviderInfo', Search.ProviderInfo.prototype, ProviderInfo); + + // Don't expand the search view vertically and align it to the top + // this is important in the static workspace mode when the search view bg is not transparent + // also the "Searching..." and "No Results" notifications will be closer to the search entry, with the distance given by margin-top in the stylesheet + Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.START; + console.debug(' SearchModule - Activated'); + } + + _disableModule() { + const reset = true; + this._updateSearchViewWidth(reset); + + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.FILL; + + + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); + } + + _updateSearchViewWidth(reset = false) { + const searchContent = Main.overview._overview._controls.layoutManager._searchController._searchResults._content; + if (!SEARCH_MAX_WIDTH) { // just store original value; + const themeNode = searchContent.get_theme_node(); + const width = themeNode.get_max_width(); + SEARCH_MAX_WIDTH = width; + } + + if (reset) { + searchContent.set_style(''); + } else { + let width = Math.round(SEARCH_MAX_WIDTH * opt.SEARCH_VIEW_SCALE); + searchContent.set_style(`max-width: ${width}px;`); + } + } +}; + +const ListSearchResults = { + _getMaxDisplayedResults() { + return opt.SEARCH_MAX_ROWS; + }, +}; + +// AppDisplay.AppSearchProvider +const AppSearchProvider = { + getInitialResultSet(terms, cancellable) { + // Defer until the parental controls manager is initialized, so the + // results can be filtered correctly. + if (!this._parentalControlsManager.initialized) { + return new Promise(resolve => { + let initializedId = this._parentalControlsManager.connect('app-filter-changed', async () => { + if (this._parentalControlsManager.initialized) { + this._parentalControlsManager.disconnect(initializedId); + resolve(await this.getInitialResultSet(terms, cancellable)); + } + }); + }); + } + + const pattern = terms.join(' '); + + let appInfoList = Shell.AppSystem.get_default().get_installed(); + + let weightList = {}; + appInfoList = appInfoList.filter(appInfo => { + try { + appInfo.get_id(); // catch invalid file encodings + } catch (e) { + return false; + } + + let string = ''; + let name; + let shouldShow = false; + if (appInfo.get_display_name) { + // show only launchers that should be visible in this DE + shouldShow = appInfo.should_show() && this._parentalControlsManager.shouldShowApp(appInfo); + + if (shouldShow) { + let id = appInfo.get_id().split('.'); + id = id[id.length - 2] || ''; + let baseName = appInfo.get_string('Name') || ''; + let dispName = appInfo.get_display_name() || ''; + let gName = appInfo.get_generic_name() || ''; + let description = appInfo.get_description() || ''; + let categories = appInfo.get_string('Categories') || ''; + let keywords = appInfo.get_string('Keywords') || ''; + name = `${dispName} ${id}`; + string = `${dispName} ${gName} ${baseName} ${description} ${categories} ${keywords} ${id}`; + } + } + + let m = -1; + if (shouldShow && opt.SEARCH_FUZZY) { + m = Me.Util.fuzzyMatch(pattern, name); + m = (m + Me.Util.strictMatch(pattern, string)) / 2; + } else if (shouldShow) { + m = Me.Util.strictMatch(pattern, string); + } + + if (m !== -1) + weightList[appInfo.get_id()] = m; + + return shouldShow && (m !== -1); + }); + + appInfoList.sort((a, b) => weightList[a.get_id()] > weightList[b.get_id()]); + + const usage = Shell.AppUsage.get_default(); + // sort apps by usage list + appInfoList.sort((a, b) => usage.compare(a.get_id(), b.get_id())); + // prefer apps where any word in their name starts with the pattern + appInfoList.sort((a, b) => Me.Util.isMoreRelevant(a.get_display_name(), b.get_display_name(), pattern)); + + let results = appInfoList.map(app => app.get_id()); + + results = results.concat(this._systemActions.getMatchingActions(terms)); + + return new Promise(resolve => resolve(results)); + }, + + // App search result size + createResultObject(resultMeta) { + if (resultMeta.id.endsWith('.desktop')) { + const icon = new AppDisplay.AppIcon(this._appSys.lookup_app(resultMeta['id']), { + expandTitleOnHover: false, + }); + icon.icon.setIconSize(opt.SEARCH_ICON_SIZE); + return icon; + } else { + return new SystemActionIcon(this, resultMeta); + // icon.icon._setSizeManually = true; + // icon.icon.setIconSize(opt.SEARCH_ICON_SIZE); + // return icon; + } + }, +}; + +const SystemActionIcon = GObject.registerClass( +class SystemActionIcon extends Search.GridSearchResult { + _init(provider, metaInfo, resultsView) { + super._init(provider, metaInfo, resultsView); + this.icon._setSizeManually = true; + this.icon.setIconSize(opt.SEARCH_ICON_SIZE); + } + + activate() { + SystemActions.getDefault().activateAction(this.metaInfo['id']); + Main.overview.hide(); + } +}); + +const SearchResult = { + activate() { + this.provider.activateResult(this.metaInfo.id, this._resultsView.terms); + + if (this.metaInfo.clipboardText) { + St.Clipboard.get_default().set_text( + St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText); + } + // don't close overview if Shift key is pressed - Shift moves windows to the workspace + if (!Me.Util.isShiftPressed()) + Main.overview.toggle(); + }, +}; + +const SearchResultsView = { + _doSearch() { + this._startingSearch = false; + + let previousResults = this._results; + this._results = {}; + + this._providers.forEach(provider => { + const onlyVShellProviders = this._terms.includes('wq//') || this._terms.includes('fq//'); + if (!onlyVShellProviders || (onlyVShellProviders && (provider.id.includes('open-windows') || provider.id.includes('recent-files')))) { + let previousProviderResults = previousResults[provider.id]; + this._doProviderSearch(provider, previousProviderResults); + } + }); + + this._updateSearchProgress(); + + this._clearSearchTimeout(); + }, + + _updateSearchProgress() { + let haveResults = this._providers.some(provider => { + let display = provider.display; + return display.getFirstResult() !== null; + }); + + this._scrollView.visible = haveResults; + this._statusBin.visible = !haveResults; + + if (!haveResults) { + if (this.searchInProgress) + this._statusText.set_text(_('Searching…')); + else + this._statusText.set_text(_('No results.')); + } + }, +}; + +// fixes app is null error if search provider id is not a desktop app id. +// is not accessible in 45 +/* const ProviderInfo = { + animateLaunch() { + let appSys = Shell.AppSystem.get_default(); + let app = appSys.lookup_app(this.provider.appInfo.get_id()); + if (app && app.state === Shell.AppState.STOPPED) + IconGrid.zoomOutActor(this._content); + }, +};*/ diff --git a/extensions/45/vertical-workspaces/lib/searchController.js b/extensions/45/vertical-workspaces/lib/searchController.js new file mode 100644 index 0000000..e69bc90 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/searchController.js @@ -0,0 +1,94 @@ +/** + * V-Shell (Vertical Workspaces) + * searchController.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +let Me; +let opt; + +export const SearchControllerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._originalOnStageKeyPress = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('searchControllerModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' SearchControllerModule - Keeping untouched'); + } + + _activateModule() { + if (!this._originalOnStageKeyPress) + this._originalOnStageKeyPress = Main.overview._overview.controls._searchController._onStageKeyPress; + + Main.overview._overview.controls._searchController._onStageKeyPress = SearchControllerCommon._onStageKeyPress; + console.debug(' SearchControllerModule - Activated'); + } + + _disableModule() { + if (this._originalOnStageKeyPress) + Main.overview._overview.controls._searchController._onStageKeyPress = this._originalOnStageKeyPress; + this._originalOnStageKeyPress = null; + + console.debug(' SearchControlerModule - Disabled'); + } +}; + +// if opt.ESC_BEHAVIOR > 0 force close the overview +const SearchControllerCommon = { + _onStageKeyPress(actor, event) { + // Ignore events while anything but the overview has + // pushed a modal (system modals, looking glass, ...) + if (Main.modalCount > 1) + return Clutter.EVENT_PROPAGATE; + + let symbol = event.get_key_symbol(); + if (symbol === Clutter.KEY_Escape) { + if (this._searchActive && !opt.ESC_BEHAVIOR) { + this.reset(); + } else if (this._showAppsButton.checked && !opt.ESC_BEHAVIOR) { + this._showAppsButton.checked = false; + } else { + this.reset(); + Main.overview.hide(); + } + + return Clutter.EVENT_STOP; + } else if (this._shouldTriggerSearch(symbol)) { + this.startSearch(event); + } + return Clutter.EVENT_PROPAGATE; + }, +}; diff --git a/extensions/vertical-workspaces/lib/settings.js b/extensions/45/vertical-workspaces/lib/settings.js similarity index 66% rename from extensions/vertical-workspaces/lib/settings.js rename to extensions/45/vertical-workspaces/lib/settings.js index 66f3a45..d8bb99c 100644 --- a/extensions/vertical-workspaces/lib/settings.js +++ b/extensions/45/vertical-workspaces/lib/settings.js @@ -9,25 +9,15 @@ 'use strict'; -const { GLib } = imports.gi; +import GLib from 'gi://GLib'; -const Config = imports.misc.config; +let Me; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +export const Options = class Options { + constructor(me) { + Me = me; -var shellVersion = parseFloat(Config.PACKAGE_VERSION); - -const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']); -var _ = Gettext.gettext; -const _schema = Me.metadata['settings-schema']; - -// common instance of Options accessible from all modules -var opt; - -var Options = class Options { - constructor() { - this._gsettings = ExtensionUtils.getSettings(_schema); + this._gsettings = Me.gSettings; this._connectionIds = []; this._writeTimeoutId = 0; this._gsettings.delay(); @@ -51,8 +41,8 @@ var Options = class Options { wsMaxSpacing: ['int', 'ws-max-spacing'], wsPreviewScale: ['int', 'ws-preview-scale'], secWsPreviewScale: ['int', 'secondary-ws-preview-scale'], - secWsPreviewShift: ['bool', 'secondary-ws-preview-shift'], - wsThumbnailsFull: ['bool', 'ws-thumbnails-full'], + secWsPreviewShift: ['boolean', 'secondary-ws-preview-shift'], + wsThumbnailsFull: ['boolean', 'ws-thumbnails-full'], secWsThumbnailsPosition: ['int', 'secondary-ws-thumbnails-position'], dashPosition: ['int', 'dash-position'], dashPositionAdjust: ['int', 'dash-position-adjust'], @@ -64,6 +54,7 @@ var Options = class Options { dashMaxIconSize: ['int', 'dash-max-icon-size'], dashShowWindowsIcon: ['int', 'dash-show-windows-icon'], dashShowRecentFilesIcon: ['int', 'dash-show-recent-files-icon'], + dashShowExtensionsIcon: ['int', 'dash-show-extensions-icon'], centerDashToWs: ['boolean', 'center-dash-to-ws'], showAppsIconPosition: ['int', 'show-app-icon-position'], wsThumbnailScale: ['int', 'ws-thumbnail-scale'], @@ -73,13 +64,17 @@ var Options = class Options { centerSearch: ['boolean', 'center-search'], centerAppGrid: ['boolean', 'center-app-grid'], dashBgOpacity: ['int', 'dash-bg-opacity'], + dashBgColor: ['int', 'dash-bg-color'], dashBgRadius: ['int', 'dash-bg-radius'], + dashBgGS3Style: ['boolean', 'dash-bg-gs3-style'], + runningDotStyle: ['int', 'running-dot-style'], enablePageShortcuts: ['boolean', 'enable-page-shortcuts'], showWsSwitcherBg: ['boolean', 'show-ws-switcher-bg'], showWsPreviewBg: ['boolean', 'show-ws-preview-bg'], wsPreviewBgRadius: ['int', 'ws-preview-bg-radius'], showBgInOverview: ['boolean', 'show-bg-in-overview'], overviewBgBrightness: ['int', 'overview-bg-brightness'], + searchBgBrightness: ['int', 'search-bg-brightness'], overviewBgBlurSigma: ['int', 'overview-bg-blur-sigma'], appGridBgBlurSigma: ['int', 'app-grid-bg-blur-sigma'], smoothBlurTransitions: ['boolean', 'smooth-blur-transitions'], @@ -87,9 +82,8 @@ var Options = class Options { searchViewAnimation: ['int', 'search-view-animation'], workspaceAnimation: ['int', 'workspace-animation'], animationSpeedFactor: ['int', 'animation-speed-factor'], - fixUbuntuDock: ['boolean', 'fix-ubuntu-dock'], winPreviewIconSize: ['int', 'win-preview-icon-size'], - alwaysShowWinTitles: ['boolean', 'always-show-win-titles'], + winTitlePosition: ['int', 'win-title-position'], startupState: ['int', 'startup-state'], overviewMode: ['int', 'overview-mode'], workspaceSwitcherAnimation: ['int', 'workspace-switcher-animation'], @@ -105,17 +99,18 @@ var Options = class Options { appGridContent: ['int', 'app-grid-content'], appGridIncompletePages: ['boolean', 'app-grid-incomplete-pages'], appGridOrder: ['int', 'app-grid-order'], + appFolderOrder: ['int', 'app-folder-order'], appGridNamesMode: ['int', 'app-grid-names'], appGridActivePreview: ['boolean', 'app-grid-active-preview'], appGridFolderCenter: ['boolean', 'app-grid-folder-center'], appGridPageWidthScale: ['int', 'app-grid-page-width-scale'], appGridSpacing: ['int', 'app-grid-spacing'], - searchWindowsEnable: ['boolean', 'search-windows-enable'], - searchRecentFilesEnable: ['boolean', 'search-recent-files-enable'], + searchWindowsOrder: ['int', 'search-windows-order'], searchFuzzy: ['boolean', 'search-fuzzy'], searchMaxResultsRows: ['int', 'search-max-results-rows'], dashShowWindowsBeforeActivation: ['int', 'dash-show-windows-before-activation'], dashIconScroll: ['int', 'dash-icon-scroll'], + dashIsolateWorkspaces: ['boolean', 'dash-isolate-workspaces'], searchWindowsIconScroll: ['int', 'search-windows-icon-scroll'], panelVisibility: ['int', 'panel-visibility'], panelPosition: ['int', 'panel-position'], @@ -123,6 +118,8 @@ var Options = class Options { wsSwPopupHPosition: ['int', 'ws-sw-popup-h-position'], wsSwPopupVPosition: ['int', 'ws-sw-popup-v-position'], wsSwPopupMode: ['int', 'ws-sw-popup-mode'], + wsSwitcherWraparound: ['boolean', 'ws-switcher-wraparound'], + wsSwitcherIgnoreLast: ['boolean', 'ws-switcher-ignore-last'], favoritesNotify: ['int', 'favorites-notify'], notificationPosition: ['int', 'notification-position'], osdPosition: ['int', 'osd-position'], @@ -131,17 +128,26 @@ var Options = class Options { hotCornerFullscreen: ['boolean', 'hot-corner-fullscreen'], hotCornerRipples: ['boolean', 'hot-corner-ripples'], alwaysActivateSelectedWindow: ['boolean', 'always-activate-selected-window'], - windowIconClickSearch: ['boolean', 'window-icon-click-search'], + winPreviewSecBtnAction: ['int', 'win-preview-sec-mouse-btn-action'], + winPreviewMidBtnAction: ['int', 'win-preview-mid-mouse-btn-action'], + winPreviewShowCloseButton: ['boolean', 'win-preview-show-close-button'], + windowIconClickAction: ['int', 'window-icon-click-action'], + overlayKeyPrimary: ['int', 'overlay-key-primary'], overlayKeySecondary: ['int', 'overlay-key-secondary'], + overviewEscBehavior: ['int', 'overview-esc-behavior'], + newWindowFocusFix: ['boolean', 'new-window-focus-fix'], + appGridPerformance: ['boolean', 'app-grid-performance'], + windowThumbnailScale: ['int', 'window-thumbnail-scale'], - workspaceThumbnailsModule: ['boolean', 'workspace-thumbnails-module'], workspaceSwitcherPopupModule: ['boolean', 'workspace-switcher-popup-module'], workspaceAnimationModule: ['boolean', 'workspace-animation-module'], workspaceModule: ['boolean', 'workspace-module'], windowManagerModule: ['boolean', 'window-manager-module'], windowPreviewModule: ['boolean', 'window-preview-module'], - winAttentionHandlerModule: ['boolean', 'win-attention-handler-module'], + windowAttentionHandlerModule: ['boolean', 'win-attention-handler-module'], + windowThumbnailModule: ['boolean', 'window-thumbnail-module'], swipeTrackerModule: ['boolean', 'swipe-tracker-module'], + searchControllerModule: ['boolean', 'search-controller-module'], searchModule: ['boolean', 'search-module'], panelModule: ['boolean', 'panel-module'], overlayKeyModule: ['boolean', 'overlay-key-module'], @@ -151,6 +157,9 @@ var Options = class Options { dashModule: ['boolean', 'dash-module'], appFavoritesModule: ['boolean', 'app-favorites-module'], appDisplayModule: ['boolean', 'app-display-module'], + windowSearchProviderModule: ['boolean', 'window-search-provider-module'], + recentFilesSearchProviderModule: ['boolean', 'recent-files-search-provider-module'], + extensionsSearchProviderModule: ['boolean', 'extensions-search-provider-module'], profileName1: ['string', 'profile-name-1'], profileName2: ['string', 'profile-name-2'], @@ -158,9 +167,6 @@ var Options = class Options { profileName4: ['string', 'profile-name-4'], }; this.cachedOptions = {}; - - this.shellVersion = shellVersion; - // this.storeProfile(0); } connect(name, callback) { @@ -175,6 +181,8 @@ var Options = class Options { GLib.source_remove(this._writeTimeoutId); this._writeTimeoutId = 0; } + + Me = null; } _updateCachedSettings() { @@ -183,7 +191,7 @@ var Options = class Options { get(option, updateCache = false) { if (!this.options[option]) { - log(`[${Me.metadata.name}] Error: Option ${option} is undefined.`); + console.error(`[${Me.metadata.name}] Error: Option ${option} is undefined.`); return null; } @@ -195,7 +203,6 @@ var Options = class Options { else gSettings = this._gsettings; - this.cachedOptions[option] = gSettings.get_value(key).deep_unpack(); } @@ -242,7 +249,8 @@ var Options = class Options { storeProfile(index) { const profile = {}; Object.keys(this.options).forEach(v => { - profile[v] = this.get(v).toString(); + if (!v.startsWith('profileName')) + profile[v] = this.get(v).toString(); }); this._gsettings.set_value(`profile-data-${index}`, new GLib.Variant('a{ss}', profile)); @@ -250,8 +258,14 @@ var Options = class Options { loadProfile(index) { const options = this._gsettings.get_value(`profile-data-${index}`).deep_unpack(); + // set the aaa-loading-data so extension.js doesn't reset V-Shell after each profile item + // delayed gsettings writes are processed alphabetically, so this key will be processed first this._gsettings.set_boolean('aaa-loading-profile', !this._gsettings.get_boolean('aaa-loading-profile')); for (let o of Object.keys(options)) { + if (!this.options[o]) { + console.error(`[${Me.metadata.name}] Error: "${o}" is not a valid profile key -> Update your profile`); + continue; + } const [type] = this.options[o]; let value = options[o]; switch (type) { @@ -275,7 +289,14 @@ var Options = class Options { } _updateSettings() { - this.DASH_POSITION = this.get('dashPosition', true); + this._updateCachedSettings(); + this.DASH_BG_ALPHA = this.get('dashBgOpacity') / 100; + this.DASH_BG_OPACITY = this.get('dashBgOpacity') * 2.5; + this.DASH_BG_COLOR = this.get('dashBgColor'); + this.DASH_BG_RADIUS = this.get('dashBgRadius'); + this.DASH_BG_LIGHT = this.DASH_BG_COLOR === 1; + this.DASH_BG_GS3_STYLE = this.get('dashBgGS3Style'); + this.DASH_POSITION = this.get('dashModule') ? this.get('dashPosition') : 2; this.DASH_TOP = this.DASH_POSITION === 0; this.DASH_RIGHT = this.DASH_POSITION === 1; this.DASH_BOTTOM = this.DASH_POSITION === 2; @@ -284,26 +305,35 @@ var Options = class Options { this.DASH_VISIBLE = this.DASH_POSITION !== 4; // 4 - disable this.DASH_FOLLOW_RECENT_WIN = false; - this.DASH_CLICK_ACTION = this.get('dashShowWindowsBeforeActivation', true); - this.DASH_ICON_SCROLL = this.get('dashIconScroll', true); + this.DASH_ISOLATE_WS = this.get('dashIsolateWorkspaces'); + + this.DASH_CLICK_ACTION = this.get('dashShowWindowsBeforeActivation'); + this.DASH_CLICK_SWITCH_BEFORE_ACTIVATION = this.DASH_CLICK_ACTION === 1; + this.DASH_CLICK_OPEN_NEW_WIN = this.DASH_CLICK_ACTION === 2; + this.DASH_CLICK_PREFER_WORKSPACE = this.DASH_CLICK_ACTION === 3; + + this.DASH_ICON_SCROLL = this.get('dashIconScroll'); this.DASH_SHIFT_CLICK_MV = true; - this.SEARCH_WINDOWS_ICON_SCROLL = this.get('searchWindowsIconScroll', true); + this.RUNNING_DOT_STYLE = this.get('runningDotStyle'); - this.DASH_POSITION_ADJUSTMENT = this.get('dashPositionAdjust', true); + this.SEARCH_WINDOWS_ICON_SCROLL = this.get('searchWindowsIconScroll'); + + this.DASH_POSITION_ADJUSTMENT = this.get('dashPositionAdjust'); this.DASH_POSITION_ADJUSTMENT = this.DASH_POSITION_ADJUSTMENT * -1 / 100; // range 1 to -1 - this.CENTER_DASH_WS = this.get('centerDashToWs', true); + this.CENTER_DASH_WS = this.get('centerDashToWs'); - this.MAX_ICON_SIZE = 64; // updates from main module - this.SHOW_WINDOWS_ICON = this.get('dashShowWindowsIcon', true); - this.SHOW_RECENT_FILES_ICON = this.get('dashShowRecentFilesIcon', true); + this.MAX_ICON_SIZE = this.get('dashMaxIconSize'); + this.SHOW_WINDOWS_ICON = this.get('dashShowWindowsIcon'); + this.SHOW_RECENT_FILES_ICON = this.get('dashShowRecentFilesIcon'); + this.SHOW_EXTENSIONS_ICON = this.get('dashShowExtensionsIcon'); - this.WS_TMB_POSITION = this.get('workspaceThumbnailsPosition', true); + this.WS_TMB_POSITION = this.get('workspaceThumbnailsPosition'); this.ORIENTATION = this.WS_TMB_POSITION > 4 ? 0 : 1; - this.WORKSPACE_MAX_SPACING = this.get('wsMaxSpacing', true); + this.WORKSPACE_MAX_SPACING = this.get('wsMaxSpacing'); // ORIENTATION || DASH_LEFT || DASH_RIGHT ? 350 : 80; this.SHOW_WS_TMB = ![4, 9].includes(this.WS_TMB_POSITION); // 4, 9 - disable - this.WS_TMB_FULL = this.get('wsThumbnailsFull', true); + this.WS_TMB_FULL = this.get('wsThumbnailsFull'); // translate ws tmb position to 0 top, 1 right, 2 bottom, 3 left // 0L 1R, 2LF, 3RF, 4DV, 5T, 6B, 7TF, 8BF, 9DH this.WS_TMB_POSITION = [3, 1, 3, 1, 4, 0, 2, 0, 2, 8][this.WS_TMB_POSITION]; @@ -311,81 +341,104 @@ var Options = class Options { this.WS_TMB_RIGHT = this.WS_TMB_POSITION === 1; this.WS_TMB_BOTTOM = this.WS_TMB_POSITION === 2; this.WS_TMB_LEFT = this.WS_TMB_POSITION === 3; - this.WS_TMB_POSITION_ADJUSTMENT = this.get('wsTmbPositionAdjust', true) * -1 / 100; // range 1 to -1 - this.SEC_WS_TMB_POSITION = this.get('secWsThumbnailsPosition', true); + this.WS_TMB_POSITION_ADJUSTMENT = this.get('wsTmbPositionAdjust') * -1 / 100; // range 1 to -1 + this.SEC_WS_TMB_POSITION = this.get('secWsThumbnailsPosition'); this.SHOW_SEC_WS_TMB = this.SEC_WS_TMB_POSITION !== 3 && this.SHOW_WS_TMB; this.SEC_WS_TMB_TOP = (this.SEC_WS_TMB_POSITION === 0 && !this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_TOP); this.SEC_WS_TMB_RIGHT = (this.SEC_WS_TMB_POSITION === 1 && this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_RIGHT); this.SEC_WS_TMB_BOTTOM = (this.SEC_WS_TMB_POSITION === 1 && !this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_BOTTOM); this.SEC_WS_TMB_LEFT = (this.SEC_WS_TMB_POSITION === 0 && this.ORIENTATION) || (this.SEC_WS_TMB_POSITION === 2 && this.WS_TMB_LEFT); - this.SEC_WS_TMB_POSITION_ADJUSTMENT = this.get('secWsTmbPositionAdjust', true) * -1 / 100; // range 1 to -1 - this.SEC_WS_PREVIEW_SHIFT = this.get('secWsPreviewShift', true); - this.SHOW_WST_LABELS = this.get('showWsTmbLabels', true); - this.SHOW_WST_LABELS_ON_HOVER = this.get('showWsTmbLabelsOnHover', true); - this.CLOSE_WS_BUTTON_MODE = this.get('closeWsButtonMode', true); + this.SEC_WS_TMB_POSITION_ADJUSTMENT = this.get('secWsTmbPositionAdjust') * -1 / 100; // range 1 to -1 + this.SEC_WS_PREVIEW_SHIFT = this.get('secWsPreviewShift'); + this.SHOW_WST_LABELS = this.get('showWsTmbLabels'); + this.SHOW_WST_LABELS_ON_HOVER = this.get('showWsTmbLabelsOnHover'); + this.CLOSE_WS_BUTTON_MODE = this.get('closeWsButtonMode'); - this.MAX_THUMBNAIL_SCALE = this.get('wsThumbnailScale', true) / 100; - this.MAX_THUMBNAIL_SCALE_APPGRID = this.get('wsThumbnailScaleAppGrid', true) / 100; - if (this.MAX_THUMBNAIL_SCALE_APPGRID === 0) - this.MAX_THUMBNAIL_SCALE_APPGRID = this.MAX_THUMBNAIL_SCALE; + this.MAX_THUMBNAIL_SCALE = this.get('wsThumbnailScale') / 100; + if (this.MAX_THUMBNAIL_SCALE === 0) { + this.MAX_THUMBNAIL_SCALE = 0.01; + this.SHOW_WS_TMB = false; + } + this.MAX_THUMBNAIL_SCALE_APPGRID = this.get('wsThumbnailScaleAppGrid') / 100; + this.SHOW_WS_TMB_APPGRID = true; + if (this.MAX_THUMBNAIL_SCALE_APPGRID === 0) { + this.MAX_THUMBNAIL_SCALE_APPGRID = 0.01; + this.SHOW_WS_TMB_APPGRID = false; + } this.MAX_THUMBNAIL_SCALE_STABLE = this.MAX_THUMBNAIL_SCALE === this.MAX_THUMBNAIL_SCALE_APPGRID; - this.SEC_MAX_THUMBNAIL_SCALE = this.get('secWsThumbnailScale', true) / 100; - this.WS_PREVIEW_SCALE = this.get('wsPreviewScale', true) / 100; - this.SEC_WS_PREVIEW_SCALE = this.get('secWsPreviewScale', true) / 100; + this.SEC_MAX_THUMBNAIL_SCALE = this.get('secWsThumbnailScale') / 100; + if (this.SEC_MAX_THUMBNAIL_SCALE === 0) { + this.SEC_MAX_THUMBNAIL_SCALE = 0.01; + this.SHOW_SEC_WS_TMB = false; + } + + this.WS_PREVIEW_SCALE = this.get('wsPreviewScale') / 100; + this.SEC_WS_PREVIEW_SCALE = this.get('secWsPreviewScale') / 100; // calculate number of possibly visible neighbor previews according to ws scale this.NUMBER_OF_VISIBLE_NEIGHBORS = Math.round(1 + (1 - this.WS_PREVIEW_SCALE) / 4); - this.SHOW_WS_TMB_BG = this.get('showWsSwitcherBg', true) && this.SHOW_WS_TMB; - this.WS_PREVIEW_BG_RADIUS = this.get('wsPreviewBgRadius', true); - this.SHOW_WS_PREVIEW_BG = this.get('showWsPreviewBg', true); + this.SHOW_WS_TMB_BG = this.get('showWsSwitcherBg') && this.SHOW_WS_TMB; + this.WS_PREVIEW_BG_RADIUS = this.get('wsPreviewBgRadius'); + this.SHOW_WS_PREVIEW_BG = this.get('showWsPreviewBg'); - this.CENTER_APP_GRID = this.get('centerAppGrid', true); + this.CENTER_APP_GRID = this.get('centerAppGrid'); - this.SHOW_SEARCH_ENTRY = this.get('showSearchEntry', true); - this.CENTER_SEARCH_VIEW = this.get('centerSearch', true); - this.APP_GRID_ANIMATION = this.get('appGridAnimation', true); + this.SHOW_SEARCH_ENTRY = this.get('showSearchEntry'); + this.CENTER_SEARCH_VIEW = this.get('centerSearch'); + this.APP_GRID_ANIMATION = this.get('appGridAnimation'); if (this.APP_GRID_ANIMATION === 4) this.APP_GRID_ANIMATION = this._getAnimationDirection(); - this.SEARCH_VIEW_ANIMATION = this.get('searchViewAnimation', true); + this.SEARCH_VIEW_ANIMATION = this.get('searchViewAnimation'); if (this.SEARCH_VIEW_ANIMATION === 4) this.SEARCH_VIEW_ANIMATION = 3; - this.WS_ANIMATION = this.get('workspaceAnimation', true); + this.WS_ANIMATION = this.get('workspaceAnimation'); - this.WIN_PREVIEW_ICON_SIZE = [64, 48, 32, 22, 8][this.get('winPreviewIconSize', true)]; - this.ALWAYS_SHOW_WIN_TITLES = this.get('alwaysShowWinTitles', true); + this.WIN_PREVIEW_ICON_SIZE = [64, 48, 32, 22, 8][this.get('winPreviewIconSize')]; + this.WIN_TITLES_POSITION = this.get('winTitlePosition'); + this.ALWAYS_SHOW_WIN_TITLES = this.WIN_TITLES_POSITION === 1; - this.STARTUP_STATE = this.get('startupState', true); - this.SHOW_BG_IN_OVERVIEW = this.get('showBgInOverview', true); - this.OVERVIEW_BG_BRIGHTNESS = this.get('overviewBgBrightness', true) / 100; - this.OVERVIEW_BG_BLUR_SIGMA = this.get('overviewBgBlurSigma', true); - this.APP_GRID_BG_BLUR_SIGMA = this.get('appGridBgBlurSigma', true); - this.SMOOTH_BLUR_TRANSITIONS = this.get('smoothBlurTransitions', true); + this.STARTUP_STATE = this.get('startupState'); + this.SHOW_BG_IN_OVERVIEW = this.get('showBgInOverview'); + this.OVERVIEW_BG_BRIGHTNESS = this.get('overviewBgBrightness') / 100; + this.SEARCH_BG_BRIGHTNESS = this.get('searchBgBrightness') / 100; + this.OVERVIEW_BG_BLUR_SIGMA = this.get('overviewBgBlurSigma'); + this.APP_GRID_BG_BLUR_SIGMA = this.get('appGridBgBlurSigma'); + this.SMOOTH_BLUR_TRANSITIONS = this.get('smoothBlurTransitions'); - this.OVERVIEW_MODE = this.get('overviewMode', true); + this.OVERVIEW_MODE = this.get('overviewMode'); this.OVERVIEW_MODE2 = this.OVERVIEW_MODE === 2; this.WORKSPACE_MODE = this.OVERVIEW_MODE ? 0 : 1; - this.STATIC_WS_SWITCHER_BG = this.get('workspaceSwitcherAnimation', true); + this.STATIC_WS_SWITCHER_BG = this.get('workspaceSwitcherAnimation'); - this.ANIMATION_TIME_FACTOR = this.get('animationSpeedFactor', true) / 100; + this.ANIMATION_TIME_FACTOR = this.get('animationSpeedFactor') / 100; - this.SEARCH_ICON_SIZE = this.get('searchIconSize', true); - this.SEARCH_VIEW_SCALE = this.get('searchViewScale', true) / 100; - this.SEARCH_MAX_ROWS = this.get('searchMaxResultsRows', true); - this.SEARCH_FUZZY = this.get('searchFuzzy', true); + this.SEARCH_ICON_SIZE = this.get('searchIconSize'); + this.SEARCH_VIEW_SCALE = this.get('searchViewScale') / 100; + this.SEARCH_MAX_ROWS = this.get('searchMaxResultsRows'); + this.SEARCH_FUZZY = this.get('searchFuzzy'); - this.APP_GRID_ALLOW_INCOMPLETE_PAGES = this.get('appGridIncompletePages', true); - this.APP_GRID_ICON_SIZE = this.get('appGridIconSize', true); - this.APP_GRID_COLUMNS = this.get('appGridColumns', true); - this.APP_GRID_ROWS = this.get('appGridRows', true); + this.APP_GRID_ALLOW_INCOMPLETE_PAGES = this.get('appGridIncompletePages'); + this.APP_GRID_ICON_SIZE = this.get('appGridIconSize'); + this.APP_GRID_COLUMNS = this.get('appGridColumns'); + this.APP_GRID_ROWS = this.get('appGridRows'); this.APP_GRID_ADAPTIVE = !this.APP_GRID_COLUMNS && !this.APP_GRID_ROWS; - this.APP_GRID_ORDER = this.get('appGridOrder', true); - this.APP_GRID_INCLUDE_DASH = this.get('appGridContent', true); + this.APP_GRID_ORDER = this.get('appGridOrder'); + this.APP_GRID_ALPHABET = [1, 2].includes(this.APP_GRID_ORDER); + this.APP_GRID_FOLDERS_FIRST = this.APP_GRID_ORDER === 1; + this.APP_GRID_FOLDERS_LAST = this.APP_GRID_ORDER === 2; + this.APP_GRID_USAGE = this.APP_GRID_ORDER === 3; + + this.APP_FOLDER_ORDER = this.get('appFolderOrder'); + this.APP_FOLDER_ALPHABET = this.APP_FOLDER_ORDER === 1; + this.APP_FOLDER_USAGE = this.APP_FOLDER_ORDER === 2; + + this.APP_GRID_INCLUDE_DASH = this.get('appGridContent'); /* APP_GRID_INCLUDE_DASH 0 - Include All 1 - Include All - Favorites and Runnings First @@ -397,46 +450,50 @@ var Options = class Options { this.APP_GRID_EXCLUDE_RUNNING = this.APP_GRID_INCLUDE_DASH === 3 || this.APP_GRID_INCLUDE_DASH === 4; this.APP_GRID_DASH_FIRST = this.APP_GRID_INCLUDE_DASH === 1; - this.APP_GRID_NAMES_MODE = this.get('appGridNamesMode', true); + this.APP_GRID_NAMES_MODE = this.get('appGridNamesMode'); - this.APP_GRID_FOLDER_ICON_SIZE = this.get('appGridFolderIconSize', true); - this.APP_GRID_FOLDER_ICON_GRID = this.get('appGridFolderIconGrid', true); - this.APP_GRID_FOLDER_COLUMNS = this.get('appGridFolderColumns', true); - this.APP_GRID_FOLDER_ROWS = this.get('appGridFolderRows', true); - this.APP_GRID_SPACING = this.get('appGridSpacing', true); + this.APP_GRID_FOLDER_ICON_SIZE = this.get('appGridFolderIconSize'); + this.APP_GRID_FOLDER_ICON_GRID = this.get('appGridFolderIconGrid'); + this.APP_GRID_FOLDER_COLUMNS = this.get('appGridFolderColumns'); + this.APP_GRID_FOLDER_ROWS = this.get('appGridFolderRows'); + this.APP_GRID_SPACING = this.get('appGridSpacing'); this.APP_GRID_FOLDER_DEFAULT = this.APP_GRID_FOLDER_ROWS === 3 && this.APP_GRID_FOLDER_COLUMNS === 3; - this.APP_GRID_ACTIVE_PREVIEW = this.get('appGridActivePreview', true); - this.APP_GRID_FOLDER_CENTER = this.get('appGridFolderCenter', true); - this.APP_GRID_PAGE_WIDTH_SCALE = this.get('appGridPageWidthScale', true) / 100; + this.APP_GRID_FOLDER_ADAPTIVE = !this.APP_GRID_FOLDER_COLUMNS && !this.APP_GRID_FOLDER_ROWS; + this.APP_GRID_ACTIVE_PREVIEW = this.get('appGridActivePreview'); + this.APP_GRID_FOLDER_CENTER = this.get('appGridFolderCenter'); + this.APP_GRID_PAGE_WIDTH_SCALE = this.get('appGridPageWidthScale') / 100; - this.APP_GRID_ICON_SIZE_DEFAULT = this.APP_GRID_ACTIVE_PREVIEW && !this.APP_GRID_ORDER ? 176 : 96; + this.APP_GRID_ICON_SIZE_DEFAULT = this.APP_GRID_ACTIVE_PREVIEW && !this.APP_GRID_USAGE ? 176 : 96; this.APP_GRID_FOLDER_ICON_SIZE_DEFAULT = 96; - this.WINDOW_SEARCH_PROVIDER_ENABLED = this.get('searchWindowsEnable', true); - this.RECENT_FILES_SEARCH_PROVIDER_ENABLED = this.get('searchRecentFilesEnable', true); + this.APP_GRID_PERFORMANCE = this.get('appGridPerformance'); - this.PANEL_POSITION_TOP = this.get('panelPosition', true) === 0; - this.PANEL_MODE = this.get('panelVisibility', true); + this.WINDOW_SEARCH_ORDER = this.get('searchWindowsOrder'); + + this.PANEL_POSITION_TOP = this.get('panelPosition') === 0; + this.PANEL_MODE = this.get('panelVisibility'); this.PANEL_DISABLED = this.PANEL_MODE === 2; this.PANEL_OVERVIEW_ONLY = this.PANEL_MODE === 1; this.START_Y_OFFSET = 0; // set from main module - this.FIX_UBUNTU_DOCK = this.get('fixUbuntuDock', true); - this.WINDOW_ATTENTION_MODE = this.get('windowAttentionMode', true); + this.WINDOW_ATTENTION_MODE = this.get('windowAttentionMode'); this.WINDOW_ATTENTION_DISABLE_NOTIFICATIONS = this.WINDOW_ATTENTION_MODE === 1; this.WINDOW_ATTENTION_FOCUS_IMMEDIATELY = this.WINDOW_ATTENTION_MODE === 2; - this.WS_SW_POPUP_H_POSITION = this.get('wsSwPopupHPosition', true) / 100; - this.WS_SW_POPUP_V_POSITION = this.get('wsSwPopupVPosition', true) / 100; - this.WS_SW_POPUP_MODE = this.get('wsSwPopupMode', true); + this.WS_SW_POPUP_H_POSITION = this.get('wsSwPopupHPosition') / 100; + this.WS_SW_POPUP_V_POSITION = this.get('wsSwPopupVPosition') / 100; + this.WS_SW_POPUP_MODE = this.get('wsSwPopupMode'); - this.SHOW_FAV_NOTIFICATION = this.get('favoritesNotify', true); - this.NOTIFICATION_POSITION = this.get('notificationPosition', true); + this.WS_WRAPAROUND = this.get('wsSwitcherWraparound'); + this.WS_IGNORE_LAST = this.get('wsSwitcherIgnoreLast'); - this.OSD_POSITION = this.get('osdPosition', true); + this.SHOW_FAV_NOTIFICATION = this.get('favoritesNotify'); + this.NOTIFICATION_POSITION = this.get('notificationPosition'); - this.HOT_CORNER_ACTION = this.get('hotCornerAction', true); - this.HOT_CORNER_POSITION = this.get('hotCornerPosition', true); + this.OSD_POSITION = this.get('osdPosition'); + + this.HOT_CORNER_ACTION = this.get('hotCornerAction'); + this.HOT_CORNER_POSITION = this.get('hotCornerPosition'); if (this.HOT_CORNER_POSITION === 6 && this.DASH_VISIBLE) this.HOT_CORNER_EDGE = true; else @@ -451,13 +508,24 @@ var Options = class Options { else this.HOT_CORNER_POSITION = 0; } - this.HOT_CORNER_FULLSCREEN = this.get('hotCornerFullscreen', true); - this.HOT_CORNER_RIPPLES = this.get('hotCornerRipples', true); + this.HOT_CORNER_FULLSCREEN = this.get('hotCornerFullscreen'); + this.HOT_CORNER_RIPPLES = this.get('hotCornerRipples'); - this.ALWAYS_ACTIVATE_SELECTED_WINDOW = this.get('alwaysActivateSelectedWindow', true); - this.WINDOW_ICON_CLICK_SEARCH = this.get('windowIconClickSearch', true); + this.ALWAYS_ACTIVATE_SELECTED_WINDOW = this.get('alwaysActivateSelectedWindow'); + this.WIN_PREVIEW_SEC_BTN_ACTION = this.get('winPreviewSecBtnAction'); + this.WIN_PREVIEW_MID_BTN_ACTION = this.get('winPreviewMidBtnAction'); + this.SHOW_CLOSE_BUTTON = this.get('winPreviewShowCloseButton'); + this.WINDOW_ICON_CLICK_ACTION = this.get('windowIconClickAction'); - this.OVERLAY_KEY_SECONDARY = this.get('overlayKeySecondary', true); + this.OVERLAY_KEY_PRIMARY = this.get('overlayKeyPrimary'); + this.OVERLAY_KEY_SECONDARY = this.get('overlayKeySecondary'); + + this.ESC_BEHAVIOR = this.get('overviewEscBehavior'); + + this.WINDOW_THUMBNAIL_ENABLED = this.get('windowThumbnailModule'); + this.WINDOW_THUMBNAIL_SCALE = this.get('windowThumbnailScale') / 100; + + this.FIX_NEW_WINDOW_FOCUS = this.get('newWindowFocusFix'); } _getAnimationDirection() { diff --git a/extensions/45/vertical-workspaces/lib/swipeTracker.js b/extensions/45/vertical-workspaces/lib/swipeTracker.js new file mode 100644 index 0000000..354f1e0 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/swipeTracker.js @@ -0,0 +1,116 @@ +/** + * V-Shell (Vertical Workspaces) + * swipeTracker.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as SwipeTracker from 'resource:///org/gnome/shell/ui/swipeTracker.js'; + +let Me; +let opt; + +export const SwipeTrackerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('swipeTrackerModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' SwipeTrackerModule - Keeping untouched'); + } + + _activateModule() { + if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL + this._setVertical(); + } else { + this._setHorizontal(); + } + console.debug(' SwipeTrackerModule - Activated'); + } + + _disableModule() { + this._setHorizontal(); + + console.debug(' SwipeTrackerModule - Disabled'); + } + + _setVertical() { + // reverse swipe gestures for enter/leave overview and ws switching + Main.overview._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL; + Main.wm._workspaceAnimation._swipeTracker.orientation = Clutter.Orientation.VERTICAL; + // overview's updateGesture() function should reflect ws tmb position to match appGrid/ws animation direction + // function in connection cannot be overridden in prototype of its class because connected is actually another copy of the original function + if (!this._originalGestureUpdateId) { + this._originalGestureUpdateId = GObject.signal_handler_find(Main.overview._swipeTracker._touchpadGesture, { signalId: 'update' }); + Main.overview._swipeTracker._touchpadGesture.block_signal_handler(this._originalGestureUpdateId); + Main.overview._swipeTracker._updateGesture = SwipeTrackerVertical._updateGesture; + this._vwGestureUpdateId = Main.overview._swipeTracker._touchpadGesture.connect('update', SwipeTrackerVertical._updateGesture.bind(Main.overview._swipeTracker)); + } + } + + _setHorizontal() { + // original swipeTrackers' orientation and updateGesture function + Main.overview._swipeTracker.orientation = Clutter.Orientation.VERTICAL; + Main.wm._workspaceAnimation._swipeTracker.orientation = Clutter.Orientation.HORIZONTAL; + Main.overview._swipeTracker._updateGesture = SwipeTracker.SwipeTracker.prototype._updateGesture; + if (this._vwGestureUpdateId) { + Main.overview._swipeTracker._touchpadGesture.disconnect(this._vwGestureUpdateId); + this._vwGestureUpdateId = 0; + } + if (this._originalGestureUpdateId) { + Main.overview._swipeTracker._touchpadGesture.unblock_signal_handler(this._originalGestureUpdateId); + this._originalGestureUpdateId = 0; + } + } +}; + +const SwipeTrackerVertical = { + _updateGesture(gesture, time, delta, distance) { + if (this._state !== 1) // State.SCROLLING) + return; + + if ((this._allowedModes & Main.actionMode) === 0 || !this.enabled) { + this._interrupt(); + return; + } + + if (opt.WS_TMB_RIGHT) + delta = -delta; + this._progress += delta / distance; + this._history.append(time, delta); + + this._progress = Math.clamp(this._progress, ...this._getBounds(this._initialProgress)); + this.emit('update', this._progress); + }, +}; diff --git a/extensions/vertical-workspaces/lib/util.js b/extensions/45/vertical-workspaces/lib/util.js similarity index 59% rename from extensions/vertical-workspaces/lib/util.js rename to extensions/45/vertical-workspaces/lib/util.js index 5f5c069..38ca6cd 100644 --- a/extensions/vertical-workspaces/lib/util.js +++ b/extensions/45/vertical-workspaces/lib/util.js @@ -10,25 +10,41 @@ 'use strict'; -const Gi = imports._gi; -const { Shell, Meta, Clutter } = imports.gi; +import GLib from 'gi://GLib'; +import Clutter from 'gi://Clutter'; +import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; +import Gio from 'gi://Gio'; -const Config = imports.misc.config; -const Main = imports.ui.main; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import { InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js'; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +let Me; +let _installedExtensions; -var shellVersion = parseFloat(Config.PACKAGE_VERSION); +export function init(me) { + Me = me; +} -var Overrides = class { +export function cleanGlobals() { + Me = null; + _installedExtensions = null; +} + +export class Overrides extends InjectionManager { constructor() { + super(); this._overrides = {}; } addOverride(name, prototype, overrideList) { + const backup = this.overrideProto(prototype, overrideList, name); + // don't update originals when override's just refreshing, keep initial content + let originals = this._overrides[name]?.originals; + if (!originals) + originals = backup; this._overrides[name] = { - originals: this.overrideProto(prototype, overrideList), + originals, prototype, }; } @@ -38,29 +54,29 @@ var Overrides = class { if (!override) return false; - this.overrideProto(override.prototype, override.originals); - this._overrides[name] = undefined; + this.overrideProto(override.prototype, override.originals, name); + delete this._overrides[name]; return true; } removeAll() { for (let name in this._overrides) { this.removeOverride(name); - this._overrides[name] = undefined; + delete this._overrides[name]; } } - hookVfunc(proto, symbol, func) { - proto[Gi.hook_up_vfunc_symbol](symbol, func); - } - - overrideProto(proto, overrides) { + overrideProto(proto, overrides, name) { const backup = {}; - + const originals = this._overrides[name]?.originals; for (let symbol in overrides) { if (symbol.startsWith('after_')) { const actualSymbol = symbol.slice('after_'.length); - const fn = proto[actualSymbol]; + let fn; + if (originals && originals[actualSymbol]) + fn = originals[actualSymbol]; + else + fn = proto[actualSymbol]; const afterFn = overrides[symbol]; proto[actualSymbol] = function (...args) { args = Array.prototype.slice.call(args); @@ -69,128 +85,58 @@ var Overrides = class { return res; }; backup[actualSymbol] = fn; - } else { + } else if (overrides[symbol] !== null) { backup[symbol] = proto[symbol]; - if (symbol.startsWith('vfunc')) { - if (shellVersion < 42) - this.hookVfunc(proto, symbol.slice(6), overrides[symbol]); - else - this.hookVfunc(proto[Gi.gobject_prototype_symbol], symbol.slice(6), overrides[symbol]); - } else { - proto[symbol] = overrides[symbol]; - } + this._installMethod(proto, symbol, overrides[symbol]); } } return backup; } -}; - -function getOverviewTranslations(opt, dash, tmbBox, searchEntryBin) { - // const tmbBox = Main.overview._overview._controls._thumbnailsBox; - let searchTranslationY = 0; - if (searchEntryBin.visible) { - const offset = (dash.visible && (!opt.DASH_VERTICAL ? dash.height + 12 : 0)) + - (opt.WS_TMB_TOP ? tmbBox.height + 12 : 0); - searchTranslationY = -searchEntryBin.height - offset - 30; - } - - let tmbTranslationX = 0; - let tmbTranslationY = 0; - let offset; - if (tmbBox.visible) { - switch (opt.WS_TMB_POSITION) { - case 3: // left - offset = 10 + (dash?.visible && opt.DASH_LEFT ? dash.width : 0); - tmbTranslationX = -tmbBox.width - offset; - tmbTranslationY = 0; - break; - case 1: // right - offset = 10 + (dash?.visible && opt.DASH_RIGHT ? dash.width : 0); - tmbTranslationX = tmbBox.width + offset; - tmbTranslationY = 0; - break; - case 0: // top - offset = 10 + (dash?.visible && opt.DASH_TOP ? dash.height : 0) + Main.panel.height; - tmbTranslationX = 0; - tmbTranslationY = -tmbBox.height - offset; - break; - case 2: // bottom - offset = 10 + (dash?.visible && opt.DASH_BOTTOM ? dash.height : 0) + Main.panel.height; // just for case the panel is at bottom - tmbTranslationX = 0; - tmbTranslationY = tmbBox.height + offset; - break; - } - } - - let dashTranslationX = 0; - let dashTranslationY = 0; - let position = opt.DASH_POSITION; - // if DtD replaced the original Dash, read its position - if (dashIsDashToDock()) - position = dash._position; - - if (dash?.visible) { - switch (position) { - case 0: // top - dashTranslationX = 0; - dashTranslationY = -dash.height - dash.margin_bottom - Main.panel.height; - break; - case 1: // right - dashTranslationX = dash.width; - dashTranslationY = 0; - break; - case 2: // bottom - dashTranslationX = 0; - dashTranslationY = dash.height + dash.margin_bottom + Main.panel.height; - break; - case 3: // left - dashTranslationX = -dash.width; - dashTranslationY = 0; - break; - } - } - - return [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY]; } -function openPreferences() { +export function openPreferences(metadata) { + if (!metadata) + metadata = Me.metadata; const windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null); let tracker = Shell.WindowTracker.get_default(); - let metaWin, isVW = null; + let metaWin, isMe = null; for (let win of windows) { const app = tracker.get_window_app(win); - if (win.get_title().includes(Me.metadata.name) && app.get_name() === 'Extensions') { + if (win.get_title()?.includes(metadata.name) && app.get_name() === 'Extensions') { // this is our existing window metaWin = win; - isVW = true; + isMe = true; break; - } else if (win.wm_class.includes('org.gnome.Shell.Extensions')) { + } else if (win.wm_class?.includes('org.gnome.Shell.Extensions')) { // this is prefs window of another extension metaWin = win; - isVW = false; + isMe = false; } } - if (metaWin && !isVW) { + if (metaWin && !isMe) { // other prefs window blocks opening another prefs window, so close it metaWin.delete(global.get_current_time()); - } else if (metaWin && isVW) { + } else if (metaWin && isMe) { // if prefs window already exist, move it to the current WS and activate it metaWin.change_workspace(global.workspace_manager.get_active_workspace()); metaWin.activate(global.get_current_time()); } - if (!metaWin || (metaWin && !isVW)) { - try { - Main.extensionManager.openExtensionPrefs(Me.metadata.uuid, '', {}); - } catch (e) { - log(e); - } + if (!metaWin || (metaWin && !isMe)) { + // delay to avoid errors if previous prefs window has been colsed + GLib.idle_add(GLib.PRIORITY_LOW, () => { + try { + Main.extensionManager.openExtensionPrefs(metadata.uuid, '', {}); + } catch (e) { + console.error(e); + } + }); } } -function activateSearchProvider(prefix = '') { +export function activateSearchProvider(prefix = '') { const searchEntry = Main.overview.searchEntry; if (!searchEntry.get_text() || !searchEntry.get_text().startsWith(prefix)) { prefix = `${prefix} `; @@ -203,16 +149,16 @@ function activateSearchProvider(prefix = '') { } } -function dashNotDefault() { +export function dashNotDefault() { return Main.overview.dash !== Main.overview._overview._controls.layoutManager._dash; } -function dashIsDashToDock() { +export function dashIsDashToDock() { return Main.overview.dash._isHorizontal !== undefined; } // Reorder Workspaces - callback for Dash and workspacesDisplay -function reorderWorkspace(direction = 0) { +export function reorderWorkspace(direction = 0) { let activeWs = global.workspace_manager.get_active_workspace(); let activeWsIdx = activeWs.index(); let targetIdx = activeWsIdx + direction; @@ -220,7 +166,14 @@ function reorderWorkspace(direction = 0) { global.workspace_manager.reorder_workspace(activeWs, targetIdx); } -function exposeWindows(adjustment, activateKeyboard) { +export function activateKeyboardForWorkspaceView() { + Main.ctrlAltTabManager._items.forEach(i => { + if (i.sortGroup === 1 && i.name === 'Windows') + Main.ctrlAltTabManager.focusGroup(i); + }); +} + +export function exposeWindows(adjustment, activateKeyboard) { // expose windows for static overview modes if (!adjustment.value && !Main.overview._animationInProgress) { if (adjustment.value === 0) { @@ -241,25 +194,25 @@ function exposeWindows(adjustment, activateKeyboard) { } } -function isShiftPressed(state = null) { +export function isShiftPressed(state = null) { if (state === null) [,, state] = global.get_pointer(); return (state & Clutter.ModifierType.SHIFT_MASK) !== 0; } -function isCtrlPressed(state = null) { +export function isCtrlPressed(state = null) { if (state === null) [,, state] = global.get_pointer(); return (state & Clutter.ModifierType.CONTROL_MASK) !== 0; } -function isAltPressed(state = null) { +export function isAltPressed(state = null) { if (state === null) [,, state] = global.get_pointer(); return (state & Clutter.ModifierType.MOD1_MASK) !== 0; } -function fuzzyMatch(term, text) { +export function fuzzyMatch(term, text) { let pos = -1; const matches = []; // convert all accented chars to their basic form and to lower case @@ -295,7 +248,7 @@ function fuzzyMatch(term, text) { return matches.reduce((r, p) => r + p) - matches.length * matches[0] + matches[0]; } -function strictMatch(term, text) { +export function strictMatch(term, text) { // remove diacritics and accents from letters let s = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); let p = term.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); @@ -309,7 +262,7 @@ function strictMatch(term, text) { return 0; } -function isMoreRelevant(stringA, stringB, pattern) { +export function isMoreRelevant(stringA, stringB, pattern) { let regex = /[^a-zA-Z\d]/; let strSplitA = stringA.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().split(regex); let strSplitB = stringB.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().split(regex); @@ -330,16 +283,51 @@ function isMoreRelevant(stringA, stringB, pattern) { return !aAny && bAny; } -function getEnabledExtensions(uuid = '') { - let extensions = []; - Main.extensionManager._extensions.forEach(e => { - if (e.state === 1 && e.uuid.includes(uuid)) - extensions.push(e); - }); - return extensions; +export function getEnabledExtensions(pattern = '') { + let result = []; + // extensionManager is unreliable at startup (if not all extensions were loaded) + // but gsettings key can contain removed extensions... + // therefore we have to look into filesystem, what's really installed + if (!_installedExtensions) { + const extensionFiles = [...collectFromDatadirs('extensions', true)]; + _installedExtensions = extensionFiles.map(({ info }) => { + let fileType = info.get_file_type(); + if (fileType !== Gio.FileType.DIRECTORY) + return null; + const uuid = info.get_name(); + return uuid; + }); + } + const enabled = Main.extensionManager._enabledExtensions; + result = _installedExtensions.filter(ext => enabled.includes(ext)); + return result.filter(uuid => uuid !== null && uuid.includes(pattern)); } -function getScrollDirection(event) { +function* collectFromDatadirs(subdir, includeUserDir) { + let dataDirs = GLib.get_system_data_dirs(); + if (includeUserDir) + dataDirs.unshift(GLib.get_user_data_dir()); + + for (let i = 0; i < dataDirs.length; i++) { + let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]); + let dir = Gio.File.new_for_path(path); + + let fileEnum; + try { + fileEnum = dir.enumerate_children('standard::name,standard::type', + Gio.FileQueryInfoFlags.NONE, null); + } catch (e) { + fileEnum = null; + } + if (fileEnum !== null) { + let info; + while ((info = fileEnum.next_file(null))) + yield { dir: fileEnum.get_child(info), info }; + } + } +} + +export function getScrollDirection(event) { // scroll wheel provides two types of direction information: // 1. Clutter.ScrollDirection.DOWN / Clutter.ScrollDirection.UP // 2. Clutter.ScrollDirection.SMOOTH + event.get_scroll_delta() @@ -362,3 +350,15 @@ function getScrollDirection(event) { return direction; } + +export function getWindows(workspace) { + // We ignore skip-taskbar windows in switchers, but if they are attached + // to their parent, their position in the MRU list may be more appropriate + // than the parent; so start with the complete list ... + let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, workspace); + // ... map windows to their parent where appropriate ... + return windows.map(w => { + return w.is_attached_dialog() ? w.get_transient_for() : w; + // ... and filter out skip-taskbar windows and duplicates + }).filter((w, i, a) => !w.skip_taskbar && a.indexOf(w) === i); +} diff --git a/extensions/45/vertical-workspaces/lib/winTmb.js b/extensions/45/vertical-workspaces/lib/winTmb.js new file mode 100644 index 0000000..4205822 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/winTmb.js @@ -0,0 +1,525 @@ +/** + * V-Shell (Vertical Workspaces) + * WinTmb + * + * @author GdH + * @copyright 2021-2023 + * @license GPL-3.0 + */ + +'use strict'; + +import GLib from 'gi://GLib'; +import Clutter from 'gi://Clutter'; +import St from 'gi://St'; +import Meta from 'gi://Meta'; +import GObject from 'gi://GObject'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; +import * as AltTab from 'resource:///org/gnome/shell/ui/altTab.js'; + +let Me; +let opt; + +const SCROLL_ICON_OPACITY = 240; +const DRAG_OPACITY = 200; +const CLOSE_BTN_OPACITY = 240; + + +export const WinTmbModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this._removeTimeouts(); + + this.moduleEnabled = opt.get('windowThumbnailModule'); + + reset = reset || !this.moduleEnabled; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WinTmb - Keeping untouched'); + } + + _activateModule() { + this._timeouts = {}; + if (!this._windowThumbnails) + this._windowThumbnails = []; + + Main.overview.connectObject('hidden', () => this.showThumbnails(), this); + console.debug(' WinTmb - Activated'); + } + + _disableModule() { + Main.overview.disconnectObject(this); + this._disconnectStateAdjustment(); + this.removeAllThumbnails(); + console.debug(' WinTmb - Disabled'); + } + + _removeTimeouts() { + if (this._timeouts) { + Object.values(this._timeouts).forEach(t => { + if (t) + GLib.source_remove(t); + }); + this._timeouts = null; + } + } + + createThumbnail(metaWin) { + const thumbnail = new WindowThumbnail(metaWin, { + 'height': Math.floor(opt.WINDOW_THUMBNAIL_SCALE * global.display.get_monitor_geometry(global.display.get_current_monitor()).height), + 'thumbnailsOnScreen': this._windowThumbnails.length, + }); + + this._windowThumbnails.push(thumbnail); + thumbnail.connect('removed', tmb => { + this._windowThumbnails.splice(this._windowThumbnails.indexOf(tmb), 1); + tmb.destroy(); + if (!this._windowThumbnails.length) + this._disconnectStateAdjustment(); + }); + + if (!this._stateAdjustmentConId) { + this._stateAdjustmentConId = Main.overview._overview.controls._stateAdjustment.connectObject('notify::value', () => { + if (!this._thumbnailsHidden && (!opt.OVERVIEW_MODE2 || opt.WORKSPACE_MODE)) + this.hideThumbnails(); + }, this); + } + } + + hideThumbnails() { + this._windowThumbnails.forEach(tmb => { + tmb.ease({ + opacity: 0, + duration: 200, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => tmb.hide(), + }); + }); + this._thumbnailsHidden = true; + } + + showThumbnails() { + this._windowThumbnails.forEach(tmb => { + tmb.show(); + tmb.ease({ + opacity: 255, + duration: 100, + mode: Clutter.AnimationMode.LINEAR, + }); + }); + this._thumbnailsHidden = false; + } + + removeAllThumbnails() { + this._windowThumbnails.forEach(tmb => tmb.remove()); + this._windowThumbnails = []; + } + + _disconnectStateAdjustment() { + Main.overview._overview.controls._stateAdjustment.disconnectObject(this); + } +}; + +const WindowThumbnail = GObject.registerClass({ + Signals: { 'removed': {} }, +}, class WindowThumbnail extends St.Widget { + _init(metaWin, args) { + this._hoverShowsPreview = false; + this._customOpacity = 255; + this._initTmbHeight = args.height; + this._minimumHeight = Math.floor(5 / 100 * global.display.get_monitor_geometry(global.display.get_current_monitor()).height); + this._scrollTimeout = 100; + this._positionOffset = args.thumbnailsOnScreen; + this._reverseTmbWheelFunc = false; + this._click_count = 1; + this._prevBtnPressTime = 0; + this.w = metaWin; + super._init({ + layout_manager: new Clutter.BinLayout(), + visible: true, + reactive: true, + can_focus: true, + track_hover: true, + }); + this.connect('button-release-event', this._onBtnReleased.bind(this)); + this.connect('scroll-event', this._onScrollEvent.bind(this)); + // this.connect('motion-event', this._onMouseMove.bind(this)); // may be useful in the future.. + + this._delegate = this; + this._draggable = DND.makeDraggable(this, { dragActorOpacity: DRAG_OPACITY }); + this._draggable.connect('drag-end', this._end_drag.bind(this)); + this._draggable.connect('drag-cancelled', this._end_drag.bind(this)); + this._draggable._animateDragEnd = eventTime => { + this._draggable._animationInProgress = true; + this._draggable._onAnimationComplete(this._draggable._dragActor, eventTime); + this.opacity = this._customOpacity; + }; + + this.clone = new Clutter.Clone({ reactive: true }); + Main.layoutManager.addChrome(this); + + this.window = this.w.get_compositor_private(); + + this.clone.set_source(this.window); + + this.add_child(this.clone); + this._addCloseButton(); + this._addScrollModeIcon(); + + this.connect('enter-event', () => { + global.display.set_cursor(Meta.Cursor.POINTING_HAND); + this._closeButton.opacity = CLOSE_BTN_OPACITY; + this._scrollModeBin.opacity = SCROLL_ICON_OPACITY; + if (this._hoverShowsPreview && !Main.overview._shown) { + this._closeButton.opacity = 50; + this._showWindowPreview(false, true); + } + }); + + this.connect('leave-event', () => { + global.display.set_cursor(Meta.Cursor.DEFAULT); + this._closeButton.opacity = 0; + this._scrollModeBin.opacity = 0; + if (this._winPreview) + this._destroyWindowPreview(); + }); + + this._setSize(true); + this.set_position(...this._getInitialPosition()); + this.show(); + this.window_id = this.w.get_id(); + this.tmbRedrawDirection = true; + + // remove thumbnail content and hide thumbnail if its window is destroyed + this.windowConnect = this.window.connect('destroy', () => { + if (this) + this.remove(); + }); + } + + _getInitialPosition() { + const offset = 20; + let monitor = Main.layoutManager.monitors[global.display.get_current_monitor()]; + let x = Math.min(monitor.x + monitor.width - (this.window.width * this.scale) - offset); + let y = Math.min(monitor.y + monitor.height - (this.window.height * this.scale) - offset - ((this._positionOffset * this._initTmbHeight) % (monitor.height - this._initTmbHeight))); + return [x, y]; + } + + _setSize(resetScale = false) { + if (resetScale) + this.scale = Math.min(1.0, this._initTmbHeight / this.window.height); + + const width = this.window.width * this.scale; + const height = this.window.height * this.scale; + this.set_size(width, height); + /* if (this.icon) { + this.icon.scale_x = this.scale; + this.icon.scale_y = this.scale; + }*/ + + // when the scale of this. actor change, this.clone resize accordingly, + // but the reactive area of the actor doesn't change until the actor is redrawn + // this updates the actor's input region area: + Main.layoutManager._queueUpdateRegions(); + } + + /* _onMouseMove(actor, event) { + let [pos_x, pos_y] = event.get_coords(); + let state = event.get_state(); + if (this._ctrlPressed(state)) { + } + }*/ + + _onBtnReleased(actor, event) { + // Clutter.Event.click_count property in no longer available, since GS42 + if ((event.get_time() - this._prevBtnPressTime) < Clutter.Settings.get_default().double_click_time) + this._click_count += 1; + else + this._click_count = 1; + + this._prevBtnPressTime = event.get_time(); + + if (this._click_count === 2 && event.get_button() === Clutter.BUTTON_PRIMARY) + this.w.activate(global.get_current_time()); + + + const button = event.get_button(); + const state = event.get_state(); + switch (button) { + case Clutter.BUTTON_PRIMARY: + if (this._ctrlPressed(state)) { + this._setSize(); + } else { + this._reverseTmbWheelFunc = !this._reverseTmbWheelFunc; + this._scrollModeBin.set_child(this._reverseTmbWheelFunc ? this._scrollModeSourceIcon : this._scrollModeResizeIcon); + } + return Clutter.EVENT_STOP; + case Clutter.BUTTON_SECONDARY: + if (this._ctrlPressed(state)) { + this.remove(); + } else { + this._hoverShowsPreview = !this._hoverShowsPreview; + this._showWindowPreview(); + } + return Clutter.EVENT_STOP; + case Clutter.BUTTON_MIDDLE: + if (this._ctrlPressed(state)) + this.w.delete(global.get_current_time()); + return Clutter.EVENT_STOP; + default: + return Clutter.EVENT_PROPAGATE; + } + } + + _onScrollEvent(actor, event) { + let direction = Me.Util.getScrollDirection(event); + + if (this._actionTimeoutActive()) + return Clutter.EVENT_PROPAGATE; + let state = event.get_state(); + switch (direction) { + case Clutter.ScrollDirection.UP: + if (this._shiftPressed(state)) { + this.opacity = Math.min(255, this.opacity + 24); + this._customOpacity = this.opacity; + } else if (this._reverseTmbWheelFunc !== this._ctrlPressed(state)) { + this._switchSourceWin(-1); + } else if (this._reverseTmbWheelFunc === this._ctrlPressed(state)) { + this.scale = Math.max(0.05, this.scale - 0.025); + } + break; + case Clutter.ScrollDirection.DOWN: + if (this._shiftPressed(state)) { + this.opacity = Math.max(48, this.opacity - 24); + this._customOpacity = this.opacity; + } else if (this._reverseTmbWheelFunc !== this._ctrlPressed(state)) { + this._switchSourceWin(+1); + } else if (this._reverseTmbWheelFunc === this._ctrlPressed(state)) { + this.scale = Math.min(1, this.scale + 0.025); + } + break; + default: + return Clutter.EVENT_PROPAGATE; + } + this._setSize(); + return Clutter.EVENT_STOP; + } + + remove() { + if (this.clone) { + this.window.disconnect(this.windowConnect); + this.clone.set_source(null); + } + if (this._winPreview) + this._destroyWindowPreview(); + + this.emit('removed'); + } + + _end_drag() { + this.set_position(this._draggable._dragOffsetX + this._draggable._dragX, this._draggable._dragOffsetY + this._draggable._dragY); + this._setSize(); + } + + _ctrlPressed(state) { + return (state & Clutter.ModifierType.CONTROL_MASK) !== 0; + } + + _shiftPressed(state) { + return (state & Clutter.ModifierType.SHIFT_MASK) !== 0; + } + + _switchSourceWin(direction) { + let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null); + windows = windows.filter(w => !(w.skip_taskbar || w.minimized)); + let idx = -1; + for (let i = 0; i < windows.length; i++) { + if (windows[i] === this.w) { + idx = i + direction; + break; + } + } + idx = idx >= windows.length ? 0 : idx; + idx = idx < 0 ? windows.length - 1 : idx; + let w = windows[idx]; + let win = w.get_compositor_private(); + this.clone.set_source(win); + this.window.disconnect(this.windowConnect); + // the new thumbnail should be the same height as the previous one + this.scale = (this.scale * this.window.height) / win.height; + this.window = win; + this.windowConnect = this.window.connect('destroy', () => { + if (this) + this.remove(); + }); + this.w = w; + + if (this._winPreview) + this._showWindowPreview(true); + } + + _actionTimeoutActive() { + const timeout = this._reverseTmbWheelFunc ? this._scrollTimeout : this._scrollTimeout / 4; + if (!this._lastActionTime || Date.now() - this._lastActionTime > timeout) { + this._lastActionTime = Date.now(); + return false; + } + return true; + } + + /* _setIcon() { + let tracker = Shell.WindowTracker.get_default(); + let app = tracker.get_window_app(this.w); + let icon = app + ? app.create_icon_texture(this.height) + : new St.Icon({ icon_name: 'icon-missing', icon_size: this.height }); + icon.x_expand = icon.y_expand = true; + if (this.icon) + this.icon.destroy(); + this.icon = icon; + }*/ + + _addCloseButton() { + const closeButton = new St.Button({ + opacity: 0, + style_class: 'window-close', + child: new St.Icon({ icon_name: 'preview-close-symbolic' }), + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.START, + x_expand: true, + y_expand: true, + }); + + closeButton.set_style(` + margin: 3px; + background-color: rgba(200, 0, 0, 0.9); + `); + + closeButton.connect('clicked', () => { + this.remove(); + return Clutter.EVENT_STOP; + }); + + this._closeButton = closeButton; + this.add_child(this._closeButton); + } + + _addScrollModeIcon() { + this._scrollModeBin = new St.Bin({ + x_expand: true, + y_expand: true, + }); + this._scrollModeResizeIcon = new St.Icon({ + icon_name: 'view-fullscreen-symbolic', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + x_expand: true, + y_expand: true, + opacity: SCROLL_ICON_OPACITY, + style_class: 'icon-dropshadow', + scale_x: 0.5, + scale_y: 0.5, + }); + this._scrollModeResizeIcon.set_style(` + margin: 13px; + color: rgb(255, 255, 255); + box-shadow: 0 0 40px 40px rgba(0,0,0,0.7); + `); + this._scrollModeSourceIcon = new St.Icon({ + icon_name: 'media-skip-forward-symbolic', + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + x_expand: true, + y_expand: true, + opacity: SCROLL_ICON_OPACITY, + style_class: 'icon-dropshadow', + scale_x: 0.5, + scale_y: 0.5, + }); + this._scrollModeSourceIcon.set_style(` + margin: 13px; + color: rgb(255, 255, 255); + box-shadow: 0 0 40px 40px rgba(0,0,0,0.7); + `); + this._scrollModeBin.set_child(this._scrollModeResizeIcon); + this.add_child(this._scrollModeBin); + this._scrollModeBin.opacity = 0; + } + + _showWindowPreview(update = false, dontDestroy = false) { + if (this._winPreview && !dontDestroy) { + this._destroyWindowPreview(); + this._previewCreationTime = 0; + this._closeButton.opacity = CLOSE_BTN_OPACITY; + if (!update) + return; + } + + if (!this._winPreview) { + this._winPreview = new AltTab.CyclerHighlight(); + global.window_group.add_actor(this._winPreview); + [this._winPreview._xPointer, this._winPreview._yPointer] = global.get_pointer(); + } + + if (!update) { + this._winPreview.opacity = 0; + this._winPreview.ease({ + opacity: 255, + duration: 70, + mode: Clutter.AnimationMode.LINEAR, + /* onComplete: () => { + this._closeButton.opacity = 50; + },*/ + }); + + this.ease({ + opacity: Math.min(50, this._customOpacity), + duration: 70, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => { + }, + }); + } else { + this._winPreview.opacity = 255; + } + this._winPreview.window = this.w; + this._winPreview._window = this.w; + global.window_group.set_child_above_sibling(this._winPreview, null); + } + + _destroyWindowPreview() { + if (this._winPreview) { + this._winPreview.ease({ + opacity: 0, + duration: 100, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => { + this._winPreview.destroy(); + this._winPreview = null; + this.opacity = this._customOpacity; + }, + }); + } + } +}); diff --git a/extensions/45/vertical-workspaces/lib/windowAttentionHandler.js b/extensions/45/vertical-workspaces/lib/windowAttentionHandler.js new file mode 100644 index 0000000..d49c1ad --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/windowAttentionHandler.js @@ -0,0 +1,156 @@ +/** + * V-Shell (Vertical Workspaces) + * windowAttentionHandler.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js'; + +let Me; +let opt; + +export const WindowAttentionHandlerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('windowAttentionHandlerModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WindowAttentionHandlerModule - Keeping untouched'); + } + + _activateModule() { + this._updateConnections(); + console.debug(' WindowAttentionHandlerModule - Activated'); + } + + _disableModule() { + const reset = true; + this._updateConnections(reset); + + console.debug(' WindowAttentionHandlerModule - Disabled'); + } + + _updateConnections(reset) { + global.display.disconnectObject(Main.windowAttentionHandler); + + const handlerFnc = reset + ? Main.windowAttentionHandler._onWindowDemandsAttention + : WindowAttentionHandlerCommon._onWindowDemandsAttention; + + global.display.connectObject( + 'window-demands-attention', handlerFnc.bind(Main.windowAttentionHandler), + 'window-marked-urgent', handlerFnc.bind(Main.windowAttentionHandler), + Main.windowAttentionHandler); + } +}; + +const WindowAttentionHandlerCommon = { + _onWindowDemandsAttention(display, window) { + // Deny attention notifications if the App Grid is open, to avoid notification spree when opening a folder + if (Main.overview._shown && Main.overview.dash.showAppsButton.checked) { + return; + } else if (opt.WINDOW_ATTENTION_FOCUS_IMMEDIATELY) { + if (!Main.overview._shown) + Main.activateWindow(window); + return; + } + + const app = this._tracker.get_window_app(window); + // const source = new WindowAttentionHandler.WindowAttentionSource(app, window); + const source = new MessageTray.Source(app.get_name()); + new Me.Util.Overrides().addOverride('MessageSource', source, WindowAttentionSourceCommon); + source._init(app, window); + Main.messageTray.add(source); + + let [title, banner] = this._getTitleAndBanner(app, window); + + const notification = new MessageTray.Notification(source, title, banner); + notification.connect('activated', () => { + source.open(); + }); + notification.setForFeedback(true); + + if (opt.WINDOW_ATTENTION_DISABLE_NOTIFICATIONS) + // just push the notification to the message tray without showing notification + source.pushNotification(notification); + else + source.showNotification(notification); + + window.connectObject('notify::title', () => { + [title, banner] = this._getTitleAndBanner(app, window); + notification.update(title, banner); + }, source); + }, +}; + +const WindowAttentionSourceCommon = { + _init(app, window) { + this._window = window; + this._app = app; + + this._window.connectObject( + 'notify::demands-attention', this._sync.bind(this), + 'notify::urgent', this._sync.bind(this), + 'focus', () => this.destroy(), + 'unmanaged', () => this.destroy(), this); + }, + + _sync() { + if (this._window.demands_attention || this._window.urgent) + return; + this.destroy(); + }, + + _createPolicy() { + if (this._app && this._app.get_app_info()) { + let id = this._app.get_id().replace(/\.desktop$/, ''); + return new MessageTray.NotificationApplicationPolicy(id); + } else { + return new MessageTray.NotificationGenericPolicy(); + } + }, + + createIcon(size) { + return this._app.create_icon_texture(size); + }, + + destroy(params) { + this._window.disconnectObject(this); + + MessageTray.Source.prototype.destroy.bind(this)(params); + }, + + open() { + Main.activateWindow(this._window); + }, +}; diff --git a/extensions/vertical-workspaces/lib/windowManager.js b/extensions/45/vertical-workspaces/lib/windowManager.js similarity index 53% rename from extensions/vertical-workspaces/lib/windowManager.js rename to extensions/45/vertical-workspaces/lib/windowManager.js index 2d46b0b..dd467cb 100644 --- a/extensions/vertical-workspaces/lib/windowManager.js +++ b/extensions/45/vertical-workspaces/lib/windowManager.js @@ -10,81 +10,101 @@ 'use strict'; -const { GObject, Clutter, Meta } = imports.gi; +import Clutter from 'gi://Clutter'; +import Meta from 'gi://Meta'; +import GObject from 'gi://GObject'; -const Main = imports.ui.main; -const WindowManager = imports.ui.windowManager; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; -let _overrides; - -const MINIMIZE_WINDOW_ANIMATION_TIME = WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME; -const MINIMIZE_WINDOW_ANIMATION_MODE = WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as WindowManager from 'resource:///org/gnome/shell/ui/windowManager.js'; +let Me; let opt; -let _firstRun = true; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('windowManagerModule', true); - reset = reset || !moduleEnabled; +export const WindowManagerModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // don't even touch this module if disabled - if (_firstRun && reset) - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; - _firstRun = false; + this._originalMinimizeSigId = 0; + this._minimizeSigId = 0; + this._originalUnminimizeSigId = 0; + this._unminimizeSigId = 0; + } - if (_overrides) - _overrides.removeAll(); - - - _replaceMinimizeFunction(reset); - - - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; } - _overrides = new _Util.Overrides(); + update(reset) { + this.moduleEnabled = opt.get('windowManagerModule'); + const conflict = false; - _overrides.addOverride('WindowManager', WindowManager.WindowManager.prototype, WindowManagerCommon); -} + reset = reset || !this.moduleEnabled || conflict; -// ------------- Fix and adapt minimize/unminimize animations -------------------------------------- - -let _originalMinimizeSigId; -let _minimizeSigId; -let _originalUnminimizeSigId; -let _unminimizeSigId; - -function _replaceMinimizeFunction(reset = false) { - if (reset) { - Main.wm._shellwm.disconnect(_minimizeSigId); - _minimizeSigId = 0; - Main.wm._shellwm.unblock_signal_handler(_originalMinimizeSigId); - _originalMinimizeSigId = 0; - - Main.wm._shellwm.disconnect(_unminimizeSigId); - _unminimizeSigId = 0; - Main.wm._shellwm.unblock_signal_handler(_originalUnminimizeSigId); - _originalUnminimizeSigId = 0; - } else if (!_minimizeSigId) { - _originalMinimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'minimize' }); - if (_originalMinimizeSigId) { - Main.wm._shellwm.block_signal_handler(_originalMinimizeSigId); - _minimizeSigId = Main.wm._shellwm.connect('minimize', WindowManagerCommon._minimizeWindow.bind(Main.wm)); - } - - _originalUnminimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'unminimize' }); - if (_originalUnminimizeSigId) { - Main.wm._shellwm.block_signal_handler(_originalUnminimizeSigId); - _unminimizeSigId = Main.wm._shellwm.connect('unminimize', WindowManagerCommon._unminimizeWindow.bind(Main.wm)); + // don't even touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); } + if (reset && this._firstActivation) + console.debug(' WindowManagerModule - Keeping untouched'); } -} + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WindowManager', WindowManager.WindowManager.prototype, WindowManagerCommon); + + if (!this._minimizeSigId) { + this._originalMinimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'minimize' }); + if (this._originalMinimizeSigId) { + Main.wm._shellwm.block_signal_handler(this._originalMinimizeSigId); + this._minimizeSigId = Main.wm._shellwm.connect('minimize', WindowManagerCommon._minimizeWindow.bind(Main.wm)); + } + + this._originalUnminimizeSigId = GObject.signal_handler_find(Main.wm._shellwm, { signalId: 'unminimize' }); + if (this._originalUnminimizeSigId) { + Main.wm._shellwm.block_signal_handler(this._originalUnminimizeSigId); + this._unminimizeSigId = Main.wm._shellwm.connect('unminimize', WindowManagerCommon._unminimizeWindow.bind(Main.wm)); + } + } + console.debug(' WindowManagerModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + if (this._minimizeSigId) { + Main.wm._shellwm.disconnect(this._minimizeSigId); + this._minimizeSigId = 0; + } + if (this._originalMinimizeSigId) { + Main.wm._shellwm.unblock_signal_handler(this._originalMinimizeSigId); + this._originalMinimizeSigId = 0; + } + + if (this._unminimizeSigId) { + Main.wm._shellwm.disconnect(this._unminimizeSigId); + this._unminimizeSigId = 0; + } + if (this._originalUnminimizeSigId) { + Main.wm._shellwm.unblock_signal_handler(this._originalUnminimizeSigId); + this._originalUnminimizeSigId = 0; + } + + console.debug(' WindowManagerModule - Disabled'); + } +}; // fix for mainstream bug - fullscreen windows should minimize using opacity transition // but its being applied directly on window actor and that doesn't work @@ -109,8 +129,8 @@ const WindowManagerCommon = { /* if (actor.meta_window.is_monitor_sized()) { actor.get_first_child().ease({ opacity: 0, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._minimizeWindowDone(shellwm, actor), }); } else { */ @@ -140,8 +160,8 @@ const WindowManagerCommon = { scale_y: yScale, x: xDest, y: yDest, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._minimizeWindowDone(shellwm, actor), }); // } @@ -176,8 +196,8 @@ const WindowManagerCommon = { actor.set_scale(1.0, 1.0); actor.ease({ opacity: 255, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._unminimizeWindowDone(shellwm, actor), }); } else { */ @@ -208,8 +228,8 @@ const WindowManagerCommon = { scale_y: 1, x: xDest, y: yDest, - duration: MINIMIZE_WINDOW_ANIMATION_TIME, - mode: MINIMIZE_WINDOW_ANIMATION_MODE, + duration: WindowManager.MINIMIZE_WINDOW_ANIMATION_TIME, + mode: WindowManager.MINIMIZE_WINDOW_ANIMATION_MODE, onStopped: () => this._unminimizeWindowDone(shellwm, actor), }); // } diff --git a/extensions/45/vertical-workspaces/lib/windowPreview.js b/extensions/45/vertical-workspaces/lib/windowPreview.js new file mode 100644 index 0000000..c775d37 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/windowPreview.js @@ -0,0 +1,619 @@ +/** + * V-Shell (Vertical Workspaces) + * windowPreview.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import GLib from 'gi://GLib'; +import Clutter from 'gi://Clutter'; +import St from 'gi://St'; +import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; +import Pango from 'gi://Pango'; +import Graphene from 'gi://Graphene'; +import Atk from 'gi://Atk'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; +import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; +import * as WindowPreview from 'resource:///org/gnome/shell/ui/windowPreview.js'; + +let Me; +let opt; + +const WINDOW_SCALE_TIME = 200; +const WINDOW_ACTIVE_SIZE_INC = 5; +const WINDOW_OVERLAY_FADE_TIME = 200; +const WINDOW_DND_SIZE = 256; +const DRAGGING_WINDOW_OPACITY = 100; + +const ControlsState = OverviewControls.ControlsState; + +export const WindowPreviewModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('windowPreviewModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WindowPreviewModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WindowPreview', WindowPreview.WindowPreview.prototype, WindowPreviewCommon); + // A shorter timeout allows user to quickly cancel the selection by leaving the preview with the mouse pointer + // if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW) + // WindowPreview.WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 150; // incompatible + console.debug(' WindowPreviewModule - Activated'); + } + + _disableModule() { + // If WindowPreview._init was injected by another extension (like Burn My Windows) + // which enables/disables before V-Shell + // don't restore the original if it's not injected, + // because it would restore injected _init and recursion would freeze GS when extensions are enabled again. + // This can happen when all extension re-enabled, not only when screen is locked/unlocked + // If _init doesn't include "fn.apply(this, args)" when reset === true, some extension already restored the original + const skipReset = WindowPreview.WindowPreview.prototype._init.toString().includes('fn.apply(this, args)'); + if (this._overrides && skipReset) { + // skip restoring original _init() + this._overrides['_init'] = null; + } + + if (this._overrides) + this._overrides.removeAll(); + + this._overrides = null; + + console.debug(' WindowPreviewModule - Disabled'); + } +}; + +const WindowPreviewCommon = { + _init(metaWindow, workspace, overviewAdjustment) { + this.metaWindow = metaWindow; + this.metaWindow._delegate = this; + this._windowActor = metaWindow.get_compositor_private(); + this._workspace = workspace; + this._overviewAdjustment = overviewAdjustment; + + const ICON_SIZE = opt.WIN_PREVIEW_ICON_SIZE; + const ICON_OVERLAP = 0.7; + + Shell.WindowPreview.prototype._init.bind(this)({ + reactive: true, + can_focus: true, + accessible_role: Atk.Role.PUSH_BUTTON, + offscreen_redirect: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY, + }); + + const windowContainer = new Clutter.Actor({ + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + }); + this.window_container = windowContainer; + + windowContainer.connect('notify::scale-x', + () => this._adjustOverlayOffsets()); + // gjs currently can't handle setting an actors layout manager during + // the initialization of the actor if that layout manager keeps track + // of its container, so set the layout manager after creating the + // container + windowContainer.layout_manager = new Shell.WindowPreviewLayout(); + this.add_child(windowContainer); + + this._addWindow(metaWindow); + + this._delegate = this; + + this._stackAbove = null; + + this._cachedBoundingBox = { + x: windowContainer.layout_manager.bounding_box.x1, + y: windowContainer.layout_manager.bounding_box.y1, + width: windowContainer.layout_manager.bounding_box.get_width(), + height: windowContainer.layout_manager.bounding_box.get_height(), + }; + + windowContainer.layout_manager.connect( + 'notify::bounding-box', layout => { + this._cachedBoundingBox = { + x: layout.bounding_box.x1, + y: layout.bounding_box.y1, + width: layout.bounding_box.get_width(), + height: layout.bounding_box.get_height(), + }; + + // A bounding box of 0x0 means all windows were removed + if (layout.bounding_box.get_area() > 0) + this.emit('size-changed'); + }); + + this._windowActor.connectObject('destroy', () => this.destroy(), this); + + this._updateAttachedDialogs(); + + let clickAction = new Clutter.ClickAction(); + clickAction.connect('clicked', act => { + const button = act.get_button(); + if (button === Clutter.BUTTON_SECONDARY) { + if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 1) { + this._closeWinAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 2) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 3 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } + } else if (button === Clutter.BUTTON_MIDDLE) { + if (opt.WIN_PREVIEW_MID_BTN_ACTION === 1) { + this._closeWinAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_MID_BTN_ACTION === 2) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WIN_PREVIEW_SEC_BTN_ACTION === 3 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } + } + return this._activate(); + }); + + + if (this._onLongPress) { + clickAction.connect('long-press', this._onLongPress.bind(this)); + } else { + clickAction.connect('long-press', (action, actor, state) => { + if (state === Clutter.LongPressState.ACTIVATE) + this.showOverlay(true); + return true; + }); + } + + this.connect('destroy', this._onDestroy.bind(this)); + + this._draggable = DND.makeDraggable(this, { + restoreOnSuccess: true, + manualMode: !!this._onLongPress, + dragActorMaxSize: WINDOW_DND_SIZE, + dragActorOpacity: DRAGGING_WINDOW_OPACITY, + }); + + // _draggable.addClickAction is new in GS45 + if (this._draggable.addClickAction) + this._draggable.addClickAction(clickAction); + else + this.add_action(clickAction); + + this._draggable.connect('drag-begin', this._onDragBegin.bind(this)); + this._draggable.connect('drag-cancelled', this._onDragCancelled.bind(this)); + this._draggable.connect('drag-end', this._onDragEnd.bind(this)); + this.inDrag = false; + + this._selected = false; + this._overlayEnabled = true; + this._overlayShown = false; + this._closeRequested = false; + this._idleHideOverlayId = 0; + + const tracker = Shell.WindowTracker.get_default(); + const app = tracker.get_window_app(this.metaWindow); + this._icon = app.create_icon_texture(ICON_SIZE); + this._icon.add_style_class_name('icon-dropshadow'); + this._icon.set({ + reactive: true, + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + }); + this._icon.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.POSITION, + })); + this._icon.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + factor: 0.5, + })); + this._icon.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: ICON_OVERLAP }), + factor: 1, + })); + + if (opt.WINDOW_ICON_CLICK_ACTION) { + const iconClickAction = new Clutter.ClickAction(); + iconClickAction.connect('clicked', act => { + if (act.get_button() === Clutter.BUTTON_PRIMARY) { + if (opt.WINDOW_ICON_CLICK_ACTION === 1) { + this._searchAppWindowsAction(); + return Clutter.EVENT_STOP; + } else if (opt.WINDOW_ICON_CLICK_ACTION === 2 && opt.WINDOW_THUMBNAIL_ENABLED) { + this._removeLaters(); + Me.Modules.winTmbModule.createThumbnail(metaWindow); + return Clutter.EVENT_STOP; + } + } /* else if (act.get_button() === Clutter.BUTTON_SECONDARY) { + return Clutter.EVENT_STOP; + }*/ + return Clutter.EVENT_PROPAGATE; + }); + this._icon.add_action(iconClickAction); + } + const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); + this._title = new St.Label({ + visible: false, + style_class: 'window-caption', + text: this._getCaption(), + reactive: true, + }); + this._title.clutter_text.single_line_mode = true; + this._title.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.X, + })); + + let offset; + if (opt.WIN_TITLES_POSITION < 2) { + // we cannot get proper title height before it gets to the stage, so 35 is estimated height + spacing + offset = -scaleFactor * (ICON_SIZE * ICON_OVERLAP + 35); + } else { + offset = scaleFactor * (ICON_SIZE * (1 - ICON_OVERLAP) + 4); + } + this._title.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.Y, + offset, + })); + this._title.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + factor: 0.5, + })); + this._title.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: 0 }), + factor: 1, + })); + this._title.clutter_text.ellipsize = Pango.EllipsizeMode.END; + this.label_actor = this._title; + this.metaWindow.connectObject( + 'notify::title', () => (this._title.text = this._getCaption()), + this); + + const layout = Meta.prefs_get_button_layout(); + this._closeButtonSide = + layout.left_buttons.includes(Meta.ButtonFunction.CLOSE) + ? St.Side.LEFT : St.Side.RIGHT; + this._closeButton = new St.Button({ + visible: false, + style_class: 'window-close', + icon_name: 'preview-close-symbolic', + }); + this._closeButton.add_constraint(new Clutter.BindConstraint({ + source: windowContainer, + coordinate: Clutter.BindCoordinate.POSITION, + })); + this._closeButton.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + pivot_point: new Graphene.Point({ x: 0.5, y: -1 }), + factor: this._closeButtonSide === St.Side.LEFT ? 0 : 1, + })); + this._closeButton.add_constraint(new Clutter.AlignConstraint({ + source: windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({ x: -1, y: 0.5 }), + factor: 0, + })); + this._closeButton.connect('clicked', () => this._deleteAll()); + + this.add_child(this._title); + this.add_child(this._icon); + this.add_child(this._closeButton); + + this._overviewAdjustment.connectObject( + 'notify::value', () => this._updateIconScale(), this); + this._updateIconScale(); + + this.connect('notify::realized', () => { + if (!this.realized) + return; + + this._title.ensure_style(); + this._icon.ensure_style(); + }); + + if (ICON_SIZE < 22) { + // disable app icon + this._icon.hide(); + } else { + this._updateIconScale(); + } + + + + // if window is created while the overview is shown, icon and title should be visible immediately + if (Main.overview._overview._controls._stateAdjustment.value < 1) { + this._icon.scale_x = 0; + this._icon.scale_y = 0; + this._title.opacity = 0; + } + + if (opt.ALWAYS_SHOW_WIN_TITLES) + this._title.show(); + + if (opt.OVERVIEW_MODE === 1) { + // spread windows on hover + this._wsStateConId = this.connect('enter-event', () => { + // don't spread windows if user don't use pointer device at this moment + if (global.get_pointer()[0] === opt.showingPointerX || Main.overview._overview._controls._stateAdjustment.value < 1) + return; + + opt.WORKSPACE_MODE = 1; + const view = this._workspace.get_parent(); + view.exposeWindows(this._workspace.metaWorkspace.index()); + this.disconnect(this._wsStateConId); + }); + } + + if (opt.OVERVIEW_MODE) { + // show window icon and title on ws windows spread + this._stateAdjustmentSigId = this._workspace.stateAdjustment.connect('notify::value', this._updateIconScale.bind(this)); + } + + const metaWin = this.metaWindow; + if (opt.DASH_ISOLATE_WS && !metaWin._wsChangedConId) { + metaWin._wsChangedConId = metaWin.connect('workspace-changed', + () => Main.overview.dash._queueRedisplay()); + } else if (!opt.DASH_ISOLATE_WS && metaWin._wsChangedConId) { + metaWin.disconnect(metaWin._wsChangedConId); + } + }, + + _closeWinAction() { + this.hide(); + this._deleteAll(); + }, + + _removeLaters() { + if (this._longPressLater) { + const laters = global.compositor.get_laters(); + laters.remove(this._longPressLater); + delete this._longPressLater; + } + }, + + _searchAppWindowsAction() { + // this action cancels long-press event and the 'long-press-cancel' event is used by the Shell to actually initiate DnD + // so the dnd initiation needs to be removed + this._removeLaters(); + const tracker = Shell.WindowTracker.get_default(); + const appName = tracker.get_window_app(this.metaWindow).get_name(); + Me.Util.activateSearchProvider(`${Me.WSP_PREFIX} ${appName}`); + }, + + _updateIconScale() { + let { currentState, initialState, finalState } = + this._overviewAdjustment.getStateTransitionParams(); + + // Current state - 0 - HIDDEN, 1 - WINDOW_PICKER, 2 - APP_GRID + const primaryMonitor = this.metaWindow.get_monitor() === global.display.get_primary_monitor(); + + const visible = + (initialState > ControlsState.HIDDEN || finalState > ControlsState.HIDDEN) && + !(finalState === ControlsState.APP_GRID && opt.WS_ANIMATION && primaryMonitor); + + let scale = 0; + if (visible) + scale = currentState >= 1 ? 1 : currentState % 1; + + if (!primaryMonitor && opt.WORKSPACE_MODE && + ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || + (initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER)) + ) + scale = 1; + else if (!primaryMonitor && opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE) + scale = 0; + /* } else if (primaryMonitor && ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || + initialState === ControlsState.APP_GRID && finalState === ControlsState.HIDDEN)) {*/ + else if (primaryMonitor && currentState > ControlsState.WINDOW_PICKER) + scale = 0; + + // in static workspace mode show icon and title on windows expose + if (opt.OVERVIEW_MODE) { + if (currentState === 1) + scale = opt.WORKSPACE_MODE; + else if (finalState === 1 || (finalState === 0 && !opt.WORKSPACE_MODE)) + return; + } + + if (!opt.WS_ANIMATION && (Main.overview._overview.controls._searchController.searchActive || + ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || + (initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER))) + ) + return; + + // if titles are in 'always show' mode, we need to add transition between visible/invisible state + // but the transition is quite expensive, + // showing the titles at the end of the transition is good enough and workspace preview transition is much smoother + if (scale === 1) { + this._icon.set({ + scale_x: 1, + scale_y: 1, + }); + this._title.ease({ + duration: 100, + opacity: 255, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } else { + this._title.opacity = 0; + this._icon.set({ + scale_x: scale, + scale_y: scale, + }); + } + }, + + showOverlay(animate) { + if (!this._overlayEnabled) + return; + + if (this._overlayShown) + return; + + this._overlayShown = true; + if (opt.WIN_TITLES_POSITION === 2) + this._restack(); + + // If we're supposed to animate and an animation in our direction + // is already happening, let that one continue + const ongoingTransition = this._title.get_transition('opacity'); + if (animate && + ongoingTransition && + ongoingTransition.get_interval().peek_final_value() === 255) + return; + + const toShow = this._windowCanClose() && opt.SHOW_CLOSE_BUTTON + ? [this._closeButton] + : []; + + if (!opt.ALWAYS_SHOW_WIN_TITLES) + toShow.push(this._title); + + + toShow.forEach(a => { + a.opacity = 0; + a.show(); + a.ease({ + opacity: 255, + duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + }); + + const [width, height] = this.window_container.get_size(); + const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); + const activeExtraSize = WINDOW_ACTIVE_SIZE_INC * 2 * scaleFactor; + const origSize = Math.max(width, height); + const scale = (origSize + activeExtraSize) / origSize; + + this.window_container.ease({ + scale_x: scale, + scale_y: scale, + duration: animate ? WINDOW_SCALE_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + this.emit('show-chrome'); + }, + + hideOverlay(animate) { + if (!this._overlayShown) + return; + this._overlayShown = false; + + if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW && Main.overview._overview.controls._stateAdjustment.value < 1) + this._activateSelected = true; + + + if (opt.WIN_TITLES_POSITION === 2) + this._restack(); + + // If we're supposed to animate and an animation in our direction + // is already happening, let that one continue + const ongoingTransition = this._title.get_transition('opacity'); + if (animate && + ongoingTransition && + ongoingTransition.get_interval().peek_final_value() === 0) + return; + + const toHide = [this._closeButton]; + + if (!opt.ALWAYS_SHOW_WIN_TITLES) + toHide.push(this._title); + + toHide.forEach(a => { + a.opacity = 255; + a.ease({ + opacity: 0, + duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => a.hide(), + }); + }); + + if (this.window_container) { + this.window_container.ease({ + scale_x: 1, + scale_y: 1, + duration: animate ? WINDOW_SCALE_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + }, + + _onDestroy() { + if (this._activateSelected) + this._activate(); + + this.metaWindow._delegate = null; + this._delegate = null; + this._destroyed = true; + + if (this._longPressLater) { + const laters = global.compositor.get_laters(); + laters.remove(this._longPressLater); + delete this._longPressLater; + } + + if (this._idleHideOverlayId > 0) { + GLib.source_remove(this._idleHideOverlayId); + this._idleHideOverlayId = 0; + } + + if (this.inDrag) { + this.emit('drag-end'); + this.inDrag = false; + } + + if (this._stateAdjustmentSigId) + this._workspace.stateAdjustment.disconnect(this._stateAdjustmentSigId); + }, +}; diff --git a/extensions/vertical-workspaces/lib/windowSearchProvider.js b/extensions/45/vertical-workspaces/lib/windowSearchProvider.js similarity index 50% rename from extensions/vertical-workspaces/lib/windowSearchProvider.js rename to extensions/45/vertical-workspaces/lib/windowSearchProvider.js index 5f90784..7deddb6 100644 --- a/extensions/vertical-workspaces/lib/windowSearchProvider.js +++ b/extensions/45/vertical-workspaces/lib/windowSearchProvider.js @@ -9,30 +9,23 @@ 'use strict'; -const { GLib, Gio, Meta, St, Shell } = imports.gi; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; -const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; -const _Util = Me.imports.lib.util; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +let Me; +let opt; // gettext -const _ = Settings._; - -const shellVersion = Settings.shellVersion; - -const ModifierType = imports.gi.Clutter.ModifierType; - -let windowSearchProvider; -let _enableTimeoutId = 0; +let _; // prefix helps to eliminate results from other search providers // so it needs to be something less common // needs to be accessible from vw module -var prefix = 'wq//'; - -let opt; +export const PREFIX = 'wq//'; const Action = { NONE: 0, @@ -42,83 +35,101 @@ const Action = { MOVE_ALL_TO_WS: 4, }; -function getOverviewSearchResult() { - return Main.overview._overview.controls._searchController._searchResults; -} +export const WindowSearchProviderModule = class { + // export for other modules + static _PREFIX = PREFIX; + constructor(me) { + Me = me; + opt = Me.opt; + _ = Me.gettext; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - if (!reset && opt.WINDOW_SEARCH_PROVIDER_ENABLED && !windowSearchProvider) { - enable(); - } else if (reset || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) { - disable(); + this._firstActivation = true; + this.moduleEnabled = false; + + this._windowSearchProvider = null; + this._enableTimeoutId = 0; + } + + cleanGlobals() { + Me = null; opt = null; + _ = null; } -} -function enable() { - // delay because Fedora had problem to register a new provider soon after Shell restarts - _enableTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 2000, - () => { - if (!windowSearchProvider) { - windowSearchProvider = new WindowSearchProvider(opt); - getOverviewSearchResult()._registerProvider( - windowSearchProvider - ); - } - _enableTimeoutId = 0; - return GLib.SOURCE_REMOVE; + update(reset) { + this.moduleEnabled = opt.get('windowSearchProviderModule'); + + reset = reset || !this.moduleEnabled; + + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); } - ); -} + if (reset && this._firstActivation) + console.debug(' WindowSearchProviderModule - Keeping untouched'); + } -function disable() { - if (windowSearchProvider) { - getOverviewSearchResult()._unregisterProvider( - windowSearchProvider + _activateModule() { + // delay because Fedora had problem to register a new provider soon after Shell restarts + this._enableTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 2000, + () => { + if (!this._windowSearchProvider) { + this._windowSearchProvider = new WindowSearchProvider(opt); + this._getOverviewSearchResult()._registerProvider(this._windowSearchProvider); + } + this._enableTimeoutId = 0; + return GLib.SOURCE_REMOVE; + } ); - windowSearchProvider = null; + console.debug(' WindowSearchProviderModule - Activated'); } - if (_enableTimeoutId) { - GLib.source_remove(_enableTimeoutId); - _enableTimeoutId = 0; + + _disableModule() { + if (this._windowSearchProvider) { + this._getOverviewSearchResult()._unregisterProvider(this._windowSearchProvider); + this._windowSearchProvider = null; + } + if (this._enableTimeoutId) { + GLib.source_remove(this._enableTimeoutId); + this._enableTimeoutId = 0; + } + + console.debug(' WindowSearchProviderModule - Disabled'); } -} -function makeResult(window, i) { - const app = Shell.WindowTracker.get_default().get_window_app(window); - const appName = app ? app.get_name() : 'Unknown'; - const windowTitle = window.get_title(); - const wsIndex = window.get_workspace().index(); + _getOverviewSearchResult() { + return Main.overview._overview.controls._searchController._searchResults; + } +}; - return { - 'id': i, - // convert all accented chars to their basic form and lower case for search - 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(), - appName, - windowTitle, - window, - }; -} - -const closeSelectedRegex = /^\/x!$/; +/* const closeSelectedRegex = /^\/x!$/; const closeAllResultsRegex = /^\/xa!$/; const moveToWsRegex = /^\/m[0-9]+$/; -const moveAllToWsRegex = /^\/ma[0-9]+$/; +const moveAllToWsRegex = /^\/ma[0-9]+$/;*/ -const WindowSearchProvider = class WindowSearchProvider { +class WindowSearchProvider { constructor() { - this.id = `open-windows@${Me.metadata.uuid}`; - this.appInfo = Gio.AppInfo.create_from_commandline('true', _('Open Windows'), null); - this.appInfo.get_description = () => _('List of open windows'); - this.appInfo.get_name = () => _('Open Windows'); - this.appInfo.get_id = () => this.id; - this.appInfo.get_icon = () => Gio.icon_new_for_string('focus-windows-symbolic'); - this.appInfo.should_show = () => true; + this.id = 'open-windows'; + // use arbitrary app to get complete appInfo object + // Gio.AppInfo.create_from_commandline lacks something that causes error with parental content / malcontent + const appSystem = Shell.AppSystem.get_default(); + let appInfo = appSystem.lookup_app('com.matjakeman.ExtensionManager.desktop')?.get_app_info(); + if (!appInfo) + appInfo = appSystem.lookup_app('org.gnome.Extensions.desktop')?.get_app_info(); + if (!appInfo) + appInfo = Gio.AppInfo.create_from_commandline('true', _('Open Windows'), null); + appInfo.get_description = () => _('Search open windows'); + appInfo.get_name = () => _('Open Windows'); + appInfo.get_id = () => this.id; + appInfo.get_icon = () => Gio.icon_new_for_string('focus-windows-symbolic'); + appInfo.should_show = () => true; - this.canLaunchSearch = true; + this.appInfo = appInfo; + this.canLaunchSearch = false; this.isRemoteProvider = false; this.action = 0; @@ -128,7 +139,7 @@ const WindowSearchProvider = class WindowSearchProvider { // do not modify original terms let termsCopy = [...terms]; // search for terms without prefix - termsCopy[0] = termsCopy[0].replace(prefix, ''); + termsCopy[0] = termsCopy[0].replace(PREFIX, ''); /* if (gOptions.get('searchWindowsCommands')) { this.action = 0; @@ -167,9 +178,9 @@ const WindowSearchProvider = class WindowSearchProvider { let m; for (let key in candidates) { if (opt.SEARCH_FUZZY) - m = _Util.fuzzyMatch(term, candidates[key].name); + m = Me.Util.fuzzyMatch(term, candidates[key].name); else - m = _Util.strictMatch(term, candidates[key].name); + m = Me.Util.strictMatch(term, candidates[key].name); if (m !== -1) results.push({ weight: m, id: key }); @@ -178,20 +189,28 @@ const WindowSearchProvider = class WindowSearchProvider { results.sort((a, b) => a.weight > b.weight); const currentWs = global.workspace_manager.get_active_workspace_index(); // prefer current workspace - results.sort((a, b) => (this.windows[a.id].window.get_workspace().index() !== currentWs) && (this.windows[b.id].window.get_workspace().index() === currentWs)); + switch (opt.WINDOW_SEARCH_ORDER) { + case 1: // MRU - current ws first + results.sort((a, b) => (this.windows[a.id].window.get_workspace().index() !== currentWs) && (this.windows[b.id].window.get_workspace().index() === currentWs)); + break; + case 2: // MRU - by workspace + results.sort((a, b) => this.windows[a.id].window.get_workspace().index() > this.windows[b.id].window.get_workspace().index()); + break; + case 3: // Stable sequence - by workspace + results.sort((a, b) => this.windows[a.id].window.get_stable_sequence() > this.windows[b.id].window.get_stable_sequence()); + results.sort((a, b) => this.windows[a.id].window.get_workspace().index() > this.windows[b.id].window.get_workspace().index()); + break; + } + results.sort((a, b) => (_terms !== ' ') && (a.weight > 0 && b.weight === 0)); this.resultIds = results.map(item => item.id); return this.resultIds; } - getResultMetas(resultIds, callback = null) { + getResultMetas(resultIds/* , callback = null*/) { const metas = resultIds.map(id => this.getResultMeta(id)); - if (shellVersion >= 43) - return new Promise(resolve => resolve(metas)); - else - callback(metas); - return null; + return new Promise(resolve => resolve(metas)); } getResultMeta(resultId) { @@ -210,12 +229,29 @@ const WindowSearchProvider = class WindowSearchProvider { }; } + makeResult(window, i) { + const app = Shell.WindowTracker.get_default().get_window_app(window); + const appName = app ? app.get_name() : 'Unknown'; + const windowTitle = window.get_title(); + const wsIndex = window.get_workspace().index(); + + return { + 'id': i, + // convert all accented chars to their basic form and lower case for search + 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(), + appName, + windowTitle, + window, + }; + } + launchSearch(/* terms, timeStamp*/) { + } activateResult(resultId/* , terms, timeStamp*/) { - const isCtrlPressed = _Util.isCtrlPressed(); - const isShiftPressed = _Util.isShiftPressed(); + const isCtrlPressed = Me.Util.isCtrlPressed(); + const isShiftPressed = Me.Util.isShiftPressed(); this.action = 0; this.targetWs = 0; @@ -267,25 +303,17 @@ const WindowSearchProvider = class WindowSearchProvider { selectedWin.activate_with_workspace(global.get_current_time(), workspace); } - getInitialResultSet(terms, callback/* , cancellable = null*/) { - // In GS 43 callback arg has been removed - /* if (shellVersion >= 43) - cancellable = callback;*/ - + getInitialResultSet(terms/* , callback*/) { let windows; this.windows = windows = {}; global.display.get_tab_list(Meta.TabList.NORMAL, null).filter(w => w.get_workspace() !== null).map( (v, i) => { - windows[`${i}-${v.get_id()}`] = makeResult(v, `${i}-${v.get_id()}`); + windows[`${i}-${v.get_id()}`] = this.makeResult(v, `${i}-${v.get_id()}`); return windows[`${i}-${v.get_id()}`]; } ); - if (shellVersion >= 43) - return new Promise(resolve => resolve(this._getResultSet(terms))); - else - callback(this._getResultSet(terms)); - return null; + return new Promise(resolve => resolve(this._getResultSet(terms))); } filterResults(results /* , maxResults*/) { @@ -293,13 +321,11 @@ const WindowSearchProvider = class WindowSearchProvider { return results; } - getSubsearchResultSet(previousResults, terms, callback/* , cancellable*/) { - // if we return previous results, quick typers get non-actual results - callback(this._getResultSet(terms)); + getSubsearchResultSet(previousResults, terms/* , callback*/) { + return this.getInitialResultSet(terms); } - /* createResultObject(resultMeta) { - const app = Shell.WindowTracker.get_default().get_window_app(resultMeta.id); - return new AppIcon(app); - }*/ -}; + getSubsearchResultSet42(terms, callback) { + callback(this._getResultSet(terms)); + } +} diff --git a/extensions/45/vertical-workspaces/lib/workspace.js b/extensions/45/vertical-workspaces/lib/workspace.js new file mode 100644 index 0000000..1ff81f1 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/workspace.js @@ -0,0 +1,463 @@ +/** + * V-Shell (Vertical Workspaces) + * workspace.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import St from 'gi://St'; +import Graphene from 'gi://Graphene'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Workspace from 'resource:///org/gnome/shell/ui/workspace.js'; +import * as Params from 'resource:///org/gnome/shell/misc/params.js'; +import * as Util from 'resource:///org/gnome/shell/misc/util.js'; + +let Me; +let opt; + +let WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95; + +export const WorkspaceModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('workspaceModule'); + const conflict = false; + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WorkspaceBackground', Workspace.WorkspaceBackground.prototype, WorkspaceBackground); + + // fix overlay base for Vertical Workspaces + this._overrides.addOverride('WorkspaceLayout', Workspace.WorkspaceLayout.prototype, WorkspaceLayout); + console.debug(' WorkspaceModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + console.debug(' WorkspaceModule - Disabled'); + } + + setWindowPreviewMaxScale(scale) { + WINDOW_PREVIEW_MAXIMUM_SCALE = scale; + } +}; + +// workaround for upstream bug (that is not that invisible in default shell) +// smaller window cannot be scaled below 0.95 (WINDOW_PREVIEW_MAXIMUM_SCALE) +// when its target scale for exposed windows view (workspace state 1) is bigger than the scale needed for ws state 0. +// in workspace state 0 where windows are not spread and window scale should follow workspace scale, +// this window follows proper top left corner position, but doesn't scale with the workspace +// so it looks bad and the window can exceed border of the workspace +// extremely annoying in OVERVIEW_MODE 1 with single smaller window on the workspace, also affects appGrid transition animation + +// disadvantage of following workaround - the WINDOW_PREVIEW_MAXIMUM_SCALE value is common for every workspace, +// on multi-monitor system can be visible unwanted scaling of windows on workspace in WORKSPACE_MODE 0 (windows not spread) +// when leaving overview while any other workspace is in the WORKSPACE_MODE 1. +const WorkspaceLayout = { + // injection to _init() + after__init() { + if (opt.OVERVIEW_MODE !== 1) + WINDOW_PREVIEW_MAXIMUM_SCALE = 0.95; + if (opt.OVERVIEW_MODE === 1) { + this._stateAdjustment.connect('notify::value', () => { + // scale 0.1 for window state 0 just needs to be smaller then possible scale of any window in spread view + const scale = this._stateAdjustment.value ? 0.95 : 0.1; + if (scale !== WINDOW_PREVIEW_MAXIMUM_SCALE) { + WINDOW_PREVIEW_MAXIMUM_SCALE = scale; + // when transition to ws state 1 (WINDOW_PICKER) begins, replace the constant with the original one + // and force recalculation of the target layout, so the transition will be smooth + this._needsLayout = true; + } + }); + } + }, + + // this fixes wrong size and position calculation of window clones while moving overview to the next (+1) workspace if vertical ws orientation is enabled in GS + _adjustSpacingAndPadding(rowSpacing, colSpacing, containerBox) { + if (this._sortedWindows.length === 0) + return [rowSpacing, colSpacing, containerBox]; + + // All of the overlays have the same chrome sizes, + // so just pick the first one. + const window = this._sortedWindows[0]; + + const [topOversize, bottomOversize] = window.chromeHeights(); + const [leftOversize, rightOversize] = window.chromeWidths(); + + const oversize = Math.max(topOversize, bottomOversize, leftOversize, rightOversize); + + if (rowSpacing !== null) + rowSpacing += oversize; + if (colSpacing !== null) + colSpacing += oversize; + + if (containerBox) { + const vertical = global.workspaceManager.layout_rows === -1; + + const monitor = Main.layoutManager.monitors[this._monitorIndex]; + + const bottomPoint = new Graphene.Point3D(); + if (vertical) + bottomPoint.x = containerBox.x2; + else + bottomPoint.y = containerBox.y2; + + + const transformedBottomPoint = + this._container.apply_transform_to_point(bottomPoint); + const bottomFreeSpace = vertical + ? (monitor.x + monitor.height) - transformedBottomPoint.x + : (monitor.y + monitor.height) - transformedBottomPoint.y; + + const [, bottomOverlap] = window.overlapHeights(); + + if ((bottomOverlap + oversize) > bottomFreeSpace && !vertical) + containerBox.y2 -= (bottomOverlap + oversize) - bottomFreeSpace; + } + + return [rowSpacing, colSpacing, containerBox]; + }, + + _createBestLayout(area) { + const [rowSpacing, columnSpacing] = + this._adjustSpacingAndPadding(this._spacing, this._spacing, null); + + // We look for the largest scale that allows us to fit the + // largest row/tallest column on the workspace. + this._layoutStrategy = new UnalignedLayoutStrategy({ + monitor: Main.layoutManager.monitors[this._monitorIndex], + rowSpacing, + columnSpacing, + }); + + let lastLayout = null; + let lastNumColumns = -1; + let lastScale = 0; + let lastSpace = 0; + + for (let numRows = 1; ; numRows++) { + const numColumns = Math.ceil(this._sortedWindows.length / numRows); + + // If adding a new row does not change column count just stop + // (for instance: 9 windows, with 3 rows -> 3 columns, 4 rows -> + // 3 columns as well => just use 3 rows then) + if (numColumns === lastNumColumns) + break; + + const layout = this._layoutStrategy.computeLayout(this._sortedWindows, { + numRows, + }); + + const [scale, space] = this._layoutStrategy.computeScaleAndSpace(layout, area); + + if (lastLayout && !this._isBetterScaleAndSpace(lastScale, lastSpace, scale, space)) + break; + + lastLayout = layout; + lastNumColumns = numColumns; + lastScale = scale; + lastSpace = space; + } + + return lastLayout; + }, +}; + +class UnalignedLayoutStrategy extends Workspace.LayoutStrategy { + _newRow() { + // Row properties: + // + // * x, y are the position of row, relative to area + // + // * width, height are the scaled versions of fullWidth, fullHeight + // + // * width also has the spacing in between windows. It's not in + // fullWidth, as the spacing is constant, whereas fullWidth is + // meant to be scaled + // + // * neither height/fullHeight have any sort of spacing or padding + return { + x: 0, y: 0, + width: 0, height: 0, + fullWidth: 0, fullHeight: 0, + windows: [], + }; + } + + // Computes and returns an individual scaling factor for @window, + // to be applied in addition to the overall layout scale. + _computeWindowScale(window) { + // Since we align windows next to each other, the height of the + // thumbnails is much more important to preserve than the width of + // them, so two windows with equal height, but maybe differering + // widths line up. + let ratio = window.boundingBox.height / this._monitor.height; + + // The purpose of this manipulation here is to prevent windows + // from getting too small. For something like a calculator window, + // we need to bump up the size just a bit to make sure it looks + // good. We'll use a multiplier of 1.5 for this. + + // Map from [0, 1] to [1.5, 1] + return Util.lerp(1.5, 1, ratio); + } + + _computeRowSizes(layout) { + let { rows, scale } = layout; + for (let i = 0; i < rows.length; i++) { + let row = rows[i]; + row.width = row.fullWidth * scale + (row.windows.length - 1) * this._columnSpacing; + row.height = row.fullHeight * scale; + } + } + + _keepSameRow(row, window, width, idealRowWidth) { + if (row.fullWidth + width <= idealRowWidth) + return true; + + let oldRatio = row.fullWidth / idealRowWidth; + let newRatio = (row.fullWidth + width) / idealRowWidth; + + if (Math.abs(1 - newRatio) < Math.abs(1 - oldRatio)) + return true; + + return false; + } + + _sortRow(row) { + // Sort windows horizontally to minimize travel distance. + // This affects in what order the windows end up in a row. + row.windows.sort((a, b) => a.windowCenter.x - b.windowCenter.x); + } + + computeLayout(windows, layoutParams) { + layoutParams = Params.parse(layoutParams, { + numRows: 0, + }); + + if (layoutParams.numRows === 0) + throw new Error(`${this.constructor.name}: No numRows given in layout params`); + + const numRows = layoutParams.numRows; + + let rows = []; + let totalWidth = 0; + for (let i = 0; i < windows.length; i++) { + let window = windows[i]; + let s = this._computeWindowScale(window); + totalWidth += window.boundingBox.width * s; + } + + let idealRowWidth = totalWidth / numRows; + + // Sort windows vertically to minimize travel distance. + // This affects what rows the windows get placed in. + let sortedWindows = windows.slice(); + sortedWindows.sort((a, b) => a.windowCenter.y - b.windowCenter.y); + + let windowIdx = 0; + for (let i = 0; i < numRows; i++) { + let row = this._newRow(); + rows.push(row); + + for (; windowIdx < sortedWindows.length; windowIdx++) { + let window = sortedWindows[windowIdx]; + let s = this._computeWindowScale(window); + let width = window.boundingBox.width * s; + let height = window.boundingBox.height * s; + row.fullHeight = Math.max(row.fullHeight, height); + + // either new width is < idealWidth or new width is nearer from idealWidth then oldWidth + if (this._keepSameRow(row, window, width, idealRowWidth) || (i === numRows - 1)) { + row.windows.push(window); + row.fullWidth += width; + } else { + break; + } + } + } + + let gridHeight = 0; + let maxRow; + for (let i = 0; i < numRows; i++) { + let row = rows[i]; + this._sortRow(row); + + if (!maxRow || row.fullWidth > maxRow.fullWidth) + maxRow = row; + gridHeight += row.fullHeight; + } + + return { + numRows, + rows, + maxColumns: maxRow.windows.length, + gridWidth: maxRow.fullWidth, + gridHeight, + }; + } + + computeScaleAndSpace(layout, area) { + let hspacing = (layout.maxColumns - 1) * this._columnSpacing; + let vspacing = (layout.numRows - 1) * this._rowSpacing; + + let spacedWidth = area.width - hspacing; + let spacedHeight = area.height - vspacing; + + let horizontalScale = spacedWidth / layout.gridWidth; + let verticalScale = spacedHeight / layout.gridHeight; + + // Thumbnails should be less than 70% of the original size + let scale = Math.min( + horizontalScale, verticalScale, WINDOW_PREVIEW_MAXIMUM_SCALE); + + let scaledLayoutWidth = layout.gridWidth * scale + hspacing; + let scaledLayoutHeight = layout.gridHeight * scale + vspacing; + let space = (scaledLayoutWidth * scaledLayoutHeight) / (area.width * area.height); + + layout.scale = scale; + + return [scale, space]; + } + + computeWindowSlots(layout, area) { + this._computeRowSizes(layout); + + let { rows, scale } = layout; + + let slots = []; + + // Do this in three parts. + let heightWithoutSpacing = 0; + for (let i = 0; i < rows.length; i++) { + let row = rows[i]; + heightWithoutSpacing += row.height; + } + + let verticalSpacing = (rows.length - 1) * this._rowSpacing; + let additionalVerticalScale = Math.min(1, (area.height - verticalSpacing) / heightWithoutSpacing); + + // keep track how much smaller the grid becomes due to scaling + // so it can be centered again + let compensation = 0; + let y = 0; + + for (let i = 0; i < rows.length; i++) { + let row = rows[i]; + + // If this window layout row doesn't fit in the actual + // geometry, then apply an additional scale to it. + let horizontalSpacing = (row.windows.length - 1) * this._columnSpacing; + let widthWithoutSpacing = row.width - horizontalSpacing; + let additionalHorizontalScale = Math.min(1, (area.width - horizontalSpacing) / widthWithoutSpacing); + + if (additionalHorizontalScale < additionalVerticalScale) { + row.additionalScale = additionalHorizontalScale; + // Only consider the scaling in addition to the vertical scaling for centering. + compensation += (additionalVerticalScale - additionalHorizontalScale) * row.height; + } else { + row.additionalScale = additionalVerticalScale; + // No compensation when scaling vertically since centering based on a too large + // height would undo what vertical scaling is trying to achieve. + } + + row.x = area.x + (Math.max(area.width - (widthWithoutSpacing * row.additionalScale + horizontalSpacing), 0) / 2); + row.y = area.y + (Math.max(area.height - (heightWithoutSpacing + verticalSpacing), 0) / 2) + y; + y += row.height * row.additionalScale + this._rowSpacing; + } + + compensation /= 2; + + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + const rowY = row.y + compensation; + const rowHeight = row.height * row.additionalScale; + + let x = row.x; + for (let j = 0; j < row.windows.length; j++) { + let window = row.windows[j]; + + let s = scale * this._computeWindowScale(window) * row.additionalScale; + let cellWidth = window.boundingBox.width * s; + let cellHeight = window.boundingBox.height * s; + + s = Math.min(s, WINDOW_PREVIEW_MAXIMUM_SCALE); + let cloneWidth = window.boundingBox.width * s; + const cloneHeight = window.boundingBox.height * s; + + let cloneX = x + (cellWidth - cloneWidth) / 2; + let cloneY; + + // If there's only one row, align windows vertically centered inside the row + if (rows.length === 1) + cloneY = rowY + (rowHeight - cloneHeight) / 2; + // If there are multiple rows, align windows to the bottom edge of the row + else + cloneY = rowY + rowHeight - cellHeight; + + // Align with the pixel grid to prevent blurry windows at scale = 1 + cloneX = Math.floor(cloneX); + cloneY = Math.floor(cloneY); + + slots.push([cloneX, cloneY, cloneWidth, cloneHeight, window]); + x += cellWidth + this._columnSpacing; + } + } + return slots; + } +} + +const WorkspaceBackground = { + _updateBorderRadius(value = false) { + // don't round already rounded corners during exposing windows + if (value === false && opt.OVERVIEW_MODE === 1) + return; + + const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); + const cornerRadius = scaleFactor * opt.WS_PREVIEW_BG_RADIUS; + + const backgroundContent = this._bgManager.backgroundActor.content; + value = value !== false + ? value + : this._stateAdjustment.value; + + backgroundContent.rounded_clip_radius = + Util.lerp(0, cornerRadius, value); + }, +}; diff --git a/extensions/45/vertical-workspaces/lib/workspaceAnimation.js b/extensions/45/vertical-workspaces/lib/workspaceAnimation.js new file mode 100644 index 0000000..32c7df1 --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/workspaceAnimation.js @@ -0,0 +1,262 @@ +/** + * V-Shell (Vertical Workspaces) + * workspacesAnimation.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Layout from 'resource:///org/gnome/shell/ui/layout.js'; +import * as WorkspaceSwitcherPopup from 'resource:///org/gnome/shell/ui/workspaceSwitcherPopup.js'; +import * as WorkspaceAnimation from 'resource:///org/gnome/shell/ui/workspaceAnimation.js'; +import * as Util from 'resource:///org/gnome/shell/misc/util.js'; + +let Me; +let opt; + +export const WorkspaceAnimationModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + this._origBaseDistance = null; + this._wsAnimationSwipeBeginId = 0; + this._wsAnimationSwipeUpdateId = 0; + this._wsAnimationSwipeEndId = 0; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('workspaceAnimationModule'); + const conflict = !WorkspaceAnimation.MonitorGroup; + if (conflict) + console.warn(`[${Me.metadata.name}] Warning: "WorkspaceAnimation" module disabled due to compatibility - GNOME Shell 45.1 or later is required`); + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceAnimationModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('MonitorGroup', WorkspaceAnimation.MonitorGroup.prototype, MonitorGroup); + this._connectWsAnimationSwipeTracker(); + + console.debug(' WorkspaceAnimationModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + const reset = true; + this._connectWsAnimationSwipeTracker(reset); + + console.debug(' WorkspaceAnimationModule - Disabled'); + } + + _connectWsAnimationSwipeTracker(reset = false) { + if (reset) { + if (this._wsAnimationSwipeBeginId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeBeginId); + this._wsAnimationSwipeBeginId = 0; + } + if (this._wsAnimationSwipeEndId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeEndId); + this._wsAnimationSwipeEndId = 0; + } + } else if (!this._wsAnimationSwipeBeginId) { + // display ws switcher popup when gesture begins and connect progress + this._wsAnimationSwipeBeginId = Main.wm._workspaceAnimation._swipeTracker.connect('begin', () => this._connectWsAnimationProgress(true)); + // we want to be sure that popup with the final ws index show up when gesture ends + this._wsAnimationSwipeEndId = Main.wm._workspaceAnimation._swipeTracker.connect('end', (tracker, duration, endProgress) => this._connectWsAnimationProgress(false, endProgress)); + } + } + + _connectWsAnimationProgress(connect, endProgress = null) { + if (Main.overview.visible) + return; + + if (connect && !this._wsAnimationSwipeUpdateId) { + this._wsAnimationSwipeUpdateId = Main.wm._workspaceAnimation._swipeTracker.connect('update', (tracker, progress) => this._showWsSwitcherPopup(progress)); + } else if (!connect && this._wsAnimationSwipeUpdateId) { + Main.wm._workspaceAnimation._swipeTracker.disconnect(this._wsAnimationSwipeUpdateId); + this._wsAnimationSwipeUpdateId = 0; + this._showWsSwitcherPopup(Math.round(endProgress)); + } + } + + _showWsSwitcherPopup(progress) { + if (Main.overview.visible) + return; + + const wsIndex = Math.round(progress); + if (Main.wm._workspaceSwitcherPopup === null) { + Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); + Main.wm._workspaceSwitcherPopup.connect('destroy', () => { + Main.wm._workspaceSwitcherPopup = null; + }); + } + + Main.wm._workspaceSwitcherPopup.display(wsIndex); + } +}; + +const MonitorGroup = { + _init(monitor, workspaceIndices, movingWindow) { + St.Widget.prototype._init.bind(this)({ + clip_to_allocation: true, + style_class: 'workspace-animation', + }); + + this._monitor = monitor; + + const constraint = new Layout.MonitorConstraint({ index: monitor.index }); + this.add_constraint(constraint); + + this._container = new Clutter.Actor(); + this.add_child(this._container); + + const stickyGroup = new WorkspaceAnimation.WorkspaceGroup(null, monitor, movingWindow); + stickyGroup._windowRecords.forEach(r => { + const metaWin = r.windowActor.metaWindow; + // conky is sticky but should never get above other windows during ws animation + // so we hide it from the overlay group, we will see the original if not covered by other windows + if (metaWin.wm_class === 'conky') + r.clone.opacity = 0; + }); + this.add_child(stickyGroup); + + this._workspaceGroups = []; + + const workspaceManager = global.workspace_manager; + const vertical = workspaceManager.layout_rows === -1; + const activeWorkspace = workspaceManager.get_active_workspace(); + + let x = 0; + let y = 0; + + for (const i of workspaceIndices) { + const ws = workspaceManager.get_workspace_by_index(i); + const fullscreen = ws.list_windows().some(w => w.get_monitor() === monitor.index && w.is_fullscreen()); + + if (i > 0 && vertical && !fullscreen && monitor.index === Main.layoutManager.primaryIndex) { + // We have to shift windows up or down by the height of the panel to prevent having a + // visible gap between the windows while switching workspaces. Since fullscreen windows + // hide the panel, they don't need to be shifted up or down. + y -= Main.panel.height; + } + + const group = new WorkspaceAnimation.WorkspaceGroup(ws, monitor, movingWindow); + + this._workspaceGroups.push(group); + this._container.add_child(group); + group.set_position(x, y); + + if (vertical) + y += this.baseDistance; + else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) + x -= this.baseDistance; + else + x += this.baseDistance; + } + + this.progress = this.getWorkspaceProgress(activeWorkspace); + + if (monitor.index === Main.layoutManager.primaryIndex) { + this._workspacesAdjustment = Main.createWorkspacesAdjustment(this); + this.bind_property_full('progress', + this._workspacesAdjustment, 'value', + GObject.BindingFlags.SYNC_CREATE, + (bind, source) => { + const indices = [ + workspaceIndices[Math.floor(source)], + workspaceIndices[Math.ceil(source)], + ]; + return [true, Util.lerp(...indices, source % 1.0)]; + }, + null); + + this.connect('destroy', () => { + // for some reason _workspaceAdjustment bound to the progress property in V-Shell + // causes the adjustment doesn't reach a whole number + // when switching ws up and that breaks the showing overview animation + // as a workaround round workspacesDisplay._scrollAdjustment value on destroy + // but it should be handled elsewhere as this workaround doesn't work when this module is disabled + const workspacesAdj = Main.overview._overview.controls._workspacesDisplay._scrollAdjustment; + workspacesAdj.value = Math.round(workspacesAdj.value); + delete this._workspacesAdjustment; + }); + } + + if (!opt.STATIC_WS_SWITCHER_BG) + return; + + // we have two options to implement static bg feature + // one is adding background to monitorGroup + // but this one has disadvantage - sticky windows will be always on top of animated windows + // which is bad for conky, for example, that window should be always below + /* this._bgManager = new Background.BackgroundManager({ + container: this, + monitorIndex: this._monitor.index, + controlPosition: false, + });*/ + + // the second option is to make background of the monitorGroup transparent so the real desktop content will stay visible, + // hide windows that should be animated and keep only sticky windows + // we can keep certain sticky windows bellow and also extensions like DING (icons on desktop) will stay visible + this.set_style('background-color: transparent;'); + // stickyGroup holds the Always on Visible Workspace windows to keep them static and above other windows during animation + this._hiddenWindows = []; + // remove (hide) background wallpaper from the animation, we will see the original one + this._workspaceGroups.forEach(w => { + w._background.opacity = 0; + }); + // hide (scale to 0) all non-sticky windows, their clones will be animated + global.get_window_actors().forEach(actor => { + const metaWin = actor.metaWindow; + if (metaWin?.get_monitor() === this._monitor.index && + !(metaWin?.wm_class === 'conky' && metaWin?.is_on_all_workspaces()) && + !(metaWin?.wm_class === 'Gjs' && metaWin?.is_on_all_workspaces())) { // DING extension uses window with Gjs class + // hide original window. we cannot use opacity since it also affects clones. + // scaling them to 0 works well + actor.scale_x = 0; + this._hiddenWindows.push(actor); + } + }); + + // restore all hidden windows at the end of animation + // todo - actors removed during transition need to be removed from the list to avoid access to destroyed actor + this.connect('destroy', () => { + this._hiddenWindows.forEach(actor => { + actor.scale_x = 1; + }); + }); + }, +}; diff --git a/extensions/45/vertical-workspaces/lib/workspaceSwitcherPopup.js b/extensions/45/vertical-workspaces/lib/workspaceSwitcherPopup.js new file mode 100644 index 0000000..358bb1f --- /dev/null +++ b/extensions/45/vertical-workspaces/lib/workspaceSwitcherPopup.js @@ -0,0 +1,108 @@ +/** + * V-Shell (Vertical Workspaces) + * workspacesSwitcherPopup.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as WorkspaceSwitcherPopup from 'resource:///org/gnome/shell/ui/workspaceSwitcherPopup.js'; + +let Me; +let opt; + +export const WorkspaceSwitcherPopupModule = class { + constructor(me) { + Me = me; + opt = Me.opt; + + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; + } + + cleanGlobals() { + Me = null; + opt = null; + } + + update(reset) { + this.moduleEnabled = opt.get('workspaceSwitcherPopupModule'); + const conflict = Me.Util.getEnabledExtensions('workspace-switcher-manager').length || + Me.Util.getEnabledExtensions('WsSwitcherPopupManager').length; + if (conflict && !reset) + console.warn(`[${Me.metadata.name}] Warning: "WorkspaceSwitcherPopup" module disabled due to potential conflict with another extension`); + + reset = reset || !this.moduleEnabled || conflict; + + // don't touch original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceSwitcherPopupModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + this._overrides.addOverride('WorkspaceSwitcherPopup', WorkspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype, WorkspaceSwitcherPopupCommon); + console.debug(' WorkspaceSwitcherPopupModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' WorkspaceSwitcherPopupModule - Disabled'); + } +}; + +const WorkspaceSwitcherPopupCommon = { + // injection to _init() + after__init() { + if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL + this._list.vertical = true; + } + this._list.set_style('margin: 0;'); + if (this.get_constraints()[0]) + this.remove_constraint(this.get_constraints()[0]); + }, + + // injection to display() + after_display() { + if (opt.WS_SW_POPUP_MODE) + this._setPopupPosition(); + else + this.opacity = 0; + }, + + _setPopupPosition() { + let workArea; + if (opt.WS_SW_POPUP_MODE === 1) { + // workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);*/ + workArea = global.display.get_monitor_geometry(Main.layoutManager.primaryIndex); + } else { + // workArea = Main.layoutManager.getWorkAreaForMonitor(global.display.get_current_monitor()); + workArea = global.display.get_monitor_geometry(global.display.get_current_monitor()); + } + + let [, natHeight] = this.get_preferred_height(global.screen_width); + let [, natWidth] = this.get_preferred_width(natHeight); + let h = opt.WS_SW_POPUP_H_POSITION; + let v = opt.WS_SW_POPUP_V_POSITION; + this.x = workArea.x + Math.floor((workArea.width - natWidth) * h); + this.y = workArea.y + Math.floor((workArea.height - natHeight) * v); + this.set_position(this.x, this.y); + }, +}; diff --git a/extensions/vertical-workspaces/lib/workspaceThumbnail.js b/extensions/45/vertical-workspaces/lib/workspaceThumbnail.js similarity index 80% rename from extensions/vertical-workspaces/lib/workspaceThumbnail.js rename to extensions/45/vertical-workspaces/lib/workspaceThumbnail.js index d0bc206..75e5250 100644 --- a/extensions/vertical-workspaces/lib/workspaceThumbnail.js +++ b/extensions/45/vertical-workspaces/lib/workspaceThumbnail.js @@ -10,56 +10,96 @@ 'use strict'; -const { GLib, Clutter, Graphene, Meta, Shell, St } = imports.gi; -const DND = imports.ui.dnd; -const Main = imports.ui.main; -const Background = imports.ui.background; -const WorkspaceThumbnail = imports.ui.workspaceThumbnail; -const ThumbnailState = WorkspaceThumbnail.ThumbnailState; +import GLib from 'gi://GLib'; +import Clutter from 'gi://Clutter'; +import St from 'gi://St'; +import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; -const ControlsState = imports.ui.overviewControls.ControlsState; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; +import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js'; +import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; +import * as WorkspaceThumbnail from 'resource:///org/gnome/shell/ui/workspaceThumbnail.js'; +import * as Background from 'resource:///org/gnome/shell/ui/background.js'; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +let Me; +let opt; -// gettext -const _ = Me.imports.lib.settings._; +const ThumbnailState = { + NEW: 0, + EXPANDING: 1, + EXPANDED: 2, + ANIMATING_IN: 3, + NORMAL: 4, + REMOVING: 5, + ANIMATING_OUT: 6, + ANIMATED_OUT: 7, + COLLAPSING: 8, + DESTROYED: 9, +}; -const _Util = Me.imports.lib.util; -const shellVersion = _Util.shellVersion; - -let _overrides; +const ControlsState = OverviewControls.ControlsState; const WORKSPACE_CUT_SIZE = 10; -const _originalMaxThumbnailScale = WorkspaceThumbnail.MAX_THUMBNAIL_SCALE; +const WORKSPACE_KEEP_ALIVE_TIME = 100; -let opt = null; +export const WorkspaceThumbnailModule = class { + constructor(me) { + Me = me; + opt = Me.opt; -function update(reset = false) { - if (_overrides) - _overrides.removeAll(); - - - if (reset) { - if (_originalMaxThumbnailScale) - WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = _originalMaxThumbnailScale; - _overrides = null; - opt = null; - return; + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - opt = Me.imports.lib.settings.opt; - _overrides = new _Util.Overrides(); + cleanGlobals() { + Me = null; + opt = null; + } - // don't limit max thumbnail scale for other clients than overview, for example AATWS. - WorkspaceThumbnail.MAX_THUMBNAIL_SCALE = 1; + update(reset) { + this.moduleEnabled = true; + const conflict = false; - _overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon); - _overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon); + reset = reset || !this.moduleEnabled || conflict; - // replacing opt.ORIENTATION local constant with boxOrientation internal variable allows external customers such as the AATWS extension to control the box orientation. - Main.overview._overview.controls._thumbnailsBox._boxOrientation = opt.ORIENTATION; -} + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspaceThumbnailModule - Keeping untouched'); + } + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + // don't limit max thumbnail scale for other clients than overview, specifically AATWS. + // this variable is not yet implemented in 45.beta.1 + + this._overrides.addOverride('WorkspaceThumbnail', WorkspaceThumbnail.WorkspaceThumbnail.prototype, WorkspaceThumbnailCommon); + this._overrides.addOverride('ThumbnailsBoxCommon', WorkspaceThumbnail.ThumbnailsBox.prototype, ThumbnailsBoxCommon); + + // replacing opt.ORIENTATION local constant with boxOrientation internal variable allows external customers such as the AATWS extension to control the box orientation. + Main.overview._overview.controls._thumbnailsBox._boxOrientation = opt.ORIENTATION; + + console.debug(' WorkspaceThumbnailModule - Activated'); + } + + _disableModule() { + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' WorkspaceThumbnailModule - Disabled'); + } +}; const WorkspaceThumbnailCommon = { // injection to _init() @@ -70,6 +110,8 @@ const WorkspaceThumbnailCommon = { // unless border is removed if (opt.SHOW_WS_TMB_BG) this.add_style_class_name('ws-tmb-labeled'); + else + this.add_style_class_name('ws-tmb-transparent'); // add workspace thumbnails labels if enabled if (opt.SHOW_WST_LABELS) { // 0 - disable @@ -77,7 +119,7 @@ const WorkspaceThumbnailCommon = { const wsIndex = this.metaWorkspace.index(); let label = `${wsIndex + 1}`; if (opt.SHOW_WST_LABELS === 2) { // 2 - index + workspace name - const settings = ExtensionUtils.getSettings('org.gnome.desktop.wm.preferences'); + const settings = Me.getSettings('org.gnome.desktop.wm.preferences'); const wsLabels = settings.get_strv('workspace-names'); if (wsLabels.length > wsIndex && wsLabels[wsIndex]) label += `: ${wsLabels[wsIndex]}`; @@ -129,7 +171,9 @@ const WorkspaceThumbnailCommon = { } }); this._nWindowsConId = this.metaWorkspace.connect('notify::n-windows', () => { - // wait for new information + if (this._updateLabelTimeout) + return; + // wait for new data this._updateLabelTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => { const newLabel = getLabel(); this._wsLabel.text = newLabel; @@ -168,7 +212,7 @@ const WorkspaceThumbnailCommon = { closeButton.opacity = 255; if (!Meta.prefs_get_dynamic_workspaces() || (Meta.prefs_get_dynamic_workspaces() && global.workspace_manager.get_n_workspaces() - 1 !== this.metaWorkspace.index())) { // color the button red if ready to react on clicks - if (opt.CLOSE_WS_BUTTON_MODE < 3 || (opt.CLOSE_WS_BUTTON_MODE === 3 && _Util.isCtrlPressed())) + if (opt.CLOSE_WS_BUTTON_MODE < 3 || (opt.CLOSE_WS_BUTTON_MODE === 3 && Me.Util.isCtrlPressed())) closeButton.add_style_class_name('workspace-close-button-hover'); } }); @@ -220,12 +264,6 @@ const WorkspaceThumbnailCommon = { this._viewport.set_child_below_sibling(this._bgManager.backgroundActor, null); - this.connect('destroy', () => { - if (this._bgManager) - this._bgManager.destroy(); - this._bgManager = null; - }); - // full brightness of the thumbnail bg draws unnecessary attention // there is a grey bg under the wallpaper this._bgManager.backgroundActor.opacity = 220; @@ -256,7 +294,7 @@ const WorkspaceThumbnailCommon = { this._lastCloseClickTime = Date.now(); return; } - } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !_Util.isCtrlPressed()) { + } else if (opt.CLOSE_WS_BUTTON_MODE === 3 && !Me.Util.isCtrlPressed()) { return; } @@ -335,7 +373,7 @@ const WorkspaceThumbnailCommon = { if (!source.app && source.shellWorkspaceLaunch) return DND.DragMotionResult.COPY_DROP; - if (source instanceof imports.ui.appDisplay.FolderIcon) + if (source instanceof AppDisplay.FolderIcon) return DND.DragMotionResult.COPY_DROP; @@ -369,17 +407,10 @@ const WorkspaceThumbnailCommon = { timestamp: time, }); return true; - } else if (source instanceof imports.ui.appDisplay.FolderIcon) { - if (shellVersion >= 44) { - for (let app of source.view._apps) { - // const app = Shell.AppSystem.get_default().lookup_app(id); - app.open_new_window(this.metaWorkspace.index()); - } - } else { - for (let id of source.view._appIds) { - const app = Shell.AppSystem.get_default().lookup_app(id); - app.open_new_window(this.metaWorkspace.index()); - } + } else if (source instanceof AppDisplay.FolderIcon) { + for (let app of source.view._apps) { + // const app = Shell.AppSystem.get_default().lookup_app(id); + app.open_new_window(this.metaWorkspace.index()); } } @@ -419,7 +450,7 @@ const ThumbnailsBoxCommon = { if (!source.metaWindow && (!source.app || !source.app.can_open_new_window()) && (source.app || !source.shellWorkspaceLaunch) && - !(source instanceof imports.ui.appDisplay.FolderIcon)) + !(source instanceof AppDisplay.FolderIcon)) return false; @@ -448,17 +479,10 @@ const ThumbnailsBoxCommon = { workspace: newWorkspaceIndex, timestamp: time, }); - } else if (source instanceof imports.ui.appDisplay.FolderIcon) { - if (shellVersion >= 44) { - for (let app of source.view._apps) { - // const app = Shell.AppSystem.get_default().lookup_app(id); - app.open_new_window(newWorkspaceIndex); - } - } else { - for (let id of source.view._appIds) { - const app = Shell.AppSystem.get_default().lookup_app(id); - app.open_new_window(newWorkspaceIndex); - } + } else if (source instanceof AppDisplay.FolderIcon) { + for (let app of source.view._apps) { + // const app = Shell.AppSystem.get_default().lookup_app(id); + app.open_new_window(newWorkspaceIndex); } } @@ -469,7 +493,7 @@ const ThumbnailsBoxCommon = { // workspace while we wait for the startup sequence to load. let workspaceManager = global.workspace_manager; Main.wm.keepWorkspaceAlive(workspaceManager.get_workspace_by_index(newWorkspaceIndex), - WorkspaceThumbnail.WORKSPACE_KEEP_ALIVE_TIME); + WORKSPACE_KEEP_ALIVE_TIME); } // Start the animation on the workspace (which is actually @@ -495,7 +519,7 @@ const ThumbnailsBoxCommon = { if (!source.metaWindow && (!source.app || !source.app.can_open_new_window()) && (source.app || !source.shellWorkspaceLaunch) && - source !== Main.xdndHandler && !(source instanceof imports.ui.appDisplay.FolderIcon)) + source !== Main.xdndHandler && !(source instanceof AppDisplay.FolderIcon)) return DND.DragMotionResult.CONTINUE; const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; @@ -537,6 +561,104 @@ const ThumbnailsBoxCommon = { return DND.DragMotionResult.CONTINUE; }, + _updateStates() { + const controlsManager = Main.overview._overview.controls; + const { currentState } = controlsManager._stateAdjustment.getStateTransitionParams(); + this.SLIDE_ANIMATION_TIME = 200; + this.RESCALE_ANIMATION_TIME = 200; + // remove rescale animation during this scale transition, it is redundant and delayed + if ((currentState < 2 && currentState > 1) || controlsManager._searchController.searchActive) + this.RESCALE_ANIMATION_TIME = 0; + + this._updateStateId = 0; + + // If we are animating the indicator, wait + if (this._animatingIndicator) + return; + + // Likewise if we are in the process of hiding + if (!this._shouldShow && this.visible) + return; + + // Then slide out any thumbnails that have been destroyed + this._iterateStateThumbnails(ThumbnailState.REMOVING, thumbnail => { + this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_OUT); + + thumbnail.ease_property('slide-position', 1, { + duration: this.SLIDE_ANIMATION_TIME, + mode: Clutter.AnimationMode.LINEAR, + onComplete: () => { + this._setThumbnailState(thumbnail, ThumbnailState.ANIMATED_OUT); + this._queueUpdateStates(); + }, + }); + }); + + // As long as things are sliding out, don't proceed + if (this._stateCounts[ThumbnailState.ANIMATING_OUT] > 0) + return; + + // Once that's complete, we can start scaling to the new size, + // collapse any removed thumbnails and expand added ones + this._iterateStateThumbnails(ThumbnailState.ANIMATED_OUT, thumbnail => { + this._setThumbnailState(thumbnail, ThumbnailState.COLLAPSING); + thumbnail.ease_property('collapse-fraction', 1, { + duration: this.RESCALE_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + this._stateCounts[thumbnail.state]--; + thumbnail.state = ThumbnailState.DESTROYED; + + let index = this._thumbnails.indexOf(thumbnail); + this._thumbnails.splice(index, 1); + thumbnail.destroy(); + + this._queueUpdateStates(); + }, + }); + }); + + this._iterateStateThumbnails(ThumbnailState.NEW, thumbnail => { + this._setThumbnailState(thumbnail, ThumbnailState.EXPANDING); + thumbnail.ease_property('collapse-fraction', 0, { + duration: this.SLIDE_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + this._setThumbnailState(thumbnail, ThumbnailState.EXPANDED); + this._queueUpdateStates(); + }, + }); + }); + + if (this._pendingScaleUpdate) { + this.ease_property('scale', this._targetScale, { + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + duration: this.RESCALE_ANIMATION_TIME, + onComplete: () => this._queueUpdateStates(), + }); + this._queueUpdateStates(); + this._pendingScaleUpdate = false; + } + + // Wait until that's done + if (this._scale !== this._targetScale || + this._stateCounts[ThumbnailState.COLLAPSING] > 0 || + this._stateCounts[ThumbnailState.EXPANDING] > 0) + return; + + // And then slide in any new thumbnails + this._iterateStateThumbnails(ThumbnailState.EXPANDED, thumbnail => { + this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_IN); + thumbnail.ease_property('slide-position', 0, { + duration: this.SLIDE_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + this._setThumbnailState(thumbnail, ThumbnailState.NORMAL); + }, + }); + }); + }, + _getPlaceholderTarget(...args) { if (this._boxOrientation) return ThumbnailsBoxVertical._getPlaceholderTarget.bind(this)(...args); @@ -551,18 +673,18 @@ const ThumbnailsBoxCommon = { return ThumbnailsBoxHorizontal._withinWorkspace.bind(this)(...args); }, - get_preferred_custom_width(...args) { + vfunc_get_preferred_width(...args) { if (this._boxOrientation) - return ThumbnailsBoxVertical.get_preferred_custom_width.bind(this)(...args); + return ThumbnailsBoxVertical.vfunc_get_preferred_width.bind(this)(...args); else - return ThumbnailsBoxHorizontal.get_preferred_custom_width.bind(this)(...args); + return ThumbnailsBoxHorizontal.vfunc_get_preferred_width.bind(this)(...args); }, - get_preferred_custom_height(...args) { + vfunc_get_preferred_height(...args) { if (this._boxOrientation) - return ThumbnailsBoxVertical.get_preferred_custom_height.bind(this)(...args); + return ThumbnailsBoxVertical.vfunc_get_preferred_height.bind(this)(...args); else - return ThumbnailsBoxHorizontal.get_preferred_custom_height.bind(this)(...args); + return ThumbnailsBoxHorizontal.vfunc_get_preferred_height.bind(this)(...args); }, vfunc_allocate(...args) { @@ -632,14 +754,9 @@ const ThumbnailsBoxVertical = { return y > workspaceY1 && y <= workspaceY2; }, - // vfunc_get_preferred_width: function(forHeight) { - // override of this vfunc doesn't work for some reason (tested on Ubuntu and Fedora), it's not reachable - get_preferred_custom_width(forHeight) { - if (!this.visible) - return [0, 0]; - - if (forHeight === -1) - return this.get_preferred_custom_height(forHeight); + vfunc_get_preferred_width(forHeight) { + if (forHeight < 10) + return [this._porthole.width, this._porthole.width]; let themeNode = this.get_theme_node(); @@ -652,19 +769,14 @@ const ThumbnailsBoxVertical = { const avail = forHeight - totalSpacing; let scale = (avail / nWorkspaces) / this._porthole.height; - // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE); const width = Math.round(this._porthole.width * scale); return themeNode.adjust_preferred_height(width, width); }, - get_preferred_custom_height(_forWidth) { - if (!this.visible) - return [0, 0]; - - // Note that for getPreferredHeight/Width we cheat a bit and skip propagating - // the size request to our children because we know how big they are and know - // that the actors aren't depending on the virtual functions being called. + vfunc_get_preferred_height(forWidth) { + if (forWidth < 10) + return [0, this._porthole.height]; let themeNode = this.get_theme_node(); let spacing = themeNode.get_length('spacing'); @@ -674,15 +786,14 @@ const ThumbnailsBoxVertical = { let totalSpacing = (nWorkspaces - 3) * spacing; const ratio = this._porthole.width / this._porthole.height; - const tmbHeight = themeNode.adjust_for_width(_forWidth) / ratio; + const tmbHeight = themeNode.adjust_for_width(forWidth) / ratio; const naturalheight = this._thumbnails.reduce((accumulator, thumbnail/* , index*/) => { const progress = 1 - thumbnail.collapse_fraction; const height = tmbHeight * progress; return accumulator + height; }, 0); - - return themeNode.adjust_preferred_width(totalSpacing, naturalheight); + return themeNode.adjust_preferred_width(totalSpacing, Math.round(naturalheight)); }, // removes extra space (extraWidth in the original function), we need the box as accurate as possible @@ -759,16 +870,10 @@ const ThumbnailsBoxVertical = { this._dropPlaceholder.allocate_preferred_size( ...this._dropPlaceholder.get_position()); - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); - } else { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); - } + const laters = global.compositor.get_laters(); + laters.add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.hide(); + }); } let childBox = new Clutter.ActorBox(); @@ -796,16 +901,10 @@ const ThumbnailsBoxVertical = { this._dropPlaceholder.allocate(childBox); - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); - } else { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); - } + const laters = global.compositor.get_laters(); + laters.add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.show(); + }); y += placeholderHeight + spacing; } @@ -925,7 +1024,10 @@ const ThumbnailsBoxHorizontal = { return x > workspaceX1 && x <= workspaceX2; }, - get_preferred_custom_height(forWidth) { + vfunc_get_preferred_height(forWidth) { + if (forWidth < 10) + return [this._porthole.height, this._porthole.height]; + let themeNode = this.get_theme_node(); forWidth = themeNode.adjust_for_width(forWidth); @@ -937,18 +1039,15 @@ const ThumbnailsBoxHorizontal = { const avail = forWidth - totalSpacing; let scale = (avail / nWorkspaces) / this._porthole.width; - // scale = Math.min(scale, opt.MAX_THUMBNAIL_SCALE); const height = Math.round(this._porthole.height * scale); + return themeNode.adjust_preferred_height(height, height); }, - get_preferred_custom_width(_forHeight) { - // Note that for getPreferredHeight/Width we cheat a bit and skip propagating - // the size request to our children because we know how big they are and know - // that the actors aren't depending on the virtual functions being called. - if (!this.visible) - return [0, 0]; + vfunc_get_preferred_width(forHeight) { + if (forHeight < 10) + return [0, this._porthole.width]; let themeNode = this.get_theme_node(); @@ -959,13 +1058,14 @@ const ThumbnailsBoxHorizontal = { const ratio = this._porthole.height / this._porthole.width; - const tmbWidth = themeNode.adjust_for_height(_forHeight) / ratio; + const tmbWidth = themeNode.adjust_for_height(forHeight) / ratio; const naturalWidth = this._thumbnails.reduce((accumulator, thumbnail) => { const progress = 1 - thumbnail.collapse_fraction; const width = tmbWidth * progress; return accumulator + width; }, 0); + return themeNode.adjust_preferred_width(totalSpacing, naturalWidth); }, @@ -1041,16 +1141,10 @@ const ThumbnailsBoxHorizontal = { this._dropPlaceholder.allocate_preferred_size( ...this._dropPlaceholder.get_position()); - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); - } else { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.hide(); - }); - } + const laters = global.compositor.get_laters(); + laters.add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.hide(); + }); } let childBox = new Clutter.ActorBox(); @@ -1078,16 +1172,10 @@ const ThumbnailsBoxHorizontal = { this._dropPlaceholder.allocate(childBox); - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); - } else { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { - this._dropPlaceholder.show(); - }); - } + const laters = global.compositor.get_laters(); + laters.add(Meta.LaterType.BEFORE_REDRAW, () => { + this._dropPlaceholder.show(); + }); x += placeholderWidth + spacing; } diff --git a/extensions/vertical-workspaces/lib/workspacesView.js b/extensions/45/vertical-workspaces/lib/workspacesView.js similarity index 77% rename from extensions/vertical-workspaces/lib/workspacesView.js rename to extensions/45/vertical-workspaces/lib/workspacesView.js index e3575f1..98b3062 100644 --- a/extensions/vertical-workspaces/lib/workspacesView.js +++ b/extensions/45/vertical-workspaces/lib/workspacesView.js @@ -10,62 +10,91 @@ 'use strict'; -const { GObject, Clutter, Meta, St } = imports.gi; +import Clutter from 'gi://Clutter'; +import St from 'gi://St'; +import Meta from 'gi://Meta'; +import GObject from 'gi://GObject'; -const Main = imports.ui.main; -const Util = imports.misc.util; -const WorkspacesView = imports.ui.workspacesView; -// first reference to constant defined using const in other module returns undefined, the SecondaryMonitorDisplay const will remain empty and unused -const SecondaryMonitorDisplay = WorkspacesView.SecondaryMonitorDisplay; -const ControlsState = imports.ui.overviewControls.ControlsState; -const FitMode = imports.ui.workspacesView.FitMode; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Overview from 'resource:///org/gnome/shell/ui/overview.js'; +import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; +import * as WorkspacesView from 'resource:///org/gnome/shell/ui/workspacesView.js'; -const SIDE_CONTROLS_ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const SEARCH_WINDOWS_PREFIX = Me.imports.lib.windowSearchProvider.prefix; -const SEARCH_RECENT_FILES_PREFIX = Me.imports.lib.recentFilesSearchProvider.prefix; - -const _Util = Me.imports.lib.util; -let _overrides; +import * as Util from 'resource:///org/gnome/shell/misc/util.js'; +let Me; let opt; +const ControlsState = OverviewControls.ControlsState; +const FitMode = WorkspacesView.FitMode; -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - opt.DESKTOP_CUBE_ENABLED = Main.extensionManager._enabledExtensions.includes('desktop-cube@schneegans.github.com'); - const cubeSupported = opt.DESKTOP_CUBE_ENABLED && !opt.ORIENTATION && !opt.OVERVIEW_MODE; +export const WorkspacesViewModule = class { + constructor(me) { + Me = me; + opt = Me.opt; - // if desktop cube extension is enabled while V-Shell is loaded, removeAll() would override its code - if (_overrides && !cubeSupported) { - _overrides.removeAll(); - global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1); + this._firstActivation = true; + this.moduleEnabled = false; + this._overrides = null; } - if (reset) { - _overrides = null; + cleanGlobals() { + Me = null; opt = null; - return; } + update(reset) { + this.moduleEnabled = true; + const conflict = false; - _overrides = new _Util.Overrides(); + reset = reset || !this.moduleEnabled || conflict; - if (!cubeSupported) - _overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon); - - _overrides.addOverride('WorkspacesDisplay', WorkspacesView.WorkspacesDisplay.prototype, WorkspacesDisplay); - _overrides.addOverride('ExtraWorkspaceView', WorkspacesView.ExtraWorkspaceView.prototype, ExtraWorkspaceView); - - if (opt.ORIENTATION) { - // switch internal workspace orientation in GS - global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1); - _overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayVertical); - } else { - _overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayHorizontal); + // don't touch the original code if module disabled + if (reset && !this._firstActivation) { + this._disableModule(); + } else if (!reset) { + this._firstActivation = false; + this._activateModule(); + } + if (reset && this._firstActivation) + console.debug(' WorkspacesViewModule - Keeping untouched'); } -} + + _activateModule() { + if (!this._overrides) + this._overrides = new Me.Util.Overrides(); + + const desktopCubeEnabled = Me.Util.getEnabledExtensions('desktop-cube@schneegans.github.com').length; + const desktopCubeConflict = desktopCubeEnabled && !opt.ORIENTATION && !opt.OVERVIEW_MODE; + + if (!desktopCubeConflict) + this._overrides.addOverride('WorkspacesView', WorkspacesView.WorkspacesView.prototype, WorkspacesViewCommon); + + this._overrides.addOverride('WorkspacesDisplay', WorkspacesView.WorkspacesDisplay.prototype, WorkspacesDisplayCommon); + this._overrides.addOverride('ExtraWorkspaceView', WorkspacesView.ExtraWorkspaceView.prototype, ExtraWorkspaceViewCommon); + this._overrides.addOverride('SecondaryMonitorDisplayCommon', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayCommon); + + if (opt.ORIENTATION) { + // switch internal workspace orientation in GS + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1); + this._overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayVertical); + } else { + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1); + this._overrides.addOverride('SecondaryMonitorDisplay', WorkspacesView.SecondaryMonitorDisplay.prototype, SecondaryMonitorDisplayHorizontal); + } + + console.debug(' WorkspacesViewModule - Activated'); + } + + _disableModule() { + global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, 1, -1); + if (this._overrides) + this._overrides.removeAll(); + this._overrides = null; + + console.debug(' WorkspacesViewModule - Disabled'); + } +}; const WorkspacesViewCommon = { _getFirstFitSingleWorkspaceBox(box, spacing, vertical) { @@ -91,7 +120,6 @@ const WorkspacesViewCommon = { } const fitSingleBox = new Clutter.ActorBox({ x1, y1 }); - fitSingleBox.set_size(workspaceWidth, workspaceHeight); return fitSingleBox; @@ -143,21 +171,7 @@ const WorkspacesViewCommon = { }, _updateVisibility() { - // replaced in _updateWorkspacesState - /* let workspaceManager = global.workspace_manager; - let active = workspaceManager.get_active_workspace_index(); - - const fitMode = this._fitModeAdjustment.value; - const singleFitMode = fitMode === FitMode.SINGLE; - - for (let w = 0; w < this._workspaces.length; w++) { - let workspace = this._workspaces[w]; - - if (this._animating || this._gestureActive || !singleFitMode) - workspace.show(); - else - workspace.visible = Math.abs(w - active) <= opt.NUMBER_OF_VISIBLE_NEIGHBORS; - }*/ + // visibility handles _updateWorkspacesState() }, // disable scaling and hide inactive workspaces @@ -175,27 +189,21 @@ const WorkspacesViewCommon = { const primaryMonitor = Main.layoutManager.primaryMonitor.index; - // define the transition values here to save time in each ws - let scaleX, scaleY; - if (opt.ORIENTATION) { // vertical 1 / horizontal 0 - scaleX = 1; - scaleY = 0.1; - } else { - scaleX = 0.1; - scaleY = 1; - } - const wsScrollProgress = adj.value % 1; const secondaryMonitor = this._monitorIndex !== global.display.get_primary_monitor(); const blockSecondaryAppGrid = opt.OVERVIEW_MODE && currentState > 1; + // Hide inactive workspaces this._workspaces.forEach((w, index) => { if (!(blockSecondaryAppGrid && secondaryMonitor)) w.stateAdjustment.value = workspaceMode; - const distanceToCurrentWorkspace = Math.abs(adj.value - index); + let distance = adj.value - index; + const distanceToCurrentWorkspace = Math.abs(distance); const scaleProgress = 1 - Math.clamp(distanceToCurrentWorkspace, 0, 1); + // const scale = Util.lerp(0.94, 1, scaleProgress); + // w.set_scale(scale, scale); // if we disable workspaces that we can't or don't need to see, transition animations will be noticeably smoother // only the current ws needs to be visible during overview transition animations @@ -210,22 +218,28 @@ const WorkspacesViewCommon = { // after transition from APP_GRID to WINDOW_PICKER state, // adjacent workspaces are hidden and we need them to show up // make them visible during animation can impact smoothness of the animation - // so we show them after the animation finished, scaling animation will make impression that they move in from outside the monitor + // so we show them after the animation finished, move them to their position from outside of the monitor if (!w.visible && distanceToCurrentWorkspace === 1 && initialState === ControlsState.APP_GRID && currentState === ControlsState.WINDOW_PICKER) { - w.scale_x = scaleX; - w.scale_y = scaleY; w.visible = true; + const directionNext = distance > 0; + if (!opt.ORIENTATION) { + const width = w.width * 0.6 * opt.WS_PREVIEW_SCALE; + w.translation_x = directionNext ? -width : width; + } + if (opt.ORIENTATION) { + const height = w.height * 0.6 * opt.WS_PREVIEW_SCALE; + w.translation_y = directionNext ? -height : height; + } + + w.opacity = 10; + w.get_parent().set_child_below_sibling(w, null); w.ease({ - duration: 100, - scale_x: 1, - scale_y: 1, + duration: 300, + translation_x: 0, + translation_y: 0, + opacity: 255, mode: Clutter.AnimationMode.EASE_OUT_QUAD, }); - } else if (!w.visible && distanceToCurrentWorkspace <= opt.NUMBER_OF_VISIBLE_NEIGHBORS && currentState === ControlsState.WINDOW_PICKER) { - w.set({ - scale_x: 1, - scale_y: 1, - }); } // force ws preview bg corner radiuses where GS doesn't do it @@ -238,9 +252,40 @@ const WorkspacesViewCommon = { w._background.opacity = 0; }); }, + + exposeWindows(workspaceIndex = null, callback) { + let adjustments = []; + if (workspaceIndex === null) { + this._workspaces.forEach(ws => { + adjustments.push(ws._background._stateAdjustment); + }); + } else { + adjustments.push(this._workspaces[workspaceIndex]._background._stateAdjustment); + } + + adjustments.forEach(adj => { + if (adj.value === 0) { + adj.value = 0; + adj.ease(1, { + duration: 200, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + opt.WORKSPACE_MODE = 1; + if (callback) + callback(); + }, + }); + } + }); + }, +}; + +const SecondaryMonitorDisplayCommon = { + exposeWindows(...args) { + this._workspacesView.exposeWindows(...args); + }, }; -// SecondaryMonitorDisplay Vertical const SecondaryMonitorDisplayVertical = { _getThumbnailParamsForState(state) { @@ -276,7 +321,7 @@ const SecondaryMonitorDisplayVertical = { const [width, height] = box.get_size(); const { expandFraction } = this._thumbnails; - const [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(height - 2 * spacing); + const [, thumbnailsWidth] = this._thumbnails.get_preferred_width(height - 2 * spacing); let scaledWidth; if (opt.SEC_WS_PREVIEW_SHIFT && !opt.PANEL_DISABLED) scaledWidth = ((height - Main.panel.height) * opt.SEC_MAX_THUMBNAIL_SCALE) * (width / height); @@ -341,26 +386,40 @@ const SecondaryMonitorDisplayVertical = { const spacing = themeNode.get_length('spacing') * expandFraction; const padding = Math.round(0.1 * height); - let thumbnailsWidth = this._getThumbnailsWidth(contentBox, spacing); - let [, thumbnailsHeight] = this._thumbnails.get_preferred_custom_height(thumbnailsWidth); - thumbnailsHeight = Math.min(thumbnailsHeight, height - 2 * spacing); - + let thumbnailsWidth = 0; + let thumbnailsHeight = 0; this._thumbnails.visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible) { + const reduceBoxHeight = opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; + + thumbnailsWidth = width * opt.SEC_MAX_THUMBNAIL_SCALE; + + let totalTmbSpacing; + [totalTmbSpacing, thumbnailsHeight] = this._thumbnails.get_preferred_height(thumbnailsWidth); + thumbnailsHeight = Math.round(thumbnailsHeight + totalTmbSpacing); + + const thumbnailsHeightMax = height - spacing - reduceBoxHeight; + + if (thumbnailsHeight > thumbnailsHeightMax) { + thumbnailsHeight = thumbnailsHeightMax; + thumbnailsWidth = Math.round(this._thumbnails.get_preferred_width(thumbnailsHeight)[1]); + } + let wsTmbX; if (opt.SEC_WS_TMB_LEFT) { // left - wsTmbX = Math.round(spacing / 4); + wsTmbX = spacing / 2; this._thumbnails._positionLeft = true; } else { - wsTmbX = Math.round(width - spacing / 4 - thumbnailsWidth); + wsTmbX = width - spacing / 2 - thumbnailsWidth; this._thumbnails._positionLeft = false; } const childBox = new Clutter.ActorBox(); - const availSpace = height - thumbnailsHeight - 2 * spacing; + const availSpace = height - thumbnailsHeight; let wsTmbY = availSpace / 2; - wsTmbY -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbY - spacing; + wsTmbY -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbY; + wsTmbY += opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; childBox.set_origin(Math.round(wsTmbX), Math.round(wsTmbY)); childBox.set_size(thumbnailsWidth, thumbnailsHeight); @@ -390,7 +449,6 @@ const SecondaryMonitorDisplayVertical = { if (opt.OVERVIEW_MODE2) this.set_child_above_sibling(this._thumbnails, null); - const visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible === visible) @@ -399,7 +457,7 @@ const SecondaryMonitorDisplayVertical = { this._thumbnails.show(); this._updateThumbnailParams(); this._thumbnails.ease_property('expand-fraction', visible ? 1 : 0, { - duration: SIDE_CONTROLS_ANIMATION_TIME, + duration: Overview.ANIMATION_TIME, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { this._thumbnails.visible = visible; @@ -417,7 +475,6 @@ const SecondaryMonitorDisplayVertical = { if (!this._thumbnails._thumbnails.length) this._thumbnails._createThumbnails(); - const { initialState, finalState, progress } = this._overviewAdjustment.getStateTransitionParams(); @@ -474,7 +531,6 @@ const SecondaryMonitorDisplayVertical = { }, }; -// SecondaryMonitorDisplay Horizontal const SecondaryMonitorDisplayHorizontal = { _getThumbnailParamsForState(state) { // const { ControlsState } = OverviewControls; @@ -549,18 +605,6 @@ const SecondaryMonitorDisplayHorizontal = { return workspaceBox; }, - _getThumbnailsHeight(box) { - if (opt.SEC_WS_TMB_HIDDEN) - return 0; - - const [width, height] = box.get_size(); - const { expandFraction } = this._thumbnails; - const [thumbnailsHeight] = this._thumbnails.get_preferred_height(width); - return Math.min( - thumbnailsHeight * expandFraction, - height * opt.SEC_MAX_THUMBNAIL_SCALE); - }, - vfunc_allocate(box) { this.set_allocation(box); @@ -571,24 +615,36 @@ const SecondaryMonitorDisplayHorizontal = { const spacing = themeNode.get_length('spacing') * expandFraction; const padding = Math.round(0.1 * height); - let thumbnailsHeight = this._getThumbnailsHeight(contentBox); - let [, thumbnailsWidth] = this._thumbnails.get_preferred_custom_width(thumbnailsHeight); - thumbnailsWidth = Math.min(thumbnailsWidth, width - 2 * spacing); - + let thumbnailsWidth = 0; + let thumbnailsHeight = 0; this._thumbnails.visible = !opt.SEC_WS_TMB_HIDDEN; if (this._thumbnails.visible) { + const reservedHeight = opt.SEC_WS_PREVIEW_SHIFT && Main.panel.visible ? Main.panel.height : 0; + + thumbnailsHeight = height * opt.SEC_MAX_THUMBNAIL_SCALE; + + let totalTmbSpacing; + [totalTmbSpacing, thumbnailsWidth] = this._thumbnails.get_preferred_width(thumbnailsHeight); + thumbnailsWidth = Math.round(thumbnailsWidth + totalTmbSpacing); + + const thumbnailsWidthMax = width - spacing; + + if (thumbnailsWidth > thumbnailsWidthMax) { + thumbnailsWidth = thumbnailsWidthMax; + thumbnailsHeight = Math.round(this._thumbnails.get_preferred_height(thumbnailsWidth)[1]); + } + let wsTmbY; if (opt.SEC_WS_TMB_TOP) - wsTmbY = Math.round(spacing / 4); + wsTmbY = spacing / 2 + reservedHeight; else - wsTmbY = Math.round(height - spacing / 4 - thumbnailsHeight); - + wsTmbY = height - spacing / 2 - thumbnailsHeight; const childBox = new Clutter.ActorBox(); - const availSpace = width - thumbnailsWidth - 2 * spacing; + const availSpace = width - thumbnailsWidth; let wsTmbX = availSpace / 2; - wsTmbX -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbX - spacing; + wsTmbX -= opt.SEC_WS_TMB_POSITION_ADJUSTMENT * wsTmbX; childBox.set_origin(Math.round(wsTmbX), Math.round(wsTmbY)); childBox.set_size(thumbnailsWidth, thumbnailsHeight); @@ -625,7 +681,6 @@ const SecondaryMonitorDisplayHorizontal = { if (!this._thumbnails._thumbnails.length) this._thumbnails._createThumbnails(); - const { initialState, finalState, progress } = this._overviewAdjustment.getStateTransitionParams(); @@ -682,7 +737,7 @@ const SecondaryMonitorDisplayHorizontal = { }, }; -const ExtraWorkspaceView = { +const ExtraWorkspaceViewCommon = { _updateWorkspaceMode() { const overviewState = this._overviewAdjustment.value; @@ -701,9 +756,23 @@ const ExtraWorkspaceView = { if (!opt.SHOW_WS_PREVIEW_BG && this._workspace._background.opacity) this._workspace._background.opacity = 0; }, + + exposeWindows() { + const adjustment = this._workspace._background._stateAdjustment; + if (adjustment.value === 0) { + adjustment.value = 0; + adjustment.ease(1, { + duration: 200, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + opt.WORKSPACE_MODE = 1; + }, + }); + } + }, }; -const WorkspacesDisplay = { +const WorkspacesDisplayCommon = { _updateWorkspacesViews() { for (let i = 0; i < this._workspacesViews.length; i++) this._workspacesViews[i].destroy(); @@ -761,8 +830,8 @@ const WorkspacesDisplay = { return Clutter.EVENT_STOP; } - if (_Util.isShiftPressed()) { - let direction = _Util.getScrollDirection(event); + if (Me.Util.isShiftPressed()) { + let direction = Me.Util.getScrollDirection(event); if (direction === null || (Date.now() - this._lastScrollTime) < 150) return Clutter.EVENT_STOP; this._lastScrollTime = Date.now(); @@ -775,11 +844,11 @@ const WorkspacesDisplay = { else direction = 0; - if (direction) { - _Util.reorderWorkspace(direction); + Me.Util.reorderWorkspace(direction); // make all workspaces on primary monitor visible for case the new position is hidden - Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => { + const primaryMonitorIndex = global.display.get_primary_monitor(); + Main.overview._overview._controls._workspacesDisplay._workspacesViews[primaryMonitorIndex]._workspaces.forEach(w => { w.visible = true; }); return Clutter.EVENT_STOP; @@ -806,7 +875,7 @@ const WorkspacesDisplay = { switch (symbol) { case Clutter.KEY_Return: case Clutter.KEY_KP_Enter: - if (_Util.isCtrlPressed()) { + if (Me.Util.isCtrlPressed()) { Main.ctrlAltTabManager._items.forEach(i => { if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i); @@ -836,17 +905,17 @@ const WorkspacesDisplay = { which = workspaceManager.n_workspaces - 1; break; case Clutter.KEY_space: - if (_Util.isCtrlPressed() && _Util.isShiftPressed()) { - _Util.openPreferences(); - } else if (_Util.isAltPressed()) { + if (Me.Util.isCtrlPressed() && Me.Util.isShiftPressed()) { + Me.Util.activateSearchProvider(Me.ESP_PREFIX); + } else if (Me.Util.isAltPressed()) { Main.ctrlAltTabManager._items.forEach(i => { if (i.sortGroup === 1 && i.name === 'Dash') Main.ctrlAltTabManager.focusGroup(i); }); - } else if (opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED && _Util.isCtrlPressed()) { - _Util.activateSearchProvider(SEARCH_RECENT_FILES_PREFIX); - } else if (opt.WINDOW_SEARCH_PROVIDER_ENABLED) { - _Util.activateSearchProvider(SEARCH_WINDOWS_PREFIX); + } else if (opt.get('recentFilesSearchProviderModule') && Me.Util.isCtrlPressed()) { + Me.Util.activateSearchProvider(Me.RFSP_PREFIX); + } else if (opt.get('windowSearchProviderModule')) { + Me.Util.activateSearchProvider(Me.WSP_PREFIX); } return Clutter.EVENT_STOP; @@ -867,16 +936,18 @@ const WorkspacesDisplay = { }); } else if (opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE && state === 1) { // expose windows for OVERVIEW_MODE 1 - const adjustment = this._workspacesViews[0]._workspaces[global.workspace_manager.get_active_workspace().index()]._background._stateAdjustment; - opt.WORKSPACE_MODE = 1; - _Util.exposeWindows(adjustment, true); + const wsIndex = global.workspace_manager.get_active_workspace().index(); + // after expose animation activate keyboard for window selection + const callback = Me.Util.activateKeyboardForWorkspaceView; + this._workspacesViews.forEach( + view => { + view.exposeWindows(wsIndex, callback); + } + ); } else { if (state === 2) return Clutter.EVENT_PROPAGATE; - Main.ctrlAltTabManager._items.forEach(i => { - if (i.sortGroup === 1 && i.name === 'Windows') - Main.ctrlAltTabManager.focusGroup(i); - }); + Me.Util.activateKeyboardForWorkspaceView(); } return Clutter.EVENT_STOP; @@ -895,14 +966,14 @@ const WorkspacesDisplay = { // Otherwise it is a workspace index ws = workspaceManager.get_workspace_by_index(which); - if (_Util.isShiftPressed()) { + if (Me.Util.isShiftPressed()) { let direction; if (which === Meta.MotionDirection.UP || which === Meta.MotionDirection.LEFT) direction = -1; else if (which === Meta.MotionDirection.DOWN || which === Meta.MotionDirection.RIGHT) direction = 1; if (direction) - _Util.reorderWorkspace(direction); + Me.Util.reorderWorkspace(direction); // make all workspaces on primary monitor visible for case the new position is hidden Main.overview._overview._controls._workspacesDisplay._workspacesViews[0]._workspaces.forEach(w => { w.visible = true; diff --git a/extensions/vertical-workspaces/metadata.json b/extensions/45/vertical-workspaces/metadata.json similarity index 74% rename from extensions/vertical-workspaces/metadata.json rename to extensions/45/vertical-workspaces/metadata.json index dbfaa91..895047e 100644 --- a/extensions/vertical-workspaces/metadata.json +++ b/extensions/45/vertical-workspaces/metadata.json @@ -3,12 +3,17 @@ "uuid": "vertical-workspaces@G-dH.github.com", "description": "Customize your GNOME Shell UX to suit your workflow, whether you like horizontally or vertically stacked workspaces.", "shell-version": [ - "42", - "43", - "44" + "45" + ], + "session-modes": [ + "user", + "unlock-dialog" ], "url": "https://github.com/G-dH/vertical-workspaces", + "donations": { + "buymeacoffee": "georgdh" + }, "gettext-domain": "vertical-workspaces", "settings-schema": "org.gnome.shell.extensions.vertical-workspaces", - "version": 28 + "version-name": "45.2" } diff --git a/extensions/vertical-workspaces/po/cs.po b/extensions/45/vertical-workspaces/po/cs.po similarity index 100% rename from extensions/vertical-workspaces/po/cs.po rename to extensions/45/vertical-workspaces/po/cs.po diff --git a/extensions/vertical-workspaces/po/vertical-workspaces.pot b/extensions/45/vertical-workspaces/po/nl.po similarity index 56% copy from extensions/vertical-workspaces/po/vertical-workspaces.pot copy to extensions/45/vertical-workspaces/po/nl.po index 44ccddb..2d9613e 100644 --- a/extensions/vertical-workspaces/po/vertical-workspaces.pot +++ b/extensions/45/vertical-workspaces/po/nl.po @@ -3,95 +3,96 @@ # This file is distributed under the same license as the vertical-workspaces package. # FIRST AUTHOR , YEAR. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: vertical-workspaces\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-03-08 12:37+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"PO-Revision-Date: 2023-05-04 14:03+0200\n" +"Last-Translator: Heimen Stoffels \n" +"Language-Team: \n" +"Language: nl\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.2.2\n" #: dash.js:743 msgid "Search Open Windows (Hotkey: Space)" -msgstr "" +msgstr "Geopende vensters doorzoeken (sneltoets: spatiebalk)" #: dash.js:816 msgid "Search Recent Files (Hotkey: Ctrl + Space)" -msgstr "" +msgstr "Onlangs gebruikte bestanden doorzoeken (sneltoets: Ctrl+spatiebalk)" #: windowSearchProvider.js:117 windowSearchProvider.js:119 msgid "Open Windows" -msgstr "" +msgstr "Geopende vensters" #: windowSearchProvider.js:118 msgid "List of open windows" -msgstr "" +msgstr "Een lijst met geopende vensters" #: appDisplay.js:1317 msgid "Force Quit" -msgstr "" +msgstr "Toepassing gedwongen afsluiten" #: appDisplay.js:1335 msgid "Move App to Current Workspace ( Shift + Click )" -msgstr "" +msgstr "Toepassing verplaatsen naar huidig werkblad (Shift+muisklik)" #: prefs.js:47 prefs.js:1465 msgid "Layout" -msgstr "" +msgstr "Indeling" #: prefs.js:53 msgid "Appearance" -msgstr "" +msgstr "Vormgeving" #: prefs.js:59 msgid "Behavior" -msgstr "" +msgstr "Gedrag" #: prefs.js:65 msgid "Misc" -msgstr "" +msgstr "Overig" #: prefs.js:71 msgid "About" -msgstr "" +msgstr "Over" #: prefs.js:119 prefs.js:517 prefs.js:1030 prefs.js:1456 msgid "Dash" -msgstr "" +msgstr "Dash" #: prefs.js:125 msgid "Dash Position" -msgstr "" +msgstr "Dashlocatie" #: prefs.js:131 msgid "Top" -msgstr "" +msgstr "Bovenaan" #: prefs.js:132 msgid "Right" -msgstr "" +msgstr "Rechts" #: prefs.js:133 prefs.js:355 msgid "Bottom" -msgstr "" +msgstr "Onderaan" #: prefs.js:134 msgid "Left" -msgstr "" +msgstr "Links" #: prefs.js:135 prefs.js:393 prefs.js:557 prefs.js:572 prefs.js:587 msgid "Hide" -msgstr "" +msgstr "Verbergen" #: prefs.js:142 msgid "Center Horizontal Dash to Workspace" -msgstr "" +msgstr "Horizontale dash centreren op werkblad" #: prefs.js:143 msgid "" @@ -99,24 +100,29 @@ msgid "" "recalculated relative to the workspace preview instead of the screen. Works " "only with the default Dash." msgstr "" +"Als de dashlocatie boven- of onderaan is, dan wordt de locatie herberekend " +"op basis van de werkbladvoorvertoning in plaats van het scherm. Werkt alleen " +"met de standaarddash." #: prefs.js:160 msgid "Fine Tune Dash Position" -msgstr "" +msgstr "Dashlocatie aanpassen" #: prefs.js:161 msgid "" "Adjusts position of the dock on chosen axis. Works only with the default " "Dash." msgstr "" +"Pas de locatie van het dock op de gekozen as aan. Werkt alleen met de " +"standaarddash." #: prefs.js:170 msgid "Workspace Thumbnails / Orientation" -msgstr "" +msgstr "Werkbladminiaturen/-oriëntatie" #: prefs.js:176 msgid "Thumbnails Position / Workspaces Orientation" -msgstr "" +msgstr "Miniatuurlocatie/Werkbladoriëntatie" #: prefs.js:177 msgid "" @@ -125,42 +131,46 @@ msgid "" "workspace thumbnails, one sets workspaces to the vertical orientation, the " "second one to horizontal." msgstr "" +"De locatie van werkbladminiaturen op het scherm stelt tevens de oriëntatie " +"van werkbladen in op verticaal of horizontaal. U heeft twee mogelijkheden om " +"miniaturen uit te schakelen: u kunt de werkbladen instellen op verticaal of " +"horizontaal." #: prefs.js:183 msgid "Left \t Vertical Orientation" -msgstr "" +msgstr "Links \t Verticale oriëntatie" #: prefs.js:184 msgid "Right \t Vertical Orientation" -msgstr "" +msgstr "Rechts \t Horizontale oriëntatie" #: prefs.js:185 msgid "Hide \t Set Vertical Orientation" -msgstr "" +msgstr "Verbergen \t Verticale oriëntatie instellen" #: prefs.js:186 msgid "Top \t Horizontal Orientation" -msgstr "" +msgstr "Bovenaan \t Horizontale oriëntatie" #: prefs.js:187 msgid "Bottom \t Horizontal Orientation" -msgstr "" +msgstr "Onderaan \t Horizontale oriëntatie" #: prefs.js:188 msgid "Hide \t Set Horizontal Orientation" -msgstr "" +msgstr "Verbergen \t Horizontale oriëntatie instellen" #: prefs.js:204 prefs.js:409 msgid "Fine Tune Workspace Thumbnails Position" -msgstr "" +msgstr "Werkbladminiatuurlocatie instellen" #: prefs.js:205 msgid "Adjusts workspace thumbnails vertical position." -msgstr "" +msgstr "Pas de verticale positie van werkbladminiaturen aan." #: prefs.js:213 msgid "Reserve Full Screen Height/Width for Thumbnails" -msgstr "" +msgstr "Gehele scherm gebruiken voor miniaturen" #: prefs.js:214 msgid "" @@ -168,24 +178,29 @@ msgid "" "the expense of space available for Dash (if the Dash is oriented in a " "different axis)." msgstr "" +"De gehele schermhoogte en -breedte zal worden gebruikt voor " +"werkbladminiaturen. Let op: dit gaat ten koste van de beschikbare dashruimte " +"(als de dash op een andere oriëntatie is ingesteld)." #: prefs.js:231 prefs.js:427 msgid "Workspace Thumbnails Max Scale" -msgstr "" +msgstr "Max. omvang van werkbladminiaturen" #: prefs.js:232 msgid "" "Adjusts maximum size of the workspace thumbnails (% relative to display " "width)." msgstr "" +"Pas de maximale omvang van werkbladminiaturen aan (% relatief in verhouding " +"tot de schermbreedte)." #: prefs.js:241 prefs.js:697 msgid "Workspace Preview" -msgstr "" +msgstr "Werkbladvoorvertoning" #: prefs.js:256 msgid "Workspaces Scale" -msgstr "" +msgstr "Werkbladomvang verkleinen" #: prefs.js:257 msgid "" @@ -193,10 +208,12 @@ msgid "" "workspaces on the screen. Default size is calculated to use all available " "space." msgstr "" +"Maak de werkbladvoorvertoningen kleiner om meer werkbladen op het scherm te " +"tonen. De standaardomvang maakt gebruik van alle beschikbare ruimte." #: prefs.js:274 msgid "Workspaces Spacing" -msgstr "" +msgstr "Ruimte tussen werkbladen" #: prefs.js:275 msgid "" @@ -204,14 +221,17 @@ msgid "" "the adjacent workspaces overlap to the current workspace overview. Default " "value should set the adjacent workspaces out of the screen." msgstr "" +"Pas de ruimte tussen werkbladvoorvertoningen aan zodat u zelf kunt bepalen " +"hoeveel werkbladen mogen overlappen. De standaardwaarde ligt buiten het " +"scherm." #: prefs.js:284 prefs.js:712 prefs.js:1072 msgid "App Grid" -msgstr "" +msgstr "Toepassingsrooster" #: prefs.js:290 msgid "Center App Grid" -msgstr "" +msgstr "Toepassingsrooster centreren" #: prefs.js:291 msgid "" @@ -220,137 +240,152 @@ msgid "" "for narrower and small resolution displays, especially if workspace " "thumbnails are bigger." msgstr "" +"Toon het toepassingsrooster op het midden van het scherm in plaats van over " +"de gehele ruimte. Let op: deze optie kan van invloed zijn op de " +"roosteromvang. Op kleine schermen wordt het rooster smaller, vooral als de " +"werkbladminiaturen groter zijn." #: prefs.js:300 msgid "Search View" -msgstr "" +msgstr "Zoekweergave" #: prefs.js:306 msgid "Center Search View" -msgstr "" +msgstr "Zoekweergave centreren" #: prefs.js:307 msgid "" "Search view will be centered to the display instead of the available space." msgstr "" +"Toon de zoekweergave op het midden van het scherm in plaats van over de " +"gehele ruimte." #: prefs.js:315 msgid "Always Show Search Entry" -msgstr "" +msgstr "Zoekvak altijd tonen" #: prefs.js:316 msgid "" "If disabled, the search entry field will be hidden when not in use, so the " "workspace preview and app grid may take up more space." msgstr "" +"Schakel uit om het zoekvak te verbergen als het niet wordt gebruikt, zodat " +"er meer ruimte voor de werkbladvoorvertoningen en het toepassingsrooster is." #: prefs.js:333 msgid "Search Results Width" -msgstr "" +msgstr "Breedte van zoekresultaten" #: prefs.js:334 msgid "" "Adjusts maximum width of search results view (% relative to default). This " "allows you to fit more (or less) app icons into the app search result." msgstr "" +"Pas de breedte van zoekresultaten aan (% relatief ten opzichte van " +"standaard). Hierdoor kunt u meer of minder pictogrammen in de resultaten " +"tonen." #: prefs.js:342 prefs.js:1483 msgid "Panel" -msgstr "" +msgstr "Bovenbalk" #: prefs.js:348 msgid "Main Panel Position" -msgstr "" +msgstr "Bovenbalklocatie" #: prefs.js:349 msgid "Allows you to place the main panel at the bottom of your monitor." -msgstr "" +msgstr "Verplaats de bovenbalk naar de onderkant van het scherm." #: prefs.js:354 msgid "Top (Default)" -msgstr "" +msgstr "Bovenaan (standaard)" #: prefs.js:362 msgid "Main Panel Visibility" -msgstr "" +msgstr "Zichtbaarheid van bovenbalk" #: prefs.js:363 msgid "Main panel can be visible always, only in the overview or never." -msgstr "" +msgstr "Toon de bovenbalk altijd, alleen op het overzicht of nooit." #: prefs.js:368 msgid "Always Visible (Default)" -msgstr "" +msgstr "Altijd tonen (standaard)" #: prefs.js:369 msgid "Overview Only" -msgstr "" +msgstr "Alleen op het overzicht" #: prefs.js:370 msgid "Always Hidden" -msgstr "" +msgstr "Altijd verbergen" #: prefs.js:378 msgid "Secondary Monitors" -msgstr "" +msgstr "Meerdere beeldschermen" #: prefs.js:384 msgid "Workspace Thumbnails Position" -msgstr "" +msgstr "Werkbladminiatuurlocaties" #: prefs.js:385 msgid "" "Allows you to place workspace thumbnails of secondary monitors on the " "opposite side than on the primary monitor." -msgstr "" +msgstr "Plaats werkbladminiaturen op andere beeldschermen dan het hoofdscherm." #: prefs.js:390 msgid "Same as Primary" -msgstr "" +msgstr "Gelijk aan hoofdscherm" #: prefs.js:391 msgid "Left / Top" -msgstr "" +msgstr "Links/Bovenaan" #: prefs.js:392 msgid "Right / Bottom" -msgstr "" +msgstr "Rechts/Onderaan" #: prefs.js:410 msgid "Adjusts secondary monitors workspace thumbnails vertical position." -msgstr "" +msgstr "Pas de verticale miniatuurpositie op andere beeldschermen aan." #: prefs.js:428 msgid "" "Adjusts maximum size of the workspace thumbnails (% relative to display " "width) for secondary monitors." msgstr "" +"Pas de maximale omvang van werkbladminiaturen op andere beeldschermen aan (% " +"relatief in verhouding tot de schermbreedte)." #: prefs.js:445 msgid "Workspace Preview Scale" -msgstr "" +msgstr "Werkbladvoorvertoningen verkleinen" #: prefs.js:446 msgid "Scales down workspace previews on secondary monitors." -msgstr "" +msgstr "Verklein de voorvertoningen op andere beeldschermen." #: prefs.js:454 msgid "Shift Workspace Preview by Panel Height" -msgstr "" +msgstr "Werkbladvoorvertoningen aanpassen aan bovenbalkhoogte" #: prefs.js:455 msgid "" "This option can help align overview of the secondary monitor with the " "primary monitor." msgstr "" +"Met deze optie kunt u het overzicht op een ander beeldscherm gelijkstellen " +"aan die op het hoofdscherm." #: prefs.js:464 msgid "Workspace Switcher Popup" -msgstr "" +msgstr "Werkbladwisselaarpop-up" #: prefs.js:480 msgid "Horizontal Position (% from left)" -msgstr "" +msgstr "Horizontale positie (in % vanaf linkerzijde)" #: prefs.js:481 msgid "" @@ -359,74 +394,80 @@ msgid "" "you want more control over the popup, try Workspace Switcher Manager " "extension." msgstr "" +"Deze pop-up wordt getoond als u van werkblad wisselt met behulp van een " +"sneltoets of gebaar buiten het overzicht om. U kunt de pop-up uitschakelen " +"op het tabblad ‘Gedrag’. Als u de pop-up naar eigen hand wilt zetten, " +"probeer dan de Workspace Switcher Manager-uitbreiding." #: prefs.js:499 msgid "Vertical Position (% from top)" -msgstr "" +msgstr "Verticale positie (in % vanaf bovenzijde)" #: prefs.js:523 msgid "Dash Max Icon Size" -msgstr "" +msgstr "Max. pictogramgrootte in dash" #: prefs.js:524 msgid "Maximum size of Dash icons in pixels. Works only with default Dash." msgstr "" +"De maximale pictogramgrootte, in pixels. Let op: dit werkt alleen in " +"combinatie met de standaarddash." #: prefs.js:529 prefs.js:732 prefs.js:752 prefs.js:853 msgid "128" -msgstr "" +msgstr "128" #: prefs.js:530 prefs.js:733 prefs.js:753 prefs.js:854 msgid "112" -msgstr "" +msgstr "112" #: prefs.js:531 prefs.js:734 prefs.js:754 prefs.js:855 msgid "96" -msgstr "" +msgstr "96" #: prefs.js:532 prefs.js:735 prefs.js:755 prefs.js:856 msgid "80" -msgstr "" +msgstr "80" #: prefs.js:533 prefs.js:676 prefs.js:736 prefs.js:756 prefs.js:857 msgid "64" -msgstr "" +msgstr "64" #: prefs.js:534 prefs.js:677 prefs.js:737 prefs.js:757 prefs.js:858 msgid "48" -msgstr "" +msgstr "48" #: prefs.js:535 prefs.js:678 prefs.js:758 prefs.js:859 msgid "32" -msgstr "" +msgstr "32" #: prefs.js:536 msgid "24" -msgstr "" +msgstr "24" #: prefs.js:537 msgid "16" -msgstr "" +msgstr "16" #: prefs.js:551 msgid "Show Apps Icon Position" -msgstr "" +msgstr "Locatie van ‘Toepassingen tonen’" #: prefs.js:552 msgid "Sets the position of the \"Show Applications\" icon in the Dash." -msgstr "" +msgstr "Stel de locatie van het pictogram ‘Toepassingen tonen’ in de dash in." #: prefs.js:558 prefs.js:573 prefs.js:588 msgid "Start" -msgstr "" +msgstr "Begin" #: prefs.js:559 prefs.js:574 prefs.js:589 msgid "End" -msgstr "" +msgstr "Einde" #: prefs.js:566 msgid "Open Windows Icon Position" -msgstr "" +msgstr "Locatie van ‘Geopende vensters’" #: prefs.js:567 msgid "" @@ -436,10 +477,15 @@ msgid "" "secondary mouse button click on the Show Apps Icon, or the Space hotkey to " "access this feature." msgstr "" +"Deze optie voegt een pictogram ‘Geopende vensters doorzoeken’ toe aan de " +"dash (als de vensterzoekdienst is ingeschakeld op het tabblad ‘Gedrag’), " +"zodat u direct kunt zoeken. Als u dit pictogram uitschakelt, dan kunt u deze " +"functie alsnog aanroepen met behulp van een rechtermuisklik op ‘Toepassingen " +"tonen’ of door te drukken op de spatiebalk." #: prefs.js:581 msgid "Recent Files Icon Position" -msgstr "" +msgstr "Locatie van ‘Onlangs gebruikte bestanden’" #: prefs.js:582 msgid "" @@ -448,164 +494,178 @@ msgid "" "recent files search provider results. Even if you disable this icon, you can " "use Ctrl + Space hotkey to access this feature." msgstr "" +"Deze optie voegt een pictogram ‘Onlangs gebruikte bestanden doorzoeken’ toe " +"aan de dash (als de bestandszoekdienst is ingeschakeld op het tabblad " +"‘Gedrag’), zodat u direct kunt zoeken. Als u dit pictogram uitschakelt, dan " +"kunt u deze functie alsnog aanroepen met behulp van de sneltoets Ctrl + " +"spatiebalk." #: prefs.js:597 msgid "Dash Background Opacity" -msgstr "" +msgstr "Doorzichtigheid van dashachtergrond" #: prefs.js:598 msgid "Adjusts the opacity of the dash background." -msgstr "" +msgstr "Pas de doorzichtigheid van de dashachtergrond aan." #: prefs.js:614 msgid "Dash Background Radius" -msgstr "" +msgstr "Dash-achtergrondstraal" #: prefs.js:615 msgid "" "Adjusts the border radius of the dash background in pixels. 0 means default " "value." msgstr "" +"Pas de straal van de dashachtergrond aan (in pixels). 0 = standaardwaarde." #: prefs.js:623 prefs.js:1050 msgid "Workspace Thumbnails" -msgstr "" +msgstr "Werkbladminiaturen" #: prefs.js:629 msgid "Show Workspace Thumbnail Labels" -msgstr "" +msgstr "Werkbladminiatuurlabels tonen" #: prefs.js:630 msgid "" "Each workspace thumbnail can show its index and name (if defined in the " "system settings) or name/title of its most recently used app/window." msgstr "" +"Toon de namen van werkbladen (indien ingesteld in de systeemvoorkeuren) of " +"naam+titel van onlangs gebruikte toepassing/venster." #: prefs.js:635 prefs.js:680 prefs.js:1001 prefs.js:1062 prefs.js:1260 #: prefs.js:1278 prefs.js:1296 prefs.js:1331 msgid "Disable" -msgstr "" +msgstr "Uitschakelen" #: prefs.js:636 msgid "Index" -msgstr "" +msgstr "Index" #: prefs.js:637 msgid "Index + WS Name" -msgstr "" +msgstr "Index + Werkbladnaam" #: prefs.js:638 msgid "Index + App Name" -msgstr "" +msgstr "Index + Toepassingsnaam" #: prefs.js:639 msgid "Index + Window Title" -msgstr "" +msgstr "Index + Venstertitel" #: prefs.js:646 msgid "Show WS Thumbnail Label on Hover" -msgstr "" +msgstr "Miniatuurlabels tonen na aanwijzen" #: prefs.js:647 msgid "Show label only when the mouse pointer hovers over a thumbnail" -msgstr "" +msgstr "Toon de labels alleen als u met de aanwijzer een miniatuur aanwijst." #: prefs.js:655 msgid "Show Wallpaper in Workspace Thumbnails" -msgstr "" +msgstr "Werkbladminiaturen voorzien van achtergrond" #: prefs.js:656 msgid "All workspace thumbnails will include the current desktop background." -msgstr "" +msgstr "Toon de huidige bureaubladachtergrond op werkbladminiaturen." #: prefs.js:664 msgid "Window Preview" -msgstr "" +msgstr "Venstervoorvertoning" #: prefs.js:670 msgid "Window Preview App Icon Size" -msgstr "" +msgstr "Pictogramgrootte in venstervoorvertoning" #: prefs.js:671 msgid "Default size is 64." -msgstr "" +msgstr "Standaard: 64." #: prefs.js:679 msgid "22" -msgstr "" +msgstr "22" #: prefs.js:687 msgid "Always Show Window Titles" -msgstr "" +msgstr "Venstertitels altijd tonen" #: prefs.js:688 msgid "" "All windows on the workspace preview will show their titles, not only the " "one with the mouse pointer." msgstr "" +"Toon venstertitels van alle vensters op de voorvertoning, niet alleen van " +"het venster onder de aanwijzer." #: prefs.js:703 msgid "Show Workspace Preview Background" -msgstr "" +msgstr "Werkbladvoorvertoning voorzien van achtergrond" #: prefs.js:704 msgid "Allows you to hide the scaling background of the workspace preview." -msgstr "" +msgstr "Toon of verberg de achtergrond op de werkbladvoorvertoning." #: prefs.js:718 msgid "Icon Size" -msgstr "" +msgstr "Pictogramgrootte" #: prefs.js:719 msgid "" "Allows to force fixed icon size and bypass the default adaptive algorithm." msgstr "" +"Stel een vaste pictogramgrootte in ter vervanging van de standaard " +"aanpasbare grootte." #: prefs.js:724 prefs.js:751 msgid "Adaptive (Default)" -msgstr "" +msgstr "Aanpasbaar (standaard)" #: prefs.js:725 msgid "256" -msgstr "" +msgstr "256" #: prefs.js:726 msgid "224" -msgstr "" +msgstr "224" #: prefs.js:727 msgid "208" -msgstr "" +msgstr "208" #: prefs.js:728 msgid "192" -msgstr "" +msgstr "192" #: prefs.js:729 msgid "176" -msgstr "" +msgstr "176" #: prefs.js:730 msgid "160" -msgstr "" +msgstr "160" #: prefs.js:731 msgid "144" -msgstr "" +msgstr "144" #: prefs.js:745 msgid "Folder Icon Size" -msgstr "" +msgstr "Pictogramgrootte van mappen" #: prefs.js:746 msgid "" "Allows to disable the default adaptive algorithm and set a fixed size of " "icons inside folders." msgstr "" +"Stel een vaste pictogramgrootte in ter vervanging van de standaard " +"aanpasbare grootte." #: prefs.js:766 msgid "Max App Folder Icon Grid Size" -msgstr "" +msgstr "Max. grootte van mappen op rooster" #: prefs.js:767 msgid "" @@ -613,128 +673,145 @@ msgid "" "content, this option allows you to increase the number to 9 icons if folder " "contains more than 4 or 8 apps. The latter avoids half empty folder icons." msgstr "" +"Elk mappictogram toont maximaal 4 pictogrammen als voorvertoning. Met behulp " +"van deze optie kunt u het aantal verhogen naar maximaal 9 als de map 4 of 8 " +"toepassingen bevat. De laatste optie voorkomt halflege mappictogrammen." #: prefs.js:772 msgid "2x2 (Default)" -msgstr "" +msgstr "2x2 (standaard)" #: prefs.js:773 msgid "3x3 for 5+ apps" -msgstr "" +msgstr "3x3 (5 of meer toepassingen)" #: prefs.js:774 msgid "3x3 for 9+ apps" -msgstr "" +msgstr "3x3 (9 of meer toepassingen)" #: prefs.js:788 msgid "Columns per Page (0 for adaptive grid)" -msgstr "" +msgstr "Aantal kolommen per pagina (0 = aanpasbaar rooster)" #: prefs.js:789 msgid "" "Number of columns in application grid. If set to 0 (default setting) the " "number will be set automatically to fit available height." msgstr "" +"Het aantal kolommen van het toepassingsrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare hoogte." #: prefs.js:803 msgid "Rows per Page (0 for adaptive grid)" -msgstr "" +msgstr "Aantal rijen per pagina (0 = aanpasbaar rooster)" #: prefs.js:804 msgid "" "Number of rows in application grid. If set to 0 (default setting) the number " "will be set automatically to fit available height." msgstr "" +"Het aantal rijen van het toepassingsrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare hoogte." #: prefs.js:818 msgid "Folder Columns per Page (0 for adaptive grid)" -msgstr "" +msgstr "Aantal mapkolommen per pagina (0 = aanpasbaar rooster)" #: prefs.js:819 msgid "" "Number of columns in folder grid. If you leave the value on 0, the number of " "columns will be calculated to fit all folder icons." msgstr "" +"Het aantal kolommen van het mappenrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare pictogrammen." #: prefs.js:833 msgid "Folder Rows per Page (0 for adaptive grid)" -msgstr "" +msgstr "Aantal maprijen per pagina (0 = aanpasbaar rooster)" #: prefs.js:834 msgid "" "Number of rows in folder grid. If you leave the value on 0, the number of " "rows will be calculated to fit all folder icons." msgstr "" +"Het aantal rijen van het mappenrooster. Stel in op 0 (standaard) om " +"automatisch aan te passen aan de beschikbare pictogrammen." #: prefs.js:841 prefs.js:1174 prefs.js:1492 msgid "Search" -msgstr "" +msgstr "Zoeken" #: prefs.js:847 msgid "App Search Icon Size" -msgstr "" +msgstr "Omvang van zoekresultaten" #: prefs.js:848 msgid "Size of results provided by the App Search Provider." -msgstr "" +msgstr "De omvang van toepassingszoekresultaten." #: prefs.js:878 msgid "Max Search Results Rows" -msgstr "" +msgstr "Aantal zoekresultaatrijen" #: prefs.js:879 msgid "" "Sets the maximum number of rows for result lists of all search providers " "except window search provider which always lists all results." msgstr "" +"Stel het maximumaantal rijen van resultaatlijsten van alle zoekdiensten (met " +"uitzondering van vensters) in." #: prefs.js:888 msgid "Overview Background" -msgstr "" +msgstr "Overzichtsachtergrond" #: prefs.js:894 msgid "Show Static Background" -msgstr "" +msgstr "Eigen achtergrond tonen" #: prefs.js:895 msgid "Show static background wallpaper instead of the solid grey color." -msgstr "" +msgstr "Stel een eigen achtergrond in in plaats van de grijze kleur." #: prefs.js:911 msgid "Blur Window Picker Background" -msgstr "" +msgstr "Vensterkiezerachtergrond vervagen" #: prefs.js:912 msgid "Blur background wallpaper (if enabled) in the window picker view." -msgstr "" +msgstr "Vervaag de achtergrond (indien ingeschakeld) van de vensterkiezer." #: prefs.js:928 msgid "Blur App Grid/Search View Background" -msgstr "" +msgstr "Toepassingsrooster-/Zoekweergave-achtergrond vervagen" #: prefs.js:929 msgid "" "Blur background wallpaper (if enabled) in the app grid and search results " "views." msgstr "" +"Vervaag de achtergrond (indien ingeschakeld) van het toepassingsrooster en " +"de zoekweergave." #: prefs.js:937 msgid "Smooth Blur Transitions" -msgstr "" +msgstr "Vloeiende vervaagovergangen" #: prefs.js:938 msgid "" "Makes blur transitions smoother but can impact overall smoothness of " "overview animations." msgstr "" +"Maak vervaagovergangen vloeiender. Let op: dit kan van invloed zijn op de " +"overige animaties." #: prefs.js:953 prefs.js:980 prefs.js:1002 msgid "Overview" -msgstr "" +msgstr "Overzicht" #: prefs.js:959 msgid "Overview Mode" -msgstr "" +msgstr "Overzichtsmodus" #: prefs.js:960 msgid "" @@ -745,71 +822,84 @@ msgid "" "workspace thumbnail scales the ws preview and exposes its windows like in " "the default overview mode." msgstr "" +"De modus ‘Vensters tonen na aanwijzen’ toont geen werkbladminiaturen totdat " +"de aanwijzer een venster aanwijst.\n" +"De statische modus toont geen verkleinde werkbladminiaturen, maar alleen de " +"dash en miniaturen boven het bureaublad. Klik op een werkbladminiatuur om de " +"voorvertoning te verkleinen en alle vensters te tonen, net als in de " +"standaard overzichtsmodus." #: prefs.js:965 prefs.js:1259 prefs.js:1277 prefs.js:1317 msgid "Default" -msgstr "" +msgstr "Standaard" #: prefs.js:966 msgid "Expose Windows on Hover" -msgstr "" +msgstr "Vensters tonen na aanwijzen" #: prefs.js:967 msgid "Static Workspace" -msgstr "" +msgstr "Statische werkbladen" #: prefs.js:974 msgid "Startup State" -msgstr "" +msgstr "Opstartmodus" #: prefs.js:975 msgid "Allows to change the state in which GNOME Shell starts a session." -msgstr "" +msgstr "Geef aan in welke modus GNOME Shell een sessie dient te starten." #: prefs.js:981 msgid "Desktop" -msgstr "" +msgstr "Bureaublad" #: prefs.js:982 prefs.js:1003 msgid "Applications" -msgstr "" +msgstr "Toepassingen" #: prefs.js:989 msgid "" "Hot Corner (Install Custom Hot Corners - Extended extension for more options)" msgstr "" +"Snelhoek (installeer voor meer mogelijkheden de uitbreiding ‘Custom Hot " +"Corners - Extended)" #: prefs.js:995 msgid "Hot corner Action" -msgstr "" +msgstr "Snelhoekactie" #: prefs.js:996 msgid "" "Disable or change behavior of hot corner. Holding down the Ctrl key while " "hitting the hot corner switches between Overview/Applications actions." msgstr "" +"Pas het gedrag van de snelhoek aan of schakel deze uit. Houd Ctrl ingedrukt " +"tijdens het aanwijzen van de snelhoek om te schakelen tussen het overzicht " +"en de toepassingen." #: prefs.js:1010 msgid "Enable Hot Corner in Full-Screen mode" -msgstr "" +msgstr "Snelhoek tonen in schermvullende modus" #: prefs.js:1011 msgid "" "If you often work with full-screen applications and want the hot corner to " "be usable." msgstr "" +"Als u regelmatig met schermvullende toepassingen werkt en de snelhoek wilt " +"blijven gebruiken." #: prefs.js:1020 msgid "Show Ripples Animation" -msgstr "" +msgstr "Golfanimatie tonen" #: prefs.js:1021 msgid "Ripples animation shows up when you trigger hot corner." -msgstr "" +msgstr "Toon een golfanimatie zodra u de snelhoek activeert." #: prefs.js:1036 msgid "Dash Icon Click" -msgstr "" +msgstr "Klikken op dashpictogram" #: prefs.js:1037 msgid "" @@ -817,18 +907,21 @@ msgid "" "window is not on the current workspace, the overview can switch to the " "workspace with the recent window." msgstr "" +"Als de aangeklikte toepassing meer dan dan één venster bevat en het onlangs " +"gebruikte venster niet op het huidige werkblad staat, dan kan worden " +"overgeschakeld naar het juiste werkblad." #: prefs.js:1042 msgid "Activate Last Used Window Immediately" -msgstr "" +msgstr "Laatstgebruikte venster focussen" #: prefs.js:1043 msgid "Switch to Workspace with Recently Used Window" -msgstr "" +msgstr "Overschakelen naar werkblad met laatstgebruikte venster" #: prefs.js:1056 msgid "Close Workspace Button" -msgstr "" +msgstr "Werkbladsluitknop" #: prefs.js:1057 msgid "" @@ -836,44 +929,49 @@ msgid "" "over it and allows you to close all windows on the workspace. You can choose " "a safety lock to prevent accidental use." msgstr "" +"De werkbladsluitknop wordt getoond zodra u een miniatuur aanwijst. Hiermee " +"kunt u alle vensters op het desbetreffende werkblad sluiten. U kunt " +"instellen dat u sluiten wilt voorkomen." #: prefs.js:1063 msgid "Single Click" -msgstr "" +msgstr "Eenmaal klikken" #: prefs.js:1064 msgid "Double Click" -msgstr "" +msgstr "Dubbelklikken" #: prefs.js:1065 msgid "Ctrl Key + Click" -msgstr "" +msgstr "Ctlr+klik" #: prefs.js:1078 msgid "Apps Order" -msgstr "" +msgstr "Toepassingsvolgorde" #: prefs.js:1079 msgid "" "Choose sorting method for the app grid. Note that sorting by alphabet and " "usage ignores folders." msgstr "" +"Kies de sorteermethode van het toepassingsrooster. Let op: sorteren op " +"alfabet of gebruik negeert mappen." #: prefs.js:1084 msgid "Custom (Default)" -msgstr "" +msgstr "Aangepast (standaard)" #: prefs.js:1085 msgid "Alphabet" -msgstr "" +msgstr "Alfabet" #: prefs.js:1086 msgid "Usage" -msgstr "" +msgstr "Gebruik" #: prefs.js:1093 msgid "App Grid Content" -msgstr "" +msgstr "Inhoud van toepassingsrooster" #: prefs.js:1094 msgid "" @@ -881,30 +979,34 @@ msgid "" "in the grid or remove also running applications. Option \"Favorites and " "Running First\" only works with the Alphabet and Usage sorting." msgstr "" +"Standaard worden favoriete toepassingen verborgen. Met behulp van deze optie " +"kunt u ze klonen op het rooster of ook actieve toepassingen verbergen. Let " +"op: de optie ‘Favorieten en actieve bovenaan’ werkt alleen in combinatie met " +"sorteren op alfabet of gebruik." #: prefs.js:1099 msgid "Include All" -msgstr "" +msgstr "Alles" #: prefs.js:1100 msgid "Include All - Favorites and Running First" -msgstr "" +msgstr "Alles - Favorieten en actieve bovenaan" #: prefs.js:1101 msgid "Exclude Favorites (Default)" -msgstr "" +msgstr "Favorieten verbergen (standaard)" #: prefs.js:1102 msgid "Exclude Running" -msgstr "" +msgstr "Actieve verbergen" #: prefs.js:1103 msgid "Exclude Favorites and Running" -msgstr "" +msgstr "Favorieten en actieve verbergen" #: prefs.js:1110 msgid "Active Icons in Folder Preview" -msgstr "" +msgstr "Actieve pictogrammen op mapvoorvertoning" #: prefs.js:1111 msgid "" @@ -913,20 +1015,27 @@ msgid "" "the app without closing the overview, so you can open multiple apps in a row " "on the current workspace and secondary button opens the folder." msgstr "" +"Schakel in om op een toepassingspictogram te kunnen klikken vanuit een " +"mapvoorvertoning. Hierdoor hoeft niet eerst de map te worden geopend. Met " +"een middelklik opent u een nieuw venster zonder het overzicht te sluiten, " +"zodat u meerdere toepassingen per rij kunt openen. Met een rechtermuisklik " +"wordt de map geopend." #: prefs.js:1120 msgid "Center Open Folders" -msgstr "" +msgstr "Geopende mappen centreren" #: prefs.js:1121 msgid "" "App folder may open in the center of the screen or above the source folder " "icon." msgstr "" +"Toon mappen op het midden van het scherm in plaats van boven het " +"mappictogram." #: prefs.js:1130 msgid "Allow Incomplete Pages" -msgstr "" +msgstr "Onvolledige pagina's tonen" #: prefs.js:1131 msgid "" @@ -934,48 +1043,51 @@ msgid "" "fill any empty slot left after an icon was (re)moved (to a folder for " "example)." msgstr "" +"Schakel uit om pictogrammen van de volgende pagina (indien beschikbaar) " +"automatisch te verplaatsen om gaten op te vullen als er pictogrammen " +"verplaatst of verwijderd zijn." #: prefs.js:1140 msgid "App Labels Behavior" -msgstr "" +msgstr "Toepassingslabels" #: prefs.js:1141 msgid "Choose how and when to display app names." -msgstr "" +msgstr "Geef aan hoe en wanneer er toepassingsnamen dienen te worden getoond." #: prefs.js:1146 msgid "Ellipsized - Expand Selected (Default)" -msgstr "" +msgstr "Ingekort - Selectie volledig tonen (standaard)" #: prefs.js:1147 msgid "Always Expanded" -msgstr "" +msgstr "Altijd volledig tonen" #: prefs.js:1148 msgid "Hidden - Show Selected Only" -msgstr "" +msgstr "Verborgen - Alleen selectie tonen" #: prefs.js:1154 msgid "Reset App Grid Layout" -msgstr "" +msgstr "Standaardindeling herstellen" #: prefs.js:1155 msgid "" "Removes all stored app grid icons positions, after the reset icons will be " "ordered alphabetically." -msgstr "" +msgstr "Wis alle pictogramlocaties en sorteer alle pictogrammen op alfabet." #: prefs.js:1163 msgid "Remove App Grid Folders" -msgstr "" +msgstr "Mappen verwijderen" #: prefs.js:1164 msgid "Removes all folders, folder apps move to root grid." -msgstr "" +msgstr "Verwijder alle mappen en verplaats toepassingen naar het hoofdrooster." #: prefs.js:1186 msgid "Enable Window Search Provider" -msgstr "" +msgstr "Vensters doorzoeken" #: prefs.js:1187 msgid "" @@ -983,10 +1095,13 @@ msgid "" "results. You can search app names and window titles. You can also use \"wq/" "\" prefix to suppress results from other search providers." msgstr "" +"Schakel de dienst om geopende vensters te zoeken in. U kunt zoeken op " +"toepassingsnamen en venstertitels. Ook kunt u ‘wq/’ voorafgaand aan een " +"zoekopdracht toevoegen om resultaten van andere diensten te verbergen." #: prefs.js:1195 msgid "Enable Recent Files Search Provider" -msgstr "" +msgstr "Onlangs gebruikte bestanden doorzoeken" #: prefs.js:1196 msgid "" @@ -995,10 +1110,15 @@ msgid "" "field. This option needs File History option enabled in the GNOME Privacy " "settings." msgstr "" +"Schakel de dienst in om onlangs gebruikte bestanden te doorzoeken. Dit kan " +"met behulp van een dashpictogram, Ctrl+spatiebalk of door ‘fq//’ voorafgaand " +"aan een zoekopdracht in te vullen. Let op: voor deze optie is " +"bestandsgeschiedenis vereist. Die optie vindt u in de GNOME-" +"privacyvoorkeuren." #: prefs.js:1204 msgid "Enable Fuzzy Match" -msgstr "" +msgstr "Onduidelijke overeenkomsten inschakelen" #: prefs.js:1205 msgid "" @@ -1006,28 +1126,34 @@ msgid "" "searching for and find \"Firefox\" even if you type \"ffx\". Works only for " "the App, Window and Recent files search providers." msgstr "" +"Schakel onduidelijke overeenkomsten in om letters over te slaan. Voorbeeld: " +"‘Firefox’ wordt gevonden als u ‘ffx’ typt. Let op: dit werkt alleen in " +"combinatie met de diensten toepassingen, vensters en onlangs gebruikte " +"bestanden." #: prefs.js:1223 msgid "Animations - General" -msgstr "" +msgstr "Animaties - Algemeen" #: prefs.js:1238 msgid "Animation Speed" -msgstr "" +msgstr "Animatiesnelheid" #: prefs.js:1239 msgid "" "Adjusts the global animation speed in % of the default duration - higher " "value means slower animation." msgstr "" +"Pas de algemene animatiesnelheid aan (in %). Hogere waarde = langzamere " +"animatie." #: prefs.js:1247 msgid "Animations - Overview" -msgstr "" +msgstr "Animaties - Overzicht" #: prefs.js:1253 msgid "App Grid Animation" -msgstr "" +msgstr "Animatie van toepassingsrooster" #: prefs.js:1254 msgid "" @@ -1035,26 +1161,29 @@ msgid "" "screen. You can choose direction, keep it Default (direction will be chosen " "automatically) or disable the animation if you don't like it." msgstr "" +"Toon een animatie vanaf de zijkant na het openen van het toepassingsrooster. " +"U kunt aangeven vanuit welke richting de animatie dient te komen (standaard: " +"automatisch) of de animatie in zijn geheel uitschakelen." #: prefs.js:1261 prefs.js:1279 msgid "Right to Left" -msgstr "" +msgstr "Van rechts naar links" #: prefs.js:1262 prefs.js:1280 msgid "Left to Right" -msgstr "" +msgstr "Van links naar rechts" #: prefs.js:1263 prefs.js:1281 msgid "Bottom to Top" -msgstr "" +msgstr "Van boven naar onder" #: prefs.js:1264 prefs.js:1282 msgid "Top to Bottom" -msgstr "" +msgstr "Van onder naar boven" #: prefs.js:1271 msgid "Search View Animation" -msgstr "" +msgstr "Animatie van zoekweergave" #: prefs.js:1272 msgid "" @@ -1062,28 +1191,33 @@ msgid "" "from the edge of the screen. You can choose direction, keep it Default " "(currently Bottom to Top) or disable the animation if you don't like it." msgstr "" +"Toon een animatie vanaf de zijkant na het openen van de zoekweergave. U kunt " +"aangeven vanuit welke richting de animatie dient te komen (standaard: van " +"onder naar boven) of de animatie in zijn geheel uitschakelen." #: prefs.js:1290 msgid "Workspace Preview Animation" -msgstr "" +msgstr "Animatie van werkbladminiaturen" #: prefs.js:1291 msgid "" "When entering / leaving the App Grid / Search view, the workspace preview " "can animate to/from workspace thumbnail." msgstr "" +"Toon een animatie na het openen en sluiten van het toepassingsrooster en de " +"zoekweergave." #: prefs.js:1297 msgid "Enable" -msgstr "" +msgstr "Inschakelen" #: prefs.js:1305 msgid "Workspace Switcher" -msgstr "" +msgstr "Werkbladwisselaar" #: prefs.js:1311 msgid "Workspace Switcher Animation" -msgstr "" +msgstr "Animatie van werkbladwisselaar" #: prefs.js:1312 msgid "" @@ -1091,14 +1225,17 @@ msgid "" "switcher animation outside of the overview. The Static Background mode also " "keeps Conky and desktop icons on their place during switching." msgstr "" +"Hiermee kunt u de animatie van de bureaubladachtergrond op de " +"werkbladwisselaar buiten het overzicht uitschakelen. De statische modus laat " +"Conky en pictogrammen op hun plaats staan tijdens het wisselen." #: prefs.js:1318 msgid "Static Background" -msgstr "" +msgstr "Statische achtergrond" #: prefs.js:1325 msgid "Workspace Switcher Popup Mode" -msgstr "" +msgstr "Werkbladwisselaarpop-upmodus" #: prefs.js:1326 msgid "" @@ -1107,54 +1244,58 @@ msgid "" "show it on the current monitor (the one with mouse pointer) instead of the " "primary." msgstr "" +"Deze pop-up wordt getoond als u van werkblad wisselt met behulp van een " +"sneltoets of gebaar buiten het overzicht. U kunt de pop-up in zijn geheel " +"uitschakelen of alleen op het huidige beeldscherm (het scherm met de " +"aanwijzer) tonen." #: prefs.js:1332 msgid "Show on Primary Monitor (Default)" -msgstr "" +msgstr "Tonen op hoofdscherm (standaard)" #: prefs.js:1333 msgid "Show on Current Monitor" -msgstr "" +msgstr "Tonen op huidig beeldscherm" #: prefs.js:1341 msgid "Notifications" -msgstr "" +msgstr "Meldingen" #: prefs.js:1347 msgid "Notification Banner Position" -msgstr "" +msgstr "Meldingslocatie" #: prefs.js:1348 msgid "Choose where the notification pop-ups appear on the screen." -msgstr "" +msgstr "Geef aan op welke locatie meldingen dienen te worden getoond." #: prefs.js:1353 msgid "Top Left" -msgstr "" +msgstr "Linksboven" #: prefs.js:1354 msgid "Top Middle" -msgstr "" +msgstr "Linksmidden" #: prefs.js:1355 msgid "Top Right (Default)" -msgstr "" +msgstr "Rechtsboven (standaard)" #: prefs.js:1356 msgid "Bottom Left" -msgstr "" +msgstr "Linksonder" #: prefs.js:1357 msgid "Bottom Middle" -msgstr "" +msgstr "Middenonder" #: prefs.js:1358 msgid "Bottom Right" -msgstr "" +msgstr "Rechtsonder" #: prefs.js:1365 msgid "Window Attention Handler" -msgstr "" +msgstr "Vensterfocusafhandeling" #: prefs.js:1366 msgid "" @@ -1163,38 +1304,41 @@ msgid "" "(notification will be pushed into the message tray silently) or focus the " "source window immediately instead." msgstr "" +"Als een venster om focus vraagt (doorgaans een nieuw venster), dan toont " +"GNOME Shell een melding. U kunt deze meldingen uitschakelen om ze direct " +"naar het berichtenvak te sturen of het venster direct focussen." #: prefs.js:1371 prefs.js:1386 msgid "Show Notifications (Default)" -msgstr "" +msgstr "Meldingen tonen (standaard)" #: prefs.js:1372 msgid "Disable Notification Popups" -msgstr "" +msgstr "Meldingen uitschakelen" #: prefs.js:1373 msgid "Immediately Focus Window" -msgstr "" +msgstr "Venster direct focussen" #: prefs.js:1380 msgid "Favorites" -msgstr "" +msgstr "Favorieten" #: prefs.js:1381 msgid "Disable pin/unpin app notifications." -msgstr "" +msgstr "Schakel meldingen omtrent favorieten uit." #: prefs.js:1387 msgid "Disable Notifications" -msgstr "" +msgstr "Meldingen uitschakelen" #: prefs.js:1402 msgid "Keyboard" -msgstr "" +msgstr "Toetsenbord" #: prefs.js:1408 msgid "Override Page Up/Down Shortcuts" -msgstr "" +msgstr "Page Up-/Down-sneltoetsgedrag aanpassen" #: prefs.js:1409 msgid "" @@ -1202,14 +1346,17 @@ msgid "" "keyboard shortcuts for the current workspace orientation. If you encounter " "any issues, check the configuration in the dconf editor." msgstr "" +"Met deze optie kunt u het gedrag van de sneltoetsen (Shift +) Super + Page " +"Up/Down binnen de huidige werkbladoriëntatie aanpassen. Let op: als u " +"problemen ervaart, bekijk dan de configuratie in dconf-bewerker." #: prefs.js:1417 msgid "Compatibility" -msgstr "" +msgstr "Compatibiliteit" #: prefs.js:1423 msgid "Fix for Dash to Dock" -msgstr "" +msgstr "Dash-to-Dock-oplossing" #: prefs.js:1424 msgid "" @@ -1218,82 +1365,90 @@ msgid "" "change monitors configuration. If you are experiencing such issues, try to " "enable this option, or (better) disable/replace the dock extension." msgstr "" +"Met het standaard Ubuntu-dock en andere Dash-to-Dock-varianten kunt u " +"problemen met het activiteitenoverzicht ervaren als u de docklocatie of " +"beeldschermvoorkeuren aanpast. In dat geval kunt u deze optie proberen of de " +"dockuitbreiding in kwestie uit te schakelen/te vervangen." #: prefs.js:1432 msgid "" "V-Shell Modules that can be disabled in case of conflict or misbehavior." msgstr "" +"V-Shellmodules die kunnen worden uitgeschakeld bij problemen of conflicten." #: prefs.js:1438 msgid "AppFavorites" -msgstr "" +msgstr "Favoriete toepassingen" #: prefs.js:1439 msgid "Pin/unpin app notification options." -msgstr "" +msgstr "Opties omtrent favorietenmeldingen." #: prefs.js:1447 msgid "AppDisplay / IconGrid" -msgstr "" +msgstr "Toepassingsweergave/-rooster" #: prefs.js:1448 msgid "App grid customization and options." -msgstr "" +msgstr "Opties omtrent het aanpassen van het toepassingsrooster." #: prefs.js:1457 msgid "Dash configuration options and support for vertical orientation." -msgstr "" +msgstr "Dashopties en ondersteuning voor verticale oriëntatie." #: prefs.js:1466 msgid "" "Hot corner options, removes right panel barrier that collides with CHC-E " "extension." msgstr "" +"Snelhoekopties. Verwijder de barrière van de rechterbalk die botst met de " +"CHC-E-uitbreiding." #: prefs.js:1474 msgid "MessageTray" -msgstr "" +msgstr "Berichtenvak" #: prefs.js:1475 msgid "Notification position options." -msgstr "" +msgstr "Opties omtrent meldingslocaties." #: prefs.js:1484 msgid "Panel options." -msgstr "" +msgstr "Bovenbalkopties." #: prefs.js:1493 msgid "Search view and app search provider customization and options." -msgstr "" +msgstr "Zoekweergave- en zoekdienstopties." #: prefs.js:1501 msgid "SwipeTracker" -msgstr "" +msgstr "Gebaar-volgsysteem" #: prefs.js:1502 msgid "Gestures for vertical workspace orientation." -msgstr "" +msgstr "Gebaren in de verticale werkbladoriëntatie." #: prefs.js:1510 msgid "WindowAttentionHandler" -msgstr "" +msgstr "Vensterfocusafhandeling" #: prefs.js:1511 msgid "Window attention handler options." -msgstr "" +msgstr "Vensterfocusafhandelingsopties." #: prefs.js:1519 msgid "WindowManager" -msgstr "" +msgstr "Vensterbeheer" #: prefs.js:1520 msgid "" "Fixes an upstream bug in the minimization animation of a full-screen window." msgstr "" +"Lost een bug omtrent de minimaliseeranimatie van schermvullende vensters op." #: prefs.js:1528 msgid "WindowPreview" -msgstr "" +msgstr "Venstervoorvertoning" #: prefs.js:1529 msgid "" @@ -1301,89 +1456,96 @@ msgid "" "errors when you close a window from an overview or exit the overview with a " "gesture when any window is selected." msgstr "" +"Venstervoorvertoningsopties. Lost een bug op omtrent het vastleggen van " +"foutmeldingen in het systeemlogboek na het sluiten van een venster vanuit " +"het overzicht of sluiten van het overzicht met een gebaar." #: prefs.js:1537 msgid "Workspace" -msgstr "" +msgstr "Werkblad" #: prefs.js:1538 msgid "" "Fixes workspace preview allocations for vertical workspaces orientation and " "window scaling in static overview modes." msgstr "" +"Lost een bug omtrent het toewijzen van werkbladvoorvertoningen in verticale " +"oriëntatie op, alsmede vensterschaling in de statische modus." #: prefs.js:1546 msgid "WorkspaceAnimation" -msgstr "" +msgstr "Werkbladanimatie" #: prefs.js:1547 msgid "Static workspace animation option." -msgstr "" +msgstr "Statische werkbladanimatie." #: prefs.js:1555 msgid "WorkspaceSwitcherPopup" -msgstr "" +msgstr "Werkbladwisselaarpop-up" #: prefs.js:1556 msgid "Workspace switcher popup position options." -msgstr "" +msgstr "Opties omtrent de werkbladwisselaarpop-up." #: prefs.js:1573 msgid "Version" -msgstr "" +msgstr "Versie" #: prefs.js:1579 msgid "Reset all options" -msgstr "" +msgstr "Standaardwaarden herstellen" #: prefs.js:1580 msgid "Set all options to default values." -msgstr "" +msgstr "Herstel alle standaardvoorkeuren." #: prefs.js:1586 msgid "Links" -msgstr "" +msgstr "Links" #: prefs.js:1590 msgid "Homepage" -msgstr "" +msgstr "Website" #: prefs.js:1591 msgid "Source code and more info about this extension" -msgstr "" +msgstr "Broncode en meer informatie over deze uitbreiding" #: prefs.js:1596 msgid "Changelog" -msgstr "" +msgstr "Wijzigingslog" #: prefs.js:1597 msgid "See what's changed." -msgstr "" +msgstr "Bekijk alle wijzigingen." #: prefs.js:1602 msgid "GNOME Extensions" -msgstr "" +msgstr "GNOME-uitbreidingen" #: prefs.js:1603 msgid "Rate and comment the extension on GNOME Extensions site." -msgstr "" +msgstr "Beoordeel deze uitbreiding op de GNOME-uitbreidingensite." #: prefs.js:1608 msgid "Report a bug or suggest new feature" -msgstr "" +msgstr "Bug melden of idee delen" #: prefs.js:1614 msgid "Buy Me a Coffee" -msgstr "" +msgstr "Trakteer me op koffie" #: prefs.js:1615 msgid "If you like this extension, you can help me with my coffee expenses." msgstr "" +"Als u deze uitbreiding graag gebruikt, dan kunt u me trakteren op een kopje " +"koffie." #: recentFilesSearchProvider.js:109 msgid "Search recent files" -msgstr "" +msgstr "Onlangs gebruikte bestanden doorzoeken" #: recentFilesSearchProvider.js:110 msgid "Recent Files" -msgstr "" +msgstr "Onlangs gebruikte bestanden" diff --git a/extensions/vertical-workspaces/po/vertical-workspaces.pot b/extensions/45/vertical-workspaces/po/vertical-workspaces.pot similarity index 100% rename from extensions/vertical-workspaces/po/vertical-workspaces.pot rename to extensions/45/vertical-workspaces/po/vertical-workspaces.pot diff --git a/extensions/45/vertical-workspaces/prefs.js b/extensions/45/vertical-workspaces/prefs.js new file mode 100644 index 0000000..1369c83 --- /dev/null +++ b/extensions/45/vertical-workspaces/prefs.js @@ -0,0 +1,2262 @@ +/** + * V-Shell (Vertical Workspaces) + * prefs.js + * + * @author GdH + * @copyright 2022 - 2023 + * @license GPL-3.0 + */ + +'use strict'; + +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import Gtk from 'gi://Gtk'; + +import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +import * as Settings from './lib/settings.js'; +import * as OptionsFactory from './lib/optionsFactory.js'; + +// gettext +let _; + +export default class VShell extends ExtensionPreferences { + _getPageList() { + const itemFactory = new OptionsFactory.ItemFactory(); + const pageList = [ + { + name: 'profiles', + title: _('Profiles'), + iconName: 'open-menu-symbolic', + optionList: this._getProfilesOptionList(itemFactory), + }, + { + name: 'layout', + title: _('Layout'), + iconName: 'view-grid-symbolic', + optionList: this._getLayoutOptionList(itemFactory), + }, + { + name: 'appearance', + title: _('Appearance'), + iconName: 'view-reveal-symbolic', + optionList: this._getAppearanceOptionList(itemFactory), + }, + { + name: 'behavior', + title: _('Behavior'), + iconName: 'system-run-symbolic', + optionList: this._getBehaviorOptionList(itemFactory), + }, + { + name: 'modules', + title: _('Modules'), + iconName: 'application-x-addon-symbolic', + optionList: this._getModulesOptionList(itemFactory), + }, + { + name: 'misc', + title: _('Misc'), + iconName: 'preferences-other-symbolic', + optionList: this._getMiscOptionList(itemFactory), + }, + { + name: 'about', + title: _('About'), + iconName: 'preferences-system-details-symbolic', + optionList: this._getAboutOptionList(itemFactory), + }, + ]; + + return pageList; + } + + fillPreferencesWindow(window) { + this.Me = {}; + this.Me.Settings = Settings; + + this.Me.gSettings = this.getSettings(); + this.Me.gettext = this.gettext.bind(this); + _ = this.Me.gettext; + this.Me.metadata = this.metadata; + + this.opt = new this.Me.Settings.Options(this.Me); + this.Me.opt = this.opt; + + OptionsFactory.init(this.Me); + + window = new OptionsFactory.AdwPrefs(this.opt).getFilledWindow(window, this._getPageList()); + window.connect('close-request', () => { + this.opt.destroy(); + this.opt = null; + this.Me = null; + _ = null; + }); + + window.set_default_size(840, 800); + } + + + // //////////////////////////////////////////////////////////////////// + _getProfilesOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + + optionList.push( + itemFactory.getRowWidget( + _('Custom Profiles'), + _('Sets of settings that can help you with the initial customization') + ) + ); + + optionList.push(itemFactory.getRowWidget( + _('Profile 1'), + null, + itemFactory.newPresetButton(this.opt, 1) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 2'), + null, + itemFactory.newPresetButton(this.opt, 2) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 3'), + null, + itemFactory.newPresetButton(this.opt, 3) + )); + + optionList.push(itemFactory.getRowWidget( + _('Profile 4'), + null, + itemFactory.newPresetButton(this.opt, 4) + )); + + return optionList; + } + + _getLayoutOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + + optionList.push( + itemFactory.getRowWidget( + _('Dash') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Dash Position'), + null, + // // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashPosition', + [ + [_('Top'), 0], + [_('Right'), 1], + [_('Bottom'), 2], + [_('Left'), 3], + [_('Hide'), 4], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Center Horizontal Dash to Workspace'), + _('If the Dash Position is set to Top or Bottom, the position will be recalculated relative to the workspace preview instead of the screen'), + itemFactory.newSwitch(), + 'centerDashToWs', + null, + 'dashModule' + ) + ); + + const dashPositionAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: -100, + step_increment: 1, + page_increment: 10, + }); + + const dashPositionScale = itemFactory.newScale(dashPositionAdjustment); + dashPositionScale.add_mark(0, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Fine Tune Dash Position'), + _('Adjusts the position of the dash on the axis given by the orientation of the workspaces'), + dashPositionScale, + 'dashPositionAdjust', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Apps Icon Position'), + _('Sets the position of the "Show Applications" icon in the Dash'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'showAppsIconPosition', + [ + [_('Hide'), 2], + [_('Start'), 0], + [_('End'), 1], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Open Windows Icon Position'), + _('Allows to add "Search Open Windows" icon into Dash (if window search provider enabled on the Modules tab) so you can directly toggle window search provider results. You can also use the secondary mouse button click on the Show Apps Icon, or the Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowWindowsIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Recent Files Icon Position'), + _('Allows to add "Search Recent Files" icon into Dash (if recent files search provider enabled on the Modules tab) so you can directly toggle recent files search provider results. You can also use Ctrl + Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowRecentFilesIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Extensions Icon Position'), + _('Allows to add "Search Extensions" icon into Dash (if extensions search provider enabled on the Module tab) so you can directly toggle extensions search provider results. You can also use the Ctrl + Shift + Space hotkey'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowExtensionsIcon', + [ + [_('Hide'), 0], + [_('Start'), 1], + [_('End'), 2], + ], + 'dashModule' + ) + ); + + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Thumbnails / Orientation') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Thumbnails Position / Workspaces Orientation'), + _('Position of the workspace thumbnails on the screen also sets orientation of the workspaces to vertical or horizontal. You have two options to disable workspace thumbnails, one sets workspaces to vertical orientation, the second one to horizontal.'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'workspaceThumbnailsPosition', + // this mess is just because of backward compatibility + [ + [_('Left \t Vertical Orientation'), 0], + [_('Right \t Vertical Orientation'), 1], + [_('Hide \t Vertical Orientation'), 4], + [_('Top \t Horizontal Orientation'), 5], + [_('Bottom \t Horizontal Orientation'), 6], + [_('Hide \t Horizontal Orientation'), 9], + ] + ) + ); + + const wstPositionAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: -100, + step_increment: 1, + page_increment: 10, + }); + + const wstPositionScale = itemFactory.newScale(wstPositionAdjustment); + wstPositionScale.add_mark(0, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Fine Tune Workspace Thumbnails Position'), + _('Adjusts the position of the thumbnails on the axis given by the orientation of the workspaces'), + wstPositionScale, + 'wsTmbPositionAdjust' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Reserve Full Screen Height/Width for Thumbnails'), + _('The whole screen height/width will be reserved for workspace thumbnails at the expense of space available for Dash (if the Dash is oriented in a different axis).'), + itemFactory.newSwitch(), + 'wsThumbnailsFull' + ) + ); + + const wsThumbnailScaleAdjustment = new Gtk.Adjustment({ + upper: 30, + lower: 0, + step_increment: 1, + page_increment: 1, + }); + + const wsThumbnailScale = itemFactory.newScale(wsThumbnailScaleAdjustment); + wsThumbnailScale.add_mark(13, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Workspace Thumbnails Max Scale - Window Picker'), + _('Adjusts maximum size of the workspace thumbnails in the overview (% relative to display width)'), + wsThumbnailScale, + 'wsThumbnailScale' + ) + ); + + const wsThumbnailAppScaleAdjustment = new Gtk.Adjustment({ + upper: 30, + lower: 0, + step_increment: 1, + page_increment: 1, + }); + + const wsThumbnailAppScale = itemFactory.newScale(wsThumbnailAppScaleAdjustment); + wsThumbnailAppScale.add_mark(13, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Workspace Thumbnails Max Scale - App View'), + _('Allows you to set different thumbnails scale for the Applications view'), + wsThumbnailAppScale, + 'wsThumbnailScaleAppGrid' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Preview') + ) + ); + + const wsScaleAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 30, + step_increment: 1, + page_increment: 10, + }); + + const wsScaleScale = itemFactory.newScale(wsScaleAdjustment); + wsScaleScale.add_mark(100, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Workspaces Scale'), + _('Allows to shrink workspace previews to adjust spacing or fit more of the adjacent workspaces on the screen. Default size is calculated to use all available space with minimal spacing'), + wsScaleScale, + 'wsPreviewScale' + ) + ); + + const wsSpacingAdjustment = new Gtk.Adjustment({ + upper: 500, + lower: 10, + step_increment: 1, + page_increment: 10, + }); + + const wsSpacingScale = itemFactory.newScale(wsSpacingAdjustment); + wsSpacingScale.add_mark(350, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Workspaces Spacing'), + _('Adjusts spacing between workspace previews so you can control how much of the adjacent workspaces overlap to the current workspace overview. Default value should set the adjacent workspaces off-screen.'), + wsSpacingScale, + 'wsMaxSpacing' + ) + ); + + + optionList.push( + itemFactory.getRowWidget( + _('App Grid') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Center App Grid'), + _('Centers the app grid relative to the display instead of available space'), + itemFactory.newSwitch(), + 'centerAppGrid' + ) + ); + + const agPageAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 50, + step_increment: 1, + page_increment: 10, + }); + + const agPageWidthScale = itemFactory.newScale(agPageAdjustment); + agPageWidthScale.add_mark(90, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('App Grid Page Width Scale'), + _('Adjusts max app grid page width relative to the available space.'), + agPageWidthScale, + 'appGridPageWidthScale', + null, + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Search View') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Center Search View'), + _('Centers the search view relative to the display instead of available space'), + itemFactory.newSwitch(), + 'centerSearch' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Always Show Search Entry'), + _('If disabled, the search entry field will be hidden when not in use, so the workspace preview and app grid may take up more space'), + itemFactory.newSwitch(), + 'showSearchEntry' + ) + ); + + const searchViewScaleAdjustment = new Gtk.Adjustment({ + upper: 150, + lower: 50, + step_increment: 1, + page_increment: 1, + }); + + const searchViewScale = itemFactory.newScale(searchViewScaleAdjustment); + searchViewScale.add_mark(100, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Search Results Width'), + _('Adjusts maximum width of search results view (% relative to default). This allows to fit more (or less) app icons into the app search result'), + searchViewScale, + 'searchViewScale', + null, + 'searchModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Panel') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Main Panel Position'), + _('Allows to place the main panel at the bottom of the primary display'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'panelPosition', + [ + [_('Top (Default)'), 0], + [_('Bottom'), 1], + ], + 'panelModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Main Panel Visibility'), + _('Allows to hide main panel when not needed'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'panelVisibility', + [ + [_('Always Visible (Default)'), 0], + [_('Overview Only'), 1], + [_('Always Hidden'), 2], + // [_('Desktop View Only'), 3], + ], + 'panelModule' + ) + ); + + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Switcher Popup') + ) + ); + + const hAdjustment = new Gtk.Adjustment({ + lower: 0, + upper: 100, + step_increment: 1, + page_increment: 1, + }); + + const hScale = itemFactory.newScale(hAdjustment); + hScale.add_mark(50, Gtk.PositionType.TOP, null); + + optionList.push( + itemFactory.getRowWidget( + _('Horizontal Position (% from left)'), + _('This popup shows up when you switch workspace using a keyboard shortcut or gesture outside of the overview. You can disable it on the "Behavior" tab. If you want more control over the popup, try the "Workspace Switcher Manager" extension'), + hScale, + 'wsSwPopupHPosition', + null, + 'workspaceSwitcherPopupModule' + ) + ); + + const vAdjustment = new Gtk.Adjustment({ + lower: 0, + upper: 100, + step_increment: 1, + page_increment: 1, + }); + + const vScale = itemFactory.newScale(vAdjustment); + vScale.add_mark(50, Gtk.PositionType.TOP, null); + + optionList.push( + itemFactory.getRowWidget( + _('Vertical Position (% from top)'), + null, + vScale, + 'wsSwPopupVPosition', + null, + 'workspaceSwitcherPopupModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Notifications and OSD') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Notification Banner Position'), + _('Choose where the notification banners appear on the screen'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'notificationPosition', + [ + [_('Top Left'), 0], + [_('Top Center (Default)'), 1], + [_('Top Right'), 2], + [_('Bottom Left'), 3], + [_('Bottom Center'), 4], + [_('Bottom Right'), 5], + ], + 'messageTrayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('OSD Popup Position'), + _('Choose where the OSD pop-ups (like sound volume level) appear on the screen'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'osdPosition', + [ + [_('Disable'), 0], + [_('Top Left'), 1], + [_('Top Center'), 2], + [_('Top Right'), 3], + [_('Center'), 4], + [_('Bottom Left'), 5], + [_('Bottom Center (Default)'), 6], + [_('Bottom Right'), 7], + ], + 'osdWindowModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Secondary Monitors') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Thumbnails Position'), + _('Allows to place workspace thumbnails of secondary monitors on the opposite side than on the primary monitor'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'secWsThumbnailsPosition', + [ + [_('Same as Primary'), 2], + [_('Left / Top'), 0], + [_('Right / Bottom'), 1], + [_('Hide'), 3], + ] + ) + ); + + const secWstPositionAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: -100, + step_increment: 1, + page_increment: 10, + }); + + const secWstPositionScale = itemFactory.newScale(secWstPositionAdjustment); + secWstPositionScale.add_mark(0, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Fine Tune Workspace Thumbnails Position'), + _('Adjusts the position of the thumbnails on the axis given by the orientation of the workspaces'), + secWstPositionScale, + 'secWsTmbPositionAdjust' + ) + ); + + const secWsThumbnailScaleAdjustment = new Gtk.Adjustment({ + upper: 30, + lower: 0, + step_increment: 1, + page_increment: 1, + }); + + const secWsThumbnailScale = itemFactory.newScale(secWsThumbnailScaleAdjustment); + secWsThumbnailScale.add_mark(13, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Workspace Thumbnails Max Scale'), + _('Adjusts maximum size of the workspace thumbnails (% relative to display width / height) for secondary monitors'), + secWsThumbnailScale, + 'secWsThumbnailScale' + ) + ); + + const wsSecScaleAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 30, + step_increment: 1, + page_increment: 10, + }); + + const wsSecScaleScale = itemFactory.newScale(wsSecScaleAdjustment); + wsScaleScale.add_mark(100, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Workspace Preview Scale'), + _('Allows to scale down workspace previews on secondary monitors'), + wsSecScaleScale, + 'secWsPreviewScale' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Shift Workspace Preview by Panel Height'), + _('This option can help align overview of the secondary monitor with the primary monitor'), + itemFactory.newSwitch(), + 'secWsPreviewShift' + ) + ); + + + + return optionList; + } + + _getAppearanceOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + + // ---------------------------------------------------------------- + optionList.push( + itemFactory.getRowWidget( + _('Dash') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Dash Max Icon Size'), + _('Maximum size of Dash icons in pixels'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashMaxIconSize', + [ + [_('128'), 128], + [_('112'), 112], + [_('96'), 96], + [_('80'), 80], + [_('64'), 64], + [_('48'), 48], + [_('32'), 32], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Dash Background Style'), + _('Allows you to change the background color of the dash to match the search results an app folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashBgColor', + [ + [_('Default'), 0], + [_('Light'), 1], + ], + 'dashModule' + ) + ); + + const dashBgAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 0, + step_increment: 1, + page_increment: 10, + }); + + const dashBgOpacityScale = itemFactory.newScale(dashBgAdjustment); + optionList.push( + itemFactory.getRowWidget( + _('Dash Background Opacity'), + _('Adjusts the opacity of the Dash background'), + dashBgOpacityScale, + 'dashBgOpacity', + null, + 'dashModule' + ) + ); + + const dashRadAdjustment = new Gtk.Adjustment({ + upper: 50, + lower: 0, + step_increment: 1, + page_increment: 1, + }); + + const dashBgRadiusScale = itemFactory.newScale(dashRadAdjustment); + optionList.push( + itemFactory.getRowWidget( + _('Dash Background Radius'), + _('Adjusts the border radius of the Dash background in pixels. 0 means the default value given by the current theme style'), + dashBgRadiusScale, + 'dashBgRadius', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Dash Background GNOME 3 Style'), + _('Background of the vertically oriented dash will imitate the GNOME 3 style'), + itemFactory.newSwitch(), + 'dashBgGS3Style', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Running App Indicator'), + _('Allows you to change style of the running app indicator under the app icon'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'runningDotStyle', + [ + [_('Dot (Default)'), 0], + [_('Line'), 1], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Thumbnails') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Workspace Thumbnail Labels'), + _('Each workspace thumbnail can show label with its index and name (if defined in the system settings) or name/title of its most recently used app/window'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'showWsTmbLabels', + [ + [_('Disable'), 0], + [_('Index'), 1], + [_('Index + WS Name'), 2], + [_('Index + App Name'), 3], + [_('Index + Window Title'), 4], + ] + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show WS Thumbnail Label on Hover'), + _('Show the label only when the mouse pointer hovers over the thumbnail'), + itemFactory.newSwitch(), + 'showWsTmbLabelsOnHover' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Wallpaper in Workspace Thumbnails'), + _('All workspace thumbnails will include the current desktop background'), + itemFactory.newSwitch(), + 'showWsSwitcherBg' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Window Preview') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Window Preview App Icon Size'), + null, + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winPreviewIconSize', + [ + [_('64 (Default)'), 0], + [_('48'), 1], + [_('32'), 2], + [_('22'), 3], + [_('Disable'), 4], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Window Title Position / Visibility'), + _('Sets the position of the window title that is displayed when the mouse hovers over the window or can always be visible'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winTitlePosition', + [ + [_('Inside Window'), 0], + [_('Inside Window Always Visible'), 1], + [_('Below Window (Default)'), 2], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Close Window Button'), + _('Allows you to hide close window button'), + itemFactory.newSwitch(), + 'winPreviewShowCloseButton', + null, + 'windowPreviewModule' + ) + ); + + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Preview') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Workspace Preview Background'), + _('Allows to hide the background of the workspace preview'), + itemFactory.newSwitch(), + 'showWsPreviewBg' + ) + ); + + const wsPreviewBgRadiusAdjustment = new Gtk.Adjustment({ + upper: 60, + lower: 5, + step_increment: 1, + page_increment: 5, + }); + + const wsPreviewBgRadiusSpinButton = itemFactory.newScale(wsPreviewBgRadiusAdjustment); + wsPreviewBgRadiusSpinButton.add_mark(30, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Workspace Background Corner Radius'), + _('Adjusts the corner radius of the workspace preview in the overview'), + wsPreviewBgRadiusSpinButton, + 'wsPreviewBgRadius' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Grid') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Icon Size'), + _('Allows to set a fixed app grid icon size and bypass the default adaptive algorithm'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appGridIconSize', + [ + [_('Adaptive (Default)'), -1], + [_('256'), 256], + [_('224'), 224], + [_('208'), 208], + [_('192'), 192], + [_('176'), 176], + [_('160'), 160], + [_('144'), 144], + [_('128'), 128], + [_('112'), 112], + [_('96'), 96], + [_('80'), 80], + [_('64'), 64], + [_('48'), 48], + // [_('32'), 32], + ], + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Folder Icon Size'), + _('Allows to set a fixed icon size and bypass the default adaptive algorithm in the open folder dialog'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appGridFolderIconSize', + [ + [_('Adaptive (Default)'), -1], + [_('128'), 128], + [_('112'), 112], + [_('96'), 96], + [_('80'), 80], + [_('64'), 64], + [_('48'), 48], + [_('32'), 32], + ], + 'appDisplayModule' + ) + ); + + const folderIconGridCombo = itemFactory.newDropDown(); + optionList.push( + itemFactory.getRowWidget( + _('Max App Folder Icon Grid Size'), + _('Sets a grid size (number of icons) in the folder preview. 3x3 options automatically switches between 2x2 and 3x3 grid depending on the number of icons in the folder'), + folderIconGridCombo, + 'appGridFolderIconGrid', + [ + [_('2x2 (Default)'), 2], + [_('3x3 for 5+ apps'), 3], + [_('3x3 for 9+ apps'), 4], + ], + 'appDisplayModule' + ) + ); + + const columnsAdjustment = new Gtk.Adjustment({ + upper: 15, + lower: 0, + step_increment: 1, + page_increment: 1, + }); + + const columnsSpinBtn = itemFactory.newSpinButton(columnsAdjustment); + optionList.push(itemFactory.getRowWidget( + _('Columns per Page (0 for adaptive grid)'), + _('Number of columns in the application grid. If set to 0 (the default), the number will be set automatically to fit the available width'), + columnsSpinBtn, + 'appGridColumns', + null, + 'appDisplayModule' + )); + + const rowsAdjustment = new Gtk.Adjustment({ + upper: 15, + lower: 0, + step_increment: 1, + page_increment: 1, + }); + + const rowsSpinBtn = itemFactory.newSpinButton(rowsAdjustment); + optionList.push(itemFactory.getRowWidget( + _('Rows per Page (0 for adaptive grid)'), + _('Number of rows in the application grid. If set to 0 (the default), the number will be set automatically to fit the available height'), + rowsSpinBtn, + 'appGridRows', + null, + 'appDisplayModule' + )); + + const folderColumnsAdjustment = new Gtk.Adjustment({ + upper: 15, + lower: 0, + step_increment: 1, + page_increment: 1, + }); + + const folderColumnsSpinBtn = itemFactory.newSpinButton(folderColumnsAdjustment); + optionList.push(itemFactory.getRowWidget( + _('Folder Columns per Page (0 for adaptive grid)'), + _('Number of columns in folder grid. If you leave the value at 0, the number of columns will be calculated to fit all the folder icons on one page'), + folderColumnsSpinBtn, + 'appGridFolderColumns', + null, + 'appDisplayModule' + )); + + const folderRowsAdjustment = new Gtk.Adjustment({ + upper: 15, + lower: 0, + step_increment: 1, + page_increment: 1, + }); + + const folderRowsSpinBtn = itemFactory.newSpinButton(folderRowsAdjustment); + optionList.push(itemFactory.getRowWidget( + _('Folder Rows per Page (0 for adaptive grid)'), + _('Number of rows in folder grid. If you leave the value at 0, the number of rows will be calculated to fit all the folder icons on one page'), + folderRowsSpinBtn, + 'appGridFolderRows', + null, + 'appDisplayModule' + )); + + const appGridSpacingAdjustment = new Gtk.Adjustment({ + upper: 30, + lower: 5, + step_increment: 1, + page_increment: 5, + }); + + const appGridSpacingScale = itemFactory.newScale(appGridSpacingAdjustment); + appGridSpacingScale.add_mark(12, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Grid Spacing'), + _('Adjusts the spacing between icons in a grid, the real impact is on folders'), + appGridSpacingScale, + 'appGridSpacing', + null, + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Search') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Search Icon Size'), + _('Size of results provided by the App Search Provider - smaller size allows to fit more results'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'searchIconSize', + [ + [_('128'), 128], + [_('112'), 112], + [_('96 (Default)'), 96], + [_('80'), 80], + [_('64'), 64], + [_('48'), 48], + [_('32'), 32], + ], + 'searchModule' + ) + ); + + const maxSearchResultsAdjustment = new Gtk.Adjustment({ + upper: 50, + lower: 5, + step_increment: 1, + page_increment: 5, + }); + + const maxSearchResultsSpinButton = itemFactory.newScale(maxSearchResultsAdjustment); + maxSearchResultsSpinButton.add_mark(10, Gtk.PositionType.TOP, null); + maxSearchResultsSpinButton.add_mark(20, Gtk.PositionType.TOP, null); + maxSearchResultsSpinButton.add_mark(30, Gtk.PositionType.TOP, null); + maxSearchResultsSpinButton.add_mark(40, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Max Search Results Rows'), + _('Sets the maximum number of rows for result lists of all search providers except the window search provider which always lists all results'), + maxSearchResultsSpinButton, + 'searchMaxResultsRows', + null, + 'searchModule' + ) + ); + + + optionList.push( + itemFactory.getRowWidget( + _('Overview Background') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Wallpaper'), + _('Replaces the solid grey background in the overview with the current desktop wallpaper'), + itemFactory.newSwitch(), + 'showBgInOverview' + ) + ); + + const brightnessBgAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 0, + step_increment: 1, + page_increment: 10, + }); + + const bgBrightnessScale = itemFactory.newScale(brightnessBgAdjustment); + optionList.push( + itemFactory.getRowWidget( + _('Brightness'), + _('Brightness of the background wallpaper in the overview'), + bgBrightnessScale, + 'overviewBgBrightness' + ) + ); + + const searchBrightnessBgAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 0, + step_increment: 1, + page_increment: 10, + }); + + const searchBgBrightnessScale = itemFactory.newScale(searchBrightnessBgAdjustment); + optionList.push( + itemFactory.getRowWidget( + _('Brightness for Search View'), + _('Allows you to set a lower background brightness for search view mode where text visibility is more important'), + searchBgBrightnessScale, + 'searchBgBrightness' + ) + ); + + const blurBgAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 0, + step_increment: 1, + page_increment: 10, + }); + + const bgBlurScale = itemFactory.newScale(blurBgAdjustment); + optionList.push( + itemFactory.getRowWidget( + _('Blur Window Picker Background'), + _('Sets the amount of background blur in the window picker view'), + bgBlurScale, + 'overviewBgBlurSigma' + ) + ); + + const blurAppBgAdjustment = new Gtk.Adjustment({ + upper: 100, + lower: 0, + step_increment: 1, + page_increment: 10, + }); + + const bgAppBlurScale = itemFactory.newScale(blurAppBgAdjustment); + optionList.push( + itemFactory.getRowWidget( + _('Blur App Grid/Search View Background'), + _('Sets the amount of background blur in the app grid and search results views'), + bgAppBlurScale, + 'appGridBgBlurSigma' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Smooth Blur Transitions'), + _('Allows for smoother blur transitions, but can affect the overall smoothness of overview animations on weak hardware'), + itemFactory.newSwitch(), + 'smoothBlurTransitions' + ) + ); + + + optionList.push( + itemFactory.getRowWidget( + _('Window Thumbnails (PIP)') + ) + ); + + const winTmbAdjustment = new Gtk.Adjustment({ + upper: 50, + lower: 5, + step_increment: 1, + page_increment: 1, + }); + + const winTmbScale = itemFactory.newScale(winTmbAdjustment); + winTmbScale.add_mark(15, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Default Window Thumbnail (PIP) Scale (% of screen height)'), + _('Default scale of window thumbnail (like Picture In Picture) that you can create using the app icon menu or window preview action'), + winTmbScale, + 'windowThumbnailScale' + ) + ); + + return optionList; + } + // ---------------------------------------------------------------- + + _getBehaviorOptionList(itemFactory) { + const optionList = []; + + optionList.push( + itemFactory.getRowWidget( + _('Overview') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Overview Mode'), + _('The Expose Windows on Hover mode does not expose the workspace preview windows until the mouse pointer enters any window\nThe Static Workspace mode keeps the workspace static when you activate the overview, it only shows Dash, workspace thumbnails and search entry over the workspace and only clicking on an active workspace thumbnail activates the default overview'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'overviewMode', + [ + [_('Default'), 0], + [_('Expose Windows on Hover'), 1], + [_('Static Workspace'), 2], + ] + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Startup State'), + _('Allows to change the state in which GNOME Shell starts a session'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'startupState', + [ + [_('Overview (Default)'), 0], + [_('Desktop'), 1], + [_('Applications'), 2], + ] + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Escape Key Behavior'), + _('Allows you to close the overview with a single press of the Escape key, even from the application grid or from search, if the search entry field does not have focus'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'overviewEscBehavior', + [ + [_('Default'), 0], + [_('Close Overview'), 1], + ], + 'searchControllerModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Overlay Key (Super/Windows)') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Single-Press Action'), + _('Disable or change behavior when you press and release the Super key. The "Search Windows" options requires the WindowSearchProvider module to be activated'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'overlayKeyPrimary', + [ + [_('Disable'), 0], + [_('Follow Global Overview Mode'), 1], + [_('Overview (Default)'), 2], + [_('Applications'), 3], + [_('Overview - Static WS Preview'), 4], + [_('Overview - Static Workspace'), 5], + [_('Search Windows'), 6], + // [_('Search Recent Files'), 7], + ], + 'overlayKeyModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Double-Press Action'), + _('Disable or change behavior when you double-press the Super key. The "Search Windows" option requires the WindowSearchProvider module to be activated. The "Static WS Overview - Expose Windows" option allows you to switch to default Activities Overview window picker view if you set static workspace (preview) for the single press/release Super key action'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'overlayKeySecondary', + [ + [_('Disable'), 0], + [_('Applications (Default)'), 1], + [_('Search Windows'), 2], + [_('Overview - Window Picker'), 3], + // [_('Search Recent Files'), 4], + ], + 'overlayKeyModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Hot Corner (Install Custom Hot Corners - Extended for more options)') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Hot Corner Action'), + _('Disable or change behavior of the hot corner. Holding down the Ctrl key while hitting the hot corner switches between Overview/Applications actions'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'hotCornerAction', + [ + [_('Disable'), 0], + [_('Follow Global Overview Mode'), 1], + [_('Overview - Window Picker'), 2], + [_('Applications'), 3], + [_('Overview - Static WS Preview'), 4], + [_('Overview - Static Workspace'), 5], + [_('Search Windows'), 6], + ], + 'layoutModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Hot Corner Position'), + _('Choose which corner of your monitors will be active. If you choose "Follow Dash" option, the corner will be placed near the left or top edge of the Dash. The last option extends the hot corner trigger to cover the entire ege of the monitor where Dash is located'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'hotCornerPosition', + [ + [_('Default'), 0], + [_('Top Left'), 1], + [_('Top Right'), 2], + [_('Bottom Left'), 3], + [_('Bottom Right'), 4], + [_('Follow Dash'), 5], + [_('Follow Dash - Hot Edge'), 6], + ], + 'layoutModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Enable Hot Corner in Full-Screen Mode'), + _('If you often work with full-screen applications and want the hot corner to be usable'), + itemFactory.newSwitch(), + 'hotCornerFullscreen', + null, + 'layoutModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Show Ripples Animation'), + _('The ripple animation is played when the hot corner is activated. The ripple size has been reduced to be less distracting'), + itemFactory.newSwitch(), + 'hotCornerRipples', + null, + 'layoutModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Dash') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Isolate Workspaces'), + _('Dash will only show apps and windows from the current workspace'), + itemFactory.newSwitch(), + 'dashIsolateWorkspaces', + null, + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Icon - Click Behavior'), + _('Choose your preferred behavior when clicking on an app icon. The "Prefer Current Workspace" option opens a new app window if not present in the current workspace. The "Open New Window" option also switches behavior of the middle click to "Activate" since its default behavior is to open a new window'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashShowWindowsBeforeActivation', + [ + [_('Activate App Immediately'), 0], + [_('First Switch to Workspace'), 1], + [_('Open New Window (if supported)'), 2], + [_('Prefer Current Workspace'), 3], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Icon - Scroll Action'), + _('Choose the behavior when scrolling over an app icon. The window cycler works with a list of windows sorted by the "Most Recently Used" and grouped by workspaces. Scrolling up cycles through previously used windows on the same workspace and then switches to another workspace, if any'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'dashIconScroll', + [ + [_('Default'), 0], + [_('Cycle App Windows - Highlight Selected'), 1], + [_('Cycle App Windows - Highlight App'), 2], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Search Windows Icon - Scroll Action'), + _('Choose the behavior when scrolling over the Search Windows icon. The window cycler works with a list of windows sorted by "Most Recently Used" of the current workspace or all workspaces. Scrolling up cycles through previously used windows on the same workspace, or all windows regardless workspace. This option is mainly useful for the static workspace overview mode.'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'searchWindowsIconScroll', + [ + [_('Default'), 0], + [_('Cycle All Windows'), 1], + [_('Cycle Windows On Current WS'), 2], + ], + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Thumbnails') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Close Workspace Button'), + _('The Close Workspace button appears on the workspace thumbnail when you hover over it and allows you to close all windows on the workspace. You can choose a "safety lock" to prevent accidental use'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'closeWsButtonMode', + [ + [_('Disable'), 0], + [_('Single Click'), 1], + [_('Double Click'), 2], + [_('Ctrl Key + Click'), 3], + ] + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Window Preview') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Secondary Button Click Action'), + _('Allows you to add a secondary mouse click action to the window preview'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winPreviewSecBtnAction', + [ + [_('Activate Window (Default)'), 0], + [_('Close Window'), 1], + [_('Search For Same App Windows'), 2], + [_('Create Window Thumbnail - PIP'), 3], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Middle Button Click Action'), + _('Allows you to add a middle mouse click action to the window preview'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'winPreviewMidBtnAction', + [ + [_('Activate Window (Default)'), 0], + [_('Close Window'), 1], + [_('Search For Same App Windows'), 2], + [_('Create Window Thumbnail - PIP'), 3], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Icon Click Action'), + _('Select the action to take when the application icon on the window preview is clicked'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'windowIconClickAction', + [ + [_('Activate Window (Default)'), 0], + [_('Search For Same App Windows'), 1], + [_('Create Window Thumbnail - PIP'), 2], + ], + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Always Activate Selected'), + _('If enabled, the currently selected window will be activated when leaving the Overview even without clicking. Usage example - press Super to open the Overview, place mouse pointer over a window, press Super again to activate the window'), + itemFactory.newSwitch(), + 'alwaysActivateSelectedWindow', + null, + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Grid') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Grid Order'), + _('Choose sorting method for the app grid. Note that sorting by usage ignores folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appGridOrder', + [ + [_('Custom (Default)'), 0], + [_('Alphabet - Folders First'), 1], + [_('Alphabet - Folders Last'), 2], + [_('Usage - No Folders'), 3], + ], + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Folder Order'), + _('Choose sorting method for app folders'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appFolderOrder', + [ + [_('Custom (Default)'), 0], + [_('Alphabet'), 1], + [_('Usage'), 2], + ], + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Grid Content'), + _('The default Shell removes favorite apps, this option allows to duplicate them in the grid or remove also running applications. Option "Favorites and Running First" only works with the Alphabet and Usage sorting'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appGridContent', + [ + [_('Include All'), 0], + [_('Include All - Favorites and Running First'), 1], + [_('Exclude Favorites (Default)'), 2], + [_('Exclude Running'), 3], + [_('Exclude Favorites and Running'), 4], + ], + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Active Icons in Folder Preview'), + _('If enabled, icons in the folder review behaves like normal icons, you can activate or even drag them directly, without having to open the folder first'), + itemFactory.newSwitch(), + 'appGridActivePreview', + null, + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Center Open Folders'), + _('App folder may open in the center of the screen or above the source folder icon'), + itemFactory.newSwitch(), + 'appGridFolderCenter', + null, + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Allow Incomplete Pages'), + _('If disabled, icons from the next page (if any) are automatically moved to fill any empty slot left after an icon was (re)moved (to a folder for example)'), + itemFactory.newSwitch(), + 'appGridIncompletePages', + null, + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Labels Behavior'), + _('Choose how and when to display app names'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appGridNamesMode', + [ + [_('Ellipsized - Expand Selected (Default)'), 0], + [_('Always Expanded'), 1], + [_('Hidden - Show Selected Only'), 2], + ], + 'appDisplayModule' + ) + ); + + optionList.push(itemFactory.getRowWidget( + _('Reset App Grid Layout'), + _('Removes all stored app grid positions, after reset icons will be sorted alphabetically, except folder contents'), + itemFactory.newResetButton(() => { + const settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); + settings.set_value('app-picker-layout', new GLib.Variant('aa{sv}', [])); + }) + )); + + optionList.push(itemFactory.getRowWidget( + _('Remove App Grid Folders'), + _('Removes all folders, folder apps will move to the root grid'), + itemFactory.newResetButton(() => { + const settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders' }); + settings.set_strv('folder-children', []); + }) + )); + + + optionList.push( + itemFactory.getRowWidget( + _('Search') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Window Search Provider - Sorting'), + _('Choose the window sorting method'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'searchWindowsOrder', + [ + [_('Most Recently Used (MRU)'), 0], + [_('MRU - Current Workspace First'), 1], + [_('MRU - By Workspaces'), 2], + [_('Stable Sequence - By Workspaces'), 3], + ], + 'windowSearchProviderModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Enable Fuzzy Match'), + _('Enabling the fuzzy match allows you to skip letters in the pattern you are searching for and find "Firefox" even if you type "ffx". Works only for the App, Window and Recent files search providers'), + itemFactory.newSwitch(), + 'searchFuzzy' + ) + ); + + /* const wspCommandSwitch = itemFactory.newSwitch(); + optionList.push( + itemFactory.getRowWidget( + _('Enable Commands in Search Entry'), + _('You can use following commands separated by the space at the end of entered pattern:\n/x! \t\t\t- close selected window\n/xa! \t\t\t- close all found windows\n/m[number] \t\t- (e.g. /m6) move selected window to workspace with given index\n/ma[number] \t- move all found windows to workspace with given index'), + wspCommandSwitch, + 'searchWindowsCommands' + ) + );*/ + + optionList.push( + itemFactory.getRowWidget( + _('Animations - General') + ) + ); + + const animationSpeedAdjustment = new Gtk.Adjustment({ + upper: 500, + lower: 1, + step_increment: 10, + page_increment: 100, + }); + + const animationSpeedScale = itemFactory.newScale(animationSpeedAdjustment); + animationSpeedScale.add_mark(100, Gtk.PositionType.TOP, null); + optionList.push( + itemFactory.getRowWidget( + _('Animation Speed'), + _('Adjusts the global animation speed in % of the default duration - higher value means slower animation'), + animationSpeedScale, + 'animationSpeedFactor' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Animations - Overview') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('App Grid Animation'), + _('When entering the App Grid view, the app grid animates from the edge of the screen. You can choose the direction, keep the Default (direction will be selected automatically) or disable the animation if you don\'t like it'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'appGridAnimation', + [ + [_('Default'), 4], + [_('Disable'), 0], + [_('Right to Left'), 1], + [_('Left to Right'), 2], + [_('Bottom to Top'), 3], + [_('Top to Bottom'), 5], + ] + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Search View Animation'), + _('When search is activated the search view with search results can animate from the edge of the screen. You can choose the direction, keep the Default (currently Bottom to Top) or disable the animation if you don\'t like it.'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'searchViewAnimation', + [ + [_('Default'), 4], + [_('Disable'), 0], + [_('Right to Left'), 1], + [_('Left to Right'), 2], + [_('Bottom to Top'), 3], + [_('Top to Bottom'), 5], + ] + ) + ); + + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Preview Animation'), + _('When entering / leaving the App Grid / Search view, the workspace preview can animate to/from workspace thumbnail.'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'workspaceAnimation', + [ + [_('Disable'), 0], + [_('Enable'), 1], + ] + ) + ); + + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Switcher') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Wraparound'), + _('Continue from the last workspace to the first and vice versa'), + itemFactory.newSwitch(), + 'wsSwitcherWraparound' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Ignore Last (empty) Workspace'), + _('In Dynamic workspaces mode, there is always one empty workspace at the end. Switcher can ignore this last workspace'), + itemFactory.newSwitch(), + 'wsSwitcherIgnoreLast' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Switcher Animation'), + _('Allows you to disable movement of the desktop background during workspace switcher animation outside of the overview. The Static Background mode also keeps Conky and desktop icons on their place during switching.'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'workspaceSwitcherAnimation', + [ + [_('Default'), 0], + [_('Static Background'), 1], + ], + 'workspaceAnimationModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workspace Switcher Popup Mode'), + _('This popup shows up when you switch workspace using a keyboard shortcut or gesture outside of the overview. You can to disable the popup at all, or show it on the current monitor (the one with mouse pointer) instead of the primary.'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'wsSwPopupMode', + [ + [_('Disable'), 0], + [_('Show on Primary Monitor (Default)'), 1], + [_('Show on Current Monitor'), 2], + ], + 'workspaceSwitcherPopupModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Notifications') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Window Attention Handler'), + _('When a window requires attention (often a new window), GNOME Shell shows you a notification about it. You can disable popups of these messages (notification will be pushed into the message tray silently) or focus the source window immediately instead'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'windowAttentionMode', + [ + [_('Show Notifications (Default)'), 0], + [_('Disable Notification Popups'), 1], + [_('Immediately Focus Window'), 2], + ], + 'windowAttentionHandlerModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Favorites'), + _('Disable pin/unpin app notifications'), + // itemFactory.newComboBox(), + itemFactory.newDropDown(), + 'favoritesNotify', + [ + [_('Show Notifications (Default)'), 1], + [_('Disable Notifications'), 0], + ], + 'appFavoritesModule' + ) + ); + + return optionList; + } + + _getModulesOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + optionList.push( + itemFactory.getRowWidget( + _('V-Shell Modules (allows you to disable modules that conflict with another extension)') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('WindowSearchProvider'), + _('Activates the window search provider that adds open windows to the search results. You can search app names and window titles. You can also use "wq//" prefix (also by pressing the Space hotkey in the overview, or clicking dash icon) to suppress results from other search providers'), + itemFactory.newSwitch(), + 'windowSearchProviderModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('RecentFilesSearchProvider'), + _('Activates the recent files search provider that can be triggered by a dash icon, Ctrl + Space hotkey or by typing "fq//" prefix in the search entry field. This option needs File History option enabled in the GNOME Privacy settings'), + itemFactory.newSwitch(), + 'recentFilesSearchProviderModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('ExtensionsSearchProvider'), + _('Activates the extensions search provider that adds extensions to the search results. You can also use "eq//" prefix (also by pressing the Ctrl + Shift + Space hotkey in the overview, or clicking dash icon) to suppress results from other search providers'), + itemFactory.newSwitch(), + 'extensionsSearchProviderModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('AppDisplay / IconGrid'), + _('App grid customization and options'), + itemFactory.newSwitch(), + 'appDisplayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('AppFavorites'), + _('Pin/unpin app notification options'), + itemFactory.newSwitch(), + 'appFavoritesModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Dash'), + _('Dash customization and options, support for vertical orientation'), + itemFactory.newSwitch(), + 'dashModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Layout'), + _('Hot corner options'), + itemFactory.newSwitch(), + 'layoutModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('MessageTray'), + _('Notification position options'), + itemFactory.newSwitch(), + 'messageTrayModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('OsdWindow'), + _('OSD position options'), + itemFactory.newSwitch(), + 'osdWindowModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('OverlayKey'), + _('Overlay (Super/Window) key options'), + itemFactory.newSwitch(), + 'overlayKeyModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Panel'), + _('Panel options'), + itemFactory.newSwitch(), + 'panelModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Search'), + _('Search view and app search provider customization and options'), + itemFactory.newSwitch(), + 'searchModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('SearchController'), + _('Escape key behavior options in the overview'), + itemFactory.newSwitch(), + 'searchControllerModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('SwipeTracker'), + _('Gestures for vertical workspace orientation'), + itemFactory.newSwitch(), + 'swipeTrackerModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('WindowAttentionHandler'), + _('Window attention handler options'), + itemFactory.newSwitch(), + 'windowAttentionHandlerModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('WindowManager'), + _('Fixes an upstream bug in the minimization animation of a full-screen window'), + itemFactory.newSwitch(), + 'windowManagerModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('WindowPreview'), + _('Window preview options, fixes an upstream bug that fills the system log with errors when you close a window from the overview or exit the overview with a gesture when any window is selected'), + itemFactory.newSwitch(), + 'windowPreviewModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('WindowThumbnail'), + _('Create Window Thumbnail (PIP) option in the app icon menu and window preview actions'), + itemFactory.newSwitch(), + 'windowThumbnailModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workspace'), + _('Fixes workspace preview allocations for vertical workspaces orientation and window scaling in static overview modes'), + itemFactory.newSwitch(), + 'workspaceModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('WorkspaceAnimation'), + _('Static workspace animation option'), + itemFactory.newSwitch(), + 'workspaceAnimationModule' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('WorkspaceSwitcherPopup'), + _('Workspace switcher popup position options'), + itemFactory.newSwitch(), + 'workspaceSwitcherPopupModule' + ) + ); + + return optionList; + } + + _getMiscOptionList(itemFactory) { + const optionList = []; + // options item format: + // (text, caption, widget, settings-variable, [options for combo], sensitivity-depends-on-bool-variable) + + optionList.push( + itemFactory.getRowWidget( + _('Keyboard') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Override Page Up/Down Shortcuts'), + _('This option automatically overrides the (Shift +) Super + Page Up/Down keyboard shortcuts for the current workspace orientation. If you encounter any issues, check the configuration in the dconf editor'), + itemFactory.newSwitch(), + 'enablePageShortcuts' + ) + ); + + /* optionList.push( + itemFactory.getRowWidget( + _('Compatibility') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Improve compatibility with Dash to Dock'), + _('With the default Ubuntu Dock and other Dash To Dock forks, you may experience issues with Activities overview after you change Dock position or re-enable the extension. This option is enabled automatically if a replacement for the Dash is detected. In any case, using Dash to Dock extension with V-Shell is problematic and not recommended.'), + itemFactory.newSwitch(), + 'fixUbuntuDock' + ) + );*/ + + optionList.push( + itemFactory.getRowWidget( + _('Performance') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Smooth App Grid Animations'), + _('This option allows V-Shell to pre-realize app grid and app folders during session startup in order to avoid stuttering animations when using them for the first time. If enabled, the session startup needs a little bit more time to finish and necessary memory will be allocated at this time'), + itemFactory.newSwitch(), + 'appGridPerformance' + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Workarounds') + ) + ); + + optionList.push( + itemFactory.getRowWidget( + _('Fix New Window Not In Focus'), + _('If you often find that the app window you open from the Activities overview does not get focus, try enabling this option.'), + itemFactory.newSwitch(), + 'newWindowFocusFix' + ) + ); + + return optionList; + } + + _getAboutOptionList(itemFactory) { + const optionList = []; + + optionList.push(itemFactory.getRowWidget( + this.Me.metadata.name + )); + + const versionName = this.Me.metadata['version-name'] ?? ''; + let version = this.Me.metadata['version'] ?? ''; + version = versionName && version ? `/${version}` : version; + const versionStr = `${versionName}${version}`; + optionList.push(itemFactory.getRowWidget( + _('Version'), + null, + itemFactory.newLabel(versionStr) + )); + + optionList.push(itemFactory.getRowWidget( + _('Reset all options'), + _('Set all options to default values.'), + itemFactory.newOptionsResetButton() + )); + + + optionList.push(itemFactory.getRowWidget( + _('Links') + )); + + optionList.push(itemFactory.getRowWidget( + _('Homepage'), + _('Source code and more info about this extension'), + itemFactory.newLinkButton('https://github.com/G-dH/vertical-workspaces') + )); + + optionList.push(itemFactory.getRowWidget( + _('Changelog'), + _("See what's changed."), + itemFactory.newLinkButton('https://github.com/G-dH/vertical-workspaces/blob/main/CHANGELOG.md') + )); + + optionList.push(itemFactory.getRowWidget( + _('GNOME Extensions'), + _('Rate and comment V-Shell on the GNOME Extensions site'), + itemFactory.newLinkButton('https://extensions.gnome.org/extension/5177') + )); + + optionList.push(itemFactory.getRowWidget( + _('Report a bug or suggest new feature'), + _('Help me to help you!'), + itemFactory.newLinkButton('https://github.com/G-dH/vertical-workspaces/issues') + )); + + optionList.push(itemFactory.getRowWidget( + _('Buy Me a Coffee'), + _('If you like V-Shell, you can help me with my coffee expenses'), + itemFactory.newLinkButton('https://buymeacoffee.com/georgdh') + )); + + return optionList; + } +} diff --git a/extensions/45/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml b/extensions/45/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml new file mode 100644 index 0000000..ecaacd1 --- /dev/null +++ b/extensions/45/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml @@ -0,0 +1,408 @@ + + + + + 0 + + + false + + + 2 + + + 350 + + + 95 + + + 100 + + + false + + + 2 + + + 0 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + 3 + + + false + + + 0 + + + 2 + + + false + + + false + + + true + + + false + + + 13 + + + 13 + + + 13 + + + 48 + + + 85 + + + 0 + + + 18 + + + true + + + 1 + + + false + + + true + + + false + + + 30 + + + true + + + 0 + + + 40 + + + 60 + + + 30 + + + false + + + 4 + + + 0 + + + 1 + + + 1 + + + 100 + + + 1 + + + 0 + + + 2 + + + 0 + + + 96 + + + 104 + + + -1 + + + 0 + + + 0 + + + 0 + + + 0 + + + 2 + + + -1 + + + 0 + + + 0 + + + 12 + + + true + + + 1 + + + 3 + + + false + + + false + + + 90 + + + 1 + + + 1 + + + false + + + 1 + + + true + + + 1 + + + false + + + 5 + + + 0 + + + 0 + + + 0 + + + 50 + + + 95 + + + 1 + + + false + + + false + + + 1 + + + 2 + + + 6 + + + 1 + + + 6 + + + true + + + true + + + false + + + 2 + + + 0 + + + true + + + 1 + + + 1 + + + 1 + + + 0 + + + false + + + true + + + 15 + + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + false + + + true + + + + false + + + + "" + + + {'workspaceThumbnailsPosition': '1', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '3', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '-80', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '-80', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'dashShowExtensionsIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '13', 'secWsThumbnailScale': '13', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'dashBgOpacity': '80', 'dashBgColor': '0', 'dashBgRadius': '0', 'dashBgGS3Style': 'true', 'runningDotStyle': '1', 'enablePageShortcuts': 'false', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'false', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '30', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '40', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '0', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '100', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '1', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '0', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '3', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'} + + + + "" + + + {'workspaceThumbnailsPosition': '5', 'wsMaxSpacing': '80', 'wsPreviewScale': '100', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '0', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'dashShowExtensionsIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '5', 'wsThumbnailScaleAppGrid': '15', 'secWsThumbnailScale': '5', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'dashBgOpacity': '20', 'dashBgColor': '1', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'false', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '50', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '50', 'appGridBgBlurSigma': '40', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '0', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'true', 'appGridPageWidthScale': '90', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '1', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'} + + + + "" + + + {'workspaceThumbnailsPosition': '0', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '0', 'dashPositionAdjust': '-100', 'wsTmbPositionAdjust': '-100', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'dashShowExtensionsIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '0', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '13', 'secWsThumbnailScale': '13', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'dashBgOpacity': '20', 'dashBgColor': '1', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'false', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '60', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '30', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '2', 'overviewMode': '1', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '90', 'appGridSpacing': '12', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '0', 'notificationPosition': '2', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '1', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'} + + + + "" + + + {'workspaceThumbnailsPosition': '6', 'wsMaxSpacing': '65', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'dashShowExtensionsIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '10', 'wsThumbnailScaleAppGrid': '10', 'secWsThumbnailScale': '10', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'dashBgOpacity': '100', 'dashBgColor': '0', 'dashBgRadius': '0', 'dashBgGS3Style': 'false', 'runningDotStyle': '1', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'true', 'wsPreviewBgRadius': '30', 'showBgInOverview': 'true', 'overviewBgBrightness': '60', 'searchBgBrightness': '30', 'overviewBgBlurSigma': '80', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'winPreviewIconSize': '1', 'winTitlePosition': '0', 'startupState': '2', 'overviewMode': '2', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appFolderOrder': '0', 'appGridNamesMode': '1', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'appGridPageWidthScale': '90', 'appGridSpacing': '5', 'searchWindowsOrder': '1', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'dashIconScroll': '1', 'dashIsolateWorkspaces': 'false', 'searchWindowsIconScroll': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'wsSwitcherWraparound': 'false', 'wsSwitcherIgnoreLast': 'false', 'favoritesNotify': '0', 'notificationPosition': '1', 'osdPosition': '6', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'winPreviewSecBtnAction': '2', 'winPreviewMidBtnAction': '1', 'winPreviewShowCloseButton': 'true', 'windowIconClickAction': '1', 'overlayKeyPrimary': '1', 'overlayKeySecondary': '1', 'overviewEscBehavior': '0', 'newWindowFocusFix': 'false', 'appGridPerformance': 'true', 'windowThumbnailScale': '20', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'windowAttentionHandlerModule': 'true', 'windowThumbnailModule': 'true', 'swipeTrackerModule': 'true', 'searchControllerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'overlayKeyModule': 'true', 'osdWindowModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true', 'windowSearchProviderModule': 'true', 'extensionsSearchProviderModule': 'true'} + + + + diff --git a/extensions/vertical-workspaces/screenshots/screenshot.jpg b/extensions/45/vertical-workspaces/screenshots/screenshot.jpg similarity index 100% rename from extensions/vertical-workspaces/screenshots/screenshot.jpg rename to extensions/45/vertical-workspaces/screenshots/screenshot.jpg diff --git a/extensions/vertical-workspaces/screenshots/screenshot0.jpg b/extensions/45/vertical-workspaces/screenshots/screenshot0.jpg similarity index 100% rename from extensions/vertical-workspaces/screenshots/screenshot0.jpg rename to extensions/45/vertical-workspaces/screenshots/screenshot0.jpg diff --git a/extensions/vertical-workspaces/screenshots/screenshot0.png b/extensions/45/vertical-workspaces/screenshots/screenshot0.png similarity index 100% rename from extensions/vertical-workspaces/screenshots/screenshot0.png rename to extensions/45/vertical-workspaces/screenshots/screenshot0.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot1.png b/extensions/45/vertical-workspaces/screenshots/screenshot1.png similarity index 100% rename from extensions/vertical-workspaces/screenshots/screenshot1.png rename to extensions/45/vertical-workspaces/screenshots/screenshot1.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot2.png b/extensions/45/vertical-workspaces/screenshots/screenshot2.png similarity index 100% rename from extensions/vertical-workspaces/screenshots/screenshot2.png rename to extensions/45/vertical-workspaces/screenshots/screenshot2.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot3.png b/extensions/45/vertical-workspaces/screenshots/screenshot3.png similarity index 100% rename from extensions/vertical-workspaces/screenshots/screenshot3.png rename to extensions/45/vertical-workspaces/screenshots/screenshot3.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot4.png b/extensions/45/vertical-workspaces/screenshots/screenshot4.png similarity index 100% rename from extensions/vertical-workspaces/screenshots/screenshot4.png rename to extensions/45/vertical-workspaces/screenshots/screenshot4.png diff --git a/extensions/vertical-workspaces/screenshots/screenshot5.png b/extensions/45/vertical-workspaces/screenshots/screenshot5.png similarity index 100% rename from extensions/vertical-workspaces/screenshots/screenshot5.png rename to extensions/45/vertical-workspaces/screenshots/screenshot5.png diff --git a/extensions/vertical-workspaces/screenshots/vertical-workspaces.gif b/extensions/45/vertical-workspaces/screenshots/vertical-workspaces.gif similarity index 100% rename from extensions/vertical-workspaces/screenshots/vertical-workspaces.gif rename to extensions/45/vertical-workspaces/screenshots/vertical-workspaces.gif diff --git a/extensions/45/vertical-workspaces/stylesheet.css b/extensions/45/vertical-workspaces/stylesheet.css new file mode 100644 index 0000000..dca1d29 --- /dev/null +++ b/extensions/45/vertical-workspaces/stylesheet.css @@ -0,0 +1,308 @@ +/* +* V-Shell (Vertical Workspaces) +* stylesheet.css +*/ + +/* General dash */ +#dash.vertical { + margin: 0px; + padding: 0px; +} + +#dash.vertical .app-well-app, +#dash.vertical .show-apps { + /* left/right padding exceeds dash bg by 6px to + cover spacing between dash and the edge of the screen + so the icons will be selectable even at the edge + this spacing must be accounted for in overview allocate() */ + padding-right: 16px; + padding-left: 16px; + /*spacing between icons*/ + padding-top: 1px; + padding-bottom: 1px; + margin: 0px; +} + +#dash.vertical .dash-separator { + height: 1px; + margin-right: 2px; + margin-left: 2px; + margin-top: 4px; + margin-bottom: 4px; + background-color: transparentize(#eeeeec, 0.7); +} + +#dash.vertical .overview-icon { + padding: 5px 0; +} + +#dash.vertical .app-well-app-running-dot { + margin: 4px 0px; +} + +#dash.vertical .app-well-app-running-dot-custom { + margin: 4px 0px; + width: 2px; + height: 16px; +} + +#dash.vertical .dash-background { + margin: 0px; +} + +#dash.vertical-gs3-left .dash-background { + border-radius: 0 18px 18px 0; + border-left: 0px; + padding: 8px 12px 8px 4px; + margin-left: 0; +} + +#dash.vertical-gs3-right .dash-background { + border-radius: 18px 0 0 18px; + border-right: 0px; + padding: 8px 4px 8px 12px; + margin-right: 0; +} + +#dash.vertical-gs3-left { + margin-right: 6px; + margin-left: 0px; + padding: 0px; +} + +#dash.vertical-gs3-right { + margin-right: 0px; + margin-left: 6px; + padding: 0px; +} + +#dash.vertical-gs3-left .app-well-app, +#dash.vertical-gs3-left .show-apps, +#dash.vertical-gs3-right .app-well-app, +#dash.vertical-gs3-right .show-apps { + /* left/right padding exceeds dash bg by 6px to + cover spacing between dash and the edge of the screen + so the icons will be selectable even at the edge + this spacing must be accounted for in overview allocate() */ + padding-right: 9px; + padding-left: 9px; +} + +.dash-background-light { + background-color: rgb(200, 200, 200); + border-color: rgba(150, 150, 150, 0.4); +} + +.dash-background-reduced { + padding: 10px; +} + +.app-well-app-running-dot { + margin-bottom: 12px; +} + +.app-well-app-running-dot-custom { + margin-bottom: 12px; + width: 16px; + height: 2px; +} + + +/* add shadow to the app grid app label to be readable if it overlaps light icon below */ +.overview-icon-with-label, .folder-name-label { + text-shadow: 1px 1px 3px rgba(33, 33, 33, 0.5); +} + +/* adjustment for the vertical ws switcher indicator popup*/ +.ws-switcher-indicator { + padding: 3px; + margin: 5px; +} + +.ws-switcher-indicator:active { + padding: 5px; + margin: 3px; +} + +/* ws thumbnails captions */ +.ws-tmb-label { + padding: 2px; + color: rgb(255, 255, 255); + background-color: rgba(40,40,40,0.8); + text-align: center; +} + +.workspace-thumbnail-indicator { + border-radius: 6px; +} + +.ws-tmb-labeled { + border: 0px; +} + +.ws-tmb-transparent { + border: 0px; + background-color: rgba(200, 200, 200, 0.2); +} + +/* app grid page indicatos */ +.page-indicator-icon { + margin: 10px 10px 10px 10px; +} + +.page-indicator { + padding: 0px; +} + +/* GS 43 App Grid - indicators that show up when dragging icon */ +.prevPageIndicator { + background: rgba(255, 255, 255, 0.1); + background-gradient-start: rgba(255, 255, 255, 0.1); + background-gradient-end: transparent; + background-gradient-direction: vertical; + border-radius: 100px 100px 0px 0px; +} +.nextPageIndicator { + background: rgba(255, 255, 255, 0.1); + background-gradient-start: transparent; + background-gradient-end: rgba(255, 255, 255, 0.1); + background-gradient-direction: vertical; + border-radius: 0px 0px 100px 100px; + +} +/* +.search-entry{ + background-color: rgba(200, 200, 200, 0.1); + color: white; + border-color: rgba(160, 160, 160, 0.4); +} + +.search-entry:hover, +.search-entry:focus { + background-color: rgba(200, 200, 200, 0.2); +}*/ + +.search-entry-om2 { + color: white; + background-color: rgba(40, 40, 40, 1); +} + +.search-entry-om2:hover, +.search-entry-om2:focus { + background-color: rgba(50, 50, 50, 1); +} + +/* for static ws mode */ +/*.search-section-content-bg,*/ +.search-section-content-bg-om2 { + border-radius: 26px; + border: 1px, rgb(60, 60, 60); + padding-top: 15px; +} + +.search-section-content-bg-om2 { + background-color: rgb(40, 40, 40); +} + +.search-section-content { + background-color: rgba(200, 200, 200, 0.1); +} + +/* "no results" / "searching..." text*/ +.search-statustext, .search-statustext-om2 { + background-color: rgba(200, 200, 200, 0.1); + color: white; + margin-top: 50px; + padding: 30px; + border-radius: 20px; + text-shadow: 0px 0px 5px rgb(23, 23, 23); +} + +.search-statustext-om2 { + background-color: rgb(40, 40, 40); + border: 1px rgba(200, 200, 200, 0.1); +} + +#panel:overview, .transparent-panel { + background-color: transparent; +} + +/* reduce spacing between app icons in search results */ +.grid-search-results { + spacing: 4px; +} + +/* hide vertical scroll bar, it's distracting in the search results */ +StButton#vhandle { + background-color: transparent; +} + +.show-apps-icon-horizontal-hide { + width: 0; + margin: 0; + spacing: 0; +} + +.show-apps-icon-vertical-hide { + height: 0; + margin: 0; + spacing: 0; +} + +.workspace-close-button { + color: white; + background-color: dimgrey; + width: 18px; + height: 18px; + padding: 2px; + margin: 2px; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); + border: 0px; + border-radius: 35px; +} + +.workspace-close-button-hover { + background-color: rgb(255, 0, 0); +} + +/* ws tmb placeholder */ +.placeholder-vertical { + background-size: contain; + height: 18px; +} + +/* reduce size of hot corner ripples to half */ +.ripple-box { + width: 26px; + height: 26px; +} + +.osd-window { + margin: 4em; +} + +.app-folder .overview-icon, +.edit-folder-button, +.folder-name-entry { + background-color: rgba(200, 200, 200, 0.08); +} + +.app-folder-dialog-vshell { + background-color: rgba(200, 200, 200, 0.08); +} + +.app-folder-dialog { + border-color: rgba(160, 160, 160, 0.3); +} + +.edit-folder-button:hover, +.app-folder:hover .overview-icon, +.app-folder:focus .overview-icon { + background-color: rgba(200, 200, 200, 0.15); +} + +/* reduce quick menu buttons height in GS44 */ +/*.quick-toggle, +.quick-menu-toggle { + min-height: 42px; +}*/ diff --git a/extensions/current b/extensions/current new file mode 120000 index 0000000..7d37386 --- /dev/null +++ b/extensions/current @@ -0,0 +1 @@ +45 \ No newline at end of file diff --git a/extensions/vertical-workspaces/extension.js b/extensions/vertical-workspaces/extension.js deleted file mode 100644 index 0a22994..0000000 --- a/extensions/vertical-workspaces/extension.js +++ /dev/null @@ -1,664 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * extension.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; - -const { GLib, Shell, St } = imports.gi; - -const Main = imports.ui.main; - -const Util = imports.misc.util; -const Background = imports.ui.background; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; -const _Util = Me.imports.lib.util; - -const WindowSearchProvider = Me.imports.lib.windowSearchProvider; -const RecentFilesSearchProvider = Me.imports.lib.recentFilesSearchProvider; -const LayoutOverride = Me.imports.lib.layout; -const AppDisplayOverride = Me.imports.lib.appDisplay; -const WorkspaceThumbnailOverride = Me.imports.lib.workspaceThumbnail; -const WorkspaceOverride = Me.imports.lib.workspace; -const WorkspacesViewOverride = Me.imports.lib.workspacesView; -const WindowPreviewOverride = Me.imports.lib.windowPreview; -const IconGridOverride = Me.imports.lib.iconGrid; -const WorkspaceAnimationOverride = Me.imports.lib.workspaceAnimation; -const WindowManagerOverride = Me.imports.lib.windowManager; -const OverviewOverride = Me.imports.lib.overview; -const OverviewControlsOverride = Me.imports.lib.overviewControls; -const SwipeTrackerOverride = Me.imports.lib.swipeTracker; -const WorkspaceSwitcherPopupOverride = Me.imports.lib.workspaceSwitcherPopup; -const SearchOverride = Me.imports.lib.search; -const PanelOverride = Me.imports.lib.panel; -const DashOverride = Me.imports.lib.dash; -const WindowAttentionHandlerOverride = Me.imports.lib.windowAttentionHandler; -const AppFavoritesOverride = Me.imports.lib.appFavorites; -const MessageTrayOverride = Me.imports.lib.messageTray; -const OsdWindowOverride = Me.imports.lib.osdWindow; -const OverlayKey = Me.imports.lib.overlayKey; - -let opt; - -let _bgManagers; - -let _enabled; -let _resetExtensionIfEnabled; -let _prevDash; - -let _showingOverviewConId; -let _monitorsChangedSigId; -let _loadingProfileTimeoutId; -let _watchDockSigId; - -let _resetTimeoutId; - -let _enableTimeoutId = 0; -let _sessionLockActive = false; - - -function init() { - ExtensionUtils.initTranslations(); -} - -function enable() { - // globally readable flag for other extensions - global.verticalWorkspacesEnabled = true; - - _enableTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 400, - () => { - activateVShell(); - // unlock after modules update to avoid unnecessary appGrid rebuild - _sessionLockActive = Main.sessionMode.isLocked; - log(`${Me.metadata.name}: enabled`); - _enableTimeoutId = 0; - return GLib.SOURCE_REMOVE; - } - ); -} - -function disable() { - _sessionLockActive = Main.sessionMode.isLocked; - if (_enableTimeoutId) { - GLib.source_remove(_enableTimeoutId); - _enableTimeoutId = 0; - } else { - resetVShell(); - } - - global.verticalWorkspacesEnabled = undefined; - log(`${Me.metadata.name}: ${_sessionLockActive ? 'suspended' : 'disabled'}`); -} - -// ------------------------------------------------------------------------------------------ - -function activateVShell() { - _enabled = true; - - _bgManagers = []; - - Settings.opt = new Settings.Options(); - opt = Settings.opt; - - _updateSettings(); - - opt.connect('changed', _updateSettings); - - _updateOverrides(); - - _prevDash = {}; - const dash = Main.overview.dash; - _prevDash.dash = dash; - _prevDash.position = dash.position; - - _monitorsChangedSigId = Main.layoutManager.connect('monitors-changed', () => _resetExtension(2000)); - - // static bg animations conflict with startup animation - // enable it on first hiding from the overview and disconnect the signal - _showingOverviewConId = Main.overview.connect('showing', _onShowingOverview); - - // switch PageUp/PageDown workspace switcher shortcuts - _switchPageShortcuts(); - _setStaticBackground(); - - // fix for upstream bug - overview always shows workspace 1 instead of the active one after restart - Main.overview._overview.controls._workspaceAdjustment.set_value(global.workspace_manager.get_active_workspace_index()); - - // if Dash to Dock detected force enable "Fix for DtD" option - if (_Util.dashIsDashToDock()) { - opt.set('fixUbuntuDock', true); - _fixUbuntuDock(true); - } else { - _fixUbuntuDock(opt.get('fixUbuntuDock')); - } -} - -function resetVShell() { - _enabled = 0; - - _fixUbuntuDock(false); - - const reset = true; - _updateOverrides(reset); - - if (_monitorsChangedSigId) { - Main.layoutManager.disconnect(_monitorsChangedSigId); - _monitorsChangedSigId = 0; - } - - _prevDash = null; - - // switch PageUp/PageDown workspace switcher shortcuts - _switchPageShortcuts(); - - _setStaticBackground(reset); - - // remove any position offsets from dash and ws thumbnails - if (!_Util.dashNotDefault()) { - Main.overview.dash.translation_x = 0; - Main.overview.dash.translation_y = 0; - } - Main.overview._overview._controls._thumbnailsBox.translation_x = 0; - Main.overview._overview._controls._thumbnailsBox.translation_y = 0; - Main.overview._overview._controls._searchEntryBin.translation_y = 0; - - Main.overview._overview._controls.set_child_above_sibling(Main.overview._overview._controls._workspacesDisplay, null); - - if (_showingOverviewConId) { - Main.overview.disconnect(_showingOverviewConId); - _showingOverviewConId = 0; - } - - if (_loadingProfileTimeoutId) { - GLib.source_remove(_loadingProfileTimeoutId); - _loadingProfileTimeoutId = 0; - } - - St.Settings.get().slow_down_factor = 1; - - Main.overview.dash._background.set_style(''); - - opt.destroy(); - opt = null; -} - -function _updateOverrides(reset = false) { - WorkspacesViewOverride.update(reset); - WorkspaceThumbnailOverride.update(reset); - OverviewOverride.update(reset); - OverviewControlsOverride.update(reset); - - WorkspaceOverride.update(reset); - WindowPreviewOverride.update(reset); - WindowManagerOverride.update(reset); - - LayoutOverride.update(reset); - DashOverride.update(reset); - PanelOverride.update(reset); - - WorkspaceAnimationOverride.update(reset); - WorkspaceSwitcherPopupOverride.update(reset); - - SwipeTrackerOverride.update(reset); - - SearchOverride.update(reset); - WindowSearchProvider.update(reset); - RecentFilesSearchProvider.update(reset); - - // don't rebuild app grid on every screen lock - if (!_sessionLockActive) { - // IconGrid needs to be patched before AppDisplay - IconGridOverride.update(reset); - AppDisplayOverride.update(reset); - } - - WindowAttentionHandlerOverride.update(reset); - AppFavoritesOverride.update(reset); - MessageTrayOverride.update(reset); - OsdWindowOverride.update(reset); - OverlayKey.update(reset); -} - -function _onShowingOverview() { - // store pointer X coordinate for OVERVIEW_MODE 1 window spread - if mouse pointer is steady, don't spread - opt.showingPointerX = global.get_pointer()[0]; - - if (opt.FIX_UBUNTU_DOCK) { - // workaround for Ubuntu Dock breaking overview allocations after changing position - const dash = Main.overview.dash; - if (_prevDash.dash !== dash || _prevDash.position !== dash._position) - _resetExtensionIfEnabled(0); - } -} - -function _resetExtension(timeout = 200) { - if (_resetTimeoutId) - GLib.source_remove(_resetTimeoutId); - _resetTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - timeout, - () => { - if (!_enabled) - return GLib.SOURCE_REMOVE; - - const dash = Main.overview.dash; - if (!timeout && _prevDash.dash && dash !== _prevDash.dash) { // !timeout means DtD workaround callback - _prevDash.dash = dash; - log(`[${Me.metadata.name}]: Dash has been replaced, resetting extension...`); - resetVShell(); - activateVShell(); - } else if (timeout) { - log(`[${Me.metadata.name}]: resetting extension...`); - resetVShell(); - activateVShell(); - } - _resetTimeoutId = 0; - return GLib.SOURCE_REMOVE; - } - ); -} - -function _fixUbuntuDock(activate = true) { - // Workaround for Ubuntu Dock breaking overview allocations after changing monitor configuration and deactivating dock - if (_watchDockSigId) { - global.settings.disconnect(_watchDockSigId); - _watchDockSigId = 0; - } - - if (_resetTimeoutId) { - GLib.source_remove(_resetTimeoutId); - _resetTimeoutId = 0; - } - - _resetExtensionIfEnabled = () => {}; - - if (!activate) - return; - - _watchDockSigId = global.settings.connect('changed::enabled-extensions', () => _resetExtension()); - _resetExtensionIfEnabled = _resetExtension; -} - -function _updateSettings(settings, key) { - if (key?.includes('profile-data')) { - const index = key.replace('profile-data-', ''); - Main.notify(`${Me.metadata.name}`, `Profile ${index} has been saved`); - } - // avoid overload while loading profile - update only once - // delayed gsettings writes are processed alphabetically - if (key === 'aaa-loading-profile') { - Main.notify(`${Me.metadata.name}`, 'Profile has been loaded'); - if (_loadingProfileTimeoutId) - GLib.source_remove(_loadingProfileTimeoutId); - _loadingProfileTimeoutId = GLib.timeout_add(100, 0, () => { - _resetExtension(); - _loadingProfileTimeoutId = 0; - return GLib.SOURCE_REMOVE; - }); - } - if (_loadingProfileTimeoutId) - return; - - opt._updateSettings(); - - opt.WORKSPACE_MIN_SPACING = Main.overview._overview._controls._thumbnailsBox.get_theme_node().get_length('spacing'); - // update variables that cannot be processed within settings - const dash = Main.overview.dash; - if (_Util.dashIsDashToDock()) { - opt.DASH_POSITION = dash._position; - opt.DASH_TOP = opt.DASH_POSITION === 0; - opt.DASH_RIGHT = opt.DASH_POSITION === 1; - opt.DASH_BOTTOM = opt.DASH_POSITION === 2; - opt.DASH_LEFT = opt.DASH_POSITION === 3; - opt.DASH_VERTICAL = opt.DASH_LEFT || opt.DASH_RIGHT; - } - - opt.DASH_VISIBLE = opt.DASH_VISIBLE && !_Util.getEnabledExtensions('dash-to-panel@jderose9.github.com').length; - - opt.MAX_ICON_SIZE = opt.get('dashMaxIconSize', true); - if (opt.MAX_ICON_SIZE < 16) { - opt.MAX_ICON_SIZE = 64; - opt.set('dashMaxIconSize', 64); - } - - const monitorWidth = global.display.get_monitor_geometry(global.display.get_primary_monitor()).width; - if (monitorWidth < 1600) { - opt.APP_GRID_ICON_SIZE_DEFAULT = opt.APP_GRID_ACTIVE_PREVIEW ? 128 : 64; - opt.APP_GRID_FOLDER_ICON_SIZE_DEFAULT = 64; - } - - imports.ui.workspace.WINDOW_PREVIEW_MAXIMUM_SCALE = opt.OVERVIEW_MODE === 1 ? 0.1 : 0.95; - - if (!_Util.dashIsDashToDock()) { // DtD has its own opacity control - Main.overview.dash._background.opacity = Math.round(opt.get('dashBgOpacity', true) * 2.5); // conversion % to 0-255 - const radius = opt.get('dashBgRadius', true); - if (radius) { - let style; - switch (opt.DASH_POSITION) { - case 1: - style = `border-radius: ${radius}px 0 0 ${radius}px;`; - break; - case 3: - style = `border-radius: 0 ${radius}px ${radius}px 0;`; - break; - default: - style = `border-radius: ${radius}px;`; - } - Main.overview.dash._background.set_style(style); - } else { - Main.overview.dash._background.set_style(''); - } - } - - // adjust search entry style for OM2 - if (opt.OVERVIEW_MODE2) - Main.overview.searchEntry.add_style_class_name('search-entry-om2'); - else - Main.overview.searchEntry.remove_style_class_name('search-entry-om2'); - - Main.overview.searchEntry.visible = opt.SHOW_SEARCH_ENTRY; - St.Settings.get().slow_down_factor = opt.ANIMATION_TIME_FACTOR; - imports.ui.search.MAX_LIST_SEARCH_RESULTS_ROWS = opt.SEARCH_MAX_ROWS; - - opt.START_Y_OFFSET = (opt.get('panelModule', true) && opt.PANEL_OVERVIEW_ONLY && opt.PANEL_POSITION_TOP) || - // better to add unnecessary space than to have a panel overlapping other objects - _Util.getEnabledExtensions('hidetopbar@mathieu.bidon.ca').length - ? Main.panel.height - : 0; - - if (settings) - _applySettings(key); -} - -function _applySettings(key) { - if (key?.endsWith('-module')) { - _updateOverrides(); - return; - } - - _setStaticBackground(); - _updateOverviewTranslations(); - _switchPageShortcuts(); - - if (key?.includes('app-grid')) { - AppDisplayOverride.update(); - return; - } - - if (key?.includes('panel')) - PanelOverride.update(); - - if (key?.includes('dash') || key?.includes('search') || key?.includes('icon')) - DashOverride.update(); - - if (key?.includes('hot-corner') || key?.includes('dash')) - LayoutOverride.update(); - - switch (key) { - case 'fix-ubuntu-dock': - _fixUbuntuDock(opt.get('fixUbuntuDock', true)); - break; - case 'ws-thumbnails-position': - _updateOverrides(); - break; - case 'workspace-switcher-animation': - WorkspaceAnimationOverride.update(); - break; - case 'search-width-scale': - SearchOverride.update(); - break; - case 'favorites-notify': - AppFavoritesOverride.update(); - break; - case 'window-attention-mode': - WindowAttentionHandlerOverride.update(); - break; - case 'show-ws-preview-bg': - PanelOverride.update(); - break; - case 'notification-position': - MessageTrayOverride.update(); - break; - case 'osd-position': - OsdWindowOverride.update(); - break; - case 'overlay-key': - OverlayKey.update(); - } -} - -function _switchPageShortcuts() { - if (!opt.get('enablePageShortcuts', true)) - return; - - const vertical = global.workspaceManager.layout_rows === -1; - const schema = 'org.gnome.desktop.wm.keybindings'; - const settings = ExtensionUtils.getSettings(schema); - - const keyLeft = 'switch-to-workspace-left'; - const keyRight = 'switch-to-workspace-right'; - const keyUp = 'switch-to-workspace-up'; - const keyDown = 'switch-to-workspace-down'; - - const keyMoveLeft = 'move-to-workspace-left'; - const keyMoveRight = 'move-to-workspace-right'; - const keyMoveUp = 'move-to-workspace-up'; - const keyMoveDown = 'move-to-workspace-down'; - - const switchPrevSc = 'Page_Up'; - const switchNextSc = 'Page_Down'; - const movePrevSc = 'Page_Up'; - const moveNextSc = 'Page_Down'; - - let switchLeft = settings.get_strv(keyLeft); - let switchRight = settings.get_strv(keyRight); - let switchUp = settings.get_strv(keyUp); - let switchDown = settings.get_strv(keyDown); - - let moveLeft = settings.get_strv(keyMoveLeft); - let moveRight = settings.get_strv(keyMoveRight); - let moveUp = settings.get_strv(keyMoveUp); - let moveDown = settings.get_strv(keyMoveDown); - - if (vertical) { - if (switchLeft.includes(switchPrevSc)) - switchLeft.splice(switchLeft.indexOf(switchPrevSc), 1); - if (switchRight.includes(switchNextSc)) - switchRight.splice(switchRight.indexOf(switchNextSc), 1); - if (moveLeft.includes(movePrevSc)) - moveLeft.splice(moveLeft.indexOf(movePrevSc), 1); - if (moveRight.includes(moveNextSc)) - moveRight.splice(moveRight.indexOf(moveNextSc), 1); - - if (!switchUp.includes(switchPrevSc)) - switchUp.push(switchPrevSc); - if (!switchDown.includes(switchNextSc)) - switchDown.push(switchNextSc); - if (!moveUp.includes(movePrevSc)) - moveUp.push(movePrevSc); - if (!moveDown.includes(moveNextSc)) - moveDown.push(moveNextSc); - } else { - if (!switchLeft.includes(switchPrevSc)) - switchLeft.push(switchPrevSc); - if (!switchRight.includes(switchNextSc)) - switchRight.push(switchNextSc); - if (!moveLeft.includes(movePrevSc)) - moveLeft.push(movePrevSc); - if (!moveRight.includes(moveNextSc)) - moveRight.push(moveNextSc); - - if (switchUp.includes(switchPrevSc)) - switchUp.splice(switchUp.indexOf(switchPrevSc), 1); - if (switchDown.includes(switchNextSc)) - switchDown.splice(switchDown.indexOf(switchNextSc), 1); - if (moveUp.includes(movePrevSc)) - moveUp.splice(moveUp.indexOf(movePrevSc), 1); - if (moveDown.includes(moveNextSc)) - moveDown.splice(moveDown.indexOf(moveNextSc), 1); - } - - settings.set_strv(keyLeft, switchLeft); - settings.set_strv(keyRight, switchRight); - settings.set_strv(keyUp, switchUp); - settings.set_strv(keyDown, switchDown); - - settings.set_strv(keyMoveLeft, moveLeft); - settings.set_strv(keyMoveRight, moveRight); - settings.set_strv(keyMoveUp, moveUp); - settings.set_strv(keyMoveDown, moveDown); -} - - -function _shouldAnimateOverview() { - return !opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2; -} - -function _updateOverviewTranslations(dash = null, tmbBox = null, searchEntryBin = null) { - dash = dash ?? Main.overview.dash; - tmbBox = tmbBox ?? Main.overview._overview._controls._thumbnailsBox; - searchEntryBin = searchEntryBin ?? Main.overview._overview._controls._searchEntryBin; - - if (!_shouldAnimateOverview()) { - tmbBox.translation_x = 0; - tmbBox.translation_y = 0; - dash.translation_x = 0; - dash.translation_y = 0; - searchEntryBin.translation_x = 0; - searchEntryBin.translation_y = 0; - return; - } - - const [tmbTranslationX, tmbTranslationY, dashTranslationX, dashTranslationY, searchTranslationY] = _Util.getOverviewTranslations(opt, dash, tmbBox, searchEntryBin); - tmbBox.translation_x = tmbTranslationX; - tmbBox.translation_y = tmbTranslationY; - if (!_Util.dashNotDefault()) { // only if dash is not dash to dock - dash.translation_x = dashTranslationX; - dash.translation_y = dashTranslationY; - } - searchEntryBin.translation_y = searchTranslationY; -} - -function _setStaticBackground(reset = false) { - _bgManagers.forEach(bg => { - Main.overview._overview._controls._stateAdjustment.disconnect(bg._fadeSignal); - bg.destroy(); - }); - - _bgManagers = []; - // if (!SHOW_BG_IN_OVERVIEW && !SHOW_WS_PREVIEW_BG) the background is used for static transition from wallpaper to empty bg in the overview - if (reset || (!opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG)) - return; - - for (const monitor of Main.layoutManager.monitors) { - const bgManager = new Background.BackgroundManager({ - monitorIndex: monitor.index, - container: Main.layoutManager.overviewGroup, - vignette: true, - }); - - bgManager.backgroundActor.content.vignette_sharpness = 0; - bgManager.backgroundActor.content.brightness = 1; - - - bgManager._fadeSignal = Main.overview._overview._controls._stateAdjustment.connect('notify::value', v => { - _updateStaticBackground(bgManager, v.value, v); - }); - - if (monitor.index === global.display.get_primary_monitor()) { - bgManager._primary = true; - _bgManagers.unshift(bgManager); // primary monitor first - } else { - bgManager._primary = false; - _bgManagers.push(bgManager); - } - } -} - -function _updateStaticBackground(bgManager, stateValue, stateAdjustment = null) { - if (!opt.SHOW_BG_IN_OVERVIEW && !opt.SHOW_WS_PREVIEW_BG) { - // if no bg shown in the overview, fade out the wallpaper - if (!(opt.OVERVIEW_MODE2 && opt.WORKSPACE_MODE && stateAdjustment?.getStateTransitionParams().finalState === 1)) - bgManager.backgroundActor.opacity = Util.lerp(255, 0, Math.min(stateValue, 1)); - } else { - let VIGNETTE, BRIGHTNESS, bgValue; - if (opt.OVERVIEW_MODE2 && stateValue <= 1 && !opt.WORKSPACE_MODE) { - VIGNETTE = 0; - BRIGHTNESS = 1; - bgValue = stateValue; - } else { - VIGNETTE = 0.2; - BRIGHTNESS = opt.OVERVIEW_BG_BRIGHTNESS; - if (opt.OVERVIEW_MODE2 && stateValue > 1 && !opt.WORKSPACE_MODE) - bgValue = stateValue - 1; - else - bgValue = stateValue; - } - - let blurEffect = bgManager.backgroundActor.get_effect('blur'); - if (!blurEffect) { - blurEffect = new Shell.BlurEffect({ - brightness: 1, - sigma: 0, - mode: Shell.BlurMode.ACTOR, - }); - bgManager.backgroundActor.add_effect_with_name('blur', blurEffect); - } - - bgManager.backgroundActor.content.vignette_sharpness = VIGNETTE; - bgManager.backgroundActor.content.brightness = BRIGHTNESS; - - let vignetteInit, brightnessInit;// , sigmaInit; - if (opt.SHOW_BG_IN_OVERVIEW && opt.SHOW_WS_PREVIEW_BG) { - vignetteInit = VIGNETTE; - brightnessInit = BRIGHTNESS; - // sigmaInit = opt.OVERVIEW_BG_BLUR_SIGMA; - } else { - vignetteInit = 0; - brightnessInit = 1; - // sigmaInit = 0; - } - - if (opt.OVERVIEW_MODE2) { - bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, bgValue); - bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, bgValue); - } else { - bgManager.backgroundActor.content.vignette_sharpness = Util.lerp(vignetteInit, VIGNETTE, Math.min(stateValue, 1)); - bgManager.backgroundActor.content.brightness = Util.lerp(brightnessInit, BRIGHTNESS, Math.min(stateValue, 1)); - } - - if (opt.OVERVIEW_BG_BLUR_SIGMA || opt.APP_GRID_BG_BLUR_SIGMA) { - // reduce number of steps of blur transition to improve performance - const step = opt.SMOOTH_BLUR_TRANSITIONS ? 0.05 : 0.2; - const searchActive = Main.overview._overview.controls._searchController.searchActive; - const progress = stateValue - (stateValue % step); - if (opt.SHOW_WS_PREVIEW_BG && stateValue < 1 && !searchActive) { // no need to animate transition, unless appGrid state is involved, static bg is covered by the ws preview bg - if (blurEffect.sigma !== opt.OVERVIEW_BG_BLUR_SIGMA) - blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; - } else if (stateValue < 1 && !searchActive) { - const sigma = Math.round(Util.lerp(0, opt.OVERVIEW_BG_BLUR_SIGMA, progress)); - if (sigma !== blurEffect.sigma) - blurEffect.sigma = sigma; - } else if ((stateValue > 1 && bgManager._primary) || searchActive) { - const sigma = Math.round(Util.lerp(opt.OVERVIEW_BG_BLUR_SIGMA, opt.APP_GRID_BG_BLUR_SIGMA, progress % 1)); - if (sigma !== blurEffect.sigma) - blurEffect.sigma = sigma; - } else if (stateValue === 1) { - blurEffect.sigma = opt.OVERVIEW_BG_BLUR_SIGMA; - } else if (stateValue === 0) { - blurEffect.sigma = 0; - } - } - } -} - diff --git a/extensions/vertical-workspaces/lib/appFavorites.js b/extensions/vertical-workspaces/lib/appFavorites.js deleted file mode 100644 index 50ebce9..0000000 --- a/extensions/vertical-workspaces/lib/appFavorites.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * appFavorites.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; - -const { Shell } = imports.gi; -const AppFavorites = imports.ui.appFavorites; -const Main = imports.ui.main; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -let opt; -let _overrides; -let _firstRun = true; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('appFavoritesModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - - // if notifications are enabled no override is needed - if (reset || opt.SHOW_FAV_NOTIFICATION) { - _overrides = null; - opt = null; - return; - } - - _overrides = new _Util.Overrides(); - - // AppFavorites.AppFavorites is const, first access returns undefined - const dummy = AppFavorites.AppFavorites; - _overrides.addOverride('AppFavorites', AppFavorites.AppFavorites.prototype, AppFavoritesCommon); -} - -const AppFavoritesCommon = { - addFavoriteAtPos(appId, pos) { - this._addFavorite(appId, pos); - }, - - removeFavorite(appId) { - this._removeFavorite(appId); - }, -}; diff --git a/extensions/vertical-workspaces/lib/dash.js b/extensions/vertical-workspaces/lib/dash.js deleted file mode 100644 index bf832bd..0000000 --- a/extensions/vertical-workspaces/lib/dash.js +++ /dev/null @@ -1,1186 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * dash.js - * - * @author GdH - * @copyright 2022-2023 - * @license GPL-3.0 - * modified dash module of https://github.com/RensAlthuis/vertical-overview extension - */ - -const { Clutter, GObject, St, Shell, Meta } = imports.gi; -const AppDisplay = imports.ui.appDisplay; -const AppFavorites = imports.ui.appFavorites; -const DND = imports.ui.dnd; -const IconGrid = imports.ui.iconGrid; -const Main = imports.ui.main; -const Overview = imports.ui.overview; -const Dash = imports.ui.dash; -const PopupMenu = imports.ui.popupMenu; -const { AppMenu } = imports.ui.appMenu; -const BoxPointer = imports.ui.boxpointer; -const AltTab = imports.ui.altTab; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const Util = Me.imports.lib.util; -const _ = Me.imports.lib.settings._; - -const shellVersion = Util.shellVersion; -let _origWorkId; -let _newWorkId; -let _showAppsIconBtnPressId; - -// added values to achieve a better ability to scale down according to available space -var BaseIconSizes = [16, 24, 32, 40, 44, 48, 56, 64, 72, 80, 96, 112, 128]; - -const RecentFilesSearchProviderPrefix = Me.imports.lib.recentFilesSearchProvider.prefix; -const WindowSearchProviderPrefix = Me.imports.lib.windowSearchProvider.prefix; - -let _overrides; -let opt; -let _firstRun = true; - -const DASH_ITEM_LABEL_SHOW_TIME = 150; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('dashModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - - const dash = Main.overview._overview._controls.layoutManager._dash; - - setToHorizontal(); - - dash.remove_style_class_name('vertical'); - dash.remove_style_class_name('vertical-left'); - dash.remove_style_class_name('vertical-right'); - - if (reset) { - _moveDashAppGridIcon(reset); - _connectShowAppsIcon(reset); - _updateSearchWindowsIcon(false); - _updateRecentFilesIcon(false); - dash.visible = true; - dash._background.opacity = 255; - dash._background.remove_style_class_name('v-shell-dash-background'); - _overrides = null; - opt = null; - return; - } - - _overrides = new Util.Overrides(); - - _overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerCommon); - _overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommon); - _overrides.addOverride('AppIcon', AppDisplay.AppIcon.prototype, AppIconCommon); - _overrides.addOverride('DashIcon', Dash.DashIcon.prototype, DashIconCommon); - - if (opt.DASH_VERTICAL) { - _overrides.addOverride('Dash', Dash.Dash.prototype, DashOverride); - setToVertical(); - dash.add_style_class_name('vertical'); - - if (!_newWorkId) { - _origWorkId = dash._workId; - dash._workId = Main.initializeDeferredWork(dash._box, dash._redisplay.bind(dash)); - _newWorkId = dash._workId; - } else { - dash._workId = _newWorkId; - } - } else { - setToHorizontal(); - if (_origWorkId) - dash._workId = _origWorkId; - } - - _updateSearchWindowsIcon(); - _updateRecentFilesIcon(); - _moveDashAppGridIcon(); - _connectShowAppsIcon(); - - if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) - dash._showWindowsIconClickedId = dash._showWindowsIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(WindowSearchProviderPrefix)); - - if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) - dash._recentFilesIconClickedId = dash._recentFilesIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(RecentFilesSearchProviderPrefix)); - - dash.visible = opt.DASH_VISIBLE; - dash._background.add_style_class_name('v-shell-dash-background'); - dash._redisplay(); -} - -function setToVertical() { - let dash = Main.overview._overview._controls.layoutManager._dash; - - dash._box.layout_manager.orientation = Clutter.Orientation.VERTICAL; - dash._dashContainer.layout_manager.orientation = Clutter.Orientation.VERTICAL; - dash._dashContainer.y_expand = false; - dash._dashContainer.x_expand = true; - dash.x_align = Clutter.ActorAlign.START; - dash.y_align = Clutter.ActorAlign.CENTER; - - let sizerBox = dash._background.get_children()[0]; - sizerBox.clear_constraints(); - sizerBox.add_constraint(new Clutter.BindConstraint({ - source: dash._showAppsIcon.icon, - coordinate: Clutter.BindCoordinate.WIDTH, - })); - sizerBox.add_constraint(new Clutter.BindConstraint({ - source: dash._dashContainer, - coordinate: Clutter.BindCoordinate.HEIGHT, - })); - dash._box.remove_all_children(); - dash._separator = null; - dash._queueRedisplay(); - dash._adjustIconSize(); - - dash.add_style_class_name(opt.DASH_LEFT ? 'vertical-left' : 'vertical-right'); -} - -function setToHorizontal() { - let dash = Main.overview._overview._controls.layoutManager._dash; - if (_origWorkId) - dash._workId = _origWorkId; // pretty sure this is a leak, but there no provided way to disconnect these... - dash._box.layout_manager.orientation = Clutter.Orientation.HORIZONTAL; - dash._dashContainer.layout_manager.orientation = Clutter.Orientation.HORIZONTAL; - dash._dashContainer.y_expand = true; - dash._dashContainer.x_expand = false; - dash.x_align = Clutter.ActorAlign.CENTER; - dash.y_align = 0; - - let sizerBox = dash._background.get_children()[0]; - sizerBox.clear_constraints(); - sizerBox.add_constraint(new Clutter.BindConstraint({ - source: dash._showAppsIcon.icon, - coordinate: Clutter.BindCoordinate.HEIGHT, - })); - sizerBox.add_constraint(new Clutter.BindConstraint({ - source: dash._dashContainer, - coordinate: Clutter.BindCoordinate.WIDTH, - })); - - dash._box.remove_all_children(); - dash._separator = null; - dash._queueRedisplay(); - dash._adjustIconSize(); -} - -function _moveDashAppGridIcon(reset = false) { - // move dash app grid icon to the front - const dash = Main.overview._overview._controls.layoutManager._dash; - - const appIconPosition = opt.get('showAppsIconPosition', true); - dash._showAppsIcon.remove_style_class_name('show-apps-icon-vertical-hide'); - dash._showAppsIcon.remove_style_class_name('show-apps-icon-horizontal-hide'); - dash._showAppsIcon.opacity = 255; - if (!reset && appIconPosition === 0) // 0 - start - dash._dashContainer.set_child_at_index(dash._showAppsIcon, 0); - if (reset || appIconPosition === 1) { // 1 - end - const index = dash._dashContainer.get_children().length - 1; - dash._dashContainer.set_child_at_index(dash._showAppsIcon, index); - } - if (!reset && appIconPosition === 2) { // 2 - hide - const style = opt.DASH_VERTICAL ? 'show-apps-icon-vertical-hide' : 'show-apps-icon-horizontal-hide'; - dash._showAppsIcon.add_style_class_name(style); - // for some reason even if the icon height in vertical mode should be set to 0 by the style, it stays visible in full size returning height 1px - dash._showAppsIcon.opacity = 0; - } -} - -function _connectShowAppsIcon(reset = false) { - if (!reset) { - if (_showAppsIconBtnPressId || Util.dashIsDashToDock()) { - // button is already connected || dash is Dash to Dock - return; - } - - Main.overview.dash._showAppsIcon.reactive = true; - _showAppsIconBtnPressId = Main.overview.dash._showAppsIcon.connect('button-press-event', (actor, event) => { - const button = event.get_button(); - if (button === Clutter.BUTTON_MIDDLE) - Util.openPreferences(); - else if (button === Clutter.BUTTON_SECONDARY) - Util.activateSearchProvider(WindowSearchProviderPrefix); - else - return Clutter.EVENT_PROPAGATE; - return Clutter.EVENT_STOP; - }); - } else if (_showAppsIconBtnPressId) { - Main.overview.dash._showAppsIcon.disconnect(_showAppsIconBtnPressId); - _showAppsIconBtnPressId = 0; - Main.overview.dash._showAppsIcon.reactive = false; - } -} - -const DashOverride = { - handleDragOver(source, actor, _x, y, _time) { - let app = Dash.getAppFromSource(source); - - // Don't allow favoriting of transient apps - if (app === null || app.is_window_backed()) - return DND.DragMotionResult.NO_DROP; - - if (!global.settings.is_writable('favorite-apps')) - return DND.DragMotionResult.NO_DROP; - - let favorites = AppFavorites.getAppFavorites().getFavorites(); - let numFavorites = favorites.length; - - let favPos = favorites.indexOf(app); - - let children = this._box.get_children(); - let numChildren = children.length; - let boxHeight = this._box.height; - - // Keep the placeholder out of the index calculation; assuming that - // the remove target has the same size as "normal" items, we don't - // need to do the same adjustment there. - if (this._dragPlaceholder) { - boxHeight -= this._dragPlaceholder.height; - numChildren--; - } - - // Same with the separator - if (this._separator) { - boxHeight -= this._separator.height; - numChildren--; - } - - let pos; - if (!this._emptyDropTarget) - pos = Math.floor(y * numChildren / boxHeight); - else - pos = 0; // always insert at the top when dash is empty - - // Put the placeholder after the last favorite if we are not - // in the favorites zone - if (pos > numFavorites) - pos = numFavorites; - - if (pos !== this._dragPlaceholderPos && this._animatingPlaceholdersCount === 0) { - this._dragPlaceholderPos = pos; - - // Don't allow positioning before or after self - if (favPos !== -1 && (pos === favPos || pos === favPos + 1)) { - this._clearDragPlaceholder(); - return DND.DragMotionResult.CONTINUE; - } - - // If the placeholder already exists, we just move - // it, but if we are adding it, expand its size in - // an animation - let fadeIn; - if (this._dragPlaceholder) { - this._dragPlaceholder.destroy(); - fadeIn = false; - } else { - fadeIn = true; - } - - this._dragPlaceholder = new Dash.DragPlaceholderItem(); - this._dragPlaceholder.child.set_width(this.iconSize / 2); - this._dragPlaceholder.child.set_height(this.iconSize); - this._box.insert_child_at_index(this._dragPlaceholder, - this._dragPlaceholderPos); - this._dragPlaceholder.show(fadeIn); - } - - if (!this._dragPlaceholder) - return DND.DragMotionResult.NO_DROP; - - let srcIsFavorite = favPos !== -1; - - if (srcIsFavorite) - return DND.DragMotionResult.MOVE_DROP; - - return DND.DragMotionResult.COPY_DROP; - }, - - _redisplay() { - let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); - - let running = this._appSystem.get_running(); - - let children = this._box.get_children().filter(actor => { - return actor.child && - actor.child._delegate && - actor.child._delegate.app; - }); - // Apps currently in the dash - let oldApps = children.map(actor => actor.child._delegate.app); - // Apps supposed to be in the dash - let newApps = []; - - for (let id in favorites) - newApps.push(favorites[id]); - - for (let i = 0; i < running.length; i++) { - let app = running[i]; - if (app.get_id() in favorites) - continue; - newApps.push(app); - } - - // Figure out the actual changes to the list of items; we iterate - // over both the list of items currently in the dash and the list - // of items expected there, and collect additions and removals. - // Moves are both an addition and a removal, where the order of - // the operations depends on whether we encounter the position - // where the item has been added first or the one from where it - // was removed. - // There is an assumption that only one item is moved at a given - // time; when moving several items at once, everything will still - // end up at the right position, but there might be additional - // additions/removals (e.g. it might remove all the launchers - // and add them back in the new order even if a smaller set of - // additions and removals is possible). - // If above assumptions turns out to be a problem, we might need - // to use a more sophisticated algorithm, e.g. Longest Common - // Subsequence as used by diff. - let addedItems = []; - let removedActors = []; - - let newIndex = 0; - let oldIndex = 0; - while (newIndex < newApps.length || oldIndex < oldApps.length) { - let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null; - let newApp = newApps.length > newIndex ? newApps[newIndex] : null; - - // No change at oldIndex/newIndex - if (oldApp === newApp) { - oldIndex++; - newIndex++; - continue; - } - - // App removed at oldIndex - if (oldApp && !newApps.includes(oldApp)) { - removedActors.push(children[oldIndex]); - oldIndex++; - continue; - } - - // App added at newIndex - if (newApp && !oldApps.includes(newApp)) { - addedItems.push({ - app: newApp, - item: this._createAppItem(newApp), - pos: newIndex, - }); - newIndex++; - continue; - } - - // App moved - let nextApp = newApps.length > newIndex + 1 - ? newApps[newIndex + 1] : null; - let insertHere = nextApp && nextApp === oldApp; - let alreadyRemoved = removedActors.reduce((result, actor) => { - let removedApp = actor.child._delegate.app; - return result || removedApp === newApp; - }, false); - - if (insertHere || alreadyRemoved) { - let newItem = this._createAppItem(newApp); - addedItems.push({ - app: newApp, - item: newItem, - pos: newIndex + removedActors.length, - }); - newIndex++; - } else { - removedActors.push(children[oldIndex]); - oldIndex++; - } - } - - for (let i = 0; i < addedItems.length; i++) { - this._box.insert_child_at_index(addedItems[i].item, - addedItems[i].pos); - } - - for (let i = 0; i < removedActors.length; i++) { - let item = removedActors[i]; - - // Don't animate item removal when the overview is transitioning - // or hidden - if (Main.overview.visible && !Main.overview.animationInProgress) - item.animateOutAndDestroy(); - else - item.destroy(); - } - - this._adjustIconSize(); - - // Skip animations on first run when adding the initial set - // of items, to avoid all items zooming in at once - - let animate = this._shownInitially && Main.overview.visible && - !Main.overview.animationInProgress; - - if (!this._shownInitially) - this._shownInitially = true; - - for (let i = 0; i < addedItems.length; i++) - addedItems[i].item.show(animate); - - // Update separator - const nFavorites = Object.keys(favorites).length; - const nIcons = children.length + addedItems.length - removedActors.length; - if (nFavorites > 0 && nFavorites < nIcons) { - // destroy the horizontal separator if it exists. - // this is incredibly janky, but I can't think of a better way atm. - if (this._separator && this._separator.height !== 1) { - this._separator.destroy(); - this._separator = null; - } - - if (!this._separator) { - this._separator = new St.Widget({ - style_class: 'dash-separator', - x_align: Clutter.ActorAlign.CENTER, - y_align: Clutter.ActorAlign.CENTER, - width: this.iconSize, - height: 1, - }); - this._box.add_child(this._separator); - } - - // FIXME: separator placement is broken (also in original dash) - let pos = nFavorites; - if (this._dragPlaceholder) - pos++; - this._box.set_child_at_index(this._separator, pos); - } else if (this._separator) { - this._separator.destroy(); - this._separator = null; - } - // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 - // Without it, StBoxLayout may use a stale size cache - this._box.queue_relayout(); - }, - - _createAppItem(app) { - let appIcon = new Dash.DashIcon(app); - - let indicator = appIcon._dot; - indicator.x_align = opt.DASH_LEFT ? Clutter.ActorAlign.START : Clutter.ActorAlign.END; - indicator.y_align = Clutter.ActorAlign.CENTER; - - appIcon.connect('menu-state-changed', - (o, opened) => { - this._itemMenuStateChanged(item, opened); - }); - - let item = new Dash.DashItemContainer(); - item.setChild(appIcon); - - // Override default AppIcon label_actor, now the - // accessible_name is set at DashItemContainer.setLabelText - appIcon.label_actor = null; - item.setLabelText(app.get_name()); - - appIcon.icon.setIconSize(this.iconSize); - this._hookUpLabel(item, appIcon); - - return item; - }, -}; - -const DashItemContainerCommon = { - // move labels according dash position - showLabel() { - if (!this._labelText) - return; - - this.label.set_text(this._labelText); - this.label.opacity = 0; - this.label.show(); - - let [stageX, stageY] = this.get_transformed_position(); - - const itemWidth = this.allocation.get_width(); - const itemHeight = this.allocation.get_height(); - - const labelWidth = this.label.get_width(); - const labelHeight = this.label.get_height(); - let xOffset = Math.floor((itemWidth - labelWidth) / 2); - let x = Math.clamp(stageX + xOffset, 0, global.stage.width - labelWidth); - - let node = this.label.get_theme_node(); - let y; - - if (opt.DASH_TOP) { - const yOffset = itemHeight - labelHeight + 3 * node.get_length('-y-offset'); - y = stageY + yOffset; - } else if (opt.DASH_BOTTOM) { - const yOffset = node.get_length('-y-offset'); - y = stageY - this.label.height - yOffset; - } else if (opt.DASH_RIGHT) { - const yOffset = Math.floor((itemHeight - labelHeight) / 2); - xOffset = 4; - - x = stageX - xOffset - this.label.width; - y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); - } else if (opt.DASH_LEFT) { - const yOffset = Math.floor((itemHeight - labelHeight) / 2); - xOffset = 4; - - x = stageX + this.width + xOffset; - y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight); - } - - this.label.set_position(x, y); - this.label.ease({ - opacity: 255, - duration: DASH_ITEM_LABEL_SHOW_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - - this.label.set_position(x, y); - this.label.ease({ - opacity: 255, - duration: DASH_ITEM_LABEL_SHOW_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - }, -}; - -const DashCommon = { - // use custom BaseIconSizes and add support for custom icons - _adjustIconSize() { - // if a user launches multiple apps at once, this function may be called again before the previous call has finished - // as a result, new icons will not reach their full size, or will be missing, if adding a new icon and changing the dash size due to lack of space at the same time - if (this._adjustingInProgress) - return; - - // For the icon size, we only consider children which are "proper" - // icons (i.e. ignoring drag placeholders) and which are not - // animating out (which means they will be destroyed at the end of - // the animation) - let iconChildren = this._box.get_children().filter(actor => { - return actor.child && - actor.child._delegate && - actor.child._delegate.icon && - !actor.animatingOut; - }); - - // add new custom icons to the list - if (this._showAppsIcon.visible) - iconChildren.push(this._showAppsIcon); - - if (this._showWindowsIcon) - iconChildren.push(this._showWindowsIcon); - - if (this._recentFilesIcon) - iconChildren.push(this._recentFilesIcon); - - - if (!iconChildren.length) - return; - - if (this._maxWidth === -1 || this._maxHeight === -1) - return; - - const dashHorizontal = !opt.DASH_VERTICAL; - - const themeNode = this.get_theme_node(); - const maxAllocation = new Clutter.ActorBox({ - x1: 0, - y1: 0, - x2: dashHorizontal ? this._maxWidth : 42, // not whatever - y2: dashHorizontal ? 42 : this._maxHeight, - }); - - let maxContent = themeNode.get_content_box(maxAllocation); - - let spacing = themeNode.get_length('spacing'); - - let firstButton = iconChildren[0].child; - let firstIcon = firstButton._delegate.icon; - - if (!firstIcon.icon) - return; - - // Enforce valid spacings during the size request - firstIcon.icon.ensure_style(); - const [, , iconWidth, iconHeight] = firstIcon.icon.get_preferred_size(); - const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size(); - let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; - - let availWidth, availHeight, maxIconSize; - if (dashHorizontal) { - availWidth = maxContent.x2 - maxContent.x1; - // Subtract icon padding and box spacing from the available width - availWidth -= iconChildren.length * (buttonWidth - iconWidth) + - (iconChildren.length - 1) * spacing + - 2 * this._background.get_theme_node().get_horizontal_padding(); - - availHeight = this._maxHeight; - availHeight -= this.margin_top + this.margin_bottom; - availHeight -= this._background.get_theme_node().get_vertical_padding(); - availHeight -= themeNode.get_vertical_padding(); - availHeight -= buttonHeight - iconHeight; - - maxIconSize = Math.min(availWidth / iconChildren.length, availHeight, opt.MAX_ICON_SIZE * scaleFactor); - } else { - availWidth = this._maxWidth; - availWidth -= this._background.get_theme_node().get_horizontal_padding(); - availWidth -= themeNode.get_horizontal_padding(); - availWidth -= buttonWidth - iconWidth; - - availHeight = maxContent.y2 - maxContent.y1; - availHeight -= iconChildren.length * (buttonHeight - iconHeight) + - (iconChildren.length - 1) * spacing + - 2 * this._background.get_theme_node().get_vertical_padding(); - - maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE * scaleFactor); - } - - let iconSizes = BaseIconSizes.map(s => s * scaleFactor); - - let newIconSize = BaseIconSizes[0]; - for (let i = 0; i < iconSizes.length; i++) { - if (iconSizes[i] <= maxIconSize) - newIconSize = BaseIconSizes[i]; - } - - if (newIconSize === this.iconSize) - return; - - // set the in-progress state here after all the possible cancels - this._adjustingInProgress = true; - - let oldIconSize = this.iconSize; - this.iconSize = newIconSize; - this.emit('icon-size-changed'); - - let scale = oldIconSize / newIconSize; - for (let i = 0; i < iconChildren.length; i++) { - let icon = iconChildren[i].child._delegate.icon; - - // Set the new size immediately, to keep the icons' sizes - // in sync with this.iconSize - icon.setIconSize(this.iconSize); - - // Don't animate the icon size change when the overview - // is transitioning, not visible or when initially filling - // the dash - if (!Main.overview.visible || Main.overview.animationInProgress || - !this._shownInitially) - continue; - - let [targetWidth, targetHeight] = icon.icon.get_size(); - - // Scale the icon's texture to the previous size and - // tween to the new size - icon.icon.set_size(icon.icon.width * scale, - icon.icon.height * scale); - - icon.icon.ease({ - width: targetWidth, - height: targetHeight, - duration: Dash.DASH_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } - - if (this._separator) { - this._separator.ease({ - width: dashHorizontal ? 1 : this.iconSize, - height: dashHorizontal ? this.iconSize : 1, - duration: Dash.DASH_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } - - this._adjustingInProgress = false; - }, -}; - -const DashIconCommon = { - after__init() { - if (opt.DASH_ICON_SCROLL) { - this._scrollConId = this.connect('scroll-event', _onScrollEvent.bind(this)); - this._leaveConId = this.connect('leave-event', _onLeaveEvent.bind(this)); - } - }, -}; - -function _onScrollEvent(source, event) { - if ((this.app && !opt.DASH_ICON_SCROLL) || (this._isSearchWindowsIcon && !opt.SEARCH_WINDOWS_ICON_SCROLL)) { - if (this._scrollConId) - this.disconnect(this._scrollConId); - if (this._leaveConId) - this.disconnect(this._leaveConId); - return Clutter.EVENT_PROPAGATE; - } - - let direction = Util.getScrollDirection(event); - if (direction === Clutter.ScrollDirection.UP) - direction = 1; - else if (direction === Clutter.ScrollDirection.DOWN) - direction = -1; - else - return Clutter.EVENT_STOP; - - // avoid uncontrollable switching if smooth scroll wheel or trackpad is used - if (this._lastScroll && Date.now() - this._lastScroll < 160) - return Clutter.EVENT_STOP; - - this._lastScroll = Date.now(); - - _switchWindow.bind(this)(direction); - return Clutter.EVENT_STOP; -} - -function _onLeaveEvent() { - if (!this._selectedMetaWin || this.has_pointer || this.toggleButton?.has_pointer) - return; - - this._selectedPreview._activateSelected = false; - this._selectedMetaWin = null; - this._scrolledWindows = null; - _showWindowPreview.bind(this)(null); -} - -function _switchWindow(direction) { - if (!this._scrolledWindows) { - // source is app icon - if (this.app) { - this._scrolledWindows = this.app.get_windows(); - const wsList = []; - this._scrolledWindows.forEach(w => { - const ws = w.get_workspace(); - if (!wsList.includes(ws)) - wsList.push(ws); - }); - // sort windows by workspaces in MRU order - this._scrolledWindows.sort((a, b) => wsList.indexOf(a.get_workspace()) > wsList.indexOf(b.get_workspace())); - // source is Search Windows icon - } else if (this._isSearchWindowsIcon) { - if (opt.SEARCH_WINDOWS_ICON_SCROLL === 1) // all windows - this._scrolledWindows = AltTab.getWindows(null); - else - this._scrolledWindows = AltTab.getWindows(global.workspace_manager.get_active_workspace()); - } - } - - let windows = this._scrolledWindows; - - if (!windows.length) - return; - - // if window selection is in the process, the previewed window must be the current one - let currentWin = this._selectedMetaWin ? this._selectedMetaWin : windows[0]; - - const currentIdx = windows.indexOf(currentWin); - let targetIdx = currentIdx + direction; - - if (targetIdx > windows.length - 1) - targetIdx = 0; - else if (targetIdx < 0) - targetIdx = windows.length - 1; - - const metaWin = windows[targetIdx]; - _showWindowPreview.bind(this)(metaWin); - this._selectedMetaWin = metaWin; -} - -function _showWindowPreview(metaWin) { - const views = Main.overview._overview.controls._workspacesDisplay._workspacesViews; - const viewsIter = [views[0]]; - // secondary monitors use different structure - views.forEach(v => { - if (v._workspacesView) - viewsIter.push(v._workspacesView); - }); - - viewsIter.forEach(view => { - // if workspaces are on primary monitor only - if (!view || !view._workspaces) - return; - - view._workspaces.forEach(ws => { - ws._windows.forEach(windowPreview => { - // metaWin === null resets opacity - let opacity = metaWin ? 50 : 255; - windowPreview._activateSelected = false; - - // minimized windows are invisible if windows are not exposed (WORKSPACE_MODE === 0) - if (!windowPreview.opacity) - windowPreview.opacity = 255; - - // app windows set to lower opacity, so they can be recognized - if (this._scrolledWindows && this._scrolledWindows.includes(windowPreview.metaWindow)) { - if (opt.DASH_ICON_SCROLL === 2) - opacity = 254; - } - if (windowPreview.metaWindow === metaWin) { - if (metaWin && metaWin.get_workspace() !== global.workspace_manager.get_active_workspace()) - Main.wm.actionMoveWorkspace(metaWin.get_workspace()); - - windowPreview.get_parent().set_child_above_sibling(windowPreview, null); - - opacity = 255; - this._selectedPreview = windowPreview; - windowPreview._activateSelected = true; - } - - // if windows are exposed, highlight selected using opacity - if ((opt.OVERVIEW_MODE && opt.WORKSPACE_MODE) || !opt.OVERVIEW_MODE) { - if (metaWin && opacity === 255) - windowPreview.showOverlay(true); - else - windowPreview.hideOverlay(true); - windowPreview.ease({ - duration: 200, - opacity, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } - }); - }); - }); -} - -const AppIconCommon = { - activate(button) { - const event = Clutter.get_current_event(); - const state = event ? event.get_state() : 0; - const isMiddleButton = button && button === Clutter.BUTTON_MIDDLE; - const isCtrlPressed = Util.isCtrlPressed(state); - const isShiftPressed = Util.isShiftPressed(state); - const openNewWindow = (this.app.can_open_new_window() && - this.app.state === Shell.AppState.RUNNING && - (isCtrlPressed || isMiddleButton) && !opt.DASH_CLICK_ACTION === 2) || - (opt.DASH_CLICK_ACTION === 2 && !this._selectedMetaWin && !isMiddleButton); - - const currentWS = global.workspace_manager.get_active_workspace(); - const appRecentWorkspace = _getAppRecentWorkspace(this.app); - // this feature shouldn't affect search results, dash icons don't have labels, so we use them as a condition - const showWidowsBeforeActivation = opt.DASH_CLICK_ACTION === 1 && !this.icon.label; - - let targetWindowOnCurrentWs = false; - if (opt.DASH_FOLLOW_RECENT_WIN) { - targetWindowOnCurrentWs = appRecentWorkspace === currentWS; - } else { - this.app.get_windows().forEach( - w => { - targetWindowOnCurrentWs = targetWindowOnCurrentWs || (w.get_workspace() === currentWS); - } - ); - } - - if ((this.app.state === Shell.AppState.STOPPED || openNewWindow) && !isShiftPressed) - this.animateLaunch(); - - if (openNewWindow) { - this.app.open_new_window(-1); - // if DASH_CLICK_ACTION == "SHOW_WINS_BEFORE", the app has more than one window and has no window on the current workspace, - // don't activate the app immediately, only move the overview to the workspace with the app's recent window - } else if (showWidowsBeforeActivation && !isShiftPressed && this.app.get_n_windows() > 1 && !targetWindowOnCurrentWs/* && !(opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE)*/) { - // this._scroll = true; - // this._scrollTime = Date.now(); - Main.wm.actionMoveWorkspace(appRecentWorkspace); - Main.overview.dash.showAppsButton.checked = false; - return; - } else if (this._selectedMetaWin) { - this._selectedMetaWin.activate(global.get_current_time()); - } else if (showWidowsBeforeActivation && opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE && !isShiftPressed && this.app.get_n_windows() > 1) { - // expose windows - Main.overview._overview._controls._thumbnailsBox._activateThumbnailAtPoint(0, 0, global.get_current_time(), true); - return; - } else if (opt.DASH_SHIFT_CLICK_MV && isShiftPressed && this.app.get_windows().length) { - this._moveAppToCurrentWorkspace(); - return; - } else if (isShiftPressed) { - return; - } else { - this.app.activate(); - } - - Main.overview.hide(); - }, - - _moveAppToCurrentWorkspace() { - this.app.get_windows().forEach(w => w.change_workspace(global.workspace_manager.get_active_workspace())); - }, - - popupMenu(side = St.Side.LEFT) { - if (shellVersion >= 42) - this.setForcedHighlight(true); - this._removeMenuTimeout(); - this.fake_release(); - - if (!this._getWindowsOnCurrentWs) { - this._getWindowsOnCurrentWs = function () { - const winList = []; - this.app.get_windows().forEach(w => { - if (w.get_workspace() === global.workspace_manager.get_active_workspace()) - winList.push(w); - }); - return winList; - }; - - this._windowsOnOtherWs = function () { - return (this.app.get_windows().length - this._getWindowsOnCurrentWs().length) > 0; - }; - } - - if (!this._menu) { - this._menu = new AppMenu(this, side, { - favoritesSection: true, - showSingleWindows: true, - }); - - this._menu.setApp(this.app); - this._openSigId = this._menu.connect('open-state-changed', (menu, isPoppedUp) => { - if (!isPoppedUp) - this._onMenuPoppedDown(); - }); - // Main.overview.connectObject('hiding', - this._hidingSigId = Main.overview.connect('hiding', - () => this._menu.close(), this); - - Main.uiGroup.add_actor(this._menu.actor); - this._menuManager.addMenu(this._menu); - } - - // once the menu is created, it stays unchanged and we need to modify our items based on current situation - if (this._addedMenuItems && this._addedMenuItems.length) - this._addedMenuItems.forEach(i => i.destroy()); - - - const popupItems = []; - - const separator = new PopupMenu.PopupSeparatorMenuItem(); - this._menu.addMenuItem(separator); - - if (this.app.get_n_windows()) { - // if (/* opt.APP_MENU_FORCE_QUIT*/true) {} - popupItems.push([_('Force Quit'), () => { - this.app.get_windows()[0].kill(); - }]); - - // if (opt.APP_MENU_CLOSE_WS) {} - const nWin = this._getWindowsOnCurrentWs().length; - if (nWin) { - popupItems.push([_(`Close ${nWin} Windows on Current Workspace`), () => { - const windows = this._getWindowsOnCurrentWs(); - let time = global.get_current_time(); - for (let win of windows) { - // increase time by 1 ms for each window to avoid errors from GS - win.delete(time++); - } - }]); - } - - if (/* opt.APP_MENU_MV_TO_WS && */this._windowsOnOtherWs()) - popupItems.push([_('Move App to Current Workspace ( Shift + Click )'), this._moveAppToCurrentWorkspace]); - } - - this._addedMenuItems = []; - this._addedMenuItems.push(separator); - popupItems.forEach(i => { - let item = new PopupMenu.PopupMenuItem(i[0]); - this._menu.addMenuItem(item); - item.connect('activate', i[1].bind(this)); - this._addedMenuItems.push(item); - }); - - this.emit('menu-state-changed', true); - - this._menu.open(BoxPointer.PopupAnimation.FULL); - this._menuManager.ignoreRelease(); - this.emit('sync-tooltip'); - - return false; - }, -}; - -function _getWindowApp(metaWin) { - const tracker = Shell.WindowTracker.get_default(); - return tracker.get_window_app(metaWin); -} - -function _getAppLastUsedWindow(app) { - let recentWin; - global.display.get_tab_list(Meta.TabList.NORMAL_ALL, null).forEach(metaWin => { - const winApp = _getWindowApp(metaWin); - if (!recentWin && winApp === app) - recentWin = metaWin; - }); - return recentWin; -} - -function _getAppRecentWorkspace(app) { - const recentWin = _getAppLastUsedWindow(app); - if (recentWin) - return recentWin.get_workspace(); - - return null; -} - -function _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON) { - const dash = Main.overview._overview._controls.layoutManager._dash; - const dashContainer = dash._dashContainer; - - if (dash._showWindowsIcon) { - dashContainer.remove_child(dash._showWindowsIcon); - if (dash._showWindowsIconClickedId) - dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId); - dash._showWindowsIconClickedId = undefined; - if (dash._showWindowsIcon) - dash._showWindowsIcon.destroy(); - dash._showWindowsIcon = undefined; - } - - if (!show || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) - return; - - if (!dash._showWindowsIcon) { - dash._showWindowsIcon = new ShowWindowsIcon(); - dash._showWindowsIcon.show(false); - dashContainer.add_child(dash._showWindowsIcon); - dash._hookUpLabel(dash._showWindowsIcon); - } - - dash._showWindowsIcon.icon.setIconSize(dash.iconSize); - if (opt.SHOW_WINDOWS_ICON === 1) { - dashContainer.set_child_at_index(dash._showWindowsIcon, 0); - } else if (opt.SHOW_WINDOWS_ICON === 2) { - const index = dashContainer.get_children().length - 1; - dashContainer.set_child_at_index(dash._showWindowsIcon, index); - } - - Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); -} - -const ShowWindowsIcon = GObject.registerClass( -class ShowWindowsIcon extends Dash.DashItemContainer { - _init() { - super._init(); - - this._isSearchWindowsIcon = true; - this._labelText = _('Search Open Windows (Hotkey: Space)'); - this.toggleButton = new St.Button({ - style_class: 'show-apps', - track_hover: true, - can_focus: true, - toggle_mode: false, - }); - - this._iconActor = null; - this.icon = new IconGrid.BaseIcon(this.labelText, { - setSizeManually: true, - showLabel: false, - createIcon: this._createIcon.bind(this), - }); - this.icon.y_align = Clutter.ActorAlign.CENTER; - - this.toggleButton.add_actor(this.icon); - this.toggleButton._delegate = this; - - this.setChild(this.toggleButton); - - if (opt.SEARCH_WINDOWS_ICON_SCROLL) { - this.reactive = true; - this._scrollConId = this.connect('scroll-event', _onScrollEvent.bind(this)); - this._leaveConId = this.connect('leave-event', _onLeaveEvent.bind(this)); - } - } - - _createIcon(size) { - this._iconActor = new St.Icon({ - icon_name: 'focus-windows-symbolic', - icon_size: size, - style_class: 'show-apps-icon', - track_hover: true, - }); - return this._iconActor; - } -}); - -function _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON) { - const dash = Main.overview._overview._controls.layoutManager._dash; - const dashContainer = dash._dashContainer; - - if (dash._recentFilesIcon) { - dashContainer.remove_child(dash._recentFilesIcon); - if (dash._recentFilesIconClickedId) - dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId); - dash._recentFilesIconClickedId = undefined; - if (dash._recentFilesIcon) - dash._recentFilesIcon.destroy(); - dash._recentFilesIcon = undefined; - } - - if (!show || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED) - return; - - if (!dash._recentFilesIcon) { - dash._recentFilesIcon = new ShowRecentFilesIcon(); - dash._recentFilesIcon.show(false); - dashContainer.add_child(dash._recentFilesIcon); - dash._hookUpLabel(dash._recentFilesIcon); - } - - dash._recentFilesIcon.icon.setIconSize(dash.iconSize); - if (opt.SHOW_RECENT_FILES_ICON === 1) { - dashContainer.set_child_at_index(dash._recentFilesIcon, 0); - } else if (opt.SHOW_RECENT_FILES_ICON === 2) { - const index = dashContainer.get_children().length - 1; - dashContainer.set_child_at_index(dash._recentFilesIcon, index); - } - - Main.overview._overview._controls.layoutManager._dash._adjustIconSize(); -} - -const ShowRecentFilesIcon = GObject.registerClass( -class ShowRecentFilesIcon extends Dash.DashItemContainer { - _init() { - super._init(); - - this._labelText = _('Search Recent Files (Hotkey: Ctrl + Space)'); - this.toggleButton = new St.Button({ - style_class: 'show-apps', - track_hover: true, - can_focus: true, - toggle_mode: false, - }); - - this._iconActor = null; - this.icon = new IconGrid.BaseIcon(this.labelText, { - setSizeManually: true, - showLabel: false, - createIcon: this._createIcon.bind(this), - }); - this.icon.y_align = Clutter.ActorAlign.CENTER; - - this.toggleButton.add_actor(this.icon); - this.toggleButton._delegate = this; - - this.setChild(this.toggleButton); - } - - _createIcon(size) { - this._iconActor = new St.Icon({ - icon_name: 'document-open-recent-symbolic', - icon_size: size, - style_class: 'show-apps-icon', - track_hover: true, - }); - return this._iconActor; - } -}); diff --git a/extensions/vertical-workspaces/lib/messageTray.js b/extensions/vertical-workspaces/lib/messageTray.js deleted file mode 100644 index b35541a..0000000 --- a/extensions/vertical-workspaces/lib/messageTray.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * messageTray.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; - -const { Clutter } = imports.gi; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const Main = imports.ui.main; - -let opt; -let _firstRun = true; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('messageTrayModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (reset) { - opt = null; - setNotificationPosition(1); - return; - } - - setNotificationPosition(opt.NOTIFICATION_POSITION); -} - -function setNotificationPosition(position) { - switch (position) { - case 0: - Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.START; - Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; - break; - case 1: - Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.CENTER; - Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; - break; - case 2: - Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.END; - Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.START; - break; - case 3: - Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.START; - Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; - break; - case 4: - Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.CENTER; - Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; - break; - case 5: - Main.messageTray._bannerBin.x_align = Clutter.ActorAlign.END; - Main.messageTray._bannerBin.y_align = Clutter.ActorAlign.END; - break; - } -} diff --git a/extensions/vertical-workspaces/lib/osdWindow.js b/extensions/vertical-workspaces/lib/osdWindow.js deleted file mode 100644 index a010558..0000000 --- a/extensions/vertical-workspaces/lib/osdWindow.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * osdWindow.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; - -const { Clutter } = imports.gi; -const Main = imports.ui.main; -const OsdWindow = imports.ui.osdWindow; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -const OsdPositions = { - 1: { - x_align: Clutter.ActorAlign.START, - y_align: Clutter.ActorAlign.START, - }, - 2: { - x_align: Clutter.ActorAlign.CENTER, - y_align: Clutter.ActorAlign.START, - }, - 3: { - x_align: Clutter.ActorAlign.END, - y_align: Clutter.ActorAlign.START, - }, - 4: { - x_align: Clutter.ActorAlign.CENTER, - y_align: Clutter.ActorAlign.CENTER, - }, - 5: { - x_align: Clutter.ActorAlign.START, - y_align: Clutter.ActorAlign.END, - }, - 6: { - x_align: Clutter.ActorAlign.CENTER, - y_align: Clutter.ActorAlign.END, - }, - 7: { - x_align: Clutter.ActorAlign.END, - y_align: Clutter.ActorAlign.END, - }, -}; - -let _overrides; -let opt; -let _firstRun = true; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('osdWindowModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - if (reset || !moduleEnabled) { - updateExistingOsdWindows(6); - _overrides = null; - opt = null; - return; - } - - _overrides = new _Util.Overrides(); - _overrides.addOverride('osdWindow', OsdWindow.OsdWindow.prototype, OsdWindowCommon); -} - -function updateExistingOsdWindows(position) { - position = position ? position : opt.OSD_POSITION; - Main.osdWindowManager._osdWindows.forEach(osd => { - osd.set(OsdPositions[position]); - }); -} - -const OsdWindowCommon = { - after_show() { - if (!opt.OSD_POSITION) - this.opacity = 0; - this.set(OsdPositions[opt.OSD_POSITION]); - }, -}; diff --git a/extensions/vertical-workspaces/lib/overlayKey.js b/extensions/vertical-workspaces/lib/overlayKey.js deleted file mode 100644 index e0fc11d..0000000 --- a/extensions/vertical-workspaces/lib/overlayKey.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * overlayKey.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; -const { GObject, Gio, GLib, Meta, St } = imports.gi; - -const Main = imports.ui.main; -const Overview = imports.ui.overview; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -const _ = Me.imports.lib.settings._; -const shellVersion = _Util.shellVersion; -const WIN_SEARCH_PREFIX = Me.imports.lib.windowSearchProvider.prefix; -const RECENT_FILES_PREFIX = Me.imports.lib.recentFilesSearchProvider.prefix; -const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard'; - -let opt; -let _firstRun = true; - -let _originalOverlayKeyHandlerId; -let _overlayKeyHandlerId; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('overlayKeyModule', true); - reset = reset || (!_firstRun && !moduleEnabled); - - // don't even touch this module if disabled - if (_firstRun && !moduleEnabled) - return; - - _firstRun = false; - - if (reset) { - _updateOverlayKey(reset); - opt = null; - return; - } - - _updateOverlayKey(); -} - -function _updateOverlayKey(reset = false) { - if (reset) { - _restoreOverlayKeyHandler(); - } else if (!_originalOverlayKeyHandlerId) { - _originalOverlayKeyHandlerId = GObject.signal_handler_find(global.display, { signalId: 'overlay-key' }); - if (_originalOverlayKeyHandlerId !== null) - global.display.block_signal_handler(_originalOverlayKeyHandlerId); - _connectOverlayKey.bind(Main.overview._overview.controls)(); - } -} - -function _restoreOverlayKeyHandler() { - // Disconnect modified overlay key handler - if (_overlayKeyHandlerId !== null) { - global.display.disconnect(_overlayKeyHandlerId); - _overlayKeyHandlerId = null; - } - - // Unblock original overlay key handler - if (_originalOverlayKeyHandlerId !== null) { - global.display.unblock_signal_handler(_originalOverlayKeyHandlerId); - _originalOverlayKeyHandlerId = null; - } -} - -function _connectOverlayKey() { - this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA }); - - this._lastOverlayKeyTime = 0; - _overlayKeyHandlerId = global.display.connect('overlay-key', () => { - if (this._a11ySettings.get_boolean('stickykeys-enable')) - return; - - const { initialState, finalState, transitioning } = - this._stateAdjustment.getStateTransitionParams(); - - const time = GLib.get_monotonic_time() / 1000; - const timeDiff = time - this._lastOverlayKeyTime; - this._lastOverlayKeyTime = time; - - const shouldShift = St.Settings.get().enable_animations - ? transitioning && finalState > initialState - : Main.overview.visible && timeDiff < Overview.ANIMATION_TIME; - - const mode = opt.OVERLAY_KEY_SECONDARY; - if (shouldShift) { - if (mode === 1) - this._shiftState(Meta.MotionDirection.UP); - else if (mode === 2) - _Util.activateSearchProvider(WIN_SEARCH_PREFIX); - else if (mode === 3) - _Util.activateSearchProvider(RECENT_FILES_PREFIX); - } else { - Main.overview.toggle(); - } - }); -} diff --git a/extensions/vertical-workspaces/lib/overview.js b/extensions/vertical-workspaces/lib/overview.js deleted file mode 100644 index 2f23d05..0000000 --- a/extensions/vertical-workspaces/lib/overview.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * overview.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; - -const Overview = imports.ui.overview; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -let _overrides; -let opt; - -function update(reset = false) { - if (_overrides) - _overrides.removeAll(); - - - if (reset) { - _overrides = null; - opt = null; - return; - } - - opt = Me.imports.lib.settings.opt; - _overrides = new _Util.Overrides(); - - _overrides.addOverride('Overview', Overview.Overview.prototype, OverviewCommon); -} - -const OverviewCommon = { - _showDone() { - this._animationInProgress = false; - this._coverPane.hide(); - - this.emit('shown'); - // Handle any calls to hide* while we were showing - if (!this._shown) - this._animateNotVisible(); - - this._syncGrab(); - - // if user activates overview during startup animation, transition needs to be shifted to the state 2 here - const controls = this._overview._controls; - if (controls._searchController._searchActive && controls._stateAdjustment.value === 1) { - if (opt.SEARCH_VIEW_ANIMATION) - controls._onSearchChanged(); - else if (!opt.OVERVIEW_MODE2) - controls._stateAdjustment.value = 2; - } - }, -}; diff --git a/extensions/vertical-workspaces/lib/panel.js b/extensions/vertical-workspaces/lib/panel.js deleted file mode 100644 index 3f44ae7..0000000 --- a/extensions/vertical-workspaces/lib/panel.js +++ /dev/null @@ -1,197 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * panel.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; - -const { GLib } = imports.gi; -const Main = imports.ui.main; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -const ANIMATION_TIME = imports.ui.overview.ANIMATION_TIME; - -let opt; -let _firstRun = true; - -let _showingOverviewConId; -let _hidingOverviewConId; -let _styleChangedConId; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('panelModule', true); - // Avoid conflict with other extensions - const conflict = _Util.getEnabledExtensions('dash-to-panel').length || - _Util.getEnabledExtensions('hidetopbar').length; - reset = reset || (!_firstRun && !moduleEnabled); - - // don't even touch this module if disabled or in potential conflict - if (_firstRun && (reset || conflict)) - return; - - _firstRun = false; - - const panelBox = Main.layoutManager.panelBox; - if (reset || !moduleEnabled) { - // _disconnectPanel(); - reset = true; - _setPanelPosition(reset); - _updateOverviewConnection(reset); - _reparentPanel(false); - - _updateStyleChangedConnection(reset); - - panelBox.translation_y = 0; - Main.panel.opacity = 255; - _setPanelStructs(true); - return; - } - - _setPanelPosition(); - _updateStyleChangedConnection(); - - if (opt.PANEL_MODE === 0) { - _updateOverviewConnection(true); - _reparentPanel(false); - panelBox.translation_y = 0; - Main.panel.opacity = 255; - _setPanelStructs(true); - } else if (opt.PANEL_MODE === 1) { - if (opt.SHOW_WS_PREVIEW_BG) { - _reparentPanel(true); - if (opt.OVERVIEW_MODE2) { - // in OM2 if the panel has been moved to the overviewGroup move panel above all - Main.layoutManager.overviewGroup.set_child_above_sibling(panelBox, null); - _updateOverviewConnection(); - } else { - // otherwise move the panel below overviewGroup so it can get below workspacesDisplay - Main.layoutManager.overviewGroup.set_child_below_sibling(panelBox, Main.overview._overview); - _updateOverviewConnection(true); - } - _showPanel(true); - } else { - // if ws preview bg is disabled, panel can stay in uiGroup - _reparentPanel(false); - _showPanel(false); - _updateOverviewConnection(); - } - // _connectPanel(); - } else if (opt.PANEL_MODE === 2) { - _updateOverviewConnection(true); - _reparentPanel(false); - _showPanel(false); - // _connectPanel(); - } - _setPanelStructs(opt.PANEL_MODE === 0); - Main.layoutManager._updateHotCorners(); -} - -function _setPanelPosition(reset = false) { - const geometry = global.display.get_monitor_geometry(global.display.get_primary_monitor()); - const panelBox = Main.layoutManager.panelBox; - const panelHeight = Main.panel.height; // panelBox height can be 0 after shell start - - if (opt.PANEL_POSITION_TOP || reset) - panelBox.set_position(geometry.x, geometry.y); - else - panelBox.set_position(geometry.x, geometry.y + geometry.height - panelHeight); -} - -function _updateStyleChangedConnection(reset = false) { - if (reset) { - if (_styleChangedConId) { - Main.panel.disconnect(_styleChangedConId); - _styleChangedConId = 0; - } - } else if (!_styleChangedConId) { - Main.panel.connect('style-changed', () => { - if (opt.PANEL_MODE === 1) - Main.panel.add_style_pseudo_class('overview'); - else if (opt.OVERVIEW_MODE2) - Main.panel.remove_style_pseudo_class('overview'); - }); - } -} - -function _updateOverviewConnection(reset = false) { - if (reset) { - if (_hidingOverviewConId) { - Main.overview.disconnect(_hidingOverviewConId); - _hidingOverviewConId = 0; - } - if (_showingOverviewConId) { - Main.overview.disconnect(_showingOverviewConId); - _showingOverviewConId = 0; - } - } else { - if (!_hidingOverviewConId) { - _hidingOverviewConId = Main.overview.connect('hiding', () => { - if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2) - _showPanel(false); - }); - } - if (!_showingOverviewConId) { - _showingOverviewConId = Main.overview.connect('showing', () => { - if (!opt.SHOW_WS_PREVIEW_BG || opt.OVERVIEW_MODE2 || Main.layoutManager.panelBox.translation_y) - _showPanel(true); - }); - } - } -} - -function _reparentPanel(reparent = false) { - const panel = Main.layoutManager.panelBox; - if (reparent && panel.get_parent() === Main.layoutManager.uiGroup) { - Main.layoutManager.uiGroup.remove_child(panel); - Main.layoutManager.overviewGroup.add_child(panel); - } else if (!reparent && panel.get_parent() === Main.layoutManager.overviewGroup) { - Main.layoutManager.overviewGroup.remove_child(panel); - // return the panel at default position, panel shouldn't cover objects that should be above - Main.layoutManager.uiGroup.insert_child_at_index(panel, 4); - } -} - -function _setPanelStructs(state) { - Main.layoutManager._trackedActors.forEach(a => { - if (a.actor === Main.layoutManager.panelBox) - a.affectsStruts = state; - }); - - // workaround to force maximized windows to resize after removing affectsStruts - // simulation of minimal swipe gesture to the opposite direction - // todo - needs better solution!!!!!!!!!!! - // const direction = _getAppGridAnimationDirection() === 2 ? 1 : -1; - // Main.overview._swipeTracker._beginTouchSwipe(null, global.get_current_time(), 1, 1); - // Main.overview._swipeTracker._updateGesture(null, global.get_current_time(), direction, 1); - // GLib.timeout_add(0, 50, () => Main.overview._swipeTracker._endGesture(global.get_current_time(), 1, true));*/ -} - -function _showPanel(show = true) { - if (show) { - Main.panel.opacity = 255; - Main.layoutManager.panelBox.ease({ - duration: ANIMATION_TIME, - translation_y: 0, - onComplete: () => { - _setPanelStructs(opt.PANEL_MODE === 0); - }, - }); - } else { - const panelHeight = Main.panel.height; - Main.layoutManager.panelBox.ease({ - duration: ANIMATION_TIME, - translation_y: opt.PANEL_POSITION_TOP ? -panelHeight + 1 : panelHeight - 1, - onComplete: () => { - Main.panel.opacity = 0; - _setPanelStructs(opt.PANEL_MODE === 0); - }, - }); - } -} diff --git a/extensions/vertical-workspaces/lib/recentFilesSearchProvider.js b/extensions/vertical-workspaces/lib/recentFilesSearchProvider.js deleted file mode 100644 index 86e38f4..0000000 --- a/extensions/vertical-workspaces/lib/recentFilesSearchProvider.js +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Vertical Workspaces - * recentFilesSearchProvider.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - */ - -'use strict'; - -const { GLib, Gio, Meta, St, Shell, Gtk } = imports.gi; - -const Main = imports.ui.main; -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); -const Settings = Me.imports.lib.settings; -const _Util = Me.imports.lib.util; - -// gettext -const _ = Settings._; - -const shellVersion = Settings.shellVersion; - -const ModifierType = imports.gi.Clutter.ModifierType; - -let recentFilesSearchProvider; -let _enableTimeoutId = 0; - -// prefix helps to eliminate results from other search providers -// so it needs to be something less common -// needs to be accessible from vw module -var prefix = 'fq//'; - -var opt; - -function getOverviewSearchResult() { - return Main.overview._overview.controls._searchController._searchResults; -} - - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - if (!reset && opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED && !recentFilesSearchProvider) { - enable(); - } else if (reset || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED) { - disable(); - opt = null; - } -} - -function enable() { - // delay because Fedora had problem to register a new provider soon after Shell restarts - _enableTimeoutId = GLib.timeout_add( - GLib.PRIORITY_DEFAULT, - 2000, - () => { - if (!recentFilesSearchProvider) { - recentFilesSearchProvider = new RecentFilesSearchProvider(opt); - getOverviewSearchResult()._registerProvider(recentFilesSearchProvider); - } - _enableTimeoutId = 0; - return GLib.SOURCE_REMOVE; - } - ); -} - -function disable() { - if (recentFilesSearchProvider) { - getOverviewSearchResult()._unregisterProvider(recentFilesSearchProvider); - recentFilesSearchProvider = null; - } - if (_enableTimeoutId) { - GLib.source_remove(_enableTimeoutId); - _enableTimeoutId = 0; - } -} - -function makeResult(window, i) { - const app = Shell.WindowTracker.get_default().get_window_app(window); - const appName = app ? app.get_name() : 'Unknown'; - const windowTitle = window.get_title(); - const wsIndex = window.get_workspace().index(); - - return { - 'id': i, - // convert all accented chars to their basic form and lower case for search - 'name': `${wsIndex + 1}: ${windowTitle} ${appName}`.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(), - appName, - windowTitle, - window, - }; -} - -const closeSelectedRegex = /^\/x!$/; -const closeAllResultsRegex = /^\/xa!$/; -const moveToWsRegex = /^\/m[0-9]+$/; -const moveAllToWsRegex = /^\/ma[0-9]+$/; - -const RecentFilesSearchProvider = class RecentFilesSearchProvider { - constructor() { - this.id = 'org.gnome.Nautilus.desktop'; - this.appInfo = Gio.AppInfo.create_from_commandline('/usr/bin/nautilus -ws recent:///', 'Recent Files', null); - // this.appInfo = Shell.AppSystem.get_default().lookup_app('org.gnome.Nautilus.desktop').appInfo; - this.appInfo.get_description = () => _('Search recent files'); - this.appInfo.get_name = () => _('Recent Files'); - this.appInfo.get_id = () => this.id; - this.appInfo.get_icon = () => Gio.icon_new_for_string('document-open-recent-symbolic'); - this.appInfo.should_show = () => true; - - this.canLaunchSearch = true; - this.isRemoteProvider = false; - } - - _getResultSet(terms) { - if (!terms[0].startsWith(prefix)) - return []; - // do not modify original terms - let termsCopy = [...terms]; - // search for terms without prefix - termsCopy[0] = termsCopy[0].replace(prefix, ''); - - const candidates = this.files; - const _terms = [].concat(termsCopy); - // let match; - - const term = _terms.join(' '); - /* match = s => { - return fuzzyMatch(term, s); - }; */ - - const results = []; - let m; - for (let id in candidates) { - const file = this.files[id]; - const name = `${file.get_age()}d: ${file.get_display_name()} ${file.get_uri_display().replace(`/${file.get_display_name()}`, '')}`; - if (opt.SEARCH_FUZZY) - m = _Util.fuzzyMatch(term, name); - else - m = _Util.strictMatch(term, name); - - if (m !== -1) - results.push({ weight: m, id }); - } - - results.sort((a, b) => this.files[a.id].get_visited() < this.files[b.id].get_visited()); - - this.resultIds = results.map(item => item.id); - return this.resultIds; - } - - getResultMetas(resultIds, callback = null) { - const metas = resultIds.map(id => this.getResultMeta(id)); - if (shellVersion >= 43) - return new Promise(resolve => resolve(metas)); - else if (callback) - callback(metas); - return null; - } - - getResultMeta(resultId) { - const result = this.files[resultId]; - return { - 'id': resultId, - 'name': `${result.get_age()}: ${result.get_display_name()}`, - 'description': `${result.get_uri_display().replace(`/${result.get_display_name()}`, '')}`, - 'createIcon': size => { - let icon = this.getIcon(result, size); - return icon; - }, - }; - } - - getIcon(result, size) { - let file = Gio.File.new_for_uri(result.get_uri()); - let info = file.query_info(Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH, - Gio.FileQueryInfoFlags.NONE, null); - let path = info.get_attribute_byte_string( - Gio.FILE_ATTRIBUTE_THUMBNAIL_PATH); - - let icon, gicon; - - if (path) { - gicon = Gio.FileIcon.new(Gio.File.new_for_path(path)); - } else { - const appInfo = Gio.AppInfo.get_default_for_type(result.get_mime_type(), false); - if (appInfo) - gicon = appInfo.get_icon(); - } - - if (gicon) - icon = new St.Icon({ gicon, icon_size: size }); - else - icon = new St.Icon({ icon_name: 'icon-missing', icon_size: size }); - - - return icon; - } - - launchSearch(/* terms, timeStamp */) { - this._openNautilus('recent:///'); - } - - _openNautilus(uri) { - try { - GLib.spawn_command_line_async(`nautilus -ws ${uri}`); - } catch (e) { - log(e); - } - } - - activateResult(resultId /* , terms, timeStamp */) { - const file = this.files[resultId]; - - if (_Util.isShiftPressed()) { - Main.overview.toggle(); - this._openNautilus(file.get_uri()); - } else { - const appInfo = Gio.AppInfo.get_default_for_type(file.get_mime_type(), false); - if (!(appInfo && appInfo.launch_uris([file.get_uri()], null))) - this._openNautilus(file.get_uri()); - } - } - - getInitialResultSet(terms, callback /* , cancellable = null*/) { - // In GS 43 callback arg has been removed - /* if (shellVersion >= 43) - cancellable = callback; */ - - const filesDict = {}; - const files = Gtk.RecentManager.get_default().get_items().filter(f => f.exists()); - - for (let file of files) - filesDict[file.get_uri()] = file; - - - this.files = filesDict; - - if (shellVersion >= 43) - return new Promise(resolve => resolve(this._getResultSet(terms))); - else - callback(this._getResultSet(terms)); - - return null; - } - - filterResults(results, maxResults) { - return results.slice(0, 20); - // return results.slice(0, maxResults); - } - - getSubsearchResultSet(previousResults, terms, callback /* , cancellable*/) { - // if we return previous results, quick typers get non-actual results - callback(this._getResultSet(terms)); - } - - /* createResultObject(resultMeta) { - return this.files[resultMeta.id]; - }*/ -}; diff --git a/extensions/vertical-workspaces/lib/search.js b/extensions/vertical-workspaces/lib/search.js deleted file mode 100644 index 8540626..0000000 --- a/extensions/vertical-workspaces/lib/search.js +++ /dev/null @@ -1,206 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * search.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; -const { Shell, Gio, St, Clutter } = imports.gi; -const Main = imports.ui.main; - -const AppDisplay = imports.ui.appDisplay; -const Search = imports.ui.search; - -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -const _ = Me.imports.lib.settings._; -const shellVersion = _Util.shellVersion; - -let opt; -let _overrides; -let _firstRun = true; - -let SEARCH_MAX_WIDTH; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('searchModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - _updateSearchViewWidth(reset); - - if (reset) { - Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.FILL; - opt = null; - _overrides = null; - return; - } - - _overrides = new _Util.Overrides(); - - _overrides.addOverride('AppSearchProvider', AppDisplay.AppSearchProvider.prototype, AppSearchProvider); - _overrides.addOverride('SearchResult', Search.SearchResult.prototype, SearchResult); - _overrides.addOverride('SearchResultsView', Search.SearchResultsView.prototype, SearchResultsView); - - // Don't expand the search view vertically and align it to the top - // this is important in the static workspace mode when the search view bg is not transparent - // also the "Searching..." and "No Results" notifications will be closer to the search entry, with the distance given by margin-top in the stylesheet - Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.START; -} - -function _updateSearchViewWidth(reset = false) { - const searchContent = Main.overview._overview._controls.layoutManager._searchController._searchResults._content; - if (!SEARCH_MAX_WIDTH) { // just store original value; - const themeNode = searchContent.get_theme_node(); - const width = themeNode.get_max_width(); - SEARCH_MAX_WIDTH = width; - } - - if (reset) { - searchContent.set_style(''); - } else { - let width = Math.round(SEARCH_MAX_WIDTH * opt.SEARCH_VIEW_SCALE); - searchContent.set_style(`max-width: ${width}px;`); - } -} - -// AppDisplay.AppSearchProvider -const AppSearchProvider = { - getInitialResultSet(terms, callback, _cancellable) { - // Defer until the parental controls manager is initialized, so the - // results can be filtered correctly. - if (!this._parentalControlsManager.initialized) { - let initializedId = this._parentalControlsManager.connect('app-filter-changed', () => { - if (this._parentalControlsManager.initialized) { - this._parentalControlsManager.disconnect(initializedId); - this.getInitialResultSet(terms, callback, _cancellable); - } - }); - return; - } - - - const pattern = terms.join(' '); - let appInfoList = Shell.AppSystem.get_default().get_installed(); - - let weightList = {}; - appInfoList = appInfoList.filter(appInfo => { - try { - appInfo.get_id(); // catch invalid file encodings - } catch (e) { - return false; - } - - let string = ''; - let name; - let shouldShow = false; - if (appInfo.get_display_name) { - // show only launchers that should be visible in this DE - shouldShow = appInfo.should_show() && this._parentalControlsManager.shouldShowApp(appInfo); - - if (shouldShow) { - let dispName = appInfo.get_display_name() || ''; - let gName = appInfo.get_generic_name() || ''; - let description = appInfo.get_description() || ''; - let categories = appInfo.get_string('Categories') || ''; - let keywords = appInfo.get_string('Keywords') || ''; - name = dispName; - string = `${dispName} ${gName} ${description} ${categories} ${keywords}`; - } - } - - let m = -1; - if (shouldShow && opt.SEARCH_FUZZY) { - m = _Util.fuzzyMatch(pattern, name); - m = (m + _Util.strictMatch(pattern, string)) / 2; - } else if (shouldShow) { - m = _Util.strictMatch(pattern, string); - } - - if (m !== -1) - weightList[appInfo.get_id()] = m; - - return shouldShow && (m !== -1); - }); - - appInfoList.sort((a, b) => weightList[a.get_id()] > weightList[b.get_id()]); - - const usage = Shell.AppUsage.get_default(); - // sort apps by usage list - appInfoList.sort((a, b) => usage.compare(a.get_id(), b.get_id())); - // prefer apps where any word in their name starts with the pattern - appInfoList.sort((a, b) => _Util.isMoreRelevant(a.get_display_name(), b.get_display_name(), pattern)); - - let results = appInfoList.map(app => app.get_id()); - - results = results.concat(this._systemActions.getMatchingActions(terms)); - - if (shellVersion < 43) - callback(results); - else - return new Promise(resolve => resolve(results)); - }, - - // App search result size - createResultObject(resultMeta) { - if (resultMeta.id.endsWith('.desktop')) { - const icon = new AppDisplay.AppIcon(this._appSys.lookup_app(resultMeta['id']), { - expandTitleOnHover: false, - }); - icon.icon.setIconSize(opt.SEARCH_ICON_SIZE); - return icon; - } else { - const icon = new AppDisplay.SystemActionIcon(this, resultMeta); - icon.icon._setSizeManually = true; - icon.icon.setIconSize(opt.SEARCH_ICON_SIZE); - return icon; - } - }, -}; - -const SearchResult = { - activate() { - this.provider.activateResult(this.metaInfo.id, this._resultsView.terms); - - if (this.metaInfo.clipboardText) { - St.Clipboard.get_default().set_text( - St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText); - } - // don't close overview if Shift key is pressed - Shift moves windows to the workspace - if (!_Util.isShiftPressed()) - Main.overview.toggle(); - }, -}; - -const SearchResultsView = { - _updateSearchProgress() { - let haveResults = this._providers.some(provider => { - let display = provider.display; - return display.getFirstResult() !== null; - }); - - this._scrollView.visible = haveResults; - this._statusBin.visible = !haveResults; - - if (!haveResults) { - if (this.searchInProgress) - this._statusText.set_text(_('Searching…')); - else - this._statusText.set_text(_('No results.')); - } - }, -}; diff --git a/extensions/vertical-workspaces/lib/windowPreview.js b/extensions/vertical-workspaces/lib/windowPreview.js deleted file mode 100644 index 5d2bd61..0000000 --- a/extensions/vertical-workspaces/lib/windowPreview.js +++ /dev/null @@ -1,379 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * windowPreview.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; - -const { Clutter, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi; - -const Main = imports.ui.main; -const WindowPreview = imports.ui.windowPreview; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; -const shellVersion = _Util.shellVersion; - -let _overrides; - -const WINDOW_SCALE_TIME = imports.ui.windowPreview.WINDOW_SCALE_TIME; -const WINDOW_ACTIVE_SIZE_INC = imports.ui.windowPreview.WINDOW_ACTIVE_SIZE_INC; -const WINDOW_OVERLAY_FADE_TIME = imports.ui.windowPreview.WINDOW_OVERLAY_FADE_TIME; -const SEARCH_WINDOWS_PREFIX = Me.imports.lib.windowSearchProvider.prefix; - -const ControlsState = imports.ui.overviewControls.ControlsState; - -let opt; -let _firstRun = true; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('windowPreviewModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - - if (reset) { - _overrides = null; - opt = null; - WindowPreview.WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 750; - return; - } - - _overrides = new _Util.Overrides(); - - _overrides.addOverride('WindowPreview', WindowPreview.WindowPreview.prototype, WindowPreviewCommon); - // A shorter timeout allows user to quickly cancel the selection by leaving the preview with the mouse pointer - if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW) - WindowPreview.WINDOW_OVERLAY_IDLE_HIDE_TIMEOUT = 150; -} - -const WindowPreviewCommon = { - // injection to _init() - after__init() { - const ICON_OVERLAP = 0.7; - - if (opt.WIN_PREVIEW_ICON_SIZE < 64) { - this.remove_child(this._icon); - this._icon.destroy(); - const tracker = Shell.WindowTracker.get_default(); - const app = tracker.get_window_app(this.metaWindow); - this._icon = app.create_icon_texture(opt.WIN_PREVIEW_ICON_SIZE); - this._icon.add_style_class_name('icon-dropshadow'); - this._icon.set({ - reactive: true, - pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), - }); - this._icon.add_constraint(new Clutter.BindConstraint({ - source: this.windowContainer, - coordinate: Clutter.BindCoordinate.POSITION, - })); - this._icon.add_constraint(new Clutter.AlignConstraint({ - source: this.windowContainer, - align_axis: Clutter.AlignAxis.X_AXIS, - factor: 0.5, - })); - this._icon.add_constraint(new Clutter.AlignConstraint({ - source: this.windowContainer, - align_axis: Clutter.AlignAxis.Y_AXIS, - pivot_point: new Graphene.Point({ x: -1, y: ICON_OVERLAP }), - factor: 1, - })); - this.add_child(this._icon); - if (opt.WIN_PREVIEW_ICON_SIZE < 22) { - // disable app icon - this._icon.hide(); - } - this._iconSize = opt.WIN_PREVIEW_ICON_SIZE; - } - - const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const iconOverlap = opt.WIN_PREVIEW_ICON_SIZE * ICON_OVERLAP; - // we cannot get proper title height before it gets to the stage, so 35 is estimated height + spacing - this._title.get_constraints()[1].offset = scaleFactor * (-iconOverlap - 35); - this.set_child_above_sibling(this._title, null); - // if window is created while the overview is shown, icon and title should be visible immediately - if (Main.overview._overview._controls._stateAdjustment.value < 1) { - this._icon.scale_x = 0; - this._icon.scale_y = 0; - this._title.opacity = 0; - } - - if (opt.ALWAYS_SHOW_WIN_TITLES) - this._title.show(); - - if (opt.OVERVIEW_MODE === 1) { - // spread windows on hover - this._wsStateConId = this.connect('enter-event', () => { - // don't spread windows if user don't use pointer device at this moment - if (global.get_pointer()[0] === opt.showingPointerX || Main.overview._overview._controls._stateAdjustment.value < 1) - return; - - const adjustment = this._workspace._background._stateAdjustment; - opt.WORKSPACE_MODE = 1; - _Util.exposeWindows(adjustment, false); - this.disconnect(this._wsStateConId); - }); - } - - if (opt.OVERVIEW_MODE) { - // show window icon and title on ws windows spread - this._stateAdjustmentSigId = this._workspace.stateAdjustment.connect('notify::value', this._updateIconScale.bind(this)); - } - - // replace click action with custom one - const action = this.get_actions()[0]; - - const handlerId = GObject.signal_handler_find(action, { signalId: 'clicked' }); - if (handlerId) - action.disconnect(handlerId); - - action.connect('clicked', act => { - const button = act.get_button(); - if (button === Clutter.BUTTON_PRIMARY) { - this._activate(); - return Clutter.EVENT_STOP; - } else if (button === Clutter.BUTTON_SECONDARY) { - // this action cancels long-press event and the 'long-press-cancel' event is used by the Shell to actually initiate DnD - // so the dnd initiation needs to be removed - if (this._longPressLater) { - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.remove(this._longPressLater); - } else { - Meta.later_remove(this._longPressLater); - delete this._longPressLater; - } - } - const tracker = Shell.WindowTracker.get_default(); - const appName = tracker.get_window_app(this.metaWindow).get_name(); - _Util.activateSearchProvider(`${SEARCH_WINDOWS_PREFIX} ${appName}`); - return Clutter.EVENT_STOP; - } - return Clutter.EVENT_PROPAGATE; - }); - - if (opt.WINDOW_ICON_CLICK_SEARCH) { - const iconClickAction = new Clutter.ClickAction(); - iconClickAction.connect('clicked', act => { - if (act.get_button() === Clutter.BUTTON_PRIMARY) { - const tracker = Shell.WindowTracker.get_default(); - const appName = tracker.get_window_app(this.metaWindow).get_name(); - _Util.activateSearchProvider(`${SEARCH_WINDOWS_PREFIX} ${appName}`); - return Clutter.EVENT_STOP; - } - return Clutter.EVENT_PROPAGATE; - }); - this._icon.add_action(iconClickAction); - } - }, - - _updateIconScale() { - let { currentState, initialState, finalState } = - this._overviewAdjustment.getStateTransitionParams(); - - // Current state - 0 - HIDDEN, 1 - WINDOW_PICKER, 2 - APP_GRID - const primaryMonitor = this.metaWindow.get_monitor() === global.display.get_primary_monitor(); - - const visible = - (initialState > ControlsState.HIDDEN || finalState > ControlsState.HIDDEN) && - !(finalState === ControlsState.APP_GRID && primaryMonitor); - - let scale = 0; - if (visible) - scale = currentState >= 1 ? 1 : currentState % 1; - - if (!primaryMonitor && opt.WORKSPACE_MODE && - ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || - (initialState === ControlsState.APP_GRID && finalState === ControlsState.WINDOW_PICKER)) - ) - scale = 1; - else if (!primaryMonitor && opt.OVERVIEW_MODE && !opt.WORKSPACE_MODE) - scale = 0; - /* } else if (primaryMonitor && ((initialState === ControlsState.WINDOW_PICKER && finalState === ControlsState.APP_GRID) || - initialState === ControlsState.APP_GRID && finalState === ControlsState.HIDDEN)) {*/ - else if (primaryMonitor && currentState > ControlsState.WINDOW_PICKER) - scale = 0; - - - // in static workspace mode show icon and title on windows expose - if (opt.OVERVIEW_MODE) { - if (currentState === 1) - scale = opt.WORKSPACE_MODE; - else if (finalState === 1 || (finalState === 0 && !opt.WORKSPACE_MODE)) - return; - } - - if (scale === 1) { - this._icon.ease({ - duration: 50, - scale_x: scale, - scale_y: scale, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - this._title.ease({ - duration: 100, - opacity: 255, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } else if (this._icon.scale_x !== 0) { - this._icon.set({ - scale_x: 0, - scale_y: 0, - }); - this._title.opacity = 0; - } - - // if titles are in 'always show' mode, we need to add transition between visible/invisible state - // but the transition is quite expensive, - // showing the titles at the end of the transition is good enough and workspace preview transition is much smoother - }, - - showOverlay(animate) { - if (!this._overlayEnabled) - return; - - if (this._overlayShown) - return; - - this._overlayShown = true; - if (!opt.ALWAYS_ACTIVATE_SELECTED_WINDOW) - this._restack(); - - // If we're supposed to animate and an animation in our direction - // is already happening, let that one continue - const ongoingTransition = this._title.get_transition('opacity'); - if (animate && - ongoingTransition && - ongoingTransition.get_interval().peek_final_value() === 255) - return; - - const toShow = this._windowCanClose() - ? [this._closeButton] - : []; - - if (!opt.ALWAYS_SHOW_WIN_TITLES) - toShow.push(this._title); - - - toShow.forEach(a => { - a.opacity = 0; - a.show(); - a.ease({ - opacity: 255, - duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - }); - - const [width, height] = this.window_container.get_size(); - const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage); - const activeExtraSize = WINDOW_ACTIVE_SIZE_INC * 2 * scaleFactor; - const origSize = Math.max(width, height); - const scale = (origSize + activeExtraSize) / origSize; - - this.window_container.ease({ - scale_x: scale, - scale_y: scale, - duration: animate ? WINDOW_SCALE_TIME : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - - this.emit('show-chrome'); - }, - - hideOverlay(animate) { - if (!this._overlayShown) - return; - this._overlayShown = false; - if (opt.ALWAYS_ACTIVATE_SELECTED_WINDOW && Main.overview._overview.controls._stateAdjustment.value < 1) { - this.get_parent()?.set_child_above_sibling(this, null); - this._activateSelected = true; - } - - if (!opt.ALWAYS_ACTIVATE_SELECTED_WINDOW) - this._restack(); - - // If we're supposed to animate and an animation in our direction - // is already happening, let that one continue - const ongoingTransition = this._title.get_transition('opacity'); - if (animate && - ongoingTransition && - ongoingTransition.get_interval().peek_final_value() === 0) - return; - - const toHide = [this._closeButton]; - - if (!opt.ALWAYS_SHOW_WIN_TITLES) - toHide.push(this._title); - - toHide.forEach(a => { - a.opacity = 255; - a.ease({ - opacity: 0, - duration: animate ? WINDOW_OVERLAY_FADE_TIME : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => a.hide(), - }); - }); - - if (this.window_container) { - this.window_container.ease({ - scale_x: 1, - scale_y: 1, - duration: animate ? WINDOW_SCALE_TIME : 0, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - }); - } - }, - - _onDestroy() { - // workaround for upstream bug - hideOverlay is called after windowPreview is destroyed, from the leave event callback - // hiding the preview now avoids firing the post-mortem leave event - this.hide(); - if (this._activateSelected) - this._activate(); - - this.metaWindow._delegate = null; - this._delegate = null; - - if (this._longPressLater) { - if (shellVersion >= 44) { - const laters = global.compositor.get_laters(); - laters.remove(this._longPressLater); - delete this._longPressLater; - } else { - Meta.later_remove(this._longPressLater); - delete this._longPressLater; - } - } - - if (this._idleHideOverlayId > 0) { - GLib.source_remove(this._idleHideOverlayId); - this._idleHideOverlayId = 0; - } - - if (this.inDrag) { - this.emit('drag-end'); - this.inDrag = false; - } - - if (this._stateAdjustmentSigId) - this._workspace.stateAdjustment.disconnect(this._stateAdjustmentSigId); - }, -}; diff --git a/extensions/vertical-workspaces/lib/workspaceAnimation.js b/extensions/vertical-workspaces/lib/workspaceAnimation.js deleted file mode 100644 index 07008c6..0000000 --- a/extensions/vertical-workspaces/lib/workspaceAnimation.js +++ /dev/null @@ -1,184 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * workspacesAnimation.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; -const Main = imports.ui.main; -const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; -const WorkspaceAnimation = imports.ui.workspaceAnimation; -const Me = imports.misc.extensionUtils.getCurrentExtension(); -const _Util = Me.imports.lib.util; - -// first reference to constant defined using const in other module returns undefined, the MonitorGroup const will remain empty and unused -let MonitorGroupDummy = WorkspaceAnimation.MonitorGroup; -MonitorGroupDummy = null; - -let _origBaseDistance; -let _wsAnimationSwipeBeginId; -let _wsAnimationSwipeUpdateId; -let _wsAnimationSwipeEndId; - -let _overrides; -let opt; -let _firstRun = true; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('workspaceAnimationModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - if (reset || !moduleEnabled) { - _connectWsAnimationSwipeTracker(true); - _overrideMonitorGroupProperty(true); - _overrides = null; - opt = null; - return; - } - - if (opt.STATIC_WS_SWITCHER_BG) { - _overrides = new _Util.Overrides(); - _overrideMonitorGroupProperty(); - _overrides.addOverride('WorkspaceAnimationMonitorGroup', WorkspaceAnimation.MonitorGroup.prototype, MonitorGroup); - } - - _connectWsAnimationSwipeTracker(); -} - -// remove spacing between workspaces during transition to remove flashing wallpaper between workspaces with maximized windows -function _overrideMonitorGroupProperty(reset = false) { - if (!_origBaseDistance) - _origBaseDistance = Object.getOwnPropertyDescriptor(WorkspaceAnimation.MonitorGroup.prototype, 'baseDistance').get; - - let getter; - if (reset) { - if (_origBaseDistance) - getter = { get: _origBaseDistance }; - } else { - getter = { - get() { - // const spacing = 100 * imports.gi.St.ThemeContext.get_for_stage(global.stage).scale_factor; - const spacing = 0; - if (global.workspace_manager.layout_rows === -1) - return this._monitor.height + spacing + (opt.PANEL_MODE ? Main.panel.height : 0); // compensation for hidden panel - else - return this._monitor.width + spacing; - }, - }; - } - - if (getter) - Object.defineProperty(WorkspaceAnimation.MonitorGroup.prototype, 'baseDistance', getter); -} - -const MonitorGroup = { - // injection to _init() - after__init() { - // we have two options to implement static bg feature - // one is adding background to monitorGroup - // but this one has disadvantage - sticky windows will be always on top of animated windows - // which is bad for conky, for example, that window should be always below - /* this._bgManager = new Background.BackgroundManager({ - container: this, - monitorIndex: this._monitor.index, - controlPosition: false, - });*/ - - // the second option is to make background of the monitorGroup transparent so the real desktop content will stay visible, - // hide windows that should be animated and keep only sticky windows - // we can keep certain sticky windows bellow and also extensions like DING (icons on desktop) will stay visible - this.set_style('background-color: transparent;'); - // stickyGroup holds the Always on Visible Workspace windows to keep them static and above other windows during animation - const stickyGroup = this.get_children()[1]; - stickyGroup._windowRecords.forEach(r => { - const metaWin = r.windowActor.metaWindow; - // conky is sticky but should never get above other windows during ws animation - // so we hide it from the overlay group, we will see the original if not covered by other windows - if (metaWin.wm_class === 'conky') - r.clone.opacity = 0; - }); - this._hiddenWindows = []; - // remove (hide) background wallpaper from the animation, we will see the original one - this._workspaceGroups.forEach(w => { - w._background.opacity = 0; - }); - // hide (scale to 0) all non-sticky windows, their clones will be animated - global.get_window_actors().forEach(actor => { - const metaWin = actor.metaWindow; - if (metaWin?.get_monitor() === this._monitor.index && !(metaWin?.wm_class === 'conky' && metaWin?.is_on_all_workspaces())) { //* && !w.is_on_all_workspaces()*/) { - // hide original window. we cannot use opacity since it also affects clones. - // scaling them to 0 works well - actor.scale_x = 0; - this._hiddenWindows.push(actor); - } - }); - - // restore all hidden windows at the end of animation - // todo - actors removed during transition need to be removed from the list to avoid access to destroyed actor - this.connect('destroy', () => { - this._hiddenWindows.forEach(actor => { - actor.scale_x = 1; - }); - }); - }, -}; - -function _connectWsAnimationSwipeTracker(reset = false) { - if (reset) { - if (_wsAnimationSwipeBeginId) { - Main.wm._workspaceAnimation._swipeTracker.disconnect(_wsAnimationSwipeBeginId); - _wsAnimationSwipeBeginId = 0; - } - if (_wsAnimationSwipeEndId) { - Main.wm._workspaceAnimation._swipeTracker.disconnect(_wsAnimationSwipeEndId); - _wsAnimationSwipeEndId = 0; - } - } else if (!_wsAnimationSwipeBeginId) { - // display ws switcher popup when gesture begins and connect progress - _wsAnimationSwipeBeginId = Main.wm._workspaceAnimation._swipeTracker.connect('begin', () => _connectWsAnimationProgress(true)); - // we want to be sure that popup with the final ws index show up when gesture ends - _wsAnimationSwipeEndId = Main.wm._workspaceAnimation._swipeTracker.connect('end', (tracker, duration, endProgress) => _connectWsAnimationProgress(false, endProgress)); - } -} - -function _connectWsAnimationProgress(connect, endProgress = null) { - if (Main.overview.visible) - return; - - if (connect && !_wsAnimationSwipeUpdateId) { - _wsAnimationSwipeUpdateId = Main.wm._workspaceAnimation._swipeTracker.connect('update', (tracker, progress) => _showWsSwitcherPopup(progress)); - } else if (!connect && _wsAnimationSwipeUpdateId) { - Main.wm._workspaceAnimation._swipeTracker.disconnect(_wsAnimationSwipeUpdateId); - _wsAnimationSwipeUpdateId = 0; - _showWsSwitcherPopup(Math.round(endProgress)); - } -} - -function _showWsSwitcherPopup(progress) { - if (Main.overview.visible) - return; - - const wsIndex = Math.round(progress); - if (Main.wm._workspaceSwitcherPopup === null) { - Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); - Main.wm._workspaceSwitcherPopup.connect('destroy', () => { - Main.wm._workspaceSwitcherPopup = null; - }); - } - - Main.wm._workspaceSwitcherPopup.display(wsIndex); -} diff --git a/extensions/vertical-workspaces/lib/workspaceSwitcherPopup.js b/extensions/vertical-workspaces/lib/workspaceSwitcherPopup.js deleted file mode 100644 index 972f35e..0000000 --- a/extensions/vertical-workspaces/lib/workspaceSwitcherPopup.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * V-Shell (Vertical Workspaces) - * workspacesSwitcherPopup.js - * - * @author GdH - * @copyright 2022 - 2023 - * @license GPL-3.0 - * - */ - -'use strict'; - -const Main = imports.ui.main; -const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const _Util = Me.imports.lib.util; -let _overrides; - -let opt; -let _firstRun = true; - -function update(reset = false) { - opt = Me.imports.lib.settings.opt; - const moduleEnabled = opt.get('workspaceSwitcherPopupModule', true); - reset = reset || !moduleEnabled; - - // don't even touch this module if disabled - if (_firstRun && reset) - return; - - _firstRun = false; - - if (_overrides) - _overrides.removeAll(); - - if (reset) { - _overrides = null; - opt = null; - return; - } - - _overrides = new _Util.Overrides(); - - const enabled = global.settings.get_strv('enabled-extensions'); - const allowWsPopupInjection = !(enabled.includes('workspace-switcher-manager@G-dH.github.com') || enabled.includes('WsSwitcherPopupManager@G-dH.github.com-dev')); - if (allowWsPopupInjection) { // 1-VERTICAL, 0-HORIZONTAL - _overrides.addOverride('WorkspaceSwitcherPopup', WorkspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype, WorkspaceSwitcherPopupOverride); - } -} - -const WorkspaceSwitcherPopupOverride = { - // injection to _init() - after__init() { - if (opt.ORIENTATION) { // 1-VERTICAL, 0-HORIZONTAL - this._list.vertical = true; - } - this._list.set_style('margin: 0;'); - this.remove_constraint(this.get_constraints()[0]); - }, - - // injection to display() - after_display() { - if (opt.WS_SW_POPUP_MODE) - this._setPopupPosition(); - else - this.opacity = 0; - }, - - _setPopupPosition() { - let workArea; - if (opt.WS_SW_POPUP_MODE === 1) { - // workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);*/ - workArea = global.display.get_monitor_geometry(Main.layoutManager.primaryIndex); - } else { - // workArea = Main.layoutManager.getWorkAreaForMonitor(global.display.get_current_monitor()); - workArea = global.display.get_monitor_geometry(global.display.get_current_monitor()); - } - - let [, natHeight] = this.get_preferred_height(global.screen_width); - let [, natWidth] = this.get_preferred_width(natHeight); - let h = opt.WS_SW_POPUP_H_POSITION; - let v = opt.WS_SW_POPUP_V_POSITION; - this.x = workArea.x + Math.floor((workArea.width - natWidth) * h); - this.y = workArea.y + Math.floor((workArea.height - natHeight) * v); - this.set_position(this.x, this.y); - }, -}; diff --git a/extensions/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml b/extensions/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml deleted file mode 100644 index 1bb4269..0000000 --- a/extensions/vertical-workspaces/schemas/org.gnome.shell.extensions.vertical-workspaces.gschema.xml +++ /dev/null @@ -1,351 +0,0 @@ - - - - - 0 - - - false - - - 2 - - - 350 - - - 100 - - - 100 - - - false - - - 0 - - - 0 - - - 1 - - - 1 - - - 1 - - - 0 - - - 3 - - - false - - - 0 - - - 2 - - - false - - - false - - - true - - - false - - - 13 - - - 13 - - - 13 - - - 48 - - - 80 - - - 17 - - - false - - - true - - - false - - - 30 - - - true - - - 0 - - - 40 - - - 95 - - - false - - - 4 - - - 4 - - - 1 - - - 1 - - - 100 - - - true - - - 1 - - - true - - - 2 - - - 0 - - - 96 - - - 104 - - - -1 - - - 0 - - - 0 - - - 0 - - - 2 - - - -1 - - - 0 - - - 0 - - - 12 - - - true - - - 0 - - - 3 - - - false - - - false - - - 90 - - - 1 - - - 1 - - - 1 - - - true - - - true - - - false - - - 5 - - - 0 - - - 0 - - - 0 - - - 50 - - - 95 - - - 1 - - - 1 - - - 2 - - - 6 - - - 1 - - - 0 - - - false - - - true - - - false - - - true - - - 1 - - - 1 - - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - - false - - - - "" - - - {'workspaceThumbnailsPosition': '1', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '3', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '-80', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '-80', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '0', 'secWsThumbnailScale': '13', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'appGridPageWidthScale': '100', 'dashBgOpacity': '80', 'dashBgRadius': '17', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'false', 'showBgInOverview': 'true', 'overviewBgBrightness': '30', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '0', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'fixUbuntuDock': 'true', 'winPreviewIconSize': '1', 'alwaysShowWinTitles': 'false', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '4', 'appGridFolderRows': '3', 'appGridSpacing': '12', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appGridNamesMode': '0', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'false', 'searchWindowsEnable': 'true', 'searchRecentFilesEnable': 'true', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '50', 'wsSwPopupMode': '1', 'favoritesNotify': '1', 'notificationPosition': '1', 'hotCornerAction': '1', 'hotCornerPosition': '0', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'workspaceThumbnailsModule': 'true', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'winAttentionHandlerModule': 'true', 'swipeTrackerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true'} - - - - "" - - - {'workspaceThumbnailsPosition': '5', 'wsMaxSpacing': '80', 'wsPreviewScale': '100', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '0', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '64', 'dashShowWindowsIcon': '2', 'dashShowRecentFilesIcon': '2', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '5', 'wsThumbnailScaleAppGrid': '15', 'secWsThumbnailScale': '13', 'showSearchEntry': 'true', 'centerSearch': 'true', 'centerAppGrid': 'true', 'appGridPageWidthScale': '80', 'dashBgOpacity': '100', 'dashBgRadius': '17', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'true', 'showBgInOverview': 'false', 'overviewBgBrightness': '30', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '0', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'fixUbuntuDock': 'true', 'winPreviewIconSize': '0', 'alwaysShowWinTitles': 'false', 'startupState': '0', 'overviewMode': '0', 'workspaceSwitcherAnimation': '0', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '3', 'appGridFolderRows': '3', 'appGridSpacing': '12', 'appGridFolderIconGrid': '2', 'appGridContent': '2', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appGridNamesMode': '0', 'appGridActivePreview': 'false', 'appGridFolderCenter': 'false', 'searchWindowsEnable': 'true', 'searchRecentFilesEnable': 'true', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '0', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'favoritesNotify': '1', 'notificationPosition': '1', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'workspaceThumbnailsModule': 'true', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'winAttentionHandlerModule': 'true', 'swipeTrackerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true'} - - - - "" - - - {'workspaceThumbnailsPosition': '0', 'wsMaxSpacing': '350', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '0', 'dashPositionAdjust': '-100', 'wsTmbPositionAdjust': '-100', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '0', 'wsThumbnailScale': '13', 'wsThumbnailScaleAppGrid': '0', 'secWsThumbnailScale': '13', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'appGridPageWidthScale': '90', 'dashBgOpacity': '85', 'dashBgRadius': '17', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'false', 'showBgInOverview': 'true', 'overviewBgBrightness': '95', 'overviewBgBlurSigma': '0', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'fixUbuntuDock': 'true', 'winPreviewIconSize': '1', 'alwaysShowWinTitles': 'true', 'startupState': '2', 'overviewMode': '1', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridSpacing': '12', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appGridNamesMode': '0', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'searchWindowsEnable': 'true', 'searchRecentFilesEnable': 'true', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '2', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'favoritesNotify': '0', 'notificationPosition': '2', 'hotCornerAction': '1', 'hotCornerPosition': '1', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'true', 'alwaysActivateSelectedWindow': 'false', 'workspaceThumbnailsModule': 'true', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'winAttentionHandlerModule': 'true', 'swipeTrackerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true'} - - - - "" - - - {'workspaceThumbnailsPosition': '6', 'wsMaxSpacing': '65', 'wsPreviewScale': '95', 'secWsPreviewScale': '100', 'secWsPreviewShift': 'false', 'wsThumbnailsFull': 'false', 'secWsThumbnailsPosition': '2', 'dashPosition': '2', 'dashPositionAdjust': '0', 'wsTmbPositionAdjust': '0', 'showWsTmbLabels': '3', 'showWsTmbLabelsOnHover': 'false', 'closeWsButtonMode': '2', 'secWsTmbPositionAdjust': '0', 'dashMaxIconSize': '48', 'dashShowWindowsIcon': '1', 'dashShowRecentFilesIcon': '1', 'centerDashToWs': 'false', 'showAppsIconPosition': '1', 'wsThumbnailScale': '10', 'wsThumbnailScaleAppGrid': '0', 'secWsThumbnailScale': '10', 'showSearchEntry': 'false', 'centerSearch': 'true', 'centerAppGrid': 'false', 'appGridPageWidthScale': '90', 'dashBgOpacity': '85', 'dashBgRadius': '17', 'enablePageShortcuts': 'true', 'showWsSwitcherBg': 'true', 'showWsPreviewBg': 'true', 'showBgInOverview': 'true', 'overviewBgBrightness': '100', 'overviewBgBlurSigma': '80', 'appGridBgBlurSigma': '80', 'smoothBlurTransitions': 'false', 'appGridAnimation': '4', 'searchViewAnimation': '4', 'workspaceAnimation': '1', 'animationSpeedFactor': '100', 'fixUbuntuDock': 'true', 'winPreviewIconSize': '2', 'alwaysShowWinTitles': 'true', 'startupState': '2', 'overviewMode': '2', 'workspaceSwitcherAnimation': '1', 'searchIconSize': '96', 'searchViewScale': '104', 'appGridIconSize': '-1', 'appGridColumns': '0', 'appGridRows': '0', 'appGridFolderIconSize': '-1', 'appGridFolderColumns': '0', 'appGridFolderRows': '0', 'appGridSpacing': '12', 'appGridFolderIconGrid': '3', 'appGridContent': '0', 'appGridIncompletePages': 'false', 'appGridOrder': '0', 'appGridNamesMode': '0', 'appGridActivePreview': 'true', 'appGridFolderCenter': 'false', 'searchWindowsEnable': 'true', 'searchRecentFilesEnable': 'true', 'searchFuzzy': 'false', 'searchMaxResultsRows': '5', 'dashShowWindowsBeforeActivation': '1', 'panelVisibility': '0', 'panelPosition': '0', 'windowAttentionMode': '2', 'wsSwPopupHPosition': '50', 'wsSwPopupVPosition': '95', 'wsSwPopupMode': '1', 'favoritesNotify': '0', 'notificationPosition': '1', 'hotCornerAction': '1', 'hotCornerPosition': '6', 'hotCornerFullscreen': 'true', 'hotCornerRipples': 'false', 'alwaysActivateSelectedWindow': 'false', 'workspaceThumbnailsModule': 'true', 'workspaceSwitcherPopupModule': 'true', 'workspaceAnimationModule': 'true', 'workspaceModule': 'true', 'windowManagerModule': 'true', 'windowPreviewModule': 'true', 'winAttentionHandlerModule': 'true', 'swipeTrackerModule': 'true', 'searchModule': 'true', 'panelModule': 'true', 'messageTrayModule': 'true', 'layoutModule': 'true', 'dashModule': 'true', 'appFavoritesModule': 'true', 'appDisplayModule': 'true'} - - - - diff --git a/extensions/vertical-workspaces/stylesheet.css b/extensions/vertical-workspaces/stylesheet.css deleted file mode 100644 index 178cf1d..0000000 --- a/extensions/vertical-workspaces/stylesheet.css +++ /dev/null @@ -1,224 +0,0 @@ -/* -* V-Shell (Vertical Workspaces) -* stylesheet.css -*/ - -/* General dash */ -#dash.vertical { - margin-right: 0px; - margin-left: 0px; - padding: 0px 0; -} - -#dash.vertical .app-well-app, -#dash.vertical .show-apps { - padding-right: 8px; - padding-left: 8px; - /*spacing between icons*/ - padding-top: 1px; - padding-bottom: 1px; - margin: 0px; -} - -#dash.vertical .dash-separator { - height: 1px; - margin-right: 2px; - margin-left: 2px; - margin-top: 4px; - margin-bottom: 4px; - background-color: transparentize(#eeeeec, 0.7); -} - -#dash.vertical .overview-icon { - padding: 5px 0; -} - -#dash.vertical .app-well-app-running-dot { - margin: 4px 0px; - width: 2px; - height: 16px; -} - -#dash.vertical .dash-background { - margin: 0; -} - -#dash.vertical-left .dash-background { - border-radius: 0 18px 18px 0; - border-left: 0px; - padding: 8px 12px 8px 4px; -} - -#dash.vertical-right .dash-background { - border-radius: 18px 0 0 18px; - border-right: 0px; - padding: 8px 4px 8px 12px; -} - -/* default horizontal dash */ -.v-shell-dash-background { - padding: 6px; -} - -.app-well-app { - margin-left: 1px; - margin-right: 1px; -} - -.app-well-app-running-dot { - margin-bottom: 6px; - width: 16px; - height: 2px; -} - -/* add shadow to the app grid app label to be readable if it overlaps light icon below */ -.overview-icon-with-label { - text-shadow: 1px 1px 3px rgba(33, 33, 33, 1); -} - -/*adjustment for the vertical ws switcher indicator popup*/ -.ws-switcher-indicator { - padding: 3px; - margin: 5px; -} - -.ws-switcher-indicator:active { - padding: 5px; - margin: 3px; -} - -/*ws thumbnails captions*/ -.ws-tmb-label { - padding: 2px; - color: rgb(255, 255, 255); - background-color: rgba(10,10,10,0.8); - text-align: center; -} - -.workspace-thumbnail-indicator { - border-radius: 6px; -} - -.ws-tmb-labeled { - border: 0px; -} - -/*app grid page indicatos*/ -.page-indicator-icon { - margin: 10px 10px 10px 10px; -} - -.page-indicator { - padding: 0px; -} - -/* GS 43 App Grid - indicators that show up when dragging icon */ -.prevPageIndicator { - background: rgba(255, 255, 255, 0.1); - background-gradient-start: rgba(255, 255, 255, 0.1); - background-gradient-end: transparent; - background-gradient-direction: vertical; - border-radius: 100px 100px 0px 0px; -} -.nextPageIndicator { - background: rgba(255, 255, 255, 0.1); - background-gradient-start: transparent; - background-gradient-end: rgba(255, 255, 255, 0.1); - background-gradient-direction: vertical; - border-radius: 0px 0px 100px 100px; - -} - -.search-entry, .search-entry-om2 { - padding: 5px; -} - -.search-entry-om2 { - border: 5px; -} - -.search-section-content { - background-color: rgba(20, 20, 20, 0.8); - color: white; -} - -/* for static ws mode */ -.search-section-content-om2 { - background-color: rgba(20, 20, 20, 0.95); - color: white; - border-radius: 20px; - border: 1px, rgb(40, 40, 40); - padding-top: 12px; -} - -/* "no results" / "searching..." text*/ -.search-statustext { - background-color: rgba(23,23,23, 0.95); - color: white; - margin-top: 50px; - padding: 30px; - border-radius: 20px; - text-shadow: 0px 0px 5px rgb(23, 23, 23); -} - -.transparent-panel { - background-color: transparent; -} - -/*reduce spacing between app icons in search results*/ -.grid-search-results { - spacing: 2px; -} - -.panel { - text-shadow: 1px 1px 3px rgba(33, 33, 33, 1); -} - -/* hide vertical scroll bar, it's distracting in the search results */ -StButton#vhandle { - background-color: transparent; -} - -.show-apps-icon-horizontal-hide { - width: 0; - margin: 0; - spacing: 0; -} - -.show-apps-icon-vertical-hide { - height: 0; - margin: 0; - spacing: 0; -} - -.workspace-close-button { - color: white; - background-color: dimgrey; - width: 18px; - height: 18px; - padding: 2px; - margin: 2px; - box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); - border: 0px; - border-radius: 35px; -} - -.workspace-close-button-hover { - background-color: rgb(255, 0, 0); -} - -/* ws tmb placeholder */ -.placeholder-vertical { - background-size: contain; - height: 18px; -} - -/* reduce size of hot corner ripples to half */ -.ripple-box { - width: 26px; - height: 26px; -} - -.osd-window { - margin: 4em; -}