From dc3d46bd55d677af1bc395807b837a06f966b376 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 11 May 2023 23:15:34 +0900 Subject: [PATCH] Add keybinds and monitor staet --- extension.js | 15 ++++-- sources/keybinds.js | 78 +++++++++++++++++++++++++++++ sources/{windows.js => renderer.js} | 56 +++++++++++++-------- sources/{layout.js => state.js} | 21 ++++---- 4 files changed, 136 insertions(+), 34 deletions(-) create mode 100644 sources/keybinds.js rename sources/{windows.js => renderer.js} (82%) rename sources/{layout.js => state.js} (88%) diff --git a/extension.js b/extension.js index e9a08ab..68706f4 100644 --- a/extension.js +++ b/extension.js @@ -6,16 +6,22 @@ const Me = ExtensionUtils.getCurrentExtension(); const Main = imports.ui.main; const PanelMenu = imports.ui.panelMenu; -const Windows = Me.imports.sources.windows; +const State = Me.imports.sources.state; +const Renderer = Me.imports.sources.renderer; +const Keybinds = Me.imports.sources.keybinds; class Extension { constructor() { + this._state = new State.StateManager(); + this._renderer = new Renderer.Renderer(this._state); + this._keybinds = new Keybinds.KeyboardManager(this._state, this._renderer); + this._layoutIndicator = null; - this._windows = new Windows.WindowManager(); } enable() { - this._windows.enable(); + this._renderer.enable(); + this._keybinds.enable(); this.settings = ExtensionUtils.getSettings( "org.gnome.shell.extensions.fairy" @@ -43,7 +49,8 @@ class Extension { } disable() { - this._windows.disable(); + this._renderer.disable(); + this._keybinds.disable(); this._layoutIndicator.destroy(); this._layoutIndicator = null; diff --git a/sources/keybinds.js b/sources/keybinds.js new file mode 100644 index 0000000..3c1ca7a --- /dev/null +++ b/sources/keybinds.js @@ -0,0 +1,78 @@ +"use strict"; + +const GObject = imports.gi.GObject; +const Main = imports.ui.main; +const Meta = imports.gi.Meta; +const Shell = imports.gi.Shell; +const ExtensionUtils = imports.misc.extensionUtils; + +var KeyboardManager = GObject.registerClass( + class KeyboardManager extends GObject.Object { + _init(state, renderer) { + super._init(); + this._state = state; + this._renderer = renderer; + // TODO: Handle rerender from here. + } + + /** + * @param {string} key + * @param {() => void} action + */ + _addBinding(key, action) { + const settings = ExtensionUtils.getSettings( + "org.gnome.shell.extensions.fairy.keybinds" + ); + + Main.wm.addKeybinding( + key, + settings, + Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, + Shell.ActionMode.NORMAL, + action + ); + } + + /** + * @param {string} key + */ + _removeBinding(key) { + Main.wm.removeKeybinding(key); + } + + enable() { + this._addBinding("incrmfact", () => { + const mon = global.display.get_current_monitor(); + const state = this._state.monitors[mon]; + state.mfact = Math.min(95, state.mfact + 5); + this._renderer.render(mon); + }); + this._addBinding("decmfact", () => { + const mon = global.display.get_current_monitor(); + const state = this._state.monitors[mon]; + state.mfact = Math.max(5, state.mfact - 5); + this._renderer.render(mon); + }); + + this._addBinding("incrnmaster", () => { + const mon = global.display.get_current_monitor(); + this._state.monitors[mon].mfact += 1; + this._renderer.render(mon); + }); + this._addBinding("decnmaster", () => { + const mon = global.display.get_current_monitor(); + if (this._state.monitors[mon].nmaster > 0) + this._state.monitors[mon].nmaster -= 1; + this._renderer.render(mon); + }); + } + + disable() { + this._removeBinding("incrmfact"); + this._removeBinding("decrmfact"); + + this._removeBinding("incrnmaster"); + this._removeBinding("decrnmaster"); + } + } +); diff --git a/sources/windows.js b/sources/renderer.js similarity index 82% rename from sources/windows.js rename to sources/renderer.js index d93fa55..9e5f360 100644 --- a/sources/windows.js +++ b/sources/renderer.js @@ -8,13 +8,11 @@ const Mainloop = imports.mainloop; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); -const Layout = Me.imports.sources.layout; - -var WindowManager = GObject.registerClass( - class WindowManager extends GObject.Object { - _init() { +var Renderer = GObject.registerClass( + class Renderer extends GObject.Object { + _init(state) { super._init(); - this._layout = new Layout.Layout(); + this._state = state log("fairy init"); } @@ -84,12 +82,17 @@ var WindowManager = GObject.registerClass( } _removeSignals() { - for (const signal of this._signals) { + for (const signal of this._displaySignals) { global.display.disconnect(signal); } - this._signals = undefined; + this._displaySignals = undefined; - for (const window of this._layout.windows) { + for (const signal of this._wmSignals) { + global.window_manager.disconnect(signal); + } + this._wmSignals = undefined; + + for (const window of this._state.windows) { if (window._signals) { for (const signal of window._signals) window.disconnect(signal); } @@ -99,11 +102,11 @@ var WindowManager = GObject.registerClass( actor._signals = []; } } - this._layout.windows = []; + this._state.windows = []; } _bindSignals() { - this._signals = [ + this._displaySignals = [ global.display.connect("window-created", (_display, window) => this._waitForWindow(window, () => { this.trackWindow(window); @@ -114,6 +117,18 @@ var WindowManager = GObject.registerClass( // // }), ]; + this._wmSignals = [ + global.window_manager.connect("switch-workspace", (_wm, _from, to, _direction) => { + // Convert gnome workspaces to fairy's tags + to = 0b1 << to; + if (Meta.prefs_get_workspaces_only_on_primary()) { + this._state.monitors[global.display.get_primary_monitor()].tags = to; + } else { + for (let i = 0; i < this._state.monitors.length; i++) + this._state.monitors[i].tags = to; + } + }), + ] } /** @@ -127,7 +142,7 @@ var WindowManager = GObject.registerClass( }), window.connect("workspace-changed", (window) => { if (!this._isValidWindow(window)) return; - const [oldW, newW] = this._layout.updateByHandle(window); + const [oldW, newW] = this._state.updateByHandle(window); if (oldW) this.render(oldW.monitor, oldW.tags); if (newW) this.render(newW.monitor, newW.tags); }), @@ -135,12 +150,12 @@ var WindowManager = GObject.registerClass( const actor = window.get_compositor_private(); actor._signals = [ actor.connect("destroy", (actor) => { - const faWindow = this._layout.popByActor(actor); + const faWindow = this._state.popByActor(actor); if (faWindow) this.render(faWindow.monitor, faWindow.tags); }), ]; - this._layout.newWindow(window); + this._state.newWindow(window); } renderAll() { @@ -155,18 +170,17 @@ var WindowManager = GObject.registerClass( renderForWindow(window) { const mon = window.get_monitor(); - // TODO: The on_all_workspaces handling is faulty. - const workspace = window.on_all_workspaces - ? ~0 - : window.get_workspace().index() + 1; - this.render(mon, workspace); + this.render(mon); } /** * @param {number} mon - * @param {number} tags + * @param {number?} tags */ render(mon, tags) { + console.log("monitors", this._state.monitors) + if (!tags) tags = this._state.monitors[mon].tags; + // We don't care which workspace it is, we just want the geometry // for the current monitor without the panel. const monGeo = global.display @@ -174,7 +188,7 @@ var WindowManager = GObject.registerClass( .get_active_workspace() .get_work_area_for_monitor(mon); - for (const window of this._layout.render(mon, tags)) { + for (const window of this._state.render(mon, tags)) { if (window.handle.get_monitor() !== mon) window.handle.move_to_monitor(mon); if (window.handle.minimized !== window.minimized) { diff --git a/sources/layout.js b/sources/state.js similarity index 88% rename from sources/layout.js rename to sources/state.js index 7b5be28..8efba6e 100644 --- a/sources/layout.js +++ b/sources/state.js @@ -2,19 +2,20 @@ const GObject = imports.gi.GObject; -var Layout = GObject.registerClass( - class Layout extends GObject.Object { +var StateManager = GObject.registerClass( + class StateManager extends GObject.Object { _init() { super._init(); // Simpler to set 30 monitors than track creation/supression of monitors. - this._monitors = new Array(30).map(() => ({ + this.monitors = [...new Array(30)].map(() => ({ /** * @type {Meta.Window} focused window's handle */ focused: null, + tags: 1, layout: "tiled", nmaster: 1, - nfact: 60, + mfact: 55, })); /** @@ -80,7 +81,7 @@ var Layout = GObject.registerClass( * @returns WindowGeometry[] */ render(mon, tags) { - const { layout, nmaster, nfact } = this._monitors[mon]; + const { layout, nmaster, mfact } = this.monitors[mon]; const windows = this.windows.filter( (x) => x.monitor === mon && x.tags & tags ); @@ -91,7 +92,7 @@ var Layout = GObject.registerClass( case "monocle": return [ { - handle: this._monitors[mon].focused, + handle: this.monitors[mon].focused, maximized: true, minimized: false, x: 0, @@ -111,17 +112,19 @@ var Layout = GObject.registerClass( handle: x.handle, maximized: false, minimized: false, - x: i < nmaster ? 0 : nfact, + x: i < nmaster ? 0 : mfact, y: stackIndex * (100 / stackLength), width: windows.length <= nmaster ? 100 : i < nmaster - ? nfact - : 100 - nfact, + ? mfact + : 100 - mfact, height: 100 / stackLength, }; }); + default: + return []; } } }