From 9a08841bb8239ed62bca26d57c60e35d765cb165 Mon Sep 17 00:00:00 2001 From: Peter Fajdiga Date: Sun, 29 Sep 2024 22:44:16 +0200 Subject: [PATCH] ignore minimized pinned clients --- src/lib/world/PinManager.ts | 2 +- src/lib/world/clientState/Pinned.ts | 10 ++- src/tests/flows/pinning.ts | 88 +++++++++++++++++++++++++ src/tests/utils/mocks/MockKwinClient.ts | 32 ++++++++- src/tests/utils/random.ts | 6 ++ 5 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 src/tests/flows/pinning.ts diff --git a/src/lib/world/PinManager.ts b/src/lib/world/PinManager.ts index 76007e7..9fb4073 100644 --- a/src/lib/world/PinManager.ts +++ b/src/lib/world/PinManager.ts @@ -17,7 +17,7 @@ class PinManager { const baseLot = new PinManager.Lot(screen.top, screen.bottom, screen.left, screen.right); let lots = [baseLot]; for (const client of this.pinnedClients) { - if (!Clients.isOnVirtualDesktop(client, kwinDesktop)) { + if (!Clients.isOnVirtualDesktop(client, kwinDesktop) || client.minimized) { continue; } diff --git a/src/lib/world/clientState/Pinned.ts b/src/lib/world/clientState/Pinned.ts index 8931d02..1fbbd57 100644 --- a/src/lib/world/clientState/Pinned.ts +++ b/src/lib/world/clientState/Pinned.ts @@ -53,7 +53,15 @@ namespace ClientState { for (const desktop of desktopManager.getDesktopsForClient(kwinClient)) { desktop.onPinsChanged(); } - }) + }); + }); + + manager.connect(kwinClient.minimizedChanged, () => { + world.do((clientManager, desktopManager) => { + for (const desktop of desktopManager.getDesktopsForClient(kwinClient)) { + desktop.onPinsChanged(); + } + }); }); manager.connect(kwinClient.desktopsChanged, () => { diff --git a/src/tests/flows/pinning.ts b/src/tests/flows/pinning.ts new file mode 100644 index 0000000..22c6306 --- /dev/null +++ b/src/tests/flows/pinning.ts @@ -0,0 +1,88 @@ +tests.register("Pin", 20, () => { + const { qtMock, workspaceMock } = initMocks(); + const config = getDefaultConfig(); + const world = new World(config); + + const screenFull = new MockQmlRect(0, 0, screenWidth, screenHeight); + const screenHalfLeft = new MockQmlRect(0, 0, screenWidth/2, screenHeight); + const screenHalfRight = new MockQmlRect(screenWidth/2, 0, screenWidth/2, screenHeight); + + const tiled1 = new MockKwinClient( + 1, + "app1", + "Application 1", + new MockQmlRect(10, 20, 100, 200), + ); + + const tiled2 = new MockKwinClient( + 2, + "app2", + "Application 2", + new MockQmlRect(10, 20, 100, 200), + ); + + const pinned = new MockKwinClient( + 3, + "browser", + "Documentation - Browser", + new MockQmlRect(10, 20, 100, 200), + ); + + function getRectInGrid(screen: QmlRect, column: number, window: number, nColumns: number, nWindows: number) { + const columnHeight = screen.height - config.gapsOuterTop - config.gapsOuterBottom; + const columnsWidth = nColumns * 100 + (nColumns-1) * config.gapsInnerHorizontal; + const windowHeight = (columnHeight - config.gapsInnerVertical * (nWindows-1)) / nWindows; + return new MockQmlRect( + screen.x + column * (100 + config.gapsInnerHorizontal) + (screen.width-columnsWidth) / 2, + screen.y + config.gapsOuterTop + (windowHeight + config.gapsInnerVertical) * window, + 100, + (columnHeight - config.gapsInnerVertical * (nWindows-1)) / nWindows, + ); + } + + function assertGrid(screen: QmlRect, grid: KwinClient[][]) { + const nColumns = grid.length; + for (let iColumn = 0; iColumn < nColumns; iColumn++) { + const column = grid[iColumn]; + const nWindows = column.length; + for (let iWindow = 0; iWindow < nWindows; iWindow++) { + const window = column[iWindow]; + assertRectEqual(window.frameGeometry, getRectInGrid(screen, iColumn, iWindow, nColumns, nWindows), 1); + } + } + } + + workspaceMock.createWindow(pinned); + workspaceMock.createWindow(tiled1); + workspaceMock.createWindow(tiled2); + assertGrid(screenFull, [ [pinned], [tiled1], [tiled2] ]); + + pinned.pin(screenHalfLeft); + assertRectEqual(pinned.frameGeometry, screenHalfLeft); + assertGrid(screenHalfRight, [ [tiled1], [tiled2] ]); + + pinned.pin(screenHalfRight); + assertRectEqual(pinned.frameGeometry, screenHalfRight); + assertGrid(screenHalfLeft, [ [tiled1], [tiled2] ]); + + pinned.unpin(); + assertRectEqual(pinned.frameGeometry, screenHalfRight); + assertGrid(screenFull, [ [tiled1], [tiled2] ]); + + pinned.pin(screenHalfRight); + assertRectEqual(pinned.frameGeometry, screenHalfRight); + assertGrid(screenHalfLeft, [ [tiled1], [tiled2] ]); + + pinned.minimized = true; + assertGrid(screenFull, [ [tiled1], [tiled2] ]); + + pinned.minimized = false; + assertRectEqual(pinned.frameGeometry, screenHalfRight); + assertGrid(screenHalfLeft, [ [tiled1], [tiled2] ]); + + workspaceMock.activeWindow = pinned; + qtMock.fireShortcut("karousel-window-toggle-floating"); + assert(pinned.tile === null); + pinned.frameGeometry = new MockQmlRect(10, 20, 100, 200); // This is needed because the window's preferredWidth can change when pinning, because frameGeometryChanged can fire before tileChanged. TODO: Ensure pinned window keeps its preferredWidth. + assertGrid(screenFull, [ [tiled1], [tiled2], [pinned] ]); +}); diff --git a/src/tests/utils/mocks/MockKwinClient.ts b/src/tests/utils/mocks/MockKwinClient.ts index 58dc809..b32b93e 100644 --- a/src/tests/utils/mocks/MockKwinClient.ts +++ b/src/tests/utils/mocks/MockKwinClient.ts @@ -24,9 +24,9 @@ class MockKwinClient { public keepAbove: boolean = false; public keepBelow: boolean = false; public shade: boolean = false; - public minimized: boolean = false; + public _minimized: boolean = false; public desktops: KwinDesktop[] = []; - public tile: Tile|null = null; + public _tile: Tile|null = null; public opacity: number = 1.0; public readonly fullScreenChanged = new MockQSignal(); @@ -153,4 +153,32 @@ class MockKwinClient { } this.frameGeometryChanged.fire(oldFrameGeometry); } + + public get minimized() { + return this._minimized; + } + + public set minimized(minimized: boolean) { + this._minimized = minimized; + this.minimizedChanged.fire(); + } + + public get tile() { + return this._tile; + } + + public set tile(tile: Tile|null) { + this._tile = tile; + this.tileChanged.fire(); + } + + public pin(geometry: QmlRect) { + runMaybe(() => this.frameGeometry = geometry); + this.tile = { __brand: "Tile" }; + this.frameGeometry = geometry; + } + + public unpin() { + this.tile = null; + } } diff --git a/src/tests/utils/random.ts b/src/tests/utils/random.ts index a70544b..2a98323 100644 --- a/src/tests/utils/random.ts +++ b/src/tests/utils/random.ts @@ -1,3 +1,9 @@ +function runMaybe(f: () => void) { + if (Math.random() < 0.5) { + f(); + } +} + function runOneOf(...fs: (() => void)[]) { const index = randomInt(fs.length); runLog.push(`${getStackFrame(1)} - Chose ${index}`);