Drag and drop plugin for GWT

Introduction

The GQuery draggable and droppable plugin lets you make any DOM element draggable and/or droppable. In addition, it offers an API which lets you add drag-and-drop support to your GWT widgets, including also the new data presentation widgets (aka cell widgets).

This article explains how to use this API in your GWT project.

Configuration

GQuery provides a bundle containing everything needed to use GQuery and its draggable and droppable plugin.

If you don't use maven, you will have to download the jar file related to the gwt version you are using:

Please note that if you plan to add drag-and-drop support for CellTree and/or CellBrowser, the droppable plugins needs to override few gwt classes to open the API of CellTree and CellBrowser widget. So ensure that the gquery-dnd-bundle.jar are set before the gwt jar files in your compilation classpath.

A note about touch events support

The GQuery draggable and droppable plugin can handle mobile device and react on touch events instead of mouse events only if it is used with GQuery 1.2.0 or later !

You can download the last stable version of GQuery here and add it in your compilation classpath before the gquery-dnd-bundle.jar.

Add drag support to your widgets

The DraggableWidget class

To make your widget draggable, you have two simple possibilities :

wrap your widget in a DraggableWidgetinstance

have your widget class extend the DraggableWidgetclass.

Example with wrapping:

Labellabel=newLabel("I want to become a draggable widget !!");//wrap the original widget in a DraggableWidgetDraggableWidget<Label>draggableLabel=newDraggableWidget<Label>(label);//configure the drag behavior (see next paragraph)draggableLabel.setDraggingCursor(Cursor.MOVE);draggableLabel.setDraggingOpacity((float)0.8);...//add the draggableLabel to the DOMRootPanel.get().add(draggableLabel);//if you want to do something in the original label, just call getOriginalWidget methoddraggableLabel.getOriginalWidget().setText("I'm now draggable");

Configure the dragging behavior

The API of the DraggableWidget object proposes a set of methods allowing you to configure the drag behavior of your widget. You can find the list of all options and their related explanation in this table.

An example is always more understandable than a long text of explanation :

Labellabel=newLabel("I want to be a droppable widget !!");//wrap the original widget in a DraggableWidgetDraggableWidget<Label>draggableLabel=newDraggableWidget<Label>(label);//configure the drag behavior//use a clone of the helper as dragging displaydraggableLabel.useOriginalWidgetAsHelper();//change the cursor during the dragdraggableLabel.setDraggingCursor(Cursor.MOVE);//set the opacity of the dragging displaydraggableLabel.setDraggingOpacity((float)0.8);// the widget can only be dragged on horizontal axisdraggableLabel.setAxis(AxisOption.Y_AXIS);//revert the dragging display on its original position is not drop occureddraggableLabel.setRevert(RevertOption.ON_INVALID_DROP);//snap the dragging display to a 50x50px griddraggableLabel.setGrid(newint[]{50,50});...

Don't hesitate to try our "options in action" example to play around with most of the options of the DraggableWidget.

The drag events

The drag-and-drop plugin fires different events during the drag operation that you can handle to implement your custom code.

Here's the list of drag events :

gwtquery.plugins.draggable.client.events.BeforeDragStartEvent : fired before the before the initialization of the drag operation

To handle an event, just register an associated event handler in your DraggableWidget :

Labellabel=newLabel("I want to be a droppable widget !!");//wrap the original widget in a DraggableWidgetDraggableWidget<Label>draggableLabel=newDraggableWidget<Label>(label);draggableLabel.addDragStartHandler(newDragStartEventHandler(){publicvoidonDragStart(DragStartEventevent){//retrieve the widget that is being draggedDraggableWidget<Label>draggableWidget=(DraggableWidget<Label>)event.getDraggableWidget();draggableWidget.getOriginalWidget().setText("I'm dragging");}});draggableLabel.addDragStopHandler(newDragStopEventHandler(){publicvoidonDragStop(DragStopEventevent){//retrieve the widget that was being draggedDraggableWidget<Label>draggableWidget=(DraggableWidget<Label>)event.getDraggableWidget();draggableWidget.getOriginalWidget().setText("I'm not dragging");}});

