Drag and Drop with Atlas: interfaces
In a previous post I gave a quick introduction to the Atlas Drag&Drop system. The core of this system is the Sys.UI.DragDropManager instance; this global-scoped object provides a cross-browser engine to handle drag&drop operations. As I said before, to build a drag&drop enabled UI we have to deal with draggable items (or controls) and drop targets. Let's start by defining
- a draggable item as a control that implements the Sys.UI.IDragSource interface
and
- a drop target as a control that implements the Sys.UI.IDropTarget interface.
Basically, the DragDropManager handles a drag&drop operation by invoking the implementations of one or both these interfaces on a particular control, while from the control's point of view the DragDropManager is basically used to
- start a drag operation, by invoking its startDragDrop() method, if we are implementing the IDragSource interface; this method accepts three parameters: the drag source (usually a reference to the control), the drag visual (e.g. something to display under the mouse during the drag operation) and a context object.
- register or unregister a drop target, by invoking the registerDropTarget() or unregisterDropTarget() methods, if we are implementing the IDropTarget interface. Both these methods accept a reference to the control that is acting as the drop zone.
To make things easier to understand, I decided to code the simplest (and dummiest) implementations of the IDragSource and the IDropTarget interfaces. The results are two behaviors (
DraggableControlBehavior and
DropZoneBehavior) that you can attach to a generic Atlas control and start seeing how things work. For example, you could insert a debug.trace() call in each method implementation to trace the calls made by the DragDropManager.While these two behaviors are really useless from an application point of view, they could be used as
skeleton implementations to build more complex controls and behaviors.
The following two paragraphs list the methods defined by the
IDragSource and the
IDropTarget interfaces; at the end of each paragraph you'll find the simplest implementation of the interface in the form of an
Atlas behavior.
The IDragSource interface
The IDragSource interface is implemented by controls that are supposed to be draggable. It defines the following methods:
- get_dataType, returns the data type (as a string) for this control. It means that this control should be dropped only onto target controls that have this data type in the list of their accepted data types.
- get_data, returns the data relative to the control. Through this method, a control can expose specific data to be retrieved during the drag operation. The DragDropManager passes to this method the context object that receives when its startDragDrop() method is called.
- get_dragMode, returns the drag mode for this control; the framework defines a Sys.UI.DragMode enumeration with two values: Move and Copy.
- onDragStart, is invoked when the drag operation starts;
- onDrag, is invoked while the control is being dragged;
- onDragEnd, is invoked when the drag operation ends, i.e. when the left mouse button is released. The DragDropManager passes to this method a boolean value ("canceled") indicating if the drag operation has to be considered "valid" or not. For example, a control dropped onto a target that doesn't accept that data type causes the cancelled parameter to be set to true. Same thing happens if a control is dropped onto an area not registered with the DragDropManager as a drop target.
DraggableControlBehavior.js
This behaviors adds a basic drag functionality to a control. Notice how the startDragDrop() method of the DragDropManager is called in a handler for the mousedown event.
Type.registerNamespace('AtlasNotes.UI');
AtlasNotes.UI.DraggableControlBehavior = function() {
AtlasNotes.UI.DraggableControlBehavior.initializeBase(this);
var _mousedownHandler;
this.initialize = function() {
AtlasNotes.UI.DraggableControlBehavior.callBaseMethod(this, 'initialize');
_mousedownHandler = Function.createDelegate(this, mousedownHandler);
this.control.element.attachEvent('onmousedown', _mousedownHandler);
}
this.dispose = function() {
this.control.element.detachEvent('onmousedown', _mousedownHandler);
_mousedownHandler = null;
AtlasNotes.UI.DraggableControlBehavior.callBaseMethod(this, 'dispose');
}
this.getDescriptor = function() {
var td = AtlasNotes.UI.DraggableControlBehavior.callBaseMethod(this, 'getDescriptor');
return td;
}
this.startDragDrop = function(dragVisual) {
Sys.UI.DragDropManager.startDragDrop(this, dragVisual, null);
}
function mousedownHandler() {
window.event.returnValue = false;
this.startDragDrop(this.control.element);
}
// IDragSource Members.
this.get_dataType = function() {
return 'HTML';
}
this.get_data = function() {
return this.control.element;
}
this.get_dragMode = function() {
return Sys.UI.DragMode.Move;
}
this.onDragStart = function() {
}
this.onDrag = function() {
}
this.onDragEnd = function(cancelled) {
debug.trace(cancelled);
}
}
AtlasNotes.UI.DraggableControlBehavior.registerClass('AtlasNotes.UI.DraggableControlBehavior', Sys.UI.Behavior);
Sys.TypeDescriptor.addType('script', 'draggableControl', AtlasNotes.UI.DraggableControlBehavior);
The IDropTarget interface
The IDropTarget interface is implemented by controls who want to act as drop targets. It defines the following methods:
- get_dropTargetElement, returns the element which acts as the drop target; often this method returns a reference to the associated DOM element.
- canDrop, returns true if the current dragged control can be dropped on the target control. The test is done on two parameters passed as arguments to the method: the first is the current drag mode, the second is the data type;
- drop, is invoked when a control is dropped on the target control. When this happens, get_dragMode(), get_dataType() and get_data() are invoked on the dragged control to get the corresponding values that are passed as parameters to this method.
- onDragEnterTarget, is invoked when a dragged control enters the area covered by the drop target;
- onDragLeaveTarget, is invoked when a dragged control leaves the drop target;
- onDragInTarget, is invoked when the dragged control is over the drop target.
The last three methods receive three parameters from the DragDropManager: the current drag mode, the data type and the data exposed by the control being dragged.
DropZoneBehavior.js
Attaching this behavior to a control will transform it into a drop zone; when something is dropped, it raises a dropped event.
Type.registerNamespace('AtlasNotes.UI');
AtlasNotes.UI.DropZoneBehavior = function() {
AtlasNotes.UI.DropZoneBehavior.initializeBase(this);
this.dropped = this.createEvent();
this.initialize = function() {
AtlasNotes.UI.DropZoneBehavior.callBaseMethod(this, 'initialize');
// Register ourselves as a drop target.
Sys.UI.DragDropManager.registerDropTarget(this);
}
this.dispose = function() {
AtlasNotes.UI.DropZoneBehavior.callBaseMethod(this, 'dispose');
}
this.getDescriptor = function() {
var td = AtlasNotes.UI.DropZoneBehavior.callBaseMethod(this, 'getDescriptor');
td.addEvent('dropped', true);
return td;
}
// IDropTarget members.
this.get_dropTargetElement = function() {
return this.control.element;
}
this.drop = function(dragMode, type, data) {
// Raise a drop event.
this.dropped.invoke(this, Sys.EventArgs.Empty);
}
this.canDrop = function(dragMode, dataType) {
return dataType == 'HTML';
}
this.onDragEnterTarget = function(dragMode, type, data) {
}
this.onDragLeaveTarget = function(dragMode, type, data) {
}
this.onDragInTarget = function(dragMode, type, data) {
}
}
AtlasNotes.UI.DropZoneBehavior.registerClass('AtlasNotes.UI.DropZoneBehavior', Sys.UI.Behavior);
Sys.TypeDescriptor.addType('script', 'dropZone', AtlasNotes.UI.DropZoneBehavior);
To start experimenting with the two behaviors, just upgrade some DOM elements to Atlas controls and then add the appropriate behavior. For example:
<control id="atlasControl1">
<behaviors>
<dropZone />
</behaviors>
</control>
<control id="atlasControl2">
<behaviors>
<draggableControl />
</behaviors>
</control>