diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 0a13a59a1b1..730e6bdf6ba 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -692,6 +692,8 @@ "--tab-border-top-color", "--tab-dirty-border-top-color", "--tabs-border-bottom-color", + "--tab-sizing-current-width", + "--tab-sizing-fixed-max-width", "--testMessageDecorationFontFamily", "--testMessageDecorationFontSize", "--title-border-bottom-color", diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 7c70e77c030..ac1cc706a91 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -28,6 +28,7 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { highlightModifiedTabs: false, tabCloseButton: 'right', tabSizing: 'fit', + tabSizingFixedMaxWidth: 160, pinnedTabSizing: 'normal', titleScrollbarSizing: 'default', focusRecentEditorAfterClose: true, diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 736b70e040e..4a635ae885f 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1337,6 +1337,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private doCloseEditor(editor: EditorInput, focusNext = (this.accessor.activeGroup === this), internalOptions?: IInternalEditorCloseOptions): void { let index: number | undefined; + // Forward to title control unless skipped via internal options + if (!internalOptions?.skipTitleUpdate) { + this.titleAreaControl.beforeCloseEditor(editor, index); + } + // Closing the active editor of the group is a bit more work if (this.model.isActive(editor)) { index = this.doCloseActiveEditor(focusNext, internalOptions); diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index df08d558acf..364234c1d31 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -111,7 +111,9 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-right, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact) { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact), +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.tab-actions-right, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.tab-actions-off:not(.sticky-compact) { padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab actions is not left (unless sticky-compact) */ } @@ -121,6 +123,19 @@ flex-shrink: 0; } +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed { + min-width: var(--tab-sizing-current-width, 50px); + max-width: var(--tab-sizing-current-width, var(--tab-sizing-fixed-max-width, 160px)); + flex: 1 0 0; /* all tabs are evenly sized and grow */ +} + +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.last-in-row { + /* prevent last tab in a row from moving to next row when tab widths are + * fixed in case rounding errors make the fixed tabs grow over the size + * of the tabs container */ + min-width: calc(var(--tab-sizing-current-width, 50px) - 1px); +} + .monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab.sizing-fit.last-in-row:not(:last-child) { flex-grow: 1; /* grow the last tab in a row for a more homogeneous look except for last row (#113801) */ } @@ -134,20 +149,23 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-compact, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-compact, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-shrink { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-shrink { - /** Sticky compact/shrink tabs do not scroll in case of overflow and are always above unsticky tabs which scroll under */ + /** Sticky compact/shrink/fixed tabs do not scroll in case of overflow and are always above unsticky tabs which scroll under */ position: sticky; z-index: 8; - /** Sticky compact/shrink tabs are even and never grow */ + /** Sticky compact/shrink/fixed tabs are even and never grow */ flex-basis: 0; flex-grow: 0; } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-compact { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-compact { /** Sticky compact tabs have a fixed width of 38px */ width: 38px; @@ -156,7 +174,8 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-shrink { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-shrink { /** Sticky shrink tabs have a fixed width of 80px */ width: 80px; @@ -166,22 +185,27 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fit.sticky-compact, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fixed.sticky-compact, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-shrink.sticky-shrink { - position: static; /** disable sticky positions for sticky compact/shrink tabs if the available space is too little */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fixed.sticky-shrink { + position: static; /** disable sticky positions for sticky compact/shrink/fixed tabs if the available space is too little */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-left::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-off::after { content: ''; display: flex; flex: 0; width: 5px; /* reserve space to hide tab fade when close button is left or off (fixes https://github.com/microsoft/vscode/issues/45728) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-left { min-width: 80px; /* make more room for close button when it shows to the left */ - padding-right: 5px; /* we need less room when sizing is shrink */ + padding-right: 5px; /* we need less room when sizing is shrink/fixed */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged { @@ -244,11 +268,13 @@ line-height: 35px; /* aligns icon and label vertically centered in the tab */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed .tab-label { position: relative; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label > .monaco-icon-label-container::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label > .monaco-icon-label-container::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .tab-label > .monaco-icon-label-container::after { content: ''; /* enables a linear gradient to overlay the end of the label when tabs overflow */ position: absolute; right: 0; @@ -261,12 +287,14 @@ height: calc(100% - 2px); } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:focus > .tab-label > .monaco-icon-label-container::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:focus > .tab-label > .monaco-icon-label-container::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed:focus > .tab-label > .monaco-icon-label-container::after { opacity: 0; /* when tab has the focus this shade breaks the tab border (fixes https://github.com/microsoft/vscode/issues/57819) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label.tab-label-has-badge::after { - padding-right: 5px; /* with tab sizing shrink and badges, we want a right-padding because the close button is hidden */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label.tab-label-has-badge::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .tab-label.tab-label-has-badge::after { + padding-right: 5px; /* with tab sizing shrink/fixed and badges, we want a right-padding because the close button is hidden */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky-compact:not(.has-icon) .monaco-icon-label { @@ -278,13 +306,16 @@ overflow: visible; /* fixes https://github.com/microsoft/vscode/issues/20182 */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container { text-overflow: clip; flex: none; } .monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, -.monaco-workbench.hc-light .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container { +.monaco-workbench.hc-light .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, +.monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container, +.monaco-workbench.hc-light .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container { text-overflow: ellipsis; } @@ -300,15 +331,20 @@ width: 28px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed > .tab-actions { flex: 0; - overflow: hidden; /* let the tab actions be pushed out of view when sizing is set to shrink to make more room */ + overflow: hidden; /* let the tab actions be pushed out of view when sizing is set to shrink/fixed to make more room */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.tab-actions-right.sizing-shrink > .tab-actions, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.tab-actions-right.sizing-shrink > .tab-actions, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink:hover > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions:focus-within { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions:focus-within, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.tab-actions-right.sizing-fixed > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.tab-actions-right.sizing-fixed > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed:hover > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed > .tab-actions:focus-within { overflow: visible; /* ...but still show the tab actions on hover, focus and when dirty or sticky */ } @@ -366,8 +402,9 @@ padding-right: 10px; /* give a little bit more room if tab actions is off */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off:not(.sticky-compact) { - padding-right: 5px; /* we need less room when sizing is shrink (unless tab is sticky-compact) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off:not(.sticky-compact), +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-off:not(.sticky-compact) { + padding-right: 5px; /* we need less room when sizing is shrink/fixed (unless tab is sticky-compact) */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty-border-top > .tab-actions { diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 93f7e6db6b2..dc858785d81 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -143,6 +143,10 @@ export class NoTabsTitleControl extends TitleControl { } } + beforeCloseEditor(): void { + // Nothing to do before closing an editor + } + closeEditor(editor: EditorInput, index: number | undefined): void { this.ifActiveEditorChanged(() => this.redraw()); } diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 7474de2821c..4d53861a144 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -108,6 +108,7 @@ export class TabsTitleControl extends TitleControl { private tabsContainer: HTMLElement | undefined; private editorToolbarContainer: HTMLElement | undefined; private tabsScrollbar: ScrollableElement | undefined; + private tabSizingFixedDisposables: DisposableStore | undefined; private readonly closeEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL)); private readonly unpinEditorAction = this._register(this.instantiationService.createInstance(UnpinEditorAction, UnpinEditorAction.ID, UnpinEditorAction.LABEL)); @@ -130,6 +131,7 @@ export class TabsTitleControl extends TitleControl { private path: IPath = isWindows ? win32 : posix; private lastMouseWheelEventTime = 0; + private isMouseOverTabs = false; constructor( parent: HTMLElement, @@ -176,6 +178,9 @@ export class TabsTitleControl extends TitleControl { this.tabsContainer.classList.add('tabs-container'); this._register(Gesture.addTarget(this.tabsContainer)); + this.tabSizingFixedDisposables = this._register(new DisposableStore()); + this.updateTabSizing(false); + // Tabs Scrollbar this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer)); this.tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode()); @@ -222,6 +227,44 @@ export class TabsTitleControl extends TitleControl { }); } + private updateTabSizing(fromEvent: boolean): void { + const [tabsContainer, tabSizingFixedDisposables] = assertAllDefined(this.tabsContainer, this.tabSizingFixedDisposables); + + tabSizingFixedDisposables.clear(); + + const options = this.accessor.partOptions; + if (options.tabSizing === 'fixed') { + tabsContainer.style.setProperty('--tab-sizing-fixed-max-width', `${options.tabSizingFixedMaxWidth}px`); + + // For https://github.com/microsoft/vscode/issues/40290 we want to + // preserve the current tab widths as long as the mouse is over the + // tabs so that you can quickly close them via mouse click. For that + // we track mouse movements over the tabs container. + + tabSizingFixedDisposables.add(addDisposableListener(tabsContainer, EventType.MOUSE_ENTER, () => { + this.isMouseOverTabs = true; + })); + tabSizingFixedDisposables.add(addDisposableListener(tabsContainer, EventType.MOUSE_LEAVE, () => { + this.isMouseOverTabs = false; + this.updateTabsFixedWidth(false); + })); + } else if (fromEvent) { + tabsContainer.style.removeProperty('--tab-sizing-fixed-max-width'); + this.updateTabsFixedWidth(false); + } + } + + private updateTabsFixedWidth(fixed: boolean): void { + this.forEachTab((editor, index, tabContainer) => { + if (fixed) { + const { width } = tabContainer.getBoundingClientRect(); + tabContainer.style.setProperty('--tab-sizing-current-width', `${width}px`); + } else { + tabContainer.style.removeProperty('--tab-sizing-current-width'); + } + }); + } + private getTabsScrollbarSizing(): number { if (this.accessor.partOptions.titleScrollbarSizing !== 'large') { return TabsTitleControl.SCROLLBAR_SIZES.default; @@ -514,6 +557,18 @@ export class TabsTitleControl extends TitleControl { labelA.ariaLabel === labelB.ariaLabel; } + beforeCloseEditor(): void { + + // Fix tabs width if the mouse is over tabs and + // before closing a tab when tab sizing is 'fixed'. + // This helps keeping the close button stable under + // the mouse and allows for rapid closing of tabs. + + if (this.isMouseOverTabs && this.accessor.partOptions.tabSizing === 'fixed') { + this.updateTabsFixedWidth(true); + } + } + closeEditor(editor: EditorInput, index: number | undefined): void { this.handleClosedEditors(index); } @@ -663,6 +718,14 @@ export class TabsTitleControl extends TitleControl { this.updateTabsScrollbarSizing(); } + // Update tabs sizing + if ( + oldOptions.tabSizingFixedMaxWidth !== newOptions.tabSizingFixedMaxWidth || + oldOptions.tabSizing !== newOptions.tabSizing + ) { + this.updateTabSizing(true); + } + // Redraw tabs when other options change if ( oldOptions.labelFormat !== newOptions.labelFormat || @@ -1229,7 +1292,7 @@ export class TabsTitleControl extends TitleControl { } const tabSizing = isTabSticky && options.pinnedTabSizing === 'shrink' ? 'shrink' /* treat sticky shrink tabs as tabSizing: 'shrink' */ : options.tabSizing; - for (const option of ['fit', 'shrink']) { + for (const option of ['fit', 'shrink', 'fixed']) { tabContainer.classList.toggle(`sizing-${option}`, tabSizing === option); } @@ -2109,7 +2172,7 @@ registerThemingParticipant((theme, collector) => { `); } - // Fade out styles via linear gradient (when tabs are set to shrink) + // Fade out styles via linear gradient (when tabs are set to shrink or fixed) // But not when: // - in high contrast theme // - if we have a contrast border (which draws an outline - https://github.com/microsoft/vscode/issues/109117) @@ -2132,11 +2195,13 @@ registerThemingParticipant((theme, collector) => { // Adjust gradient for focused and unfocused hover background const makeTabHoverBackgroundRule = (color: Color, colorDrag: Color, hasFocus = false) => ` - .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container${hasFocus ? '.active' : ''} > .title .tabs-container > .tab.sizing-shrink:not(.dragged):not(.sticky-compact):hover > .tab-label > .monaco-icon-label-container::after { + .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container${hasFocus ? '.active' : ''} > .title .tabs-container > .tab.sizing-shrink:not(.dragged):not(.sticky-compact):hover > .tab-label > .monaco-icon-label-container::after, + .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container${hasFocus ? '.active' : ''} > .title .tabs-container > .tab.sizing-fixed:not(.dragged):not(.sticky-compact):hover > .tab-label > .monaco-icon-label-container::after { background: linear-gradient(to left, ${color}, transparent) !important; } - .monaco-workbench .part.editor > .content.dragged-over .editor-group-container${hasFocus ? '.active' : ''} > .title .tabs-container > .tab.sizing-shrink:not(.dragged):not(.sticky-compact):hover > .tab-label > .monaco-icon-label-container::after { + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container${hasFocus ? '.active' : ''} > .title .tabs-container > .tab.sizing-shrink:not(.dragged):not(.sticky-compact):hover > .tab-label > .monaco-icon-label-container::after, + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container${hasFocus ? '.active' : ''} > .title .tabs-container > .tab.sizing-fixed:not(.dragged):not(.sticky-compact):hover > .tab-label > .monaco-icon-label-container::after { background: linear-gradient(to left, ${colorDrag}, transparent) !important; } `; @@ -2160,18 +2225,22 @@ registerThemingParticipant((theme, collector) => { const adjustedColorDrag = editorDragAndDropBackground.flatten(adjustedTabDragBackground); collector.addRule(` .monaco-workbench .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.active):not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after, - .monaco-workbench .part.editor > .content.dragged-over .editor-group-container:not(.active) > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after { + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container:not(.active) > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after, + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-fixed.dragged-over:not(.active):not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after, + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container:not(.active) > .title .tabs-container > .tab.sizing-fixed.dragged-over:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after { background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important; } `); } const makeTabBackgroundRule = (color: Color, colorDrag: Color, focused: boolean, active: boolean) => ` - .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container${focused ? '.active' : ':not(.active)'} > .title .tabs-container > .tab.sizing-shrink${active ? '.active' : ''}:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after { + .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container${focused ? '.active' : ':not(.active)'} > .title .tabs-container > .tab.sizing-shrink${active ? '.active' : ''}:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after, + .monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container${focused ? '.active' : ':not(.active)'} > .title .tabs-container > .tab.sizing-fixed${active ? '.active' : ''}:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after { background: linear-gradient(to left, ${color}, transparent); } - .monaco-workbench .part.editor > .content.dragged-over .editor-group-container${focused ? '.active' : ':not(.active)'} > .title .tabs-container > .tab.sizing-shrink${active ? '.active' : ''}:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after { + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container${focused ? '.active' : ':not(.active)'} > .title .tabs-container > .tab.sizing-shrink${active ? '.active' : ''}:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after, + .monaco-workbench .part.editor > .content.dragged-over .editor-group-container${focused ? '.active' : ':not(.active)'} > .title .tabs-container > .tab.sizing-fixed${active ? '.active' : ''}:not(.dragged):not(.sticky-compact) > .tab-label > .monaco-icon-label-container::after { background: linear-gradient(to left, ${colorDrag}, transparent); } `; diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 2eebc74f6a9..888a2987f77 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -418,6 +418,8 @@ export abstract class TitleControl extends Themable { abstract openEditors(editors: EditorInput[]): void; + abstract beforeCloseEditor(editor: EditorInput, index: number | undefined): void; + abstract closeEditor(editor: EditorInput, index: number | undefined): void; abstract closeEditors(editors: EditorInput[]): void; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 73941e9c331..c79b3a9de68 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -141,13 +141,20 @@ const registry = Registry.as(ConfigurationExtensions.Con }, 'workbench.editor.tabSizing': { 'type': 'string', - 'enum': ['fit', 'shrink'], + 'enum': ['fit', 'shrink', 'fixed'], 'default': 'fit', 'enumDescriptions': [ localize('workbench.editor.tabSizing.fit', "Always keep tabs large enough to show the full editor label."), - localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once.") + localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once."), + localize('workbench.editor.tabSizing.fixed', "Make all tabs the same size, while allowing them to get smaller when the available space is not enough to show all tabs at once.") ], - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the sizing of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the size of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + }, + 'workbench.editor.tabSizingFixedMaxWidth': { + 'type': 'number', + 'default': 160, + 'minimum': 50, + 'markdownDescription': localize('workbench.editor.tabSizingFixedMaxWidth', "Controls the maximum width of tabs when {0} is set to {1}.", '`#workbench.editor.tabSizing#`', '`fixed`') }, 'workbench.editor.pinnedTabSizing': { 'type': 'string', @@ -158,7 +165,7 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('workbench.editor.pinnedTabSizing.compact', "A pinned tab will show in a compact form with only icon or first letter of the editor name."), localize('workbench.editor.pinnedTabSizing.shrink', "A pinned tab shrinks to a compact fixed size showing parts of the editor name.") ], - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'pinnedTabSizing' }, "Controls the sizing of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'pinnedTabSizing' }, "Controls the size of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is disabled.") }, 'workbench.editor.splitSizing': { 'type': 'string', @@ -168,7 +175,7 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('workbench.editor.splitSizingDistribute', "Splits all the editor groups to equal parts."), localize('workbench.editor.splitSizingSplit', "Splits the active editor group to equal parts.") ], - 'description': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'splitSizing' }, "Controls the sizing of editor groups when splitting them.") + 'description': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'splitSizing' }, "Controls the size of editor groups when splitting them.") }, 'workbench.editor.splitOnDragAndDrop': { 'type': 'boolean', diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 58d17e8f95d..408a6bc955a 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1100,7 +1100,8 @@ interface IEditorPartConfiguration { scrollToSwitchTabs?: boolean; highlightModifiedTabs?: boolean; tabCloseButton?: 'left' | 'right' | 'off'; - tabSizing?: 'fit' | 'shrink'; + tabSizing?: 'fit' | 'shrink' | 'fixed'; + tabSizingFixedMaxWidth?: number; pinnedTabSizing?: 'normal' | 'compact' | 'shrink'; titleScrollbarSizing?: 'default' | 'large'; focusRecentEditorAfterClose?: boolean;