Skip to content

Commit

Permalink
GH-446: Made the electron menu items dynamic.
Browse files Browse the repository at this point in the history
From now on, we update the main menu and its items when
 - the `currentWidget` changes, and
 - the curren selection is set on the selection service.

Closes #446.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
  • Loading branch information
Akos Kitta committed May 14, 2019
1 parent be87341 commit 4955217
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import * as electron from 'electron';
import { inject, injectable } from 'inversify';
import {
CommandRegistry, isOSX, ActionMenuNode, CompositeMenuNode,
MAIN_MENU_BAR, MenuModelRegistry, MenuPath
MAIN_MENU_BAR, MenuModelRegistry, MenuPath, ILogger
} from '../../common';
import { PreferenceService, KeybindingRegistry, Keybinding } from '../../browser';
import { ContextKeyService } from '../../browser/context-key-service';
Expand All @@ -30,6 +30,9 @@ export class ElectronMainMenuFactory {
protected _menu: Electron.Menu;
protected _toggledCommands: Set<string> = new Set();

@inject(ILogger)
protected readonly logger: ILogger;

@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;

Expand Down Expand Up @@ -57,7 +60,13 @@ export class ElectronMainMenuFactory {

createMenuBar(): Electron.Menu {
const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
const start = Date.now();
const template = this.fillMenuTemplate([], menuModel);
this.logger.isDebug().then(debug => {
if (debug) {
this.logger.debug(`Recalculated the menu templates in ${Date.now() - start} ms.`);
}
});
if (isOSX) {
template.unshift(this.createOSXMenu());
}
Expand Down Expand Up @@ -122,10 +131,14 @@ export class ElectronMainMenuFactory {
throw new Error(`Unknown command with ID: ${commandId}.`);
}

const args = anchor ? [anchor] : [];
if (!this.commandRegistry.isVisible(commandId, ...args)
|| (!!menu.action.when && !this.contextKeyService.match(menu.action.when))) {
continue;
// Do not render empty groups for the context menu only.
// But we show all disabled items in the main application menu.
if (anchor) {
const args = anchor ? [anchor] : [];
if (!this.commandRegistry.isVisible(commandId, ...args)
|| (!!menu.action.when && !this.contextKeyService.match(menu.action.when))) {
continue;
}
}

const bindings = this.keybindingRegistry.getKeybindingsForCommand(commandId);
Expand All @@ -143,8 +156,8 @@ export class ElectronMainMenuFactory {
label: menu.label,
type: this.commandRegistry.getToggledHandler(commandId) ? 'checkbox' : 'normal',
checked: this.commandRegistry.isToggled(commandId),
enabled: true, // https://github.com/theia-ide/theia/issues/446
visible: true,
enabled: this.commandRegistry.isEnabled(commandId),
visible: anchor ? this.commandRegistry.isVisible(commandId) : true, // We filter items for the context menu only.
click: () => this.execute(commandId, anchor),
accelerator
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import * as electron from 'electron';
import { inject, injectable } from 'inversify';
import {
Command, CommandContribution, CommandRegistry,
isOSX, MenuModelRegistry, MenuContribution, Disposable
isOSX, MenuModelRegistry, MenuContribution, Disposable, DisposableCollection
} from '../../common';
import { KeybindingContribution, KeybindingRegistry } from '../../browser';
import { FrontendApplication, FrontendApplicationContribution, CommonMenus } from '../../browser';
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
import { FrontendApplicationStateService, FrontendApplicationState } from '../../browser/frontend-application-state';
import { SelectionService } from '../../common/selection-service';

export namespace ElectronCommands {
export const TOGGLE_DEVELOPER_TOOLS: Command = {
Expand Down Expand Up @@ -71,6 +72,9 @@ export class ElectronMenuContribution implements FrontendApplicationContribution
@inject(FrontendApplicationStateService)
protected readonly stateService: FrontendApplicationStateService;

@inject(SelectionService)
protected readonly selectionService: SelectionService;

constructor(
@inject(ElectronMainMenuFactory) protected readonly factory: ElectronMainMenuFactory
) { }
Expand All @@ -96,20 +100,33 @@ export class ElectronMenuContribution implements FrontendApplicationContribution
// between them as the user switches windows.
electron.remote.getCurrentWindow().on('focus', () => this.setMenu());
}

// Poor man's way to update the Electron menu items dynamically.
// https://github.com/theia-ide/theia/issues/446
// https://github.com/microsoft/vscode/blob/a6774a6961a802453d7ae985d4f555b8f3e0bb88/src/vs/platform/menubar/electron-main/menubar.ts#L200-L202
const toDisposeOnClosingWindow = new DisposableCollection();

// Listen on current widget changes.
const currentChangedListener = () => this.setMenu();
app.shell.currentChanged.connect(currentChangedListener);
toDisposeOnClosingWindow.push(Disposable.create(() => app.shell.currentChanged.disconnect(currentChangedListener)));

// Listen on the selection service. To be able to update the items on the `Navigator` selections, for instance.
toDisposeOnClosingWindow.push(this.selectionService.onSelectionChanged(() => this.setMenu()));

// Make sure the application menu is complete, once the frontend application is ready.
// https://github.com/theia-ide/theia/issues/5100
let onStateChange: Disposable | undefined = undefined;
const stateServiceListener = (state: FrontendApplicationState) => {
if (state === 'ready') {
this.setMenu();
}
if (state === 'closing_window') {
if (!!onStateChange) {
onStateChange.dispose();
if (!toDisposeOnClosingWindow.disposed) {
toDisposeOnClosingWindow.dispose();
}
}
};
onStateChange = this.stateService.onStateChanged(stateServiceListener);
toDisposeOnClosingWindow.push(this.stateService.onStateChanged(stateServiceListener));
}

private setMenu(menu: electron.Menu = this.factory.createMenuBar(), window: electron.BrowserWindow = electron.remote.getCurrentWindow()): void {
Expand Down

0 comments on commit 4955217

Please sign in to comment.