HelloElectron—键盘与快捷键绑定(五)
Electron官方教程中给出了键盘快捷键 的绑定方式,同时Demo中也给出了如何实现响应Electron的键盘事件以及,监听渲染器进程中捕获的键盘事件。本篇文章将集成键盘快捷键,以及自定义一个简单的菜单。
本教程相关代码可以参考此提交:键盘与快捷键绑定 · Jakentop/plantuml-editor@1f6b22e (github.com)
通过菜单绑定键盘快捷键
菜单即Electron的顶部菜单栏,同时Electron文档中也提供了一个MenuApi的文档,通过这篇文档我们在结合官方给出的示例即可以完成键盘事件的绑定了。
首先我们先梳理下需要实现的功能:在Main进程中点击某个Menu的某一项,随后触发一次IPC通信(由主进程向渲染器进程发送事件,渲染器提供回调)。在渲染器的回调用调用update方法查询UML图像(update方法则是通过第三篇的方式,想主进程发送IPC通信并回调)
这张图展示了我们需要实现的,整个通信流程。
创建菜单
菜单中提供了一系列功能,来定制化Electron的菜单界面,同时即使在主进程创建后也允许通过IPC有渲染器进程触发改动菜单的界面。这里我们主要使用了一个简单的Demo,对于详细的参数配置建议大家可以看官方的API文档
// main/main.window.ts
export async function createWindow() {
// ...其他创建窗口代码
initMenu(win)
}
function initMenu(window: BrowserWindow): Menu {
const menu = new Menu()
menu.append(new MenuItem({
label: '操作',
submenu: [
{
label: '查询',
click: () => window.webContents.send('vevent:uml:update'),
accelerator: process.platform === 'darwin' ? 'Cmd+Enter' : 'Ctrl+Enter',
},
],
}))
Menu.setApplicationMenu(menu)
return menu
}
// preload/index.ts
contextBridge.exposeInMainWorld('electronAPI', <IElectronAPI>{
// 在ElectronAPI中添加此方法,注意需要同步更新IElectronAPI的方法签名
onVEventUmlUpdate: callback => ipcRenderer.on('vevent:uml:update', callback),
})
// render/views/WorkSpace.vue
// 注册Ipc给的事件(通过调用preload中创建的对象)
window.electronAPI.onVEventUmlUpdate(() => {
update()
})
main.window.ts
此文件是项目模板中提供的,主要定义了主进程中的window窗口的相关配置等,因此我们把菜单的创建放在该方法中
MenuItem
类是菜单项,用来配置具体有哪些菜单label
是一个公有参数,用来标识这个菜单的标签名称是什么submenu
子菜单的意思,理论上可以写多个Menu的递归click
点击这个菜单Item的时候会触发的事件,如果有这个参数时role
参数无效accelerator
此参数代表这个item可以用什么键盘事件触发process.planform
这是Nodejs的一个全局变量,用来标识当前运行的系统环境
preload.ts
我们需要注册一个回调给render方法去实现,这样才可以让主进程给渲染器进程发送消息,因此我们需要在预加载脚本中添加一个回调(为什么这样做可以参考第三篇内容)。
ipcRenderer.on
在渲染器进程中注册一个事件,可以理解成主进程会发送一个事件过来。
一个无法监听到的问题
在完成上述操作后,我们就可以通过点击菜单或者选择Ctrl+Enter来触发主进程向渲染器进程发送的事件,但是当我们的焦点在MonacoEditor的时候,就会发现主进程并没与监听到键盘事件。 这种场景下是无法触发的,键盘快捷键 | Electron (electronjs.org)可以参考这篇文章给出的解决方案,实际如果事件被render捕获了,他就不会被electron监听到。为此Electron提供了一种方案event-before-input-event只需要在webContents中监听这个事件,他就会在触发Render进程中的事件前,在Electron中发生回调。因此我们只需要按照官方文档中提供的参数配置即可。
添加before-input-event
事件
一下代码是在win对象上提供了一个事件监听的回调,即当有before-input-event
事件的时候会拦截并执行响应的回调
export async function createWindow() {
// 拦截渲染器的键盘输入事件
win.webContents.on('before-input-event', (_event, input) => {
if (input.type === 'keyUp' && input.control && input.key.toLowerCase() === 'enter'){
win.webContents.send('vevent:uml:update')
// event.preventDefault()
}
})
}
- 通过
input.type
我们可以知道该事件拦截的类型 - 同时我们判断是否按下了
control
以及enter
- 注意根据文档,enter并没有一个专门的变量来表示,因此我们需要使用
key.toLowerCase()
的形式 - 调用
toLowerCase()
的好处在于防止某些特殊版本中会返回Enter或者enter两种类型的key名称 - 注意:此操作一定是在渲染器中的keyUp、keyDown事件之前触发的。因此可以通过
event.preventDefault()
的方式阻止渲染器接收到该事件! - 因此在这里我注释了
event.preventDefault()
方法,大家可以尝试下如果将这个注释去掉,并且去掉if条件中的input.type === 'keyUp'
此时MonacoEditor的换行功能就无效了
一个历史问题
在之前,我再WorkSpace.vue中的组件上添加了一个v-on事件,如下图所示但是实际上他是不生效的 这是因为这种方式的注册事件只有当该元素支持获取焦点时才会有效,由于div标签是无法获取焦点的,因此这里的事件必然不会生效。对于想了解如何在JS中监听全局事件的小伙伴可以参考这篇文章。
其实在这里我们完全可以直接通过在渲染器中监听全局事件,当监听到按键后直接给主进程发送消息即可。当然在这里是为了演示Electron的Menu相关功能。
转载自:https://juejin.cn/post/7140201274942685197