Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
jbphet opened this issue Oct 27, 2022 · 7 comments
Open

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

jbphet opened this issue Oct 27, 2022 · 7 comments

Comments

@jbphet
Copy link
Contributor

jbphet commented Oct 27, 2022

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 phetsims/scenery#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
Copy link
Member

zepumph commented Nov 11, 2022

@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:

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<KeyOptions, SelfOptions>()( {
       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<KeypadOptions, SelfOptions>()( {
+    const options = optionize<KeypadOptions, SelfOptions, NodeOptions>()( {
       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
Copy link
Contributor

matthew-blackman commented Nov 16, 2022

@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:

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<KeypadOptions, SelfOptions>()( {
+    const options = optionize<KeypadOptions, SelfOptions, NodeOptions>()( {
       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<IntentionalAny, Key> = {};
+
     // 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<KeyOptions, SelfOptions>()( {
       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
Copy link
Contributor

matthew-blackman commented Nov 21, 2022

@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:

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<KeypadOptions, SelfOptions>()( {
+    const options = optionize<KeypadOptions, SelfOptions, NodeOptions>()( {
       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<OneKeyStroke, Key> = {};
+
     // 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<KeyOptions, SelfOptions>()( {
       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
Copy link
Member

zepumph commented Nov 22, 2022

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

@matthew-blackman
Copy link
Contributor

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

@matthew-blackman
Copy link
Contributor

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

@zepumph
Copy link
Member

zepumph commented Mar 8, 2023

Lots of good work over in phetsims/scenery-phet#790!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants