-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use nested options for slider in NumberControl #451
Comments
@mbarlow12 has been in the file recently, please let me know if you want my help! |
If anyone needs an example of how to apply the "nested options" patterns, see FineCoarseSpinner or ComboDisplay. Both demonstrate how to document nested options and set their default value. E.g. in FineCoarseSpinner: numberDisplayOptions: null, // {*|null} options propagated to the NumberDisplay subcomponent ComboDisplay demonstrates how to use an additional // defaults for NumberDisplay
options.numberDisplayOptions = _.extend( {
...
}, options.numberDisplayOptions ); FineCoarseSpinner demonstrates how to handle the case where the client is not allowed to set a nested option: assert && assert( !options.arrowButtonOptions || options.arrowButtonOptions.numberOfArrows === undefined,
'FineCoarseSpinner sets arrowButtonOptions.numberOfArrows' ); |
Since we nested options for
// {*|null} options propagated to ArrowButton
arrowButtonOptions: null,
// {*|null} options propagated to HSlider
sliderOptions: null,
// {*|null} options propagated to NumberDisplay
numberDisplayOptions: null, |
@zepumph would you like to review? Whatever we decide on for phetsims/phet-info#91 will have an impact here. |
In NumberControl.js: // Defaults for NumberDisplay
var numberDisplayOptions = _.extend( {
// value
font: new PhetFont( 12 ),
align: 'right',
valueMaxWidth: null, // {null|number} maxWidth to use for value display, to constrain width for i18n
valueMinBackgroundWidth: 0, // {number} min width for the value display's background
xMargin: 8,
yMargin: 2,
backgroundStroke: 'lightGray',
backgroundLineWidth: 1,
cornerRadius: 0,
decimalPlaces: 0,
useRichText: false,
// {string} See NumberDisplay.valuePattern for additional requirements
valuePattern: NumberDisplay.DEFAULT_VALUE_PATTERN,
// phet-io
tandem: options.tandem.createTandem( 'numberDisplay' )
}, options.numberDisplayOptions ); Any of these default values that are the same as NumberDisplay's default values are redundant, and can be removed. The redundant defaults are: align: 'right',
xMargin: 8,
yMargin: 2,
backgroundStroke: 'lightGray',
cornerRadius: 0,
decimalPlaces: 0,
useRichText: false,
valuePattern: NumberDisplay.DEFAULT_VALUE_PATTERN, Also, a potential problem... These options don't exist in NumberDisplay. Was this an existing problem, or a problem that was introduced? valueMaxWidth: null, // {null|number} maxWidth to use for value display, to constrain width for i18n
valueMinBackgroundWidth: 0, // {number} min width for the value display's background |
Btw... I looked at this because I'm doing related work in #446 and phetsims/sun#472. And getting rid of |
I didn't know that was ready for production. I marked phetsims/phet-info#91 for developer meeting to understand that better. For now I'm going to try adding it in and just see how it goes. I also notice usages in wave-interference.
I think that will be excellent. Thanks! Also see phetsims/phet-core#62 |
I just realized this won't work, since one set of options is in |
When it comes to merge. Many of the default nested options rely on options passed to the NumberControl. Here is a patch that extends some of the options, and then merges the rest of them. Perhaps we could make something like this work. like extend all options for NumberControl, and then do a single merge for all nested options (this patch doesn't quite get to that suggestion). Index: js/NumberControl.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- js/NumberControl.js (revision 453ea22f561260f39f4154b8f2b4ade6f84dd83d)
+++ js/NumberControl.js (date 1561754989365)
@@ -19,6 +19,7 @@
const HBox = require( 'SCENERY/nodes/HBox' );
const HSlider = require( 'SUN/HSlider' );
const inherit = require( 'PHET_CORE/inherit' );
+ const merge = require( 'PHET_CORE/merge' );
const Node = require( 'SCENERY/nodes/Node' );
const NumberControlIO = require( 'SCENERY_PHET/NumberControlIO' );
const NumberDisplay = require( 'SCENERY_PHET/NumberDisplay' );
@@ -42,22 +43,36 @@
'rightEnd'
];
const POINTER_AREA_OPTION_NAMES = [ 'touchAreaXDilation', 'touchAreaYDilation', 'mouseAreaXDilation', 'mouseAreaYDilation' ];
- const DEFAULT_CALLBACK = _.noop;
/**
* @param {string} title
* @param {Property.<number>} numberProperty
* @param {Range} numberRange
- * @param {Object} [options] - subcomponent objects: sliderOptions, numberDisplayOptions, arrowButtonOptions, titleNodeOptions
+ * @param {Object} [options] - subcomponent options objects:
+ * sliderOptions,
+ * numberDisplayOptions,
+ * arrowButtonOptions,
+ * titleNodeOptions
* @mixes AccessibleSlider
* @constructor
*/
function NumberControl( title, numberProperty, numberRange, options ) {
+
+ // Make sure that general callbacks (for all components) and specific callbacks (for a specific component) aren't
+ // used in tandem. This must be called before defaults are set.
+ validateCallbacks( options || {} );
+
options = _.extend( {
-
// General Callbacks
- startCallback: DEFAULT_CALLBACK, // called when interaction begins, default value set in validateCallbacks()
- endCallback: DEFAULT_CALLBACK, // called when interaction ends, default value set in validateCallbacks()
+ startCallback: _.noop, // called when interaction begins, default value set in validateCallbacks()
+ endCallback: _.noop, // called when interaction ends, default value set in validateCallbacks()
+
+ // phet-io
+ tandem: Tandem.required
+ } );
+
+ options = merge( {
+
delta: 1,
enabledProperty: new Property( true ), // {Property.<boolean>} is this control enabled?
@@ -70,117 +85,118 @@
layoutFunction: NumberControl.createLayoutFunction1(),
// {*|null} options propagated to ArrowButton
- arrowButtonOptions: null,
-
- // {*|null} options propagated to HSlider
- sliderOptions: null,
-
- // {*|null} options propagated to NumberDisplay
- numberDisplayOptions: null,
-
- // {*|null} options propagated to the title Text node
- titleNodeOptions: null,
-
- // phet-io
- tandem: Tandem.required,
- phetioType: NumberControlIO,
-
- // a11y
- groupFocusHighlight: true
- }, options );
-
- // validate options
- assert && assert( !options.startDrag, 'use options.startCallback instead of options.startDrag' );
- assert && assert( !options.endDrag, 'use options.endCallback instead of options.endDrag' );
- assert && assert( options.disabledOpacity > 0 && options.disabledOpacity < 1,
- `invalid disabledOpacity: ${options.disabledOpacity}` );
- assert && assert( !options.shiftKeyboardStep,
- 'shift keyboard stop handled by arrow buttons, do not use with NumberControl' );
- assert && assert( !options.shiftKeyboardStep,
- 'shift keyboard stop handled by arrow buttons, do not use with NumberControl' );
- assert && options.sliderOptions && assert( options.isAccessible === undefined,
- 'NumberControl sets isAccessible for Slider' );
-
- // Make sure that general callbacks (for all components) and specific callbacks (for a specific component) aren't
- // used in tandem.
- validateCallbacks( options );
-
- // Defaults for ArrowButton
- let arrowButtonOptions = _.extend( {
- scale: 0.85,
+ arrowButtonOptions: {
+ scale: 0.85,
- // Values chosen to match previous behavior, see https://github.com/phetsims/scenery-phet/issues/489.
- // touchAreaXDilation is 1/2 of its original value because touchArea is shifted.
- touchAreaXDilation: 3.5,
- touchAreaYDilation: 7,
- mouseAreaXDilation: 0,
- mouseAreaYDilation: 0,
+ // Values chosen to match previous behavior, see https://github.com/phetsims/scenery-phet/issues/489.
+ // touchAreaXDilation is 1/2 of its original value because touchArea is shifted.
+ touchAreaXDilation: 3.5,
+ touchAreaYDilation: 7,
+ mouseAreaXDilation: 0,
+ mouseAreaYDilation: 0,
- // callbacks
- leftStart: options.startCallback, // called when left arrow is pressed
- leftEnd: options.endCallback, // called when left arrow is released
- rightStart: options.startCallback, // called when right arrow is pressed
- rightEnd: options.endCallback // called when right arrow is released
- }, options.arrowButtonOptions );
-
- // Arrow button pointer areas need to be asymmetrical, see https://github.com/phetsims/scenery-phet/issues/489.
- // Get the pointer area options related to ArrowButton so that we can handle pointer areas here.
- // And do not propagate those options to ArrowButton instances.
- const arrowButtonPointerAreaOptions = _.pick( arrowButtonOptions, POINTER_AREA_OPTION_NAMES );
- arrowButtonOptions = _.omit( arrowButtonOptions, POINTER_AREA_OPTION_NAMES );
+ // callbacks
+ leftStart: options.startCallback, // called when left arrow is pressed
+ leftEnd: options.endCallback, // called when left arrow is released
+ rightStart: options.startCallback, // called when right arrow is pressed
+ rightEnd: options.endCallback // called when right arrow is released
+ },
- // a11y - for alternative input, the number control is accessed entirely through slider interaction and these
- // arrow buttons are not tab navigable
- assert && assert( arrowButtonOptions.tagName === undefined,
- 'NumberControl handles alternative input for arrow buttons' );
- arrowButtonOptions.tagName = null;
-
- // Defaults for HSlider
- let sliderOptions = _.extend( {
+ // {*|null} options propagated to HSlider
+ sliderOptions: {
- startDrag: options.startCallback, // called when dragging starts on the slider
- endDrag: options.endCallback, // called when dragging ends on the slider
+ startDrag: options.startCallback, // called when dragging starts on the slider
+ endDrag: options.endCallback, // called when dragging ends on the slider
- // With the exception of startDrag and endDrag (use startCallback and endCallback respectively),
- // all HSlider options may be used. These are the ones that NumberControl overrides:
- majorTickLength: 20,
- minorTickStroke: 'rgba( 0, 0, 0, 0.3 )',
+ // With the exception of startDrag and endDrag (use startCallback and endCallback respectively),
+ // all HSlider options may be used. These are the ones that NumberControl overrides:
+ majorTickLength: 20,
+ minorTickStroke: 'rgba( 0, 0, 0, 0.3 )',
- // other slider options that are specific to NumberControl
- majorTicks: [], // array of objects with these fields: { value: {number}, label: {Node} }
- minorTickSpacing: 0, // zero indicates no minor ticks
+ // other slider options that are specific to NumberControl
+ majorTicks: [], // array of objects with these fields: { value: {number}, label: {Node} }
+ minorTickSpacing: 0, // zero indicates no minor ticks
- // constrain the slider value to the provided range and the same delta as the arrow buttons
- constrainValue: value => {
- const newValue = Util.roundToInterval( value, options.delta ); // constrain to multiples of delta, see #384
- return numberRange.constrainValue( newValue );
- },
+ // constrain the slider value to the provided range and the same delta as the arrow buttons
+ constrainValue: value => {
+ const newValue = Util.roundToInterval( value, options.delta ); // constrain to multiples of delta, see #384
+ return numberRange.constrainValue( newValue );
+ },
- // phet-io
- tandem: options.tandem.createTandem( 'slider' )
- }, options.sliderOptions );
+ // phet-io
+ tandem: options.tandem.createTandem( 'slider' )
+ },
+
+ // {*|null} options propagated to NumberDisplay
+ numberDisplayOptions: {
+ // value
+ font: new PhetFont( 12 ),
+ maxWidth: null, // {null|number} maxWidth to use for value display, to constrain width for i18n
+
+ // phet-io
+ tandem: options.tandem.createTandem( 'numberDisplay' )
+ },
+
+ // {*|null} options propagated to the title Text node
+ titleNodeOptions: {
+ font: new PhetFont( 12 ),
+ maxWidth: null, // {null|string} maxWidth to use for title, to constrain width for i18n
+ fill: 'black',
+ tandem: options.tandem.createTandem( 'titleNode' )
+ },
+
+ // phet-io
+ phetioType: NumberControlIO,
+
+ // a11y
+ groupFocusHighlight: true
+ }, options );
+
+ // validate options
+ assert && assert( !options.startDrag, 'use options.startCallback instead of options.startDrag' );
+ assert && assert( !options.endDrag, 'use options.endCallback instead of options.endDrag' );
+ assert && assert( options.disabledOpacity > 0 && options.disabledOpacity < 1,
+ `invalid disabledOpacity: ${options.disabledOpacity}` );
+ assert && assert( !options.shiftKeyboardStep,
+ 'shift keyboard stop handled by arrow buttons, do not use with NumberControl' );
+ assert && assert( !options.shiftKeyboardStep,
+ 'shift keyboard stop handled by arrow buttons, do not use with NumberControl' );
+ assert && options.sliderOptions && assert( options.isAccessible === undefined,
+ 'NumberControl sets isAccessible for Slider' );
+
+ // Arrow button pointer areas need to be asymmetrical, see https://github.com/phetsims/scenery-phet/issues/489.
+ // Get the pointer area options related to ArrowButton so that we can handle pointer areas here.
+ // And do not propagate those options to ArrowButton instances.
+ const arrowButtonPointerAreaOptions = _.pick( options.arrowButtonOptions, POINTER_AREA_OPTION_NAMES );
+ options.arrowButtonOptions = _.omit( options.arrowButtonOptions, POINTER_AREA_OPTION_NAMES );
+
+ // a11y - for alternative input, the number control is accessed entirely through slider interaction and these
+ // arrow buttons are not tab navigable
+ assert && assert( options.arrowButtonOptions.tagName === undefined,
+ 'NumberControl handles alternative input for arrow buttons' );
+ options.arrowButtonOptions.tagName = null;
// Slider options for track (if not specified as trackNode)
if ( !options.sliderOptions.trackNode ) {
- sliderOptions = _.extend( {
+ options.sliderOptions = _.extend( {
trackSize: new Dimension2( 180, 3 )
- }, sliderOptions );
+ }, options.sliderOptions );
}
- // Slider options for thumb (if not specified as thumbNode)
+ // Slider options for thumb (if n ot specified as thumbNode)
if ( !options.sliderOptions.thumbNode ) {
- sliderOptions = _.extend( {
+ options.sliderOptions = _.extend( {
thumbSize: new Dimension2( 17, 34 ),
thumbTouchAreaXDilation: 6
- }, sliderOptions );
+ }, options.sliderOptions );
}
- assert && assert( !sliderOptions.hasOwnProperty( 'isAccessible' ), 'NumberControl sets isAccessible' );
- assert && assert( !sliderOptions.hasOwnProperty( 'shiftKeyboardStep' ), 'NumberControl sets shiftKeyboardStep' );
- assert && assert( !sliderOptions.hasOwnProperty( 'phetioType' ), 'NumberControl sets phetioType' );
+ assert && assert( !options.sliderOptions.hasOwnProperty( 'isAccessible' ), 'NumberControl sets isAccessible' );
+ assert && assert( !options.sliderOptions.hasOwnProperty( 'shiftKeyboardStep' ), 'NumberControl sets shiftKeyboardStep' );
+ assert && assert( !options.sliderOptions.hasOwnProperty( 'phetioType' ), 'NumberControl sets phetioType' );
// slider options set by NumberControl, note this may not be the long term pattern, see https://github.com/phetsims/phet-info/issues/96
- sliderOptions = _.extend( {
+ options.sliderOptions = _.extend( {
// NumberControl uses the AccessibleSlider trait, so don't include any accessibility on the slider
isAccessible: false,
@@ -190,38 +206,21 @@
// Make sure Slider gets created with the right IO Type
phetioType: SliderIO
- }, sliderOptions );
+ }, options.sliderOptions );
// highlight color for thumb defaults to a brighter version of the thumb color
- if ( sliderOptions.thumbFill && !sliderOptions.thumbFillHighlighted ) {
+ if ( options.sliderOptions.thumbFill && !options.sliderOptions.thumbFillHighlighted ) {
// @private {Property.<Color>}
- this.thumbFillProperty = new PaintColorProperty( sliderOptions.thumbFill );
+ this.thumbFillProperty = new PaintColorProperty( options.sliderOptions.thumbFill );
// Reference to the DerivedProperty not needed, since we dispose what it listens to above.
- sliderOptions.thumbFillHighlighted = new DerivedProperty( [ this.thumbFillProperty ], color => color.brighterColor() );
+ options.sliderOptions.thumbFillHighlighted = new DerivedProperty( [ this.thumbFillProperty ], color => color.brighterColor() );
}
- // Defaults for NumberDisplay
- const numberDisplayOptions = _.extend( {
- // value
- font: new PhetFont( 12 ),
- maxWidth: null, // {null|number} maxWidth to use for value display, to constrain width for i18n
-
- // phet-io
- tandem: options.tandem.createTandem( 'numberDisplay' )
- }, options.numberDisplayOptions );
+ const titleNode = new Text( title, options.titleNodeOptions );
- const titleNodeOptions = _.extend( {
- font: new PhetFont( 12 ),
- maxWidth: null, // {null|string} maxWidth to use for title, to constrain width for i18n
- fill: 'black',
- tandem: options.tandem.createTandem( 'titleNode' )
- }, options.titleNodeOptions );
-
- const titleNode = new Text( title, titleNodeOptions );
-
- const numberDisplay = new NumberDisplay( numberProperty, numberRange, numberDisplayOptions );
+ const numberDisplay = new NumberDisplay( numberProperty, numberRange, options.numberDisplayOptions );
const leftArrowButton = new ArrowButton( 'left', () => {
let value = numberProperty.get() - options.delta;
@@ -229,10 +228,10 @@
value = Math.max( value, numberRange.min ); // constrain to range
numberProperty.set( value );
}, _.extend( {
- startCallback: arrowButtonOptions.leftStart,
- endCallback: arrowButtonOptions.leftEnd,
+ startCallback: options.arrowButtonOptions.leftStart,
+ endCallback: options.arrowButtonOptions.leftEnd,
tandem: options.tandem.createTandem( 'leftArrowButton' )
- }, arrowButtonOptions ) );
+ }, options.arrowButtonOptions ) );
const rightArrowButton = new ArrowButton( 'right', () => {
let value = numberProperty.get() + options.delta;
@@ -240,10 +239,10 @@
value = Math.min( value, numberRange.max ); // constrain to range
numberProperty.set( value );
}, _.extend( {
- startCallback: arrowButtonOptions.rightStart,
- endCallback: arrowButtonOptions.rightEnd,
+ startCallback: options.arrowButtonOptions.rightStart,
+ endCallback: options.arrowButtonOptions.rightEnd,
tandem: options.tandem.createTandem( 'rightArrowButton' )
- }, arrowButtonOptions ) );
+ }, options.arrowButtonOptions ) );
// arrow button touchAreas, asymmetrical, see https://github.com/phetsims/scenery-phet/issues/489
leftArrowButton.touchArea = leftArrowButton.localBounds
@@ -267,21 +266,21 @@
};
numberProperty.link( arrowEnabledListener );
- const slider = new HSlider( numberProperty, numberRange, sliderOptions );
+ const slider = new HSlider( numberProperty, numberRange, options.sliderOptions );
// major ticks
- const majorTicks = sliderOptions.majorTicks;
+ const majorTicks = options.sliderOptions.majorTicks;
for ( let i = 0; i < majorTicks.length; i++ ) {
slider.addMajorTick( majorTicks[ i ].value, majorTicks[ i ].label );
}
// minor ticks, exclude values where we already have major ticks
- if ( sliderOptions.minorTickSpacing > 0 ) {
+ if ( options.sliderOptions.minorTickSpacing > 0 ) {
for ( let minorTickValue = numberRange.min; minorTickValue <= numberRange.max; ) {
if ( !_.find( majorTicks, majorTick => majorTick.value === minorTickValue ) ) {
slider.addMinorTick( minorTickValue );
}
- minorTickValue += sliderOptions.minorTickSpacing;
+ minorTickValue += options.sliderOptions.minorTickSpacing;
}
}
@@ -294,7 +293,7 @@
Node.call( this, options );
// a11y - the number control acts like a range input for a11y, pass slider options without tandem
- const accessibleSliderOptions = _.omit( sliderOptions, [ 'tandem' ] );
+ const accessibleSliderOptions = _.omit( options.sliderOptions, [ 'tandem' ] );
this.initializeAccessibleSlider( numberProperty, slider.enabledRangeProperty, slider.enabledProperty, accessibleSliderOptions );
// a11y - the focus highlight for NumberControl should surround the Slider's thumb
@@ -350,8 +349,8 @@
* @param {Object} options
*/
function validateCallbacks( options ) {
- const normalCallbacksPresent = !!( options.startCallback !== DEFAULT_CALLBACK ||
- options.endCallback !== DEFAULT_CALLBACK );
+ const normalCallbacksPresent = !!( options.startCallback ||
+ options.endCallback );
let arrowCallbacksPresent = false;
let sliderCallbacksPresent = false;
|
@samreid please review the above comments. After the two investigations above. I would be fine either keeping things the way they are, or investigating merging all nested options in a single call. |
Hey sorry for the stream of consciousness above. I actually ended up implementing the proposal in #451 (comment). It worked well to have an extend call for all options for NumberControl, and then a second merge for all nested options at once. Though this doesn't simplify things into a single call, it get's closer than X extend calls, one for each nested object. @samreid please review. |
@zepumph and @chrisklus reviewed the changes and the preceding comments today, and all seems well. Closing. |
CT is showing some issues likely related to these changes.
|
In the preceding 2 commits, I fixed the NumberControl options extend and fixed a typo in energy-skate-park. The following commits will be the results of |
@zepumph can you please review the changes? |
This looks great thank you. CT has been clear for a few days now. I appreciate the cleanup! |
similar to #448, but for slider. This comment was discovered while investigating other work:
So all the options for NumberControl that aren't Node options are being propagated to the slider.
This has a few code smells.
The text was updated successfully, but these errors were encountered: