<commit_before>// Copyright 2014-2017, University of Colorado Boulder

/**
 * Visual representation of a round button.  It is provided with a 'button
 * model' that is monitored to change the appearance of the button.
 *
 * Note: this is only the visual representation and does not have associated
 * input listeners so that it can be reused in multiple contexts.
 *
 * @author <NAME> (PhET Interactive Simulations)
 * @author <NAME> (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var ButtonInteractionState = require( 'SUN/buttons/ButtonInteractionState' );
  var Circle = require( 'SCENERY/nodes/Circle' );
  var ColorConstants = require( 'SUN/ColorConstants' );
  var DerivedProperty = require( 'AXON/DerivedProperty' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PaintColorProperty = require( 'SCENERY/util/PaintColorProperty' );
  var RadialGradient = require( 'SCENERY/util/RadialGradient' );
  var Shape = require( 'KITE/Shape' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Vector2 = require( 'DOT/Vector2' );

  // constants
  var HIGHLIGHT_GRADIENT_LENGTH = 5; // In screen coords, which are roughly pixels.
  var DEFAULT_COLOR = ColorConstants.LIGHT_BLUE;

  /**
   * @param {PushButtonModel} pushButtonModel
   * @param {Property} interactionStateProperty - A property that is used to drive the visual appearance of the button.
   * @param {Object} [options]
   * @constructor
   */
  function RoundButtonView( pushButtonModel, interactionStateProperty, options ) {
    this.buttonModel = pushButtonModel; // @protected // TODO: rename to pushButtonModel

    options = _.extend( {

      radius: ( options && options.content ) ? undefined : 30,
      content: null,
      cursor: 'pointer',
      baseColor: DEFAULT_COLOR,
      disabledBaseColor: ColorConstants.LIGHT_GRAY,
      minXMargin: 5, // Minimum margin in x direction, i.e. on left and right
      minYMargin: 5, // Minimum margin in y direction, i.e. on top and bottom
      fireOnDown: false,
      touchAreaDilation: 0, // radius dilation for touch area
      mouseAreaDilation: 0, // radius dilation for mouse area
      stroke: undefined, // undefined by default, which will cause a stroke to be derived from the base color
      lineWidth: 0.5, // Only meaningful if stroke is non-null
      tandem: Tandem.optional, // This duplicates the parent option and works around https://github.com/phetsims/tandem/issues/50

      // By default, icons are centered in the button, but icons with odd
      // shapes that are not wrapped in a normalizing parent node may need to
      // specify offsets to line things up properly
      xContentOffset: 0,
      yContentOffset: 0,

      // Strategy for controlling the button's appearance, excluding any
      // content.  This can be a stock strategy from this file or custom.  To
      // create a custom one, model it off of the stock strategies defined in
      // this file.
      buttonAppearanceStrategy: RoundButtonView.ThreeDAppearanceStrategy,

      // Strategy for controlling the appearance of the button's content based
      // on the button's state.  This can be a stock strategy from this file,
      // or custom.  To create a custom one, model it off of the stock
      // version(s) defined in this file.
      contentAppearanceStrategy: RoundButtonView.FadeContentWhenDisabled
    }, options );

    Node.call( this );
    var content = options.content; // convenience variable
    var upCenter = new Vector2( options.xContentOffset, options.yContentOffset );

    // For performance reasons, the content should be unpickable.
    if ( content ) {
      content.pickable = false;
    }

    // Make the base color into a property so that the appearance strategy can update itself if changes occur.
    this.baseColorProperty = new PaintColorProperty( options.baseColor ); // @private

    // @private {PressListener}
    var pressListener = pushButtonModel.createListener( { tandem: options.tandem.createTandem( 'pressListener' ) } );
    this.addInputListener( pressListener );
    this.addAccessibleInputListener( pressListener.a11yListener );

    // Use the user-specified radius if present, otherwise calculate the
    // radius based on the content and the margin.
    var buttonRadius = options.radius ||
                       Math.max( content.width + options.minXMargin * 2, content.height + options.minYMargin * 2 ) / 2;

    // Create the basic button shape.
    var button = new Circle( buttonRadius, { fill: options.baseColor, lineWidth: options.lineWidth } );
    this.addChild( button );

    // Hook up the strategy that will control the basic button appearance.
    var buttonAppearanceStrategy = new options.buttonAppearanceStrategy(
      button,
      interactionStateProperty,
      this.baseColorProperty,
      options
    );

    // Add the content to the button.
    if ( content ) {
      content.center = upCenter;
      this.addChild( content );
    }

    // Hook up the strategy that will control the content appearance.
    var contentAppearanceStrategy = new options.contentAppearanceStrategy( content, interactionStateProperty );

    // Control the pointer state based on the interaction state.
    var self = this;

    function handleInteractionStateChanged( state ) {
      self.cursor = state === ButtonInteractionState.DISABLED ||
                    state === ButtonInteractionState.DISABLED_PRESSED ? null : 'pointer';
    }

    interactionStateProperty.link( handleInteractionStateChanged );

    // Dilate the pointer areas.
    this.touchArea = Shape.circle( 0, 0, buttonRadius + options.touchAreaDilation );
    this.mouseArea = Shape.circle( 0, 0, buttonRadius + options.mouseAreaDilation );

    // Set pickable such that sub-nodes are pruned from hit testing.
    this.pickable = null;

    // a11y
    this.focusHighlight = new Shape.circle( 0, 0, buttonRadius + 5 );

    // Mutate with the options after the layout is complete so that
    // width-dependent fields like centerX will work.
    this.mutate( options );

    // define a dispose function
    this.disposeRoundButtonView = function() {
      buttonAppearanceStrategy.dispose();
      contentAppearanceStrategy.dispose();
      pressListener.dispose();
      interactionStateProperty.unlink( handleInteractionStateChanged );
    };
  }

  sun.register( 'RoundButtonView', RoundButtonView );

  /**
   * Strategy for making a button look 3D-ish by using gradients that create the appearance of highlighted and shaded
   * edges.  The gradients are intended to make the light source appear to be above and to the left of the button.
   *
   * @param {Node} button
   * @param {Property.<boolean>} interactionStateProperty
   * @param {Property.<Color>} baseColorProperty
   * @param {Object} [options]
   * @constructor
   * @public
   */
  RoundButtonView.ThreeDAppearanceStrategy = function( button, interactionStateProperty, baseColorProperty, options ) {

    // Color properties
    var baseBrighter8 = new PaintColorProperty( baseColorProperty, { luminanceFactor: 0.8 } );<commit_msg>Dispose baseColorProperty, see https://github.com/phetsims/sun/issues/121<commit_after>// Copyright 2014-2017, University of Colorado Boulder

/**
 * Visual representation of a round button.  It is provided with a 'button
 * model' that is monitored to change the appearance of the button.
 *
 * Note: this is only the visual representation and does not have associated
 * input listeners so that it can be reused in multiple contexts.
 *
 * @author <NAME> (PhET Interactive Simulations)
 * @author <NAME> (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var ButtonInteractionState = require( 'SUN/buttons/ButtonInteractionState' );
  var Circle = require( 'SCENERY/nodes/Circle' );
  var ColorConstants = require( 'SUN/ColorConstants' );
  var DerivedProperty = require( 'AXON/DerivedProperty' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PaintColorProperty = require( 'SCENERY/util/PaintColorProperty' );
  var RadialGradient = require( 'SCENERY/util/RadialGradient' );
  var Shape = require( 'KITE/Shape' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Vector2 = require( 'DOT/Vector2' );

  // constants
  var HIGHLIGHT_GRADIENT_LENGTH = 5; // In screen coords, which are roughly pixels.
  var DEFAULT_COLOR = ColorConstants.LIGHT_BLUE;

  /**
   * @param {PushButtonModel} pushButtonModel
   * @param {Property} interactionStateProperty - A property that is used to drive the visual appearance of the button.
   * @param {Object} [options]
   * @constructor
   */
  function RoundButtonView( pushButtonModel, interactionStateProperty, options ) {
    this.buttonModel = pushButtonModel; // @protected // TODO: rename to pushButtonModel

    options = _.extend( {

      radius: ( options && options.content ) ? undefined : 30,
      content: null,
      cursor: 'pointer',
      baseColor: DEFAULT_COLOR,
      disabledBaseColor: ColorConstants.LIGHT_GRAY,
      minXMargin: 5, // Minimum margin in x direction, i.e. on left and right
      minYMargin: 5, // Minimum margin in y direction, i.e. on top and bottom
      fireOnDown: false,
      touchAreaDilation: 0, // radius dilation for touch area
      mouseAreaDilation: 0, // radius dilation for mouse area
      stroke: undefined, // undefined by default, which will cause a stroke to be derived from the base color
      lineWidth: 0.5, // Only meaningful if stroke is non-null
      tandem: Tandem.optional, // This duplicates the parent option and works around https://github.com/phetsims/tandem/issues/50

      // By default, icons are centered in the button, but icons with odd
      // shapes that are not wrapped in a normalizing parent node may need to
      // specify offsets to line things up properly
      xContentOffset: 0,
      yContentOffset: 0,

      // Strategy for controlling the button's appearance, excluding any
      // content.  This can be a stock strategy from this file or custom.  To
      // create a custom one, model it off of the stock strategies defined in
      // this file.
      buttonAppearanceStrategy: RoundButtonView.ThreeDAppearanceStrategy,

      // Strategy for controlling the appearance of the button's content based
      // on the button's state.  This can be a stock strategy from this file,
      // or custom.  To create a custom one, model it off of the stock
      // version(s) defined in this file.
      contentAppearanceStrategy: RoundButtonView.FadeContentWhenDisabled
    }, options );

    Node.call( this );
    var content = options.content; // convenience variable
    var upCenter = new Vector2( options.xContentOffset, options.yContentOffset );

    // For performance reasons, the content should be unpickable.
    if ( content ) {
      content.pickable = false;
    }

    // Make the base color into a property so that the appearance strategy can update itself if changes occur.
    this.baseColorProperty = new PaintColorProperty( options.baseColor ); // @private

    // @private {PressListener}
    var pressListener = pushButtonModel.createListener( { tandem: options.tandem.createTandem( 'pressListener' ) } );
    this.addInputListener( pressListener );
    this.addAccessibleInputListener( pressListener.a11yListener );

    // Use the user-specified radius if present, otherwise calculate the
    // radius based on the content and the margin.
    var buttonRadius = options.radius ||
                       Math.max( content.width + options.minXMargin * 2, content.height + options.minYMargin * 2 ) / 2;

    // Create the basic button shape.
    var button = new Circle( buttonRadius, { fill: options.baseColor, lineWidth: options.lineWidth } );
    this.addChild( button );

    // Hook up the strategy that will control the basic button appearance.
    var buttonAppearanceStrategy = new options.buttonAppearanceStrategy(
      button,
      interactionStateProperty,
      this.baseColorProperty,
      options
    );

    // Add the content to the button.
    if ( content ) {
      content.center = upCenter;
      this.addChild( content );
    }

    // Hook up the strategy that will control the content appearance.
    var contentAppearanceStrategy = new options.contentAppearanceStrategy( content, interactionStateProperty );

    // Control the pointer state based on the interaction state.
    var self = this;

    function handleInteractionStateChanged( state ) {
      self.cursor = state === ButtonInteractionState.DISABLED ||
                    state === ButtonInteractionState.DISABLED_PRESSED ? null : 'pointer';
    }

    interactionStateProperty.link( handleInteractionStateChanged );

    // Dilate the pointer areas.
    this.touchArea = Shape.circle( 0, 0, buttonRadius + options.touchAreaDilation );
    this.mouseArea = Shape.circle( 0, 0, buttonRadius + options.mouseAreaDilation );

    // Set pickable such that sub-nodes are pruned from hit testing.
    this.pickable = null;

    // a11y
    this.focusHighlight = new Shape.circle( 0, 0, buttonRadius + 5 );

    // Mutate with the options after the layout is complete so that
    // width-dependent fields like centerX will work.
    this.mutate( options );

    // define a dispose function
    this.disposeRoundButtonView = function() {
      buttonAppearanceStrategy.dispose();
      contentAppearanceStrategy.dispose();
      pressListener.dispose();
      interactionStateProperty.unlink( handleInteractionStateChanged );
      this.baseColorProperty.dispose();
    };
  }

  sun.register( 'RoundButtonView', RoundButtonView );

  /**
   * Strategy for making a button look 3D-ish by using gradients that create the appearance of highlighted and shaded
   * edges.  The gradients are intended to make the light source appear to be above and to the left of the button.
   *
   * @param {Node} button
   * @param {Property.<boolean>} interactionStateProperty
   * @param {Property.<Color>} baseColorProperty
   * @param {Object} [options]
   * @constructor
   * @public
   */
  RoundButtonView.ThreeDAppearanceStrategy = function( button, interactionStateProperty, baseColorProperty, options ) {

    // Color properties
    var baseBrighter8 = new PaintColorProperty( baseColorProperty, { luminanceFactor: 0.8 } );
{'fraction_non_alphanumeric': 0.08715469613259669, 'fraction_numerical': 0.006008287292817679, 'mean_word_length': 3.2391686182669788, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '335297', 'n_tokens_mistral': 3970, 'n_tokens_neox': 3774, 'n_words': 1378}