From 8a7db19e7339eda7a883fafa59f8b9cde4dbe80b Mon Sep 17 00:00:00 2001 From: fullex <106392080+0xfullex@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:07:16 +0800 Subject: [PATCH] fix: Resolve a series of miniWindow display issues and improve app behavior across platforms (#3072) --- src/main/services/ShortcutService.ts | 13 +- src/main/services/WindowService.ts | 133 ++++++++++++++---- src/renderer/src/i18n/locales/en-us.json | 2 +- src/renderer/src/i18n/locales/ja-jp.json | 2 +- src/renderer/src/i18n/locales/ru-ru.json | 2 +- src/renderer/src/i18n/locales/zh-cn.json | 2 +- src/renderer/src/i18n/locales/zh-tw.json | 2 +- .../src/pages/settings/GeneralSettings.tsx | 2 +- 8 files changed, 112 insertions(+), 46 deletions(-) diff --git a/src/main/services/ShortcutService.ts b/src/main/services/ShortcutService.ts index c4bad34d..03caa02d 100644 --- a/src/main/services/ShortcutService.ts +++ b/src/main/services/ShortcutService.ts @@ -23,17 +23,8 @@ function getShortcutHandler(shortcut: Shortcut) { configManager.setZoomFactor(1) } case 'show_app': - return (window: BrowserWindow) => { - if (window.isVisible()) { - if (window.isFocused()) { - window.hide() - } else { - window.focus() - } - } else { - window.show() - window.focus() - } + return () => { + windowService.toggleMainWindow() } case 'mini_window': return () => { diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts index f510f814..f72c5b1b 100644 --- a/src/main/services/WindowService.ts +++ b/src/main/services/WindowService.ts @@ -16,6 +16,9 @@ export class WindowService { private mainWindow: BrowserWindow | null = null private miniWindow: BrowserWindow | null = null private wasFullScreen: boolean = false + //hacky-fix: store the focused status of mainWindow before miniWindow shows + //to restore the focus status when miniWindow hides + private wasMainWindowFocused: boolean = false private selectionMenuWindow: BrowserWindow | null = null private lastSelectedText: string = '' private contextMenu: Menu | null = null @@ -30,6 +33,7 @@ export class WindowService { public createMainWindow(): BrowserWindow { if (this.mainWindow && !this.mainWindow.isDestroyed()) { this.mainWindow.show() + this.mainWindow.focus() return this.mainWindow } @@ -56,7 +60,7 @@ export class WindowService { titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight, backgroundColor: isMac ? undefined : theme === 'dark' ? '#181818' : '#FFFFFF', trafficLightPosition: { x: 8, y: 12 }, - ...(process.platform === 'linux' ? { icon } : {}), + ...(isLinux ? { icon } : {}), webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false, @@ -68,6 +72,12 @@ export class WindowService { this.setupMainWindow(this.mainWindow, mainWindowState) + //preload miniWindow to resolve series of issues about miniWindow in Mac + const enableQuickAssistant = configManager.getEnableQuickAssistant() + if (enableQuickAssistant && !this.miniWindow) { + this.miniWindow = this.createMiniWindow(true) + } + return this.mainWindow } @@ -148,6 +158,8 @@ export class WindowService { // show window only when laucn to tray not set const isLaunchToTray = configManager.getLaunchToTray() if (!isLaunchToTray) { + //[mac]hacky-fix: miniWindow set visibleOnFullScreen:true will cause dock icon disappeared + app.dock?.show() mainWindow.show() } }) @@ -305,9 +317,8 @@ export class WindowService { event.preventDefault() mainWindow.hide() - if (isMac && isTrayOnClose) { - app.dock?.hide() //for mac to hide to tray - } + //for mac users, should hide dock icon if close to tray + app.dock?.hide() }) mainWindow.on('closed', () => { @@ -328,44 +339,48 @@ export class WindowService { if (this.mainWindow && !this.mainWindow.isDestroyed()) { if (this.mainWindow.isMinimized()) { - return this.mainWindow.restore() + this.mainWindow.restore() + return } + //[macOS] Known Issue + // setVisibleOnAllWorkspaces true/false will NOT bring window to current desktop in Mac (works fine with Windows) + // AppleScript may be a solution, but it's not worth + this.mainWindow.setVisibleOnAllWorkspaces(true) this.mainWindow.show() this.mainWindow.focus() + this.mainWindow.setVisibleOnAllWorkspaces(false) } else { this.mainWindow = this.createMainWindow() - this.mainWindow.focus() } - - //for mac users, when window is shown, should show dock icon (dock may be set to hide when launch) - app.dock?.show() } - public showMiniWindow() { - const enableQuickAssistant = configManager.getEnableQuickAssistant() - - if (!enableQuickAssistant) { + public toggleMainWindow() { + // should not toggle main window when in full screen + if (this.wasFullScreen) { return } - if (this.selectionMenuWindow && !this.selectionMenuWindow.isDestroyed()) { - this.selectionMenuWindow.hide() - } - - if (this.miniWindow && !this.miniWindow.isDestroyed()) { - if (this.miniWindow.isMinimized()) { - this.miniWindow.restore() + if (this.mainWindow && !this.mainWindow.isDestroyed() && this.mainWindow.isVisible()) { + if (this.mainWindow.isFocused()) { + // if tray is enabled, hide the main window, else do nothing + if (configManager.getTray()) { + this.mainWindow.hide() + app.dock?.hide() + } + } else { + this.mainWindow.focus() } - this.miniWindow.show() - this.miniWindow.center() - this.miniWindow.focus() return } + this.showMainWindow() + } + + public createMiniWindow(isPreload: boolean = false): BrowserWindow { this.miniWindow = new BrowserWindow({ width: 500, height: 520, - show: true, + show: false, autoHideMenuBar: true, transparent: isMac, vibrancy: 'under-window', @@ -375,6 +390,11 @@ export class WindowService { alwaysOnTop: true, resizable: false, useContentSize: true, + ...(isMac ? { type: 'panel' } : {}), + skipTaskbar: true, + minimizable: false, + maximizable: false, + fullscreenable: false, webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false, @@ -383,8 +403,23 @@ export class WindowService { } }) + //miniWindow should show in current desktop + this.miniWindow?.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }) + //make miniWindow always on top of fullscreen apps with level set + this.miniWindow.setAlwaysOnTop(true, 'screen-saver', 1) + + this.miniWindow.on('ready-to-show', () => { + if (isPreload) { + return + } + + this.wasMainWindowFocused = this.mainWindow?.isFocused() || false + this.miniWindow?.center() + this.miniWindow?.show() + }) + this.miniWindow.on('blur', () => { - this.miniWindow?.hide() + this.hideMiniWindow() }) this.miniWindow.on('closed', () => { @@ -410,9 +445,48 @@ export class WindowService { hash: '#/mini' }) } + + return this.miniWindow + } + + public showMiniWindow() { + const enableQuickAssistant = configManager.getEnableQuickAssistant() + + if (!enableQuickAssistant) { + return + } + + if (this.selectionMenuWindow && !this.selectionMenuWindow.isDestroyed()) { + this.selectionMenuWindow.hide() + } + + if (this.miniWindow && !this.miniWindow.isDestroyed()) { + this.wasMainWindowFocused = this.mainWindow?.isFocused() || false + + if (this.miniWindow.isMinimized()) { + this.miniWindow.restore() + } + this.miniWindow.show() + return + } + + this.miniWindow = this.createMiniWindow() } public hideMiniWindow() { + //hacky-fix:[mac/win] previous window(not self-app) should be focused again after miniWindow hide + if (isWin) { + this.miniWindow?.minimize() + this.miniWindow?.hide() + return + } else if (isMac) { + this.miniWindow?.hide() + if (!this.wasMainWindowFocused) { + app.hide() + } + return + } + this.miniWindow?.hide() } @@ -421,11 +495,12 @@ export class WindowService { } public toggleMiniWindow() { - if (this.miniWindow) { - this.miniWindow.isVisible() ? this.miniWindow.hide() : this.miniWindow.show() - } else { - this.showMiniWindow() + if (this.miniWindow && !this.miniWindow.isDestroyed() && this.miniWindow.isVisible()) { + this.hideMiniWindow() + return } + + this.showMiniWindow() } public showSelectionMenu(bounds: { x: number; y: number }) { diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index a2054b9f..8a5e85f4 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1190,7 +1190,7 @@ "reset_defaults_confirm": "Are you sure you want to reset all shortcuts?", "reset_to_default": "Reset to Default", "search_message": "Search Message", - "show_app": "Show App", + "show_app": "Show/Hide App", "show_settings": "Open Settings", "title": "Keyboard Shortcuts", "toggle_new_context": "Clear Context", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 442b4c0a..d775707a 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1189,7 +1189,7 @@ "reset_defaults_confirm": "すべてのショートカットをリセットしてもよろしいですか?", "reset_to_default": "デフォルトにリセット", "search_message": "メッセージを検索", - "show_app": "アプリを表示", + "show_app": "アプリを表示/非表示", "show_settings": "設定を開く", "title": "ショートカット", "toggle_new_context": "コンテキストをクリア", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 636317d1..de4f0a66 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1189,7 +1189,7 @@ "reset_defaults_confirm": "Вы уверены, что хотите сбросить все горячие клавиши?", "reset_to_default": "Сбросить настройки по умолчанию", "search_message": "Поиск сообщения", - "show_app": "Показать приложение", + "show_app": "Показать/скрыть приложение", "show_settings": "Открыть настройки", "title": "Горячие клавиши", "toggle_new_context": "Очистить контекст", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index c7f6eb53..3ff6a79d 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1190,7 +1190,7 @@ "reset_defaults_confirm": "确定要重置所有快捷键吗?", "reset_to_default": "重置为默认", "search_message": "搜索消息", - "show_app": "显示应用", + "show_app": "显示/隐藏应用", "show_settings": "打开设置", "title": "快捷方式", "toggle_new_context": "清除上下文", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 222b1f0d..14027805 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1189,7 +1189,7 @@ "reset_defaults_confirm": "確定要重設所有快捷鍵嗎?", "reset_to_default": "重設為預設", "search_message": "搜尋訊息", - "show_app": "顯示應用程式", + "show_app": "顯示/隱藏應用程式", "show_settings": "開啟設定", "title": "快速方式", "toggle_new_context": "清除上下文", diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index 65ef1102..34dfd062 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -175,7 +175,7 @@ const GeneralSettings: FC = () => { {t('settings.tray.onclose')} - updateTrayOnClose(checked)} disabled={!tray} /> + updateTrayOnClose(checked)} />