phetsims / projectile-motion

"Projectile Motion" is an educational simulation in HTML5, by PhET Interactive Simulations.
GNU General Public License v3.0
15 stars 13 forks source link

Add the ability to enter custom values from the keyboard #307

Open jbphet opened 1 year ago

jbphet commented 1 year ago

The "Lab" screen in this sim supports the ability to specify a "Custom" projectile, and then enter values for several parameters such as mass, diameter, etc. Currently, the user has to interact with the keypad in the sim to enter these values, and can't just type the numbers on their keyboard. Part of the reason for this behavior is that we didn't want to make the inputs real HTML input fields, because that caused the keyboard to pop up on devices like iPads. With the recent advances that we've made on keyboard input listeners (see https://github.com/phetsims/scenery/issues/1445), it should now be possible to add a global key listener that would support entering numeric values.

Here is a screenshot from the Lab screen that shows the state where the values can be entered:

image

During the 10/27/2022 developer meeting, we discussed adding numeric entry to several sims, and decided to set up this particular issue as a sort of "pilot project" for handling numeric entry from the keypad. Some of what is done here may need to in common code to make it easier to add the same or similar functionality to other sims.

zepumph commented 1 year ago

@matthew-blackman and I experiemented with this a bit, just in a basic case where you can focus the keypad and listen to number keys, it worked well enough that I'll put the patch in here for next time:

