UNPKG

monaco-editor-core

Version:

A browser based code editor

259 lines (258 loc) • 13.5 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { addDisposableListener, getWindow } from '../../../base/browser/dom.js'; import { StandardMouseEvent } from '../../../base/browser/mouseEvent.js'; import { ToggleMenuAction, ToolBar } from '../../../base/browser/ui/toolbar/toolbar.js'; import { Separator, toAction } from '../../../base/common/actions.js'; import { coalesceInPlace } from '../../../base/common/arrays.js'; import { intersection } from '../../../base/common/collections.js'; import { BugIndicatingError } from '../../../base/common/errors.js'; import { Emitter } from '../../../base/common/event.js'; import { Iterable } from '../../../base/common/iterator.js'; import { DisposableStore } from '../../../base/common/lifecycle.js'; import { localize } from '../../../nls.js'; import { createAndFillInActionBarActions } from './menuEntryActionViewItem.js'; import { IMenuService, MenuItemAction, SubmenuItemAction } from '../common/actions.js'; import { createConfigureKeybindingAction } from '../common/menuService.js'; import { ICommandService } from '../../commands/common/commands.js'; import { IContextKeyService } from '../../contextkey/common/contextkey.js'; import { IContextMenuService } from '../../contextview/browser/contextView.js'; import { IKeybindingService } from '../../keybinding/common/keybinding.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js'; /** * The `WorkbenchToolBar` does * - support hiding of menu items * - lookup keybindings for each actions automatically * - send `workbenchActionExecuted`-events for each action * * See {@link MenuWorkbenchToolBar} for a toolbar that is backed by a menu. */ let WorkbenchToolBar = class WorkbenchToolBar extends ToolBar { constructor(container, _options, _menuService, _contextKeyService, _contextMenuService, _keybindingService, _commandService, telemetryService) { super(container, _contextMenuService, { // defaults getKeyBinding: (action) => _keybindingService.lookupKeybinding(action.id) ?? undefined, // options (override defaults) ..._options, // mandatory (overide options) allowContextMenu: true, skipTelemetry: typeof _options?.telemetrySource === 'string', }); this._options = _options; this._menuService = _menuService; this._contextKeyService = _contextKeyService; this._contextMenuService = _contextMenuService; this._keybindingService = _keybindingService; this._commandService = _commandService; this._sessionDisposables = this._store.add(new DisposableStore()); // telemetry logic const telemetrySource = _options?.telemetrySource; if (telemetrySource) { this._store.add(this.actionBar.onDidRun(e => telemetryService.publicLog2('workbenchActionExecuted', { id: e.action.id, from: telemetrySource }))); } } setActions(_primary, _secondary = [], menuIds) { this._sessionDisposables.clear(); const primary = _primary.slice(); // for hiding and overflow we set some items to undefined const secondary = _secondary.slice(); const toggleActions = []; let toggleActionsCheckedCount = 0; const extraSecondary = []; let someAreHidden = false; // unless disabled, move all hidden items to secondary group or ignore them if (this._options?.hiddenItemStrategy !== -1 /* HiddenItemStrategy.NoHide */) { for (let i = 0; i < primary.length; i++) { const action = primary[i]; if (!(action instanceof MenuItemAction) && !(action instanceof SubmenuItemAction)) { // console.warn(`Action ${action.id}/${action.label} is not a MenuItemAction`); continue; } if (!action.hideActions) { continue; } // collect all toggle actions toggleActions.push(action.hideActions.toggle); if (action.hideActions.toggle.checked) { toggleActionsCheckedCount++; } // hidden items move into overflow or ignore if (action.hideActions.isHidden) { someAreHidden = true; primary[i] = undefined; if (this._options?.hiddenItemStrategy !== 0 /* HiddenItemStrategy.Ignore */) { extraSecondary[i] = action; } } } } // count for max if (this._options?.overflowBehavior !== undefined) { const exemptedIds = intersection(new Set(this._options.overflowBehavior.exempted), Iterable.map(primary, a => a?.id)); const maxItems = this._options.overflowBehavior.maxItems - exemptedIds.size; let count = 0; for (let i = 0; i < primary.length; i++) { const action = primary[i]; if (!action) { continue; } count++; if (exemptedIds.has(action.id)) { continue; } if (count >= maxItems) { primary[i] = undefined; extraSecondary[i] = action; } } } // coalesce turns Array<IAction|undefined> into IAction[] coalesceInPlace(primary); coalesceInPlace(extraSecondary); super.setActions(primary, Separator.join(extraSecondary, secondary)); // add context menu for toggle and configure keybinding actions if (toggleActions.length > 0 || primary.length > 0) { this._sessionDisposables.add(addDisposableListener(this.getElement(), 'contextmenu', e => { const event = new StandardMouseEvent(getWindow(this.getElement()), e); const action = this.getItemAction(event.target); if (!(action)) { return; } event.preventDefault(); event.stopPropagation(); const primaryActions = []; // -- Configure Keybinding Action -- if (action instanceof MenuItemAction && action.menuKeybinding) { primaryActions.push(action.menuKeybinding); } else if (!(action instanceof SubmenuItemAction || action instanceof ToggleMenuAction)) { // only enable the configure keybinding action for actions that support keybindings const supportsKeybindings = !!this._keybindingService.lookupKeybinding(action.id); primaryActions.push(createConfigureKeybindingAction(this._commandService, this._keybindingService, action.id, undefined, supportsKeybindings)); } // -- Hide Actions -- if (toggleActions.length > 0) { let noHide = false; // last item cannot be hidden when using ignore strategy if (toggleActionsCheckedCount === 1 && this._options?.hiddenItemStrategy === 0 /* HiddenItemStrategy.Ignore */) { noHide = true; for (let i = 0; i < toggleActions.length; i++) { if (toggleActions[i].checked) { toggleActions[i] = toAction({ id: action.id, label: action.label, checked: true, enabled: false, run() { } }); break; // there is only one } } } // add "hide foo" actions if (!noHide && (action instanceof MenuItemAction || action instanceof SubmenuItemAction)) { if (!action.hideActions) { // no context menu for MenuItemAction instances that support no hiding // those are fake actions and need to be cleaned up return; } primaryActions.push(action.hideActions.hide); } else { primaryActions.push(toAction({ id: 'label', label: localize('hide', "Hide"), enabled: false, run() { } })); } } const actions = Separator.join(primaryActions, toggleActions); // add "Reset Menu" action if (this._options?.resetMenu && !menuIds) { menuIds = [this._options.resetMenu]; } if (someAreHidden && menuIds) { actions.push(new Separator()); actions.push(toAction({ id: 'resetThisMenu', label: localize('resetThisMenu', "Reset Menu"), run: () => this._menuService.resetHiddenStates(menuIds) })); } if (actions.length === 0) { return; } this._contextMenuService.showContextMenu({ getAnchor: () => event, getActions: () => actions, // add context menu actions (iff appicable) menuId: this._options?.contextMenu, menuActionOptions: { renderShortTitle: true, ...this._options?.menuOptions }, skipTelemetry: typeof this._options?.telemetrySource === 'string', contextKeyService: this._contextKeyService, }); })); } } }; WorkbenchToolBar = __decorate([ __param(2, IMenuService), __param(3, IContextKeyService), __param(4, IContextMenuService), __param(5, IKeybindingService), __param(6, ICommandService), __param(7, ITelemetryService) ], WorkbenchToolBar); export { WorkbenchToolBar }; /** * A {@link WorkbenchToolBar workbench toolbar} that is purely driven from a {@link MenuId menu}-identifier. * * *Note* that Manual updates via `setActions` are NOT supported. */ let MenuWorkbenchToolBar = class MenuWorkbenchToolBar extends WorkbenchToolBar { constructor(container, menuId, options, menuService, contextKeyService, contextMenuService, keybindingService, commandService, telemetryService) { super(container, { resetMenu: menuId, ...options }, menuService, contextKeyService, contextMenuService, keybindingService, commandService, telemetryService); this._onDidChangeMenuItems = this._store.add(new Emitter()); this.onDidChangeMenuItems = this._onDidChangeMenuItems.event; // update logic const menu = this._store.add(menuService.createMenu(menuId, contextKeyService, { emitEventsForSubmenuChanges: true })); const updateToolbar = () => { const primary = []; const secondary = []; createAndFillInActionBarActions(menu, options?.menuOptions, { primary, secondary }, options?.toolbarOptions?.primaryGroup, options?.toolbarOptions?.shouldInlineSubmenu, options?.toolbarOptions?.useSeparatorsInPrimaryActions); container.classList.toggle('has-no-actions', primary.length === 0 && secondary.length === 0); super.setActions(primary, secondary); }; this._store.add(menu.onDidChange(() => { updateToolbar(); this._onDidChangeMenuItems.fire(this); })); updateToolbar(); } /** * @deprecated The WorkbenchToolBar does not support this method because it works with menus. */ setActions() { throw new BugIndicatingError('This toolbar is populated from a menu.'); } }; MenuWorkbenchToolBar = __decorate([ __param(3, IMenuService), __param(4, IContextKeyService), __param(5, IContextMenuService), __param(6, IKeybindingService), __param(7, ICommandService), __param(8, ITelemetryService) ], MenuWorkbenchToolBar); export { MenuWorkbenchToolBar };