Dragging an object from one location to another by grabbing it with mouse, holding the mouse button pressed, and then releasing the button to "drop" it to the other location is a common way to move, copy, or associate objects. For example, most operating systems allow dragging and dropping files between folders or dragging a document on a program to open it. In Vaadin, it is possible to drag and drop components and parts of certain components.

Dragged objects, or transferables, are essentially data objects. You can drag and drop rows in Table and nodes in Tree components, either within or between the components. You can also drag entire components by wrapping them inside DragAndDropWrapper.

Dragging starts from a drag source, which defines the transferable. Transferables implement the Transferable interfaces. For trees and tables, which are bound to Container data sources, a node or row transferable is a reference to an Item in the Vaadin Data Model. Dragged components are referenced with a WrapperTransferable. Starting dragging does not require any client-server communication, you only need to enable dragging. All drag and drop logic occurs in two operations: determining (accepting) where dropping is allowed and actually dropping. Drops can be done on a drop target, which implements the DropTarget interface. Three components implement the interface: Tree, Table, and DragAndDropWrapper. These accept and drop operations need to be provided in a drop handler. Essentially all you need to do to enable drag and drop is to enable dragging in the drag source and implement the getAcceptCriterion() and drop() methods in the DropHandler interface.

The client-server architecture of Vaadin causes special requirements for the drag and drop functionality. The logic for determining where a dragged object can be dropped, that is, accepting a drop, should normally be done on the client-side, in the browser. Server communications are too slow to have much of such logic on the server-side. The drag and drop feature therefore offers a number of ways to avoid the server communications to ensure a good user experience.

Most of the user-defined drag and drop logic occurs in a drop handler, which is provided by implementing the drop() method in the DropHandler interface. A closely related definition is the drop accept criterion, which is defined in the getAcceptCriterion() method in the same interface. It is described in Section 12.12.4, “Accepting Drops” later.

The drop() method gets a DragAndDropEvent as its parameters. The event object provides references to two important object: Transferable and TargetDetails.

A Transferable contains a reference to the object (component or data item) that is being dragged. A tree or table item is represented as a TreeTransferable or TableTransferable object, which carries the item identifier of the dragged tree or table item. These special transferables, which are bound to some data in a container, are DataBoundTransferable. Dragged components are represented as WrapperTransferable objects, as the components are wrapped in a DragAndDropWrapper.

The TargetDetails object provides information about the exact location where the transferable object is being dropped. The exact class of the details object depends on the drop target and you need to cast it to the proper subclass to get more detailed information. If the target is selection component, essentially a tree or a table, the AbstractSelectTargetDetails object tells the item on which the drop is being made. For trees, the TreeTargetDetails gives some more details. For wrapped components, the information is provided in a WrapperDropDetails object. In addition to the target item or component, the details objects provide a drop location. For selection components, the location can be obtained with the getDropLocation() and for wrapped components with verticalDropLocation() and horizontalDropLocation(). The locations are specified as either VerticalDropLocation or HorizontalDropLocation objects. The drop location objects specify whether the transferable is being dropped above, below, or directly on (at the middle of) a component or item.

Dropping on a Tree, Table, and a wrapped component is explained further in the following sections.

You can drag items from, to, or within a Tree. Making tree a drag source requires simply setting the drag mode with setDragMode(). Tree currently supports only one drag mode, TreeDragMode.NODE, which allows dragging single tree nodes. While dragging, the dragged node is referenced with a TreeTransferable object, which is a DataBoundTransferable. The tree node is identified by the item ID of the container item.

When a transferable is dropped on a tree, the drop location is stored in a TreeTargetDetails object, which identifies the target location by item ID of the tree node on which the drop is made. You can get the item ID with getItemIdOver() method in AbstractSelectTargetDetails, which the TreeTargetDetails inherits. A drop can occur directly on or above or below a node; the exact location is a VerticalDropLocation, which you can get with the getDropLocation() method.

In the example below, we have a Tree and we allow reordering the tree items by drag and drop.

final Tree tree = new Tree("Inventory");
tree.setContainerDataSource(TreeExample.createTreeContent());
layout.addComponent(tree);
        
// Expand all items
for (Iterator<?> it = tree.rootItemIds().iterator(); it.hasNext();)
    tree.expandItemsRecursively(it.next());
        
// Set the tree in drag source mode
tree.setDragMode(TreeDragMode.NODE);
        
// Allow the tree to receive drag drops and handle them
tree.setDropHandler(new DropHandler() {
    public AcceptCriterion getAcceptCriterion() {
        return AcceptAll.get();
    }

    public void drop(DragAndDropEvent event) {
        // Wrapper for the object that is dragged
        Transferable t = event.getTransferable();
        
        // Make sure the drag source is the same tree
        if (t.getSourceComponent() != tree)
            return;
        
        TreeTargetDetails target = (TreeTargetDetails)
            event.getTargetDetails();

        // Get ids of the dragged item and the target item
        Object sourceItemId = t.getData("itemId");
        Object targetItemId = target.getItemIdOver();

        // On which side of the target the item was dropped 
        VerticalDropLocation location = target.getDropLocation();
        
        HierarchicalContainer container = (HierarchicalContainer)
        tree.getContainerDataSource();

        // Drop right on an item -> make it a child
        if (location == VerticalDropLocation.MIDDLE)
            tree.setParent(sourceItemId, targetItemId);

        // Drop at the top of a subtree -> make it previous
        else if (location == VerticalDropLocation.TOP) {
            Object parentId = container.getParent(targetItemId);
            container.setParent(sourceItemId, parentId);
            container.moveAfterSibling(sourceItemId, targetItemId);
            container.moveAfterSibling(targetItemId, sourceItemId);
        }
        
        // Drop below another item -> make it next 
        else if (location == VerticalDropLocation.BOTTOM) {
            Object parentId = container.getParent(targetItemId);
            container.setParent(sourceItemId, parentId);
            container.moveAfterSibling(sourceItemId, targetItemId);
        }
    }
});

Tree defines some specialized accept criteria for trees.

In addition, the accept criteria defined in AbstractSelect are available for a Tree, as listed in Section 12.12.4, “Accepting Drops”.

You can not drop the objects you are dragging around just anywhere. Before a drop is possible, the specific drop location on which the mouse hovers must be accepted. Hovering a dragged object over an accepted location displays an accept indicator, which allows the user to position the drop properly. As such checks have to be done all the time when the mouse pointer moves around the drop targets, it is not feasible to send the accept requests to the server-side, so drops on a target are normally accepted by a client-side accept criterion.

A drop handler must define the criterion on the objects which it accepts to be dropped on the target. The criterion needs to be provided in the getAcceptCriterion() method of the DropHandler interface. A criterion is represented in an AcceptCriterion object, which can be a composite of multiple criteria that are evaluated using logical operations. There are two basic types of criteria: client-side and server-side criteria. The various built-in criteria allow accepting drops based on the identity of the source and target components, and on the data flavor of the dragged objects.

To allow dropping any transferable objects, you can return a universal accept criterion, which you can get with AcceptAll.get().

