@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
259 lines (258 loc) • 8.55 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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 } from '@sussudio/base/browser/dom.mjs';
import { ToolBar } from '@sussudio/base/browser/ui/toolbar/toolbar.mjs';
import { Separator, toAction } from '@sussudio/base/common/actions.mjs';
import { coalesceInPlace } from '@sussudio/base/common/arrays.mjs';
import { BugIndicatingError } from '@sussudio/base/common/errors.mjs';
import { DisposableStore } from '@sussudio/base/common/lifecycle.mjs';
import { localize } from 'vscode-nls.mjs';
import { createAndFillInActionBarActions } from './menuEntryActionViewItem.mjs';
import { IMenuService, MenuItemAction, SubmenuItemAction } from '../common/actions.mjs';
import { IContextKeyService } from '../../contextkey/common/contextkey.mjs';
import { IContextMenuService } from '../../contextview/browser/contextView.mjs';
import { IKeybindingService } from '../../keybinding/common/keybinding.mjs';
import { ITelemetryService } from '../../telemetry/common/telemetry.mjs';
/**
* 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 {
_options;
_menuService;
_contextKeyService;
_contextMenuService;
_sessionDisposables = this._store.add(new DisposableStore());
constructor(
container,
_options,
_menuService,
_contextKeyService,
_contextMenuService,
keybindingService,
telemetryService,
) {
super(container, _contextMenuService, {
// defaults
getKeyBinding: (action) => keybindingService.lookupKeybinding(action.id) ?? undefined,
// options (override defaults)
..._options,
// mandatory (overide options)
allowContextMenu: true,
});
this._options = _options;
this._menuService = _menuService;
this._contextKeyService = _contextKeyService;
this._contextMenuService = _contextMenuService;
// telemetry logic
if (_options?.telemetrySource) {
this._store.add(
this.actionBar.onDidRun((e) =>
telemetryService.publicLog2('workbenchActionExecuted', { id: e.action.id, from: _options.telemetrySource }),
),
);
}
}
setActions(_primary, _secondary = [], menuIds) {
this._sessionDisposables.clear();
const primary = _primary.slice();
const secondary = _secondary.slice();
const toggleActions = [];
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);
// 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?.maxNumberOfItems !== undefined) {
let count = 0;
for (let i = 0; i < primary.length; i++) {
const action = primary[i];
if (!action) {
continue;
}
if (++count >= this._options.maxNumberOfItems) {
primary[i] = undefined;
extraSecondary[i] = action;
}
}
}
coalesceInPlace(primary);
coalesceInPlace(extraSecondary);
super.setActions(primary, Separator.join(extraSecondary, secondary));
// add context menu for toggle actions
if (toggleActions.length > 0) {
this._sessionDisposables.add(
addDisposableListener(this.getElement(), 'contextmenu', (e) => {
const action = this.getItemAction(e.target);
if (!action) {
return;
}
e.preventDefault();
e.stopPropagation();
let actions = toggleActions;
// add "hide foo" actions
let hideAction;
if (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;
}
hideAction = action.hideActions.hide;
} else {
hideAction = toAction({
id: 'label',
label: localize('hide', 'Hide'),
enabled: false,
run() {},
});
}
actions = [hideAction, new Separator(), ...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),
}),
);
}
this._contextMenuService.showContextMenu({
getAnchor: () => e,
getActions: () => actions,
// add context menu actions (iff appicable)
menuId: this._options?.contextMenu,
menuActionOptions: { renderShortTitle: true, ...this._options?.menuOptions },
contextKeyService: this._contextKeyService,
});
}),
);
}
}
};
WorkbenchToolBar = __decorate(
[
__param(2, IMenuService),
__param(3, IContextKeyService),
__param(4, IContextMenuService),
__param(5, IKeybindingService),
__param(6, 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,
telemetryService,
) {
super(
container,
{ resetMenu: menuId, ...options },
menuService,
contextKeyService,
contextMenuService,
keybindingService,
telemetryService,
);
// 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,
);
super.setActions(primary, secondary);
};
this._store.add(menu.onDidChange(updateToolbar));
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, ITelemetryService),
],
MenuWorkbenchToolBar,
);
export { MenuWorkbenchToolBar };