Skip to content

Commit b79eaca

Browse files
authored
support to hide submenus too (microsoft#155063)
microsoft#154804
1 parent b7f71bd commit b79eaca

File tree

3 files changed

+59
-48
lines changed

3 files changed

+59
-48
lines changed

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

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
2626
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
2727
import { isDark } from 'vs/platform/theme/common/theme';
2828
import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
29+
import { assertType } from 'vs/base/common/types';
2930

3031
export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string): IDisposable {
3132
const groups = menu.getActions(options);
@@ -129,6 +130,23 @@ export interface IMenuEntryActionViewItemOptions {
129130
hoverDelegate?: IHoverDelegate;
130131
}
131132

133+
function registerConfigureMenu(contextMenuService: IContextMenuService, item: BaseActionViewItem, action: MenuItemAction | SubmenuItemAction): IDisposable {
134+
assertType(item.element);
135+
return addDisposableListener(item.element, 'contextmenu', event => {
136+
if (!action.hideActions) {
137+
return;
138+
}
139+
140+
event.preventDefault();
141+
event.stopPropagation();
142+
143+
contextMenuService.showContextMenu({
144+
getAnchor: () => item.element!,
145+
getActions: () => action.hideActions!.asList()
146+
});
147+
}, true);
148+
}
149+
132150
export class MenuEntryActionViewItem extends ActionViewItem {
133151

134152
private _wantsAltCommand: boolean = false;
@@ -204,20 +222,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
204222
updateAltState();
205223
}));
206224

207-
208-
this._register(addDisposableListener(container, 'contextmenu', event => {
209-
if (!this._menuItemAction.hideActions) {
210-
return;
211-
}
212-
213-
event.preventDefault();
214-
event.stopPropagation();
215-
216-
this._contextMenuService.showContextMenu({
217-
getAnchor: () => container,
218-
getActions: () => this._menuItemAction.hideActions!.asList()
219-
});
220-
}, true));
225+
this._register(registerConfigureMenu(this._contextMenuService, this, this._menuItemAction));
221226
}
222227

223228
override updateLabel(): void {
@@ -308,40 +313,43 @@ export class SubmenuEntryActionViewItem extends DropdownMenuActionViewItem {
308313
constructor(
309314
action: SubmenuItemAction,
310315
options: IDropdownMenuActionViewItemOptions | undefined,
311-
@IContextMenuService contextMenuService: IContextMenuService,
316+
@IContextMenuService protected _contextMenuService: IContextMenuService,
312317
@IThemeService protected _themeService: IThemeService
313318
) {
314319
const dropdownOptions = Object.assign({}, options ?? Object.create(null), {
315320
menuAsChild: options?.menuAsChild ?? false,
316321
classNames: options?.classNames ?? (ThemeIcon.isThemeIcon(action.item.icon) ? ThemeIcon.asClassName(action.item.icon) : undefined),
317322
});
318323

319-
super(action, { getActions: () => action.actions }, contextMenuService, dropdownOptions);
324+
super(action, { getActions: () => action.actions }, _contextMenuService, dropdownOptions);
320325
}
321326

322327
override render(container: HTMLElement): void {
323328
super.render(container);
324-
if (this.element) {
325-
container.classList.add('menu-entry');
326-
const { icon } = (<SubmenuItemAction>this._action).item;
327-
if (icon && !ThemeIcon.isThemeIcon(icon)) {
328-
this.element.classList.add('icon');
329-
const setBackgroundImage = () => {
330-
if (this.element) {
331-
this.element.style.backgroundImage = (
332-
isDark(this._themeService.getColorTheme().type)
333-
? asCSSUrl(icon.dark)
334-
: asCSSUrl(icon.light)
335-
);
336-
}
337-
};
329+
assertType(this.element);
330+
331+
container.classList.add('menu-entry');
332+
const action = <SubmenuItemAction>this._action;
333+
const { icon } = action.item;
334+
if (icon && !ThemeIcon.isThemeIcon(icon)) {
335+
this.element.classList.add('icon');
336+
const setBackgroundImage = () => {
337+
if (this.element) {
338+
this.element.style.backgroundImage = (
339+
isDark(this._themeService.getColorTheme().type)
340+
? asCSSUrl(icon.dark)
341+
: asCSSUrl(icon.light)
342+
);
343+
}
344+
};
345+
setBackgroundImage();
346+
this._register(this._themeService.onDidColorThemeChange(() => {
347+
// refresh when the theme changes in case we go between dark <-> light
338348
setBackgroundImage();
339-
this._register(this._themeService.onDidColorThemeChange(() => {
340-
// refresh when the theme changes in case we go between dark <-> light
341-
setBackgroundImage();
342-
}));
343-
}
349+
}));
344350
}
351+
352+
this._register(registerConfigureMenu(this._contextMenuService, this, action));
345353
}
346354
}
347355

@@ -461,6 +469,8 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem {
461469
event.stopPropagation();
462470
}
463471
}));
472+
473+
this._register(registerConfigureMenu(this._contextMenuService, this, (<SubmenuItemAction>this.action)));
464474
}
465475

