diff --git a/js/input.js b/js/input.js index 432b93b..3cb4d5d 100644 --- a/js/input.js +++ b/js/input.js @@ -2,6 +2,8 @@ const inputConfig = { clickRadius: 10, // Radius to be considered a click (pixels). If farther, turns into a drag clickTiming: 500, // Timing window to be considered a click (ms). If longer, turns into a drag dClickTiming: 500, // Timing window to be considered a double click (ms). + + keyboardHoldTiming: 100, // Timing window after which to consider holding a key (ms) }; /** @@ -299,5 +301,118 @@ mouse.listen.window.right.onpaintend.on(() => */ /** - * Mouse input processing + * Keyboard input processing */ +// Base object generator functions + +const keyboard = { + keys: {}, + + isPressed(code) { + return this.keys[key].pressed; + }, + + isHeld(code) { + return !!this; + }, + + shortcuts: {}, + onShortcut(shortcut, callback) { + /** + * Adds a shortcut handler (shorcut must be in format: {ctrl?: bool, alt?: bool, shift?: bool, key: string (code)}) + * key must be the "code" parameter from keydown event; A key is "KeyA" for example + */ + if (this.shortcuts[shortcut.key] === undefined) + this.shortcuts[shortcut.key] = []; + + this.shortcuts[shortcut.key].push({ + ctrl: shortcut.ctrl, + alt: shortcut.alt, + shift: shortcut.shift, + id: guid(), + callback, + }); + }, + deleteShortcut(id) { + this.shortcuts.keys().forEach((key) => { + this.shortcuts[key] = this.shortcuts[key].filter((v) => v.id !== id); + }); + }, + + listen: { + onkeydown: new Observer(), + onkeyup: new Observer(), + onkeyholdstart: new Observer(), + onkeyholdend: new Observer(), + onkeyclick: new Observer(), + onshortcut: new Observer(), + }, +}; + +window.onkeydown = (evn) => { + keyboard.listen.onkeydown.emit({ + code: evn.code, + key: evn.key, + evn, + }); + + keyboard.keys[evn.code] = { + pressed: true, + held: false, + _hold_to: setTimeout(() => { + keyboard.keys[evn.code].held = true; + delete keyboard.keys[evn.code]._hold_to; + keyboard.listen.onkeyholdstart.emit({ + code: evn.code, + key: evn.key, + evn, + }); + }, inputConfig.keyboardHoldTiming), + }; + + // Process shortcuts + const callbacks = keyboard.shortcuts[evn.code]; + + if (callbacks) + callbacks.forEach((callback) => { + if ( + !!callback.ctrl === evn.ctrlKey && + !!callback.alt === evn.altKey && + !!callback.shift === evn.shiftKey + ) { + keyboard.listen.onshortcut.emit({ + code: evn.code, + key: evn.key, + id: callback.id, + evn, + }); + callback.callback(evn); + } + }); +}; + +window.onkeyup = (evn) => { + keyboard.listen.onkeyup.emit({ + code: evn.code, + key: evn.key, + evn, + }); + if (keyboard.keys[evn.code] && keyboard.keys[evn.code].held) { + keyboard.listen.onkeyholdend.emit({ + code: evn.code, + key: evn.key, + evn, + }); + } else { + keyboard.listen.onkeyclick.emit({ + code: evn.code, + key: evn.key, + evn, + }); + } + + keyboard.keys[evn.code] = { + pressed: false, + held: false, + }; +}; diff --git a/js/util.js b/js/util.js index c1c1eb0..c2db570 100644 --- a/js/util.js +++ b/js/util.js @@ -2,26 +2,42 @@ * Implementation of a simple Oberver Pattern for custom event handling */ function Observer() { - this.handlers = new Set(); + this.handlers = new Set(); } Observer.prototype = { - // Adds handler for this message - on(callback) { - this.handlers.add(callback); - return callback; - }, - clear(callback) { - return this.handlers.delete(callback); - }, - emit(msg) { - this.handlers.forEach(async (handler) => { - try { - await handler(msg); - } catch (e) { - console.warn('Observer failed to run handler'); - console.warn(handler); - } - }); - }, + // Adds handler for this message + on(callback) { + this.handlers.add(callback); + return callback; + }, + clear(callback) { + return this.handlers.delete(callback); + }, + emit(msg) { + this.handlers.forEach(async (handler) => { + try { + await handler(msg); + } catch (e) { + console.warn("Observer failed to run handler"); + console.warn(handler); + } + }); + }, +}; + +/** + * Generates unique id + */ +const guid = (size = 3) => { + const s4 = () => { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + }; + // returns id of format 'aaaaaaaa'-'aaaa'-'aaaa'-'aaaa'-'aaaaaaaaaaaa' + let id = ""; + for (var i = 0; i < size - 1; i++) id += s4() + "-"; + id += s4(); + return id; };