diff --git a/src/client/app/common/hotkey.ts b/src/client/app/common/hotkey.ts index 62726887d1..66336fca6e 100644 --- a/src/client/app/common/hotkey.ts +++ b/src/client/app/common/hotkey.ts @@ -1,29 +1,42 @@ import keyCode from './keycode'; +import { concat } from '../../../prelude/array'; -const getKeyMap = keymap => Object.keys(keymap).map(input => { - const result = {} as any; +type pattern = { + which: string[]; + ctrl?: boolean; + shift?: boolean; + alt?: boolean; +}; - const { keyup, keydown } = keymap[input]; +type action = { + patterns: pattern[]; - input.split('+').forEach(keyName => { - switch (keyName.toLowerCase()) { - case 'ctrl': - case 'alt': - case 'shift': - case 'meta': - result[keyName] = true; - break; - default: { - result.keyCode = keyCode(keyName); - if (!Array.isArray(result.keyCode)) result.keyCode = [result.keyCode]; + callback: Function; +}; + +const getKeyMap = keymap => Object.entries(keymap).map(([patterns, callback]): action => { + const result = { + patterns: [], + callback: callback + } as action; + + result.patterns = patterns.split('|').map(part => { + const pattern = { + which: [] + } as pattern; + + part.trim().split('+').forEach(key => { + key = key.trim().toLowerCase(); + switch (key) { + case 'ctrl': pattern.ctrl = true; break; + case 'alt': pattern.alt = true; break; + case 'shift': pattern.shift = true; break; + default: pattern.which = keyCode(key).map(k => k.toLowerCase()); } - } - }); + }); - result.callback = { - keydown: keydown || keymap[input], - keyup - }; + return pattern; + }); return result; }); @@ -36,28 +49,39 @@ export default { bind(el, binding) { el._hotkey_global = binding.modifiers.global === true; - el._keymap = getKeyMap(binding.value); + const actions = getKeyMap(binding.value); - el.dataset.reservedKeyCodes = el._keymap.map(key => `'${key.keyCode}'`).join(' '); + const reservedKeys = concat(actions.map(a => a.patterns.map(p => p.which))); + + el.dataset.reservedKeys = reservedKeys.map(key => `'${key}'`).join(' '); el._keyHandler = e => { - const reservedKeyCodes = document.activeElement ? ((document.activeElement as any).dataset || {}).reservedKeyCodes || '' : ''; + const key = e.code.toLowerCase(); + + const targetReservedKeys = document.activeElement ? ((document.activeElement as any).dataset || {}).reservedKeys || '' : ''; if (document.activeElement && ignoreElemens.some(el => document.activeElement.matches(el))) return; - for (const hotkey of el._keymap) { - if (el._hotkey_global && reservedKeyCodes.includes(`'${e.keyCode}'`)) break; + for (const action of actions) { + if (el._hotkey_global && targetReservedKeys.includes(`'${key}'`)) break; - const callback = hotkey.keyCode.includes(e.keyCode) && - !!hotkey.ctrl === e.ctrlKey && - !!hotkey.alt === e.altKey && - !!hotkey.shift === e.shiftKey && - !!hotkey.meta === e.metaKey && - hotkey.callback[e.type]; + const matched = action.patterns.some(pattern => { + let matched = pattern.which.includes(key); + if (pattern.ctrl && !e.ctrlKey) matched = false; + if (pattern.shift && !e.shiftKey) matched = false; + if (pattern.alt && !e.altKey) matched = false; - if (callback) { - e.preventDefault(); - e.stopPropagation(); - callback(e); + if (matched) { + e.preventDefault(); + e.stopPropagation(); + action.callback(e); + return true; + } else { + return false; + } + }); + + if (matched) { + break; } } }; diff --git a/src/client/app/common/keycode.ts b/src/client/app/common/keycode.ts index 55043ad76b..5786c1dc0a 100644 --- a/src/client/app/common/keycode.ts +++ b/src/client/app/common/keycode.ts @@ -1,116 +1,20 @@ -export default searchInput => { - // Keyboard Events - if (searchInput && typeof searchInput === 'object') { - const hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode; - if (hasKeyCode) { - searchInput = hasKeyCode; - } +export default (input: string): string[] => { + if (Object.keys(aliases).some(a => a.toLowerCase() == input.toLowerCase())) { + const codes = aliases[input]; + return Array.isArray(codes) ? codes : [codes]; + } else { + return [input]; } - - // Numbers - // if (typeof searchInput === 'number') { - // return names[searchInput] - // } - - // Everything else (cast to string) - const search = String(searchInput); - - // check codes - const foundNamedKeyCodes = codes[search.toLowerCase()]; - if (foundNamedKeyCodes) { - return foundNamedKeyCodes; - } - - // check aliases - const foundNamedKeyAliases = aliases[search.toLowerCase()]; - if (foundNamedKeyAliases) { - return foundNamedKeyAliases; - } - - // weird character? - if (search.length === 1) { - return search.charCodeAt(0); - } - - return undefined; }; -/** - * Get by name - * - * exports.code['enter'] // => 13 - */ - -export const codes = { - 'backspace': 8, - 'tab': 9, - 'enter': 13, - 'shift': 16, - 'ctrl': 17, - 'alt': 18, - 'pause/break': 19, - 'caps lock': 20, - 'esc': 27, - 'space': 32, - 'page up': 33, - 'page down': 34, - 'end': 35, - 'home': 36, - 'left': 37, - 'up': 38, - 'right': 39, - 'down': 40, - // 'add': 43, - 'insert': 45, - 'delete': 46, - 'command': 91, - 'left command': 91, - 'right command': 93, - 'numpad *': 106, - 'numpad plus': [43, 107], - 'numpad add': 43, // as a trick - 'numpad -': 109, - 'numpad .': 110, - 'numpad /': 111, - 'num lock': 144, - 'scroll lock': 145, - 'my computer': 182, - 'my calculator': 183, - ';': 186, - '=': 187, - ',': 188, - '-': 189, - '.': 190, - '/': 191, - '`': 192, - '[': 219, - '\\': 220, - ']': 221, - "'": 222 -}; - -// Helper aliases - export const aliases = { - 'windows': 91, - '⇧': 16, - '⌥': 18, - '⌃': 17, - '⌘': 91, - 'ctl': 17, - 'control': 17, - 'option': 18, - 'pause': 19, - 'break': 19, - 'caps': 20, - 'return': 13, - 'escape': 27, - 'spc': 32, - 'pgup': 33, - 'pgdn': 34, - 'ins': 45, - 'del': 46, - 'cmd': 91 + 'esc': 'Escape', + 'enter': ['Enter', 'NumpadEnter'], + 'up': 'ArrowUp', + 'down': 'ArrowDown', + 'left': 'ArrowLeft', + 'right': 'ArrowRight', + 'plus': ['NumpadAdd', 'Semicolon'], }; /*! @@ -119,15 +23,11 @@ export const aliases = { // lower case chars for (let i = 97; i < 123; i++) { - codes[String.fromCharCode(i)] = i - 32; + const char = String.fromCharCode(i); + aliases[char] = `Key${char.toUpperCase()}`; } // numbers -for (let i = 48; i < 58; i++) { - codes[i - 48] = [i, (i - 48) + 96]; -} - -// function keys -for (let i = 1; i < 13; i++) { - codes['f' + i] = i + 111; +for (let i = 0; i < 10; i++) { + aliases[i] = [`Numpad${i}`, `Digit${i}`]; } diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue index f415e89d0e..811f82283d 100644 --- a/src/client/app/common/views/components/reaction-picker.vue +++ b/src/client/app/common/views/components/reaction-picker.vue @@ -72,7 +72,7 @@ export default Vue.extend({ 'esc': this.close, 'enter': this.choose, 'space': this.choose, - 'numpad plus': this.choose, + 'plus': this.choose, 'up': this.focusUp, 'right': this.focusRight, 'down': this.focusDown, diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue index b8c5f31511..26f6ef4554 100644 --- a/src/client/app/desktop/views/components/notes.note.vue +++ b/src/client/app/desktop/views/components/notes.note.vue @@ -115,7 +115,7 @@ export default Vue.extend({ return { 'r': this.reply, 'a': () => this.react(true), - 'numpad plus': () => this.react(true), + 'plus': () => this.react(true), 'n': this.renote, 'up': this.focusBefore, 'shift+tab': this.focusBefore,