Skip to content

Commit 64ef178

Browse files
committed
新增:页面全局快捷键注册方法
1 parent fe1778f commit 64ef178

8 files changed

Lines changed: 236 additions & 15 deletions

File tree

electron/lib/util.ts

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Showdown from "showdown"
66
import iconvLite from "iconv-lite";
77
import chardet from "chardet";
88
import {Iconv} from "iconv"
9+
import {isMac, isWin} from "./env";
910

1011
export const sleep = (time = 1000) => {
1112
return new Promise((resolve) => {
@@ -103,7 +104,11 @@ export const StrUtil = {
103104
Date.now(),
104105
(Math.floor(Math.random() * 1000000) + '').padStart(6, '0')
105106
].join('')
106-
}
107+
},
108+
ucFirst(str: string) {
109+
if (!str) return '';
110+
return str.charAt(0).toUpperCase() + str.slice(1);
111+
},
107112
}
108113

109114
export const TimeUtil = {
@@ -469,3 +474,105 @@ export const MarkdownUtil = {
469474
},
470475
}
471476

477+
type HotkeyModifierType = 'Control' | 'Option' | 'Command' | 'Ctrl' | 'Alt' | 'Win' | 'Meta' | 'Shift';
478+
type HotkeyType = { key: string, modifiers: HotkeyModifierType[] }
479+
480+
export const HotKeyUtil = {
481+
orderModifiers(modifiers: HotkeyModifierType[]) {
482+
const order = ['Control', 'Ctrl', 'Command', 'Meta', 'Win', 'Option', 'Alt', 'Shift'];
483+
return modifiers.sort((a, b) => {
484+
return order.indexOf(a) - order.indexOf(b);
485+
});
486+
},
487+
unifyObject(hotkey: HotkeyType) {
488+
return {
489+
key: hotkey.key.toUpperCase(),
490+
modifiers: this.orderModifiers(hotkey.modifiers.map(modifier => StrUtil.ucFirst(modifier)))
491+
};
492+
},
493+
unifyString(hotkey: string): HotkeyType {
494+
const parts = hotkey.split('+');
495+
const key = (parts.pop() || '');
496+
const modifiers: any[] = [];
497+
parts.forEach(part => {
498+
modifiers.push(StrUtil.ucFirst(part.trim()));
499+
});
500+
return this.unifyObject({key, modifiers});
501+
},
502+
unify(hotkeys: string | string[] | HotkeyType | HotkeyType[]): HotkeyType[] {
503+
if (typeof hotkeys === 'string') {
504+
return [this.unifyString(hotkeys)];
505+
} else if (Array.isArray(hotkeys)) {
506+
return hotkeys.map(hotkey => {
507+
if (typeof hotkey === 'string') {
508+
return this.unifyString(hotkey);
509+
} else {
510+
return this.unifyObject(hotkey);
511+
}
512+
});
513+
} else {
514+
return [this.unifyObject(hotkeys)];
515+
}
516+
},
517+
getFromEvent(event: any): HotkeyType | null {
518+
const valid = [
519+
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
520+
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
521+
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
522+
'Space',
523+
]
524+
const key = (event.key || '').toUpperCase();
525+
if (!event || !event.key || !valid.includes(key)) {
526+
return null
527+
}
528+
const modifiers: HotkeyModifierType[] = [];
529+
if (isWin) {
530+
if (event.ctrlKey || event.control) {
531+
modifiers.push('Ctrl');
532+
}
533+
if (event.altKey || event.alt) {
534+
modifiers.push('Alt');
535+
}
536+
if (event.metaKey || event.meta) {
537+
modifiers.push('Win');
538+
}
539+
} else if (isMac) {
540+
if (event.ctrlKey || event.control) {
541+
modifiers.push('Control');
542+
}
543+
if (event.altKey || event.alt) {
544+
modifiers.push('Option');
545+
}
546+
if (event.metaKey || event.meta) {
547+
modifiers.push('Command');
548+
}
549+
} else {
550+
if (event.ctrlKey || event.control) {
551+
modifiers.push('Ctrl');
552+
}
553+
if (event.altKey || event.alt) {
554+
modifiers.push('Alt');
555+
}
556+
if (event.metaKey || event.meta) {
557+
modifiers.push('Meta');
558+
}
559+
}
560+
if (event.shiftKey || event.shift) {
561+
modifiers.push('Shift');
562+
}
563+
return this.unifyObject({key, modifiers});
564+
},
565+
match(hotkeysForMatch: HotkeyType[], hotkey: HotkeyType): boolean {
566+
if (!hotkeysForMatch || !hotkey) {
567+
return false
568+
}
569+
const hotKeyStr = hotkey.modifiers.join('+') + '+' + hotkey.key;
570+
for (const key of hotkeysForMatch) {
571+
const keyStr = key.modifiers.join('+') + '+' + key.key;
572+
if (keyStr === hotKeyStr) {
573+
return true
574+
}
575+
}
576+
return false
577+
}
578+
}

