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

Refactor PluginMenuContributionHandler #11290

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
## v1.28.0 - Unreleased
- [plugin] added support for property `SourceControlInputBox#visible` [#11412](https://github.com/eclipse-theia/theia/pull/11412) - Contributed on behalf of STMicroelectronics

<a name="breaking_changes_1.28.0">[Breaking Changes:](#breaking_changes_1.28.0)</a>

- [core] `handleDefault`, `handleElectronDefault` method no longer called in `BrowserMainMenuFactory.registerMenu()`, `DynamicMenuWidget.buildSubMenus()` or `ElectronMainMenuFactory.fillSubmenus()`. Override the respective calling function rather than `handleDefault`. The argument to each of the three methods listed above is now `MenuNode` and not `CompositeMenuNode`, and the methods are truly recursive and called on entire menu tree. `ActionMenuNode.action` removed; access relevant field on `ActionMenuNode.command`, `.when` etc. [#11290](https://github.com/eclipse-theia/theia/pull/11290)
- [plugin-ext] `CodeEditorWidgetUtil` moved to `packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts`. `MenusContributionPointHandler` extensively refactored. See PR description for details. [#11290](https://github.com/eclipse-theia/theia/pull/11290)

## v1.27.0 - 6/30/2022

- [core] added better styling for active sidepanel borders [#11330](https://github.com/eclipse-theia/theia/pull/11330)
Expand Down
41 changes: 21 additions & 20 deletions examples/api-samples/src/browser/menu/sample-browser-menu-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import { injectable, ContainerModule } from '@theia/core/shared/inversify';
import { Menu as MenuWidget } from '@theia/core/shared/@phosphor/widgets';
import { Disposable } from '@theia/core/lib/common/disposable';
import { MenuNode, CompositeMenuNode } from '@theia/core/lib/common/menu';
import { BrowserMainMenuFactory, MenuCommandRegistry, DynamicMenuWidget } from '@theia/core/lib/browser/menu/browser-menu-plugin';
import { MenuNode, CompositeMenuNode, MenuPath } from '@theia/core/lib/common/menu';
import { BrowserMainMenuFactory, MenuCommandRegistry, DynamicMenuWidget, BrowserMenuOptions } from '@theia/core/lib/browser/menu/browser-menu-plugin';
import { PlaceholderMenuNode } from './sample-menu-contribution';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
Expand All @@ -28,20 +28,21 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
@injectable()
class SampleBrowserMainMenuFactory extends BrowserMainMenuFactory {

protected override handleDefault(menuCommandRegistry: MenuCommandRegistry, menuNode: MenuNode): void {
if (menuNode instanceof PlaceholderMenuNode && menuCommandRegistry instanceof SampleMenuCommandRegistry) {
menuCommandRegistry.registerPlaceholderMenu(menuNode);
protected override registerMenu(menuCommandRegistry: MenuCommandRegistry, menu: MenuNode, args: unknown[]): void {
if (menu instanceof PlaceholderMenuNode && menuCommandRegistry instanceof SampleMenuCommandRegistry) {
menuCommandRegistry.registerPlaceholderMenu(menu);
} else {
super.registerMenu(menuCommandRegistry, menu, args);
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected override createMenuCommandRegistry(menu: CompositeMenuNode, args: any[] = []): MenuCommandRegistry {
protected override createMenuCommandRegistry(menu: CompositeMenuNode, args: unknown[] = []): MenuCommandRegistry {
const menuCommandRegistry = new SampleMenuCommandRegistry(this.services);
this.registerMenu(menuCommandRegistry, menu, args);
return menuCommandRegistry;
}

override createMenuWidget(menu: CompositeMenuNode, options: MenuWidget.IOptions & { commands: MenuCommandRegistry }): DynamicMenuWidget {
override createMenuWidget(menu: CompositeMenuNode, options: BrowserMenuOptions): DynamicMenuWidget {
return new SampleDynamicMenuWidget(menu, options, this.services);
}

Expand All @@ -60,8 +61,8 @@ class SampleMenuCommandRegistry extends MenuCommandRegistry {
this.placeholders.set(id, menu);
}

override snapshot(): this {
super.snapshot();
override snapshot(menuPath: MenuPath): this {
super.snapshot(menuPath);
for (const menu of this.placeholders.values()) {
this.toDispose.push(this.registerPlaceholder(menu));
}
Expand All @@ -70,28 +71,28 @@ class SampleMenuCommandRegistry extends MenuCommandRegistry {

protected registerPlaceholder(menu: PlaceholderMenuNode): Disposable {
const { id } = menu;
const unregisterCommand = this.addCommand(id, {
return this.addCommand(id, {
execute: () => { /* NOOP */ },
label: menu.label,
icon: menu.icon,
isEnabled: () => false,
isVisible: () => true
});
return Disposable.create(() => unregisterCommand.dispose());
}

}

class SampleDynamicMenuWidget extends DynamicMenuWidget {

protected override handleDefault(menuNode: MenuNode): MenuWidget.IItemOptions[] {
if (menuNode instanceof PlaceholderMenuNode) {
return [{
command: menuNode.id,
type: 'command'
}];
protected override buildSubMenus(parentItems: MenuWidget.IItemOptions[], menu: MenuNode, commands: MenuCommandRegistry): MenuWidget.IItemOptions[] {
if (menu instanceof PlaceholderMenuNode) {
parentItems.push({
command: menu.id,
type: 'command',
});
} else {
super.buildSubMenus(parentItems, menu, commands);
}
return [];
return parentItems;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// *****************************************************************************

import { injectable, ContainerModule } from '@theia/core/shared/inversify';
import { CompositeMenuNode } from '@theia/core/lib/common/menu';
import { CompoundMenuNode, MenuNode } from '@theia/core/lib/common/menu';
import { ElectronMainMenuFactory, ElectronMenuOptions } from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory';
import { PlaceholderMenuNode } from '../../browser/menu/sample-menu-contribution';

Expand All @@ -25,17 +25,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {

@injectable()
class SampleElectronMainMenuFactory extends ElectronMainMenuFactory {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected override handleElectronDefault(menuNode: CompositeMenuNode, args: any[] = [], options?: ElectronMenuOptions): Electron.MenuItemConstructorOptions[] {
if (menuNode instanceof PlaceholderMenuNode) {
return [{
label: menuNode.label,
enabled: false,
visible: true
}];
protected override fillMenuTemplate(
parentItems: Electron.MenuItemConstructorOptions[], menuModel: MenuNode & CompoundMenuNode, args: unknown[] = [], options: ElectronMenuOptions
): Electron.MenuItemConstructorOptions[] {
if (menuModel instanceof PlaceholderMenuNode) {
parentItems.push({ label: menuModel.label, enabled: false, visible: true });
} else {
super.fillMenuTemplate(parentItems, menuModel, args, options);
}
return [];
return parentItems;
}

}
2 changes: 1 addition & 1 deletion examples/playwright/src/tests/theia-main-menu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ test.describe('Theia Main Menu', () => {
expect(label).toBe('New File');

const shortCut = await menuItem?.shortCut();
expect(shortCut).toBe(OSUtil.isMacOS ? 'N' : 'Alt+N');
expect(shortCut).toBe(OSUtil.isMacOS ? 'N' : 'Alt+N');

const hasSubmenu = await menuItem?.hasSubmenu();
expect(hasSubmenu).toBe(false);
Expand Down
17 changes: 15 additions & 2 deletions examples/playwright/src/theia-menu-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { ElementHandle } from '@playwright/test';

import { textContent } from './util';
import { elementContainsClass, textContent } from './util';

export class TheiaMenuItem {

Expand All @@ -30,15 +30,28 @@ export class TheiaMenuItem {
return this.element.waitForSelector('.p-Menu-itemShortcut');
}

protected isHidden(): Promise<boolean> {
return elementContainsClass(this.element, 'p-mod-collapsed');
}

async label(): Promise<string | undefined> {
if (await this.isHidden()) {
return undefined;
}
return textContent(this.labelElementHandle());
}

async shortCut(): Promise<string | undefined> {
if (await this.isHidden()) {
return undefined;
}
return textContent(this.shortCutElementHandle());
}

async hasSubmenu(): Promise<boolean> {
if (await this.isHidden()) {
return false;
}
return (await this.element.getAttribute('data-type')) === 'submenu';
}

Expand All @@ -47,7 +60,7 @@ export class TheiaMenuItem {
if (classAttribute === undefined || classAttribute === null) {
return false;
}
return !classAttribute.includes('p-mod-disabled');
return !classAttribute.includes('p-mod-disabled') && !classAttribute.includes('p-mod-collapsed');
}

async click(): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/browser/context-key-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export namespace ContextKey {
}

export interface ContextKeyChangeEvent {
affects(keys: Set<string>): boolean;
affects(keys: { has(key: string): boolean }): boolean;
}

export const ContextKeyService = Symbol('ContextKeyService');
Expand Down
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 @@ -112,5 +112,10 @@ export interface RenderContextMenuOptions {
* Default is `true`.
*/
includeAnchorArg?: boolean;
/**
* A DOM context to use when evaluating any `when` clauses
* of menu items registered for this item.
*/
context?: HTMLElement;
onHide?: () => void;
}
8 changes: 7 additions & 1 deletion packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ import {
InMemoryResources,
messageServicePath,
InMemoryTextResourceResolver,
UntitledResourceResolver
UntitledResourceResolver,
MenuCommandAdapterRegistry,
MenuCommandExecutor,
MenuCommandAdapterRegistryImpl,
MenuCommandExecutorImpl
} from '../common';
import { KeybindingRegistry, KeybindingContext, KeybindingContribution } from './keybinding';
import { FrontendApplication, FrontendApplicationContribution, DefaultFrontendApplicationContribution } from './frontend-application';
Expand Down Expand Up @@ -241,6 +245,8 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is

bind(MenuModelRegistry).toSelf().inSingletonScope();
bindContributionProvider(bind, MenuContribution);
bind(MenuCommandAdapterRegistry).to(MenuCommandAdapterRegistryImpl).inSingletonScope();
bind(MenuCommandExecutor).to(MenuCommandExecutorImpl).inSingletonScope();

bind(KeyboardLayoutService).toSelf().inSingletonScope();
bind(KeybindingRegistry).toSelf().inSingletonScope();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export class BrowserContextMenuRenderer extends ContextMenuRenderer {
super();
}

protected doRender({ menuPath, anchor, args, onHide }: RenderContextMenuOptions): ContextMenuAccess {
const contextMenu = this.menuFactory.createContextMenu(menuPath, args);
protected doRender({ menuPath, anchor, args, onHide, context }: RenderContextMenuOptions): ContextMenuAccess {
const contextMenu = this.menuFactory.createContextMenu(menuPath, args, context);
const { x, y } = coordinateFromAnchor(anchor);
if (onHide) {
contextMenu.aboutToClose.connect(() => onHide!());
Expand Down
Loading