Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement : Avoid electron windows to overlap #9560

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 49 additions & 23 deletions packages/core/src/electron-main/electron-main-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const createYargs: (argv?: string[], cwd?: string) => Argv = require('yargs/yarg
*/
export interface TheiaBrowserWindowOptions extends BrowserWindowConstructorOptions {
isMaximized?: boolean;
isFullScreen?: boolean;
}

/**
Expand Down Expand Up @@ -214,8 +215,9 @@ export class ElectronMainApplication {
*
* @param options
*/
async createWindow(asyncOptions: MaybePromise<TheiaBrowserWindowOptions> = this.getDefaultBrowserWindowOptions()): Promise<BrowserWindow> {
const options = await asyncOptions;
async createWindow(asyncOptions: MaybePromise<TheiaBrowserWindowOptions> = this.getDefaultTheiaWindowOptions()): Promise<BrowserWindow> {
let options = await asyncOptions;
options = this.avoidOverlap(options);
const electronWindow = new BrowserWindow(options);
this.attachReadyToShow(electronWindow);
this.attachSaveWindowState(electronWindow);
Expand All @@ -224,14 +226,32 @@ export class ElectronMainApplication {
return electronWindow;
}

protected async getDefaultBrowserWindowOptions(): Promise<TheiaBrowserWindowOptions> {
const windowOptionsFromConfig = this.config.electron.windowOptions;
let windowState: TheiaBrowserWindowOptions | undefined = this.electronStore.get('windowstate', undefined);
if (!windowState) {
windowState = this.getDefaultWindowState();
}
async getLastWindowOptions(): Promise<TheiaBrowserWindowOptions> {
const windowState: TheiaBrowserWindowOptions | undefined = this.electronStore.get('windowstate') || this.getDefaultTheiaWindowOptions();
return {
...windowState,
...this.getDefaultOptions()
colin-grant-work marked this conversation as resolved.
Show resolved Hide resolved
};
}

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,
Expand All @@ -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<BrowserWindow> {
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<BrowserWindow> {
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;
}
Expand Down Expand Up @@ -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()
};
}

/**
Expand All @@ -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,
Expand Down