diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts
index aaf950b4c1f..4a6be70c633 100644
--- a/src/vs/base/browser/dom.ts
+++ b/src/vs/base/browser/dom.ts
@@ -749,9 +749,14 @@ export function isActiveDocument(element: Element): boolean {
/**
* Returns the active document across all child windows.
- * Use this instead of `document` when reacting to dom events to handle multiple windows.
+ * Use this instead of `document` when reacting to dom
+ * events to handle multiple windows.
*/
export function getActiveDocument(): Document {
+ if (getWindowsCount() <= 1) {
+ return document;
+ }
+
const documents = Array.from(getWindows()).map(window => window.document);
return documents.find(doc => doc.hasFocus()) ?? document;
}
@@ -797,9 +802,6 @@ export function createStyleSheet(container: HTMLElement = document.head, beforeA
// With
as container, the stylesheet becomes global and is tracked
// to support auxiliary windows to clone the stylesheet.
if (container === document.head) {
- const clonedGlobalStylesheets = new Set();
- globalStylesheets.set(style, clonedGlobalStylesheets);
-
for (const targetWindow of getWindows()) {
if (targetWindow === window) {
continue; // main window is already tracked
@@ -845,13 +847,18 @@ function cloneGlobalStyleSheet(globalStylesheet: HTMLStyleElement, targetWindow:
});
observer.observe(globalStylesheet, { childList: true });
- globalStylesheets.get(globalStylesheet)?.add(clone);
+ let clonedGlobalStylesheets = globalStylesheets.get(globalStylesheet);
+ if (!clonedGlobalStylesheets) {
+ clonedGlobalStylesheets = new Set();
+ globalStylesheets.set(globalStylesheet, clonedGlobalStylesheets);
+ }
+ clonedGlobalStylesheets.add(clone);
return toDisposable(() => {
observer.disconnect();
targetWindow.document.head.removeChild(clone);
- globalStylesheets.get(globalStylesheet)?.delete(clone);
+ clonedGlobalStylesheets?.delete(clone);
});
}
diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts
index 1554dfade7b..9eb840c0707 100644
--- a/src/vs/workbench/browser/layout.ts
+++ b/src/vs/workbench/browser/layout.ts
@@ -5,7 +5,7 @@
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
-import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, focusWindow, isActiveDocument } from 'vs/base/browser/dom';
+import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, focusWindow, isActiveDocument, getWindow } from 'vs/base/browser/dom';
import { onDidChangeFullscreen, isFullscreen, isWCOEnabled } from 'vs/base/browser/browser';
import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup';
import { isWindows, isLinux, isMacintosh, isWeb, isNative, isIOS } from 'vs/base/common/platform';
@@ -47,11 +47,12 @@ import { IBannerService } from 'vs/workbench/services/banner/browser/bannerServi
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
import { AuxiliaryBarPart } from 'vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
-import { IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
+import { IAuxiliaryWindowService, isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
//#region Layout Implementation
interface ILayoutRuntimeState {
+ activeContainer: 'main' | number /* window ID */;
fullscreen: boolean;
maximized: boolean;
hasFocus: boolean;
@@ -451,10 +452,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
private onWindowFocusChanged(hasFocus: boolean): void {
if (hasFocus) {
- // This is a bit simplified: we assume that the active container
- // has changed when receiving focus, but we might end up with
- // the same active container as before...
- this._onDidChangeActiveContainer.fire();
+ const activeContainerId = this.getActiveContainerId();
+ if (this.state.runtime.activeContainer !== activeContainerId) {
+ this.state.runtime.activeContainer = activeContainerId;
+ this._onDidChangeActiveContainer.fire();
+ }
}
if (this.state.runtime.hasFocus !== hasFocus) {
@@ -463,6 +465,18 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
}
+ private getActiveContainerId(): 'main' | number {
+ const activeContainer = this.activeContainer;
+ if (activeContainer !== this.container) {
+ const containerWindow = getWindow(activeContainer);
+ if (isAuxiliaryWindow(containerWindow)) {
+ return containerWindow.vscodeWindowId;
+ }
+ }
+
+ return 'main';
+ }
+
private doUpdateLayoutConfiguration(skipLayout?: boolean): void {
// Menubar visibility
@@ -601,6 +615,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
// Layout Runtime State
const layoutRuntimeState: ILayoutRuntimeState = {
+ activeContainer: this.getActiveContainerId(),
fullscreen: isFullscreen(),
hasFocus: this.hostService.hasFocus,
maximized: false,
diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts
index 30219f5b97d..963596b3eec 100644
--- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts
+++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts
@@ -680,12 +680,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Close all inactive editors first to prevent UI flicker
for (const inactiveEditor of inactiveEditors) {
- this.doCloseEditor(inactiveEditor, false);
+ this.doCloseEditor(inactiveEditor, true);
}
// Close active one last
if (activeEditor) {
- this.doCloseEditor(activeEditor, false);
+ this.doCloseEditor(activeEditor, true);
}
}
@@ -1108,8 +1108,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Without an editor pane, recover by closing the active editor
// (if the input is still the active one)
if (!pane && this.activeEditor === editor) {
- const focusNext = !options || !options.preserveFocus;
- this.doCloseEditor(editor, focusNext, { fromError: true });
+ this.doCloseEditor(editor, options?.preserveFocus, { fromError: true });
}
return pane;
@@ -1283,7 +1282,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// ...and a close afterwards (unless we copy)
if (!keepCopy) {
- this.doCloseEditor(editor, false /* do not focus next one behind if any */, { ...internalOptions, context: EditorCloseContext.MOVE });
+ this.doCloseEditor(editor, true /* do not focus next one behind if any */, { ...internalOptions, context: EditorCloseContext.MOVE });
}
}
@@ -1348,12 +1347,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Do close
- this.doCloseEditor(editor, options?.preserveFocus ? false : undefined, internalOptions);
+ this.doCloseEditor(editor, options?.preserveFocus, internalOptions);
return true;
}
- private doCloseEditor(editor: EditorInput, focusNext = (this.groupsView.activeGroup === this), internalOptions?: IInternalEditorCloseOptions): void {
+ private doCloseEditor(editor: EditorInput, preserveFocus = (this.groupsView.activeGroup !== this), internalOptions?: IInternalEditorCloseOptions): void {
// Forward to title control unless skipped via internal options
if (!internalOptions?.skipTitleUpdate) {
@@ -1362,7 +1361,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Closing the active editor of the group is a bit more work
if (this.model.isActive(editor)) {
- this.doCloseActiveEditor(focusNext, internalOptions);
+ this.doCloseActiveEditor(preserveFocus, internalOptions);
}
// Closing inactive editor is just a model update
@@ -1376,9 +1375,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
}
- private doCloseActiveEditor(focusNext = (this.groupsView.activeGroup === this), internalOptions?: IInternalEditorCloseOptions): void {
+ private doCloseActiveEditor(preserveFocus = (this.groupsView.activeGroup !== this), internalOptions?: IInternalEditorCloseOptions): void {
const editorToClose = this.activeEditor;
- const restoreFocus = this.shouldRestoreFocus(this.element);
+ const restoreFocus = !preserveFocus && this.shouldRestoreFocus(this.element);
// Optimization: if we are about to close the last editor in this group and settings
// are configured to close the group since it will be empty, we first set the last
@@ -1408,8 +1407,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Open next active if there are more to show
const nextActiveEditor = this.model.activeEditor;
if (nextActiveEditor) {
- const preserveFocus = !focusNext;
-
let activation: EditorActivation | undefined = undefined;
if (preserveFocus && this.groupsView.activeGroup !== this) {
// If we are opening the next editor in an inactive group
@@ -1723,7 +1720,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Close active editor last if contained in editors list to close
if (closeActiveEditor) {
- this.doCloseActiveEditor(options?.preserveFocus ? false : undefined);
+ this.doCloseActiveEditor(options?.preserveFocus);
}
// Forward to title control
@@ -1827,7 +1824,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
if (!editor.matches(replacement)) {
let closed = false;
if (forceReplaceDirty) {
- this.doCloseEditor(editor, false, { context: EditorCloseContext.REPLACE });
+ this.doCloseEditor(editor, true, { context: EditorCloseContext.REPLACE });
closed = true;
} else {
closed = await this.doCloseEditorWithConfirmationHandling(editor, { preserveFocus: true }, { context: EditorCloseContext.REPLACE });
@@ -1848,7 +1845,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Close replaced active editor unless they match
if (!activeReplacement.editor.matches(activeReplacement.replacement)) {
if (activeReplacement.forceReplaceDirty) {
- this.doCloseEditor(activeReplacement.editor, false, { context: EditorCloseContext.REPLACE });
+ this.doCloseEditor(activeReplacement.editor, true, { context: EditorCloseContext.REPLACE });
} else {
await this.doCloseEditorWithConfirmationHandling(activeReplacement.editor, { preserveFocus: true }, { context: EditorCloseContext.REPLACE });
}
diff --git a/src/vs/workbench/electron-sandbox/actions/windowActions.ts b/src/vs/workbench/electron-sandbox/actions/windowActions.ts
index 6535966e5b9..93027940589 100644
--- a/src/vs/workbench/electron-sandbox/actions/windowActions.ts
+++ b/src/vs/workbench/electron-sandbox/actions/windowActions.ts
@@ -29,7 +29,7 @@ import { isMacintosh } from 'vs/base/common/platform';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { getActiveWindow } from 'vs/base/browser/dom';
-import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService';
+import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
export class CloseWindowAction extends Action2 {
diff --git a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts
index 8c1a57c8aa8..f29a470f3a8 100644
--- a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts
+++ b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts
@@ -37,13 +37,21 @@ export interface IAuxiliaryWindow extends IDisposable {
readonly onDidLayout: Event;
readonly onDidClose: Event;
- readonly window: Window & typeof globalThis;
+ readonly window: AuxiliaryWindow;
readonly container: HTMLElement;
layout(): void;
}
-export type AuxiliaryWindow = Window & typeof globalThis;
+export type AuxiliaryWindow = Window & typeof globalThis & {
+ readonly vscodeWindowId: number;
+};
+
+export function isAuxiliaryWindow(obj: unknown): obj is AuxiliaryWindow {
+ const candidate = obj as AuxiliaryWindow | undefined;
+
+ return !!candidate && Object.hasOwn(candidate, 'vscodeWindowId');
+}
export class BrowserAuxiliaryWindowService extends Disposable implements IAuxiliaryWindowService {
@@ -51,6 +59,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
private static readonly DEFAULT_SIZE = { width: 800, height: 600 };
+ private static WINDOW_IDS = 0;
+
private readonly _onDidOpenAuxiliaryWindow = this._register(new Emitter());
readonly onDidOpenAuxiliaryWindow = this._onDidOpenAuxiliaryWindow.event;
@@ -72,7 +82,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
disposables.add(registerWindow(auxiliaryWindow));
disposables.add(toDisposable(() => auxiliaryWindow.close()));
- const { container, onDidLayout, onDidClose } = this.create(auxiliaryWindow, disposables);
+ const { container, onDidLayout, onDidClose } = await this.create(auxiliaryWindow, disposables);
const result: IAuxiliaryWindow = {
window: auxiliaryWindow,
@@ -118,11 +128,11 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
})).result;
}
- return auxiliaryWindow?.window;
+ return auxiliaryWindow?.window as AuxiliaryWindow | undefined;
}
- protected create(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore) {
- this.patchMethods(auxiliaryWindow);
+ protected async create(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore) {
+ await this.patchMethods(auxiliaryWindow);
this.applyMeta(auxiliaryWindow);
this.applyCSS(auxiliaryWindow, disposables);
@@ -263,7 +273,17 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
return { onDidLayout, onDidClose };
}
- protected patchMethods(auxiliaryWindow: AuxiliaryWindow): void {
+ protected async resolveWindowId(auxiliaryWindow: AuxiliaryWindow): Promise {
+ return BrowserAuxiliaryWindowService.WINDOW_IDS++;
+ }
+
+ protected async patchMethods(auxiliaryWindow: AuxiliaryWindow): Promise {
+
+ // Add a `vscodeWindowId` property to identify auxiliary windows
+ const resolvedWindowId = await this.resolveWindowId(auxiliaryWindow);
+ Object.defineProperty(auxiliaryWindow, 'vscodeWindowId', {
+ get: () => resolvedWindowId
+ });
// Disallow `createElement` because it would create
// HTML Elements in the "wrong" context and break
diff --git a/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts
index 717e29eabd9..e871ae52974 100644
--- a/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts
+++ b/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts
@@ -5,7 +5,7 @@
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
-import { BrowserAuxiliaryWindowService, IAuxiliaryWindowService, AuxiliaryWindow as BaseAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
+import { BrowserAuxiliaryWindowService, IAuxiliaryWindowService, AuxiliaryWindow as BrowserAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
import { ISandboxGlobals } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWindowsConfiguration } from 'vs/platform/window/common/window';
@@ -14,17 +14,10 @@ import { INativeHostService } from 'vs/platform/native/common/native';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { getActiveWindow } from 'vs/base/browser/dom';
-type AuxiliaryWindow = BaseAuxiliaryWindow & {
+type NativeAuxiliaryWindow = BrowserAuxiliaryWindow & {
readonly vscode: ISandboxGlobals;
- readonly vscodeWindowId: number;
};
-export function isAuxiliaryWindow(obj: unknown): obj is AuxiliaryWindow {
- const candidate = obj as AuxiliaryWindow | undefined;
-
- return !!candidate?.vscode && Object.hasOwn(candidate, 'vscodeWindowId');
-}
-
export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService {
constructor(
@@ -36,7 +29,7 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
super(layoutService, dialogService);
}
- protected override create(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore) {
+ protected override create(auxiliaryWindow: NativeAuxiliaryWindow, disposables: DisposableStore) {
// Zoom level
const windowConfig = this.configurationService.getValue();
@@ -46,19 +39,12 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
return super.create(auxiliaryWindow, disposables);
}
- protected override patchMethods(auxiliaryWindow: AuxiliaryWindow): void {
- super.patchMethods(auxiliaryWindow);
+ protected override resolveWindowId(auxiliaryWindow: NativeAuxiliaryWindow): Promise {
+ return auxiliaryWindow.vscode.ipcRenderer.invoke('vscode:getWindowId');
+ }
- // Obtain window identifier
- let resolvedWindowId: number;
- (async () => {
- resolvedWindowId = await auxiliaryWindow.vscode.ipcRenderer.invoke('vscode:getWindowId');
- })();
-
- // Add a `windowId` property
- Object.defineProperty(auxiliaryWindow, 'vscodeWindowId', {
- get: () => resolvedWindowId
- });
+ protected override async patchMethods(auxiliaryWindow: NativeAuxiliaryWindow): Promise {
+ await super.patchMethods(auxiliaryWindow);
// Enable `window.focus()` to work in Electron by
// asking the main process to focus the window.
@@ -69,7 +55,7 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
originalWindowFocus();
if (getActiveWindow() !== auxiliaryWindow) {
- that.nativeHostService.focusWindow({ targetWindowId: resolvedWindowId });
+ that.nativeHostService.focusWindow({ targetWindowId: auxiliaryWindow.vscodeWindowId });
}
};
}
diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts
index 8909c6bd0f5..430d0c6c649 100644
--- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts
+++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts
@@ -14,7 +14,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService';
-import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService';
+import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
import { getActiveDocument, getWindowsCount, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom';
import { DomEmitter } from 'vs/base/browser/event';
import { memoize } from 'vs/base/common/decorators';