Drag and Drop

[TODO - this was written just before names got refactored - needs updating to reflect new names]

Managing Drag and drop involves a sequence of events, illustrated by this diagram:

In the rest of this document, we walk through each of the steps that occur during a drag and drop in sequence.

1. The mouseDown event

Drag drop initiates when the user presses the mouse down on the page. A global mouse event handler listens for these events and responds as follows:

Is a drag drop in progress?If so, cancel the drag drop and return.
(Handles a Mozilla issue where Mozilla forgets to send a mouse up event.
Is the event on a control whose ID_isDragHandle="true"?If so, calls bubble() to bubble an ID_dragButtonDown event up the control hierarchy.

2. onBubbleEvent()

Controls override onBubbleEvent() to listen for bubble events.

ListControl's onBubbleEvent works as follows:

Is the bubble event == ID_dragButtonDown? Yes?

Find the item the event occurred on.

Get the control for that item.

Get the control's peer element by calling Control.getPeer(). Call this DOM element the existingElement.

Clone existingElement to make a dragElement - this is the element that will be dragged around.

Call Control.doDragDrop(), passing it the item being dragged, existingElement, dragElement, and the eventArgs that for the ID_dragButtonDown event.

3. doDragDrop()

This sets up a new drag drop interaction, registering global event handlers for mouseMove and mouseUp events. By itself, doDragDrop does very little work - it waits until the mouse moves before starting the drag...

4. mouseMove events

The global mouse move handler behaves as follows:

Is there a drag drop that hasn't started yet? Yes?

Call onDragBegin() on the control that initiated the drag drop.

Add dragElement to the document and make it visible.

Is there an ongoing drag drop? Yes?

Position the dragElement under to the mouse.

Determine the control under mouse, and call acceptDrop() on that control to see if it can become the target for the drop.

Call onDragLeave() and onDragEnter() on the target control when it changes.

Call onDragOver() on the drop target so it can provide feedback during a drag drop.

5. acceptsDrop()

acceptDrop() is called during a drag move event to see if a particular control can handle a drop. Control classes override this to specify if a drop is acceptable.

The default Control implementation works by looking at the type name of the data item being dragged. It returns true if that type name matches the value of the ID_dropTypeName property on the control.

This may be overridden to do more specific processing if desired.

6. The mouseUp event

When the mouse is relesed, a global handler looks to see what actions need to be taken, as follows:

Is there an ongoing drag drop operation? Yes?

Remove the dragElement from the document.

Call onDragEnd() on the control that initiated the drag drop.

If the mouse was released over a drop target (a control whose acceptDrop() returned true), call that control's onDragLeave() and then call its onDrop() method.

7. onDragEnd()

The drag drop mechanism calls these onDragEnd() when a drag drop finishes, before it calls onDrop().

Implementors of onDragEnd() are responsible for looking at the dragEventArgs.targetControl property. It it is non-null, this means the target control has accepted the drop. In that case, onDragEnd() must remove the objects being dragged (the dragEventArgs.objects array) from their data model. If targetControl is null, this means the drag drop operation was cancelled and onDragEnd() should do nothing.

8. onDrop()

This is the last method called in a drag drop sequence. When the target control's onDrop() is called, the items in the dragEventArgs.objects array are ready to the added to the target control's data model - they have already been removed from the source control's data model.