]> source.dussan.org Git - vaadin-framework.git/commitdiff
Customizable DnD preview handle strategy in Dnd manager (#10377,#14880).
authorDenis Anisimov <denis@vaadin.com>
Sun, 26 Oct 2014 15:30:34 +0000 (17:30 +0200)
committerMarkus Koivisto <markus@vaadin.com>
Wed, 15 Apr 2015 08:51:08 +0000 (11:51 +0300)
Change-Id: Idead68d8274de92b9ba7d7f8d71e669244a017fa

client/src/com/vaadin/client/ui/dd/DDEventHandleStrategy.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java

diff --git a/client/src/com/vaadin/client/ui/dd/DDEventHandleStrategy.java b/client/src/com/vaadin/client/ui/dd/DDEventHandleStrategy.java
new file mode 100644 (file)
index 0000000..9f8714c
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.dd;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.vaadin.client.WidgetUtil;
+import com.vaadin.client.ui.dd.VDragAndDropManager.DDManagerMediator;
+
+/**
+ * Strategy to handle native preview events for VDragAndDropManager.
+ * 
+ * The strategy could be overridden via GWT Deferred Binding mechanism.
+ * 
+ * @author Vaadin Ltd
+ */
+public class DDEventHandleStrategy {
+
+    /**
+     * Returns {@code true} if {@code event} interrupts Drag and Drop.
+     * 
+     * @param event
+     *            GWT event to handle
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     * @return whether {@code true} interrupts DnD
+     */
+    public boolean isDragInterrupted(NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        int typeInt = event.getTypeInt();
+        if (typeInt == Event.ONKEYDOWN) {
+            int keyCode = event.getNativeEvent().getKeyCode();
+            if (keyCode == KeyCodes.KEY_ESCAPE) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Handles key down {@code event}.
+     * 
+     * Default implementation doesn't do anything.
+     * 
+     * @param event
+     *            key down GWT event
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     */
+    public void handleKeyDownEvent(NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        // no use for handling for any key down event
+    }
+
+    /**
+     * Get target element for {@code event}.
+     * 
+     * @param event
+     *            GWT event to find target
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     * @return target element for {@code event}
+     */
+    public Element getTargetElement(NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        NativeEvent gwtEvent = event.getNativeEvent();
+        Element targetElement;
+        if (WidgetUtil.isTouchEvent(gwtEvent)
+                || mediator.getManager().getDragElement() != null) {
+            int x = WidgetUtil.getTouchOrMouseClientX(gwtEvent);
+            int y = WidgetUtil.getTouchOrMouseClientY(gwtEvent);
+            // Util.browserDebugger();
+            targetElement = WidgetUtil.getElementFromPoint(x, y);
+        } else {
+            Node targetNode = Node.as(gwtEvent.getEventTarget());
+
+            if (Element.is(targetNode)) {
+                targetElement = Element.as(targetNode);
+            } else {
+                targetElement = targetNode.getParentElement();
+            }
+        }
+        return targetElement;
+    }
+
+    /**
+     * Updates drag image DOM element. This method updates drag image position
+     * and adds additional styles. Default implementation hides drag element to
+     * be able to get target element by the point (see
+     * {@link #getTargetElement(NativePreviewEvent, DDManagerMediator)}. Method
+     * {@link #restoreDragImage(String, DDManagerMediator, NativePreviewEvent)}
+     * is used later on to restore the drag element in its state before
+     * temporary update. Returns "display" CSS style property of the original
+     * drag image. This value will be passed to the
+     * {@link #restoreDragImage(String, DDManagerMediator, NativePreviewEvent)}
+     * method.
+     * 
+     * @param event
+     *            GWT event for active DnD operation
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     * @return "display" CSS style property of drag image element to restore it
+     *         later on
+     */
+    public String updateDragImage(NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        VDragAndDropManager manager = mediator.getManager();
+        manager.updateDragImagePosition(event.getNativeEvent(),
+                manager.getDragElement());
+        String display = null;
+        if (manager.getDragElement() != null) {
+            // to detect the "real" target, hide dragelement temporary and
+            // use elementFromPoint
+            display = manager.getDragElement().getStyle().getDisplay();
+            manager.getDragElement().getStyle().setDisplay(Display.NONE);
+        }
+        return display;
+    }
+
+    /**
+     * Restores drag image after temporary update by
+     * {@link #updateDragImage(NativePreviewEvent, DDManagerMediator)}.
+     * 
+     * @param originalImageDisplay
+     *            original "display" CSS style property of drag image element
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     * @param event
+     *            GWT event for active DnD operation
+     */
+    public void restoreDragImage(String originalImageDisplay,
+            DDManagerMediator mediator, NativePreviewEvent event) {
+        VDragAndDropManager manager = mediator.getManager();
+        if (manager.getDragElement() != null) {
+            manager.getDragElement().getStyle()
+                    .setProperty("display", originalImageDisplay);
+        }
+    }
+
+    /**
+     * Handles event when drag image element (
+     * {@link VDragAndDropManager#getDragElement()} return value) is not null or
+     * {@code event} is touch event.
+     * 
+     * If method returns {@code true} then event processing will be stoped.
+     * 
+     * @param target
+     *            target element over which DnD event has happened
+     * @param event
+     *            GWT event for active DnD operation
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     * @return {@code true} is strategy handled the event and no further steps
+     *         to handle required.
+     */
+    public boolean handleDragImageEvent(Element target,
+            NativePreviewEvent event, DDManagerMediator mediator) {
+        VDragAndDropManager manager = mediator.getManager();
+
+        // ApplicationConnection.getConsole().log(
+        // "Event on dragImage, target changed");
+        // special handling for events over dragImage
+        // pretty much all events are mousemove althout below
+        // kind of happens mouseover
+        switch (event.getTypeInt()) {
+        case Event.ONMOUSEOVER:
+        case Event.ONMOUSEOUT:
+            // ApplicationConnection
+            // .getConsole()
+            // .log(
+            // "IGNORING proxy image event, fired because of hack or not significant");
+            return true;
+        case Event.ONMOUSEMOVE:
+        case Event.ONTOUCHMOVE:
+            VDropHandler findDragTarget = findDragTarget(target, mediator);
+            if (findDragTarget != manager.getCurrentDropHandler()) {
+                // dragleave on old
+                handleDragLeave(mediator, true);
+                // dragenter on new
+                manager.setCurrentDropHandler(findDragTarget);
+
+                handleDragEnter(target, mediator);
+            } else if (findDragTarget != null) {
+                handleDragOver(target, mediator);
+            }
+            // prevent text selection on IE
+            event.getNativeEvent().preventDefault();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Handles drag enter on new element.
+     * 
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     * @param target
+     *            target element over which DnD event has happened
+     */
+    protected void handleDragEnter(Element target, DDManagerMediator mediator) {
+        VDragAndDropManager manager = mediator.getManager();
+        if (manager.getCurrentDropHandler() != null) {
+            mediator.getDragEvent().setElementOver(target);
+            manager.getCurrentDropHandler().dragEnter(mediator.getDragEvent());
+        }
+    }
+
+    /**
+     * Handles drag over on element.
+     * 
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     * @param target
+     *            target element over which DnD event has happened
+     */
+    protected void handleDragOver(Element target, DDManagerMediator mediator) {
+        mediator.getDragEvent().setElementOver(target);
+        mediator.getManager().getCurrentDropHandler()
+                .dragOver(mediator.getDragEvent());
+    }
+
+    /**
+     * Final phase of event handling.
+     * 
+     * @param targetElement
+     *            target element over which DnD event has happened
+     * @param event
+     *            GWT event for active DnD operation
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     */
+    public void handleEvent(Element targetElement, NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        switch (event.getTypeInt()) {
+        case Event.ONMOUSEOVER:
+            handleMouseOver(targetElement, event, mediator);
+            break;
+        case Event.ONMOUSEOUT:
+            handleMouseOut(targetElement, event, mediator);
+            break;
+        case Event.ONMOUSEMOVE:
+        case Event.ONTOUCHMOVE:
+            handleMouseMove(targetElement, event, mediator);
+            break;
+        case Event.ONTOUCHEND:
+            handleTouchEnd(targetElement, event, mediator);
+            break;
+        case Event.ONMOUSEUP:
+            handleMouseUp(targetElement, event, mediator);
+            break;
+        }
+    }
+
+    /**
+     * Called to handle {@link Event#ONMOUSEMOVE} event.
+     * 
+     * @param target
+     *            target element over which DnD event has happened
+     * @param event
+     *            ONMOUSEMOVE GWT event for active DnD operation
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     */
+    protected void handleMouseMove(Element target, NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        VDragAndDropManager manager = mediator.getManager();
+        if (manager.getCurrentDropHandler() != null) {
+            handleDragOver(target, mediator);
+        }
+        event.getNativeEvent().preventDefault();
+    }
+
+    /**
+     * Called to handle {@link Event#ONTOUCHEND} event.
+     * 
+     * @param target
+     *            target element over which DnD event has happened
+     * @param event
+     *            ONTOUCHEND GWT event for active DnD operation
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     */
+    protected void handleTouchEnd(Element target, NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        /* Avoid simulated event on drag end */
+        event.getNativeEvent().preventDefault();
+        handleMouseUp(target, event, mediator);
+    }
+
+    /**
+     * Called to handle {@link Event#ONMOUSEUP} event.
+     * 
+     * @param target
+     *            target element over which DnD event has happened
+     * @param event
+     *            ONMOUSEUP GWT event for active DnD operation
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     */
+    protected void handleMouseUp(Element target, NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        mediator.getManager().endDrag();
+    }
+
+    /**
+     * Called to handle {@link Event#ONMOUSEOUT} event.
+     * 
+     * @param target
+     *            target element over which DnD event has happened
+     * @param event
+     *            ONMOUSEOUT GWT event for active DnD operation
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     */
+    protected void handleMouseOut(Element target, NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        VDragAndDropManager manager = mediator.getManager();
+        Element relatedTarget = Element.as(event.getNativeEvent()
+                .getRelatedEventTarget());
+        VDropHandler newDragHanler = findDragTarget(relatedTarget, mediator);
+        if (manager.getDragElement() != null
+                && manager.getDragElement().isOrHasChild(relatedTarget)) {
+            // ApplicationConnection.getConsole().log(
+            // "Mouse out of dragImage, ignored");
+            return;
+        }
+
+        if (manager.getCurrentDropHandler() != newDragHanler) {
+            handleDragLeave(mediator, true);
+            manager.setCurrentDropHandler(null);
+        }
+    }
+
+    /**
+     * Handles drag leave on old element.
+     * 
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     */
+    protected void handleDragLeave(DDManagerMediator mediator,
+            boolean clearServerCallback) {
+        VDragAndDropManager manager = mediator.getManager();
+        if (manager.getCurrentDropHandler() != null) {
+            manager.getCurrentDropHandler().dragLeave(mediator.getDragEvent());
+            mediator.getDragEvent().getDropDetails().clear();
+            if (clearServerCallback) {
+                mediator.clearServerCallback();
+            }
+        }
+    }
+
+    /**
+     * Called to handle {@link Event#ONMOUSEOVER} event.
+     * 
+     * @param target
+     *            target element over which DnD event has happened
+     * @param event
+     *            ONMOUSEOVER GWT event for active DnD operation
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     */
+    protected void handleMouseOver(Element target, NativePreviewEvent event,
+            DDManagerMediator mediator) {
+        VDragAndDropManager manager = mediator.getManager();
+        VDropHandler dragHandler = findDragTarget(target, mediator);
+
+        if (dragHandler != null
+                && dragHandler != manager.getCurrentDropHandler()) {
+            handleDragLeave(mediator, false);
+
+            manager.setCurrentDropHandler(dragHandler);
+            // ApplicationConnection.getConsole().log(
+            // "DropHandler now"
+            // + currentDropHandler.getPaintable());
+            handleDragEnter(target, mediator);
+        } else if (dragHandler == null
+                && manager.getCurrentDropHandler() != null) {
+            // ApplicationConnection.getConsole().log("Invalid state!?");
+            handleDragLeave(mediator, false);
+            manager.setCurrentDropHandler(null);
+        }
+    }
+
+    /**
+     * Find drag handler for the {@code target} element.
+     * 
+     * @param target
+     *            target element over which DnD event has happened
+     * @param mediator
+     *            VDragAndDropManager data accessor
+     * @return drop handler of target element
+     */
+    protected VDropHandler findDragTarget(Element target,
+            DDManagerMediator mediator) {
+        return mediator.getManager().findDragTarget(target);
+    }
+
+}
index 47f8eb1b66473414bf2087ba378ba93de66ebc5a..19004cfaa987614d6f1a7900a72a56d4bcf0b433 100644 (file)
@@ -23,9 +23,7 @@ import com.google.gwt.dom.client.EventTarget;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.dom.client.Node;
 import com.google.gwt.dom.client.Style;
-import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.Event;
@@ -61,183 +59,98 @@ public class VDragAndDropManager {
 
     public static final String ACTIVE_DRAG_SOURCE_STYLENAME = "v-active-drag-source";
 
+    /**
+     * Implementation if this interface is provided as a parameter to
+     * DDEventHandleStrategy methods. The mediator instance allows to manage
+     * DnD.
+     */
+    public interface DDManagerMediator {
+        /**
+         * Returns DnD manager instance.
+         */
+        VDragAndDropManager getManager();
+
+        /**
+         * Returns current drag event.
+         */
+        VDragEvent getDragEvent();
+
+        /**
+         * Clean up server communication callback.
+         */
+        void clearServerCallback();
+    }
+
     private final class DefaultDragAndDropEventHandler implements
             NativePreviewHandler {
 
         @Override
         public void onPreviewNativeEvent(NativePreviewEvent event) {
-            NativeEvent nativeEvent = event.getNativeEvent();
+            if (getEventHandleStrategy().isDragInterrupted(event,
+                    managerMediator)) {
+                // end drag if ESC is hit
+                interruptDrag();
+                event.cancel();
+                event.getNativeEvent().preventDefault();
+                return;
+            }
 
             int typeInt = event.getTypeInt();
             if (typeInt == Event.ONKEYDOWN) {
-                int keyCode = event.getNativeEvent().getKeyCode();
-                if (keyCode == KeyCodes.KEY_ESCAPE) {
-                    // end drag if ESC is hit
-                    interruptDrag();
-                    event.cancel();
-                    event.getNativeEvent().preventDefault();
-                }
-                // no use for handling for any key down event
+                getEventHandleStrategy().handleKeyDownEvent(event,
+                        managerMediator);
                 return;
             }
 
+            NativeEvent nativeEvent = event.getNativeEvent();
             currentDrag.setCurrentGwtEvent(nativeEvent);
-            updateDragImagePosition();
-
-            Node targetNode = Node.as(nativeEvent.getEventTarget());
-            Element targetElement;
-            if (Element.is(targetNode)) {
-                targetElement = Element.as(targetNode);
-            } else {
-                targetElement = targetNode.getParentElement();
-            }
-
-            if (WidgetUtil.isTouchEvent(nativeEvent) || dragElement != null) {
-                // to detect the "real" target, hide dragelement temporary and
-                // use elementFromPoint
-                String display = dragElement.getStyle().getDisplay();
-                dragElement.getStyle().setDisplay(Display.NONE);
-                try {
-                    int x = WidgetUtil.getTouchOrMouseClientX(nativeEvent);
-                    int y = WidgetUtil.getTouchOrMouseClientY(nativeEvent);
-                    // Util.browserDebugger();
-                    targetElement = WidgetUtil.getElementFromPoint(x, y);
-                    if (targetElement == null) {
-                        // ApplicationConnection.getConsole().log(
-                        // "Event on dragImage, ignored");
-                        event.cancel();
-                        nativeEvent.stopPropagation();
-                        return;
-
-                    } else {
-                        // ApplicationConnection.getConsole().log(
-                        // "Event on dragImage, target changed");
-                        // special handling for events over dragImage
-                        // pretty much all events are mousemove althout below
-                        // kind of happens mouseover
-                        switch (typeInt) {
-                        case Event.ONMOUSEOVER:
-                        case Event.ONMOUSEOUT:
-                            // ApplicationConnection
-                            // .getConsole()
-                            // .log(
-                            // "IGNORING proxy image event, fired because of hack or not significant");
-                            return;
-                        case Event.ONMOUSEMOVE:
-                        case Event.ONTOUCHMOVE:
-                            VDropHandler findDragTarget = findDragTarget(targetElement);
-                            if (findDragTarget != currentDropHandler) {
-                                // dragleave on old
-                                if (currentDropHandler != null) {
-                                    currentDropHandler.dragLeave(currentDrag);
-                                    currentDrag.getDropDetails().clear();
-                                    serverCallback = null;
-                                }
-                                // dragenter on new
-                                currentDropHandler = findDragTarget;
-                                if (findDragTarget != null) {
-                                    // ApplicationConnection.getConsole().log(
-                                    // "DropHandler now"
-                                    // + currentDropHandler
-                                    // .getPaintable());
-                                }
-
-                                if (currentDropHandler != null) {
-                                    currentDrag.setElementOver(targetElement);
-                                    currentDropHandler.dragEnter(currentDrag);
-                                }
-                            } else if (findDragTarget != null) {
-                                currentDrag.setElementOver(targetElement);
-                                currentDropHandler.dragOver(currentDrag);
-                            }
-                            // prevent text selection on IE
-                            nativeEvent.preventDefault();
-                            return;
-                        default:
-                            // just update element over and let the actual
-                            // handling code do the thing
-                            // ApplicationConnection.getConsole().log(
-                            // "Target just modified on "
-                            // + event.getType());
-                            currentDrag.setElementOver(targetElement);
-                            break;
-                        }
-
-                    }
-                } catch (RuntimeException e) {
-                    // ApplicationConnection.getConsole().log(
-                    // "ERROR during elementFromPoint hack.");
-                    throw e;
-                } finally {
-                    dragElement.getStyle().setProperty("display", display);
-                }
-            }
 
-            switch (typeInt) {
-            case Event.ONMOUSEOVER:
-                VDropHandler target = findDragTarget(targetElement);
+            String display = getEventHandleStrategy().updateDragImage(event,
+                    managerMediator);
 
-                if (target != null && target != currentDropHandler) {
-                    if (currentDropHandler != null) {
-                        currentDropHandler.dragLeave(currentDrag);
-                        currentDrag.getDropDetails().clear();
-                    }
+            Element targetElement = getEventHandleStrategy().getTargetElement(
+                    event, managerMediator);
 
-                    currentDropHandler = target;
-                    // ApplicationConnection.getConsole().log(
-                    // "DropHandler now"
-                    // + currentDropHandler.getPaintable());
-                    currentDrag.setElementOver(targetElement);
-                    target.dragEnter(currentDrag);
-                } else if (target == null && currentDropHandler != null) {
-                    // ApplicationConnection.getConsole().log("Invalid state!?");
-                    currentDropHandler.dragLeave(currentDrag);
-                    currentDrag.getDropDetails().clear();
-                    currentDropHandler = null;
-                }
-                break;
-            case Event.ONMOUSEOUT:
-                Element relatedTarget = Element.as(nativeEvent
-                        .getRelatedEventTarget());
-                VDropHandler newDragHanler = findDragTarget(relatedTarget);
-                if (dragElement != null
-                        && dragElement.isOrHasChild(relatedTarget)) {
-                    // ApplicationConnection.getConsole().log(
-                    // "Mouse out of dragImage, ignored");
+            try {
+                if (handleDragImage(targetElement, event)) {
                     return;
                 }
-
-                if (currentDropHandler != null
-                        && currentDropHandler != newDragHanler) {
-                    currentDropHandler.dragLeave(currentDrag);
-                    currentDrag.getDropDetails().clear();
-                    currentDropHandler = null;
-                    serverCallback = null;
-                }
-                break;
-            case Event.ONMOUSEMOVE:
-            case Event.ONTOUCHMOVE:
-                if (currentDropHandler != null) {
-                    currentDrag.setElementOver(targetElement);
-                    currentDropHandler.dragOver(currentDrag);
-                }
-                nativeEvent.preventDefault();
-
-                break;
-
-            case Event.ONTOUCHEND:
-                /* Avoid simulated event on drag end */
-                event.getNativeEvent().preventDefault();
-            case Event.ONMOUSEUP:
-                endDrag();
-                break;
-
-            default:
-                break;
+            } catch (RuntimeException e) {
+                // ApplicationConnection.getConsole().log(
+                // "ERROR during elementFromPoint hack.");
+                throw e;
+            } finally {
+                getEventHandleStrategy().restoreDragImage(display,
+                        managerMediator, event);
             }
 
+            getEventHandleStrategy().handleEvent(targetElement, event,
+                    managerMediator);
         }
 
+        private boolean handleDragImage(Element target, NativePreviewEvent event) {
+            if (!WidgetUtil.isTouchEvent(event.getNativeEvent())
+                    && getDragElement() == null) {
+                return false;
+            } else if (target == null) {
+                // ApplicationConnection.getConsole().log(
+                // "Event on dragImage, ignored");
+                event.cancel();
+                event.getNativeEvent().stopPropagation();
+                return true;
+            } else if (getEventHandleStrategy().handleDragImageEvent(target,
+                    event, managerMediator)) {
+                return true;
+            } else {
+                // just update element over and let the actual
+                // handling code do the thing
+                // ApplicationConnection.getConsole().log(
+                // "Target just modified on "
+                // + event.getType());
+                currentDrag.setElementOver(target);
+                return false;
+            }
+        }
     }
 
     /*
@@ -250,6 +163,26 @@ public class VDragAndDropManager {
     private HandlerRegistration handlerRegistration;
     private VDragEvent currentDrag;
 
+    private DDManagerMediator managerMediator = new DDManagerMediator() {
+
+        @Override
+        public VDragAndDropManager getManager() {
+            return VDragAndDropManager.this;
+        }
+
+        @Override
+        public VDragEvent getDragEvent() {
+            return currentDrag;
+        }
+
+        @Override
+        public void clearServerCallback() {
+            serverCallback = null;
+        }
+    };
+
+    private DDEventHandleStrategy eventHandleStrategy;
+
     /**
      * If dragging is currently on a drophandler, this field has reference to it
      */
@@ -456,13 +389,12 @@ public class VDragAndDropManager {
         return currentDrag;
     }
 
-    private void updateDragImagePosition() {
-        if (currentDrag.getCurrentGwtEvent() != null && dragElement != null) {
-            Style style = dragElement.getStyle();
-            int clientY = WidgetUtil.getTouchOrMouseClientY(currentDrag
-                    .getCurrentGwtEvent());
-            int clientX = WidgetUtil.getTouchOrMouseClientX(currentDrag
-                    .getCurrentGwtEvent());
+    protected void updateDragImagePosition(NativeEvent gwtEvent,
+            Element dragImage) {
+        if (gwtEvent != null && dragImage != null) {
+            Style style = dragImage.getStyle();
+            int clientY = WidgetUtil.getTouchOrMouseClientY(gwtEvent);
+            int clientX = WidgetUtil.getTouchOrMouseClientX(gwtEvent);
             style.setTop(clientY, Unit.PX);
             style.setLeft(clientX, Unit.PX);
         }
@@ -475,7 +407,7 @@ public class VDragAndDropManager {
      * @param element
      * @return
      */
-    private VDropHandler findDragTarget(Element element) {
+    protected VDropHandler findDragTarget(Element element) {
         try {
             Widget w = WidgetUtil.findWidget(element, null);
             if (w == null) {
@@ -565,8 +497,8 @@ public class VDragAndDropManager {
 
                 }
             } else {
-                currentDrag.setCurrentGwtEvent(null);
                 currentDropHandler.dragLeave(currentDrag);
+                currentDrag.setCurrentGwtEvent(null);
             }
             currentDropHandler = null;
             serverCallback = null;
@@ -711,6 +643,21 @@ public class VDragAndDropManager {
         Profiler.leave("VDragAndDropManager.handleServerResponse");
     }
 
+    /**
+     * Returns DnD strategy to handle native preview events used by the manager.
+     * 
+     * Subclasses can override this method to return custom strategy or use GWT
+     * deferred binding.
+     * 
+     * @return internal DnD native preview event handler
+     */
+    protected DDEventHandleStrategy getEventHandleStrategy() {
+        if (eventHandleStrategy == null) {
+            eventHandleStrategy = GWT.create(DDEventHandleStrategy.class);
+        }
+        return eventHandleStrategy;
+    }
+
     private void runDeferredCommands() {
         if (deferredCommand != null) {
             Command command = deferredCommand;
@@ -732,7 +679,8 @@ public class VDragAndDropManager {
 
             dragElement = node;
             dragElement.addClassName("v-drag-element");
-            updateDragImagePosition();
+            updateDragImagePosition(currentDrag.getCurrentGwtEvent(),
+                    dragElement);
 
             if (isStarted) {
                 attachDragElement();