diff options
Diffstat (limited to 'documentation/advanced/advanced-dragndrop.asciidoc')
-rw-r--r-- | documentation/advanced/advanced-dragndrop.asciidoc | 567 |
1 files changed, 0 insertions, 567 deletions
diff --git a/documentation/advanced/advanced-dragndrop.asciidoc b/documentation/advanced/advanced-dragndrop.asciidoc deleted file mode 100644 index 69335c6369..0000000000 --- a/documentation/advanced/advanced-dragndrop.asciidoc +++ /dev/null @@ -1,567 +0,0 @@ ---- -title: Drag and Drop -order: 12 -layout: page ---- - -[[advanced.dragndrop]] -= Drag and Drop - -((("Drag and Drop", id="term.advanced.dragndrop", range="startofrange"))) - - -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 [classname]#Table# and nodes in [classname]#Tree# -components, either within or between the components. You can also drag entire -components by wrapping them inside [classname]#DragAndDropWrapper#. - -Dragging starts from a __drag source__, which defines the transferable. -Transferables implement the [classname]#Transferable# interfaces. For trees and -tables, which are bound to [classname]#Container# data sources, a node or row -transferable is a reference to an [classname]#Item# in the Vaadin Data Model. -Dragged components are referenced with a [classname]#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 -[classname]#DropTarget# interface. Three components implement the interface: -[classname]#Tree#, [classname]#Table#, and [classname]#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 [methodname]#getAcceptCriterion()# and -[methodname]#drop()# methods in the [classname]#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. - -[[advanced.dragndrop.drophandler]] -== Handling Drops - -Most of the user-defined drag and drop logic occurs in a __drop handler__, which -is provided by implementing the [methodname]#drop()# method in the -[classname]#DropHandler# interface. A closely related definition is the drop -accept criterion, which is defined in the [methodname]#getAcceptCriterion()# -method in the same interface. It is described in -<<advanced.dragndrop.acceptcriteria>> later. - -The [methodname]#drop()# method gets a [classname]#DragAndDropEvent# as its -parameters. The event object provides references to two important object: -[classname]#Transferable# and [classname]#TargetDetails#. - -A [classname]#Transferable# contains a reference to the object (component or -data item) that is being dragged. A tree or table item is represented as a -[classname]#TreeTransferable# or [classname]#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 -[classname]#DataBoundTransferable#. Dragged components are represented as -[classname]#WrapperTransferable# objects, as the components are wrapped in a -[classname]#DragAndDropWrapper#. - -The [classname]#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 [classname]#AbstractSelectTargetDetails# -object tells the item on which the drop is being made. For trees, the -[classname]#TreeTargetDetails# gives some more details. For wrapped components, -the information is provided in a [classname]#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 -[methodname]#getDropLocation()# and for wrapped components with -[methodname]#verticalDropLocation()# and [methodname]#horizontalDropLocation()#. -The locations are specified as either [classname]#VerticalDropLocation# or -[classname]#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 [classname]#Tree#, [classname]#Table#, and a wrapped component is -explained further in the following sections. - - -[[advanced.dragndrop.treedrop]] -== Dropping Items On a [classname]#Tree# - -You can drag items from, to, or within a [classname]#Tree#. Making tree a drag -source requires simply setting the drag mode with [methodname]#setDragMode()#. -[classname]#Tree# currently supports only one drag mode, -[literal]#++TreeDragMode.NODE++#, which allows dragging single tree nodes. While -dragging, the dragged node is referenced with a [classname]#TreeTransferable# -object, which is a [classname]#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 -[classname]#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 -[methodname]#getItemIdOver()# method in -[classname]#AbstractSelectTargetDetails#, which the -[classname]#TreeTargetDetails# inherits. A drop can occur directly on or above -or below a node; the exact location is a [classname]#VerticalDropLocation#, -which you can get with the [methodname]#getDropLocation()# method. - -In the example below, we have a [classname]#Tree# and we allow reordering the -tree items by drag and drop. - - -[source, java] ----- -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); - } - } -}); ----- - -[[advanced.dragndrop.treedrop.criteria]] -=== Accept Criteria for Trees - -[classname]#Tree# defines some specialized accept criteria for trees. - -[classname]#TargetInSubtree#(client-side):: Accepts if the target item is in the specified sub-tree. The sub-tree is specified by the item ID of the root of the sub-tree in the constructor. The second constructor includes a depth parameter, which specifies how deep from the given root node are drops accepted. Value [literal]#++-1++# means infinite, that is, the entire sub-tree, and is therefore the same as the simpler constructor. -[classname]#TargetItemAllowsChildren#(client-side):: Accepts a drop if the tree has [methodname]#setChildrenAllowed()# enabled for the target item. The criterion does not require parameters, so the class is a singleton and can be acquired with [methodname]#Tree.TargetItemAllowsChildren.get()#. For example, the following composite criterion accepts drops only on nodes that allow children, but between all nodes: -+ -[source, java] ----- -return new Or (Tree.TargetItemAllowsChildren.get(), new Not(VerticalLocationIs.MIDDLE)); ----- - -[classname]#TreeDropCriterion#(server-side):: Accepts drops on only some items, which as specified by a set of item IDs. You must extend the abstract class and implement the [methodname]#getAllowedItemIds()# to return the set. While the criterion is server-side, it is lazy-loading, so that the list of accepted target nodes is loaded only once from the server for each drag operation. See <<advanced.dragndrop.acceptcriteria>> for an example. - - -In addition, the accept criteria defined in [classname]#AbstractSelect# are -available for a [classname]#Tree#, as listed in -<<advanced.dragndrop.acceptcriteria>>. - - - -[[advanced.dragndrop.tabledrop]] -== Dropping Items On a [classname]#Table# - -You can drag items from, to, or within a [classname]#Table#. Making table a drag -source requires simply setting the drag mode with [methodname]#setDragMode()#. -[classname]#Table# supports dragging both single rows, with -[literal]#++TableDragMode.ROW++#, and multiple rows, with -[literal]#++TableDragMode.MULTIROW++#. While dragging, the dragged node or nodes -are referenced with a [classname]#TreeTransferable# object, which is a -[classname]#DataBoundTransferable#. Tree nodes are identified by the item IDs of -the container items. - -When a transferable is dropped on a table, the drop location is stored in a -[classname]#AbstractSelectTargetDetails# object, which identifies the target row -by its item ID. You can get the item ID with [methodname]#getItemIdOver()# -method. A drop can occur directly on or above or below a row; the exact location -is a [classname]#VerticalDropLocation#, which you can get with the -[methodname]#getDropLocation()# method from the details object. - -[[advanced.dragndrop.tabledrop.criteria]] -=== Accept Criteria for Tables - -[classname]#Table# defines one specialized accept criterion for tables. - -[classname]#TableDropCriterion#(server-side):: Accepts drops only on (or above or below) items that are specified by a set of item IDs. You must extend the abstract class and implement the [methodname]#getAllowedItemIds()# to return the set. While the criterion is server-side, it is lazy-loading, so that the list of accepted target items is loaded only once from the server for each drag operation. - - - - -[[advanced.dragndrop.acceptcriteria]] -== Accepting Drops - -((("Drag and Drop", "Accept Criteria", id="term.advanced.dragndrop.acceptcriteria", range="startofrange"))) - - -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 -[classname]#getAcceptCriterion()# method of the [classname]#DropHandler# -interface. A criterion is represented in an [classname]#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 [methodname]#AcceptAll.get()#. - - -[source, java] ----- -tree.setDropHandler(new DropHandler() { - public AcceptCriterion getAcceptCriterion() { - return AcceptAll.get(); - } - ... ----- - -[[advanced.dragndrop.acceptcriteria.client-side]] -=== Client-Side Criteria - -The __client-side criteria__, which inherit the -[classname]#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 -[package]#com.vaadin.event.dd.acceptcriterion#: - -[classname]#AcceptAll#:: Accepts all transferables and targets. -[classname]#And#:: Performs the logical AND operation on two or more client-side criteria; accepts the transferable if all the given sub-criteria accept it. -[classname]#ContainsDataFlavour#:: The transferable must contain the defined data flavour. -[classname]#Not#:: Performs the logical NOT operation on a client-side criterion; accepts the transferable if and only if the sub-criterion does not accept it. -[classname]#Or#:: Performs the logical OR operation on two or more client-side criteria; accepts the transferable if any of the given sub-criteria accepts it. -[classname]#SourceIs#:: Accepts all transferables from any of the given source components -[classname]#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. -[classname]#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 [classname]#Tree# and [classname]#Table# -define some component-specific client-side accept criteria. See -<<advanced.dragndrop.treedrop>> for more details. - -[classname]#AbstractSelect# defines the following criteria for all selection -components, including [classname]#Tree# and [classname]#Table#. - -[classname]#AcceptItem#:: Accepts only specific items from a specific selection component. The selection component, which must inherit [classname]#AbstractSelect#, is given as the first parameter for the constructor. It is followed by a list of allowed item identifiers in the drag source. -[classname]#AcceptItem.ALL#:: Accepts all transferables as long as they are items. -[classname]#TargetItemIs#:: Accepts all drops on the specified target items. The constructor requires the target component ( [classname]#AbstractSelect#) followed by a list of allowed item identifiers. -[classname]#VerticalLocationIs.MIDDLE#,[classname]#TOP#, and[classname]#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: -[source, java] ----- -public AcceptCriterion getAcceptCriterion() { - return new Not(VerticalLocationIs.MIDDLE); -} ----- - - - - - -[[advanced.dragndrop.acceptcriteria.server-side]] -=== Server-Side Criteria - -The __server-side criteria__ are verified on the server-side with the -[methodname]#accept()# method of the [classname]#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 -[classname]#TableDropCriterion# and [classname]#TreeDropCriterion#. They do the -server visit once for each drag and drop operation and return all accepted rows -or nodes for current [classname]#Transferable# at once. - -The [methodname]#accept()# method gets the drag event as a parameter so it can -perform its logic much like in [methodname]#drop()#. - - -[source, java] ----- -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 [classname]#ServerSideCriterion# provides a -generic [methodname]#accept()# method. The more specific -[classname]#TableDropCriterion# and [classname]#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. - - -[source, java] ----- -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; -} ----- - - -[[advanced.dragndrop.acceptcriteria.indicators]] -=== Accept Indicators - -When a dragged object hovers on a drop target, an __accept indicator__ is -displayed to show whether or not the location is accepted. For -[parameter]#MIDDLE# location, the indicator is a box around the target (tree -node, table row, or component). For vertical drop locations, the accepted -locations are shown as horizontal lines, and for horizontal drop locations as -vertical lines. - -For [classname]#DragAndDropWrapper# drop targets, you can disable the accept -indicators or __drag hints__ with the [parameter]#no-vertical-drag-hints#, -[parameter]#no-horizontal-drag-hints#, and [parameter]#no-box-drag-hints# -styles. You need to add the styles to the __layout that contains__ the wrapper, -not to the wrapper itself. - - -[source, java] ----- -// Have a wrapper -DragAndDropWrapper wrapper = new DragAndDropWrapper(c); -layout.addComponent(wrapper); - -// Disable the hints -layout.addStyleName("no-vertical-drag-hints"); -layout.addStyleName("no-horizontal-drag-hints"); -layout.addStyleName("no-box-drag-hints"); ----- - - -(((range="endofrange", startref="term.advanced.dragndrop.acceptcriteria"))) - -[[advanced.dragndrop.dragging]] -== Dragging Components - -Dragging a component requires wrapping the source component within a -[classname]#DragAndDropWrapper#. You can then allow dragging by putting the -wrapper (and the component) in drag mode with [methodname]#setDragStartMode()#. -The method supports two drag modes: [parameter]#DragStartMode.WRAPPER# and -[parameter]#DragStartMode.COMPONENT#, which defines whether the entire wrapper -is shown as the drag image while dragging or just the wrapped component. - - -[source, java] ----- -// 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 [classname]#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 [methodname]#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 [classname]#WrapperTransferable#. You -can get the reference to the dragged component with -[methodname]#getDraggedComponent()#. The method will return [literal]#++null++# -if the transferable is not a component. Also HTML 5 drags (see later) are held -in wrapper transferables. - - -[[advanced.dragndrop.drop-on-component]] -== Dropping on a Component - -Drops on a component are enabled by wrapping the component in a -[classname]#DragAndDropWrapper#. The wrapper is an ordinary component; the -constructor takes the wrapped component as a parameter. You just need to define -the [classname]#DropHandler# for the wrapper with -[methodname]#setDropHandler()#. - -In the following example, we allow moving components in an absolute layout. -Details on the drop handler are given later. - - -[source, java] ----- -// 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) { - ... - } -}); ----- - -[[advanced.dragndrop.drop-on-component.details]] -=== Target Details for Wrapped Components - -The drop handler receives the drop target details in a -[classname]#WrapperTargetDetails# object, which implements the -[classname]#TargetDetails# interface. - - -[source, java] ----- -public void drop(DragAndDropEvent event) { - WrapperTransferable t = - (WrapperTransferable) event.getTransferable(); - WrapperTargetDetails details = - (WrapperTargetDetails) event.getTargetDetails(); ----- - -The wrapper target details include a [classname]#MouseEventDetails# object, -which you can get with [methodname]#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 [classname]#WrapperTransferable#) with -[methodname]#getMouseDownEvent()#. - - -[source, java] ----- -// 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 -[methodname]#getAbsoluteLeft()# and [methodname]#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 [methodname]#verticalDropLocation()# and -[methodname]#horizontalDropLocation()# return the more detailed drop location in -the target. - - - -[[advanced.dragndrop.external]] -== Dragging Files from Outside the Browser - -The [classname]#DragAndDropWrapper# allows dragging files from outside the -browser and dropping them on a component wrapped in the wrapper. Dropped files -are automatically uploaded to the application and can be acquired from the -wrapper with [methodname]#getFiles()#. The files are represented as -[classname]#Html5File# objects as defined in the inner class. You can define an -upload [classname]#Receiver# to receive the content of a file to an -[classname]#OutputStream#. - -Dragging and dropping files to browser is supported in HTML 5 and requires a -compatible browser, such as Mozilla Firefox 3.6 or newer. - - -(((range="endofrange", startref="term.advanced.dragndrop"))) - - |