Path: blob/main/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts
5334 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import './media/auxiliaryBarPart.css';6import { localize } from '../../../../nls.js';7import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';8import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';9import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';10import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';11import { INotificationService } from '../../../../platform/notification/common/notification.js';12import { IStorageService } from '../../../../platform/storage/common/storage.js';13import { contrastBorder } from '../../../../platform/theme/common/colorRegistry.js';14import { IThemeService } from '../../../../platform/theme/common/themeService.js';15import { ActiveAuxiliaryContext, AuxiliaryBarFocusContext } from '../../../common/contextkeys.js';16import { ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_TOP_ACTIVE_BORDER, ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_TOP_FOREGROUND, ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_DRAG_AND_DROP_BORDER, PANEL_INACTIVE_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_BORDER, SIDE_BAR_TITLE_BORDER, SIDE_BAR_FOREGROUND } from '../../../common/theme.js';17import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js';18import { IExtensionService } from '../../../services/extensions/common/extensions.js';19import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, Parts, Position } from '../../../services/layout/browser/layoutService.js';20import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js';21import { IAction, Separator, SubmenuAction, toAction } from '../../../../base/common/actions.js';22import { ToggleAuxiliaryBarAction } from './auxiliaryBarActions.js';23import { assertReturnsDefined } from '../../../../base/common/types.js';24import { LayoutPriority } from '../../../../base/browser/ui/splitview/splitview.js';25import { ToggleSidebarPositionAction } from '../../actions/layoutActions.js';26import { ICommandService } from '../../../../platform/commands/common/commands.js';27import { AbstractPaneCompositePart, CompositeBarPosition } from '../paneCompositePart.js';28import { ActionsOrientation } from '../../../../base/browser/ui/actionbar/actionbar.js';29import { IPaneCompositeBarOptions } from '../paneCompositeBar.js';30import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js';31import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';32import { getContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';33import { IHoverService } from '../../../../platform/hover/browser/hover.js';34import { VisibleViewContainersTracker } from '../visibleViewContainersTracker.js';35import { Extensions } from '../../panecomposite.js';3637interface IAuxiliaryBarPartConfiguration {38position: ActivityBarPosition;3940canShowLabels: boolean;41showLabels: boolean;42}4344export class AuxiliaryBarPart extends AbstractPaneCompositePart {4546static readonly activeViewSettingsKey = 'workbench.auxiliarybar.activepanelid';47static readonly pinnedViewsKey = 'workbench.auxiliarybar.pinnedPanels';48static readonly placeholdeViewContainersKey = 'workbench.auxiliarybar.placeholderPanels';49static readonly viewContainersWorkspaceStateKey = 'workbench.auxiliarybar.viewContainersWorkspaceState';5051// Use the side bar dimensions52override readonly minimumWidth: number = 170;53override readonly maximumWidth: number = Number.POSITIVE_INFINITY;54override readonly minimumHeight: number = 0;55override readonly maximumHeight: number = Number.POSITIVE_INFINITY;5657get preferredHeight(): number | undefined {58// Don't worry about titlebar or statusbar visibility59// The difference is minimal and keeps this function clean60return this.layoutService.mainContainerDimension.height * 0.4;61}6263get preferredWidth(): number | undefined {64const activeComposite = this.getActivePaneComposite();6566if (!activeComposite) {67return undefined;68}6970const width = activeComposite.getOptimalWidth();71if (typeof width !== 'number') {72return undefined;73}7475return Math.max(width, 300);76}7778readonly priority = LayoutPriority.Low;7980private configuration: IAuxiliaryBarPartConfiguration;81private readonly visibleViewContainersTracker: VisibleViewContainersTracker;8283constructor(84@INotificationService notificationService: INotificationService,85@IStorageService storageService: IStorageService,86@IContextMenuService contextMenuService: IContextMenuService,87@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,88@IKeybindingService keybindingService: IKeybindingService,89@IHoverService hoverService: IHoverService,90@IInstantiationService instantiationService: IInstantiationService,91@IThemeService themeService: IThemeService,92@IViewDescriptorService viewDescriptorService: IViewDescriptorService,93@IContextKeyService contextKeyService: IContextKeyService,94@IExtensionService extensionService: IExtensionService,95@ICommandService private commandService: ICommandService,96@IMenuService menuService: IMenuService,97@IConfigurationService private readonly configurationService: IConfigurationService98) {99super(100Parts.AUXILIARYBAR_PART,101{102hasTitle: true,103trailingSeparator: true,104borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0,105},106AuxiliaryBarPart.activeViewSettingsKey,107ActiveAuxiliaryContext.bindTo(contextKeyService),108AuxiliaryBarFocusContext.bindTo(contextKeyService),109'auxiliarybar',110'auxiliarybar',111undefined,112SIDE_BAR_TITLE_BORDER,113ViewContainerLocation.AuxiliaryBar,114Extensions.Auxiliary,115MenuId.AuxiliaryBarTitle,116undefined,117notificationService,118storageService,119contextMenuService,120layoutService,121keybindingService,122hoverService,123instantiationService,124themeService,125viewDescriptorService,126contextKeyService,127extensionService,128menuService,129);130131// Track visible view containers for auto-hide132this.visibleViewContainersTracker = this._register(instantiationService.createInstance(VisibleViewContainersTracker, ViewContainerLocation.AuxiliaryBar));133this._register(this.visibleViewContainersTracker.onDidChange((e) => this.onDidChangeAutoHideViewContainers(e)));134135this.configuration = this.resolveConfiguration();136137this._register(configurationService.onDidChangeConfiguration(e => {138if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION)) {139this.configuration = this.resolveConfiguration();140this.onDidChangeActivityBarLocation();141} else if (e.affectsConfiguration('workbench.secondarySideBar.showLabels')) {142this.configuration = this.resolveConfiguration();143this.updateCompositeBar(true);144} else if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_AUTO_HIDE)) {145this.onDidChangeActivityBarLocation();146}147}));148}149150private onDidChangeAutoHideViewContainers(e: { before: number; after: number }): void {151// Only update if auto-hide is enabled and composite bar would show152const autoHide = this.configurationService.getValue<boolean>(LayoutSettings.ACTIVITY_BAR_AUTO_HIDE);153if (autoHide && (this.configuration.position === ActivityBarPosition.TOP || this.configuration.position === ActivityBarPosition.BOTTOM)) {154const visibleBefore = e.before > 1;155const visibleAfter = e.after > 1;156if (visibleBefore !== visibleAfter) {157this.onDidChangeActivityBarLocation();158}159}160}161162private resolveConfiguration(): IAuxiliaryBarPartConfiguration {163const position = this.configurationService.getValue<ActivityBarPosition>(LayoutSettings.ACTIVITY_BAR_LOCATION);164165const canShowLabels = position !== ActivityBarPosition.TOP && position !== ActivityBarPosition.BOTTOM; // use same style as activity bar in this case166const showLabels = canShowLabels && this.configurationService.getValue('workbench.secondarySideBar.showLabels') !== false;167168return { position, canShowLabels, showLabels };169}170171private onDidChangeActivityBarLocation(): void {172this.updateCompositeBar();173174const id = this.getActiveComposite()?.getId();175if (id) {176this.onTitleAreaUpdate(id);177}178}179180override updateStyles(): void {181super.updateStyles();182183const container = assertReturnsDefined(this.getContainer());184container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND) || '';185const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder);186const isPositionLeft = this.layoutService.getSideBarPosition() === Position.RIGHT;187188container.style.color = this.getColor(SIDE_BAR_FOREGROUND) || '';189190container.style.borderLeftColor = borderColor ?? '';191container.style.borderRightColor = borderColor ?? '';192193container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : 'none';194container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : 'none';195196container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : '0px';197container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '0px';198}199200protected getCompositeBarOptions(): IPaneCompositeBarOptions {201const $this = this;202return {203partContainerClass: 'auxiliarybar',204pinnedViewContainersKey: AuxiliaryBarPart.pinnedViewsKey,205placeholderViewContainersKey: AuxiliaryBarPart.placeholdeViewContainersKey,206viewContainersWorkspaceStateKey: AuxiliaryBarPart.viewContainersWorkspaceStateKey,207icon: !this.configuration.showLabels,208orientation: ActionsOrientation.HORIZONTAL,209recomputeSizes: true,210activityHoverOptions: {211position: () => this.getCompositeBarPosition() === CompositeBarPosition.BOTTOM ? HoverPosition.ABOVE : HoverPosition.BELOW,212},213fillExtraContextMenuActions: actions => this.fillExtraContextMenuActions(actions),214compositeSize: 0,215iconSize: 16,216// Add 10px spacing if the overflow action is visible to no confuse the user with ... between the toolbars217get overflowActionSize() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? 40 : 30; },218colors: theme => ({219activeBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),220inactiveBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),221get activeBorderBottomColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_ACTIVE_BORDER); },222get activeForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_FOREGROUND); },223get inactiveForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND); },224badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),225badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),226get dragAndDropBorder() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_DRAG_AND_DROP_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER); }227}),228compact: true229};230}231232private fillExtraContextMenuActions(actions: IAction[]): void {233const currentPositionRight = this.layoutService.getSideBarPosition() === Position.LEFT;234235if (this.getCompositeBarPosition() === CompositeBarPosition.TITLE) {236const viewsSubmenuAction = this.getViewsSubmenuAction();237if (viewsSubmenuAction) {238actions.push(new Separator());239actions.push(viewsSubmenuAction);240}241}242243const activityBarPositionMenu = this.menuService.getMenuActions(MenuId.ActivityBarPositionMenu, this.contextKeyService, { shouldForwardArgs: true, renderShortTitle: true });244const positionActions = getContextMenuActions(activityBarPositionMenu).secondary;245246const toggleShowLabelsAction = toAction({247id: 'workbench.action.auxiliarybar.toggleShowLabels',248label: this.configuration.showLabels ? localize('showIcons', "Show Icons") : localize('showLabels', "Show Labels"),249enabled: this.configuration.canShowLabels,250run: () => this.configurationService.updateValue('workbench.secondarySideBar.showLabels', !this.configuration.showLabels)251});252253actions.push(...[254new Separator(),255new SubmenuAction('workbench.action.panel.position', localize('activity bar position', "Activity Bar Position"), positionActions),256toAction({ id: ToggleSidebarPositionAction.ID, label: currentPositionRight ? localize('move second side bar left', "Move Secondary Side Bar Left") : localize('move second side bar right', "Move Secondary Side Bar Right"), run: () => this.commandService.executeCommand(ToggleSidebarPositionAction.ID) }),257toggleShowLabelsAction,258toAction({ id: ToggleAuxiliaryBarAction.ID, label: localize('hide second side bar', "Hide Secondary Side Bar"), run: () => this.commandService.executeCommand(ToggleAuxiliaryBarAction.ID) })259]);260}261262protected shouldShowCompositeBar(): boolean {263if (this.configuration.position === ActivityBarPosition.HIDDEN) {264return false;265}266267// Check if auto-hide is enabled and there's only one visible view container268// while the activity bar is configured to be top or bottom.269if (this.configuration.position === ActivityBarPosition.TOP || this.configuration.position === ActivityBarPosition.BOTTOM) {270const autoHide = this.configurationService.getValue<boolean>(LayoutSettings.ACTIVITY_BAR_AUTO_HIDE);271if (autoHide) {272// Use visible composite count from the composite bar if available (considers pinned state),273// otherwise fall back to the tracker's count (based on active view descriptors).274// Note: We access paneCompositeBar directly to avoid circular calls with getVisiblePaneCompositeIds()275const visibleCount = this.visibleViewContainersTracker.visibleCount;276if (visibleCount <= 1) {277return false;278}279}280}281282return true;283}284285protected getCompositeBarPosition(): CompositeBarPosition {286switch (this.configuration.position) {287case ActivityBarPosition.TOP: return CompositeBarPosition.TOP;288case ActivityBarPosition.BOTTOM: return CompositeBarPosition.BOTTOM;289case ActivityBarPosition.HIDDEN: return CompositeBarPosition.TITLE;290case ActivityBarPosition.DEFAULT: return CompositeBarPosition.TITLE;291default: return CompositeBarPosition.TITLE;292}293}294295override toJSON(): object {296return {297type: Parts.AUXILIARYBAR_PART298};299}300}301302303