electron/mapi/manager/lib/hooks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type PluginHookType = never
88
| 'PluginEvent'
99
| 'SubInputChange'
1010
| 'ScreenCapture'
11+
| 'Hotkey'
1112

1213
type HookType = never
1314
| 'Show'

electron/mapi/manager/plugin/event.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,24 @@ export const ManagerPluginEvent = {
7272
}
7373
ManagerPluginEvent.pluginEvents[event] = ManagerPluginEvent.pluginEvents[event].filter(c => c !== context)
7474
},
75+
registerHotkey: async (context: PluginContext, data: any) => {
76+
if (!context._event) {
77+
context._event = {}
78+
}
79+
if (!context._event['Hotkey']) {
80+
context._event['Hotkey'] = []
81+
}
82+
const {id, hotkeys} = data
83+
context._event['Hotkey'].push({
84+
id, hotkeys
85+
})
86+
},
87+
unregisterHotkeyAll: async (context: PluginContext, data: any) => {
88+
if (!context._event || !context._event['HotKey']) {
89+
return
90+
}
91+
context._event['Hotkey'] = []
92+
},
7593
isMacOs: async (context: PluginContext, data: any) => {
7694
return isMac
7795
},

electron/mapi/manager/type.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import {ActiveWindow, PluginRecord} from "../../../src/types/Manager";
55
export type PluginContext = (BrowserView | {}) & {
66
_plugin: PluginRecord,
77
_window?: BrowserWindow,
8+
_event?: {
9+
[key: string]: any[],
10+
}
811
}
912

