From 5fc5259b51c10e1a722ff842f136f438dfcb08df Mon Sep 17 00:00:00 2001 From: Marla Schulz Date: Wed, 26 Apr 2023 09:24:06 -0700 Subject: [PATCH] Add dynamic layout for Text, see: https://github.com/phetsims/center-and-variability/issues/147 --- js/common/CAVConstants.ts | 4 +++- js/common/view/CAVPlotNode.ts | 18 +++++++++++------- js/variability/view/MADInfoNode.ts | 20 +++++++++++++------- js/variability/view/MADNode.ts | 15 +++++++++------ js/variability/view/RangeInfoNode.ts | 17 ++++++++++------- js/variability/view/RangeNode.ts | 17 +++++++++++------ 6 files changed, 57 insertions(+), 34 deletions(-) diff --git a/js/common/CAVConstants.ts b/js/common/CAVConstants.ts index 5806b62a..22893209 100644 --- a/js/common/CAVConstants.ts +++ b/js/common/CAVConstants.ts @@ -46,7 +46,9 @@ const CAVConstants = { NUMBER_LINE_MARGIN_X: NUMBER_LINE_MARGIN_X, // TODO-design: This is the color from the design doc, but perhaps #777777 or darker would be better? Let's discuss once the IQR lines are drawn - GRAY_DATA_POINT_FILL: '#8f8f8f' + GRAY_DATA_POINT_FILL: '#8f8f8f', + + INFO_DIALOG_MAX_TEXT_WIDTH: 400 }; centerAndVariability.register( 'CAVConstants', CAVConstants ); diff --git a/js/common/view/CAVPlotNode.ts b/js/common/view/CAVPlotNode.ts index 1ce0a6f9..2107ebc4 100644 --- a/js/common/view/CAVPlotNode.ts +++ b/js/common/view/CAVPlotNode.ts @@ -9,7 +9,7 @@ */ import centerAndVariability from '../../centerAndVariability.js'; -import { Node, NodeOptions, Rectangle, TColor, Text } from '../../../../scenery/js/imports.js'; +import { ManualConstraint, Node, NodeOptions, Rectangle, TColor, Text } from '../../../../scenery/js/imports.js'; import optionize from '../../../../phet-core/js/optionize.js'; import CAVModel from '../model/CAVModel.js'; import CAVObject from '../model/CAVObject.js'; @@ -19,7 +19,6 @@ import CAVObjectType from '../model/CAVObjectType.js'; import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js'; import NumberLineNode from './NumberLineNode.js'; import Bounds2 from '../../../../dot/js/Bounds2.js'; -import PhetFont from '../../../../scenery-phet/js/PhetFont.js'; import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js'; import PickRequired from '../../../../phet-core/js/types/PickRequired.js'; import BooleanProperty from '../../../../axon/js/BooleanProperty.js'; @@ -70,13 +69,18 @@ export default class CAVPlotNode extends Node { } ); backgroundNode.addChild( numberLineNode ); - backgroundNode.addChild( new Text( CenterAndVariabilityStrings.distanceInMetersStringProperty, { + const distanceInMetersText = new Text( CenterAndVariabilityStrings.distanceInMetersStringProperty, { + top: numberLineNode.bottom + 2, + maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH + } ); + backgroundNode.addChild( distanceInMetersText ); + + ManualConstraint.create( this, [ numberLineNode, distanceInMetersText ], ( numberLineProxy, textProxy ) => { // TODO-UX: This may be asymmetrical if it accounts for edge labels - centerX: numberLineNode.centerX, - top: numberLineNode.bottom + 2, - font: new PhetFont( 13 ) - } ) ); + textProxy.centerX = numberLineProxy.centerX; + } ); + backgroundNode.addChild( this.dotLayer ); // TODO: This overlaps with draggingEnabled diff --git a/js/variability/view/MADInfoNode.ts b/js/variability/view/MADInfoNode.ts index 835ddb36..a8879ea4 100644 --- a/js/variability/view/MADInfoNode.ts +++ b/js/variability/view/MADInfoNode.ts @@ -1,7 +1,7 @@ // Copyright 2023, University of Colorado Boulder import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; -import { HBox, HSeparator, Text, VBox, VStrut } from '../../../../scenery/js/imports.js'; +import { HBox, HSeparator, Text, VBox } from '../../../../scenery/js/imports.js'; import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js'; import PatternStringProperty from '../../../../axon/js/PatternStringProperty.js'; import VariabilityModel from '../model/VariabilityModel.js'; @@ -9,6 +9,7 @@ import PickRequired from '../../../../phet-core/js/types/PickRequired.js'; import PhetioObject from '../../../../tandem/js/PhetioObject.js'; import centerAndVariability from '../../centerAndVariability.js'; import Utils from '../../../../dot/js/Utils.js'; +import CAVConstants from '../../common/CAVConstants.js'; import MADNode from './MADNode.js'; export default class MADInfoNode extends VBox { @@ -50,10 +51,16 @@ export default class MADInfoNode extends VBox { align: 'left', spacing: 6, children: [ - new Text( CenterAndVariabilityStrings.meanAbsoluteDeviationMADStringProperty, { fontSize: 25 } ), - new VStrut( 10 ), - new Text( CenterAndVariabilityStrings.madDescriptionStringProperty, { fontSize: 18 } ), - new VStrut( 10 ), + new Text( CenterAndVariabilityStrings.meanAbsoluteDeviationMADStringProperty, { + fontSize: 25, + maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH, + layoutOptions: { bottomMargin: 10 } + } ), + new Text( CenterAndVariabilityStrings.madDescriptionStringProperty, { + fontSize: 18, + maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH, + layoutOptions: { bottomMargin: 10 } + } ), new HBox( { spacing: 10, children: [ @@ -71,8 +78,7 @@ export default class MADInfoNode extends VBox { // TODO-design: I changed the wording slightly from the design doc new Text( new PatternStringProperty( CenterAndVariabilityStrings.madCalculationResultPatternStringProperty, { mad: new DerivedProperty( [ model.madValueProperty ], madValue => madValue === null ? null : Utils.toFixed( madValue, 1 ) ) - } ), { fontSize: 18, visibleProperty: hasEnoughDataProperty } ), - new VStrut( 10 ), + } ), { fontSize: 18, visibleProperty: hasEnoughDataProperty, maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH, layoutOptions: { bottomMargin: 10 } } ), new MADNode( model, { parentContext: 'info', diff --git a/js/variability/view/MADNode.ts b/js/variability/view/MADNode.ts index d67c038b..553d6f0a 100644 --- a/js/variability/view/MADNode.ts +++ b/js/variability/view/MADNode.ts @@ -1,6 +1,6 @@ // Copyright 2023, University of Colorado Boulder -import { Line, Node, Rectangle, Text } from '../../../../scenery/js/imports.js'; +import { Line, ManualConstraint, Node, Rectangle, Text } from '../../../../scenery/js/imports.js'; import centerAndVariability from '../../centerAndVariability.js'; import VariabilityModel from '../model/VariabilityModel.js'; import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js'; @@ -26,11 +26,15 @@ export default class MADNode extends CAVPlotNode { ...options } ); - const needAtLeastOneKick = new Text( CenterAndVariabilityStrings.needAtLeastOneKickStringProperty, { + const needAtLeastOneKickText = new Text( CenterAndVariabilityStrings.needAtLeastOneKickStringProperty, { fontSize: 18, - top: 100 + top: 100, + maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH } ); - this.addChild( needAtLeastOneKick ); + ManualConstraint.create( this, [ needAtLeastOneKickText ], textProxy => { + textProxy.center = this.modelViewTransform.modelToViewXY( 8, 2 ); + } ); + this.addChild( needAtLeastOneKickText ); const madRectangle = new Rectangle( 0, 50, 100, 72, { fill: '#e0c0f5', @@ -138,8 +142,7 @@ export default class MADNode extends CAVPlotNode { leftReadout.visible = ( options.parentContext === 'info' || model.isShowingMADProperty.value ) && mad !== null && sortedDots.length > 1; rightReadout.visible = ( options.parentContext === 'info' || model.isShowingMADProperty.value ) && mad !== null && sortedDots.length > 1; - needAtLeastOneKick.center = this.modelViewTransform.modelToViewXY( 8, 2 ); - needAtLeastOneKick.visible = model.numberOfDataPointsProperty.value === 0 && ( options.parentContext === 'info' || model.isShowingMADProperty.value ); + needAtLeastOneKickText.visible = model.numberOfDataPointsProperty.value === 0 && ( options.parentContext === 'info' || model.isShowingMADProperty.value ); }; model.objectChangedEmitter.addListener( update ); model.isShowingMADProperty.link( update ); diff --git a/js/variability/view/RangeInfoNode.ts b/js/variability/view/RangeInfoNode.ts index e273d93c..586b5c4d 100644 --- a/js/variability/view/RangeInfoNode.ts +++ b/js/variability/view/RangeInfoNode.ts @@ -1,7 +1,7 @@ // Copyright 2023, University of Colorado Boulder import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; -import { Text, VBox, VStrut } from '../../../../scenery/js/imports.js'; +import { Text, VBox } from '../../../../scenery/js/imports.js'; import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js'; import PatternStringProperty from '../../../../axon/js/PatternStringProperty.js'; import VariabilityModel from '../model/VariabilityModel.js'; @@ -9,6 +9,7 @@ import PickRequired from '../../../../phet-core/js/types/PickRequired.js'; import PhetioObject from '../../../../tandem/js/PhetioObject.js'; import centerAndVariability from '../../centerAndVariability.js'; import RangeNode from './RangeNode.js'; +import CAVConstants from '../../common/CAVConstants.js'; export default class RangeInfoNode extends VBox { public constructor( model: VariabilityModel, options: PickRequired ) { @@ -18,19 +19,21 @@ export default class RangeInfoNode extends VBox { align: 'left', spacing: 5, children: [ - new Text( CenterAndVariabilityStrings.rangeStringProperty, { fontSize: 25 } ), - new VStrut( 10 ), - new Text( CenterAndVariabilityStrings.rangeDescriptionStringProperty, { fontSize: 18 } ), + new Text( CenterAndVariabilityStrings.rangeStringProperty, { + fontSize: 25, + maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH, + layoutOptions: { bottomMargin: 10 } + } ), + new Text( CenterAndVariabilityStrings.rangeDescriptionStringProperty, { fontSize: 18, maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH } ), // TODO: String key name new Text( new PatternStringProperty( CenterAndVariabilityStrings.rangeCalculationPatternStringProperty, { max: model.maxValueProperty, min: model.minValueProperty - } ), { fontSize: 18, visibleProperty: hasEnoughDataProperty } ), + } ), { fontSize: 18, visibleProperty: hasEnoughDataProperty, maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH } ), new Text( new PatternStringProperty( CenterAndVariabilityStrings.rangeCalculationResultPatternStringProperty, { range: model.rangeValueProperty - } ), { fontSize: 18, visibleProperty: hasEnoughDataProperty } ), - new VStrut( 10 ), + } ), { fontSize: 18, visibleProperty: hasEnoughDataProperty, maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH, layoutOptions: { bottomMargin: 10 } } ), new RangeNode( model, { parentContext: 'info', diff --git a/js/variability/view/RangeNode.ts b/js/variability/view/RangeNode.ts index 9b324b87..eb36b635 100644 --- a/js/variability/view/RangeNode.ts +++ b/js/variability/view/RangeNode.ts @@ -1,8 +1,9 @@ // Copyright 2023, University of Colorado Boulder +// TODO: File description import PhetFont from '../../../../scenery-phet/js/PhetFont.js'; import MedianBarNode from '../../common/view/MedianBarNode.js'; -import { Rectangle, Text } from '../../../../scenery/js/imports.js'; +import { ManualConstraint, Rectangle, Text } from '../../../../scenery/js/imports.js'; import centerAndVariability from '../../centerAndVariability.js'; import VariabilityModel from '../model/VariabilityModel.js'; import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js'; @@ -26,11 +27,16 @@ export default class RangeNode extends CAVPlotNode { ...options } ); - const needAtLeastOneKick = new Text( CenterAndVariabilityStrings.needAtLeastOneKickStringProperty, { + const needAtLeastOneKickText = new Text( CenterAndVariabilityStrings.needAtLeastOneKickStringProperty, { fontSize: 18, - top: 100 + top: 100, + maxWidth: CAVConstants.INFO_DIALOG_MAX_TEXT_WIDTH } ); - this.addChild( needAtLeastOneKick ); + + ManualConstraint.create( this, [ needAtLeastOneKickText ], textProxy => { + needAtLeastOneKickText.center = this.modelViewTransform.modelToViewXY( 8, 2 ); + } ); + this.addChild( needAtLeastOneKickText ); // TODO: Combine into a single node? const rangeTextReadout = new Text( '', { @@ -93,8 +99,7 @@ export default class RangeNode extends CAVPlotNode { rangeRectangle.visible = rangeVisibility; rangeBar.visible = rangeVisibility; rangeTextReadout.visible = rangeVisibility; - needAtLeastOneKick.center = this.modelViewTransform.modelToViewXY( 8, 2 ); - needAtLeastOneKick.visible = model.numberOfDataPointsProperty.value === 0 && ( options.parentContext === 'info' || + needAtLeastOneKickText.visible = model.numberOfDataPointsProperty.value === 0 && ( options.parentContext === 'info' || ( options.parentContext === 'accordion' && model.isShowingRangeProperty.value ) ); }; model.objectChangedEmitter.addListener( updateRangeNode );