feat: add ability to change ordering of monitors

This commit is contained in:
Lucas Oskorep
2025-05-16 02:58:51 -04:00
parent c7f45ecf3b
commit 1d3d9dc402
4 changed files with 148 additions and 76 deletions

View File

@@ -12,24 +12,18 @@ enum Orientation {
export default class WindowContainer { export default class WindowContainer {
_id: number;
_tiledItems: (WindowWrapper | WindowContainer)[]; _tiledItems: (WindowWrapper | WindowContainer)[];
_tiledWindowLookup: Map<number, WindowWrapper>; _tiledWindowLookup: Map<number, WindowWrapper>;
_workspace: number;
_orientation: Orientation = Orientation.HORIZONTAL; _orientation: Orientation = Orientation.HORIZONTAL;
_workArea: Rect; _workArea: Rect;
constructor(monitorId: number, workspaceArea: Rect, workspace: number) { constructor(workspaceArea: Rect,) {
this._id = monitorId; // this._id = monitorId;
this._workspace = workspace;
this._tiledItems = []; this._tiledItems = [];
this._tiledWindowLookup = new Map<number, WindowWrapper>(); this._tiledWindowLookup = new Map<number, WindowWrapper>();
this._workArea = workspaceArea; this._workArea = workspaceArea;
} }
getWorkspace(): number {
return this._workspace;
}
move(rect: Rect): void { move(rect: Rect): void {
this._workArea = rect; this._workArea = rect;
@@ -108,75 +102,103 @@ export default class WindowContainer {
} }
tileWindows() { tileWindows() {
Logger.log("TILING WINDOWS ON MONITOR", this._id) Logger.log("TILING WINDOWS IN CONTAINER")
Logger.log("Workspace", this._workspace);
Logger.log("WorkArea", this._workArea); Logger.log("WorkArea", this._workArea);
// Get all windows for current workspace // Get all windows for current workspaceArea
let tilable = this._getTilableItems(); this._tileItems()
if (tilable.length !== 0) {
this._tileItems(tilable)
}
return true return true
} }
_getTilableItems(): (WindowWrapper | WindowContainer)[] { _tileItems() {
return Array.from(this._tiledItems.values()) if (this._tiledItems.length === 0) {
}
_tileItems(windows: (WindowWrapper | WindowContainer)[]) {
if (windows.length === 0) {
return; return;
} }
if (this._orientation === Orientation.HORIZONTAL) { const bounds = this.getBounds();
this._tileHorizontally(windows); this._tiledItems.forEach((item, index) => {
const rect = bounds[index];
if (item instanceof WindowContainer) {
item.move(rect);
} else { } else {
this._tileVertically(windows); item.safelyResizeWindow(rect);
} }
})
} }
_tileVertically(items: (WindowWrapper | WindowContainer)[]) {
getBounds(): Rect[] {
if (this._orientation === Orientation.HORIZONTAL) {
return this.getHorizontalBounds();
}
return this.getVerticalBounds();
}
getVerticalBounds(): Rect[] {
const items = this._tiledItems
const containerHeight = Math.floor(this._workArea.height / items.length); const containerHeight = Math.floor(this._workArea.height / items.length);
return items.map((_, index) => {
items.forEach((item, index) => {
const y = this._workArea.y + (index * containerHeight); const y = this._workArea.y + (index * containerHeight);
const rect = { return {
x: this._workArea.x, x: this._workArea.x,
y: y, y: y,
width: this._workArea.width, width: this._workArea.width,
height: containerHeight height: containerHeight
}; } as Rect;
if (item != null) {
if (item instanceof WindowContainer) {
item.move(rect)
} else {
item.safelyResizeWindow(rect);
}
}
}); });
} }
_tileHorizontally(windows: (WindowWrapper | WindowContainer)[]) { getHorizontalBounds(): Rect[] {
const windowWidth = Math.floor(this._workArea.width / windows.length); const windowWidth = Math.floor(this._workArea.width / this._tiledItems.length);
windows.forEach((item, index) => { return this._tiledItems.map((_, index) => {
const x = this._workArea.x + (index * windowWidth); const x = this._workArea.x + (index * windowWidth);
const rect = { return {
x: x, x: x,
y: this._workArea.y, y: this._workArea.y,
width: windowWidth, width: windowWidth,
height: this._workArea.height height: this._workArea.height
}; } as Rect;
if (item != null) {
if (item instanceof WindowContainer) {
item.move(rect)
} else {
item.safelyResizeWindow(rect);
}
}
}); });
} }
getIndexOfItemNested(item: WindowWrapper): number {
for (let i = 0; i < this._tiledItems.length; i++) {
const container = this._tiledItems[i];
if (container instanceof WindowContainer) {
const index = container.getIndexOfItemNested(item);
if (index !== -1) {
return i;
}
} else if (container.getWindowId() === item.getWindowId()) {
return i;
}
}
return -1;
}
// TODO: update this to work with nested containers - all other logic should already be working
itemDragged(item: WindowWrapper, x: number, y: number): void {
let original_index = this.getIndexOfItemNested(item);
if (original_index === -1) {
Logger.error("Item not found in container during drag op", item.getWindowId());
return;
}
let new_index = this.getIndexOfItemNested(item);
this.getBounds().forEach((rect, index) => {
if (rect.x < x && rect.x + rect.width > x && rect.y < y && rect.y + rect.height > y) {
new_index = index;
}
})
if (original_index !== new_index) {
this._tiledItems.splice(original_index, 1);
this._tiledItems.splice(new_index, 0, item);
this.tileWindows()
}
}
} }

View File

@@ -23,7 +23,7 @@ export default class Monitor {
const workspaceCount = global.workspace_manager.get_n_workspaces() const workspaceCount = global.workspace_manager.get_n_workspaces()
Logger.log("Workspace Count", workspaceCount); Logger.log("Workspace Count", workspaceCount);
for (let i = 0; i < workspaceCount; i++) { for (let i = 0; i < workspaceCount; i++) {
this._workspaces.push(new WindowContainer(monitorId, this._workArea, i)); this._workspaces.push(new WindowContainer(this._workArea));
} }
} }
@@ -61,7 +61,6 @@ export default class Monitor {
addWindow(winWrap: WindowWrapper) { addWindow(winWrap: WindowWrapper) {
const window_workspace = winWrap.getWindow().get_workspace().index(); const window_workspace = winWrap.getWindow().get_workspace().index();
Logger.log("Adding window to workspace", window_workspace);
this._workspaces[window_workspace].addWindow(winWrap); this._workspaces[window_workspace].addWindow(winWrap);
} }
@@ -72,4 +71,16 @@ export default class Monitor {
this._workspaces[activeWorkspace.index()].tileWindows() this._workspaces[activeWorkspace.index()].tileWindows()
} }
removeWorkspace(workspaceId: number): void {
this._workspaces.splice(workspaceId, 1);
}
addWorkspace(): void {
this._workspaces.push(new WindowContainer(this._workArea));
}
itemDragged(item: WindowWrapper, x: number, y: number): void {
this._workspaces[item.getWorkspace()].itemDragged(item, x, y);
}
} }

