Electron自定义顶部菜单、右击菜单、绑定快捷键
前言
Electron的菜单模块一般包含两项:Menu(菜单)和MenuItem(菜单项);
目前有两种方式自定义菜单:
- 构建模板,使用Menu.buildFromTemplate(template)载入模板,在本文中顶部菜单采用该方式;
- 构建菜单项,使用const menu = new Menu() 和 menu.append()的方式添加菜单,在本文中右键菜单采用该方式;
绑定快捷键会在每种菜单中都有呈现;学习当中遇到的一些坑,也会在本文中呈现;
自定义顶部菜单
- 在项目根目录下创建文件/main/menu.js,具体涉及
- 定义模板内容,
- 载入模板:Menu.buildFromTemplate(template)
- 设置应用菜单:Menu.setApplicationMenu(menu)
const { Menu, BrowserWindow } = require('electron')
const path = require('path')
//创建菜单集合
let template = [
{
label: '文件(F)',
submenu: [
{
label: '新建文件',
accelerator: 'ctrl+n', //绑定快捷键
click: () => {
console.log('代开文件')
}
},
{
label: '新建窗口',
accelerator: 'ctrl+shift+n',
click: () => { //绑定事件
newWindow = new BrowserWindow({
width: 500,
height: 300,
//主题渲染内容
webPreferences: {
nodeIntegration: true, //集成node环境
}
})
newWindow.loadFile('green.html')
newWindow.on('closed', () => {
newWindow = null
})
}
}
]
},
{
label: '帮助(H)',
submenu: [
{
label: '更多',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://electronjs.org')
}
}
]
}
]
//载入模板
const menu = Menu.buildFromTemplate(template)
//主进程设置应用菜单
Menu.setApplicationMenu(menu)
- 在主进程文件main.js中引入menu.js
//主进程,引入主进程模块
const { app, BrowserView, BrowserWindow, dialog, ipcMain } = require('electron');
const path = require("path");
const createView = () => {
var view = new BrowserView();
//嵌入网页
view.setBackgroundColor('#000000');
view.setBounds({ x: 0, y: 120, width: 1000, height: 600 })
view.webContents.loadURL('https://www.baidu.com')
}
const createWindow = () => {
var mainWindow = new BrowserWindow({
width: 1000,
height: 800,
webPreferences: {
nodeIntegration: true, //在渲染进程中开启nodejs
// webSecurity: false, //禁用同源策略,测试网站使用
enableRemoteModule: true, //开启 remote模块, 可在渲染进程中使用部分主进程模块
// allowRunningInsecureContent: false
}
});
//开启调试模式
mainWindow.webContents.openDevTools();
//引入自定义的顶部菜单
require(path.join(__dirname, './main/menu.js'))
//打开渲染进程文件
mainWindow.loadFile(path.join(__dirname, 'demo4.html'));
}
app.on('ready', createWindow)
//监听窗口关闭的事件,关闭的时候退出应用,MacOS需要排除(因为MacOS的关闭不是真正意义的关闭,进程还在)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
})
//监听重建窗口, MacOS中点击dock中的应用图标的时候需要重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
自定义右键菜单
- 只显示主要代码demo5.js
// 自定义右键菜单
const { remote, clipboard } = require('electron')
const { Menu, MenuItem } = remote
const menu = new Menu()
//添加菜单项
menu.append(
//如果role为copy,click就不起作用了
new MenuItem({
label: '复制',
type: 'normal',
role: 'copy', //为菜单项指定了role,就不能使用click自定义解释该行为;而是使用role 提供的原生体验
accelerator: 'ctrl+c', //绑定快捷键
})
)
menu.append(
new MenuItem({
label: '粘贴',
accelerator: 'ctrl+v',
click: function () {
clipboard.writeText('demo5-ceshi', 'selection')
console.log(clipboard.readText('selection'))
}
})
)
menu.append(
new MenuItem({
type: 'separator'
})
)
menu.append(
new MenuItem({
label: '默认',
type: 'checkbox',
checked: true
})
)
window.addEventListener('contextmenu', (e) => {
e.preventDefault();
menu.popup({
window: remote.getCurrentWindow()
})
}, false)
- 辅助代码渲染进程demo5.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>右键菜单</title>
</head>
<body>
<div>
<h2>自定义右键菜单、绑定快捷键(第二种方式)</h2>
</div>
<script src="./renderer/demo5.js"></script>
</body>
</html>
- 主进程main.js加载渲染进程demo5.html
const createWindow = () => {
var mainWindow = new BrowserWindow({
width: 1000,
height: 800,
webPreferences: {
nodeIntegration: true, //在渲染进程中开启nodejs
// webSecurity: false, //禁用同源策略,测试网站使用
enableRemoteModule: true, //开启 remote模块, 可在渲染进程中使用部分主进程模块
// allowRunningInsecureContent: false
}
});
//开启调试模式
mainWindow.webContents.openDevTools();
//打开渲染进程文件
mainWindow.loadFile(path.join(__dirname, 'demo5.html'));
}
需要注意到的坑
- 入坑原因:设置role之后click不起作用了;
MenuItem菜单项中的role和click一般不建议一起使用;推荐使用role自带的原生行为体验;
可以通过角色来为menu添加预定义行为。
最好给任何一个菜单指定 role去匹配一个标准角色, 而不是尝试在 click 函数中手动实现该行为。 内置的 role 行为将提供最佳的原生体验。
使用 role 时, label 和 accelerator 值是可选的, 并为每个平台,将默认为适当值。
Every menu item must have either a role, label, or in the case of a separator a type.