diff options
| author | Darius Jahandarie <djahandarie@gmail.com> | 2023-12-06 03:53:16 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-06 03:53:16 +0000 | 
| commit | bd5bc1a5db29903bc098995cd9262c4576bf76af (patch) | |
| tree | c9214189e0214480fcf6539ad1c6327aef6cbd1c /ext/js/input/hotkey-handler.js | |
| parent | fd6bba8a2a869eaf2b2c1fa49001f933fce3c618 (diff) | |
| parent | 23e6fb76319c9ed7c9bcdc3efba39bc5dd38f288 (diff) | |
Merge pull request #339 from toasted-nutbread/type-annotations
Type annotations
Diffstat (limited to 'ext/js/input/hotkey-handler.js')
| -rw-r--r-- | ext/js/input/hotkey-handler.js | 102 | 
1 files changed, 73 insertions, 29 deletions
| diff --git a/ext/js/input/hotkey-handler.js b/ext/js/input/hotkey-handler.js index 2fd35a5c..f1512b8f 100644 --- a/ext/js/input/hotkey-handler.js +++ b/ext/js/input/hotkey-handler.js @@ -22,28 +22,25 @@ import {yomitan} from '../yomitan.js';  /**   * Class which handles hotkey events and actions. + * @augments EventDispatcher<import('hotkey-handler').EventType>   */  export class HotkeyHandler extends EventDispatcher {      /** -     * Information describing a hotkey. -     * @typedef {object} HotkeyDefinition -     * @property {string} action A string indicating which action to perform. -     * @property {string} key A keyboard key code indicating which key needs to be pressed. -     * @property {string[]} modifiers An array of keyboard modifiers which also need to be pressed. Supports: `'alt', 'ctrl', 'shift', 'meta'`. -     * @property {string[]} scopes An array of scopes for which the hotkey is valid. If this array does not contain `this.scope`, the hotkey will not be registered. -     * @property {boolean} enabled A boolean indicating whether the hotkey is currently enabled. -     */ - -    /**       * Creates a new instance of the class.       */      constructor() {          super(); +        /** @type {Map<string, (argument: unknown) => (boolean|void)>} */          this._actions = new Map(); +        /** @type {Map<string, import('hotkey-handler').HotkeyHandlers>} */          this._hotkeys = new Map(); +        /** @type {Map<import('settings').InputsHotkeyScope, import('settings').InputsHotkeyOptions[]>} */          this._hotkeyRegistrations = new Map(); +        /** @type {EventListenerCollection} */          this._eventListeners = new EventListenerCollection(); +        /** @type {boolean} */          this._isPrepared = false; +        /** @type {boolean} */          this._hasEventListeners = false;      } @@ -60,7 +57,7 @@ export class HotkeyHandler extends EventDispatcher {      /**       * Registers a set of actions that this hotkey handler supports. -     * @param {*[][]} actions An array of `[name, handler]` entries, where `name` is a string and `handler` is a function. +     * @param {[name: string, handler: (argument: unknown) => (boolean|void)][]} actions An array of `[name, handler]` entries, where `name` is a string and `handler` is a function.       */      registerActions(actions) {          for (const [name, handler] of actions) { @@ -70,8 +67,8 @@ export class HotkeyHandler extends EventDispatcher {      /**       * Registers a set of hotkeys for a given scope. -     * @param {string} scope The scope that the hotkey definitions must be for in order to be activated. -     * @param {HotkeyDefinition[]} hotkeys An array of hotkey definitions. +     * @param {import('settings').InputsHotkeyScope} scope The scope that the hotkey definitions must be for in order to be activated. +     * @param {import('settings').InputsHotkeyOptions[]} hotkeys An array of hotkey definitions.       */      registerHotkeys(scope, hotkeys) {          let registrations = this._hotkeyRegistrations.get(scope); @@ -85,7 +82,7 @@ export class HotkeyHandler extends EventDispatcher {      /**       * Removes all registered hotkeys for a given scope. -     * @param {string} scope The scope that the hotkey definitions were registered in. +     * @param {import('settings').InputsHotkeyScope} scope The scope that the hotkey definitions were registered in.       */      clearHotkeys(scope) {          const registrations = this._hotkeyRegistrations.get(scope); @@ -98,8 +95,8 @@ export class HotkeyHandler extends EventDispatcher {      /**       * Assigns a set of hotkeys for a given scope. This is an optimized shorthand for calling       * `clearHotkeys`, then calling `registerHotkeys`. -     * @param {string} scope The scope that the hotkey definitions must be for in order to be activated. -     * @param {HotkeyDefinition[]} hotkeys An array of hotkey definitions. +     * @param {import('settings').InputsHotkeyScope} scope The scope that the hotkey definitions must be for in order to be activated. +     * @param {import('settings').InputsHotkeyOptions[]} hotkeys An array of hotkey definitions.       */      setHotkeys(scope, hotkeys) {          let registrations = this._hotkeyRegistrations.get(scope); @@ -109,14 +106,24 @@ export class HotkeyHandler extends EventDispatcher {          } else {              registrations.length = 0;          } -        registrations.push(...hotkeys); +        for (const {action, argument, key, modifiers, scopes, enabled} of hotkeys) { +            registrations.push({ +                action, +                argument, +                key, +                modifiers: [...modifiers], +                scopes: [...scopes], +                enabled +            }); +        }          this._updateHotkeyRegistrations();      }      /**       * Adds a single event listener to a specific event. -     * @param {string} eventName The string representing the event's name. -     * @param {Function} callback The event listener callback to add. +     * @template [TEventDetails=unknown] +     * @param {import('hotkey-handler').EventType} eventName The string representing the event's name. +     * @param {(details: TEventDetails) => void} callback The event listener callback to add.       * @returns {void}       */      on(eventName, callback) { @@ -128,8 +135,9 @@ export class HotkeyHandler extends EventDispatcher {      /**       * Removes a single event listener from a specific event. -     * @param {string} eventName The string representing the event's name. -     * @param {Function} callback The event listener callback to add. +     * @template [TEventDetails=unknown] +     * @param {import('hotkey-handler').EventType} eventName The string representing the event's name. +     * @param {(details: TEventDetails) => void} callback The event listener callback to add.       * @returns {boolean} `true` if the callback was removed, `false` otherwise.       */      off(eventName, callback) { @@ -142,37 +150,50 @@ export class HotkeyHandler extends EventDispatcher {      /**       * Attempts to simulate an action for a given combination of key and modifiers.       * @param {string} key A keyboard key code indicating which key needs to be pressed. -     * @param {string[]} modifiers An array of keyboard modifiers which also need to be pressed. Supports: `'alt', 'ctrl', 'shift', 'meta'`. +     * @param {import('input').ModifierKey[]} modifiers An array of keyboard modifiers which also need to be pressed. Supports: `'alt', 'ctrl', 'shift', 'meta'`.       * @returns {boolean} `true` if an action was performed, `false` otherwise.       */      simulate(key, modifiers) {          const hotkeyInfo = this._hotkeys.get(key);          return (              typeof hotkeyInfo !== 'undefined' && -            this._invokeHandlers(modifiers, hotkeyInfo) +            this._invokeHandlers(modifiers, hotkeyInfo, key)          );      }      // Message handlers +    /** +     * @param {{key: string, modifiers: import('input').ModifierKey[]}} details +     * @returns {boolean} +     */      _onMessageForwardHotkey({key, modifiers}) {          return this.simulate(key, modifiers);      }      // Private -    _onKeyDown(e) { -        const hotkeyInfo = this._hotkeys.get(e.code); +    /** +     * @param {KeyboardEvent} event +     */ +    _onKeyDown(event) { +        const hotkeyInfo = this._hotkeys.get(event.code);          if (typeof hotkeyInfo !== 'undefined') { -            const eventModifiers = DocumentUtil.getActiveModifiers(e); -            if (this._invokeHandlers(eventModifiers, hotkeyInfo, e.key)) { -                e.preventDefault(); +            const eventModifiers = DocumentUtil.getActiveModifiers(event); +            if (this._invokeHandlers(eventModifiers, hotkeyInfo, event.key)) { +                event.preventDefault();                  return;              }          } -        this.trigger('keydownNonHotkey', e); +        this.trigger('keydownNonHotkey', event);      } +    /** +     * @param {import('input').ModifierKey[]} modifiers +     * @param {import('hotkey-handler').HotkeyHandlers} hotkeyInfo +     * @param {string} key +     * @returns {boolean} +     */      _invokeHandlers(modifiers, hotkeyInfo, key) {          for (const {modifiers: handlerModifiers, action, argument} of hotkeyInfo.handlers) {              if (!this._areSame(handlerModifiers, modifiers) || !this._isHotkeyPermitted(modifiers, key)) { continue; } @@ -189,6 +210,11 @@ export class HotkeyHandler extends EventDispatcher {          return false;      } +    /** +     * @param {Set<unknown>} set +     * @param {unknown[]} array +     * @returns {boolean} +     */      _areSame(set, array) {          if (set.size !== array.length) { return false; }          for (const value of array) { @@ -199,6 +225,9 @@ export class HotkeyHandler extends EventDispatcher {          return true;      } +    /** +     * @returns {void} +     */      _updateHotkeyRegistrations() {          if (this._hotkeys.size === 0 && this._hotkeyRegistrations.size === 0) { return; } @@ -219,10 +248,16 @@ export class HotkeyHandler extends EventDispatcher {          this._updateEventHandlers();      } +    /** +     * @returns {void} +     */      _updateHasEventListeners() {          this._hasEventListeners = this.hasListeners('keydownNonHotkey');      } +    /** +     * @returns {void} +     */      _updateEventHandlers() {          if (this._isPrepared && (this._hotkeys.size > 0 || this._hasEventListeners)) {              if (this._eventListeners.size !== 0) { return; } @@ -232,6 +267,11 @@ export class HotkeyHandler extends EventDispatcher {          }      } +    /** +     * @param {import('input').ModifierKey[]} modifiers +     * @param {string} key +     * @returns {boolean} +     */      _isHotkeyPermitted(modifiers, key) {          return !(              (modifiers.length === 0 || (modifiers.length === 1 && modifiers[0] === 'shift')) && @@ -240,6 +280,10 @@ export class HotkeyHandler extends EventDispatcher {          );      } +    /** +     * @param {string} key +     * @returns {boolean} +     */      _isKeyCharacterInput(key) {          return key.length === 1;      } |