Skip to content

Commit 46139fc

Browse files
committed
feat: add keybind services
1 parent 9e08c2a commit 46139fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2827
-1639
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { type IDisposable, isWebKit } from '@alilc/lowcode-shared';
2+
3+
export const DOMEventType = {
4+
// Mouse
5+
CLICK: 'click',
6+
AUXCLICK: 'auxclick',
7+
DBLCLICK: 'dblclick',
8+
MOUSE_UP: 'mouseup',
9+
MOUSE_DOWN: 'mousedown',
10+
MOUSE_OVER: 'mouseover',
11+
MOUSE_MOVE: 'mousemove',
12+
MOUSE_OUT: 'mouseout',
13+
MOUSE_ENTER: 'mouseenter',
14+
MOUSE_LEAVE: 'mouseleave',
15+
MOUSE_WHEEL: 'wheel',
16+
POINTER_UP: 'pointerup',
17+
POINTER_DOWN: 'pointerdown',
18+
POINTER_MOVE: 'pointermove',
19+
POINTER_LEAVE: 'pointerleave',
20+
CONTEXT_MENU: 'contextmenu',
21+
WHEEL: 'wheel',
22+
// Keyboard
23+
KEY_DOWN: 'keydown',
24+
KEY_PRESS: 'keypress',
25+
KEY_UP: 'keyup',
26+
// HTML Document
27+
LOAD: 'load',
28+
BEFORE_UNLOAD: 'beforeunload',
29+
UNLOAD: 'unload',
30+
PAGE_SHOW: 'pageshow',
31+
PAGE_HIDE: 'pagehide',
32+
PASTE: 'paste',
33+
ABORT: 'abort',
34+
ERROR: 'error',
35+
RESIZE: 'resize',
36+
SCROLL: 'scroll',
37+
FULLSCREEN_CHANGE: 'fullscreenchange',
38+
WK_FULLSCREEN_CHANGE: 'webkitfullscreenchange',
39+
// Form
40+
SELECT: 'select',
41+
CHANGE: 'change',
42+
SUBMIT: 'submit',
43+
RESET: 'reset',
44+
FOCUS: 'focus',
45+
FOCUS_IN: 'focusin',
46+
FOCUS_OUT: 'focusout',
47+
BLUR: 'blur',
48+
INPUT: 'input',
49+
// Local Storage
50+
STORAGE: 'storage',
51+
// Drag
52+
DRAG_START: 'dragstart',
53+
DRAG: 'drag',
54+
DRAG_ENTER: 'dragenter',
55+
DRAG_LEAVE: 'dragleave',
56+
DRAG_OVER: 'dragover',
57+
DROP: 'drop',
58+
DRAG_END: 'dragend',
59+
// Animation
60+
ANIMATION_START: isWebKit ? 'webkitAnimationStart' : 'animationstart',
61+
ANIMATION_END: isWebKit ? 'webkitAnimationEnd' : 'animationend',
62+
ANIMATION_ITERATION: isWebKit ? 'webkitAnimationIteration' : 'animationiteration',
63+
} as const;
64+
65+
class DomListener implements IDisposable {
66+
private _handler: (e: any) => void;
67+
private _node: EventTarget;
68+
private readonly _type: string;
69+
private readonly _options: boolean | AddEventListenerOptions;
70+
71+
constructor(node: EventTarget, type: string, handler: (e: any) => void, options?: boolean | AddEventListenerOptions) {
72+
this._node = node;
73+
this._type = type;
74+
this._handler = handler;
75+
this._options = options || false;
76+
this._node.addEventListener(this._type, this._handler, this._options);
77+
}
78+
79+
dispose(): void {
80+
if (!this._handler) {
81+
// Already disposed
82+
return;
83+
}
84+
85+
this._node.removeEventListener(this._type, this._handler, this._options);
86+
87+
// Prevent leakers from holding on to the dom or handler func
88+
this._node = null!;
89+
this._handler = null!;
90+
}
91+
}
92+
93+
export function addDisposableListener<K extends keyof GlobalEventHandlersEventMap>(
94+
node: EventTarget,
95+
type: K,
96+
handler: (event: GlobalEventHandlersEventMap[K]) => void,
97+
useCapture?: boolean,
98+
): IDisposable;
99+
export function addDisposableListener(
100+
node: EventTarget,
101+
type: string,
102+
handler: (event: any) => void,
103+
useCapture?: boolean,
104+
): IDisposable;
105+
export function addDisposableListener(
106+
node: EventTarget,
107+
type: string,
108+
handler: (event: any) => void,
109+
options: AddEventListenerOptions,
110+
): IDisposable;
111+
export function addDisposableListener(
112+
node: EventTarget,
113+
type: string,
114+
handler: (event: any) => void,
115+
useCaptureOrOptions?: boolean | AddEventListenerOptions,
116+
): IDisposable {
117+
return new DomListener(node, type, handler, useCaptureOrOptions);
118+
}

