diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 2cd9d04f791..420715ff1fe 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -336,7 +336,18 @@ export class CodeWindow extends Disposable implements ICodeWindow { return !!this.documentEdited; } - focus(): void { + focus(options?: { force: boolean }): void { + // macOS: Electron >6.x changed its behaviour to not + // bring the application to the foreground when a window + // is focused programmatically. Only via `app.focus` and + // the option `steal: true` can you get the previous + // behaviour back. The only reason to use this option is + // when a window is getting focused while the application + // is not in the foreground. + if (isMacintosh && options?.force) { + app.focus({ steal: true }); + } + if (!this._win) { return; } @@ -719,7 +730,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.showTimeoutHandle = setTimeout(() => { if (this._win && !this._win.isVisible() && !this._win.isMinimized()) { this._win.show(); - this._win.focus(); + this.focus({ force: true }); this._win.webContents.openDevTools(); } }, 10000); diff --git a/src/vs/platform/electron/common/electron.ts b/src/vs/platform/electron/common/electron.ts index 759bcd96ab3..1061d20d16f 100644 --- a/src/vs/platform/electron/common/electron.ts +++ b/src/vs/platform/electron/common/electron.ts @@ -44,7 +44,15 @@ export interface ICommonElectronService { unmaximizeWindow(): Promise; minimizeWindow(): Promise; - focusWindow(options?: { windowId?: number }): Promise; + /** + * Make the window focused. + * + * @param options Pass `force: true` if you want to make the window take + * focus even if the application does not have focus currently. This option + * should only be used if it is necessary to steal focus from the current + * focused application which may not be VSCode. + */ + focusWindow(options?: { windowId?: number, force?: boolean }): Promise; // Dialogs showMessageBox(options: MessageBoxOptions): Promise; diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index 0c2f9a735ca..91693f0be75 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -172,14 +172,14 @@ export class ElectronMainService implements IElectronMainService { } } - async focusWindow(windowId: number | undefined, options?: { windowId?: number; }): Promise { + async focusWindow(windowId: number | undefined, options?: { windowId?: number; force?: boolean; }): Promise { if (options && typeof options.windowId === 'number') { windowId = options.windowId; } const window = this.windowById(windowId); if (window) { - window.focus(); + window.focus({ force: options?.force ?? false }); } } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index bf481595ade..e73561154ab 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -61,7 +61,7 @@ export interface ICodeWindow extends IDisposable { load(config: INativeWindowConfiguration, isReload?: boolean): void; reload(configuration?: INativeWindowConfiguration, cli?: ParsedArgs): void; - focus(): void; + focus(options?: { force: boolean }): void; close(): void; getBounds(): Rectangle; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 1aec9526b10..9181bf457f4 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -167,9 +167,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private readonly _onWindowReady = this._register(new Emitter()); readonly onWindowReady = this._onWindowReady.event; - private readonly _onWindowClose = this._register(new Emitter()); - readonly onWindowClose = this._onWindowClose.event; - private readonly _onWindowsCountChanged = this._register(new Emitter()); readonly onWindowsCountChanged = this._onWindowsCountChanged.event; @@ -1626,18 +1623,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return state; } - focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow { - const lastActive = this.getLastActiveWindow(); - if (lastActive) { - lastActive.focus(); - - return lastActive; - } - - // No window - open new empty one - return this.open({ context, cli, forceEmpty: true })[0]; - } - getLastActiveWindow(): ICodeWindow | undefined { return getLastActiveWindow(WindowsMainService.WINDOWS); } @@ -1695,6 +1680,5 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Emit this._onWindowsCountChanged.fire({ oldCount: WindowsMainService.WINDOWS.length + 1, newCount: WindowsMainService.WINDOWS.length }); - this._onWindowClose.fire(win.id); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index f62c408d63f..0202085db6d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -813,7 +813,7 @@ export class DebugSession implements IDebugSession { } if (this.configurationService.getValue('debug').focusWindowOnBreak) { - this.hostService.focus(); + this.hostService.focus({ force: true /* Application may not be active */ }); } } } diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index 590778fa56e..78bb77dcccb 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -32,8 +32,14 @@ export interface IHostService { /** * Attempt to bring the window to the foreground and focus it. + * + * @param options Pass `force: true` if you want to make the window take + * focus even if the application does not have focus currently. This option + * should only be used if it is necessary to steal focus from the current + * focused application which may not be VSCode. It may not be supported + * in all environments. */ - focus(): Promise; + focus(options?: { force: boolean }): Promise; //#endregion diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index 86aa6287b4a..3b633ceafd1 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -82,8 +82,8 @@ export class NativeHostService extends Disposable implements IHostService { return this.electronService.toggleFullScreen(); } - focus(): Promise { - return this.electronService.focusWindow(); + focus(options?: { force: boolean }): Promise { + return this.electronService.focusWindow(options); } restart(): Promise { diff --git a/src/vs/workbench/services/url/electron-sandbox/urlService.ts b/src/vs/workbench/services/url/electron-sandbox/urlService.ts index cdb0cce5e88..1cf519c5cc4 100644 --- a/src/vs/workbench/services/url/electron-sandbox/urlService.ts +++ b/src/vs/workbench/services/url/electron-sandbox/urlService.ts @@ -66,7 +66,7 @@ export class RelayURLService extends NativeURLService implements IURLHandler, IO const result = await super.open(uri, options); if (result) { - await this.electronService.focusWindow(); + await this.electronService.focusWindow({ force: true /* Application may not be active */ }); } return result; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 7966cf330a9..ed24e6a26cc 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1032,7 +1032,7 @@ export class TestHostService implements IHostService { async restart(): Promise { } async reload(): Promise { } - async focus(): Promise { } + async focus(options?: { force: boolean }): Promise { } async openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise { }