6
头图

虽然我们在平时使用桌面软件时,大多数情况下都是用鼠标进行操作,一些软件提供了菜单与键盘快捷键进行操作,mac系统上比较常见一些。如果在使用软件时有对应的快捷键操作或者菜单,会让用户的使用体验更加顺畅和方便一点。本章主要对注册快捷键的几种方式进行说明以及菜单的系统差异化处理。

Menu菜单

菜单这个东西呢,一般常见于mac系统上,win的话国内比较少见,算是一种约定俗成吧。实际上快捷键和菜单方面可以说是一体的,我们可以在菜单上注册对应的键盘按键,故mac系统可以使用这个方式把快捷键一起处理了。
优点:最简洁的快捷键处理,简单方便。
缺点:只能在应用程序被聚焦时触发(焦点在程序上,不只是显示),由于win一般不使用菜单,故只能用于mac上。

import { app, dialog, Menu } from 'electron'
import config from './index'
import global from './global'
const os = require('os')
const isMac = process.platform === 'darwin'

const menuConfig = [{
  label: app.name,
  submenu: [{
    label: '关于',
    accelerator: isMac ? 'Alt+Cmd+I' : 'Alt+Shift+I',
    click: function () {
      info()
    }
  }]
}, {
  label: '设置',
  submenu: [{
    label: '快速重启',
    accelerator: 'CmdOrCtrl+F5',
    role: 'reload'
  }, {
    label: '退出',
    accelerator: 'CmdOrCtrl+Q',
    role: 'quit'
  }]
}, {
  label: '开发者设置',
  submenu: [{
    label: '切换到开发者模式',
    accelerator: 'CmdOrCtrl+I',
    role: 'toggledevtools'
  }]
}]

function info() {
  dialog.showMessageBox({
    title: '关于',
    type: 'info',
    message: 'vue-cli-electron',
    // detail: `版本信息:\nelectron版本:${process.versions.electron}\n当前系统:${os.type()} ${os.arch()} ${os.release()}\n当前版本:${process.env.VUE_APP_ENV},${process.env.VUE_APP_VERSION}`,
    detail: `版本信息:\nelectron版本:${process.versions.electron}\n当前系统:${os.type()} ${os.arch()} ${os.release()}\n当前版本:${global.envConfig.VUE_APP_ENV},${global.envConfig.VUE_APP_VERSION}`,
    noLink: true,
    buttons: ['确定']
  })
}

菜单的快捷键注册是用accelerator添加的,当然你也可以根据系统的不同进行差异化处理,role的话是electron内置的行为,比如copy - 复制paste - 粘贴等等,可以参考官方文档

在windows下我们把菜单隐藏掉,mac的话正常显示:

function setMenu(win) {
  let menu
  if (config.devToolsShow) {
    menu = Menu.buildFromTemplate(menuConfig)
    Menu.setApplicationMenu(menu)
    win.webContents.openDevTools({ mode: 'detach' })
  } else {
    if (isMac) {
      menu = Menu.buildFromTemplate(menuConfig)
      Menu.setApplicationMenu(menu)
    } else {
      Menu.setApplicationMenu(null)
    }
    win.webContents.openDevTools({ mode: 'detach' })
    // win.webContents.closeDevTools()
  }
}

主进程监听快捷键

globalShortcut

globalShortcut模块可以在操作系统中注册/注销全局快捷键,但是个人不推荐用这种方式来注册快捷键:
优点:响应级别最高,只要软件在运行中,无论软件处于什么状态(没聚焦甚至隐藏),都会响应。
缺点:如果快捷键已经被其他应用程序注册掉,那么会注册失败。注册成功后,由于其响应级别最高,所以会影响其他软件快捷键的使用,在启动软件后,其他软件的快捷键相同的话其他软件的快捷键无法生效。

// 主进程,渲染进程可以使用remote来注册:
const { app, globalShortcut } = require('electron')

app.whenReady().then(() => {
  const ret = globalShortcut.register('CommandOrControl+X', () => {
    console.log('CommandOrControl+X is pressed')
  })
  if (!ret) {
    console.log('registration failed')
  }
  // 检查快捷键是否注册成功
  console.log(globalShortcut.isRegistered('CommandOrControl+X'))
})

app.on('will-quit', () => {
  // 注销快捷键
  globalShortcut.unregister('CommandOrControl+X')

  // 注销所有快捷键
  globalShortcut.unregisterAll()
})

electron-localshortcut

第三方的npm包,api和globalShortcut基本一致,相对而言没那么激进,是针对于窗口注册的,故需传入窗口进行注册,当窗口没聚焦时,不会相应。
优点:针对于窗口的监听,响应需处于聚焦状态,基本上能满足大多数场景。
缺点:需要引入第三方包,页面有webview,且焦点在webview上时无法触发。

