123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- import { app, BaseWindow, BrowserWindow, session, webContents, WebContents, MenuItemConstructorOptions } from 'electron/main';
- const isMac = process.platform === 'darwin';
- const isWindows = process.platform === 'win32';
- const isLinux = process.platform === 'linux';
- type RoleId = 'about' | 'close' | 'copy' | 'cut' | 'delete' | 'forcereload' | 'front' | 'help' | 'hide' | 'hideothers' | 'minimize' |
- 'paste' | 'pasteandmatchstyle' | 'quit' | 'redo' | 'reload' | 'resetzoom' | 'selectall' | 'services' | 'recentdocuments' | 'clearrecentdocuments' |
- 'showsubstitutions' | 'togglesmartquotes' | 'togglesmartdashes' | 'toggletextreplacement' | 'startspeaking' | 'stopspeaking' |
- 'toggledevtools' | 'togglefullscreen' | 'undo' | 'unhide' | 'window' | 'zoom' | 'zoomin' | 'zoomout' | 'togglespellchecker' |
- 'appmenu' | 'filemenu' | 'editmenu' | 'viewmenu' | 'windowmenu' | 'sharemenu'
- interface Role {
- label: string;
- accelerator?: string;
- checked?: boolean;
- windowMethod?: ((window: BaseWindow) => 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 || isLinux) && { appMethod: () => app.showAboutPanel() })
- },
- close: {
- label: isMac ? 'Close Window' : 'Close',
- accelerator: 'CommandOrControl+W',
- windowMethod: w => w.close()
- },
- copy: {
- label: 'Copy',
- accelerator: 'CommandOrControl+C',
- webContentsMethod: wc => wc.copy(),
- registerAccelerator: false
- },
- cut: {
- label: 'Cut',
- accelerator: 'CommandOrControl+X',
- webContentsMethod: wc => wc.cut(),
- registerAccelerator: false
- },
- delete: {
- label: 'Delete',
- webContentsMethod: wc => wc.delete()
- },
- forcereload: {
- label: 'Force Reload',
- accelerator: 'Shift+CmdOrCtrl+R',
- nonNativeMacOSRole: true,
- windowMethod: (window: BaseWindow) => {
- if (window instanceof BrowserWindow) {
- window.webContents.reloadIgnoringCache();
- }
- }
- },
- front: {
- label: 'Bring All to Front'
- },
- help: {
- label: 'Help'
- },
- hide: {
- get label () {
- return `Hide ${app.name}`;
- },
- accelerator: 'Command+H'
- },
- hideothers: {
- label: 'Hide Others',
- accelerator: 'Command+Alt+H'
- },
- minimize: {
- label: 'Minimize',
- accelerator: 'CommandOrControl+M',
- windowMethod: w => w.minimize()
- },
- paste: {
- label: 'Paste',
- accelerator: 'CommandOrControl+V',
- webContentsMethod: wc => wc.paste(),
- registerAccelerator: false
- },
- pasteandmatchstyle: {
- label: 'Paste and Match Style',
- accelerator: isMac ? 'Cmd+Option+Shift+V' : 'Shift+CommandOrControl+V',
- webContentsMethod: wc => wc.pasteAndMatchStyle(),
- registerAccelerator: false
- },
- quit: {
- get label () {
- switch (process.platform) {
- case 'darwin': return `Quit ${app.name}`;
- case 'win32': return 'Exit';
- default: return 'Quit';
- }
- },
- accelerator: isWindows ? undefined : 'CommandOrControl+Q',
- appMethod: () => app.quit()
- },
- redo: {
- label: 'Redo',
- accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z',
- webContentsMethod: wc => wc.redo()
- },
- reload: {
- label: 'Reload',
- accelerator: 'CmdOrCtrl+R',
- nonNativeMacOSRole: true,
- windowMethod: (w: BaseWindow) => {
- if (w instanceof BrowserWindow) {
- w.reload();
- }
- }
- },
- resetzoom: {
- label: 'Actual Size',
- accelerator: 'CommandOrControl+0',
- nonNativeMacOSRole: true,
- webContentsMethod: (webContents: WebContents) => {
- webContents.zoomLevel = 0;
- }
- },
- selectall: {
- label: 'Select All',
- accelerator: 'CommandOrControl+A',
- webContentsMethod: wc => wc.selectAll()
- },
- services: {
- label: 'Services'
- },
- recentdocuments: {
- label: 'Open Recent'
- },
- clearrecentdocuments: {
- label: 'Clear Menu'
- },
- showsubstitutions: {
- label: 'Show Substitutions'
- },
- togglesmartquotes: {
- label: 'Smart Quotes'
- },
- togglesmartdashes: {
- label: 'Smart Dashes'
- },
- toggletextreplacement: {
- label: 'Text Replacement'
- },
- startspeaking: {
- label: 'Start Speaking'
- },
- stopspeaking: {
- label: 'Stop Speaking'
- },
- toggledevtools: {
- label: 'Toggle Developer Tools',
- accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
- nonNativeMacOSRole: true,
- webContentsMethod: wc => {
- const bw = wc.getOwnerBrowserWindow();
- if (bw) bw.webContents.toggleDevTools();
- }
- },
- togglefullscreen: {
- label: 'Toggle Full Screen',
- accelerator: isMac ? 'Control+Command+F' : 'F11',
- windowMethod: (window: BaseWindow) => {
- window.setFullScreen(!window.isFullScreen());
- }
- },
- undo: {
- label: 'Undo',
- accelerator: 'CommandOrControl+Z',
- webContentsMethod: wc => wc.undo()
- },
- unhide: {
- label: 'Show All'
- },
- window: {
- label: 'Window'
- },
- zoom: {
- label: 'Zoom'
- },
- zoomin: {
- label: 'Zoom In',
- accelerator: 'CommandOrControl+Plus',
- nonNativeMacOSRole: true,
- webContentsMethod: (webContents: WebContents) => {
- webContents.zoomLevel += 0.5;
- }
- },
- zoomout: {
- label: 'Zoom Out',
- accelerator: 'CommandOrControl+-',
- nonNativeMacOSRole: true,
- webContentsMethod: (webContents: WebContents) => {
- webContents.zoomLevel -= 0.5;
- }
- },
- togglespellchecker: {
- label: 'Check Spelling While Typing',
- get checked () {
- const wc = webContents.getFocusedWebContents();
- const ses = wc ? wc.session : session.defaultSession;
- return ses.spellCheckerEnabled;
- },
- nonNativeMacOSRole: true,
- webContentsMethod: (wc: WebContents) => {
- const ses = wc ? wc.session : session.defaultSession;
- ses.spellCheckerEnabled = !ses.spellCheckerEnabled;
- }
- },
- // App submenu should be used for Mac only
- appmenu: {
- get label () {
- return app.name;
- },
- submenu: [
- { role: 'about' },
- { type: 'separator' },
- { role: 'services' },
- { type: 'separator' },
- { role: 'hide' },
- { role: 'hideOthers' },
- { role: 'unhide' },
- { type: 'separator' },
- { role: 'quit' }
- ]
- },
- // File submenu
- filemenu: {
- label: 'File',
- submenu: [
- isMac ? { role: 'close' } : { role: 'quit' }
- ]
- },
- // Edit submenu
- editmenu: {
- label: 'Edit',
- submenu: [
- { role: 'undo' },
- { role: 'redo' },
- { type: 'separator' },
- { role: 'cut' },
- { role: 'copy' },
- { role: 'paste' },
- ...(isMac
- ? [
- { role: 'pasteAndMatchStyle' },
- { role: 'delete' },
- { role: 'selectAll' },
- { type: 'separator' },
- {
- label: 'Substitutions',
- submenu: [
- { role: 'showSubstitutions' },
- { type: 'separator' },
- { role: 'toggleSmartQuotes' },
- { role: 'toggleSmartDashes' },
- { role: 'toggleTextReplacement' }
- ]
- },
- {
- label: 'Speech',
- submenu: [
- { role: 'startSpeaking' },
- { role: 'stopSpeaking' }
- ]
- }
- ] as MenuItemConstructorOptions[]
- : [
- { role: 'delete' },
- { type: 'separator' },
- { role: 'selectAll' }
- ] as MenuItemConstructorOptions[])
- ]
- },
- // View submenu
- viewmenu: {
- label: 'View',
- submenu: [
- { role: 'reload' },
- { role: 'forceReload' },
- { role: 'toggleDevTools' },
- { type: 'separator' },
- { role: 'resetZoom' },
- { role: 'zoomIn' },
- { role: 'zoomOut' },
- { type: 'separator' },
- { role: 'togglefullscreen' }
- ]
- },
- // Window submenu
- windowmenu: {
- label: 'Window',
- submenu: [
- { role: 'minimize' },
- { role: 'zoom' },
- ...(isMac
- ? [
- { type: 'separator' },
- { role: 'front' }
- ] as MenuItemConstructorOptions[]
- : [
- { role: 'close' }
- ] as MenuItemConstructorOptions[])
- ]
- },
- // Share submenu
- sharemenu: {
- label: 'Share',
- submenu: []
- }
- };
- const hasRole = (role: keyof typeof roleList) => {
- return Object.hasOwn(roleList, role);
- };
- const canExecuteRole = (role: keyof typeof roleList) => {
- if (!hasRole(role)) return false;
- if (!isMac) return true;
- // macOS handles all roles natively except for a few
- return roleList[role].nonNativeMacOSRole;
- };
- export function getDefaultType (role: RoleId) {
- if (shouldOverrideCheckStatus(role)) return 'checkbox';
- return 'normal';
- }
- export function getDefaultLabel (role: RoleId) {
- return hasRole(role) ? roleList[role].label : '';
- }
- export function getCheckStatus (role: RoleId) {
- if (hasRole(role)) return roleList[role].checked;
- }
- export function shouldOverrideCheckStatus (role: RoleId) {
- return hasRole(role) && Object.hasOwn(roleList[role], 'checked');
- }
- export function getDefaultAccelerator (role: RoleId) {
- if (hasRole(role)) return roleList[role].accelerator;
- }
- export function shouldRegisterAccelerator (role: RoleId) {
- const hasRoleRegister = hasRole(role) && roleList[role].registerAccelerator !== undefined;
- return hasRoleRegister ? roleList[role].registerAccelerator : true;
- }
- export function getDefaultSubmenu (role: RoleId) {
- if (!hasRole(role)) return;
- let { submenu } = roleList[role];
- // remove null items from within the submenu
- if (Array.isArray(submenu)) {
- submenu = submenu.filter((item) => item != null);
- }
- return submenu;
- }
- export function execute (role: RoleId, focusedWindow: BaseWindow, focusedWebContents: WebContents) {
- if (!canExecuteRole(role)) return false;
- const { appMethod, webContentsMethod, windowMethod } = roleList[role];
- if (appMethod) {
- appMethod();
- return true;
- }
- if (windowMethod && focusedWindow != null) {
- windowMethod(focusedWindow);
- return true;
- }
- if (webContentsMethod && focusedWebContents != null) {
- webContentsMethod(focusedWebContents);
- return true;
- }
- return false;
- }
|