Skip to content

Commit 3eb0872

Browse files
authored
Merge pull request microsoft#162491 from microsoft/joh/contextmenu-menu
Simplify context menu usage from menus
2 parents 2ae0992 + 88c6e02 commit 3eb0872

File tree

20 files changed

+166
-184
lines changed

20 files changed

+166
-184
lines changed

src/vs/editor/standalone/browser/standaloneServices.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,9 +948,11 @@ class StandaloneContextMenuService extends ContextMenuService {
948948
@INotificationService notificationService: INotificationService,
949949
@IContextViewService contextViewService: IContextViewService,
950950
@IKeybindingService keybindingService: IKeybindingService,
951-
@IThemeService themeService: IThemeService
951+
@IThemeService themeService: IThemeService,
952+
@IMenuService menuService: IMenuService,
953+
@IContextKeyService contextKeyService: IContextKeyService,
952954
) {
953-
super(telemetryService, notificationService, contextViewService, keybindingService, themeService);
955+
super(telemetryService, notificationService, contextViewService, keybindingService, themeService, menuService, contextKeyService);
954956
this.configure({ blockMouse: false }); // we do not want that in the standalone editor
955957
}
956958
}

src/vs/platform/actions/browser/toolbar.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { coalesceInPlace } from 'vs/base/common/arrays';
1010
import { BugIndicatingError } from 'vs/base/common/errors';
1111
import { DisposableStore } from 'vs/base/common/lifecycle';
1212
import { localize } from 'vs/nls';
13-
import { createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
13+
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
1414
import { IMenuActionOptions, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
1515
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
1616
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -182,23 +182,15 @@ export class WorkbenchToolBar extends ToolBar {
182182
}));
183183
}
184184

185-
// add context menu actions (iff appicable)
186-
if (this._options?.contextMenu) {
187-
const menu = this._menuService.createMenu(this._options.contextMenu, this._contextKeyService);
188-
const contextMenuActions: IAction[] = [];
189-
createAndFillInContextMenuActions(menu, { ...this._options?.menuOptions, renderShortTitle: true, }, contextMenuActions);
190-
menu.dispose();
191-
192-
if (contextMenuActions.length > 0) {
193-
actions = [...actions, new Separator(), ...contextMenuActions];
194-
}
195-
}
196-
197185
// this.getElement().classList.toggle('config', true);
198186

199187
this._contextMenuService.showContextMenu({
200188
getAnchor: () => e,
201189
getActions: () => actions,
190+
// add context menu actions (iff appicable)
191+
menuId: this._options?.contextMenu,
192+
menuActionOptions: { renderShortTitle: true, ...this._options?.menuOptions },
193+
contextKeyService: this._contextKeyService,
202194
onHide: () => this.getElement().classList.toggle('config', false),
203195
});
204196
}));

src/vs/platform/contextview/browser/contextMenuService.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55

66
import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
77
import { ModifierKeyEmitter } from 'vs/base/browser/dom';
8+
import { IAction, Separator } from 'vs/base/common/actions';
89
import { Emitter } from 'vs/base/common/event';
910
import { Disposable } from 'vs/base/common/lifecycle';
11+
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
12+
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
13+
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
1014
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
1115
import { INotificationService } from 'vs/platform/notification/common/notification';
1216
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1317
import { IThemeService } from 'vs/platform/theme/common/themeService';
1418
import { ContextMenuHandler, IContextMenuHandlerOptions } from './contextMenuHandler';
15-
import { IContextMenuService, IContextViewService } from './contextView';
19+
import { IContextMenuMenuDelegate, IContextMenuService, IContextViewService } from './contextView';
1620

1721
export class ContextMenuService extends Disposable implements IContextMenuService {
1822

@@ -27,18 +31,20 @@ export class ContextMenuService extends Disposable implements IContextMenuServic
2731
return this._contextMenuHandler;
2832
}
2933

30-
private readonly _onDidShowContextMenu = new Emitter<void>();
34+
private readonly _onDidShowContextMenu = this._store.add(new Emitter<void>());
3135
readonly onDidShowContextMenu = this._onDidShowContextMenu.event;
3236

33-
private readonly _onDidHideContextMenu = new Emitter<void>();
37+
private readonly _onDidHideContextMenu = this._store.add(new Emitter<void>());
3438
readonly onDidHideContextMenu = this._onDidHideContextMenu.event;
3539

3640
constructor(
3741
@ITelemetryService private readonly telemetryService: ITelemetryService,
3842
@INotificationService private readonly notificationService: INotificationService,
3943
@IContextViewService private readonly contextViewService: IContextViewService,
4044
@IKeybindingService private readonly keybindingService: IKeybindingService,
41-
@IThemeService private readonly themeService: IThemeService
45+
@IThemeService private readonly themeService: IThemeService,
46+
@IMenuService private readonly menuService: IMenuService,
47+
@IContextKeyService private readonly contextKeyService: IContextKeyService,
4248
) {
4349
super();
4450
}
@@ -49,7 +55,10 @@ export class ContextMenuService extends Disposable implements IContextMenuServic
4955

5056
// ContextMenu
5157

52-
showContextMenu(delegate: IContextMenuDelegate): void {
58+
showContextMenu(delegate: IContextMenuDelegate | IContextMenuMenuDelegate): void {
59+
60+
delegate = ContextMenuMenuDelegate.transform(delegate, this.menuService, this.contextKeyService);
61+
5362
this.contextMenuHandler.showContextMenu({
5463
...delegate,
5564
onHide: (didCancel) => {
@@ -62,3 +71,33 @@ export class ContextMenuService extends Disposable implements IContextMenuServic
6271
this._onDidShowContextMenu.fire();
6372
}
6473
}
74+
75+
export namespace ContextMenuMenuDelegate {
76+
77+
function is(thing: IContextMenuDelegate | IContextMenuMenuDelegate): thing is IContextMenuMenuDelegate {
78+
return thing && (<IContextMenuMenuDelegate>thing).menuId instanceof MenuId;
79+
}
80+
81+
export function transform(delegate: IContextMenuDelegate | IContextMenuMenuDelegate, menuService: IMenuService, globalContextKeyService: IContextKeyService): IContextMenuDelegate {
82+
if (!is(delegate)) {
83+
return delegate;
84+
}
85+
const { menuId, menuActionOptions, contextKeyService } = delegate;
86+
return {
87+
...delegate,
88+
getActions: () => {
89+
const target: IAction[] = [];
90+
if (menuId) {
91+
const menu = menuService.createMenu(menuId, contextKeyService ?? globalContextKeyService);
92+
createAndFillInContextMenuActions(menu, menuActionOptions, target);
93+
menu.dispose();
94+
}
95+
if (!delegate.getActions) {
96+
return target;
97+
} else {
98+
return Separator.join(delegate.getActions(), target);
99+
}
100+
}
101+
};
102+
}
103+
}

src/vs/platform/contextview/browser/contextView.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55

66
import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
77
import { AnchorAlignment, AnchorAxisAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
8+
import { IAction } from 'vs/base/common/actions';
89
import { Event } from 'vs/base/common/event';
910
import { IDisposable } from 'vs/base/common/lifecycle';
11+
import { IMenuActionOptions, MenuId } from 'vs/platform/actions/common/actions';
12+
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
1013
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
1114

1215
export const IContextViewService = createDecorator<IContextViewService>('contextViewService');
@@ -44,5 +47,25 @@ export interface IContextMenuService {
4447
readonly onDidShowContextMenu: Event<void>;
4548
readonly onDidHideContextMenu: Event<void>;
4649

47-
showContextMenu(delegate: IContextMenuDelegate): void;
50+
showContextMenu(delegate: IContextMenuDelegate | IContextMenuMenuDelegate): void;
4851
}
52+
53+
export type IContextMenuMenuDelegate = {
54+
/**
55+
* The MenuId that should be used to populate the context menu.
56+
*/
57+
menuId?: MenuId;
58+
/**
59+
* Optional options how menu actions are invoked
60+
*/
61+
menuActionOptions?: IMenuActionOptions;
62+
/**
63+
* Optional context key service which drives the given menu
64+
*/
65+
contextKeyService?: IContextKeyService;
66+
67+
/**
68+
* Optional getter for extra actions. They will be prepended to the menu actions.
69+
*/
70+
getActions?(): IAction[];
71+
} & Omit<IContextMenuDelegate, 'getActions'>;

src/vs/workbench/browser/parts/editor/editorGroupView.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
3636
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
3737
import { IAction } from 'vs/base/common/actions';
3838
import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitleControl';
39-
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
39+
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
4040
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
41-
import { createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
41+
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
4242
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
4343
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
4444
import { hash } from 'vs/base/common/hash';
@@ -364,13 +364,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
364364
}
365365

366366
private createContainerContextMenu(): void {
367-
const menu = this._register(this.menuService.createMenu(MenuId.EmptyEditorGroupContext, this.contextKeyService));
368-
369-
this._register(addDisposableListener(this.element, EventType.CONTEXT_MENU, e => this.onShowContainerContextMenu(menu, e)));
370-
this._register(addDisposableListener(this.element, TouchEventType.Contextmenu, () => this.onShowContainerContextMenu(menu)));
367+
this._register(addDisposableListener(this.element, EventType.CONTEXT_MENU, e => this.onShowContainerContextMenu(e)));
368+
this._register(addDisposableListener(this.element, TouchEventType.Contextmenu, () => this.onShowContainerContextMenu()));
371369
}
372370

373-
private onShowContainerContextMenu(menu: IMenu, e?: MouseEvent): void {
371+
private onShowContainerContextMenu(e?: MouseEvent): void {
374372
if (!this.isEmpty) {
375373
return; // only for empty editor groups
376374
}
@@ -382,14 +380,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
382380
anchor = { x: event.posx, y: event.posy };
383381
}
384382

385-
// Fill in contributed actions
386-
const actions: IAction[] = [];
387-
createAndFillInContextMenuActions(menu, undefined, actions);
388-
389383
// Show it
390384
this.contextMenuService.showContextMenu({
385+
menuId: MenuId.EmptyEditorGroupContext,
386+
contextKeyService: this.contextKeyService,
391387
getAnchor: () => anchor,
392-
getActions: () => actions,
393388
onHide: () => {
394389
this.focus();
395390
}

src/vs/workbench/browser/parts/editor/titleControl.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/bro
1212
import { IAction, SubmenuAction, ActionRunner } from 'vs/base/common/actions';
1313
import { ResolvedKeybinding } from 'vs/base/common/keybindings';
1414
import { dispose, DisposableStore } from 'vs/base/common/lifecycle';
15-
import { createActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
16-
import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
15+
import { createActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
16+
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
1717
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1818
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
1919
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -108,7 +108,6 @@ export abstract class TitleControl extends Themable {
108108

109109
private readonly editorToolBarMenuDisposables = this._register(new DisposableStore());
110110

111-
private contextMenu: IMenu;
112111
private renderDropdownAsChildElement: boolean;
113112

114113
constructor(
@@ -140,7 +139,6 @@ export abstract class TitleControl extends Themable {
140139

141140
this.groupLockedContext = ActiveEditorGroupLockedContext.bindTo(contextKeyService);
142141

143-
this.contextMenu = this._register(this.menuService.createMenu(MenuId.EditorTitleContext, this.contextKeyService));
144142
this.renderDropdownAsChildElement = false;
145143

146144
this.create(parent);
@@ -374,14 +372,12 @@ export abstract class TitleControl extends Themable {
374372
anchor = { x: event.posx, y: event.posy };
375373
}
376374

377-
// Fill in contributed actions
378-
const actions: IAction[] = [];
379-
createAndFillInContextMenuActions(this.contextMenu, { shouldForwardArgs: true, arg: this.resourceContext.get() }, actions);
380-
381375
// Show it
382376
this.contextMenuService.showContextMenu({
383377
getAnchor: () => anchor,
384-
getActions: () => actions,
378+
menuId: MenuId.EditorTitleContext,
379+
menuActionOptions: { shouldForwardArgs: true, arg: this.resourceContext.get() },
380+
contextKeyService: this.contextKeyService,
385381
getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) }),
386382
getKeyBinding: action => this.getKeybinding(action),
387383
onHide: () => {

src/vs/workbench/browser/parts/titlebar/titlebarPart.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { getZoomFactor } from 'vs/base/browser/browser';
1111
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/window/common/window';
1212
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
1313
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
14-
import { IAction } from 'vs/base/common/actions';
1514
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
1615
import { DisposableStore } from 'vs/base/common/lifecycle';
1716
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
@@ -25,8 +24,8 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
2524
import { Emitter, Event } from 'vs/base/common/event';
2625
import { IStorageService } from 'vs/platform/storage/common/storage';
2726
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
28-
import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
29-
import { Action2, IMenuService, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
27+
import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
28+
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
3029
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
3130
import { IHostService } from 'vs/workbench/services/host/browser/host';
3231
import { Codicon } from 'vs/base/common/codicons';
@@ -92,7 +91,6 @@ export class TitlebarPart extends Part implements ITitleService {
9291
@IThemeService themeService: IThemeService,
9392
@IStorageService storageService: IStorageService,
9493
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
95-
@IMenuService private readonly menuService: IMenuService,
9694
@IContextKeyService private readonly contextKeyService: IContextKeyService,
9795
@IHostService private readonly hostService: IHostService,
9896
@IHoverService hoverService: IHoverService,
@@ -382,16 +380,11 @@ export class TitlebarPart extends Part implements ITitleService {
382380
const event = new StandardMouseEvent(e);
383381
const anchor = { x: event.posx, y: event.posy };
384382

385-
// Fill in contributed actions
386-
const menu = this.menuService.createMenu(menuId, this.contextKeyService);
387-
const actions: IAction[] = [];
388-
createAndFillInContextMenuActions(menu, undefined, actions);
389-
menu.dispose();
390-
391383
// Show it
392384
this.contextMenuService.showContextMenu({
393385
getAnchor: () => anchor,
394-
getActions: () => actions,
386+
menuId,
387+
contextKeyService: this.contextKeyService,
395388
domForShadowRoot: isMacintosh && isNative ? event.target : undefined
396389
});
397390
}

src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/l
2626
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
2727
import Severity from 'vs/base/common/severity';
2828
import { basename, dirname } from 'vs/base/common/resources';
29-
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
30-
import { IAction } from 'vs/base/common/actions';
31-
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
29+
import { MenuId } from 'vs/platform/actions/common/actions';
3230
import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
3331
import { CancellationToken } from 'vs/base/common/cancellation';
3432
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
@@ -78,10 +76,9 @@ export class BulkEditPane extends ViewPane {
7876
@ILabelService private readonly _labelService: ILabelService,
7977
@ITextModelService private readonly _textModelService: ITextModelService,
8078
@IDialogService private readonly _dialogService: IDialogService,
81-
@IMenuService private readonly _menuService: IMenuService,
8279
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
83-
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
8480
@IStorageService private readonly _storageService: IStorageService,
81+
@IContextKeyService contextKeyService: IContextKeyService,
8582
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
8683
@IKeybindingService keybindingService: IKeybindingService,
8784
@IContextMenuService contextMenuService: IContextMenuService,
@@ -92,13 +89,13 @@ export class BulkEditPane extends ViewPane {
9289
) {
9390
super(
9491
{ ...options, titleMenuId: MenuId.BulkEditTitle },
95-
keybindingService, contextMenuService, configurationService, _contextKeyService, viewDescriptorService, _instaService, openerService, themeService, telemetryService
92+
keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instaService, openerService, themeService, telemetryService
9693
);
9794

9895
this.element.classList.add('bulk-edit-panel', 'show-file-icons');
99-
this._ctxHasCategories = BulkEditPane.ctxHasCategories.bindTo(_contextKeyService);
100-
this._ctxGroupByFile = BulkEditPane.ctxGroupByFile.bindTo(_contextKeyService);
101-
this._ctxHasCheckedChanges = BulkEditPane.ctxHasCheckedChanges.bindTo(_contextKeyService);
96+
this._ctxHasCategories = BulkEditPane.ctxHasCategories.bindTo(contextKeyService);
97+
this._ctxGroupByFile = BulkEditPane.ctxGroupByFile.bindTo(contextKeyService);
98+
this._ctxHasCheckedChanges = BulkEditPane.ctxHasCheckedChanges.bindTo(contextKeyService);
10299
}
103100

104101
override dispose(): void {
@@ -380,16 +377,11 @@ export class BulkEditPane extends ViewPane {
380377
}
381378

382379
private _onContextMenu(e: ITreeContextMenuEvent<any>): void {
383-
const menu = this._menuService.createMenu(MenuId.BulkEditContext, this._contextKeyService);
384-
const actions: IAction[] = [];
385-
createAndFillInContextMenuActions(menu, undefined, actions);
386380

387381
this._contextMenuService.showContextMenu({
388-
getActions: () => actions,
389-
getAnchor: () => e.anchor,
390-
onHide: () => {
391-
menu.dispose();
392-
}
382+
menuId: MenuId.BulkEditContext,
383+
contextKeyService: this.contextKeyService,
384+
getAnchor: () => e.anchor
393385
});
394386
}
395387
}

src/vs/workbench/contrib/comments/browser/commentMenus.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ export class CommentMenus implements IDisposable {
3535
return this.getMenu(MenuId.CommentThreadTitleContext, contextKeyService);
3636
}
3737

38-
getCommentThreadCommentContextActions(contextKeyService: IContextKeyService): IMenu {
39-
return this.getMenu(MenuId.CommentThreadCommentContext, contextKeyService);
40-
}
41-
4238
private getMenu(menuId: MenuId, contextKeyService: IContextKeyService): IMenu {
4339
const menu = this.menuService.createMenu(menuId, contextKeyService);
4440

0 commit comments

Comments
 (0)