Skip to content

Commit

Permalink
create BothHandsPDOMNode.js, #165
Browse files Browse the repository at this point in the history
  • Loading branch information
zepumph committed Sep 2, 2020
1 parent 865b233 commit c216b14
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 49 deletions.
88 changes: 88 additions & 0 deletions js/common/view/BothHandsPDOMNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2020, University of Colorado Boulder

/**
* PDOM view for interacting with both hands at the same time. This adds a custom interaction, as well as PDOM formatting
* like adding the "application" role to support alternative input.
*
* @author Michael Kauzmann (PhET Interactive Simulations)
*/

import merge from '../../../../phet-core/js/merge.js';
import sceneryPhetStrings from '../../../../scenery-phet/js/sceneryPhetStrings.js';
import Node from '../../../../scenery/js/nodes/Node.js';
import Utterance from '../../../../utterance-queue/js/Utterance.js';
import ratioAndProportion from '../../ratioAndProportion.js';
import ratioAndProportionStrings from '../../ratioAndProportionStrings.js';
import RatioInteractionListener from './RatioInteractionListener.js';

class BothHandsPDOMNode extends Node {

/**
* @param {NumberProperty} leftValueProperty
* @param {NumberProperty} rightValueProperty
* @param {Range} valueRange - the total range of the hand
* @param {Property.<boolean>} firstInteractionProperty - upon successful interaction, this will be marked as false
* @param {number} keyboardStep
* @param {EnumerationProperty.<TickMarkView>} tickMarkViewProperty
* @param {Property.<number>} tickMarkRangeProperty
* @param {HandPositionsDescriber} handPositionsDescriber
* @param {RatioDescriber} ratioDescriber
* @param {Object} [options]
*/
constructor( leftValueProperty, rightValueProperty, valueRange, firstInteractionProperty, keyboardStep,
tickMarkViewProperty, tickMarkRangeProperty, handPositionsDescriber,
ratioDescriber, options ) {

options = merge( {
ariaRole: 'application',
focusable: true,
containerTagName: 'div', // @zepumph thought this was a bit easier to navigate in the PDOM
tagName: 'div',
innerContent: ratioAndProportionStrings.a11y.bothHands,
ariaLabel: ratioAndProportionStrings.a11y.bothHands,
helpText: ratioAndProportionStrings.a11y.bothHandsHelpText
}, options );

super();

this.setAccessibleAttribute( 'aria-roledescription', sceneryPhetStrings.a11y.grabDrag.movable );

const ratioInteractionListener = new RatioInteractionListener( this, leftValueProperty,
rightValueProperty, valueRange, firstInteractionProperty, tickMarkRangeProperty, keyboardStep );
this.addInputListener( ratioInteractionListener );

const bothHandsPositionUtterance = new Utterance( {

// give enough time for the user to stop interacting with te hands
// before describing current positions, to prevent too many of these
// from queuing up in rapid presses
alertStableDelay: 500
} );
const bothHandsRatioUtterance = new Utterance( {

// a longer delay before speaking the bothHandsPositionUtterance gives
// more consistent behavior on Safari, where often the alerts would be
// lost
alertStableDelay: 1000
} );
ratioInteractionListener.isBeingInteractedWithProperty.lazyLink( isBeingInteractedWith => {

// when no longer being interacted with, trigger an alert
if ( !isBeingInteractedWith ) {
bothHandsPositionUtterance.alert = handPositionsDescriber.getBothHandsPositionText( tickMarkViewProperty.value );
phet.joist.sim.utteranceQueue.addToBack( bothHandsPositionUtterance );

bothHandsRatioUtterance.alert = ratioDescriber.getRatioDescriptionString();
phet.joist.sim.utteranceQueue.addToBack( bothHandsRatioUtterance );
}
} );

// @public (read-only) - expose this from the listener for general consumption
this.isBeingInteractedWithProperty = ratioInteractionListener.isBeingInteractedWithProperty;

this.mutate( options );
}
}

ratioAndProportion.register( 'BothHandsPDOMNode', BothHandsPDOMNode );
export default BothHandsPDOMNode;
58 changes: 9 additions & 49 deletions js/common/view/RatioAndProportionScreenView.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import LinearFunction from '../../../../dot/js/LinearFunction.js';
import ScreenView from '../../../../joist/js/ScreenView.js';
import merge from '../../../../phet-core/js/merge.js';
import ResetAllButton from '../../../../scenery-phet/js/buttons/ResetAllButton.js';
import sceneryPhetStrings from '../../../../scenery-phet/js/sceneryPhetStrings.js';
import ParallelDOM from '../../../../scenery/js/accessibility/pdom/ParallelDOM.js';
import Image from '../../../../scenery/js/nodes/Image.js';
import Node from '../../../../scenery/js/nodes/Node.js';
Expand All @@ -21,18 +20,17 @@ import Color from '../../../../scenery/js/util/Color.js';
import RadioButtonGroup from '../../../../sun/js/buttons/RadioButtonGroup.js';
import FontAwesomeNode from '../../../../sun/js/FontAwesomeNode.js';
import soundManager from '../../../../tambo/js/soundManager.js';
import Utterance from '../../../../utterance-queue/js/Utterance.js';
import numberedTickMarkIconImage from '../../../images/numbered-tick-mark-icon_png.js';
import tickMarkIconImage from '../../../images/tick-mark-icon_png.js';
import ratioAndProportion from '../../ratioAndProportion.js';
import ratioAndProportionStrings from '../../ratioAndProportionStrings.js';
import RatioAndProportionConstants from '../RatioAndProportionConstants.js';
import BothHandsPDOMNode from './BothHandsPDOMNode.js';
import HandPositionsDescriber from './HandPositionsDescriber.js';
import RAPTickMarkLabelsNode from './RAPTickMarkLabelsNode.js';
import RatioAndProportionColorProfile from './RatioAndProportionColorProfile.js';
import RatioDescriber from './RatioDescriber.js';
import RatioHalf from './RatioHalf.js';
import RatioInteractionListener from './RatioInteractionListener.js';
import InProportionSoundGenerator from './sound/InProportionSoundGenerator.js';
import MovingInProportionSoundGenerator from './sound/MovingInProportionSoundGenerator.js';
import StaccatoFrequencySoundGenerator from './sound/StaccatoFrequencySoundGenerator.js';
Expand Down Expand Up @@ -151,49 +149,11 @@ class RatioAndProportionScreenView extends ScreenView {
helpText: ratioAndProportionStrings.a11y.rightHandHelpText
} );

const bothHandsInteractionNode = new Node( {
ariaRole: 'application',
focusable: true,
tagName: 'div',
innerContent: ratioAndProportionStrings.a11y.bothHands,
ariaLabel: ratioAndProportionStrings.a11y.bothHands,
helpText: ratioAndProportionStrings.a11y.bothHandsHelpText,
children: [
this.leftRatioHalf,
this.rightRatioHalf
]
} );
bothHandsInteractionNode.setAccessibleAttribute( 'aria-roledescription', sceneryPhetStrings.a11y.grabDrag.movable );

const ratioInteractionListener = new RatioInteractionListener( bothHandsInteractionNode, model.leftValueProperty,
model.rightValueProperty, model.valueRange, model.firstInteractionProperty, options.tickMarkRangeProperty, keyboardStep );
bothHandsInteractionNode.addInputListener( ratioInteractionListener );

const bothHandsPositionUtterance = new Utterance( {

// give enough time for the user to stop interacting with te hands
// before describing current positions, to prevent too many of these
// from queuing up in rapid presses
alertStableDelay: 500
} );
const bothHandsRatioUtterance = new Utterance( {

// a longer delay before speaking the bothHandsPositionUtterance gives
// more consistent behavior on Safari, where often the alerts would be
// lost
alertStableDelay: 1000
} );
ratioInteractionListener.isBeingInteractedWithProperty.lazyLink( isBeingInteractedWith => {

// when no longer being interacted with, trigger an alert
if ( !isBeingInteractedWith ) {
bothHandsPositionUtterance.alert = this.handPositionsDescriber.getBothHandsPositionText( tickMarkViewProperty.value );
phet.joist.sim.utteranceQueue.addToBack( bothHandsPositionUtterance );

bothHandsRatioUtterance.alert = this.ratioDescriber.getRatioDescriptionString();
phet.joist.sim.utteranceQueue.addToBack( bothHandsRatioUtterance );
}
} );
const bothHandsPDOMNode = new BothHandsPDOMNode( model.leftValueProperty, model.rightValueProperty, model.valueRange,
model.firstInteractionProperty, keyboardStep, tickMarkViewProperty, options.tickMarkRangeProperty,
this.handPositionsDescriber, this.ratioDescriber, {
children: [ this.leftRatioHalf, this.rightRatioHalf ]
} );

// @private TODO: add support for mechamarker input again https://github.com/phetsims/ratio-and-proportion/issues/89
// this.markerInput = new ProportionMarkerInput( model );
Expand All @@ -202,7 +162,7 @@ class RatioAndProportionScreenView extends ScreenView {
this.leftRatioHalf.isBeingInteractedWithProperty,
this.rightRatioHalf.isBeingInteractedWithProperty,
// this.markerInput.isBeingInteractedWithProperty, // TODO: add support for mechamarker input again https://github.com/phetsims/ratio-and-proportion/issues/89
ratioInteractionListener.isBeingInteractedWithProperty
bothHandsPDOMNode.isBeingInteractedWithProperty
] );

this.inProportionSoundGenerator.addEnableControlProperty( soundGeneratorEnabledProperty );
Expand Down Expand Up @@ -281,14 +241,14 @@ class RatioAndProportionScreenView extends ScreenView {
this.resetAllButton,

// Main ratio on top
bothHandsInteractionNode
bothHandsPDOMNode
];

// accessible order (ratio first in nav order)
this.pdomPlayAreaNode.accessibleOrder = [
this.leftRatioHalf,
this.rightRatioHalf,
bothHandsInteractionNode,
bothHandsPDOMNode,
this.tickMarkViewRadioButtonGroup
];

Expand Down

0 comments on commit c216b14

Please sign in to comment.