feat: adding resizing
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user