Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: tsify menu #24358

Merged
merged 1 commit into from Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api/menu.md
Expand Up @@ -96,7 +96,7 @@ Appends the `menuItem` to the menu.

* `id` String

Returns `MenuItem` the item with the specified `id`
Returns `MenuItem | null` the item with the specified `id`

#### `menu.insert(pos, menuItem)`

Expand Down
8 changes: 4 additions & 4 deletions filenames.auto.gni
Expand Up @@ -201,10 +201,10 @@ auto_filenames = {
"lib/browser/api/global-shortcut.ts",
"lib/browser/api/in-app-purchase.ts",
"lib/browser/api/ipc-main.ts",
"lib/browser/api/menu-item-roles.js",
"lib/browser/api/menu-item.js",
"lib/browser/api/menu-utils.js",
"lib/browser/api/menu.js",
"lib/browser/api/menu-item-roles.ts",
"lib/browser/api/menu-item.ts",
"lib/browser/api/menu-utils.ts",
"lib/browser/api/menu.ts",
"lib/browser/api/message-channel.ts",
"lib/browser/api/module-list.ts",
"lib/browser/api/native-theme.ts",
Expand Down
@@ -1,44 +1,56 @@
'use strict';

const { app } = require('electron');
import { app, BrowserWindow, WebContents, MenuItemConstructorOptions } from 'electron';

const isMac = process.platform === 'darwin';
const isWindows = process.platform === 'win32';
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: {
get label () {
return isLinux ? 'About' : `About ${app.name}`;
},
...(isWindows && { appMethod: 'showAboutPanel' })
...(isWindows && { appMethod: () => app.showAboutPanel() })
},
close: {
label: isMac ? 'Close Window' : 'Close',
accelerator: 'CommandOrControl+W',
windowMethod: 'close'
windowMethod: w => w.close()
},
copy: {
label: 'Copy',
accelerator: 'CommandOrControl+C',
webContentsMethod: 'copy',
webContentsMethod: wc => wc.copy(),
registerAccelerator: false
},
cut: {
label: 'Cut',
accelerator: 'CommandOrControl+X',
webContentsMethod: 'cut',
webContentsMethod: wc => wc.cut(),
registerAccelerator: false
},
delete: {
label: 'Delete',
webContentsMethod: 'delete'
webContentsMethod: wc => wc.delete()
},
forcereload: {
label: 'Force Reload',
accelerator: 'Shift+CmdOrCtrl+R',
nonNativeMacOSRole: true,
windowMethod: (window) => {
windowMethod: (window: BrowserWindow) => {
window.webContents.reloadIgnoringCache();
}
},
Expand All @@ -61,18 +73,18 @@ const roles = {
minimize: {
label: 'Minimize',
accelerator: 'CommandOrControl+M',
windowMethod: 'minimize'
windowMethod: w => w.minimize()
},
paste: {
label: 'Paste',
accelerator: 'CommandOrControl+V',
webContentsMethod: 'paste',
webContentsMethod: wc => wc.paste(),
registerAccelerator: false
},
pasteandmatchstyle: {
label: 'Paste and Match Style',
accelerator: isMac ? 'Cmd+Option+Shift+V' : 'Shift+CommandOrControl+V',
webContentsMethod: 'pasteAndMatchStyle',
webContentsMethod: wc => wc.pasteAndMatchStyle(),
registerAccelerator: false
},
quit: {
Expand All @@ -84,31 +96,31 @@ const roles = {
}
},
accelerator: isWindows ? undefined : 'CommandOrControl+Q',
appMethod: 'quit'
appMethod: () => app.quit()
},
redo: {
label: 'Redo',
accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z',
webContentsMethod: 'redo'
webContentsMethod: wc => wc.redo()
},
reload: {
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
nonNativeMacOSRole: true,
windowMethod: 'reload'
windowMethod: w => w.reload()
},
resetzoom: {
label: 'Actual Size',
accelerator: 'CommandOrControl+0',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContentsMethod: (webContents: WebContents) => {
webContents.zoomLevel = 0;
}
},
selectall: {
label: 'Select All',
accelerator: 'CommandOrControl+A',
webContentsMethod: 'selectAll'
webContentsMethod: wc => wc.selectAll()
},
services: {
label: 'Services'
Expand All @@ -129,19 +141,19 @@ const roles = {
label: 'Toggle Developer Tools',
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
nonNativeMacOSRole: true,
windowMethod: 'toggleDevTools'
windowMethod: w => w.webContents.toggleDevTools()
},
togglefullscreen: {
label: 'Toggle Full Screen',
accelerator: isMac ? 'Control+Command+F' : 'F11',
windowMethod: (window) => {
windowMethod: (window: BrowserWindow) => {
window.setFullScreen(!window.isFullScreen());
}
},
undo: {
label: 'Undo',
accelerator: 'CommandOrControl+Z',
webContentsMethod: 'undo'
webContentsMethod: wc => wc.undo()
},
unhide: {
label: 'Show All'
Expand All @@ -156,15 +168,15 @@ const roles = {
label: 'Zoom In',
accelerator: 'CommandOrControl+Plus',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContentsMethod: (webContents: WebContents) => {
webContents.zoomLevel += 0.5;
}
},
zoomout: {
label: 'Zoom Out',
accelerator: 'CommandOrControl+-',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContentsMethod: (webContents: WebContents) => {
webContents.zoomLevel -= 0.5;
}
},
Expand Down Expand Up @@ -214,11 +226,11 @@ const roles = {
{ role: 'stopSpeaking' }
]
}
] : [
] as MenuItemConstructorOptions[] : [
{ role: 'delete' },
{ type: 'separator' },
{ role: 'selectAll' }
])
] as MenuItemConstructorOptions[])
]
},
// View submenu
Expand All @@ -245,76 +257,66 @@ const roles = {
...(isMac ? [
{ type: 'separator' },
{ role: 'front' }
] : [
] as MenuItemConstructorOptions[] : [
{ role: 'close' }
])
] as MenuItemConstructorOptions[])
]
}
};

