From 59ed07956d1f37a30d2d9ea08e352a8945bc78ee Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:29:35 +1100 Subject: [PATCH 1/3] Allow change of window style without app restart --- src/handlers/appInfoHandlers.ts | 1 + src/main-process/appWindow.ts | 81 ++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/handlers/appInfoHandlers.ts b/src/handlers/appInfoHandlers.ts index dc16f497d..b33525516 100644 --- a/src/handlers/appInfoHandlers.ts +++ b/src/handlers/appInfoHandlers.ts @@ -44,6 +44,7 @@ export class AppInfoHandlers { IPC_CHANNELS.SET_WINDOW_STYLE, async (_event: Electron.IpcMainInvokeEvent, style: DesktopSettings['windowStyle']): Promise => { await useDesktopConfig().setAsync('windowStyle', style); + await appWindow.recreateWindow(); } ); ipcMain.handle(IPC_CHANNELS.GET_WINDOW_STYLE, async (): Promise => { diff --git a/src/main-process/appWindow.ts b/src/main-process/appWindow.ts index 0b8a69c09..06edda7c8 100644 --- a/src/main-process/appWindow.ts +++ b/src/main-process/appWindow.ts @@ -40,8 +40,7 @@ export class AppWindow { /** The "edit" menu - cut/copy/paste etc. */ private editMenu?: Menu; /** Whether this window was created with title bar overlay enabled. When `false`, Electron throws when calling {@link BrowserWindow.setTitleBarOverlay}. */ - public readonly customWindowEnabled: boolean = - process.platform !== 'darwin' && useDesktopConfig().get('windowStyle') === 'custom'; + private customWindowEnabled: boolean = false; /** Always returns `undefined` in production. When running unpackaged, returns `DEV_SERVER_URL` if set, otherwise `undefined`. */ private get devUrlOverride() { @@ -49,11 +48,23 @@ export class AppWindow { } public constructor() { + const store = this.loadWindowStore(); + this.store = store; + + this.window = this.#createWindow(); + + this.sendQueuedEventsOnReady(); + this.setupTray(); + this.menu = this.buildMenu(); + this.buildTextMenu(); + } + + #createWindow() { + const { store } = this; + const installed = useDesktopConfig().get('installState') === 'installed'; const primaryDisplay = screen.getPrimaryDisplay(); const { width, height } = installed ? primaryDisplay.workAreaSize : { width: 1024, height: 768 }; - const store = this.loadWindowStore(); - this.store = store; // Retrieve stored window size, or use default if not available const storedWidth = store.get('windowWidth', width); @@ -62,6 +73,7 @@ export class AppWindow { const storedY = store.get('windowY'); // macOS requires different handling to linux / win32 + this.customWindowEnabled = process.platform !== 'darwin' && useDesktopConfig().get('windowStyle') === 'custom'; const customChrome: Electron.BrowserWindowConstructorOptions = this.customWindowEnabled ? { titleBarStyle: 'hidden', @@ -69,7 +81,7 @@ export class AppWindow { } : {}; - this.window = new BrowserWindow({ + const window = new BrowserWindow({ title: 'ComfyUI', width: storedWidth, height: storedHeight, @@ -89,17 +101,23 @@ export class AppWindow { autoHideMenuBar: true, ...customChrome, }); - this.window.once('ready-to-show', () => this.window.show()); + this.window = window; + window.once('ready-to-show', () => window.show()); - if (!installed && storedX === undefined) this.window.center(); - if (store.get('windowMaximized')) this.window.maximize(); + if (!installed && storedX === undefined) window.center(); + if (store.get('windowMaximized')) window.maximize(); - this.setupWindowEvents(); + this.setupWindowEvents(window); this.setupAppEvents(); - this.sendQueuedEventsOnReady(); - this.setupTray(); - this.menu = this.buildMenu(); - this.buildTextMenu(); + return window; + } + + async recreateWindow() { + const { window } = this; + + this.window = this.#createWindow(); + window.close(); + await this.reloadLastComfyUIUrl(); } public isReady(): boolean { @@ -140,12 +158,23 @@ export class AppWindow { }); } + /** The last set of server args that were used to form the window URL. */ + private lastServerArgs?: ServerArgs; + public async loadComfyUI(serverArgs: ServerArgs) { + this.lastServerArgs = serverArgs; + const host = serverArgs.host === '0.0.0.0' ? 'localhost' : serverArgs.host; const url = this.devUrlOverride ?? `http://${host}:${serverArgs.port}`; await this.window.loadURL(url); } + /** Reloads using most recent used args from {@link loadComfyUI}. @throws When args have not yet been set. */ + public async reloadLastComfyUIUrl() { + if (this.lastServerArgs) return await this.loadComfyUI(this.lastServerArgs); + throw new Error('Cannot reload ComfyUI URL without server args. loadComfyUI must be called first.'); + } + public openDevTools(): void { this.window.webContents.openDevTools(); } @@ -237,27 +266,27 @@ export class AppWindow { } } - private setupWindowEvents(): void { - // eslint-disable-next-line unicorn/consistent-function-scoping + private setupWindowEvents(window: BrowserWindow): void { const updateBounds = () => { - if (!this.window) return; + if (!window) return; // If maximized, do not update position / size. - const isMaximized = this.window.isMaximized(); - this.store.set('windowMaximized', isMaximized); + const { store } = this; + const isMaximized = window.isMaximized(); + store.set('windowMaximized', isMaximized); if (isMaximized) return; - const { width, height, x, y } = this.window.getBounds(); - this.store.set('windowWidth', width); - this.store.set('windowHeight', height); - this.store.set('windowX', x); - this.store.set('windowY', y); + const { width, height, x, y } = window.getBounds(); + store.set('windowWidth', width); + store.set('windowHeight', height); + store.set('windowX', x); + store.set('windowY', y); }; - this.window.on('resize', updateBounds); - this.window.on('move', updateBounds); + window.on('resize', updateBounds); + window.on('move', updateBounds); - this.window.webContents.setWindowOpenHandler(({ url }) => { + window.webContents.setWindowOpenHandler(({ url }) => { // eslint-disable-next-line @typescript-eslint/no-floating-promises shell.openExternal(url); return { action: 'deny' }; From c1048944e003aad37850cec85137aea8e9dad613 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Sun, 12 Jan 2025 01:13:35 +1100 Subject: [PATCH 2/3] Add error handling & resilience to window style change --- src/handlers/appInfoHandlers.ts | 3 +-- src/main-process/appWindow.ts | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/handlers/appInfoHandlers.ts b/src/handlers/appInfoHandlers.ts index b33525516..4dda31869 100644 --- a/src/handlers/appInfoHandlers.ts +++ b/src/handlers/appInfoHandlers.ts @@ -43,8 +43,7 @@ export class AppInfoHandlers { ipcMain.handle( IPC_CHANNELS.SET_WINDOW_STYLE, async (_event: Electron.IpcMainInvokeEvent, style: DesktopSettings['windowStyle']): Promise => { - await useDesktopConfig().setAsync('windowStyle', style); - await appWindow.recreateWindow(); + await appWindow.setWindowStyle(style); } ); ipcMain.handle(IPC_CHANNELS.GET_WINDOW_STYLE, async (): Promise => { diff --git a/src/main-process/appWindow.ts b/src/main-process/appWindow.ts index 06edda7c8..745e4f418 100644 --- a/src/main-process/appWindow.ts +++ b/src/main-process/appWindow.ts @@ -20,6 +20,7 @@ import { getAppResourcesPath } from '../install/resourcePaths'; import type { ElectronContextMenuOptions } from '../preload'; import { AppWindowSettings } from '../store/AppWindowSettings'; import { useDesktopConfig } from '../store/desktopConfig'; +import type { DesktopSettings } from '../store/desktopSettings'; /** * Creates a single application window that displays the renderer and encapsulates all the logic for sending messages to the renderer. @@ -112,7 +113,13 @@ export class AppWindow { return window; } - async recreateWindow() { + /** + * Recreates the application window by closing the current window and creating a new one. + * + * After the new window is created, it reloads the last ComfyUI URL. + * @returns A promise that resolves after the recreated window has loaded the last ComfyUI URL + */ + async recreateWindow(): Promise { const { window } = this; this.window = this.#createWindow(); @@ -120,6 +127,27 @@ export class AppWindow { await this.reloadLastComfyUIUrl(); } + /** + * Changes the custom window style for win32 / linux. Recreates the window if the style is changed. + * @param style The new window style to be applied + * @returns A promise that resolves when the window style has been set and the window has been recreated. + * Ignores attempts to unset the style or set it to the current value. + */ + async setWindowStyle(style: DesktopSettings['windowStyle']): Promise { + log.info(`Setting window style:`, style); + if (!style) return; + + const store = useDesktopConfig(); + const current = store.get('windowStyle'); + if (style === current) { + log.warn(`Ignoring attempt to set window style to current value [${current}]`); + // return; + } + + store.set('windowStyle', style); + await this.recreateWindow(); + } + public isReady(): boolean { return this.rendererReady; } From f90aaa3d7d10a2b74ac5e24f70fb7d3208b34ff5 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Mon, 20 Jan 2025 04:05:20 +1100 Subject: [PATCH 3/3] nit --- src/main-process/appWindow.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main-process/appWindow.ts b/src/main-process/appWindow.ts index 745e4f418..88b2fea6e 100644 --- a/src/main-process/appWindow.ts +++ b/src/main-process/appWindow.ts @@ -49,8 +49,7 @@ export class AppWindow { } public constructor() { - const store = this.loadWindowStore(); - this.store = store; + this.store = this.loadWindowStore(); this.window = this.#createWindow();