Path: blob/main/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts
3296 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 } 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';3435interface IAuxiliaryBarPartConfiguration {36position: ActivityBarPosition;3738canShowLabels: boolean;39showLabels: boolean;40}4142export class AuxiliaryBarPart extends AbstractPaneCompositePart {4344static readonly activeViewSettingsKey = 'workbench.auxiliarybar.activepanelid';45static readonly pinnedViewsKey = 'workbench.auxiliarybar.pinnedPanels';46static readonly placeholdeViewContainersKey = 'workbench.auxiliarybar.placeholderPanels';47static readonly viewContainersWorkspaceStateKey = 'workbench.auxiliarybar.viewContainersWorkspaceState';4849// Use the side bar dimensions50override readonly minimumWidth: number = 170;51override readonly maximumWidth: number = Number.POSITIVE_INFINITY;52override readonly minimumHeight: number = 0;53override readonly maximumHeight: number = Number.POSITIVE_INFINITY;5455get preferredHeight(): number | undefined {56// Don't worry about titlebar or statusbar visibility57// The difference is minimal and keeps this function clean58return this.layoutService.mainContainerDimension.height * 0.4;59}6061get preferredWidth(): number | undefined {62const activeComposite = this.getActivePaneComposite();6364if (!activeComposite) {65return;66}6768const width = activeComposite.getOptimalWidth();69if (typeof width !== 'number') {70return;71}7273return Math.max(width, 300);74}7576readonly priority = LayoutPriority.Low;7778private configuration: IAuxiliaryBarPartConfiguration;7980constructor(81@INotificationService notificationService: INotificationService,82@IStorageService storageService: IStorageService,83@IContextMenuService contextMenuService: IContextMenuService,84@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,85@IKeybindingService keybindingService: IKeybindingService,86@IHoverService hoverService: IHoverService,87@IInstantiationService instantiationService: IInstantiationService,88@IThemeService themeService: IThemeService,89@IViewDescriptorService viewDescriptorService: IViewDescriptorService,90@IContextKeyService contextKeyService: IContextKeyService,91@IExtensionService extensionService: IExtensionService,92@ICommandService private commandService: ICommandService,93@IMenuService menuService: IMenuService,94@IConfigurationService private readonly configurationService: IConfigurationService95) {96super(97Parts.AUXILIARYBAR_PART,98{99hasTitle: true,100borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0,101},102AuxiliaryBarPart.activeViewSettingsKey,103ActiveAuxiliaryContext.bindTo(contextKeyService),104AuxiliaryBarFocusContext.bindTo(contextKeyService),105'auxiliarybar',106'auxiliarybar',107undefined,108SIDE_BAR_TITLE_BORDER,109notificationService,110storageService,111contextMenuService,112layoutService,113keybindingService,114hoverService,115instantiationService,116themeService,117viewDescriptorService,118contextKeyService,119extensionService,120menuService,121);122123this.configuration = this.resolveConfiguration();124125this._register(configurationService.onDidChangeConfiguration(e => {126if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION)) {127this.configuration = this.resolveConfiguration();128this.onDidChangeActivityBarLocation();129} else if (e.affectsConfiguration('workbench.secondarySideBar.showLabels')) {130this.configuration = this.resolveConfiguration();131this.updateCompositeBar(true);132}133}));134}135136private resolveConfiguration(): IAuxiliaryBarPartConfiguration {137const position = this.configurationService.getValue<ActivityBarPosition>(LayoutSettings.ACTIVITY_BAR_LOCATION);138139const canShowLabels = position !== ActivityBarPosition.TOP && position !== ActivityBarPosition.BOTTOM; // use same style as activity bar in this case140const showLabels = canShowLabels && this.configurationService.getValue('workbench.secondarySideBar.showLabels') !== false;141142return { position, canShowLabels, showLabels };143}144145private onDidChangeActivityBarLocation(): void {146this.updateCompositeBar();147148const id = this.getActiveComposite()?.getId();149if (id) {150this.onTitleAreaUpdate(id);151}152}153154override updateStyles(): void {155super.updateStyles();156157const container = assertReturnsDefined(this.getContainer());158container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND) || '';159const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder);160const isPositionLeft = this.layoutService.getSideBarPosition() === Position.RIGHT;161162container.style.color = this.getColor(SIDE_BAR_FOREGROUND) || '';163164container.style.borderLeftColor = borderColor ?? '';165container.style.borderRightColor = borderColor ?? '';166167container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : 'none';168container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : 'none';169170container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : '0px';171container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '0px';172}173174protected getCompositeBarOptions(): IPaneCompositeBarOptions {175const $this = this;176return {177partContainerClass: 'auxiliarybar',178pinnedViewContainersKey: AuxiliaryBarPart.pinnedViewsKey,179placeholderViewContainersKey: AuxiliaryBarPart.placeholdeViewContainersKey,180viewContainersWorkspaceStateKey: AuxiliaryBarPart.viewContainersWorkspaceStateKey,181icon: !this.configuration.showLabels,182orientation: ActionsOrientation.HORIZONTAL,183recomputeSizes: true,184activityHoverOptions: {185position: () => this.getCompositeBarPosition() === CompositeBarPosition.BOTTOM ? HoverPosition.ABOVE : HoverPosition.BELOW,186},187fillExtraContextMenuActions: actions => this.fillExtraContextMenuActions(actions),188compositeSize: 0,189iconSize: 16,190// Add 10px spacing if the overflow action is visible to no confuse the user with ... between the toolbars191get overflowActionSize() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? 40 : 30; },192colors: theme => ({193activeBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),194inactiveBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),195get activeBorderBottomColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_ACTIVE_BORDER); },196get activeForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_FOREGROUND); },197get inactiveForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND); },198badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),199badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),200get dragAndDropBorder() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_DRAG_AND_DROP_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER); }201}),202compact: true203};204}205206private fillExtraContextMenuActions(actions: IAction[]): void {207const currentPositionRight = this.layoutService.getSideBarPosition() === Position.LEFT;208209if (this.getCompositeBarPosition() === CompositeBarPosition.TITLE) {210const viewsSubmenuAction = this.getViewsSubmenuAction();211if (viewsSubmenuAction) {212actions.push(new Separator());213actions.push(viewsSubmenuAction);214}215}216217const activityBarPositionMenu = this.menuService.getMenuActions(MenuId.ActivityBarPositionMenu, this.contextKeyService, { shouldForwardArgs: true, renderShortTitle: true });218const positionActions = getContextMenuActions(activityBarPositionMenu).secondary;219220const toggleShowLabelsAction = toAction({221id: 'workbench.action.auxiliarybar.toggleShowLabels',222label: this.configuration.showLabels ? localize('showIcons', "Show Icons") : localize('showLabels', "Show Labels"),223enabled: this.configuration.canShowLabels,224run: () => this.configurationService.updateValue('workbench.secondarySideBar.showLabels', !this.configuration.showLabels)225});226227actions.push(...[228new Separator(),229new SubmenuAction('workbench.action.panel.position', localize('activity bar position', "Activity Bar Position"), positionActions),230toAction({ 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) }),231toggleShowLabelsAction,232toAction({ id: ToggleAuxiliaryBarAction.ID, label: localize('hide second side bar', "Hide Secondary Side Bar"), run: () => this.commandService.executeCommand(ToggleAuxiliaryBarAction.ID) })233]);234}235236protected shouldShowCompositeBar(): boolean {237return this.configuration.position !== ActivityBarPosition.HIDDEN;238}239240protected getCompositeBarPosition(): CompositeBarPosition {241switch (this.configuration.position) {242case ActivityBarPosition.TOP: return CompositeBarPosition.TOP;243case ActivityBarPosition.BOTTOM: return CompositeBarPosition.BOTTOM;244case ActivityBarPosition.HIDDEN: return CompositeBarPosition.TITLE;245case ActivityBarPosition.DEFAULT: return CompositeBarPosition.TITLE;246default: return CompositeBarPosition.TITLE;247}248}249250override toJSON(): object {251return {252type: Parts.AUXILIARYBAR_PART253};254}255}256257258