mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-24 17:31:37 +01:00
Add settings for fixed-width tabs (#181729)
* Add settings for fixed-width tabs This is meant at least partially to address #40290 and is a continuation of the unfinished work from #40750. * Only apply fixed width when the setting is on * Implement chrome-like tab width behavior Tabs shrink uniformly (down to a limit) but stay fixed-width when the mouse is over the tab bar. * Rename width setting to max width * Make the ifs more readable * Have event handlers only if the option is set * 🎨 * Handle sizing artifacts present with wrapping tabs To achieve this, it's best to remove the transition delay. * Rename setting to apply only for fixed-sized tabs * Set default fixed max tab width to 160px * Minor code tweaks from review comments * formatting Co-authored-by: Benjamin Pasero <benjamin.pasero@gmail.com> * Use disposable listeners * Remove redundant check for last-in-row * Apply fade gradient from shrink tabSizing to fixed * Trying to make the code cleaner and understandable * Remove transition On advice of @bpasero, removed transition because the editor doesn't really use transition that much. * some cleanup * fix typo * Simplify workaround for the overflow issue * 💄 * 💄 * Let the tabs fix their width before closing the editor * 💄 --------- Co-authored-by: Benjamin Pasero <benjamin.pasero@microsoft.com> Co-authored-by: Benjamin Pasero <benjamin.pasero@gmail.com>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -141,13 +141,20 @@ const registry = Registry.as<IConfigurationRegistry>(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<IConfigurationRegistry>(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<IConfigurationRegistry>(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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user