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 vscode's titleBarStyle #10044

Merged
merged 1 commit into from
Oct 7, 2021
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -10,6 +10,10 @@
<a name="breaking_changes_1.19.0">[Breaking Changes:](#breaking_changes_1.19.0)</a>

- [view-container] `ViewContainerPart` constructor takes new 2 parameters: `originalContainerId` and `originalContainerTitle`. The existing `viewContainerId` parameter has been renamed to `currentContainerId` to enable drag & drop views. [#9644](https://github.com/eclipse-theia/theia/pull/9644)
- [electron] `ElectronMainMenuFactory` now inherits from `BrowserMainMenuFactory` and its methods have been renamed. [#10044](https://github.com/eclipse-theia/theia/pull/10044)
- renamed `handleDefault` to `handleElectronDefault`
- renamed `createContextMenu` to `createElectronContextMenu`
- renamed `createMenuBar` to `createElectronMenuBar`

## v1.18.0 - 9/30/2021

Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
class SampleElectronMainMenuFactory extends ElectronMainMenuFactory {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected handleDefault(menuNode: CompositeMenuNode, args: any[] = [], options?: ElectronMenuOptions): Electron.MenuItemConstructorOptions[] {
protected handleElectronDefault(menuNode: CompositeMenuNode, args: any[] = [], options?: ElectronMenuOptions): Electron.MenuItemConstructorOptions[] {
if (menuNode instanceof PlaceholderMenuNode) {
return [{
label: menuNode.label,
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ export class ElectronMenuUpdater {
this.setMenu();
}

private setMenu(menu: Menu | null = this.factory.createMenuBar(), electronWindow: BrowserWindow = remote.getCurrentWindow()): void {
private setMenu(menu: Menu | null = this.factory.createElectronMenuBar(), electronWindow: BrowserWindow = remote.getCurrentWindow()): void {
if (isOSX) {
remote.Menu.setApplicationMenu(menu);
} else {
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ export class BrowserContextMenuRenderer extends ContextMenuRenderer {
super();
}

protected doRender({ menuPath, anchor, args, onHide }: RenderContextMenuOptions): BrowserContextMenuAccess {
protected doRender({ menuPath, anchor, args, onHide }: RenderContextMenuOptions): ContextMenuAccess {
const contextMenu = this.menuFactory.createContextMenu(menuPath, args);
const { x, y } = coordinateFromAnchor(anchor);
if (onHide) {
30 changes: 25 additions & 5 deletions packages/core/src/browser/menu/browser-menu-plugin.ts
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ import { injectable, inject } from 'inversify';
import { MenuBar, Menu as MenuWidget, Widget } from '@phosphor/widgets';
import { CommandRegistry as PhosphorCommandRegistry } from '@phosphor/commands';
import {
CommandRegistry, ActionMenuNode, CompositeMenuNode,
CommandRegistry, ActionMenuNode, CompositeMenuNode, environment,
MenuModelRegistry, MAIN_MENU_BAR, MenuPath, DisposableCollection, Disposable, MenuNode
} from '../../common';
import { KeybindingRegistry } from '../keybinding';
@@ -28,6 +28,7 @@ import { ContextMenuContext } from './context-menu-context';
import { waitForRevealed } from '../widgets';
import { ApplicationShell } from '../shell';
import { CorePreferences } from '../core-preferences';
import { PreferenceService } from '../preferences/preference-service';

export abstract class MenuBarWidget extends MenuBar {
abstract activateMenu(label: string, ...labels: string[]): Promise<MenuWidget>;
@@ -371,21 +372,40 @@ export class BrowserMenuBarContribution implements FrontendApplicationContributi
@inject(ApplicationShell)
protected readonly shell: ApplicationShell;

@inject(PreferenceService)
protected readonly preferenceService: PreferenceService;

constructor(
@inject(BrowserMainMenuFactory) protected readonly factory: BrowserMainMenuFactory
) { }

onStart(app: FrontendApplication): void {
const logo = this.createLogo();
app.shell.addWidget(logo, { area: 'top' });
const menu = this.factory.createMenuBar();
app.shell.addWidget(menu, { area: 'top' });
this.appendMenu(app.shell);
}

get menuBar(): MenuBarWidget | undefined {
return this.shell.topPanel.widgets.find(w => w instanceof MenuBarWidget) as MenuBarWidget | undefined;
}

protected appendMenu(shell: ApplicationShell): void {
const logo = this.createLogo();
shell.addWidget(logo, { area: 'top' });
const menu = this.factory.createMenuBar();
shell.addWidget(menu, { area: 'top' });
// Hiding the menu is only necessary in electron
// In the browser we hide the whole top panel
if (environment.electron.is()) {
this.preferenceService.ready.then(() => {
menu.setHidden(['compact', 'hidden'].includes(this.preferenceService.get('window.menuBarVisibility', '')));
});
this.preferenceService.onPreferenceChanged(change => {
if (change.preferenceName === 'window.menuBarVisibility') {
menu.setHidden(['compact', 'hidden'].includes(change.newValue));
}
});
}
}

protected createLogo(): Widget {
const logo = new Widget();
logo.id = 'theia:icon';
Original file line number Diff line number Diff line change
@@ -17,11 +17,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import * as electron from '../../../shared/electron';
import { inject, injectable } from 'inversify';
import { ContextMenuRenderer, RenderContextMenuOptions, ContextMenuAccess, FrontendApplicationContribution, CommonCommands, coordinateFromAnchor } from '../../browser';
import { inject, injectable, postConstruct } from 'inversify';
import {
ContextMenuRenderer, RenderContextMenuOptions, ContextMenuAccess, FrontendApplicationContribution, CommonCommands, coordinateFromAnchor, PreferenceService
} from '../../browser';
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
import { ContextMenuContext } from '../../browser/menu/context-menu-context';
import { MenuPath, MenuContribution, MenuModelRegistry } from '../../common';
import { BrowserContextMenuRenderer } from '../../browser/menu/browser-context-menu-renderer';
import { RequestTitleBarStyle, TitleBarStyleAtStartup } from '../../electron-common/messaging/electron-messages';

export class ElectronContextMenuAccess extends ContextMenuAccess {
constructor(readonly menu: electron.Menu) {
@@ -73,27 +77,45 @@ export class ElectronTextInputContextMenuContribution implements FrontendApplica
}

@injectable()
export class ElectronContextMenuRenderer extends ContextMenuRenderer {
export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer {

@inject(ContextMenuContext)
protected readonly context: ContextMenuContext;

constructor(@inject(ElectronMainMenuFactory) private menuFactory: ElectronMainMenuFactory) {
super();
@inject(PreferenceService)
protected readonly preferenceService: PreferenceService;

protected useNativeStyle: boolean = true;

constructor(@inject(ElectronMainMenuFactory) private electronMenuFactory: ElectronMainMenuFactory) {
super(electronMenuFactory);
}

@postConstruct()
protected async init(): Promise<void> {
electron.ipcRenderer.on(TitleBarStyleAtStartup, (_event, style: string) => {
this.useNativeStyle = style === 'native';
});
electron.ipcRenderer.send(RequestTitleBarStyle);
}

protected doRender({ menuPath, anchor, args, onHide }: RenderContextMenuOptions): ElectronContextMenuAccess {
const menu = this.menuFactory.createContextMenu(menuPath, args);
const { x, y } = coordinateFromAnchor(anchor);
const zoom = electron.webFrame.getZoomFactor();
// x and y values must be Ints or else there is a conversion error
menu.popup({ x: Math.round(x * zoom), y: Math.round(y * zoom) });
// native context menu stops the event loop, so there is no keyboard events
this.context.resetAltPressed();
if (onHide) {
menu.once('menu-will-close', () => onHide());
protected doRender(options: RenderContextMenuOptions): ContextMenuAccess {
if (this.useNativeStyle) {
const { menuPath, anchor, args, onHide } = options;
const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args);
const { x, y } = coordinateFromAnchor(anchor);
const zoom = electron.webFrame.getZoomFactor();
// x and y values must be Ints or else there is a conversion error
menu.popup({ x: Math.round(x * zoom), y: Math.round(y * zoom) });
// native context menu stops the event loop, so there is no keyboard events
this.context.resetAltPressed();
if (onHide) {
menu.once('menu-will-close', () => onHide());
}
return new ElectronContextMenuAccess(menu);
} else {
return super.doRender(options);
}
return new ElectronContextMenuAccess(menu);
}

}
Original file line number Diff line number Diff line change
@@ -24,10 +24,9 @@ import {
} from '../../common';
import { Keybinding } from '../../common/keybinding';
import { PreferenceService, KeybindingRegistry, CommonCommands } from '../../browser';
import { ContextKeyService } from '../../browser/context-key-service';
import debounce = require('lodash.debounce');
import { ContextMenuContext } from '../../browser/menu/context-menu-context';
import { MAXIMIZED_CLASS } from '../../browser/shell/theia-dock-panel';
import { BrowserMainMenuFactory } from '../../browser/menu/browser-menu-plugin';

/**
* Representation of possible electron menu options.
@@ -55,23 +54,18 @@ export type ElectronMenuItemRole = ('undo' | 'redo' | 'cut' | 'copy' | 'paste' |
'moveTabToNewWindow' | 'windowMenu');

@injectable()
export class ElectronMainMenuFactory {
export class ElectronMainMenuFactory extends BrowserMainMenuFactory {

protected _menu: Electron.Menu | undefined;
protected _toggledCommands: Set<string> = new Set();

@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;

@inject(ContextMenuContext)
protected readonly context: ContextMenuContext;

constructor(
@inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry,
@inject(PreferenceService) protected readonly preferencesService: PreferenceService,
@inject(MenuModelRegistry) protected readonly menuProvider: MenuModelRegistry,
@inject(KeybindingRegistry) protected readonly keybindingRegistry: KeybindingRegistry
) {
super();
preferencesService.onPreferenceChanged(
debounce(e => {
if (e.preferenceName === 'window.menuBarVisibility') {
@@ -92,15 +86,16 @@ export class ElectronMainMenuFactory {

async setMenuBar(): Promise<void> {
await this.preferencesService.ready;
const createdMenuBar = this.createMenuBar();
if (isOSX) {
const createdMenuBar = this.createElectronMenuBar();
electron.remote.Menu.setApplicationMenu(createdMenuBar);
} else {
} else if (this.preferencesService.get('window.titleBarStyle') === 'native') {
const createdMenuBar = this.createElectronMenuBar();
electron.remote.getCurrentWindow().setMenu(createdMenuBar);
}
}

createMenuBar(): Electron.Menu | null {
createElectronMenuBar(): Electron.Menu | null {
msujew marked this conversation as resolved.
Show resolved Hide resolved
const preference = this.preferencesService.get<string>('window.menuBarVisibility') || 'classic';
const maxWidget = document.getElementsByClassName(MAXIMIZED_CLASS);
if (preference === 'visible' || (preference === 'classic' && maxWidget.length === 0)) {
@@ -118,7 +113,7 @@ export class ElectronMainMenuFactory {
return null;
}

createContextMenu(menuPath: MenuPath, args?: any[]): Electron.Menu {
createElectronContextMenu(menuPath: MenuPath, args?: any[]): Electron.Menu {
const menuModel = this.menuProvider.getMenu(menuPath);
const template = this.fillMenuTemplate([], menuModel, args, { showDisabled: false });
return electron.remote.Menu.buildFromTemplate(template);
@@ -221,13 +216,13 @@ export class ElectronMainMenuFactory {
this._toggledCommands.add(commandId);
}
} else {
items.push(...this.handleDefault(menu, args, options));
items.push(...this.handleElectronDefault(menu, args, options));
}
}
return items;
}

protected handleDefault(menuNode: MenuNode, args: any[] = [], options?: ElectronMenuOptions): Electron.MenuItemConstructorOptions[] {
protected handleElectronDefault(menuNode: MenuNode, args: any[] = [], options?: ElectronMenuOptions): Electron.MenuItemConstructorOptions[] {
return [];
}

Loading