未验证 提交 d63937a4 编写于 作者: I Isidor Nikolic 提交者: GitHub

Merge pull request #54826 from Microsoft/sbatten/removeOldMenu

Remove the old menu implementation
......@@ -63,8 +63,6 @@ import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService';
import { MenubarChannel } from 'vs/platform/menubar/common/menubarIpc';
// TODO@sbatten: Remove after conversion to new dynamic menubar
import { CodeMenu } from 'vs/code/electron-main/menus';
export class CodeApplication {
......@@ -511,14 +509,6 @@ export class CodeApplication {
}
}
// TODO@sbatten: Remove when menu is converted
// Install Menu
const instantiationService = accessor.get(IInstantiationService);
const configurationService = accessor.get(IConfigurationService);
if (platform.isMacintosh || configurationService.getValue<string>('window.titleBarStyle') !== 'custom') {
instantiationService.createInstance(CodeMenu);
}
// Jump List
this.historyMainService.updateWindowsJumpList();
this.historyMainService.onRecentlyOpenedChange(() => this.historyMainService.updateWindowsJumpList());
......
......@@ -7,14 +7,7 @@
import * as nativeKeymap from 'native-keymap';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IStateService } from 'vs/platform/state/common/state';
import { Event, Emitter, once } from 'vs/base/common/event';
import { ConfigWatcher } from 'vs/base/node/config';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ipcMain as ipc } from 'electron';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { ILogService } from 'vs/platform/log/common/log';
import { Emitter } from 'vs/base/common/event';
export class KeyboardLayoutMonitor {
......@@ -38,109 +31,4 @@ export class KeyboardLayoutMonitor {
}
return this._emitter.event(callback);
}
}
export interface IKeybinding {
id: string;
label: string;
isNative: boolean;
}
export class KeybindingsResolver {
private static readonly lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings';
private commandIds: Set<string>;
private keybindings: { [commandId: string]: IKeybinding };
private keybindingsWatcher: ConfigWatcher<IUserFriendlyKeybinding[]>;
private _onKeybindingsChanged = new Emitter<void>();
onKeybindingsChanged: Event<void> = this._onKeybindingsChanged.event;
constructor(
@IStateService private stateService: IStateService,
@IEnvironmentService environmentService: IEnvironmentService,
@IWindowsMainService private windowsMainService: IWindowsMainService,
@ILogService private logService: ILogService
) {
this.commandIds = new Set<string>();
this.keybindings = this.stateService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null);
this.keybindingsWatcher = new ConfigWatcher<IUserFriendlyKeybinding[]>(environmentService.appKeybindingsPath, { changeBufferDelay: 100, onError: error => this.logService.error(error) });
this.registerListeners();
}
private registerListeners(): void {
// Listen to resolved keybindings from window
ipc.on('vscode:keybindingsResolved', (event, rawKeybindings: string) => {
let keybindings: IKeybinding[] = [];
try {
keybindings = JSON.parse(rawKeybindings);
} catch (error) {
// Should not happen
}
// Fill hash map of resolved keybindings and check for changes
let keybindingsChanged = false;
let keybindingsCount = 0;
const resolvedKeybindings: { [commandId: string]: IKeybinding } = Object.create(null);
keybindings.forEach(keybinding => {
keybindingsCount++;
resolvedKeybindings[keybinding.id] = keybinding;
if (!this.keybindings[keybinding.id] || keybinding.label !== this.keybindings[keybinding.id].label) {
keybindingsChanged = true;
}
});
// A keybinding might have been unassigned, so we have to account for that too
if (Object.keys(this.keybindings).length !== keybindingsCount) {
keybindingsChanged = true;
}
if (keybindingsChanged) {
this.keybindings = resolvedKeybindings;
this.stateService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart
this._onKeybindingsChanged.fire();
}
});
// Resolve keybindings when any first window is loaded
const onceOnWindowReady = once(this.windowsMainService.onWindowReady);
onceOnWindowReady(win => this.resolveKeybindings(win));
// Resolve keybindings again when keybindings.json changes
this.keybindingsWatcher.onDidUpdateConfiguration(() => this.resolveKeybindings());
// Resolve keybindings when window reloads because an installed extension could have an impact
this.windowsMainService.onWindowReload(() => this.resolveKeybindings());
}
private resolveKeybindings(win = this.windowsMainService.getLastActiveWindow()): void {
if (this.commandIds.size && win) {
const commandIds: string[] = [];
this.commandIds.forEach(id => commandIds.push(id));
win.sendWhenReady('vscode:resolveKeybindings', JSON.stringify(commandIds));
}
}
public getKeybinding(commandId: string): IKeybinding {
if (!commandId) {
return void 0;
}
if (!this.commandIds.has(commandId)) {
this.commandIds.add(commandId);
}
return this.keybindings[commandId];
}
public dispose(): void {
this._onKeybindingsChanged.dispose();
this.keybindingsWatcher.dispose();
}
}
\ No newline at end of file
......@@ -17,47 +17,28 @@ import product from 'vs/platform/node/product';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel, getPathLabel } from 'vs/base/common/labels';
import { KeybindingsResolver } from 'vs/code/electron-main/keyboard';
import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows';
import { IHistoryMainService } from 'vs/platform/history/common/history';
import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IMenubarData, IMenubarMenuItemAction, IMenubarMenuItemSeparator } from 'vs/platform/menubar/common/menubar';
import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction } from 'vs/platform/menubar/common/menubar';
import URI from 'vs/base/common/uri';
// interface IExtensionViewlet {
// id: string;
// label: string;
// }
const telemetryFrom = 'menu';
export class Menubar {
private static readonly MAX_MENU_RECENT_ENTRIES = 10;
// private keys = [
// 'files.autoSave',
// 'editor.multiCursorModifier',
// 'workbench.sideBar.location',
// 'workbench.statusBar.visible',
// 'workbench.activityBar.visible',
// 'window.enableMenuBarMnemonics',
// 'window.nativeTabs'
// ];
private isQuitting: boolean;
private appMenuInstalled: boolean;
private menuUpdater: RunOnceScheduler;
private keybindingsResolver: KeybindingsResolver;
// private extensionViewlets: IExtensionViewlet[];
private nativeTabMenuItems: Electron.MenuItem[];
private menubarMenus: IMenubarData = {};
private keybindings: { [commandId: string]: IMenubarKeybinding };
constructor(
@IUpdateService private updateService: IUpdateService,
@IInstantiationService instantiationService: IInstantiationService,
......@@ -67,11 +48,9 @@ export class Menubar {
@ITelemetryService private telemetryService: ITelemetryService,
@IHistoryMainService private historyMainService: IHistoryMainService
) {
// this.extensionViewlets = [];
// this.nativeTabMenuItems = [];
this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver);
this.keybindings = Object.create(null);
this.install();
......@@ -112,9 +91,6 @@ export class Menubar {
// Listen to update service
// this.updateService.onStateChange(() => this.updateMenu());
// Listen to keybindings change
this.keybindingsResolver.onKeybindingsChanged(() => this.scheduleUpdateMenu());
}
private get currentEnableMenuBarMnemonics(): boolean {
......@@ -229,19 +205,6 @@ export class Menubar {
menubar.append(editMenuItem);
}
// Recent
const recentMenu = new Menu();
const recentMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miRecent', comment: ['&& denotes a mnemonic'] }, "&&Recent")), submenu: recentMenu, enabled: recentMenu.items.length > 0 });
if (this.shouldDrawMenu('Recent')) {
if (this.shouldFallback('Recent')) {
this.setFallbackMenuById(recentMenu, 'Recent');
} else {
this.setMenuById(recentMenu, 'Recent');
}
menubar.append(recentMenuItem);
}
// Selection
const selectionMenu = new Menu();
const selectionMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection")), submenu: selectionMenu });
......@@ -278,6 +241,15 @@ export class Menubar {
menubar.append(gotoMenuItem);
}
// Terminal
const terminalMenu = new Menu();
const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "Ter&&minal")), submenu: terminalMenu });
if (this.shouldDrawMenu('Terminal')) {
this.setMenuById(terminalMenu, 'Terminal');
menubar.append(terminalMenuItem);
}
// Debug
const debugMenu = new Menu();
const debugMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")), submenu: debugMenu });
......@@ -291,8 +263,8 @@ export class Menubar {
const taskMenu = new Menu();
const taskMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTask', comment: ['&& denotes a mnemonic'] }, "&&Tasks")), submenu: taskMenu });
if (this.shouldDrawMenu('Task')) {
this.setMenuById(taskMenu, 'Task');
if (this.shouldDrawMenu('Tasks')) {
this.setMenuById(taskMenu, 'Tasks');
menubar.append(taskMenuItem);
}
......@@ -309,16 +281,18 @@ export class Menubar {
}
// Preferences
const preferencesMenu = new Menu();
const preferencesMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu });
if (!isMacintosh) {
const preferencesMenu = new Menu();
const preferencesMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu });
if (this.shouldDrawMenu('Preferences')) {
if (this.shouldFallback('Preferences')) {
this.setFallbackMenuById(preferencesMenu, 'Preferences');
} else {
this.setMenuById(preferencesMenu, 'Preferences');
if (this.shouldDrawMenu('Preferences')) {
if (this.shouldFallback('Preferences')) {
this.setFallbackMenuById(preferencesMenu, 'Preferences');
} else {
this.setMenuById(preferencesMenu, 'Preferences');
}
menubar.append(preferencesMenuItem);
}
menubar.append(preferencesMenuItem);
}
// Help
......@@ -340,6 +314,14 @@ export class Menubar {
private setMacApplicationMenu(macApplicationMenu: Electron.Menu): void {
const about = new MenuItem({ label: nls.localize('mAbout', "About {0}", product.nameLong), role: 'about' });
const checkForUpdates = this.getUpdateMenuItems();
let preferences;
if (this.shouldDrawMenu('Preferences')) {
const preferencesMenu = new Menu();
this.setMenuById(preferencesMenu, 'Preferences');
preferences = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu });
}
const servicesMenu = new Menu();
const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu });
const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' });
......@@ -355,6 +337,14 @@ export class Menubar {
const actions = [about];
actions.push(...checkForUpdates);
if (preferences) {
actions.push(...[
__separator__(),
preferences
]);
}
actions.push(...[
__separator__(),
services,
......@@ -372,7 +362,6 @@ export class Menubar {
private shouldDrawMenu(menuId: string): boolean {
switch (menuId) {
case 'File':
case 'Recent':
case 'Help':
return true;
default:
......@@ -381,7 +370,7 @@ export class Menubar {
}
private shouldFallback(menuId: string): boolean {
return this.shouldDrawMenu(menuId) && (this.windowsMainService.getWindowCount() === 0 || !this.menubarMenus[menuId]);
return this.shouldDrawMenu(menuId) && (this.windowsMainService.getWindowCount() === 0);
}
private setFallbackMenuById(menu: Electron.Menu, menuId: string): void {
......@@ -406,32 +395,12 @@ export class Menubar {
case 'Recent':
menu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor'));
const { workspaces, files } = this.historyMainService.getRecentlyOpened();
// Workspaces
if (workspaces.length > 0) {
menu.append(__separator__());
this.insertRecentMenuItems(menu);
for (let i = 0; i < Menubar.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) {
menu.append(this.createOpenRecentMenuItem(workspaces[i], 'openRecentWorkspace', false));
}
}
// Files
if (files.length > 0) {
menu.append(__separator__());
for (let i = 0; i < Menubar.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) {
menu.append(this.createOpenRecentMenuItem(files[i], 'openRecentFile', true));
}
}
if (workspaces.length || files.length) {
menu.append(__separator__());
menu.append(this.createMenuItem(nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."), 'workbench.action.openRecent'));
menu.append(__separator__());
menu.append(new MenuItem(this.likeAction('workbench.action.clearRecentFiles', { label: this.mnemonicLabel(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")), click: () => this.historyMainService.clearRecentlyOpened() })));
}
menu.append(__separator__());
menu.append(this.createMenuItem(nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."), 'workbench.action.openRecent'));
menu.append(__separator__());
menu.append(new MenuItem(this.likeAction('workbench.action.clearRecentFiles', { label: this.mnemonicLabel(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")), click: () => this.historyMainService.clearRecentlyOpened() })));
break;
......@@ -496,22 +465,59 @@ export class Menubar {
}
}
private setMenuById(menu: Electron.Menu, menuId: string): void {
console.log(`Attempting to set menu for ${menuId}`);
// Build dynamic menu
this.menubarMenus[menuId].items.forEach((item: IMenubarMenuItemAction | IMenubarMenuItemSeparator) => {
if (item.id === 'vscode.menubar.separator') {
private setMenu(menu: Electron.Menu, items: Array<MenubarMenuItem>) {
items.forEach((item: MenubarMenuItem) => {
if (isMenubarMenuItemSeparator(item)) {
menu.append(__separator__());
} else {
let menuItem: Electron.MenuItem;
let action: IMenubarMenuItemAction = <IMenubarMenuItemAction>item;
menuItem = this.createMenuItem(action.label, action.id, action.enabled, action.checked);
} else if (isMenubarMenuItemSubmenu(item)) {
const submenu = new Menu();
const submenuItem = new MenuItem({ label: this.mnemonicLabel(item.label), submenu: submenu });
this.setMenu(submenu, item.submenu.items);
menu.append(submenuItem);
} else if (isMenubarMenuItemAction(item)) {
if (item.id === 'workbench.action.openRecent') {
this.insertRecentMenuItems(menu);
}
// Store the keybinding
if (item.keybinding) {
this.keybindings[item.id] = item.keybinding;
}
const menuItem = this.createMenuItem(item.label, item.id, item.enabled, item.checked);
menu.append(menuItem);
}
});
}
private setMenuById(menu: Electron.Menu, menuId: string): void {
if (this.menubarMenus[menuId]) {
this.setMenu(menu, this.menubarMenus[menuId].items);
}
}
private insertRecentMenuItems(menu: Electron.Menu) {
const { workspaces, files } = this.historyMainService.getRecentlyOpened();
// Workspaces
if (workspaces.length > 0) {
for (let i = 0; i < Menubar.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) {
menu.append(this.createOpenRecentMenuItem(workspaces[i], 'openRecentWorkspace', false));
}
menu.append(__separator__());
}
// Files
if (files.length > 0) {
for (let i = 0; i < Menubar.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) {
menu.append(this.createOpenRecentMenuItem(files[i], 'openRecentFile', true));
}
menu.append(__separator__());
}
}
private createOpenRecentMenuItem(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): Electron.MenuItem {
let label: string;
let uri: URI;
......@@ -679,7 +685,7 @@ export class Menubar {
}
private withKeybinding(commandId: string, options: Electron.MenuItemConstructorOptions): Electron.MenuItemConstructorOptions {
const binding = this.keybindingsResolver.getKeybinding(commandId);
const binding = this.keybindings[commandId];
// Apply binding if there is one
if (binding && binding.label) {
......
此差异已折叠。
......@@ -93,7 +93,10 @@ export class MenuId {
static readonly MenubarAppearanceMenu = new MenuId();
static readonly MenubarLayoutMenu = new MenuId();
static readonly MenubarGoMenu = new MenuId();
static readonly MenubarSwitchEditorMenu = new MenuId();
static readonly MenubarSwitchGroupMenu = new MenuId();
static readonly MenubarDebugMenu = new MenuId();
static readonly MenubarNewBreakpointMenu = new MenuId();
static readonly MenubarTasksMenu = new MenuId();
static readonly MenubarWindowMenu = new MenuId();
static readonly MenubarPreferencesMenu = new MenuId();
......
......@@ -23,7 +23,13 @@ export interface IMenubarData {
}
export interface IMenubarMenu {
items: Array<IMenubarMenuItemAction | IMenubarMenuItemSeparator>;
items: Array<MenubarMenuItem>;
}
export interface IMenubarKeybinding {
id: string;
label: string;
isNative: boolean;
}
export interface IMenubarMenuItemAction {
......@@ -31,8 +37,29 @@ export interface IMenubarMenuItemAction {
label: string;
checked: boolean;
enabled: boolean;
keybinding?: IMenubarKeybinding;
}
export interface IMenubarMenuItemSubmenu {
id: string;
label: string;
submenu: IMenubarMenu;
}
export interface IMenubarMenuItemSeparator {
id: 'vscode.menubar.separator';
}
export type MenubarMenuItem = IMenubarMenuItemAction | IMenubarMenuItemSubmenu | IMenubarMenuItemSeparator;
export function isMenubarMenuItemSubmenu(menuItem: MenubarMenuItem): menuItem is IMenubarMenuItemSubmenu {
return (<IMenubarMenuItemSubmenu>menuItem).submenu !== undefined;
}
export function isMenubarMenuItemAction(menuItem: MenubarMenuItem): menuItem is IMenubarMenuItemAction {
return (<IMenubarMenuItemAction>menuItem).checked !== undefined || (<IMenubarMenuItemAction>menuItem).enabled !== undefined;
}
export function isMenubarMenuItemSeparator(menuItem: MenubarMenuItem): menuItem is IMenubarMenuItemSeparator {
return (<IMenubarMenuItemSeparator>menuItem).id === 'vscode.menubar.separator';
}
\ No newline at end of file
......@@ -10,7 +10,6 @@ import { Menubar } from 'vs/code/electron-main/menubar';
import { ILogService } from 'vs/platform/log/common/log';
import { TPromise } from 'vs/base/common/winjs.base';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { isMacintosh, isWindows } from 'vs/base/common/platform';
export class MenubarService implements IMenubarService {
_serviceBrand: any;
......@@ -22,10 +21,7 @@ export class MenubarService implements IMenubarService {
@ILogService private logService: ILogService
) {
// Install Menu
// TODO@sbatten: Remove if block
if (isMacintosh && isWindows) {
this._menubar = this.instantiationService.createInstance(Menubar);
}
this._menubar = this.instantiationService.createInstance(Menubar);
}
updateMenubar(windowId: number, menus: IMenubarData): TPromise<void> {
......
......@@ -16,7 +16,6 @@ if (isMacintosh) {
helpMenuRegistration();
// Menu registration
function goMenuRegistration() {
// Forward/Back
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
......@@ -38,8 +37,8 @@ function goMenuRegistration() {
});
// Switch Editor
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '2_switch_editor',
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
group: '1_any',
command: {
id: 'workbench.action.nextEditor',
title: nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor")
......@@ -47,8 +46,8 @@ function goMenuRegistration() {
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '2_switch_editor',
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
group: '1_any',
command: {
id: 'workbench.action.previousEditor',
title: nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor")
......@@ -56,27 +55,79 @@ function goMenuRegistration() {
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '2_switch_editor',
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
group: '2_used',
command: {
id: 'workbench.action.openNextRecentlyUsedEditorInGroup',
title: nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group")
},
order: 3
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '2_switch_editor',
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, {
group: '2_used',
command: {
id: 'workbench.action.openPreviousRecentlyUsedEditorInGroup',
title: nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group")
},
order: 4
order: 2
});
// Switch Group
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '3_switch_group',
group: '2_switch',
title: nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor"),
submenu: MenuId.MenubarSwitchEditorMenu,
order: 1
});
// Switch Group
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '1_focus_index',
command: {
id: 'workbench.action.focusFirstEditorGroup',
title: nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "Group &&1")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '1_focus_index',
command: {
id: 'workbench.action.focusSecondEditorGroup',
title: nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "Group &&2")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '1_focus_index',
command: {
id: 'workbench.action.focusThirdEditorGroup',
title: nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "Group &&3")
},
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '1_focus_index',
command: {
id: 'workbench.action.focusFourthEditorGroup',
title: nls.localize({ key: 'miFocusFourthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&4")
},
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '1_focus_index',
command: {
id: 'workbench.action.focusFifthEditorGroup',
title: nls.localize({ key: 'miFocusFifthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&5")
},
order: 5
});
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '2_next_prev',
command: {
id: 'workbench.action.focusNextGroup',
title: nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group")
......@@ -84,8 +135,8 @@ function goMenuRegistration() {
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '3_switch_group',
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '2_next_prev',
command: {
id: 'workbench.action.focusPreviousGroup',
title: nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group")
......@@ -93,40 +144,47 @@ function goMenuRegistration() {
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '3_switch_group',
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '3_directional',
command: {
id: 'workbench.action.focusLeftGroup',
title: nls.localize({ key: 'miFocusLeftGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Left")
},
order: 3
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '3_switch_group',
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '3_directional',
command: {
id: 'workbench.action.focusRightGroup',
title: nls.localize({ key: 'miFocusRightGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Right")
},
order: 4
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '3_switch_group',
MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, {
group: '3_directional',
command: {
id: 'workbench.action.focusAboveGroup',
title: nls.localize({ key: 'miFocusAboveGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Above")
},
order: 5
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '3_switch_group',
group: '3_directional',
command: {
id: 'workbench.action.focusBelowGroup',
title: nls.localize({ key: 'miFocusBelowGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Below")
},
order: 6
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: '2_switch',
title: nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group"),
submenu: MenuId.MenubarSwitchGroupMenu,
order: 2
});
// Go to
......@@ -330,22 +388,24 @@ function helpMenuRegistration() {
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '5_tools',
command: {
id: 'workbench.action.showAccessibilityOptions',
title: nls.localize({ key: 'miAccessibilityOptions', comment: ['&& denotes a mnemonic'] }, "Accessibility &&Options")
},
order: 3
});
// About
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: 'z_about',
command: {
id: 'workbench.action.showAboutDialog',
title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")
},
order: 1
});
if (!isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '5_tools',
command: {
id: 'workbench.action.showAccessibilityOptions',
title: nls.localize({ key: 'miAccessibilityOptions', comment: ['&& denotes a mnemonic'] }, "Accessibility &&Options")
},
order: 3
});
// About
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: 'z_about',
command: {
id: 'workbench.action.showAboutDialog',
title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")
},
order: 1
});
}
}
......@@ -10,7 +10,7 @@ import 'vs/css!./media/menubarpart';
import * as nls from 'vs/nls';
import * as browser from 'vs/base/browser/browser';
import { Part } from 'vs/workbench/browser/part';
import { IMenubarService, IMenubarMenu, IMenubarMenuItemAction, IMenubarData } from 'vs/platform/menubar/common/menubar';
import { IMenubarService, IMenubarMenu, IMenubarMenuItemAction, IMenubarData, IMenubarMenuItemSubmenu, IMenubarKeybinding } from 'vs/platform/menubar/common/menubar';
import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { IWindowService, MenuBarVisibility, IWindowsService } from 'vs/platform/windows/common/windows';
......@@ -145,6 +145,7 @@ export class MenubarPart extends Part {
};
if (isMacintosh) {
this.topLevelMenus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService));
this.topLevelMenus['Window'] = this._register(this.menuService.createMenu(MenuId.MenubarWindowMenu, this.contextKeyService));
}
......@@ -430,7 +431,9 @@ export class MenubarPart extends Part {
if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') {
this.setupCustomMenubar();
} else {
this.setupNativeMenubar();
// Send menus to main process to be rendered by Electron
this.menubarService.updateMenubar(this.windowService.getCurrentWindowId(), this.getMenubarMenus());
}
}
......@@ -438,14 +441,6 @@ export class MenubarPart extends Part {
this.menuUpdater.schedule();
}
private setupNativeMenubar(): void {
// TODO@sbatten: Remove once native menubar is ready
if (isMacintosh && isWindows) {
this.menubarService.updateMenubar(this.windowService.getCurrentWindowId(), this.getMenubarMenus());
}
}
private clearMnemonic(topLevelElement: HTMLElement): void {
topLevelElement.accessKey = null;
}
......@@ -800,37 +795,76 @@ export class MenubarPart extends Part {
}
}
private getMenubarMenus(): IMenubarData {
let ret: IMenubarData = {};
private getMenubarKeybinding(id: string): IMenubarKeybinding {
const binding = this.keybindingService.lookupKeybinding(id);
if (!binding) {
return null;
}
for (let topLevelMenuName of Object.keys(this.topLevelMenus)) {
const menu = this.topLevelMenus[topLevelMenuName];
let menubarMenu: IMenubarMenu = { items: [] };
let groups = menu.getActions();
for (let group of groups) {
const [, actions] = group;
// first try to resolve a native accelerator
const electronAccelerator = binding.getElectronAccelerator();
if (electronAccelerator) {
return { id, label: electronAccelerator, isNative: true };
}
// we need this fallback to support keybindings that cannot show in electron menus (e.g. chords)
const acceleratorLabel = binding.getLabel();
if (acceleratorLabel) {
return { id, label: acceleratorLabel, isNative: false };
}
return null;
}
private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu) {
let groups = menu.getActions();
for (let group of groups) {
const [, actions] = group;
actions.forEach(menuItemAction => {
actions.forEach(menuItem => {
if (menuItem instanceof SubmenuItemAction) {
const submenu = { items: [] };
this.populateMenuItems(this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService), submenu);
let menubarSubmenuItem: IMenubarMenuItemSubmenu = {
id: menuItem.id,
label: menuItem.label,
submenu: submenu
};
menuToPopulate.items.push(menubarSubmenuItem);
} else {
let menubarMenuItem: IMenubarMenuItemAction = {
id: menuItemAction.id,
label: menuItemAction.label,
checked: menuItemAction.checked,
enabled: menuItemAction.enabled
id: menuItem.id,
label: menuItem.label,
checked: menuItem.checked,
enabled: menuItem.enabled,
keybinding: this.getMenubarKeybinding(menuItem.id)
};
this.setCheckedStatus(menubarMenuItem);
menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem);
menubarMenu.items.push(menubarMenuItem);
});
menuToPopulate.items.push(menubarMenuItem);
}
});
menubarMenu.items.push({ id: 'vscode.menubar.separator' });
}
menuToPopulate.items.push({ id: 'vscode.menubar.separator' });
}
if (menubarMenu.items.length > 0) {
menubarMenu.items.pop();
}
if (menuToPopulate.items.length > 0) {
menuToPopulate.items.pop();
}
}
private getMenubarMenus(): IMenubarData {
let ret: IMenubarData = {};
for (let topLevelMenuName of Object.keys(this.topLevelMenus)) {
const menu = this.topLevelMenus[topLevelMenuName];
let menubarMenu: IMenubarMenu = { items: [] };
this.populateMenuItems(menu, menubarMenu);
ret[topLevelMenuName] = menubarMenu;
}
......
......@@ -163,23 +163,34 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFileAction.ID,
title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")
},
order: 1
});
if (!isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFileAction.ID,
title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFolderAction.ID,
title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")
},
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFolderAction.ID,
title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")
},
order: 2
});
} else {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
command: {
id: OpenFileFolderAction.ID,
title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")
},
order: 1
});
}
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '2_open',
......@@ -226,12 +237,14 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"),
submenu: MenuId.MenubarPreferencesMenu,
group: '5_autosave',
order: 2
});
if (!isMacintosh) {
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"),
submenu: MenuId.MenubarPreferencesMenu,
group: '5_autosave',
order: 2
});
}
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '6_close',
......
......@@ -9,7 +9,6 @@ import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import * as errors from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import * as arrays from 'vs/base/common/arrays';
import * as objects from 'vs/base/common/objects';
import * as DOM from 'vs/base/browser/dom';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
......@@ -21,13 +20,11 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IWindowsService, IWindowService, IWindowSettings, IPath, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
import { IWorkbenchThemeService, VS_HC_THEME, VS_DARK_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as browser from 'vs/base/browser/browser';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
import { Themable } from 'vs/workbench/common/theme';
import { ipcRenderer as ipc, webFrame } from 'electron';
......@@ -77,9 +74,7 @@ export class ElectronWindow extends Themable {
@IWorkbenchThemeService protected themeService: IWorkbenchThemeService,
@INotificationService private notificationService: INotificationService,
@ICommandService private commandService: ICommandService,
@IExtensionService private extensionService: IExtensionService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IKeybindingService private keybindingService: IKeybindingService,
@ITelemetryService private telemetryService: ITelemetryService,
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
@IFileService private fileService: IFileService,
......@@ -141,23 +136,6 @@ export class ElectronWindow extends Themable {
});
});
// Support resolve keybindings event
ipc.on('vscode:resolveKeybindings', (event: any, rawActionIds: string) => {
let actionIds: string[] = [];
try {
actionIds = JSON.parse(rawActionIds);
} catch (error) {
// should not happen
}
// Resolve keys using the keybinding service and send back to browser process
this.resolveKeybindings(actionIds).done(keybindings => {
if (keybindings.length) {
ipc.send('vscode:keybindingsResolved', JSON.stringify(keybindings));
}
}, () => errors.onUnexpectedError);
});
ipc.on('vscode:reportError', (event: any, error: string) => {
if (error) {
const errorParsed = JSON.parse(error);
......@@ -376,31 +354,6 @@ export class ElectronWindow extends Themable {
}
}
private resolveKeybindings(actionIds: string[]): TPromise<{ id: string; label: string, isNative: boolean; }[]> {
return TPromise.join([this.lifecycleService.when(LifecyclePhase.Running), this.extensionService.whenInstalledExtensionsRegistered()]).then(() => {
return arrays.coalesce(actionIds.map(id => {
const binding = this.keybindingService.lookupKeybinding(id);
if (!binding) {
return null;
}
// first try to resolve a native accelerator
const electronAccelerator = binding.getElectronAccelerator();
if (electronAccelerator) {
return { id, label: electronAccelerator, isNative: true };
}
// we need this fallback to support keybindings that cannot show in electron menus (e.g. chords)
const acceleratorLabel = binding.getLabel();
if (acceleratorLabel) {
return { id, label: acceleratorLabel, isNative: false };
}
return null;
}));
});
}
private onAddFoldersRequest(request: IAddFoldersRequest): void {
// Buffer all pending requests
......
......@@ -358,40 +358,47 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
group: '4_new_breakpoint',
MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, {
group: '1_breakpoints',
command: {
id: TOGGLE_CONDITIONAL_BREAKPOINT_ID,
title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Conditional Breakpoint...")
title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint...")
},
order: 2
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
group: '4_new_breakpoint',
MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, {
group: '1_breakpoints',
command: {
id: TOGGLE_INLINE_BREAKPOINT_ID,
title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle Inline Breakp&&oint")
title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Inline Breakp&&oint")
},
order: 3
order: 2
});
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
group: '4_new_breakpoint',
MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, {
group: '1_breakpoints',
command: {
id: AddFunctionBreakpointAction.ID,
title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Function Breakpoint...")
title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint...")
},
order: 4
order: 3
});
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
group: '4_new_breakpoint',
MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, {
group: '1_breakpoints',
command: {
id: TOGGLE_LOG_POINT_ID,
title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Logpoint...")
title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "&&Logpoint...")
},
order: 5
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
group: '4_new_breakpoint',
title: nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint"),
submenu: MenuId.MenubarNewBreakpointMenu,
order: 2
});
// Modify Breakpoints
......@@ -422,6 +429,16 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
order: 3
});
// Install Debuggers
MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
group: 'z_install',
command: {
id: 'debug.installAdditionalDebuggers',
title: nls.localize({ key: 'miInstallAdditionalDebuggers', comment: ['&& denotes a mnemonic'] }, "&&Install Additional Debuggers...")
},
order: 1
});
// Touch Bar
if (isMacintosh) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册