diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 9d614c541178e..9d667250f77f5 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -104,7 +104,7 @@ export interface IWindowSettings { openFilesInNewWindow: 'on' | 'off' | 'default'; openFoldersInNewWindow: 'on' | 'off' | 'default'; openWithoutArgumentsInNewWindow: 'on' | 'off'; - restoreWindows: 'all' | 'folders' | 'one' | 'none'; + restoreWindows: 'preserve' | 'all' | 'folders' | 'one' | 'none'; restoreFullscreen: boolean; zoomLevel: number; titleBarStyle: 'native' | 'custom'; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 6d622945d68ae..858fdb00ec16c 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -59,7 +59,7 @@ interface INewWindowState extends ISingleWindowState { hasDefaultState?: boolean; } -type RestoreWindowsSetting = 'all' | 'folders' | 'one' | 'none'; +type RestoreWindowsSetting = 'preserve' | 'all' | 'folders' | 'one' | 'none'; interface IOpenBrowserWindowOptions { userEnv?: IProcessEnvironment; @@ -307,7 +307,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow); } - // 3.) All windows (except extension host) for N >= 2 to support restoreWindows: all or for auto update + // 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 @@ -812,6 +812,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private getPathsToOpen(openConfig: IOpenConfiguration): IPathToOpen[] { let windowsToOpen: IPathToOpen[]; let isCommandLineOrAPICall = false; + let restoredWindows = false; // Extract paths: from API if (openConfig.urisToOpen && openConfig.urisToOpen.length > 0) { @@ -833,6 +834,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Extract windows: from previous session else { windowsToOpen = this.doGetWindowsFromLastSession(); + restoredWindows = true; } // Convert multiple folders into workspace (if opened via API or CLI) @@ -853,6 +855,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } } + // Check for `window.startup` setting to include all windows + // from the previous session if this is the initial startup and we have + // not restored windows already otherwise. + // Use `unshift` to ensure any new window to open comes last + // for proper focus treatment. + if (openConfig.initialStartup && !restoredWindows && this.configurationService.getValue('window').restoreWindows === 'preserve') { + windowsToOpen.unshift(...this.doGetWindowsFromLastSession().filter(window => window.workspace || window.folderUri || window.backupPath)); + } + return windowsToOpen; } @@ -959,6 +970,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // folders: restore last opened folders only case 'one': case 'all': + case 'preserve': case 'folders': // Collect previously opened windows @@ -1014,7 +1026,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic const windowConfig = this.configurationService.getValue('window'); restoreWindows = windowConfig?.restoreWindows || 'all'; // by default restore all windows - if (!['all', 'folders', 'one', 'none'].includes(restoreWindows)) { + if (!['preserve', 'all', 'folders', 'one', 'none'].includes(restoreWindows)) { restoreWindows = 'all'; // by default restore all windows } } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 8f79ac279d340..61ee068866017 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -23,7 +23,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { LifecyclePhase, StartupKind, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, IPath } from 'vs/platform/windows/common/windows'; +import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, IPath, IWindowSettings } from 'vs/platform/windows/common/windows'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IEditor } from 'vs/editor/common/editorCommon'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -576,7 +576,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const initialFilesToOpen = this.getInitialFilesToOpen(); // Only restore editors if we are not instructed to open files initially - this.state.editor.restoreEditors = initialFilesToOpen === undefined; + // or when `window.restoreWindows` setting is explicitly set to `preserve` + const forceRestoreEditors = this.configurationService.getValue('window').restoreWindows === 'preserve'; + this.state.editor.restoreEditors = !!forceRestoreEditors || initialFilesToOpen === undefined; // Files to open, diff or create if (initialFilesToOpen !== undefined) { diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 12b109dcfc2d8..43d45e49904e1 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -211,16 +211,17 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; }, 'window.restoreWindows': { 'type': 'string', - 'enum': ['all', 'folders', 'one', 'none'], + 'enum': ['preserve', 'all', 'folders', 'one', 'none'], 'enumDescriptions': [ - nls.localize('window.reopenFolders.all', "Reopen all windows."), - nls.localize('window.reopenFolders.folders', "Reopen all folders. Empty workspaces will not be restored."), - nls.localize('window.reopenFolders.one', "Reopen the last active window."), - nls.localize('window.reopenFolders.none', "Never reopen a window. Always start with an empty one.") + nls.localize('window.reopenFolders.preserve', "Reopen all windows. If a folder or workspace is opened (e.g. from the command line) it opens as new window. Files will open in one of the restored windows."), + nls.localize('window.reopenFolders.all', "Reopen all windows unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.folders', "Reopen all windows that had folders or workspaces opened unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.one', "Reopen the last active window unless a folder, workspace or file is opened (e.g. from the command line)."), + nls.localize('window.reopenFolders.none', "Never reopen a window. Unless a folder or workspace is opened (e.g. from the command line), an empty window will appear.") ], 'default': 'all', 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart.") + 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after starting for the first time. This setting has no effect when the application is already running.") }, 'window.restoreFullscreen': { 'type': 'boolean',