diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index c8cdf695fcb..8764654ad5a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -118,7 +118,7 @@ export interface IEditorGroupTitleHeight { /** * The height offset to e.g. use when drawing drop overlays. * This number may be smaller than `height` if the title control - * decides to have an `offset` that is within the title area + * decides to have an `offset` that is within the title control * (e.g. when breadcrumbs are enabled). */ offset: number; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index ee684781ba9..f013b140ff4 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -11,7 +11,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { Emitter, Relay } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor, IDomNodePagePosition } from 'vs/base/browser/dom'; +import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition } from 'vs/base/browser/dom'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; @@ -19,7 +19,6 @@ import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_HEADER_BORDER } from 'vs/workbench/common/theme'; import { ICloseEditorsFilter, GroupsOrder, ICloseEditorOptions, ICloseAllEditorsOptions, IEditorReplacement } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { MultiEditorTabsControl } from 'vs/workbench/browser/parts/editor/multiEditorTabsControl'; import { EditorPanes } from 'vs/workbench/browser/parts/editor/editorPanes'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { EditorProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; @@ -29,12 +28,10 @@ import { MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DeferredPromise, Promises, RunOnceWorker } from 'vs/base/common/async'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; -import { EditorTabsControl } from 'vs/workbench/browser/parts/editor/editorTabsControl'; import { IEditorGroupsAccessor, IEditorGroupView, fillActiveEditorViewState, EditorServiceImpl, IEditorGroupTitleHeight, IInternalEditorOpenOptions, IInternalMoveCopyOptions, IInternalEditorCloseOptions, IInternalEditorTitleControlOptions } from 'vs/workbench/browser/parts/editor/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAction } from 'vs/base/common/actions'; -import { SingleEditorTabsControl } from 'vs/workbench/browser/parts/editor/singleEditorTabsControl'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -55,6 +52,7 @@ import { TelemetryTrustedValue } from 'vs/platform/telemetry/common/telemetryUti import { defaultProgressBarStyles } from 'vs/platform/theme/browser/defaultStyles'; import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { EditorGroupWatermark } from 'vs/workbench/browser/parts/editor/editorGroupWatermark'; +import { EditorTitleControl } from 'vs/workbench/browser/parts/editor/editorTitleControl'; export class EditorGroupView extends Themable implements IEditorGroupView { @@ -118,7 +116,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private readonly scopedInstantiationService: IInstantiationService; private readonly titleContainer: HTMLElement; - private titleAreaControl: EditorTabsControl; + private readonly titleControl: EditorTitleControl; private readonly progressBar: ProgressBar; @@ -200,7 +198,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.element.appendChild(this.titleContainer); // Title control - this.titleAreaControl = this.createTitleAreaControl(); + this.titleControl = this._register(this.scopedInstantiationService.createInstance(EditorTitleControl, this.titleContainer, this.accessor, this)); // Editor container this.editorContainer = document.createElement('div'); @@ -458,24 +456,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.titleContainer.classList.toggle('show-file-icons', this.accessor.partOptions.showIcons); } - private createTitleAreaControl(): EditorTabsControl { - - // Clear old if existing - if (this.titleAreaControl) { - this.titleAreaControl.dispose(); - clearNode(this.titleContainer); - } - - // Create new based on options - if (this.accessor.partOptions.showTabs) { - this.titleAreaControl = this.scopedInstantiationService.createInstance(MultiEditorTabsControl, this.titleContainer, this.accessor, this); - } else { - this.titleAreaControl = this.scopedInstantiationService.createInstance(SingleEditorTabsControl, this.titleContainer, this.accessor, this); - } - - return this.titleAreaControl; - } - private restoreEditors(from: IEditorGroupView | ISerializedEditorGroupModel | null): Promise | undefined { if (this.count === 0) { return; // nothing to show @@ -702,26 +682,21 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Title container this.updateTitleContainer(); + // Title control + this.titleControl.updateOptions(event.oldPartOptions, event.newPartOptions); + // Title control Switch between showing tabs <=> not showing tabs if (event.oldPartOptions.showTabs !== event.newPartOptions.showTabs) { - // Recreate title control - this.createTitleAreaControl(); - // Re-layout this.relayout(); // Ensure to show active editor if any if (this.model.activeEditor) { - this.titleAreaControl.openEditor(this.model.activeEditor); + this.titleControl.openEditor(this.model.activeEditor); } } - // Just update title control - else { - this.titleAreaControl.updateOptions(event.oldPartOptions, event.newPartOptions); - } - // Styles this.updateStyles(); @@ -739,13 +714,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.pinEditor(editor); // Forward to title control - this.titleAreaControl.updateEditorDirty(editor); + this.titleControl.updateEditorDirty(editor); } private onDidChangeEditorLabel(editor: EditorInput): void { // Forward to title control - this.titleAreaControl.updateEditorLabel(editor); + this.titleControl.updateEditorLabel(editor); } private onDidVisibilityChange(visible: boolean): void { @@ -780,7 +755,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } get titleHeight(): IEditorGroupTitleHeight { - return this.titleAreaControl.getHeight(); + return this.titleControl.getHeight(); } notifyIndexChanged(newIndex: number): void { @@ -798,7 +773,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.element.classList.toggle('inactive', !isActive); // Update title control - this.titleAreaControl.setActive(isActive); + this.titleControl.setActive(isActive); // Update styles this.updateStyles(); @@ -925,7 +900,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to title control if (editor) { - this.titleAreaControl.pinEditor(editor); + this.titleControl.pinEditor(editor); } } } @@ -952,14 +927,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // title control and also make sure to emit this as an event const newIndexOfEditor = this.getIndexOfEditor(editor); if (newIndexOfEditor !== oldIndexOfEditor) { - this.titleAreaControl.moveEditor(editor, oldIndexOfEditor, newIndexOfEditor); + this.titleControl.moveEditor(editor, oldIndexOfEditor, newIndexOfEditor); } // Forward sticky state to title control if (sticky) { - this.titleAreaControl.stickEditor(editor); + this.titleControl.stickEditor(editor); } else { - this.titleAreaControl.unstickEditor(editor); + this.titleControl.unstickEditor(editor); } } } @@ -1119,7 +1094,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Show in title control after editor control because some actions depend on it // but respect the internal options in case title control updates should skip. if (!internalOptions?.skipTitleUpdate) { - this.titleAreaControl.openEditor(editor); + this.titleControl.openEditor(editor); } return openEditorPromise; @@ -1169,7 +1144,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { })); // Update the title control all at once with all editors - this.titleAreaControl.openEditors(inactiveEditors.map(({ editor }) => editor)); + this.titleControl.openEditors(inactiveEditors.map(({ editor }) => editor)); // Opening many editors at once can put any editor to be // the active one depending on options. As such, we simply @@ -1200,8 +1175,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // in source and target if the title update was skipped if (internalOptions.skipTitleUpdate) { const movedEditors = editors.map(({ editor }) => editor); - target.titleAreaControl.openEditors(movedEditors); - this.titleAreaControl.closeEditors(movedEditors); + target.titleControl.openEditors(movedEditors); + this.titleControl.closeEditors(movedEditors); } } @@ -1241,9 +1216,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.model.moveEditor(editor, moveToIndex); this.model.pin(editor); - // Forward to title area - this.titleAreaControl.moveEditor(editor, currentIndex, moveToIndex); - this.titleAreaControl.pinEditor(editor); + // Forward to title control + this.titleControl.moveEditor(editor, currentIndex, moveToIndex); + this.titleControl.pinEditor(editor); } private doMoveOrCopyEditorAcrossGroups(editor: EditorInput, target: EditorGroupView, openOptions?: IEditorOpenOptions, internalOptions?: IInternalMoveCopyOptions): void { @@ -1299,7 +1274,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // in target if the title update was skipped if (internalOptions.skipTitleUpdate) { const copiedEditors = editors.map(({ editor }) => editor); - target.titleAreaControl.openEditors(copiedEditors); + target.titleControl.openEditors(copiedEditors); } } @@ -1346,7 +1321,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to title control unless skipped via internal options if (!internalOptions?.skipTitleUpdate) { - this.titleAreaControl.beforeCloseEditor(editor); + this.titleControl.beforeCloseEditor(editor); } // Closing the active editor of the group is a bit more work @@ -1361,7 +1336,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to title control unless skipped via internal options if (!internalOptions?.skipTitleUpdate) { - this.titleAreaControl.closeEditor(editor); + this.titleControl.closeEditor(editor); } } @@ -1711,7 +1686,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to title control if (editors.length) { - this.titleAreaControl.closeEditors(editors); + this.titleControl.closeEditors(editors); } } @@ -1763,7 +1738,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Forward to title control if (editorsToClose.length) { - this.titleAreaControl.closeEditors(editorsToClose); + this.titleControl.closeEditors(editorsToClose); } } @@ -1922,16 +1897,16 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.lastLayout = { width, height, top, left }; this.element.classList.toggle('max-height-478px', height <= 478); - // Layout the title area first to receive the size it occupies - const titleAreaSize = this.titleAreaControl.layout({ + // Layout the title control first to receive the size it occupies + const titleControlSize = this.titleControl.layout({ container: new Dimension(width, height), available: new Dimension(width, height - this.editorPane.minimumHeight) }); // Pass the container width and remaining height to the editor layout - const editorHeight = Math.max(0, height - titleAreaSize.height); + const editorHeight = Math.max(0, height - titleControlSize.height); this.editorContainer.style.height = `${editorHeight}px`; - this.editorPane.layout({ width, height: editorHeight, top: top + titleAreaSize.height, left }); + this.editorPane.layout({ width, height: editorHeight, top: top + titleControlSize.height, left }); } relayout(): void { @@ -1956,8 +1931,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this._onWillDispose.fire(); - this.titleAreaControl.dispose(); - super.dispose(); } } diff --git a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts new file mode 100644 index 00000000000..371f29b0218 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Dimension, clearNode } from 'vs/base/browser/dom'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; +import { IEditorGroupsAccessor, IEditorGroupTitleHeight, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; +import { EditorTabsControl, IEditorTabsControlDimensions } from 'vs/workbench/browser/parts/editor/editorTabsControl'; +import { MultiEditorTabsControl } from 'vs/workbench/browser/parts/editor/multiEditorTabsControl'; +import { SingleEditorTabsControl } from 'vs/workbench/browser/parts/editor/singleEditorTabsControl'; +import { IEditorPartOptions } from 'vs/workbench/common/editor'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; + +export class EditorTitleControl extends Themable { + + private editorTabsControl: EditorTabsControl; + + constructor( + private parent: HTMLElement, + private accessor: IEditorGroupsAccessor, + private group: IEditorGroupView, + @IInstantiationService private instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService + ) { + super(themeService); + + this.editorTabsControl = this.createEditorTabsControl(); + } + + private createEditorTabsControl(): EditorTabsControl { + if (this.accessor.partOptions.showTabs) { + return this.instantiationService.createInstance(MultiEditorTabsControl, this.parent, this.accessor, this.group); + } + + return this.instantiationService.createInstance(SingleEditorTabsControl, this.parent, this.accessor, this.group); + } + + openEditor(editor: EditorInput): void { + return this.editorTabsControl.openEditor(editor); + } + + openEditors(editors: EditorInput[]): void { + return this.editorTabsControl.openEditors(editors); + } + + beforeCloseEditor(editor: EditorInput): void { + return this.editorTabsControl.beforeCloseEditor(editor); + } + + closeEditor(editor: EditorInput): void { + return this.editorTabsControl.closeEditor(editor); + } + + closeEditors(editors: EditorInput[]): void { + return this.editorTabsControl.closeEditors(editors); + } + + moveEditor(editor: EditorInput, fromIndex: number, targetIndex: number): void { + return this.editorTabsControl.moveEditor(editor, fromIndex, targetIndex); + } + + pinEditor(editor: EditorInput): void { + return this.editorTabsControl.pinEditor(editor); + } + + stickEditor(editor: EditorInput): void { + return this.editorTabsControl.stickEditor(editor); + } + + unstickEditor(editor: EditorInput): void { + return this.editorTabsControl.unstickEditor(editor); + } + + setActive(isActive: boolean): void { + return this.editorTabsControl.setActive(isActive); + } + + updateEditorLabel(editor: EditorInput): void { + return this.editorTabsControl.updateEditorLabel(editor); + } + + updateEditorDirty(editor: EditorInput): void { + return this.editorTabsControl.updateEditorDirty(editor); + } + + updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { + + // Update editor tabs control if options changed + if (oldOptions.showTabs !== newOptions.showTabs) { + + // Clear old + this.editorTabsControl.dispose(); + clearNode(this.parent); + + // Create new + this.editorTabsControl = this.createEditorTabsControl(); + } + + // Forward into editor tabs control + this.editorTabsControl.updateOptions(oldOptions, newOptions); + } + + layout(dimensions: IEditorTabsControlDimensions): Dimension { + return this.editorTabsControl.layout(dimensions); + } + + getHeight(): IEditorGroupTitleHeight { + return this.editorTabsControl.getHeight(); + } + + override dispose(): void { + this.editorTabsControl.dispose(); + + super.dispose(); + } +} diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 47bd76c264e..c8a5ce0ae17 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -1089,7 +1089,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { if (Array.isArray(data)) { const group = data[0]; if (group.identifier === this.group.id) { - return false; // groups cannot be dropped on title area it originates from + return false; // groups cannot be dropped on group it originates from } }