feat: support global keyboard input in the dev sample
This commit is contained in:
+79
-32
@@ -213,7 +213,7 @@
|
|||||||
<input id="input" class="hidden-input" type="text" autocomplete="off" aria-hidden="true" tabindex="-1">
|
<input id="input" class="hidden-input" type="text" autocomplete="off" aria-hidden="true" tabindex="-1">
|
||||||
|
|
||||||
<div class="input-row">
|
<div class="input-row">
|
||||||
<div class="hint">Keyboard input is captured directly by the screen</div>
|
<div class="hint">Keyboard works globally: digits, numpad, Enter, Backspace, +, -, *, /, %, ^</div>
|
||||||
<select id="angleMode">
|
<select id="angleMode">
|
||||||
<option value="deg">Degrees</option>
|
<option value="deg">Degrees</option>
|
||||||
<option value="rad">Radians</option>
|
<option value="rad">Radians</option>
|
||||||
@@ -338,10 +338,6 @@
|
|||||||
if (calc.stack.length >= 1) calc.remove(0);
|
if (calc.stack.length >= 1) calc.remove(0);
|
||||||
} else if (name === 'clear') {
|
} else if (name === 'clear') {
|
||||||
calc.clear();
|
calc.clear();
|
||||||
} else if (name === 'enter') {
|
|
||||||
if (calc.isEditing) {
|
|
||||||
pushEditingValueIfNeeded();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
pushEditingValueIfNeeded();
|
pushEditingValueIfNeeded();
|
||||||
calc.command(name);
|
calc.command(name);
|
||||||
@@ -354,7 +350,63 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isInputChar(key) {
|
function isInputChar(key) {
|
||||||
return /^[0-9a-fA-F.+\-]$/.test(key);
|
return /^[0-9a-fA-F.]$/.test(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldIgnoreKeyboardEvent(event) {
|
||||||
|
const target = event.target;
|
||||||
|
if (!target) return false;
|
||||||
|
|
||||||
|
const tagName = target.tagName;
|
||||||
|
return (
|
||||||
|
tagName === 'INPUT' ||
|
||||||
|
tagName === 'TEXTAREA' ||
|
||||||
|
tagName === 'SELECT' ||
|
||||||
|
target.isContentEditable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKeyboardAction(event) {
|
||||||
|
const numpadMap = {
|
||||||
|
Numpad0: { type: 'input', value: '0' },
|
||||||
|
Numpad1: { type: 'input', value: '1' },
|
||||||
|
Numpad2: { type: 'input', value: '2' },
|
||||||
|
Numpad3: { type: 'input', value: '3' },
|
||||||
|
Numpad4: { type: 'input', value: '4' },
|
||||||
|
Numpad5: { type: 'input', value: '5' },
|
||||||
|
Numpad6: { type: 'input', value: '6' },
|
||||||
|
Numpad7: { type: 'input', value: '7' },
|
||||||
|
Numpad8: { type: 'input', value: '8' },
|
||||||
|
Numpad9: { type: 'input', value: '9' },
|
||||||
|
NumpadDecimal: { type: 'input', value: '.' },
|
||||||
|
NumpadAdd: { type: 'command', value: 'add' },
|
||||||
|
NumpadSubtract: { type: 'command', value: 'sub' },
|
||||||
|
NumpadMultiply: { type: 'command', value: 'mul' },
|
||||||
|
NumpadDivide: { type: 'command', value: 'div' },
|
||||||
|
NumpadEnter: { type: 'command', value: 'enter' },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (numpadMap[event.code]) {
|
||||||
|
return numpadMap[event.code];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInputChar(event.key)) {
|
||||||
|
return { type: 'input', value: event.key };
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyMap = {
|
||||||
|
Enter: { type: 'command', value: 'enter' },
|
||||||
|
Backspace: { type: 'edit', value: 'Backspace' },
|
||||||
|
Escape: { type: 'command', value: 'clear' },
|
||||||
|
'+': { type: 'command', value: 'add' },
|
||||||
|
'-': { type: 'command', value: 'sub' },
|
||||||
|
'*': { type: 'command', value: 'mul' },
|
||||||
|
'/': { type: 'command', value: 'div' },
|
||||||
|
'%': { type: 'command', value: 'mod' },
|
||||||
|
'^': { type: 'command', value: 'pow' },
|
||||||
|
};
|
||||||
|
|
||||||
|
return keyMap[event.key] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusScreen() {
|
function focusScreen() {
|
||||||
@@ -382,47 +434,42 @@
|
|||||||
syncInputFromState();
|
syncInputFromState();
|
||||||
}
|
}
|
||||||
|
|
||||||
screen.addEventListener('keydown', (event) => {
|
function handleKeydown(event) {
|
||||||
|
if (shouldIgnoreKeyboardEvent(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = getKeyboardAction(event);
|
||||||
|
if (!action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (event.key === 'Enter') {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
execute('enter');
|
|
||||||
|
if (action.type === 'input') {
|
||||||
|
editXWithKey(action.value);
|
||||||
|
render();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'Backspace') {
|
if (action.type === 'edit') {
|
||||||
event.preventDefault();
|
|
||||||
if (calc.isEditing) {
|
if (calc.isEditing) {
|
||||||
editXWithKey('Backspace');
|
editXWithKey(action.value);
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInputChar(event.key)) {
|
if (action.type === 'command') {
|
||||||
event.preventDefault();
|
execute(action.value);
|
||||||
editXWithKey(event.key);
|
|
||||||
render();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyMap = {
|
|
||||||
'+': 'add',
|
|
||||||
'-': 'sub',
|
|
||||||
'*': 'mul',
|
|
||||||
'/': 'div',
|
|
||||||
'%': 'mod',
|
|
||||||
'^': 'pow',
|
|
||||||
};
|
|
||||||
|
|
||||||
if (keyMap[event.key]) {
|
|
||||||
event.preventDefault();
|
|
||||||
execute(keyMap[event.key]);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorEl.textContent = error.message;
|
errorEl.textContent = error.message;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
window.addEventListener('keydown', handleKeydown);
|
||||||
|
|
||||||
screen.addEventListener('click', focusScreen);
|
screen.addEventListener('click', focusScreen);
|
||||||
window.addEventListener('load', focusScreen);
|
window.addEventListener('load', focusScreen);
|
||||||
|
|||||||
Reference in New Issue
Block a user