diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 1b1a39b3..39c4275b 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -432,7 +432,7 @@ export class UnpinEditorAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, ThemeIcon.asClassName(Codicon.pinned)); + super(id, label, ThemeIcon.asClassName(Codicon.starFull)); } override run(context?: IEditorCommandsContext): Promise { @@ -440,6 +440,24 @@ export class UnpinEditorAction extends Action { } } +export class PinEditorAction extends Action { + + static readonly ID = 'workbench.action.pinEditor'; + static readonly LABEL = localize('pinEditor', "Pin Editor"); + + constructor( + id: string, + label: string, + @ICommandService private readonly commandService: ICommandService + ) { + super(id, label, ThemeIcon.asClassName(Codicon.star)); + } + + override async run(context?: IEditorCommandsContext): Promise { + return this.commandService.executeCommand('workbench.action.pinEditor', undefined, context); + } +} + export class CloseEditorTabAction extends Action { static readonly ID = 'workbench.action.closeActiveEditor'; diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css index 93559402..dd6c233f 100644 --- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css @@ -385,7 +385,7 @@ .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/fixed to make more room */ + overflow: visible; /* ensure tab actions are always visible */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.tab-actions-right.sizing-shrink > .tab-actions, @@ -399,18 +399,8 @@ overflow: visible; /* ...but still show the tab actions on hover, focus and when dirty or sticky */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-action-off:not(.dirty) > .tab-actions, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky-compact > .tab-actions { - display: none; /* hide the tab actions when we are configured to hide it (unless dirty, but always when sticky-compact) */ -} - -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active > .tab-actions .action-label, /* always show tab actions for active tab */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-actions .action-label:focus, /* always show tab actions on focus */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover > .tab-actions .action-label, /* always show tab actions on hover */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:hover > .tab-actions .action-label, /* always show tab actions on hover */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.sticky:not(.pinned-action-off) > .tab-actions .action-label, /* always show tab actions for sticky tabs */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label { /* always show tab actions for dirty tabs */ - opacity: 1; + display: none; /* only hide tab actions when sticky-compact */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions .actions-container { @@ -444,11 +434,11 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky:not(.pinned-action-off) > .tab-actions .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-actions .action-label { - opacity: 0.5; /* show tab actions dimmed for inactive group */ + opacity: 1; } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions .action-label { - opacity: 0; + opacity: 1; } /* Tab Actions: Off */ diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index b23be82e..b5883980 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -35,7 +35,7 @@ import { MergeGroupMode, IMergeGroupOptions } from '../../../services/editor/com import { addDisposableListener, EventType, EventHelper, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode, DragAndDropObserver, isMouseEvent, getWindow } from '../../../../base/browser/dom.js'; import { localize } from '../../../../nls.js'; import { IEditorGroupsView, EditorServiceImpl, IEditorGroupView, IInternalEditorOpenOptions, IEditorPartsView } from './editor.js'; -import { CloseEditorTabAction, UnpinEditorAction } from './editorActions.js'; +import { CloseEditorTabAction, PinEditorAction, UnpinEditorAction } from './editorActions.js'; import { assertAllDefined, assertIsDefined } from '../../../../base/common/types.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { basenameOrAuthority } from '../../../../base/common/resources.js'; @@ -113,6 +113,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { private readonly closeEditorAction = this._register(this.instantiationService.createInstance(CloseEditorTabAction, CloseEditorTabAction.ID, CloseEditorTabAction.LABEL)); private readonly unpinEditorAction = this._register(this.instantiationService.createInstance(UnpinEditorAction, UnpinEditorAction.ID, UnpinEditorAction.LABEL)); + private readonly pinEditorAction = this._register(this.instantiationService.createInstance(PinEditorAction, PinEditorAction.ID, PinEditorAction.LABEL)); private readonly tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER)); private tabLabels: IEditorInputLabel[] = []; @@ -1510,6 +1511,8 @@ export class MultiEditorTabsControl extends EditorTabsControl { this.layout(this.dimensions, options); } + // In MultiEditorTabsControl.ts, modify the redrawTab method: + private redrawTab(editor: EditorInput, tabIndex: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel, tabActionBar: ActionBar): void { const isTabSticky = this.tabsModel.isSticky(tabIndex); const options = this.groupsView.partOptions; @@ -1518,47 +1521,46 @@ export class MultiEditorTabsControl extends EditorTabsControl { this.redrawTabLabel(editor, tabIndex, tabContainer, tabLabelWidget, tabLabel); // Action - const hasUnpinAction = isTabSticky && options.tabActionUnpinVisibility; - const hasCloseAction = !hasUnpinAction && options.tabActionCloseVisibility; - const hasAction = hasUnpinAction || hasCloseAction; + const hasCloseAction = options.tabActionCloseVisibility; + const hasAction = true; // Always show actions - let tabAction; - if (hasAction) { - tabAction = hasUnpinAction ? this.unpinEditorAction : this.closeEditorAction; + // Clear existing actions + if (!tabActionBar.isEmpty()) { + tabActionBar.clear(); + } + + // Add pin/unpin action based on sticky state + if (isTabSticky) { + tabActionBar.push(this.unpinEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.unpinEditorAction) }); } else { - // Even if the action is not visible, add it as it contains the dirty indicator - tabAction = isTabSticky ? this.unpinEditorAction : this.closeEditorAction; + tabActionBar.push(this.pinEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.pinEditorAction) }); } - if (!tabActionBar.hasAction(tabAction)) { - if (!tabActionBar.isEmpty()) { - tabActionBar.clear(); - } - - tabActionBar.push(tabAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(tabAction) }); + // Add close action + if (hasCloseAction) { + tabActionBar.push(this.closeEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.closeEditorAction) }); } - tabContainer.classList.toggle(`pinned-action-off`, isTabSticky && !hasUnpinAction); - tabContainer.classList.toggle(`close-action-off`, !hasUnpinAction && !hasCloseAction); + // Update tab classes and styles + tabContainer.classList.toggle('sticky', isTabSticky); + tabContainer.classList.toggle('close-action-off', !hasCloseAction); + tabContainer.classList.toggle('tab-actions-right', hasAction && options.tabActionLocation === 'right'); + tabContainer.classList.toggle('tab-actions-left', hasAction && options.tabActionLocation === 'left'); - for (const option of ['left', 'right']) { - tabContainer.classList.toggle(`tab-actions-${option}`, hasAction && options.tabActionLocation === option); - } - - const tabSizing = isTabSticky && options.pinnedTabSizing === 'shrink' ? 'shrink' /* treat sticky shrink tabs as tabSizing: 'shrink' */ : options.tabSizing; - for (const option of ['fit', 'shrink', 'fixed']) { - tabContainer.classList.toggle(`sizing-${option}`, tabSizing === option); - } + // Update tab sizing classes + const tabSizing = isTabSticky && options.pinnedTabSizing === 'shrink' ? 'shrink' : options.tabSizing; + tabContainer.classList.toggle('sizing-fit', tabSizing === 'fit'); + tabContainer.classList.toggle('sizing-shrink', tabSizing === 'shrink'); + tabContainer.classList.toggle('sizing-fixed', tabSizing === 'fixed'); tabContainer.classList.toggle('has-icon', options.showIcons && options.hasIcons); + // Update sticky classes tabContainer.classList.toggle('sticky', isTabSticky); - for (const option of ['normal', 'compact', 'shrink']) { - tabContainer.classList.toggle(`sticky-${option}`, isTabSticky && options.pinnedTabSizing === option); - } + tabContainer.classList.toggle('sticky-compact', isTabSticky && options.pinnedTabSizing === 'compact'); + tabContainer.classList.toggle('sticky-shrink', isTabSticky && options.pinnedTabSizing === 'shrink'); - // If not wrapping tabs, sticky compact/shrink tabs need a position to remain at their location - // when scrolling to stay in view (requirement for position: sticky) + // Update tab position if needed if (!options.wrapTabs && isTabSticky && options.pinnedTabSizing !== 'normal') { let stickyTabWidth = 0; switch (options.pinnedTabSizing) { @@ -1569,16 +1571,13 @@ export class MultiEditorTabsControl extends EditorTabsControl { stickyTabWidth = MultiEditorTabsControl.TAB_WIDTH.shrink; break; } - tabContainer.style.left = `${tabIndex * stickyTabWidth}px`; } else { tabContainer.style.left = 'auto'; } - // Borders / outline + // Draw borders and selection state this.redrawTabBorders(tabIndex, tabContainer); - - // Selection / active / dirty state this.redrawTabSelectedActiveAndDirty(this.groupsView.activeGroup === this.groupView, editor, tabContainer, tabActionBar); }