packages/engine-core/src/common/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import * as Schemas from './schemas';
33
export { Schemas };
44

55
export * from './charCode';
6+
export * from './dom';
67
export * from './glob';
78
export * from './keyCodes';
9+
export * from './keyCodeUtils';
810
export * from './path';
911
export * from './strings';
1012
export * from './ternarySearchTree';
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
import { KeyCode } from './keyCodes';
2+
3+
class KeyCodeStrMap {
4+
public _keyCodeToStr: string[];
5+
public _strToKeyCode: { [str: string]: KeyCode };
6+
7+
constructor() {
8+
this._keyCodeToStr = [];
9+
this._strToKeyCode = Object.create(null);
10+
}
11+
12+
define(keyCode: KeyCode, str: string): void {
13+
this._keyCodeToStr[keyCode] = str;
14+
this._strToKeyCode[str.toLowerCase()] = keyCode;
15+
}
16+
17+
keyCodeToStr(keyCode: KeyCode): string {
18+
return this._keyCodeToStr[keyCode];
19+
}
20+
21+
strToKeyCode(str: string): KeyCode {
22+
return this._strToKeyCode[str.toLowerCase()] || KeyCode.Unknown;
23+
}
24+
}
25+
26+
const uiMap = new KeyCodeStrMap();
27+
const userSettingsUSMap = new KeyCodeStrMap();
28+
const userSettingsGeneralMap = new KeyCodeStrMap();
29+
30+
export const KeyCodeUtils = {
31+
toString(keyCode: KeyCode): string {
32+
return uiMap.keyCodeToStr(keyCode);
33+
},
34+
fromString(key: string): KeyCode {
35+
return uiMap.strToKeyCode(key);
36+
},
37+
toUserSettingsUS(keyCode: KeyCode): string {
38+
return userSettingsUSMap.keyCodeToStr(keyCode);
39+
},
40+
toUserSettingsGeneral(keyCode: KeyCode): string {
41+
return userSettingsGeneralMap.keyCodeToStr(keyCode);
42+
},
43+
fromUserSettings(key: string): KeyCode {
44+
return userSettingsUSMap.strToKeyCode(key) || userSettingsGeneralMap.strToKeyCode(key);
45+
},
46+
};
47+
48+
export const EVENT_CODE_TO_KEY_CODE_MAP: { [keyCode: string]: KeyCode } = {};
49+
50+
(function () {
51+
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
52+
// see https://www.toptal.com/developers/keycode/table
53+
54+
const empty = '';
55+
type IMappingEntry = [string, KeyCode, string, string, string];
56+
57+
const mappings: IMappingEntry[] = [
58+
// scanCode, keyCode, keyCodeStr, usUserSettingsLabel, generalUserSettingsLabel
59+
['Unidentified', KeyCode.Unknown, 'unknown', empty, empty],
60+
['KeyA', KeyCode.KeyA, 'A', empty, empty],
61+
['KeyB', KeyCode.KeyB, 'B', empty, empty],
62+
['KeyC', KeyCode.KeyC, 'C', empty, empty],
63+
['KeyD', KeyCode.KeyD, 'D', empty, empty],
64+
['KeyE', KeyCode.KeyE, 'E', empty, empty],
65+
['KeyF', KeyCode.KeyF, 'F', empty, empty],
66+
['KeyG', KeyCode.KeyG, 'G', empty, empty],
67+
['KeyH', KeyCode.KeyH, 'H', empty, empty],
68+
['KeyI', KeyCode.KeyI, 'I', empty, empty],
69+
['KeyJ', KeyCode.KeyJ, 'J', empty, empty],
70+
['KeyK', KeyCode.KeyK, 'K', empty, empty],
71+
['KeyL', KeyCode.KeyL, 'L', empty, empty],
72+
['KeyM', KeyCode.KeyM, 'M', empty, empty],
73+
['KeyN', KeyCode.KeyN, 'N', empty, empty],
74+
['KeyO', KeyCode.KeyO, 'O', empty, empty],
75+
['KeyP', KeyCode.KeyP, 'P', empty, empty],
76+
['KeyQ', KeyCode.KeyQ, 'Q', empty, empty],
77+
['KeyR', KeyCode.KeyR, 'R', empty, empty],
78+
['KeyS', KeyCode.KeyS, 'S', empty, empty],
79+
['KeyT', KeyCode.KeyT, 'T', empty, empty],
80+
['KeyU', KeyCode.KeyU, 'U', empty, empty],
81+
['KeyV', KeyCode.KeyV, 'V', empty, empty],
82+
['KeyW', KeyCode.KeyW, 'W', empty, empty],
83+
['KeyX', KeyCode.KeyX, 'X', empty, empty],
84+
['KeyY', KeyCode.KeyY, 'Y', empty, empty],
85+
['KeyZ', KeyCode.KeyZ, 'Z', empty, empty],
86+
['Digit1', KeyCode.Digit1, '1', empty, empty],
87+
['Digit2', KeyCode.Digit2, '2', empty, empty],
88+
['Digit3', KeyCode.Digit3, '3', empty, empty],
89+
['Digit4', KeyCode.Digit4, '4', empty, empty],
90+
['Digit5', KeyCode.Digit5, '5', empty, empty],
91+
['Digit6', KeyCode.Digit6, '6', empty, empty],
92+
['Digit7', KeyCode.Digit7, '7', empty, empty],
93+
['Digit8', KeyCode.Digit8, '8', empty, empty],
94+
['Digit9', KeyCode.Digit9, '9', empty, empty],
95+
['Digit0', KeyCode.Digit0, '0', empty, empty],
96+
['Enter', KeyCode.Enter, 'Enter', empty, empty],
97+
['Escape', KeyCode.Escape, 'Escape', empty, empty],
98+
['Backspace', KeyCode.Backspace, 'Backspace', empty, empty],
99+
['Tab', KeyCode.Tab, 'Tab', empty, empty],
100+
['Space', KeyCode.Space, 'Space', empty, empty],
101+
['Minus', KeyCode.Minus, '-', '-', 'OEM_MINUS'],
102+
['Equal', KeyCode.Equal, '=', '=', 'OEM_PLUS'],
103+
['BracketLeft', KeyCode.BracketLeft, '[', '[', 'OEM_4'],
104+
['BracketRight', KeyCode.BracketRight, ']', ']', 'OEM_6'],
105+
['Backslash', KeyCode.Backslash, '\\', '\\', 'OEM_5'],
106+
['Semicolon', KeyCode.Semicolon, ';', ';', 'OEM_1'],
107+
['Quote', KeyCode.Quote, `'`, `'`, 'OEM_7'],
108+
['Backquote', KeyCode.Backquote, '`', '`', 'OEM_3'],
109+
['Comma', KeyCode.Comma, ',', ',', 'OEM_COMMA'],
110+
['Period', KeyCode.Period, '.', '.', 'OEM_PERIOD'],
111+
['Slash', KeyCode.Slash, '/', '/', 'OEM_2'],
112+
['CapsLock', KeyCode.CapsLock, 'CapsLock', empty, empty],
113+
['F1', KeyCode.F1, 'F1', empty, empty],
114+
['F2', KeyCode.F2, 'F2', empty, empty],
115+
['F3', KeyCode.F3, 'F3', empty, empty],
116+
['F4', KeyCode.F4, 'F4', empty, empty],
117+
['F5', KeyCode.F5, 'F5', empty, empty],
118+
['F6', KeyCode.F6, 'F6', empty, empty],
119+
['F7', KeyCode.F7, 'F7', empty, empty],
120+
['F8', KeyCode.F8, 'F8', empty, empty],
121+
['F9', KeyCode.F9, 'F9', empty, empty],
122+
['F10', KeyCode.F10, 'F10', empty, empty],
123+
['F11', KeyCode.F11, 'F11', empty, empty],
124+
['F12', KeyCode.F12, 'F12', empty, empty],
125+
['PrintScreen', KeyCode.Unknown, empty, empty, empty],
126+
['ScrollLock', KeyCode.ScrollLock, 'ScrollLock', empty, empty],
127+
['Pause', KeyCode.PauseBreak, 'PauseBreak', empty, empty],
128+
['Insert', KeyCode.Insert, 'Insert', empty, empty],
129+
['Home', KeyCode.Home, 'Home', empty, empty],
130+
['PageUp', KeyCode.PageUp, 'PageUp', empty, empty],
131+
['Delete', KeyCode.Delete, 'Delete', empty, empty],
132+
['End', KeyCode.End, 'End', empty, empty],
133+
['PageDown', KeyCode.PageDown, 'PageDown', empty, empty],
134+
['ArrowRight', KeyCode.RightArrow, 'RightArrow', 'Right', empty],
135+
['ArrowLeft', KeyCode.LeftArrow, 'LeftArrow', 'Left', empty],
136+
['ArrowDown', KeyCode.DownArrow, 'DownArrow', 'Down', empty],
137+
['ArrowUp', KeyCode.UpArrow, 'UpArrow', 'Up', empty],
138+
['NumLock', KeyCode.NumLock, 'NumLock', empty, empty],
139+
['NumpadDivide', KeyCode.NumpadDivide, 'NumPad_Divide', empty, empty],
140+
['NumpadMultiply', KeyCode.NumpadMultiply, 'NumPad_Multiply', empty, empty],
141+
['NumpadSubtract', KeyCode.NumpadSubtract, 'NumPad_Subtract', empty, empty],
142+
['NumpadAdd', KeyCode.NumpadAdd, 'NumPad_Add', empty, empty],
143+
['NumpadEnter', KeyCode.Enter, empty, empty, empty],
144+
['Numpad1', KeyCode.Numpad1, 'NumPad1', empty, empty],
145+
['Numpad2', KeyCode.Numpad2, 'NumPad2', empty, empty],
146+
['Numpad3', KeyCode.Numpad3, 'NumPad3', empty, empty],
147+
['Numpad4', KeyCode.Numpad4, 'NumPad4', empty, empty],
148+
['Numpad5', KeyCode.Numpad5, 'NumPad5', empty, empty],
149+
['Numpad6', KeyCode.Numpad6, 'NumPad6', empty, empty],
150+
['Numpad7', KeyCode.Numpad7, 'NumPad7', empty, empty],
151+
['Numpad8', KeyCode.Numpad8, 'NumPad8', empty, empty],
152+
['Numpad9', KeyCode.Numpad9, 'NumPad9', empty, empty],
153+
['Numpad0', KeyCode.Numpad0, 'NumPad0', empty, empty],
154+
['NumpadDecimal', KeyCode.NumpadDecimal, 'NumPad_Decimal', empty, empty],
155+
['IntlBackslash', KeyCode.IntlBackslash, 'OEM_102', empty, empty],
156+
['ContextMenu', KeyCode.ContextMenu, 'ContextMenu', empty, empty],
157+
['Power', KeyCode.Unknown, empty, empty, empty],
158+
['NumpadEqual', KeyCode.Unknown, empty, empty, empty],
159+
['F13', KeyCode.F13, 'F13', empty, empty],
160+
['F14', KeyCode.F14, 'F14', empty, empty],
161+
['F15', KeyCode.F15, 'F15', empty, empty],
162+
['F16', KeyCode.F16, 'F16', empty, empty],
163+
['F17', KeyCode.F17, 'F17', empty, empty],
164+
['F18', KeyCode.F18, 'F18', empty, empty],
165+
['F19', KeyCode.F19, 'F19', empty, empty],
166+
['F20', KeyCode.F20, 'F20', empty, empty],
167+
['F21', KeyCode.F21, 'F21', empty, empty],
168+
['F22', KeyCode.F22, 'F22', empty, empty],
169+
['F23', KeyCode.F23, 'F23', empty, empty],
170+
['F24', KeyCode.F24, 'F24', empty, empty],
171+
['AudioVolumeMute', KeyCode.AudioVolumeMute, 'AudioVolumeMute', empty, empty],
172+
['AudioVolumeUp', KeyCode.AudioVolumeUp, 'AudioVolumeUp', empty, empty],
173+
['AudioVolumeDown', KeyCode.AudioVolumeDown, 'AudioVolumeDown', empty, empty],
174+
['NumpadComma', KeyCode.NUMPAD_SEPARATOR, 'NumPad_Separator', empty, empty],
175+
['IntlRo', KeyCode.ABNT_C1, 'ABNT_C1', empty, empty],
176+
['NumpadClear', KeyCode.Clear, 'Clear', empty, empty],
177+
[empty, KeyCode.Ctrl, 'Ctrl', empty, empty],
178+
[empty, KeyCode.Shift, 'Shift', empty, empty],
179+
[empty, KeyCode.Alt, 'Alt', empty, empty],
180+
[empty, KeyCode.Meta, 'Meta', empty, empty],
181+
['ControlLeft', KeyCode.Ctrl, empty, empty, empty],
182+
['ShiftLeft', KeyCode.Shift, empty, empty, empty],
183+
['AltLeft', KeyCode.Alt, empty, empty, empty],
184+
['MetaLeft', KeyCode.Meta, empty, empty, empty],
185+
['ControlRight', KeyCode.Ctrl, empty, empty, empty],
186+
['ShiftRight', KeyCode.Shift, empty, empty, empty],
187+
['AltRight', KeyCode.Alt, empty, empty, empty],
188+
['MetaRight', KeyCode.Meta, empty, empty, empty],
189+
['MediaTrackNext', KeyCode.MediaTrackNext, 'MediaTrackNext', empty, empty],
190+
['MediaTrackPrevious', KeyCode.MediaTrackPrevious, 'MediaTrackPrevious', empty, empty],
191+
['MediaStop', KeyCode.MediaStop, 'MediaStop', empty, empty],
192+
['MediaPlayPause', KeyCode.MediaPlayPause, 'MediaPlayPause', empty, empty],
193+
['MediaSelect', KeyCode.LaunchMediaPlayer, 'LaunchMediaPlayer', empty, empty],
194+
['LaunchMail', KeyCode.LaunchMail, 'LaunchMail', empty, empty],
195+
['LaunchApp2', KeyCode.LaunchApp2, 'LaunchApp2', empty, empty],
196+
['LaunchScreenSaver', KeyCode.Unknown, empty, empty, empty],
197+
['BrowserSearch', KeyCode.BrowserSearch, 'BrowserSearch', empty, empty],
198+
['BrowserHome', KeyCode.BrowserHome, 'BrowserHome', empty, empty],
199+
['BrowserBack', KeyCode.BrowserBack, 'BrowserBack', empty, empty],
200+
['BrowserForward', KeyCode.BrowserForward, 'BrowserForward', empty, empty],
201+
202+
// See https://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html
203+
// If an Input Method Editor is processing key input and the event is keydown, return 229.
204+
[empty, KeyCode.KEY_IN_COMPOSITION, 'KeyInComposition', empty, empty],
205+
[empty, KeyCode.ABNT_C2, 'ABNT_C2', empty, empty],
206+
[empty, KeyCode.OEM_8, 'OEM_8', empty, empty],
207+
];
208+
209+
const seenKeyCode: boolean[] = [];
210+
211+
for (const mapping of mappings) {
212+
const [scanCode, keyCode, keyCodeStr, usUserSettingsLabel, generalUserSettingsLabel] = mapping;
213+
214+
if (!seenKeyCode[keyCode]) {
215+
seenKeyCode[keyCode] = true;
216+
if (!keyCodeStr) {
217+
throw new Error(`String representation missing for key code ${keyCode} around scan code ${scanCode}`);
218+
}
219+
uiMap.define(keyCode, keyCodeStr);
220+
userSettingsUSMap.define(keyCode, usUserSettingsLabel || keyCodeStr);
221+
userSettingsGeneralMap.define(keyCode, generalUserSettingsLabel || usUserSettingsLabel || keyCodeStr);
222+
}
223+
224+
if (scanCode) {
225+
EVENT_CODE_TO_KEY_CODE_MAP[scanCode] = keyCode;
226+
}
227+
}
228+
229+
console.log(
230+
'%c [ IMMUTABLE_KEY_CODE_TO_CODE ]-500',
231+
'font-size:13px; background:pink; color:#bf2c9f;',
232+
uiMap,
233+
userSettingsUSMap,
234+
userSettingsGeneralMap,
235+
EVENT_CODE_TO_KEY_CODE_MAP,
236+
);
237+
})();
238+
239+
export function KeyChord(firstPart: number, secondPart: number): number {
240+
const chordPart = ((secondPart & 0x0000ffff) << 16) >>> 0;
241+
return (firstPart | chordPart) >>> 0;
242+
}

0 commit comments

Comments
 (0)