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(); } }