Skip to content

Commit

Permalink
new position landmark mapping, phetsims/gravity-force-lab-basics#107
Browse files Browse the repository at this point in the history
  • Loading branch information
zepumph committed Apr 3, 2019
1 parent 87a2ea9 commit 2cac7d1
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 99 deletions.
35 changes: 22 additions & 13 deletions js/ISLCA11yStrings.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,27 +82,36 @@ define( require => {
distanceFromOtherObjectPattern: {
value: '{{distance}} {{otherObjectLabel}}'
},
positionMarkPattern: {
value: '{{position}} {{unit}} mark'
},
positionDistanceFromOtherObjectPattern: {
value: '{{positionMark}}, {{distanceFromOtherObject}}.'
value: '{{positionLandmark}}, {{distanceFromOtherObject}}.'
},
distanceFromOtherObjectSentencePattern: {
value: '{{distanceFromOtherObject}}.'
},
// closer/further away
progressDistanceFromOtherObjectPattern: {
value: '{{progress}}, {{distanceFromOtherObject}}.'

// position landmarks
leftSideOfTrack: {
value: 'Left side of track'
},
rightSideOfTrack: {
value: 'Right side of track'
},
edgeDistancePattern: {
value: '{{distanceAndUnits}} away'
lastStopRight: {
value: 'Last stop right'
},
arrivedAtEdgePattern: {
value: 'At {{side}} edge, {{distanceClause}}.'
lastStopLeft: {
value: 'Last stop left'
},
closestToOtherObjectPattern: {
value: '{{positionMark}}, {{edgePhrase}}, {{distanceFromOtherObject}}.'
trackEndLeft: {
value: 'Track end left'
},
trackEndRight: {
value: 'Track end right'
},

// closer/further away
progressDistanceFromOtherObjectPattern: {
value: '{{progress}}, {{distanceFromOtherObject}}.'
},
sidePattern: {
value: '{{side}} edge'
Expand Down
3 changes: 3 additions & 0 deletions js/view/ISLCObjectNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ define( function( require ) {
accessibleSliderOptions
);

// a11y - keep value text in sync as the model changes
model.forceProperty.lazyLink( () => this.updateOnFocusAriaValueText() );

// TODO: move to MassNode since ChargeNodes don't have a changing radiusProperty.
this.objectModel.radiusProperty.link( function() {

Expand Down
151 changes: 65 additions & 86 deletions js/view/describers/PositionDescriber.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,23 @@ define( require => {
const quantitativeAndQualitativePatternString = ISLCA11yStrings.quantitativeAndQualitativePattern.value;
const centersOfObjectsDistancePatternString = ISLCA11yStrings.centersOfObjectsDistancePattern.value;

const positionMarkPatternString = ISLCA11yStrings.positionMarkPattern.value;
const positionDistanceFromOtherObjectPatternString = ISLCA11yStrings.positionDistanceFromOtherObjectPattern.value;
const progressDistanceFromOtherObjectPatternString = ISLCA11yStrings.progressDistanceFromOtherObjectPattern.value;
const arrivedAtEdgePatternString = ISLCA11yStrings.arrivedAtEdgePattern.value;
const edgeDistancePatternString = ISLCA11yStrings.edgeDistancePattern.value;
const closestToOtherObjectPatternString = ISLCA11yStrings.closestToOtherObjectPattern.value;
const sidePatternString = ISLCA11yStrings.sidePattern.value;
const distanceAndUnitsPatternString = ISLCA11yStrings.distanceAndUnitsPattern.value;
const quantitativeDistancePatternString = ISLCA11yStrings.quantitativeDistancePattern.value;
const distanceFromOtherObjectPatternString = ISLCA11yStrings.distanceFromOtherObjectPattern.value;
const distanceFromOtherObjectSentencePatternString = ISLCA11yStrings.distanceFromOtherObjectSentencePattern.value;
const lastStopString = ISLCA11yStrings.lastStop.value;

// track landmarks
const leftSideOfTrackString = ISLCA11yStrings.leftSideOfTrack.value;
const rightSideOfTrackString = ISLCA11yStrings.rightSideOfTrack.value;
const lastStopRightString = ISLCA11yStrings.lastStopRight.value;
const lastStopLeftString = ISLCA11yStrings.lastStopLeft.value;
const trackEndLeftString = ISLCA11yStrings.trackEndLeft.value;
const trackEndRightString = ISLCA11yStrings.trackEndRight.value;

const leftString = ISLCA11yStrings.left.value;
const rightString = ISLCA11yStrings.right.value;
const farthestFromString = ISLCA11yStrings.farthestFrom.value;
Expand Down Expand Up @@ -91,7 +95,7 @@ define( require => {
options = _.extend( {
unit: unitsMeterString,
units: unitsMetersString,
centerOffset: 0,
centerOffset: 0, // {number} the point considered the "center" of the track space

// {number} => {number}
convertDistanceMetric: distance => distance
Expand Down Expand Up @@ -270,31 +274,35 @@ define( require => {
}

/**
* fillIn just the position mark clause of some sentences
* @param {ISLCObjectEnum} thisObjectEnum
* Map object positions to landmarks. This is not a traditional linear/numeric mapping
* but instead it is based on the two objects and if they are touching each other or the edges.
* @param {ISLCObjectEnum} objectEnum
* @returns {string}
*/
getPositionMark( thisObjectEnum ) {
const position = this.getConvertedPositionFromEnum( thisObjectEnum );
const unit = this.unit;
return StringUtils.fillIn( positionMarkPatternString, {
position: position, unit: unit
} );
}
getPositionLandmark( objectEnum ) {

/**
* Returns the string filled in string '{{position}} {{unit}} mark, {{distance}} {{units}} from {{otherObjectLabel}}.'
*
* @param {ISLCObjectEnum} thisObjectEnum
* @returns {string}
*/
getPositionAndDistanceFromOtherObjectText( thisObjectEnum ) {
const positionMark = this.getPositionMark( thisObjectEnum );
return this.getSpherePositionAriaValueText(
thisObjectEnum,
positionDistanceFromOtherObjectPatternString,
{ positionMark: positionMark }
);
// object 1 touching left
if ( this.object1AtMin( objectEnum ) ) {
return trackEndLeftString;
}

// object 2 touching right
else if ( this.object2AtMax( objectEnum ) ) {
return trackEndRightString;
}

// objects touching each other
else if ( this.objectTouchingBoundary( objectEnum ) ) {
return this.isObject1( objectEnum ) ? lastStopRightString : lastStopLeftString;
}

// objects not touching any boundary, based on the side relative to the center
else {
const object = this.getObjectFromEnum( objectEnum );

// TODO: why does centerOffset not work as expected here?
return object.positionProperty.get() < 0 ? leftSideOfTrackString : rightSideOfTrackString;
}
}

/**
Expand All @@ -313,66 +321,21 @@ define( require => {
}

/**
* Special case string for when an object is at a boundary. This either means touching
* an edge, or touching the other object. This is because in both cases, this object cannot move further in
* that direction.
* The aria-value text when
* Returns the desired value for the ISLCObjectNodes' aria-valuetext attributes when they receive keyboard focus.
*
* @param {ISLCObjectEnum} thisObjectEnum
* @returns {string}
*/
getBoundaryTouchingValueText( thisObjectEnum ) {
const positionMark = this.getPositionMark( thisObjectEnum );
const edgePhrase = this.getEdgePhrase( thisObjectEnum );

getFocusAriaValueText( thisObjectEnum ) {
const positionMark = this.getPositionLandmark( thisObjectEnum );
return this.getSpherePositionAriaValueText(
thisObjectEnum,
closestToOtherObjectPatternString,
{ positionMark: positionMark, edgePhrase: edgePhrase }
positionDistanceFromOtherObjectPatternString,
{ positionLandmark: positionMark }
);
}

/**
* Returns the filled in string 'At {{side}} edge, {{distance}} {{units}} away.'
*
* @param {ISLCObjectEnum} thisObjectEnum
* @returns {string}
*/
getArrivedAtEdgeText( thisObjectEnum ) {
assert && assert( this.objectTouchingBoundary( thisObjectEnum ) );

const distanceClause = this.useQuantitativeDistance ?

// quantitative distance
StringUtils.fillIn( edgeDistancePatternString, {
distanceAndUnits: this.getDistanceAndUnits()
} ) :

// qualitative distance
this.getDistanceClause( thisObjectEnum );

// partially fill in the string with the "side" template var
return StringUtils.fillIn( arrivedAtEdgePatternString, {
side: this.getEdgeFromObjectEnum( thisObjectEnum ),
distanceClause: distanceClause
} );
}

/**
* Returns the desired value for the ISLCObjectNodes' aria-valuetext attributes when they receive keyboard focus.
*
* @param {ISLCObjectEnum} thisObjectEnum
* @returns {string}
*/
getFocusAriaValueText( thisObjectEnum ) {
let text = this.getPositionAndDistanceFromOtherObjectText( thisObjectEnum );

// this covers when the object is at edges, and closest to the other mass
if ( this.objectTouchingBoundary( thisObjectEnum ) ) {
text = this.getBoundaryTouchingValueText( thisObjectEnum );
}
return text;
}

/**
* Returns a function used by AccessibleSlider to format its aria-valuetext attribute. Of note is that this function
* is called AFTER the Slider's position Property has been set. Since, the PositionDescriber links to the PositionProperty
Expand All @@ -393,24 +356,41 @@ define( require => {
* @returns {string} - the string that will fill the aria-valuetext attribute
*/
return ( formattedValue, oldValue ) => {

// "normally" should just be short distance
let newAriaValueText = this.getDistanceFromOtherObjectText( objectEnum );

// closer/farther text
if ( this.lastMoveCloser !== this.movedCloser ) {
newAriaValueText = this.getProgressPositionAndDistanceFromOtherObjectText( objectEnum );
}

if ( this.objectAtEdgeIgnoreOtherObject( objectEnum ) ) {
newAriaValueText = this.getArrivedAtEdgeText( objectEnum );
}

if ( this.objectsClosest() ) {
newAriaValueText = this.getBoundaryTouchingValueText( objectEnum );
// border/edge cases use the same as on focus value text
if ( this.objectsClosest() || this.objectAtEdgeIgnoreOtherObject( objectEnum ) ) {
newAriaValueText = this.getFocusAriaValueText( objectEnum );
}

return newAriaValueText;
};
}

/**
* @param {ISLCObjectEnum} objectEnum
* @returns {boolean}
*/
object1AtMin( objectEnum ) {
return this.isObject1( objectEnum ) && this.objectAtTouchingMin( objectEnum );
}

/**
* @param {ISLCObjectEnum} objectEnum
* @returns {boolean}
*/
object2AtMax( objectEnum ) {
return this.isObject2( objectEnum ) && this.objectAtTouchingMax( objectEnum );
}


/**
* Since ISLCObject.enabledRangeProperty is affected by the other object, this method determines if the object is
* actually at the edge of the sliding area, not just if you are stuck next to the other mass
Expand All @@ -419,8 +399,7 @@ define( require => {
* @returns {boolean}
*/
objectAtEdgeIgnoreOtherObject( objectEnum ) {
return ( this.isObject1( objectEnum ) && this.objectAtTouchingMin( objectEnum ) ) ||
( this.isObject2( objectEnum ) && this.objectAtTouchingMax( objectEnum ) );
return this.object1AtMin( objectEnum ) || this.object2AtMax( objectEnum );

}

Expand Down

0 comments on commit 2cac7d1

Please sign in to comment.