```diff Index: js/keypad/Key.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/js/keypad/Key.ts b/js/keypad/Key.ts --- a/js/keypad/Key.ts (revision 5a9d545803ad99f6199953954520ab0dc9a100d4) +++ b/js/keypad/Key.ts (date 1668185846827) @@ -15,6 +15,7 @@ type SelfOptions = { horizontalSpan?: number; verticalSpan?: number; + keyboardIdentifier?: string | null; }; export type KeyOptions = SelfOptions; @@ -30,6 +31,8 @@ // The tandem component name to use when creating a button from this key. public readonly buttonTandemName: string; + public readonly keyboardIdentifier: string | null; + /** * @param label - node or string that will appear on the key * @param identifier - ID for this key, see KeyID.js @@ -42,11 +45,13 @@ const options = optionize()( { horizontalSpan: 1, - verticalSpan: 1 + verticalSpan: 1, + keyboardIdentifier: null }, providedOptions ); this.horizontalSpan = options.horizontalSpan; this.verticalSpan = options.verticalSpan; + this.keyboardIdentifier = options.keyboardIdentifier; this.buttonTandemName = `${_.camelCase( this.identifier )}Button`; } Index: js/demo/components/demoKeypad.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/js/demo/components/demoKeypad.ts b/js/demo/components/demoKeypad.ts --- a/js/demo/components/demoKeypad.ts (revision 5a9d545803ad99f6199953954520ab0dc9a100d4) +++ b/js/demo/components/demoKeypad.ts (date 1668185846856) @@ -200,8 +200,8 @@ resize: false, children: [ integerVBox, - floatingPointVBox, - positiveAndNegativeFloatingPointVBox + // floatingPointVBox, + // positiveAndNegativeFloatingPointVBox ], center: layoutBounds.center } ); Index: js/keypad/Keypad.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/js/keypad/Keypad.ts b/js/keypad/Keypad.ts --- a/js/keypad/Keypad.ts (revision 5a9d545803ad99f6199953954520ab0dc9a100d4) +++ b/js/keypad/Keypad.ts (date 1668186023976) @@ -9,7 +9,7 @@ import merge from '../../../phet-core/js/merge.js'; import optionize from '../../../phet-core/js/optionize.js'; -import { Font, Node, NodeOptions, Text, TPaint } from '../../../scenery/js/imports.js'; +import { Font, KeyboardListener, Node, NodeOptions, Text, TPaint } from '../../../scenery/js/imports.js'; import RectangularPushButton from '../../../sun/js/buttons/RectangularPushButton.js'; import Tandem from '../../../tandem/js/Tandem.js'; import BackspaceIcon from '../BackspaceIcon.js'; @@ -29,16 +29,16 @@ const DEFAULT_BUTTON_COLOR = 'white'; const PLUS_CHAR = '\u002b'; const MINUS_CHAR = '\u2212'; -const _0 = new Key( '0', KeyID.ZERO ); -const _1 = new Key( '1', KeyID.ONE ); -const _2 = new Key( '2', KeyID.TWO ); -const _3 = new Key( '3', KeyID.THREE ); -const _4 = new Key( '4', KeyID.FOUR ); -const _5 = new Key( '5', KeyID.FIVE ); -const _6 = new Key( '6', KeyID.SIX ); -const _7 = new Key( '7', KeyID.SEVEN ); -const _8 = new Key( '8', KeyID.EIGHT ); -const _9 = new Key( '9', KeyID.NINE ); +const _0 = new Key( '0', KeyID.ZERO, { keyboardIdentifier: '0' } ); +const _1 = new Key( '1', KeyID.ONE, { keyboardIdentifier: '1' } ); +const _2 = new Key( '2', KeyID.TWO, { keyboardIdentifier: '2' } ); +const _3 = new Key( '3', KeyID.THREE, { keyboardIdentifier: '3' } ); +const _4 = new Key( '4', KeyID.FOUR, { keyboardIdentifier: '4' } ); +const _5 = new Key( '5', KeyID.FIVE, { keyboardIdentifier: '5' } ); +const _6 = new Key( '6', KeyID.SIX, { keyboardIdentifier: '6' } ); +const _7 = new Key( '7', KeyID.SEVEN, { keyboardIdentifier: '7' } ); +const _8 = new Key( '8', KeyID.EIGHT, { keyboardIdentifier: '8' } ); +const _9 = new Key( '9', KeyID.NINE, { keyboardIdentifier: '9' } ); const WIDE_ZERO = new Key( '0', KeyID.ZERO, { horizontalSpan: 2 } ); const BACKSPACE_KEY = new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), KeyID.BACKSPACE ); const PLUS_MINUS_KEY = new Key( `${PLUS_CHAR}/${MINUS_CHAR}`, KeyID.PLUS_MINUS ); @@ -90,7 +90,7 @@ */ public constructor( layout: ( Key | null )[][], providedOptions?: KeypadOptions ) { - const options = optionize()( { + const options = optionize()( { buttonWidth: DEFAULT_BUTTON_WIDTH, buttonHeight: DEFAULT_BUTTON_HEIGHT, xSpacing: 10, @@ -102,7 +102,10 @@ accumulator: null, accumulatorOptions: null, tandem: Tandem.REQUIRED, - tandemNameSuffix: 'Keypad' + tandemNameSuffix: 'Keypad', + + tagName: 'div', + focusable: true }, providedOptions ); super(); @@ -148,12 +151,15 @@ } } + const keyboardKeys: IntentionalAny = []; + // interpret the layout specification for ( let row = 0; row < layout.length; row++ ) { const startRow = row; for ( let column = 0; column < layout[ row ].length; column++ ) { const button = layout[ row ][ column ]; if ( button ) { + button.keyboardIdentifier && keyboardKeys.push( button.keyboardIdentifier ); const keyBefore = layout[ row ][ column - 1 ]; const startColumn = column + ( column > 0 && keyBefore ? @@ -181,6 +187,13 @@ } } + this.addInputListener( new KeyboardListener( { + keys: keyboardKeys, + callback: ( x ) => { + console.log( x ); + } + } ) ); + this.mutate( options ); }
matthew-blackman commented 1 year ago

@zepumph and I worked on this in a patch and got it to the point where one can type into the keypad if it is in focus. The solution is still a bit hacky and not complete but it seems to work based on a first pass. Patch below:

```Index: js/keypad/Keypad.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/js/keypad/Keypad.ts b/js/keypad/Keypad.ts --- a/js/keypad/Keypad.ts (revision 4b6693637f6713bcfb225e68f528ae2820be553f) +++ b/js/keypad/Keypad.ts (date 1668625387184) @@ -9,7 +9,7 @@ import merge from '../../../phet-core/js/merge.js'; import optionize from '../../../phet-core/js/optionize.js'; -import { Font, Node, NodeOptions, Text, TPaint } from '../../../scenery/js/imports.js'; +import { Font, KeyboardListener, Node, NodeOptions, Text, TPaint } from '../../../scenery/js/imports.js'; import RectangularPushButton from '../../../sun/js/buttons/RectangularPushButton.js'; import Tandem from '../../../tandem/js/Tandem.js'; import BackspaceIcon from '../BackspaceIcon.js'; @@ -29,16 +29,16 @@ const DEFAULT_BUTTON_COLOR = 'white'; const PLUS_CHAR = '\u002b'; const MINUS_CHAR = '\u2212'; -const _0 = new Key( '0', KeyID.ZERO ); -const _1 = new Key( '1', KeyID.ONE ); -const _2 = new Key( '2', KeyID.TWO ); -const _3 = new Key( '3', KeyID.THREE ); -const _4 = new Key( '4', KeyID.FOUR ); -const _5 = new Key( '5', KeyID.FIVE ); -const _6 = new Key( '6', KeyID.SIX ); -const _7 = new Key( '7', KeyID.SEVEN ); -const _8 = new Key( '8', KeyID.EIGHT ); -const _9 = new Key( '9', KeyID.NINE ); +const _0 = new Key( '0', KeyID.ZERO, { keyboardIdentifier: '0' } ); +const _1 = new Key( '1', KeyID.ONE, { keyboardIdentifier: '1' } ); +const _2 = new Key( '2', KeyID.TWO, { keyboardIdentifier: '2' } ); +const _3 = new Key( '3', KeyID.THREE, { keyboardIdentifier: '3' } ); +const _4 = new Key( '4', KeyID.FOUR, { keyboardIdentifier: '4' } ); +const _5 = new Key( '5', KeyID.FIVE, { keyboardIdentifier: '5' } ); +const _6 = new Key( '6', KeyID.SIX, { keyboardIdentifier: '6' } ); +const _7 = new Key( '7', KeyID.SEVEN, { keyboardIdentifier: '7' } ); +const _8 = new Key( '8', KeyID.EIGHT, { keyboardIdentifier: '8' } ); +const _9 = new Key( '9', KeyID.NINE, { keyboardIdentifier: '9' } ); const WIDE_ZERO = new Key( '0', KeyID.ZERO, { horizontalSpan: 2 } ); const BACKSPACE_KEY = new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), KeyID.BACKSPACE ); const PLUS_MINUS_KEY = new Key( `${PLUS_CHAR}/${MINUS_CHAR}`, KeyID.PLUS_MINUS ); @@ -90,7 +90,7 @@ */ public constructor( layout: ( Key | null )[][], providedOptions?: KeypadOptions ) { - const options = optionize()( { + const options = optionize()( { buttonWidth: DEFAULT_BUTTON_WIDTH, buttonHeight: DEFAULT_BUTTON_HEIGHT, xSpacing: 10, @@ -102,7 +102,10 @@ accumulator: null, accumulatorOptions: null, tandem: Tandem.REQUIRED, - tandemNameSuffix: 'Keypad' + tandemNameSuffix: 'Keypad', + + tagName: 'div', + focusable: true }, providedOptions ); super(); @@ -148,18 +151,24 @@ } } + const keyboardKeys: Record = {}; + // interpret the layout specification for ( let row = 0; row < layout.length; row++ ) { const startRow = row; for ( let column = 0; column < layout[ row ].length; column++ ) { - const button = layout[ row ][ column ]; - if ( button ) { + const key = layout[ row ][ column ]; + if ( key ) { + if ( key.keyboardIdentifier ) { + keyboardKeys[ key.keyboardIdentifier ] = key; + } + const keyBefore = layout[ row ][ column - 1 ]; const startColumn = column + ( column > 0 && keyBefore ? keyBefore.horizontalSpan - 1 : 0 ); - const verticalSpan = button.verticalSpan; - const horizontalSpan = button.horizontalSpan; + const verticalSpan = key.verticalSpan; + const horizontalSpan = key.horizontalSpan; // check for overlap between the buttons for ( let x = startRow; x < ( startRow + verticalSpan ); x++ ) { @@ -170,9 +179,9 @@ } // create and add the buttons - const buttonWidth = button.horizontalSpan * options.buttonWidth + ( button.horizontalSpan - 1 ) * options.xSpacing; - const buttonHeight = button.verticalSpan * options.buttonHeight + ( button.verticalSpan - 1 ) * options.ySpacing; - const buttonNode = createKeyNode( button, this.keyAccumulator, buttonWidth, buttonHeight, options.tandem, options ); + const buttonWidth = key.horizontalSpan * options.buttonWidth + ( key.horizontalSpan - 1 ) * options.xSpacing; + const buttonHeight = key.verticalSpan * options.buttonHeight + ( key.verticalSpan - 1 ) * options.ySpacing; + const buttonNode = createKeyNode( key, this.keyAccumulator, buttonWidth, buttonHeight, options.tandem, options ); buttonNode.left = startColumn * options.buttonWidth + startColumn * options.xSpacing; buttonNode.top = startRow * options.buttonHeight + startRow * options.ySpacing; this.buttonNodes.push( buttonNode ); @@ -181,6 +190,14 @@ } } + this.addInputListener( new KeyboardListener( { + keys: Object.keys( keyboardKeys ), + callback: ( sceneryEvent, listener ) => { + const keyObject = keyboardKeys[ listener.keysPressed ]; + this.keyAccumulator.handleKeyPressed( keyObject.identifier ); + } + } ) ); + this.mutate( options ); } Index: js/keypad/Key.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/js/keypad/Key.ts b/js/keypad/Key.ts --- a/js/keypad/Key.ts (revision 4b6693637f6713bcfb225e68f528ae2820be553f) +++ b/js/keypad/Key.ts (date 1668622794047) @@ -15,6 +15,7 @@ type SelfOptions = { horizontalSpan?: number; verticalSpan?: number; + keyboardIdentifier?: string | null; }; export type KeyOptions = SelfOptions; @@ -30,6 +31,8 @@ // The tandem component name to use when creating a button from this key. public readonly buttonTandemName: string; + public readonly keyboardIdentifier: string | null; + /** * @param label - node or string that will appear on the key * @param identifier - ID for this key, see KeyID.js @@ -42,11 +45,13 @@ const options = optionize()( { horizontalSpan: 1, - verticalSpan: 1 + verticalSpan: 1, + keyboardIdentifier: null }, providedOptions ); this.horizontalSpan = options.horizontalSpan; this.verticalSpan = options.verticalSpan; + this.keyboardIdentifier = options.keyboardIdentifier; this.buttonTandemName = `${_.camelCase( this.identifier )}Button`; } Index: js/demo/components/demoKeypad.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/js/demo/components/demoKeypad.ts b/js/demo/components/demoKeypad.ts --- a/js/demo/components/demoKeypad.ts (revision 4b6693637f6713bcfb225e68f528ae2820be553f) +++ b/js/demo/components/demoKeypad.ts (date 1668622794039) @@ -200,8 +200,8 @@ resize: false, children: [ integerVBox, - floatingPointVBox, - positiveAndNegativeFloatingPointVBox + // floatingPointVBox, + // positiveAndNegativeFloatingPointVBox ], center: layoutBounds.center } ); ```
matthew-blackman commented 1 year ago

@zepumph and I discussed adding full keyboard support to Keypad keyboard listeners, as well as exporting/importing the OneKeyStroke type to Kaypad.ts. Patch with these changes is as follows:

```Index: scenery-phet/js/keypad/Keypad.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery-phet/js/keypad/Keypad.ts b/scenery-phet/js/keypad/Keypad.ts --- a/scenery-phet/js/keypad/Keypad.ts (revision 4b6693637f6713bcfb225e68f528ae2820be553f) +++ b/scenery-phet/js/keypad/Keypad.ts (date 1669052193421) @@ -9,7 +9,7 @@ import merge from '../../../phet-core/js/merge.js'; import optionize from '../../../phet-core/js/optionize.js'; -import { Font, Node, NodeOptions, Text, TPaint } from '../../../scenery/js/imports.js'; +import { Font, KeyboardListener, Node, NodeOptions, Text, TPaint } from '../../../scenery/js/imports.js'; import RectangularPushButton from '../../../sun/js/buttons/RectangularPushButton.js'; import Tandem from '../../../tandem/js/Tandem.js'; import BackspaceIcon from '../BackspaceIcon.js'; @@ -17,6 +17,7 @@ import sceneryPhet from '../sceneryPhet.js'; import Key from './Key.js'; import KeyID, { KeyIDValue } from './KeyID.js'; +import type { OneKeyStroke } from '../../../scenery/js/imports.js'; import NumberAccumulator, { NumberAccumulatorOptions } from './NumberAccumulator.js'; import AbstractKeyAccumulator from './AbstractKeyAccumulator.js'; import ReadOnlyProperty from '../../../axon/js/ReadOnlyProperty.js'; @@ -29,20 +30,21 @@ const DEFAULT_BUTTON_COLOR = 'white'; const PLUS_CHAR = '\u002b'; const MINUS_CHAR = '\u2212'; -const _0 = new Key( '0', KeyID.ZERO ); -const _1 = new Key( '1', KeyID.ONE ); -const _2 = new Key( '2', KeyID.TWO ); -const _3 = new Key( '3', KeyID.THREE ); -const _4 = new Key( '4', KeyID.FOUR ); -const _5 = new Key( '5', KeyID.FIVE ); -const _6 = new Key( '6', KeyID.SIX ); -const _7 = new Key( '7', KeyID.SEVEN ); -const _8 = new Key( '8', KeyID.EIGHT ); -const _9 = new Key( '9', KeyID.NINE ); +const _0 = new Key( '0', KeyID.ZERO, { keyboardIdentifier: '0' } ); +const _1 = new Key( '1', KeyID.ONE, { keyboardIdentifier: '1' } ); +const _2 = new Key( '2', KeyID.TWO, { keyboardIdentifier: '2' } ); +const _3 = new Key( '3', KeyID.THREE, { keyboardIdentifier: '3' } ); +const _4 = new Key( '4', KeyID.FOUR, { keyboardIdentifier: '4' } ); +const _5 = new Key( '5', KeyID.FIVE, { keyboardIdentifier: '5' } ); +const _6 = new Key( '6', KeyID.SIX, { keyboardIdentifier: '6' } ); +const _7 = new Key( '7', KeyID.SEVEN, { keyboardIdentifier: '7' } ); +const _8 = new Key( '8', KeyID.EIGHT, { keyboardIdentifier: '8' } ); +const _9 = new Key( '9', KeyID.NINE, { keyboardIdentifier: '9' } ); const WIDE_ZERO = new Key( '0', KeyID.ZERO, { horizontalSpan: 2 } ); -const BACKSPACE_KEY = new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), KeyID.BACKSPACE ); -const PLUS_MINUS_KEY = new Key( `${PLUS_CHAR}/${MINUS_CHAR}`, KeyID.PLUS_MINUS ); -const DECIMAL_KEY = new Key( '.', KeyID.DECIMAL ); +const BACKSPACE_KEY = new Key( ( new BackspaceIcon( { scale: 1.5 } ) ), + KeyID.BACKSPACE, { keyboardIdentifier: 'backspace' } ); +const PLUS_MINUS_KEY = new Key( `${PLUS_CHAR}/${MINUS_CHAR}`, KeyID.PLUS_MINUS, { keyboardIdentifier: 'minus' } ); +const DECIMAL_KEY = new Key( '.', KeyID.DECIMAL, { keyboardIdentifier: 'period' } ); export type KeypadLayout = ( Key | null )[][]; @@ -90,7 +92,7 @@ */ public constructor( layout: ( Key | null )[][], providedOptions?: KeypadOptions ) { - const options = optionize()( { + const options = optionize()( { buttonWidth: DEFAULT_BUTTON_WIDTH, buttonHeight: DEFAULT_BUTTON_HEIGHT, xSpacing: 10, @@ -102,7 +104,9 @@ accumulator: null, accumulatorOptions: null, tandem: Tandem.REQUIRED, - tandemNameSuffix: 'Keypad' + tandemNameSuffix: 'Keypad', + tagName: 'div', + focusable: true }, providedOptions ); super(); @@ -148,18 +152,24 @@ } } + const keyboardKeys: Record = {}; + // interpret the layout specification for ( let row = 0; row < layout.length; row++ ) { const startRow = row; for ( let column = 0; column < layout[ row ].length; column++ ) { - const button = layout[ row ][ column ]; - if ( button ) { + const key = layout[ row ][ column ]; + if ( key ) { + if ( key.keyboardIdentifier ) { + keyboardKeys[ key.keyboardIdentifier ] = key; + } + const keyBefore = layout[ row ][ column - 1 ]; const startColumn = column + ( column > 0 && keyBefore ? keyBefore.horizontalSpan - 1 : 0 ); - const verticalSpan = button.verticalSpan; - const horizontalSpan = button.horizontalSpan; + const verticalSpan = key.verticalSpan; + const horizontalSpan = key.horizontalSpan; // check for overlap between the buttons for ( let x = startRow; x < ( startRow + verticalSpan ); x++ ) { @@ -170,9 +180,9 @@ } // create and add the buttons - const buttonWidth = button.horizontalSpan * options.buttonWidth + ( button.horizontalSpan - 1 ) * options.xSpacing; - const buttonHeight = button.verticalSpan * options.buttonHeight + ( button.verticalSpan - 1 ) * options.ySpacing; - const buttonNode = createKeyNode( button, this.keyAccumulator, buttonWidth, buttonHeight, options.tandem, options ); + const buttonWidth = key.horizontalSpan * options.buttonWidth + ( key.horizontalSpan - 1 ) * options.xSpacing; + const buttonHeight = key.verticalSpan * options.buttonHeight + ( key.verticalSpan - 1 ) * options.ySpacing; + const buttonNode = createKeyNode( key, this.keyAccumulator, buttonWidth, buttonHeight, options.tandem, options ); buttonNode.left = startColumn * options.buttonWidth + startColumn * options.xSpacing; buttonNode.top = startRow * options.buttonHeight + startRow * options.ySpacing; this.buttonNodes.push( buttonNode ); @@ -181,6 +191,14 @@ } } + this.addInputListener( new KeyboardListener( { + keys: Object.keys( keyboardKeys ), + callback: ( sceneryEvent, listener ) => { + const keyObject = keyboardKeys[ listener.keysPressed ]; + this.keyAccumulator.handleKeyPressed( keyObject.identifier ); + } + } ) ); + this.mutate( options ); } Index: scenery/js/accessibility/KeyboardUtils.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery/js/accessibility/KeyboardUtils.ts b/scenery/js/accessibility/KeyboardUtils.ts --- a/scenery/js/accessibility/KeyboardUtils.ts (revision 6dc203980b55270140ca2394eadeee8b6f6f2e6b) +++ b/scenery/js/accessibility/KeyboardUtils.ts (date 1669050960016) @@ -112,6 +112,7 @@ KEY_EQUALS: 'Equal', KEY_PLUS: 'Equal', KEY_MINUS: 'Minus', + KEY_PERIOD: 'Period', ARROW_KEYS: ARROW_KEYS, WASD_KEYS: WASD_KEYS, Index: scenery/js/listeners/KeyboardListener.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery/js/listeners/KeyboardListener.ts b/scenery/js/listeners/KeyboardListener.ts --- a/scenery/js/listeners/KeyboardListener.ts (revision 6dc203980b55270140ca2394eadeee8b6f6f2e6b) +++ b/scenery/js/listeners/KeyboardListener.ts (date 1669049927980) @@ -59,7 +59,7 @@ 'v' | 'b' | 'n' | 'm' | 'ctrl' | 'alt' | 'shift' | 'tab'; type AllowedKeys = keyof typeof EnglishStringToCodeMap; -type OneKeyStroke = `${AllowedKeys}` | +export type OneKeyStroke = `${AllowedKeys}` | `${ModifierKey}+${AllowedKeys}` | `${ModifierKey}+${ModifierKey}+${AllowedKeys}`; // These combinations are not supported by TypeScript: "TS2590: Expression produces a union type that is too complex to Index: scenery/js/accessibility/EnglishStringToCodeMap.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery/js/accessibility/EnglishStringToCodeMap.ts b/scenery/js/accessibility/EnglishStringToCodeMap.ts --- a/scenery/js/accessibility/EnglishStringToCodeMap.ts (revision 6dc203980b55270140ca2394eadeee8b6f6f2e6b) +++ b/scenery/js/accessibility/EnglishStringToCodeMap.ts (date 1669050992615) @@ -55,6 +55,7 @@ equals: KeyboardUtils.KEY_EQUALS, plus: KeyboardUtils.KEY_PLUS, minus: KeyboardUtils.KEY_MINUS, + period: KeyboardUtils.KEY_PERIOD, escape: KeyboardUtils.KEY_ESCAPE, delete: KeyboardUtils.KEY_DELETE, backspace: KeyboardUtils.KEY_BACKSPACE, Index: scenery-phet/js/keypad/Key.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery-phet/js/keypad/Key.ts b/scenery-phet/js/keypad/Key.ts --- a/scenery-phet/js/keypad/Key.ts (revision 4b6693637f6713bcfb225e68f528ae2820be553f) +++ b/scenery-phet/js/keypad/Key.ts (date 1669049466108) @@ -15,6 +15,7 @@ type SelfOptions = { horizontalSpan?: number; verticalSpan?: number; + keyboardIdentifier?: string | null; }; export type KeyOptions = SelfOptions; @@ -30,6 +31,8 @@ // The tandem component name to use when creating a button from this key. public readonly buttonTandemName: string; + public readonly keyboardIdentifier: string | null; + /** * @param label - node or string that will appear on the key * @param identifier - ID for this key, see KeyID.js @@ -42,11 +45,13 @@ const options = optionize()( { horizontalSpan: 1, - verticalSpan: 1 + verticalSpan: 1, + keyboardIdentifier: null }, providedOptions ); this.horizontalSpan = options.horizontalSpan; this.verticalSpan = options.verticalSpan; + this.keyboardIdentifier = options.keyboardIdentifier; this.buttonTandemName = `${_.camelCase( this.identifier )}Button`; } Index: scenery/js/imports.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery/js/imports.ts b/scenery/js/imports.ts --- a/scenery/js/imports.ts (revision 6dc203980b55270140ca2394eadeee8b6f6f2e6b) +++ b/scenery/js/imports.ts (date 1669052168722) @@ -229,6 +229,7 @@ export { default as KeyboardDragListener } from './listeners/KeyboardDragListener.js'; export type { KeyboardDragListenerOptions } from './listeners/KeyboardDragListener.js'; export { default as KeyboardListener } from './listeners/KeyboardListener.js'; +export type { OneKeyStroke } from './listeners/KeyboardListener.js'; export { default as SpriteListenable } from './listeners/SpriteListenable.js'; export { default as SwipeListener } from './listeners/SwipeListener.js'; ```
zepumph commented 1 year ago

We got a first pass in https://github.com/phetsims/scenery-phet/issues/790, let's see if we want to use it in Projectile motion.

matthew-blackman commented 1 year ago

Keyboard input appears to be working in Projectile Motion when supportsInteractiveDescription is turned on. It requires manually focusing on the keypad.

matthew-blackman commented 1 year ago

Unassigning @jbphet as we will pick this back up when ready to implement the updated Keypad into Projectile Motion.

zepumph commented 1 year ago

Lots of good work over in https://github.com/phetsims/scenery-phet/issues/790!