Skip to content

Commit

Permalink
progress toward scenery input scanning for overlapping KeyboardListen…
Browse files Browse the repository at this point in the history
…ers, see #1570
  • Loading branch information
jessegreenberg committed Mar 21, 2024
1 parent e23d7d1 commit 9b2dd10
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 35 deletions.
16 changes: 16 additions & 0 deletions js/accessibility/KeyboardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,22 @@ const KeyboardUtils = {
return MODIFIER_KEYS.includes( key );
},

/**
* Returns an array of duplicate entries in the provided array.
*/
findArrayDuplicates( array: string[] ): string[] {

// Create a set and then destructure to remove duplicates if any entries are found more than once.
return [ ...new Set( array.filter( ( item, index ) => array.indexOf( item ) !== index ) ) ];
},

/**
* Returns true if the subsetArray is a subset of the supersetArray.
*/
isArraySubset( subsetArray: string[], supersetArray: string[] ): boolean {
return subsetArray.every( value => supersetArray.includes( value ) );
},

ALL_KEYS: ALL_KEY_CODES
};

Expand Down
43 changes: 41 additions & 2 deletions js/input/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ import platform from '../../../phet-core/js/platform.js';
import EventType from '../../../tandem/js/EventType.js';
import NullableIO from '../../../tandem/js/types/NullableIO.js';
import NumberIO from '../../../tandem/js/types/NumberIO.js';
import { BatchedDOMEvent, BatchedDOMEventCallback, BatchedDOMEventType, BrowserEvents, Display, EventContext, EventContextIO, Mouse, Node, PDOMInstance, PDOMPointer, PDOMUtils, Pen, Pointer, scenery, SceneryEvent, SceneryListenerFunction, SupportedEventTypes, TInputListener, Touch, Trail, WindowTouch } from '../imports.js';
import { BatchedDOMEvent, BatchedDOMEventCallback, BatchedDOMEventType, BrowserEvents, Display, EventContext, EventContextIO, KeyboardListener, Mouse, Node, OneKeyStroke, PDOMInstance, PDOMPointer, PDOMUtils, Pen, Pointer, scenery, SceneryEvent, SceneryListenerFunction, SupportedEventTypes, TInputListener, Touch, Trail, WindowTouch } from '../imports.js';
import PhetioObject, { PhetioObjectOptions } from '../../../tandem/js/PhetioObject.js';
import IOType from '../../../tandem/js/types/IOType.js';
import ArrayIO from '../../../tandem/js/types/ArrayIO.js';
Expand Down Expand Up @@ -794,9 +794,27 @@ export default class Input extends PhetioObject {
sceneryLog && sceneryLog.Input && sceneryLog.Input( `keydown(${Input.debugText( null, context.domEvent )});` );
sceneryLog && sceneryLog.Input && sceneryLog.push();

this.dispatchGlobalEvent<KeyboardEvent>( 'globalkeydown', context, true );
const keyboardListeners: KeyboardListener<OneKeyStroke[]>[] = [];
this.recursiveScanForGlobalKeyboardListeners( this.rootNode, keyboardListeners );

// also add any listeners along the trail
const trail = this.getPDOMEventTrail( context.domEvent, 'keydown' );
if ( trail ) {
const nodes = trail.nodes;
nodes.forEach( node => {

// skip the global listeners, they will have been added by the above scan
const nodeKeyboardListeners = node.inputListeners.filter( listener => listener instanceof KeyboardListener && !listener.global );

// @ts-expect-error
keyboardListeners.push( ...nodeKeyboardListeners );
} );
}

KeyboardListener.inspectKeyboardListeners( keyboardListeners, context.domEvent );

this.dispatchGlobalEvent<KeyboardEvent>( 'globalkeydown', context, true );

trail && this.dispatchPDOMEvent<KeyboardEvent>( trail, 'keydown', context, true );

this.dispatchGlobalEvent<KeyboardEvent>( 'globalkeydown', context, false );
Expand All @@ -823,6 +841,8 @@ export default class Input extends PhetioObject {

this.dispatchGlobalEvent<KeyboardEvent>( 'globalkeyup', context, false );

KeyboardListener.undeferKeyboardListeners( context.domEvent.code );

sceneryLog && sceneryLog.Input && sceneryLog.pop();
}, {
phetioPlayback: true,
Expand All @@ -848,6 +868,25 @@ export default class Input extends PhetioObject {
} );
}

public recursiveScanForGlobalKeyboardListeners( node: Node, listeners: KeyboardListener<OneKeyStroke[]>[] ): KeyboardListener<OneKeyStroke[]>[] {

// The KeyboardListener will be assigned to a Node
if ( !node.isDisposed && node.isVisible() && node.isInputEnabled() && node.isPDOMVisible() ) {
// Reverse iteration follows the z-order from "visually in front" to "visually in back" like normal dipatch
for ( let i = node._children.length - 1; i >= 0; i-- ) {
this.recursiveScanForGlobalKeyboardListeners( node._children[ i ], listeners );
}

// if the node has a KeyboardListener that is global, add it to the list
const globalKeyboardListeners = node.inputListeners.filter( listener => listener instanceof KeyboardListener && listener.global );

// @ts-expect-error
listeners.push( ...globalKeyboardListeners );
}

return listeners;
}

/**
* Called to batch a raw DOM event (which may be immediately fired, depending on the settings). (scenery-internal)
*
Expand Down
Loading

0 comments on commit 9b2dd10

Please sign in to comment.