feat: adding resizing

This commit is contained in:
Lucas Oskorep
2025-10-16 00:46:33 -04:00
parent fe069b1de0
commit c4d4768d29
4 changed files with 175 additions and 23 deletions

View File

@@ -16,7 +16,7 @@ export default class WindowContainer {
_tiledWindowLookup: Map<number, WindowWrapper>; _tiledWindowLookup: Map<number, WindowWrapper>;
_orientation: Orientation = Orientation.HORIZONTAL; _orientation: Orientation = Orientation.HORIZONTAL;
_workArea: Rect; _workArea: Rect;
_customSizes: Map<number, number>; _customSizes: Map<number, number>; // Maps index to custom width (horizontal) or height (vertical)
constructor(workspaceArea: Rect,) { constructor(workspaceArea: Rect,) {
this._tiledItems = []; this._tiledItems = [];
@@ -74,9 +74,10 @@ export default class WindowContainer {
removeWindow(win_id: number): void { removeWindow(win_id: number): void {
if (this._tiledWindowLookup.has(win_id)) { if (this._tiledWindowLookup.has(win_id)) {
this._tiledWindowLookup.delete(win_id); this._tiledWindowLookup.delete(win_id);
this._customSizes.delete(win_id); // Clean up custom size when window is removed
const index = this._getIndexOfWindow(win_id) const index = this._getIndexOfWindow(win_id)
this._tiledItems.splice(index, 1); this._tiledItems.splice(index, 1);
// Shift custom sizes after removed index
this._shiftCustomSizesAfterRemoval(index);
} else { } else {
for (const item of this._tiledItems) { for (const item of this._tiledItems) {
if (item instanceof WindowContainer) { if (item instanceof WindowContainer) {
@@ -87,6 +88,22 @@ export default class WindowContainer {
this.tileWindows() this.tileWindows()
} }
_shiftCustomSizesAfterRemoval(removedIndex: number): void {
// Rebuild the custom sizes map with shifted indices
const newCustomSizes = new Map<number, number>();
this._customSizes.forEach((size, index) => {
if (index < removedIndex) {
// Keep indices before removal
newCustomSizes.set(index, size);
} else if (index > removedIndex) {
// Shift down indices after removal
newCustomSizes.set(index - 1, size);
}
// Skip the removed index
});
this._customSizes = newCustomSizes;
}
disconnectSignals(): void { disconnectSignals(): void {
this._tiledItems.forEach((item) => { this._tiledItems.forEach((item) => {
if (item instanceof WindowContainer) { if (item instanceof WindowContainer) {
@@ -139,23 +156,31 @@ export default class WindowContainer {
let totalCustomHeight = 0; let totalCustomHeight = 0;
let numFlexibleItems = 0; let numFlexibleItems = 0;
this._tiledItems.forEach((item) => { this._tiledItems.forEach((item, index) => {
if (item instanceof WindowWrapper && this._customSizes.has(item.getWindowId())) { if (this._customSizes.has(index)) {
totalCustomHeight += this._customSizes.get(item.getWindowId())!; totalCustomHeight += this._customSizes.get(index)!;
} else { } else {
numFlexibleItems++; numFlexibleItems++;
} }
}); });
// Ensure custom sizes don't exceed container height
if (totalCustomHeight > this._workArea.height) {
Logger.warn("Custom heights exceed container, resetting all sizes");
this._customSizes.clear();
totalCustomHeight = 0;
numFlexibleItems = this._tiledItems.length;
}
const remainingHeight = this._workArea.height - totalCustomHeight; const remainingHeight = this._workArea.height - totalCustomHeight;
const flexHeight = numFlexibleItems > 0 ? Math.floor(remainingHeight / numFlexibleItems) : 0; const flexHeight = numFlexibleItems > 0 ? Math.floor(remainingHeight / numFlexibleItems) : 0;
// Build the bounds array // Build the bounds array
let currentY = this._workArea.y; let currentY = this._workArea.y;
return this._tiledItems.map((item) => { return this._tiledItems.map((item, index) => {
let height = flexHeight; let height = flexHeight;
if (item instanceof WindowWrapper && this._customSizes.has(item.getWindowId())) { if (this._customSizes.has(index)) {
height = this._customSizes.get(item.getWindowId())!; height = this._customSizes.get(index)!;
} }
const rect = { const rect = {
@@ -174,23 +199,31 @@ export default class WindowContainer {
let totalCustomWidth = 0; let totalCustomWidth = 0;
let numFlexibleItems = 0; let numFlexibleItems = 0;
this._tiledItems.forEach((item) => { this._tiledItems.forEach((item, index) => {
if (item instanceof WindowWrapper && this._customSizes.has(item.getWindowId())) { if (this._customSizes.has(index)) {
totalCustomWidth += this._customSizes.get(item.getWindowId())!; totalCustomWidth += this._customSizes.get(index)!;
} else { } else {
numFlexibleItems++; numFlexibleItems++;
} }
}); });
// Ensure custom sizes don't exceed container width
if (totalCustomWidth > this._workArea.width) {
Logger.warn("Custom widths exceed container, resetting all sizes");
this._customSizes.clear();
totalCustomWidth = 0;
numFlexibleItems = this._tiledItems.length;
}
const remainingWidth = this._workArea.width - totalCustomWidth; const remainingWidth = this._workArea.width - totalCustomWidth;
const flexWidth = numFlexibleItems > 0 ? Math.floor(remainingWidth / numFlexibleItems) : 0; const flexWidth = numFlexibleItems > 0 ? Math.floor(remainingWidth / numFlexibleItems) : 0;
// Build the bounds array // Build the bounds array
let currentX = this._workArea.x; let currentX = this._workArea.x;
return this._tiledItems.map((item) => { return this._tiledItems.map((item, index) => {
let width = flexWidth; let width = flexWidth;
if (item instanceof WindowWrapper && this._customSizes.has(item.getWindowId())) { if (this._customSizes.has(index)) {
width = this._customSizes.get(item.getWindowId())!; width = this._customSizes.get(index)!;
} }
const rect = { const rect = {
@@ -227,7 +260,7 @@ export default class WindowContainer {
Logger.error("Item not found in container during drag op", item.getWindowId()); Logger.error("Item not found in container during drag op", item.getWindowId());
return; return;
} }
let new_index = this.getIndexOfItemNested(item); let new_index = original_index;
this.getBounds().forEach((rect, index) => { this.getBounds().forEach((rect, index) => {
if (rect.x < x && rect.x + rect.width > x && rect.y < y && rect.y + rect.height > y) { if (rect.x < x && rect.x + rect.width > x && rect.y < y && rect.y + rect.height > y) {
new_index = index; new_index = index;
@@ -253,13 +286,20 @@ export default class WindowContainer {
return; return;
} }
// Find the index of the window
const index = this._getIndexOfWindow(win_id);
if (index === -1) {
Logger.error("Window not found in container during resize");
return;
}
const rect = window.getRect(); const rect = window.getRect();
if (this._orientation === Orientation.HORIZONTAL) { if (this._orientation === Orientation.HORIZONTAL) {
this._customSizes.set(win_id, rect.width); this._customSizes.set(index, rect.width);
Logger.log(`Window ${win_id} manually resized to width: ${rect.width}`); Logger.log(`Window at index ${index} manually resized to width: ${rect.width}`);
} else { } else {
this._customSizes.set(win_id, rect.height); this._customSizes.set(index, rect.height);
Logger.log(`Window ${win_id} manually resized to height: ${rect.height}`); Logger.log(`Window at index ${index} manually resized to height: ${rect.height}`);
} }
} }
@@ -274,5 +314,80 @@ export default class WindowContainer {
} }
} }
windowResizing(win_id: number, resizeOp: Meta.GrabOp): void {
const window = this.getWindow(win_id);
if (!window) {
// Check nested containers
for (const item of this._tiledItems) {
if (item instanceof WindowContainer) {
item.windowResizing(win_id, resizeOp);
}
}
return;
}
// Check if the resize direction matches the container orientation
const isHorizontalResize = resizeOp === Meta.GrabOp.RESIZING_E || resizeOp === Meta.GrabOp.RESIZING_W;
const isVerticalResize = resizeOp === Meta.GrabOp.RESIZING_N || resizeOp === Meta.GrabOp.RESIZING_S;
if ((this._orientation === Orientation.HORIZONTAL && !isHorizontalResize) ||
(this._orientation === Orientation.VERTICAL && !isVerticalResize)) {
// Resize direction doesn't match container orientation, ignore
return;
}
// Find the index of the window
const index = this._getIndexOfWindow(win_id);
if (index === -1) {
return;
}
// Get the new size
const rect = window.getRect();
const newSize = this._orientation === Orientation.HORIZONTAL ? rect.width : rect.height;
const oldSize = this._customSizes.get(index);
if (oldSize === undefined) {
// First time resizing this window, just set the size
this._customSizes.set(index, newSize);
this.tileWindows();
return;
}
// Calculate the delta (how much the window changed)
const delta = newSize - oldSize;
// Determine which adjacent window to adjust based on resize direction
let adjacentIndex = -1;
if (resizeOp === Meta.GrabOp.RESIZING_E || resizeOp === Meta.GrabOp.RESIZING_S) {
// Resizing right/down edge - adjust the next window
adjacentIndex = index + 1;
} else if (resizeOp === Meta.GrabOp.RESIZING_W || resizeOp === Meta.GrabOp.RESIZING_N) {
// Resizing left/up edge - adjust the previous window
adjacentIndex = index - 1;
}
// Update current window size
this._customSizes.set(index, newSize);
// Adjust adjacent window only if it has a custom size
if (adjacentIndex >= 0 && adjacentIndex < this._tiledItems.length &&
this._customSizes.has(adjacentIndex)) {
const adjacentSize = this._customSizes.get(adjacentIndex)!;
const newAdjacentSize = adjacentSize - 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);
}
}
// Re-tile all windows in real-time
this.tileWindows();
}
} }

View File

@@ -68,7 +68,6 @@ export default class Monitor {
this._workArea = global.workspace_manager.get_active_workspace().get_work_area_for_monitor(this._id); this._workArea = global.workspace_manager.get_active_workspace().get_work_area_for_monitor(this._id);
const activeWorkspace = global.workspace_manager.get_active_workspace(); const activeWorkspace = global.workspace_manager.get_active_workspace();
this._workspaces[activeWorkspace.index()].move(this._workArea); this._workspaces[activeWorkspace.index()].move(this._workArea);
this._workspaces[activeWorkspace.index()].tileWindows()
} }
removeWorkspace(workspaceId: number): void { removeWorkspace(workspaceId: number): void {
@@ -100,4 +99,15 @@ export default class Monitor {
} }
} }
windowResizing(win_id: number, resizeOp: Meta.GrabOp): void {
// Find which workspace contains the window and notify it
for (const container of this._workspaces) {
const win = container.getWindow(win_id);
if (win) {
container.windowResizing(win_id, resizeOp);
return;
}
}
}
} }

View File

@@ -104,6 +104,9 @@ export class WindowWrapper {
this._window.connect("position-changed", (_metaWindow) => { this._window.connect("position-changed", (_metaWindow) => {
windowManager.handleWindowPositionChanged(this); windowManager.handleWindowPositionChanged(this);
}), }),
this._window.connect("size-changed", (_metaWindow) => {
windowManager.handleWindowSizeChanged(this);
}),
); );
} }

View File

@@ -24,6 +24,8 @@ export interface IWindowManager {
handleWindowPositionChanged(winWrap: WindowWrapper): void; handleWindowPositionChanged(winWrap: WindowWrapper): void;
handleWindowSizeChanged(winWrap: WindowWrapper): void;
syncActiveWindow(): number | null; syncActiveWindow(): number | null;
} }
@@ -45,6 +47,8 @@ export default class WindowManager implements IWindowManager {
_grabbedWindowMonitor: number = _UNUSED_MONITOR_ID; _grabbedWindowMonitor: number = _UNUSED_MONITOR_ID;
_grabbedWindowId: number = _UNUSED_WINDOW_ID; _grabbedWindowId: number = _UNUSED_WINDOW_ID;
_changingGrabbedMonitor: boolean = false; _changingGrabbedMonitor: boolean = false;
_resizingWindow: boolean = false;
_resizeOp: Meta.GrabOp | null = null;
_showingOverview: boolean = false; _showingOverview: boolean = false;
@@ -202,7 +206,15 @@ export default class WindowManager implements IWindowManager {
Logger.log("Grab Op Start", op); Logger.log("Grab Op Start", op);
Logger.log(display, window, op) Logger.log(display, window, op)
Logger.log(window.get_monitor()) Logger.log(window.get_monitor())
this._getWrappedWindow(window)?.startDragging();
const isResizing = this._isResizeOperation(op);
if (isResizing) {
this._resizingWindow = true;
this._resizeOp = op;
} else {
this._getWrappedWindow(window)?.startDragging();
}
this._grabbedWindowMonitor = window.get_monitor(); this._grabbedWindowMonitor = window.get_monitor();
this._grabbedWindowId = window.get_id(); this._grabbedWindowId = window.get_id();
} }
@@ -230,6 +242,8 @@ export default class WindowManager implements IWindowManager {
} }
} }
this._resizingWindow = false;
this._resizeOp = null;
this._grabbedWindowId = _UNUSED_WINDOW_ID; this._grabbedWindowId = _UNUSED_WINDOW_ID;
this._getWrappedWindow(window)?.stopDragging(); this._getWrappedWindow(window)?.stopDragging();
this._tileMonitors(); this._tileMonitors();
@@ -263,7 +277,7 @@ export default class WindowManager implements IWindowManager {
let wrapped = this._getAndRemoveWrappedWindow(window); let wrapped = this._getAndRemoveWrappedWindow(window);
if (wrapped === undefined) { if (wrapped === undefined) {
Logger.error("WINDOW NOT DEFINED") Logger.error("WINDOW NOT DEFINED")
wrapped = new WindowWrapper(window, this.handleWindowMinimized); wrapped = new WindowWrapper(window, this.handleWindowMinimized.bind(this));
wrapped.connectWindowSignals(this); wrapped.connectWindowSignals(this);
} }
let new_mon = this._monitors.get(monitorId); let new_mon = this._monitors.get(monitorId);
@@ -300,6 +314,16 @@ export default class WindowManager implements IWindowManager {
} }
} }
public handleWindowSizeChanged(winWrap: WindowWrapper): void {
if (this._resizingWindow && winWrap.getWindowId() === this._grabbedWindowId) {
// Check if this is a valid resize direction for the container
const monitor = this._monitors.get(winWrap.getWindow().get_monitor());
if (monitor && this._resizeOp) {
monitor.windowResizing(winWrap.getWindowId(), this._resizeOp);
}
}
}
public handleWindowMinimized(winWrap: WindowWrapper): void { public handleWindowMinimized(winWrap: WindowWrapper): void {
const monitor_id = winWrap.getWindow().get_monitor() const monitor_id = winWrap.getWindow().get_monitor()
@@ -364,7 +388,7 @@ export default class WindowManager implements IWindowManager {
public addWindowToMonitor(window: Meta.Window) { public addWindowToMonitor(window: Meta.Window) {
Logger.log("ADDING WINDOW TO MONITOR", window, window); Logger.log("ADDING WINDOW TO MONITOR", window, window);
var wrapper = new WindowWrapper(window, this.handleWindowMinimized) var wrapper = new WindowWrapper(window, this.handleWindowMinimized.bind(this))
wrapper.connectWindowSignals(this); wrapper.connectWindowSignals(this);
this._addWindowWrapperToMonitor(wrapper); this._addWindowWrapperToMonitor(wrapper);