Skip to content

Commit

Permalink
Generalized ToggleNode to handle an arbitrary number of values, see #360
Browse files Browse the repository at this point in the history
  • Loading branch information
samreid committed May 14, 2018
1 parent 407f749 commit 0884cd0
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 45 deletions.
42 changes: 42 additions & 0 deletions js/BooleanToggleNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2013-2017, University of Colorado Boulder

/**
* Shows one node if the property is true or another node if the property is false. Used to indicate boolean state.
* This is a convenience API for true/false nodes, see SelectedNode for the general case.
*
* @author Sam Reid (PhET Interactive Simulations)
* @author Chris Malley (PixelZoom, Inc.)
*/
define( function( require ) {
'use strict';

// modules
var inherit = require( 'PHET_CORE/inherit' );
var ToggleNode = require( 'SUN/ToggleNode' );
var sun = require( 'SUN/sun' );

/**
* @param {Node} trueNode
* @param {Node} falseNode
* @param {Property.<boolean>} booleanProperty
* @param {Object} [options]
* @constructor
*/
function BooleanToggleNode( trueNode, falseNode, booleanProperty, options ) {

options = _.extend( {

// For compatibility with prior usage, we align the x coordinate
alignChildren: ToggleNode.HORIZONTAL
}, options );

ToggleNode.call( this, [
{ value: true, node: trueNode },
{ value: false, node: falseNode }
], booleanProperty, options );
}

sun.register( 'BooleanToggleNode', BooleanToggleNode );

return inherit( ToggleNode, BooleanToggleNode );
} );
110 changes: 72 additions & 38 deletions js/ToggleNode.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Copyright 2013-2017, University of Colorado Boulder
// Copyright 2018, University of Colorado Boulder