npm install --save electron-localshortcut

const electronLocalshortcut = require('electron-localshortcut')
const win = new BrowserWindow()
win.loadUrl('https://github.com')
win.show()

electronLocalshortcut.register(win, 'Ctrl+A', () => {
  console.log('You pressed ctrl & A')
})
// 检查快捷键是否注册成功
console.log(electronLocalshortcut.isRegistered(win, 'Ctrl+A'))
// 注销快捷键
electronLocalshortcut.unregister(win, 'Ctrl+A')
electronLocalshortcut.unregisterAll(win)

渲染进程监听快捷键

自己页面监听

实际上就是网页keyup的监听,可以用通信把键盘按键的信息传送到主进程。
当然你也可以在主进程中使用before-input-event,渲染进程中调度页面中的keydown和keyup事件之前,会发出before-input-event事件,可以在主进程捕获按键信息。
优点:针对于页面的监听,常用于某个页面的特殊按键监听。
缺点:页面有iframe或webview,且焦点在iframe或webview上时,无法触发(iframe可以触发before-input-event)。

渲染进程:
window.addEventListener('keyup', handleKeyPress, true)
function handleKeyPress(event) {
  $message.success(`keyup 监听${event.key}`)
  console.log(`You pressed ${event.key}`)
}
window.removeEventListener('keyup', handleKeyPress, true)


主进程:
win.webContents.on('before-input-event', (event, input) => {
  if (input.control && input.key.toLowerCase() === 'i') {
    console.log('Pressed Control+I')
    event.preventDefault()
  }
})

组合按键:当然一般来说快捷键都是组合按键的,我们可以借助第三方按键库来处理。

npm i mousetrap

渲染进程:

Mousetrap.bind('ctrl+k', function() { 
  $message.success(`Mousetrap ctrl+k`)
})

Mousetrap.unbind('ctrl+k')

webview或iframe嵌入网页监听

如果我们的页面中有webview或iframe,且焦点在webview或iframe上时,会导致我们的快捷键失效。

  1. Menu菜单和globalShortcut不会失效。
  2. electron-localshortcut:webview无效,iframe有效
  3. keyup:webview无效,iframe无效,before-input-event:iframe有效。

这样看下来在应用webview的时候比较难处理,只有Menu菜单和globalShortcut对其生效,那么还有其他方式吗?
我们可以通过向webview注入preload.js来进行键盘按键的监听,然后将键盘信息回传给我们的渲染进程

vue.config.js中electronBuilder修改为:
preload: { preload: 'src/renderer/preload/ipcRenderer.js', webviewPreload: 'src/renderer/preload/webview.js' }

webviewPreload是我们向webview注入的js,当然你得新建对应文件,注意一下我们建立的文件是webview.js,编译后为webviewPreload.js,故我们注入的为webviewPreload.js。
注:webview 启动及Node integration启用须在主进程窗口webPreferences进行配置。
webview.js:
const { ipcRenderer } = require('electron')
window.addEventListener('keyup', handleKeyPress, true)
function handleKeyPress(event) {
    console.log(event.key)
    ipcRenderer.sendToHost('ipc-message', {
        data: event.key
    })
}

渲染进程:
<webview ref="webviewRef" class="webview" src="https://www.baidu.com" :preload="state.preload" ></webview>
const { remote } = require('electron')
const path = require('path')

const webview = webviewRef.value
state.preload = path.resolve(remote.app.getAppPath(), './webviewPreload.js')
webview.addEventListener('ipc-message', webMessage)
webview.addEventListener('dom-ready', () => {
  webview.openDevTools()
})
function webMessage(event) {
  console.log('在此监听事件中接收webview嵌套页面所响应的事件', event)
  $message.success(`webview:${event.args[0].data}`)
}

简单梳理一下,我们把webviewPreload.js注入webview,通过这个js进行键盘的keyup监听,然后通过sendToHost把信息回传给渲染进程,渲染进程中获取回传的信息。
渲染进程是看不到我们注入js以及webview的打印,可以使用webview.openDevTools(),打开webview的控制台查看。
本例子就没说明组合键了,想用组合键请自行注入mousetrap进行操作。

本系列更新只有利用周末和下班时间整理,比较多的内容的话更新会比较慢,希望能对你有所帮助,请多多star或点赞收藏支持一下

本文地址:https://xuxin123.com/electron/menu-shortcut
本文github地址:链接


陌路凡歌
7.9k 声望258 粉丝

人笨,多听,多写,多看书