From 82965720f7f8ea5c45143af2500a178e0315172c Mon Sep 17 00:00:00 2001 From: Stephen Michel Date: Thu, 25 Sep 2025 07:46:37 -0400 Subject: [PATCH 1/3] Add global move-window actions Only implemented horizontally, but done in one commit to avoid updating the compiled gschema twice. --- keybindings.js | 9 +++ prefsKeybinding.js | 4 ++ schemas/gschemas.compiled | Bin 12333 -> 12637 bytes ...gnome.shell.extensions.paperwm.gschema.xml | 21 ++++++- tiling.js | 54 +++++++++++++++--- utils.js | 7 +++ 6 files changed, 84 insertions(+), 11 deletions(-) diff --git a/keybindings.js b/keybindings.js index 2899781fa..c1d2b6dca 100644 --- a/keybindings.js +++ b/keybindings.js @@ -264,6 +264,15 @@ export function setupActions(settings) { registerMinimapAction("move-down", (_mw, space) => space.swap(Meta.MotionDirection.DOWN)); + registerMinimapAction("move-global-left", + (_mw, space) => space.swapGlobal(Meta.MotionDirection.LEFT)); + registerMinimapAction("move-global-right", + (_mw, space) => space.swapGlobal(Meta.MotionDirection.RIGHT)); + registerMinimapAction("move-global-up", + (_mw, space) => space.swapGlobal(Meta.MotionDirection.UP)); + registerMinimapAction("move-global-down", + (_mw, space) => space.swapGlobal(Meta.MotionDirection.DOWN)); + registerPaperAction("toggle-scratch-window", Scratch.toggleScratchWindow); diff --git a/prefsKeybinding.js b/prefsKeybinding.js index 67ff8bb81..3c509727e 100644 --- a/prefsKeybinding.js +++ b/prefsKeybinding.js @@ -69,6 +69,10 @@ const actions = { 'move-right', 'move-up', 'move-down', + 'move-global-left', + 'move-global-right', + 'move-global-up', + 'move-global-down', 'slurp-in', 'barf-out', 'barf-out-active', diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index 484bc32864f8f3279e98bd501039d8a7dd491ad9..89758b2fb0768bf747b331dc7c4d4c6071491a19 100644 GIT binary patch literal 12637 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 K%HWC_&;1{7rNCnV literal 12333 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 OLzz>MX@fgvy!Zdbk&%P| diff --git a/schemas/org.gnome.shell.extensions.paperwm.gschema.xml b/schemas/org.gnome.shell.extensions.paperwm.gschema.xml index 1819e96b0..029b9c1a5 100644 --- a/schemas/org.gnome.shell.extensions.paperwm.gschema.xml +++ b/schemas/org.gnome.shell.extensions.paperwm.gschema.xml @@ -347,11 +347,11 @@ period', 'period', 'Right']]]> - Move the active window to the right + Move the active window to the right, staying on the same monitor comma', 'comma', 'Left']]]> - Move the active window to the left + Move the active window to the left, staying on the same monitor Up']]]> @@ -362,6 +362,23 @@ Move the active window down + + + Move the active window to the right + + + + Move the active window to the left + + + + Move the active window up or to the workspace above + + + + Move the active window down or to the workspace below + + i']]]> Consume window into the active column diff --git a/tiling.js b/tiling.js index b6fd65925..7d2f17904 100644 --- a/tiling.js +++ b/tiling.js @@ -1095,6 +1095,45 @@ export class Space extends Array { ensureViewport(this.selectedWindow, this, { force: true }); } + swapGlobal(direction, metaWindow) { + metaWindow = metaWindow || this.selectedWindow; + + let [index, row] = this.positionOf(metaWindow); + let targetIndex = index; + let targetRow = row; + const dir = Utils.motionToDisplayDirection[direction]; + switch (direction) { + case Meta.MotionDirection.LEFT: + targetIndex--; + break; + case Meta.MotionDirection.RIGHT: + targetIndex++; + break; + case Meta.MotionDirection.DOWN: + targetRow++; + break; + case Meta.MotionDirection.UP: + targetRow--; + break; + } + if (targetIndex < 0 || targetIndex >= this.length) { + spaces.switchMonitor(dir, true, true, metaWindow); + return; + } + let column = this[index]; + if (targetRow < 0 || targetRow >= column.length) { + // TODO: Move to workspace above/below + return; + } + + Lib.swap(this[index], row, targetRow); + Lib.swap(this, index, targetIndex); + + this.layout(); + this.emit('swapped', index, targetIndex, row, targetRow); + ensureViewport(this.selectedWindow, this, { force: true }); + } + switchLinear(dir, loop) { let index = this.selectedIndex(); let column = this[index]; @@ -1190,13 +1229,8 @@ export class Space extends Array { switchGlobalUp() { this.switchGlobal(Meta.MotionDirection.UP); } switchGlobalDown() { this.switchGlobal(Meta.MotionDirection.DOWN); } switchGlobal(direction) { - const motionToDisplayDirection = { - [Meta.MotionDirection.LEFT]: Meta.DisplayDirection.LEFT, - [Meta.MotionDirection.RIGHT]: Meta.DisplayDirection.RIGHT, - [Meta.MotionDirection.UP]: Meta.DisplayDirection.UP, - [Meta.MotionDirection.DOWN]: Meta.DisplayDirection.DOWN, - }; - const dir = motionToDisplayDirection[direction] + const dir = Utils.motionToDisplayDirection[direction]; + let space = this; const switchMonitor = () => { @@ -2566,8 +2600,10 @@ export const Spaces = class Spaces extends Map { return nSpaces <= nMonitors; } - switchMonitor(direction, move, warp = true) { - let focus = display.focus_window; + switchMonitor(direction, move, warp = true, focus = null) { + // For unkown reasons, display.focus_window is null if you are in the + // middle of moving a winodw, aka if navigation is open. + focus = focus ?? display.focus_window; let monitor = focusMonitor(); let currentSpace = this.monitors.get(monitor); let i = display.get_monitor_neighbor_index(monitor.index, direction); diff --git a/utils.js b/utils.js index 15bb86af8..8c523907b 100644 --- a/utils.js +++ b/utils.js @@ -105,6 +105,13 @@ export function setBackgroundImage(actor, resource_path) { actor.content_repeat = Clutter.ContentRepeat.BOTH; } +export const motionToDisplayDirection = { + [Meta.MotionDirection.LEFT]: Meta.DisplayDirection.LEFT, + [Meta.MotionDirection.RIGHT]: Meta.DisplayDirection.RIGHT, + [Meta.MotionDirection.UP]: Meta.DisplayDirection.UP, + [Meta.MotionDirection.DOWN]: Meta.DisplayDirection.DOWN, +}; + /** * Backwards compatible function. Attempts to use Cogl.Color with a fallback * to Clutter.Color. From d2e28a1e0d03fb4ae3566fb98f98de9d2cc3aed0 Mon Sep 17 00:00:00 2001 From: Stephen Michel Date: Thu, 25 Sep 2025 08:06:58 -0400 Subject: [PATCH 2/3] Move window to proper location --- tiling.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/tiling.js b/tiling.js index 7d2f17904..623934f89 100644 --- a/tiling.js +++ b/tiling.js @@ -2616,6 +2616,12 @@ export const Spaces = class Spaces extends Map { let space = this.monitors.get(newMonitor); if (move && focus) { + const customIndex = getMoveWindowPositionIndex(space, direction); + if (customIndex !== null) { + // namespaced prop to avoid possible future collisions + focus.paperwm_openAtIndex = customIndex; + } + let metaWindow = focus.get_transient_for() || focus; if (currentSpace && currentSpace.indexOf(metaWindow) !== -1) { @@ -4200,7 +4206,13 @@ Opening "${metaWindow?.title}" on current space.`); } ok && clone.set_position(x, y); - if (!space.addWindow(metaWindow, getOpenWindowPositionIndex(space))) + // When moving a window from another monitor, we may request a certain index + const openAtIndex = metaWindow.paperwm_openAtIndex ?? getOpenWindowPositionIndex(space); + if (metaWindow.paperwm_openAtIndex) { + delete metaWindow.paperwm_openAtIndex; + } + + if (!space.addWindow(metaWindow, openAtIndex)) return; metaWindow.unmake_above(); @@ -4303,6 +4315,43 @@ Opening "${metaWindow?.title}" on current space.`); } } +/** + * When we're moving a window from an existing monitor, we want to insert with + * minimal disruption. E.g. if we're moving window E to the left, + * + * ([a b] c d) ([E f]) + * (a [b E] c d) ([f]) + * + * so that visually it looks like this: + * + * [a b] [E f] + * [b E] [f g] + * + * regardless of the setting for inserting new windows. + */ +function getMoveWindowPositionIndex(space, direction) { + const visibleColumns = space.filter(([mw]) => space.isVisible(mw)); + + if (visibleColumns.length === 0) { + return null; + } + + switch (direction) { + case Meta.DisplayDirection.LEFT: { + const windowAtTarget = visibleColumns[visibleColumns.length - 1][0]; + return space.indexOf(windowAtTarget) + 1; + break; + } + case Meta.DisplayDirection.RIGHT: { + const windowAtTarget = visibleColumns[0][0]; + return space.indexOf(windowAtTarget); + } + default: + // No special handling yet for moving up/down + return null; + } +} + /** * Gets the window index to add a new window in the space: * { RIGHT: 0, LEFT: 1, START: 2, END: 3 }; From 27e787e02acd0a97cad1caadaa4ee473b69e94df Mon Sep 17 00:00:00 2001 From: Stephen Michel Date: Fri, 26 Sep 2025 13:31:37 -0400 Subject: [PATCH 3/3] WIP: Enable swapGlobal vertically Only kind of works.. when moving to a monitor to the side, then down, you end up back on the first monitor. --- tiling.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tiling.js b/tiling.js index 623934f89..0790f5237 100644 --- a/tiling.js +++ b/tiling.js @@ -1122,7 +1122,8 @@ export class Space extends Array { } let column = this[index]; if (targetRow < 0 || targetRow >= column.length) { - // TODO: Move to workspace above/below + spaces.selectSequenceSpace(direction, true); + Navigator.finishNavigation(); return; }