diff --git a/js/description/ph-scale-description-logic.js b/js/description/ph-scale-description-logic.js new file mode 100644 index 00000000..aaa642c4 --- /dev/null +++ b/js/description/ph-scale-description-logic.js @@ -0,0 +1,133 @@ +// Copyright 2023-2024, University of Colorado Boulder + +// @author Jonathan Olson + +/* eslint-disable */ + +export default () => { + let context; + let strings; + + const totalVolumeToEnum = totalVolume => { + if ( totalVolume === 0 ) { + return 'empty'; + } + else if ( totalVolume < 1.2 ) { + return 'partiallyFilled'; + } + else { + return 'full'; + } + }; + + return phet.joist.DescriptionContext.registerLogic( { + launch( descriptionContext, descriptionStrings ) { + context = descriptionContext; + strings = descriptionStrings; + + const macroScreen = context.get( 'phScale.macroScreen' ); + if ( macroScreen ) { + const macroScreenView = context.get( 'phScale.macroScreen.view' ); + + // solution + //const soluteProperty = context.get( 'phScale.macroScreen.model.solution.soluteProperty' ); + const soluteProperty = context.get( 'phScale.macroScreen.model.dropper.soluteProperty' ); // TODO: why was this not defined? + const solutionPHProperty = context.get( 'phScale.macroScreen.model.solution.pHProperty' ); + const solutionTotalVolumeProperty = context.get( 'phScale.macroScreen.model.solution.totalVolumeProperty' ); + const soluteVolumeProperty = context.get( 'phScale.macroScreen.model.solution.soluteVolumeProperty' ); + const waterVolumeProperty = context.get( 'phScale.macroScreen.model.solution.waterVolumeProperty' ); + + // pH Meter + const pHMeterPHProperty = context.get( 'phScale.macroScreen.model.pHMeter.pHProperty' ); + const pHMeterPositionProperty = context.get( 'phScale.macroScreen.model.pHMeter.probe.positionProperty' ); + + // Dropper + const dropperEnabledProperty = context.get( 'phScale.macroScreen.model.dropper.enabledProperty' ); + const isDispensingProperty = context.get( 'phScale.macroScreen.model.dropper.isDispensingProperty' ); + const isAutofillingProperty = context.get( 'phScale.macroScreen.model.isAutofillingProperty' ); + + // Water faucet + const waterFaucetEnabledProperty = context.get( 'phScale.macroScreen.model.waterFaucet.enabledProperty' ); + const waterFaucetFlowRateProperty = context.get( 'phScale.macroScreen.model.waterFaucet.flowRateProperty' ); + + // Drain faucet + const drainFaucetEnabledProperty = context.get( 'phScale.macroScreen.model.drainFaucet.enabledProperty' ); + const drainFaucetFlowRateProperty = context.get( 'phScale.macroScreen.model.drainFaucet.flowRateProperty' ); + + + const simStateDescriptionNode = new phet.scenery.Node( { + tagName: 'p' + } ); + + context.nodeSet( macroScreenView, 'screenSummaryContent', new phet.scenery.Node( { + children: [ + new phet.scenery.Node( { + tagName: 'p', + innerContent: strings.screenSummary() + } ), + simStateDescriptionNode + ] + } ) ); + + context.multilink( [ + soluteProperty, + solutionTotalVolumeProperty + ], ( + solute, + solutionTotalVolume + ) => { + simStateDescriptionNode.innerContent = strings.screenSummarySimStateDescription( + solute.tandemName, + totalVolumeToEnum( solutionTotalVolume ) + ); + } ); + + const solutes = [ + phet.phScale.Solute.BATTERY_ACID, + phet.phScale.Solute.BLOOD, + phet.phScale.Solute.CHICKEN_SOUP, + phet.phScale.Solute.COFFEE, + phet.phScale.Solute.DRAIN_CLEANER, + phet.phScale.Solute.HAND_SOAP, + phet.phScale.Solute.MILK, + phet.phScale.Solute.ORANGE_JUICE, + phet.phScale.Solute.SODA, + phet.phScale.Solute.SPIT, + phet.phScale.Solute.VOMIT, + phet.phScale.Solute.WATER + ]; + + const soluteComboBox = context.get( 'phScale.macroScreen.view.soluteComboBox' ); + context.nodeSet( soluteComboBox, 'accessibleName', strings.soluteComboBoxAccessibleName() ); + for ( const solute of solutes ) { + context.propertySet( soluteComboBox.a11yNamePropertyMap.get( solute ), strings.soluteName( solute.tandemName ) ); + } + + // pdomOrder + // context.nodeSet( macroScreenView.pdomPlayAreaNode, 'pdomOrder', [ + // context.get( 'phScale.macroScreen.view.dropperNode.button' ), + // context.get( 'phScale.macroScreen.view.soluteComboBox' ), + // context.get( 'phScale.macroScreen.view.waterFaucetNode.faucetNode' ), + // context.get( 'phScale.macroScreen.view.drainFaucetNode' ), + // context.get( 'phScale.macroScreen.view.pHMeterNode.probeNode' ), + // context.get( 'phScale.macroScreen.view.resetAllButton' ) + // ] ); + + const alerter = new phet.sceneryPhet.Alerter( { + alertToVoicing: false, + descriptionAlertNode: macroScreenView + } ); + + context.lazyLink( isDispensingProperty, isDispensing => { + alerter.alert( strings.dropperDispensingAlert( isDispensing ) ); + } ); + } + }, + added( tandemID, obj ) { + // Will be called when an object is dynamically added after sim start-up + }, + removed( tandemID, obj ) { + // Will be called when an object is dynamically removed after sim start-up + } + } ); +}; \ No newline at end of file diff --git a/js/description/ph-scale-description-strings_en.js b/js/description/ph-scale-description-strings_en.js new file mode 100644 index 00000000..4d25626c --- /dev/null +++ b/js/description/ph-scale-description-strings_en.js @@ -0,0 +1,52 @@ +// Copyright 2023-2024, University of Colorado Boulder + +// @author Jonathan Olson + +// eslint-disable + +export default () => { + const soluteMap = { + batteryAcid: 'battery acid', + blood: 'blood', + chickenSoup: 'chicken soup', + coffee: 'coffee', + drainCleaner: 'drain cleaner', + handSoap: 'hand soap', + milk: 'milk', + orangeJuice: 'orange juice', + soda: 'soda', + spit: 'spit', + vomit: 'vomit', + water: 'water' + }; + + const totalVolumeMap = { + empty: 'empty', + partiallyFilled: 'partially filled', + full: 'full' + }; + + return phet.joist.DescriptionContext.registerStrings( { + locale: 'en', + screenSummary() { + return 'Place description of the screen here, or break into multiple paragraphs'; + }, + screenSummarySimStateDescription( + solute, + totalVolume + ) { + return `The solute is ${soluteMap[ solute ]}, and the total volume is ${totalVolumeMap[ totalVolume ]}.`; + }, + soluteComboBoxAccessibleName() { + return 'Solute'; + }, + soluteName( solute ) { + return soluteMap[ solute ]; + }, + dropperDispensingAlert( + isDispensing + ) { + return isDispensing ? 'Dispensing' : 'Not dispensing'; + } + } ); +}; \ No newline at end of file diff --git a/js/description/ph-scale-description-strings_es.js b/js/description/ph-scale-description-strings_es.js new file mode 100644 index 00000000..57f72c30 --- /dev/null +++ b/js/description/ph-scale-description-strings_es.js @@ -0,0 +1,52 @@ +// Copyright 2023-2024, University of Colorado Boulder + +// @author Jonathan Olson + +// eslint-disable + +export default () => { + const soluteMap = { + batteryAcid: 'ácido de batería', + blood: 'sangre', + chickenSoup: 'sopa de pollo', + coffee: 'café', + drainCleaner: 'limpiador de desagües', + handSoap: 'jabón de manos', + milk: 'leche', + orangeJuice: 'jugo de naranja', + soda: 'gaseosa', + spit: 'saliva', + vomit: 'vómito', + water: 'agua' + }; + + const totalVolumeMap = { + empty: 'vacía', + partiallyFilled: 'parcialmente lleno', + full: 'lleno' + }; + + return phet.joist.DescriptionContext.registerStrings( { + locale: 'es', + screenSummary() { + return 'Coloque la descripción de la pantalla aquí o divídala en varios párrafos'; + }, + screenSummarySimStateDescription( + solute, + totalVolume + ) { + return `El soluto es ${soluteMap[ solute ]}, y el volumen total es ${totalVolumeMap[ totalVolume ]}.`; + }, + soluteComboBoxAccessibleName() { + return 'Soluto'; + }, + soluteName( solute ) { + return soluteMap[ solute ]; + }, + dropperDispensingAlert( + isDispensing + ) { + return isDispensing ? 'dispensando' : 'no dispensando'; + } + } ); +}; \ No newline at end of file diff --git a/js/ph-scale-main.ts b/js/ph-scale-main.ts index 2eed63e0..5a904653 100644 --- a/js/ph-scale-main.ts +++ b/js/ph-scale-main.ts @@ -14,9 +14,25 @@ import MacroScreen from './macro/MacroScreen.js'; import MicroScreen from './micro/MicroScreen.js'; import MySolutionScreen from './mysolution/MySolutionScreen.js'; import PhScaleStrings from './PhScaleStrings.js'; +import DescriptionContext from '../../joist/js/DescriptionContext.js'; +import PHScaleDescriptionStrings_en from './description/ph-scale-description-strings_en.js'; // eslint-disable-line default-import-match-filename +import PHScaleDescriptionStrings_es from './description/ph-scale-description-strings_es.js'; // eslint-disable-line default-import-match-filename +import PHScaleDescriptionLogic from './description/ph-scale-description-logic.js'; // eslint-disable-line default-import-match-filename +import Alerter from '../../scenery-phet/js/accessibility/describers/Alerter.js'; simLauncher.launch( () => { + if ( phet.chipper.queryParameters.supportsDescriptionPlugin ) { + PHScaleDescriptionStrings_en(); + PHScaleDescriptionStrings_es(); + PHScaleDescriptionLogic(); + + phet.log && phet.log( Alerter ); + + phet.log && phet.log( PHScaleDescriptionStrings_en.toString() ); + phet.log && phet.log( PHScaleDescriptionLogic.toString() ); + } + const screens = [ new MacroScreen( Tandem.ROOT.createTandem( 'macroScreen' ) ), new MicroScreen( Tandem.ROOT.createTandem( 'microScreen' ) ), @@ -28,5 +44,9 @@ simLauncher.launch( () => { phetioDesigned: true } ); + phet.chipper.queryParameters.supportsDescriptionPlugin && sim.isConstructionCompleteProperty.lazyLink( isConstructionComplete => { + DescriptionContext.startupComplete(); + } ); + sim.start(); } ); \ No newline at end of file diff --git a/package.json b/package.json index f1688487..1c651a30 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ ], "simFeatures": { "supportsDynamicLocale": true, + "supportsDescriptionPlugin": true, + "supportsInteractiveDescription": true, "colorProfiles": [ "default" ] diff --git a/ph-scale_description_editor.html b/ph-scale_description_editor.html new file mode 100644 index 00000000..b3a9d6ce --- /dev/null +++ b/ph-scale_description_editor.html @@ -0,0 +1,635 @@ + + + + + + + + + + + + + pH Scale Description Editor + + + + + + + + + + + + +
+
+
+
+
+
+
+ +
+
+
+
+

Activity log of Responsive Descriptions

+ +
+ +
+
+
+
+

State Descriptions for pH Scale

+ +
+
+
+ + + + + + diff --git a/ph-scale_en.html b/ph-scale_en.html index 5492dc1d..ecb808f8 100644 --- a/ph-scale_en.html +++ b/ph-scale_en.html @@ -45,6 +45,8 @@ ], "simFeatures": { "supportsDynamicLocale": true, + "supportsDescriptionPlugin": true, + "supportsInteractiveDescription": true, "colorProfiles": [ "default" ]