學透 Electron 自定義菜單

導語:近幾年,隨着 Electron/ NW.js 等技術的興起,也催生了一批優秀的桌面端開發者工具,好比 VSCode、微信開發者工具、飛冰(ICE) 等等。對於開發者而言,桌面端開發者工具的優點是:可視化能力、操做系統層面的 API 訪問、和良好的開發調試體驗。所以,最近準備系統性的深刻學習下 Electron 技術而且將學習的知識進行適當沉澱。本篇文章主要總結 Electron 的自定義菜單。

傳統的 Web APP 的開發基本上不會涉及到菜單,可是在 Electron 裏面它提供了對於菜單全面的控制,你能夠經過 Menu、MenuItem 模塊來建立應用所需的自定義菜單。這篇文章咱們一塊兒探討下 Electron 中有哪些菜單種類,又是如何經過代碼去自定義菜單的?git

首先,咱們一塊兒看看基本的菜單介紹,方便你們對於基本的概念有初步的認識。github

菜單介紹

Electron 裏的菜單大致上分爲三類:應用菜單、上下文菜單和 Dock 菜單(僅針對 OSX 系統)。api

這裏以微信開發者工具爲例(微信開發者工具基於 NW.js 進行開發,主要出於 Windows XP兼容性考慮),來分別介紹這幾種菜單的含義。打開微信開發者工具,能夠經過下圖,很清晰的發現3個菜單所處的位置。數組

這三種菜單的含義分別是:緩存

  • 應用菜單:應用菜單一般位於應用程序的頂部,提供了用戶可能用到的各類操做,如程序的快捷方式、經常使用的文件夾及系統命令等。
  • 上下文菜單:在應用裏面點擊右鍵看到的菜單。
  • Dock 菜單:只在 OSX 系統纔有,一般功能較少,提供特別經常使用的功能。

瞭解了菜單的基本概念後,接下來咱們一塊兒看看如何經過代碼去實現自定義菜單的功能。微信

應用菜單

首先看看應用菜單,Electron 默認會有一個標準的應用菜單,咱們一塊兒看看默認的應用菜單效果:微信開發

仔細分析下默認應用菜單包括的菜單結構以下:app

若是你但願定製應用菜單,你須要自行實現整個菜單的定義。這裏須要注意,應用菜單隻能在 Electron 的主進程中進行訪問。例如:electron

// main.js
const {
    app,
    Menu
 } = require('electron');

app.on('ready', () => {
    const appMenu = Menu.buildFromTemplate(menuTemplate);
    Menu.setApplicationMenu(appMenu);
});

這裏面重點關注 app 的 ready 這段代碼塊,應用菜單經過 Menu.setApplicationMenu 進行設置。接下來分別從菜單模板、分隔符、快捷鍵和子菜單幾個方面來系統介紹下應用菜單的內容。函數

菜單模板:

菜單的 template 是一個對象數組,每一個對象會定義一個獨立的菜單,它會顯示在應用菜單的 Bar 位置,顯示的文字經過 label 屬性進行定義。

以這段代碼爲例,咱們定義了兩個菜單,每一個菜單都包含兩個菜單項,菜單項就是咱們點擊菜單時下拉出來的內容。

const template = [
    {
        label: 'Edit App',
        submenu: [
            {
                label: 'Undo'
            },
            {
                label: 'Redo'
            }
        ]
    },
    {
        label: 'View App',
        submenu: [
            {
                label: 'Reload'
            },
            {
                label: 'Toggle Full Screen'
            }
        ]
    }
];

對應的效果:

這裏值得注意的是:對於 OSX 而言,應用菜單的第一個菜單項是應用程序的名字,會使得 Edit App 這個菜單被覆蓋掉。所以,咱們須要針對 OSX 進行特殊處理,處理的過程一般是:

if (process.platform === 'darwin') {
    template.unshift({
        label: app.getName(),
        submenu: [
            {
                label: 'Quit',
                accelerator: 'CmdOrCtrl+Q',
                click() {
                    app.quit();
                }
            }
        ]
    });
}

分隔符:

經過 type: 'separator' 能夠在兩個菜單項之間定義一個分隔符,分隔符的做用主要是將功能類似的菜單項分隔在一塊兒,便於更好的操做。

const template = [
    {
        label: 'Edit App',
        submenu: [
            {
                label: 'Undo'
            },
            {
                type: 'separator'
            },
            {
                label: 'Redo'
            }
        ]
    }
];

能夠看到,Undo 和 Redo 之間出現了一個分隔符。

接下來,咱們一塊兒瞭解下經常使用的快捷鍵和內置的 role 功能。

快捷鍵:

快捷鍵咱們平常開發過程當中用得不少,好比 Ctrl + A 全選,Ctrl + C 複製,Ctrl + V 粘貼。能夠供咱們選擇的快捷鍵有:

  • Command (簡寫Cmd)
  • Control(簡寫Ctrl)
  • CommandOrControl(簡寫CmdOrCtrl)
  • Alt
  • Option
  • AltGr
  • Shift
  • Super

