From 19ad2a565fd32fb97023a6df30cb120decec376e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pekka=20Hyv=C3=B6nen?= Date: Wed, 17 May 2017 14:20:26 +0300 Subject: [PATCH] Fix drag image offset for Grid rows for mobile (#9336) Asks the polyfill to always center the drag image based on the touch coordinates. Also temporarely removes the transform offset for the row. --- .../grid/GridDragSourceConnector.java | 43 +++++++++-------- .../DragSourceExtensionConnector.java | 48 +++++++++++++++---- .../com/vaadin/client/ui/ui/UIConnector.java | 6 ++- .../java/com/vaadin/tests/dnd/DragImage.java | 2 + 4 files changed, 68 insertions(+), 31 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 5e83fef651..7f8fb429a2 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 @@ -26,7 +26,8 @@ import java.util.stream.Collectors; import com.google.gwt.animation.client.AnimationScheduler; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; -import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Float; +import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.TableRowElement; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Window; @@ -39,7 +40,6 @@ import com.vaadin.client.widget.escalator.RowContainer; import com.vaadin.client.widget.grid.selection.SelectionModel; import com.vaadin.client.widgets.Escalator; import com.vaadin.client.widgets.Grid; -import com.vaadin.server.SerializableFunction; import com.vaadin.shared.Range; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.dnd.DragSourceState; @@ -150,8 +150,8 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector { protected void setDragImage(NativeEvent dragStartEvent) { // do not call super since need to handle specifically // 1. use resource if set (never needs safari hack) - // 2. add number badge if necessary (with safari hack if needed) - // 3. just use normal (with safari hack if needed) + // 2. add row count badge if necessary + // 3. apply hacks for safari/mobile drag image if needed // Add badge showing the number of dragged columns String imageUrl = getResourceUrl(DragSourceState.RESOURCE_DRAG_IMAGE); @@ -171,13 +171,18 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector { + STYLE_SUFFIX_DRAG_BADGE); badge.setInnerHTML(draggedItemKeys.size() + ""); - badge.getStyle().setMarginLeft( - getRelativeX(draggedRowElement, dragStartEvent) + 10, - Style.Unit.PX); - badge.getStyle().setMarginTop( - getRelativeY(draggedRowElement, dragStartEvent) - - draggedRowElement.getOffsetHeight() + 10, - Style.Unit.PX); + if (BrowserInfo.get().isTouchDevice()) { + // the drag image is centered on the touch coordinates + // -> show the badge on the right edge of the row + badge.getStyle().setFloat(Float.RIGHT); + badge.getStyle().setMarginRight(20, Unit.PX); + } else { + badge.getStyle().setMarginLeft( + getRelativeX(draggedRowElement, dragStartEvent) + + 10, + Unit.PX); + } + badge.getStyle().setMarginTop(-20, Unit.PX); draggedRowElement.appendChild(badge); @@ -187,15 +192,11 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector { badge.removeFromParent(); }, (Element) dragStartEvent.getEventTarget().cast()); } - fixDragImageForSafari(draggedRowElement); + fixDragImageForDesktopSafari(draggedRowElement); + fixDragImageTransformForMobile(draggedRowElement); } } - private int getRelativeY(Element element, NativeEvent event) { - int relativeTop = element.getAbsoluteTop() - Window.getScrollTop(); - return WidgetUtil.getTouchOrMouseClientY(event) - relativeTop; - } - private int getRelativeX(Element element, NativeEvent event) { int relativeLeft = element.getAbsoluteLeft() - Window.getScrollLeft(); return WidgetUtil.getTouchOrMouseClientX(event) - relativeLeft; @@ -204,8 +205,8 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector { @Override protected Map createDataTransferData( NativeEvent dragStartEvent) { - Map dataMap = super - .createDataTransferData(dragStartEvent); + Map dataMap = super.createDataTransferData( + dragStartEvent); // Add data provided by the generator functions getDraggedRows(dragStartEvent).forEach(row -> { @@ -331,9 +332,9 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector { * Gets drag data provided by the generator functions. * * @param row - * The row data. + * The row data. * @return The generated drag data type mapped to the corresponding drag - * data. If there are no generator functions, returns an empty map. + * data. If there are no generator functions, returns an empty map. */ private Map getRowDragData(JsonObject row) { // Collect a map of data types and data that is provided by the 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 7d79fb74dc..61ff4dea8a 100644 --- a/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java +++ b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java @@ -211,20 +211,22 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { } /** - * Fixes missing drag image for Safari by making the dragged element + * Fixes missing drag image for desktop Safari by making the dragged element * position to relative if needed. Safari won't show drag image unless the * dragged element position is relative or absolute / fixed, but not with * display block for the latter. *

- * This method is a NOOP for non-safari browser. + * This method is a NOOP for non-safari browser, or mobile safari which is + * using the DnD Polyfill. *

* This fix is not needed if a custom drag image is used on Safari. * * @param draggedElement * the element that forms the drag image */ - protected void fixDragImageForSafari(Element draggedElement) { - if (!BrowserInfo.get().isSafari()) { + protected void fixDragImageForDesktopSafari(Element draggedElement) { + if (!BrowserInfo.get().isSafari() + || BrowserInfo.get().isTouchDevice()) { return; } final Style style = draggedElement.getStyle(); @@ -252,13 +254,41 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { }, draggedElement); } + /** + * Fix drag image offset for touch devices when the dragged image has been + * offset with css transform: translate/translate3d. + *

+ * This necessary for e.g grid rows. + *

+ * This method is NOOP for non-touch browsers. + * + * @param draggedElement + * the element that forms the drag image + */ + protected void fixDragImageTransformForMobile(Element draggedElement) { + if (!BrowserInfo.get().isTouchDevice()) { + return; + } + + Style style = draggedElement.getStyle(); + String transition = style.getProperty("transform"); + if (transition == null || transition.isEmpty() + || !transition.startsWith("translate")) { + return; + } + style.clearProperty("transform"); + AnimationScheduler.get().requestAnimationFrame(timestamp -> { + draggedElement.getStyle().setProperty("transform", transition); + }, draggedElement); + } + /** * Creates the data map to be set as the {@code DataTransfer} object's data. * * @param dragStartEvent - * The drag start event + * The drag start event * @return The map from type to data, or {@code null} for not setting any - * data. Returning {@code null} will cancel the drag start. + * data. Returning {@code null} will cancel the drag start. */ protected Map createDataTransferData( NativeEvent dragStartEvent) { @@ -299,8 +329,10 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector { dragStartEvent.getDataTransfer() .setDragImage(dragImage.getElement(), 0, 0); } else { - fixDragImageForSafari( - (Element) dragStartEvent.getCurrentEventTarget().cast()); + Element draggedElement = (Element) dragStartEvent + .getCurrentEventTarget().cast(); + fixDragImageForDesktopSafari(draggedElement); + fixDragImageTransformForMobile(draggedElement); } } 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 49a7e181b2..5618cabb19 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 @@ -1231,9 +1231,11 @@ public class UIConnector extends AbstractSingleComponentContainerConnector } } - // TODO add configuration to use custom drag start decider private static native void initializeMobileDndPolyfill() /*-{ - $wnd.DragDropPolyfill.Initialize(); + var conf = new Object(); + // this is needed or the drag image will offset to weird place in for grid rows + conf['dragImageCenterOnTouch'] = true; + $wnd.DragDropPolyfill.Initialize(conf); }-*/; } diff --git a/uitest/src/main/java/com/vaadin/tests/dnd/DragImage.java b/uitest/src/main/java/com/vaadin/tests/dnd/DragImage.java index d64958305f..fcac0b1743 100644 --- a/uitest/src/main/java/com/vaadin/tests/dnd/DragImage.java +++ b/uitest/src/main/java/com/vaadin/tests/dnd/DragImage.java @@ -24,6 +24,8 @@ public class DragImage extends AbstractTestUIWithLog { @Override protected void setup(VaadinRequest request) { + setMobileHtml5DndEnabled(true); + HorizontalLayout layout1 = new HorizontalLayout(); layout1.setCaption("No custom drag image"); Styles styles = Page.getCurrent().getStyles(); -- 2.39.5