tree.setDropHandler(new DropHandler() {
    public AcceptCriterion getAcceptCriterion() {
        return AcceptAll.get();
    }
    ...

The client-side criteria, which inherit the ClientSideCriterion, are verified on the client-side, so server requests are not needed for verifying whether each component on which the mouse pointer hovers would accept a certain object.

The following client-side criteria are define in com.vaadin.event.dd.acceptcriterion:

AcceptAll
Accepts all transferables and targets.
And
Logical AND operation on two client-side criterion; accepts the transferable if all the defined sub-criteria accept it.
ContainsDataFlavour
The transferable must contain the defined data flavour.
Not
Logical NOT operation on two client-side criterion; accepts the transferable if and only if the sub-criterion does not accept it.
Or
Logical OR operation on two client-side criterion; accepts the transferable if any of the defined sub-criteria accept it.
SourceIs
Accepts all transferables from any of the given source components
SourceIsTarget
Accepts the transferable only if the source component is the same as the target. This criterion is useful for ensuring that items are dragged only within a tree or a table, and not from outside it.
TargetDetailIs
Accepts any transferable if the target detail, such as the item of a tree node or table row, is of the given data flavor and has the given value.

In addition, target components such as Tree and Table define some component-specific client-side accept criteria. See Section 12.12.2, “Dropping Items On a Tree for more details.

AbstractSelect defines the following criteria for all selection components, including Tree and Table.

AcceptItem
Accepts only specific items from a specific selection component. The selection component, which must inherit AbstractSelect, is given as the first parameter for the constructor. It is followed by a list of allowed item identifiers in the drag source.
AcceptItem.ALL
Accepts all transferables as long as they are items.
TargetItemIs
Accepts all drops on the specified target items. The constructor requires the target component (AbstractSelect) followed by a list of allowed item identifiers.
VerticalLocationIs.MIDDLE, TOP, and BOTTOM
The three static criteria accepts drops on, above, or below an item. For example, you could accept drops only in between items with the following:
public AcceptCriterion getAcceptCriterion() {
    return new Not(VerticalLocationIs.MIDDLE);
}

The server-side criteria are verified on the server-side with the accept() method of the ServerSideCriterion class. This allows fully programmable logic for accepting drops, but the negative side is that it causes a very large amount of server requests. A request is made for every target position on which the pointer hovers. This problem is eased in many cases by the component-specific lazy loading criteria TableDropCriterion and TreeDropCriterion. They do the server visit once for each drag and drop operation and return all accepted rows or nodes for current Transferable at once.

The accept() method gets the drag event as a parameter so it can perform its logic much like in drop().

public AcceptCriterion getAcceptCriterion() {
    // Server-side accept criterion that allows drops on any other
    // location except on nodes that may not have children
    ServerSideCriterion criterion = new ServerSideCriterion() {
        public boolean accept(DragAndDropEvent dragEvent) {
            TreeTargetDetails target = (TreeTargetDetails)
                dragEvent.getTargetDetails();

            // The tree item on which the load hovers
            Object targetItemId = target.getItemIdOver();

            // On which side of the target the item is hovered
            VerticalDropLocation location = target.getDropLocation();
            if (location == VerticalDropLocation.MIDDLE)
                if (! tree.areChildrenAllowed(targetItemId))
                    return false; // Not accepted

            return true; // Accept everything else
        }
    };
    return criterion;
}

The server-side criteria base class ServerSideCriterion provides a generic accept() method. The more specific TableDropCriterion and TreeDropCriterion are conveniency extensions that allow definiting allowed drop targets as a set of items. They also provide some optimization by lazy loading, which reduces server communications significantly.

public AcceptCriterion getAcceptCriterion() {
    // Server-side accept criterion that allows drops on any
    // other tree node except on node that may not have children
    TreeDropCriterion criterion = new TreeDropCriterion() {
        @Override
        protected Set<Object> getAllowedItemIds(
                DragAndDropEvent dragEvent, Tree tree) {
            HashSet<Object> allowed = new HashSet<Object>();
            for (Iterator<Object> i =
                   tree.getItemIds().iterator(); i.hasNext();) {
                Object itemId = i.next();
                if (tree.hasChildren(itemId))
                    allowed.add(itemId);
            }
            return allowed;
        }
    };
    return criterion;
}

Dragging a component requires wrapping the source component within a DragAndDropWrapper. You can then allow dragging by putting the wrapper (and the component) in drag mode with setDragStartMode(). The method supports two drag modes: DragStartMode.WRAPPER and DragStartMode.COMPONENT, which defines whether the entire wrapper is shown as the drag image while dragging or just the wrapped component.

// Have a component to drag
final Button button = new Button("An Absolute Button");

// Put the component in a D&D wrapper and allow dragging it
final DragAndDropWrapper buttonWrap = new DragAndDropWrapper(button);
buttonWrap.setDragStartMode(DragStartMode.COMPONENT);

// Set the wrapper to wrap tightly around the component
buttonWrap.setSizeUndefined();
        
// Add the wrapper, not the component, to the layout
layout.addComponent(buttonWrap, "left: 50px; top: 50px;");

The default height of DragAndDropWrapper is undefined, but the default width is 100%. If you want to ensure that the wrapper fits tightly around the wrapped component, you should call setSizeUndefined() for the wrapper. Doing so, you should make sure that the wrapped component does not have a relative size, which would cause a paradox.

Dragged components are referenced in the WrapperTransferable. You can get the reference to the dragged component with getDraggedComponent(). The method will return null if the transferable is not a component. Also HTML 5 drags (see later) are held in wrapper transferables.

Drops on a component are enabled by wrapping the component in a DragAndDropWrapper. The wrapper is an ordinary component; the constructor takes the wrapped component as a parameter. You just need to define the DropHandler for the wrapper with setDropHandler().

In the following example, we allow moving components in an absolute layout. Details on the drop handler are given later.

// A layout that allows moving its contained components
// by dragging and dropping them
final AbsoluteLayout absLayout = new AbsoluteLayout();
absLayout.setWidth("100%");
absLayout.setHeight("400px");

... put some (wrapped) components in the layout ...

// Wrap the layout to allow handling drops
DragAndDropWrapper layoutWrapper =
        new DragAndDropWrapper(absLayout);

// Handle moving components within the AbsoluteLayout
layoutWrapper.setDropHandler(new DropHandler() {
    public AcceptCriterion getAcceptCriterion() {
        return AcceptAll.get();
    }
    
    public void drop(DragAndDropEvent event) {
        ...        
    }
});

The drop handler receives the drop target details in a WrapperTargetDetails object, which implements the TargetDetails interface.

public void drop(DragAndDropEvent event) {
    WrapperTransferable t =
        (WrapperTransferable) event.getTransferable();
    WrapperTargetDetails details =
        (WrapperTargetDetails) event.getTargetDetails();

The wrapper target details include a MouseEventDetails object, which you can get with getMouseEvent(). You can use it to get the mouse coordinates for the position where the mouse button was released and the drag ended. Similarly, you can find out the drag start position from the transferable object (if it is a WrapperTransferable) with getMouseDownEvent().

// Calculate the drag coordinate difference
int xChange = details.getMouseEvent().getClientX()
              - t.getMouseDownEvent().getClientX();
int yChange = details.getMouseEvent().getClientY()
              - t.getMouseDownEvent().getClientY();

// Move the component in the absolute layout
ComponentPosition pos =
    absLayout.getPosition(t.getSourceComponent());
pos.setLeftValue(pos.getLeftValue() + xChange);
pos.setTopValue(pos.getTopValue() + yChange);

You can get the absolute x and y coordinates of the target wrapper with getAbsoluteLeft() and getAbsoluteTop(), which allows you to translate the absolute mouse coordinates to coordinates relative to the wrapper. Notice that the coordinates are really the position of the wrapper, not the wrapped component; the wrapper reserves some space for the accept indicators.

The verticalDropLocation() and horizontalDropLocation() return the more detailed drop location in the target.