diff --git a/src/core/constants.js b/src/core/constants.js index 6c36301c70..f5f8e89eab 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -879,92 +879,104 @@ export const MITER = 'miter'; * @final */ export const AUTO = 'auto'; - +// INPUT /** - * @typedef {18} ALT + * @typedef {'AltLeft' | 'AltRight'} ALT * @property {ALT} ALT * @final */ -// INPUT -export const ALT = 18; +export const ALT = 'AltLeft'; + /** - * @typedef {8} BACKSPACE + * @typedef {'Backspace'} BACKSPACE * @property {BACKSPACE} BACKSPACE * @final */ -export const BACKSPACE = 8; +export const BACKSPACE = 'Backspace'; + /** - * @typedef {17} CONTROL + * @typedef {'ControlLeft' | 'ControlRight'} CONTROL * @property {CONTROL} CONTROL * @final */ -export const CONTROL = 17; +export const CONTROL = 'ControlLeft'; + /** - * @typedef {46} DELETE + * @typedef {'Delete'} DELETE * @property {DELETE} DELETE * @final */ -export const DELETE = 46; +export const DELETE = 'Delete'; + /** - * @typedef {40} DOWN_ARROW + * @typedef {'ArrowDown'} DOWN_ARROW * @property {DOWN_ARROW} DOWN_ARROW * @final */ -export const DOWN_ARROW = 40; +export const DOWN_ARROW = 'ArrowDown'; + /** - * @typedef {13} ENTER + * @typedef {'Enter'} ENTER * @property {ENTER} ENTER * @final */ -export const ENTER = 13; +export const ENTER = 'Enter'; + /** - * @typedef {27} ESCAPE + * @typedef {'Escape'} ESCAPE * @property {ESCAPE} ESCAPE * @final */ -export const ESCAPE = 27; +export const ESCAPE = 'Escape'; + /** - * @typedef {37} LEFT_ARROW + * @typedef {'ArrowLeft'} LEFT_ARROW * @property {LEFT_ARROW} LEFT_ARROW * @final */ -export const LEFT_ARROW = 37; +export const LEFT_ARROW = 'ArrowLeft'; + /** - * @typedef {18} OPTION + * @typedef {'AltLeft' | 'AltRight'} OPTION * @property {OPTION} OPTION * @final */ -export const OPTION = 18; +export const OPTION = 'AltLeft'; + /** - * @typedef {13} RETURN + * @typedef {'Enter'} RETURN * @property {RETURN} RETURN * @final */ -export const RETURN = 13; +export const RETURN = 'Enter'; + /** - * @typedef {39} RIGHT_ARROW + * @typedef {'ArrowRight'} RIGHT_ARROW * @property {RIGHT_ARROW} RIGHT_ARROW * @final */ -export const RIGHT_ARROW = 39; +export const RIGHT_ARROW = 'ArrowRight'; + /** - * @typedef {16} SHIFT + * @typedef {'ShiftLeft' | 'ShiftRight'} SHIFT * @property {SHIFT} SHIFT * @final */ -export const SHIFT = 16; +export const SHIFT = 'ShiftLeft'; + /** - * @typedef {9} TAB + * @typedef {'Tab'} TAB * @property {TAB} TAB * @final */ -export const TAB = 9; +export const TAB = 'Tab'; + /** - * @typedef {38} UP_ARROW + * @typedef {'ArrowUp'} UP_ARROW * @property {UP_ARROW} UP_ARROW * @final */ -export const UP_ARROW = 38; +export const UP_ARROW = 'ArrowUp'; // RENDERING /** diff --git a/src/core/main.js b/src/core/main.js index 0e31be3397..9b65ef009b 100644 --- a/src/core/main.js +++ b/src/core/main.js @@ -422,6 +422,7 @@ class p5 { this._styles = []; this._downKeys = {}; //Holds the key codes of currently pressed keys + this._downKeyCodes = {}; } } diff --git a/src/events/keyboard.js b/src/events/keyboard.js index a2554947ce..58f19095c1 100644 --- a/src/events/keyboard.js +++ b/src/events/keyboard.js @@ -4,7 +4,17 @@ * @for p5 * @requires core */ +export function isCode(input) { + if (typeof input !== 'string') { + return false; + } + if (input.length === 1 && /[0-9]/.test(input)) { + return true; + } + + return input.length > 1; +} function keyboard(p5, fn){ /** * A `Boolean` system variable that's `true` if any key is currently pressed @@ -95,7 +105,9 @@ function keyboard(p5, fn){ * * */ + fn.keyIsPressed = false; + fn.code = null; /** * A `String` system variable that contains the value of the last key typed. @@ -439,14 +451,15 @@ function keyboard(p5, fn){ * */ fn._onkeydown = function(e) { - if (this._downKeys[e.which]) { - // prevent multiple firings + if (this._downKeys[e.code]) { return; } this.keyIsPressed = true; this.keyCode = e.which; - this._downKeys[e.which] = true; - this.key = e.key || String.fromCharCode(e.which) || e.which; + this.key = e.key; + this.code = e.code; + this._downKeyCodes[e.code] = true; + this._downKeys[e.key] = true; const context = this._isGlobal ? window : this; if (typeof context.keyPressed === 'function' && !e.charCode) { const executeDefault = context.keyPressed(e); @@ -612,17 +625,20 @@ function keyboard(p5, fn){ * */ fn._onkeyup = function(e) { - this._downKeys[e.which] = false; + delete this._downKeyCodes[e.code]; + delete this._downKeys[e.key]; + if (!this._areDownKeys()) { this.keyIsPressed = false; + this.key = ''; + this.code = null; + } else { + // If other keys are still pressed, update code to the last pressed key + const lastPressedKey = Object.keys(this._downKeys).pop(); + this.code = lastPressedKey; } - this._lastKeyCodeTyped = null; - - this.key = e.key || String.fromCharCode(e.which) || e.which; - this.keyCode = e.which; - const context = this._isGlobal ? window : this; if (typeof context.keyReleased === 'function') { const executeDefault = context.keyReleased(e); @@ -807,7 +823,7 @@ function keyboard(p5, fn){ * keycode.info. * * @method keyIsDown - * @param {Number} code key to check. + * @param {Number|String} code key to check. * @return {Boolean} whether the key is down or not. * * @example @@ -899,11 +915,23 @@ function keyboard(p5, fn){ * * */ - fn.keyIsDown = function(code) { - // p5._validateParameters('keyIsDown', arguments); - return this._downKeys[code] || false; - }; + function isCode(input) { + if (typeof input !== 'string') { + return false; + } + if (input.length === 1 && /[0-9]/.test(input)) { + return true; + } + return input.length > 1; + } + fn.keyIsDown = function(input) { + if (isCode(input)) { + return this._downKeyCodes[input] || this._downKeys[input] || false; + } else { + return this._downKeys[input] || this._downKeyCodes[input] || false; + } + } /** * The _areDownKeys function returns a boolean true if any keys pressed * and a false if no keys are currently pressed. diff --git a/test/unit/events/keyboard.js b/test/unit/events/keyboard.js index 0e55657e88..dc31f62336 100644 --- a/test/unit/events/keyboard.js +++ b/test/unit/events/keyboard.js @@ -1,4 +1,5 @@ import p5 from '../../../src/app.js'; +import { isCode } from '../../../src/events/keyboard.js'; import { parallelSketches } from '../../js/p5_helpers'; suite('Keyboard Events', function() { @@ -164,18 +165,62 @@ suite('Keyboard Events', function() { }); }); + suite('isCode', function() { + test('returns false for non-string inputs', function() { + assert.isFalse(isCode(null)); + assert.isFalse(isCode(undefined)); + assert.isFalse(isCode(123)); + assert.isFalse(isCode({})); + assert.isFalse(isCode([])); + }); + + test('returns false for single non-digit characters', function() { + assert.isFalse(isCode('a')); + assert.isFalse(isCode('Z')); + assert.isFalse(isCode('!')); + assert.isFalse(isCode(' ')); + }); + + test('returns true for multi-character strings', function() { + assert.isTrue(isCode('Enter')); + assert.isTrue(isCode('ArrowUp')); + assert.isTrue(isCode('Shift')); + assert.isTrue(isCode('Control')); + assert.isTrue(isCode('ab')); + }); + + test('handles edge cases correctly', function() { + assert.isFalse(isCode('')); // empty string + assert.isTrue(isCode('11')); // multi-digit number + assert.isTrue(isCode('1a')); // digit + letter + }); + }); + suite('p5.prototype.keyIsDown', function() { test('keyIsDown should return a boolean', function() { - assert.isBoolean(myp5.keyIsDown(65)); + assert.isBoolean(myp5.keyIsDown('a')); + assert.isBoolean(myp5.keyIsDown('Enter')); }); test('keyIsDown should return true if key is down', function() { - window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 35 })); - assert.strictEqual(myp5.keyIsDown(35), true); - }); - - test.todo('keyIsDown should return false if key is not down', function() { - assert.strictEqual(myp5.keyIsDown(35), false); + // Test single character keys + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' })); + assert.strictEqual(myp5.keyIsDown('a'), true); + + // Test digit keys + window.dispatchEvent(new KeyboardEvent('keydown', { key: '1', code: 'Digit1' })); + assert.strictEqual(myp5.keyIsDown('1'), true); + + // Test special keys + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter' })); + assert.strictEqual(myp5.keyIsDown('Enter'), true); + }); + + test('keyIsDown should return false if key is not down', function() { + // Ensure key is not down + window.dispatchEvent(new KeyboardEvent('keyup')); + assert.strictEqual(myp5.keyIsDown('z'), false); + }); }); });