Skip to content

Commit

Permalink
Submenu contribution to editor/title and view/title not working #12706
Browse files Browse the repository at this point in the history
* introduce option in RenderContextMenuOptions to ask renderer to not
render a menu with single submenu. Instead render only the children
* add helper method to menu-model-registry to remove the parent nodes
from a menu node as described above
* adjust renderers and callers accordingly
  • Loading branch information
jfaltermeier committed Aug 17, 2023
1 parent f84e7d6 commit 811dc11
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 9 deletions.
5 changes: 5 additions & 0 deletions packages/core/src/browser/context-menu-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,9 @@ export interface RenderContextMenuOptions {
context?: HTMLElement;
contextKeyService?: ContextMatcher;
onHide?: () => void;
/**
* If true a single submenu in the context menu is not rendered but its children are rendered on the top level.
* Default is `false`.
*/
skipSingleRootNode?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export class BrowserContextMenuRenderer extends ContextMenuRenderer {
super();
}

protected doRender({ menuPath, anchor, args, onHide, context, contextKeyService }: RenderContextMenuOptions): ContextMenuAccess {
const contextMenu = this.menuFactory.createContextMenu(menuPath, args, context, contextKeyService);
protected doRender({ menuPath, anchor, args, onHide, context, contextKeyService, skipSingleRootNode }: RenderContextMenuOptions): ContextMenuAccess {
const contextMenu = this.menuFactory.createContextMenu(menuPath, args, context, contextKeyService, skipSingleRootNode);
const { x, y } = coordinateFromAnchor(anchor);
if (onHide) {
contextMenu.aboutToClose.connect(() => onHide!());
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/browser/menu/browser-menu-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory {
}
}

createContextMenu(path: MenuPath, args?: unknown[], context?: HTMLElement, contextKeyService?: ContextMatcher): MenuWidget {
const menuModel = this.menuProvider.getMenu(path);
createContextMenu(path: MenuPath, args?: unknown[], context?: HTMLElement, contextKeyService?: ContextMatcher, skipSingleRootNode?: boolean): MenuWidget {
const menuModel = skipSingleRootNode ? this.menuProvider.removeSingleRootNode(this.menuProvider.getMenu(path), path) : this.menuProvider.getMenu(path);
const menuCommandRegistry = this.createMenuCommandRegistry(menuModel, args).snapshot(path);
const contextMenu = this.createMenuWidget(menuModel, { commands: menuCommandRegistry, context, rootMenuPath: path, contextKeyService });
return contextMenu;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@ export class TabBarToolbar extends ReactWidget {
args: [this.current],
anchor,
context: this.current?.node,
onHide: () => toDisposeOnHide.dispose()
onHide: () => toDisposeOnHide.dispose(),
skipSingleRootNode: true,
});
}

Expand Down
51 changes: 51 additions & 0 deletions packages/core/src/common/menu/menu-model-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,57 @@ export class MenuModelRegistry {
return this.findGroup(menuPath);
}

/**
* Checks the given menu model whether it will show a menu with a single submenu.
*
* @param fullMenuModel the menu model to analyze
* @param menuPath the menu's path
* @returns if the menu will show a single submenu this returns a menu that will show the child elements of the submenu,
* otherwise the given `fullMenuModel` is return
*/
removeSingleRootNode(fullMenuModel: MutableCompoundMenuNode, menuPath: MenuPath): CompoundMenuNode {
// check whether all children are compound menus and that there is only one child that has further children
if (!this.allChildrenCompound(fullMenuModel.children)) {
return fullMenuModel;
}
let nonEmptyNode = undefined;
for (const child of fullMenuModel.children) {
if (!this.isEmpty(child.children!)) {
if (nonEmptyNode === undefined) {
nonEmptyNode = child;
} else {
return fullMenuModel;
}
}
}

return CompoundMenuNode.is(nonEmptyNode) ? nonEmptyNode : fullMenuModel;
}

private allChildrenCompound(children: ReadonlyArray<MenuNode>): boolean {
for (const child of children) {
if (!CompoundMenuNode.is(child)) {
return false;
}
}
return true;
}

private isEmpty(children: ReadonlyArray<MenuNode>): boolean {
if (children.length === 0) {
return true;
}
if (!this.allChildrenCompound(children)) {
return false;
}
for (const child of children) {
if (!this.isEmpty(child.children!)) {
return false;
}
}
return true;
}

/**
* Returns the {@link MenuPath path} at which a given menu node can be accessed from this registry, if it can be determined.
* Returns `undefined` if the `parent` of any node in the chain is unknown.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer {

protected override doRender(options: RenderContextMenuOptions): ContextMenuAccess {
if (this.useNativeStyle) {
const { menuPath, anchor, args, onHide, context, contextKeyService } = options;
const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args, context, contextKeyService);
const { menuPath, anchor, args, onHide, context, contextKeyService, skipSingleRootNode } = options;
const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args, context, contextKeyService, skipSingleRootNode);
const { x, y } = coordinateFromAnchor(anchor);

const menuHandle = window.electronTheiaCore.popup(menu, x, y, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory {
return undefined;
}

createElectronContextMenu(menuPath: MenuPath, args?: any[], context?: HTMLElement, contextKeyService?: ContextMatcher): MenuDto[] {
const menuModel = this.menuProvider.getMenu(menuPath);
createElectronContextMenu(menuPath: MenuPath, args?: any[], context?: HTMLElement, contextKeyService?: ContextMatcher, skipSingleRootNode?: boolean): MenuDto[] {
const menuModel = skipSingleRootNode ? this.menuProvider.removeSingleRootNode(this.menuProvider.getMenu(menuPath), menuPath) : this.menuProvider.getMenu(menuPath);
return this.fillMenuTemplate([], menuModel, args, { showDisabled: true, context, rootMenuPath: menuPath, contextKeyService });
}

Expand Down

0 comments on commit 811dc11

Please sign in to comment.