From e28ac3fa23023c054d2b52135e24664f5b3d067a Mon Sep 17 00:00:00 2001 From: Jonathan Olson Date: Tue, 7 Dec 2021 18:32:03 -0700 Subject: [PATCH] Converting DerivedProperty/Multilink to properly parameterized TypeScript, and usage of IReadOnlyProperty. See https://github.com/phetsims/axon/issues/371 --- js/common/model/RAPModel.ts | 11 ++++++----- js/common/model/RAPRatio.ts | 3 ++- js/common/view/BothHandsPDOMNode.ts | 2 +- js/common/view/RAPScreenView.ts | 2 +- js/common/view/RAPTickMarkLabelsNode.ts | 7 ++++--- js/common/view/RatioHalf.ts | 7 ++++--- js/common/view/RatioHalfTickMarksNode.ts | 2 +- js/common/view/RatioHandNode.ts | 7 ++++--- js/common/view/describers/RatioDescriber.ts | 6 +++--- js/common/view/sound/InProportionSoundGenerator.ts | 5 +++-- .../view/sound/MovingInProportionSoundGenerator.ts | 3 ++- .../view/sound/StaccatoFrequencySoundGenerator.ts | 8 ++++---- js/create/view/CreateScreenSummaryNode.ts | 7 ++++--- js/create/view/CreateScreenView.ts | 2 +- js/create/view/MyChallengeAccordionBox.ts | 2 +- js/discover/view/DiscoverScreenSummaryNode.ts | 5 +++-- 16 files changed, 44 insertions(+), 35 deletions(-) diff --git a/js/common/model/RAPModel.ts b/js/common/model/RAPModel.ts index 53506278..58b822ae 100644 --- a/js/common/model/RAPModel.ts +++ b/js/common/model/RAPModel.ts @@ -21,6 +21,7 @@ import RAPRatio from './RAPRatio.js'; import RatioTerm from './RatioTerm.js'; import Tandem from '../../../../tandem/js/Tandem.js'; import RAPRatioTuple from './RAPRatioTuple.js'; +import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js'; // constant to help achieve feedback in 40% of the visual screen height (2 default tick marks). Calculated by taking the // fitness distance when the right hand is 2 tick marks from the target ratio. This number is based on a target ratio of @@ -38,9 +39,9 @@ class RAPModel { ratio: RAPRatio; targetRatioProperty: NumberProperty; - unclampedFitnessProperty: DerivedProperty; - ratioFitnessProperty: DerivedProperty; - inProportionProperty: DerivedProperty; + unclampedFitnessProperty: IReadOnlyProperty; + ratioFitnessProperty: IReadOnlyProperty; + inProportionProperty: IReadOnlyProperty; public constructor( tandem: Tandem ) { @@ -65,7 +66,7 @@ class RAPModel { this.ratio.tupleProperty, this.targetRatioProperty, this.ratio.movingInDirectionProperty - ], ( ratioTuple: RAPRatioTuple, ratio: number ) => { + ], ( ratioTuple: RAPRatioTuple, ratio: number, movingInDirection: boolean ) => { const antecedent = ratioTuple.antecedent; const consequent = ratioTuple.consequent; @@ -117,7 +118,7 @@ unclampedFitness: ${unclampedFitness} } ); // @public - whether or not the model is in its "in proportion" state. - this.inProportionProperty = new DerivedProperty( [ + this.inProportionProperty = new DerivedProperty( [ this.unclampedFitnessProperty, this.ratio.movingInDirectionProperty ], this.inProportion.bind( this ), { diff --git a/js/common/model/RAPRatio.ts b/js/common/model/RAPRatio.ts index de0e9316..f1b4bd76 100644 --- a/js/common/model/RAPRatio.ts +++ b/js/common/model/RAPRatio.ts @@ -18,6 +18,7 @@ import ratioAndProportion from '../../ratioAndProportion.js'; import rapConstants from '../rapConstants.js'; import RAPRatioTuple from './RAPRatioTuple.js'; import Tandem from '../../../../tandem/js/Tandem.js'; +import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js'; // The threshold for velocity of a moving ratio value to indicate that it is "moving." const VELOCITY_THRESHOLD = 0.01; @@ -41,7 +42,7 @@ class RAPRatio { lockedProperty: BooleanProperty; antecedentVelocityTracker: VelocityTracker; consequentVelocityTracker: VelocityTracker; - movingInDirectionProperty: DerivedProperty; + movingInDirectionProperty: IReadOnlyProperty; ratioLockListenerEnabled: boolean; diff --git a/js/common/view/BothHandsPDOMNode.ts b/js/common/view/BothHandsPDOMNode.ts index dc136e91..09c526db 100644 --- a/js/common/view/BothHandsPDOMNode.ts +++ b/js/common/view/BothHandsPDOMNode.ts @@ -264,7 +264,7 @@ class BothHandsPDOMNode extends Node { options.tickMarkRangeProperty, options.ratioTupleProperty, options.unclampedFitnessProperty - ], () => { + ], ( ...args: any[] ) => { dynamicDescription.innerContent = this.bothHandsDescriber.getBothHandsDynamicDescription(); diff --git a/js/common/view/RAPScreenView.ts b/js/common/view/RAPScreenView.ts index 2115213b..b10c30b2 100644 --- a/js/common/view/RAPScreenView.ts +++ b/js/common/view/RAPScreenView.ts @@ -147,7 +147,7 @@ class RAPScreenView extends ScreenView { // A collection of properties that keep track of which cues should be displayed for both the antecedent and consequent hands. const cueArrowsState = new CueArrowsState(); - const tickMarksAndLabelsColorProperty = new DerivedProperty( [ model.ratioFitnessProperty ], + const tickMarksAndLabelsColorProperty = new DerivedProperty( [ model.ratioFitnessProperty ], ( fitness: number ) => Color.interpolateRGBA( RAPColors.tickMarksAndLabelsOutOfFitnessProperty.value, RAPColors.tickMarksAndLabelsInFitnessProperty.value, fitness diff --git a/js/common/view/RAPTickMarkLabelsNode.ts b/js/common/view/RAPTickMarkLabelsNode.ts index 12b2e19c..390e6037 100644 --- a/js/common/view/RAPTickMarkLabelsNode.ts +++ b/js/common/view/RAPTickMarkLabelsNode.ts @@ -6,6 +6,7 @@ * @author Michael Kauzmann (PhET Interactive Simulations) */ +import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js'; import Property from '../../../../axon/js/Property.js'; import PhetFont from '../../../../scenery-phet/js/PhetFont.js'; import { Color, Node, NodeOptions, Text } from '../../../../scenery/js/imports.js'; @@ -20,7 +21,7 @@ class RAPTickMarkLabelsNode extends Node { private heightOfText: number | null; private tickMarkViewProperty: Property; private tickMarkRangeProperty: Property; - private colorProperty: Property; + private colorProperty: IReadOnlyProperty; /** * @param {Property.} tickMarkViewProperty @@ -30,7 +31,7 @@ class RAPTickMarkLabelsNode extends Node { * @param {Object} [options] */ constructor( tickMarkViewProperty: Property, tickMarkRangeProperty: Property, height: number, - colorProperty: Property, options?: Omit ) { + colorProperty: IReadOnlyProperty, options?: Omit ) { super(); @@ -47,7 +48,7 @@ class RAPTickMarkLabelsNode extends Node { this.mutate( options ); - Property.multilink( [ tickMarkRangeProperty, tickMarkViewProperty ], this.update.bind( this ) ); + Property.multilink( [ tickMarkRangeProperty, tickMarkViewProperty ], this.update.bind( this ) ); } /** diff --git a/js/common/view/RatioHalf.ts b/js/common/view/RatioHalf.ts index d14460fd..8abed96a 100644 --- a/js/common/view/RatioHalf.ts +++ b/js/common/view/RatioHalf.ts @@ -37,6 +37,7 @@ import HandPositionsDescriber from './describers/HandPositionsDescriber.js'; import RAPRatioTuple from '../model/RAPRatioTuple.js'; import CueArrowsState from './CueArrowsState.js'; import RatioDescriber from './describers/RatioDescriber.js'; +import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js'; // constants const MIN_FRAMING_RECTANGLE_HEIGHT = 32; @@ -86,12 +87,12 @@ type RatioHalfDefinedOptions = { playTickMarkBumpSoundProperty: Property; setJumpingOverProportionShouldTriggerSound: ( shouldTriggerSound: boolean ) => void; getIdealValue: () => number; - inProportionProperty: Property; + inProportionProperty: IReadOnlyProperty; isRight?: boolean; // TODO: Why is lint wrong here? https://github.com/phetsims/ratio-and-proportion/issues/404 handColorProperty?: Property; // eslint-disable-line no-undef - a11yDependencies?: Property[]; + a11yDependencies?: IReadOnlyProperty[]; bothHandsCueDisplay?: CueDisplay; } @@ -229,7 +230,7 @@ class RatioHalf extends Rectangle { const viewSounds = new ViewSounds( options.tickMarkRangeProperty, options.tickMarkViewProperty, options.playTickMarkBumpSoundProperty ); // This follows the spec outlined in https://github.com/phetsims/ratio-and-proportion/issues/81 - const cueDisplayStateProperty: DerivedProperty = new DerivedProperty( [ + const cueDisplayStateProperty: IReadOnlyProperty = new DerivedProperty( [ options.cueArrowsState.interactedWithKeyboardProperty, options.cueArrowsState.interactedWithMouseProperty, options.cueArrowsState.keyboardFocusedProperty, diff --git a/js/common/view/RatioHalfTickMarksNode.ts b/js/common/view/RatioHalfTickMarksNode.ts index 12e94ede..036c555c 100644 --- a/js/common/view/RatioHalfTickMarksNode.ts +++ b/js/common/view/RatioHalfTickMarksNode.ts @@ -45,7 +45,7 @@ class RatioHalfTickMarksNode extends GridNode { this.tickMarkViewProperty = tickMarkViewProperty; this.tickMarkRangeProperty = tickMarkRangeProperty; - Property.multilink( [ tickMarkRangeProperty, tickMarkViewProperty ], this.update.bind( this ) ); + Property.multilink( [ tickMarkRangeProperty, tickMarkViewProperty ], this.update.bind( this ) ); } /** diff --git a/js/common/view/RatioHandNode.ts b/js/common/view/RatioHandNode.ts index a10710b9..88e42195 100644 --- a/js/common/view/RatioHandNode.ts +++ b/js/common/view/RatioHandNode.ts @@ -26,6 +26,7 @@ import getKeyboardInputSnappingMapper from './getKeyboardInputSnappingMapper.js' import RAPColors from './RAPColors.js'; import TickMarkView from './TickMarkView.js'; import ratioAndProportionStrings from '../../ratioAndProportionStrings.js'; +import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js'; type CreateIconOptions = { @@ -53,9 +54,9 @@ class RatioHandNode extends Node { tickMarkViewProperty: Property, keyboardStep: number, colorProperty: Property, - cueDisplayProperty: Property, + cueDisplayProperty: IReadOnlyProperty, getIdealValue: () => number, - inProportionProperty: Property, + inProportionProperty: IReadOnlyProperty, options?: any ) { const shiftKeyboardStep = rapConstants.toFixed( keyboardStep * rapConstants.SHIFT_KEY_MULTIPLIER ); // eslint-disable-line bad-sim-text @@ -91,7 +92,7 @@ class RatioHandNode extends Node { // @ts-ignore this.initializeVoicing( options ); - Property.multilink( [ valueProperty ].concat( options.a11yDependencies ), () => { + Property.multilink( [ valueProperty ].concat( options.a11yDependencies ), () => { // @ts-ignore this.voicingObjectResponse = options.voicingCreateObjectResponse(); diff --git a/js/common/view/describers/RatioDescriber.ts b/js/common/view/describers/RatioDescriber.ts index 0455cb84..31ac3ea7 100644 --- a/js/common/view/describers/RatioDescriber.ts +++ b/js/common/view/describers/RatioDescriber.ts @@ -11,8 +11,8 @@ import sceneryPhetStrings from '../../../../../scenery-phet/js/sceneryPhetString import ratioAndProportion from '../../../ratioAndProportion.js'; import ratioAndProportionStrings from '../../../ratioAndProportionStrings.js'; import rapConstants from '../../rapConstants.js'; -import Property from '../../../../../axon/js/Property.js'; import RAPModel from '../../model/RAPModel.js'; +import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js'; // constants const RATIO_FITNESS_STRINGS_CAPITALIZED = [ @@ -58,8 +58,8 @@ assert && assert( RATIO_FITNESS_STRINGS_LOWERCASE.length === RATIO_FITNESS_STRIN class RatioDescriber { - private ratioFitnessProperty: Property; - private unclampedFitnessProperty: Property; + private ratioFitnessProperty: IReadOnlyProperty; + private unclampedFitnessProperty: IReadOnlyProperty; private model: RAPModel; /** diff --git a/js/common/view/sound/InProportionSoundGenerator.ts b/js/common/view/sound/InProportionSoundGenerator.ts index 433bf490..47a57aee 100644 --- a/js/common/view/sound/InProportionSoundGenerator.ts +++ b/js/common/view/sound/InProportionSoundGenerator.ts @@ -16,6 +16,7 @@ import inProportionSound from '../../../../sounds/in-proportion/in-proportion_mp import ratioAndProportion from '../../../ratioAndProportion.js'; import RAPModel from '../../model/RAPModel.js'; import Property from '../../../../../axon/js/Property.js'; +import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js'; const SUCCESS_OUTPUT_LEVEL = 0.8; const SILENT_LEVEL = 0; @@ -33,7 +34,7 @@ class InProportionSoundGenerator extends SoundClip { private model: RAPModel; private targetRatioProperty: Property; - private fitnessProperty: Property; + private fitnessProperty: IReadOnlyProperty; private playedSuccessYetProperty: Property; private timePlayedSoFarProperty: Property; private previousRatioWasLargerThanTarget: boolean; @@ -69,7 +70,7 @@ class InProportionSoundGenerator extends SoundClip { // of this sound is always played, even when the outside enabledControlProperty is set to false. this.timePlayedSoFarProperty = new NumberProperty( MANDATORY_PLAY_TIME ); - const playedMandatoryPortionYetProperty: DerivedProperty = new DerivedProperty( [ this.timePlayedSoFarProperty, this.playedSuccessYetProperty ], + const playedMandatoryPortionYetProperty: IReadOnlyProperty = new DerivedProperty( [ this.timePlayedSoFarProperty, this.playedSuccessYetProperty ], ( timePlayed: number, playedSuccessYet: boolean ) => playedSuccessYet && timePlayed <= MANDATORY_PLAY_TIME ); // In addition to any supplemental enabledControlProperty that the client wants to pass in, make sure to set up diff --git a/js/common/view/sound/MovingInProportionSoundGenerator.ts b/js/common/view/sound/MovingInProportionSoundGenerator.ts index a204ac72..7a12f464 100644 --- a/js/common/view/sound/MovingInProportionSoundGenerator.ts +++ b/js/common/view/sound/MovingInProportionSoundGenerator.ts @@ -14,6 +14,7 @@ import choirLoopSound from '../../../../sounds/moving-in-proportion/moving-in-pr import movingInProportionOrganLoopSound from '../../../../sounds/moving-in-proportion/moving-in-proportion-organ-loop_mp3.js'; import ratioAndProportion from '../../../ratioAndProportion.js'; import RAPModel from '../../model/RAPModel.js'; +import RAPRatioTuple from '../../model/RAPRatioTuple.js'; class MovingInProportionSoundGenerator extends SoundGenerator { @@ -52,7 +53,7 @@ class MovingInProportionSoundGenerator extends SoundGenerator { model.inProportionProperty, model.ratio.tupleProperty, model.ratioFitnessProperty - ], ( movingInDirection: boolean, inProportion: boolean ) => { + ], ( movingInDirection: boolean, inProportion: boolean, tuple: RAPRatioTuple, ratioFitness: number ) => { if ( movingInDirection && // only when moving !model.valuesTooSmallForInProportion() && // no moving in proportion success if too small inProportion && // must be fit enough to play the moving in proportion success diff --git a/js/common/view/sound/StaccatoFrequencySoundGenerator.ts b/js/common/view/sound/StaccatoFrequencySoundGenerator.ts index bcb47ca0..64de58fa 100644 --- a/js/common/view/sound/StaccatoFrequencySoundGenerator.ts +++ b/js/common/view/sound/StaccatoFrequencySoundGenerator.ts @@ -38,7 +38,7 @@ import g001Sound from '../../../../sounds/staccato/staccato-g-001_mp3.js'; import g002Sound from '../../../../sounds/staccato/staccato-g-002_mp3.js'; import gSound from '../../../../sounds/staccato/staccato-g_mp3.js'; import ratioAndProportion from '../../../ratioAndProportion.js'; -import Property from '../../../../../axon/js/Property.js'; +import IReadOnlyProperty from '../../../../../axon/js/IReadOnlyProperty.js'; // organize the sounds by variation and note const staccatoSounds = [ @@ -55,8 +55,8 @@ const staccatoSounds = [ class StaccatoFrequencySoundGenerator extends SoundGenerator { - private inProportionProperty: Property; - private fitnessProperty: Property; + private inProportionProperty: IReadOnlyProperty; + private fitnessProperty: IReadOnlyProperty; private staccatoSoundClips: SoundClip[][]; private timeLinearFunction: LinearFunction; private timeSinceLastPlay: number; @@ -67,7 +67,7 @@ class StaccatoFrequencySoundGenerator extends SoundGenerator { * @param {Property.} inProportionProperty - true when the model ratio is in proportion * @param {Object} [options] */ - constructor( fitnessProperty: Property, fitnessRange: Range, inProportionProperty: Property, options: object ) { + constructor( fitnessProperty: IReadOnlyProperty, fitnessRange: Range, inProportionProperty: IReadOnlyProperty, options: object ) { options = merge( { initialOutputLevel: 0.25 }, options ); diff --git a/js/create/view/CreateScreenSummaryNode.ts b/js/create/view/CreateScreenSummaryNode.ts index f51c48db..2bfbfa21 100644 --- a/js/create/view/CreateScreenSummaryNode.ts +++ b/js/create/view/CreateScreenSummaryNode.ts @@ -17,14 +17,15 @@ import HandPositionsDescriber from '../../common/view/describers/HandPositionsDe import MyChallengeAccordionBox from './MyChallengeAccordionBox.js'; import BackgroundColorHandler from '../../common/view/BackgroundColorHandler.js'; import TickMarkView from '../../common/view/TickMarkView.js'; +import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js'; class CreateScreenSummaryNode extends Node { - constructor( ratioFitnessProperty: Property, + constructor( ratioFitnessProperty: IReadOnlyProperty, ratioTupleProperty: Property, tickMarkViewProperty: Property, ratioDescriber: RatioDescriber, - inProportionProperty: Property, + inProportionProperty: IReadOnlyProperty, handPositionsDescriber: HandPositionsDescriber, tickMarkRangeProperty: Property, myChallengeAccordionBox: MyChallengeAccordionBox ) { @@ -79,7 +80,7 @@ class CreateScreenSummaryNode extends Node { inProportionProperty, tickMarkRangeProperty ], ( tickMarkView: TickMarkView, targetAntecedent: number, targetConsequent: number, - currentTuple: RAPRatioTuple, fitness: number, inProportion: boolean ) => { + currentTuple: RAPRatioTuple, fitness: number, inProportion: boolean, tickMarkRange: number ) => { stateOfSimNode.innerContent = StringUtils.fillIn( ratioAndProportionStrings.a11y.screenSummaryQualitativeStateOfSim, { color: BackgroundColorHandler.getCurrentColorRegion( fitness, inProportion ), diff --git a/js/create/view/CreateScreenView.ts b/js/create/view/CreateScreenView.ts index 75bc730c..80d58dd4 100644 --- a/js/create/view/CreateScreenView.ts +++ b/js/create/view/CreateScreenView.ts @@ -101,7 +101,7 @@ class CreateScreenView extends RAPScreenView { } ) ); // The "lock ratio" checkbox should not be enabled when the ratio is not in proportion. - Property.multilink( [ + Property.multilink<[ boolean, number ]>( [ model.inProportionProperty, model.ratioFitnessProperty ], ( inProportion: boolean ) => { diff --git a/js/create/view/MyChallengeAccordionBox.ts b/js/create/view/MyChallengeAccordionBox.ts index 505f7986..b1c6e702 100644 --- a/js/create/view/MyChallengeAccordionBox.ts +++ b/js/create/view/MyChallengeAccordionBox.ts @@ -97,7 +97,7 @@ class MyChallengeAccordionBox extends AccordionBox { // unlocked, see https://github.com/phetsims/ratio-and-proportion/issues/227 for extensive investigation. NOTE: // This should be above the creation of the NumberPickers to make sure that this fires before the RAPModel.targetRatioProperty // changes. - Property.multilink( [ targetAntecedentProperty, targetConsequentProperty ], () => { + Property.multilink( [ targetAntecedentProperty, targetConsequentProperty ], () => { // if currently locked, then it is about to be unlocked ratioLockedProperty.value && this.alertDescriptionUtterance( ratioUnlockedFromMyChallenge ); diff --git a/js/discover/view/DiscoverScreenSummaryNode.ts b/js/discover/view/DiscoverScreenSummaryNode.ts index 891547be..df9c4110 100644 --- a/js/discover/view/DiscoverScreenSummaryNode.ts +++ b/js/discover/view/DiscoverScreenSummaryNode.ts @@ -16,12 +16,13 @@ import HandPositionsDescriber from '../../common/view/describers/HandPositionsDe import TickMarkView from '../../common/view/TickMarkView.js'; import BackgroundColorHandler from '../../common/view/BackgroundColorHandler.js'; import RatioDescriber from '../../common/view/describers/RatioDescriber.js'; +import IReadOnlyProperty from '../../../../axon/js/IReadOnlyProperty.js'; class DiscoverScreenSummaryNode extends Node { - constructor( ratioFitnessProperty: Property, ratioTupleProperty: Property, + constructor( ratioFitnessProperty: IReadOnlyProperty, ratioTupleProperty: Property, targetRatioProperty: Property, tickMarkViewProperty: Property, - ratioDescriber: RatioDescriber, inProportionProperty: Property, handPositionsDescriber: HandPositionsDescriber, + ratioDescriber: RatioDescriber, inProportionProperty: IReadOnlyProperty, handPositionsDescriber: HandPositionsDescriber, ratioToChallengeNameMap: Map ) { const stateOfSimNode = new Node( {