This commit is contained in:
Jeremy Rose 2020-07-06 13:24:54 -07:00 коммит произвёл GitHub
Родитель 2a3437e5b5
Коммит 71a7e1b2e3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 156 добавлений и 131 удалений

Просмотреть файл

@ -96,7 +96,7 @@ Appends the `menuItem` to the menu.
* `id` String * `id` String
Returns `MenuItem` the item with the specified `id` Returns `MenuItem | null` the item with the specified `id`
#### `menu.insert(pos, menuItem)` #### `menu.insert(pos, menuItem)`

Просмотреть файл

@ -201,10 +201,10 @@ auto_filenames = {
"lib/browser/api/global-shortcut.ts", "lib/browser/api/global-shortcut.ts",
"lib/browser/api/in-app-purchase.ts", "lib/browser/api/in-app-purchase.ts",
"lib/browser/api/ipc-main.ts", "lib/browser/api/ipc-main.ts",
"lib/browser/api/menu-item-roles.js", "lib/browser/api/menu-item-roles.ts",
"lib/browser/api/menu-item.js", "lib/browser/api/menu-item.ts",
"lib/browser/api/menu-utils.js", "lib/browser/api/menu-utils.ts",
"lib/browser/api/menu.js", "lib/browser/api/menu.ts",
"lib/browser/api/message-channel.ts", "lib/browser/api/message-channel.ts",
"lib/browser/api/module-list.ts", "lib/browser/api/module-list.ts",
"lib/browser/api/native-theme.ts", "lib/browser/api/native-theme.ts",

Просмотреть файл

@ -1,44 +1,56 @@
'use strict'; import { app, BrowserWindow, WebContents, MenuItemConstructorOptions } from 'electron';
const { app } = require('electron');
const isMac = process.platform === 'darwin'; const isMac = process.platform === 'darwin';
const isWindows = process.platform === 'win32'; const isWindows = process.platform === 'win32';
const isLinux = process.platform === 'linux'; const isLinux = process.platform === 'linux';
const roles = { type RoleId = 'about' | 'close' | 'copy' | 'cut' | 'delete' | 'forcereload' | 'front' | 'help' | 'hide' | 'hideothers' | 'minimize' |
'paste' | 'pasteandmatchstyle' | 'quit' | 'redo' | 'reload' | 'resetzoom' | 'selectall' | 'services' | 'recentdocuments' | 'clearrecentdocuments' | 'startspeaking' | 'stopspeaking' |
'toggledevtools' | 'togglefullscreen' | 'undo' | 'unhide' | 'window' | 'zoom' | 'zoomin' | 'zoomout' | 'appmenu' | 'filemenu' | 'editmenu' | 'viewmenu' | 'windowmenu'
interface Role {
label: string;
accelerator?: string;
windowMethod?: ((window: BrowserWindow) => void);
webContentsMethod?: ((webContents: WebContents) => void);
appMethod?: () => void;
registerAccelerator?: boolean;
nonNativeMacOSRole?: boolean;
submenu?: MenuItemConstructorOptions[];
}
export const roleList: Record<RoleId, Role> = {
about: { about: {
get label () { get label () {
return isLinux ? 'About' : `About ${app.name}`; return isLinux ? 'About' : `About ${app.name}`;
}, },
...(isWindows && { appMethod: 'showAboutPanel' }) ...(isWindows && { appMethod: () => app.showAboutPanel() })
}, },
close: { close: {
label: isMac ? 'Close Window' : 'Close', label: isMac ? 'Close Window' : 'Close',
accelerator: 'CommandOrControl+W', accelerator: 'CommandOrControl+W',
windowMethod: 'close' windowMethod: w => w.close()
}, },
copy: { copy: {
label: 'Copy', label: 'Copy',
accelerator: 'CommandOrControl+C', accelerator: 'CommandOrControl+C',
webContentsMethod: 'copy', webContentsMethod: wc => wc.copy(),
registerAccelerator: false registerAccelerator: false
}, },
cut: { cut: {
label: 'Cut', label: 'Cut',
accelerator: 'CommandOrControl+X', accelerator: 'CommandOrControl+X',
webContentsMethod: 'cut', webContentsMethod: wc => wc.cut(),
registerAccelerator: false registerAccelerator: false
}, },
delete: { delete: {
label: 'Delete', label: 'Delete',
webContentsMethod: 'delete' webContentsMethod: wc => wc.delete()
}, },
forcereload: { forcereload: {
label: 'Force Reload', label: 'Force Reload',
accelerator: 'Shift+CmdOrCtrl+R', accelerator: 'Shift+CmdOrCtrl+R',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
windowMethod: (window) => { windowMethod: (window: BrowserWindow) => {
window.webContents.reloadIgnoringCache(); window.webContents.reloadIgnoringCache();
} }
}, },
@ -61,18 +73,18 @@ const roles = {
minimize: { minimize: {
label: 'Minimize', label: 'Minimize',
accelerator: 'CommandOrControl+M', accelerator: 'CommandOrControl+M',
windowMethod: 'minimize' windowMethod: w => w.minimize()
}, },
paste: { paste: {
label: 'Paste', label: 'Paste',
accelerator: 'CommandOrControl+V', accelerator: 'CommandOrControl+V',
webContentsMethod: 'paste', webContentsMethod: wc => wc.paste(),
registerAccelerator: false registerAccelerator: false
}, },
pasteandmatchstyle: { pasteandmatchstyle: {
label: 'Paste and Match Style', label: 'Paste and Match Style',
accelerator: isMac ? 'Cmd+Option+Shift+V' : 'Shift+CommandOrControl+V', accelerator: isMac ? 'Cmd+Option+Shift+V' : 'Shift+CommandOrControl+V',
webContentsMethod: 'pasteAndMatchStyle', webContentsMethod: wc => wc.pasteAndMatchStyle(),
registerAccelerator: false registerAccelerator: false
}, },
quit: { quit: {
@ -84,31 +96,31 @@ const roles = {
} }
}, },
accelerator: isWindows ? undefined : 'CommandOrControl+Q', accelerator: isWindows ? undefined : 'CommandOrControl+Q',
appMethod: 'quit' appMethod: () => app.quit()
}, },
redo: { redo: {
label: 'Redo', label: 'Redo',
accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z', accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z',
webContentsMethod: 'redo' webContentsMethod: wc => wc.redo()
}, },
reload: { reload: {
label: 'Reload', label: 'Reload',
accelerator: 'CmdOrCtrl+R', accelerator: 'CmdOrCtrl+R',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
windowMethod: 'reload' windowMethod: w => w.reload()
}, },
resetzoom: { resetzoom: {
label: 'Actual Size', label: 'Actual Size',
accelerator: 'CommandOrControl+0', accelerator: 'CommandOrControl+0',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
webContentsMethod: (webContents) => { webContentsMethod: (webContents: WebContents) => {
webContents.zoomLevel = 0; webContents.zoomLevel = 0;
} }
}, },
selectall: { selectall: {
label: 'Select All', label: 'Select All',
accelerator: 'CommandOrControl+A', accelerator: 'CommandOrControl+A',
webContentsMethod: 'selectAll' webContentsMethod: wc => wc.selectAll()
}, },
services: { services: {
label: 'Services' label: 'Services'
@ -129,19 +141,19 @@ const roles = {
label: 'Toggle Developer Tools', label: 'Toggle Developer Tools',
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I', accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
windowMethod: 'toggleDevTools' windowMethod: w => w.webContents.toggleDevTools()
}, },
togglefullscreen: { togglefullscreen: {
label: 'Toggle Full Screen', label: 'Toggle Full Screen',
accelerator: isMac ? 'Control+Command+F' : 'F11', accelerator: isMac ? 'Control+Command+F' : 'F11',
windowMethod: (window) => { windowMethod: (window: BrowserWindow) => {
window.setFullScreen(!window.isFullScreen()); window.setFullScreen(!window.isFullScreen());
} }
}, },
undo: { undo: {
label: 'Undo', label: 'Undo',
accelerator: 'CommandOrControl+Z', accelerator: 'CommandOrControl+Z',
webContentsMethod: 'undo' webContentsMethod: wc => wc.undo()
}, },
unhide: { unhide: {
label: 'Show All' label: 'Show All'
@ -156,7 +168,7 @@ const roles = {
label: 'Zoom In', label: 'Zoom In',
accelerator: 'CommandOrControl+Plus', accelerator: 'CommandOrControl+Plus',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
webContentsMethod: (webContents) => { webContentsMethod: (webContents: WebContents) => {
webContents.zoomLevel += 0.5; webContents.zoomLevel += 0.5;
} }
}, },
@ -164,7 +176,7 @@ const roles = {
label: 'Zoom Out', label: 'Zoom Out',
accelerator: 'CommandOrControl+-', accelerator: 'CommandOrControl+-',
nonNativeMacOSRole: true, nonNativeMacOSRole: true,
webContentsMethod: (webContents) => { webContentsMethod: (webContents: WebContents) => {
webContents.zoomLevel -= 0.5; webContents.zoomLevel -= 0.5;
} }
}, },
@ -214,11 +226,11 @@ const roles = {
{ role: 'stopSpeaking' } { role: 'stopSpeaking' }
] ]
} }
] : [ ] as MenuItemConstructorOptions[] : [
{ role: 'delete' }, { role: 'delete' },
{ type: 'separator' }, { type: 'separator' },
{ role: 'selectAll' } { role: 'selectAll' }
]) ] as MenuItemConstructorOptions[])
] ]
}, },
// View submenu // View submenu
@ -245,40 +257,38 @@ const roles = {
...(isMac ? [ ...(isMac ? [
{ type: 'separator' }, { type: 'separator' },
{ role: 'front' } { role: 'front' }
] : [ ] as MenuItemConstructorOptions[] : [
{ role: 'close' } { role: 'close' }
]) ] as MenuItemConstructorOptions[])
] ]
} }
}; };
exports.roleList = roles; const canExecuteRole = (role: keyof typeof roleList) => {
if (!Object.prototype.hasOwnProperty.call(roleList, role)) return false;
const canExecuteRole = (role) => {
if (!Object.prototype.hasOwnProperty.call(roles, role)) return false;
if (!isMac) return true; if (!isMac) return true;
// macOS handles all roles natively except for a few // macOS handles all roles natively except for a few
return roles[role].nonNativeMacOSRole; return roleList[role].nonNativeMacOSRole;
}; };
exports.getDefaultLabel = (role) => { export function getDefaultLabel (role: RoleId) {
return Object.prototype.hasOwnProperty.call(roles, role) ? roles[role].label : ''; return Object.prototype.hasOwnProperty.call(roleList, role) ? roleList[role].label : '';
}; }
exports.getDefaultAccelerator = (role) => { export function getDefaultAccelerator (role: RoleId) {
if (Object.prototype.hasOwnProperty.call(roles, role)) return roles[role].accelerator; if (Object.prototype.hasOwnProperty.call(roleList, role)) return roleList[role].accelerator;
}; }
exports.shouldRegisterAccelerator = (role) => { export function shouldRegisterAccelerator (role: RoleId) {
const hasRoleRegister = Object.prototype.hasOwnProperty.call(roles, role) && roles[role].registerAccelerator !== undefined; const hasRoleRegister = Object.prototype.hasOwnProperty.call(roleList, role) && roleList[role].registerAccelerator !== undefined;
return hasRoleRegister ? roles[role].registerAccelerator : true; return hasRoleRegister ? roleList[role].registerAccelerator : true;
}; }
exports.getDefaultSubmenu = (role) => { export function getDefaultSubmenu (role: RoleId) {
if (!Object.prototype.hasOwnProperty.call(roles, role)) return; if (!Object.prototype.hasOwnProperty.call(roleList, role)) return;
let { submenu } = roles[role]; let { submenu } = roleList[role];
// remove null items from within the submenu // remove null items from within the submenu
if (Array.isArray(submenu)) { if (Array.isArray(submenu)) {
@ -286,35 +296,27 @@ exports.getDefaultSubmenu = (role) => {
} }
return submenu; return submenu;
}; }
exports.execute = (role, focusedWindow, focusedWebContents) => { export function execute (role: RoleId, focusedWindow: BrowserWindow, focusedWebContents: WebContents) {
if (!canExecuteRole(role)) return false; if (!canExecuteRole(role)) return false;
const { appMethod, webContentsMethod, windowMethod } = roles[role]; const { appMethod, webContentsMethod, windowMethod } = roleList[role];
if (appMethod) { if (appMethod) {
app[appMethod](); appMethod();
return true; return true;
} }
if (windowMethod && focusedWindow != null) { if (windowMethod && focusedWindow != null) {
if (typeof windowMethod === 'function') { windowMethod(focusedWindow);
windowMethod(focusedWindow);
} else {
focusedWindow[windowMethod]();
}
return true; return true;
} }
if (webContentsMethod && focusedWebContents != null) { if (webContentsMethod && focusedWebContents != null) {
if (typeof webContentsMethod === 'function') { webContentsMethod(focusedWebContents);
webContentsMethod(focusedWebContents);
} else {
focusedWebContents[webContentsMethod]();
}
return true; return true;
} }
return false; return false;
}; }

Просмотреть файл

@ -1,12 +1,9 @@
'use strict'; import * as roles from './menu-item-roles';
import { Menu, Event, BrowserWindow, WebContents } from 'electron';
const roles = require('@electron/internal/browser/api/menu-item-roles');
let nextCommandId = 0; let nextCommandId = 0;
const MenuItem = function (options) { const MenuItem = function (this: any, options: any) {
const { Menu } = require('electron');
// Preserve extra fields specified by user // Preserve extra fields specified by user
for (const key in options) { for (const key in options) {
if (!(key in this)) this[key] = options[key]; if (!(key in this)) this[key] = options[key];
@ -47,7 +44,7 @@ const MenuItem = function (options) {
this.overrideReadOnlyProperty('commandId', ++nextCommandId); this.overrideReadOnlyProperty('commandId', ++nextCommandId);
const click = options.click; const click = options.click;
this.click = (event, focusedWindow, focusedWebContents) => { this.click = (event: Event, focusedWindow: BrowserWindow, focusedWebContents: WebContents) => {
// Manually flip the checked flags when clicked. // Manually flip the checked flags when clicked.
if (this.type === 'checkbox' || this.type === 'radio') { if (this.type === 'checkbox' || this.type === 'radio') {
this.checked = !this.checked; this.checked = !this.checked;
@ -69,13 +66,13 @@ MenuItem.prototype.getDefaultRoleAccelerator = function () {
return roles.getDefaultAccelerator(this.role); return roles.getDefaultAccelerator(this.role);
}; };
MenuItem.prototype.overrideProperty = function (name, defaultValue = null) { MenuItem.prototype.overrideProperty = function (name: string, defaultValue: any = null) {
if (this[name] == null) { if (this[name] == null) {
this[name] = defaultValue; this[name] = defaultValue;
} }
}; };
MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) { MenuItem.prototype.overrideReadOnlyProperty = function (name: string, defaultValue: any) {
this.overrideProperty(name, defaultValue); this.overrideProperty(name, defaultValue);
Object.defineProperty(this, name, { Object.defineProperty(this, name, {
enumerable: true, enumerable: true,

Просмотреть файл

@ -1,6 +1,4 @@
'use strict'; function splitArray<T> (arr: T[], predicate: (x: T) => boolean) {
function splitArray (arr, predicate) {
const result = arr.reduce((multi, item) => { const result = arr.reduce((multi, item) => {
const current = multi[multi.length - 1]; const current = multi[multi.length - 1];
if (predicate(item)) { if (predicate(item)) {
@ -9,7 +7,7 @@ function splitArray (arr, predicate) {
current.push(item); current.push(item);
} }
return multi; return multi;
}, [[]]); }, [[]] as T[][]);
if (result[result.length - 1].length === 0) { if (result[result.length - 1].length === 0) {
return result.slice(0, result.length - 1); return result.slice(0, result.length - 1);
@ -17,7 +15,7 @@ function splitArray (arr, predicate) {
return result; return result;
} }
function joinArrays (arrays, joinIDs) { function joinArrays (arrays: any[][], joinIDs: any[]) {
return arrays.reduce((joined, arr, i) => { return arrays.reduce((joined, arr, i) => {
if (i > 0 && arr.length) { if (i > 0 && arr.length) {
if (joinIDs.length > 0) { if (joinIDs.length > 0) {
@ -31,14 +29,14 @@ function joinArrays (arrays, joinIDs) {
}, []); }, []);
} }
function pushOntoMultiMap (map, key, value) { function pushOntoMultiMap<K, V> (map: Map<K, V[]>, key: K, value: V) {
if (!map.has(key)) { if (!map.has(key)) {
map.set(key, []); map.set(key, []);
} }
map.get(key).push(value); map.get(key)!.push(value);
} }
function indexOfGroupContainingID (groups, id, ignoreGroup) { function indexOfGroupContainingID<T> (groups: {id?: T}[][], id: T, ignoreGroup: {id?: T}[]) {
return groups.findIndex( return groups.findIndex(
candidateGroup => candidateGroup =>
candidateGroup !== ignoreGroup && candidateGroup !== ignoreGroup &&
@ -50,11 +48,11 @@ function indexOfGroupContainingID (groups, id, ignoreGroup) {
// Sort nodes topologically using a depth-first approach. Encountered cycles // Sort nodes topologically using a depth-first approach. Encountered cycles
// are broken. // are broken.
function sortTopologically (originalOrder, edgesById) { function sortTopologically<T> (originalOrder: T[], edgesById: Map<T, T[]>) {
const sorted = []; const sorted = [] as T[];
const marked = new Set(); const marked = new Set<T>();
const visit = (mark) => { const visit = (mark: T) => {
if (marked.has(mark)) return; if (marked.has(mark)) return;
marked.add(mark); marked.add(mark);
const edges = edgesById.get(mark); const edges = edgesById.get(mark);
@ -68,7 +66,7 @@ function sortTopologically (originalOrder, edgesById) {
return sorted; return sorted;
} }
function attemptToMergeAGroup (groups) { function attemptToMergeAGroup<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
for (let i = 0; i < groups.length; i++) { for (let i = 0; i < groups.length; i++) {
const group = groups[i]; const group = groups[i];
for (const item of group) { for (const item of group) {
@ -87,7 +85,7 @@ function attemptToMergeAGroup (groups) {
return false; return false;
} }
function mergeGroups (groups) { function mergeGroups<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
let merged = true; let merged = true;
while (merged) { while (merged) {
merged = attemptToMergeAGroup(groups); merged = attemptToMergeAGroup(groups);
@ -95,7 +93,7 @@ function mergeGroups (groups) {
return groups; return groups;
} }
function sortItemsInGroup (group) { function sortItemsInGroup<T> (group: {before?: T[], after?: T[], id?: T}[]) {
const originalOrder = group.map((node, i) => i); const originalOrder = group.map((node, i) => i);
const edges = new Map(); const edges = new Map();
const idToIndex = new Map(group.map((item, i) => [item.id, i])); const idToIndex = new Map(group.map((item, i) => [item.id, i]));
@ -123,7 +121,7 @@ function sortItemsInGroup (group) {
return sortedNodes.map(i => group[i]); return sortedNodes.map(i => group[i]);
} }
function findEdgesInGroup (groups, i, edges) { function findEdgesInGroup<T> (groups: {beforeGroupContaining?: T[], afterGroupContaining?: T[], id?: T}[][], i: number, edges: Map<any, any>) {
const group = groups[i]; const group = groups[i];
for (const item of group) { for (const item of group) {
if (item.beforeGroupContaining) { if (item.beforeGroupContaining) {
@ -147,7 +145,7 @@ function findEdgesInGroup (groups, i, edges) {
} }
} }
function sortGroups (groups) { function sortGroups<T> (groups: {id?: T}[][]) {
const originalOrder = groups.map((item, i) => i); const originalOrder = groups.map((item, i) => i);
const edges = new Map(); const edges = new Map();
@ -159,8 +157,8 @@ function sortGroups (groups) {
return sortedGroupIndexes.map(i => groups[i]); return sortedGroupIndexes.map(i => groups[i]);
} }
function sortMenuItems (menuItems) { export function sortMenuItems (menuItems: {type?: string, id?: string}[]) {
const isSeparator = (item) => item.type === 'separator'; const isSeparator = (item: {type?: string}) => item.type === 'separator';
const separators = menuItems.filter(i => i.type === 'separator'); const separators = menuItems.filter(i => i.type === 'separator');
// Split the items into their implicit groups based upon separators. // Split the items into their implicit groups based upon separators.
@ -172,5 +170,3 @@ function sortMenuItems (menuItems) {
const joined = joinArrays(sortedGroups, separators); const joined = joinArrays(sortedGroups, separators);
return joined; return joined;
} }
module.exports = { sortMenuItems };

Просмотреть файл

@ -1,13 +1,11 @@
'use strict'; import { BaseWindow, MenuItem, webContents, Menu as MenuType, BrowserWindow, MenuItemConstructorOptions } from 'electron';
import { sortMenuItems } from './menu-utils';
const { BaseWindow, MenuItem, webContents } = require('electron');
const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils');
const EventEmitter = require('events').EventEmitter;
const v8Util = process._linkedBinding('electron_common_v8_util'); const v8Util = process._linkedBinding('electron_common_v8_util');
const bindings = process._linkedBinding('electron_browser_menu'); const bindings = process._linkedBinding('electron_browser_menu');
const { Menu } = bindings; const { Menu } = bindings as { Menu: typeof MenuType };
let applicationMenu = null; let applicationMenu: MenuType | null = null;
let groupIdIndex = 0; let groupIdIndex = 0;
/* Instance Methods */ /* Instance Methods */
@ -19,17 +17,17 @@ Menu.prototype._init = function () {
}; };
Menu.prototype._isCommandIdChecked = function (id) { Menu.prototype._isCommandIdChecked = function (id) {
return this.commandsMap[id] ? this.commandsMap[id].checked : undefined; return this.commandsMap[id] ? this.commandsMap[id].checked : false;
}; };
Menu.prototype._isCommandIdEnabled = function (id) { Menu.prototype._isCommandIdEnabled = function (id) {
return this.commandsMap[id] ? this.commandsMap[id].enabled : undefined; return this.commandsMap[id] ? this.commandsMap[id].enabled : false;
}; };
Menu.prototype._shouldCommandIdWorkWhenHidden = function (id) { Menu.prototype._shouldCommandIdWorkWhenHidden = function (id) {
return this.commandsMap[id] ? this.commandsMap[id].acceleratorWorksWhenHidden : undefined; return this.commandsMap[id] ? !!this.commandsMap[id].acceleratorWorksWhenHidden : false;
}; };
Menu.prototype._isCommandIdVisible = function (id) { Menu.prototype._isCommandIdVisible = function (id) {
return this.commandsMap[id] ? this.commandsMap[id].visible : undefined; return this.commandsMap[id] ? this.commandsMap[id].visible : false;
}; };
Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator) { Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator) {
@ -40,13 +38,14 @@ Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator
}; };
Menu.prototype._shouldRegisterAcceleratorForCommandId = function (id) { Menu.prototype._shouldRegisterAcceleratorForCommandId = function (id) {
return this.commandsMap[id] ? this.commandsMap[id].registerAccelerator : undefined; return this.commandsMap[id] ? this.commandsMap[id].registerAccelerator : false;
}; };
Menu.prototype._executeCommand = function (event, id) { Menu.prototype._executeCommand = function (event, id) {
const command = this.commandsMap[id]; const command = this.commandsMap[id];
if (!command) return; if (!command) return;
command.click(event, BaseWindow.getFocusedWindow(), webContents.getFocusedWebContents()); const focusedWindow = BaseWindow.getFocusedWindow();
command.click(event, focusedWindow instanceof BrowserWindow ? focusedWindow : undefined, webContents.getFocusedWebContents());
}; };
Menu.prototype._menuWillShow = function () { Menu.prototype._menuWillShow = function () {
@ -73,17 +72,17 @@ Menu.prototype.popup = function (options = {}) {
// find which window to use // find which window to use
const wins = BaseWindow.getAllWindows(); const wins = BaseWindow.getAllWindows();
if (!wins || wins.indexOf(window) === -1) { if (!wins || wins.indexOf(window as any) === -1) {
window = BaseWindow.getFocusedWindow(); window = BaseWindow.getFocusedWindow() as any;
if (!window && wins && wins.length > 0) { if (!window && wins && wins.length > 0) {
window = wins[0]; window = wins[0] as any;
} }
if (!window) { if (!window) {
throw new Error('Cannot open Menu without a BaseWindow present'); throw new Error('Cannot open Menu without a BaseWindow present');
} }
} }
this.popupAt(window, x, y, positioningItem, callback); this.popupAt(window as unknown as BaseWindow, x, y, positioningItem, callback);
return { browserWindow: window, x, y, position: positioningItem }; return { browserWindow: window, x, y, position: positioningItem };
}; };
@ -102,8 +101,9 @@ Menu.prototype.getMenuItemById = function (id) {
let found = items.find(item => item.id === id) || null; let found = items.find(item => item.id === id) || null;
for (let i = 0; !found && i < items.length; i++) { for (let i = 0; !found && i < items.length; i++) {
if (items[i].submenu) { const { submenu } = items[i];
found = items[i].submenu.getMenuItemById(id); if (submenu) {
found = submenu.getMenuItemById(id);
} }
} }
return found; return found;
@ -155,7 +155,7 @@ Menu.getApplicationMenu = () => applicationMenu;
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder; Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
// set application menu with a preexisting menu // set application menu with a preexisting menu
Menu.setApplicationMenu = function (menu) { Menu.setApplicationMenu = function (menu: MenuType) {
if (menu && menu.constructor !== Menu) { if (menu && menu.constructor !== Menu) {
throw new TypeError('Invalid menu'); throw new TypeError('Invalid menu');
} }
@ -200,7 +200,7 @@ Menu.buildFromTemplate = function (template) {
/* Helper Functions */ /* Helper Functions */
// validate the template against having the wrong attribute // validate the template against having the wrong attribute
function areValidTemplateItems (template) { function areValidTemplateItems (template: (MenuItemConstructorOptions | MenuItem)[]) {
return template.every(item => return template.every(item =>
item != null && item != null &&
typeof item === 'object' && typeof item === 'object' &&
@ -209,7 +209,7 @@ function areValidTemplateItems (template) {
item.type === 'separator')); item.type === 'separator'));
} }
function sortTemplate (template) { function sortTemplate (template: (MenuItemConstructorOptions | MenuItem)[]) {
const sorted = sortMenuItems(template); const sorted = sortMenuItems(template);
for (const item of sorted) { for (const item of sorted) {
if (Array.isArray(item.submenu)) { if (Array.isArray(item.submenu)) {
@ -220,15 +220,15 @@ function sortTemplate (template) {
} }
// Search between separators to find a radio menu item and return its group id // Search between separators to find a radio menu item and return its group id
function generateGroupId (items, pos) { function generateGroupId (items: (MenuItemConstructorOptions | MenuItem)[], pos: number) {
if (pos > 0) { if (pos > 0) {
for (let idx = pos - 1; idx >= 0; idx--) { for (let idx = pos - 1; idx >= 0; idx--) {
if (items[idx].type === 'radio') return items[idx].groupId; if (items[idx].type === 'radio') return (items[idx] as any).groupId;
if (items[idx].type === 'separator') break; if (items[idx].type === 'separator') break;
} }
} else if (pos < items.length) { } else if (pos < items.length) {
for (let idx = pos; idx <= items.length - 1; idx++) { for (let idx = pos; idx <= items.length - 1; idx++) {
if (items[idx].type === 'radio') return items[idx].groupId; if (items[idx].type === 'radio') return (items[idx] as any).groupId;
if (items[idx].type === 'separator') break; if (items[idx].type === 'separator') break;
} }
} }
@ -236,7 +236,7 @@ function generateGroupId (items, pos) {
return groupIdIndex; return groupIdIndex;
} }
function removeExtraSeparators (items) { function removeExtraSeparators (items: (MenuItemConstructorOptions | MenuItem)[]) {
// fold adjacent separators together // fold adjacent separators together
let ret = items.filter((e, idx, arr) => { let ret = items.filter((e, idx, arr) => {
if (e.visible === false) return true; if (e.visible === false) return true;
@ -252,7 +252,7 @@ function removeExtraSeparators (items) {
return ret; return ret;
} }
function insertItemByType (item, pos) { function insertItemByType (this: MenuType, item: MenuItem, pos: number) {
const types = { const types = {
normal: () => this.insertItem(pos, item.commandId, item.label), normal: () => this.insertItem(pos, item.commandId, item.label),
checkbox: () => this.insertCheckItem(pos, item.commandId, item.label), checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),

10
spec-main/ambient.d.ts поставляемый
Просмотреть файл

@ -1,16 +1,6 @@
declare let standardScheme: string; declare let standardScheme: string;
declare namespace Electron { declare namespace Electron {
interface Menu {
_executeCommand(event: any, id: number): void;
_menuWillShow(): void;
getAcceleratorTextAt(index: number): string;
}
interface MenuItem {
getDefaultRoleAccelerator(): Accelerator | undefined;
}
interface WebContents { interface WebContents {
getOwnerBrowserWindow(): BrowserWindow; getOwnerBrowserWindow(): BrowserWindow;
getWebPreferences(): any; getWebPreferences(): any;

40
typings/internal-electron.d.ts поставляемый
Просмотреть файл

@ -107,6 +107,45 @@ declare namespace Electron {
length(): number; length(): number;
} }
interface Menu {
_init(): void;
_isCommandIdChecked(id: string): boolean;
_isCommandIdEnabled(id: string): boolean;
_shouldCommandIdWorkWhenHidden(id: string): boolean;
_isCommandIdVisible(id: string): boolean;
_getAcceleratorForCommandId(id: string, useDefaultAccelerator: boolean): Accelerator | undefined;
_shouldRegisterAcceleratorForCommandId(id: string): boolean;
_callMenuWillShow(): void;
_executeCommand(event: any, id: number): void;
_menuWillShow(): void;
commandsMap: Record<string, MenuItem>;
groupsMap: Record<string, {
checked: boolean;
}[]>;
getItemCount(): number;
popupAt(window: BaseWindow, x: number, y: number, positioning: number, callback: () => void): void;
closePopupAt(id: number): void;
setSublabel(index: number, label: string): void;
setToolTip(index: number, tooltip: string): void;
setIcon(index: number, image: string | NativeImage): void;
setRole(index: number, role: string): void;
insertItem(index: number, commandId: number, label: string): void;
insertCheckItem(index: number, commandId: number, label: string): void;
insertRadioItem(index: number, commandId: number, label: string, groupId: number): void;
insertSeparator(index: number): void;
insertSubMenu(index: number, commandId: number, label: string, submenu?: Menu): void;
delegate?: any;
getAcceleratorTextAt(index: number): string;
}
interface MenuItem {
overrideReadOnlyProperty(property: string, value: any): void;
groupId: number;
getDefaultRoleAccelerator(): Accelerator | undefined;
acceleratorWorksWhenHidden?: boolean;
}
const deprecate: ElectronInternal.DeprecationUtil; const deprecate: ElectronInternal.DeprecationUtil;
namespace Main { namespace Main {
@ -123,6 +162,7 @@ declare namespace Electron {
static getAllWindows(): BaseWindow[]; static getAllWindows(): BaseWindow[];
isFocused(): boolean; isFocused(): boolean;
static getFocusedWindow(): BaseWindow | undefined; static getFocusedWindow(): BaseWindow | undefined;
setMenu(menu: Menu): void;
} }
class WebContentsView { class WebContentsView {
constructor(options: BrowserWindowConstructorOptions) constructor(options: BrowserWindowConstructorOptions)