瀏覽代碼

fixed window toggling with multiple windows (fixes #619)

Eugene Pankov 5 年之前
父節點
當前提交
f58cab0820

+ 33 - 3
app/lib/app.ts

@@ -1,4 +1,4 @@
-import { app, ipcMain, Menu, Tray, shell } from 'electron'
+import { app, ipcMain, Menu, Tray, shell, globalShortcut } from 'electron'
 // eslint-disable-next-line no-duplicate-imports
 import * as electron from 'electron'
 import { loadConfig } from './config'
@@ -9,8 +9,17 @@ export class Application {
     private windows: Window[] = []
 
     constructor () {
-        ipcMain.on('app:config-change', () => {
-            this.broadcast('host:config-change')
+        ipcMain.on('app:config-change', (_event, config) => {
+            this.broadcast('host:config-change', config)
+        })
+        
+        ipcMain.on('app:register-global-hotkey', (_event, specs) => {
+            globalShortcut.unregisterAll()
+            for (let spec of specs) {
+                globalShortcut.register(spec, () => {
+                    this.onGlobalHotkey()
+                })
+            }
         })
 
         const configData = loadConfig()
@@ -45,6 +54,9 @@ export class Application {
                 this.enableTray()
             }
         })
+        window.closed$.subscribe(() => {
+            this.windows = this.windows.filter(x => x !== window)
+        })
         if (process.platform === 'darwin') {
             this.setupMenu()
         }
@@ -52,6 +64,24 @@ export class Application {
         return window
     }
 
+    onGlobalHotkey () {
+        if (this.windows.some(x => x.isFocused())) {
+            for (let window of this.windows) {
+                window.hide()
+            }
+        } else {
+            for (let window of this.windows) {
+                window.present()
+            }
+        }
+    }
+
+    presentAllWindows() {
+        for (let window of this.windows) {
+            window.present()
+        }
+    }
+
     broadcast (event: string, ...args): void {
         for (const window of this.windows) {
             window.send(event, ...args)

+ 1 - 0
app/lib/index.ts

@@ -34,6 +34,7 @@ process.on('uncaughtException' as any, err => {
 })
 
 app.on('second-instance', (_event, argv, cwd) => {
+    application.presentAllWindows()
     application.send('host:second-instance', parseArgs(argv, cwd), cwd)
 })
 

+ 46 - 4
app/lib/window.ts

@@ -1,6 +1,6 @@
 import { Subject, Observable } from 'rxjs'
 import { debounceTime } from 'rxjs/operators'
-import { BrowserWindow, app, ipcMain, Rectangle, screen } from 'electron'
+import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen } from 'electron'
 import ElectronConfig = require('electron-config')
 import * as os from 'os'
 import * as path from 'path'
@@ -23,17 +23,20 @@ export interface WindowOptions {
 export class Window {
     ready: Promise<void>
     private visible = new Subject<boolean>()
+    private closed = new Subject<void>()
     private window: BrowserWindow
     private windowConfig: ElectronConfig
     private windowBounds: Rectangle
     private closing = false
     private lastVibrancy: {enabled: boolean, type?: string} | null = null
     private disableVibrancyWhileDragging = false
+    private configStore: any
 
     get visible$ (): Observable<boolean> { return this.visible }
+    get closed$ (): Observable<void> { return this.closed }
 
     constructor (options?: WindowOptions) {
-        let configData = loadConfig()
+        this.configStore = loadConfig()
 
         options = options || {}
 
@@ -70,7 +73,7 @@ export class Window {
             }
         }
 
-        if ((configData.appearance || {}).frame === 'native') {
+        if ((this.configStore.appearance || {}).frame === 'native') {
             bwOptions.frame = true
         } else {
             if (process.platform === 'darwin') {
@@ -86,7 +89,7 @@ export class Window {
         this.window.once('ready-to-show', () => {
             if (process.platform === 'darwin') {
                 this.window.setVibrancy('window')
-            } else if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
+            } else if (process.platform === 'win32' && (this.configStore.appearance || {}).vibrancy) {
                 this.setVibrancy(true)
             }
 
@@ -153,12 +156,49 @@ export class Window {
             return
         }
         this.window.webContents.send(event, ...args)
+        if (event === 'host:config-change') {
+            this.configStore = args[0]
+        }
     }
 
     isDestroyed (): boolean {
         return !this.window || this.window.isDestroyed()
     }
 
+    isFocused (): boolean {
+        return this.window.isFocused()
+    }
+
+    hide () {
+        if (process.platform === 'darwin') {
+            // Lose focus
+            Menu.sendActionToFirstResponder('hide:')
+        }
+        this.window.blur()
+        if (process.platform !== 'darwin') {
+            this.window.hide()
+        }
+    }
+
+    present () {
+        if (!this.window.isVisible()) {
+            // unfocused, invisible
+            this.window.show()
+            this.window.focus()
+        } else {
+            if (!this.configStore.appearance?.dock || this.configStore.appearance?.dock === 'off') {
+                // not docked, visible
+                setTimeout(() => {
+                    this.window.show()
+                    this.window.focus()
+                })
+            } else {
+                // docked, visible
+                this.window.hide()
+            }
+        }
+    }
+
     private setupWindowManagement () {
         this.window.on('show', () => {
             this.visible.next(true)
@@ -326,6 +366,8 @@ export class Window {
 
     private destroy () {
         this.window = null
+        this.closed.next()
         this.visible.complete()
+        this.closed.complete()
     }
 }

+ 0 - 42
terminus-core/src/components/appRoot.component.ts

@@ -128,13 +128,6 @@ export class AppRootComponent {
             this.docking.dock()
         })
 
-        this.hostApp.secondInstance$.subscribe(() => {
-            this.presentWindow()
-        })
-        this.hotkeys.globalHotkey.subscribe(() => {
-            this.onGlobalHotkey()
-        })
-
         this.hostApp.windowCloseRequest$.subscribe(async () => {
             this.app.closeWindow()
         })
@@ -175,41 +168,6 @@ export class AppRootComponent {
         })
     }
 
-    onGlobalHotkey () {
-        if (this.hostApp.getWindow().isFocused()) {
-            this.hideWindow()
-        } else {
-            this.presentWindow()
-        }
-    }
-
-    presentWindow () {
-        if (!this.hostApp.getWindow().isVisible()) {
-            // unfocused, invisible
-            this.hostApp.getWindow().show()
-            this.hostApp.getWindow().focus()
-        } else {
-            if (this.config.store.appearance.dock === 'off') {
-                // not docked, visible
-                setTimeout(() => {
-                    this.hostApp.getWindow().show()
-                    this.hostApp.getWindow().focus()
-                })
-            } else {
-                // docked, visible
-                this.hostApp.getWindow().hide()
-            }
-        }
-    }
-
-    hideWindow () {
-        this.electron.loseFocus()
-        this.hostApp.getWindow().blur()
-        if (this.hostApp.platform !== Platform.macOS) {
-            this.hostApp.getWindow().hide()
-        }
-    }
-
     async ngOnInit () {
         this.ready = true
 

+ 1 - 1
terminus-core/src/services/config.service.ts

@@ -157,7 +157,7 @@ export class ConfigService {
     save (): void {
         fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8')
         this.emitChange()
-        this.hostApp.broadcastConfigChange()
+        this.hostApp.broadcastConfigChange(this.store)
     }
 
     /**

+ 0 - 9
terminus-core/src/services/electron.service.ts

@@ -43,15 +43,6 @@ export class ElectronService {
         this.MenuItem = this.remote.MenuItem
     }
 
-    /**
-     * Removes OS focus from Terminus' window
-     */
-    loseFocus (): void {
-        if (process.platform === 'darwin') {
-            this.remote.Menu.sendActionToFirstResponder('hide:')
-        }
-    }
-
     async showMessageBox (
         browserWindow: Electron.BrowserWindow,
         options: Electron.MessageBoxOptions

+ 6 - 3
terminus-core/src/services/hostApp.service.ts

@@ -166,7 +166,6 @@ export class HostAppService {
             this.configChangeBroadcast.next()
         }))
 
-
         if (
             isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED) &&
             !isWindowsBuild(WIN_BUILD_FLUENT_BG_MOVE_BUG_FIXED)
@@ -251,8 +250,8 @@ export class HostAppService {
     /**
      * Notifies other windows of config file changes
      */
-    broadcastConfigChange (): void {
-        this.electron.ipcRenderer.send('app:config-change')
+    broadcastConfigChange (configStore: any): void {
+        this.electron.ipcRenderer.send('app:config-change', configStore)
     }
 
     emitReady (): void {
@@ -267,6 +266,10 @@ export class HostAppService {
         this.electron.ipcRenderer.send('window-close')
     }
 
+    registerGlobalHotkey (specs: string[]): void {
+        this.electron.ipcRenderer.send('app:register-global-hotkey', specs)
+    }
+
     relaunch (): void {
         if (this.isPortable) {
             this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE })

+ 8 - 6
terminus-core/src/services/hotkeys.service.ts

@@ -2,8 +2,9 @@ import { Injectable, Inject, NgZone, EventEmitter } from '@angular/core'
 import { Observable, Subject } from 'rxjs'
 import { HotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider'
 import { stringifyKeySequence } from './hotkeys.util'
-import { ConfigService } from '../services/config.service'
-import { ElectronService } from '../services/electron.service'
+import { ConfigService } from './config.service'
+import { ElectronService } from './electron.service'
+import { HostAppService } from './hostApp.service'
 
 export interface PartialHotkeyMatch {
     id: string
@@ -30,7 +31,6 @@ export class HotkeysService {
      */
     get hotkey$ (): Observable<string> { return this._hotkey }
 
-    globalHotkey = new EventEmitter<void>()
     private _hotkey = new Subject<string>()
     private currentKeystrokes: EventBufferEntry[] = []
     private disabledLevel = 0
@@ -38,6 +38,7 @@ export class HotkeysService {
 
     private constructor (
         private zone: NgZone,
+        private hostApp: HostAppService,
         private electron: ElectronService,
         private config: ConfigService,
         @Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[],
@@ -182,6 +183,7 @@ export class HotkeysService {
         if (typeof value === 'string') {
             value = [value]
         }
+        const specs: string[] = []
         value.forEach((item: string | string[]) => {
             item = typeof item === 'string' ? [item] : item
 
@@ -190,13 +192,13 @@ export class HotkeysService {
                 electronKeySpec = electronKeySpec.replace('⌘', 'Command')
                 electronKeySpec = electronKeySpec.replace('⌥', 'Alt')
                 electronKeySpec = electronKeySpec.replace(/-/g, '+')
-                this.electron.globalShortcut.register(electronKeySpec, () => {
-                    this.globalHotkey.emit()
-                })
+                specs.push(electronKeySpec)
             } catch (err) {
                 console.error('Could not register the global hotkey:', err)
             }
         })
+        
+        this.hostApp.registerGlobalHotkey(specs)
     }
 
     private getHotkeysConfig () {