咱們把上面的代碼修改一下,增長快捷鍵,快捷鍵經過 accelerator 屬性進行定義。

const template = [
    {
        label: 'Edit App',
        submenu: [
            {
                label: 'Undo',
                accelerator: 'CmdOrCtrl+Z'
            },
            {
                type: 'separator'
            },
            {
                label: 'Redo',
                accelerator: 'Shift+CmdOrCtrl+Z',
            }
        ]
    }
];

添加完快捷鍵後,可能你會問,點擊某個菜單或者某個快捷鍵後如何觸發相應的邏輯呢?這個能夠經過編寫 click() 自定義回調函數或者使用 Electron 內置的 role 進行指定。咱們將上述代碼繼續修改:

const template = [
    {
        label: 'Edit App',
        submenu: [
            {
                label: 'Undo',
                accelerator: 'CmdOrCtrl+Z',
                role: 'undo'
            },
            {
                type: 'separator'
            },
            {
                label: 'Redo',
                accelerator: 'Shift+CmdOrCtrl+Z',
                role: 'redo'
            }
        ]
    }
];

增長了 role 以後能夠發現就有對應的操做效果了,Electron 的全部內置的 role 以下:

  • undo: 撤銷
  • redo:重作
  • cut:剪切
  • copy:複製
  • paste:粘貼
  • pasteAndMatchStyle
  • selectAll:全選
  • delete:刪除
  • minimize:當前窗口最小化
  • close:關閉當前窗口
  • quit:退出應用程序
  • reload:刷新當前窗口
  • forceReload:強制刷新當前窗口,忽略緩存
  • toggleDevTools:打開或者關閉 devtool
  • togglefullscreen:進行全屏切換
  • resetZoom:重置窗口大小
  • zoomIn:放大窗口的10%.
  • zoomOut:縮小窗口的10%.

完整的 Role 能夠查看:https://electronjs.org/docs/api/menu-item#roles

子菜單:

咱們在前面的基礎上增長一個新的菜單 Sub Menu,能夠看到這個菜單裏面的菜單項新增了 submenu 屬性,經過這個屬性能夠繼續定義子菜單,此處咱們定義了 Submenu item1 和 Submenu item2。

const template = [
    {
        label: 'Edit App',
        submenu: [
            {
                label: 'Undo',
                accelerator: 'CmdOrCtrl+Z',
                role: 'undo'
            },
            {
                type: 'separator'
            },
            {
                label: 'Redo',
                accelerator: 'Shift+CmdOrCtrl+Z',
                role: 'redo'
            }
        ]
    },
    {
        label: 'Sub Menu',
        submenu: [
            {
                label: 'Submenu item',
                submenu: [
                    {
                        label: 'Submenu item1'
                    },
                    {
                        label: 'Submenu item2'
                    }
                ]
            }
        ]
    },
];

子菜單的效果以下:

到這裏,應用菜單這個最重要的內容就介紹完了,接下來咱們看看上下文菜單這個部分。

上下文菜單

上下文菜單(context menu)就是咱們一般說的右鍵菜單,文章開頭有展現效果。須要注意的是:上下文菜單,須要在渲染進程中進行實現。在渲染進程中是須要經過remote模塊調用主進程中的模塊。

實現上下文菜單很簡單,只須要監聽到 contextmenu 事件,而後將菜單展現出來便可。

//renderer.js
const { remote } = require('electron');
const { Menu } = remote;

const createContextMenu = () => {
    const contextTemplate = [
        {
            label: 'Cut',
            role: 'cut'
        },
        {
            label: 'Copy',
            role: 'copy'
        }
    ];
    const contextMenu = Menu.buildFromTemplate(contextTemplate);
    return contextMenu;
}

window.addEventListener('contextmenu', (event) => {
    event.preventDefault();
    const contextMenu = createContextMenu();
    contextMenu.popup({
        window: remote.getCurrentWindow()
    });
}, false);

Dock菜單

最後,咱們一塊兒看看 Dock 菜單,Dock 的菜單實現也是在主進程中,實現思路和前面基本相似,核心是經過 app.dock.setMenu 這個 API 進行實現的。

// main.js
const createDockMenu = () => {
    const dockTempalte = [
        {
            label: 'New Window',
            click () {
                console.log('New Window');
            }
        }, {
            label: 'New Window with Settings',
            submenu: [
                { label: 'Basic' },
                { label: 'Pro' }
            ]
        },
        {
            label: 'New Command...'
        }
    ];

    const dockMenu = Menu.buildFromTemplate(dockTempalte);
    app.dock.setMenu(dockMenu);
}

app.on('ready', function() {
    createDockMenu();
});

Dock 菜單的效果以下:

至此,這篇文章到這裏就結束了,感謝您的閱讀。後面的文章會涉及到對話框、 IPC 通訊、Electron 應用的測試、打包、發佈和自動更新等內容。


個人我的博客:https://github.com/cpselvis/b...

想學習更多幹貨內容能夠掃碼關注個人公衆號:推送頻率每週一篇

相關文章
相關標籤/搜索