116 lines
3.6 KiB
JavaScript
116 lines
3.6 KiB
JavaScript
export class KeyboardManager {
|
|
|
|
constructor() {
|
|
this.observers = [];
|
|
|
|
KeyboardManager.KeyTab = "Tab";
|
|
KeyboardManager.KeyBackspace = "Backspace";
|
|
KeyboardManager.KeyUp = "ArrowUp";
|
|
KeyboardManager.KeyDown = "ArrowDown";
|
|
|
|
KeyboardManager.KeyModifierShift = "Shift";
|
|
KeyboardManager.KeyModifierCtrl = "Control";
|
|
// ⌘ key on Mac, ⊞ key on Windows
|
|
KeyboardManager.KeyModifierMeta = "Meta";
|
|
KeyboardManager.KeyModifierAlt = "Alt";
|
|
|
|
KeyboardManager.KeyEventDown = "KeyEventDown";
|
|
KeyboardManager.KeyEventUp = "KeyEventUp";
|
|
|
|
KeyboardManager.AllModifiers = [
|
|
KeyboardManager.KeyModifierShift,
|
|
KeyboardManager.KeyModifierCtrl,
|
|
KeyboardManager.KeyModifierMeta,
|
|
KeyboardManager.KeyModifierAlt
|
|
];
|
|
|
|
window.addEventListener('keydown', this.handleKeyDown.bind(this));
|
|
window.addEventListener('keyup', this.handleKeyUp.bind(this));
|
|
}
|
|
|
|
modifiersForEvent(event) {
|
|
const eventModifiers = KeyboardManager.AllModifiers.filter((modifier) => {
|
|
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
|
|
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
|
|
const matches = (
|
|
((event.ctrlKey || event.key == KeyboardManager.KeyModifierCtrl) && modifier === KeyboardManager.KeyModifierCtrl) ||
|
|
((event.metaKey || event.key == KeyboardManager.KeyModifierMeta) && modifier === KeyboardManager.KeyModifierMeta) ||
|
|
((event.altKey || event.key == KeyboardManager.KeyModifierAlt) && modifier === KeyboardManager.KeyModifierAlt) ||
|
|
((event.shiftKey || event.key == KeyboardManager.KeyModifierShift) && modifier === KeyboardManager.KeyModifierShift)
|
|
);
|
|
|
|
return matches;
|
|
});
|
|
|
|
return eventModifiers;
|
|
}
|
|
|
|
eventMatchesKeyAndModifiers(event, key, modifiers = []) {
|
|
const eventModifiers = this.modifiersForEvent(event);
|
|
|
|
if(eventModifiers.length != modifiers.length) {
|
|
return false;
|
|
}
|
|
|
|
for(const modifier of modifiers) {
|
|
if(!eventModifiers.includes(modifier)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Modifers match, check key
|
|
if(!key) {
|
|
return true;
|
|
}
|
|
|
|
// In the browser, shift + f results in key 'f', but in Electron, shift + f results in 'F'
|
|
// In our case we don't differentiate between the two.
|
|
return key.toLowerCase() == event.key.toLowerCase();
|
|
}
|
|
|
|
notifyObserver(event, keyEventType) {
|
|
for(const observer of this.observers) {
|
|
if(observer.element && event.target != observer.element) {
|
|
continue;
|
|
}
|
|
|
|
if(observer.elements && !observer.elements.includes(event.target)) {
|
|
continue;
|
|
}
|
|
|
|
if(observer.notElement && observer.notElement == event.target) {
|
|
continue;
|
|
}
|
|
|
|
if(observer.notElementIds && observer.notElementIds.includes(event.target.id)) {
|
|
continue;
|
|
}
|
|
|
|
if(this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
|
|
const callback = keyEventType == KeyboardManager.KeyEventDown ? observer.onKeyDown : observer.onKeyUp;
|
|
if(callback) {
|
|
callback(event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
handleKeyDown(event) {
|
|
this.notifyObserver(event, KeyboardManager.KeyEventDown);
|
|
}
|
|
|
|
handleKeyUp(event) {
|
|
this.notifyObserver(event, KeyboardManager.KeyEventUp);
|
|
}
|
|
|
|
addKeyObserver({key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds}) {
|
|
const observer = {key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds};
|
|
this.observers.push(observer);
|
|
return observer;
|
|
}
|
|
|
|
removeKeyObserver(observer) {
|
|
this.observers.splice(this.observers.indexOf(observer), 1);
|
|
}
|
|
}
|