View File

@@ -32,6 +32,18 @@ export class WindowWrapper {
return this._window.get_id(); return this._window.get_id();
} }
getWorkspace(): number {
return this._window.get_workspace().index();
}
getMonitor(): number {
return this._window.get_monitor();
}
getRect(): Rect {
return this._window.get_frame_rect();
}
connectWindowSignals( connectWindowSignals(
windowManager: IWindowManager, windowManager: IWindowManager,
): void { ): void {
@@ -67,6 +79,10 @@ export class WindowWrapper {
}), }),
this._window.connect("workspace-changed", (_metaWindow) => { this._window.connect("workspace-changed", (_metaWindow) => {
Logger.log("WORKSPACE CHANGED FOR WINDOW", this._window.get_id()); Logger.log("WORKSPACE CHANGED FOR WINDOW", this._window.get_id());
windowManager.handleWindowChangedWorkspace(this);
}),
this._window.connect("position-changed", (_metaWindow) => {
windowManager.handleWindowPositionChanged(this);
}), }),
); );
} }

View File

@@ -23,41 +23,33 @@ export interface IWindowManager {
handleWindowUnminimized(winWrap: WindowWrapper): void; handleWindowUnminimized(winWrap: WindowWrapper): void;
handleWindowChangedWorkspace(winWrap: WindowWrapper): void;
handleWindowPositionChanged(winWrap: WindowWrapper): void;
// removeFromTree(window: Meta.Window): void;
syncActiveWindow(): number | null; syncActiveWindow(): number | null;
} }
const _UNUSED_MONITOR_ID = -1 const _UNUSED_MONITOR_ID = -1
const _UNUSED_WINDOW_ID = -1
export default class WindowManager implements IWindowManager { export default class WindowManager implements IWindowManager {
_displaySignals: number[]; _displaySignals: number[] = [];
_windowManagerSignals: number[]; _windowManagerSignals: number[] = [];
_workspaceManagerSignals: number[]; _workspaceManagerSignals: number[] = [];
_overviewSignals: number[]; _overviewSignals: number[] = [];
_activeWindowId: number | null; _activeWindowId: number | null = null;
_grabbedWindowMonitor: number; _monitors: Map<number, Monitor> = new Map<number, Monitor>();
_monitors: Map<number, Monitor>;
_sessionProxy: Gio.DBusProxy | null;
_lockedSignalId: number | null;
_isScreenLocked: boolean;
_minimizedItems: Map<number, WindowWrapper>; _minimizedItems: Map<number, WindowWrapper> = new Map<number, WindowWrapper>();
_grabbedWindowMonitor: number = _UNUSED_MONITOR_ID;
_grabbedWindowId: number = _UNUSED_WINDOW_ID;
constructor() { constructor() {
this._displaySignals = [];
this._windowManagerSignals = [];
this._workspaceManagerSignals = [];
this._overviewSignals = [];
this._activeWindowId = null;
this._grabbedWindowMonitor = _UNUSED_MONITOR_ID;
this._monitors = new Map<number, Monitor>();
this._sessionProxy = null;
this._lockedSignalId = null;
this._isScreenLocked = false; // Initialize to unlocked state
this._minimizedItems = new Map<number, WindowWrapper>();
} }
@@ -93,7 +85,7 @@ export default class WindowManager implements IWindowManager {
Logger.log("SHOWING DESKTOP CHANGED"); Logger.log("SHOWING DESKTOP CHANGED");
}), }),
global.display.connect("workareas-changed", (display) => { global.display.connect("workareas-changed", (display) => {
Logger.log("WORK AREAS CHANGED", ); Logger.log("WORK AREAS CHANGED",);
console.log(display.get_workspace_manager().get_active_workspace_index()) console.log(display.get_workspace_manager().get_active_workspace_index())
}), }),
global.display.connect("in-fullscreen-changed", () => { global.display.connect("in-fullscreen-changed", () => {
@@ -113,9 +105,15 @@ export default class WindowManager implements IWindowManager {
}), }),
global.workspace_manager.connect("workspace-added", (_, wsIndex) => { global.workspace_manager.connect("workspace-added", (_, wsIndex) => {
Logger.log("WORKSPACE ADDED", wsIndex); Logger.log("WORKSPACE ADDED", wsIndex);
this._monitors.forEach((monitor: Monitor) => {
monitor.addWorkspace();
})
}), }),
global.workspace_manager.connect("workspace-removed", (_, wsIndex) => { global.workspace_manager.connect("workspace-removed", (_, wsIndex) => {
Logger.log("WORKSPACE REMOVED", wsIndex); Logger.log("WORKSPACE REMOVED", wsIndex);
this._monitors.forEach((monitor: Monitor) => {
monitor.removeWorkspace(wsIndex);
})
}), }),
global.workspace_manager.connect("active-workspace-changed", (source) => { global.workspace_manager.connect("active-workspace-changed", (source) => {
Logger.log("Active workspace-changed", source.get_active_workspace().index()); Logger.log("Active workspace-changed", source.get_active_workspace().index());
@@ -197,12 +195,15 @@ export default class WindowManager implements IWindowManager {
Logger.log("Grab Op Start"); Logger.log("Grab Op Start");
Logger.log(display, window, op) Logger.log(display, window, op)
Logger.log(window.get_monitor()) Logger.log(window.get_monitor())
this._grabbedWindowMonitor = window.get_monitor(); this._grabbedWindowMonitor = window.get_monitor();
this._grabbedWindowId = window.get_id();
} }
handleGrabOpEnd(display: Meta.Display, window: Meta.Window, op: Meta.GrabOp): void { handleGrabOpEnd(display: Meta.Display, window: Meta.Window, op: Meta.GrabOp): void {
Logger.log("Grab Op End ", op); Logger.log("Grab Op End ", op);
Logger.log("primary display", display.get_primary_monitor()) Logger.log("primary display", display.get_primary_monitor())
this._grabbedWindowId = _UNUSED_WINDOW_ID;
var rect = window.get_frame_rect() var rect = window.get_frame_rect()
Logger.info("Release Location", window.get_monitor(), rect.x, rect.y, rect.width, rect.height) Logger.info("Release Location", window.get_monitor(), rect.x, rect.y, rect.width, rect.height)
const old_mon_id = this._grabbedWindowMonitor; const old_mon_id = this._grabbedWindowMonitor;
@@ -229,6 +230,20 @@ export default class WindowManager implements IWindowManager {
Logger.info("monitor_start and monitor_end", this._grabbedWindowMonitor, window.get_monitor()); Logger.info("monitor_start and monitor_end", this._grabbedWindowMonitor, window.get_monitor());
} }
public handleWindowPositionChanged(winWrap: WindowWrapper): void {
if (winWrap.getWindowId() === this._grabbedWindowId) {
const rect = winWrap.getRect();
Logger.log("GRABBED WINDOW POSITION CHANGED", rect.x);
const [mouseX, mouseY, _] = global.get_pointer();
this._monitors.get(winWrap.getMonitor())?.itemDragged(winWrap, mouseX, mouseY);
// Log or use the coordinates
console.log(`Mouse position: X=${mouseX}, Y=${mouseY}`);
}
}
public handleWindowMinimized(winWrap: WindowWrapper): void { public handleWindowMinimized(winWrap: WindowWrapper): void {
Logger.warn("WARNING MINIMIZING WINDOW"); Logger.warn("WARNING MINIMIZING WINDOW");
Logger.log("WARNING MINIMIZED", JSON.stringify(winWrap)); Logger.log("WARNING MINIMIZED", JSON.stringify(winWrap));
@@ -253,6 +268,13 @@ export default class WindowManager implements IWindowManager {
this._tileMonitors() this._tileMonitors()
} }
public handleWindowChangedWorkspace(winWrap: WindowWrapper): void {
const monitor = winWrap.getWindow().get_monitor();
this._monitors.get(monitor)?.removeWindow(winWrap);
this._monitors.get(monitor)?.addWindow(winWrap);
}
public captureExistingWindows() { public captureExistingWindows() {
Logger.log("CAPTURING WINDOWS") Logger.log("CAPTURING WINDOWS")
const workspace = global.workspace_manager.get_active_workspace(); const workspace = global.workspace_manager.get_active_workspace();
@@ -303,6 +325,7 @@ export default class WindowManager implements IWindowManager {
this._addWindowWrapperToMonitor(wrapper); this._addWindowWrapperToMonitor(wrapper);
} }
_addWindowWrapperToMonitor(winWrap: WindowWrapper) { _addWindowWrapperToMonitor(winWrap: WindowWrapper) {
if (winWrap.getWindow().minimized) { if (winWrap.getWindow().minimized) {
this._minimizedItems.set(winWrap.getWindow().get_id(), winWrap); this._minimizedItems.set(winWrap.getWindow().get_id(), winWrap);