《Electron实战》中文版:构建应用程序菜单及上下文菜单

本文为《Electron实战》中文版第7章内容,私聊留言得完整版本。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

本章包括

  • 使用Electron的Menu和MenuItem模块创建菜单
  • 通过模版构建菜单
  • 为目标操作系统自定义菜单
  • 为菜单项分配通用操作系统角色
  • 使菜单项具有自定义的、特定于应用程序的功能
  • 为界面不同部分创建自定义的上下文菜单

在基于浏览器的应用程序中,开发人员只能访问应用程序窗口的可见区域,不能向浏览器的工具栏或菜单栏添加控件,应用程序的整个功能界面必须位于窗口内。开发人员同样在窗口内面临一些限制,不能修改右键单击界面时出现的上下文菜单,为每个选项和命令找到一个合适位置可能是一个挑战。而Electron则使开发人员能够在浏览器窗口之外添加功能,例如当用户右键单击界面组件时出现的自定义应用程序和上下文菜单。

在本章中,我们将探讨如何在Fire Sale中创建和配置这些菜单。我们将用自己的菜单替换Electron提供的默认菜单,并在菜单中展示常见的操作系统功能。我们将给菜单项分配快捷方式,以便在应用程序的任何位置都能触发它们。在实现了基本的菜单功能之后,我们将添加我们自己应用程序所特定的菜单项——特别是,能够从文件系统中打开Markdown文件,在界面左侧窗格中显示其内容,并在右侧窗格中将其内容呈现为HTML的功能。最后,当用户在左侧窗格中单击右键时,我们创建一个自定义上下文菜单,其中包含常见的文本操作任务(剪切、复制和粘贴,如图7.1所示)。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.1 本章,我们会构建能触发界面功能的自定义菜单项。

在前几章中,我们已经在Fire Sale中实现了一个菜单。那么为什么现在还要定制呢?开发人员可以覆盖Electron的默认菜单,但是他们需要从头开始构建。在本章,我们会还原大多数桌面应用程序常见的基本功能。在打下基础之后,我们再使用自定义功能对其进行扩展。例如,用户可以通过菜单保存当前活动的文件,以及将HTML导出到单独的文件。除了能够从应用程序菜单中访问此功能外,用户还可以使用快捷键来触发菜单项。在本章,我们会构建一个Fire Sale菜单,其结构如图7.2所示。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.2 Fire Sale菜单结构。

7.1. 仿制并替换默认菜单

首先创建一个名为./app/application.js的新文件,到本章结束时,这个文件将会变得很大,因此我们现在就把它分拆成独立的文件。我们先从复制、粘贴菜单开始。

代码清单7.1 :创建带有复制和粘贴功能的编辑菜单:./app/application-menu.js

<code>const { app, BrowserWindow, Menu, shell } = require('electron');const mainProcess = require('./main');const template = [    {        label: 'Edit',        submenu: [            {                label: 'Copy',                accelerator: 'CommandOrControl+C',                role: 'copy',            },            {                label: 'Paste',                accelerator: 'CommandOrControl+V',                role: 'paste',            },        ]    }];module.exports = Menu.buildFromTemplate(template); /<code>

下一步,当应用程序触发ready事件时设置菜单。

代码清单7.2 :从应用程序文件中加载菜单:./app/main.js

<code>const { app, BrowserWindow, dialog, Menu } = require('electron');const applicationMenu = require('./application-menu');const fs = require('fs');const windows = new Set();const openFiles = new Map();app.on('ready', () => {    Menu.setApplicationMenu(applicationMenu);    createWindow();});// ... Additional methods below .../<code>

Electron有Menu和MenuItem模块可用于构建菜单。理论上,我们可以只用MenuItem就可以构建一个菜单,但这种方法可能很繁琐,且容易出错。为了方便起见,Menu模块提供了buildFromTemplate()方法,该方法接受一个常规的JavaScript对象数组。Electron会在内部根据你所提供的数组来创建菜单项。

7.1.1. macOS与缺少编辑菜单的情况

如果在Windows中启动应用程序,应该会看到一个“编辑”菜单,不出意外的话,其中会有两个菜单项:复制和粘贴。但如果你在macOS上测试应用程序,结果会有些不同,如图7.3所示。

