summaryrefslogtreecommitdiff
path: root/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js')
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js235
1 files changed, 235 insertions, 0 deletions
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js
new file mode 100644
index 0000000..790ef64
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js
@@ -0,0 +1,235 @@
+import {Atk, Clutter} from './dependencies/gi.js';
+
+import {
+ Main,
+ SearchController,
+ Workspace,
+ WorkspaceThumbnail,
+} from './dependencies/shell/ui.js';
+
+import {Utils} from './imports.js';
+
+const APP_SPREAD_RESTORE_ACTION = 'dock-app-spread-restore';
+
+export class AppSpread {
+ constructor() {
+ this.app = null;
+ this.supported = true;
+ this.windows = [];
+
+ // fail early and do nothing, if mandatory gnome shell functions are missing
+ if (Main.overview.isDummy ||
+ !Workspace?.Workspace?.prototype._isOverviewWindow ||
+ !WorkspaceThumbnail?.WorkspaceThumbnail?.prototype._isOverviewWindow) {
+ log('Dash to dock: Unable to temporarily replace shell functions ' +
+ 'for app spread - using previews instead');
+ this.supported = false;
+ return;
+ }
+
+ this._signalHandlers = new Utils.GlobalSignalsHandler();
+ this._methodInjections = new Utils.InjectionsHandler();
+ this._vfuncInjections = new Utils.VFuncInjectionsHandler();
+ }
+
+ get isInAppSpread() {
+ return !!this.app;
+ }
+
+ destroy() {
+ if (!this.supported)
+ return;
+ this._hideAppSpread();
+ this._signalHandlers.destroy();
+ this._methodInjections.destroy();
+ this._vfuncInjections.destroy();
+ }
+
+ toggle(app) {
+ const newApp = this.app !== app;
+ if (this.app)
+ Main.overview.hide(); // also triggers hook 'hidden'
+
+ if (app && newApp)
+ this._showAppSpread(app);
+ }
+
+ _updateWindows() {
+ this.windows = this.app.get_windows();
+ }
+
+ _restoreDefaultWindows() {
+ const {workspaceManager} = global;
+
+ for (let i = 0; i < workspaceManager.nWorkspaces; i++) {
+ const metaWorkspace = workspaceManager.get_workspace_by_index(i);
+ metaWorkspace.list_windows().forEach(w => metaWorkspace.emit('window-added', w));
+ }
+ }
+
+ _filterWindows() {
+ const {workspaceManager} = global;
+
+ for (let i = 0; i < workspaceManager.nWorkspaces; i++) {
+ const metaWorkspace = workspaceManager.get_workspace_by_index(i);
+ metaWorkspace.list_windows().filter(w => !this.windows.includes(w)).forEach(
+ w => metaWorkspace.emit('window-removed', w));
+ }
+ }
+
+ _restoreDefaultOverview() {
+ this._hideAppSpread();
+ this._restoreDefaultWindows();
+ }
+
+ _showAppSpread(app) {
+ if (this.isInAppSpread)
+ return;
+
+ // Checked in overview "hide" event handler _hideAppSpread
+ this.app = app;
+ this._updateWindows();
+
+ // we need to hook into overview 'hidden' like this, in case app spread
+ // overview is hidden by choosing another app it should then do its
+ // cleanup too
+ this._signalHandlers.add(Main.overview, 'hidden', () => this._hideAppSpread());
+
+ const appSpread = this;
+ this._methodInjections.add([
+ // Filter workspaces to only show current app windows
+ Workspace.Workspace.prototype, '_isOverviewWindow',
+ function (originalMethod, window) {
+ /* eslint-disable no-invalid-this */
+ const isOverviewWindow = originalMethod.call(this, window);
+ return isOverviewWindow && appSpread.windows.includes(window);
+ /* eslint-enable no-invalid-this */
+ },
+ ],
+ [
+ // Filter thumbnails to only show current app windows
+ WorkspaceThumbnail.WorkspaceThumbnail.prototype, '_isOverviewWindow',
+ function (originalMethod, windowActor) {
+ /* eslint-disable no-invalid-this */
+ const isOverviewWindow = originalMethod.call(this, windowActor);
+ return isOverviewWindow && appSpread.windows.includes(windowActor.metaWindow);
+ /* eslint-enable no-invalid-this */
+ },
+ ]);
+
+ const activitiesButton = Main.panel.statusArea?.activities;
+
+ if (activitiesButton) {
+ this._signalHandlers.add(Main.overview, 'showing', () => {
+ activitiesButton.remove_style_pseudo_class('overview');
+ activitiesButton.remove_accessible_state(Atk.StateType.CHECKED);
+ });
+
+ let hasEventVFunc = false;
+ try {
+ hasEventVFunc = !!activitiesButton.constructor.prototype.vfunc_event;
+ } catch {}
+
+ if (hasEventVFunc) {
+ this._vfuncInjections.add([
+ activitiesButton.constructor.prototype,
+ 'event',
+ function (event) {
+ if (event.type() === Clutter.EventType.TOUCH_END ||
+ event.type() === Clutter.EventType.BUTTON_RELEASE) {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ }
+ return Clutter.EVENT_PROPAGATE;
+ },
+ ]);
+ } else {
+ /* Shell >= 50 uses gestures on the activities button */
+ const click = new Clutter.ClickGesture();
+ click.set_recognize_on_press(true);
+ click.set_enabled(true);
+ click.connect('recognize', () => {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ });
+ activitiesButton.add_action_with_name(APP_SPREAD_RESTORE_ACTION, click);
+ }
+
+ this._vfuncInjections.add([
+ activitiesButton.constructor.prototype,
+ 'key_release_event',
+ function (keyEvent) {
+ const keyval = keyEvent.get_key_symbol?.() ?? keyEvent.keyval;
+ if (keyval === Clutter.KEY_Return || keyval === Clutter.KEY_space) {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ }
+ return Clutter.EVENT_PROPAGATE;
+ },
+ ]);
+ }
+
+ this._signalHandlers.add(Main.overview.dash.showAppsButton, 'notify::checked', () => {
+ if (Main.overview.dash.showAppsButton.checked)
+ this._restoreDefaultOverview();
+ });
+
+ // If closing windows in AppSpread, and only one window left:
+ // exit app spread and focus remaining window (handled in _hideAppSpread)
+ this._signalHandlers.add(this.app, 'windows-changed', () => {
+ this._updateWindows();
+
+ if (this.windows.length <= 1)
+ Main.overview.hide();
+ });
+
+ this._disableSearch();
+
+ Main.overview.show();
+ }
+
+ _hideAppSpread() {
+ if (!this.isInAppSpread)
+ return;
+
+ if (Main.overview.visible) {
+ Main.panel.statusArea?.activities.add_style_pseudo_class('overview');
+ Main.panel.statusArea?.activities.add_accessible_state(Atk.StateType.CHECKED);
+ }
+
+ // Restore original behaviour
+ this.app = null;
+ this._enableSearch();
+ this._methodInjections.clear();
+ this._signalHandlers.clear();
+ this._vfuncInjections.clear();
+ Main.panel.statusArea?.activities.remove_action_by_name(APP_SPREAD_RESTORE_ACTION);
+
+ // Check reason for leaving AppSpread was closing app windows and only one window left...
+ if (this.windows.length === 1)
+ Main.activateWindow(this.windows[0]);
+
+ this.windows = [];
+ }
+
+ _disableSearch() {
+ if (Main.overview.searchEntry) {
+ Main.overview.searchEntry.opacity = 0;
+ Main.overview.searchEntry.reactive = false;
+ }
+
+ if (!SearchController.SearchController.prototype._shouldTriggerSearch)
+ return;
+
+ this._methodInjections.add(
+ SearchController.SearchController.prototype,
+ '_shouldTriggerSearch', () => false);
+ }
+
+ _enableSearch() {
+ if (Main.overview.searchEntry) {
+ Main.overview.searchEntry.opacity = 255;
+ Main.overview.searchEntry.reactive = true;
+ }
+ }
+}