From 9b21a720bee2a8b574dfb61113811a71a147fe93 Mon Sep 17 00:00:00 2001 From: Peter Fajdiga Date: Tue, 24 Sep 2024 23:27:26 +0200 Subject: [PATCH] add key bindings for moving window to next/previous position (resolves #58) --- src/lib/keyBindings/Actions.ts | 34 +++++++++++---- src/lib/keyBindings/definition.ts | 10 +++++ src/lib/layout/Column.ts | 9 +++- src/lib/layout/Window.ts | 6 +-- src/lib/world/clientState/Tiled.ts | 2 +- src/tests/flows/layout.ts | 70 ++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/lib/keyBindings/Actions.ts b/src/lib/keyBindings/Actions.ts index 1e976a8..90c81e0 100644 --- a/src/lib/keyBindings/Actions.ts +++ b/src/lib/keyBindings/Actions.ts @@ -86,28 +86,28 @@ class Actions { if (leftColumn === null) { return; } - window.moveToColumn(leftColumn); + window.moveToColumn(leftColumn, true); grid.desktop.autoAdjustScroll(); } else { // move from shared column into own column const newColumn = new Column(grid, grid.getLeftColumn(column)); - window.moveToColumn(newColumn); + window.moveToColumn(newColumn, true); } } - public readonly windowMoveRight = (cm: ClientManager, dm: DesktopManager, window: Window, column: Column, grid: Grid) => { + public readonly windowMoveRight = (cm: ClientManager, dm: DesktopManager, window: Window, column: Column, grid: Grid, bottom: boolean = true) => { if (column.getWindowCount() === 1) { // move from own column into existing column const rightColumn = grid.getRightColumn(column); if (rightColumn === null) { return; } - window.moveToColumn(rightColumn); + window.moveToColumn(rightColumn, bottom); grid.desktop.autoAdjustScroll(); } else { // move from shared column into own column const newColumn = new Column(grid, column); - window.moveToColumn(newColumn); + window.moveToColumn(newColumn, true); } } @@ -121,14 +121,32 @@ class Actions { column.moveWindowDown(window); } + public readonly windowMoveNext = (cm: ClientManager, dm: DesktopManager, window: Window, column: Column, grid: Grid) => { + const canMoveDown = window !== column.getLastWindow(); + if (canMoveDown) { + column.moveWindowDown(window); + } else { + this.windowMoveRight(cm, dm, window, column, grid, false); + } + } + + public readonly windowMovePrevious = (cm: ClientManager, dm: DesktopManager, window: Window, column: Column, grid: Grid) => { + const canMoveUp = window !== column.getFirstWindow(); + if (canMoveUp) { + column.moveWindowUp(window); + } else { + this.windowMoveLeft(cm, dm, window, column, grid); + } + } + public readonly windowMoveStart = (cm: ClientManager, dm: DesktopManager, window: Window, column: Column, grid: Grid) => { const newColumn = new Column(grid, null); - window.moveToColumn(newColumn); + window.moveToColumn(newColumn, true); } public readonly windowMoveEnd = (cm: ClientManager, dm: DesktopManager, window: Window, column: Column, grid: Grid) => { const newColumn = new Column(grid, grid.getLastColumn()); - window.moveToColumn(newColumn); + window.moveToColumn(newColumn, true); } public readonly windowToggleFloating = (cm: ClientManager, dm: DesktopManager) => { @@ -253,7 +271,7 @@ class Actions { if (targetColumn === null) { return; } - window.moveToColumn(targetColumn); + window.moveToColumn(targetColumn, true); grid.desktop.autoAdjustScroll(); }; diff --git a/src/lib/keyBindings/definition.ts b/src/lib/keyBindings/definition.ts index 8fc1617..a39542d 100644 --- a/src/lib/keyBindings/definition.ts +++ b/src/lib/keyBindings/definition.ts @@ -81,6 +81,16 @@ function getKeyBindings(world: World, actions: Actions): KeyBinding[] { defaultKeySequence: "Meta+Shift+S", action: () => world.doIfTiledFocused(actions.windowMoveDown), }, + { + name: "window-move-next", + description: "Move window to the next position in grid", + action: () => world.doIfTiledFocused(actions.windowMoveNext), + }, + { + name: "window-move-previous", + description: "Move window to the previous position in grid", + action: () => world.doIfTiledFocused(actions.windowMovePrevious), + }, { name: "window-move-start", description: "Move window to start", diff --git a/src/lib/layout/Column.ts b/src/lib/layout/Column.ts index 723aff2..914a88f 100644 --- a/src/lib/layout/Column.ts +++ b/src/lib/layout/Column.ts @@ -256,8 +256,13 @@ class Column { } } - public onWindowAdded(window: Window) { - this.windows.insertEnd(window); + public onWindowAdded(window: Window, bottom: boolean) { + if (bottom) { + this.windows.insertEnd(window); + } else { + this.windows.insertStart(window); + } + if (this.width === 0) { this.setWidth(window.client.preferredWidth, false); } diff --git a/src/lib/layout/Window.ts b/src/lib/layout/Window.ts index 53628c3..4bb996a 100644 --- a/src/lib/layout/Window.ts +++ b/src/lib/layout/Window.ts @@ -14,16 +14,16 @@ class Window { }; this.skipArrange = false; this.column = column; - column.onWindowAdded(this); + column.onWindowAdded(this, true); } - public moveToColumn(targetColumn: Column) { + public moveToColumn(targetColumn: Column, bottom: boolean) { if (targetColumn === this.column) { return; } this.column.onWindowRemoved(this, false); this.column = targetColumn; - targetColumn.onWindowAdded(this); + targetColumn.onWindowAdded(this, bottom); } public arrange(x: number, y: number, width: number, height: number) { diff --git a/src/lib/world/clientState/Tiled.ts b/src/lib/world/clientState/Tiled.ts index c19c89d..092a701 100644 --- a/src/lib/world/clientState/Tiled.ts +++ b/src/lib/world/clientState/Tiled.ts @@ -156,7 +156,7 @@ namespace ClientState { } const newColumn = new Column(grid, grid.getLastFocusedColumn() ?? grid.getLastColumn()); - window.moveToColumn(newColumn); + window.moveToColumn(newColumn, true); } private static prepareClientForTiling(client: ClientWrapper, config: LayoutConfig) { diff --git a/src/tests/flows/layout.ts b/src/tests/flows/layout.ts index 8f29f4e..2627af4 100644 --- a/src/tests/flows/layout.ts +++ b/src/tests/flows/layout.ts @@ -106,6 +106,76 @@ tests.register("Focus and move windows", 1, () => { assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 2, 2)); assertRectEqual(client2.frameGeometry, getRectInGrid(1, 1, 2, 2)); + qtMock.fireShortcut("karousel-window-move-previous"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 2, 1)); + assertRectEqual(client2.frameGeometry, getRectInGrid(1, 0, 2, 2)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 1, 2, 2)); + + qtMock.fireShortcut("karousel-window-move-previous"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 3, 1)); + assertRectEqual(client2.frameGeometry, getRectInGrid(1, 0, 3, 1)); + assertRectEqual(client1.frameGeometry, getRectInGrid(2, 0, 3, 1)); + + qtMock.fireShortcut("karousel-window-move-previous"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 2, 2)); + assertRectEqual(client2.frameGeometry, getRectInGrid(0, 1, 2, 2)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 2, 1)); + + qtMock.fireShortcut("karousel-window-move-previous"); + assertRectEqual(client2.frameGeometry, getRectInGrid(0, 0, 2, 2)); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 1, 2, 2)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 2, 1)); + + qtMock.fireShortcut("karousel-window-move-previous"); + assertRectEqual(client2.frameGeometry, getRectInGrid(0, 0, 3, 1)); + assertRectEqual(client3.frameGeometry, getRectInGrid(1, 0, 3, 1)); + assertRectEqual(client1.frameGeometry, getRectInGrid(2, 0, 3, 1)); + + qtMock.fireShortcut("karousel-window-move-previous"); + assertRectEqual(client2.frameGeometry, getRectInGrid(0, 0, 3, 1)); + assertRectEqual(client3.frameGeometry, getRectInGrid(1, 0, 3, 1)); + assertRectEqual(client1.frameGeometry, getRectInGrid(2, 0, 3, 1)); + + qtMock.fireShortcut("karousel-window-move-next"); + assertRectEqual(client2.frameGeometry, getRectInGrid(0, 0, 2, 2)); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 1, 2, 2)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 2, 1)); + + qtMock.fireShortcut("karousel-window-move-next"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 2, 2)); + assertRectEqual(client2.frameGeometry, getRectInGrid(0, 1, 2, 2)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 2, 1)); + + qtMock.fireShortcut("karousel-window-move-next"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 3, 1)); + assertRectEqual(client2.frameGeometry, getRectInGrid(1, 0, 3, 1)); + assertRectEqual(client1.frameGeometry, getRectInGrid(2, 0, 3, 1)); + + qtMock.fireShortcut("karousel-window-move-next"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 2, 1)); + assertRectEqual(client2.frameGeometry, getRectInGrid(1, 0, 2, 2)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 1, 2, 2)); + + qtMock.fireShortcut("karousel-window-move-next"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 2, 1)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 2, 2)); + assertRectEqual(client2.frameGeometry, getRectInGrid(1, 1, 2, 2)); + + qtMock.fireShortcut("karousel-window-move-next"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 3, 1)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 3, 1)); + assertRectEqual(client2.frameGeometry, getRectInGrid(2, 0, 3, 1)); + + qtMock.fireShortcut("karousel-window-move-next"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 3, 1)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 3, 1)); + assertRectEqual(client2.frameGeometry, getRectInGrid(2, 0, 3, 1)); + + qtMock.fireShortcut("karousel-window-move-left"); + assertRectEqual(client3.frameGeometry, getRectInGrid(0, 0, 2, 1)); + assertRectEqual(client1.frameGeometry, getRectInGrid(1, 0, 2, 2)); + assertRectEqual(client2.frameGeometry, getRectInGrid(1, 1, 2, 2)); + const col1Win1 = client3; const col2Win1 = client1; const col2Win2 = client2;