All drag events provide useful methods to retrieve the dragging widget and the associated dragging display (called the helper) with the exception of the BeforeDragStartEventwhere the helper is null because the drag operation is not initialized yet.

Add drop support to your widgets

The DroppableWidget class

In the same way, to make your widget droppable, you have two possibilities :

wrap your widget in a DroppableWidgetinstance

extend you widget class with the DroppableWidgetclass

Example with wrapping:

Labellabel=newLabel("I want to be a droppable widget !!");//wrap the original widget in a DroppableWidgetDroppableWidget<Label>droppableLabel=newDroppableWidget<Label>(label);//configure the drop behaviour (see next paragraph)droppableLabel.setTolerance(DroppableTolerance.POINTER);//add the droppableLabel to the DOMRootPanel.get().add(droppableLabel);//if you want to do something in the original label, just call getOriginalWidget methoddroppableLabel.getOriginalWidget().setText("I'm now droppable");

Scope and Accept options

We will define what we mean by an "acceptable draggable".

A draggable element is accepted by a droppable if and only if they have the same scope value and if true is returned when we call the AcceptFunction of the droppable by passing the draggable. When we say that a draggable is accepted by a droppable, it means that draggable can be dropped on that droppable.The scope options is a key used to group draggable and droppable items.

The Accept options is a kind of filter apply to group of draggable with the same scope as the droppable. It can be a css selector. All draggable matching this selector will be accepted :

// all DraggableWidget with "draggableLabel" as css class name will be accepteddroppableLabel.setAccept(".draggableLabel");

By default, the scope is defined to "default" and a droppable accepts all draggables with the same scope.

The checkers example use a AcceptFunction to authorize drop of a piece only in the authorized squares.

The cell widgets

Since its 2.1 release, GWT proposes a new kind of widget, the data presentation widgets and introduces the concept of cells. The GWTQuery drag-and-drop plugins offers an API allowing to make any cell draggable and/or droppable.

DragAndDropCellList class

To add drag-and-drop support in a CellList, you have simply to use a DragAndDropCellList widget.

// Create a ConcactCell (normal cell displaying info a a contact)ContactCellcontactCell=newContactCell(Images.INSTANCE.contact());// Create a drag and drop cell listDragAndDropCellList<ContactInfo>cellList=newDragAndDropCellList<ContactInfo>(contactCell,ContactDatabase.ContactInfo.KEY_PROVIDER);// The cell of this CellList are only draggablecellList.setCellDraggableOnly();// setup the drag operationDraggableOptionsoptions=newDraggableOptions();// use a clone of the original cell as drag helperoptions.setHelper(HelperType.CLONE);// set the opacity of the drag helperoptions.setOpacity((float)0.9);// append the drag helper to the body elementoptions.setAppendTo("body");// configure the drag operations of the cell list with this optionscellList.setDraggableOptions(options);

In the same way, to add drag-and-drop in a CellTree (or a CellBrowser), just use a DragAndDropCellTree (or a DragAndDropCellBrowser) and use DragAndDropNodeInfo to define your tree model :

