diff --git a/ambient.d.ts b/ambient.d.ts index e0792f1..9e84e6a 100644 --- a/ambient.d.ts +++ b/ambient.d.ts @@ -2,3 +2,10 @@ import "@girs/gjs"; import "@girs/gjs/dom"; import "@girs/gnome-shell/ambient"; import "@girs/gnome-shell/extensions/global"; + +// Extend Meta.Window with our custom property +declare namespace Meta { + interface Window { + _aerospikeData?: any; + } +} diff --git a/extension.ts b/extension.ts index 9693118..b14453a 100644 --- a/extension.ts +++ b/extension.ts @@ -1,65 +1,31 @@ -import GLib from 'gi://GLib'; -import St from 'gi://St'; import Meta from 'gi://Meta'; import {Extension, ExtensionMetadata} from 'resource:///org/gnome/shell/extensions/extension.js'; -import Mtk from "@girs/mtk-16"; - import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import Gio from 'gi://Gio'; import Shell from 'gi://Shell'; -// import cairo from "cairo"; -// import Shell from 'gi://Shell'; -// import * as Main from 'resource:///org/gnome/shell/ui/main.js'; - -type WinWrapper = { - window: Meta.Window | null; - signals: Signal[] | null; -} - -type Signal = { - name: string; - id: number; -} - - +import WindowManager from './src/windowManager.js' +import {Logger} from "./src/utils/logger.js"; export default class aerospike extends Extension { settings: Gio.Settings; keyBindings: Map; - borderActor: St.Widget | null; - focusWindowSignals: any[]; - lastFocusedWindow: Meta.Window | null; - _focusSignal: number | null; - _windowCreateId: number | null; - _windows: Map; - _activeWindowId: number | null; + windowManager: WindowManager; constructor(metadata: ExtensionMetadata) { super(metadata); this.settings = this.getSettings('org.gnome.shell.extensions.aerospike'); this.keyBindings = new Map(); - // Initialize instance variables - this.borderActor = null; - this.focusWindowSignals = []; - this.lastFocusedWindow = null; - this._focusSignal = null; - this._windowCreateId = null; - this._windows = new Map(); - this._activeWindowId = null; - + this.windowManager = new WindowManager(); } enable() { - console.log("STARTING AEROSPIKE!") - this._captureExistingWindows(); - // Connect window signals - this._windowCreateId = global.display.connect( - 'window-created', - (display, window) => { - this.handleWindowCreated(window); - } - ); + Logger.log("STARTING AEROSPIKE!") this.bindSettings(); + this.windowManager.enable() + } + + disable() { + this.windowManager.disable() } @@ -166,315 +132,6 @@ export default class aerospike extends Extension { this.keyBindings.set(settingName, keyBindingAction); } - handleWindowCreated(window: Meta.Window) { - console.log("WINDOW CREATED", window); - if (!this._isWindowTileable(window)) { - return; - } - console.log("WINDOW IS TILABLE"); - const actor = window.get_compositor_private(); - if (!actor) { - return; - } - - - this._addWindow(window); - } - - _captureExistingWindows() { - console.log("CAPTURING WINDOWS") - const workspace = global.workspace_manager.get_active_workspace(); - const windows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace); - console.log("WINDOWS", windows); - windows.forEach(window => { - if (this._isWindowTileable(window)) { - this._addWindow(window); - } - }); - - this._tileWindows(); - } - - getUsableMonitorSpace(window: Meta.Window) { - // Get the current workspace - const workspace = window.get_workspace(); - - // Get the monitor index that this window is on - const monitorIndex = window.get_monitor(); - - // Get the work area - const workArea = workspace.get_work_area_for_monitor(monitorIndex); - - return { - x: workArea.x, - y: workArea.y, - width: workArea.width, - height: workArea.height - }; - } - - - // Function to safely resize a window after it's ready - safelyResizeWindow(win: Meta.Window, x: number, y: number, width: number, height: number): void { - const actor = win.get_compositor_private(); - - if (!actor) { - console.log("No actor available, can't resize safely yet"); - return; - } - - // Set a flag to track if the resize has been done - let resizeDone = false; - - // Connect to the first-frame signal - const id = actor.connect('first-frame', () => { - // Disconnect the signal handler - actor.disconnect(id); - - if (!resizeDone) { - resizeDone = true; - - // Add a small delay - GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => { - try { - this.resizeWindow(win, x, y, width, height); - } catch (e) { - console.error("Error resizing window:", e); - } - return GLib.SOURCE_REMOVE; - }); - } - }); - - // Fallback timeout in case the first-frame signal doesn't fire - // (for windows that are already mapped) - GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => { - if (!resizeDone) { - resizeDone = true; - try { - this.resizeWindow(win, x, y, width, height); - } catch (e) { - console.error("Error resizing window (fallback):", e); - } - } - return GLib.SOURCE_REMOVE; - }); - } - - resizeWindow(win: Meta.Window, x:number, y:number, width:number, height:number) { - // First, ensure window is not maximized or fullscreen - if (win.get_maximized()) { - console.log("WINDOW MAXIMIZED") - win.unmaximize(Meta.MaximizeFlags.BOTH); - } - - if (win.is_fullscreen()) { - console.log("WINDOW IS FULLSCREEN") - win.unmake_fullscreen(); - } - console.log("WINDOW", win.get_window_type(), win.allows_move()); - console.log("MONITOR INFO", this.getUsableMonitorSpace(win)); - console.log("NEW_SIZE", x, y, width, height); - // win.move_resize_frame(false, 50, 50, 300, 300); - win.move_resize_frame(false, x, y, width, height); - console.log("RESIZED WINDOW", win.get_frame_rect().height, win.get_frame_rect().width, win.get_frame_rect().x, win.get_frame_rect().y); - } - - _addWindow(window: Meta.Window) { - const windowId = window.get_id(); - - // Connect to window signals - const signals: Signal[] = []; - console.log("ADDING WINDOW", window); - // const act = window.get_compositor_private(); - // const id = act.connect('first-frame', _ => { - // this.resizeWindow(window); - // act.disconnect(id); - // }); - - const destroyId = window.connect('unmanaging', () => { - console.log("REMOVING WINDOW", windowId); - this._handleWindowClosed(windowId); - }); - signals.push({name: 'unmanaging', id: destroyId}); - - const focusId = window.connect('notify::has-focus', () => { - if (window.has_focus()) { - this._activeWindowId = windowId; - } - }); - signals.push({name: 'notify::has-focus', id: focusId}); - - // Add window to managed windows - this._windows.set(windowId, { - window: window, - signals: signals - }); - - // If this is the first window, make it the active one - if (this._windows.size === 1 || window.has_focus()) { - this._activeWindowId = windowId; - } - - this._tileWindows(); - } - - _handleWindowClosed(windowId: number) { - print("closing window", windowId); - const windowData = this._windows.get(windowId); - if (!windowData) { - return; - } - - // Disconnect signals - if (windowData.signals) { - windowData.signals.forEach(signal => { - try { - - if (windowData.window != null) { - windowData.window.disconnect(signal.id); - } - } catch (e) { - // Window might already be gone - } - }); - } - - // Remove from managed windows - this._windows.delete(windowId); - - // If this was the active window, find a new one - if (this._activeWindowId === windowId && this._windows.size > 0) { - this._activeWindowId = Array.from(this._windows.keys())[0]; - } else if (this._windows.size === 0) { - this._activeWindowId = null; - } - - // Retile remaining windows - this._tileWindows(); - } - - - _tileWindows() { - console.log("TILING WINDOWS") - const workspace = global.workspace_manager.get_active_workspace(); - const workArea = workspace.get_work_area_for_monitor( - global.display.get_primary_monitor() - ); - console.log("Workspace", workspace); - console.log("WorkArea", workArea); - - // Get all windows for current workspace - const windows = Array.from(this._windows.values()) - .filter(({window}) => { - - if (window != null) { - return window.get_workspace() === workspace; - } - }) - .map(({window}) => window); - - if (windows.length === 0) { - return; - } - this._tileHorizontally(windows, workArea) - - } - - _tileHorizontally(windows: (Meta.Window | null)[], workArea: Mtk.Rectangle) { - const windowWidth = Math.floor(workArea.width / windows.length); - - windows.forEach((window, index) => { - const x = workArea.x + (index * windowWidth); - const rect = { - x: x, - y: workArea.y, - width: windowWidth, - height: workArea.height - }; - if (window != null) { - this.safelyResizeWindow(window, rect.x, rect.y, rect.width, rect.height); - } - }); - } - - _isWindowTileable(window: Meta.Window) { - if (!window || !window.get_compositor_private()) { - return false; - } - - const windowType = window.get_window_type(); - console.log("WINDOW TYPE", windowType); - // Skip certain types of windows - return !window.is_skip_taskbar() && - windowType !== Meta.WindowType.DESKTOP && - windowType !== Meta.WindowType.DOCK && - windowType !== Meta.WindowType.DIALOG && - windowType !== Meta.WindowType.MODAL_DIALOG && - windowType !== Meta.WindowType.UTILITY && - windowType !== Meta.WindowType.MENU; - } - - // _updateBorder(window: Meta.Window) { - // console.log("UPDATING THE BORDER") - // // Clear the previous border - // this._clearBorder(); - // // Set a new border for the currently focused window - // if (window) { - // this._setBorder(window); - // this.lastFocusedWindow = window; - // } - // } - // - // _setBorder(window: Meta.Window) { - // console.log("SETTING THE BORDER") - // if (!window) return; - // - // const rect = window.get_frame_rect(); - // if (!rect) return; - // - // // Create a new actor for the border using St.Widget - // this.borderActor = new St.Widget({ - // name: 'active-window-border', - // // style_class: 'active-window-border', - // reactive: false, - // x: rect.x - 1, // Adjust for border width - // y: rect.y - 1, - // width: rect.width + 2, // Increased to accommodate border - // height: rect.height + 2, - // // Initial style with default color.ts - // // style: `border: 4px solid hsl(${this.hue}, 100%, 50%); border-radius: 5px;`, - // // style: `border: 2px solid rgba(0, 0, 0, 0.5); border-radius: 3px;` - // }); - // - // // Add the border actor to the UI group - // global.window_group.add_child(this.borderActor); - // // Main.layoutManager.uiGroup.add_child(this.borderActor); - // - // // Listen to window's changes in position and size - // this.focusWindowSignals?.push(window.connect('position-changed', () => this._updateBorderPosition(window))); - // this.focusWindowSignals?.push(window.connect('size-changed', () => this._updateBorderPosition(window))); - // this.focusWindowSignals?.push(window.connect('unmanaged', () => this._clearBorder())); - // - // this._updateBorderPosition(window); - // - // // Start the color.ts cycling - // this._startColorCycle(); - // } - - - disable() { - console.log("DISABLED AEROSPIKE!") - // Disconnect the focus signal and remove any existing borders - if (this._focusSignal) { - global.display.disconnect(this._focusSignal); - this._focusSignal = null; - } - - // Clear the border on the last focused window if it exists - // this._clearBorder(); - this.lastFocusedWindow = null; - } } \ No newline at end of file diff --git a/justfile b/justfile index bea9a43..c694103 100644 --- a/justfile +++ b/justfile @@ -32,6 +32,9 @@ run: install-and-run: install run +live-debug: + journalctl /usr/bin/gnome-shell -f -o cat + #pack: build # gnome-extensions pack dist \ # --force \ diff --git a/prefs.ts b/prefs.ts index b809720..8c1f4cf 100644 --- a/prefs.ts +++ b/prefs.ts @@ -1,4 +1,4 @@ // This file is just a wrapper around the compiled TypeScript code -import MyExtensionPreferences from './src/prefs.js'; +import MyExtensionPreferences from './src/prefs/prefs.js'; export default MyExtensionPreferences; \ No newline at end of file diff --git a/src/monitor.ts b/src/monitor.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/prefs.ts b/src/prefs/prefs.ts similarity index 100% rename from src/prefs.ts rename to src/prefs/prefs.ts diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index e9e97d9..0000000 --- a/src/utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Utility functions and type definitions - -/** - * Interface for the extension settings - */ -export interface ExtensionSettings { - keybinding1: string[]; - keybinding2: string[]; - keybinding3: string[]; - keybinding4: string[]; - dropdownOption: string; - colorSelection: string; -} - -/** - * Log a message with the extension name prefix - */ -export function log(message: string): void { - console.log(`[MyExtension] ${message}`); -} \ No newline at end of file diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..4072ad3 --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,30 @@ +export class Logger { + + static fatal(...args: any[]) { + console.log(`[Aerospike] [FATAL]`, ...args); + } + + static error(...args: any[]) { + console.log(`[Aerospike] [ERROR]`, ...args); + } + + static warn(...args: any[]) { + console.log(`[Aerospike] [WARN]`, ...args); + } + + static info(...args: any[]) { + console.log(`[Aerospike] [INFO]`, ...args); + } + + static debug(...args: any[]) { + console.log(`[Aerospike] [DEBUG]`, ...args); + } + + static trace(...args: any[]) { + console.log(`[Aerospike] [TRACE]`, ...args); + } + + static log(...args: any[]) { + console.log(`[Aerospike] [LOG]`, ...args); + } +} diff --git a/src/window.ts b/src/window.ts new file mode 100644 index 0000000..fa59df3 --- /dev/null +++ b/src/window.ts @@ -0,0 +1,219 @@ +import Meta from 'gi://Meta'; +import GLib from "gi://GLib"; +import Clutter from "gi://Clutter"; +import {IWindowManager} from "./windowManager.js"; +import {Logger} from "./utils/logger.js"; + +export type Signal = { + name: string; + id: number; +} + +export class WindowWrapper { + _window: Meta.Window; + _signals: Signal[]; + + constructor(window: Meta.Window) { + this._window = window; + this._signals = []; + } + + connectWindowSignals( + windowManager: IWindowManager, + ): void { + + const windowId = this._window.get_id(); + + + // Handle window destruction + const destroyId = this._window.connect('unmanaging', () => { + Logger.log("REMOVING WINDOW", windowId); + windowManager.handleWindowClosed(windowId) + }); + this._signals.push({name: 'unmanaging', id: destroyId}); + + // Handle focus changes + const focusId = this._window.connect('notify::has-focus', () => { + if (this._window.has_focus()) { + windowManager._activeWindowId = windowId; + } + }); + this._signals.push({name: 'notify::has-focus', id: focusId}); + + // Track window movement using position-changed signal + let lastPositionChangeTime = 0; + let dragInProgress = false; + + // const positionChangedId = this._window.connect('position-changed', window => { + // Logger.log("position-changed", window.get_id()); + // Logger.log(window.get_monitor()) + // // const currentTime = Date.now(); + // // const [x, y, _] = global.get_pointer(); + // // + // // // If this is the first move or it's been a while since the last move, consider it the start of a drag + // // if (!dragInProgress) { + // // dragInProgress = true; + // // Logger.log(`Window drag started for window ${windowId}. Mouse position: ${x}, ${y}`); + // // } + // // + // // // Update the time of the last position change + // // lastPositionChangeTime = currentTime; + // // + // // // Set a timeout to detect when dragging stops (when position changes stop coming in) + // // GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { + // // const timeSinceLastMove = Date.now() - lastPositionChangeTime; + // // // If it's been more than 200ms since the last move and we were dragging, consider the drag ended + // // if (timeSinceLastMove >= 200 && dragInProgress) { + // // dragInProgress = false; + // // const [endX, endY, _] = global.get_pointer(); + // // Logger.log(`Window drag ended for window ${windowId}. Mouse position: ${endX}, ${endY}`); + // // } + // // return GLib.SOURCE_REMOVE; // Remove the timeout + // // }); + // }); + // this._signals.push({name: 'position-changed', id: positionChangedId}); + + // Handle minimization + const minimizeId = this._window.connect('notify::minimized', () => { + if (this._window.minimized) { + Logger.log(`Window minimized: ${windowId}`); + // Remove window from managed windows temporarily + // windowManager.removeFromTree(this._window); + // If this was the active window, find a new one + windowManager.syncActiveWindow() + // Retile remaining windows + windowManager._tileWindows(); + + } else if (!this._window.minimized) { + Logger.log(`Window unminimized: ${windowId}`); + windowManager.addWindow(this._window); + + } + }); + this._signals.push({name: 'notify::minimized', id: minimizeId}); + + // Handle maximization + const maximizeId = this._window.connect('notify::maximized-horizontally', () => { + if (this._window.get_maximized()) { + Logger.log(`Window maximized: ${windowId}`); + } else { + Logger.log(`Window unmaximized: ${windowId}`); + } + }); + this._signals.push({name: 'notify::maximized-horizontally', id: maximizeId}); + } + + disconnectWindowSignals(): void { + + // Disconnect signals + if (this._signals) { + this._signals.forEach(signal => { + try { + if (this._window != null) { + this._window.disconnect(signal.id); + } + } catch (e) { + // Window might already be gone + } + }); + } + } + + resizeWindow(x: number, y: number, width: number, height: number) { + // First, ensure window is not maximized or fullscreen + if (this._window.get_maximized()) { + Logger.log("WINDOW MAXIMIZED") + this._window.unmaximize(Meta.MaximizeFlags.BOTH); + } + + if (this._window.is_fullscreen()) { + Logger.log("WINDOW IS FULLSCREEN") + this._window.unmake_fullscreen(); + } + Logger.log("WINDOW", this._window.get_window_type(), this._window.allows_move()); + Logger.log("MONITOR INFO", getUsableMonitorSpace(this._window)); + Logger.log("NEW_SIZE", x, y, width, height); + // win.move_resize_frame(false, 50, 50, 300, 300); + this._window.move_resize_frame(false, x, y, width, height); + Logger.log("RESIZED WINDOW", this._window.get_frame_rect().height, this._window.get_frame_rect().width, this._window.get_frame_rect().x, this._window.get_frame_rect().y); + } + + safelyResizeWindow(x: number, y: number, width: number, height: number): void { + Logger.log("SAFELY RESIZE", x, y, width, height); + const actor = this._window.get_compositor_private(); + + if (!actor) { + Logger.log("No actor available, can't resize safely yet"); + return; + } + +// Set a flag to track if the resize has been done + let resizeDone = false; + +// Connect to the first-frame signal + const id = actor.connect('first-frame', () => { + // Disconnect the signal handler + actor.disconnect(id); + + if (!resizeDone) { + resizeDone = true; + + // Add a small delay + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => { + try { + this.resizeWindow(x, y, width, height); + } catch (e) { + console.error("Error resizing window:", e); + } + return GLib.SOURCE_feaREMOVE; + }); + } + }); + +// Fallback timeout in case the first-frame signal doesn't fire +// (for windows that are already mapped) + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => { + if (!resizeDone) { + resizeDone = true; + try { + this.resizeWindow(x, y, width, height); + } catch (e) { + console.error("Error resizing window (fallback):", e); + } + } + return GLib.SOURCE_REMOVE; + }); + } + + // if (!this._window) return; + // this._window.unmaximize(Meta.MaximizeFlags.HORIZONTAL); + // this._window.unmaximize(Meta.MaximizeFlags.VERTICAL); + // this._window.unmaximize(Meta.MaximizeFlags.BOTH); + // + // let windowActor = this._window.get_compositor_private() as Clutter.Actor; + // if (!windowActor) return; + // windowActor.remove_all_transitions(); + // + // this._window.move_frame(true, x, y); + // this._window.move_resize_frame(true, x, y, width, height); + + +} + +function getUsableMonitorSpace(window: Meta.Window) { + // Get the current workspace + const workspace = window.get_workspace(); + + // Get the monitor index that this window is on + const monitorIndex = window.get_monitor(); + + // Get the work area + const workArea = workspace.get_work_area_for_monitor(monitorIndex); + + return { + x: workArea.x, + y: workArea.y, + width: workArea.width, + height: workArea.height + }; +} \ No newline at end of file diff --git a/src/windowManager.ts b/src/windowManager.ts new file mode 100644 index 0000000..d64113c --- /dev/null +++ b/src/windowManager.ts @@ -0,0 +1,274 @@ +import Meta from "gi://Meta"; + +import {WindowWrapper} from './window.js'; +import Mtk from "@girs/mtk-16"; +import {Logger} from "./utils/logger.js"; + + +export interface IWindowManager { + _activeWindowId: number | null; + + _tileWindows(): void; + + addWindow(window: Meta.Window): void; + + handleWindowClosed(windowId: number): void; + + // removeFromTree(window: Meta.Window): void; + syncActiveWindow(): number | null; +} + +export default class WindowManager implements IWindowManager { + _focusSignal: number | null; + _displaySignals: number[]; + _windowCreateId: number | null; + _windows: Map; + _activeWindowId: number | null; + + constructor() { + this._focusSignal = null; + this._windowCreateId = null; + this._displaySignals = []; + this._windows = new Map(); + this._activeWindowId = null; + } + + public enable(): void { + Logger.log("Starting Aerospike Window Manager"); + this.captureExistingWindows(); + // Connect window signals + this._windowCreateId = global.display.connect( + 'window-created', + (display, window) => { + this.handleWindowCreated(display, window); + } + ); + this.instantiateDisplaySignals() + } + + instantiateDisplaySignals(): void { + this._displaySignals.push( + global.display.connect("grab-op-begin", (display, window, op) => { + this.handleGrabOpBegin(display, window, op) + }), + global.display.connect("grab-op-end", (display, window, op) => { + this.handleGrabOpEnd(display, window, op) + }) + ) + + } + + handleGrabOpBegin(display: Meta.Display, window: Meta.Window, op: Meta.GrabOp): void { + Logger.log("Grab Op Start"); + Logger.log(display, window, op) + Logger.log(window.get_monitor()) + } + + handleGrabOpEnd(display: Meta.Display, window: Meta.Window, op: Meta.GrabOp): void { + Logger.log("Grab Op End "); + Logger.log(display, window, op) + this._tileWindows(); + } + + public disable(): void { + Logger.log("DISABLED AEROSPIKE WINDOW MANAGER!") + // Disconnect the focus signal and remove any existing borders + if (this._focusSignal) { + global.display.disconnect(this._focusSignal); + this._focusSignal = null; + } + } + + public captureExistingWindows() { + Logger.log("CAPTURING WINDOWS") + const workspace = global.workspace_manager.get_active_workspace(); + const windows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace); + Logger.log("WINDOWS", windows); + windows.forEach(window => { + if (this._isWindowTileable(window)) { + this.addWindow(window); + } + }); + + this._tileWindows(); + } + + + handleWindowCreated(display: Meta.Display, window: Meta.Window) { + Logger.log("WINDOW CREATED ON DISPLAY", window, display); + if (!this._isWindowTileable(window)) { + return; + } + Logger.log("WINDOW IS TILABLE"); + const actor = window.get_compositor_private(); + if (!actor) { + return; + } + this.addWindow(window); + } + + + /** + * Handle window closed event + */ + handleWindowClosed(windowId: number): void { + Logger.log("closing window", windowId); + const window = this._windows.get(windowId); + if (!window) { + return; + } + window.disconnectWindowSignals() + // Remove from managed windows + this._windows.delete(windowId); + + this.syncActiveWindow(); + // Retile remaining windows + this._tileWindows(); + } + + + public addWindow(window: Meta.Window) { + const windowId = window.get_id(); + + Logger.log("ADDING WINDOW", window); + + var wrapper = new WindowWrapper(window) + wrapper.connectWindowSignals(this) + + // Add window to managed windows + this._windows.set(windowId, wrapper); + + // If this is the first window, make it the active one + if (this._windows.size === 1 || window.has_focus()) { + this._activeWindowId = windowId; + } + + this._tileWindows(); + } + + // public UnmanageWindow(window: Meta.Window) { + // this._windows.delete(window.get_id()); + // this._unmanagedWindows.add(window.get_id()) + // } + // + // public ManageWindow(window: Meta.Window) { + // this._windows.set(window.get_id(), { + // window, + // }) + // } + + /** + * Synchronizes the active window with GNOME's currently active window + * + * This function queries GNOME Shell for the current focused window and + * updates the extension's active window tracking to match. + * + * @returns The window ID of the active window, or null if no window is active + */ + public syncActiveWindow(): number | null { + // // Get the active workspace + // const workspace = global.workspace_manager.get_active_workspace(); + // + // // Check if there is an active window + // const activeWindow = global.display.get_focus_window(); + // + // if (!activeWindow) { + // Logger.log("No active window found in GNOME"); + // this._activeWindowId = null; + // return null; + // } + // + // // Get the window ID + // const windowId = activeWindow.get_id(); + // + // // Check if this window is being managed by our extension + // if (this._windows.has(windowId)) { + // Logger.log(`Setting active window to ${windowId}`); + // this._activeWindowId = windowId; + // return windowId; + // } else { + // Logger.log(`Window ${windowId} is not managed by this extension`); + // + // // Try to find a managed window on the current workspace to make active + // const managedWindows = Array.from(this._windows.entries()) + // .filter(([_, wrapper]) => + // wrapper.window && wrapper.window.get_workspace() === workspace); + // + // if (managedWindows.length > 0) { + // // Take the first managed window on this workspace + // const firstWindowId = managedWindows[0][0]; + // Logger.log(`Using managed window ${firstWindowId} as active instead`); + // this._activeWindowId = firstWindowId; + // return firstWindowId; + // } + // + // // No managed windows on this workspace + // Logger.log("No managed windows found on the active workspace"); + // this._activeWindowId = null; + // return null; + // } + return null; + } + + _isWindowTileable(window: Meta.Window) { + if (!window || !window.get_compositor_private()) { + return false; + } + + const windowType = window.get_window_type(); + Logger.log("WINDOW TYPE", windowType); + // Skip certain types of windows + return !window.is_skip_taskbar() && + windowType !== Meta.WindowType.DESKTOP && + windowType !== Meta.WindowType.DOCK && + windowType !== Meta.WindowType.DIALOG && + windowType !== Meta.WindowType.MODAL_DIALOG && + windowType !== Meta.WindowType.UTILITY && + windowType !== Meta.WindowType.MENU; + } + + _tileWindows() { + Logger.log("TILING WINDOWS") + const workspace = global.workspace_manager.get_active_workspace(); + const workArea = workspace.get_work_area_for_monitor( + global.display.get_primary_monitor() + ); + Logger.log("Workspace", workspace); + Logger.log("WorkArea", workArea); + + // Get all windows for current workspace + const windows = Array.from(this._windows.values()) + .filter(({_window}) => { + + if (_window != null) { + return _window.get_workspace() === workspace; + } + }) + .map(x => x); + + if (windows.length === 0) { + return; + } + this._tileHorizontally(windows, workArea) + + } + + + _tileHorizontally(windows: (WindowWrapper | null)[], workArea: Mtk.Rectangle) { + const windowWidth = Math.floor(workArea.width / windows.length); + + windows.forEach((window, index) => { + const x = workArea.x + (index * windowWidth); + const rect = { + x: x, + y: workArea.y, + width: windowWidth, + height: workArea.height + }; + if (window != null) { + window.safelyResizeWindow(rect.x, rect.y, rect.width, rect.height); + } + }); + } + +} diff --git a/src/workspace.ts b/src/workspace.ts new file mode 100644 index 0000000..e69de29 diff --git a/tsconfig.json b/tsconfig.json index b9a89fa..97f97da 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,6 @@ "src/**/*" ], "files": [ - "extension.ts", - "winGroup.ts" + "extension.ts" ], }