From 5aef762e5ff991a4590501e6274180dc7558fb1b Mon Sep 17 00:00:00 2001 From: Lucas Oskorep Date: Thu, 16 Oct 2025 02:54:49 -0400 Subject: [PATCH] feat: resize bug fixing --- src/wm/container.ts | 73 +++++++++++++++++++++++++++++++++++------ src/wm/window.ts | 6 ++-- src/wm/windowManager.ts | 15 +++++++++ 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/src/wm/container.ts b/src/wm/container.ts index c10d1c8..5c2558e 100644 --- a/src/wm/container.ts +++ b/src/wm/container.ts @@ -89,19 +89,53 @@ export default class WindowContainer { } _shiftCustomSizesAfterRemoval(removedIndex: number): void { + Logger.log(`=== _shiftCustomSizesAfterRemoval called ===`); + Logger.log(`Removed index: ${removedIndex}`); + Logger.log(`Total items after removal: ${this._tiledItems.length}`); + Logger.log(`Custom sizes before shift:`, this._customSizes); + + const removedSize = this._customSizes.get(removedIndex); + Logger.log(`Removed window's custom size: ${removedSize}`); + // Rebuild the custom sizes map with shifted indices const newCustomSizes = new Map(); this._customSizes.forEach((size, index) => { if (index < removedIndex) { // Keep indices before removal + Logger.log(`Keeping index ${index} with size ${size}`); newCustomSizes.set(index, size); } else if (index > removedIndex) { // Shift down indices after removal + Logger.log(`Shifting index ${index} -> ${index - 1} with size ${size}`); newCustomSizes.set(index - 1, size); } // Skip the removed index }); + + Logger.log(`Custom sizes after index shift:`, newCustomSizes); + + // Distribute removed window's size evenly among all remaining windows + if (removedSize !== undefined && this._tiledItems.length > 1) { + // After removal, _tiledItems will have one fewer item + const remainingWindowCount = this._tiledItems.length - 1; + const sizePerWindow = Math.floor(removedSize / remainingWindowCount); + + Logger.log(`Distributing ${removedSize}px evenly among ${remainingWindowCount} remaining windows (${sizePerWindow}px each)`); + + // Add proportional size to each custom-sized window + // Flexible windows will automatically get their share through the bounds calculation + newCustomSizes.forEach((size, index) => { + const newSize = size + sizePerWindow; + Logger.log(`Index ${index}: ${size}px + ${sizePerWindow}px = ${newSize}px`); + newCustomSizes.set(index, newSize); + }); + } else { + Logger.log(`Not distributing space - removedSize: ${removedSize}, remainingWindowCount: ${this._tiledItems.length - 1}`); + } + + Logger.log(`Final custom sizes:`, newCustomSizes); this._customSizes = newCustomSizes; + Logger.log(`=== _shiftCustomSizesAfterRemoval complete ===`); } disconnectSignals(): void { @@ -357,6 +391,11 @@ export default class WindowContainer { // Calculate the delta (how much the window changed) const delta = newSize - oldSize; + // If delta is 0, the window didn't actually resize (hit its minimum) + if (delta === 0) { + return; + } + // Determine which adjacent window to adjust based on resize direction let adjacentIndex = -1; if (resizeOp === Meta.GrabOp.RESIZING_E || resizeOp === Meta.GrabOp.RESIZING_S) { @@ -371,22 +410,36 @@ export default class WindowContainer { this._customSizes.set(index, newSize); // Adjust adjacent window only if it has a custom size + // When both windows have custom sizes, always apply opposite delta to maintain total width + let oldAdjacentSize: number | undefined = undefined; if (adjacentIndex >= 0 && adjacentIndex < this._tiledItems.length && this._customSizes.has(adjacentIndex)) { - const adjacentSize = this._customSizes.get(adjacentIndex)!; - const newAdjacentSize = adjacentSize - delta; + const adjacentItem = this._tiledItems[adjacentIndex]; + if (adjacentItem instanceof WindowWrapper) { + oldAdjacentSize = this._customSizes.get(adjacentIndex)!; + const newAdjacentSize = oldAdjacentSize - delta; - // Ensure the adjacent window doesn't go below minimum size (e.g., 100px) - if (newAdjacentSize >= 100) { - this._customSizes.set(adjacentIndex, newAdjacentSize); - } else { - // Don't allow the resize if it would make adjacent window too small - this._customSizes.set(index, oldSize); + // Check if adjacent window allows resize + if (!adjacentItem.getWindow().allows_resize()) { + Logger.log("Adjacent window doesn't allow resize, reverting"); + this._customSizes.set(index, oldSize); + oldAdjacentSize = undefined; // Don't check later + } else { + // Always apply the opposite delta to the adjacent window + // This keeps the total width constant + this._customSizes.set(adjacentIndex, newAdjacentSize); + + // Immediately apply resize to adjacent window during drag + const bounds = this.getBounds(); + const adjacentRect = bounds[adjacentIndex]; + adjacentItem.safelyResizeWindow(adjacentRect); + } } } - // Re-tile all windows in real-time - this.tileWindows(); + // Don't call full tileWindows() during resize - just update the adjacent window above + // Full tiling and validation will happen in handleGrabOpEnd + // Skip post-resize validation during real-time resizing } diff --git a/src/wm/window.ts b/src/wm/window.ts index cb209cd..79147d3 100644 --- a/src/wm/window.ts +++ b/src/wm/window.ts @@ -127,10 +127,8 @@ export class WindowWrapper { safelyResizeWindow(rect: Rect, _retry: number = 2): void { // Keep minimal logging - if (this._dragging) { - Logger.info("STOPPED RESIZE BECAUSE ITEM IS BEING DRAGGED") - return; - } + // Note: we allow resizing even during drag operations to support position updates + // The dragging flag only prevents REORDERING, not position/size changes // Logger.log("SAFELY RESIZE", rect.x, rect.y, rect.width, rect.height); const actor = this._window.get_compositor_private(); diff --git a/src/wm/windowManager.ts b/src/wm/windowManager.ts index f057447..df21c46 100644 --- a/src/wm/windowManager.ts +++ b/src/wm/windowManager.ts @@ -211,6 +211,7 @@ export default class WindowManager implements IWindowManager { if (isResizing) { this._resizingWindow = true; this._resizeOp = op; + // Don't mark as dragging during resize - we need to update positions freely } else { this._getWrappedWindow(window)?.startDragging(); } @@ -290,6 +291,20 @@ export default class WindowManager implements IWindowManager { return; } if (winWrap.getWindowId() === this._grabbedWindowId) { + // Check if we're doing a pure NSEW resize - if so, don't allow position-based swapping + if (this._resizingWindow && this._resizeOp) { + const isPureNSEWResize = + this._resizeOp === Meta.GrabOp.RESIZING_E || + this._resizeOp === Meta.GrabOp.RESIZING_W || + this._resizeOp === Meta.GrabOp.RESIZING_N || + this._resizeOp === Meta.GrabOp.RESIZING_S; + + if (isPureNSEWResize) { + // Skip itemDragged - don't allow swaps during NSEW resize + return; + } + } + const [mouseX, mouseY, _] = global.get_pointer(); let monitorIndex = -1;