Skip to content

Commit

Permalink
Add settings for fixed-width tabs (#181729)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people committed May 22, 2023
1 parent 4d0b34b commit cbbd922
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 36 deletions.
2 changes: 2 additions & 0 deletions build/lib/stylelint/vscode-known-variables.json
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/browser/parts/editor/editor.ts
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/browser/parts/editor/editorGroupView.ts
Expand Up @@ -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);
Expand Down
83 changes: 60 additions & 23 deletions src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css
Expand Up @@ -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) */
}

Expand All @@ -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) */
}
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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;
}

Expand All @@ -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 */
}

Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts
Expand Up @@ -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());
}
Expand Down

0 comments on commit cbbd922

Please sign in to comment.