exports.roleList = roles;

const canExecuteRole = (role) => {
if (!Object.prototype.hasOwnProperty.call(roles, role)) return false;
const canExecuteRole = (role: keyof typeof roleList) => {
if (!Object.prototype.hasOwnProperty.call(roleList, role)) return false;
if (!isMac) return true;

// macOS handles all roles natively except for a few
return roles[role].nonNativeMacOSRole;
return roleList[role].nonNativeMacOSRole;
};

exports.getDefaultLabel = (role) => {
return Object.prototype.hasOwnProperty.call(roles, role) ? roles[role].label : '';
};
export function getDefaultLabel (role: RoleId) {
return Object.prototype.hasOwnProperty.call(roleList, role) ? roleList[role].label : '';
}

exports.getDefaultAccelerator = (role) => {
if (Object.prototype.hasOwnProperty.call(roles, role)) return roles[role].accelerator;
};
export function getDefaultAccelerator (role: RoleId) {
if (Object.prototype.hasOwnProperty.call(roleList, role)) return roleList[role].accelerator;
}

exports.shouldRegisterAccelerator = (role) => {
const hasRoleRegister = Object.prototype.hasOwnProperty.call(roles, role) && roles[role].registerAccelerator !== undefined;
return hasRoleRegister ? roles[role].registerAccelerator : true;
};
export function shouldRegisterAccelerator (role: RoleId) {
const hasRoleRegister = Object.prototype.hasOwnProperty.call(roleList, role) && roleList[role].registerAccelerator !== undefined;
return hasRoleRegister ? roleList[role].registerAccelerator : true;
}

exports.getDefaultSubmenu = (role) => {
if (!Object.prototype.hasOwnProperty.call(roles, role)) return;
export function getDefaultSubmenu (role: RoleId) {
if (!Object.prototype.hasOwnProperty.call(roleList, role)) return;

let { submenu } = roles[role];
let { submenu } = roleList[role];

// remove null items from within the submenu
if (Array.isArray(submenu)) {
submenu = submenu.filter((item) => item != null);
}

return submenu;
};
}

exports.execute = (role, focusedWindow, focusedWebContents) => {
export function execute (role: RoleId, focusedWindow: BrowserWindow, focusedWebContents: WebContents) {
if (!canExecuteRole(role)) return false;

const { appMethod, webContentsMethod, windowMethod } = roles[role];
const { appMethod, webContentsMethod, windowMethod } = roleList[role];

if (appMethod) {
app[appMethod]();
appMethod();
return true;
}

if (windowMethod && focusedWindow != null) {
if (typeof windowMethod === 'function') {
windowMethod(focusedWindow);
} else {
focusedWindow[windowMethod]();
}
windowMethod(focusedWindow);
return true;
}

if (webContentsMethod && focusedWebContents != null) {
if (typeof webContentsMethod === 'function') {
webContentsMethod(focusedWebContents);
} else {
focusedWebContents[webContentsMethod]();
}
webContentsMethod(focusedWebContents);
return true;
}

return false;
};
}
15 changes: 6 additions & 9 deletions lib/browser/api/menu-item.js → lib/browser/api/menu-item.ts
@@ -1,12 +1,9 @@
'use strict';

const roles = require('@electron/internal/browser/api/menu-item-roles');
import * as roles from './menu-item-roles';
import { Menu, Event, BrowserWindow, WebContents } from 'electron';

let nextCommandId = 0;

const MenuItem = function (options) {
const { Menu } = require('electron');

const MenuItem = function (this: any, options: any) {
// Preserve extra fields specified by user
for (const key in options) {
if (!(key in this)) this[key] = options[key];
Expand Down Expand Up @@ -47,7 +44,7 @@ const MenuItem = function (options) {
this.overrideReadOnlyProperty('commandId', ++nextCommandId);

const click = options.click;
this.click = (event, focusedWindow, focusedWebContents) => {
this.click = (event: Event, focusedWindow: BrowserWindow, focusedWebContents: WebContents) => {
// Manually flip the checked flags when clicked.
if (this.type === 'checkbox' || this.type === 'radio') {
this.checked = !this.checked;
Expand All @@ -69,13 +66,13 @@ MenuItem.prototype.getDefaultRoleAccelerator = function () {
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) {
this[name] = defaultValue;
}
};

MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
MenuItem.prototype.overrideReadOnlyProperty = function (name: string, defaultValue: any) {
this.overrideProperty(name, defaultValue);
Object.defineProperty(this, name, {
enumerable: true,
Expand Down