Skip to content

Commit

Permalink
first pass at RatioInteractionListener for keyboard control of entire…
Browse files Browse the repository at this point in the history
… ratio, #44
  • Loading branch information
zepumph committed May 13, 2020
1 parent 81e074b commit d656a66
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 5 deletions.
24 changes: 19 additions & 5 deletions js/free-objects/view/FreeObjectsScreenView.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import ChallengeComboBoxItem from './ChallengeComboBoxItem.js';
import GridDescriber from './GridDescriber.js';
import RatioAndProportionScreenSummaryNode from './RatioAndProportionScreenSummaryNode.js';
import RatioDescriber from './RatioDescriber.js';
import RatioInteractionListener from './RatioInteractionListener.js';
import ProportionFitnessSoundGenerator from './sound/ProportionFitnessSoundGenerator.js';
import ProportionMarkerInput from './ProportionMarkerInput.js';
import RatioHalf from './RatioHalf.js';
Expand Down Expand Up @@ -95,6 +96,20 @@ class FreeObjectsScreenView extends ScreenView {
labelContent: ratioAndProportionStrings.a11y.rightHand
} );

const a11yRatioContainer = new Node( {
tagName: 'div',
role: 'application',
focusable: true,
labelContent: 'Ratio:',
children: [
this.leftRatioHalf,
this.rightRatioHalf
]
} );
const ratioInteractionListener = new RatioInteractionListener( a11yRatioContainer, model.leftValueProperty,
model.rightValueProperty, model.valueRange, model.firstInteractionProperty );
a11yRatioContainer.addInputListener( ratioInteractionListener );

// @private
this.markerInput = new ProportionMarkerInput( model );

Expand All @@ -105,7 +120,8 @@ class FreeObjectsScreenView extends ScreenView {
DerivedProperty.or( [
this.leftRatioHalf.isBeingInteractedWithProperty,
this.rightRatioHalf.isBeingInteractedWithProperty,
this.markerInput.isBeingInteractedWithProperty
this.markerInput.isBeingInteractedWithProperty,
ratioInteractionListener.isBeingInteractedWithProperty
] ),
model.leftVelocityProperty,
model.rightVelocityProperty );
Expand Down Expand Up @@ -170,14 +186,12 @@ class FreeObjectsScreenView extends ScreenView {
comboBoxParent,

// Main ratio on top
this.leftRatioHalf,
this.rightRatioHalf
a11yRatioContainer
];

// accessible order (markers first in nav order)
this.pdomPlayAreaNode.accessibleOrder = [
this.leftRatioHalf,
this.rightRatioHalf,
a11yRatioContainer,
gridViewAquaRadioButtonGroup,
comboBox,
comboBoxParent,
Expand Down
94 changes: 94 additions & 0 deletions js/free-objects/view/RatioInteractionListener.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2020, University of Colorado Boulder

/**
* Keyboard drag listener for moving both ratio halves at the same time
*
* @author Michael Kauzmann (PhET Interactive Simulations)
*/

import BooleanProperty from '../../../../axon/js/BooleanProperty.js';
import KeyStateTracker from '../../../../scenery/js/accessibility/KeyStateTracker.js';
import designingProperties from '../../common/designingProperties.js';
import ratioAndProportion from '../../ratioAndProportion.js';

class RatioInteractionListener {

/**
* @param {Node} targetNode
* @param {Property.<number>}leftValueProperty
* @param {Property.<number>}rightValueProperty
* @param {Range} valueRange
*/
constructor( targetNode, leftValueProperty, rightValueProperty, valueRange, firstInteractionProperty ) {

this.keyStateTracker = new KeyStateTracker();
this.valueRange = valueRange;
this.targetNode = targetNode;
this.firstInteractionProperty = firstInteractionProperty;

this.isBeingInteractedWithProperty = new BooleanProperty( false );

this.keyStateTracker.keydownEmitter.addListener( event => {
this.isBeingInteractedWithProperty.value = true;

if ( event.key === 'ArrowDown' ) {
this.updateValue( rightValueProperty, false );
}
else if ( event.key === 'ArrowUp' ) {
this.updateValue( rightValueProperty, true );
}
else if ( event.key.toLowerCase() === 'w' ) {
this.updateValue( leftValueProperty, true );
}
else if ( event.key.toLowerCase() === 's' ) {
this.updateValue( leftValueProperty, false );
}
} );

this.keyStateTracker.keyupEmitter.addListener( () => {
if ( !this.keyStateTracker.keysAreDown() ) {
this.isBeingInteractedWithProperty.value = false;
}
} );
}

/**
* @private
* @param property
* @param increment
*/
updateValue( property, increment ) {
this.firstInteractionProperty.value = true;
const value = 1 / designingProperties.gridBaseUnitProperty.value;
const amount = this.keyStateTracker.shiftKeyDown ? value / 4 : value;
property.value = this.valueRange.constrainValue( property.value + ( amount * ( increment ? 1 : -1 ) ) );
}

/**
* @public
*/
blur() {
this.isBeingInteractedWithProperty.value = false;
}

/**
* @public
* @param {SceneryEvent} sceneryEvent
*/
keydown( sceneryEvent ) {

// TODO: targetNode is only because there are currently children listeners on each ratio half that we don't want bubbling, https://github.com/phetsims/ratio-and-proportion/issues/44
sceneryEvent.currentTarget === this.targetNode && this.keyStateTracker.keydownUpdate( sceneryEvent.domEvent );
}

/**
* @public
* @param {SceneryEvent} sceneryEvent
*/
keyup( sceneryEvent ) {
sceneryEvent.currentTarget === this.targetNode && this.keyStateTracker.keyupUpdate( sceneryEvent.domEvent );
}
}

ratioAndProportion.register( 'RatioInteractionListener', RatioInteractionListener );
export default RatioInteractionListener;

0 comments on commit d656a66

Please sign in to comment.