Skip to content

Commit

Permalink
support to hide submenus too (#155063)
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken authored and andreamah committed Jul 14, 2022
1 parent 42e956e commit 81ba821
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 48 deletions.
80 changes: 45 additions & 35 deletions src/vs/platform/actions/browser/menuEntryActionViewItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { isDark } from 'vs/platform/theme/common/theme';
import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { assertType } from 'vs/base/common/types';

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

function registerConfigureMenu(contextMenuService: IContextMenuService, item: BaseActionViewItem, action: MenuItemAction | SubmenuItemAction): IDisposable {
assertType(item.element);
return addDisposableListener(item.element, 'contextmenu', event => {
if (!action.hideActions) {
return;
}

event.preventDefault();
event.stopPropagation();

contextMenuService.showContextMenu({
getAnchor: () => item.element!,
getActions: () => action.hideActions!.asList()
});
}, true);
}

export class MenuEntryActionViewItem extends ActionViewItem {

private _wantsAltCommand: boolean = false;
Expand Down Expand Up @@ -204,20 +222,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
updateAltState();
}));


this._register(addDisposableListener(container, 'contextmenu', event => {
if (!this._menuItemAction.hideActions) {
return;
}

event.preventDefault();
event.stopPropagation();

this._contextMenuService.showContextMenu({
getAnchor: () => container,
getActions: () => this._menuItemAction.hideActions!.asList()
});
}, true));
this._register(registerConfigureMenu(this._contextMenuService, this, this._menuItemAction));
}

override updateLabel(): void {
Expand Down Expand Up @@ -308,40 +313,43 @@ export class SubmenuEntryActionViewItem extends DropdownMenuActionViewItem {
constructor(
action: SubmenuItemAction,
options: IDropdownMenuActionViewItemOptions | undefined,
@IContextMenuService contextMenuService: IContextMenuService,
@IContextMenuService protected _contextMenuService: IContextMenuService,
@IThemeService protected _themeService: IThemeService
) {
const dropdownOptions = Object.assign({}, options ?? Object.create(null), {
menuAsChild: options?.menuAsChild ?? false,
classNames: options?.classNames ?? (ThemeIcon.isThemeIcon(action.item.icon) ? ThemeIcon.asClassName(action.item.icon) : undefined),
});

super(action, { getActions: () => action.actions }, contextMenuService, dropdownOptions);
super(action, { getActions: () => action.actions }, _contextMenuService, dropdownOptions);
}

override render(container: HTMLElement): void {
super.render(container);
if (this.element) {
container.classList.add('menu-entry');
const { icon } = (<SubmenuItemAction>this._action).item;
if (icon && !ThemeIcon.isThemeIcon(icon)) {
this.element.classList.add('icon');
const setBackgroundImage = () => {
if (this.element) {
this.element.style.backgroundImage = (
isDark(this._themeService.getColorTheme().type)
? asCSSUrl(icon.dark)
: asCSSUrl(icon.light)
);
}
};
assertType(this.element);

container.classList.add('menu-entry');
const action = <SubmenuItemAction>this._action;
const { icon } = action.item;
if (icon && !ThemeIcon.isThemeIcon(icon)) {
this.element.classList.add('icon');
const setBackgroundImage = () => {
if (this.element) {
this.element.style.backgroundImage = (
isDark(this._themeService.getColorTheme().type)
? asCSSUrl(icon.dark)
: asCSSUrl(icon.light)
);
}
};
setBackgroundImage();
this._register(this._themeService.onDidColorThemeChange(() => {
// refresh when the theme changes in case we go between dark <-> light
setBackgroundImage();
this._register(this._themeService.onDidColorThemeChange(() => {
// refresh when the theme changes in case we go between dark <-> light
setBackgroundImage();
}));
}
}));
}

this._register(registerConfigureMenu(this._contextMenuService, this, action));
}
}

Expand Down Expand Up @@ -461,6 +469,8 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem {
event.stopPropagation();
}
}));

this._register(registerConfigureMenu(this._contextMenuService, this, (<SubmenuItemAction>this.action)));
}

override focus(fromRight?: boolean): void {
Expand Down
5 changes: 3 additions & 2 deletions src/vs/platform/actions/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ export interface ISubmenuItem {
rememberDefaultAction?: boolean; // for dropdown menu: if true the last executed action is remembered as the default action
}

export function isIMenuItem(item: IMenuItem | ISubmenuItem): item is IMenuItem {
export function isIMenuItem(item: any): item is IMenuItem {
return (item as IMenuItem).command !== undefined;
}

export function isISubmenuItem(item: IMenuItem | ISubmenuItem): item is ISubmenuItem {
export function isISubmenuItem(item: any): item is ISubmenuItem {
return (item as ISubmenuItem).submenu !== undefined;
}

Expand Down Expand Up @@ -350,6 +350,7 @@ export class SubmenuItemAction extends SubmenuAction {

constructor(
readonly item: ISubmenuItem,
readonly hideActions: MenuItemActionManageActions,
private readonly _menuService: IMenuService,
private readonly _contextKeyService: IContextKeyService,
private readonly _options?: IMenuActionOptions
Expand Down
22 changes: 11 additions & 11 deletions src/vs/platform/actions/common/menuService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { RunOnceScheduler } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IMenu, IMenuActionOptions, IMenuCreateOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuItemActionManageActions, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { IMenu, IMenuActionOptions, IMenuCreateOptions, IMenuItem, IMenuService, isIMenuItem, isISubmenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuItemActionManageActions, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { ICommandAction, ILocalizedString } from 'vs/platform/action/common/action';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
Expand Down Expand Up @@ -251,18 +251,17 @@ class Menu implements IMenu {
for (const item of items) {
if (this._contextKeyService.contextMatchesRules(item.when)) {
let action: MenuItemAction | SubmenuItemAction | undefined;
if (isIMenuItem(item)) {
const isMenuItem = isIMenuItem(item);
const hideActions = new MenuItemActionManageActions(new HideMenuItemAction(this._id, isMenuItem ? item.command : item, this._hiddenStates), allToggleActions);

if (isMenuItem) {
if (!this._hiddenStates.isHidden(this._id, item.command.id)) {
action = new MenuItemAction(
item.command, item.alt, options,
new MenuItemActionManageActions(new HideMenuItemAction(this._id, item.command, this._hiddenStates), allToggleActions),
this._contextKeyService, this._commandService
);
action = new MenuItemAction(item.command, item.alt, options, hideActions, this._contextKeyService, this._commandService);
}
// add toggle commmand
toggleActions.push(new ToggleMenuItemAction(this._id, item.command, this._hiddenStates));
} else {
action = new SubmenuItemAction(item, this._menuService, this._contextKeyService, options);
action = new SubmenuItemAction(item, hideActions, this._menuService, this._contextKeyService, options);
if (action.actions.length === 0) {
action.dispose();
action = undefined;
Expand Down Expand Up @@ -397,10 +396,11 @@ class HideMenuItemAction implements IAction {

run: () => void;

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

dispose(): void {
Expand Down

0 comments on commit 81ba821

Please sign in to comment.