diff --git a/js/common/model/MembraneChannelsModel.ts b/js/common/model/MembraneChannelsModel.ts index cae0b66..9609951 100644 --- a/js/common/model/MembraneChannelsModel.ts +++ b/js/common/model/MembraneChannelsModel.ts @@ -71,6 +71,7 @@ export default class MembraneChannelsModel extends PhetioObject { public getSlotPosition( slot: Slot ): number { return SLOT_POSITIONS[ slots.indexOf( slot ) ]; } + public readonly slots = slots; public readonly timeSpeedProperty: EnumerationProperty; diff --git a/js/common/view/ChannelDragNode.ts b/js/common/view/ChannelDragNode.ts index cf1cb9a..2e2ac0e 100644 --- a/js/common/view/ChannelDragNode.ts +++ b/js/common/view/ChannelDragNode.ts @@ -3,12 +3,10 @@ import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; import Bounds2 from '../../../../dot/js/Bounds2.js'; -import Utils from '../../../../dot/js/Utils.js'; import Vector2 from '../../../../dot/js/Vector2.js'; import Vector2Property from '../../../../dot/js/Vector2Property.js'; import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js'; import DragListener from '../../../../scenery/js/listeners/DragListener.js'; -import KeyboardListener from '../../../../scenery/js/listeners/KeyboardListener.js'; import { PressListenerEvent } from '../../../../scenery/js/listeners/PressListener.js'; import Node from '../../../../scenery/js/nodes/Node.js'; import Tandem from '../../../../tandem/js/Tandem.js'; @@ -69,10 +67,9 @@ export default class ChannelDragNode extends Node { return slotDragIndicatorNode.globalBounds.intersectsBounds( this.globalBounds ) && !model.isSlotFilled( slotDragIndicatorNode.slot ); } ); - const closest = _.sortBy( overlappingSlotDragIndicatorNodes, slotDragIndicatorNode => { + return _.sortBy( overlappingSlotDragIndicatorNodes, slotDragIndicatorNode => { return slotDragIndicatorNode.globalBounds.center.distance( this.globalBounds.center ); } )[ 0 ]; - return closest; }; // eslint-disable-next-line consistent-this,@typescript-eslint/no-this-alias @@ -148,68 +145,6 @@ export default class ChannelDragNode extends Node { } ); this.addInputListener( this.dragListener ); - let currentSlotIndex = initSlot === null ? 0 : model.getSlotIndex( initSlot ); - - // TODO: Move on hold - const keyboardInputListener = new KeyboardListener( { - - // TODO: esc goes back to the toolbox - // TODO: on blur also go back to the toolbox - keys: [ 'arrowRight', 'arrowLeft', 'home', 'end', 'space', 'enter' ], - fire: ( event, keysPressed ) => { - - if ( keysPressed.includes( 'arrowRight' ) ) { - console.log( 'right' ); - - // move to the next slot in the model. - currentSlotIndex++; - currentSlotIndex = Utils.clamp( currentSlotIndex, 0, model.slots.length ); - if ( currentSlotIndex >= model.slots.length ) { - this.positionProperty.value = new Vector2( 100, 50 ); - } - else { - const x = model.getSlotPosition( model.getSlotForIndex( currentSlotIndex ) ); - this.positionProperty.value = new Vector2( x, 10 ); - } - } - if ( keysPressed.includes( 'arrowLeft' ) ) { - // move to the next slot in the model. - currentSlotIndex--; - currentSlotIndex = Utils.clamp( currentSlotIndex, 0, model.slots.length - 1 ); - - const x = model.getSlotPosition( model.getSlotForIndex( currentSlotIndex ) ); - this.positionProperty.value = new Vector2( x, 10 ); - } - - if ( keysPressed.includes( 'home' ) ) { - //TODO: - } - if ( keysPressed.includes( 'end' ) ) { - //TODO: - } - if ( keysPressed.includes( 'space' ) || keysPressed.includes( 'enter' ) ) { - - if ( currentSlotIndex >= model.slots.length ) { - this.dispose(); - homes[ 0 ].focus(); - } - else { - // if over an empty slot, fill it and delete the node - // const contents = model.getSlotContents( model.getSlotForIndex( currentSlotIndex ) ); - - // TODO: When dropping with mouse, it should also replace. - model.setSlotContents( model.getSlotForIndex( currentSlotIndex ), this.type ); - this.dispose(); - - homes[ 0 ].focus(); - - // // TODO: Same as above, but we may want to animate the old one back to the toolbox. Is animation valuable here, or would be a distraction? - } - } - } - } ); - this.addInputListener( keyboardInputListener ); - // TODO: Interactive highlight? // this.setInteractiveHighlight( new HighlightFromNode( this ) ); } diff --git a/js/common/view/ChannelToolNode.ts b/js/common/view/ChannelToolNode.ts index 485153a..e7893a2 100644 --- a/js/common/view/ChannelToolNode.ts +++ b/js/common/view/ChannelToolNode.ts @@ -36,7 +36,7 @@ export default class ChannelToolNode extends VBox { return { click: () => { - view.createFromKeyboard( type, [ this, channelNode ], true ); // TODO: swapped with the mouse one, watch out!!!! + view.forwardFromKeyboard( type ); } }; }; diff --git a/js/common/view/MembraneChannelsScreenView.ts b/js/common/view/MembraneChannelsScreenView.ts index 341c1ca..1ccc113 100644 --- a/js/common/view/MembraneChannelsScreenView.ts +++ b/js/common/view/MembraneChannelsScreenView.ts @@ -248,13 +248,26 @@ export default class MembraneChannelsScreenView extends ScreenView { membraneChannelNode.press( event ); } + public forwardFromKeyboard( type: ChannelType ): void { + const slot = this.model.getLeftmostEmptySlot() || this.model.getMiddleSlot(); + + this.model.setSlotContents( slot, type ); + + // TODO: There must be a better way to do this internally, https://github.com/phetsims/membrane-channels/issues/20 + this.observationWindow.focus(); + this.observationWindow.membraneGroupSortInteractionView.model.selectedGroupItemProperty.value = this.model.getSlotIndex( slot ); + this.observationWindow.membraneGroupSortInteractionView.model.isGroupItemKeyboardGrabbedProperty.value = true; + this.observationWindow.membraneGroupSortInteractionView.model.hasKeyboardGrabbedGroupItemProperty.value = true; + this.observationWindow.membraneGroupSortInteractionView.model.isKeyboardFocusedProperty.value = true; + } + /** * Called when the user presses a membrane protein in the accordion box to create one. * * @param type * @param homes - the nodes that the membrane protein can be returned to, in sequential order (1st visible one takes precedence) */ - public createFromKeyboard( type: ChannelType, homes: Node[], focus: boolean ): ChannelDragNode { + public createFromKeyboard( type: ChannelType, homes: Node[] ): ChannelDragNode { // TODO: duplicated with create from mouse // Move over the first available slot @@ -270,10 +283,6 @@ export default class MembraneChannelsScreenView extends ScreenView { ); this.addChild( channelDragNode ); - if ( focus ) { - channelDragNode.focus(); - } - // TODO: once keyboarded, prevent mouse+touch, or do this on init // channelDragNode.pickable = false; // keyboard only diff --git a/js/common/view/ObservationWindow.ts b/js/common/view/ObservationWindow.ts index c81b821..56b676c 100644 --- a/js/common/view/ObservationWindow.ts +++ b/js/common/view/ObservationWindow.ts @@ -40,7 +40,7 @@ export default class ObservationWindow extends InteractiveHighlightingNode { private readonly stepEmitter = new Emitter<[ number ]>( { parameters: [ { valueType: 'number' } ] } ); - private readonly membraneGroupSortInteractionView: MembraneGroupSortInteractionView; + public readonly membraneGroupSortInteractionView: MembraneGroupSortInteractionView; public constructor( private readonly model: MembraneChannelsModel, view: MembraneChannelsScreenView, public readonly modelViewTransform: ModelViewTransform2, canvasBounds: Bounds2, tandem: Tandem ) {