From 005483aafbcef243f1d0ecc46aa04303793751fe Mon Sep 17 00:00:00 2001 From: mattchristenson Date: Tue, 29 Sep 2026 20:00:04 -0500 Subject: [PATCH] fix: keep each workspace's windows apart when a workspace is removed Workspace and monitor nodes are named by workspace index (ws{n}, mo{m}ws{n}). When GNOME removes a workspace (dynamic workspaces: its last window closed), it renumbers the ones after it, but Forge only dropped the removed node. The next window placed by its workspace index was filed under another workspace's node, and the windows of two workspaces shared one split (#470). Rename the nodes of the following workspaces down by one on removal. Fixes #470 Co-Authored-By: Claude Opus 5.5 --- lib/extension/tree.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/extension/tree.js b/lib/extension/tree.js index ea957d29..eb5c6722 100644 --- a/lib/extension/tree.js +++ b/lib/extension/tree.js @@ -709,9 +709,33 @@ export class Tree extends Node { global.window_group.remove_child(existingWsNode.actorBin); this.removeChild(existingWsNode); + // GNOME renumbered the workspaces after it: rename their nodes to match + this._renumberWorkspaces(wsIndex + 1); return true; } + /** + * Workspace and monitor nodes are named by workspace index (`ws{n}`, `mo{m}ws{n}`), and GNOME + * renumbers the workspaces after one that is removed. Rename the nodes of the workspaces from + * `fromIndex` on down by one, so that each keeps the windows of its workspace. Without this, a + * window was filed under the node of another workspace (#470). (GNOME only ever adds a workspace + * at the end; a window dropped between two in the overview goes to a new last workspace and the + * windows after it move one up, each through its own workspace change.) + */ + _renumberWorkspaces(fromIndex) { + const indexOf = (node) => parseInt(String(node.nodeValue).slice(2), 10); + this.nodeWorkpaces + .filter((ws) => indexOf(ws) >= fromIndex) + .sort((a, b) => indexOf(a) - indexOf(b)) // lowest first: the new names are free already + .forEach((ws) => { + const index = indexOf(ws) - 1; + ws._data = `ws${index}`; + ws.getNodeByType(NODE_TYPES.MONITOR).forEach((monitor) => { + monitor._data = String(monitor.nodeValue).replace(/ws\d+$/, `ws${index}`); + }); + }); + } + get nodeWorkpaces() { let nodeWorkspaces = this.getNodeByType(NODE_TYPES.WORKSPACE); return nodeWorkspaces;