summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorPatrik Lindström <patrik@vaadin.com>2015-09-22 12:01:17 +0300
committerJohannes Dahlström <johannesd@vaadin.com>2015-10-07 13:15:38 +0000
commitd1e8d68248dd9ec97378fa9b909e29dde4358073 (patch)
tree4a3cee8071cf97d89833e540e669504dec695917 /client
parentec8f3e9bc6955e9916e297b74a12b4a431b69928 (diff)
downloadvaadin-framework-d1e8d68248dd9ec97378fa9b909e29dde4358073.tar.gz
vaadin-framework-d1e8d68248dd9ec97378fa9b909e29dde4358073.zip
Add handles to Grid default header for column drag resize (#16838)
Change-Id: If8399a13585ae691ea3b3ee4a41a1a38b342adb9
Diffstat (limited to 'client')
-rw-r--r--client/src/com/vaadin/client/ui/dd/DragAndDropHandler.java100
-rw-r--r--client/src/com/vaadin/client/ui/dd/DragHandle.java220
-rw-r--r--client/src/com/vaadin/client/widgets/Escalator.java4
-rw-r--r--client/src/com/vaadin/client/widgets/Grid.java124
4 files changed, 387 insertions, 61 deletions
diff --git a/client/src/com/vaadin/client/ui/dd/DragAndDropHandler.java b/client/src/com/vaadin/client/ui/dd/DragAndDropHandler.java
index e2b51fb72d..63c44f1fd5 100644
--- a/client/src/com/vaadin/client/ui/dd/DragAndDropHandler.java
+++ b/client/src/com/vaadin/client/ui/dd/DragAndDropHandler.java
@@ -21,6 +21,7 @@ import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
+import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.RootPanel;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.widgets.Grid;
@@ -46,20 +47,20 @@ public class DragAndDropHandler {
* Called when the drag has started. The drag can be canceled by
* returning {@code false}.
*
- * @param startEvent
+ * @param e
* the original event that started the drag
* @return {@code true} if the drag is OK to start, {@code false} to
* cancel
*/
- boolean onDragStart(NativeEvent startEvent);
+ boolean onDragStart(Event e);
/**
* Called on drag.
*
- * @param event
+ * @param e
* the event related to the drag
*/
- void onDragUpdate(NativePreviewEvent event);
+ void onDragUpdate(Event e);
/**
* Called after the has ended on a drop or cancel.
@@ -77,45 +78,58 @@ public class DragAndDropHandler {
void onDragCancel();
}
- private HandlerRegistration dragStartNativePreviewHandlerRegistration;
+ private HandlerRegistration startPreviewHandler;
private HandlerRegistration dragHandlerRegistration;
-
+ private DragAndDropCallback callback;
private boolean dragging;
- private DragAndDropCallback callback;
+ // XXX: This is a hack to stop a click event from propagating through to the
+ // client once dragging has completed. In the Grid case, this caused
+ // erroneous selections and/or sorting events.
+ private Timer stopTimer = new Timer() {
+ @Override
+ public void run() {
+ Event.releaseCapture(RootPanel.getBodyElement());
+ if (callback != null) {
+ callback.onDragEnd();
+ callback = null;
+ }
+ if (dragHandlerRegistration != null) {
+ dragHandlerRegistration.removeHandler();
+ dragHandlerRegistration = null;
+ }
+ dragging = false;
+ }
+ };
- private final NativePreviewHandler dragHandler = new NativePreviewHandler() {
+ private final NativePreviewHandler dragPreviewHandler = new NativePreviewHandler() {
@Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
if (dragging) {
final int typeInt = event.getTypeInt();
switch (typeInt) {
+ case Event.ONMOUSEMOVE:
+ case Event.ONTOUCHMOVE:
+ callback.onDragUpdate(Event.as(event.getNativeEvent()));
+ break;
case Event.ONKEYDOWN:
+ // End drag if ESC is pressed
int keyCode = event.getNativeEvent().getKeyCode();
if (keyCode == KeyCodes.KEY_ESCAPE) {
- // end drag if ESC is hit
cancelDrag(event);
}
break;
- case Event.ONMOUSEMOVE:
- case Event.ONTOUCHMOVE:
- callback.onDragUpdate(event);
- // prevent text selection on IE
- event.getNativeEvent().preventDefault();
- break;
case Event.ONTOUCHCANCEL:
cancelDrag(event);
break;
case Event.ONTOUCHEND:
- /* Avoid simulated event on drag end */
- event.getNativeEvent().preventDefault();
- //$FALL-THROUGH$
case Event.ONMOUSEUP:
- callback.onDragUpdate(event);
+ callback.onDragUpdate(Event.as(event.getNativeEvent()));
callback.onDrop();
stopDrag();
- event.cancel();
+ break;
+ case Event.ONCLICK:
break;
default:
break;
@@ -123,6 +137,12 @@ public class DragAndDropHandler {
} else {
stopDrag();
}
+
+ // Kill events - as long as this thing is active, we don't want to
+ // let any event through.
+ event.getNativeEvent().stopPropagation();
+ event.getNativeEvent().preventDefault();
+ event.cancel();
}
};
@@ -142,7 +162,7 @@ public class DragAndDropHandler {
public void onDragStartOnDraggableElement(
final NativeEvent dragStartingEvent,
final DragAndDropCallback callback) {
- dragStartNativePreviewHandlerRegistration = Event
+ startPreviewHandler = Event
.addNativePreviewHandler(new NativePreviewHandler() {
private int startX = WidgetUtil
@@ -184,14 +204,17 @@ public class DragAndDropHandler {
.getNativeEvent());
if (Math.abs(startX - currentX) > 3
|| Math.abs(startY - currentY) > 3) {
- removeNativePreviewHandlerRegistration();
+ removeStartPreviewHandler();
startDrag(dragStartingEvent, event, callback);
}
+ event.getNativeEvent().stopPropagation();
+ event.getNativeEvent().preventDefault();
+ event.cancel();
break;
default:
// on any other events, clean up this preview
// listener
- removeNativePreviewHandlerRegistration();
+ removeStartPreviewHandler();
break;
}
}
@@ -200,27 +223,22 @@ public class DragAndDropHandler {
private void startDrag(NativeEvent startEvent,
NativePreviewEvent triggerEvent, DragAndDropCallback callback) {
- if (callback.onDragStart(startEvent)) {
+ if (callback.onDragStart(Event.as(startEvent))) {
+ this.callback = callback;
dragging = true;
+
// just capture something to prevent text selection in IE
Event.setCapture(RootPanel.getBodyElement());
- this.callback = callback;
+
dragHandlerRegistration = Event
- .addNativePreviewHandler(dragHandler);
- callback.onDragUpdate(triggerEvent);
+ .addNativePreviewHandler(dragPreviewHandler);
+ callback.onDragUpdate(Event.as(triggerEvent.getNativeEvent()));
}
}
private void stopDrag() {
- dragging = false;
- if (dragHandlerRegistration != null) {
- dragHandlerRegistration.removeHandler();
- dragHandlerRegistration = null;
- }
- Event.releaseCapture(RootPanel.getBodyElement());
- if (callback != null) {
- callback.onDragEnd();
- callback = null;
+ if (!stopTimer.isRunning()) {
+ stopTimer.schedule(100);
}
}
@@ -228,14 +246,12 @@ public class DragAndDropHandler {
callback.onDragCancel();
callback.onDragEnd();
stopDrag();
- event.cancel();
- event.getNativeEvent().preventDefault();
}
- private void removeNativePreviewHandlerRegistration() {
- if (dragStartNativePreviewHandlerRegistration != null) {
- dragStartNativePreviewHandlerRegistration.removeHandler();
- dragStartNativePreviewHandlerRegistration = null;
+ private void removeStartPreviewHandler() {
+ if (startPreviewHandler != null) {
+ startPreviewHandler.removeHandler();
+ startPreviewHandler = null;
}
}
}
diff --git a/client/src/com/vaadin/client/ui/dd/DragHandle.java b/client/src/com/vaadin/client/ui/dd/DragHandle.java
new file mode 100644
index 0000000000..c3a0768a72
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/dd/DragHandle.java
@@ -0,0 +1,220 @@
+/*
+ * 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.DivElement;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.EventListener;
+import com.vaadin.client.WidgetUtil;
+import com.vaadin.client.ui.dd.DragAndDropHandler.DragAndDropCallback;
+
+/**
+ * Drag handle implementation. Drag handles are used for moving or resizing
+ * widgets. This is a minimal-case component, meant to be used specifically as a
+ * drag handle attached to another widget or element. As such, it does
+ * <b>not</b> provide access to the events it's listening to (from the point of
+ * view of this component, there really is no use for that). For the more
+ * general, event-providing interface that this component is based on, see
+ * {@link DragAndDropHandler}.
+ *
+ * @since
+ */
+public class DragHandle {
+
+ /**
+ * Callback interface for the DragHandle event life cycle
+ */
+ public interface DragHandleCallback {
+
+ /**
+ * Called when dragging starts
+ */
+ public void onStart();
+
+ /**
+ * Called when the drag handle has moved.
+ *
+ * @param deltaX
+ * change in X direction since start
+ * @param deltaY
+ * change in Y direction since start
+ */
+ public void onUpdate(double deltaX, double deltaY);
+
+ /**
+ * Called when the drag operation has been cancelled (usually by
+ * pressing ESC)
+ */
+ public void onCancel();
+
+ /**
+ * Called when the drag operation completes successfully
+ */
+ public void onComplete();
+
+ }
+
+ private Element parent;
+ private DivElement element;
+ private String baseClassName;
+
+ private DragAndDropHandler dndHandler;
+ private DragAndDropCallback dndCallback;
+
+ private DragHandleCallback userCallback;
+
+ /**
+ * Creates a new DragHandle.
+ *
+ * @param baseName
+ * CSS style name to use for this DragHandle element. This
+ * parameter is supplied to the constructor (rather than added
+ * later) both to provide the "-dragged" style and to make sure
+ * that the drag handle can be properly styled (it's otherwise
+ * invisible)
+ * @param callback
+ * Callback object allows hooking up the drag handle to the rest
+ * of the program logic
+ */
+ public DragHandle(String baseName, DragHandleCallback callback) {
+ parent = null;
+ element = DivElement.as(DOM.createElement("div"));
+ baseClassName = baseName;
+ userCallback = callback;
+ addStyleName(baseClassName);
+
+ dndCallback = new DragAndDropCallback() {
+
+ private double startX;
+ private double startY;
+
+ @Override
+ public void onDrop() {
+ removeDraggingStyle();
+ userCallback.onComplete();
+ }
+
+ @Override
+ public void onDragUpdate(Event e) {
+ double dx = WidgetUtil.getTouchOrMouseClientX(e) - startX;
+ double dy = WidgetUtil.getTouchOrMouseClientY(e) - startY;
+ userCallback.onUpdate(dx, dy);
+ }
+
+ @Override
+ public boolean onDragStart(Event e) {
+ addDraggingStyle();
+ startX = WidgetUtil.getTouchOrMouseClientX(e);
+ startY = WidgetUtil.getTouchOrMouseClientY(e);
+ userCallback.onStart();
+ return true;
+ }
+
+ @Override
+ public void onDragEnd() {
+ removeDraggingStyle();
+ userCallback.onComplete();
+ }
+
+ @Override
+ public void onDragCancel() {
+ removeDraggingStyle();
+ userCallback.onCancel();
+ }
+
+ private void addDraggingStyle() {
+ addStyleName(baseClassName + "-dragged");
+ }
+
+ private void removeDraggingStyle() {
+ removeStyleName(baseClassName + "-dragged");
+ }
+ };
+ dndHandler = new DragAndDropHandler();
+
+ DOM.sinkEvents(element, Event.ONMOUSEDOWN | Event.ONTOUCHSTART);
+ DOM.setEventListener(element, new EventListener() {
+ @Override
+ public void onBrowserEvent(Event event) {
+ dndHandler.onDragStartOnDraggableElement(event, dndCallback);
+ event.stopPropagation();
+ }
+ });
+ }
+
+ /**
+ * Returns the current parent element for this drag handle. May be null.
+ *
+ * @return an Element or null
+ */
+ public Element getParent() {
+ return parent;
+ }
+
+ /**
+ * Gets the element used as actual drag handle.
+ *
+ * @return an Element
+ */
+ public Element getElement() {
+ return element;
+ }
+
+ /**
+ * Adds this drag handle to an HTML element.
+ *
+ * @param elem
+ * an element
+ */
+ public void addTo(Element elem) {
+ removeFromParent();
+ parent = elem;
+ parent.appendChild(element);
+ }
+
+ /**
+ * Removes this drag handle from whatever it was attached to.
+ */
+ public void removeFromParent() {
+ if (parent != null) {
+ parent.removeChild(element);
+ parent = null;
+ }
+ }
+
+ /**
+ * Adds CSS style name to the drag handle element.
+ *
+ * @param styleName
+ * a CSS style name
+ */
+ public void addStyleName(String styleName) {
+ element.addClassName(styleName);
+ }
+
+ /**
+ * Removes existing style name from drag handle element.
+ *
+ * @param styleName
+ * a CSS style name
+ */
+ public void removeStyleName(String styleName) {
+ element.removeClassName(styleName);
+ }
+
+}
diff --git a/client/src/com/vaadin/client/widgets/Escalator.java b/client/src/com/vaadin/client/widgets/Escalator.java
index 9b9616f474..1400d63b6b 100644
--- a/client/src/com/vaadin/client/widgets/Escalator.java
+++ b/client/src/com/vaadin/client/widgets/Escalator.java
@@ -5323,8 +5323,8 @@ public class Escalator extends Widget implements RequiresResize,
int[] indices = new int[splitArgs.length - 1];
for (int i = 0; i < indices.length; ++i) {
String tmp = splitArgs[i + 1];
- indices[i] = Integer
- .parseInt(tmp.substring(0, tmp.length() - 1));
+ indices[i] = Integer.parseInt(tmp.substring(0,
+ tmp.indexOf("]", 1)));
}
return new SubPartArguments(type, indices);
}
diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java
index 2eefc87f44..506b3fc876 100644
--- a/client/src/com/vaadin/client/widgets/Grid.java
+++ b/client/src/com/vaadin/client/widgets/Grid.java
@@ -86,6 +86,9 @@ import com.vaadin.client.renderers.WidgetRenderer;
import com.vaadin.client.ui.FocusUtil;
import com.vaadin.client.ui.SubPartAware;
import com.vaadin.client.ui.dd.DragAndDropHandler;
+import com.vaadin.client.ui.dd.DragAndDropHandler.DragAndDropCallback;
+import com.vaadin.client.ui.dd.DragHandle;
+import com.vaadin.client.ui.dd.DragHandle.DragHandleCallback;
import com.vaadin.client.widget.escalator.Cell;
import com.vaadin.client.widget.escalator.ColumnConfiguration;
import com.vaadin.client.widget.escalator.EscalatorUpdater;
@@ -951,8 +954,8 @@ public class Grid<T> extends ResizeComposite implements
}
/**
- * A single cell in a grid header row. Has a textual caption.
- *
+ * A single cell in a grid header row. Has a caption and, if it's in a
+ * default row, a drag handle.
*/
public static class HeaderCell extends StaticSection.StaticCell {
}
@@ -3037,7 +3040,9 @@ public class Grid<T> extends ResizeComposite implements
}
- /** @see Grid#autoColumnWidthsRecalculator */
+ /**
+ * @see Grid#autoColumnWidthsRecalculator
+ */
private class AutoColumnWidthsRecalculator {
private double lastCalculatedInnerWidth = -1;
@@ -3986,7 +3991,7 @@ public class Grid<T> extends ResizeComposite implements
private AutoScroller autoScroller = new AutoScroller(this);
- private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropHandler.DragAndDropCallback() {
+ private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() {
private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() {
@@ -4071,10 +4076,9 @@ public class Grid<T> extends ResizeComposite implements
}
@Override
- public void onDragUpdate(NativePreviewEvent event) {
- if (event != null) {
- clientX = WidgetUtil.getTouchOrMouseClientX(event
- .getNativeEvent());
+ public void onDragUpdate(Event e) {
+ if (e != null) {
+ clientX = WidgetUtil.getTouchOrMouseClientX(e);
autoScrollX = 0;
}
resolveDragElementHorizontalPosition(clientX);
@@ -4179,7 +4183,7 @@ public class Grid<T> extends ResizeComposite implements
}
@Override
- public boolean onDragStart(NativeEvent startingEvent) {
+ public boolean onDragStart(Event e) {
calculatePossibleDropPositions();
if (possibleDropPositions.isEmpty()) {
@@ -4200,8 +4204,7 @@ public class Grid<T> extends ResizeComposite implements
// start the auto scroll handler
autoScroller.setScrollArea(60);
- autoScroller.start(startingEvent, ScrollAxis.HORIZONTAL,
- autoScrollerCallback);
+ autoScroller.start(e, ScrollAxis.HORIZONTAL, autoScrollerCallback);
return true;
}
@@ -5458,22 +5461,91 @@ public class Grid<T> extends ResizeComposite implements
// Assign colspan to cell before rendering
cell.setColSpan(metadata.getColspan());
- TableCellElement element = cell.getElement();
+ Element td = cell.getElement();
+ td.removeAllChildren();
+
+ Element content;
+ // Wrap text or html content in default header to isolate
+ // the content from the possible column resize drag handle
+ // next to it
+ if (metadata.getType() != GridStaticCellType.WIDGET) {
+ content = DOM.createDiv();
+
+ if (staticRow instanceof HeaderRow) {
+ content.setClassName(getStylePrimaryName()
+ + "-column-header-content");
+ if (((HeaderRow) staticRow).isDefault()) {
+ content.setClassName(content.getClassName() + " "
+ + getStylePrimaryName()
+ + "-column-default-header-content");
+ }
+ } else if (staticRow instanceof FooterRow) {
+ content.setClassName(getStylePrimaryName()
+ + "-column-footer-content");
+ } else {
+ getLogger().severe(
+ "Unhandled static row type "
+ + staticRow.getClass()
+ .getCanonicalName());
+ }
+
+ td.appendChild(content);
+ } else {
+ content = td;
+ }
+ setCustomStyleName(content, metadata.getStyleName());
+
switch (metadata.getType()) {
case TEXT:
- element.setInnerText(metadata.getText());
+ content.setInnerText(metadata.getText());
break;
case HTML:
- element.setInnerHTML(metadata.getHtml());
+ content.setInnerHTML(metadata.getHtml());
break;
case WIDGET:
preDetach(row, Arrays.asList(cell));
- element.setInnerHTML("");
+ content.setInnerHTML("");
postAttach(row, Arrays.asList(cell));
break;
}
- setCustomStyleName(element, metadata.getStyleName());
+ // XXX: Should add only once in preAttach/postAttach or when
+ // resizable status changes
+ // Only add resize handles to default header row for now
+ if (staticRow instanceof HeaderRow
+ && ((HeaderRow) staticRow).isDefault()) {
+
+ final int column = cell.getColumn();
+ DragHandle dragger = new DragHandle(getStylePrimaryName()
+ + "-column-resize-handle",
+ new DragHandleCallback() {
+
+ private Column<?, T> col = getVisibleColumn(column);
+ private double initialWidth = 0;
+
+ @Override
+ public void onUpdate(double deltaX,
+ double deltaY) {
+ col.setWidth(initialWidth + deltaX);
+ }
+
+ @Override
+ public void onStart() {
+ initialWidth = col.getWidthActual();
+ }
+
+ @Override
+ public void onComplete() {
+ // NOP
+ }
+
+ @Override
+ public void onCancel() {
+ col.setWidth(initialWidth);
+ }
+ });
+ dragger.addTo(td);
+ }
cellFocusHandler.updateFocusedCellStyle(cell, container);
}
@@ -7140,15 +7212,33 @@ public class Grid<T> extends ResizeComposite implements
* handles details[] (translated to spacer[] for Escalator), cell[],
* header[] and footer[]
*/
+
+ // "#header[0][0]/DRAGhANDLE"
Element escalatorElement = escalator.getSubPartElement(subPart
.replaceFirst("^details\\[", "spacer["));
if (escalatorElement != null) {
+
+ int detailIdx = subPart.indexOf("/");
+ if (detailIdx > 0) {
+ String detail = subPart.substring(detailIdx + 1);
+ getLogger().severe(
+ "Looking up detail from index " + detailIdx
+ + " onward: \"" + detail + "\"");
+ if (detail.equalsIgnoreCase("content")) {
+ // XXX: Fix this to look up by class name!
+ return DOM.asOld(Element.as(escalatorElement.getChild(0)));
+ }
+ if (detail.equalsIgnoreCase("draghandle")) {
+ // XXX: Fix this to look up by class name!
+ return DOM.asOld(Element.as(escalatorElement.getChild(1)));
+ }
+ }
+
return DOM.asOld(escalatorElement);
}
SubPartArguments args = SubPartArguments.create(subPart);
-
Element editor = getSubPartElementEditor(args);
if (editor != null) {
return DOM.asOld(editor);