From f2e2952d1da400c57b550b73208ad4c1955d40f0 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sun, 14 May 2023 17:20:35 +0900 Subject: [PATCH] Add move/add tags for windows --- ...g.gnome.shell.extensions.fairy.gschema.xml | 77 ++++++++++++++++++- sources/keybinds.js | 49 ++++++++---- sources/renderer.js | 64 +++++++++------ sources/state.js | 13 +++- 4 files changed, 161 insertions(+), 42 deletions(-) diff --git a/schemas/org.gnome.shell.extensions.fairy.gschema.xml b/schemas/org.gnome.shell.extensions.fairy.gschema.xml index 0652ad7..97a0a66 100644 --- a/schemas/org.gnome.shell.extensions.fairy.gschema.xml +++ b/schemas/org.gnome.shell.extensions.fairy.gschema.xml @@ -164,6 +164,81 @@ 9']]]> add the current's monitor tag to 9 - + + + 1']]]> + Move the selected window to the tag 1 + + + 2']]]> + Move the selected window to the tag 2 + + + 3']]]> + Move the selected window to the tag 3 + + + 4']]]> + Move the selected window to the tag 4 + + + 5']]]> + Move the selected window to the tag 5 + + + 6']]]> + Move the selected window to the tag 6 + + + 7']]]> + Move the selected window to the tag 7 + + + 8']]]> + Move the selected window to the tag 8 + + + 9']]]> + Move the selected window to the tag 9 + + + + 1']]]> + Add the selected window to the tag 1 + + + 2']]]> + Add the selected window to the tag 2 + + + 3']]]> + Add the selected window to the tag 3 + + + 4']]]> + Add the selected window to the tag 4 + + + 5']]]> + Add the selected window to the tag 5 + + + 6']]]> + Add the selected window to the tag 6 + + + 7']]]> + Add the selected window to the tag 7 + + + 8']]]> + Add the selected window to the tag 8 + + + 9']]]> + Add the selected window to the tag 9 + + + diff --git a/sources/keybinds.js b/sources/keybinds.js index de5340b..0880f43 100644 --- a/sources/keybinds.js +++ b/sources/keybinds.js @@ -53,15 +53,10 @@ var KeyboardManager = GObject.registerClass( const state = this._state.monitors[mon]; const idx = this._state.workIndexByHandle(state.focused); const newW = this._state.workIndex(mon, state.tags, idx + 1); - this._state.focus(newW.handle); - }); - this._addBinding("cycle-next", () => { - const mon = global.display.get_current_monitor(); - const state = this._state.monitors[mon]; - const idx = this._state.workIndexByHandle(state.focused); - const win = this._state.workIndex(mon, state.tags, idx - 1); - this._state.focus(win.handle); + if (newW && newW.handle !== state.focused) + this._state.focus(newW.handle); }); + this._addBinding("cycle-next", () => this._focusNext()); this._addBinding("incrmfact", () => { const mon = global.display.get_current_monitor(); @@ -114,14 +109,14 @@ var KeyboardManager = GObject.registerClass( this._renderer.render(mon); }); - for (const tagNbr = 1; tagNbr < 10; tagNbr++) { + for (let tagNbr = 0; tagNbr < 9; tagNbr++) { const tag = 0b1 << tagNbr; - this._addBinding(`set-tag-${tagNbr}`, () => { + this._addBinding(`set-tag-${tagNbr + 1}`, () => { const mon = global.display.get_current_monitor(); this._renderer.setTags(mon, tag); }); - this._addBinding(`add-tag-${tagNbr}`, () => { + this._addBinding(`add-tag-${tagNbr + 1}`, () => { const mon = global.display.get_current_monitor(); const currTags = this._state.monitors[mon].tags; // Add the tag to the monitor but if the tag is already present, remove it @@ -133,12 +128,29 @@ var KeyboardManager = GObject.registerClass( : currTags | tag ); }); + this._addBinding(`moveto-tag-${tagNbr + 1}`, () => { + const mon = global.display.get_current_monitor(); + const handle = this._state.monitors[mon].focused; + const window = this._state.windows.find((x) => x.handle === handle); + this._focusNext(); + window.tags = tag; + window.handle.change_workspace_by_index(tagNbr, false); + this._renderer.renderAll(); + }); + this._addBinding(`addto-tag-${tagNbr + 1}`, () => { + const mon = global.display.get_current_monitor(); + const handle = this._state.monitors[mon].focused; + const window = this._state.windows.find((x) => x.handle === handle); + if (window.tags & tag) window.tags &= ~tag; + else window.tags |= tag; + this._renderer.renderAll(); + }); } this._addBinding("set-tag-all", () => { const mon = global.display.get_current_monitor(); const takkenTags = 0; - for (const i = 0; i < this._state.monitors.length; i++) + for (let i = 0; i < this._state.monitors.length; i++) takkenTags |= this._state.monitors[i]; this._state.monitors[mon].tags |= ~takkenTags; @@ -163,11 +175,14 @@ var KeyboardManager = GObject.registerClass( this._removeBinding("swap-prev"); this._removeBinding("zoom"); - for (const i = 1; i < 10; i++) { + for (let i = 1; i < 10; i++) { this._removeBinding(`set-tag-${i}`); this._removeBinding(`add-tag-${i}`); + this._removeBinding(`moveto-tag-${i}`); + this._removeBinding(`addto-tag-${i}`); } this._removeBinding("set-tag-all"); + this._removeBinding("moveto-tag-all"); } _switchLayout(mode) { @@ -179,5 +194,13 @@ var KeyboardManager = GObject.registerClass( state.oldLayout = currentLayout; this._renderer.render(mon); } + + _focusNext() { + const mon = global.display.get_current_monitor(); + const state = this._state.monitors[mon]; + const idx = this._state.workIndexByHandle(state.focused); + const win = this._state.workIndex(mon, state.tags, idx - 1); + if (win && win.handle !== state.focused) this._state.focus(win.handle); + } } ); diff --git a/sources/renderer.js b/sources/renderer.js index 9c5a92d..554bbb3 100644 --- a/sources/renderer.js +++ b/sources/renderer.js @@ -141,11 +141,11 @@ var Renderer = GObject.registerClass( log("Switch to tags", tags); if (Meta.prefs_get_workspaces_only_on_primary()) { const primaryMon = global.display.get_primary_monitor(); - this.setTag(primaryMon, tags); + this.setTags(primaryMon, tags); } else { for (let i = 0; i < this._state.monitors.length; i++) { this._state.monitors[i].tags = tags; - this.render(i, tags); + this.render(i); } } }), @@ -153,13 +153,13 @@ var Renderer = GObject.registerClass( } /** - * @param {Meta.Window} window + * @param {Meta.Window} handle */ - trackWindow(window) { - if (!this._isValidWindow(window)) return; + trackWindow(handle) { + if (!this._isValidWindow(handle)) return; // Add window signals - window._signals = [ - window.connect("unmanaging", (handle) => { + handle._signals = [ + handle.connect("unmanaging", (handle) => { handle._isInvalid = true; const idx = this._state.workIndexByHandle(handle); const faWindow = this._state.popByHandle(handle); @@ -170,29 +170,34 @@ var Renderer = GObject.registerClass( const newWindow = this._state.workIndex(faWindow.monitor, tags, idx); if (newWindow) this._state.focus(newWindow.handle); - this.render(faWindow.monitor, tags); + this.render(faWindow.monitor); }), - window.connect("workspace-changed", (window) => { - if (!this._isValidWindow(window)) return; - const [oldW, newW] = this._state.updateByHandle(window); - if (oldW) this.render(oldW.monitor, oldW.tags); - if (newW) this.render(newW.monitor, newW.tags); + handle.connect("workspace-changed", (handle) => { + log("Workspace changed for window"); + if (handle._ignoreWorkspaceChange) { + handle._ignoreWorkspaceChange = false; + return; + } + if (!this._isValidWindow(handle)) return; + const [oldW, newW] = this._state.updateByHandle(handle); + if (oldW) this.render(oldW.monitor); + if (newW) this.render(newW.monitor); }), - window.connect("focus", (window) => { - if (!this._isValidWindow(window)) return; - this._state.monitors[window.get_monitor()].focused = window; + handle.connect("focus", (handle) => { + if (!this._isValidWindow(handle)) return; + this._state.monitors[handle.get_monitor()].focused = handle; }), ]; - this._state.newWindow(window); + this._state.newWindow(handle); } - setTag(mon, tags) { + setTags(mon, tags) { const currTags = this._state.monitors[mon].tags; this._state.monitors[mon].tags = tags; this._setGWorkspaceIfNeeded(mon); - for (const i = 0; i < this._state.monitors.length; i++) { + for (let i = 0; i < this._state.monitors.length; i++) { if (this._state.monitors[i] & tags && mon !== i) { // Remove the selected tag from other monitors. // If the other monitor had only this tag, swap monitor's tags instead. @@ -213,7 +218,8 @@ var Renderer = GObject.registerClass( const tag = (tags & ~(tags - 1)); if (tags !== tag) return; // Retrieve the gnome workspace for the tag (inverse of 0b1 << tag) - const workspace = Math.log2(tag) + 1; + const workspace = Math.log2(tag); + console.log("Switching to", tags, tag, workspace) global.display .get_workspace_manager() @@ -224,7 +230,7 @@ var Renderer = GObject.registerClass( renderAll() { const monN = global.display.get_n_monitors(); for (let mon = 0; mon < monN; mon++) { - this.render(mon, this._state.monitors[mon].tags); + this.render(mon); } } @@ -235,10 +241,9 @@ var Renderer = GObject.registerClass( /** * @param {number} mon - * @param {number?} tags */ - render(mon, tags) { - if (!tags) tags = this._state.monitors[mon].tags; + render(mon) { + const 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. @@ -246,10 +251,21 @@ var Renderer = GObject.registerClass( .get_workspace_manager() .get_active_workspace() .get_work_area_for_monitor(mon); + const workIdx = global.display + .get_workspace_manager() + .get_active_workspace_index(); for (const window of this._state.render(mon, tags)) { if (window.handle.get_monitor() !== mon) window.handle.move_to_monitor(mon); + // if (!(window.tags & 0b1 << workIdx) || window.currentWorkspace !== workIdx) { + if (window.handle.get_workspace().index() !== workIdx) { + // The window is visible because another tag as been bringed + // so we need to ask gnome to move windows (temporarly to the current workspace) + log("Invalid workspace", window.tags, 0b1 << workIdx); + window.handle._ignoreWorkspaceChange = true; + window.handle.change_workspace_by_index(workIdx, true); + } if (window.floating) continue; diff --git a/sources/state.js b/sources/state.js index 163ba0f..c8a4ec5 100644 --- a/sources/state.js +++ b/sources/state.js @@ -4,7 +4,6 @@ const GObject = imports.gi.GObject; const GLib = imports.gi.GLib; const Mainloop = imports.mainloop; - var StateManager = GObject.registerClass( class StateManager extends GObject.Object { _init() { @@ -146,8 +145,8 @@ var StateManager = GObject.registerClass( if (newIdx < 0) newIdx = windows.length + newIdx; newIdx %= windows.length; - const gIdx = this.windows.findIndex(x => x === windows[idx]); - const gNewIdx = this.windows.findIndex(x => x === windows[newIdx]); + const gIdx = this.windows.findIndex((x) => x === windows[idx]); + const gNewIdx = this.windows.findIndex((x) => x === windows[newIdx]); const tmp = this.windows[gIdx]; this.windows[gIdx] = this.windows[gNewIdx]; @@ -180,9 +179,13 @@ var StateManager = GObject.registerClass( // TODO: Implement other layouts switch (layout) { case "monocle": + const focused = this.windows.find( + (x) => x.handle === this.monitors[mon].focused + ); return [ { - handle: this.monitors[mon].focused, + ...focused, + handle: focused.hanlde, maximized: true, minimized: false, x: 0, @@ -199,6 +202,7 @@ var StateManager = GObject.registerClass( : windows.length - nmaster; const stackIndex = i < nmaster ? i : i - nmaster; return { + ...x, handle: x.handle, maximized: false, minimized: false, @@ -215,6 +219,7 @@ var StateManager = GObject.registerClass( }); case "floating": return windows.map((x) => ({ + ...x, handle: x.handle, floating: true, }));