diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index e13f5c40d0671..8ed01ca7556a1 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -37,6 +37,7 @@ const createYargs: (argv?: string[], cwd?: string) => Argv = require('yargs/yarg */ export interface TheiaBrowserWindowOptions extends BrowserWindowConstructorOptions { isMaximized?: boolean; + isFullScreen?: boolean; } /** @@ -214,8 +215,9 @@ export class ElectronMainApplication { * * @param options */ - async createWindow(asyncOptions: MaybePromise = this.getDefaultBrowserWindowOptions()): Promise { - const options = await asyncOptions; + async createWindow(asyncOptions: MaybePromise = this.getDefaultTheiaWindowOptions()): Promise { + let options = await asyncOptions; + options = this.avoidOverlap(options); const electronWindow = new BrowserWindow(options); this.attachReadyToShow(electronWindow); this.attachSaveWindowState(electronWindow); @@ -224,14 +226,32 @@ export class ElectronMainApplication { return electronWindow; } - protected async getDefaultBrowserWindowOptions(): Promise { - const windowOptionsFromConfig = this.config.electron.windowOptions; - let windowState: TheiaBrowserWindowOptions | undefined = this.electronStore.get('windowstate', undefined); - if (!windowState) { - windowState = this.getDefaultWindowState(); - } + async getLastWindowOptions(): Promise { + const windowState: TheiaBrowserWindowOptions | undefined = this.electronStore.get('windowstate') || this.getDefaultTheiaWindowOptions(); return { ...windowState, + ...this.getDefaultOptions() + }; + } + + protected avoidOverlap(options: TheiaBrowserWindowOptions): TheiaBrowserWindowOptions { + const existingWindowsBounds = BrowserWindow.getAllWindows().map(window => window.getBounds()); + if (existingWindowsBounds.length > 0) { + while (existingWindowsBounds.some(window => window.x === options.x || window.y === options.y)) { + // if the window is maximized or in fullscreen, use the default window options. + if (options.isMaximized || options.isFullScreen) { + options = this.getDefaultTheiaWindowOptions(); + } + options.x = options.x! + 30; + options.y = options.y! + 30; + + } + } + return options; + } + + protected getDefaultOptions(): TheiaBrowserWindowOptions { + return { show: false, title: this.config.applicationName, minWidth: 200, @@ -243,18 +263,20 @@ export class ElectronMainApplication { // Issue: https://github.com/eclipse-theia/theia/issues/8577 nodeIntegrationInWorker: false, }, - ...windowOptionsFromConfig, + ...this.config.electron?.windowOptions || {}, }; } protected async openDefaultWindow(): Promise { - const [uri, electronWindow] = await Promise.all([this.createWindowUri(), this.createWindow()]); + const options = await this.getLastWindowOptions(); + const [uri, electronWindow] = await Promise.all([this.createWindowUri(), this.createWindow(options)]); electronWindow.loadURL(uri.toString(true)); return electronWindow; } protected async openWindowWithWorkspace(workspacePath: string): Promise { - const [uri, electronWindow] = await Promise.all([this.createWindowUri(), this.createWindow()]); + const options = await this.getLastWindowOptions(); + const [uri, electronWindow] = await Promise.all([this.createWindowUri(), this.createWindow(options)]); electronWindow.loadURL(uri.withFragment(workspacePath).toString(true)); return electronWindow; } @@ -289,17 +311,25 @@ export class ElectronMainApplication { .withQuery(`port=${await this.backendPort}`); } - protected getDefaultWindowState(): BrowserWindowConstructorOptions { + protected getDefaultTheiaWindowOptions(): TheiaBrowserWindowOptions { // The `screen` API must be required when the application is ready. // See: https://electronjs.org/docs/api/screen#screen // We must center by hand because `browserWindow.center()` fails on multi-screen setups // See: https://github.com/electron/electron/issues/3490 const { bounds } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()); - const height = Math.floor(bounds.height * (2 / 3)); - const width = Math.floor(bounds.width * (2 / 3)); - const y = Math.floor(bounds.y + (bounds.height - height) / 2); - const x = Math.floor(bounds.x + (bounds.width - width) / 2); - return { width, height, x, y }; + const height = Math.round(bounds.height * (2 / 3)); + const width = Math.round(bounds.width * (2 / 3)); + const y = Math.round(bounds.y + (bounds.height - height) / 2); + const x = Math.round(bounds.x + (bounds.width - width) / 2); + return { + isFullScreen: false, + isMaximized: false, + width, + height, + x, + y, + ...this.getDefaultOptions() + }; } /** @@ -315,13 +345,9 @@ export class ElectronMainApplication { protected attachSaveWindowState(electronWindow: BrowserWindow): void { const saveWindowState = () => { try { - let bounds; - if (electronWindow.isMaximized()) { - bounds = this.electronStore.get('windowstate', {}); - } else { - bounds = electronWindow.getBounds(); - } + const bounds = electronWindow.getBounds(); this.electronStore.set('windowstate', { + isFullScreen: electronWindow.isFullScreen(), isMaximized: electronWindow.isMaximized(), width: bounds.width, height: bounds.height,