feat: decouple input normalization from action registry
This commit is contained in:
parent
ebf3d2dc28
commit
7fc3935f05
3
TODO.md
3
TODO.md
|
@ -17,4 +17,5 @@
|
|||
- ✔ EventEmitter::emit is too hot
|
||||
- ❌ entityList.fromJSON()
|
||||
- ❌ Socket WebWorker can't connect in Firefox
|
||||
- ❌ Transients
|
||||
- ✔ Entity packets
|
||||
- ✔ Decouple input normalization from action registry
|
||||
|
|
79
packages/input/action-registry.js
Normal file
79
packages/input/action-registry.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
import * as I from 'immutable';
|
||||
|
||||
import {compose} from '@avocado/core';
|
||||
import {EventEmitter} from '@avocado/mixins';
|
||||
|
||||
const decorate = compose(
|
||||
EventEmitter,
|
||||
);
|
||||
|
||||
export class ActionRegistry extends decorate(class {}) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.mapActionToKey = new Map();
|
||||
this.mapKeyToAction = {};
|
||||
this.normalizer = undefined;
|
||||
this._state = I.Map();
|
||||
}
|
||||
|
||||
actionForKey(key) {
|
||||
return this.mapKeyToAction[key];
|
||||
}
|
||||
|
||||
listen(normalizer) {
|
||||
// Only listen once.
|
||||
if (this.normalizer) {
|
||||
return;
|
||||
}
|
||||
this.normalizer = normalizer;
|
||||
this.normalizer.on('keyUp', this.onKeyUp, this);
|
||||
this.normalizer.on('keyDown', this.onKeyDown, this);
|
||||
}
|
||||
|
||||
keyForAction(action) {
|
||||
return this.mapActionToKey.get(action);
|
||||
}
|
||||
|
||||
mapKeysToActions(map) {
|
||||
for (const key in map) {
|
||||
const action = map[key];
|
||||
this.mapKeyToAction[key] = action;
|
||||
this.mapActionToKey.set(action, key);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyDown(key) {
|
||||
if (this.mapKeyToAction[key]) {
|
||||
const action = this.mapKeyToAction[key];
|
||||
this._state = this._state.set(action, true);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyUp(key) {
|
||||
if (this.mapKeyToAction[key]) {
|
||||
const action = this.mapKeyToAction[key];
|
||||
this._state = this._state.delete(action);
|
||||
}
|
||||
}
|
||||
|
||||
get state() {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
set state(state) {
|
||||
this._state = state;
|
||||
}
|
||||
|
||||
stopListening(normalizer) {
|
||||
if (!this.normalizer) {
|
||||
return;
|
||||
}
|
||||
this.normalizer.off('keyUp', this.onKeyUp);
|
||||
this.normalizer.off('keyDown', this.onKeyDown);
|
||||
this.normalizer = undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {InputPacket} from './packet/input.packet';
|
|
@ -1,126 +1,3 @@
|
|||
import * as I from 'immutable';
|
||||
|
||||
import {compose} from '@avocado/core';
|
||||
import {EventEmitter} from '@avocado/mixins';
|
||||
|
||||
const decorate = compose(
|
||||
EventEmitter,
|
||||
);
|
||||
|
||||
export class ActionRegistry extends decorate(class {}) {
|
||||
|
||||
static normalizeKey(key) {
|
||||
return key.toLowerCase();
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// Track events.
|
||||
this.target = undefined;
|
||||
this.mapActionToKey = new Map();
|
||||
this.mapKeyToAction = {};
|
||||
// Track actions.
|
||||
this._state = I.Map();
|
||||
// Handle lame OS input event behavior. See: https://mzl.la/2Ob0WQE
|
||||
this.keysDown = {};
|
||||
this.keyUpDelays = {};
|
||||
// Bind event handlers.
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.onKeyUp = this.onKeyUp.bind(this);
|
||||
}
|
||||
|
||||
actionForKey(key) {
|
||||
return this.mapKeyToAction[key];
|
||||
}
|
||||
|
||||
listen(target = window.document) {
|
||||
// Only listen once.
|
||||
if (this.target) {
|
||||
return;
|
||||
}
|
||||
this.target = target;
|
||||
this.target.addEventListener('blur', this.onBlur);
|
||||
this.target.addEventListener('keydown', this.onKeyDown);
|
||||
this.target.addEventListener('keyup', this.onKeyUp);
|
||||
}
|
||||
|
||||
keyForAction(action) {
|
||||
return this.mapActionToKey.get(action);
|
||||
}
|
||||
|
||||
mapKeysToActions(map) {
|
||||
for (const key in map) {
|
||||
const action = map[key];
|
||||
this.mapKeyToAction[key] = action;
|
||||
this.mapActionToKey.set(action, key);
|
||||
}
|
||||
}
|
||||
|
||||
onBlur(event) {
|
||||
event = event || window.event;
|
||||
this.setAllKeysUp();
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
event = event || window.event;
|
||||
const key = this.constructor.normalizeKey(event.key);
|
||||
if (this.keysDown[key]) {
|
||||
if (this.keyUpDelays[key]) {
|
||||
clearTimeout(this.keyUpDelays[key]);
|
||||
delete this.keyUpDelays[key];
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.keysDown[key] = true;
|
||||
if (this.mapKeyToAction[key]) {
|
||||
const action = this.mapKeyToAction[key];
|
||||
this._state = this._state.set(action, true);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyUp(event) {
|
||||
event = event || window.event;
|
||||
const key = this.constructor.normalizeKey(event.key);
|
||||
this.keyUpDelays[key] = setTimeout(() => {
|
||||
delete this.keyUpDelays[key];
|
||||
delete this.keysDown[key];
|
||||
if (this.mapKeyToAction[key]) {
|
||||
const action = this.mapKeyToAction[key];
|
||||
this._state = this._state.delete(action);
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
setAllKeysUp() {
|
||||
this.keysDown = {};
|
||||
for (const key in this.keyUpDelays) {
|
||||
const handle = this.keyUpDelays[key];
|
||||
clearTimeout(handle);
|
||||
}
|
||||
this.keyUpDelays = {};
|
||||
this._state = I.Map();
|
||||
}
|
||||
|
||||
get state() {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
set state(state) {
|
||||
this._state = state;
|
||||
}
|
||||
|
||||
stopListening() {
|
||||
this.setAllKeysUp();
|
||||
if (!this.target) {
|
||||
return;
|
||||
}
|
||||
this.target.removeEventListener('blur', this.onBlur);
|
||||
this.target.removeEventListener('keydown', this.onKeyDown);
|
||||
this.target.removeEventListener('keyup', this.onKeyUp);
|
||||
this.target = undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {ActionRegistry} from './action-registry';
|
||||
export {InputNormalizer} from './normalizer';
|
||||
export {InputPacket} from './packet/input.packet';
|
||||
|
|
88
packages/input/normalizer.js
Normal file
88
packages/input/normalizer.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
import {compose} from '@avocado/core';
|
||||
import {EventEmitter} from '@avocado/mixins';
|
||||
|
||||
const decorate = compose(
|
||||
EventEmitter,
|
||||
);
|
||||
|
||||
export class InputNormalizer extends decorate(class{}) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// Track events.
|
||||
this.target = undefined;
|
||||
this.targetForKeyUp = undefined;
|
||||
// Handle lame OS input event behavior. See: https://mzl.la/2Ob0WQE
|
||||
this.keysDown = {};
|
||||
this.keyUpDelays = {};
|
||||
// Bind event handlers.
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.onKeyUp = this.onKeyUp.bind(this);
|
||||
}
|
||||
|
||||
actionForKey(key) {
|
||||
return this.mapKeyToAction[key];
|
||||
}
|
||||
|
||||
listen(target = window.document, targetForKeyUp = window.document) {
|
||||
// Only listen once.
|
||||
if (this.target) {
|
||||
return;
|
||||
}
|
||||
this.target = target;
|
||||
this.targetForKeyUp = targetForKeyUp;
|
||||
this.target.addEventListener('blur', this.onBlur);
|
||||
this.target.addEventListener('keydown', this.onKeyDown);
|
||||
this.targetForKeyUp.addEventListener('keyup', this.onKeyUp);
|
||||
}
|
||||
|
||||
onBlur(event) {
|
||||
this.setAllKeysUp();
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
const {key} = event;
|
||||
if (this.keysDown[key]) {
|
||||
if (this.keyUpDelays[key]) {
|
||||
clearTimeout(this.keyUpDelays[key]);
|
||||
delete this.keyUpDelays[key];
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.keysDown[key] = true;
|
||||
this.emit('keyDown', key);
|
||||
}
|
||||
|
||||
onKeyUp(event) {
|
||||
const {key} = event;
|
||||
this.keyUpDelays[key] = setTimeout(() => {
|
||||
delete this.keyUpDelays[key];
|
||||
delete this.keysDown[key];
|
||||
this.emit('keyUp', key);
|
||||
}, 20);
|
||||
}
|
||||
|
||||
setAllKeysUp() {
|
||||
this.keysDown = {};
|
||||
for (const key in this.keyUpDelays) {
|
||||
const handle = this.keyUpDelays[key];
|
||||
clearTimeout(handle);
|
||||
this.emit('keyUp', key);
|
||||
}
|
||||
this.keyUpDelays = {};
|
||||
}
|
||||
|
||||
stopListening() {
|
||||
this.setAllKeysUp();
|
||||
if (!this.target) {
|
||||
return;
|
||||
}
|
||||
this.target.removeEventListener('blur', this.onBlur);
|
||||
this.target.removeEventListener('keydown', this.onKeyDown);
|
||||
this.targetForKeyUp.removeEventListener('keyup', this.onKeyUp);
|
||||
this.target = undefined;
|
||||
this.targetForKeyUp = undefined;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user