DragAndDropCellTreecellTree=newDragAndDropCellTree(newMemberTreeViewModel(),null,DroppableCellTreeResource.INSTANCE);...publicclassMemberTreeViewModelimplementsTreeViewModel{...public<T>NodeInfo<?>getNodeInfo(Tvalue){if(value==null){// root nodeDragAndDropNodeInfo<Permission>permissionNodeInfo=newDragAndDropNodeInfo<Permission>(permissionDataProvider,permissionCell);// permission cell are only droppablepermissionNodeInfo.setCellDroppableOnly();// setup drop operationDroppableOptionsoptions=permissionNodeInfo.getDroppableOptions();options.setDroppableHoverClass(Resource.INSTANCE.css().droppableHover());// use a DroppableFunction to define when happens when a drop occurs. We can also add a DropHandler directly in the tree.options.setOnDrop(newDroppableFunction(){publicvoidf(DragAndDropContextcontext){//retrieve the Member data that was droppedMemberInfodroppedMember=context.getDraggableData();//retrieve the Permission where the member was droppedPermissiondropPermission=context.getDroppableData();MemberDatabase.get().permissionChange(droppedMember,dropPermission);}});returnpermissionNodeInfo;}elseif(valueinstanceofPermission){// Permission Tree node. Permissionp=(Permission)value;//retrieve all members having this permissionListDataProvider<MemberInfo>dataProvider=MemberDatabase.get().getDataProvider(p);DragAndDropNodeInfo<MemberInfo>memberNodeInfo=newDragAndDropNodeInfo<MemberInfo>(dataProvider,memberCell,newSingleSelectionModel<MemberInfo>(),null);// member cell are only draggablememberNodeInfo.setCellDraggableOnly();// setup the drag operationDraggableOptionsoptions=memberNodeInfo.getDraggableOptions();// opacity of the drag helperoptions.setOpacity((float)0.9);// cursor during the drag operationoptions.setCursor(Cursor.MOVE);...returnmemberNodeInfo;}Stringtype=value.getClass().getName();thrownewIllegalArgumentException("Unsupported object type: "+type);}}

Please note that the droppable plugins needs to override few gwt classes to open the API of CellTree and CellBrowser widget. So ensure that the gquery-dnd-bundle.jar are set before the gwt jar files in your compilation classpath.

// Create a DragAndDropCellTable.DragAndDropCellTable<ContactInfo>table=newDragAndDropCellTable<ContactInfo>();// Name.DragAndDropColumn<ContactInfo,String>nameColumn=newDragAndDropColumn<ContactInfo,String>(newTextCell()){@OverridepublicStringgetValue(ContactInfoobject){returnobject.getLastName();}};//cells are only draggablenameColumn.setCellDraggableOnly();DraggableOptionsnameOptions=nameColumn.getDraggableOptions();// opacity of the helpernameOptions.setOpacity((float)0.8);// set the revert optionnameOptions.setRevert(RevertOption.ON_INVALID_DROP);table.addColumn(nameColumn,"Name");// Address.DragAndDropColumn<ContactInfo,String>addressColumn=newDragAndDropColumn<ContactInfo,String>(newTextCell()){@OverridepublicStringgetValue(ContactInfoobject){returnobject.getAddress();}};//setup drag operationDraggableOptionsaddressDragOptions=addressColumn.getDraggableOptions();// opacity of the helperaddressDragOptions.setOpacity((float)0.8);// only draggable on the vertical axisaddressDragOptions.setAxis(AxisOption.Y_AXIS);// set the scope for the drag and drop operationaddressDragOptions.setScope("addressScope");//setup the drop operationDroppableOptionsaddressDropOptions=addressColumn.getDroppableOptions();// set the scope for the drag and drop operation, btw the address cell accepts only an other address celladdressDropOptions.setScope("addressScope");//as the drop concerns only cells from this column, use a callback function instead of handle DropEvent from the CellTableaddressDropOptions.setOnDrop(newDroppableFunction(){publicvoidf(DragAndDropContextcontext){ContactInfodroppedContact=context.getDraggableData();ContactInfodropTargetContact=context.getDroppableData();dropTargetContact.setAddress(droppedContact.getAddress());}});table.addColumn(nameColumn,"Address");

DraggableOptions and DroppableOptions

Events

All DragAndDrop cell widgets propose a set of methods allowing the registration of handlers for all drag and drop events.

DragAndDropCellList<ContactInfo>cellList=newDragAndDropCellList<ContactInfo>(contactCell,ContactDatabase.ContactInfo.KEY_PROVIDER);cellList.addDragStartHandler(newDragStartEventHandler(){publicvoidonDragStart(DragStartEventevent){//called when a cell of the cellList start to drag...}});cellList.addDropHandler(newDropEventHandler(){publicvoidonDrop(DropEventevent){//called when a drop occurs on a cell of this CelList...}});

All drag events provide useful methods to retrieve the data associated with the cell that being dragged or playing the role of the drop target.

publicvoidonDrop(DropEventevent){//assume that cell is displaying a ContactInfo objectContactInfodraggedContact=event.getDraggableData();//assume that cell where the drop occurs is displaying Permission objectPermissiondropPermission=event.getDroppableData();draggedContact.setPermission(dropPermission);}