mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-22 11:28:00 +00:00
updated window docking logic
This commit is contained in:
@@ -23,6 +23,7 @@ export class Application {
|
|||||||
private windows: Window[] = []
|
private windows: Window[] = []
|
||||||
private globalHotkey$ = new Subject<void>()
|
private globalHotkey$ = new Subject<void>()
|
||||||
private quitRequested = false
|
private quitRequested = false
|
||||||
|
private configStore: any
|
||||||
userPluginsPath: string
|
userPluginsPath: string
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
@@ -32,6 +33,7 @@ export class Application {
|
|||||||
|
|
||||||
ipcMain.on('app:config-change', (_event, config) => {
|
ipcMain.on('app:config-change', (_event, config) => {
|
||||||
this.broadcast('host:config-change', config)
|
this.broadcast('host:config-change', config)
|
||||||
|
this.configStore = config
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
|
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
|
||||||
@@ -61,10 +63,10 @@ export class Application {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const configData = loadConfig()
|
this.configStore = loadConfig()
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
app.commandLine.appendSwitch('no-sandbox')
|
app.commandLine.appendSwitch('no-sandbox')
|
||||||
if (((configData.appearance || {}).opacity || 1) !== 1) {
|
if (((this.configStore.appearance || {}).opacity || 1) !== 1) {
|
||||||
app.commandLine.appendSwitch('enable-transparent-visuals')
|
app.commandLine.appendSwitch('enable-transparent-visuals')
|
||||||
app.disableHardwareAcceleration()
|
app.disableHardwareAcceleration()
|
||||||
}
|
}
|
||||||
@@ -84,7 +86,7 @@ export class Application {
|
|||||||
app.commandLine.appendSwitch('lang', 'EN')
|
app.commandLine.appendSwitch('lang', 'EN')
|
||||||
app.allowRendererProcessReuse = false
|
app.allowRendererProcessReuse = false
|
||||||
|
|
||||||
for (const flag of configData.flags || [['force_discrete_gpu', '0']]) {
|
for (const flag of this.configStore.flags || [['force_discrete_gpu', '0']]) {
|
||||||
app.commandLine.appendSwitch(flag[0], flag[1])
|
app.commandLine.appendSwitch(flag[0], flag[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +106,9 @@ export class Application {
|
|||||||
async newWindow (options?: WindowOptions): Promise<Window> {
|
async newWindow (options?: WindowOptions): Promise<Window> {
|
||||||
const window = new Window(this, options)
|
const window = new Window(this, options)
|
||||||
this.windows.push(window)
|
this.windows.push(window)
|
||||||
|
if (this.windows.length === 1){
|
||||||
|
window.makeMain()
|
||||||
|
}
|
||||||
window.visible$.subscribe(visible => {
|
window.visible$.subscribe(visible => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
this.disableTray()
|
this.disableTray()
|
||||||
@@ -113,16 +118,28 @@ export class Application {
|
|||||||
})
|
})
|
||||||
window.closed$.subscribe(() => {
|
window.closed$.subscribe(() => {
|
||||||
this.windows = this.windows.filter(x => x !== window)
|
this.windows = this.windows.filter(x => x !== window)
|
||||||
|
if (!this.windows.some(x => x.isMainWindow)) {
|
||||||
|
this.windows[0]?.makeMain()
|
||||||
|
this.windows[0]?.present()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
this.setupMenu()
|
this.setupMenu()
|
||||||
}
|
}
|
||||||
await window.ready
|
await window.ready
|
||||||
|
window.present()
|
||||||
return window
|
return window
|
||||||
}
|
}
|
||||||
|
|
||||||
onGlobalHotkey (): void {
|
onGlobalHotkey (): void {
|
||||||
if (this.windows.some(x => x.isFocused() && x.isVisible())) {
|
let isPresent = this.windows.some(x => x.isFocused() && x.isVisible())
|
||||||
|
const isDockedOnTop = this.windows.some(x => x.isDockedOnTop())
|
||||||
|
if (isDockedOnTop) {
|
||||||
|
// if docked and on top, hide even if not focused right now
|
||||||
|
isPresent = this.windows.some(x => x.isVisible())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent) {
|
||||||
for (const window of this.windows) {
|
for (const window of this.windows) {
|
||||||
window.hide()
|
window.hide()
|
||||||
}
|
}
|
||||||
@@ -191,7 +208,7 @@ export class Application {
|
|||||||
|
|
||||||
focus (): void {
|
focus (): void {
|
||||||
for (const window of this.windows) {
|
for (const window of this.windows) {
|
||||||
window.show()
|
window.present()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,6 +32,7 @@ const activityIcon = nativeImage.createFromPath(`${app.getAppPath()}/assets/acti
|
|||||||
|
|
||||||
export class Window {
|
export class Window {
|
||||||
ready: Promise<void>
|
ready: Promise<void>
|
||||||
|
isMainWindow = false
|
||||||
private visible = new Subject<boolean>()
|
private visible = new Subject<boolean>()
|
||||||
private closed = new Subject<void>()
|
private closed = new Subject<void>()
|
||||||
private window?: GlasstronWindow
|
private window?: GlasstronWindow
|
||||||
@@ -157,6 +158,11 @@ export class Window {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeMain () {
|
||||||
|
this.isMainWindow = true
|
||||||
|
this.window.webContents.send('host:became-main-window')
|
||||||
|
}
|
||||||
|
|
||||||
setVibrancy (enabled: boolean, type?: string, userRequested?: boolean): void {
|
setVibrancy (enabled: boolean, type?: string, userRequested?: boolean): void {
|
||||||
if (userRequested ?? true) {
|
if (userRequested ?? true) {
|
||||||
this.lastVibrancy = { enabled, type }
|
this.lastVibrancy = { enabled, type }
|
||||||
@@ -181,11 +187,6 @@ export class Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
show (): void {
|
|
||||||
this.window.show()
|
|
||||||
this.window.moveTop()
|
|
||||||
}
|
|
||||||
|
|
||||||
focus (): void {
|
focus (): void {
|
||||||
this.window.focus()
|
this.window.focus()
|
||||||
}
|
}
|
||||||
@@ -197,6 +198,7 @@ export class Window {
|
|||||||
this.window.webContents.send(event, ...args)
|
this.window.webContents.send(event, ...args)
|
||||||
if (event === 'host:config-change') {
|
if (event === 'host:config-change') {
|
||||||
this.configStore = args[0]
|
this.configStore = args[0]
|
||||||
|
this.enableDockedWindowStyles(this.isDockedOnTop())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,45 +214,53 @@ export class Window {
|
|||||||
return this.window.isVisible()
|
return this.window.isVisible()
|
||||||
}
|
}
|
||||||
|
|
||||||
hide (): void {
|
isDockedOnTop (): boolean {
|
||||||
|
return this.isMainWindow && this.configStore.appearance?.dock && this.configStore.appearance?.dock !== 'off' && (this.configStore.appearance?.dockAlwaysOnTop ?? true)
|
||||||
|
}
|
||||||
|
|
||||||
|
async hide (): Promise<void> {
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
// Lose focus
|
// Lose focus
|
||||||
Menu.sendActionToFirstResponder('hide:')
|
Menu.sendActionToFirstResponder('hide:')
|
||||||
}
|
if (this.isDockedOnTop()) {
|
||||||
this.window.blur()
|
await this.enableDockedWindowStyles(false)
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
this.window.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
present (): void {
|
|
||||||
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 {
|
|
||||||
if (this.configStore.appearance?.dockAlwaysOnTop) {
|
|
||||||
// docked, visible, on top
|
|
||||||
this.window.hide()
|
|
||||||
} else {
|
|
||||||
// docked, visible, not on top
|
|
||||||
this.window.focus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.window.blur()
|
||||||
|
this.window.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
async show (): Promise<void> {
|
||||||
|
await this.enableDockedWindowStyles(this.isDockedOnTop())
|
||||||
|
this.window.show()
|
||||||
|
this.window.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
async present (): Promise<void> {
|
||||||
|
await this.show()
|
||||||
|
this.window.moveTop()
|
||||||
}
|
}
|
||||||
|
|
||||||
passCliArguments (argv: string[], cwd: string, secondInstance: boolean): void {
|
passCliArguments (argv: string[], cwd: string, secondInstance: boolean): void {
|
||||||
this.send('cli', parseArgs(argv, cwd), cwd, secondInstance)
|
this.send('cli', parseArgs(argv, cwd), cwd, secondInstance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async enableDockedWindowStyles (enabled: boolean) {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
if (enabled) {
|
||||||
|
app.dock.hide()
|
||||||
|
this.window.setAlwaysOnTop(true, 'screen-saver', 1)
|
||||||
|
this.window.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })
|
||||||
|
this.window.setFullScreenable(false)
|
||||||
|
} else {
|
||||||
|
await app.dock.show()
|
||||||
|
this.window.setAlwaysOnTop(false)
|
||||||
|
this.window.setVisibleOnAllWorkspaces(false)
|
||||||
|
this.window.setFullScreenable(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private setupWindowManagement () {
|
private setupWindowManagement () {
|
||||||
this.window.on('show', () => {
|
this.window.on('show', () => {
|
||||||
this.visible.next(true)
|
this.visible.next(true)
|
||||||
@@ -312,7 +322,7 @@ export class Window {
|
|||||||
config: this.configStore,
|
config: this.configStore,
|
||||||
executable: app.getPath('exe'),
|
executable: app.getPath('exe'),
|
||||||
windowID: this.window.id,
|
windowID: this.window.id,
|
||||||
isFirstWindow: this.window.id === 1,
|
isMainWindow: this.isMainWindow,
|
||||||
userPluginsPath: this.application.userPluginsPath,
|
userPluginsPath: this.application.userPluginsPath,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -359,8 +369,7 @@ export class Window {
|
|||||||
if (this.window.isMinimized()) {
|
if (this.window.isMinimized()) {
|
||||||
this.window.restore()
|
this.window.restore()
|
||||||
}
|
}
|
||||||
this.window.show()
|
this.present()
|
||||||
this.window.moveTop()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('window-close', event => {
|
ipcMain.on('window-close', event => {
|
||||||
|
@@ -16,7 +16,7 @@ export interface PluginInfo {
|
|||||||
export interface BootstrapData {
|
export interface BootstrapData {
|
||||||
config: Record<string, any>
|
config: Record<string, any>
|
||||||
executable: string
|
executable: string
|
||||||
isFirstWindow: boolean
|
isMainWindow: boolean
|
||||||
windowID: number
|
windowID: number
|
||||||
installedPlugins: PluginInfo[]
|
installedPlugins: PluginInfo[]
|
||||||
userPluginsPath: string
|
userPluginsPath: string
|
||||||
|
@@ -89,7 +89,7 @@ export class AppService {
|
|||||||
}, 30000)
|
}, 30000)
|
||||||
|
|
||||||
config.ready$.toPromise().then(async () => {
|
config.ready$.toPromise().then(async () => {
|
||||||
if (this.bootstrapData.isFirstWindow) {
|
if (this.bootstrapData.isMainWindow) {
|
||||||
if (config.store.recoverTabs) {
|
if (config.store.recoverTabs) {
|
||||||
const tabs = await this.tabRecovery.recoverTabs()
|
const tabs = await this.tabRecovery.recoverTabs()
|
||||||
for (const tab of tabs) {
|
for (const tab of tabs) {
|
||||||
@@ -115,7 +115,7 @@ export class AppService {
|
|||||||
this.tabsChanged.next()
|
this.tabsChanged.next()
|
||||||
this.tabOpened.next(tab)
|
this.tabOpened.next(tab)
|
||||||
|
|
||||||
if (this.bootstrapData.isFirstWindow) {
|
if (this.bootstrapData.isMainWindow) {
|
||||||
tab.recoveryStateChangedHint$.subscribe(() => {
|
tab.recoveryStateChangedHint$.subscribe(() => {
|
||||||
this.tabRecovery.saveTabs(this.tabs)
|
this.tabRecovery.saveTabs(this.tabs)
|
||||||
})
|
})
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable, NgZone } from '@angular/core'
|
import { Injectable, NgZone, Inject } from '@angular/core'
|
||||||
import type { Display } from 'electron'
|
import type { Display } from 'electron'
|
||||||
import { ConfigService, DockingService, Screen, PlatformService } from 'tabby-core'
|
import { ConfigService, DockingService, Screen, PlatformService, BootstrapData, BOOTSTRAP_DATA } from 'tabby-core'
|
||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from '../services/electron.service'
|
||||||
import { ElectronHostWindow, Bounds } from './hostWindow.service'
|
import { ElectronHostWindow, Bounds } from './hostWindow.service'
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ export class ElectronDockingService extends DockingService {
|
|||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
private hostWindow: ElectronHostWindow,
|
private hostWindow: ElectronHostWindow,
|
||||||
platform: PlatformService,
|
platform: PlatformService,
|
||||||
|
@Inject(BOOTSTRAP_DATA) private bootstrapData: BootstrapData,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.screensChanged$.subscribe(() => this.repositionWindow())
|
this.screensChanged$.subscribe(() => this.repositionWindow())
|
||||||
@@ -25,7 +26,7 @@ export class ElectronDockingService extends DockingService {
|
|||||||
dock (): void {
|
dock (): void {
|
||||||
const dockSide = this.config.store.appearance.dock
|
const dockSide = this.config.store.appearance.dock
|
||||||
|
|
||||||
if (dockSide === 'off') {
|
if (dockSide === 'off' || !this.bootstrapData.isMainWindow) {
|
||||||
this.hostWindow.setAlwaysOnTop(false)
|
this.hostWindow.setAlwaysOnTop(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -43,6 +43,10 @@ export class ElectronHostWindow extends HostWindowService {
|
|||||||
electron.ipcRenderer.on('host:window-focused', () => zone.run(() => {
|
electron.ipcRenderer.on('host:window-focused', () => zone.run(() => {
|
||||||
this.windowFocused.next()
|
this.windowFocused.next()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
electron.ipcRenderer.on('host:became-main-window', () => zone.run(() => {
|
||||||
|
this.bootstrapData.isMainWindow = true
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
getWindow (): BrowserWindow {
|
getWindow (): BrowserWindow {
|
||||||
|
Reference in New Issue
Block a user