scratch-vm/src/io/keyboard.js

151 lines
4.6 KiB
JavaScript
Raw Normal View History

2017-04-17 15:10:04 -04:00
const Cast = require('../util/cast');
2018-05-02 15:34:17 -04:00
/**
* Names used internally for keys used in scratch, also known as "scratch keys".
* @enum {string}
*/
const KEY_NAME = {
SPACE: 'space',
LEFT: 'left arrow',
UP: 'up arrow',
RIGHT: 'right arrow',
DOWN: 'down arrow'
};
/**
* An array of the names of scratch keys.
* @type {Array<string>}
*/
const KEY_NAME_LIST = Object.keys(KEY_NAME).map(name => KEY_NAME[name]);
2017-04-17 19:42:48 -04:00
class Keyboard {
constructor (runtime) {
/**
* List of currently pressed scratch keys.
* A scratch key is:
* A key you can press on a keyboard, excluding modifier keys.
* An uppercase string of length one;
* except for special key names for arrow keys and space (e.g. 'left arrow').
* Can be a non-english unicode letter like: æ ø ש נ 廿.
* @type{Array.<string>}
2017-04-17 19:42:48 -04:00
*/
this._keysPressed = [];
/**
* Reference to the owning Runtime.
* Can be used, for example, to activate hats.
* @type{!Runtime}
*/
this.runtime = runtime;
}
/**
* Convert from a keyboard event key name to a Scratch key name.
* @param {string} keyString the input key string.
* @return {string} the corresponding Scratch key, or an empty string.
*/
_keyStringToScratchKey (keyString) {
keyString = Cast.toString(keyString);
// Convert space and arrow keys to their Scratch key names.
2017-04-17 19:42:48 -04:00
switch (keyString) {
2018-05-02 15:34:17 -04:00
case ' ': return KEY_NAME.SPACE;
2018-04-30 17:06:23 -04:00
case 'ArrowLeft':
2018-05-02 15:34:17 -04:00
case 'Left': return KEY_NAME.LEFT;
2018-04-30 17:06:23 -04:00
case 'ArrowUp':
2018-05-02 15:34:17 -04:00
case 'Up': return KEY_NAME.UP;
2018-04-30 17:06:23 -04:00
case 'Right':
2018-05-02 15:34:17 -04:00
case 'ArrowRight': return KEY_NAME.RIGHT;
2018-04-30 17:06:23 -04:00
case 'Down':
2018-05-02 15:34:17 -04:00
case 'ArrowDown': return KEY_NAME.DOWN;
2017-04-17 19:42:48 -04:00
}
// Ignore modifier keys
if (keyString.length > 1) {
return '';
}
return keyString.toUpperCase();
}
2017-04-17 19:42:48 -04:00
/**
* Convert from a block argument to a Scratch key name.
* @param {string} keyArg the input arg.
* @return {string} the corresponding Scratch key.
2017-04-17 19:42:48 -04:00
*/
_keyArgToScratchKey (keyArg) {
// If a number was dropped in, try to convert from ASCII to Scratch key.
if (typeof keyArg === 'number') {
// Check for the ASCII range containing numbers, some punctuation,
// and uppercase letters.
if (keyArg >= 48 && keyArg <= 90) {
return String.fromCharCode(keyArg);
}
switch (keyArg) {
2018-05-02 15:34:17 -04:00
case 32: return KEY_NAME.SPACE;
case 37: return KEY_NAME.LEFT;
case 38: return KEY_NAME.UP;
case 39: return KEY_NAME.RIGHT;
case 40: return KEY_NAME.DOWN;
}
}
keyArg = Cast.toString(keyArg);
// If the arg matches a special key name, return it.
2018-05-02 15:34:17 -04:00
if (KEY_NAME_LIST.includes(keyArg)) {
return keyArg;
}
// Use only the first character.
if (keyArg.length > 1) {
keyArg = keyArg[0];
}
2018-04-30 17:05:33 -04:00
// Check for the space character.
if (keyArg === ' ') {
2018-05-02 15:34:17 -04:00
return KEY_NAME.SPACE;
2018-04-30 17:05:33 -04:00
}
return keyArg.toUpperCase();
}
2017-04-17 19:42:48 -04:00
/**
* Keyboard DOM event handler.
* @param {object} data Data from DOM event.
*/
postData (data) {
if (!data.key) return;
const scratchKey = this._keyStringToScratchKey(data.key);
if (scratchKey === '') return;
const index = this._keysPressed.indexOf(scratchKey);
if (data.isDown) {
// If not already present, add to the list.
if (index < 0) {
this._keysPressed.push(scratchKey);
}
// Always trigger hats, even if it was already pressed.
this.runtime.startHats('event_whenkeypressed', {
KEY_OPTION: scratchKey
});
this.runtime.startHats('event_whenkeypressed', {
KEY_OPTION: 'any'
});
} else if (index > -1) {
// If already present, remove from the list.
this._keysPressed.splice(index, 1);
}
}
2017-04-17 19:42:48 -04:00
/**
* Get key down state for a specified key.
* @param {Any} keyArg key argument.
2017-04-17 19:42:48 -04:00
* @return {boolean} Is the specified key down?
*/
getKeyIsDown (keyArg) {
if (keyArg === 'any') {
2017-04-17 19:42:48 -04:00
return this._keysPressed.length > 0;
}
const scratchKey = this._keyArgToScratchKey(keyArg);
return this._keysPressed.indexOf(scratchKey) > -1;
}
2017-04-17 19:42:48 -04:00
}
module.exports = Keyboard;