Skip to content

Commit

Permalink
Add custom window / native window controls (#457)
Browse files Browse the repository at this point in the history
* Allow preload types to be used in frontend

Fix enum imported via meta file

* Hide default titlebar on win32 / linux

* Add interop for theme change handling

Limitation: Cannot theme hover background of OS-controlled buttons - using a white background in dark mode appears to have no hover

* Export overlay options

* Add windowStyle setting

* Allow disabling of modern window style

* Fix height change ignored

Removes initial test height

* Prevent zero overlay height

It resets the height of the buttons, but the CSS is set explicitly to zero, breaking anything relying on it.

* Remove coupling requirement from initial feature

- Removes on-by-default behaviour, leaving no changes to default behaviour
- Allows linked commits to happen async

* Add TS types, nits

* Add API - setWindowStyle
  • Loading branch information
webfiltered authored Dec 29, 2024
1 parent e6c1263 commit 260e3e0
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 11 deletions.
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ export const IPC_CHANNELS = {
VALIDATE_COMFYUI_SOURCE: 'validate-comfyui-source',
SHOW_DIRECTORY_PICKER: 'show-directory-picker',
INSTALL_COMFYUI: 'install-comfyui',
CHANGE_THEME: 'change-theme',
SHOW_CONTEXT_MENU: 'show-context-menu',
RESTART_CORE: 'restart-core',
GET_GPU: 'get-gpu',
SET_WINDOW_STYLE: 'set-window-style',
} as const;

export enum ProgressStatus {
Expand Down
7 changes: 7 additions & 0 deletions src/handlers/appInfoHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { app, ipcMain } from 'electron';
import { IPC_CHANNELS } from '../constants';
import { useDesktopConfig } from '../store/desktopConfig';
import type { TorchDeviceType } from '../preload';
import type { DesktopSettings } from '../store/desktopSettings';

/**
* Handles information about the app and current state in IPC channels.
Expand All @@ -20,5 +21,11 @@ export class AppInfoHandlers {
ipcMain.handle(IPC_CHANNELS.GET_GPU, async (): Promise<TorchDeviceType | undefined> => {
return await useDesktopConfig().getAsync('detectedGpu');
});
ipcMain.handle(
IPC_CHANNELS.SET_WINDOW_STYLE,
async (_event: Electron.IpcMainInvokeEvent, style: DesktopSettings['windowStyle']): Promise<void> => {
await useDesktopConfig().setAsync('windowStyle', style);
}
);
}
}
36 changes: 35 additions & 1 deletion src/main-process/appWindow.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { BrowserWindow, screen, app, shell, ipcMain, Tray, Menu, dialog, MenuItem } from 'electron';
import {
BrowserWindow,
screen,
app,
shell,
ipcMain,
Tray,
Menu,
dialog,
MenuItem,
nativeTheme,
type TitleBarOverlayOptions,
} from 'electron';
import path from 'node:path';
import Store from 'electron-store';
import { AppWindowSettings } from '../store/AppWindowSettings';
Expand All @@ -18,6 +30,10 @@ export class AppWindow {
private store: Store<AppWindowSettings>;
private messageQueue: Array<{ channel: string; data: unknown }> = [];
private rendererReady: boolean = false;
/** Default dark mode config for system window overlay (min/max/close window). */
private darkOverlay = { color: '#00000000', symbolColor: '#ddd' };
/** Default light mode config for system window overlay (min/max/close window). */
private lightOverlay = { ...this.darkOverlay, symbolColor: '#333' };
/** The application menu. */
private menu: Electron.Menu | null;
/** The "edit" menu - cut/copy/paste etc. */
Expand All @@ -36,6 +52,15 @@ export class AppWindow {
const storedX = store.get('windowX');
const storedY = store.get('windowY');

// macOS requires different handling to linux / win32
const customChrome: Pick<Electron.BrowserWindowConstructorOptions, 'titleBarStyle' | 'titleBarOverlay'> =
process.platform !== 'darwin' && useDesktopConfig().get('windowStyle') === 'custom'
? {
titleBarStyle: 'hidden',
titleBarOverlay: nativeTheme.shouldUseDarkColors ? this.darkOverlay : this.lightOverlay,
}
: {};

this.window = new BrowserWindow({
title: 'ComfyUI',
width: storedWidth,
Expand All @@ -53,6 +78,7 @@ export class AppWindow {
devTools: true,
},
autoHideMenuBar: true,
...customChrome,
});

if (!installed && storedX === undefined) this.window.center();
Expand Down Expand Up @@ -245,6 +271,14 @@ export class AppWindow {
});
}

changeTheme(options: TitleBarOverlayOptions): void {
if (process.platform === 'darwin' || useDesktopConfig().get('windowStyle') !== 'custom') return;

if (options.height) options.height = Math.round(options.height);
if (!options.height) delete options.height;
this.window.setTitleBarOverlay(options);
}

showSystemContextMenu(options?: ElectronContextMenuOptions): void {
if (options?.type === 'text') {
this.editMenu?.popup(options.pos);
Expand Down
5 changes: 4 additions & 1 deletion src/main-process/comfyDesktopApp.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { app, dialog, ipcMain, Notification } from 'electron';
import { app, dialog, ipcMain, Notification, type TitleBarOverlayOptions } from 'electron';
import log from 'electron-log/main';
import * as Sentry from '@sentry/electron/main';
import { graphics } from 'systeminformation';
Expand Down Expand Up @@ -91,6 +91,9 @@ export class ComfyDesktopApp {
}

registerIPCHandlers(): void {
ipcMain.on(IPC_CHANNELS.CHANGE_THEME, (_event, options: TitleBarOverlayOptions) => {
this.appWindow.changeTheme(options);
});
ipcMain.on(IPC_CHANNELS.SHOW_CONTEXT_MENU, (_event, options?: ElectronContextMenuOptions) => {
this.appWindow.showSystemContextMenu(options);
});
Expand Down
1 change: 1 addition & 0 deletions src/main_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export type {
PathValidationResult,
SystemPaths,
DownloadProgressUpdate,
ElectronOverlayOptions,
} from './preload';
44 changes: 35 additions & 9 deletions src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { contextBridge, ipcRenderer } from 'electron';
import { IPC_CHANNELS, ELECTRON_BRIDGE_API, ProgressStatus, DownloadStatus } from './constants';
import type { DownloadState } from './models/DownloadManager';
import path from 'node:path';
import type { DesktopSettings } from './store/desktopSettings';

/**
* Open a folder in the system's default file explorer.
Expand Down Expand Up @@ -41,6 +42,22 @@ export interface DownloadProgressUpdate {
message?: string;
}

/** @todo Type inference chain broken by comfyui-electron-types. This is duplication. */
export interface ElectronOverlayOptions {
/**
* The CSS color of the Window Controls Overlay when enabled.
*/
color?: string;
/**
* The CSS color of the symbols on the Window Controls Overlay when enabled.
*/
symbolColor?: string;
/**
* The height of the title bar and Window Controls Overlay in pixels.
*/
height?: number;
}

export interface ElectronContextMenuOptions {
type: 'system' | 'text' | 'image';
pos?: Electron.Point;
Expand Down Expand Up @@ -86,14 +103,15 @@ const electronAPI = {
console.log('Sending ready event to main process');
ipcRenderer.send(IPC_CHANNELS.RENDERER_READY);
},
isPackaged: () => {
/** Emulates app.ispackaged in renderer */
isPackaged: (): Promise<boolean> => {
return ipcRenderer.invoke(IPC_CHANNELS.IS_PACKAGED);
}, //Emulates app.ispackaged in renderer
},
restartApp: (customMessage?: string, delay?: number): void => {
console.log('Sending restarting app message to main process with custom message:', customMessage);
ipcRenderer.send(IPC_CHANNELS.RESTART_APP, { customMessage, delay });
},
reinstall: () => {
reinstall: (): Promise<void> => {
return ipcRenderer.invoke(IPC_CHANNELS.REINSTALL);
},
openDialog: (options: Electron.OpenDialogOptions) => {
Expand Down Expand Up @@ -161,9 +179,8 @@ const electronAPI = {
getElectronVersion: () => {
return ipcRenderer.invoke(IPC_CHANNELS.GET_ELECTRON_VERSION);
},
getComfyUIVersion: () => {
return __COMFYUI_VERSION__;
},
/** The ComfyUI core version (as defined in package.json) */
getComfyUIVersion: () => __COMFYUI_VERSION__,
/**
* Send an error message to Sentry
* @param error The error object or message to send
Expand Down Expand Up @@ -243,6 +260,12 @@ const electronAPI = {
installComfyUI: (installOptions: InstallOptions) => {
ipcRenderer.send(IPC_CHANNELS.INSTALL_COMFYUI, installOptions);
},
/**
* Update the Window Controls Overlay theme overrides
* @param theme The theme settings to apply
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window_Controls_Overlay_API}
*/
changeTheme: (theme: ElectronOverlayOptions): void => ipcRenderer.send(IPC_CHANNELS.CHANGE_THEME, theme),
/**
* Opens native context menus.
*
Expand All @@ -261,15 +284,18 @@ const electronAPI = {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return await ipcRenderer.invoke(IPC_CHANNELS.GET_GPU);
},
/** Sets the window style */
setWindowStyle: (style: DesktopSettings['windowStyle']): Promise<void> => {
return ipcRenderer.invoke(IPC_CHANNELS.SET_WINDOW_STYLE, style);
},
},
/** Restart the python server without restarting desktop. */
restartCore: async (): Promise<void> => {
console.log('Restarting core process');
await ipcRenderer.invoke(IPC_CHANNELS.RESTART_APP);
},
getPlatform: (): NodeJS.Platform => {
return process.platform;
},
/** Gets the platform reported by node.js */
getPlatform: () => process.platform,
} as const;

export type ElectronAPI = typeof electronAPI;
Expand Down
6 changes: 6 additions & 0 deletions src/store/desktopSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ export type DesktopSettings = {
/** The pytorch device that the user selected during installation. */
selectedDevice?: TorchDeviceType;
'Comfy-Desktop.RestoredCustomNodes': boolean;
/**
* Controls whether to use a custom window on linux/win32
* - `custom`: Modern, theme-reactive, feels like an integral part of the UI
* - `default`: Impersonal, static, plain - default window title bar
*/
windowStyle?: 'custom' | 'default';
};

0 comments on commit 260e3e0

Please sign in to comment.