From 0be2e1cc5e50bd9643e3b3dd573a0d3367e3f28f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Mar 2019 12:11:20 +0100 Subject: [PATCH] debt - extract layout from workbench --- src/vs/workbench/browser/layout.ts | 1143 ++++++++++++++++++++++++ src/vs/workbench/browser/workbench.ts | 1159 +------------------------ 2 files changed, 1163 insertions(+), 1139 deletions(-) create mode 100644 src/vs/workbench/browser/layout.ts diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts new file mode 100644 index 00000000000..52ed06bf0a2 --- /dev/null +++ b/src/vs/workbench/browser/layout.ts @@ -0,0 +1,1143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, position, size } from 'vs/base/browser/dom'; +import { onDidChangeFullscreen, isFullscreen, getZoomFactor } from 'vs/base/browser/browser'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { IUntitledResourceInput, IResourceDiffInput } from 'vs/workbench/common/editor'; +import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; +import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; +import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; +import { Position, Parts, IWorkbenchLayoutService, ILayoutOptions } from 'vs/workbench/services/layout/browser/layoutService'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ITitleService } from 'vs/workbench/services/title/common/titleService'; +import { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { LifecyclePhase, StartupKind, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWindowService, IPath, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { Sizing, Direction, Grid, View } from 'vs/base/browser/ui/grid/grid'; +import { WorkbenchLegacyLayout } from 'vs/workbench/browser/legacyLayout'; +import { IDimension } from 'vs/platform/layout/browser/layoutService'; +import { Part } from 'vs/workbench/browser/part'; +import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; +import { coalesce } from 'vs/base/common/arrays'; + +enum Settings { + MENUBAR_VISIBLE = 'window.menuBarVisibility', + ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', + STATUSBAR_VISIBLE = 'workbench.statusBar.visible', + + SIDEBAR_POSITION = 'workbench.sideBar.location', + PANEL_POSITION = 'workbench.panel.defaultLocation', + + ZEN_MODE_RESTORE = 'zenMode.restore' +} + +enum Storage { + SIDEBAR_HIDDEN = 'workbench.sidebar.hidden', + + PANEL_HIDDEN = 'workbench.panel.hidden', + PANEL_POSITION = 'workbench.panel.location', + + ZEN_MODE_ENABLED = 'workbench.zenmode.active', + CENTERED_LAYOUT_ENABLED = 'workbench.centerededitorlayout.active', +} + +export class Layout extends Disposable implements IWorkbenchLayoutService { + + _serviceBrand: ServiceIdentifier; + + private readonly _onTitleBarVisibilityChange: Emitter = this._register(new Emitter()); + get onTitleBarVisibilityChange(): Event { return this._onTitleBarVisibilityChange.event; } + + private readonly _onZenMode: Emitter = this._register(new Emitter()); + get onZenModeChange(): Event { return this._onZenMode.event; } + + private readonly _onLayout = this._register(new Emitter()); + get onLayout(): Event { return this._onLayout.event; } + + private _dimension: IDimension; + get dimension(): IDimension { return this._dimension; } + + private _container: HTMLElement = document.createElement('div'); + get container(): HTMLElement { return this._container; } + + get hasWorkbench(): boolean { return true; } + + private parts: Map = new Map(); + + private workbenchGrid: Grid | WorkbenchLegacyLayout; + + private disposed: boolean; + + private titleBarPartView: View; + private activityBarPartView: View; + private sideBarPartView: View; + private panelPartView: View; + private editorPartView: View; + private statusBarPartView: View; + + private environmentService: IEnvironmentService; + private configurationService: IConfigurationService; + private lifecycleService: ILifecycleService; + private storageService: IStorageService; + private windowService: IWindowService; + private editorService: IEditorService; + private editorGroupService: IEditorGroupsService; + private panelService: IPanelService; + private titleService: ITitleService; + private viewletService: IViewletService; + private contextService: IWorkspaceContextService; + private backupFileService: IBackupFileService; + + protected readonly state = { + fullscreen: false, + + menuBar: { + visibility: 'default' as MenuBarVisibility, + toggled: false + }, + + activityBar: { + hidden: false + }, + + sideBar: { + hidden: false, + position: Position.LEFT, + width: 300, + viewletToRestore: undefined as string | undefined + }, + + editor: { + hidden: false, + centered: false, + restoreCentered: false, + restoreEditors: false, + editorsToOpen: [] as Promise | IResourceEditor[] + }, + + panel: { + hidden: false, + position: Position.BOTTOM, + height: 350, + width: 350, + panelToRestore: undefined as string | undefined + }, + + statusBar: { + hidden: false + }, + + zenMode: { + active: false, + restore: false, + transitionedToFullScreen: false, + transitionedToCenteredEditorLayout: false, + wasSideBarVisible: false, + wasPanelVisible: false, + transitionDisposeables: [] as IDisposable[] + } + }; + + constructor( + protected readonly parent: HTMLElement + ) { + super(); + } + + protected initLayout(accessor: ServicesAccessor): void { + + // Services + this.environmentService = accessor.get(IEnvironmentService); + this.configurationService = accessor.get(IConfigurationService); + this.lifecycleService = accessor.get(ILifecycleService); + this.windowService = accessor.get(IWindowService); + this.contextService = accessor.get(IWorkspaceContextService); + this.storageService = accessor.get(IStorageService); + + // Parts + this.editorService = accessor.get(IEditorService); + this.editorGroupService = accessor.get(IEditorGroupsService); + this.panelService = accessor.get(IPanelService); + this.viewletService = accessor.get(IViewletService); + this.titleService = accessor.get(ITitleService); + accessor.get(IStatusbarService); // not used, but called to ensure instantiated + accessor.get(IActivityBarService); // not used, but called to ensure instantiated + + // Listeners + this.registerLayoutListeners(); + + // State + this.initLayoutState(accessor.get(ILifecycleService)); + } + + protected getPart(key: Parts): Part { + const part = this.parts.get(key); + if (!part) { + throw new Error(`Unknown part ${key}`); + } + + return part; + } + + private registerLayoutListeners(): void { + + // Storage + this._register(this.storageService.onWillSaveState(e => this.saveLayoutState(e))); + + // Restore editor if hidden and it changes + this._register(this.editorService.onDidVisibleEditorsChange(() => this.setEditorHidden(false))); + this._register(this.editorGroupService.onDidActivateGroup(() => this.setEditorHidden(false))); + + // Configuration changes + this._register(this.configurationService.onDidChangeConfiguration(() => this.doUpdateLayoutConfiguration())); + + // Fullscreen changes + this._register(onDidChangeFullscreen(() => this.onFullscreenChanged())); + + // Group changes + this._register(this.editorGroupService.onDidAddGroup(() => this.centerEditorLayout(this.state.editor.centered))); + this._register(this.editorGroupService.onDidRemoveGroup(() => this.centerEditorLayout(this.state.editor.centered))); + + // Prevent workbench from scrolling #55456 + this._register(addDisposableListener(this.container, EventType.SCROLL, () => this.container.scrollTop = 0)); + + // Menubar visibility changes + if ((isWindows || isLinux) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); + } + } + + private onMenubarToggled(visible: boolean) { + if (visible !== this.state.menuBar.toggled) { + this.state.menuBar.toggled = visible; + + if (this.state.fullscreen && (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default')) { + this._onTitleBarVisibilityChange.fire(); + this.layout(); + } + } + } + + private onFullscreenChanged(): void { + this.state.fullscreen = isFullscreen(); + + // Apply as CSS class + if (this.state.fullscreen) { + addClass(this.container, 'fullscreen'); + } else { + removeClass(this.container, 'fullscreen'); + + if (this.state.zenMode.transitionedToFullScreen && this.state.zenMode.active) { + this.toggleZenMode(); + } + } + + // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update + if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this._onTitleBarVisibilityChange.fire(); + this.layout(); // handle title bar when fullscreen changes + } + } + + private doUpdateLayoutConfiguration(skipLayout?: boolean): void { + + // Sidebar position + const newSidebarPositionValue = this.configurationService.getValue(Settings.SIDEBAR_POSITION); + const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT; + if (newSidebarPosition !== this.getSideBarPosition()) { + this.setSideBarPosition(newSidebarPosition); + } + + // Panel position + this.updatePanelPosition(); + + if (!this.state.zenMode.active) { + + // Statusbar visibility + const newStatusbarHiddenValue = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); + if (newStatusbarHiddenValue !== this.state.statusBar.hidden) { + this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout); + } + + // Activitybar visibility + const newActivityBarHiddenValue = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); + if (newActivityBarHiddenValue !== this.state.activityBar.hidden) { + this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout); + } + } + + // Menubar visibility + const newMenubarVisibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); + this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); + } + + private setSideBarPosition(position: Position): void { + const activityBar = this.getPart(Parts.ACTIVITYBAR_PART); + const sideBar = this.getPart(Parts.SIDEBAR_PART); + const wasHidden = this.state.sideBar.hidden; + + if (this.state.sideBar.hidden) { + this.setSideBarHidden(false, true /* Skip Layout */); + } + + const newPositionValue = (position === Position.LEFT) ? 'left' : 'right'; + const oldPositionValue = (this.state.sideBar.position === Position.LEFT) ? 'left' : 'right'; + this.state.sideBar.position = position; + + // Adjust CSS + removeClass(activityBar.getContainer(), oldPositionValue); + removeClass(sideBar.getContainer(), oldPositionValue); + addClass(activityBar.getContainer(), newPositionValue); + addClass(sideBar.getContainer(), newPositionValue); + + // Update Styles + activityBar.updateStyles(); + sideBar.updateStyles(); + + // Layout + if (this.workbenchGrid instanceof Grid) { + if (!wasHidden) { + this.state.sideBar.width = this.workbenchGrid.getViewSize(this.sideBarPartView); + } + + this.workbenchGrid.removeView(this.sideBarPartView); + this.workbenchGrid.removeView(this.activityBarPartView); + + if (!this.state.panel.hidden && this.state.panel.position === Position.BOTTOM) { + this.workbenchGrid.removeView(this.panelPartView); + } + + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + + private initLayoutState(lifecycleService: ILifecycleService): void { + + // Fullscreen + this.state.fullscreen = isFullscreen(); + + // Menubar visibility + this.state.menuBar.visibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); + + // Activity bar visibility + this.state.activityBar.hidden = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); + + // Sidebar visibility + this.state.sideBar.hidden = this.storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); + + // Sidebar position + this.state.sideBar.position = (this.configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; + + // Sidebar viewlet + if (!this.state.sideBar.hidden) { + + // Only restore last viewlet if window was reloaded or we are in development mode + let viewletToRestore: string; + if (!this.environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) { + viewletToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, this.viewletService.getDefaultViewletId()); + } else { + viewletToRestore = this.viewletService.getDefaultViewletId(); + } + + if (viewletToRestore) { + this.state.sideBar.viewletToRestore = viewletToRestore; + } else { + this.state.sideBar.hidden = true; // we hide sidebar if there is no viewlet to restore + } + } + + // Editor centered layout + this.state.editor.restoreCentered = this.storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); + + // Editors to open + this.state.editor.editorsToOpen = this.resolveEditorsToOpen(); + + // Panel visibility + this.state.panel.hidden = this.storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); + + // Panel position + this.updatePanelPosition(); + + // Panel to restore + if (!this.state.panel.hidden) { + const panelRegistry = Registry.as(PanelExtensions.Panels); + + let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); + if (!panelRegistry.hasPanel(panelToRestore)) { + panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown + } + + if (panelToRestore) { + this.state.panel.panelToRestore = panelToRestore; + } else { + this.state.panel.hidden = true; // we hide panel if there is no panel to restore + } + } + + // Statusbar visibility + this.state.statusBar.hidden = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); + + // Zen mode enablement + this.state.zenMode.restore = this.storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && this.configurationService.getValue(Settings.ZEN_MODE_RESTORE); + } + + private resolveEditorsToOpen(): Promise | IResourceEditor[] { + const configuration = this.windowService.getConfiguration(); + const hasInitialFilesToOpen = this.hasInitialFilesToOpen(); + + // Only restore editors if we are not instructed to open files initially + this.state.editor.restoreEditors = !hasInitialFilesToOpen; + + // Files to open, diff or create + if (hasInitialFilesToOpen) { + + // Files to diff is exclusive + const filesToDiff = this.toInputs(configuration.filesToDiff, false); + if (filesToDiff && filesToDiff.length === 2) { + return [{ + leftResource: filesToDiff[0].resource, + rightResource: filesToDiff[1].resource, + options: { pinned: true }, + forceFile: true + }]; + } + + const filesToCreate = this.toInputs(configuration.filesToCreate, true); + const filesToOpen = this.toInputs(configuration.filesToOpen, false); + + // Otherwise: Open/Create files + return [...filesToOpen, ...filesToCreate]; + } + + // Empty workbench + else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.configurationService.inspect('workbench.startupEditor').value === 'newUntitledFile') { + const isEmpty = this.editorGroupService.count === 1 && this.editorGroupService.activeGroup.count === 0; + if (!isEmpty) { + return []; // do not open any empty untitled file if we restored editors from previous session + } + + return this.backupFileService.hasBackups().then(hasBackups => { + if (hasBackups) { + return []; // do not open any empty untitled file if we have backups to restore + } + + return [{}]; + }); + } + + return []; + } + + private hasInitialFilesToOpen(): boolean { + const configuration = this.windowService.getConfiguration(); + + return !!( + (configuration.filesToCreate && configuration.filesToCreate.length > 0) || + (configuration.filesToOpen && configuration.filesToOpen.length > 0) || + (configuration.filesToDiff && configuration.filesToDiff.length > 0)); + } + + private toInputs(paths: IPath[] | undefined, isNew: boolean): Array { + if (!paths || !paths.length) { + return []; + } + + return coalesce(paths.map(p => { + const resource = p.fileUri; + if (!resource) { + return undefined; + } + + let input: IResourceInput | IUntitledResourceInput; + if (isNew) { + input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput; + } else { + input = { resource, options: { pinned: true }, forceFile: true } as IResourceInput; + } + + if (!isNew && typeof p.lineNumber === 'number') { + input.options!.selection = { + startLineNumber: p.lineNumber, + startColumn: p.columnNumber || 1 + }; + } + + return input; + })); + } + + private updatePanelPosition() { + const defaultPanelPosition = this.configurationService.getValue(Settings.PANEL_POSITION); + const panelPosition = this.storageService.get(Storage.PANEL_POSITION, StorageScope.WORKSPACE, defaultPanelPosition); + + this.state.panel.position = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM; + } + + registerPart(part: Part): void { + this.parts.set(part.getId(), part); + } + + isRestored(): boolean { + return this.lifecycleService.phase >= LifecyclePhase.Restored; + } + + hasFocus(part: Parts): boolean { + const activeElement = document.activeElement; + if (!activeElement) { + return false; + } + + const container = this.getContainer(part); + + return isAncestor(activeElement, container); + } + + getContainer(part: Parts): HTMLElement { + switch (part) { + case Parts.TITLEBAR_PART: + return this.getPart(Parts.TITLEBAR_PART).getContainer(); + case Parts.ACTIVITYBAR_PART: + return this.getPart(Parts.ACTIVITYBAR_PART).getContainer(); + case Parts.SIDEBAR_PART: + return this.getPart(Parts.SIDEBAR_PART).getContainer(); + case Parts.PANEL_PART: + return this.getPart(Parts.PANEL_PART).getContainer(); + case Parts.EDITOR_PART: + return this.getPart(Parts.EDITOR_PART).getContainer(); + case Parts.STATUSBAR_PART: + return this.getPart(Parts.STATUSBAR_PART).getContainer(); + } + } + + isVisible(part: Parts): boolean { + switch (part) { + case Parts.TITLEBAR_PART: + if (getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + return false; + } else if (!this.state.fullscreen) { + return true; + } else if (isMacintosh) { + return false; + } else if (this.state.menuBar.visibility === 'visible') { + return true; + } else if (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default') { + return this.state.menuBar.toggled; + } + + return false; + case Parts.SIDEBAR_PART: + return !this.state.sideBar.hidden; + case Parts.PANEL_PART: + return !this.state.panel.hidden; + case Parts.STATUSBAR_PART: + return !this.state.statusBar.hidden; + case Parts.ACTIVITYBAR_PART: + return !this.state.activityBar.hidden; + case Parts.EDITOR_PART: + return this.workbenchGrid instanceof Grid ? !this.state.editor.hidden : true; + } + + return true; // any other part cannot be hidden + } + + getTitleBarOffset(): number { + let offset = 0; + if (this.isVisible(Parts.TITLEBAR_PART)) { + if (this.workbenchGrid instanceof Grid) { + offset = this.getPart(Parts.TITLEBAR_PART).maximumHeight; + } else { + offset = this.workbenchGrid.partLayoutInfo.titlebar.height; + + if (isMacintosh || this.state.menuBar.visibility === 'hidden') { + offset /= getZoomFactor(); + } + } + } + + return offset; + } + + getWorkbenchElement(): HTMLElement { + return this.container; + } + + toggleZenMode(skipLayout?: boolean, restoring = false): void { + this.state.zenMode.active = !this.state.zenMode.active; + this.state.zenMode.transitionDisposeables = dispose(this.state.zenMode.transitionDisposeables); + + const setLineNumbers = (lineNumbers: any) => this.editorService.visibleTextEditorWidgets.forEach(editor => editor.updateOptions({ lineNumbers })); + + // Check if zen mode transitioned to full screen and if now we are out of zen mode + // -> we need to go out of full screen (same goes for the centered editor layout) + let toggleFullScreen = false; + + // Zen Mode Active + if (this.state.zenMode.active) { + const config: { + fullScreen: boolean; + centerLayout: boolean; + hideTabs: boolean; + hideActivityBar: boolean; + hideStatusBar: boolean; + hideLineNumbers: boolean; + } = this.configurationService.getValue('zenMode'); + + toggleFullScreen = !this.state.fullscreen && config.fullScreen; + + this.state.zenMode.transitionedToFullScreen = restoring ? config.fullScreen : toggleFullScreen; + this.state.zenMode.transitionedToCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout; + this.state.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART); + this.state.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART); + + this.setPanelHidden(true, true); + this.setSideBarHidden(true, true); + + if (config.hideActivityBar) { + this.setActivityBarHidden(true, true); + } + + if (config.hideStatusBar) { + this.setStatusBarHidden(true, true); + } + + if (config.hideLineNumbers) { + setLineNumbers('off'); + this.state.zenMode.transitionDisposeables.push(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); + } + + if (config.hideTabs && this.editorGroupService.partOptions.showTabs) { + this.state.zenMode.transitionDisposeables.push(this.editorGroupService.enforcePartOptions({ showTabs: false })); + } + + if (config.centerLayout) { + this.centerEditorLayout(true, true); + } + } + + // Zen Mode Inactive + else { + if (this.state.zenMode.wasPanelVisible) { + this.setPanelHidden(false, true); + } + + if (this.state.zenMode.wasSideBarVisible) { + this.setSideBarHidden(false, true); + } + + if (this.state.zenMode.transitionedToCenteredEditorLayout) { + this.centerEditorLayout(false, true); + } + + setLineNumbers(this.configurationService.getValue('editor.lineNumbers')); + + // Status bar and activity bar visibility come from settings -> update their visibility. + this.doUpdateLayoutConfiguration(true); + + this.editorGroupService.activeGroup.focus(); + + toggleFullScreen = this.state.zenMode.transitionedToFullScreen && this.state.fullscreen; + } + + if (!skipLayout) { + this.layout(); + } + + if (toggleFullScreen) { + this.windowService.toggleFullScreen(); + } + + // Event + this._onZenMode.fire(this.state.zenMode.active); + } + + private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.statusBar.hidden = hidden; + + // Adjust CSS + if (hidden) { + addClass(this.container, 'nostatusbar'); + } else { + removeClass(this.container, 'nostatusbar'); + } + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + } + + protected createWorkbenchLayout(instantiationService: IInstantiationService): void { + const titleBar = this.getPart(Parts.TITLEBAR_PART); + const editorPart = this.getPart(Parts.EDITOR_PART); + const activityBar = this.getPart(Parts.ACTIVITYBAR_PART); + const panelPart = this.getPart(Parts.PANEL_PART); + const sideBar = this.getPart(Parts.SIDEBAR_PART); + const statusBar = this.getPart(Parts.STATUSBAR_PART); + + if (this.configurationService.getValue('workbench.useExperimentalGridLayout')) { + + // Create view wrappers for all parts + this.titleBarPartView = new View(titleBar); + this.sideBarPartView = new View(sideBar); + this.activityBarPartView = new View(activityBar); + this.editorPartView = new View(editorPart); + this.panelPartView = new View(panelPart); + this.statusBarPartView = new View(statusBar); + + this.workbenchGrid = new Grid(this.editorPartView, { proportionalLayout: false }); + + this.container.prepend(this.workbenchGrid.element); + } else { + this.workbenchGrid = instantiationService.createInstance( + WorkbenchLegacyLayout, + this.parent, + this.container, + { + titlebar: titleBar, + activitybar: activityBar, + editor: editorPart, + sidebar: sideBar, + panel: panelPart, + statusbar: statusBar, + } + ); + } + } + + layout(options?: ILayoutOptions): void { + if (!this.disposed) { + this._dimension = getClientArea(this.parent); + + if (this.workbenchGrid instanceof Grid) { + position(this.container, 0, 0, 0, 0, 'relative'); + size(this.container, this._dimension.width, this._dimension.height); + + // Layout the grid widget + this.workbenchGrid.layout(this._dimension.width, this._dimension.height); + + // Layout grid views + this.layoutGrid(); + } else { + this.workbenchGrid.layout(options); + } + + // Emit as event + this._onLayout.fire(this._dimension); + } + } + + private layoutGrid(): void { + if (!(this.workbenchGrid instanceof Grid)) { + return; + } + + let panelInGrid = this.workbenchGrid.hasView(this.panelPartView); + let sidebarInGrid = this.workbenchGrid.hasView(this.sideBarPartView); + let activityBarInGrid = this.workbenchGrid.hasView(this.activityBarPartView); + let statusBarInGrid = this.workbenchGrid.hasView(this.statusBarPartView); + let titlebarInGrid = this.workbenchGrid.hasView(this.titleBarPartView); + + // Add parts to grid + if (!statusBarInGrid) { + this.workbenchGrid.addView(this.statusBarPartView, Sizing.Split, this.editorPartView, Direction.Down); + statusBarInGrid = true; + } + + if (!titlebarInGrid && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + this.workbenchGrid.addView(this.titleBarPartView, Sizing.Split, this.editorPartView, Direction.Up); + titlebarInGrid = true; + } + + if (!activityBarInGrid) { + this.workbenchGrid.addView(this.activityBarPartView, Sizing.Split, panelInGrid && this.state.sideBar.position === this.state.panel.position ? this.panelPartView : this.editorPartView, this.state.sideBar.position === Position.RIGHT ? Direction.Right : Direction.Left); + activityBarInGrid = true; + } + + if (!sidebarInGrid) { + this.workbenchGrid.addView(this.sideBarPartView, this.state.sideBar.width !== undefined ? this.state.sideBar.width : Sizing.Split, this.activityBarPartView, this.state.sideBar.position === Position.LEFT ? Direction.Right : Direction.Left); + sidebarInGrid = true; + } + + if (!panelInGrid) { + this.workbenchGrid.addView(this.panelPartView, this.getPanelDimension(this.state.panel.position) !== undefined ? this.getPanelDimension(this.state.panel.position) : Sizing.Split, this.editorPartView, this.state.panel.position === Position.BOTTOM ? Direction.Down : Direction.Right); + panelInGrid = true; + } + + // Hide parts + if (this.state.panel.hidden) { + this.panelPartView.hide(); + } + + if (this.state.statusBar.hidden) { + this.statusBarPartView.hide(); + } + + if (!this.isVisible(Parts.TITLEBAR_PART)) { + this.titleBarPartView.hide(); + } + + if (this.state.activityBar.hidden) { + this.activityBarPartView.hide(); + } + + if (this.state.sideBar.hidden) { + this.sideBarPartView.hide(); + } + + if (this.state.editor.hidden) { + this.editorPartView.hide(); + } + + // Show visible parts + if (!this.state.editor.hidden) { + this.editorPartView.show(); + } + + if (!this.state.statusBar.hidden) { + this.statusBarPartView.show(); + } + + if (this.isVisible(Parts.TITLEBAR_PART)) { + this.titleBarPartView.show(); + } + + if (!this.state.activityBar.hidden) { + this.activityBarPartView.show(); + } + + if (!this.state.sideBar.hidden) { + this.sideBarPartView.show(); + } + + if (!this.state.panel.hidden) { + this.panelPartView.show(); + } + } + + private getPanelDimension(position: Position): number { + return position === Position.BOTTOM ? this.state.panel.height : this.state.panel.width; + } + + isEditorLayoutCentered(): boolean { + return this.state.editor.centered; + } + + centerEditorLayout(active: boolean, skipLayout?: boolean): void { + this.state.editor.centered = active; + + this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE); + + let smartActive = active; + if (this.editorGroupService.groups.length > 1 && this.configurationService.getValue('workbench.editor.centeredLayoutAutoResize')) { + smartActive = false; // Respect the auto resize setting - do not go into centered layout if there is more than 1 group. + } + + // Enter Centered Editor Layout + if (this.editorGroupService.isLayoutCentered() !== smartActive) { + this.editorGroupService.centerLayout(smartActive); + + if (!skipLayout) { + this.layout(); + } + } + } + + resizePart(part: Parts, sizeChange: number): void { + let view: View; + switch (part) { + case Parts.SIDEBAR_PART: + view = this.sideBarPartView; + case Parts.PANEL_PART: + view = this.panelPartView; + case Parts.EDITOR_PART: + view = this.editorPartView; + if (this.workbenchGrid instanceof Grid) { + this.workbenchGrid.resizeView(view, this.workbenchGrid.getViewSize(view) + sizeChange); + } else { + this.workbenchGrid.resizePart(part, sizeChange); + } + break; + default: + return; // Cannot resize other parts + } + } + + setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.activityBar.hidden = hidden; + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + } + + setEditorHidden(hidden: boolean, skipLayout?: boolean): void { + if (!(this.workbenchGrid instanceof Grid) || hidden === this.state.editor.hidden) { + return; + } + + this.state.editor.hidden = hidden; + + // The editor and the panel cannot be hidden at the same time + if (this.state.editor.hidden && this.state.panel.hidden) { + this.setPanelHidden(false, true); + } + + if (!skipLayout) { + this.layout(); + } + } + + setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.sideBar.hidden = hidden; + + // Adjust CSS + if (hidden) { + addClass(this.container, 'nosidebar'); + } else { + removeClass(this.container, 'nosidebar'); + } + + // If sidebar becomes hidden, also hide the current active Viewlet if any + if (hidden && this.viewletService.getActiveViewlet()) { + this.viewletService.hideActiveViewlet(); + + // Pass Focus to Editor or Panel if Sidebar is now hidden + const activePanel = this.panelService.getActivePanel(); + if (this.hasFocus(Parts.PANEL_PART) && activePanel) { + activePanel.focus(); + } else { + this.editorGroupService.activeGroup.focus(); + } + } + + // If sidebar becomes visible, show last active Viewlet or default viewlet + else if (!hidden && !this.viewletService.getActiveViewlet()) { + const viewletToOpen = this.viewletService.getLastActiveViewletId(); + if (viewletToOpen) { + const viewlet = this.viewletService.openViewlet(viewletToOpen, true); + if (!viewlet) { + this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); + } + } + } + + // Remember in settings + const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; + if (hidden !== defaultHidden) { + this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); + } + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + } + + setPanelHidden(hidden: boolean, skipLayout?: boolean): void { + this.state.panel.hidden = hidden; + + // Adjust CSS + if (hidden) { + addClass(this.container, 'nopanel'); + } else { + removeClass(this.container, 'nopanel'); + } + + // If panel part becomes hidden, also hide the current active panel if any + if (hidden && this.panelService.getActivePanel()) { + this.panelService.hideActivePanel(); + this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden + } + + // If panel part becomes visible, show last active panel or default panel + else if (!hidden && !this.panelService.getActivePanel()) { + const panelToOpen = this.panelService.getLastActivePanelId(); + if (panelToOpen) { + const focus = !skipLayout; + this.panelService.openPanel(panelToOpen, focus); + } + } + + // Remember in settings + if (!hidden) { + this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); + } + + // The editor and panel cannot be hidden at the same time + if (hidden && this.state.editor.hidden) { + this.setEditorHidden(false, true); + } + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + } + + toggleMaximizedPanel(): void { + if (this.workbenchGrid instanceof Grid) { + this.workbenchGrid.maximizeViewSize(this.panelPartView); + } else { + this.workbenchGrid.layout({ toggleMaximizedPanel: true, source: Parts.PANEL_PART }); + } + } + + isPanelMaximized(): boolean { + if (this.workbenchGrid instanceof Grid) { + try { + return this.workbenchGrid.getViewSize2(this.panelPartView).height === this.getPart(Parts.PANEL_PART).maximumHeight; + } catch (e) { + return false; + } + } else { + return this.workbenchGrid.isPanelMaximized(); + } + } + + getSideBarPosition(): Position { + return this.state.sideBar.position; + } + + setMenubarVisibility(visibility: MenuBarVisibility, skipLayout: boolean): void { + if (this.state.menuBar.visibility !== visibility) { + this.state.menuBar.visibility = visibility; + + // Layout + if (!skipLayout) { + if (this.workbenchGrid instanceof Grid) { + const dimensions = getClientArea(this.parent); + this.workbenchGrid.layout(dimensions.width, dimensions.height); + } else { + this.workbenchGrid.layout(); + } + } + } + } + + getMenubarVisibility(): MenuBarVisibility { + return this.state.menuBar.visibility; + } + + getPanelPosition(): Position { + return this.state.panel.position; + } + + setPanelPosition(position: Position): void { + const panelPart = this.getPart(Parts.PANEL_PART); + const wasHidden = this.state.panel.hidden; + + if (this.state.panel.hidden) { + this.setPanelHidden(false, true /* Skip Layout */); + } else { + this.savePanelDimension(); + } + + const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; + const oldPositionValue = (this.state.panel.position === Position.BOTTOM) ? 'bottom' : 'right'; + this.state.panel.position = position; + + function positionToString(position: Position): string { + switch (position) { + case Position.LEFT: return 'left'; + case Position.RIGHT: return 'right'; + case Position.BOTTOM: return 'bottom'; + } + } + + this.storageService.store(Storage.PANEL_POSITION, positionToString(this.state.panel.position), StorageScope.WORKSPACE); + + // Adjust CSS + removeClass(panelPart.getContainer(), oldPositionValue); + addClass(panelPart.getContainer(), newPositionValue); + + // Update Styles + panelPart.updateStyles(); + + // Layout + if (this.workbenchGrid instanceof Grid) { + if (!wasHidden) { + this.savePanelDimension(); + } + + this.workbenchGrid.removeView(this.panelPartView); + this.layout(); + } else { + this.workbenchGrid.layout(); + } + } + + private savePanelDimension(): void { + if (!(this.workbenchGrid instanceof Grid)) { + return; + } + + if (this.state.panel.position === Position.BOTTOM) { + this.state.panel.height = this.workbenchGrid.getViewSize(this.panelPartView); + } else { + this.state.panel.width = this.workbenchGrid.getViewSize(this.panelPartView); + } + } + + private saveLayoutState(e: IWillSaveStateEvent): void { + + // Zen Mode + if (this.state.zenMode.active) { + this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE); + } else { + this.storageService.remove(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE); + } + + if (e.reason === WillSaveStateReason.SHUTDOWN && this.state.zenMode.active) { + if (!this.configurationService.getValue(Settings.ZEN_MODE_RESTORE)) { + this.toggleZenMode(true); // We will not restore zen mode, need to clear all zen mode state changes + } + } + } + + dispose(): void { + super.dispose(); + + this.disposed = true; + } + + //#endregion +} diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index ec9696bcc0e..47cb92971b3 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -7,37 +7,27 @@ import 'vs/workbench/browser/style'; import { localize } from 'vs/nls'; import { setFileNameComparer } from 'vs/base/common/comparers'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event'; -import { EventType, addDisposableListener, addClasses, addClass, removeClass, isAncestor, getClientArea, position, size, removeClasses } from 'vs/base/browser/dom'; +import { addClasses, addClass, removeClasses } from 'vs/base/browser/dom'; import { runWhenIdle, IdleValue } from 'vs/base/common/async'; -import { getZoomLevel, onDidChangeFullscreen, isFullscreen, getZoomFactor } from 'vs/base/browser/browser'; +import { getZoomLevel } from 'vs/base/browser/browser'; import { mark } from 'vs/base/common/performance'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; -import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { IResourceInput } from 'vs/platform/editor/common/editor'; +import { isWindows, isLinux } from 'vs/base/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IEditorInputFactoryRegistry, Extensions as EditorExtensions, IUntitledResourceInput, IResourceDiffInput } from 'vs/workbench/common/editor'; -import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; -import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; +import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; -import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; import { getServices } from 'vs/platform/instantiation/common/extensions'; -import { Position, Parts, IWorkbenchLayoutService, ILayoutOptions } from 'vs/workbench/services/layout/browser/layoutService'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { Position, Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IFileService } from 'vs/platform/files/common/files'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ITitleService } from 'vs/workbench/services/title/common/titleService'; -import { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { LifecyclePhase, StartupKind, ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWindowService, IPath, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { LifecyclePhase, ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { NotificationService } from 'vs/workbench/services/notification/common/notificationService'; import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter'; @@ -48,50 +38,17 @@ import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/no import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { Sizing, Direction, Grid, View } from 'vs/base/browser/ui/grid/grid'; -import { WorkbenchLegacyLayout } from 'vs/workbench/browser/legacyLayout'; import { setARIAContainer } from 'vs/base/browser/ui/aria/aria'; import { restoreFontInfo, readFontInfo, saveFontInfo } from 'vs/editor/browser/config/configuration'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { ILogService } from 'vs/platform/log/common/log'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { WorkbenchContextKeysHandler } from 'vs/workbench/browser/contextkeys'; -import { IDimension } from 'vs/platform/layout/browser/layoutService'; -import { Part } from 'vs/workbench/browser/part'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; import { coalesce } from 'vs/base/common/arrays'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { Layout } from 'vs/workbench/browser/layout'; -export interface IWorkbenchOptions { - hasInitialFilesToOpen: boolean; -} - -enum Settings { - MENUBAR_VISIBLE = 'window.menuBarVisibility', - ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible', - STATUSBAR_VISIBLE = 'workbench.statusBar.visible', - - SIDEBAR_POSITION = 'workbench.sideBar.location', - PANEL_POSITION = 'workbench.panel.defaultLocation', - - FONT_ALIASING = 'workbench.fontAliasing', - ZEN_MODE_RESTORE = 'zenMode.restore' -} - -enum Storage { - SIDEBAR_HIDDEN = 'workbench.sidebar.hidden', - - PANEL_HIDDEN = 'workbench.panel.hidden', - PANEL_POSITION = 'workbench.panel.location', - - ZEN_MODE_ENABLED = 'workbench.zenmode.active', - CENTERED_LAYOUT_ENABLED = 'workbench.centerededitorlayout.active', -} - -export class Workbench extends Disposable implements IWorkbenchLayoutService { - - _serviceBrand: ServiceIdentifier; +export class Workbench extends Layout { private readonly _onShutdown = this._register(new Emitter()); get onShutdown(): Event { return this._onShutdown.event; } @@ -99,14 +56,12 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { private readonly _onWillShutdown = this._register(new Emitter()); get onWillShutdown(): Event { return this._onWillShutdown.event; } - private workbench: HTMLElement = document.createElement('div'); - constructor( - private readonly parent: HTMLElement, + parent: HTMLElement, private readonly serviceCollection: ServiceCollection, logService: ILogService ) { - super(); + super(parent); this.registerErrorHandler(logService); } @@ -289,7 +244,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto'; private setFontAliasing(configurationService: IConfigurationService) { - const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>(Settings.FONT_ALIASING); + const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>('workbench.fontAliasing'); if (this.fontAliasing === aliasing) { return; } @@ -298,11 +253,11 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { // Remove all const fontAliasingValues: (typeof aliasing)[] = ['antialiased', 'none', 'auto']; - removeClasses(this.workbench, ...fontAliasingValues.map(value => `monaco-font-aliasing-${value}`)); + removeClasses(this.container, ...fontAliasingValues.map(value => `monaco-font-aliasing-${value}`)); // Add specific if (fontAliasingValues.some(option => option === aliasing)) { - addClass(this.workbench, `monaco-font-aliasing-${aliasing}`); + addClass(this.container, `monaco-font-aliasing-${aliasing}`); } } @@ -319,7 +274,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { this.state.fullscreen ? 'fullscreen' : undefined ]); - addClasses(this.workbench, ...workbenchClasses); + addClasses(this.container, ...workbenchClasses); addClasses(document.body, platformClass); // used by our fonts // Apply font aliasing @@ -344,7 +299,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { // TODO@Ben cleanup once moved to grid // Insert all workbench parts at the beginning. Issue #52531 // This is primarily for the title bar to allow overriding -webkit-app-region - this.workbench.insertBefore(partContainer, this.workbench.lastChild); + this.container.insertBefore(partContainer, this.container.lastChild); } this.getPart(id).create(partContainer, options); @@ -354,7 +309,7 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { this.createNotificationsHandlers(instantiationService, notificationService); // Add Workbench to DOM - this.parent.appendChild(this.workbench); + this.parent.appendChild(this.container); } private createPart(id: string, role: string, classes: string[]): HTMLElement { @@ -369,8 +324,8 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { private createNotificationsHandlers(instantiationService: IInstantiationService, notificationService: NotificationService): void { // Instantiate Notification components - const notificationsCenter = this._register(instantiationService.createInstance(NotificationsCenter, this.workbench, notificationService.model)); - const notificationsToasts = this._register(instantiationService.createInstance(NotificationsToasts, this.workbench, notificationService.model)); + const notificationsCenter = this._register(instantiationService.createInstance(NotificationsCenter, this.container, notificationService.model)); + const notificationsToasts = this._register(instantiationService.createInstance(NotificationsToasts, this.container, notificationService.model)); this._register(instantiationService.createInstance(NotificationsAlerts, notificationService.model)); const notificationsStatus = instantiationService.createInstance(NotificationsStatus, notificationService.model); @@ -466,1078 +421,4 @@ export class Workbench extends Disposable implements IWorkbenchLayoutService { mark('didStartWorkbench'); }); } - - //#region ILayoutService - - private readonly _onTitleBarVisibilityChange: Emitter = this._register(new Emitter()); - get onTitleBarVisibilityChange(): Event { return this._onTitleBarVisibilityChange.event; } - - private readonly _onZenMode: Emitter = this._register(new Emitter()); - get onZenModeChange(): Event { return this._onZenMode.event; } - - private readonly _onLayout = this._register(new Emitter()); - get onLayout(): Event { return this._onLayout.event; } - - private _dimension: IDimension; - get dimension(): IDimension { return this._dimension; } - - get container(): HTMLElement { return this.workbench; } - - get hasWorkbench(): boolean { return true; } - - private parts: Map = new Map(); - - private workbenchGrid: Grid | WorkbenchLegacyLayout; - - private disposed: boolean; - - private titleBarPartView: View; - private activityBarPartView: View; - private sideBarPartView: View; - private panelPartView: View; - private editorPartView: View; - private statusBarPartView: View; - - private environmentService: IEnvironmentService; - private configurationService: IConfigurationService; - private lifecycleService: ILifecycleService; - private storageService: IStorageService; - private windowService: IWindowService; - private editorService: IEditorService; - private editorGroupService: IEditorGroupsService; - private panelService: IPanelService; - private titleService: ITitleService; - private viewletService: IViewletService; - private contextService: IWorkspaceContextService; - private backupFileService: IBackupFileService; - - private readonly state = { - fullscreen: false, - - menuBar: { - visibility: 'default' as MenuBarVisibility, - toggled: false - }, - - activityBar: { - hidden: false - }, - - sideBar: { - hidden: false, - position: Position.LEFT, - width: 300, - viewletToRestore: undefined as string | undefined - }, - - editor: { - hidden: false, - centered: false, - restoreCentered: false, - restoreEditors: false, - editorsToOpen: [] as Promise | IResourceEditor[] - }, - - panel: { - hidden: false, - position: Position.BOTTOM, - height: 350, - width: 350, - panelToRestore: undefined as string | undefined - }, - - statusBar: { - hidden: false - }, - - zenMode: { - active: false, - restore: false, - transitionedToFullScreen: false, - transitionedToCenteredEditorLayout: false, - wasSideBarVisible: false, - wasPanelVisible: false, - transitionDisposeables: [] as IDisposable[] - } - }; - - private initLayout(accessor: ServicesAccessor) { - - // Services - this.environmentService = accessor.get(IEnvironmentService); - this.configurationService = accessor.get(IConfigurationService); - this.lifecycleService = accessor.get(ILifecycleService); - this.windowService = accessor.get(IWindowService); - this.contextService = accessor.get(IWorkspaceContextService); - this.storageService = accessor.get(IStorageService); - - // Parts - this.editorService = accessor.get(IEditorService); - this.editorGroupService = accessor.get(IEditorGroupsService); - this.panelService = accessor.get(IPanelService); - this.viewletService = accessor.get(IViewletService); - this.titleService = accessor.get(ITitleService); - accessor.get(IStatusbarService); // not used, but called to ensure instantiated - accessor.get(IActivityBarService); // not used, but called to ensure instantiated - - // Listeners - this.registerLayoutListeners(); - - // State - this.initLayoutState(accessor.get(ILifecycleService)); - } - - private registerLayoutListeners(): void { - - // Storage - this._register(this.storageService.onWillSaveState(e => this.saveLayoutState(e))); - - // Restore editor if hidden and it changes - this._register(this.editorService.onDidVisibleEditorsChange(() => this.setEditorHidden(false))); - this._register(this.editorGroupService.onDidActivateGroup(() => this.setEditorHidden(false))); - - // Configuration changes - this._register(this.configurationService.onDidChangeConfiguration(() => this.doUpdateLayoutConfiguration())); - - // Fullscreen changes - this._register(onDidChangeFullscreen(() => this.onFullscreenChanged())); - - // Group changes - this._register(this.editorGroupService.onDidAddGroup(() => this.centerEditorLayout(this.state.editor.centered))); - this._register(this.editorGroupService.onDidRemoveGroup(() => this.centerEditorLayout(this.state.editor.centered))); - - // Prevent workbench from scrolling #55456 - this._register(addDisposableListener(this.workbench, EventType.SCROLL, () => this.workbench.scrollTop = 0)); - - // Menubar visibility changes - if ((isWindows || isLinux) && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { - this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); - } - } - - private onMenubarToggled(visible: boolean) { - if (visible !== this.state.menuBar.toggled) { - this.state.menuBar.toggled = visible; - - if (this.state.fullscreen && (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default')) { - this._onTitleBarVisibilityChange.fire(); - this.layout(); - } - } - } - - private onFullscreenChanged(): void { - this.state.fullscreen = isFullscreen(); - - // Apply as CSS class - if (this.state.fullscreen) { - addClass(this.workbench, 'fullscreen'); - } else { - removeClass(this.workbench, 'fullscreen'); - - if (this.state.zenMode.transitionedToFullScreen && this.state.zenMode.active) { - this.toggleZenMode(); - } - } - - // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { - this._onTitleBarVisibilityChange.fire(); - this.layout(); // handle title bar when fullscreen changes - } - } - - private doUpdateLayoutConfiguration(skipLayout?: boolean): void { - - // Sidebar position - const newSidebarPositionValue = this.configurationService.getValue(Settings.SIDEBAR_POSITION); - const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT; - if (newSidebarPosition !== this.getSideBarPosition()) { - this.setSideBarPosition(newSidebarPosition); - } - - // Panel position - this.updatePanelPosition(); - - if (!this.state.zenMode.active) { - - // Statusbar visibility - const newStatusbarHiddenValue = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); - if (newStatusbarHiddenValue !== this.state.statusBar.hidden) { - this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout); - } - - // Activitybar visibility - const newActivityBarHiddenValue = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); - if (newActivityBarHiddenValue !== this.state.activityBar.hidden) { - this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout); - } - } - - // Menubar visibility - const newMenubarVisibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); - this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); - } - - private setSideBarPosition(position: Position): void { - const activityBar = this.getPart(Parts.ACTIVITYBAR_PART); - const sideBar = this.getPart(Parts.SIDEBAR_PART); - const wasHidden = this.state.sideBar.hidden; - - if (this.state.sideBar.hidden) { - this.setSideBarHidden(false, true /* Skip Layout */); - } - - const newPositionValue = (position === Position.LEFT) ? 'left' : 'right'; - const oldPositionValue = (this.state.sideBar.position === Position.LEFT) ? 'left' : 'right'; - this.state.sideBar.position = position; - - // Adjust CSS - removeClass(activityBar.getContainer(), oldPositionValue); - removeClass(sideBar.getContainer(), oldPositionValue); - addClass(activityBar.getContainer(), newPositionValue); - addClass(sideBar.getContainer(), newPositionValue); - - // Update Styles - activityBar.updateStyles(); - sideBar.updateStyles(); - - // Layout - if (this.workbenchGrid instanceof Grid) { - if (!wasHidden) { - this.state.sideBar.width = this.workbenchGrid.getViewSize(this.sideBarPartView); - } - - this.workbenchGrid.removeView(this.sideBarPartView); - this.workbenchGrid.removeView(this.activityBarPartView); - - if (!this.state.panel.hidden && this.state.panel.position === Position.BOTTOM) { - this.workbenchGrid.removeView(this.panelPartView); - } - - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - - private initLayoutState(lifecycleService: ILifecycleService): void { - - // Fullscreen - this.state.fullscreen = isFullscreen(); - - // Menubar visibility - this.state.menuBar.visibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); - - // Activity bar visibility - this.state.activityBar.hidden = !this.configurationService.getValue(Settings.ACTIVITYBAR_VISIBLE); - - // Sidebar visibility - this.state.sideBar.hidden = this.storageService.getBoolean(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY); - - // Sidebar position - this.state.sideBar.position = (this.configurationService.getValue(Settings.SIDEBAR_POSITION) === 'right') ? Position.RIGHT : Position.LEFT; - - // Sidebar viewlet - if (!this.state.sideBar.hidden) { - - // Only restore last viewlet if window was reloaded or we are in development mode - let viewletToRestore: string; - if (!this.environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) { - viewletToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, this.viewletService.getDefaultViewletId()); - } else { - viewletToRestore = this.viewletService.getDefaultViewletId(); - } - - if (viewletToRestore) { - this.state.sideBar.viewletToRestore = viewletToRestore; - } else { - this.state.sideBar.hidden = true; // we hide sidebar if there is no viewlet to restore - } - } - - // Editor centered layout - this.state.editor.restoreCentered = this.storageService.getBoolean(Storage.CENTERED_LAYOUT_ENABLED, StorageScope.WORKSPACE, false); - - // Editors to open - this.state.editor.editorsToOpen = this.resolveEditorsToOpen(); - - // Panel visibility - this.state.panel.hidden = this.storageService.getBoolean(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE, true); - - // Panel position - this.updatePanelPosition(); - - // Panel to restore - if (!this.state.panel.hidden) { - const panelRegistry = Registry.as(PanelExtensions.Panels); - - let panelToRestore = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); - if (!panelRegistry.hasPanel(panelToRestore)) { - panelToRestore = panelRegistry.getDefaultPanelId(); // fallback to default if panel is unknown - } - - if (panelToRestore) { - this.state.panel.panelToRestore = panelToRestore; - } else { - this.state.panel.hidden = true; // we hide panel if there is no panel to restore - } - } - - // Statusbar visibility - this.state.statusBar.hidden = !this.configurationService.getValue(Settings.STATUSBAR_VISIBLE); - - // Zen mode enablement - this.state.zenMode.restore = this.storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && this.configurationService.getValue(Settings.ZEN_MODE_RESTORE); - } - - private resolveEditorsToOpen(): Promise | IResourceEditor[] { - const configuration = this.windowService.getConfiguration(); - const hasInitialFilesToOpen = this.hasInitialFilesToOpen(); - - // Only restore editors if we are not instructed to open files initially - this.state.editor.restoreEditors = !hasInitialFilesToOpen; - - // Files to open, diff or create - if (hasInitialFilesToOpen) { - - // Files to diff is exclusive - const filesToDiff = this.toInputs(configuration.filesToDiff, false); - if (filesToDiff && filesToDiff.length === 2) { - return [{ - leftResource: filesToDiff[0].resource, - rightResource: filesToDiff[1].resource, - options: { pinned: true }, - forceFile: true - }]; - } - - const filesToCreate = this.toInputs(configuration.filesToCreate, true); - const filesToOpen = this.toInputs(configuration.filesToOpen, false); - - // Otherwise: Open/Create files - return [...filesToOpen, ...filesToCreate]; - } - - // Empty workbench - else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.configurationService.inspect('workbench.startupEditor').value === 'newUntitledFile') { - const isEmpty = this.editorGroupService.count === 1 && this.editorGroupService.activeGroup.count === 0; - if (!isEmpty) { - return []; // do not open any empty untitled file if we restored editors from previous session - } - - return this.backupFileService.hasBackups().then(hasBackups => { - if (hasBackups) { - return []; // do not open any empty untitled file if we have backups to restore - } - - return [{}]; - }); - } - - return []; - } - - private hasInitialFilesToOpen(): boolean { - const configuration = this.windowService.getConfiguration(); - - return !!( - (configuration.filesToCreate && configuration.filesToCreate.length > 0) || - (configuration.filesToOpen && configuration.filesToOpen.length > 0) || - (configuration.filesToDiff && configuration.filesToDiff.length > 0)); - } - - private toInputs(paths: IPath[] | undefined, isNew: boolean): Array { - if (!paths || !paths.length) { - return []; - } - - return coalesce(paths.map(p => { - const resource = p.fileUri; - if (!resource) { - return undefined; - } - - let input: IResourceInput | IUntitledResourceInput; - if (isNew) { - input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput; - } else { - input = { resource, options: { pinned: true }, forceFile: true } as IResourceInput; - } - - if (!isNew && typeof p.lineNumber === 'number') { - input.options!.selection = { - startLineNumber: p.lineNumber, - startColumn: p.columnNumber || 1 - }; - } - - return input; - })); - } - - private updatePanelPosition() { - const defaultPanelPosition = this.configurationService.getValue(Settings.PANEL_POSITION); - const panelPosition = this.storageService.get(Storage.PANEL_POSITION, StorageScope.WORKSPACE, defaultPanelPosition); - - this.state.panel.position = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM; - } - - registerPart(part: Part): void { - this.parts.set(part.getId(), part); - } - - isRestored(): boolean { - return this.lifecycleService.phase >= LifecyclePhase.Restored; - } - - hasFocus(part: Parts): boolean { - const activeElement = document.activeElement; - if (!activeElement) { - return false; - } - - const container = this.getContainer(part); - - return isAncestor(activeElement, container); - } - - getContainer(part: Parts): HTMLElement { - switch (part) { - case Parts.TITLEBAR_PART: - return this.getPart(Parts.TITLEBAR_PART).getContainer(); - case Parts.ACTIVITYBAR_PART: - return this.getPart(Parts.ACTIVITYBAR_PART).getContainer(); - case Parts.SIDEBAR_PART: - return this.getPart(Parts.SIDEBAR_PART).getContainer(); - case Parts.PANEL_PART: - return this.getPart(Parts.PANEL_PART).getContainer(); - case Parts.EDITOR_PART: - return this.getPart(Parts.EDITOR_PART).getContainer(); - case Parts.STATUSBAR_PART: - return this.getPart(Parts.STATUSBAR_PART).getContainer(); - } - } - - isVisible(part: Parts): boolean { - switch (part) { - case Parts.TITLEBAR_PART: - if (getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { - return false; - } else if (!this.state.fullscreen) { - return true; - } else if (isMacintosh) { - return false; - } else if (this.state.menuBar.visibility === 'visible') { - return true; - } else if (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default') { - return this.state.menuBar.toggled; - } - - return false; - case Parts.SIDEBAR_PART: - return !this.state.sideBar.hidden; - case Parts.PANEL_PART: - return !this.state.panel.hidden; - case Parts.STATUSBAR_PART: - return !this.state.statusBar.hidden; - case Parts.ACTIVITYBAR_PART: - return !this.state.activityBar.hidden; - case Parts.EDITOR_PART: - return this.workbenchGrid instanceof Grid ? !this.state.editor.hidden : true; - } - - return true; // any other part cannot be hidden - } - - getTitleBarOffset(): number { - let offset = 0; - if (this.isVisible(Parts.TITLEBAR_PART)) { - if (this.workbenchGrid instanceof Grid) { - offset = this.getPart(Parts.TITLEBAR_PART).maximumHeight; - } else { - offset = this.workbenchGrid.partLayoutInfo.titlebar.height; - - if (isMacintosh || this.state.menuBar.visibility === 'hidden') { - offset /= getZoomFactor(); - } - } - } - - return offset; - } - - getWorkbenchElement(): HTMLElement { - return this.workbench; - } - - toggleZenMode(skipLayout?: boolean, restoring = false): void { - this.state.zenMode.active = !this.state.zenMode.active; - this.state.zenMode.transitionDisposeables = dispose(this.state.zenMode.transitionDisposeables); - - const setLineNumbers = (lineNumbers: any) => this.editorService.visibleTextEditorWidgets.forEach(editor => editor.updateOptions({ lineNumbers })); - - // Check if zen mode transitioned to full screen and if now we are out of zen mode - // -> we need to go out of full screen (same goes for the centered editor layout) - let toggleFullScreen = false; - - // Zen Mode Active - if (this.state.zenMode.active) { - const config: { - fullScreen: boolean; - centerLayout: boolean; - hideTabs: boolean; - hideActivityBar: boolean; - hideStatusBar: boolean; - hideLineNumbers: boolean; - } = this.configurationService.getValue('zenMode'); - - toggleFullScreen = !this.state.fullscreen && config.fullScreen; - - this.state.zenMode.transitionedToFullScreen = restoring ? config.fullScreen : toggleFullScreen; - this.state.zenMode.transitionedToCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout; - this.state.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART); - this.state.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART); - - this.setPanelHidden(true, true); - this.setSideBarHidden(true, true); - - if (config.hideActivityBar) { - this.setActivityBarHidden(true, true); - } - - if (config.hideStatusBar) { - this.setStatusBarHidden(true, true); - } - - if (config.hideLineNumbers) { - setLineNumbers('off'); - this.state.zenMode.transitionDisposeables.push(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); - } - - if (config.hideTabs && this.editorGroupService.partOptions.showTabs) { - this.state.zenMode.transitionDisposeables.push(this.editorGroupService.enforcePartOptions({ showTabs: false })); - } - - if (config.centerLayout) { - this.centerEditorLayout(true, true); - } - } - - // Zen Mode Inactive - else { - if (this.state.zenMode.wasPanelVisible) { - this.setPanelHidden(false, true); - } - - if (this.state.zenMode.wasSideBarVisible) { - this.setSideBarHidden(false, true); - } - - if (this.state.zenMode.transitionedToCenteredEditorLayout) { - this.centerEditorLayout(false, true); - } - - setLineNumbers(this.configurationService.getValue('editor.lineNumbers')); - - // Status bar and activity bar visibility come from settings -> update their visibility. - this.doUpdateLayoutConfiguration(true); - - this.editorGroupService.activeGroup.focus(); - - toggleFullScreen = this.state.zenMode.transitionedToFullScreen && this.state.fullscreen; - } - - if (!skipLayout) { - this.layout(); - } - - if (toggleFullScreen) { - this.windowService.toggleFullScreen(); - } - - // Event - this._onZenMode.fire(this.state.zenMode.active); - } - - private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.state.statusBar.hidden = hidden; - - // Adjust CSS - if (hidden) { - addClass(this.workbench, 'nostatusbar'); - } else { - removeClass(this.workbench, 'nostatusbar'); - } - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - private createWorkbenchLayout(instantiationService: IInstantiationService): void { - const titleBar = this.getPart(Parts.TITLEBAR_PART); - const editorPart = this.getPart(Parts.EDITOR_PART); - const activityBar = this.getPart(Parts.ACTIVITYBAR_PART); - const panelPart = this.getPart(Parts.PANEL_PART); - const sideBar = this.getPart(Parts.SIDEBAR_PART); - const statusBar = this.getPart(Parts.STATUSBAR_PART); - - if (this.configurationService.getValue('workbench.useExperimentalGridLayout')) { - - // Create view wrappers for all parts - this.titleBarPartView = new View(titleBar); - this.sideBarPartView = new View(sideBar); - this.activityBarPartView = new View(activityBar); - this.editorPartView = new View(editorPart); - this.panelPartView = new View(panelPart); - this.statusBarPartView = new View(statusBar); - - this.workbenchGrid = new Grid(this.editorPartView, { proportionalLayout: false }); - - this.workbench.prepend(this.workbenchGrid.element); - } else { - this.workbenchGrid = instantiationService.createInstance( - WorkbenchLegacyLayout, - this.parent, - this.workbench, - { - titlebar: titleBar, - activitybar: activityBar, - editor: editorPart, - sidebar: sideBar, - panel: panelPart, - statusbar: statusBar, - } - ); - } - } - - layout(options?: ILayoutOptions): void { - if (!this.disposed) { - this._dimension = getClientArea(this.parent); - - if (this.workbenchGrid instanceof Grid) { - position(this.workbench, 0, 0, 0, 0, 'relative'); - size(this.workbench, this._dimension.width, this._dimension.height); - - // Layout the grid widget - this.workbenchGrid.layout(this._dimension.width, this._dimension.height); - - // Layout grid views - this.layoutGrid(); - } else { - this.workbenchGrid.layout(options); - } - - // Emit as event - this._onLayout.fire(this._dimension); - } - } - - private layoutGrid(): void { - if (!(this.workbenchGrid instanceof Grid)) { - return; - } - - let panelInGrid = this.workbenchGrid.hasView(this.panelPartView); - let sidebarInGrid = this.workbenchGrid.hasView(this.sideBarPartView); - let activityBarInGrid = this.workbenchGrid.hasView(this.activityBarPartView); - let statusBarInGrid = this.workbenchGrid.hasView(this.statusBarPartView); - let titlebarInGrid = this.workbenchGrid.hasView(this.titleBarPartView); - - // Add parts to grid - if (!statusBarInGrid) { - this.workbenchGrid.addView(this.statusBarPartView, Sizing.Split, this.editorPartView, Direction.Down); - statusBarInGrid = true; - } - - if (!titlebarInGrid && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { - this.workbenchGrid.addView(this.titleBarPartView, Sizing.Split, this.editorPartView, Direction.Up); - titlebarInGrid = true; - } - - if (!activityBarInGrid) { - this.workbenchGrid.addView(this.activityBarPartView, Sizing.Split, panelInGrid && this.state.sideBar.position === this.state.panel.position ? this.panelPartView : this.editorPartView, this.state.sideBar.position === Position.RIGHT ? Direction.Right : Direction.Left); - activityBarInGrid = true; - } - - if (!sidebarInGrid) { - this.workbenchGrid.addView(this.sideBarPartView, this.state.sideBar.width !== undefined ? this.state.sideBar.width : Sizing.Split, this.activityBarPartView, this.state.sideBar.position === Position.LEFT ? Direction.Right : Direction.Left); - sidebarInGrid = true; - } - - if (!panelInGrid) { - this.workbenchGrid.addView(this.panelPartView, this.getPanelDimension(this.state.panel.position) !== undefined ? this.getPanelDimension(this.state.panel.position) : Sizing.Split, this.editorPartView, this.state.panel.position === Position.BOTTOM ? Direction.Down : Direction.Right); - panelInGrid = true; - } - - // Hide parts - if (this.state.panel.hidden) { - this.panelPartView.hide(); - } - - if (this.state.statusBar.hidden) { - this.statusBarPartView.hide(); - } - - if (!this.isVisible(Parts.TITLEBAR_PART)) { - this.titleBarPartView.hide(); - } - - if (this.state.activityBar.hidden) { - this.activityBarPartView.hide(); - } - - if (this.state.sideBar.hidden) { - this.sideBarPartView.hide(); - } - - if (this.state.editor.hidden) { - this.editorPartView.hide(); - } - - // Show visible parts - if (!this.state.editor.hidden) { - this.editorPartView.show(); - } - - if (!this.state.statusBar.hidden) { - this.statusBarPartView.show(); - } - - if (this.isVisible(Parts.TITLEBAR_PART)) { - this.titleBarPartView.show(); - } - - if (!this.state.activityBar.hidden) { - this.activityBarPartView.show(); - } - - if (!this.state.sideBar.hidden) { - this.sideBarPartView.show(); - } - - if (!this.state.panel.hidden) { - this.panelPartView.show(); - } - } - - private getPanelDimension(position: Position): number { - return position === Position.BOTTOM ? this.state.panel.height : this.state.panel.width; - } - - isEditorLayoutCentered(): boolean { - return this.state.editor.centered; - } - - centerEditorLayout(active: boolean, skipLayout?: boolean): void { - this.state.editor.centered = active; - - this.storageService.store(Storage.CENTERED_LAYOUT_ENABLED, active, StorageScope.WORKSPACE); - - let smartActive = active; - if (this.editorGroupService.groups.length > 1 && this.configurationService.getValue('workbench.editor.centeredLayoutAutoResize')) { - smartActive = false; // Respect the auto resize setting - do not go into centered layout if there is more than 1 group. - } - - // Enter Centered Editor Layout - if (this.editorGroupService.isLayoutCentered() !== smartActive) { - this.editorGroupService.centerLayout(smartActive); - - if (!skipLayout) { - this.layout(); - } - } - } - - resizePart(part: Parts, sizeChange: number): void { - let view: View; - switch (part) { - case Parts.SIDEBAR_PART: - view = this.sideBarPartView; - case Parts.PANEL_PART: - view = this.panelPartView; - case Parts.EDITOR_PART: - view = this.editorPartView; - if (this.workbenchGrid instanceof Grid) { - this.workbenchGrid.resizeView(view, this.workbenchGrid.getViewSize(view) + sizeChange); - } else { - this.workbenchGrid.resizePart(part, sizeChange); - } - break; - default: - return; // Cannot resize other parts - } - } - - setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.state.activityBar.hidden = hidden; - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - setEditorHidden(hidden: boolean, skipLayout?: boolean): void { - if (!(this.workbenchGrid instanceof Grid) || hidden === this.state.editor.hidden) { - return; - } - - this.state.editor.hidden = hidden; - - // The editor and the panel cannot be hidden at the same time - if (this.state.editor.hidden && this.state.panel.hidden) { - this.setPanelHidden(false, true); - } - - if (!skipLayout) { - this.layout(); - } - } - - setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { - this.state.sideBar.hidden = hidden; - - // Adjust CSS - if (hidden) { - addClass(this.workbench, 'nosidebar'); - } else { - removeClass(this.workbench, 'nosidebar'); - } - - // If sidebar becomes hidden, also hide the current active Viewlet if any - if (hidden && this.viewletService.getActiveViewlet()) { - this.viewletService.hideActiveViewlet(); - - // Pass Focus to Editor or Panel if Sidebar is now hidden - const activePanel = this.panelService.getActivePanel(); - if (this.hasFocus(Parts.PANEL_PART) && activePanel) { - activePanel.focus(); - } else { - this.editorGroupService.activeGroup.focus(); - } - } - - // If sidebar becomes visible, show last active Viewlet or default viewlet - else if (!hidden && !this.viewletService.getActiveViewlet()) { - const viewletToOpen = this.viewletService.getLastActiveViewletId(); - if (viewletToOpen) { - const viewlet = this.viewletService.openViewlet(viewletToOpen, true); - if (!viewlet) { - this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); - } - } - } - - // Remember in settings - const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; - if (hidden !== defaultHidden) { - this.storageService.store(Storage.SIDEBAR_HIDDEN, hidden ? 'true' : 'false', StorageScope.WORKSPACE); - } else { - this.storageService.remove(Storage.SIDEBAR_HIDDEN, StorageScope.WORKSPACE); - } - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - setPanelHidden(hidden: boolean, skipLayout?: boolean): void { - this.state.panel.hidden = hidden; - - // Adjust CSS - if (hidden) { - addClass(this.workbench, 'nopanel'); - } else { - removeClass(this.workbench, 'nopanel'); - } - - // If panel part becomes hidden, also hide the current active panel if any - if (hidden && this.panelService.getActivePanel()) { - this.panelService.hideActivePanel(); - this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden - } - - // If panel part becomes visible, show last active panel or default panel - else if (!hidden && !this.panelService.getActivePanel()) { - const panelToOpen = this.panelService.getLastActivePanelId(); - if (panelToOpen) { - const focus = !skipLayout; - this.panelService.openPanel(panelToOpen, focus); - } - } - - // Remember in settings - if (!hidden) { - this.storageService.store(Storage.PANEL_HIDDEN, 'false', StorageScope.WORKSPACE); - } else { - this.storageService.remove(Storage.PANEL_HIDDEN, StorageScope.WORKSPACE); - } - - // The editor and panel cannot be hidden at the same time - if (hidden && this.state.editor.hidden) { - this.setEditorHidden(false, true); - } - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - } - - toggleMaximizedPanel(): void { - if (this.workbenchGrid instanceof Grid) { - this.workbenchGrid.maximizeViewSize(this.panelPartView); - } else { - this.workbenchGrid.layout({ toggleMaximizedPanel: true, source: Parts.PANEL_PART }); - } - } - - isPanelMaximized(): boolean { - if (this.workbenchGrid instanceof Grid) { - try { - return this.workbenchGrid.getViewSize2(this.panelPartView).height === this.getPart(Parts.PANEL_PART).maximumHeight; - } catch (e) { - return false; - } - } else { - return this.workbenchGrid.isPanelMaximized(); - } - } - - getSideBarPosition(): Position { - return this.state.sideBar.position; - } - - setMenubarVisibility(visibility: MenuBarVisibility, skipLayout: boolean): void { - if (this.state.menuBar.visibility !== visibility) { - this.state.menuBar.visibility = visibility; - - // Layout - if (!skipLayout) { - if (this.workbenchGrid instanceof Grid) { - const dimensions = getClientArea(this.parent); - this.workbenchGrid.layout(dimensions.width, dimensions.height); - } else { - this.workbenchGrid.layout(); - } - } - } - } - - getMenubarVisibility(): MenuBarVisibility { - return this.state.menuBar.visibility; - } - - getPanelPosition(): Position { - return this.state.panel.position; - } - - setPanelPosition(position: Position): void { - const panelPart = this.getPart(Parts.PANEL_PART); - const wasHidden = this.state.panel.hidden; - - if (this.state.panel.hidden) { - this.setPanelHidden(false, true /* Skip Layout */); - } else { - this.savePanelDimension(); - } - - const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; - const oldPositionValue = (this.state.panel.position === Position.BOTTOM) ? 'bottom' : 'right'; - this.state.panel.position = position; - - function positionToString(position: Position): string { - switch (position) { - case Position.LEFT: return 'left'; - case Position.RIGHT: return 'right'; - case Position.BOTTOM: return 'bottom'; - } - } - - this.storageService.store(Storage.PANEL_POSITION, positionToString(this.state.panel.position), StorageScope.WORKSPACE); - - // Adjust CSS - removeClass(panelPart.getContainer(), oldPositionValue); - addClass(panelPart.getContainer(), newPositionValue); - - // Update Styles - panelPart.updateStyles(); - - // Layout - if (this.workbenchGrid instanceof Grid) { - if (!wasHidden) { - this.savePanelDimension(); - } - - this.workbenchGrid.removeView(this.panelPartView); - this.layout(); - } else { - this.workbenchGrid.layout(); - } - } - - private savePanelDimension(): void { - if (!(this.workbenchGrid instanceof Grid)) { - return; - } - - if (this.state.panel.position === Position.BOTTOM) { - this.state.panel.height = this.workbenchGrid.getViewSize(this.panelPartView); - } else { - this.state.panel.width = this.workbenchGrid.getViewSize(this.panelPartView); - } - } - - private saveLayoutState(e: IWillSaveStateEvent): void { - - // Zen Mode - if (this.state.zenMode.active) { - this.storageService.store(Storage.ZEN_MODE_ENABLED, true, StorageScope.WORKSPACE); - } else { - this.storageService.remove(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE); - } - - if (e.reason === WillSaveStateReason.SHUTDOWN && this.state.zenMode.active) { - if (!this.configurationService.getValue(Settings.ZEN_MODE_RESTORE)) { - this.toggleZenMode(true); // We will not restore zen mode, need to clear all zen mode state changes - } - } - } - - private getPart(key: Parts): Part { - const part = this.parts.get(key); - if (!part) { - throw new Error('unknown part'); - } - return part; - } - - dispose(): void { - super.dispose(); - - this.disposed = true; - } - - //#endregion } -- GitLab