windows - move state handling into own class
This commit is contained in:
parent
75eeac0104
commit
f413b81fcd
|
@ -297,10 +297,10 @@ export class LaunchMainService implements ILaunchMainService {
|
||||||
return this.browserWindowToInfo(window.win, folderURIs, window.remoteAuthority);
|
return this.browserWindowToInfo(window.win, folderURIs, window.remoteAuthority);
|
||||||
}
|
}
|
||||||
|
|
||||||
private browserWindowToInfo(win: BrowserWindow, folderURIs: URI[] = [], remoteAuthority?: string): IWindowInfo {
|
private browserWindowToInfo(window: BrowserWindow, folderURIs: URI[] = [], remoteAuthority?: string): IWindowInfo {
|
||||||
return {
|
return {
|
||||||
pid: win.webContents.getOSProcessId(),
|
pid: window.webContents.getOSProcessId(),
|
||||||
title: win.getTitle(),
|
title: window.getTitle(),
|
||||||
folderURIs,
|
folderURIs,
|
||||||
remoteAuthority
|
remoteAuthority
|
||||||
};
|
};
|
||||||
|
|
|
@ -124,9 +124,11 @@ export interface IWindowsMainService {
|
||||||
|
|
||||||
readonly _serviceBrand: undefined;
|
readonly _serviceBrand: undefined;
|
||||||
|
|
||||||
|
readonly onWindowsCountChanged: Event<IWindowsCountChangedEvent>;
|
||||||
|
|
||||||
readonly onWindowOpened: Event<ICodeWindow>;
|
readonly onWindowOpened: Event<ICodeWindow>;
|
||||||
readonly onWindowReady: Event<ICodeWindow>;
|
readonly onWindowReady: Event<ICodeWindow>;
|
||||||
readonly onWindowsCountChanged: Event<IWindowsCountChangedEvent>;
|
readonly onWindowDestroyed: Event<ICodeWindow>;
|
||||||
|
|
||||||
open(openConfig: IOpenConfiguration): ICodeWindow[];
|
open(openConfig: IOpenConfiguration): ICodeWindow[];
|
||||||
openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[];
|
openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[];
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/e
|
||||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||||
import { IStateService } from 'vs/platform/state/node/state';
|
import { IStateService } from 'vs/platform/state/node/state';
|
||||||
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
|
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
|
||||||
import { screen, BrowserWindow, MessageBoxOptions, Display, app, WebContents } from 'electron';
|
import { screen, BrowserWindow, MessageBoxOptions, Display, WebContents } from 'electron';
|
||||||
import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
|
@ -30,7 +30,7 @@ import { Schemas } from 'vs/base/common/network';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { normalizePath, originalFSPath, removeTrailingPathSeparator, extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
import { normalizePath, originalFSPath, removeTrailingPathSeparator, extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
||||||
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||||
import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData, IWindowsState, IWindowState } from 'vs/platform/windows/electron-main/windowsStateStorage';
|
import { IWindowState, WindowsStateHandler } from 'vs/platform/windows/electron-main/windowsStateHandler';
|
||||||
import { getWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
|
import { getWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
|
||||||
import { once } from 'vs/base/common/functional';
|
import { once } from 'vs/base/common/functional';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
@ -145,24 +145,22 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
|
|
||||||
declare readonly _serviceBrand: undefined;
|
declare readonly _serviceBrand: undefined;
|
||||||
|
|
||||||
private static readonly windowsStateStorageKey = 'windowsState';
|
|
||||||
|
|
||||||
private static readonly WINDOWS: ICodeWindow[] = [];
|
private static readonly WINDOWS: ICodeWindow[] = [];
|
||||||
|
|
||||||
private readonly windowsState: IWindowsState;
|
|
||||||
private lastClosedWindowState?: IWindowState;
|
|
||||||
|
|
||||||
private shuttingDown = false;
|
|
||||||
|
|
||||||
private readonly _onWindowOpened = this._register(new Emitter<ICodeWindow>());
|
private readonly _onWindowOpened = this._register(new Emitter<ICodeWindow>());
|
||||||
readonly onWindowOpened = this._onWindowOpened.event;
|
readonly onWindowOpened = this._onWindowOpened.event;
|
||||||
|
|
||||||
private readonly _onWindowReady = this._register(new Emitter<ICodeWindow>());
|
private readonly _onWindowReady = this._register(new Emitter<ICodeWindow>());
|
||||||
readonly onWindowReady = this._onWindowReady.event;
|
readonly onWindowReady = this._onWindowReady.event;
|
||||||
|
|
||||||
|
private readonly _onWindowDestroyed = this._register(new Emitter<ICodeWindow>());
|
||||||
|
readonly onWindowDestroyed = this._onWindowDestroyed.event;
|
||||||
|
|
||||||
private readonly _onWindowsCountChanged = this._register(new Emitter<IWindowsCountChangedEvent>());
|
private readonly _onWindowsCountChanged = this._register(new Emitter<IWindowsCountChangedEvent>());
|
||||||
readonly onWindowsCountChanged = this._onWindowsCountChanged.event;
|
readonly onWindowsCountChanged = this._onWindowsCountChanged.event;
|
||||||
|
|
||||||
|
private readonly windowsStateHandler = this._register(new WindowsStateHandler(this.stateService, this.lifecycleMainService, this, this.logService));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly machineId: string,
|
private readonly machineId: string,
|
||||||
private readonly initialUserEnv: IProcessEnvironment,
|
private readonly initialUserEnv: IProcessEnvironment,
|
||||||
|
@ -179,166 +177,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.windowsState = restoreWindowsState(this.stateService.getItem<WindowsStateStorageData>(WindowsMainService.windowsStateStorageKey));
|
|
||||||
this.lifecycleMainService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners());
|
this.lifecycleMainService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners());
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerListeners(): void {
|
private registerListeners(): void {
|
||||||
|
|
||||||
// When a window looses focus, save all windows state. This allows to
|
|
||||||
// prevent loss of window-state data when OS is restarted without properly
|
|
||||||
// shutting down the application (https://github.com/microsoft/vscode/issues/87171)
|
|
||||||
app.on('browser-window-blur', () => {
|
|
||||||
if (!this.shuttingDown) {
|
|
||||||
this.saveWindowsState();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle various lifecycle events around windows
|
|
||||||
this.lifecycleMainService.onBeforeWindowClose(window => this.onBeforeWindowClose(window));
|
|
||||||
this.lifecycleMainService.onBeforeShutdown(() => this.onBeforeShutdown());
|
|
||||||
this.onWindowsCountChanged(e => {
|
|
||||||
if (e.newCount - e.oldCount > 0) {
|
|
||||||
// clear last closed window state when a new window opens. this helps on macOS where
|
|
||||||
// otherwise closing the last window, opening a new window and then quitting would
|
|
||||||
// use the state of the previously closed window when restarting.
|
|
||||||
this.lastClosedWindowState = undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Signal a window is ready after having entered a workspace
|
// Signal a window is ready after having entered a workspace
|
||||||
this._register(this.workspacesMainService.onWorkspaceEntered(event => {
|
this._register(this.workspacesMainService.onWorkspaceEntered(event => this._onWindowReady.fire(event.window)));
|
||||||
this._onWindowReady.fire(event.window);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS:
|
|
||||||
// - macOS: since the app will not quit when closing the last window, you will always first get
|
|
||||||
// the onBeforeShutdown() event followed by N onBeforeWindowClose() events for each window
|
|
||||||
// - other: on other OS, closing the last window will quit the app so the order depends on the
|
|
||||||
// user interaction: closing the last window will first trigger onBeforeWindowClose()
|
|
||||||
// and then onBeforeShutdown(). Using the quit action however will first issue onBeforeShutdown()
|
|
||||||
// and then onBeforeWindowClose().
|
|
||||||
//
|
|
||||||
// Here is the behavior on different OS depending on action taken (Electron 1.7.x):
|
|
||||||
//
|
|
||||||
// Legend
|
|
||||||
// - quit(N): quit application with N windows opened
|
|
||||||
// - close(1): close one window via the window close button
|
|
||||||
// - closeAll: close all windows via the taskbar command
|
|
||||||
// - onBeforeShutdown(N): number of windows reported in this event handler
|
|
||||||
// - onBeforeWindowClose(N, M): number of windows reported and quitRequested boolean in this event handler
|
|
||||||
//
|
|
||||||
// macOS
|
|
||||||
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
|
||||||
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
|
||||||
// - quit(0): onBeforeShutdown(0)
|
|
||||||
// - close(1): onBeforeWindowClose(1, false)
|
|
||||||
//
|
|
||||||
// Windows
|
|
||||||
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
|
||||||
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
|
||||||
// - close(1): onBeforeWindowClose(2, false)[not last window]
|
|
||||||
// - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window]
|
|
||||||
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0)
|
|
||||||
//
|
|
||||||
// Linux
|
|
||||||
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
|
||||||
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
|
||||||
// - close(1): onBeforeWindowClose(2, false)[not last window]
|
|
||||||
// - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window]
|
|
||||||
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0)
|
|
||||||
//
|
|
||||||
private onBeforeShutdown(): void {
|
|
||||||
this.shuttingDown = true;
|
|
||||||
|
|
||||||
this.saveWindowsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private saveWindowsState(): void {
|
|
||||||
const currentWindowsState: IWindowsState = {
|
|
||||||
openedWindows: [],
|
|
||||||
lastPluginDevelopmentHostWindow: this.windowsState.lastPluginDevelopmentHostWindow,
|
|
||||||
lastActiveWindow: this.lastClosedWindowState
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1.) Find a last active window (pick any other first window otherwise)
|
|
||||||
if (!currentWindowsState.lastActiveWindow) {
|
|
||||||
let activeWindow = this.getLastActiveWindow();
|
|
||||||
if (!activeWindow || activeWindow.isExtensionDevelopmentHost) {
|
|
||||||
activeWindow = this.getWindows().find(window => !window.isExtensionDevelopmentHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeWindow) {
|
|
||||||
currentWindowsState.lastActiveWindow = this.toWindowState(activeWindow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.) Find extension host window
|
|
||||||
const extensionHostWindow = this.getWindows().find(window => window.isExtensionDevelopmentHost && !window.isExtensionTestHost);
|
|
||||||
if (extensionHostWindow) {
|
|
||||||
currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.) All windows (except extension host) for N >= 2 to support `restoreWindows: all` or for auto update
|
|
||||||
//
|
|
||||||
// Careful here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0)
|
|
||||||
// so if we ever want to persist the UI state of the last closed window (window count === 1), it has
|
|
||||||
// to come from the stored lastClosedWindowState on Win/Linux at least
|
|
||||||
if (this.getWindowCount() > 1) {
|
|
||||||
currentWindowsState.openedWindows = this.getWindows().filter(window => !window.isExtensionDevelopmentHost).map(window => this.toWindowState(window));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Persist
|
|
||||||
const state = getWindowsStateStoreData(currentWindowsState);
|
|
||||||
this.stateService.setItem(WindowsMainService.windowsStateStorageKey, state);
|
|
||||||
|
|
||||||
if (this.shuttingDown) {
|
|
||||||
this.logService.trace('onBeforeShutdown', state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See note on #onBeforeShutdown() for details how these events are flowing
|
|
||||||
private onBeforeWindowClose(win: ICodeWindow): void {
|
|
||||||
if (this.lifecycleMainService.quitRequested) {
|
|
||||||
return; // during quit, many windows close in parallel so let it be handled in the before-quit handler
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Window close, update our stored UI state of this window
|
|
||||||
const state: IWindowState = this.toWindowState(win);
|
|
||||||
if (win.isExtensionDevelopmentHost && !win.isExtensionTestHost) {
|
|
||||||
this.windowsState.lastPluginDevelopmentHostWindow = state; // do not let test run window state overwrite our extension development state
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any non extension host window with same workspace or folder
|
|
||||||
else if (!win.isExtensionDevelopmentHost && (!!win.openedWorkspace || !!win.openedFolderUri)) {
|
|
||||||
this.windowsState.openedWindows.forEach(o => {
|
|
||||||
const sameWorkspace = win.openedWorkspace && o.workspace && o.workspace.id === win.openedWorkspace.id;
|
|
||||||
const sameFolder = win.openedFolderUri && o.folderUri && extUriBiasedIgnorePathCase.isEqual(o.folderUri, win.openedFolderUri);
|
|
||||||
|
|
||||||
if (sameWorkspace || sameFolder) {
|
|
||||||
o.uiState = state.uiState;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Windows and Linux closing the last window will trigger quit. Since we are storing all UI state
|
|
||||||
// before quitting, we need to remember the UI state of this window to be able to persist it.
|
|
||||||
// On macOS we keep the last closed window state ready in case the user wants to quit right after or
|
|
||||||
// wants to open another window, in which case we use this state over the persisted one.
|
|
||||||
if (this.getWindowCount() === 1) {
|
|
||||||
this.lastClosedWindowState = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private toWindowState(win: ICodeWindow): IWindowState {
|
|
||||||
return {
|
|
||||||
workspace: win.openedWorkspace,
|
|
||||||
folderUri: win.openedFolderUri,
|
|
||||||
backupPath: win.backupPath,
|
|
||||||
remoteAuthority: win.remoteAuthority,
|
|
||||||
uiState: win.serializeWindowState()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[] {
|
openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[] {
|
||||||
|
@ -434,13 +279,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
|
|
||||||
// Otherwise, find a good window based on open params
|
// Otherwise, find a good window based on open params
|
||||||
else {
|
else {
|
||||||
const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length);
|
const focusLastActive = this.windowsStateHandler.state.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length);
|
||||||
let focusLastOpened = true;
|
let focusLastOpened = true;
|
||||||
let focusLastWindow = true;
|
let focusLastWindow = true;
|
||||||
|
|
||||||
// 2.) focus last active window if we are not instructed to open any paths
|
// 2.) focus last active window if we are not instructed to open any paths
|
||||||
if (focusLastActive) {
|
if (focusLastActive) {
|
||||||
const lastActiveWindow = usedWindows.filter(window => this.windowsState.lastActiveWindow && window.backupPath === this.windowsState.lastActiveWindow.backupPath);
|
const lastActiveWindow = usedWindows.filter(window => this.windowsStateHandler.state.lastActiveWindow && window.backupPath === this.windowsStateHandler.state.lastActiveWindow.backupPath);
|
||||||
if (lastActiveWindow.length) {
|
if (lastActiveWindow.length) {
|
||||||
lastActiveWindow[0].focus();
|
lastActiveWindow[0].focus();
|
||||||
focusLastOpened = false;
|
focusLastOpened = false;
|
||||||
|
@ -987,10 +832,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
// Collect previously opened windows
|
// Collect previously opened windows
|
||||||
const openedWindows: IWindowState[] = [];
|
const openedWindows: IWindowState[] = [];
|
||||||
if (restoreWindowsSetting !== 'one') {
|
if (restoreWindowsSetting !== 'one') {
|
||||||
openedWindows.push(...this.windowsState.openedWindows);
|
openedWindows.push(...this.windowsStateHandler.state.openedWindows);
|
||||||
}
|
}
|
||||||
if (this.windowsState.lastActiveWindow) {
|
if (this.windowsStateHandler.state.lastActiveWindow) {
|
||||||
openedWindows.push(this.windowsState.lastActiveWindow);
|
openedWindows.push(this.windowsStateHandler.state.lastActiveWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowsToOpen: IPathToOpen[] = [];
|
const windowsToOpen: IPathToOpen[] = [];
|
||||||
|
@ -1287,7 +1132,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
|
|
||||||
// Fill in previously opened workspace unless an explicit path is provided and we are not unit testing
|
// Fill in previously opened workspace unless an explicit path is provided and we are not unit testing
|
||||||
if (!cliArgs.length && !folderUris.length && !fileUris.length && !openConfig.cli.extensionTestsPath) {
|
if (!cliArgs.length && !folderUris.length && !fileUris.length && !openConfig.cli.extensionTestsPath) {
|
||||||
const extensionDevelopmentWindowState = this.windowsState.lastPluginDevelopmentHostWindow;
|
const extensionDevelopmentWindowState = this.windowsStateHandler.state.lastPluginDevelopmentHostWindow;
|
||||||
const workspaceToOpen = extensionDevelopmentWindowState && (extensionDevelopmentWindowState.workspace || extensionDevelopmentWindowState.folderUri);
|
const workspaceToOpen = extensionDevelopmentWindowState && (extensionDevelopmentWindowState.workspace || extensionDevelopmentWindowState.folderUri);
|
||||||
if (workspaceToOpen) {
|
if (workspaceToOpen) {
|
||||||
if (isSingleFolderWorkspaceIdentifier(workspaceToOpen)) {
|
if (isSingleFolderWorkspaceIdentifier(workspaceToOpen)) {
|
||||||
|
@ -1471,7 +1316,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
// Window Events
|
// Window Events
|
||||||
once(createdWindow.onReady)(() => this._onWindowReady.fire(createdWindow));
|
once(createdWindow.onReady)(() => this._onWindowReady.fire(createdWindow));
|
||||||
once(createdWindow.onClose)(() => this.onWindowClosed(createdWindow));
|
once(createdWindow.onClose)(() => this.onWindowClosed(createdWindow));
|
||||||
once(createdWindow.onDestroy)(() => this.onBeforeWindowClose(createdWindow)); // try to save state before destroy because close will not fire
|
once(createdWindow.onDestroy)(() => this._onWindowDestroyed.fire(createdWindow));
|
||||||
createdWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own
|
createdWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own
|
||||||
createdWindow.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow));
|
createdWindow.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow));
|
||||||
|
|
||||||
|
@ -1536,14 +1381,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
if (!configuration.extensionTestsPath) {
|
if (!configuration.extensionTestsPath) {
|
||||||
|
|
||||||
// extension development host Window - load from stored settings if any
|
// extension development host Window - load from stored settings if any
|
||||||
if (!!configuration.extensionDevelopmentPath && this.windowsState.lastPluginDevelopmentHostWindow) {
|
if (!!configuration.extensionDevelopmentPath && this.windowsStateHandler.state.lastPluginDevelopmentHostWindow) {
|
||||||
return this.windowsState.lastPluginDevelopmentHostWindow.uiState;
|
return this.windowsStateHandler.state.lastPluginDevelopmentHostWindow.uiState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Known Workspace - load from stored settings
|
// Known Workspace - load from stored settings
|
||||||
const workspace = configuration.workspace;
|
const workspace = configuration.workspace;
|
||||||
if (workspace) {
|
if (workspace) {
|
||||||
const stateForWorkspace = this.windowsState.openedWindows.filter(o => o.workspace && o.workspace.id === workspace.id).map(o => o.uiState);
|
const stateForWorkspace = this.windowsStateHandler.state.openedWindows.filter(o => o.workspace && o.workspace.id === workspace.id).map(o => o.uiState);
|
||||||
if (stateForWorkspace.length) {
|
if (stateForWorkspace.length) {
|
||||||
return stateForWorkspace[0];
|
return stateForWorkspace[0];
|
||||||
}
|
}
|
||||||
|
@ -1551,7 +1396,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
|
|
||||||
// Known Folder - load from stored settings
|
// Known Folder - load from stored settings
|
||||||
if (configuration.folderUri) {
|
if (configuration.folderUri) {
|
||||||
const stateForFolder = this.windowsState.openedWindows.filter(o => o.folderUri && extUriBiasedIgnorePathCase.isEqual(o.folderUri, configuration.folderUri)).map(o => o.uiState);
|
const stateForFolder = this.windowsStateHandler.state.openedWindows.filter(o => o.folderUri && extUriBiasedIgnorePathCase.isEqual(o.folderUri, configuration.folderUri)).map(o => o.uiState);
|
||||||
if (stateForFolder.length) {
|
if (stateForFolder.length) {
|
||||||
return stateForFolder[0];
|
return stateForFolder[0];
|
||||||
}
|
}
|
||||||
|
@ -1559,14 +1404,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
|
|
||||||
// Empty windows with backups
|
// Empty windows with backups
|
||||||
else if (configuration.backupPath) {
|
else if (configuration.backupPath) {
|
||||||
const stateForEmptyWindow = this.windowsState.openedWindows.filter(o => o.backupPath === configuration.backupPath).map(o => o.uiState);
|
const stateForEmptyWindow = this.windowsStateHandler.state.openedWindows.filter(o => o.backupPath === configuration.backupPath).map(o => o.uiState);
|
||||||
if (stateForEmptyWindow.length) {
|
if (stateForEmptyWindow.length) {
|
||||||
return stateForEmptyWindow[0];
|
return stateForEmptyWindow[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// First Window
|
// First Window
|
||||||
const lastActiveState = this.lastClosedWindowState || this.windowsState.lastActiveWindow;
|
const lastActiveState = this.windowsStateHandler.lastClosedState || this.windowsStateHandler.state.lastActiveWindow;
|
||||||
if (!lastActive && lastActiveState) {
|
if (!lastActive && lastActiveState) {
|
||||||
return lastActiveState.uiState;
|
return lastActiveState.uiState;
|
||||||
}
|
}
|
||||||
|
@ -1660,10 +1505,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onWindowClosed(win: ICodeWindow): void {
|
private onWindowClosed(window: ICodeWindow): void {
|
||||||
|
|
||||||
// Remove from our list so that Electron can clean it up
|
// Remove from our list so that Electron can clean it up
|
||||||
const index = WindowsMainService.WINDOWS.indexOf(win);
|
const index = WindowsMainService.WINDOWS.indexOf(window);
|
||||||
WindowsMainService.WINDOWS.splice(index, 1);
|
WindowsMainService.WINDOWS.splice(index, 1);
|
||||||
|
|
||||||
// Emit
|
// Emit
|
||||||
|
|
295
src/vs/platform/windows/electron-main/windowsStateHandler.ts
Normal file
295
src/vs/platform/windows/electron-main/windowsStateHandler.ts
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { app } from 'electron';
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
||||||
|
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||||
|
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||||
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
|
import { IStateService } from 'vs/platform/state/node/state';
|
||||||
|
import { ICodeWindow, IWindowsMainService, IWindowState as IWindowUIState } from 'vs/platform/windows/electron-main/windows';
|
||||||
|
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||||
|
|
||||||
|
export type WindowsStateStorageData = object;
|
||||||
|
|
||||||
|
export interface IWindowState {
|
||||||
|
workspace?: IWorkspaceIdentifier;
|
||||||
|
folderUri?: URI;
|
||||||
|
backupPath?: string;
|
||||||
|
remoteAuthority?: string;
|
||||||
|
uiState: IWindowUIState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWindowsState {
|
||||||
|
lastActiveWindow?: IWindowState;
|
||||||
|
lastPluginDevelopmentHostWindow?: IWindowState;
|
||||||
|
openedWindows: IWindowState[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISerializedWindowsState {
|
||||||
|
readonly lastActiveWindow?: ISerializedWindowState;
|
||||||
|
readonly lastPluginDevelopmentHostWindow?: ISerializedWindowState;
|
||||||
|
readonly openedWindows: ISerializedWindowState[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ISerializedWindowState {
|
||||||
|
readonly workspaceIdentifier?: { id: string; configURIPath: string };
|
||||||
|
readonly folder?: string;
|
||||||
|
readonly backupPath?: string;
|
||||||
|
readonly remoteAuthority?: string;
|
||||||
|
readonly uiState: IWindowUIState;
|
||||||
|
|
||||||
|
// deprecated
|
||||||
|
readonly folderUri?: UriComponents;
|
||||||
|
readonly folderPath?: string;
|
||||||
|
readonly workspace?: { id: string; configPath: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WindowsStateHandler extends Disposable {
|
||||||
|
|
||||||
|
private static readonly windowsStateStorageKey = 'windowsState';
|
||||||
|
|
||||||
|
get state() { return this._windowsState; }
|
||||||
|
private readonly _windowsState: IWindowsState;
|
||||||
|
|
||||||
|
get lastClosedState() { return this._lastClosedState; }
|
||||||
|
private _lastClosedState?: IWindowState;
|
||||||
|
|
||||||
|
private shuttingDown = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@IStateService private readonly stateService: IStateService,
|
||||||
|
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
|
||||||
|
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
|
||||||
|
@ILogService private readonly logService: ILogService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._windowsState = restoreWindowsState(this.stateService.getItem<WindowsStateStorageData>(WindowsStateHandler.windowsStateStorageKey));
|
||||||
|
|
||||||
|
this.registerListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerListeners(): void {
|
||||||
|
|
||||||
|
// When a window looses focus, save all windows state. This allows to
|
||||||
|
// prevent loss of window-state data when OS is restarted without properly
|
||||||
|
// shutting down the application (https://github.com/microsoft/vscode/issues/87171)
|
||||||
|
app.on('browser-window-blur', () => {
|
||||||
|
if (!this.shuttingDown) {
|
||||||
|
this.saveWindowsState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle various lifecycle events around windows
|
||||||
|
this.lifecycleMainService.onBeforeWindowClose(window => this.onBeforeWindowClose(window));
|
||||||
|
this.lifecycleMainService.onBeforeShutdown(() => this.onBeforeShutdown());
|
||||||
|
this.windowsMainService.onWindowsCountChanged(e => {
|
||||||
|
if (e.newCount - e.oldCount > 0) {
|
||||||
|
// clear last closed window state when a new window opens. this helps on macOS where
|
||||||
|
// otherwise closing the last window, opening a new window and then quitting would
|
||||||
|
// use the state of the previously closed window when restarting.
|
||||||
|
this._lastClosedState = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// try to save state before destroy because close will not fire
|
||||||
|
this.windowsMainService.onWindowDestroyed(window => this.onBeforeWindowClose(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS:
|
||||||
|
// - macOS: since the app will not quit when closing the last window, you will always first get
|
||||||
|
// the onBeforeShutdown() event followed by N onBeforeWindowClose() events for each window
|
||||||
|
// - other: on other OS, closing the last window will quit the app so the order depends on the
|
||||||
|
// user interaction: closing the last window will first trigger onBeforeWindowClose()
|
||||||
|
// and then onBeforeShutdown(). Using the quit action however will first issue onBeforeShutdown()
|
||||||
|
// and then onBeforeWindowClose().
|
||||||
|
//
|
||||||
|
// Here is the behavior on different OS depending on action taken (Electron 1.7.x):
|
||||||
|
//
|
||||||
|
// Legend
|
||||||
|
// - quit(N): quit application with N windows opened
|
||||||
|
// - close(1): close one window via the window close button
|
||||||
|
// - closeAll: close all windows via the taskbar command
|
||||||
|
// - onBeforeShutdown(N): number of windows reported in this event handler
|
||||||
|
// - onBeforeWindowClose(N, M): number of windows reported and quitRequested boolean in this event handler
|
||||||
|
//
|
||||||
|
// macOS
|
||||||
|
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
||||||
|
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
||||||
|
// - quit(0): onBeforeShutdown(0)
|
||||||
|
// - close(1): onBeforeWindowClose(1, false)
|
||||||
|
//
|
||||||
|
// Windows
|
||||||
|
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
||||||
|
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
||||||
|
// - close(1): onBeforeWindowClose(2, false)[not last window]
|
||||||
|
// - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window]
|
||||||
|
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0)
|
||||||
|
//
|
||||||
|
// Linux
|
||||||
|
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
||||||
|
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
||||||
|
// - close(1): onBeforeWindowClose(2, false)[not last window]
|
||||||
|
// - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window]
|
||||||
|
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0)
|
||||||
|
//
|
||||||
|
private onBeforeShutdown(): void {
|
||||||
|
this.shuttingDown = true;
|
||||||
|
|
||||||
|
this.saveWindowsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private saveWindowsState(): void {
|
||||||
|
const currentWindowsState: IWindowsState = {
|
||||||
|
openedWindows: [],
|
||||||
|
lastPluginDevelopmentHostWindow: this._windowsState.lastPluginDevelopmentHostWindow,
|
||||||
|
lastActiveWindow: this._lastClosedState
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1.) Find a last active window (pick any other first window otherwise)
|
||||||
|
if (!currentWindowsState.lastActiveWindow) {
|
||||||
|
let activeWindow = this.windowsMainService.getLastActiveWindow();
|
||||||
|
if (!activeWindow || activeWindow.isExtensionDevelopmentHost) {
|
||||||
|
activeWindow = this.windowsMainService.getWindows().find(window => !window.isExtensionDevelopmentHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeWindow) {
|
||||||
|
currentWindowsState.lastActiveWindow = this.toWindowState(activeWindow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.) Find extension host window
|
||||||
|
const extensionHostWindow = this.windowsMainService.getWindows().find(window => window.isExtensionDevelopmentHost && !window.isExtensionTestHost);
|
||||||
|
if (extensionHostWindow) {
|
||||||
|
currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.) All windows (except extension host) for N >= 2 to support `restoreWindows: all` or for auto update
|
||||||
|
//
|
||||||
|
// Careful here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0)
|
||||||
|
// so if we ever want to persist the UI state of the last closed window (window count === 1), it has
|
||||||
|
// to come from the stored lastClosedWindowState on Win/Linux at least
|
||||||
|
if (this.windowsMainService.getWindowCount() > 1) {
|
||||||
|
currentWindowsState.openedWindows = this.windowsMainService.getWindows().filter(window => !window.isExtensionDevelopmentHost).map(window => this.toWindowState(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persist
|
||||||
|
const state = getWindowsStateStoreData(currentWindowsState);
|
||||||
|
this.stateService.setItem(WindowsStateHandler.windowsStateStorageKey, state);
|
||||||
|
|
||||||
|
if (this.shuttingDown) {
|
||||||
|
this.logService.trace('[WindowsStateHandler] onBeforeShutdown', state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See note on #onBeforeShutdown() for details how these events are flowing
|
||||||
|
private onBeforeWindowClose(window: ICodeWindow): void {
|
||||||
|
if (this.lifecycleMainService.quitRequested) {
|
||||||
|
return; // during quit, many windows close in parallel so let it be handled in the before-quit handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Window close, update our stored UI state of this window
|
||||||
|
const state: IWindowState = this.toWindowState(window);
|
||||||
|
if (window.isExtensionDevelopmentHost && !window.isExtensionTestHost) {
|
||||||
|
this._windowsState.lastPluginDevelopmentHostWindow = state; // do not let test run window state overwrite our extension development state
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any non extension host window with same workspace or folder
|
||||||
|
else if (!window.isExtensionDevelopmentHost && (!!window.openedWorkspace || !!window.openedFolderUri)) {
|
||||||
|
this._windowsState.openedWindows.forEach(openedWindow => {
|
||||||
|
const sameWorkspace = window.openedWorkspace && openedWindow.workspace && openedWindow.workspace.id === window.openedWorkspace.id;
|
||||||
|
const sameFolder = window.openedFolderUri && openedWindow.folderUri && extUriBiasedIgnorePathCase.isEqual(openedWindow.folderUri, window.openedFolderUri);
|
||||||
|
|
||||||
|
if (sameWorkspace || sameFolder) {
|
||||||
|
openedWindow.uiState = state.uiState;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Windows and Linux closing the last window will trigger quit. Since we are storing all UI state
|
||||||
|
// before quitting, we need to remember the UI state of this window to be able to persist it.
|
||||||
|
// On macOS we keep the last closed window state ready in case the user wants to quit right after or
|
||||||
|
// wants to open another window, in which case we use this state over the persisted one.
|
||||||
|
if (this.windowsMainService.getWindowCount() === 1) {
|
||||||
|
this._lastClosedState = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private toWindowState(window: ICodeWindow): IWindowState {
|
||||||
|
return {
|
||||||
|
workspace: window.openedWorkspace,
|
||||||
|
folderUri: window.openedFolderUri,
|
||||||
|
backupPath: window.backupPath,
|
||||||
|
remoteAuthority: window.remoteAuthority,
|
||||||
|
uiState: window.serializeWindowState()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreWindowsState(data: WindowsStateStorageData | undefined): IWindowsState {
|
||||||
|
const result: IWindowsState = { openedWindows: [] };
|
||||||
|
const windowsState = data as ISerializedWindowsState || { openedWindows: [] };
|
||||||
|
|
||||||
|
if (windowsState.lastActiveWindow) {
|
||||||
|
result.lastActiveWindow = restoreWindowState(windowsState.lastActiveWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowsState.lastPluginDevelopmentHostWindow) {
|
||||||
|
result.lastPluginDevelopmentHostWindow = restoreWindowState(windowsState.lastPluginDevelopmentHostWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(windowsState.openedWindows)) {
|
||||||
|
result.openedWindows = windowsState.openedWindows.map(windowState => restoreWindowState(windowState));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreWindowState(windowState: ISerializedWindowState): IWindowState {
|
||||||
|
const result: IWindowState = { uiState: windowState.uiState };
|
||||||
|
if (windowState.backupPath) {
|
||||||
|
result.backupPath = windowState.backupPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowState.remoteAuthority) {
|
||||||
|
result.remoteAuthority = windowState.remoteAuthority;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowState.folder) {
|
||||||
|
result.folderUri = URI.parse(windowState.folder);
|
||||||
|
} else if (windowState.folderUri) {
|
||||||
|
result.folderUri = URI.revive(windowState.folderUri);
|
||||||
|
} else if (windowState.folderPath) {
|
||||||
|
result.folderUri = URI.file(windowState.folderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowState.workspaceIdentifier) {
|
||||||
|
result.workspace = { id: windowState.workspaceIdentifier.id, configPath: URI.parse(windowState.workspaceIdentifier.configURIPath) };
|
||||||
|
} else if (windowState.workspace) {
|
||||||
|
result.workspace = { id: windowState.workspace.id, configPath: URI.file(windowState.workspace.configPath) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWindowsStateStoreData(windowsState: IWindowsState): WindowsStateStorageData {
|
||||||
|
return {
|
||||||
|
lastActiveWindow: windowsState.lastActiveWindow && serializeWindowState(windowsState.lastActiveWindow),
|
||||||
|
lastPluginDevelopmentHostWindow: windowsState.lastPluginDevelopmentHostWindow && serializeWindowState(windowsState.lastPluginDevelopmentHostWindow),
|
||||||
|
openedWindows: windowsState.openedWindows.map(ws => serializeWindowState(ws))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function serializeWindowState(windowState: IWindowState): ISerializedWindowState {
|
||||||
|
return {
|
||||||
|
workspaceIdentifier: windowState.workspace && { id: windowState.workspace.id, configURIPath: windowState.workspace.configPath.toString() },
|
||||||
|
folder: windowState.folderUri && windowState.folderUri.toString(),
|
||||||
|
backupPath: windowState.backupPath,
|
||||||
|
remoteAuthority: windowState.remoteAuthority,
|
||||||
|
uiState: windowState.uiState
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,107 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
|
||||||
import { IWindowState as IWindowUIState } from 'vs/platform/windows/electron-main/windows';
|
|
||||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
|
||||||
|
|
||||||
export type WindowsStateStorageData = object;
|
|
||||||
|
|
||||||
export interface IWindowState {
|
|
||||||
workspace?: IWorkspaceIdentifier;
|
|
||||||
folderUri?: URI;
|
|
||||||
backupPath?: string;
|
|
||||||
remoteAuthority?: string;
|
|
||||||
uiState: IWindowUIState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWindowsState {
|
|
||||||
lastActiveWindow?: IWindowState;
|
|
||||||
lastPluginDevelopmentHostWindow?: IWindowState;
|
|
||||||
openedWindows: IWindowState[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISerializedWindowsState {
|
|
||||||
readonly lastActiveWindow?: ISerializedWindowState;
|
|
||||||
readonly lastPluginDevelopmentHostWindow?: ISerializedWindowState;
|
|
||||||
readonly openedWindows: ISerializedWindowState[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISerializedWindowState {
|
|
||||||
readonly workspaceIdentifier?: { id: string; configURIPath: string };
|
|
||||||
readonly folder?: string;
|
|
||||||
readonly backupPath?: string;
|
|
||||||
readonly remoteAuthority?: string;
|
|
||||||
readonly uiState: IWindowUIState;
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
readonly folderUri?: UriComponents;
|
|
||||||
readonly folderPath?: string;
|
|
||||||
readonly workspace?: { id: string; configPath: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function restoreWindowsState(data: WindowsStateStorageData | undefined): IWindowsState {
|
|
||||||
const result: IWindowsState = { openedWindows: [] };
|
|
||||||
const windowsState = data as ISerializedWindowsState || { openedWindows: [] };
|
|
||||||
|
|
||||||
if (windowsState.lastActiveWindow) {
|
|
||||||
result.lastActiveWindow = restoreWindowState(windowsState.lastActiveWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowsState.lastPluginDevelopmentHostWindow) {
|
|
||||||
result.lastPluginDevelopmentHostWindow = restoreWindowState(windowsState.lastPluginDevelopmentHostWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(windowsState.openedWindows)) {
|
|
||||||
result.openedWindows = windowsState.openedWindows.map(windowState => restoreWindowState(windowState));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function restoreWindowState(windowState: ISerializedWindowState): IWindowState {
|
|
||||||
const result: IWindowState = { uiState: windowState.uiState };
|
|
||||||
if (windowState.backupPath) {
|
|
||||||
result.backupPath = windowState.backupPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowState.remoteAuthority) {
|
|
||||||
result.remoteAuthority = windowState.remoteAuthority;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowState.folder) {
|
|
||||||
result.folderUri = URI.parse(windowState.folder);
|
|
||||||
} else if (windowState.folderUri) {
|
|
||||||
result.folderUri = URI.revive(windowState.folderUri);
|
|
||||||
} else if (windowState.folderPath) {
|
|
||||||
result.folderUri = URI.file(windowState.folderPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowState.workspaceIdentifier) {
|
|
||||||
result.workspace = { id: windowState.workspaceIdentifier.id, configPath: URI.parse(windowState.workspaceIdentifier.configURIPath) };
|
|
||||||
} else if (windowState.workspace) {
|
|
||||||
result.workspace = { id: windowState.workspace.id, configPath: URI.file(windowState.workspace.configPath) };
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getWindowsStateStoreData(windowsState: IWindowsState): WindowsStateStorageData {
|
|
||||||
return {
|
|
||||||
lastActiveWindow: windowsState.lastActiveWindow && serializeWindowState(windowsState.lastActiveWindow),
|
|
||||||
lastPluginDevelopmentHostWindow: windowsState.lastPluginDevelopmentHostWindow && serializeWindowState(windowsState.lastPluginDevelopmentHostWindow),
|
|
||||||
openedWindows: windowsState.openedWindows.map(ws => serializeWindowState(ws))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function serializeWindowState(windowState: IWindowState): ISerializedWindowState {
|
|
||||||
return {
|
|
||||||
workspaceIdentifier: windowState.workspace && { id: windowState.workspace.id, configURIPath: windowState.workspace.configPath.toString() },
|
|
||||||
folder: windowState.folderUri && windowState.folderUri.toString(),
|
|
||||||
backupPath: windowState.backupPath,
|
|
||||||
remoteAuthority: windowState.remoteAuthority,
|
|
||||||
uiState: windowState.uiState
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'vs/base/common/path';
|
import * as path from 'vs/base/common/path';
|
||||||
import { restoreWindowsState, getWindowsStateStoreData, IWindowsState, IWindowState } from 'vs/platform/windows/electron-main/windowsStateStorage';
|
import { restoreWindowsState, getWindowsStateStoreData, IWindowsState, IWindowState } from 'vs/platform/windows/electron-main/windowsStateHandler';
|
||||||
import { IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
import { IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
||||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
Loading…
Reference in a new issue