phetsims / scenery-phet

Reusable components based on Scenery that are specific to PhET simulations.
MIT License
8 stars 6 forks source link

Add a drag handler for items in a toolbox/carousel/bucket #186

Closed samreid closed 8 years ago

samreid commented 8 years ago

From https://github.com/phetsims/bending-light/issues/193

Many of our simulations have a drag and drop capability where the user drags something from a toolbox/bucket/carousel into the play area, and can drop the object back where it came from. From https://github.com/phetsims/bending-light/issues/193#issuecomment-139452673

By the way, this is a recurring problem that comes up in a wide variety of simulations. We normally just code custom solutions each time, but having a pattern that works in multiple simulations would be great. A general solution should also work with items dragged out of a carousel. I can see this strategy being used in CCK Basics, Function Builder, Balancing Act, Build a Molecule, etc.

Here is a list of all of the features we need from these toolbox items:

Layout (1) The layout in the toolbox is correct, and easy to resize/reposition (2) The toolboxes can move around, when the play area size adjusts.

Dragging out (1) When dragging out of the toolbox, a tool should animate to full size and drag as a unit (all items drag together) and should drag by the "hot spot". Not every node has a hot spot. (2) The node can look different when dragged into/out of its home (such as gaining error bars, in curve-fitting), but normally should look just the same (though a different size) (3) If it is a composite node (such as a sensor with a probe + body), they translate together. No element in the composite node should be able to leave the bounds (visible area or sim-specified bounds) (4) Items "move to front" when dragged

Dragging the play area (1) The exact model coordinates and shapes are critically important. (2) When dragging out of the toolbox or in the play area, it should be constrained to be within the visible bounds or a simulation specified region. (3) The cursor shouldn't get away from the node (4) When dragging a composite object (such as a sensor with multiple probes) a in the play area, it can be dragged by any component.

When the play area resizes (1) When the play area resizes, the object should be moved into the visible bounds.

Dropping back into the toolbox (1) When any component of a multi-component node (such as a sensor body + probe) is dropped into the toolbox, the entire tool animates back to the toolbox, into the correct positioning (2) When dropping back into the toolbox, the object does not necessarily go back to its original position, in the case of stacked items. (3) Composite items (such as nodes that have multiple part that can be dragged independently) move back to their original relative positions/orientations. Animation would be nice but not necessary here. (4) The target drop location cannot just use the toolbox bounds--for dragging back to a bucket, dropping nearby/on top may also need to trigger an "animate back to bucket"

While animating back to the toolbox (1) We need to decide what should happen when the user clicks on an object animating back to the toolbox

When the simulation is reset (1) All animations should be cleared (2) All objects should instantly appear in the toolboxes (which may be in a different location that when the simulation started)

When an item in the toolbox is clicked (1) We need to decide what should happen to an item in the toolbox/bucket/carousel when click/released. This is a question for designers. I'm sure our simulations vary as to what happens here.

When dropped behind a control panel (1) Not sure if this is specific to bending light or will occur elsewhere, but if a sensor/tool is dropped behind a control panel, it should "bump" out to the side so it can be seen/grabbed, see https://github.com/phetsims/bending-light/issues/200

@pixelzoom suggested: Would it be useful to identify the various implementations, and compare/contrast? Or maybe that's too big a job, and we just need to identify one good pattern and commonize it?

@samreid looked through the active runnables and found these drag and drop UI components: https://github.com/phetsims/bending-light/issues/193#issuecomment-139654031

I looked through the active-runnables to see what kind of drag/drop UI components we have:

acid-base-solutions: none area-builder: buckets toolboxes carousel balancing-act: carousel objects get smaller when dragged bending-light: toolboxes build-a-molecule: buckets in carousels build-an-atom; buckets with custom layout charges-and-fields: toolbox curve-fitting: bucket, items get error bars when dragged energy-forms-and-changes energy-skate-park-basics: toolbox fluid-pressure-and-flow: toolbox for sensors forces-and-motion-basics toolbox pullers and items fraction-matcher: toolbox function-builder: carousel gene-expression-basics: toolbox isotopes-and-atomic-mass: buckets least-squares-regression: buckets making-tens: toolbox molecule-shapes: pseudo-toolbox (items animate only) molecule-shapes-basics: pseudo-toolbox (items animate only) optics-lab: toolbox protein-synthesis: toolbox w/stacks seasons: toolbox sugar-and-salt-solutions: toolbox with multiprobe under-pressure: toolbox

I may have missed something.

I'd really like to get something working nicely for bending light, since many open bending light issues depend on the majority of these features.

samreid commented 8 years ago

I would really like a solution here that plays nicely with objects that just use some of the features, such as items that can be dragged in the play area (like MovableDragHandler). We should try to share as much behavior/code as possible.

samreid commented 8 years ago

@jbphet @pixelzoom @pixelzoom I'd like to work on this soon, so please review my notes above and let me know your thoughts.

samreid commented 8 years ago

@ariel-phet I'd also like to get input from the design team. Can you and/or other designers review the behaviors listed in the issue description and see if there are other features that need to be implemented, or any new upcoming user interface elements that will need a different kind of behavior? Not sure if this warrants a mini-design meeting or not, but having as much information as possible will be valuable as we work on a reusable code solution for these related problems.

