ElectronでローカルショートカットキーのイベントをNext.js(レンダラープロセス)でハンドリングする方法
概要
- ElectronのローカルショートカットキーのイベントをNext.jsでハンドリングできるようにする
- メインプロセスからレンダラープロセスへ通知する方法はいくつか種類がありわかりづらい。今回は
contextBridge
を使用するパターンを用いる
前提
Next.js公式のElectronのサンプルプロジェクトをベースにします
$ npx create-next-app --example with-electron-typescript sample
package.json
に指定されているElectronのバージョンが古いので ^24
に上げています
contextIsolation
を true
にする
Electronでは、メインプロセスとレンダラープロセス間のやり取りをセキュアにするために contextBridge
という仕組みが用意されています。
contextBridge
を使用するためには、メインウィンドウ作成時に contextIsolation
を true
にする必要があります。
const mainWindow = new BrowserWindow({
// 省略
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: join(__dirname, 'preload.js'),
},
})
preload.ts
で contextBridge
の設定
preloadスクリプトを書くことで、レンダラープロセスが起動する前に一部のコードを事前にロードすることができます。
contextBridge.exposeInMainWorld
は2つの引数をとり、1つ目に指定された引数の文字列でアクセスできるようになります (window.electron
)
2つ目の引数はレンダラープロセスで利用するメソッドなどを定義します。
ここで定義したリスナーは、window.electron.on を使って呼び出すことができます。
contextBridge.exposeInMainWorld('electron', {
on: (channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => IpcRenderer) => {
ipcRenderer.on(channel, (_event, ...args) => listener(_event, ...args))
},
})
メインウィンドウにローカルショートカットキーを追加し、発火時に通知
公式ドキュメントのこちらの方法で追加します。
今回は ⌘+N
の入力をハンドリングする想定です。
通知は、mainWindow.webContents.send
で行います。
app.on('ready', async () => {
// 省略
const mainWindow = new BrowserWindow({
// 省略
})
// 省略
const menu = Menu.getApplicationMenu()
if (menu != null) {
menu.append(new MenuItem({
label: 'Test',
submenu: [{
label: '作成',
accelerator: process.platform === 'darwin' ? 'Cmd+N' : 'Alt+N',
click: () => {
mainWindow.webContents.send('shortcut-event', 'args1')
}
}]
}))
Menu.setApplicationMenu(menu)
}
})
レンダラープロセス側(Next.js)で購読
購読は、window.electron.on
で行いますが、型定義がないので別途定義しておきます。
Electronをレンダラー側でimportしなくても済むようにいくつかの変数はany
型で定義しています
declare global {
interface Window {
electron: {
on: (channel: string, listener: (event: any, ...args: any[]) => void) => void
}
}
}
window.electron.on
で指定するチャンネル名は、先ほどの mainWindow.webContents.send
で指定したチャンネル名と同じものにします。
任意のReactコンポーネントの、useEffect
内で購読します。
useEffect(() => {
window.electron.on('shortcut-event', (event, ...args) => {
// ショートカットーキーが押された時のアクションを記載
// argsには先ほど送信した、「args1」が含まれます
})
}, [])
参考
Discussion