();
const registerToggleViewItem = (command: Command, mode: 'tree' | 'list') => {
const id = command.id;
- const item: TabBarToolbarItem = {
+ const item: TabBarToolbarAction = {
id,
command: id,
tooltip: command.label,
diff --git a/packages/scm/src/browser/scm-tree-widget.tsx b/packages/scm/src/browser/scm-tree-widget.tsx
index 732c395b8b371..65390c0d810d7 100644
--- a/packages/scm/src/browser/scm-tree-widget.tsx
+++ b/packages/scm/src/browser/scm-tree-widget.tsx
@@ -23,7 +23,7 @@ import { isOSX } from '@theia/core/lib/common/os';
import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable';
import { TreeWidget, TreeNode, SelectableTreeNode, TreeModel, TreeProps, NodeProps, TREE_NODE_SEGMENT_CLASS, TREE_NODE_SEGMENT_GROW_CLASS } from '@theia/core/lib/browser/tree';
import { ScmTreeModel, ScmFileChangeRootNode, ScmFileChangeGroupNode, ScmFileChangeFolderNode, ScmFileChangeNode } from './scm-tree-model';
-import { MenuCommandExecutor, MenuModelRegistry, ActionMenuNode, CompoundMenuNode, MenuPath } from '@theia/core/lib/common/menu';
+import { MenuModelRegistry, CompoundMenuNode, MenuPath, CommandMenu } from '@theia/core/lib/common/menu';
import { ScmResource } from './scm-provider';
import { ContextMenuRenderer, LabelProvider, CorePreferences, DiffUris, ACTION_ITEM } from '@theia/core/lib/browser';
import { ScmContextKeyService } from './scm-context-key-service';
@@ -48,7 +48,6 @@ export class ScmTreeWidget extends TreeWidget {
static RESOURCE_CONTEXT_MENU = ['RESOURCE_CONTEXT_MENU'];
static RESOURCE_INLINE_MENU = ['RESOURCE_CONTEXT_MENU', 'inline'];
- @inject(MenuCommandExecutor) protected readonly menuCommandExecutor: MenuCommandExecutor;
@inject(MenuModelRegistry) protected readonly menus: MenuModelRegistry;
@inject(ScmContextKeyService) protected readonly contextKeys: ScmContextKeyService;
@inject(EditorManager) protected readonly editorManager: EditorManager;
@@ -109,7 +108,6 @@ export class ScmTreeWidget extends TreeWidget {
model={this.model}
treeNode={node}
renderExpansionToggle={() => this.renderExpansionToggle(node, props)}
- commandExecutor={this.menuCommandExecutor}
contextMenuRenderer={this.contextMenuRenderer}
menus={this.menus}
contextKeys={this.contextKeys}
@@ -128,7 +126,6 @@ export class ScmTreeWidget extends TreeWidget {
treeNode={node}
sourceUri={node.sourceUri}
renderExpansionToggle={() => this.renderExpansionToggle(node, props)}
- commandExecutor={this.menuCommandExecutor}
contextMenuRenderer={this.contextMenuRenderer}
menus={this.menus}
contextKeys={this.contextKeys}
@@ -149,7 +146,6 @@ export class ScmTreeWidget extends TreeWidget {
model={this.model}
treeNode={node}
contextMenuRenderer={this.contextMenuRenderer}
- commandExecutor={this.menuCommandExecutor}
menus={this.menus}
contextKeys={this.contextKeys}
labelProvider={this.labelProvider}
@@ -536,7 +532,6 @@ export abstract class ScmElement
export namespace ScmElement {
export interface Props extends ScmTreeWidget.Props {
renderExpansionToggle: () => React.ReactNode;
- commandExecutor: MenuCommandExecutor;
}
export interface State {
hover: boolean
@@ -547,7 +542,7 @@ export class ScmResourceComponent extends ScmElement
override render(): JSX.Element | undefined {
const { hover } = this.state;
- const { model, treeNode, colors, parentPath, sourceUri, decoration, labelProvider, commandExecutor, menus, contextKeys, caption, isLightTheme } = this.props;
+ const { model, treeNode, colors, parentPath, sourceUri, decoration, labelProvider, menus, contextKeys, caption, isLightTheme } = this.props;
const resourceUri = new URI(sourceUri);
const decorationIcon = treeNode.decorations;
@@ -584,7 +579,6 @@ export class ScmResourceComponent extends ScmElement
hover,
menu: menus.getMenu(ScmTreeWidget.RESOURCE_INLINE_MENU),
menuPath: ScmTreeWidget.RESOURCE_INLINE_MENU,
- commandExecutor,
args: this.contextMenuArgs,
contextKeys,
model,
@@ -669,7 +663,7 @@ export class ScmResourceGroupElement extends ScmElement {
override render(): React.ReactNode {
- const { hover, menu, menuPath, args, commandExecutor, model, treeNode, contextKeys, children } = this.props;
+ const { hover, menu, menuPath, args, model, treeNode, contextKeys, children } = this.props;
return
{hover && menu.children
- .map((node, index) => node instanceof ActionMenuNode &&
- )}
+ .map((node, index) => CommandMenu.is(node) &&
+ )}
{children}
;
@@ -793,7 +785,6 @@ export namespace ScmInlineActions {
hover: boolean;
menu: CompoundMenuNode;
menuPath: MenuPath;
- commandExecutor: MenuCommandExecutor;
model: ScmTreeModel;
treeNode: TreeNode;
contextKeys: ScmContextKeyService;
@@ -804,14 +795,14 @@ export namespace ScmInlineActions {
export class ScmInlineAction extends React.Component {
override render(): React.ReactNode {
- const { node, model, treeNode, args, commandExecutor, menuPath, contextKeys } = this.props;
+ const { node, menuPath, model, treeNode, args, contextKeys } = this.props;
let isActive: boolean = false;
model.execInNodeContext(treeNode, () => {
- isActive = contextKeys.match(node.when);
+ isActive = node.isVisible(menuPath, contextKeys, undefined, ...args);
});
- if (!commandExecutor.isVisible(menuPath, node.command, ...args) || !isActive) {
+ if (!isActive) {
return false;
}
return
@@ -822,14 +813,13 @@ export class ScmInlineAction extends React.Component
{
protected execute = (event: React.MouseEvent) => {
event.stopPropagation();
- const { commandExecutor, menuPath, node, args } = this.props;
- commandExecutor.executeCommand([menuPath[0]], node.command, ...args);
+ const { node, menuPath, args } = this.props;
+ node.run(menuPath, ...args);
};
}
export namespace ScmInlineAction {
export interface Props {
- node: ActionMenuNode;
- commandExecutor: MenuCommandExecutor;
+ node: CommandMenu;
menuPath: MenuPath;
model: ScmTreeModel;
treeNode: TreeNode;
diff --git a/packages/terminal/src/browser/terminal-frontend-contribution.ts b/packages/terminal/src/browser/terminal-frontend-contribution.ts
index 0921fc1faa7a2..4e4ae1b5c5740 100644
--- a/packages/terminal/src/browser/terminal-frontend-contribution.ts
+++ b/packages/terminal/src/browser/terminal-frontend-contribution.ts
@@ -28,7 +28,7 @@ import {
Event,
ViewColumn,
OS,
- CompoundMenuNodeRole
+ MAIN_MENU_BAR
} from '@theia/core/lib/common';
import {
ApplicationShell, KeybindingContribution, KeyCode, Key, WidgetManager, PreferenceService,
@@ -43,7 +43,6 @@ import { ContributedTerminalProfileStore, NULL_PROFILE, TerminalProfile, Termina
import { UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler';
import { ShellTerminalServerProxy } from '../common/shell-terminal-protocol';
import URI from '@theia/core/lib/common/uri';
-import { MAIN_MENU_BAR } from '@theia/core';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
@@ -738,14 +737,9 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu
commandId: TerminalCommands.KILL_TERMINAL.id
});
- menus.registerSubmenu(TerminalMenus.TERMINAL_CONTRIBUTIONS, '', {
- role: CompoundMenuNodeRole.Group
- });
+ menus.registerSubmenu(TerminalMenus.TERMINAL_CONTRIBUTIONS, '');
- menus.registerSubmenu(TerminalMenus.TERMINAL_TITLE_CONTRIBUTIONS, '', {
- role: CompoundMenuNodeRole.Group,
- when: 'isTerminalTab'
- });
+ menus.registerSubmenu(TerminalMenus.TERMINAL_TITLE_CONTRIBUTIONS, '', { when: 'isTerminalTab' });
}
registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
diff --git a/packages/test/src/browser/view/test-tree-widget.tsx b/packages/test/src/browser/view/test-tree-widget.tsx
index 3c5e0c7852bb7..206382ff424e3 100644
--- a/packages/test/src/browser/view/test-tree-widget.tsx
+++ b/packages/test/src/browser/view/test-tree-widget.tsx
@@ -26,7 +26,7 @@ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import { TestController, TestExecutionState, TestItem, TestService } from '../test-service';
import * as React from '@theia/core/shared/react';
import { DeltaKind, TreeDelta } from '../../common/tree-delta';
-import { ActionMenuNode, CommandRegistry, Disposable, DisposableCollection, Event, MenuModelRegistry, nls } from '@theia/core';
+import { AcceleratorSource, CommandMenu, CommandRegistry, Disposable, DisposableCollection, Event, MenuModelRegistry, nls } from '@theia/core';
import { TestExecutionStateManager } from './test-execution-state-manager';
import { TestOutputUIModel } from './test-output-ui-model';
import { TEST_VIEW_INLINE_MENU } from './test-view-contribution';
@@ -301,7 +301,7 @@ export class TestTreeWidget extends TreeWidget {
return this.contextKeys.with({ view: this.id, controllerId: node.controller.id, testId: testItem.id, testItemHasUri: !!testItem.uri }, () => {
const menu = this.menus.getMenu(TEST_VIEW_INLINE_MENU);
const args = [node.testItem];
- const inlineCommands = menu.children.filter((item): item is ActionMenuNode => item instanceof ActionMenuNode);
+ const inlineCommands = menu.children.filter((item): item is CommandMenu => CommandMenu.is(item));
const tailDecorations = super.renderTailDecorations(node, props);
return
{inlineCommands.length > 0 &&
@@ -316,17 +316,17 @@ export class TestTreeWidget extends TreeWidget {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- protected renderInlineCommand(actionMenuNode: ActionMenuNode, index: number, tabbable: boolean, args: any[]): React.ReactNode {
- if (!actionMenuNode.icon || !this.commands.isVisible(actionMenuNode.command, ...args) || (actionMenuNode.when && !this.contextKeys.match(actionMenuNode.when))) {
+ protected renderInlineCommand(actionMenuNode: CommandMenu, index: number, tabbable: boolean, args: any[]): React.ReactNode {
+ if (!actionMenuNode.icon || !actionMenuNode.isVisible(TEST_VIEW_INLINE_MENU, this.contextKeys, this.node, ...args)) {
return false;
}
const className = [TREE_NODE_SEGMENT_CLASS, TREE_NODE_TAIL_CLASS, actionMenuNode.icon, ACTION_ITEM, 'theia-test-tree-inline-action'].join(' ');
const tabIndex = tabbable ? 0 : undefined;
- const titleString = actionMenuNode.label + this.resolveKeybindingForCommand(actionMenuNode.command);
+ const titleString = actionMenuNode.label + (AcceleratorSource.is(actionMenuNode) ? actionMenuNode.getAccelerator(undefined).join(' ') : '');
return
{
e.stopPropagation();
- this.commands.executeCommand(actionMenuNode.command, ...args);
+ actionMenuNode.run(TEST_VIEW_INLINE_MENU, ...args);
}} />;
}
diff --git a/packages/test/src/browser/view/test-view-contribution.ts b/packages/test/src/browser/view/test-view-contribution.ts
index b28ba19ab162b..26f1a5d261485 100644
--- a/packages/test/src/browser/view/test-view-contribution.ts
+++ b/packages/test/src/browser/view/test-view-contribution.ts
@@ -274,6 +274,18 @@ export class TestViewContribution extends AbstractViewContribution
DeflatedToolbarTree;
+ @inject(CommandRegistry) commandRegistry: CommandRegistry;
+ @inject(ContextKeyService) contextKeyService: ContextKeyService;
+ @inject(KeybindingRegistry) keybindingRegistry: KeybindingRegistry;
+ @inject(LabelParser) labelParser: LabelParser;
+
@inject(ContributionProvider) @named(ToolbarContribution)
protected widgetContributions: ContributionProvider;
@@ -68,15 +75,15 @@ export class ToolbarController {
for (const column of Object.keys(schema.items)) {
const currentColumn = schema.items[column as ToolbarAlignment];
for (const group of currentColumn) {
- const newGroup: ToolbarItem[] = [];
+ const newGroup: TabBarToolbarItem[] = [];
for (const item of group) {
if (item.group === 'contributed') {
const contribution = this.getContributionByID(item.id);
if (contribution) {
- newGroup.push(contribution);
+ newGroup.push(new ReactToolbarItemImpl(this.commandRegistry, this.contextKeyService, contribution));
}
} else {
- newGroup.push({ ...item });
+ newGroup.push(new RenderedToolbarItemImpl(this.commandRegistry, this.contextKeyService, this.keybindingRegistry, this.labelParser, item));
}
}
if (newGroup.length) {
diff --git a/packages/toolbar/src/browser/toolbar-interfaces.ts b/packages/toolbar/src/browser/toolbar-interfaces.ts
index 1d1f0776c64ed..787fbf18bbe3c 100644
--- a/packages/toolbar/src/browser/toolbar-interfaces.ts
+++ b/packages/toolbar/src/browser/toolbar-interfaces.ts
@@ -15,7 +15,8 @@
// *****************************************************************************
import { interfaces } from '@theia/core/shared/inversify';
-import { ReactTabBarToolbarItem, RenderedToolbarItem, TabBarToolbar, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
+import { ReactTabBarToolbarAction, RenderedToolbarAction, TabBarToolbar } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
+import { TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item';
export enum ToolbarAlignment {
LEFT = 'left',
@@ -25,7 +26,7 @@ export enum ToolbarAlignment {
export interface ToolbarTreeSchema {
items: {
- [key in ToolbarAlignment]: ToolbarItem[][];
+ [key in ToolbarAlignment]: TabBarToolbarItem[][];
};
}
@@ -44,7 +45,7 @@ export interface ToolbarContributionProperties {
toJSON(): DeflatedContributedToolbarItem;
}
-export type ToolbarContribution = ReactTabBarToolbarItem & ToolbarContributionProperties;
+export type ToolbarContribution = ReactTabBarToolbarAction & ToolbarContributionProperties;
export const ToolbarContribution = Symbol('ToolbarContribution');
@@ -52,9 +53,9 @@ export const Toolbar = Symbol('Toolbar');
export const ToolbarFactory = Symbol('ToolbarFactory');
export type Toolbar = TabBarToolbar;
-export type ToolbarItem = ToolbarContribution | RenderedToolbarItem;
+export type ToolbarItem = ToolbarContribution | RenderedToolbarAction;
export interface DeflatedContributedToolbarItem { id: string; group: 'contributed' };
-export type ToolbarItemDeflated = DeflatedContributedToolbarItem | TabBarToolbarItem;
+export type ToolbarItemDeflated = DeflatedContributedToolbarItem | RenderedToolbarAction;
export const LateInjector = Symbol('LateInjector');
diff --git a/packages/toolbar/src/browser/toolbar.tsx b/packages/toolbar/src/browser/toolbar.tsx
index 6188e20f107cd..932214932814a 100644
--- a/packages/toolbar/src/browser/toolbar.tsx
+++ b/packages/toolbar/src/browser/toolbar.tsx
@@ -16,21 +16,20 @@
import * as React from '@theia/core/shared/react';
import { Anchor, ContextMenuAccess, KeybindingRegistry, PreferenceService, Widget, WidgetManager } from '@theia/core/lib/browser';
-import { LabelIcon } from '@theia/core/lib/browser/label-parser';
-import { ReactTabBarToolbarItem, RenderedToolbarItem, TabBarToolbar, TabBarToolbarFactory } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
+import { TabBarToolbar, TabBarToolbarFactory } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
-import { MenuPath, ProgressService } from '@theia/core';
+import { DisposableCollection, MenuPath, ProgressService } from '@theia/core';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { ProgressBarFactory } from '@theia/core/lib/browser/progress-bar-factory';
import { Deferred } from '@theia/core/lib/common/promise-util';
import {
- ToolbarItem,
ToolbarAlignment,
ToolbarAlignmentString,
ToolbarItemPosition,
} from './toolbar-interfaces';
import { ToolbarController } from './toolbar-controller';
import { ToolbarMenus } from './toolbar-constants';
+import { TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item';
const TOOLBAR_BACKGROUND_DATA_ID = 'toolbar-wrapper';
export const TOOLBAR_PROGRESSBAR_ID = 'main-toolbar-progress';
@@ -80,22 +79,21 @@ export class ToolbarImpl extends TabBarToolbar {
}
protected updateInlineItems(): void {
+ this.toDisposeOnUpdateItems.dispose();
+ this.toDisposeOnUpdateItems = new DisposableCollection();
this.inline.clear();
const { items } = this.model.toolbarItems;
- const contextKeys = new Set();
for (const column of Object.keys(items)) {
for (const group of items[column as ToolbarAlignment]) {
for (const item of group) {
this.inline.set(item.id, item);
-
- if (item.when) {
- this.contextKeyService.parseKeys(item.when)?.forEach(key => contextKeys.add(key));
+ if (item.onDidChange) {
+ this.toDisposeOnUpdateItems.push(item.onDidChange(() => this.maybeUpdate()));
}
}
}
}
- this.updateContextKeyListener(contextKeys);
}
protected handleContextMenu = (e: React.MouseEvent): ContextMenuAccess => this.doHandleContextMenu(e);
@@ -142,7 +140,7 @@ export class ToolbarImpl extends TabBarToolbar {
return args;
}
- protected renderGroupsInColumn(groups: ToolbarItem[][], alignment: ToolbarAlignment): React.ReactNode[] {
+ protected renderGroupsInColumn(groups: TabBarToolbarItem[][], alignment: ToolbarAlignment): React.ReactNode[] {
const nodes: React.ReactNode[] = [];
groups.forEach((group, groupIndex) => {
if (nodes.length && group.length) {
@@ -181,7 +179,7 @@ export class ToolbarImpl extends TabBarToolbar {
);
}
- protected renderColumnWrapper(alignment: ToolbarAlignment, columnGroup: ToolbarItem[][]): React.ReactNode {
+ protected renderColumnWrapper(alignment: ToolbarAlignment, columnGroup: TabBarToolbarItem[][]): React.ReactNode {
let children: React.ReactNode;
if (alignment === ToolbarAlignment.LEFT) {
children = (
@@ -235,23 +233,11 @@ export class ToolbarImpl extends TabBarToolbar {
);
}
- protected renderItemWithDraggableWrapper(item: ToolbarItem, position: ToolbarItemPosition): React.ReactNode {
+ protected renderItemWithDraggableWrapper(item: TabBarToolbarItem, position: ToolbarItemPosition): React.ReactNode {
const stringifiedPosition = JSON.stringify(position);
- let toolbarItemClassNames = '';
- let renderBody: React.ReactNode;
+ const toolbarItemClassNames = '';
+ const renderBody = item.render(this);
- if (!ReactTabBarToolbarItem.is(item)) {
- toolbarItemClassNames = TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM;
- if (this.evaluateWhenClause(item.when)) {
- toolbarItemClassNames += ' enabled';
- }
- renderBody = this.renderItem(item);
- } else {
- const contribution = this.model.getContributionByID(item.id);
- if (contribution) {
- renderBody = contribution.render();
- }
- }
return (
this.executeCommand(e, item)}
onDragOver={this.handleOnDragEnter}
onDragLeave={this.handleOnDragLeave}
onContextMenu={this.handleContextMenu}
@@ -279,41 +261,6 @@ export class ToolbarImpl extends TabBarToolbar {
);
}
- protected override renderItem(
- item: RenderedToolbarItem,
- ): React.ReactNode {
- const classNames = [];
- if (item.text) {
- for (const labelPart of this.labelParser.parse(item.text)) {
- if (typeof labelPart !== 'string' && LabelIcon.is(labelPart)) {
- const className = `fa fa-${labelPart.name}${labelPart.animation ? ' fa-' + labelPart.animation : ''}`;
- classNames.push(...className.split(' '));
- }
- }
- }
- const command = this.commands.getCommand(item.command!);
- const iconClass = (typeof item.icon === 'function' && item.icon()) || item.icon || command?.iconClass;
- if (iconClass) {
- classNames.push(iconClass);
- }
- let itemTooltip = '';
- if (item.tooltip) {
- itemTooltip = item.tooltip;
- } else if (command?.label) {
- itemTooltip = command.label;
- }
- const keybindingString = this.resolveKeybindingForCommand(command?.id);
- itemTooltip = `${itemTooltip}${keybindingString}`;
-
- return (
-
- );
- }
-
protected handleOnDragStart = (e: React.DragEvent
): void => this.doHandleOnDragStart(e);
protected doHandleOnDragStart(e: React.DragEvent): void {
const draggedElement = e.currentTarget;
diff --git a/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts b/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts
index 08782d516a2d5..9daf957f8b7a6 100644
--- a/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts
+++ b/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts
@@ -21,7 +21,7 @@ import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
-import { CompoundMenuNodeRole, MenuModelRegistry, MessageService, SelectionService, nls } from '@theia/core/lib/common';
+import { MenuModelRegistry, MessageService, SelectionService, nls } from '@theia/core/lib/common';
import { Color } from '@theia/core/lib/common/color';
import { Command, CommandRegistry } from '@theia/core/lib/common/command';
import URI from '@theia/core/lib/common/uri';
@@ -182,10 +182,6 @@ export class VSXExtensionsContribution extends AbstractViewContribution widget === this.getTabBarDelegate()
+ }));
+
+ this.toDisposeOnUpdateTitle.push(this.toolbarRegistry.registerItem({
+ id: VSXExtensionsCommands.CLEAR_ALL.id,
+ command: VSXExtensionsCommands.CLEAR_ALL.id,
+ text: VSXExtensionsCommands.CLEAR_ALL.label,
+ group: 'other_1',
+ priority: 1,
+ onDidChange: this.model.onDidChange,
+ isVisible: (widget: Widget) => widget === this.getTabBarDelegate()
+ }));
}
protected override getToggleVisibilityGroupLabel(): string {
- return 'a/' + nls.localizeByDefault('Views');
+ return nls.localizeByDefault('Views');
}
}
export namespace VSXExtensionsViewContainer {