《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實戰》中文版!


分享到:


相關文章: