From df072f2e286227deab4130bbb0e656e06180e2cc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Sep 2022 17:55:07 +0200 Subject: [PATCH 1/7] web - allow to specify a full editor layout as embedder --- .../code/browser/workbench/workbench-dev.html | 1 + src/vs/workbench/browser/layout.ts | 150 +++++++++++++----- src/vs/workbench/browser/web.api.ts | 55 ++++++- src/vs/workbench/common/editor.ts | 15 +- src/vs/workbench/electron-sandbox/window.ts | 2 +- .../editor/common/editorGroupsService.ts | 20 +++ .../host/browser/browserHostService.ts | 7 +- src/vs/workbench/workbench.web.main.ts | 6 +- 8 files changed, 192 insertions(+), 64 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index 7fdc35e0862e6..be6d30bfb98cb 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -28,6 +28,7 @@ + diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index ec50fb9eff15e..06f1330ca8040 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -9,7 +9,7 @@ import { EventType, addDisposableListener, getClientArea, Dimension, position, s import { onDidChangeFullscreen, isFullscreen } 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'; -import { EditorInputCapabilities, isResourceEditorInput, IUntypedEditorInput, pathsToEditors } from 'vs/workbench/common/editor'; +import { EditorInputCapabilities, GroupIdentifier, isResourceEditorInput, IUntypedEditorInput, pathsToEditors } from 'vs/workbench/common/editor'; import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment } from 'vs/workbench/services/layout/browser/layoutService'; @@ -24,7 +24,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IEditor } from 'vs/editor/common/editorCommon'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { EditorGroupLayout, GroupsOrder, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { SerializableGrid, ISerializableView, ISerializedGrid, Orientation, ISerializedNode, ISerializedLeafNode, Direction, IViewSize, Sizing } from 'vs/base/browser/ui/grid/grid'; import { Part } from 'vs/workbench/browser/part'; import { IStatusbarService } from 'vs/workbench/services/statusbar/browser/statusbar'; @@ -63,6 +63,11 @@ interface IWorkbenchLayoutWindowRuntimeState { }; } +interface IEditorToOpen { + editor: IUntypedEditorInput; + viewColumn?: number; +} + interface IWorkbenchLayoutWindowInitializationState { views: { defaults: string[] | undefined; @@ -74,7 +79,10 @@ interface IWorkbenchLayoutWindowInitializationState { }; editor: { restoreEditors: boolean; - editorsToOpen: Promise; + editorsToOpen: Promise; + }; + layout?: { + editors?: EditorGroupLayout; }; } @@ -94,10 +102,15 @@ enum WorkbenchLayoutClasses { WINDOW_BORDER = 'border' } -interface IInitialFilesToOpen { - filesToOpenOrCreate?: IPath[]; - filesToDiff?: IPath[]; - filesToMerge?: IPath[]; +interface IPathToOpen extends IPath { + viewColumn?: number; +} + +interface IInitialEditorsState { + filesToOpenOrCreate?: IPathToOpen[]; + filesToDiff?: IPathToOpen[]; + filesToMerge?: IPathToOpen[]; + editorLayout?: EditorGroupLayout; } export abstract class Layout extends Disposable implements IWorkbenchLayoutService { @@ -460,11 +473,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi }); // Window Initialization State - const initialFilesToOpen = this.getInitialFilesToOpen(); + const initialEditorsState = this.getInitialEditorsState(); const windowInitializationState: IWorkbenchLayoutWindowInitializationState = { + layout: { + editors: initialEditorsState?.editorLayout + }, editor: { - restoreEditors: this.shouldRestoreEditors(this.contextService, initialFilesToOpen), - editorsToOpen: this.resolveEditorsToOpen(fileService, initialFilesToOpen) + restoreEditors: this.shouldRestoreEditors(this.contextService, initialEditorsState), + editorsToOpen: this.resolveEditorsToOpen(fileService, initialEditorsState), }, views: { defaults: this.getDefaultLayoutViews(this.environmentService, this.storageService), @@ -553,7 +569,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return undefined; } - private shouldRestoreEditors(contextService: IWorkspaceContextService, initialFilesToOpen: IInitialFilesToOpen | undefined): boolean { + private shouldRestoreEditors(contextService: IWorkspaceContextService, initialEditorsState: IInitialEditorsState | undefined): boolean { // Restore editors based on a set of rules: // - never when running on temporary workspace @@ -565,40 +581,56 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } const forceRestoreEditors = this.configurationService.getValue('window.restoreWindows') === 'preserve'; - return !!forceRestoreEditors || initialFilesToOpen === undefined; + return !!forceRestoreEditors || initialEditorsState === undefined; } protected willRestoreEditors(): boolean { return this.windowState.initialization.editor.restoreEditors; } - private async resolveEditorsToOpen(fileService: IFileService, initialFilesToOpen: IInitialFilesToOpen | undefined): Promise { - if (initialFilesToOpen) { + private async resolveEditorsToOpen(fileService: IFileService, initialEditorsState: IInitialEditorsState | undefined): Promise { + if (initialEditorsState) { - // Merge editor - const filesToMerge = await pathsToEditors(initialFilesToOpen.filesToMerge, fileService); + // Merge editor (single) + const filesToMerge = coalesce(await pathsToEditors(initialEditorsState.filesToMerge, fileService)); if (filesToMerge.length === 4 && isResourceEditorInput(filesToMerge[0]) && isResourceEditorInput(filesToMerge[1]) && isResourceEditorInput(filesToMerge[2]) && isResourceEditorInput(filesToMerge[3])) { return [{ - input1: { resource: filesToMerge[0].resource }, - input2: { resource: filesToMerge[1].resource }, - base: { resource: filesToMerge[2].resource }, - result: { resource: filesToMerge[3].resource }, - options: { pinned: true } + editor: { + input1: { resource: filesToMerge[0].resource }, + input2: { resource: filesToMerge[1].resource }, + base: { resource: filesToMerge[2].resource }, + result: { resource: filesToMerge[3].resource }, + options: { pinned: true } + } }]; } - // Diff editor - const filesToDiff = await pathsToEditors(initialFilesToOpen.filesToDiff, fileService); + // Diff editor (single) + const filesToDiff = coalesce(await pathsToEditors(initialEditorsState.filesToDiff, fileService)); if (filesToDiff.length === 2) { return [{ - original: { resource: filesToDiff[0].resource }, - modified: { resource: filesToDiff[1].resource }, - options: { pinned: true } + editor: { + original: { resource: filesToDiff[0].resource }, + modified: { resource: filesToDiff[1].resource }, + options: { pinned: true } + } }]; } - // Normal editor - return pathsToEditors(initialFilesToOpen.filesToOpenOrCreate, fileService); + // Normal editor (multiple) + const filesToOpenOrCreate: IEditorToOpen[] = []; + const resolvedFilesToOpenOrCreate = await pathsToEditors(initialEditorsState.filesToOpenOrCreate, fileService); + for (let i = 0; i < resolvedFilesToOpenOrCreate.length; i++) { + const resolvedFileToOpenOrCreate = resolvedFilesToOpenOrCreate[i]; + if (resolvedFileToOpenOrCreate) { + filesToOpenOrCreate.push({ + editor: resolvedFileToOpenOrCreate, + viewColumn: initialEditorsState.filesToOpenOrCreate?.[i].viewColumn + }); + } + } + + return filesToOpenOrCreate; } // Empty workbench configured to open untitled file if empty @@ -612,7 +644,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return []; // do not open any empty untitled file if we have backups to restore } - return [{ resource: undefined }]; // open empty untitled file + return [{ + editor: { resource: undefined } // open empty untitled file + }]; } return []; @@ -621,30 +655,30 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private _openedDefaultEditors: boolean = false; get openedDefaultEditors() { return this._openedDefaultEditors; } - private getInitialFilesToOpen(): IInitialFilesToOpen | undefined { + private getInitialEditorsState(): IInitialEditorsState | undefined { - // Check for editors from `defaultLayout` options first + // Check for editors / editor layout from `defaultLayout` options first const defaultLayout = this.environmentService.options?.defaultLayout; - if (defaultLayout?.editors?.length && (defaultLayout.force || this.storageService.isNew(StorageScope.WORKSPACE))) { + if ((defaultLayout?.editors?.length || defaultLayout?.layout?.editors) && (defaultLayout.force || this.storageService.isNew(StorageScope.WORKSPACE))) { this._openedDefaultEditors = true; return { - filesToOpenOrCreate: defaultLayout.editors.map(file => { - const legacyOverride = file.openWith; - const legacySelection = file.selection && file.selection.start && isNumber(file.selection.start.line) ? { - startLineNumber: file.selection.start.line, - startColumn: isNumber(file.selection.start.column) ? file.selection.start.column : 1, - endLineNumber: isNumber(file.selection.end.line) ? file.selection.end.line : undefined, - endColumn: isNumber(file.selection.end.line) ? (isNumber(file.selection.end.column) ? file.selection.end.column : 1) : undefined, + editorLayout: defaultLayout.layout?.editors, + filesToOpenOrCreate: defaultLayout?.editors?.map(editor => { + const legacySelection = editor.selection && editor.selection.start && isNumber(editor.selection.start.line) ? { + startLineNumber: editor.selection.start.line, + startColumn: isNumber(editor.selection.start.column) ? editor.selection.start.column : 1, + endLineNumber: isNumber(editor.selection.end.line) ? editor.selection.end.line : undefined, + endColumn: isNumber(editor.selection.end.line) ? (isNumber(editor.selection.end.column) ? editor.selection.end.column : 1) : undefined, } : undefined; return { - fileUri: URI.revive(file.uri), - openOnlyIfExists: file.openOnlyIfExists, + viewColumn: editor.viewColumn, + fileUri: URI.revive(editor.uri), + openOnlyIfExists: editor.openOnlyIfExists, options: { selection: legacySelection, - override: legacyOverride, - ...file.options // keep at the end to override legacy selection/override that may be `undefined` + ...editor.options // keep at the end to override legacy selection/override that may be `undefined` } }; }) @@ -686,6 +720,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // first ensure the editor part is ready await this.editorGroupService.whenReady; + // apply editor layout if any + if (this.windowState.initialization.layout?.editors) { + this.editorGroupService.applyLayout(this.windowState.initialization.layout.editors); + } + // then see for editors to open as instructed // it is important that we trigger this from // the overall restore flow to reduce possible @@ -694,11 +733,34 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // signaling that layout is restored, but we do // not need to await the editors from having // fully loaded. + const editors = await this.windowState.initialization.editor.editorsToOpen; let openEditorsPromise: Promise | undefined = undefined; if (editors.length) { - openEditorsPromise = this.editorService.openEditors(editors, undefined, { validateTrust: true }); + + // we have to map editors to their groups as instructed + // by the input. this is important to ensure that we open + // the editors in the groups they belong to. + + const editorGroupsInVisualOrder = this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE); + const mapEditorsToGroup = new Map>(); + + for (const editor of editors) { + const group = editorGroupsInVisualOrder[(editor.viewColumn ?? 1) - 1]; // viewColumn is index+1 based + + let editorsByGroup = mapEditorsToGroup.get(group.id); + if (!editorsByGroup) { + editorsByGroup = new Set(); + mapEditorsToGroup.set(group.id, editorsByGroup); + } + + editorsByGroup.add(editor.editor); + } + + openEditorsPromise = Promise.all(Array.from(mapEditorsToGroup).map(async ([groupId, editors]) => { + return this.editorService.openEditors(Array.from(editors), groupId, { validateTrust: true }); + })); } // do not block the overall layout ready flow from potentially diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index bb99447347d66..b273c87976e48 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -18,6 +18,7 @@ import type { IProgress, IProgressCompositeOptions, IProgressDialogOptions, IPro import { IObservableValue } from 'vs/base/common/observableValue'; import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; /** * The `IWorkbench` interface is the API facade for web embedders @@ -253,7 +254,9 @@ export interface IWorkbenchConstructionOptions { readonly commands?: readonly ICommand[]; /** - * Optional default layout to apply on first time the workspace is opened (unless `force` is specified). + * Optional default layout to apply on first time the workspace is opened + * (unless `force` is specified). This includes visibility of views and + * editors including editor grid layout. */ readonly defaultLayout?: IDefaultLayout; @@ -587,6 +590,10 @@ export interface IInitialColorTheme { } export interface IDefaultView { + + /** + * The identifier of the view to show by default. + */ readonly id: string; } @@ -608,27 +615,63 @@ export interface IRange { export interface IDefaultEditor { + /** + * The location of the editor in the editor grid layout. + * Editors are layed out in editor groups and the view + * column is counted from top left to bottom right in + * the order of appearance beginning with `1`. + * + * If not provided, the editor will open in the active + * group. + */ + readonly viewColumn?: number; + + /** + * The resource of the editor to open. + */ readonly uri: UriComponents; + + /** + * Optional extra options like which editor + * to use or which text to select. + */ readonly options?: IEditorOptions; + /** + * Will not open an untitled editor in case + * the resource does not exist. + */ readonly openOnlyIfExists?: boolean; /** * @deprecated use `options` instead */ readonly selection?: IRange; - - /** - * @deprecated use `options.override` instead - */ - readonly openWith?: string; } export interface IDefaultLayout { + /** + * A list of views to show by default. + */ readonly views?: IDefaultView[]; + + /** + * A list of editors to show by default. + */ readonly editors?: IDefaultEditor[]; + /** + * The layout to use for the workbench. + */ + readonly layout?: { + + /** + * The layout of the editor area. + */ + readonly editors?: EditorGroupLayout; + }; + /** * Forces this layout to be applied even if this isn't * the first time the workspace has been opened diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 21352f7fa9c7d..68e90f0865ab0 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -19,7 +19,6 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { ICompositeControl, IComposite } from 'vs/workbench/common/composite'; import { FileType, IFileService } from 'vs/platform/files/common/files'; import { IPathData } from 'vs/platform/window/common/window'; -import { coalesce } from 'vs/base/common/arrays'; import { IExtUri } from 'vs/base/common/resources'; import { Schemas } from 'vs/base/common/network'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -1382,20 +1381,20 @@ class EditorFactoryRegistry implements IEditorFactoryRegistry { Registry.add(EditorExtensions.EditorFactory, new EditorFactoryRegistry()); -export async function pathsToEditors(paths: IPathData[] | undefined, fileService: IFileService): Promise<(IResourceEditorInput | IUntitledTextResourceEditorInput)[]> { +export async function pathsToEditors(paths: IPathData[] | undefined, fileService: IFileService): Promise> { if (!paths || !paths.length) { return []; } - const editors = await Promise.all(paths.map(async path => { + return await Promise.all(paths.map(async path => { const resource = URI.revive(path.fileUri); if (!resource) { - return; + return undefined; } const canHandleResource = await fileService.canHandleResource(resource); if (!canHandleResource) { - return; + return undefined; } let exists = path.exists; @@ -1410,11 +1409,11 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService } if (!exists && path.openOnlyIfExists) { - return; + return undefined; } if (type === FileType.Directory) { - return; + return undefined; } const options: IEditorOptions = { @@ -1431,8 +1430,6 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService return input; })); - - return coalesce(editors); } export const enum EditorsOrder { diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 3a558cc6b011e..bc3814d31d26c 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -860,7 +860,7 @@ export class NativeWindow extends Disposable { const diffMode = !!(request.filesToDiff && (request.filesToDiff.length === 2)); const mergeMode = !!(request.filesToMerge && (request.filesToMerge.length === 4)); - const inputs = await pathsToEditors(mergeMode ? request.filesToMerge : diffMode ? request.filesToDiff : request.filesToOpenOrCreate, this.fileService); + const inputs = coalesce(await pathsToEditors(mergeMode ? request.filesToMerge : diffMode ? request.filesToDiff : request.filesToOpenOrCreate, this.fileService)); if (inputs.length) { const openedEditorPanes = await this.openResources(inputs, diffMode, mergeMode); diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 2e19d0d0cbfbc..5ceeaa56b7e20 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -62,12 +62,32 @@ export const enum GroupsArrangement { } export interface GroupLayoutArgument { + + /** + * Only applies when there are multiple groups + * arranged next to each other in a row or column. + * If provided, their sum must be 1 to be applied + * per row or column. + */ size?: number; + + /** + * Editor groups will be laid out orthogonal to the + * parent orientation. + */ groups?: GroupLayoutArgument[]; } export interface EditorGroupLayout { + + /** + * The initial orientation of the editor groups at the root. + */ orientation: GroupOrientation; + + /** + * The editor groups at the root of the layout. + */ groups: GroupLayoutArgument[]; } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index efba94ca42afb..63ddc7626025b 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -36,6 +36,7 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Schemas } from 'vs/base/common/network'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { coalesce } from 'vs/base/common/arrays'; /** * A workspace to open in the workbench can either be: @@ -258,7 +259,7 @@ export class BrowserHostService extends Disposable implements IHostService { // Support mergeMode if (options?.mergeMode && fileOpenables.length === 4) { - const editors = await pathsToEditors(fileOpenables, this.fileService); + const editors = coalesce(await pathsToEditors(fileOpenables, this.fileService)); if (editors.length !== 4 || !isResourceEditorInput(editors[0]) || !isResourceEditorInput(editors[1]) || !isResourceEditorInput(editors[2]) || !isResourceEditorInput(editors[3])) { return; // invalid resources } @@ -288,7 +289,7 @@ export class BrowserHostService extends Disposable implements IHostService { // Support diffMode if (options?.diffMode && fileOpenables.length === 2) { - const editors = await pathsToEditors(fileOpenables, this.fileService); + const editors = coalesce(await pathsToEditors(fileOpenables, this.fileService)); if (editors.length !== 2 || !isResourceEditorInput(editors[0]) || !isResourceEditorInput(editors[1])) { return; // invalid resources } @@ -333,7 +334,7 @@ export class BrowserHostService extends Disposable implements IHostService { openables = [openable]; } - editorService.openEditors(await pathsToEditors(openables, this.fileService), undefined, { validateTrust: true }); + editorService.openEditors(coalesce(await pathsToEditors(openables, this.fileService)), undefined, { validateTrust: true }); } // New Window: open into empty window diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index cef9e9d6638dc..e4b85b54623ba 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -191,6 +191,7 @@ import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/url import type { IUpdateProvider, IUpdate } from 'vs/workbench/services/update/browser/updateService'; // eslint-disable-next-line no-duplicate-imports import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; +import { EditorGroupLayout, GroupLayoutArgument, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService'; export { @@ -273,9 +274,12 @@ export { IInitialColorTheme, // Default layout + IDefaultLayout, IDefaultView, IDefaultEditor, - IDefaultLayout, + EditorGroupLayout, + GroupOrientation, + GroupLayoutArgument, IPosition, IRange as ISelection, From ef06cae15864b8433d733f7fd08e05536f0c3946 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Sep 2022 07:24:43 +0200 Subject: [PATCH 2/7] cleanup --- src/vs/workbench/browser/layout.ts | 9 +++++---- src/vs/workbench/common/editor.ts | 7 ++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 06f1330ca8040..21c94a6634af6 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -110,7 +110,8 @@ interface IInitialEditorsState { filesToOpenOrCreate?: IPathToOpen[]; filesToDiff?: IPathToOpen[]; filesToMerge?: IPathToOpen[]; - editorLayout?: EditorGroupLayout; + + layout?: EditorGroupLayout; } export abstract class Layout extends Disposable implements IWorkbenchLayoutService { @@ -476,7 +477,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const initialEditorsState = this.getInitialEditorsState(); const windowInitializationState: IWorkbenchLayoutWindowInitializationState = { layout: { - editors: initialEditorsState?.editorLayout + editors: initialEditorsState?.layout }, editor: { restoreEditors: this.shouldRestoreEditors(this.contextService, initialEditorsState), @@ -625,7 +626,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (resolvedFileToOpenOrCreate) { filesToOpenOrCreate.push({ editor: resolvedFileToOpenOrCreate, - viewColumn: initialEditorsState.filesToOpenOrCreate?.[i].viewColumn + viewColumn: initialEditorsState.filesToOpenOrCreate?.[i].viewColumn // take over `viewColumn` from initial state }); } } @@ -663,7 +664,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._openedDefaultEditors = true; return { - editorLayout: defaultLayout.layout?.editors, + layout: defaultLayout.layout?.editors, filesToOpenOrCreate: defaultLayout?.editors?.map(editor => { const legacySelection = editor.selection && editor.selection.start && isNumber(editor.selection.start.line) ? { startLineNumber: editor.selection.start.line, diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 68e90f0865ab0..a219cd7ed2e39 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1421,14 +1421,11 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService pinned: true }; - let input: IResourceEditorInput | IUntitledTextResourceEditorInput; if (!exists) { - input = { resource, options, forceUntitled: true }; - } else { - input = { resource, options }; + return { resource, options, forceUntitled: true }; } - return input; + return { resource, options }; })); } From f65093c382638fbd341b6f84d1c66f92eae864ae Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Sep 2022 07:26:34 +0200 Subject: [PATCH 3/7] :lipstick: - more readonly --- src/vs/workbench/browser/layout.ts | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 21c94a6634af6..5db6c535c8833 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -55,40 +55,40 @@ interface IWorkbenchLayoutWindowRuntimeState { maximized: boolean; hasFocus: boolean; windowBorder: boolean; - menuBar: { + readonly menuBar: { toggled: boolean; }; - zenMode: { - transitionDisposables: DisposableStore; + readonly zenMode: { + readonly transitionDisposables: DisposableStore; }; } interface IEditorToOpen { - editor: IUntypedEditorInput; - viewColumn?: number; + readonly editor: IUntypedEditorInput; + readonly viewColumn?: number; } interface IWorkbenchLayoutWindowInitializationState { - views: { - defaults: string[] | undefined; - containerToRestore: { + readonly views: { + readonly defaults: string[] | undefined; + readonly containerToRestore: { sideBar?: string; panel?: string; auxiliaryBar?: string; }; }; - editor: { - restoreEditors: boolean; - editorsToOpen: Promise; + readonly editor: { + readonly restoreEditors: boolean; + readonly editorsToOpen: Promise; }; - layout?: { - editors?: EditorGroupLayout; + readonly layout?: { + readonly editors?: EditorGroupLayout; }; } interface IWorkbenchLayoutWindowState { - runtime: IWorkbenchLayoutWindowRuntimeState; - initialization: IWorkbenchLayoutWindowInitializationState; + readonly runtime: IWorkbenchLayoutWindowRuntimeState; + readonly initialization: IWorkbenchLayoutWindowInitializationState; } enum WorkbenchLayoutClasses { @@ -103,15 +103,15 @@ enum WorkbenchLayoutClasses { } interface IPathToOpen extends IPath { - viewColumn?: number; + readonly viewColumn?: number; } interface IInitialEditorsState { - filesToOpenOrCreate?: IPathToOpen[]; - filesToDiff?: IPathToOpen[]; - filesToMerge?: IPathToOpen[]; + readonly filesToOpenOrCreate?: IPathToOpen[]; + readonly filesToDiff?: IPathToOpen[]; + readonly filesToMerge?: IPathToOpen[]; - layout?: EditorGroupLayout; + readonly layout?: EditorGroupLayout; } export abstract class Layout extends Disposable implements IWorkbenchLayoutService { From fe28af71c5068ac35aa50036699db2961ef64715 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Sep 2022 07:31:24 +0200 Subject: [PATCH 4/7] :lipstick: - renames --- src/vs/workbench/browser/layout.ts | 166 ++++++++++++++--------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 5db6c535c8833..adfcb2e9526d1 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -50,7 +50,7 @@ import { AuxiliaryBarPart } from 'vs/workbench/browser/parts/auxiliarybar/auxili import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { LayoutStateKeys, LayoutStateModel, WorkbenchLayoutSettings } from 'vs/workbench/browser/layoutState'; -interface IWorkbenchLayoutWindowRuntimeState { +interface ILayoutRuntimeState { fullscreen: boolean; maximized: boolean; hasFocus: boolean; @@ -68,7 +68,7 @@ interface IEditorToOpen { readonly viewColumn?: number; } -interface IWorkbenchLayoutWindowInitializationState { +interface ILayoutInitializationState { readonly views: { readonly defaults: string[] | undefined; readonly containerToRestore: { @@ -86,12 +86,12 @@ interface IWorkbenchLayoutWindowInitializationState { }; } -interface IWorkbenchLayoutWindowState { - readonly runtime: IWorkbenchLayoutWindowRuntimeState; - readonly initialization: IWorkbenchLayoutWindowInitializationState; +interface ILayoutState { + readonly runtime: ILayoutRuntimeState; + readonly initialization: ILayoutInitializationState; } -enum WorkbenchLayoutClasses { +enum LayoutClasses { SIDEBAR_HIDDEN = 'nosidebar', EDITOR_HIDDEN = 'noeditorarea', PANEL_HIDDEN = 'nopanel', @@ -201,7 +201,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private logService!: ILogService; private telemetryService!: ITelemetryService; - private windowState!: IWorkbenchLayoutWindowState; + private state!: ILayoutState; private stateModel!: LayoutStateModel; private disposed = false; @@ -293,8 +293,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private onMenubarToggled(visible: boolean): void { - if (visible !== this.windowState.runtime.menuBar.toggled) { - this.windowState.runtime.menuBar.toggled = visible; + if (visible !== this.state.runtime.menuBar.toggled) { + this.state.runtime.menuBar.toggled = visible; const menuBarVisibility = getMenuBarVisibility(this.configurationService); @@ -304,7 +304,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // The menu bar toggles the title bar in full screen for toggle and classic settings - else if (this.windowState.runtime.fullscreen && (menuBarVisibility === 'toggle' || menuBarVisibility === 'classic')) { + else if (this.state.runtime.fullscreen && (menuBarVisibility === 'toggle' || menuBarVisibility === 'classic')) { this.workbenchGrid.setViewVisible(this.titleBarPartView, this.shouldShowTitleBar()); } @@ -316,13 +316,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private onFullscreenChanged(): void { - this.windowState.runtime.fullscreen = isFullscreen(); + this.state.runtime.fullscreen = isFullscreen(); // Apply as CSS class - if (this.windowState.runtime.fullscreen) { - this.container.classList.add(WorkbenchLayoutClasses.FULLSCREEN); + if (this.state.runtime.fullscreen) { + this.container.classList.add(LayoutClasses.FULLSCREEN); } else { - this.container.classList.remove(WorkbenchLayoutClasses.FULLSCREEN); + this.container.classList.remove(LayoutClasses.FULLSCREEN); const zenModeExitInfo = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_EXIT_INFO); const zenModeActive = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); @@ -332,7 +332,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Change edge snapping accordingly - this.workbenchGrid.edgeSnapping = this.windowState.runtime.fullscreen; + this.workbenchGrid.edgeSnapping = this.state.runtime.fullscreen; // Changing fullscreen state of the window has an impact // on custom title bar visibility, so we need to update @@ -344,15 +344,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.updateWindowBorder(true); } - this._onDidChangeFullscreen.fire(this.windowState.runtime.fullscreen); + this._onDidChangeFullscreen.fire(this.state.runtime.fullscreen); } private onWindowFocusChanged(hasFocus: boolean): void { - if (this.windowState.runtime.hasFocus === hasFocus) { + if (this.state.runtime.hasFocus === hasFocus) { return; } - this.windowState.runtime.hasFocus = hasFocus; + this.state.runtime.hasFocus = hasFocus; this.updateWindowBorder(); } @@ -415,21 +415,21 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const inactiveBorder = theme.getColor(WINDOW_INACTIVE_BORDER); let windowBorder = false; - if (!this.windowState.runtime.fullscreen && !this.windowState.runtime.maximized && (activeBorder || inactiveBorder)) { + if (!this.state.runtime.fullscreen && !this.state.runtime.maximized && (activeBorder || inactiveBorder)) { windowBorder = true; // If the inactive color is missing, fallback to the active one - const borderColor = this.windowState.runtime.hasFocus ? activeBorder : inactiveBorder ?? activeBorder; + const borderColor = this.state.runtime.hasFocus ? activeBorder : inactiveBorder ?? activeBorder; this.container.style.setProperty('--window-border-color', borderColor?.toString() ?? 'transparent'); } - if (windowBorder === this.windowState.runtime.windowBorder) { + if (windowBorder === this.state.runtime.windowBorder) { return; } - this.windowState.runtime.windowBorder = windowBorder; + this.state.runtime.windowBorder = windowBorder; - this.container.classList.toggle(WorkbenchLayoutClasses.WINDOW_BORDER, windowBorder); + this.container.classList.toggle(LayoutClasses.WINDOW_BORDER, windowBorder); if (!skipLayout) { this.layout(); @@ -473,9 +473,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.doUpdateLayoutConfiguration(); }); - // Window Initialization State + // Layout Initialization State const initialEditorsState = this.getInitialEditorsState(); - const windowInitializationState: IWorkbenchLayoutWindowInitializationState = { + const initialLayoutState: ILayoutInitializationState = { layout: { editors: initialEditorsState?.layout }, @@ -489,8 +489,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } }; - // Window Runtime State - const windowRuntimeState: IWorkbenchLayoutWindowRuntimeState = { + // Layout Runtime State + const layoutRuntimeState: ILayoutRuntimeState = { fullscreen: isFullscreen(), hasFocus: this.hostService.hasFocus, maximized: false, @@ -503,9 +503,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } }; - this.windowState = { - initialization: windowInitializationState, - runtime: windowRuntimeState, + this.state = { + initialization: initialLayoutState, + runtime: layoutRuntimeState, }; // Sidebar View Container To Restore @@ -520,7 +520,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } if (viewContainerToRestore) { - this.windowState.initialization.views.containerToRestore.sideBar = viewContainerToRestore; + this.state.initialization.views.containerToRestore.sideBar = viewContainerToRestore; } else { this.stateModel.setRuntimeValue(LayoutStateKeys.SIDEBAR_HIDDEN, true); } @@ -531,7 +531,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const viewContainerToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Panel)?.id); if (viewContainerToRestore) { - this.windowState.initialization.views.containerToRestore.panel = viewContainerToRestore; + this.state.initialization.views.containerToRestore.panel = viewContainerToRestore; } else { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_HIDDEN, true); } @@ -542,7 +542,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const viewContainerToRestore = this.storageService.get(AuxiliaryBarPart.activePanelSettingsKey, StorageScope.WORKSPACE, this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.AuxiliaryBar)?.id); if (viewContainerToRestore) { - this.windowState.initialization.views.containerToRestore.auxiliaryBar = viewContainerToRestore; + this.state.initialization.views.containerToRestore.auxiliaryBar = viewContainerToRestore; } else { this.stateModel.setRuntimeValue(LayoutStateKeys.AUXILIARYBAR_HIDDEN, true); } @@ -586,7 +586,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } protected willRestoreEditors(): boolean { - return this.windowState.initialization.editor.restoreEditors; + return this.state.initialization.editor.restoreEditors; } private async resolveEditorsToOpen(fileService: IFileService, initialEditorsState: IInitialEditorsState | undefined): Promise { @@ -722,8 +722,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi await this.editorGroupService.whenReady; // apply editor layout if any - if (this.windowState.initialization.layout?.editors) { - this.editorGroupService.applyLayout(this.windowState.initialization.layout.editors); + if (this.state.initialization.layout?.editors) { + this.editorGroupService.applyLayout(this.state.initialization.layout.editors); } // then see for editors to open as instructed @@ -735,7 +735,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // not need to await the editors from having // fully loaded. - const editors = await this.windowState.initialization.editor.editorsToOpen; + const editors = await this.state.initialization.editor.editorsToOpen; let openEditorsPromise: Promise | undefined = undefined; if (editors.length) { @@ -781,7 +781,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Restore default views (only when `IDefaultLayout` is provided) const restoreDefaultViewsPromise = (async () => { - if (this.windowState.initialization.views.defaults?.length) { + if (this.state.initialization.views.defaults?.length) { mark('code/willOpenDefaultViews'); const locationsRestored: { id: string; order: number }[] = []; @@ -806,7 +806,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return false; }; - const defaultViews = [...this.windowState.initialization.views.defaults].reverse().map((v, index) => ({ id: v, order: index })); + const defaultViews = [...this.state.initialization.views.defaults].reverse().map((v, index) => ({ id: v, order: index })); let i = defaultViews.length; while (i) { @@ -831,17 +831,17 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // If we opened a view in the sidebar, stop any restore there if (locationsRestored[ViewContainerLocation.Sidebar]) { - this.windowState.initialization.views.containerToRestore.sideBar = locationsRestored[ViewContainerLocation.Sidebar].id; + this.state.initialization.views.containerToRestore.sideBar = locationsRestored[ViewContainerLocation.Sidebar].id; } // If we opened a view in the panel, stop any restore there if (locationsRestored[ViewContainerLocation.Panel]) { - this.windowState.initialization.views.containerToRestore.panel = locationsRestored[ViewContainerLocation.Panel].id; + this.state.initialization.views.containerToRestore.panel = locationsRestored[ViewContainerLocation.Panel].id; } // If we opened a view in the auxiliary bar, stop any restore there if (locationsRestored[ViewContainerLocation.AuxiliaryBar]) { - this.windowState.initialization.views.containerToRestore.auxiliaryBar = locationsRestored[ViewContainerLocation.AuxiliaryBar].id; + this.state.initialization.views.containerToRestore.auxiliaryBar = locationsRestored[ViewContainerLocation.AuxiliaryBar].id; } mark('code/didOpenDefaultViews'); @@ -855,13 +855,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Restoring views could mean that sidebar already // restored, as such we need to test again await restoreDefaultViewsPromise; - if (!this.windowState.initialization.views.containerToRestore.sideBar) { + if (!this.state.initialization.views.containerToRestore.sideBar) { return; } mark('code/willRestoreViewlet'); - const viewlet = await this.paneCompositeService.openPaneComposite(this.windowState.initialization.views.containerToRestore.sideBar, ViewContainerLocation.Sidebar); + const viewlet = await this.paneCompositeService.openPaneComposite(this.state.initialization.views.containerToRestore.sideBar, ViewContainerLocation.Sidebar); if (!viewlet) { await this.paneCompositeService.openPaneComposite(this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id, ViewContainerLocation.Sidebar); // fallback to default viewlet as needed } @@ -875,13 +875,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Restoring views could mean that panel already // restored, as such we need to test again await restoreDefaultViewsPromise; - if (!this.windowState.initialization.views.containerToRestore.panel) { + if (!this.state.initialization.views.containerToRestore.panel) { return; } mark('code/willRestorePanel'); - const panel = await this.paneCompositeService.openPaneComposite(this.windowState.initialization.views.containerToRestore.panel, ViewContainerLocation.Panel); + const panel = await this.paneCompositeService.openPaneComposite(this.state.initialization.views.containerToRestore.panel, ViewContainerLocation.Panel); if (!panel) { await this.paneCompositeService.openPaneComposite(this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Panel)?.id, ViewContainerLocation.Panel); // fallback to default panel as needed } @@ -895,13 +895,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Restoring views could mean that panel already // restored, as such we need to test again await restoreDefaultViewsPromise; - if (!this.windowState.initialization.views.containerToRestore.auxiliaryBar) { + if (!this.state.initialization.views.containerToRestore.auxiliaryBar) { return; } mark('code/willRestoreAuxiliaryBar'); - const panel = await this.paneCompositeService.openPaneComposite(this.windowState.initialization.views.containerToRestore.auxiliaryBar, ViewContainerLocation.AuxiliaryBar); + const panel = await this.paneCompositeService.openPaneComposite(this.state.initialization.views.containerToRestore.auxiliaryBar, ViewContainerLocation.AuxiliaryBar); if (!panel) { await this.paneCompositeService.openPaneComposite(this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.AuxiliaryBar)?.id, ViewContainerLocation.AuxiliaryBar); // fallback to default panel as needed } @@ -1050,11 +1050,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // macOS desktop does not need a title bar when full screen if (isMacintosh && isNative) { - return !this.windowState.runtime.fullscreen; + return !this.state.runtime.fullscreen; } // non-fullscreen native must show the title bar - if (isNative && !this.windowState.runtime.fullscreen) { + if (isNative && !this.state.runtime.fullscreen) { return true; } @@ -1066,16 +1066,16 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // remaining behavior is based on menubar visibility switch (getMenuBarVisibility(this.configurationService)) { case 'classic': - return !this.windowState.runtime.fullscreen || this.windowState.runtime.menuBar.toggled; + return !this.state.runtime.fullscreen || this.state.runtime.menuBar.toggled; case 'compact': case 'hidden': return false; case 'toggle': - return this.windowState.runtime.menuBar.toggled; + return this.state.runtime.menuBar.toggled; case 'visible': return true; default: - return isWeb ? false : !this.windowState.runtime.fullscreen || this.windowState.runtime.menuBar.toggled; + return isWeb ? false : !this.state.runtime.fullscreen || this.state.runtime.menuBar.toggled; } } @@ -1109,7 +1109,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi toggleZenMode(skipLayout?: boolean, restoring = false): void { this.stateModel.setRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE, !this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE)); - this.windowState.runtime.zenMode.transitionDisposables.clear(); + this.state.runtime.zenMode.transitionDisposables.clear(); const setLineNumbers = (lineNumbers?: LineNumbersType) => { const setEditorLineNumbers = (editor: IEditor) => { @@ -1147,7 +1147,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Zen Mode Active if (this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE)) { - toggleFullScreen = !this.windowState.runtime.fullscreen && config.fullScreen && !isIOS; + toggleFullScreen = !this.state.runtime.fullscreen && config.fullScreen && !isIOS; if (!restoring) { zenModeExitInfo.transitionedToFullScreen = toggleFullScreen; @@ -1173,17 +1173,17 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (config.hideLineNumbers) { setLineNumbers('off'); - this.windowState.runtime.zenMode.transitionDisposables.add(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); + this.state.runtime.zenMode.transitionDisposables.add(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); } if (config.hideTabs && this.editorGroupService.partOptions.showTabs) { - this.windowState.runtime.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: false })); + this.state.runtime.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: false })); } if (config.silentNotifications && zenModeExitInfo.handleNotificationsDoNotDisturbMode) { this.notificationService.doNotDisturbMode = true; } - this.windowState.runtime.zenMode.transitionDisposables.add(this.configurationService.onDidChangeConfiguration(e => { + this.state.runtime.zenMode.transitionDisposables.add(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(WorkbenchLayoutSettings.ZEN_MODE_SILENT_NOTIFICATIONS)) { const zenModeSilentNotifications = !!this.configurationService.getValue(WorkbenchLayoutSettings.ZEN_MODE_SILENT_NOTIFICATIONS); if (zenModeExitInfo.handleNotificationsDoNotDisturbMode) { @@ -1231,7 +1231,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.focus(); - toggleFullScreen = zenModeExitInfo.transitionedToFullScreen && this.windowState.runtime.fullscreen; + toggleFullScreen = zenModeExitInfo.transitionedToFullScreen && this.state.runtime.fullscreen; } if (!skipLayout) { @@ -1251,9 +1251,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Adjust CSS if (hidden) { - this.container.classList.add(WorkbenchLayoutClasses.STATUSBAR_HIDDEN); + this.container.classList.add(LayoutClasses.STATUSBAR_HIDDEN); } else { - this.container.classList.remove(WorkbenchLayoutClasses.STATUSBAR_HIDDEN); + this.container.classList.remove(LayoutClasses.STATUSBAR_HIDDEN); } // Propagate to grid @@ -1301,7 +1301,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.container.prepend(workbenchGrid.element); this.container.setAttribute('role', 'application'); this.workbenchGrid = workbenchGrid; - this.workbenchGrid.edgeSnapping = this.windowState.runtime.fullscreen; + this.workbenchGrid.edgeSnapping = this.state.runtime.fullscreen; for (const part of [titleBar, editorPart, activityBar, panelPart, sideBar, statusBar, auxiliaryBarPart]) { this._register(part.onDidVisibilityChange((visible) => { @@ -1484,9 +1484,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Adjust CSS if (hidden) { - this.container.classList.add(WorkbenchLayoutClasses.EDITOR_HIDDEN); + this.container.classList.add(LayoutClasses.EDITOR_HIDDEN); } else { - this.container.classList.remove(WorkbenchLayoutClasses.EDITOR_HIDDEN); + this.container.classList.remove(LayoutClasses.EDITOR_HIDDEN); } // Propagate to grid @@ -1500,12 +1500,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi getLayoutClasses(): string[] { return coalesce([ - !this.isVisible(Parts.SIDEBAR_PART) ? WorkbenchLayoutClasses.SIDEBAR_HIDDEN : undefined, - !this.isVisible(Parts.EDITOR_PART) ? WorkbenchLayoutClasses.EDITOR_HIDDEN : undefined, - !this.isVisible(Parts.PANEL_PART) ? WorkbenchLayoutClasses.PANEL_HIDDEN : undefined, - !this.isVisible(Parts.AUXILIARYBAR_PART) ? WorkbenchLayoutClasses.AUXILIARYBAR_HIDDEN : undefined, - !this.isVisible(Parts.STATUSBAR_PART) ? WorkbenchLayoutClasses.STATUSBAR_HIDDEN : undefined, - this.windowState.runtime.fullscreen ? WorkbenchLayoutClasses.FULLSCREEN : undefined + !this.isVisible(Parts.SIDEBAR_PART) ? LayoutClasses.SIDEBAR_HIDDEN : undefined, + !this.isVisible(Parts.EDITOR_PART) ? LayoutClasses.EDITOR_HIDDEN : undefined, + !this.isVisible(Parts.PANEL_PART) ? LayoutClasses.PANEL_HIDDEN : undefined, + !this.isVisible(Parts.AUXILIARYBAR_PART) ? LayoutClasses.AUXILIARYBAR_HIDDEN : undefined, + !this.isVisible(Parts.STATUSBAR_PART) ? LayoutClasses.STATUSBAR_HIDDEN : undefined, + this.state.runtime.fullscreen ? LayoutClasses.FULLSCREEN : undefined ]); } @@ -1514,9 +1514,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Adjust CSS if (hidden) { - this.container.classList.add(WorkbenchLayoutClasses.SIDEBAR_HIDDEN); + this.container.classList.add(LayoutClasses.SIDEBAR_HIDDEN); } else { - this.container.classList.remove(WorkbenchLayoutClasses.SIDEBAR_HIDDEN); + this.container.classList.remove(LayoutClasses.SIDEBAR_HIDDEN); } // If sidebar becomes hidden, also hide the current active Viewlet if any @@ -1651,9 +1651,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Adjust CSS if (hidden) { - this.container.classList.add(WorkbenchLayoutClasses.PANEL_HIDDEN); + this.container.classList.add(LayoutClasses.PANEL_HIDDEN); } else { - this.container.classList.remove(WorkbenchLayoutClasses.PANEL_HIDDEN); + this.container.classList.remove(LayoutClasses.PANEL_HIDDEN); } // If panel part becomes hidden, also hide the current active panel if any @@ -1755,9 +1755,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Adjust CSS if (hidden) { - this.container.classList.add(WorkbenchLayoutClasses.AUXILIARYBAR_HIDDEN); + this.container.classList.add(LayoutClasses.AUXILIARYBAR_HIDDEN); } else { - this.container.classList.remove(WorkbenchLayoutClasses.AUXILIARYBAR_HIDDEN); + this.container.classList.remove(LayoutClasses.AUXILIARYBAR_HIDDEN); } // If auxiliary bar becomes hidden, also hide the current active pane composite if any @@ -1813,15 +1813,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } hasWindowBorder(): boolean { - return this.windowState.runtime.windowBorder; + return this.state.runtime.windowBorder; } getWindowBorderWidth(): number { - return this.windowState.runtime.windowBorder ? 2 : 0; + return this.state.runtime.windowBorder ? 2 : 0; } getWindowBorderRadius(): string | undefined { - return this.windowState.runtime.windowBorder && isMacintosh ? '5px' : undefined; + return this.state.runtime.windowBorder && isMacintosh ? '5px' : undefined; } isPanelMaximized(): boolean { @@ -1939,17 +1939,17 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } isWindowMaximized() { - return this.windowState.runtime.maximized; + return this.state.runtime.maximized; } updateWindowMaximizedState(maximized: boolean) { - this.container.classList.toggle(WorkbenchLayoutClasses.MAXIMIZED, maximized); + this.container.classList.toggle(LayoutClasses.MAXIMIZED, maximized); - if (this.windowState.runtime.maximized === maximized) { + if (this.state.runtime.maximized === maximized) { return; } - this.windowState.runtime.maximized = maximized; + this.state.runtime.maximized = maximized; this.updateWindowBorder(); this._onDidChangeWindowMaximized.fire(maximized); From d86a6137cb58c57eb586605422001fdfb5d725c6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Sep 2022 07:37:35 +0200 Subject: [PATCH 5/7] :lipstick: - move things --- src/vs/workbench/browser/layout.ts | 294 ++++++++++++++++++++++- src/vs/workbench/browser/layoutState.ts | 295 ------------------------ 2 files changed, 291 insertions(+), 298 deletions(-) delete mode 100644 src/vs/workbench/browser/layoutState.ts diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index adfcb2e9526d1..69e2ab6faa402 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -14,8 +14,8 @@ import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment } from 'vs/workbench/services/layout/browser/layoutService'; import { isTemporaryWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { StartupKind, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -48,7 +48,8 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; import { AuxiliaryBarPart } from 'vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { LayoutStateKeys, LayoutStateModel, WorkbenchLayoutSettings } from 'vs/workbench/browser/layoutState'; + +//#region Layout Implementation interface ILayoutRuntimeState { fullscreen: boolean; @@ -2235,3 +2236,290 @@ type ZenModeConfiguration = { function getZenModeConfiguration(configurationService: IConfigurationService): ZenModeConfiguration { return configurationService.getValue(WorkbenchLayoutSettings.ZEN_MODE_CONFIG); } + +//#region Layout State Model + +interface IWorkbenchLayoutStateKey { + readonly name: string; + readonly runtime: boolean; + readonly defaultValue: unknown; + readonly scope: StorageScope; + readonly target: StorageTarget; + readonly zenModeIgnore?: boolean; +} + +type StorageKeyType = string | boolean | number | object; + +abstract class WorkbenchLayoutStateKey implements IWorkbenchLayoutStateKey { + + abstract readonly runtime: boolean; + + constructor(readonly name: string, readonly scope: StorageScope, readonly target: StorageTarget, public defaultValue: T) { } +} + +class RuntimeStateKey extends WorkbenchLayoutStateKey { + + readonly runtime = true; + + constructor(name: string, scope: StorageScope, target: StorageTarget, defaultValue: T, readonly zenModeIgnore?: boolean) { + super(name, scope, target, defaultValue); + } +} + +class InitializationStateKey extends WorkbenchLayoutStateKey { + readonly runtime = false; +} + +const LayoutStateKeys = { + + // Editor + EDITOR_CENTERED: new RuntimeStateKey('editor.centered', StorageScope.WORKSPACE, StorageTarget.USER, false), + + // Zen Mode + ZEN_MODE_ACTIVE: new RuntimeStateKey('zenMode.active', StorageScope.WORKSPACE, StorageTarget.USER, false), + ZEN_MODE_EXIT_INFO: new RuntimeStateKey('zenMode.exitInfo', StorageScope.WORKSPACE, StorageTarget.USER, { + transitionedToCenteredEditorLayout: false, + transitionedToFullScreen: false, + handleNotificationsDoNotDisturbMode: false, + wasVisible: { + auxiliaryBar: false, + panel: false, + sideBar: false, + }, + }), + + // Part Sizing + GRID_SIZE: new InitializationStateKey('grid.size', StorageScope.PROFILE, StorageTarget.MACHINE, { width: 800, height: 600 }), + SIDEBAR_SIZE: new InitializationStateKey('sideBar.size', StorageScope.PROFILE, StorageTarget.MACHINE, 200), + AUXILIARYBAR_SIZE: new InitializationStateKey('auxiliaryBar.size', StorageScope.PROFILE, StorageTarget.MACHINE, 200), + PANEL_SIZE: new InitializationStateKey('panel.size', StorageScope.PROFILE, StorageTarget.MACHINE, 300), + + PANEL_LAST_NON_MAXIMIZED_HEIGHT: new RuntimeStateKey('panel.lastNonMaximizedHeight', StorageScope.PROFILE, StorageTarget.MACHINE, 300), + PANEL_LAST_NON_MAXIMIZED_WIDTH: new RuntimeStateKey('panel.lastNonMaximizedWidth', StorageScope.PROFILE, StorageTarget.MACHINE, 300), + PANEL_WAS_LAST_MAXIMIZED: new RuntimeStateKey('panel.wasLastMaximized', StorageScope.WORKSPACE, StorageTarget.USER, false), + + // Part Positions + SIDEBAR_POSITON: new RuntimeStateKey('sideBar.position', StorageScope.WORKSPACE, StorageTarget.USER, Position.LEFT), + PANEL_POSITION: new RuntimeStateKey('panel.position', StorageScope.WORKSPACE, StorageTarget.USER, Position.BOTTOM), + PANEL_ALIGNMENT: new RuntimeStateKey('panel.alignment', StorageScope.PROFILE, StorageTarget.USER, 'center'), + + // Part Visibility + ACTIVITYBAR_HIDDEN: new RuntimeStateKey('activityBar.hidden', StorageScope.WORKSPACE, StorageTarget.USER, false, true), + SIDEBAR_HIDDEN: new RuntimeStateKey('sideBar.hidden', StorageScope.WORKSPACE, StorageTarget.USER, false), + EDITOR_HIDDEN: new RuntimeStateKey('editor.hidden', StorageScope.WORKSPACE, StorageTarget.USER, false), + PANEL_HIDDEN: new RuntimeStateKey('panel.hidden', StorageScope.WORKSPACE, StorageTarget.USER, true), + AUXILIARYBAR_HIDDEN: new RuntimeStateKey('auxiliaryBar.hidden', StorageScope.WORKSPACE, StorageTarget.USER, true), + STATUSBAR_HIDDEN: new RuntimeStateKey('statusBar.hidden', StorageScope.WORKSPACE, StorageTarget.USER, false, true) + +} as const; + +interface ILayoutStateChangeEvent { + readonly key: RuntimeStateKey; + readonly value: T; +} + +enum WorkbenchLayoutSettings { + PANEL_POSITION = 'workbench.panel.defaultLocation', + PANEL_OPENS_MAXIMIZED = 'workbench.panel.opensMaximized', + ZEN_MODE_CONFIG = 'zenMode', + ZEN_MODE_SILENT_NOTIFICATIONS = 'zenMode.silentNotifications', + EDITOR_CENTERED_LAYOUT_AUTO_RESIZE = 'workbench.editor.centeredLayoutAutoResize', +} + +enum LegacyWorkbenchLayoutSettings { + ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', // Deprecated to UI State + STATUSBAR_VISIBLE = 'workbench.statusBar.visible', // Deprecated to UI State + SIDEBAR_POSITION = 'workbench.sideBar.location', // Deprecated to UI State +} + +class LayoutStateModel extends Disposable { + + static readonly STORAGE_PREFIX = 'workbench.'; + + private readonly _onDidChangeState = this._register(new Emitter>()); + readonly onDidChangeState = this._onDidChangeState.event; + + private readonly stateCache = new Map(); + + constructor( + private readonly storageService: IStorageService, + private readonly configurationService: IConfigurationService, + private readonly contextService: IWorkspaceContextService, + private readonly container: HTMLElement + ) { + super(); + + this._register(this.configurationService.onDidChangeConfiguration(configurationChange => this.updateStateFromLegacySettings(configurationChange))); + } + + private updateStateFromLegacySettings(configurationChangeEvent: IConfigurationChangeEvent): void { + const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); + + if (configurationChangeEvent.affectsConfiguration(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE) && !isZenMode) { + this.setRuntimeValueAndFire(LayoutStateKeys.ACTIVITYBAR_HIDDEN, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE)); + } + + if (configurationChangeEvent.affectsConfiguration(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE) && !isZenMode) { + this.setRuntimeValueAndFire(LayoutStateKeys.STATUSBAR_HIDDEN, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE)); + } + + if (configurationChangeEvent.affectsConfiguration(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION)) { + this.setRuntimeValueAndFire(LayoutStateKeys.SIDEBAR_POSITON, positionFromString(this.configurationService.getValue(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION) ?? 'left')); + } + } + + private updateLegacySettingsFromState(key: RuntimeStateKey, value: T): void { + const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); + if (key.zenModeIgnore && isZenMode) { + return; + } + + if (key === LayoutStateKeys.ACTIVITYBAR_HIDDEN) { + this.configurationService.updateValue(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE, !value); + } else if (key === LayoutStateKeys.STATUSBAR_HIDDEN) { + this.configurationService.updateValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE, !value); + } else if (key === LayoutStateKeys.SIDEBAR_POSITON) { + this.configurationService.updateValue(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION, positionToString(value as Position)); + } + } + + load(): void { + let key: keyof typeof LayoutStateKeys; + + // Load stored values for all keys + for (key in LayoutStateKeys) { + const stateKey = LayoutStateKeys[key] as WorkbenchLayoutStateKey; + const value = this.loadKeyFromStorage(stateKey); + + if (value !== undefined) { + this.stateCache.set(stateKey.name, value); + } + } + + // Apply legacy settings + this.stateCache.set(LayoutStateKeys.ACTIVITYBAR_HIDDEN.name, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE)); + this.stateCache.set(LayoutStateKeys.STATUSBAR_HIDDEN.name, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE)); + this.stateCache.set(LayoutStateKeys.SIDEBAR_POSITON.name, positionFromString(this.configurationService.getValue(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION) ?? 'left')); + + // Set dynamic defaults: part sizing and side bar visibility + const workbenchDimensions = getClientArea(this.container); + LayoutStateKeys.PANEL_POSITION.defaultValue = positionFromString(this.configurationService.getValue(WorkbenchLayoutSettings.PANEL_POSITION) ?? 'bottom'); + LayoutStateKeys.GRID_SIZE.defaultValue = { height: workbenchDimensions.height, width: workbenchDimensions.width }; + LayoutStateKeys.SIDEBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4); + LayoutStateKeys.AUXILIARYBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4); + LayoutStateKeys.PANEL_SIZE.defaultValue = (this.stateCache.get(LayoutStateKeys.PANEL_POSITION.name) ?? LayoutStateKeys.PANEL_POSITION.defaultValue) === 'bottom' ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4; + LayoutStateKeys.SIDEBAR_HIDDEN.defaultValue = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; + + // Apply all defaults + for (key in LayoutStateKeys) { + const stateKey = LayoutStateKeys[key]; + if (this.stateCache.get(stateKey.name) === undefined) { + this.stateCache.set(stateKey.name, stateKey.defaultValue); + } + } + + // Register for runtime key changes + this._register(this.storageService.onDidChangeValue(storageChangeEvent => { + let key: keyof typeof LayoutStateKeys; + for (key in LayoutStateKeys) { + const stateKey = LayoutStateKeys[key] as WorkbenchLayoutStateKey; + if (stateKey instanceof RuntimeStateKey && stateKey.scope === StorageScope.PROFILE && stateKey.target === StorageTarget.USER) { + if (`${LayoutStateModel.STORAGE_PREFIX}${stateKey.name}` === storageChangeEvent.key) { + const value = this.loadKeyFromStorage(stateKey) ?? stateKey.defaultValue; + if (this.stateCache.get(stateKey.name) !== value) { + this.stateCache.set(stateKey.name, value); + this._onDidChangeState.fire({ key: stateKey, value }); + } + } + } + } + })); + } + + save(workspace: boolean, global: boolean): void { + let key: keyof typeof LayoutStateKeys; + + const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); + + for (key in LayoutStateKeys) { + const stateKey = LayoutStateKeys[key] as WorkbenchLayoutStateKey; + if ((workspace && stateKey.scope === StorageScope.WORKSPACE) || + (global && stateKey.scope === StorageScope.PROFILE)) { + if (isZenMode && stateKey instanceof RuntimeStateKey && stateKey.zenModeIgnore) { + continue; // Don't write out specific keys while in zen mode + } + + this.saveKeyToStorage(stateKey); + } + } + } + + getInitializationValue(key: InitializationStateKey): T { + return this.stateCache.get(key.name) as T; + } + + setInitializationValue(key: InitializationStateKey, value: T): void { + this.stateCache.set(key.name, value); + } + + getRuntimeValue(key: RuntimeStateKey, fallbackToSetting?: boolean): T { + if (fallbackToSetting) { + switch (key) { + case LayoutStateKeys.ACTIVITYBAR_HIDDEN: + this.stateCache.set(key.name, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE)); + break; + case LayoutStateKeys.STATUSBAR_HIDDEN: + this.stateCache.set(key.name, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE)); + break; + case LayoutStateKeys.SIDEBAR_POSITON: + this.stateCache.set(key.name, this.configurationService.getValue(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION) ?? 'left'); + break; + } + } + + return this.stateCache.get(key.name) as T; + } + + setRuntimeValue(key: RuntimeStateKey, value: T): void { + this.stateCache.set(key.name, value); + const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); + + if (key.scope === StorageScope.PROFILE) { + if (!isZenMode || !key.zenModeIgnore) { + this.saveKeyToStorage(key); + this.updateLegacySettingsFromState(key, value); + } + } + } + + private setRuntimeValueAndFire(key: RuntimeStateKey, value: T): void { + const previousValue = this.stateCache.get(key.name); + if (previousValue === value) { + return; + } + + this.setRuntimeValue(key, value); + this._onDidChangeState.fire({ key, value }); + } + + private saveKeyToStorage(key: WorkbenchLayoutStateKey): void { + const value = this.stateCache.get(key.name) as T; + this.storageService.store(`${LayoutStateModel.STORAGE_PREFIX}${key.name}`, typeof value === 'object' ? JSON.stringify(value) : value, key.scope, key.target); + } + + private loadKeyFromStorage(key: WorkbenchLayoutStateKey): T | undefined { + let value: any = this.storageService.get(`${LayoutStateModel.STORAGE_PREFIX}${key.name}`, key.scope); + + if (value !== undefined) { + switch (typeof key.defaultValue) { + case 'boolean': value = value === 'true'; break; + case 'number': value = parseInt(value); break; + case 'object': value = JSON.parse(value); break; + } + } + + return value as T | undefined; + } +} + +//#endregion diff --git a/src/vs/workbench/browser/layoutState.ts b/src/vs/workbench/browser/layoutState.ts deleted file mode 100644 index 4df700b69d74c..0000000000000 --- a/src/vs/workbench/browser/layoutState.ts +++ /dev/null @@ -1,295 +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 { getClientArea } from 'vs/base/browser/dom'; -import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { PanelAlignment, Position, positionFromString, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; - -interface IWorkbenchLayoutStateKey { - readonly name: string; - readonly runtime: boolean; - readonly defaultValue: unknown; - readonly scope: StorageScope; - readonly target: StorageTarget; - readonly zenModeIgnore?: boolean; -} - -type StorageKeyType = string | boolean | number | object; - -abstract class WorkbenchLayoutStateKey implements IWorkbenchLayoutStateKey { - - abstract readonly runtime: boolean; - - constructor(readonly name: string, readonly scope: StorageScope, readonly target: StorageTarget, public defaultValue: T) { } -} - -class RuntimeStateKey extends WorkbenchLayoutStateKey { - - readonly runtime = true; - - constructor(name: string, scope: StorageScope, target: StorageTarget, defaultValue: T, readonly zenModeIgnore?: boolean) { - super(name, scope, target, defaultValue); - } -} - -class InitializationStateKey extends WorkbenchLayoutStateKey { - readonly runtime = false; -} - -export const LayoutStateKeys = { - - // Editor - EDITOR_CENTERED: new RuntimeStateKey('editor.centered', StorageScope.WORKSPACE, StorageTarget.USER, false), - - // Zen Mode - ZEN_MODE_ACTIVE: new RuntimeStateKey('zenMode.active', StorageScope.WORKSPACE, StorageTarget.USER, false), - ZEN_MODE_EXIT_INFO: new RuntimeStateKey('zenMode.exitInfo', StorageScope.WORKSPACE, StorageTarget.USER, { - transitionedToCenteredEditorLayout: false, - transitionedToFullScreen: false, - handleNotificationsDoNotDisturbMode: false, - wasVisible: { - auxiliaryBar: false, - panel: false, - sideBar: false, - }, - }), - - // Part Sizing - GRID_SIZE: new InitializationStateKey('grid.size', StorageScope.PROFILE, StorageTarget.MACHINE, { width: 800, height: 600 }), - SIDEBAR_SIZE: new InitializationStateKey('sideBar.size', StorageScope.PROFILE, StorageTarget.MACHINE, 200), - AUXILIARYBAR_SIZE: new InitializationStateKey('auxiliaryBar.size', StorageScope.PROFILE, StorageTarget.MACHINE, 200), - PANEL_SIZE: new InitializationStateKey('panel.size', StorageScope.PROFILE, StorageTarget.MACHINE, 300), - - PANEL_LAST_NON_MAXIMIZED_HEIGHT: new RuntimeStateKey('panel.lastNonMaximizedHeight', StorageScope.PROFILE, StorageTarget.MACHINE, 300), - PANEL_LAST_NON_MAXIMIZED_WIDTH: new RuntimeStateKey('panel.lastNonMaximizedWidth', StorageScope.PROFILE, StorageTarget.MACHINE, 300), - PANEL_WAS_LAST_MAXIMIZED: new RuntimeStateKey('panel.wasLastMaximized', StorageScope.WORKSPACE, StorageTarget.USER, false), - - // Part Positions - SIDEBAR_POSITON: new RuntimeStateKey('sideBar.position', StorageScope.WORKSPACE, StorageTarget.USER, Position.LEFT), - PANEL_POSITION: new RuntimeStateKey('panel.position', StorageScope.WORKSPACE, StorageTarget.USER, Position.BOTTOM), - PANEL_ALIGNMENT: new RuntimeStateKey('panel.alignment', StorageScope.PROFILE, StorageTarget.USER, 'center'), - - // Part Visibility - ACTIVITYBAR_HIDDEN: new RuntimeStateKey('activityBar.hidden', StorageScope.WORKSPACE, StorageTarget.USER, false, true), - SIDEBAR_HIDDEN: new RuntimeStateKey('sideBar.hidden', StorageScope.WORKSPACE, StorageTarget.USER, false), - EDITOR_HIDDEN: new RuntimeStateKey('editor.hidden', StorageScope.WORKSPACE, StorageTarget.USER, false), - PANEL_HIDDEN: new RuntimeStateKey('panel.hidden', StorageScope.WORKSPACE, StorageTarget.USER, true), - AUXILIARYBAR_HIDDEN: new RuntimeStateKey('auxiliaryBar.hidden', StorageScope.WORKSPACE, StorageTarget.USER, true), - STATUSBAR_HIDDEN: new RuntimeStateKey('statusBar.hidden', StorageScope.WORKSPACE, StorageTarget.USER, false, true) - -} as const; - -interface ILayoutStateChangeEvent { - readonly key: RuntimeStateKey; - readonly value: T; -} - -export enum WorkbenchLayoutSettings { - PANEL_POSITION = 'workbench.panel.defaultLocation', - PANEL_OPENS_MAXIMIZED = 'workbench.panel.opensMaximized', - ZEN_MODE_CONFIG = 'zenMode', - ZEN_MODE_SILENT_NOTIFICATIONS = 'zenMode.silentNotifications', - EDITOR_CENTERED_LAYOUT_AUTO_RESIZE = 'workbench.editor.centeredLayoutAutoResize', -} - -enum LegacyWorkbenchLayoutSettings { - ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', // Deprecated to UI State - STATUSBAR_VISIBLE = 'workbench.statusBar.visible', // Deprecated to UI State - SIDEBAR_POSITION = 'workbench.sideBar.location', // Deprecated to UI State -} - -export class LayoutStateModel extends Disposable { - - static readonly STORAGE_PREFIX = 'workbench.'; - - private readonly _onDidChangeState = this._register(new Emitter>()); - readonly onDidChangeState = this._onDidChangeState.event; - - private readonly stateCache = new Map(); - - constructor( - private readonly storageService: IStorageService, - private readonly configurationService: IConfigurationService, - private readonly contextService: IWorkspaceContextService, - private readonly container: HTMLElement - ) { - super(); - - this._register(this.configurationService.onDidChangeConfiguration(configurationChange => this.updateStateFromLegacySettings(configurationChange))); - } - - private updateStateFromLegacySettings(configurationChangeEvent: IConfigurationChangeEvent): void { - const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); - - if (configurationChangeEvent.affectsConfiguration(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE) && !isZenMode) { - this.setRuntimeValueAndFire(LayoutStateKeys.ACTIVITYBAR_HIDDEN, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE)); - } - - if (configurationChangeEvent.affectsConfiguration(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE) && !isZenMode) { - this.setRuntimeValueAndFire(LayoutStateKeys.STATUSBAR_HIDDEN, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE)); - } - - if (configurationChangeEvent.affectsConfiguration(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION)) { - this.setRuntimeValueAndFire(LayoutStateKeys.SIDEBAR_POSITON, positionFromString(this.configurationService.getValue(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION) ?? 'left')); - } - } - - private updateLegacySettingsFromState(key: RuntimeStateKey, value: T): void { - const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); - if (key.zenModeIgnore && isZenMode) { - return; - } - - if (key === LayoutStateKeys.ACTIVITYBAR_HIDDEN) { - this.configurationService.updateValue(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE, !value); - } else if (key === LayoutStateKeys.STATUSBAR_HIDDEN) { - this.configurationService.updateValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE, !value); - } else if (key === LayoutStateKeys.SIDEBAR_POSITON) { - this.configurationService.updateValue(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION, positionToString(value as Position)); - } - } - - load(): void { - let key: keyof typeof LayoutStateKeys; - - // Load stored values for all keys - for (key in LayoutStateKeys) { - const stateKey = LayoutStateKeys[key] as WorkbenchLayoutStateKey; - const value = this.loadKeyFromStorage(stateKey); - - if (value !== undefined) { - this.stateCache.set(stateKey.name, value); - } - } - - // Apply legacy settings - this.stateCache.set(LayoutStateKeys.ACTIVITYBAR_HIDDEN.name, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE)); - this.stateCache.set(LayoutStateKeys.STATUSBAR_HIDDEN.name, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE)); - this.stateCache.set(LayoutStateKeys.SIDEBAR_POSITON.name, positionFromString(this.configurationService.getValue(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION) ?? 'left')); - - // Set dynamic defaults: part sizing and side bar visibility - const workbenchDimensions = getClientArea(this.container); - LayoutStateKeys.PANEL_POSITION.defaultValue = positionFromString(this.configurationService.getValue(WorkbenchLayoutSettings.PANEL_POSITION) ?? 'bottom'); - LayoutStateKeys.GRID_SIZE.defaultValue = { height: workbenchDimensions.height, width: workbenchDimensions.width }; - LayoutStateKeys.SIDEBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4); - LayoutStateKeys.AUXILIARYBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4); - LayoutStateKeys.PANEL_SIZE.defaultValue = (this.stateCache.get(LayoutStateKeys.PANEL_POSITION.name) ?? LayoutStateKeys.PANEL_POSITION.defaultValue) === 'bottom' ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4; - LayoutStateKeys.SIDEBAR_HIDDEN.defaultValue = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; - - // Apply all defaults - for (key in LayoutStateKeys) { - const stateKey = LayoutStateKeys[key]; - if (this.stateCache.get(stateKey.name) === undefined) { - this.stateCache.set(stateKey.name, stateKey.defaultValue); - } - } - - // Register for runtime key changes - this._register(this.storageService.onDidChangeValue(storageChangeEvent => { - let key: keyof typeof LayoutStateKeys; - for (key in LayoutStateKeys) { - const stateKey = LayoutStateKeys[key] as WorkbenchLayoutStateKey; - if (stateKey instanceof RuntimeStateKey && stateKey.scope === StorageScope.PROFILE && stateKey.target === StorageTarget.USER) { - if (`${LayoutStateModel.STORAGE_PREFIX}${stateKey.name}` === storageChangeEvent.key) { - const value = this.loadKeyFromStorage(stateKey) ?? stateKey.defaultValue; - if (this.stateCache.get(stateKey.name) !== value) { - this.stateCache.set(stateKey.name, value); - this._onDidChangeState.fire({ key: stateKey, value }); - } - } - } - } - })); - } - - save(workspace: boolean, global: boolean): void { - let key: keyof typeof LayoutStateKeys; - - const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); - - for (key in LayoutStateKeys) { - const stateKey = LayoutStateKeys[key] as WorkbenchLayoutStateKey; - if ((workspace && stateKey.scope === StorageScope.WORKSPACE) || - (global && stateKey.scope === StorageScope.PROFILE)) { - if (isZenMode && stateKey instanceof RuntimeStateKey && stateKey.zenModeIgnore) { - continue; // Don't write out specific keys while in zen mode - } - - this.saveKeyToStorage(stateKey); - } - } - } - - getInitializationValue(key: InitializationStateKey): T { - return this.stateCache.get(key.name) as T; - } - - setInitializationValue(key: InitializationStateKey, value: T): void { - this.stateCache.set(key.name, value); - } - - getRuntimeValue(key: RuntimeStateKey, fallbackToSetting?: boolean): T { - if (fallbackToSetting) { - switch (key) { - case LayoutStateKeys.ACTIVITYBAR_HIDDEN: - this.stateCache.set(key.name, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.ACTIVITYBAR_VISIBLE)); - break; - case LayoutStateKeys.STATUSBAR_HIDDEN: - this.stateCache.set(key.name, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE)); - break; - case LayoutStateKeys.SIDEBAR_POSITON: - this.stateCache.set(key.name, this.configurationService.getValue(LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION) ?? 'left'); - break; - } - } - - return this.stateCache.get(key.name) as T; - } - - setRuntimeValue(key: RuntimeStateKey, value: T): void { - this.stateCache.set(key.name, value); - const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); - - if (key.scope === StorageScope.PROFILE) { - if (!isZenMode || !key.zenModeIgnore) { - this.saveKeyToStorage(key); - this.updateLegacySettingsFromState(key, value); - } - } - } - - private setRuntimeValueAndFire(key: RuntimeStateKey, value: T): void { - const previousValue = this.stateCache.get(key.name); - if (previousValue === value) { - return; - } - - this.setRuntimeValue(key, value); - this._onDidChangeState.fire({ key, value }); - } - - private saveKeyToStorage(key: WorkbenchLayoutStateKey): void { - const value = this.stateCache.get(key.name) as T; - this.storageService.store(`${LayoutStateModel.STORAGE_PREFIX}${key.name}`, typeof value === 'object' ? JSON.stringify(value) : value, key.scope, key.target); - } - - private loadKeyFromStorage(key: WorkbenchLayoutStateKey): T | undefined { - let value: any = this.storageService.get(`${LayoutStateModel.STORAGE_PREFIX}${key.name}`, key.scope); - - if (value !== undefined) { - switch (typeof key.defaultValue) { - case 'boolean': value = value === 'true'; break; - case 'number': value = parseInt(value); break; - case 'object': value = JSON.parse(value); break; - } - } - - return value as T | undefined; - } -} From 47411e3c8f18d0e6f7224fb3f19ec48053d3224a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Sep 2022 14:28:52 +0200 Subject: [PATCH 6/7] fix region --- src/vs/workbench/browser/layout.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 69e2ab6faa402..7e145c9586ace 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -2237,6 +2237,8 @@ function getZenModeConfiguration(configurationService: IConfigurationService): Z return configurationService.getValue(WorkbenchLayoutSettings.ZEN_MODE_CONFIG); } +//#endregion + //#region Layout State Model interface IWorkbenchLayoutStateKey { From 7613d994e52ceb28d24f6cb490c5d6a5e6bf19fb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Sep 2022 10:34:34 +0200 Subject: [PATCH 7/7] remove deprecated --- src/vs/workbench/browser/layout.ts | 12 +++++++----- src/vs/workbench/browser/web.api.ts | 25 ++----------------------- src/vs/workbench/workbench.web.main.ts | 4 +--- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 7e145c9586ace..ea93a14b908d3 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -667,11 +667,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return { layout: defaultLayout.layout?.editors, filesToOpenOrCreate: defaultLayout?.editors?.map(editor => { - const legacySelection = editor.selection && editor.selection.start && isNumber(editor.selection.start.line) ? { - startLineNumber: editor.selection.start.line, - startColumn: isNumber(editor.selection.start.column) ? editor.selection.start.column : 1, - endLineNumber: isNumber(editor.selection.end.line) ? editor.selection.end.line : undefined, - endColumn: isNumber(editor.selection.end.line) ? (isNumber(editor.selection.end.column) ? editor.selection.end.column : 1) : undefined, + // TODO@bpasero remove me eventually + const editor2 = editor as any; + const legacySelection = editor2.selection && editor2.selection.start && isNumber(editor2.selection.start.line) ? { + startLineNumber: editor2.selection.start.line, + startColumn: isNumber(editor2.selection.start.column) ? editor2.selection.start.column : 1, + endLineNumber: isNumber(editor2.selection.end.line) ? editor2.selection.end.line : undefined, + endColumn: isNumber(editor2.selection.end.line) ? (isNumber(editor2.selection.end.column) ? editor2.selection.end.column : 1) : undefined, } : undefined; return { diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 5dd39b548a129..333c3ef2e3464 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -17,7 +17,7 @@ import type { TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel'; import type { IProgress, IProgressCompositeOptions, IProgressDialogOptions, IProgressNotificationOptions, IProgressOptions, IProgressStep, IProgressWindowOptions } from 'vs/platform/progress/common/progress'; import type { IObservableValue } from 'vs/base/common/observableValue'; import type { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; -import type { IEditorOptions } from 'vs/platform/editor/common/editor'; +import type { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import type { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; /** @@ -597,22 +597,6 @@ export interface IDefaultView { readonly id: string; } -/** - * @deprecated use `IDefaultEditor.options` instead - */ -export interface IPosition { - readonly line: number; - readonly column: number; -} - -/** - * @deprecated use `IDefaultEditor.options` instead - */ -export interface IRange { - readonly start: IPosition; - readonly end: IPosition; -} - export interface IDefaultEditor { /** @@ -635,18 +619,13 @@ export interface IDefaultEditor { * Optional extra options like which editor * to use or which text to select. */ - readonly options?: IEditorOptions; + readonly options?: ITextEditorOptions; /** * Will not open an untitled editor in case * the resource does not exist. */ readonly openOnlyIfExists?: boolean; - - /** - * @deprecated use `options` instead - */ - readonly selection?: IRange; } export interface IDefaultLayout { diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index e4b85b54623ba..71c1e74f838d2 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -178,7 +178,7 @@ import 'vs/workbench/contrib/offline/browser/offline.contribution'; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! import { create, commands, env, window, workspace, logger } from 'vs/workbench/browser/web.factory'; -import { IWorkbench, ICommand, ICommonTelemetryPropertiesResolver, IDefaultEditor, IDefaultLayout, IDefaultView, IDevelopmentOptions, IExternalUriResolver, IExternalURLOpener, IHomeIndicator, IInitialColorTheme, IPosition, IProductQualityChangeHandler, IRange, IResourceUriProvider, ISettingsSyncOptions, IShowPortCandidate, ITunnel, ITunnelFactory, ITunnelOptions, ITunnelProvider, IWelcomeBanner, IWelcomeBannerAction, IWindowIndicator, IWorkbenchConstructionOptions, Menu } from 'vs/workbench/browser/web.api'; +import { IWorkbench, ICommand, ICommonTelemetryPropertiesResolver, IDefaultEditor, IDefaultLayout, IDefaultView, IDevelopmentOptions, IExternalUriResolver, IExternalURLOpener, IHomeIndicator, IInitialColorTheme, IProductQualityChangeHandler, IResourceUriProvider, ISettingsSyncOptions, IShowPortCandidate, ITunnel, ITunnelFactory, ITunnelOptions, ITunnelProvider, IWelcomeBanner, IWelcomeBannerAction, IWindowIndicator, IWorkbenchConstructionOptions, Menu } from 'vs/workbench/browser/web.api'; import { UriComponents, URI } from 'vs/base/common/uri'; import { IWebSocketFactory, IWebSocket } from 'vs/platform/remote/browser/browserSocketFactory'; import { Event, Emitter } from 'vs/base/common/event'; @@ -280,8 +280,6 @@ export { EditorGroupLayout, GroupOrientation, GroupLayoutArgument, - IPosition, - IRange as ISelection, // Env env,