samreid commented 8 years ago

I marked 8 issues in Bending Light as pertaining to this issue, with the label "toolbox": https://github.com/phetsims/bending-light/labels/toolbox

pixelzoom commented 8 years ago

My first comment is that it is important to divide responsibilities between the container (toolbox, carousel,..) and the items in container. Containers should have responsibility for managing the collection as a whole - layout, moving the entire collection, showing some portion of the collection,... The items should be responsible for what happens when the user interacts with them (interactivity, model element creation, node creation,...) So a good first step would be to take the features enumerated in https://github.com/phetsims/scenery-phet/issues/186#issue-106090617 and determine whether they are responsibilities of the container or items.

pixelzoom commented 8 years ago

I don't think it's feasible to have a single type of drag handler that meet all needs, since the nature of the items in the container necessarily differs from sim-to-sim. (Another reason for decoupling the container from the items, btw.) I do think it's possible to have a couple of varieties of drag handlers. So another good step would be to identify the types of items in containers, and how their behavior differs.

For example, in some toolboxes there are a finite number of tools, so model elements and nodes could be created in advance, and the items in the toolbox could simply be scaled down versions of the tool nodes. In others cases there is an infinite (or large) number of instances of a tool, and we want to create/destroy model elements and associated nodes. My point is... Very different situations, very different behavior.

samreid commented 8 years ago

For example, in some toolboxes there are a finite number of tools, so model elements and nodes could be created in advance, and the items in the toolbox could simply be scaled down versions of the tool nodes. In others cases there is an infinite (or large) number of instances of a tool, and we want to create/destroy model elements and associated nodes. My point is... Very different situations, very different behavior.

One strategy I've seen used before when there is an unlimited number of nodes is for the actual node to be on the top, and when it is dragged it automatically creates a new instance beneath itself (in the toolbox). Rather than having an "icon" forward events to a newly created node, it is already the node itself. So one of each type of node would be created "in advance", with the next one (beneath it) created when the top one is dragged. Thoughts about that strategy?

pixelzoom commented 8 years ago

Thoughts about that strategy?

Sounds like a viable strategy. But again, I'm skeptical that there's a single strategy that applies for all cases.

pixelzoom commented 8 years ago

My only experience with this topic has been in Java sims. In the Glaciers simulation, the pattern I used is embodied in InteractiveToolIconNode, an inner class of AbstractToolIconNode. From the Java doc:

    /**
     * InteractiveToolIconNode adds interactivity to ToolIconNode.
     * When an interactive tool icon receives a mouse press, it asks a specified tool producer
     * to create a tool model element. As long as the mouse remains pressed, drag events 
     * are used to change the new tool's position.
     */

Not saying that this is "the" pattern to follow, just throwing it out there as a data point for what I did in Glaciers (seemed to work well there.)

samreid commented 8 years ago

I'm going to start tinkering with support for the above features in scenery-phet.

samreid commented 8 years ago

Question for designers: should we generally show the shadow of a dragged item? Some sims do, some sims don't.

samreid commented 8 years ago

Question for designers: How far should a use be able to drag an object off the screen? (a) halfway (b) not at all (c) all except for some pixels (say 20px)?

samreid commented 8 years ago

@jonathanolson @pixelzoom is there a way to automatically create a drop shadow from a given scenery node, or would it have to be custom coded based on knowledge about the particular node?

pixelzoom commented 8 years ago

Question for designers: How far should a use be able to drag an object off the screen? (a) halfway (b) not at all (c) all except for some pixels (say 20px)?

Configurable.

is there a way to automatically create a drop shadow from a given scenery node, or would it have to be custom coded based on knowledge about the particular node?

I haven't had to do this. If supported by scenery, I'd guess it would be something like one of Node.toShape, Node.getShape, Node.toPath, Node.getPath. But I don't see any of those.

jonathanolson commented 8 years ago

@jonathanolson @pixelzoom is there a way to automatically create a drop shadow from a given scenery node, or would it have to be custom coded based on knowledge about the particular node?

Drop shadows are supported by SVG (feOffset filter), and a "drop shadow" filter has been something I've thought about. This could probably be mimicked with Canvas/WebGL as necessary also.

jbphet commented 8 years ago

Question for designers: How far should a use be able to drag an object off the screen? (a) halfway (b) not at all (c) all except for some pixels (say 20px)?

+1 for configurable

samreid commented 8 years ago

Building blocks like animateTo() and reparent() are making things a lot simpler. I'm going to punt on a general solution that can be used everywhere and move ToolListener to Bending Light for the protractor. Not sure what else we should do with this thread--except that it is a good reference for the different features a tool often needs to support. I'll mark it for developer meeting to discuss and see if there's anything else to do here.

pixelzoom commented 8 years ago

The listener is now bending-light.ProtractorDragListener, which is specific to the protractor.

pixelzoom commented 8 years ago

9/29/15 dev meeting: We discussed this and are going to close this issue. If anyone else wants to find a way to generalize this, that would be awesome.