phetsims / expression-exchange

"Expression Exchange" is an educational simulation in HTML5, by PhET Interactive Simulations.
GNU General Public License v3.0
2 stars 2 forks source link

Approach to event forwarding for CoinTermNodes #58

Closed Denz1994 closed 7 years ago

Denz1994 commented 7 years ago

After some discussion with @samreid over Masses and Springs, we discovered that forwarding the events of an icon its respective node can be done without duplicating code in the event listeners. Adding a method to the node that takes in the event from the icon makes forwards the events without requiring a translate or end listener for the icon.

Here is the code that may be deleted by forwarding using the startDrag() event.

      translate: function( translationParams ) {
        unboundedPosition.setXY(
          unboundedPosition.x + translationParams.delta.x,
          unboundedPosition.y + translationParams.delta.y
        );
        createdCoinTerm.setPositionAndDestination( new Vector2(
          Util.clamp( unboundedPosition.x, options.dragBounds.minX, options.dragBounds.maxX ),
          Util.clamp( unboundedPosition.y, options.dragBounds.minY, options.dragBounds.maxY )
        ) );
      },

      end: function( event, trail ) {
        createdCoinTerm.userControlledProperty.set( false );
        createdCoinTerm = null;
      }

Reference commit: https://github.com/phetsims/masses-and-springs/commit/f48258733e1cd04ea76eec70ad65a8382ef1fd56

samreid commented 7 years ago

More history on this strategy: @jonathanolson developed it for Charges and Fields, and I employed it in Circuit Construction Kit, it's an elegant way to use the correct input listener code without duplicates or adapters.

jbphet commented 7 years ago

@samreid - I'm seeing some variation in the usage in Charges and Fields and CCK. What would you say is the best file to check out as an example of this pattern?

samreid commented 7 years ago

For instance ChargesAndFieldsToolboxPanel.js:

    // When pressed, creates a model element and triggers startDrag() on the corresponding view
    electricPotentialSensorIconNode.addInputListener( {
      down: function( event ) {
        // Ignore non-left-mouse-button
        if ( event.pointer.isMouse && event.domEvent.button !== 0 ) {
          return;
        }

        electricPotentialSensor.isActiveProperty.set( true );

        // initial position of the pointer in the screenView coordinates
        var initialViewPosition = self.globalToParentPoint( event.pointer.point )
          .plus( new Vector2( 0, -SENSOR_HEIGHT * 6 / 25 ) );
        electricPotentialSensor.positionProperty.set( modelViewTransform.viewToModelPosition( initialViewPosition ) );

        electricPotentialSensorNode.movableDragHandler.startDrag( event );
      }
    } );

or SensorToolbox.js

      return {
        down: function( event ) {
          var viewPosition = circuitNode.globalToLocalPoint( event.pointer.point );
          meterModel.draggingProbesWithBodyProperty.set( true );
          meterModel.visibleProperty.set( true );
          meterModel.bodyPositionProperty.set( viewPosition );
          meterNode.dragHandler.startDrag( event );
        }

or CircuitElementToolNode.js

        down: function( event ) {

          // Ignore non-left-mouse-button, see #64
          if ( event.pointer.isMouse && event.domEvent.button !== 0 ) {
            return;
          }

          // initial position of the pointer in the coordinate frame of the CircuitNode
          var viewPosition = circuitNode.globalToLocalPoint( event.pointer.point );

          // Create the new CircuitElement at the correct location
          var circuitElement = createElement( viewPosition );

          // Add the CircuitElement to the Circuit
          circuit.circuitElements.add( circuitElement );

          // Send the start drag event through so the new element will begin dragging.
          // From: https://github.com/phetsims/scenery-phet/issues/195#issuecomment-186300071
          // @jonathanolson and I looked into the way Charges and Fields just calls startDrag(event) on the play area drag
          // listener (which adds a listener to the pointer, in the usual SimpleDragHandler way), and it seems like a good
          // pattern.
          circuitElement.startDragEmitter.emit1( event );
        }

Note: the last line in the last example ultimately calls startDrag() on the drag listener.

jbphet commented 7 years ago

This has been implemented, closing.