Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support to hide submenus too #155063

Merged
merged 1 commit into from Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 45 additions & 35 deletions src/vs/platform/actions/browser/menuEntryActionViewItem.ts
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
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
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