import '../../workbench/browser/style.js';
import './media/style.css';
import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js';
import { Emitter, Event, setGlobalLeakWarningThreshold } from '../../base/common/event.js';
import { getActiveDocument, getActiveElement, getClientArea, getWindowId, getWindows, IDimension, isAncestorUsingFlowTo, isHTMLElement, size, Dimension, runWhenWindowIdle } from '../../base/browser/dom.js';
import { DeferredPromise, RunOnceScheduler } from '../../base/common/async.js';
import { isFullscreen, onDidChangeFullscreen, isChrome, isFirefox, isSafari } from '../../base/browser/browser.js';
import { mark } from '../../base/common/performance.js';
import { onUnexpectedError, setUnexpectedErrorHandler } from '../../base/common/errors.js';
import { isWindows, isLinux, isWeb, isNative, isMacintosh } from '../../base/common/platform.js';
import { Parts, Position, PanelAlignment, IWorkbenchLayoutService, SINGLE_WINDOW_PARTS, MULTI_WINDOW_PARTS, IPartVisibilityChangeEvent, positionToString } from '../../workbench/services/layout/browser/layoutService.js';
import { ILayoutOffsetInfo } from '../../platform/layout/browser/layoutService.js';
import { Part } from '../../workbench/browser/part.js';
import { Direction, ISerializableView, ISerializedGrid, ISerializedLeafNode, ISerializedNode, IViewSize, Orientation, SerializableGrid } from '../../base/browser/ui/grid/grid.js';
import { IEditorGroupsService } from '../../workbench/services/editor/common/editorGroupsService.js';
import { IEditorService } from '../../workbench/services/editor/common/editorService.js';
import { IPaneCompositePartService } from '../../workbench/services/panecomposite/browser/panecomposite.js';
import { IViewDescriptorService, ViewContainerLocation } from '../../workbench/common/views.js';
import { ILogService } from '../../platform/log/common/log.js';
import { IInstantiationService, refineServiceDecorator, ServicesAccessor } from '../../platform/instantiation/common/instantiation.js';
import { ITitleService } from '../../workbench/services/title/browser/titleService.js';
import { mainWindow, CodeWindow } from '../../base/browser/window.js';
import { coalesce } from '../../base/common/arrays.js';
import { ServiceCollection } from '../../platform/instantiation/common/serviceCollection.js';
import { InstantiationService } from '../../platform/instantiation/common/instantiationService.js';
import { getSingletonServiceDescriptors } from '../../platform/instantiation/common/extensions.js';
import { ILifecycleService, LifecyclePhase, WillShutdownEvent } from '../../workbench/services/lifecycle/common/lifecycle.js';
import { IStorageService, WillSaveStateReason, StorageScope, StorageTarget } from '../../platform/storage/common/storage.js';
import { IConfigurationChangeEvent, IConfigurationService } from '../../platform/configuration/common/configuration.js';
import { IHostService } from '../../workbench/services/host/browser/host.js';
import { IDialogService } from '../../platform/dialogs/common/dialogs.js';
import { INotificationService } from '../../platform/notification/common/notification.js';
import { NotificationService } from '../../workbench/services/notification/common/notificationService.js';
import { IHoverService, WorkbenchHoverDelegate } from '../../platform/hover/browser/hover.js';
import { setHoverDelegateFactory } from '../../base/browser/ui/hover/hoverDelegateFactory.js';
import { setBaseLayerHoverDelegate } from '../../base/browser/ui/hover/hoverDelegate2.js';
import { Registry } from '../../platform/registry/common/platform.js';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../workbench/common/contributions.js';
import { IEditorFactoryRegistry, EditorExtensions } from '../../workbench/common/editor.js';
import { setARIAContainer } from '../../base/browser/ui/aria/aria.js';
import { FontMeasurements } from '../../editor/browser/config/fontMeasurements.js';
import { createBareFontInfoFromRawSettings } from '../../editor/common/config/fontInfoFromSettings.js';
import { toErrorMessage } from '../../base/common/errorMessage.js';
import { WorkbenchContextKeysHandler } from '../../workbench/browser/contextkeys.js';
import { PixelRatio } from '../../base/browser/pixelRatio.js';
import { AccessibilityProgressSignalScheduler } from '../../platform/accessibilitySignal/browser/progressAccessibilitySignalScheduler.js';
import { setProgressAccessibilitySignalScheduler } from '../../base/browser/ui/progressbar/progressAccessibilitySignal.js';
import { AccessibleViewRegistry } from '../../platform/accessibility/browser/accessibleViewRegistry.js';
import { NotificationAccessibleView } from '../../workbench/browser/parts/notifications/notificationAccessibleView.js';
import { NotificationsCenter } from '../../workbench/browser/parts/notifications/notificationsCenter.js';
import { NotificationsAlerts } from '../../workbench/browser/parts/notifications/notificationsAlerts.js';
import { NotificationsStatus } from '../../workbench/browser/parts/notifications/notificationsStatus.js';
import { registerNotificationCommands } from '../../workbench/browser/parts/notifications/notificationsCommands.js';
import { NotificationsToasts } from '../../workbench/browser/parts/notifications/notificationsToasts.js';
import { IMarkdownRendererService } from '../../platform/markdown/browser/markdownRenderer.js';
import { EditorMarkdownCodeBlockRenderer } from '../../editor/browser/widget/markdownRenderer/browser/editorMarkdownCodeBlockRenderer.js';
import { SyncDescriptor } from '../../platform/instantiation/common/descriptors.js';
import { TitleService } from './parts/titlebarPart.js';
import { IContextKeyService } from '../../platform/contextkey/common/contextkey.js';
import { EditorMaximizedContext, IsPhoneLayoutContext, KeyboardVisibleContext } from '../common/contextkeys.js';
import {
NotificationsPosition,
NotificationsSettings,
getNotificationsPosition
} from '../../workbench/common/notifications.js';
import { SessionsLayoutPolicy } from './layoutPolicy.js';
import { MobileNavigationStack } from './mobileNavigationStack.js';
import { MobileTitlebarPart } from './parts/mobile/mobileTitlebarPart.js';
import { autorun } from '../../base/common/observable.js';
import { ISessionsManagementService } from '../services/sessions/common/sessionsManagement.js';
export interface IWorkbenchOptions {
extraClasses?: string[];
}
enum LayoutClasses {
SIDEBAR_HIDDEN = 'nosidebar',
MAIN_EDITOR_AREA_HIDDEN = 'nomaineditorarea',
PANEL_HIDDEN = 'nopanel',
AUXILIARYBAR_HIDDEN = 'noauxiliarybar',
CHATBAR_HIDDEN = 'nochatbar',
STATUSBAR_HIDDEN = 'nostatusbar',
SHELL_GRADIENT_BACKGROUND = 'shell-gradient-background',
FULLSCREEN = 'fullscreen',
MAXIMIZED = 'maximized',
PHONE_LAYOUT = 'phone-layout'
}
interface IPartVisibilityState {
sidebar: boolean;
auxiliaryBar: boolean;
editor: boolean;
panel: boolean;
chatBar: boolean;
}
export interface IAgentWorkbenchLayoutService extends IWorkbenchLayoutService {
isEditorMaximized(): boolean;
setEditorMaximized(maximized: boolean): void;
readonly onDidChangeEditorMaximized: Event<void>;
}
export const IAgentWorkbenchLayoutService = refineServiceDecorator<IWorkbenchLayoutService, IAgentWorkbenchLayoutService>(IWorkbenchLayoutService);
export class Workbench extends Disposable implements IAgentWorkbenchLayoutService {
declare readonly _serviceBrand: undefined;
private readonly _onWillShutdown = this._register(new Emitter<WillShutdownEvent>());
readonly onWillShutdown = this._onWillShutdown.event;
private readonly _onDidShutdown = this._register(new Emitter<void>());
readonly onDidShutdown = this._onDidShutdown.event;
private readonly _onDidChangeZenMode = this._register(new Emitter<boolean>());
readonly onDidChangeZenMode = this._onDidChangeZenMode.event;
private readonly _onDidChangeMainEditorCenteredLayout = this._register(new Emitter<boolean>());
readonly onDidChangeMainEditorCenteredLayout = this._onDidChangeMainEditorCenteredLayout.event;
private readonly _onDidChangePanelAlignment = this._register(new Emitter<PanelAlignment>());
readonly onDidChangePanelAlignment = this._onDidChangePanelAlignment.event;
private readonly _onDidChangeWindowMaximized = this._register(new Emitter<{ windowId: number; maximized: boolean }>());
readonly onDidChangeWindowMaximized = this._onDidChangeWindowMaximized.event;
private readonly _onDidChangePanelPosition = this._register(new Emitter<string>());
readonly onDidChangePanelPosition = this._onDidChangePanelPosition.event;
private readonly _onDidChangePartVisibility = this._register(new Emitter<IPartVisibilityChangeEvent>());
readonly onDidChangePartVisibility = this._onDidChangePartVisibility.event;
private readonly _onDidChangeNotificationsVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeNotificationsVisibility = this._onDidChangeNotificationsVisibility.event;
private readonly _onDidChangeAuxiliaryBarMaximized = this._register(new Emitter<void>());
readonly onDidChangeAuxiliaryBarMaximized = this._onDidChangeAuxiliaryBarMaximized.event;
private readonly _onDidChangeEditorMaximized = this._register(new Emitter<void>());
readonly onDidChangeEditorMaximized = this._onDidChangeEditorMaximized.event;
private readonly _onDidLayoutMainContainer = this._register(new Emitter<IDimension>());
readonly onDidLayoutMainContainer = this._onDidLayoutMainContainer.event;
private readonly _onDidLayoutActiveContainer = this._register(new Emitter<IDimension>());
readonly onDidLayoutActiveContainer = this._onDidLayoutActiveContainer.event;
private readonly _onDidLayoutContainer = this._register(new Emitter<{ container: HTMLElement; dimension: IDimension }>());
readonly onDidLayoutContainer = this._onDidLayoutContainer.event;
private readonly _onDidAddContainer = this._register(new Emitter<{ container: HTMLElement; disposables: DisposableStore }>());
readonly onDidAddContainer = this._onDidAddContainer.event;
private readonly _onDidChangeActiveContainer = this._register(new Emitter<void>());
readonly onDidChangeActiveContainer = this._onDidChangeActiveContainer.event;
readonly mainContainer = document.createElement('div');
get activeContainer(): HTMLElement {
return this.getContainerFromDocument(getActiveDocument());
}
get containers(): Iterable<HTMLElement> {
const containers: HTMLElement[] = [];
for (const { window } of getWindows()) {
containers.push(this.getContainerFromDocument(window.document));
}
return containers;
}
private getContainerFromDocument(targetDocument: Document): HTMLElement {
if (targetDocument === this.mainContainer.ownerDocument) {
return this.mainContainer;
} else {
return targetDocument.body.getElementsByClassName('monaco-workbench')[0] as HTMLElement;
}
}
private _mainContainerDimension!: IDimension;
get mainContainerDimension(): IDimension { return this._mainContainerDimension; }
get activeContainerDimension(): IDimension {
return this.getContainerDimension(this.activeContainer);
}
private getContainerDimension(container: HTMLElement): IDimension {
if (container === this.mainContainer) {
return this.mainContainerDimension;
} else {
return getClientArea(container);
}
}
get mainContainerOffset(): ILayoutOffsetInfo {
return this.computeContainerOffset();
}
get activeContainerOffset(): ILayoutOffsetInfo {
return this.computeContainerOffset();
}
private computeContainerOffset(): ILayoutOffsetInfo {
let top = 0;
let quickPickTop = 0;
if (this.isVisible(Parts.TITLEBAR_PART, mainWindow)) {
top = this.getPart(Parts.TITLEBAR_PART).maximumHeight;
quickPickTop = top;
} else if (this.mobileTopBarElement) {
top = this.mobileTopBarElement.offsetHeight;
quickPickTop = top;
}
return { top, quickPickTop };
}
private readonly parts = new Map<string, Part>();
private workbenchGrid!: SerializableGrid<ISerializableView>;
private titleBarPartView!: ISerializableView;
private sideBarPartView!: ISerializableView;
private panelPartView!: ISerializableView;
private auxiliaryBarPartView!: ISerializableView;
private editorPartView!: ISerializableView;
private chatBarPartView!: ISerializableView;
private readonly partVisibility: IPartVisibilityState = {
sidebar: true,
auxiliaryBar: true,
editor: false,
panel: false,
chatBar: true
};
private mainWindowFullscreen = false;
private readonly maximized = new Set<number>();
private readonly layoutPolicy = this._register(new SessionsLayoutPolicy());
private readonly mobileNavStack = this._register(new MobileNavigationStack());
private mobileTopBarElement: HTMLElement | undefined;
private readonly mobileTopBarDisposables = this._register(new DisposableStore());
private _editorMaximized = false;
private _editorLastNonMaximizedVisibility: IPartVisibilityState | undefined;
private readonly restoredPromise = new DeferredPromise<void>();
readonly whenRestored = this.restoredPromise.p;
private restored = false;
readonly openedDefaultEditors = false;
private editorGroupService!: IEditorGroupsService;
private editorService!: IEditorService;
private paneCompositeService!: IPaneCompositePartService;
private viewDescriptorService!: IViewDescriptorService;
private sessionsManagementService!: ISessionsManagementService;
private instantiationService!: IInstantiationService;
constructor(
protected readonly parent: HTMLElement,
private readonly options: IWorkbenchOptions | undefined,
private readonly serviceCollection: ServiceCollection,
private readonly logService: ILogService
) {
super();
const metaElements = mainWindow.document.head.getElementsByTagName('meta');
let viewportMeta: HTMLMetaElement | undefined;
for (let i = 0; i < metaElements.length; i++) {
if (metaElements[i].name === 'viewport') {
viewportMeta = metaElements[i];
break;
}
}
if (viewportMeta && !viewportMeta.content.includes('viewport-fit=')) {
viewportMeta.content = `${viewportMeta.content}, viewport-fit=cover`;
}
mark('code/willStartWorkbench');
this.registerErrorHandler(logService);
}
private registerErrorHandler(logService: ILogService): void {
if (!isFirefox) {
Error.stackTraceLimit = 100;
}
mainWindow.addEventListener('unhandledrejection', (event) => {
onUnexpectedError(event.reason);
event.preventDefault();
});
setUnexpectedErrorHandler(error => this.handleUnexpectedError(error, logService));
}
private previousUnexpectedError: { message: string | undefined; time: number } = { message: undefined, time: 0 };
private handleUnexpectedError(error: unknown, logService: ILogService): void {
const message = toErrorMessage(error, true);
if (!message) {
return;
}
const now = Date.now();
if (message === this.previousUnexpectedError.message && now - this.previousUnexpectedError.time <= 1000) {
return;
}
this.previousUnexpectedError.time = now;
this.previousUnexpectedError.message = message;
logService.error(message);
}
startup(): IInstantiationService {
try {
this._register(setGlobalLeakWarningThreshold(175));
const instantiationService = this.initServices(this.serviceCollection);
instantiationService.invokeFunction(accessor => {
const lifecycleService = accessor.get(ILifecycleService);
const storageService = accessor.get(IStorageService);
const configurationService = accessor.get(IConfigurationService);
const hostService = accessor.get(IHostService);
const hoverService = accessor.get(IHoverService);
const dialogService = accessor.get(IDialogService);
const notificationService = accessor.get(INotificationService) as NotificationService;
const markdownRendererService = accessor.get(IMarkdownRendererService);
if (isWeb && typeof (configurationService as IConfigurationService & { acquireInstantiationService?(i: IInstantiationService): void }).acquireInstantiationService === 'function') {
(configurationService as IConfigurationService & { acquireInstantiationService(i: IInstantiationService): void }).acquireInstantiationService(instantiationService);
}
markdownRendererService.setDefaultCodeBlockRenderer(instantiationService.createInstance(EditorMarkdownCodeBlockRenderer));
setHoverDelegateFactory((placement, enableInstantHover) => instantiationService.createInstance(WorkbenchHoverDelegate, placement, { instantHover: enableInstantHover }, {}));
setBaseLayerHoverDelegate(hoverService);
this.initLayout(accessor);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).start(accessor);
Registry.as<IEditorFactoryRegistry>(EditorExtensions.EditorFactory).start(accessor);
this._register(instantiationService.createInstance(WorkbenchContextKeysHandler));
const editorMaximizedContext = EditorMaximizedContext.bindTo(accessor.get(IContextKeyService));
this._register(this.onDidChangeEditorMaximized(() => {
editorMaximizedContext.set(this.isEditorMaximized());
}));
const contextKeyService = accessor.get(IContextKeyService);
const isPhoneLayoutCtx = IsPhoneLayoutContext.bindTo(contextKeyService);
this._register(autorun(reader => {
isPhoneLayoutCtx.set(this.layoutPolicy.viewportClass.read(reader) === 'phone');
}));
if (mainWindow.visualViewport) {
const keyboardVisibleCtx = KeyboardVisibleContext.bindTo(contextKeyService);
const KEYBOARD_HEIGHT_THRESHOLD_PX = 100;
const onViewportResize = () => {
const vp = mainWindow.visualViewport;
if (!vp) {
return;
}
const heightDiff = mainWindow.innerHeight - vp.height;
keyboardVisibleCtx.set(heightDiff > KEYBOARD_HEIGHT_THRESHOLD_PX);
};
mainWindow.visualViewport.addEventListener('resize', onViewportResize);
this._register({ dispose: () => mainWindow.visualViewport?.removeEventListener('resize', onViewportResize) });
}
this.registerListeners(lifecycleService, storageService, configurationService, hostService, dialogService);
this.renderWorkbench(instantiationService, notificationService, storageService, configurationService);
this.createWorkbenchLayout();
if (this.layoutPolicy.viewportClass.get() === 'phone') {
this.createMobileTitlebar();
}
this.createWorkbenchManagement(instantiationService);
this.layout();
this.restore(lifecycleService);
});
return instantiationService;
} catch (error) {
onUnexpectedError(error);
throw error;
}
}
private initServices(serviceCollection: ServiceCollection): IInstantiationService {
serviceCollection.set(IAgentWorkbenchLayoutService, this);
serviceCollection.set(ITitleService, new SyncDescriptor(TitleService, []));
const contributedServices = getSingletonServiceDescriptors();
for (const [id, descriptor] of contributedServices) {
serviceCollection.set(id, descriptor);
}
const instantiationService = new InstantiationService(serviceCollection, true);
instantiationService.invokeFunction(accessor => {
const lifecycleService = accessor.get(ILifecycleService);
lifecycleService.phase = LifecyclePhase.Ready;
});
return instantiationService;
}
private registerListeners(lifecycleService: ILifecycleService, storageService: IStorageService, configurationService: IConfigurationService, hostService: IHostService, dialogService: IDialogService): void {
this._register(configurationService.onDidChangeConfiguration(e => this.updateFontAliasing(e, configurationService)));
if (isNative) {
this._register(storageService.onWillSaveState(e => {
if (e.reason === WillSaveStateReason.SHUTDOWN) {
this.storeFontInfo(storageService);
}
}));
} else {
this._register(lifecycleService.onWillShutdown(() => this.storeFontInfo(storageService)));
}
this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event)));
this._register(lifecycleService.onDidShutdown(() => {
this._onDidShutdown.fire();
this.dispose();
}));
this._register(hostService.onDidChangeFocus(focus => {
if (!focus) {
storageService.flush();
}
}));
this._register(dialogService.onWillShowDialog(() => this.mainContainer.classList.add('modal-dialog-visible')));
this._register(dialogService.onDidShowDialog(() => this.mainContainer.classList.remove('modal-dialog-visible')));
}
private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto' | undefined;
private updateFontAliasing(e: IConfigurationChangeEvent | undefined, configurationService: IConfigurationService) {
if (!isMacintosh) {
return;
}
if (e && !e.affectsConfiguration('workbench.fontAliasing')) {
return;
}
const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>('workbench.fontAliasing');
if (this.fontAliasing === aliasing) {
return;
}
this.fontAliasing = aliasing;
const fontAliasingValues: (typeof aliasing)[] = ['antialiased', 'none', 'auto'];
this.mainContainer.classList.remove(...fontAliasingValues.map(value => `monaco-font-aliasing-${value}`));
if (fontAliasingValues.some(option => option === aliasing)) {
this.mainContainer.classList.add(`monaco-font-aliasing-${aliasing}`);
}
}
private restoreFontInfo(storageService: IStorageService, configurationService: IConfigurationService): void {
const storedFontInfoRaw = storageService.get('editorFontInfo', StorageScope.APPLICATION);
if (storedFontInfoRaw) {
try {
const storedFontInfo = JSON.parse(storedFontInfoRaw);
if (Array.isArray(storedFontInfo)) {
FontMeasurements.restoreFontInfo(mainWindow, storedFontInfo);
}
} catch (err) {
}
}
FontMeasurements.readFontInfo(mainWindow, createBareFontInfoFromRawSettings(configurationService.getValue('editor'), PixelRatio.getInstance(mainWindow).value));
}
private storeFontInfo(storageService: IStorageService): void {
const serializedFontInfo = FontMeasurements.serializeFontInfo(mainWindow);
if (serializedFontInfo) {
storageService.store('editorFontInfo', JSON.stringify(serializedFontInfo), StorageScope.APPLICATION, StorageTarget.MACHINE);
}
}
private renderWorkbench(instantiationService: IInstantiationService, notificationService: NotificationService, storageService: IStorageService, configurationService: IConfigurationService): void {
setARIAContainer(this.mainContainer);
setProgressAccessibilitySignalScheduler((msDelayTime: number, msLoopTime?: number) => instantiationService.createInstance(AccessibilityProgressSignalScheduler, msDelayTime, msLoopTime));
const initialDimension = getClientArea(this.parent);
this.layoutPolicy.update(initialDimension.width, initialDimension.height);
const visibilityDefaults = this.layoutPolicy.getPartVisibilityDefaults();
this.partVisibility.sidebar = visibilityDefaults.sidebar;
this.partVisibility.auxiliaryBar = visibilityDefaults.auxiliaryBar;
this.partVisibility.panel = visibilityDefaults.panel;
this.partVisibility.chatBar = visibilityDefaults.chatBar;
this.partVisibility.editor = visibilityDefaults.editor;
const platformClass = isWindows ? 'windows' : isLinux ? 'linux' : 'mac';
const workbenchClasses = coalesce([
'monaco-workbench',
'agent-sessions-workbench',
LayoutClasses.SHELL_GRADIENT_BACKGROUND,
platformClass,
isWeb ? 'web' : undefined,
isChrome ? 'chromium' : isFirefox ? 'firefox' : isSafari ? 'safari' : undefined,
...this.getLayoutClasses(),
...(this.options?.extraClasses ? this.options.extraClasses : [])
]);
this.mainContainer.classList.add(...workbenchClasses);
this.updateFontAliasing(undefined, configurationService);
this.restoreFontInfo(storageService, configurationService);
for (const { id, role, classes } of [
{ id: Parts.TITLEBAR_PART, role: 'none', classes: ['titlebar'] },
{ id: Parts.SIDEBAR_PART, role: 'none', classes: ['sidebar', 'left'] },
{ id: Parts.AUXILIARYBAR_PART, role: 'none', classes: ['auxiliarybar', 'basepanel', 'right'] },
{ id: Parts.CHATBAR_PART, role: 'main', classes: ['chatbar', 'basepanel', 'right'] },
{ id: Parts.PANEL_PART, role: 'none', classes: ['panel', 'basepanel', positionToString(this.getPanelPosition())] },
]) {
const partContainer = this.createPartContainer(id, role, classes);
mark(`code/willCreatePart/${id}`);
this.getPart(id).create(partContainer);
mark(`code/didCreatePart/${id}`);
}
this.createEditorPart();
this.createNotificationsHandlers(instantiationService, notificationService, configurationService);
this.parent.appendChild(this.mainContainer);
}
private createMobileTitlebar(): void {
this.mobileTopBarDisposables.clear();
const mobileTitlebar = this.mobileTopBarDisposables.add(this.instantiationService.createInstance(MobileTitlebarPart, this.mainContainer));
this.mobileTopBarElement = mobileTitlebar.element;
this.mobileTopBarDisposables.add(mobileTitlebar.onDidClickHamburger(() => {
this.toggleMobileSidebarDrawer();
}));
this.mobileTopBarDisposables.add(mobileTitlebar.onDidClickNewSession(() => {
this.sessionsManagementService.openNewSessionView();
}));
}
private toggleMobileSidebarDrawer(): void {
const isOpen = this.partVisibility.sidebar;
if (isOpen) {
this.closeMobileSidebarDrawer();
} else {
this.openMobileSidebarDrawer();
}
}
private openMobileSidebarDrawer(): void {
if (!this.mobileNavStack.has('sidebar')) {
this.mobileNavStack.push('sidebar');
}
this.setSideBarHidden(false);
}
private closeMobileSidebarDrawer(): void {
this.setSideBarHidden(true);
if (this.mobileNavStack.has('sidebar')) {
this.mobileNavStack.popSilently('sidebar');
}
}
private createNotificationsHandlers(
instantiationService: IInstantiationService,
notificationService: NotificationService,
configurationService: IConfigurationService
): void {
const notificationsCenter = this._register(instantiationService.createInstance(NotificationsCenter, this.mainContainer, notificationService.model));
const notificationsToasts = this._register(instantiationService.createInstance(NotificationsToasts, this.mainContainer, notificationService.model));
this._register(instantiationService.createInstance(NotificationsAlerts, notificationService.model));
const notificationsStatus = this._register(instantiationService.createInstance(NotificationsStatus, notificationService.model));
this._register(notificationsCenter.onDidChangeVisibility(() => {
notificationsStatus.update(notificationsCenter.isVisible, notificationsToasts.isVisible);
notificationsToasts.update(notificationsCenter.isVisible);
}));
this._register(notificationsToasts.onDidChangeVisibility(() => {
notificationsStatus.update(notificationsCenter.isVisible, notificationsToasts.isVisible);
}));
registerNotificationCommands(notificationsCenter, notificationsToasts, notificationService.model);
AccessibleViewRegistry.register(new NotificationAccessibleView());
this.registerSessionsNotificationOffsets(configurationService, notificationsCenter, notificationsToasts);
this.registerNotifications({
onDidChangeNotificationsVisibility: Event.map(
Event.any(notificationsToasts.onDidChangeVisibility, notificationsCenter.onDidChangeVisibility),
() => notificationsToasts.isVisible || notificationsCenter.isVisible
)
});
}
private registerSessionsNotificationOffsets(
configurationService: IConfigurationService,
notificationsCenter: NotificationsCenter,
notificationsToasts: NotificationsToasts
): void {
const applySessionsNotificationOffsets = () => {
const position = getNotificationsPosition(configurationService);
const notificationsCenterContainer = this.getWorkbenchChildByClassName('notifications-center');
const notificationsToastsContainer = this.getWorkbenchChildByClassName('notifications-toasts');
if (position === NotificationsPosition.TOP_RIGHT) {
notificationsCenterContainer?.style.setProperty('top', '40px');
notificationsToastsContainer?.style.setProperty('top', '40px');
}
};
this._register(this.onDidLayoutMainContainer(() => applySessionsNotificationOffsets()));
this._register(notificationsCenter.onDidChangeVisibility(() => applySessionsNotificationOffsets()));
this._register(notificationsToasts.onDidChangeVisibility(() => applySessionsNotificationOffsets()));
this._register(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(NotificationsSettings.NOTIFICATIONS_POSITION)) {
applySessionsNotificationOffsets();
}
}));
}
private getWorkbenchChildByClassName(className: string): HTMLElement | undefined {
for (const child of this.mainContainer.children) {
if (isHTMLElement(child) && child.classList.contains(className)) {
return child;
}
}
return undefined;
}
private createPartContainer(id: string, role: string, classes: string[]): HTMLElement {
const part = document.createElement('div');
part.classList.add('part', ...classes);
part.id = id;
part.setAttribute('role', role);
return part;
}
private createEditorPart(): void {
const editorPartContainer = document.createElement('div');
editorPartContainer.classList.add('part', 'editor');
editorPartContainer.id = Parts.EDITOR_PART;
editorPartContainer.setAttribute('role', 'main');
mark('code/willCreatePart/workbench.parts.editor');
this.getPart(Parts.EDITOR_PART).create(editorPartContainer, { restorePreviousState: false });
mark('code/didCreatePart/workbench.parts.editor');
this.mainContainer.appendChild(editorPartContainer);
}
private restore(lifecycleService: ILifecycleService): void {
mark('code/didStartWorkbench');
performance.measure('perf: workbench create & restore', 'code/didLoadWorkbenchMain', 'code/didStartWorkbench');
this.restoreParts();
lifecycleService.phase = LifecyclePhase.Restored;
this.setRestored();
const eventuallyPhaseScheduler = this._register(new RunOnceScheduler(() => {
this._register(runWhenWindowIdle(mainWindow, () => lifecycleService.phase = LifecyclePhase.Eventually, 2500));
}, 2500));
eventuallyPhaseScheduler.schedule();
}
private restoreParts(): void {
const partsToRestore: { location: ViewContainerLocation; visible: boolean }[] = [
{ location: ViewContainerLocation.Sidebar, visible: this.partVisibility.sidebar },
{ location: ViewContainerLocation.Panel, visible: this.partVisibility.panel },
{ location: ViewContainerLocation.AuxiliaryBar, visible: this.partVisibility.auxiliaryBar },
{ location: ViewContainerLocation.ChatBar, visible: this.partVisibility.chatBar },
];
for (const { location, visible } of partsToRestore) {
if (visible) {
const defaultViewContainer = this.viewDescriptorService.getDefaultViewContainer(location);
if (defaultViewContainer) {
this.paneCompositeService.openPaneComposite(defaultViewContainer.id, location);
}
}
}
}
initLayout(accessor: ServicesAccessor): void {
this.editorGroupService = accessor.get(IEditorGroupsService);
this.editorService = accessor.get(IEditorService);
this.paneCompositeService = accessor.get(IPaneCompositePartService);
this.viewDescriptorService = accessor.get(IViewDescriptorService);
this.sessionsManagementService = accessor.get(ISessionsManagementService);
this.instantiationService = accessor.get(IInstantiationService);
accessor.get(ITitleService);
this.registerLayoutListeners();
this._register(this.editorService.onWillOpenEditor(e => {
const targetsMainEditorPart = this.editorGroupService.mainPart.groups.some(group => group.id === e.groupId);
if (!targetsMainEditorPart) {
return;
}
if (!this.partVisibility.editor) {
this.setEditorHidden(false);
}
}));
this._register(this.editorService.onDidCloseEditor(() => {
if (this.partVisibility.editor && this.areAllGroupsEmpty()) {
this.setEditorHidden(true);
}
}));
this._mainContainerDimension = getClientArea(this.parent, new Dimension(800, 600));
this.layoutPolicy.update(this._mainContainerDimension.width, this._mainContainerDimension.height);
const visDefaults = this.layoutPolicy.getPartVisibilityDefaults();
this.partVisibility.sidebar = visDefaults.sidebar;
this.partVisibility.auxiliaryBar = visDefaults.auxiliaryBar;
this.partVisibility.panel = visDefaults.panel;
this.partVisibility.chatBar = visDefaults.chatBar;
this.partVisibility.editor = visDefaults.editor;
}
private areAllGroupsEmpty(): boolean {
for (const group of this.editorGroupService.groups) {
if (!group.isEmpty) {
return false;
}
}
return true;
}
private registerLayoutListeners(): void {
this._register(onDidChangeFullscreen(windowId => {
if (windowId === getWindowId(mainWindow)) {
this.mainWindowFullscreen = isFullscreen(mainWindow);
this.updateFullscreenClass();
this.layout();
}
}));
const onWindowResize = () => this.layout();
mainWindow.addEventListener('resize', onWindowResize);
this._register({ dispose: () => mainWindow.removeEventListener('resize', onWindowResize) });
}
private updateFullscreenClass(): void {
if (this.mainWindowFullscreen) {
this.mainContainer.classList.add(LayoutClasses.FULLSCREEN);
} else {
this.mainContainer.classList.remove(LayoutClasses.FULLSCREEN);
}
}
createWorkbenchLayout(): void {
const titleBar = this.getPart(Parts.TITLEBAR_PART);
const editorPart = this.getPart(Parts.EDITOR_PART);
const panelPart = this.getPart(Parts.PANEL_PART);
const auxiliaryBarPart = this.getPart(Parts.AUXILIARYBAR_PART);
const sideBar = this.getPart(Parts.SIDEBAR_PART);
const chatBarPart = this.getPart(Parts.CHATBAR_PART);
this.titleBarPartView = titleBar;
this.sideBarPartView = sideBar;
this.panelPartView = panelPart;
this.auxiliaryBarPartView = auxiliaryBarPart;
this.chatBarPartView = chatBarPart;
this.editorPartView = editorPart;
const viewMap: { [key: string]: ISerializableView } = {
[Parts.TITLEBAR_PART]: this.titleBarPartView,
[Parts.PANEL_PART]: this.panelPartView,
[Parts.SIDEBAR_PART]: this.sideBarPartView,
[Parts.AUXILIARYBAR_PART]: this.auxiliaryBarPartView,
[Parts.CHATBAR_PART]: this.chatBarPartView,
[Parts.EDITOR_PART]: this.editorPartView
};
const fromJSON = ({ type }: { type: string }) => viewMap[type];
const workbenchGrid = SerializableGrid.deserialize(
this.createGridDescriptor(),
{ fromJSON },
{ proportionalLayout: false }
);
this.mainContainer.prepend(workbenchGrid.element);
this.mainContainer.setAttribute('role', 'application');
this.workbenchGrid = workbenchGrid;
this.workbenchGrid.edgeSnapping = this.mainWindowFullscreen;
for (const part of [titleBar, panelPart, sideBar, auxiliaryBarPart, chatBarPart, editorPart]) {
this._register(part.onDidVisibilityChange(visible => {
if (part === sideBar) {
this.setSideBarHidden(!visible);
} else if (part === panelPart) {
this.setPanelHidden(!visible);
} else if (part === auxiliaryBarPart) {
this.setAuxiliaryBarHidden(!visible);
} else if (part === chatBarPart) {
this.setChatBarHidden(!visible);
} else if (part === editorPart) {
this.setEditorHidden(!visible);
}
this._onDidChangePartVisibility.fire({ partId: part.getId(), visible });
this.handleContainerDidLayout(this.mainContainer, this._mainContainerDimension);
}));
}
this._register(this.mobileNavStack.onDidPop(layer => {
switch (layer) {
case 'sidebar':
this.closeMobileSidebarDrawer();
break;
case 'panel':
this.setPanelHidden(true);
break;
case 'auxbar':
this.setAuxiliaryBarHidden(true);
break;
case 'editor':
break;
}
}));
}
createWorkbenchManagement(_instantiationService: IInstantiationService): void {
}
private createGridDescriptor(): ISerializedGrid {
const { width, height } = this._mainContainerDimension;
return this.createDesktopGridDescriptor(width, height);
}
private createDesktopGridDescriptor(width: number, height: number): ISerializedGrid {
const sizes = this.layoutPolicy.getPartSizes(width, height);
const sideBarSize = this.partVisibility.sidebar ? sizes.sideBarSize : Math.max(sizes.sideBarSize, 250);
const auxiliaryBarSize = this.partVisibility.auxiliaryBar ? sizes.auxiliaryBarSize : Math.max(sizes.auxiliaryBarSize, 300);
const panelSize = this.partVisibility.panel ? sizes.panelSize : Math.max(sizes.panelSize, 250);
const editorSize = 600;
const titleBarHeight = this.titleBarPartView?.minimumHeight ?? 30;
const effectiveSideBarWidth = this.partVisibility.sidebar ? sideBarSize : 0;
const rightSectionWidth = Math.max(0, width - effectiveSideBarWidth);
const effectiveAuxBarWidth = this.partVisibility.auxiliaryBar ? auxiliaryBarSize : 0;
const effectiveEditorWidth = this.partVisibility.editor ? editorSize : 0;
const chatBarWidth = Math.max(0, rightSectionWidth - effectiveAuxBarWidth - effectiveEditorWidth);
const contentHeight = Math.max(0, height - titleBarHeight);
const topRightHeight = Math.max(0, contentHeight - panelSize);
const isPhone = this.layoutPolicy.viewportClass.get() === 'phone';
const titleBarNode: ISerializedLeafNode = {
type: 'leaf',
data: { type: Parts.TITLEBAR_PART },
size: titleBarHeight,
visible: !isPhone
};
const sideBarNode: ISerializedLeafNode = {
type: 'leaf',
data: { type: Parts.SIDEBAR_PART },
size: sideBarSize,
visible: this.partVisibility.sidebar
};
const auxiliaryBarNode: ISerializedLeafNode = {
type: 'leaf',
data: { type: Parts.AUXILIARYBAR_PART },
size: auxiliaryBarSize,
visible: this.partVisibility.auxiliaryBar
};
const chatBarNode: ISerializedLeafNode = {
type: 'leaf',
data: { type: Parts.CHATBAR_PART },
size: chatBarWidth,
visible: this.partVisibility.chatBar
};
const editorNode: ISerializedLeafNode = {
type: 'leaf',
data: { type: Parts.EDITOR_PART },
size: editorSize,
visible: this.partVisibility.editor
};
const panelNode: ISerializedLeafNode = {
type: 'leaf',
data: { type: Parts.PANEL_PART },
size: panelSize,
visible: this.partVisibility.panel
};
const topRightSection: ISerializedNode = {
type: 'branch',
data: [chatBarNode, editorNode, auxiliaryBarNode],
size: topRightHeight
};
const rightSection: ISerializedNode = {
type: 'branch',
data: [topRightSection, panelNode],
size: rightSectionWidth
};
const contentSection: ISerializedNode = {
type: 'branch',
data: [sideBarNode, rightSection],
size: contentHeight
};
const result: ISerializedGrid = {
root: {
type: 'branch',
size: width,
data: [
titleBarNode,
contentSection
]
},
orientation: Orientation.VERTICAL,
width,
height
};
return result;
}
private _previousViewportClass: string | undefined;
layout(): void {
this._mainContainerDimension = getClientArea(
this.mainWindowFullscreen ? mainWindow.document.body : this.parent
);
const previousClass = this._previousViewportClass;
this.layoutPolicy.update(this._mainContainerDimension.width, this._mainContainerDimension.height);
const currentClass = this.layoutPolicy.viewportClass.get();
this.mainContainer.classList.toggle(LayoutClasses.PHONE_LAYOUT, currentClass === 'phone');
if (previousClass !== undefined && previousClass !== currentClass) {
if (currentClass === 'phone' && !this.mobileTopBarElement) {
this.createMobileTitlebar();
this.workbenchGrid.setViewVisible(this.titleBarPartView, false);
const defaults = this.layoutPolicy.getPartVisibilityDefaults();
if (this.partVisibility.sidebar !== defaults.sidebar) {
this.setSideBarHidden(!defaults.sidebar);
}
if (this.partVisibility.auxiliaryBar !== defaults.auxiliaryBar) {
this.setAuxiliaryBarHidden(!defaults.auxiliaryBar);
}
if (this.partVisibility.panel !== defaults.panel) {
this.setPanelHidden(!defaults.panel);
}
} else if (currentClass !== 'phone' && this.mobileTopBarElement) {
this.mobileTopBarDisposables.clear();
this.mobileTopBarElement = undefined;
this.workbenchGrid.setViewVisible(this.titleBarPartView, true);
const defaults = this.layoutPolicy.getPartVisibilityDefaults();
if (this.partVisibility.sidebar !== defaults.sidebar) {
this.setSideBarHidden(!defaults.sidebar);
}
if (this.partVisibility.chatBar !== defaults.chatBar) {
this.setChatBarHidden(!defaults.chatBar);
}
if (this.partVisibility.auxiliaryBar !== defaults.auxiliaryBar) {
this.setAuxiliaryBarHidden(!defaults.auxiliaryBar);
}
if (this.partVisibility.panel !== defaults.panel) {
this.setPanelHidden(!defaults.panel);
}
}
for (const partId of [Parts.CHATBAR_PART, Parts.SIDEBAR_PART, Parts.AUXILIARYBAR_PART, Parts.PANEL_PART]) {
this.parts.get(partId)?.updateStyles();
}
}
this._previousViewportClass = currentClass;
this.logService.trace(`Workbench#layout, height: ${this._mainContainerDimension.height}, width: ${this._mainContainerDimension.width}`);
size(this.mainContainer, this._mainContainerDimension.width, this._mainContainerDimension.height);
const mobileTopBarHeight = this.mobileTopBarElement?.offsetHeight ?? 0;
const gridHeight = this._mainContainerDimension.height - mobileTopBarHeight;
this.workbenchGrid.layout(this._mainContainerDimension.width, gridHeight);
this.layoutMobileSidebar();
this.handleContainerDidLayout(this.mainContainer, this._mainContainerDimension);
}
private layoutMobileSidebar(): void {
const sidebarContainer = this.getContainer(mainWindow, Parts.SIDEBAR_PART);
const sidebarPart = this.getPart(Parts.SIDEBAR_PART);
if (!sidebarContainer) {
return;
}
const isPhone = this.layoutPolicy.viewportClass.get() === 'phone';
if (!isPhone || !this.partVisibility.sidebar) {
sidebarContainer.classList.remove('mobile-overlay-sidebar');
return;
}
sidebarContainer.classList.add('mobile-overlay-sidebar');
const topBarHeight = this.mobileTopBarElement?.offsetHeight ?? 48;
const drawerWidth = this._mainContainerDimension.width;
const drawerHeight = Math.max(0, this._mainContainerDimension.height - topBarHeight);
sidebarPart.layout(drawerWidth, drawerHeight, topBarHeight, 0);
}
private handleContainerDidLayout(container: HTMLElement, dimension: IDimension): void {
this._onDidLayoutContainer.fire({ container, dimension });
if (container === this.mainContainer) {
this._onDidLayoutMainContainer.fire(dimension);
}
if (container === this.activeContainer) {
this._onDidLayoutActiveContainer.fire(dimension);
}
}
getLayoutClasses(): string[] {
return coalesce([
!this.partVisibility.sidebar ? LayoutClasses.SIDEBAR_HIDDEN : undefined,
!this.partVisibility.editor ? LayoutClasses.MAIN_EDITOR_AREA_HIDDEN : undefined,
!this.partVisibility.panel ? LayoutClasses.PANEL_HIDDEN : undefined,
!this.partVisibility.auxiliaryBar ? LayoutClasses.AUXILIARYBAR_HIDDEN : undefined,
!this.partVisibility.chatBar ? LayoutClasses.CHATBAR_HIDDEN : undefined,
LayoutClasses.STATUSBAR_HIDDEN,
this.mainWindowFullscreen ? LayoutClasses.FULLSCREEN : undefined,
this.layoutPolicy.viewportClass.get() === 'phone' ? LayoutClasses.PHONE_LAYOUT : undefined,
]);
}
registerPart(part: Part): IDisposable {
const id = part.getId();
this.parts.set(id, part);
return toDisposable(() => this.parts.delete(id));
}
getPart(key: Parts): Part {
const part = this.parts.get(key);
if (!part) {
throw new Error(`Unknown part ${key}`);
}
return part;
}
hasFocus(part: Parts): boolean {
const container = this.getContainer(mainWindow, part);
if (!container) {
return false;
}
const activeElement = getActiveElement();
if (!activeElement) {
return false;
}
return isAncestorUsingFlowTo(activeElement, container);
}
focusPart(part: MULTI_WINDOW_PARTS, targetWindow: Window): void;
focusPart(part: SINGLE_WINDOW_PARTS): void;
focusPart(part: Parts, targetWindow: Window = mainWindow): void {
switch (part) {
case Parts.EDITOR_PART:
this.editorGroupService.activeGroup.focus();
break;
case Parts.PANEL_PART:
this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel)?.focus();
break;
case Parts.SIDEBAR_PART:
this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)?.focus();
break;
case Parts.AUXILIARYBAR_PART:
this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar)?.focus();
break;
case Parts.CHATBAR_PART:
this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.ChatBar)?.focus();
break;
default: {
const container = this.getContainer(targetWindow, part);
container?.focus();
}
}
}
focus(): void {
this.focusPart(Parts.CHATBAR_PART);
}
getContainer(targetWindow: Window): HTMLElement;
getContainer(targetWindow: Window, part: Parts): HTMLElement | undefined;
getContainer(targetWindow: Window, part?: Parts): HTMLElement | undefined {
if (typeof part === 'undefined') {
return this.getContainerFromDocument(targetWindow.document);
}
if (targetWindow === mainWindow) {
return this.parts.get(part)?.getContainer();
}
if (part === Parts.EDITOR_PART) {
const container = this.getContainerFromDocument(targetWindow.document);
const partCandidate = this.editorGroupService.getPart(container);
if (partCandidate instanceof Part) {
return partCandidate.getContainer();
}
}
return undefined;
}
whenContainerStylesLoaded(_window: CodeWindow): Promise<void> | undefined {
return undefined;
}
isActivityBarHidden(): boolean {
return true;
}
isVisible(part: SINGLE_WINDOW_PARTS): boolean;
isVisible(part: MULTI_WINDOW_PARTS, targetWindow: Window): boolean;
isVisible(part: Parts, targetWindow?: Window): boolean {
switch (part) {
case Parts.TITLEBAR_PART:
return this.layoutPolicy.viewportClass.get() !== 'phone';
case Parts.SIDEBAR_PART:
return this.partVisibility.sidebar;
case Parts.AUXILIARYBAR_PART:
return this.partVisibility.auxiliaryBar;
case Parts.EDITOR_PART:
return this.partVisibility.editor;
case Parts.PANEL_PART:
return this.partVisibility.panel;
case Parts.CHATBAR_PART:
return this.partVisibility.chatBar;
case Parts.ACTIVITYBAR_PART:
case Parts.STATUSBAR_PART:
case Parts.BANNER_PART:
default:
return false;
}
}
setPartHidden(hidden: boolean, part: Parts): void {
switch (part) {
case Parts.SIDEBAR_PART:
this.setSideBarHidden(hidden);
break;
case Parts.AUXILIARYBAR_PART:
this.setAuxiliaryBarHidden(hidden);
break;
case Parts.EDITOR_PART:
this.setEditorHidden(hidden);
break;
case Parts.PANEL_PART:
this.setPanelHidden(hidden);
break;
case Parts.CHATBAR_PART:
this.setChatBarHidden(hidden);
break;
}
}
private setSideBarHidden(hidden: boolean): void {
if (this.partVisibility.sidebar === !hidden) {
return;
}
this.partVisibility.sidebar = !hidden;
this.mainContainer.classList.toggle(LayoutClasses.SIDEBAR_HIDDEN, hidden);
this.workbenchGrid.setViewVisible(
this.sideBarPartView,
!hidden,
);
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)) {
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.Sidebar);
}
if (!hidden && !this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)) {
const viewletToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.Sidebar) ??
this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id;
if (viewletToOpen) {
this.paneCompositeService.openPaneComposite(viewletToOpen, ViewContainerLocation.Sidebar);
}
}
this.layoutMobileSidebar();
}
private setAuxiliaryBarHidden(hidden: boolean): void {
if (this.partVisibility.auxiliaryBar === !hidden) {
return;
}
this.partVisibility.auxiliaryBar = !hidden;
this.mainContainer.classList.toggle(LayoutClasses.AUXILIARYBAR_HIDDEN, hidden);
this.workbenchGrid.setViewVisible(
this.auxiliaryBarPartView,
!hidden,
);
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar)) {
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.AuxiliaryBar);
}
if (!hidden && !this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar)) {
const paneCompositeToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.AuxiliaryBar) ??
this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.AuxiliaryBar)?.id;
if (paneCompositeToOpen) {
this.paneCompositeService.openPaneComposite(paneCompositeToOpen, ViewContainerLocation.AuxiliaryBar);
}
}
}
private setEditorHidden(hidden: boolean): void {
if (this.partVisibility.editor === !hidden) {
return;
}
if (hidden && this._editorMaximized) {
this.setEditorMaximized(false);
}
this.partVisibility.editor = !hidden;
this.mainContainer.classList.toggle(LayoutClasses.MAIN_EDITOR_AREA_HIDDEN, hidden);
if (this.editorPartView) {
this.workbenchGrid.setViewVisible(this.editorPartView, !hidden);
}
}
private setPanelHidden(hidden: boolean): void {
if (this.partVisibility.panel === !hidden) {
return;
}
if (hidden && this.workbenchGrid.hasMaximizedView()) {
this.workbenchGrid.exitMaximizedView();
}
const panelHadFocus = !hidden || this.hasFocus(Parts.PANEL_PART);
this.partVisibility.panel = !hidden;
this.mainContainer.classList.toggle(LayoutClasses.PANEL_HIDDEN, hidden);
this.workbenchGrid.setViewVisible(
this.panelPartView,
!hidden,
);
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel)) {
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.Panel);
if (panelHadFocus) {
this.focusPart(Parts.CHATBAR_PART);
}
}
if (!hidden) {
if (!this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel)) {
const panelToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.Panel) ??
this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Panel)?.id;
if (panelToOpen) {
this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.Panel);
}
}
this.focusPart(Parts.PANEL_PART);
}
}
private setChatBarHidden(hidden: boolean): void {
if (this.partVisibility.chatBar === !hidden) {
return;
}
this.partVisibility.chatBar = !hidden;
this.mainContainer.classList.toggle(LayoutClasses.CHATBAR_HIDDEN, hidden);
this.workbenchGrid.setViewVisible(this.chatBarPartView, !hidden);
if (hidden && this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.ChatBar)) {
this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.ChatBar);
}
if (!hidden && !this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.ChatBar)) {
const paneCompositeToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.ChatBar) ??
this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.ChatBar)?.id;
if (paneCompositeToOpen) {
this.paneCompositeService.openPaneComposite(paneCompositeToOpen, ViewContainerLocation.ChatBar);
}
}
}
getSideBarPosition(): Position {
return Position.LEFT;
}
getPanelPosition(): Position {
return Position.BOTTOM;
}
setPanelPosition(_position: Position): void {
}
getPanelAlignment(): PanelAlignment {
return 'justify';
}
setPanelAlignment(_alignment: PanelAlignment): void {
}
getSize(part: Parts): IViewSize {
const view = this.getPartView(part);
if (!view) {
return { width: 0, height: 0 };
}
return this.workbenchGrid.getViewSize(view);
}
setSize(part: Parts, size: IViewSize): void {
const view = this.getPartView(part);
if (view) {
this.workbenchGrid.resizeView(view, size);
}
}
resizePart(part: Parts, sizeChangeWidth: number, sizeChangeHeight: number): void {
const view = this.getPartView(part);
if (!view) {
return;
}
const currentSize = this.workbenchGrid.getViewSize(view);
this.workbenchGrid.resizeView(view, {
width: currentSize.width + sizeChangeWidth,
height: currentSize.height + sizeChangeHeight
});
}
private getPartView(part: Parts): ISerializableView | undefined {
switch (part) {
case Parts.TITLEBAR_PART:
return this.titleBarPartView;
case Parts.SIDEBAR_PART:
return this.sideBarPartView;
case Parts.AUXILIARYBAR_PART:
return this.auxiliaryBarPartView;
case Parts.EDITOR_PART:
return this.editorPartView;
case Parts.PANEL_PART:
return this.panelPartView;
case Parts.CHATBAR_PART:
return this.chatBarPartView;
default:
return undefined;
}
}
getMaximumEditorDimensions(_container: HTMLElement): IDimension {
const sidebarWidth = this.partVisibility.sidebar ? this.workbenchGrid.getViewSize(this.sideBarPartView).width : 0;
const auxiliaryBarWidth = this.partVisibility.auxiliaryBar ? this.workbenchGrid.getViewSize(this.auxiliaryBarPartView).width : 0;
const panelHeight = this.partVisibility.panel ? this.workbenchGrid.getViewSize(this.panelPartView).height : 0;
const titleBarHeight = this.workbenchGrid.getViewSize(this.titleBarPartView).height;
return new Dimension(
this._mainContainerDimension.width - sidebarWidth - auxiliaryBarWidth,
this._mainContainerDimension.height - titleBarHeight - panelHeight
);
}
toggleMaximizedPanel(): void {
if (!this.workbenchGrid) {
return;
}
if (this.isPanelMaximized()) {
this.workbenchGrid.exitMaximizedView();
} else {
this.workbenchGrid.maximizeView(this.panelPartView, [this.titleBarPartView, this.sideBarPartView]);
}
}
isPanelMaximized(): boolean {
if (!this.workbenchGrid) {
return false;
}
return this.workbenchGrid.isViewMaximized(this.panelPartView);
}
toggleMaximizedAuxiliaryBar(): void {
}
setAuxiliaryBarMaximized(_maximized: boolean): boolean {
return false;
}
isAuxiliaryBarMaximized(): boolean {
return false;
}
isEditorMaximized(): boolean {
return this._editorMaximized;
}
setEditorMaximized(maximized: boolean): void {
if (maximized === this._editorMaximized) {
return;
}
if (maximized) {
this._editorLastNonMaximizedVisibility = {
sidebar: this.partVisibility.sidebar,
auxiliaryBar: this.partVisibility.auxiliaryBar,
editor: this.partVisibility.editor,
panel: this.partVisibility.panel,
chatBar: this.partVisibility.chatBar,
};
if (!this.partVisibility.editor) {
this.setEditorHidden(false);
}
if (this.partVisibility.sidebar) {
this.setSideBarHidden(true);
}
if (this.partVisibility.chatBar) {
this.setChatBarHidden(true);
}
this._editorMaximized = true;
} else {
const state = this._editorLastNonMaximizedVisibility;
this.setSideBarHidden(!state?.sidebar);
this.setChatBarHidden(!state?.chatBar);
this._editorMaximized = false;
}
this._onDidChangeEditorMaximized.fire();
}
toggleZenMode(): void {
}
toggleMenuBar(): void {
}
isMainEditorLayoutCentered(): boolean {
return false;
}
centerMainEditorLayout(_active: boolean): void {
}
hasMainWindowBorder(): boolean {
return false;
}
getMainWindowBorderRadius(): string | undefined {
return undefined;
}
isWindowMaximized(targetWindow: Window): boolean {
return this.maximized.has(getWindowId(targetWindow));
}
updateWindowMaximizedState(targetWindow: Window, maximized: boolean): void {
const windowId = getWindowId(targetWindow);
if (maximized) {
this.maximized.add(windowId);
if (targetWindow === mainWindow) {
this.mainContainer.classList.add(LayoutClasses.MAXIMIZED);
}
} else {
this.maximized.delete(windowId);
if (targetWindow === mainWindow) {
this.mainContainer.classList.remove(LayoutClasses.MAXIMIZED);
}
}
this._onDidChangeWindowMaximized.fire({ windowId, maximized });
}
getVisibleNeighborPart(part: Parts, direction: Direction): Parts | undefined {
if (!this.workbenchGrid) {
return undefined;
}
const view = this.getPartView(part);
if (!view) {
return undefined;
}
const neighbor = this.workbenchGrid.getNeighborViews(view, direction, false);
if (neighbor.length === 0) {
return undefined;
}
const neighborView = neighbor[0];
if (neighborView === this.titleBarPartView) {
return Parts.TITLEBAR_PART;
}
if (neighborView === this.sideBarPartView) {
return Parts.SIDEBAR_PART;
}
if (neighborView === this.auxiliaryBarPartView) {
return Parts.AUXILIARYBAR_PART;
}
if (neighborView === this.editorPartView) {
return Parts.EDITOR_PART;
}
if (neighborView === this.panelPartView) {
return Parts.PANEL_PART;
}
if (neighborView === this.chatBarPartView) {
return Parts.CHATBAR_PART;
}
return undefined;
}
isRestored(): boolean {
return this.restored;
}
setRestored(): void {
this.restored = true;
this.restoredPromise.complete();
}
registerNotifications(delegate: { onDidChangeNotificationsVisibility: Event<boolean> }): void {
this._register(delegate.onDidChangeNotificationsVisibility(visible => this._onDidChangeNotificationsVisibility.fire(visible)));
}
}