在macOS中,菜单名会是“Electron”而不是“编辑”,因为macOS上的第一个菜单总是“应用程序”菜单。要在Electron中解决这个问题,我们需要将”编辑”菜单以及后续所有菜单项都向后移动一个位置(如清单7.3和图7.4所示),以便为本章后续要实现的应用程序菜单腾出空间。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.3 macOS使用第一个菜单作为“应用程序”菜单,但这并不是所预期的效果。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.4 通过将所有菜单向后移动一个位置,“编辑”菜单就能正常显示了。很快,我们就会实现一个与原生macOS应用程序行为类似的“应用程序”菜单


代码清单7.3 :将macOS原有菜单项前移:./app/application-menu.js

<code>const { app, BrowserWindow, Menu, shell } = require('electron');const mainProcess = require('./main');const template = [    // ... Menu template from the last section. ...];if (process.platform === 'darwin') {    const name = 'Fire Sale';    template.unshift({ label: name });}module.exports = Menu.buildFromTemplate(template);/<code>

使用Electron构建应用程序的好处之一是,开发人员可以针对macOS、Windows和Linux使用同一套代码。需要注意的是,开发人员在编写代码时应该考虑每种受支持操作系统的特性。幸运的是,Node提供了process对象,该对象提供了一些能对应用程序运行环境进行自我检测的属性、方法和事件。

process.platform返回当前应用程序运行的平台名称,在写这篇文章的时候,process.platform支持这五个平台:darwin、freebsd、linux、sunos或win32,其中Darwin是UNIX操作系统,macOS正是基于该系统开发的。我们可以在运行时通过检测process.platform是否等于darwin来调整菜单。如果是,那么应用程序正运行在macOS上,所有菜单项应该向右移动一个位置。

为了以正确顺序显示菜单而做的一切额外工作,所得到的效果,正是图7.4所示。另外我们不需要通过“Edit”菜单来特别实现,即可获得对“听写”和“表情&符号”的支持。

7.1.2. 替换Electron默认菜单的隐性成本

Electron提供了一个默认菜单,但这是一个要么都有或要么全无的功能,当我们要替换菜单时,我们就要放弃它所有原始功能。我们不仅会丢失一些菜单项,而且还同时会丢失快捷键。比如在macOS上使用Command-X,或者在Windows和Linux上使用Control-X快捷键,从左侧窗格中剪切文本的。在macOS或Windows上分别使用Command-A或Control-A命令选择所有文本的,以及Command-Z或Control-Z命令执行撤销的,都将失效。如果你使用的是macOS,请尝试使用Command-Q退出应用程序,如果使用两次都无效,那么我们就失去了在macOS中隐藏此应用程序或其他应用程序的功能。在所有操作系统上,我们失去了“撤消”和“重做”功能、“最小化”和“关闭”窗口以及选择文本的能力,剩下的就只有复制和粘贴的功能,如图7.5所示,这是因为为我们认为的将其添加回了自定义菜单中。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.5 在Electron的内置菜单中实现的编辑和窗口菜单。

是否将这些功能添加回应用程序取决于开发人员,如果他们想在应用程序中省略这些功能,他们完全可以这样做。你的最初想法可能是,重新实现这些功能就像重新造轮子。幸运的是,Electron可以很容易地创建可以执行常用操作系统任务的菜单项。创建新菜单项时,可以设置一些选项。到目前为止,我们已经接触了label和type选项,你可能注意到我们这前面每个清单中的第三个菜单项的type选项都设置成了separator。

为了构建Electron菜单,我们先从实现“编辑”和“窗口”菜单开始,实现方式类似于在Electron默认菜单中定义它们一样,如图7.5所示。

代码清单7.4 :“编辑”菜单模版:./app/application-menu.js

<code>const template = [    {        label: 'Edit',        submenu: [            {                label: 'Undo',                accelerator: 'CommandOrControl+Z',                role: 'undo',            },            {                label: 'Redo',                accelerator: 'Shift+CommandOrControl+Z',                role: 'redo',            },            { type: 'separator' },            {                label: 'Cut',                accelerator: 'CommandOrControl+X',                role: 'cut',            },            {                label: 'Copy',                accelerator: 'CommandOrControl+C',                role: 'copy',            },            {                label: 'Paste',                accelerator: 'CommandOrControl+V',                role: 'paste',            },            {                label: 'Select All',                accelerator: 'CommandOrControl+A',                role: 'selectall',            },        ],    },    {        label: 'Window',        submenu: [            {                label: 'Minimize',                accelerator: 'CommandOrControl+M',                role: 'minimize',            },            {                label: 'Close',                accelerator: 'CommandOrControl+W',                role: 'close',            },        ],    },];if (process.platform === 'darwin') {    const name = app.getName();    template.unshift({ label: name });}module.exports = Menu.buildFromTemplate(template);/<code>

7.1.4. 定义菜单项的角色和快捷键

你可能已经注意到,到目前为止添加的所有菜单项都有一个特殊的role属性。这个设置很重要,因为像复制和粘贴这样的功能很难手工实现。菜单项可以具有一个role属性,它与操作系统向应用程序提供的内置功能相关联。在Windows、Linux和macOS上,菜单项的role属性可以设置为以下任意一种:

  • undo
  • redo
  • cut
  • copy
  • paste
  • selectall
  • minimize
  • close

这些角色与我们在用自己的菜单替换默认菜单时所丢失的许多功能重叠。添加具有这些角色的菜单项可以将这些功能恢复到菜单中,但是却不会恢复许多用户已经习惯的快捷键。

Electron提供了一个名为accelerator的额外属性,用于定义可以触发菜单项功能的快捷键。在创建菜单项时,可以将accelerator属性设置为符合Electron约定的字符串。清单7.5是一个添加复制功能的菜单项。

码清单7.5 :使用role和accelerator属性:./app/application-menu.js

<code>const { app, BrowserWindow, Menu, MenuItem, shell } = require('electron');const copyMenuItem = new MenuItem({    label: 'Copy',    accelerator: 'CommandOrControl+C',    role: 'copy'});/<code>

在Windows和Linux上,通常在快捷键前面加上Control键。在macOS上,常使用Command键来达到类似目的。一般情况下,Command键在Linux和Windows上也是不可用的,不需要在菜单项中依赖process.platform来做条件判断,Electron提供了更方便的CommandOrControl,它在macOS上,会将快捷键绑定到Command键,在Windows和Linux上,Electron会使用Control键。另外,Electron还提供了Command、Control和CommandOrControl的简写别名,分别是Cmd、Ctrl和CmdOrCtrl。

7.1.5. macOS上还原应用程序菜单

当Electron运行时,它将模板编译成MenuItems集合,并相应地将其设置为应用程序的菜单。还原了复制和粘贴等常用操作的快捷键,应用程序在Windows和Linux中的行为也达到了预期。然而,在macOS中,应用程序仍然缺少一些重要功能,至少缺少退出应用程序的功能。macOS中的标准应用程序菜单的结构如图7.6所示。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.6 macOS应用程序的菜单结构。

当运行于macOS上时,Electron提供了一组额外的角色,可以轻松还原大多数Mac应用程序的常用菜单。这些额外的角色有:

  • about
  • hide
  • hideothers
  • unhide
  • front
  • window
  • help
  • services

Electron提供的默认应用程序菜单的一些菜单项,包括应用程序的“关于”面板、macOS提供的公开服务、隐藏应用程序、隐藏其他所有应用程序以及退出应用程序,如图7.7所示。


《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.7 应用程序菜单中的菜单项使用Electron的特殊角色,可以让你在不用重新造轮子的情况下使用操作系统功能

实现应用程序菜单类似于实现“编辑”和“窗口”菜单。定义快捷方式时,Command比CommandOrControl更可取,因为该菜单只出现在macOS上。此外,我们使用模板字符串来给“关于”、“隐藏”和“退出”菜单加上应用程序名称,因为习惯上在这些菜单项中应该包含应用程序的名称。

代码清单7.6 :macOS的应用程序菜单:./app/application-menu.js

<code>if (process.platform === 'darwin') {    const name = 'Fire Sale';    template.unshift({        label: name,        submenu: [            {                label: `About ${name}`,                role: 'about',            },            { type: 'separator' },            {                label: 'Services',                role: 'services',                submenu: [],            },            { type: 'separator' },            {                label: `Hide ${name}`,                accelerator: 'Command+H',                role: 'hide',            },            {                label: 'Hide Others',                accelerator: 'Command+Alt+H',                role: 'hideothers',            },            {                label: 'Show All',                role: 'unhide',            },            { type: 'separator' },            {                label: `Quit ${name}`,                accelerator: 'Command+Q',                click() { app.quit(); },            },        ],    });}我们的应用程序现在几乎拥有macOS上原生应用程序的所有功能,但是我们仍然需要处理一些细微的差异。在macOS上,窗口菜单有一些额外的菜单项——最明显的就是“Bring All to Front”菜单项,它可以将当前应用程序所有窗口移到最前面。此外,macOS特有的window角色还具有从“窗口”菜单中关闭和最小化当前窗口的功能,还列出了应用程序所有窗口,以及可以将它们显示在前面的功能。在不支持此角色的平台上将会忽略此角色。代码清单7.7 :合并应用程序“编辑”、“窗口”菜单:./app/application-menu.jsconst template = [    {        label: 'Edit',        submenu: [            // "Edit" menu shown in Listing 7.4        ],    },    {        label: 'Window',        role: 'window',        submenu: [            // "Window" menu shown in Listing 7.4        ],    },];if (process.platform === 'darwin') {    const name = app.getName();    template.unshift({        label: name,        submenu: [            // #Application menu shown in Listing 7.6        ],    });    const windowMenu = template.find(item => item.label === 'Window');    windowMenu.role = 'window';    windowMenu.submenu.push(        { type: 'separator' },        {            label: 'Bring All to Front',            role: 'front',        }    );}/<code>
《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.8 macOS中的“窗口”菜单可以让你查看应用程序当前打开的所有窗口。

7.1.8. 添加帮助菜单

无论什么平台,添加“帮助”菜单都是一个很好的选择,尤其在macOS上这样做还有一个额外的好处。即使你的应用程序还没有任何文档或技术支持提供时,内置的“帮助”菜单可以让用户搜索应用程序的菜单项,如图7.9所示。这在大多数macOS应用程序中都是有效的,对于快速检索那些嵌套很深的菜单来说非常有用。你可以随时按下Command-Shift-?组合键来访问菜单的搜索功能。

要将“帮助”菜单添加到应用程序中(如图7.10所示的结构),请添加一个具有help角色的额外菜单和一个包含该菜单项的子菜单。你必须提供一个数组作为子菜单,如清单7.8所示,即便它是空的。现在我们还可以添加激活开发者工具的功能,当然有些应用程序可能希望在发布之前删除此功能。但像Atom、Nylas Mail和Visual Studio Code这样的流行应用程序选择了保留这个功能。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.9 在macOS中,“帮助”菜单可以让用户搜索应用程序的菜单项。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.10 如清单7.8我们构建的“帮助”菜单结构。

代码清单7.8 :创建“帮助”菜单:./app/application-menu.js

<code>const template = [    // "Edit" and "Window" menus defined in Listing 7.7    {        label: 'Help',        role: 'help',        submenu: [            {                label: 'Visit Website',                click() { /* To be implemented */ }            },            {                label: 'Toggle Developer Tools',                click(item, focusedWindow) {                    if (focusedWindow)                         focusedWindow.webContents.toggleDevTools();                }            }        ],    }];/<code>

click()方法有三个可选参数:菜单项本身、当前处于焦点的BrowserWindow实例和事件对象。在清单7.8中,我们使用了第二个参数——当前焦点窗口——来确定哪个窗口应该切换开发者工具。

7.2. 添加应用程序的菜单功能

通过这些工作来还原我们最初可以自由获得的功能,只有当我们可以使用模板来添加自定义功能时才是值得的。用户通常希望能够从“文件”菜单中打开和保存文件,Fire Sale目前缺少这个功能。我们现在已经可以使用界面中的“Open File”按钮从文件系统中选择并打开一个Markdown文件,下一步(如图7.11所示)是通过修改“文件”菜单,添加“新建文件”、“打开文件”、“保存文件”、和“导出HTML”等菜单项以及对应的快捷键来触发相应的操作。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.11 本章,我们会添加一个本应用程序的“文件”菜单功能。

当用户单击“打开文件”菜单项或按下相应的快捷键时,菜单项会触发主进程中的openFile()函数,与从界面上按下按钮触发的函数相同。单击“新建文件”将调用主进程的createWindow()函数。我们先在模板中添加“文件”菜单开始,将图7.11中所示的每个功能都作为菜单项添加到模板的submenu数组中。

然而,针对保存或导出文件功能,我们分别需要Markdown窗格和HTML窗格的当前内容。如果当前有打开的文件,我们还需要该文件的名称,由于主进程无法访问该信息,因此需要我们向当前焦点窗口发送一条消息,该窗口应该为我们收集这些信息,并触发与用户单击界面按钮时相同的功能。

代码清单7.9 :自定义菜单功能:./app/application-menu.js

<code>const template = [    {        label: 'File',        submenu: [            {                label: 'New File',                accelerator: 'CommandOrControl+N',                click() {                    mainProcess.createWindow();                }            },            {                label: 'Open File',                accelerator: 'CommandOrControl+O',                click(item, focusedWindow) {                    mainProcess.getFileFromUser(focusedWindow);                },            },            {                label: 'Save File',                accelerator: 'CommandOrControl+S',                click(item, focusedWindow) {                    focusedWindow.webContents.send('save-markdown');                },            },            {                label: 'Export HTML',                accelerator: 'Shift+CommandOrControl+S',                click(item, focusedWindow) {                    focusedWindow.webContents.send('save-html');                },            },        ],    },    // "Edit", "Window", and "Help" menus are defined here as well.];/<code>

向焦点窗口发送消息是成功的一半,我们仍然需要配置渲染进程来监听这些消息并进行相应地操作。我们先设置一个IPC监听器来负责接收这些消息,并在接收到消息时调用现有的保存和导出功能。

代码清单7.10 :给渲染进程添加IPC监听器:./app/renderer.js

<code>ipcRenderer.on('save-markdown', () => {    mainProcess.saveMarkdown(currentWindow, filePath, markdownView.value);});ipcRenderer.on('save-html', () => {    mainProcess.saveHtml(currentWindow, filePath, markdownView.value);});/<code>

7.2.1. 处理没有焦点窗口的情况

在Windows和Linux中,当所有窗口都关闭时,应用程序就退出。在macOS上,即使所有窗口都关闭了,应用程序仍然还在运行,单击图标时会打开一个新窗口。但在某些情况下,用户可能会选择我们刚刚实现的三个菜单项中的一个,而此时的焦点窗口是undefined。在第9章,我们会介绍如何启用和禁用菜单项。现在,我们采用一种更简单的方法:如果用户选择“打开文件”,则打开一个新窗口;如果没有内容要保存或导出的话,则显示一条错误消息。

要在用户试图保存或导出不存在的文件时显示错误消息,我们可以使用dialog.showErrorBox()函数,它类似于dialog.showMessageBox(),但是它是专门用来显示错误消息的,并且没有那么多配置选项。

代码清单7.11 :试图保存或导出不存在的文件时给出错误提示:./app/application-menu.js

<code>const { app, dialog, Menu, MenuItem shell } = require('electron');const mainProcess = require('./main');const template = [    {        label: 'File',        submenu: [            {                label: 'New File',                accelerator: 'CommandOrControl+N',                click() {                    mainProcess.createWindow();                }            },            {                label: 'Open File',                accelerator: 'CommandOrControl+O',                click(item, focusedWindow) {                    mainProcess.getFileFromUser(focusedWindow);                },            },            {                label: 'Save File',                accelerator: 'CommandOrControl+S',                click(item, focusedWindow) {                    if (!focusedWindow) {                        return dialog.showErrorBox(                            'Cannot Save or Export',                            'There is currently no active document to save or export.'                            );                    }                    focusedWindow.webContents.send('save-markdown');                },            },            {                label: 'Export HTML',                accelerator: 'Shift+CommandOrControl+S',                click(item, focusedWindow) {                    if (!focusedWindow) {                        return dialog.showErrorBox(                            'Cannot Save or Export',                            'There is currently no active document to save or export.'                            );                }                focusedWindow.webContents.send('save-html');            },        },    ], },/<code> 

如果用户选择的是“打开文件”,并且也没有窗口来接收该命令的话,那么问题就简单了。我们只需创建一个新窗口,等窗口显示出来就触发打开文件对话框,就像窗口一直存在一样。

代码清单7.12 :当没有窗口用于显示用户打开的新文件时就创建一个新窗口:./app/application-menu.js

<code>const template = [    {        label: 'File',        submenu: [            {                label: 'Open File',                accelerator: 'CommandOrControl+O',                click(item, focusedWindow) {                    if (focusedWindow) {                        return mainProcess.getFileFromUser(focusedWindow);                    }                    const newWindow = mainProcess.createWindow();                                        newWindow.on('show', () => {                        mainProcess.getFileFromUser(newWindow);                    });                },            }, // "Save File" and "Export HTML" menus are defined here.        ],    }, // "Edit", "Window", and "Help" menus are defined here.];/<code>

首先,我们检查focusedWindow是否存在。如果有,我们希望触发之前实现的功能,并尽早从函数返回。如果没有焦点窗口,我们需要创建一个。幸运的是,在第5章中就创建了这个函数来帮助我们完成这个过程。当新窗口完成初始化后,就可以像使用现有窗口一样使用它。我们的代码现在已经可以匹配这种情况,可以继续实现其他功能。

7.3. 构建上下文菜单

在上一节,我们定义了一个菜单,并在app模块触发“ready”事件时将其设置为主进程中的应用程序菜单。应用程序一次只能有一个应用程序菜单,但是,我们可以在渲染进程中定义额外的菜单,如图7.12所示,当用户右键单击(或在某些计算机上用两个手指单击)界面的某一部位,这些菜单就会弹出。

《Electron实战》中文版:构建应用程序菜单及上下文菜单

图7.12 Electron让开发人员可以自定义DOM特定部位上的右键上下文菜单。

下一步,我们在左边markdown窗格中监听contextmenu事件。

代码清单7.13 :监听contextmenu事件:./app/renderer.js

<code>markdownView.addEventListener('contextmenu', (event) => {    event.preventDefault();    alert('One day, a context menu will go here.');});/<code>

注意,除非用户右键单击左侧窗格,否则警告不会触发。如果你想要从应用程序中的任何位置触发上下文菜单,请在window对象上而不是在DOM节点上监听。Menu模块在渲染进程中是不可用的,但是可以使用remote模块从主进程的上下文中访问它,如下面的清单所示。导入该模块,我们就可以使用Menu.buildFromTemplate()来构造一个菜单,如清单7.15所示。

代码清单7.14 :创建上下文菜单:./app/renderer.js

<code>const { remote, ipcRenderer } = require('electron');const { Menu } = remote;const path = require('path');const mainProcess = remote.require('./main.js');const currentWindow = remote.getCurrentWindow();// Our existing renderer code…const markdownContextMenu = Menu.buildFromTemplate([    { label: 'Open File', click() { mainProcess.getFileFromUser(); } },    { type: 'separator' },    { label: 'Cut', role: 'cut' },    { label: 'Copy', role: 'copy' },    { label: 'Paste', role: 'paste' },    { label: 'Select All', role: 'selectall' },]);/<code>

要触发此菜单,要使用一个新函数来替换contextmenu事件监听器,该函数将调用新创建的菜单上的popup()方法,如下所示。

代码清单7.15 :触发上下文菜单:./app/renderer.js

<code>markdownView.addEventListener('contextmenu', (event) => {    event.preventDefault();    markdownContextMenu.popup();});/<code>

popup()方法有四个参数:BrowserWindow、x、y和positioningItem。所有这些参数都是可选的,如果省略了它们,那么上下文菜单将直接显示在当前浏览器窗口中鼠标光标下,这正是我们所期望的。有了这些代码,我们就可以在Markdown窗格中触发一个上下文菜单。在向应用程序添加更多功能时,我们就向上下文菜单以及其他上下文菜单添加功能即可。本章的完整代码可以在https://github.com/electron-in-action/firesale/tree/chapter-7或附录中找到。或者,你可以从https://github.com/electron-in-action/firesale.git的GitHub代码库中进行克隆,然后切到chapter-7分支,并运行npm install来看实际效果。

总结

  • Electron可以让开发人员定制应用程序菜单和上下文菜单。
  • Electron提供了Menu和MenuItem模块用于构建菜单。
  • Menu.buildFromTemplate()函数让开发人员可以从JavaScript对象数组中构建菜单,而不必使用MenuItem构造函数。
  • Electron提供了一个内置的应用程序菜单,其中充满了合理的默认设置。覆盖这个菜单意味着我们必须替换内置的功能。
  • process.platform可以让开发人员检测他们应用程序当前所运行的操作系统。
  • macOS的第一个菜单项是一个特殊的应用程序菜单。
  • Electron为MenuItem提供了角色,可以让开发人员轻松实现常用的操作系统级别的功能。
  • MenuItem有一个click()方法,用于定义用户单击时的行为。
  • MenuItem支持一个快捷方式属性,可以让开发人员定义一个快捷键来触发它的动作。
  • Electron支持渲染进程中的contextmenu事件,该事件在用户右键单击DOM时触发。


《Electron实战》中文版!


分享到:


相關文章: