Skip to content

Commit cfd3a08

Browse files
committed
add support so that IContextMenuService can open with just a menu identifier
1 parent 9fb452c commit cfd3a08

File tree

3 files changed

+94
-20
lines changed

3 files changed

+94
-20
lines changed

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/services/contextmenu/electron-sandbox/contextmenuService.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,35 @@
55

66
import { IAction, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, Separator, SubmenuAction } from 'vs/base/common/actions';
77
import * as dom from 'vs/base/browser/dom';
8-
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
8+
import { IContextMenuMenuDelegate, IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
99
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1010
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
1111
import { getZoomFactor } from 'vs/base/browser/browser';
1212
import { unmnemonicLabel } from 'vs/base/common/labels';
1313
import { INotificationService } from 'vs/platform/notification/common/notification';
1414
import { IContextMenuDelegate, IContextMenuEvent } from 'vs/base/browser/contextmenu';
1515
import { once } from 'vs/base/common/functional';
16-
import { Disposable } from 'vs/base/common/lifecycle';
1716
import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu';
1817
import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu';
1918
import { getTitleBarStyle } from 'vs/platform/window/common/window';
2019
import { isMacintosh, isWindows } from 'vs/base/common/platform';
2120
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
22-
import { ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService';
21+
import { ContextMenuMenuDelegate, ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService';
2322
import { IThemeService } from 'vs/platform/theme/common/themeService';
2423
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
2524
import { stripIcons } from 'vs/base/common/iconLabels';
2625
import { coalesce } from 'vs/base/common/arrays';
2726
import { Event, Emitter } from 'vs/base/common/event';
2827
import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview';
28+
import { IMenuService } from 'vs/platform/actions/common/actions';
29+
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
30+
import { Disposable } from 'vs/base/common/lifecycle';
2931

30-
export class ContextMenuService extends Disposable implements IContextMenuService {
32+
export class ContextMenuService implements IContextMenuService {
3133

3234
declare readonly _serviceBrand: undefined;
3335

34-
private impl: IContextMenuService;
36+
private impl: HTMLContextMenuService | NativeContextMenuService;
3537

3638
get onDidShowContextMenu(): Event<void> { return this.impl.onDidShowContextMenu; }
3739
get onDidHideContextMenu(): Event<void> { return this.impl.onDidHideContextMenu; }
@@ -42,22 +44,27 @@ export class ContextMenuService extends Disposable implements IContextMenuServic
4244
@IKeybindingService keybindingService: IKeybindingService,
4345
@IConfigurationService configurationService: IConfigurationService,
4446
@IContextViewService contextViewService: IContextViewService,
45-
@IThemeService themeService: IThemeService
47+
@IThemeService themeService: IThemeService,
48+
@IMenuService menuService: IMenuService,
49+
@IContextKeyService contextKeyService: IContextKeyService,
4650
) {
47-
super();
4851

4952
// Custom context menu: Linux/Windows if custom title is enabled
5053
if (!isMacintosh && getTitleBarStyle(configurationService) === 'custom') {
51-
this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService);
54+
this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService, menuService, contextKeyService);
5255
}
5356

5457
// Native context menu: otherwise
5558
else {
56-
this.impl = new NativeContextMenuService(notificationService, telemetryService, keybindingService);
59+
this.impl = new NativeContextMenuService(notificationService, telemetryService, keybindingService, menuService, contextKeyService);
5760
}
5861
}
5962

60-
showContextMenu(delegate: IContextMenuDelegate): void {
63+
dispose(): void {
64+
this.impl.dispose();
65+
}
66+
67+
showContextMenu(delegate: IContextMenuDelegate | IContextMenuMenuDelegate): void {
6168
this.impl.showContextMenu(delegate);
6269
}
6370
}
@@ -66,21 +73,26 @@ class NativeContextMenuService extends Disposable implements IContextMenuService
6673

6774
declare readonly _serviceBrand: undefined;
6875

69-
private readonly _onDidShowContextMenu = new Emitter<void>();
76+
private readonly _onDidShowContextMenu = this._store.add(new Emitter<void>());
7077
readonly onDidShowContextMenu = this._onDidShowContextMenu.event;
7178

72-
private readonly _onDidHideContextMenu = new Emitter<void>();
79+
private readonly _onDidHideContextMenu = this._store.add(new Emitter<void>());
7380
readonly onDidHideContextMenu = this._onDidHideContextMenu.event;
7481

7582
constructor(
7683
@INotificationService private readonly notificationService: INotificationService,
7784
@ITelemetryService private readonly telemetryService: ITelemetryService,
78-
@IKeybindingService private readonly keybindingService: IKeybindingService
85+
@IKeybindingService private readonly keybindingService: IKeybindingService,
86+
@IMenuService private readonly menuService: IMenuService,
87+
@IContextKeyService private readonly contextKeyService: IContextKeyService,
7988
) {
8089
super();
8190
}
8291

83-
showContextMenu(delegate: IContextMenuDelegate): void {
92+
showContextMenu(delegate: IContextMenuDelegate | IContextMenuMenuDelegate): void {
93+
94+
delegate = ContextMenuMenuDelegate.transform(delegate, this.menuService, this.contextKeyService);
95+
8496
const actions = delegate.getActions();
8597
if (actions.length) {
8698
const onHide = once(() => {

0 commit comments

Comments
 (0)