466476
override focus(fromRight?: boolean): void {

src/vs/platform/actions/common/actions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ export interface ISubmenuItem {
3535
rememberDefaultAction?: boolean; // for dropdown menu: if true the last executed action is remembered as the default action
3636
}
3737

38-
export function isIMenuItem(item: IMenuItem | ISubmenuItem): item is IMenuItem {
38+
export function isIMenuItem(item: any): item is IMenuItem {
3939
return (item as IMenuItem).command !== undefined;
4040
}
4141

42-
export function isISubmenuItem(item: IMenuItem | ISubmenuItem): item is ISubmenuItem {
42+
export function isISubmenuItem(item: any): item is ISubmenuItem {
4343
return (item as ISubmenuItem).submenu !== undefined;
4444
}
4545

@@ -350,6 +350,7 @@ export class SubmenuItemAction extends SubmenuAction {
350350

351351
constructor(
352352
readonly item: ISubmenuItem,
353+
readonly hideActions: MenuItemActionManageActions,
353354
private readonly _menuService: IMenuService,
354355
private readonly _contextKeyService: IContextKeyService,
355356
private readonly _options?: IMenuActionOptions

src/vs/platform/actions/common/menuService.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { RunOnceScheduler } from 'vs/base/common/async';
77
import { Emitter, Event } from 'vs/base/common/event';
88
import { DisposableStore } from 'vs/base/common/lifecycle';
9-
import { IMenu, IMenuActionOptions, IMenuCreateOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuItemActionManageActions, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions';
9+
import { IMenu, IMenuActionOptions, IMenuCreateOptions, IMenuItem, IMenuService, isIMenuItem, isISubmenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuItemActionManageActions, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions';
1010
import { ICommandAction, ILocalizedString } from 'vs/platform/action/common/action';
1111
import { ICommandService } from 'vs/platform/commands/common/commands';
1212
import { ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -251,18 +251,17 @@ class Menu implements IMenu {
251251
for (const item of items) {
252252
if (this._contextKeyService.contextMatchesRules(item.when)) {
253253
let action: MenuItemAction | SubmenuItemAction | undefined;
254-
if (isIMenuItem(item)) {
254+
const isMenuItem = isIMenuItem(item);
255+
const hideActions = new MenuItemActionManageActions(new HideMenuItemAction(this._id, isMenuItem ? item.command : item, this._hiddenStates), allToggleActions);
256+
257+
if (isMenuItem) {
255258
if (!this._hiddenStates.isHidden(this._id, item.command.id)) {
256-
action = new MenuItemAction(
257-
item.command, item.alt, options,
258-
new MenuItemActionManageActions(new HideMenuItemAction(this._id, item.command, this._hiddenStates), allToggleActions),
259-
this._contextKeyService, this._commandService
260-
);
259+
action = new MenuItemAction(item.command, item.alt, options, hideActions, this._contextKeyService, this._commandService);
261260
}
262261
// add toggle commmand
263262
toggleActions.push(new ToggleMenuItemAction(this._id, item.command, this._hiddenStates));
264263
} else {
265-
action = new SubmenuItemAction(item, this._menuService, this._contextKeyService, options);
264+
action = new SubmenuItemAction(item, hideActions, this._menuService, this._contextKeyService, options);
266265
if (action.actions.length === 0) {
267266
action.dispose();
268267
action = undefined;
@@ -397,10 +396,11 @@ class HideMenuItemAction implements IAction {
397396

398397
run: () => void;
399398

400-
constructor(id: MenuId, command: ICommandAction, hiddenStates: PersistedMenuHideState) {
401-
this.id = `hide/${id.id}/${command.id}`;
399+
constructor(menu: MenuId, command: ICommandAction | ISubmenuItem, hiddenStates: PersistedMenuHideState) {
400+
const id = isISubmenuItem(command) ? command.submenu.id : command.id;
401+
this.id = `hide/${menu.id}/${id}`;
402402
this.label = localize('hide.label', 'Hide \'{0}\'', typeof command.title === 'string' ? command.title : command.title.value);
403-
this.run = () => { hiddenStates.updateHidden(id, command.id, true); };
403+
this.run = () => { hiddenStates.updateHidden(menu, id, true); };
404404
}
405405

406406
dispose(): void {

0 commit comments

Comments
 (0)