/**
* Shows one node if the property is true or another node if the property is false.
* Used to indicate boolean state.
* Display one of N nodes based on a given Property. Maintains the bounds of the union of children for layout.
* Supports null and undefined as possible values. Will not work correctly if the children are changed externally
* after instantiation (manages its own children and their visibility).
*
* @author Sam Reid (PhET Interactive Simulations)
* @author Chris Malley (PixelZoom, Inc.)
*/
define( function( require ) {
'use strict';
Expand All @@ -14,52 +14,52 @@ define( function( require ) {
var inherit = require( 'PHET_CORE/inherit' );
var Node = require( 'SCENERY/nodes/Node' );
var sun = require( 'SUN/sun' );
var Tandem = require( 'TANDEM/Tandem' );

/**
* @param {Node} trueNode
* @param {Node} falseNode
* @param {Property.<boolean>} booleanProperty
* @param {Object[]} elements - Array of {value:{Object}, node:{Node}}
* @param {Property.<Object>} valueProperty
* @param {Object} [options]
* @constructor
*/
function ToggleNode( trueNode, falseNode, booleanProperty, options ) {

function ToggleNode( elements, valueProperty, options ) {
assert && assert( Array.isArray( elements ), 'elements should be an array' );
if ( assert ) {
elements.forEach( function( element ) {
var keys = _.keys( element );
assert( keys.length === 2, 'each element should have two keys' );
assert( keys[ 0 ] === 'value' || keys[ 1 ] === 'value', 'element should have a value key' );
assert( element.node instanceof Node, 'element.node should be a node' );
} );
}
options = _.extend( {

// align centers of the nodes, see https://github.com/phetsims/sun/issues/2
alignIcons: function( trueNode, falseNode ) {
falseNode.center = trueNode.center;
},

tandem: Tandem.required
// By default, line up the centers of all nodes with the center of the first node. NOTE this is different than
// in ToggleNode
alignChildren: ToggleNode.CENTER
}, options );

options.alignIcons( trueNode, falseNode );

Node.call( this );

this.addChild( falseNode );
this.addChild( trueNode );

// initial visibility of nodes
trueNode.setVisible( booleanProperty.get() );
falseNode.setVisible( !booleanProperty.get() );

// swap visibility of trueNode and falseNode when booleanProperty changes, must be removed in dispose
var visibilityListener = function() {
trueNode.swapVisibility( falseNode );
var valueChangeListener = function( value ) {
var matchCount = 0;
for ( var i = 0; i < elements.length; i++ ) {
var element = elements[ i ];
var visible = element.value === value;
element.node.visible = visible;
if ( visible ) {
matchCount++;
}
}
assert && assert( matchCount === 1, 'Wrong number of matches: ' + matchCount );
};
booleanProperty.lazyLink( visibilityListener );
valueProperty.link( valueChangeListener );

options.children = _.map( elements, 'node' );
options.alignChildren( options.children );
Node.call( this, options );

// @private - called by dispose
// @private
this.disposeToggleNode = function() {
if ( booleanProperty.hasListener( visibilityListener ) ) {
booleanProperty.unlink( visibilityListener );
}
valueProperty.unlink( valueChangeListener );
};

this.mutate( options );
}

sun.register( 'ToggleNode', ToggleNode );
Expand All @@ -74,5 +74,39 @@ define( function( require ) {
this.disposeToggleNode();
Node.prototype.dispose.call( this );
}
}, {

/**
* Center the latter nodes on the x center of the first node.
* @param {Node[]} children
* @public
*/
HORIZONTAL: function( children ) {
for ( var i = 1; i < children.length; i++ ) {
children[ i ].centerX = children[ 0 ].centerX;
}
},

/**
* Center the latter nodes on the y center of the first node.
* @param {Node[]} children
* @public
*/
VERTICAL: function( children ) {
for ( var i = 1; i < children.length; i++ ) {
children[ i ].centerY = children[ 0 ].centerY;
}
},

/**
* Center the latter nodes on the x,y center of the first node.
* @param {Node[]} children
* @public
*/
CENTER: function( children ) {
for ( var i = 1; i < children.length; i++ ) {
children[ i ].center = children[ 0 ].center;
}
}
} );
} );
} );
5 changes: 2 additions & 3 deletions js/buttons/BooleanRectangularToggleButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ define( function( require ) {
var RectangularToggleButton = require( 'SUN/buttons/RectangularToggleButton' );
var sun = require( 'SUN/sun' );
var Tandem = require( 'TANDEM/Tandem' );
var ToggleNode = require( 'SUN/ToggleNode' );
var BooleanToggleNode = require( 'SUN/BooleanToggleNode' );

/**
* @param {Node} trueNode
Expand All @@ -24,9 +24,8 @@ define( function( require ) {

options = _.extend( { tandem: Tandem.required }, options );

//TODO ToggleNode links to booleanProperty, must be cleaned up in dispose
assert && assert( !options.content, 'options.content cannot be set' );
options.content = new ToggleNode( trueNode, falseNode, booleanProperty, {
options.content = new BooleanToggleNode( trueNode, falseNode, booleanProperty, {
tandem: options.tandem.createTandem( 'toggleNode' )
} );

Expand Down
4 changes: 2 additions & 2 deletions js/buttons/BooleanRoundToggleButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ define( function( require ) {
var RoundToggleButton = require( 'SUN/buttons/RoundToggleButton' );
var sun = require( 'SUN/sun' );
var Tandem = require( 'TANDEM/Tandem' );
var ToggleNode = require( 'SUN/ToggleNode' );
var BooleanToggleNode = require( 'SUN/BooleanToggleNode' );

/**
* @param {Node} trueNode
Expand All @@ -27,7 +27,7 @@ define( function( require ) {
}, options );

assert && assert( !options.content, 'BooleanRoundToggleButton sets content' );
options.content = new ToggleNode( trueNode, falseNode, booleanProperty, {
options.content = new BooleanToggleNode( trueNode, falseNode, booleanProperty, {
tandem: options.tandem.createTandem( 'toggleNode' )
} );

Expand Down
4 changes: 2 additions & 2 deletions js/demo/MemoryTestsScreenView.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ define( function( require ) {
var Property = require( 'AXON/Property' );
var ScreenView = require( 'JOIST/ScreenView' );
var Text = require( 'SCENERY/nodes/Text' );
var ToggleNode = require( 'SUN/ToggleNode' );
var BooleanToggleNode = require( 'SUN/BooleanToggleNode' );

var sun = require( 'SUN/sun' );

Expand All @@ -39,7 +39,7 @@ define( function( require ) {
return new ABSwitch( booleanProperty, true, new Text( 'true' ), false, new Text( 'false' ) );
} ),
new ComponentHolder( function() {
return new ToggleNode( new Text( 'true' ), new Text( 'false' ), booleanProperty );
return new BooleanToggleNode( new Text( 'true' ), new Text( 'false' ), booleanProperty );
} )
];

Expand Down

0 comments on commit 0884cd0

Please sign in to comment.