Path: blob/main/src/vs/base/browser/ui/toolbar/toolbar.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 { IContextMenuProvider } from '../../contextmenu.js';6import { ActionBar, ActionsOrientation, IActionViewItemProvider } from '../actionbar/actionbar.js';7import { AnchorAlignment } from '../contextview/contextview.js';8import { DropdownMenuActionViewItem } from '../dropdown/dropdownActionViewItem.js';9import { Action, IAction, IActionRunner, SubmenuAction } from '../../../common/actions.js';10import { Codicon } from '../../../common/codicons.js';11import { ThemeIcon } from '../../../common/themables.js';12import { EventMultiplexer } from '../../../common/event.js';13import { ResolvedKeybinding } from '../../../common/keybindings.js';14import { Disposable, DisposableStore } from '../../../common/lifecycle.js';15import './toolbar.css';16import * as nls from '../../../../nls.js';17import { IHoverDelegate } from '../hover/hoverDelegate.js';18import { createInstantHoverDelegate } from '../hover/hoverDelegateFactory.js';1920export interface IToolBarOptions {21orientation?: ActionsOrientation;22actionViewItemProvider?: IActionViewItemProvider;23ariaLabel?: string;24getKeyBinding?: (action: IAction) => ResolvedKeybinding | undefined;25actionRunner?: IActionRunner;26toggleMenuTitle?: string;27anchorAlignmentProvider?: () => AnchorAlignment;28renderDropdownAsChildElement?: boolean;29moreIcon?: ThemeIcon;30allowContextMenu?: boolean;31skipTelemetry?: boolean;32hoverDelegate?: IHoverDelegate;3334/**35* If true, toggled primary items are highlighted with a background color.36*/37highlightToggledItems?: boolean;3839/**40* Render action with icons (default: `true`)41*/42icon?: boolean;4344/**45* Render action with label (default: `false`)46*/47label?: boolean;48}4950/**51* A widget that combines an action bar for primary actions and a dropdown for secondary actions.52*/53export class ToolBar extends Disposable {54private options: IToolBarOptions;55protected readonly actionBar: ActionBar;56private toggleMenuAction: ToggleMenuAction;57private toggleMenuActionViewItem: DropdownMenuActionViewItem | undefined;58private submenuActionViewItems: DropdownMenuActionViewItem[] = [];59private hasSecondaryActions: boolean = false;60private readonly element: HTMLElement;6162private _onDidChangeDropdownVisibility = this._register(new EventMultiplexer<boolean>());63get onDidChangeDropdownVisibility() { return this._onDidChangeDropdownVisibility.event; }64private readonly disposables = this._register(new DisposableStore());6566constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) {67super();6869options.hoverDelegate = options.hoverDelegate ?? this._register(createInstantHoverDelegate());70this.options = options;7172this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem?.show(), options.toggleMenuTitle));7374this.element = document.createElement('div');75this.element.className = 'monaco-toolbar';76container.appendChild(this.element);7778this.actionBar = this._register(new ActionBar(this.element, {79orientation: options.orientation,80ariaLabel: options.ariaLabel,81actionRunner: options.actionRunner,82allowContextMenu: options.allowContextMenu,83highlightToggledItems: options.highlightToggledItems,84hoverDelegate: options.hoverDelegate,85actionViewItemProvider: (action, viewItemOptions) => {86if (action.id === ToggleMenuAction.ID) {87this.toggleMenuActionViewItem = new DropdownMenuActionViewItem(88action,89(<ToggleMenuAction>action).menuActions,90contextMenuProvider,91{92actionViewItemProvider: this.options.actionViewItemProvider,93actionRunner: this.actionRunner,94keybindingProvider: this.options.getKeyBinding,95classNames: ThemeIcon.asClassNameArray(options.moreIcon ?? Codicon.toolBarMore),96anchorAlignmentProvider: this.options.anchorAlignmentProvider,97menuAsChild: !!this.options.renderDropdownAsChildElement,98skipTelemetry: this.options.skipTelemetry,99isMenu: true,100hoverDelegate: this.options.hoverDelegate101}102);103this.toggleMenuActionViewItem.setActionContext(this.actionBar.context);104this.disposables.add(this._onDidChangeDropdownVisibility.add(this.toggleMenuActionViewItem.onDidChangeVisibility));105106return this.toggleMenuActionViewItem;107}108109if (options.actionViewItemProvider) {110const result = options.actionViewItemProvider(action, viewItemOptions);111112if (result) {113return result;114}115}116117if (action instanceof SubmenuAction) {118const result = new DropdownMenuActionViewItem(119action,120action.actions,121contextMenuProvider,122{123actionViewItemProvider: this.options.actionViewItemProvider,124actionRunner: this.actionRunner,125keybindingProvider: this.options.getKeyBinding,126classNames: action.class,127anchorAlignmentProvider: this.options.anchorAlignmentProvider,128menuAsChild: !!this.options.renderDropdownAsChildElement,129skipTelemetry: this.options.skipTelemetry,130hoverDelegate: this.options.hoverDelegate131}132);133result.setActionContext(this.actionBar.context);134this.submenuActionViewItems.push(result);135this.disposables.add(this._onDidChangeDropdownVisibility.add(result.onDidChangeVisibility));136137return result;138}139140return undefined;141}142}));143}144145set actionRunner(actionRunner: IActionRunner) {146this.actionBar.actionRunner = actionRunner;147}148149get actionRunner(): IActionRunner {150return this.actionBar.actionRunner;151}152153set context(context: unknown) {154this.actionBar.context = context;155this.toggleMenuActionViewItem?.setActionContext(context);156for (const actionViewItem of this.submenuActionViewItems) {157actionViewItem.setActionContext(context);158}159}160161getElement(): HTMLElement {162return this.element;163}164165focus(): void {166this.actionBar.focus();167}168169getItemsWidth(): number {170let itemsWidth = 0;171for (let i = 0; i < this.actionBar.length(); i++) {172itemsWidth += this.actionBar.getWidth(i);173}174return itemsWidth;175}176177getItemAction(indexOrElement: number | HTMLElement) {178return this.actionBar.getAction(indexOrElement);179}180181getItemWidth(index: number): number {182return this.actionBar.getWidth(index);183}184185getItemsLength(): number {186return this.actionBar.length();187}188189setAriaLabel(label: string): void {190this.actionBar.setAriaLabel(label);191}192193setActions(primaryActions: ReadonlyArray<IAction>, secondaryActions?: ReadonlyArray<IAction>): void {194this.clear();195196const primaryActionsToSet = primaryActions ? primaryActions.slice(0) : [];197198// Inject additional action to open secondary actions if present199this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0);200if (this.hasSecondaryActions && secondaryActions) {201this.toggleMenuAction.menuActions = secondaryActions.slice(0);202primaryActionsToSet.push(this.toggleMenuAction);203}204205primaryActionsToSet.forEach(action => {206this.actionBar.push(action, { icon: this.options.icon ?? true, label: this.options.label ?? false, keybinding: this.getKeybindingLabel(action) });207});208}209210isEmpty(): boolean {211return this.actionBar.isEmpty();212}213214private getKeybindingLabel(action: IAction): string | undefined {215const key = this.options.getKeyBinding?.(action);216217return key?.getLabel() ?? undefined;218}219220private clear(): void {221this.submenuActionViewItems = [];222this.disposables.clear();223this.actionBar.clear();224}225226override dispose(): void {227this.clear();228this.disposables.dispose();229super.dispose();230}231}232233export class ToggleMenuAction extends Action {234235static readonly ID = 'toolbar.toggle.more';236237private _menuActions: ReadonlyArray<IAction>;238private toggleDropdownMenu: () => void;239240constructor(toggleDropdownMenu: () => void, title?: string) {241title = title || nls.localize('moreActions', "More Actions...");242super(ToggleMenuAction.ID, title, undefined, true);243244this._menuActions = [];245this.toggleDropdownMenu = toggleDropdownMenu;246}247248override async run(): Promise<void> {249this.toggleDropdownMenu();250}251252get menuActions(): ReadonlyArray<IAction> {253return this._menuActions;254}255256set menuActions(actions: ReadonlyArray<IAction>) {257this._menuActions = actions;258}259}260261262