1013
export type SearchQuery = {

electron/mapi/manager/window/index.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {Events} from "../../event/main";
1313
import {ManagerSystem} from "../system";
1414
import {AppsMain} from "../../app/main";
1515
import {ManagerPluginEvent} from "../plugin/event";
16+
import {PluginContext} from "../type";
17+
import {HotKeyUtil} from "../../../lib/util";
1618

1719
const browserViews = new Map<WebContents, BrowserView>()
1820
const detachWindows = new Map<WebContents, BrowserWindow>()
@@ -262,12 +264,31 @@ export const ManagerWindow = {
262264
// exit when Escape key is pressed
263265
if (mainWindowView === view) {
264266
if (input.key === 'Escape') {
265-
if (mainWindowView) {
266-
ManagerWindow.close()
267-
AppRuntime.mainWindow.webContents.focus()
267+
if (!(input.meta || input.control || input.shift || input.alt)) {
268+
if (mainWindowView) {
269+
ManagerWindow.close()
270+
AppRuntime.mainWindow.webContents.focus()
271+
}
272+
}
273+
}
274+
} else {
275+
if (input.key === 'Escape') {
276+
if (!(input.meta || input.control || input.shift || input.alt)) {
277+
view._window.isFullScreen() && view._window.setFullScreen(false);
268278
}
269279
}
270280
}
281+
} else if (input.type === 'keyDown') {
282+
if ((view as PluginContext)._event && (view as PluginContext)._event['Hotkey']) {
283+
const hotkey = HotKeyUtil.getFromEvent(input)
284+
if (hotkey) {
285+
(view as PluginContext)._event['Hotkey'].forEach(({id, hotkeys}) => {
286+
if (HotKeyUtil.match(hotkeys, hotkey)) {
287+
executePluginHooks(view as BrowserView, 'Hotkey', {id, hotkey});
288+
}
289+
})
290+
}
291+
}
271292
}
272293
})
273294
const windowOption = {
@@ -476,13 +497,18 @@ export const ManagerWindow = {
476497
// console.log('detach.render-process-gone')
477498
win.close();
478499
});
479-
view.webContents.on('before-input-event', (event, input) => {
480-
if (input.type !== 'keyDown') return;
481-
if (!(input.meta || input.control || input.shift || input.alt)) {
482-
if (input.key === 'Escape') {
483-
win.isFullScreen() && win.setFullScreen(false);
500+
win.webContents.on('before-input-event', (event, input) => {
501+
if (input.type === 'keyDown') {
502+
if ((view as PluginContext)._event && (view as PluginContext)._event['Hotkey']) {
503+
const hotkey = HotKeyUtil.getFromEvent(input)
504+
if (hotkey) {
505+
(view as PluginContext)._event['Hotkey'].forEach(({id, hotkeys}) => {
506+
if (HotKeyUtil.match(hotkeys, hotkey)) {
507+
executePluginHooks(view as BrowserView, 'Hotkey', {id, hotkey});
508+
}
509+
})
510+
}
484511
}
485-
return;
486512
}
487513
});
488514
if (isMac) {

electron/preload/focusany.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import os from "os";
22
import electronRemote from "@electron/remote";
33
import path from "path";
44
import fs from "fs";
5-
import {EncodeUtil, FileUtil, StrUtil, TimeUtil} from "../lib/util";
5+
import {EncodeUtil, FileUtil, HotKeyUtil, StrUtil, TimeUtil} from "../lib/util";
66
import {ipcRenderer, shell} from 'electron'
7+
import {isMac} from "../lib/env";
78

89
const ipcSendSync = (type: string, data?: any) => {
910
executeHook('Log', `${type}`, data)
@@ -123,6 +124,48 @@ export const FocusAny = {
123124
delete FocusAny.hooks.onPluginEventCallbacks[event]
124125
ipcSend('unregisterPluginEvent', {event})
125126
},
127+
registerHotkey(
128+
key: string
129+
| string[]
130+
| HotkeyQuickType
131+
| HotkeyType
132+
| HotkeyType[]
133+
,
134+
callback: () => void,
135+
) {
136+
if ('save' === key) {
137+
if (isMac) {
138+
key = 'Command+S'
139+
} else {
140+
key = 'Ctrl+S'
141+
}
142+
}
143+
const hotkeys = HotKeyUtil.unify(key)
144+
if (!('hotKeyListeners' in FocusAny.hooks)) {
145+
FocusAny.hooks.hotKeyListeners = []
146+
FocusAny.hooks.onHotkey = (payload: {
147+
id: string, hotkey: HotkeyType
148+
}) => {
149+
const {id, hotkey} = payload
150+
FocusAny.hooks.hotKeyListeners.forEach((listener: {
151+
id: string,
152+
hotkeys: HotkeyType[],
153+
callback: () => void
154+
}) => {
155+
if (listener.id === id) {
156+
listener.callback();
157+
}
158+
});
159+
}
160+
}
161+
const id = StrUtil.randomString(16)
162+
FocusAny.hooks.hotKeyListeners.push({id, hotkeys, callback})
163+
ipcSend('registerHotkey', {id, hotkeys})
164+
},
165+
unregisterHotkeyAll() {
166+
FocusAny.hooks.hotKeyListeners = []
167+
ipcSend('unregisterHotkeyAll', {})
168+
},
126169
onLog(cb: Function) {
127170
FocusAny.hooks.onLog = cb
128171
},

sdk/focusany.d.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ declare type PlatformType = 'win' | 'osx' | 'linux'
2323

2424
declare type EditionType = 'open' | 'pro'
2525

26-
declare type PluginEvent = 'ClipboardChange' | 'UserChange'
26+
declare type PluginEvent = 'ClipboardChange' | 'UserChange' | '_HotKey'
27+
28+
declare type HotkeyModifierType = 'Control' | 'Option' | 'Command' | 'Ctrl' | 'Alt' | 'Win' | 'Meta' | 'Shift'
29+
30+
declare type HotkeyType = { key: string, modifiers: HotkeyModifierType[] }
31+
32+
declare type HotkeyQuickType = 'save'
2733

2834
declare type ActionMatch = (
2935
ActionMatchText
@@ -158,6 +164,26 @@ interface FocusAnyApi {
158164
*/
159165
offPluginEventAll(event: PluginEvent): void;
160166

167+
/**
168+
* register hot
169+
* @param key
170+
* @param callback
171+
*/
172+
registerHotkey(
173+
key: string
174+
| string[]
175+
| HotkeyQuickType
176+
| HotkeyType
177+
| HotkeyType[]
178+
,
179+
callback: () => void,
180+
): void;
181+
182+
/**
183+
* unregister all hotkey
184+
*/
185+
unregisterHotkeyAll(): void;
186+
161187
/**
162188
* 插件主窗口是否显示
163189
*/

src/pages/PageDetachWindow.vue

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,6 @@ window.__page.onLeaveFullScreen(() => {
105105
isFullscreen.value = false
106106
console.log('onLeaveFullScreen')
107107
})
108-
window.addEventListener('keydown', (e) => {
109-
console.log('PageDetachWindow.keydown', e)
110-
})
111108
</script>
112109

113110
<template>

0 commit comments

Comments
 (0)