improvement(shortcut): Support more keyboard shortcuts

This commit is contained in:
icinggslits 2025-02-16 05:17:42 +08:00 committed by 亢奋猫
parent a869857fc1
commit 23a2a6b57c
2 changed files with 165 additions and 25 deletions

View File

@ -56,6 +56,59 @@ function handleZoom(delta: number) {
} }
} }
const convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat = (
shortcut: string | string[]
): string => {
const accelerator = (() => {
if (Array.isArray(shortcut)) {
return shortcut
} else {
return shortcut.split('+').map((key) => key.trim())
}
})()
return accelerator
.map((key) => {
switch (key) {
case 'Control':
return 'CommandOrControl'
case 'Ctrl':
return 'CommandOrControl'
case 'ArrowUp':
return 'Up'
case 'ArrowDown':
return 'Down'
case 'ArrowLeft':
return 'Left'
case 'ArrowRight':
return 'Right'
case 'AltGraph':
return 'Alt'
case 'Slash':
return '/'
case 'Semicolon':
return ';'
case 'BracketLeft':
return '['
case 'BracketRight':
return ']'
case 'Backslash':
return '\\'
case 'Quote':
return "'"
case 'Comma':
return ','
case 'Minus':
return '-'
case 'Equal':
return '='
default:
return key
}
})
.join('+')
}
export function registerShortcuts(window: BrowserWindow) { export function registerShortcuts(window: BrowserWindow) {
window.webContents.setZoomFactor(configManager.getZoomFactor()) window.webContents.setZoomFactor(configManager.getZoomFactor())
@ -104,7 +157,10 @@ export function registerShortcuts(window: BrowserWindow) {
} }
if (shortcut.enabled) { if (shortcut.enabled) {
globalShortcut.register(formatShortcutKey(shortcut.shortcut), () => handler(window)) const accelerator = convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat(
shortcut.shortcut
)
globalShortcut.register(accelerator, () => handler(window))
} }
} catch (error) { } catch (error) {
Logger.error(`[ShortcutService] Failed to register shortcut ${shortcut.key}`) Logger.error(`[ShortcutService] Failed to register shortcut ${shortcut.key}`)
@ -120,12 +176,16 @@ export function registerShortcuts(window: BrowserWindow) {
if (showAppAccelerator) { if (showAppAccelerator) {
const handler = getShortcutHandler({ key: 'show_app' } as Shortcut) const handler = getShortcutHandler({ key: 'show_app' } as Shortcut)
handler && globalShortcut.register(showAppAccelerator, () => handler(window)) const accelerator =
convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat(showAppAccelerator)
handler && globalShortcut.register(accelerator, () => handler(window))
} }
if (showMiniWindowAccelerator) { if (showMiniWindowAccelerator) {
const handler = getShortcutHandler({ key: 'mini_window' } as Shortcut) const handler = getShortcutHandler({ key: 'mini_window' } as Shortcut)
handler && globalShortcut.register(showMiniWindowAccelerator, () => handler(window)) const accelerator =
convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat(showMiniWindowAccelerator)
handler && globalShortcut.register(accelerator, () => handler(window))
} }
} catch (error) { } catch (error) {
Logger.error('[ShortcutService] Failed to unregister shortcuts') Logger.error('[ShortcutService] Failed to unregister shortcuts')

View File

@ -8,7 +8,7 @@ import { initialState, resetShortcuts, toggleShortcut, updateShortcut } from '@r
import { Shortcut } from '@renderer/types' import { Shortcut } from '@renderer/types'
import { Button, Input, InputRef, Switch, Table as AntTable, Tooltip } from 'antd' import { Button, Input, InputRef, Switch, Table as AntTable, Tooltip } from 'antd'
import type { ColumnsType } from 'antd/es/table' import type { ColumnsType } from 'antd/es/table'
import { FC, useRef, useState } from 'react' import React, { FC, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
@ -92,8 +92,32 @@ const ShortcutSettings: FC = () => {
return isMac ? '⇧' : 'Shift' return isMac ? '⇧' : 'Shift'
case 'CommandOrControl': case 'CommandOrControl':
return isMac ? '⌘' : 'Ctrl' return isMac ? '⌘' : 'Ctrl'
case ' ': case 'ArrowUp':
return 'Space' return '↑'
case 'ArrowDown':
return '↓'
case 'ArrowLeft':
return '←'
case 'ArrowRight':
return '→'
case 'Slash':
return '/'
case 'Semicolon':
return ';'
case 'BracketLeft':
return '['
case 'BracketRight':
return ']'
case 'Backslash':
return '\\'
case 'Quote':
return "'"
case 'Comma':
return ','
case 'Minus':
return '-'
case 'Equal':
return '='
default: default:
return key.charAt(0).toUpperCase() + key.slice(1) return key.charAt(0).toUpperCase() + key.slice(1)
} }
@ -101,13 +125,61 @@ const ShortcutSettings: FC = () => {
.join(' + ') .join(' + ')
} }
const usableEndKeys = (key: string): string | null => { const usableEndKeys = (event: React.KeyboardEvent): string | null => {
if (key.length === 1) { const { code } = event
return key.toUpperCase() // No lock keys
} // Among the commonly used keys, not including: Escape, NumpadMultiply, NumpadDivide, NumpadSubtract, NumpadAdd, NumpadDecimal
switch (key) { // The react-hotkeys-hook library does not differentiate between `Digit` and `Numpad`
switch (code) {
case 'KeyA':
case 'KeyB':
case 'KeyC':
case 'KeyD':
case 'KeyE':
case 'KeyF':
case 'KeyG':
case 'KeyH':
case 'KeyI':
case 'KeyJ':
case 'KeyK':
case 'KeyL':
case 'KeyM':
case 'KeyN':
case 'KeyO':
case 'KeyP':
case 'KeyQ':
case 'KeyR':
case 'KeyS':
case 'KeyT':
case 'KeyU':
case 'KeyV':
case 'KeyW':
case 'KeyX':
case 'KeyY':
case 'KeyZ':
case 'Digit0':
case 'Digit1':
case 'Digit2':
case 'Digit3':
case 'Digit4':
case 'Digit5':
case 'Digit6':
case 'Digit7':
case 'Digit8':
case 'Digit9':
case 'Numpad0':
case 'Numpad1':
case 'Numpad2':
case 'Numpad3':
case 'Numpad4':
case 'Numpad5':
case 'Numpad6':
case 'Numpad7':
case 'Numpad8':
case 'Numpad9':
return code.slice(-1)
case 'Space':
case 'Enter': case 'Enter':
case 'Escape':
case 'Backspace': case 'Backspace':
case 'Tab': case 'Tab':
case 'Delete': case 'Delete':
@ -139,10 +211,24 @@ const ShortcutSettings: FC = () => {
case 'F17': case 'F17':
case 'F18': case 'F18':
case 'F19': case 'F19':
case 'F20': return code
return key case 'Backquote':
case ' ': return '`'
return 'Space' case 'Period':
return '.'
case 'NumpadEnter':
return 'Enter'
// The react-hotkeys-hook library does not handle the symbol strings for the following keys
case 'Slash':
case 'Semicolon':
case 'BracketLeft':
case 'BracketRight':
case 'Backslash':
case 'Quote':
case 'Comma':
case 'Minus':
case 'Equal':
return code
default: default:
return null return null
} }
@ -156,15 +242,9 @@ const ShortcutSettings: FC = () => {
if (e.metaKey) keys.push('Command') if (e.metaKey) keys.push('Command')
if (e.altKey) keys.push('Alt') if (e.altKey) keys.push('Alt')
if (e.shiftKey) keys.push('Shift') if (e.shiftKey) keys.push('Shift')
const endKey = usableEndKeys(e)
const key = e.key if (endKey) {
keys.push(endKey)
if (key.length === 1 && !['Control', 'Alt', 'Shift', 'Meta'].includes(key)) {
if (key === ' ') {
keys.push('Space')
} else {
keys.push(key.toUpperCase())
}
} }
if (!isValidShortcut(keys)) { if (!isValidShortcut(keys)) {