diff options
author | Pekka Hyvönen <pekka@vaadin.com> | 2017-05-11 14:36:27 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-11 14:36:27 +0300 |
commit | f72ac12fd257e218c370a4d686be4cc99c2a22d6 (patch) | |
tree | df7937c1835dcd6517717332147cae383a27c05c /client | |
parent | e2e3058a497f43f34f2fcfadf6b63de9211be659 (diff) | |
download | vaadin-framework-f72ac12fd257e218c370a4d686be4cc99c2a22d6.tar.gz vaadin-framework-f72ac12fd257e218c370a4d686be4cc99c2a22d6.zip |
Add mobile html5 dnd support using polyfill (#9282)
First step of mobile DND support.
- Add mobile html5 dnd support using polyfill
- Adds a switch for enabling mobile html5 dnd support
- Adds polyfill only when needed
- Ignore native Android Chrome drag start because doesn't work properly
(no dragend event fired)
- Add documentation on enabling mobile HTML5 DnD support
- Add mention of drag-drop-polyfill license
- Fixed issue in polyfill when not using "snapback"
- Add mention of forked polyfill
Fixes #9174
Diffstat (limited to 'client')
4 files changed, 108 insertions, 9 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java index 134e76e7e5..64e9c2e7b5 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java @@ -74,8 +74,9 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector { protected void extend(ServerConnector target) { gridConnector = (GridConnector) target; - // Do not make elements draggable on touch devices - if (BrowserInfo.get().isTouchDevice()) { + // HTML5 DnD is by default not enabled for mobile devices + if (BrowserInfo.get().isTouchDevice() && !getConnection() + .getUIConnector().isMobileHTML5DndEnabled()) { return; } @@ -89,6 +90,15 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector { @Override protected void onDragStart(Event event) { + NativeEvent nativeEvent = (NativeEvent) event; + + // Do not allow drag starts from native Android Chrome, since it doesn't + // work properly (doesn't fire dragend reliably) + if (isAndoidChrome() && isNativeDragEvent(nativeEvent)) { + event.preventDefault(); + event.stopPropagation(); + return; + } // Collect the keys of dragged rows draggedItemKeys = getDraggedRows(event).stream() @@ -195,8 +205,17 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector { @Override protected void onDragEnd(Event event) { + NativeEvent nativeEvent = (NativeEvent) event; + + // for android chrome we use the polyfill, in case browser fires a + // native dragend event after the polyfill, we need to ignore that one + if (isAndoidChrome() && isNativeDragEvent((nativeEvent))) { + event.preventDefault(); + event.stopPropagation(); + return; + } // Ignore event if there are no items dragged - if (draggedItemKeys.size() > 0) { + if (draggedItemKeys != null && draggedItemKeys.size() > 0) { super.onDragEnd(event); } diff --git a/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java index e718fb90d7..9e0a76788d 100644 --- a/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java +++ b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java @@ -67,8 +67,9 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { protected void extend(ServerConnector target) { dragSourceWidget = ((ComponentConnector) target).getWidget(); - // Do not make elements draggable on touch devices - if (BrowserInfo.get().isTouchDevice()) { + // HTML5 DnD is by default not enabled for mobile devices + if (BrowserInfo.get().isTouchDevice() && !getConnection() + .getUIConnector().isMobileHTML5DndEnabled()) { return; } @@ -159,6 +160,14 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { // Convert elemental event to have access to dataTransfer NativeEvent nativeEvent = (NativeEvent) event; + // Do not allow drag starts from native Android Chrome, since it doesn't + // work properly (doesn't fire dragend reliably) + if (isAndoidChrome() && isNativeDragEvent(nativeEvent)) { + event.preventDefault(); + event.stopPropagation(); + return; + } + // Set effectAllowed parameter if (getState().effectAllowed != null) { setEffectAllowed(nativeEvent.getDataTransfer(), @@ -281,11 +290,20 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { * browser event to be handled */ protected void onDragEnd(Event event) { + NativeEvent nativeEvent = (NativeEvent) event; + + // for android chrome we use the polyfill, in case browser fires a + // native dragend event after the polyfill dragend, we need to ignore + // that one + if (isNativeDragEvent((nativeEvent))) { + event.preventDefault(); + event.stopPropagation(); + return; + } // Initiate server start dragend event when there is a DragEndListener // attached on the server side if (hasEventListener(DragSourceState.EVENT_DRAGEND)) { - String dropEffect = getDropEffect( - ((NativeEvent) event).getDataTransfer()); + String dropEffect = getDropEffect(nativeEvent.getDataTransfer()); assert dropEffect != null : "Drop effect should never be null"; @@ -318,6 +336,40 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { return dragSourceWidget.getElement(); } + /** + * Returns whether the given event is a native (android) drag start/end + * event, and not produced by the drag-drop-polyfill. + * + * @param nativeEvent + * the event to test + * @return {@code true} if native event, {@code false} if not (polyfill + * event) + */ + protected boolean isNativeDragEvent(NativeEvent nativeEvent) { + return isTrusted(nativeEvent) || isComposed(nativeEvent); + } + + /** + * Returns whether the current browser is Android Chrome. + * + * @return {@code true} if Android Chrome, {@code false} if not + * + */ + protected boolean isAndoidChrome() { + BrowserInfo browserInfo = BrowserInfo.get(); + return browserInfo.isAndroid() && browserInfo.isChrome(); + } + + private native boolean isTrusted(NativeEvent event) + /*-{ + return event.isTrusted; + }-*/; + + private native boolean isComposed(NativeEvent event) + /*-{ + return event.isComposed; + }-*/; + private native void setEffectAllowed(DataTransfer dataTransfer, String effectAllowed) /*-{ diff --git a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java index 49592488de..acd6d48575 100644 --- a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java +++ b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java @@ -80,8 +80,9 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector { protected void extend(ServerConnector target) { dropTargetWidget = ((ComponentConnector) target).getWidget(); - // Do not make elements drop target on touch devices - if (BrowserInfo.get().isTouchDevice()) { + // HTML5 DnD is by default not enabled for mobile devices + if (BrowserInfo.get().isTouchDevice() && !getConnection() + .getUIConnector().isMobileHTML5DndEnabled()) { return; } diff --git a/client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java b/client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java index 96e7aa1498..49a7e181b2 100644 --- a/client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java @@ -69,6 +69,8 @@ import com.vaadin.client.ValueMap; import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; +import com.vaadin.client.extensions.DragSourceExtensionConnector; +import com.vaadin.client.extensions.DropTargetExtensionConnector; import com.vaadin.client.ui.AbstractConnector; import com.vaadin.client.ui.AbstractSingleComponentContainerConnector; import com.vaadin.client.ui.ClickEventHandler; @@ -165,6 +167,11 @@ public class UIConnector extends AbstractSingleComponentContainerConnector Window.Location.reload(); } + + @Override + public void initializeMobileHtml5DndPolyfill() { + initializeMobileDndPolyfill(); + } }); registerRpc(ScrollClientRpc.class, new ScrollClientRpc() { @Override @@ -1160,6 +1167,20 @@ public class UIConnector extends AbstractSingleComponentContainerConnector return activeTheme; } + /** + * Returns whether HTML5 DnD extensions {@link DragSourceExtensionConnector} + * and {@link DropTargetExtensionConnector} and alike should be enabled for + * mobile devices. + * <p> + * By default, it is disabled. + * + * @return {@code true} if enabled, {@code false} if not + * @since 8.1 + */ + public boolean isMobileHTML5DndEnabled() { + return getState().enableMobileHTML5DnD; + } + private static Logger getLogger() { return Logger.getLogger(UIConnector.class.getName()); } @@ -1209,4 +1230,10 @@ public class UIConnector extends AbstractSingleComponentContainerConnector return result; } } + + // TODO add configuration to use custom drag start decider + private static native void initializeMobileDndPolyfill() + /*-{ + $wnd.DragDropPolyfill.Initialize(); + }-*/; } |