зеркало из https://github.com/electron/electron.git
chore: tsify menu (#24358)
This commit is contained in:
Родитель
2a3437e5b5
Коммит
71a7e1b2e3
|
@ -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),
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче