From cb8c2eb27f408f9698abb1784f4dbb3047b73c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Sun, 6 Aug 2023 13:20:38 +0200 Subject: [PATCH] windowsNavigator: Use InjectionManager instead of custom classes Once the shell is ported to ESM, it will no longer be possible to replace entire classes (even when exported). Prepare for that by overriding methods of the regular classes, instead of creating custom subclasses. Part-of: --- extensions/windowsNavigator/extension.js | 515 ++++++++++++----------- 1 file changed, 271 insertions(+), 244 deletions(-) diff --git a/extensions/windowsNavigator/extension.js b/extensions/windowsNavigator/extension.js index 2ae74cf8..32544b9b 100644 --- a/extensions/windowsNavigator/extension.js +++ b/extensions/windowsNavigator/extension.js @@ -1,264 +1,291 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ import Clutter from 'gi://Clutter'; import Graphene from 'gi://Graphene'; -import GObject from 'gi://GObject'; import St from 'gi://St'; +import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; + const Main = imports.ui.main; const OverviewControls = imports.ui.overviewControls; -const Workspace = imports.ui.workspace; -const WorkspacesView = imports.ui.workspacesView; +const {WindowPreview} = imports.ui.windowPreview; +const {Workspace} = imports.ui.workspace; +const {WorkspacesView} = imports.ui.workspacesView; const WINDOW_SLOT = 4; -class MyWorkspace extends Workspace.Workspace { - static { - GObject.registerClass(this); - } - - constructor(...args) { - super(...args); - - if (this.metaWorkspace && this.metaWorkspace.index() < 9) { - this._tip = new St.Label({ - style_class: 'extension-windowsNavigator-window-tooltip', - visible: false, - }); - this.add_actor(this._tip); - - this.connect('notify::scale-x', () => { - this._tip.set_scale(1 / this.scale_x, 1 / this.scale_x); - }); - } else { - this._tip = null; - } - } - - vfunc_allocate(box) { - super.vfunc_allocate(box); - - if (this._tip) - this._tip.allocate_preferred_size(0, 0); - } - - showTooltip() { - if (!this._tip) - return; - this._tip.text = (this.metaWorkspace.index() + 1).toString(); - this._tip.show(); - this.set_child_below_sibling(this._tip, null); - } - - hideTooltip() { - if (this._tip) - this._tip.hide(); - } - - getWindowWithTooltip(id) { - const {layoutManager} = this._container; - const slot = layoutManager._windowSlots[id - 1]; - return slot ? slot[WINDOW_SLOT].metaWindow : null; - } - - showWindowsTooltips() { - const {layoutManager} = this._container; - for (let i = 0; i < layoutManager._windowSlots.length; i++) { - if (layoutManager._windowSlots[i]) - layoutManager._windowSlots[i][WINDOW_SLOT].showTooltip(`${i + 1}`); - } - } - - hideWindowsTooltips() { - const {layoutManager} = this._container; - for (let i in layoutManager._windowSlots) { - if (layoutManager._windowSlots[i]) - layoutManager._windowSlots[i][WINDOW_SLOT].hideTooltip(); - } - } - - // overriding _addWindowClone to apply the tooltip patch on the cloned - // windowPreview - _addWindowClone(metaWindow) { - const clone = super._addWindowClone(metaWindow); - - // appling the tooltip patch - (function patchPreview() { - this._text = new St.Label({ - style_class: 'extension-windowsNavigator-window-tooltip', - visible: false, - }); - - this._text.add_constraint(new Clutter.BindConstraint({ - source: this.windowContainer, - coordinate: Clutter.BindCoordinate.POSITION, - })); - this._text.add_constraint(new Clutter.AlignConstraint({ - source: this.windowContainer, - align_axis: Clutter.AlignAxis.X_AXIS, - pivot_point: new Graphene.Point({x: 0.5, y: -1}), - factor: this._closeButtonSide === St.Side.LEFT ? 1 : 0, - })); - this._text.add_constraint(new Clutter.AlignConstraint({ - source: this.windowContainer, - align_axis: Clutter.AlignAxis.Y_AXIS, - pivot_point: new Graphene.Point({x: -1, y: 0.5}), - factor: 0, - })); - - this.add_child(this._text); - }).call(clone); - - clone.showTooltip = function (text) { - this._text.set({text}); - this._text.show(); - }; - - clone.hideTooltip = function () { - if (this._text && this._text.visible) - this._text.hide(); - }; - - return clone; - } -} - -class MyWorkspacesView extends WorkspacesView.WorkspacesView { - static { - GObject.registerClass(this); - } - - constructor(...args) { - super(...args); - - this._pickWorkspace = false; - this._pickWindow = false; - global.stage.connectObject( - 'key-press-event', this._onKeyPress.bind(this), - 'key-release-event', this._onKeyRelease.bind(this), - this); - } - - _hideTooltips() { - if (global.stage.get_key_focus() === global.stage) - global.stage.set_key_focus(this._prevFocusActor); - this._pickWindow = false; - for (let i = 0; i < this._workspaces.length; i++) - this._workspaces[i].hideWindowsTooltips(); - } - - _hideWorkspacesTooltips() { - global.stage.set_key_focus(this._prevFocusActor); - this._pickWorkspace = false; - for (let i = 0; i < this._workspaces.length; i++) - this._workspaces[i].hideTooltip(); - } - - _onKeyRelease(s, o) { - if (this._pickWindow && - (o.get_key_symbol() === Clutter.KEY_Alt_L || - o.get_key_symbol() === Clutter.KEY_Alt_R)) - this._hideTooltips(); - if (this._pickWorkspace && - (o.get_key_symbol() === Clutter.KEY_Control_L || - o.get_key_symbol() === Clutter.KEY_Control_R)) - this._hideWorkspacesTooltips(); - } - - _onKeyPress(s, o) { - const {ControlsState} = OverviewControls; - if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER) - return false; - - let workspaceManager = global.workspace_manager; - - if ((o.get_key_symbol() === Clutter.KEY_Alt_L || - o.get_key_symbol() === Clutter.KEY_Alt_R) && - !this._pickWorkspace) { - this._prevFocusActor = global.stage.get_key_focus(); - global.stage.set_key_focus(null); - this._active = workspaceManager.get_active_workspace_index(); - this._pickWindow = true; - this._workspaces[workspaceManager.get_active_workspace_index()].showWindowsTooltips(); - return true; - } - if ((o.get_key_symbol() === Clutter.KEY_Control_L || - o.get_key_symbol() === Clutter.KEY_Control_R) && - !this._pickWindow) { - this._prevFocusActor = global.stage.get_key_focus(); - global.stage.set_key_focus(null); - this._pickWorkspace = true; - for (let i = 0; i < this._workspaces.length; i++) - this._workspaces[i].showTooltip(); - return true; - } - - if (global.stage.get_key_focus() !== global.stage) - return false; - - // ignore shift presses, they're required to get numerals in azerty keyboards - if ((this._pickWindow || this._pickWorkspace) && - (o.get_key_symbol() === Clutter.KEY_Shift_L || - o.get_key_symbol() === Clutter.KEY_Shift_R)) - return true; - - if (this._pickWindow) { - if (this._active !== workspaceManager.get_active_workspace_index()) { - this._hideTooltips(); - return false; - } - - let c = o.get_key_symbol() - Clutter.KEY_KP_0; - if (c > 9 || c <= 0) { - c = o.get_key_symbol() - Clutter.KEY_0; - if (c > 9 || c <= 0) { - this._hideTooltips(); - log(c); - return false; - } - } - - let win = this._workspaces[this._active].getWindowWithTooltip(c); - this._hideTooltips(); - - if (win) - Main.activateWindow(win, global.get_current_time()); - - return true; - } - if (this._pickWorkspace) { - let c = o.get_key_symbol() - Clutter.KEY_KP_0; - if (c > 9 || c <= 0) { - c = o.get_key_symbol() - Clutter.KEY_0; - if (c > 9 || c <= 0) { - this._hideWorkspacesTooltips(); - return false; - } - } - - let workspace = this._workspaces[c - 1]; - if (workspace !== undefined) - workspace.metaWorkspace.activate(global.get_current_time()); - - this._hideWorkspacesTooltips(); - return true; - } - return false; - } -} - export default class Extension { constructor() { - this._origWorkspace = Workspace.Workspace; - this._origWorkspacesView = WorkspacesView.WorkspacesView; + this._injectionManager = new InjectionManager(); } enable() { - Workspace.Workspace = MyWorkspace; - WorkspacesView.WorkspacesView = MyWorkspacesView; + const previewProto = WindowPreview.prototype; + + this._injectionManager.overrideMethod(previewProto, '_init', originalMethod => { + /* eslint-disable no-invalid-this */ + return function (...args) { + originalMethod.call(this, ...args); + + this._text = new St.Label({ + style_class: 'extension-windowsNavigator-window-tooltip', + visible: false, + }); + + this._text.add_constraint(new Clutter.BindConstraint({ + source: this.windowContainer, + coordinate: Clutter.BindCoordinate.POSITION, + })); + this._text.add_constraint(new Clutter.AlignConstraint({ + source: this.windowContainer, + align_axis: Clutter.AlignAxis.X_AXIS, + pivot_point: new Graphene.Point({x: 0.5, y: -1}), + factor: this._closeButtonSide === St.Side.LEFT ? 1 : 0, + })); + this._text.add_constraint(new Clutter.AlignConstraint({ + source: this.windowContainer, + align_axis: Clutter.AlignAxis.Y_AXIS, + pivot_point: new Graphene.Point({x: -1, y: 0.5}), + factor: 0, + })); + + this.add_child(this._text); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(previewProto, 'showTooltip', () => { + /* eslint-disable no-invalid-this */ + return function (text) { + this._text.set({text}); + this._text.show(); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(previewProto, 'hideTooltip', () => { + /* eslint-disable no-invalid-this */ + return function () { + this._text?.hide(); + }; + /* eslint-enable */ + }); + + const workspaceProto = Workspace.prototype; + this._injectionManager.overrideMethod(workspaceProto, '_init', originalMethod => { + /* eslint-disable no-invalid-this */ + return function (...args) { + originalMethod.call(this, ...args); + + if (this.metaWorkspace && this.metaWorkspace.index() < 9) { + this._tip = new St.Label({ + style_class: 'extension-windowsNavigator-window-tooltip', + visible: false, + }); + this.add_actor(this._tip); + + this.connect('notify::scale-x', () => { + this._tip.set_scale(1 / this.scale_x, 1 / this.scale_x); + }); + } else { + this._tip = null; + } + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(workspaceProto, 'vfunc_allocate', originalMethod => { + /* eslint-disable no-invalid-this */ + return function (box) { + originalMethod.call(this, box); + + this._tip?.allocate_preferred_size(0, 0); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(workspaceProto, 'showTooltip', () => { + /* eslint-disable no-invalid-this */ + return function () { + if (!this._tip) + return; + this._tip.text = (this.metaWorkspace.index() + 1).toString(); + this._tip.show(); + this.set_child_below_sibling(this._tip, null); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(workspaceProto, 'hideTooltip', () => { + /* eslint-disable no-invalid-this */ + return function () { + this._tip?.hide(); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(workspaceProto, 'getWindowWithTooltip', () => { + /* eslint-disable no-invalid-this */ + return function (id) { + const {layoutManager} = this._container; + const slot = layoutManager._windowSlots[id - 1]; + return slot ? slot[WINDOW_SLOT].metaWindow : null; + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(workspaceProto, 'showWindowsTooltips', () => { + /* eslint-disable no-invalid-this */ + return function () { + const {layoutManager} = this._container; + for (let i = 0; i < layoutManager._windowSlots.length; i++) { + if (layoutManager._windowSlots[i]) + layoutManager._windowSlots[i][WINDOW_SLOT].showTooltip(`${i + 1}`); + } + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(workspaceProto, 'hideWindowsTooltips', () => { + /* eslint-disable no-invalid-this */ + return function () { + const {layoutManager} = this._container; + for (let i in layoutManager._windowSlots) { + if (layoutManager._windowSlots[i]) + layoutManager._windowSlots[i][WINDOW_SLOT].hideTooltip(); + } + }; + /* eslint-enable */ + }); + + const viewProto = WorkspacesView.prototype; + this._injectionManager.overrideMethod(viewProto, '_init', originalMethod => { + /* eslint-disable no-invalid-this */ + return function (...args) { + originalMethod.call(this, ...args); + + this._pickWorkspace = false; + this._pickWindow = false; + global.stage.connectObject( + 'key-press-event', this._onKeyPress.bind(this), + 'key-release-event', this._onKeyRelease.bind(this), + this); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(viewProto, '_hideTooltips', () => { + /* eslint-disable no-invalid-this */ + return function () { + if (global.stage.get_key_focus() === global.stage) + global.stage.set_key_focus(this._prevFocusActor); + this._pickWindow = false; + for (let i = 0; i < this._workspaces.length; i++) + this._workspaces[i].hideWindowsTooltips(); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(viewProto, '_hideWorkspacesTooltips', () => { + /* eslint-disable no-invalid-this */ + return function () { + global.stage.set_key_focus(this._prevFocusActor); + this._pickWorkspace = false; + for (let i = 0; i < this._workspaces.length; i++) + this._workspaces[i].hideTooltip(); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(viewProto, '_onKeyRelease', () => { + /* eslint-disable no-invalid-this */ + return function (actor, event) { + if (this._pickWindow && + (event.get_key_symbol() === Clutter.KEY_Alt_L || + event.get_key_symbol() === Clutter.KEY_Alt_R)) + this._hideTooltips(); + if (this._pickWorkspace && + (event.get_key_symbol() === Clutter.KEY_Control_L || + event.get_key_symbol() === Clutter.KEY_Control_R)) + this._hideWorkspacesTooltips(); + }; + /* eslint-enable */ + }); + this._injectionManager.overrideMethod(viewProto, '_onKeyPress', () => { + /* eslint-disable no-invalid-this */ + return function (actor, event) { + const {ControlsState} = OverviewControls; + if (this._overviewAdjustment.value !== ControlsState.WINDOW_PICKER) + return false; + + let workspaceManager = global.workspace_manager; + + if ((event.get_key_symbol() === Clutter.KEY_Alt_L || + event.get_key_symbol() === Clutter.KEY_Alt_R) && + !this._pickWorkspace) { + this._prevFocusActor = global.stage.get_key_focus(); + global.stage.set_key_focus(null); + this._active = workspaceManager.get_active_workspace_index(); + this._pickWindow = true; + this._workspaces[workspaceManager.get_active_workspace_index()].showWindowsTooltips(); + return true; + } + if ((event.get_key_symbol() === Clutter.KEY_Control_L || + event.get_key_symbol() === Clutter.KEY_Control_R) && + !this._pickWindow) { + this._prevFocusActor = global.stage.get_key_focus(); + global.stage.set_key_focus(null); + this._pickWorkspace = true; + for (let i = 0; i < this._workspaces.length; i++) + this._workspaces[i].showTooltip(); + return true; + } + + if (global.stage.get_key_focus() !== global.stage) + return false; + + // ignore shift presses, they're required to get numerals in azerty keyboards + if ((this._pickWindow || this._pickWorkspace) && + (event.get_key_symbol() === Clutter.KEY_Shift_L || + event.get_key_symbol() === Clutter.KEY_Shift_R)) + return true; + + if (this._pickWindow) { + if (this._active !== workspaceManager.get_active_workspace_index()) { + this._hideTooltips(); + return false; + } + + let c = event.get_key_symbol() - Clutter.KEY_KP_0; + if (c > 9 || c <= 0) { + c = event.get_key_symbol() - Clutter.KEY_0; + if (c > 9 || c <= 0) { + this._hideTooltips(); + log(c); + return false; + } + } + + let win = this._workspaces[this._active].getWindowWithTooltip(c); + this._hideTooltips(); + + if (win) + Main.activateWindow(win, global.get_current_time()); + + return true; + } + if (this._pickWorkspace) { + let c = event.get_key_symbol() - Clutter.KEY_KP_0; + if (c > 9 || c <= 0) { + c = event.get_key_symbol() - Clutter.KEY_0; + if (c > 9 || c <= 0) { + this._hideWorkspacesTooltips(); + return false; + } + } + + let workspace = this._workspaces[c - 1]; + if (workspace !== undefined) + workspace.metaWorkspace.activate(global.get_current_time()); + + this._hideWorkspacesTooltips(); + return true; + } + return false; + }; + /* eslint-enable */ + }); } disable() { - Workspace.Workspace = this._origWorkspace; - WorkspacesView.WorkspacesView = this._origWorkspacesView; + this._injectionManager.clear(); } }