]> source.dussan.org Git - vaadin-framework.git/commitdiff
Migrate 7.7.5 branch patches to v8. (#7969)
authorDenis <denis@vaadin.com>
Thu, 15 Dec 2016 10:40:54 +0000 (12:40 +0200)
committerPekka Hyvönen <pekka@vaadin.com>
Thu, 15 Dec 2016 10:40:54 +0000 (12:40 +0200)
* Prevent adding several scrollbar handlers (#19189).

Change-Id: Ib0cc6c6835aab6d263f153362a328bcf2be7bc5c

* Prevent adding several scrollbar handlers (#19189).

* Keep expand ratio for last row/column when reducing grid layout size (#20297)

Change-Id: Iff53a803596f4fc1eae8e4bfa307b9c1f4df961a

* Fixed drag and drop failure when message dragged from email client (#20451)

When dragging message form email client on Windows, item.webkitGetAsEntry()
might return null creating NPE on the client side. Added additional checks
for this situation.

Change-Id: I569f7e6d0d7b137f24be53d1fbce384695ae8c73

* Change expected pre-release version number pattern in publish report

Change-Id: Icdacecc490d2490ea9e262f5c5736c1dece2a89d

* Mark TextField/TextArea as busy when a text change event is pending
(#20469)

Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50
# Conflicts:
# client/src/main/java/com/vaadin/client/ui/VTextField.java
# uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java

* Fixed touch scrolling issue in Surface and WP devices (#18737)

Fixed by using pointerevents instead of touchevents when the browser is
IE11,
or Edge. Also added touch-action: none; css rules into escalator.css to
prevent
default touch behaviour on IE11 and Edge. Does not affect IE8 to IE10
browsers,
behaviour on those will stay the same as before the fix.

No new unit tests since we do not have automatic touch testing
possibilities yet.
Please test manually with Surface: IE11 and Edge, use for example
uitest:
com.vaadin.tests.components.grid.basics.GridBasicsomponents.grid.basics.GridBasics

Change-Id: Iddbf1852e6ffafc855f749d6f4ebb235ed0f5703

* Add lazy/simple resize mode to Grid (#20108)

Change-Id: I47427efc28c350382dba8c1f50fd332a3f4585e4

# Conflicts:
# client/src/main/java/com/vaadin/client/connectors/GridConnector.java
# client/src/main/java/com/vaadin/client/widgets/Grid.java
# server/src/main/java/com/vaadin/ui/Grid.java
# shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java
# themes/src/main/themes/VAADIN/themes/base/grid/grid.scss
# uitest/src/main/java/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java

Change-Id: Ieca56121875198ed559a41c143b28926e2695433

* Fix NPE in case some items don't contain all properties of Grid.
This could occur in when parent is a different entity than its children
in hierarchical data.

Change-Id: Icd53b5b5e5544a3680d0cd99702ab78224b2dc08

# Conflicts:
# server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java
# server/src/main/java/com/vaadin/ui/Grid.java

* Mark TextField/TextArea as busy when a text change event is pending
(#20469)

Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50
# Conflicts:
# client/src/main/java/com/vaadin/client/ui/VTextField.java
# uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java

* Add lazy/simple resize mode to Grid (#20108)

Change-Id: I47427efc28c350382dba8c1f50fd332a3f4585e4

* Removed V8 VTextField unused import, forgotten @RunLocally.

* Don't rely on selenium "sendKeys" behavior.

* Revert "Change expected pre-release version number pattern in publish report"

This reverts commit 8df27b952dddb691aead6a633c5b3724c98bf343.

* Migrate TextField/TextArea patch from 7.7 to master (modern components)

Mark TextField/TextArea as busy when a text change event is pending
(#20469)

Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50

36 files changed:
client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java
client/src/main/java/com/vaadin/client/ui/VTextField.java
client/src/main/java/com/vaadin/client/ui/dd/DragHandle.java
client/src/main/java/com/vaadin/client/ui/dd/VHtml5DragEvent.java
client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java
client/src/main/java/com/vaadin/client/widgets/Escalator.java
client/src/main/java/com/vaadin/client/widgets/Grid.java
compatibility-client/src/main/java/com/vaadin/v7/client/connectors/GridConnector.java
compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextField.java
compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java
compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java
compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java
compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java [new file with mode: 0644]
compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/ColumnResizeMode.java [new file with mode: 0644]
compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/GridState.java
compatibility-themes/src/main/themes/VAADIN/themes/base/escalator/escalator.scss
compatibility-themes/src/main/themes/VAADIN/themes/base/grid/grid.scss
server/src/main/java/com/vaadin/ui/Grid.java
server/src/main/java/com/vaadin/ui/GridLayout.java
server/src/test/java/com/vaadin/tests/server/component/gridlayout/GridLayoutTest.java
shared/src/main/java/com/vaadin/shared/ui/grid/ColumnResizeMode.java [new file with mode: 0644]
shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java
themes/src/main/themes/VAADIN/themes/valo/components/_escalator.scss
themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss
uitest/src/main/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResize.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java
uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMissingProperty.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridBasicFeatures.java
uitest/src/main/java/com/vaadin/v7/tests/components/textfield/TextChangeEvents.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResizeTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMissingPropertyTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/v7/tests/components/textfield/TextChangeEventsTest.java [new file with mode: 0644]

index 7d906fc4af2a0a811d6a36fd51b38425bf8c29af..828c795777964863ea9234bb5b2d2c7501faaf36 100644 (file)
@@ -53,9 +53,7 @@ import com.vaadin.client.widget.grid.sort.SortEvent;
 import com.vaadin.client.widget.grid.sort.SortOrder;
 import com.vaadin.client.widgets.Grid;
 import com.vaadin.client.widgets.Grid.Column;
-import com.vaadin.client.widgets.Grid.FooterCell;
 import com.vaadin.client.widgets.Grid.FooterRow;
-import com.vaadin.client.widgets.Grid.HeaderCell;
 import com.vaadin.client.widgets.Grid.HeaderRow;
 import com.vaadin.shared.MouseEventDetails;
 import com.vaadin.shared.data.sort.SortDirection;
@@ -212,6 +210,11 @@ public class GridConnector extends AbstractListingConnector
                 .map(this::getColumn).toArray(size -> new Column[size]));
     }
 
+    @OnStateChange("columnResizeMode")
+    void updateColumnResizeMode() {
+        getWidget().setColumnResizeMode(getState().columnResizeMode);
+    }
+
     /**
      * Updates the grid header section on state change.
      */
@@ -235,18 +238,21 @@ public class GridConnector extends AbstractListingConnector
         }
     }
 
-    private void updateStaticRow(RowState rowState, Grid.StaticSection.StaticRow row) {
+    private void updateStaticRow(RowState rowState,
+            Grid.StaticSection.StaticRow row) {
         rowState.cells.forEach((columnId, cellState) -> {
             updateStaticCellFromState(row.getCell(getColumn(columnId)),
                     cellState);
         });
-        for (Map.Entry<CellState, Set<String>> cellGroupEntry : rowState.cellGroups.entrySet()) {
+        for (Map.Entry<CellState, Set<String>> cellGroupEntry : rowState.cellGroups
+                .entrySet()) {
             Set<String> group = cellGroupEntry.getValue();
 
-            Grid.Column<?, ?>[] columns =
-                    group.stream().map(idToColumn::get).toArray(size->new Grid.Column<?, ?>[size]);
+            Grid.Column<?, ?>[] columns = group.stream().map(idToColumn::get)
+                    .toArray(size -> new Grid.Column<?, ?>[size]);
             // Set state to be the same as first in group.
-            updateStaticCellFromState(row.join(columns), cellGroupEntry.getKey());
+            updateStaticCellFromState(row.join(columns),
+                    cellGroupEntry.getKey());
         }
     }
 
index 1eb499505a590f18346527e56b1b368f0dfc544d..1ed84ab2746f397c6f2596a9f8628cab567d2b25 100644 (file)
@@ -72,4 +72,5 @@ public class VTextField extends TextBoxBase
     public void onFocus(FocusEvent event) {
         addStyleDependentName(CLASSNAME_FOCUS);
     }
+
 }
index b800c5e53b51f6dd3c6e31dad9aee3a7f99f798a..c5a9475d3972a6e9f83fcf561778160b65c9964d 100644 (file)
@@ -37,14 +37,14 @@ import com.vaadin.client.ui.dd.DragAndDropHandler.DragAndDropCallback;
 public class DragHandle {
 
     /**
-     * Callback interface for the DragHandle event life cycle
+     * Callback interface for the DragHandle event life cycle.
      */
     public interface DragHandleCallback {
 
         /**
-         * Called when dragging starts
+         * Called when dragging starts.
          */
-        public void onStart();
+        void onStart();
 
         /**
          * Called when the drag handle has moved.
@@ -54,18 +54,18 @@ public class DragHandle {
          * @param deltaY
          *            change in Y direction since start
          */
-        public void onUpdate(double deltaX, double deltaY);
+        void onUpdate(double deltaX, double deltaY);
 
         /**
          * Called when the drag operation has been cancelled (usually by
-         * pressing ESC)
+         * pressing ESC).
          */
-        public void onCancel();
+        void onCancel();
 
         /**
-         * Called when the drag operation completes successfully
+         * Called when the drag operation completes successfully.
          */
-        public void onComplete();
+        void onComplete();
 
     }
 
@@ -78,6 +78,20 @@ public class DragHandle {
 
     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)
+     */
+    public DragHandle(String baseName) {
+        this(baseName, null);
+    }
+
     /**
      * Creates a new DragHandle.
      *
@@ -106,22 +120,28 @@ public class DragHandle {
             @Override
             public void onDrop() {
                 removeDraggingStyle();
-                userCallback.onComplete();
+                if (userCallback != null) {
+                    userCallback.onComplete();
+                }
             }
 
             @Override
             public void onDragUpdate(Event e) {
-                double dx = WidgetUtil.getTouchOrMouseClientX(e) - startX;
-                double dy = WidgetUtil.getTouchOrMouseClientY(e) - startY;
-                userCallback.onUpdate(dx, dy);
+                if (userCallback != null) {
+                    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();
+                if (userCallback != null) {
+                    startX = WidgetUtil.getTouchOrMouseClientX(e);
+                    startY = WidgetUtil.getTouchOrMouseClientY(e);
+                    userCallback.onStart();
+                }
                 return true;
             }
 
@@ -133,7 +153,9 @@ public class DragHandle {
             @Override
             public void onDragCancel() {
                 removeDraggingStyle();
-                userCallback.onCancel();
+                if (userCallback != null) {
+                    userCallback.onCancel();
+                }
             }
 
             private void addDraggingStyle() {
@@ -156,6 +178,18 @@ public class DragHandle {
         });
     }
 
+    /**
+     * Sets the user-facing drag handle callback method. This allows code using
+     * the DragHandle to react to the situations where a drag handle first
+     * touched, when it's moved and when it's released.
+     *
+     * @param dragHandleCallback
+     *            the callback object to use (can be null)
+     */
+    public void setCallback(DragHandleCallback dragHandleCallback) {
+        userCallback = dragHandleCallback;
+    }
+
     /**
      * Returns the current parent element for this drag handle. May be null.
      *
index 581d9263ac258fe4ec2c1ee92ed7c094a55938fb..830f668a0045392031b401d16cd11c83fedaa46d 100644 (file)
@@ -90,8 +90,11 @@ public class VHtml5DragEvent extends NativeEvent {
         // Chrome >= v21 and Opera >= v?
         if (this.dataTransfer.items) {
             var item = this.dataTransfer.items[fileIndex];
-            if (item.webkitGetAsEntry) {
-                return item.webkitGetAsEntry().isFile;
+            if (typeof item.webkitGetAsEntry == "function") {
+                var entry = item.webkitGetAsEntry();
+                if (typeof entry !== "undefined" && entry !== null) {
+                    return entry.isFile;
+                }
             }
         }
 
index 2b615ebdb7601939b23e0cd6e09a71845e04dfd7..ed8d3cd57b8b6d7b94d5daf7c5af91933371e742 100644 (file)
@@ -436,6 +436,9 @@ public abstract class ScrollbarBundle implements DeferredWorker {
         boolean offsetSizeBecomesGreaterThanScrollSize = showsScrollHandle()
                 && newOffsetSizeIsGreaterThanScrollSize;
         if (offsetSizeBecomesGreaterThanScrollSize && getScrollPos() != 0) {
+            if (offsetSizeTemporaryScrollHandler != null) {
+                offsetSizeTemporaryScrollHandler.removeHandler();
+            }
             // must be a field because Java insists.
             offsetSizeTemporaryScrollHandler = addScrollHandler(
                     new ScrollHandler() {
@@ -661,6 +664,9 @@ public abstract class ScrollbarBundle implements DeferredWorker {
              */
             boolean delayedSizeSet = !BrowserInfo.get().isFirefox();
             if (delayedSizeSet) {
+                if (scrollSizeTemporaryScrollHandler != null) {
+                    scrollSizeTemporaryScrollHandler.removeHandler();
+                }
                 scrollSizeTemporaryScrollHandler = addScrollHandler(
                         new ScrollHandler() {
                             @Override
index e3e0dfa0e42dff55c168d73b5b4e404d6cb17b72..e3953b436aafd3a363eb6a60cdc3b13a5035b7af 100644 (file)
@@ -307,6 +307,8 @@ public class Escalator extends Widget
     static class JsniUtil {
         public static class TouchHandlerBundle {
 
+            public static final String POINTER_EVENT_TYPE_TOUCH = "touch";
+
             /**
              * A <a href=
              * "http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsOverlay.html"
@@ -339,6 +341,11 @@ public class Escalator extends Widget
                 /*-{
                     return this.targetTouches[0].pageY;
                 }-*/;
+
+                public native String getPointerType()
+                /*-{
+                    return this.pointerType;
+                }-*/;
             }
 
             private final Escalator escalator;
@@ -456,6 +463,15 @@ public class Escalator extends Widget
                 }
 
                 int pagePosition(CustomTouchEvent event) {
+                    // Use native event's screen x and y for IE11 and Edge
+                    // since there is no touches for these browsers (#18737)
+                    if (isCurrentBrowserIE11OrEdge()) {
+                        return vertical
+                                ? event.getNativeEvent().getClientY()
+                                + Window.getScrollTop()
+                                : event.getNativeEvent().getClientX()
+                                + Window.getScrollLeft();
+                    }
                     JsArray<Touch> a = event.getNativeEvent().getTouches();
                     return vertical ? a.get(0).getPageY() : a.get(0).getPageX();
                 }
@@ -495,7 +511,7 @@ public class Escalator extends Widget
             };
 
             public void touchStart(final CustomTouchEvent event) {
-                if (event.getNativeEvent().getTouches().length() == 1) {
+                if (allowTouch(event)) {
                     if (yMov == null) {
                         yMov = new Movement(true);
                         xMov = new Movement(false);
@@ -543,6 +559,18 @@ public class Escalator extends Widget
                 }
             }
 
+            // Allow touchStart for IE11 and Edge even though there is no touch
+            // (#18737),
+            // otherwise allow touch only if there is a single touch in the
+            // event
+            private boolean allowTouch(final TouchHandlerBundle.CustomTouchEvent event) {
+                if (isCurrentBrowserIE11OrEdge()) {
+                    return (POINTER_EVENT_TYPE_TOUCH.equals(event.getPointerType()));
+                } else {
+                    return (event.getNativeEvent().getTouches().length() == 1);
+                }
+            }
+
             private double easingInOutCos(double val, double max) {
                 return 0.5 - 0.5 * Math.cos(Math.PI * Math.signum(val)
                         * Math.min(Math.abs(val), max) / max);
@@ -881,7 +909,7 @@ public class Escalator extends Widget
 
         public native void detachScrollListener(Element element)
         /*
-         * Attaching events with JSNI instead of the GWT event mechanism because
+         * Detaching events with JSNI instead of the GWT event mechanism because
          * GWT didn't provide enough details in events, or triggering the event
          * handlers with GWT bindings was unsuccessful. Maybe, with more time
          * and skill, it could be done with better success. JavaScript overlay
@@ -958,6 +986,50 @@ public class Escalator extends Widget
             element.removeEventListener("touchcancel", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction);
         }-*/;
 
+        /**
+         * Using pointerdown, pointermove, pointerup, and pointercancel for IE11 and Edge instead of
+         * touch* listeners (#18737)
+         *
+         * @param element
+         */
+        public native void attachPointerEventListeners(Element element)
+        /*
+         * Attaching events with JSNI instead of the GWT event mechanism because
+         * GWT didn't provide enough details in events, or triggering the event
+         * handlers with GWT bindings was unsuccessful. Maybe, with more time
+         * and skill, it could be done with better success. JavaScript overlay
+         * types might work. This might also get rid of the JsniWorkaround
+         * class.
+         */
+        /*-{
+            element.addEventListener("pointerdown", this.@com.vaadin.client.widgets.JsniWorkaround::touchStartFunction);
+            element.addEventListener("pointermove", this.@com.vaadin.client.widgets.JsniWorkaround::touchMoveFunction);
+            element.addEventListener("pointerup", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction);
+            element.addEventListener("pointercancel", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction);
+        }-*/;
+
+        /**
+         * Using pointerdown, pointermove, pointerup, and pointercancel for IE11 and Edge instead of
+         * touch* listeners (#18737)
+         *
+         * @param element
+         */
+        public native void detachPointerEventListeners(Element element)
+        /*
+         * Detaching events with JSNI instead of the GWT event mechanism because
+         * GWT didn't provide enough details in events, or triggering the event
+         * handlers with GWT bindings was unsuccessful. Maybe, with more time
+         * and skill, it could be done with better success. JavaScript overlay
+         * types might work. This might also get rid of the JsniWorkaround
+         * class.
+         */
+        /*-{
+            element.removeEventListener("pointerdown", this.@com.vaadin.client.widgets.JsniWorkaround::touchStartFunction);
+            element.removeEventListener("pointermove", this.@com.vaadin.client.widgets.JsniWorkaround::touchMoveFunction);
+            element.removeEventListener("pointerup", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction);
+            element.removeEventListener("pointercancel", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction);
+        }-*/;
+
         public void scrollToColumn(final int columnIndex,
                 final ScrollDestination destination, final int padding) {
             assert columnIndex >= columnConfiguration.frozenColumns : "Can't scroll to a frozen column";
@@ -5717,7 +5789,13 @@ public class Escalator extends Widget
         scroller.attachScrollListener(verticalScrollbar.getElement());
         scroller.attachScrollListener(horizontalScrollbar.getElement());
         scroller.attachMousewheelListener(getElement());
-        scroller.attachTouchListeners(getElement());
+
+        if (isCurrentBrowserIE11OrEdge()) {
+            // Touch listeners doesn't work for IE11 and Edge (#18737)
+            scroller.attachPointerEventListeners(getElement());
+        } else {
+            scroller.attachTouchListeners(getElement());
+        }
     }
 
     @Override
@@ -5726,7 +5804,13 @@ public class Escalator extends Widget
         scroller.detachScrollListener(verticalScrollbar.getElement());
         scroller.detachScrollListener(horizontalScrollbar.getElement());
         scroller.detachMousewheelListener(getElement());
-        scroller.detachTouchListeners(getElement());
+
+        if (isCurrentBrowserIE11OrEdge()) {
+            // Touch listeners doesn't work for IE11 and Edge (#18737)
+            scroller.detachPointerEventListeners(getElement());
+        } else {
+            scroller.detachTouchListeners(getElement());
+        }
 
         /*
          * We can call paintRemoveRows here, because static ranges are simple to
@@ -6772,4 +6856,12 @@ public class Escalator extends Widget
     double getMinCellWidth(int colIndex) {
         return columnConfiguration.getMinCellWidth(colIndex);
     }
+
+    /**
+     * Internal method for checking whether the browser is IE11 or Edge
+     * @return true only if the current browser is IE11, or Edge
+     */
+    private static boolean isCurrentBrowserIE11OrEdge() {
+        return BrowserInfo.get().isIE11() || BrowserInfo.get().isEdge();
+    }
 }
index 368ad8693b65192be6bff4f0cb524c4da213c1d8..1ffda08ef71a878bccd0e7be320018c496c20daf 100644 (file)
@@ -37,6 +37,7 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.core.shared.GWT;
 import com.google.gwt.dom.client.BrowserEvents;
 import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.EventTarget;
 import com.google.gwt.dom.client.NativeEvent;
@@ -174,6 +175,7 @@ import com.vaadin.client.widgets.Grid.StaticSection.StaticRow;
 import com.vaadin.shared.Range;
 import com.vaadin.shared.Registration;
 import com.vaadin.shared.data.sort.SortDirection;
+import com.vaadin.shared.ui.grid.ColumnResizeMode;
 import com.vaadin.shared.ui.grid.GridConstants;
 import com.vaadin.shared.ui.grid.GridConstants.Section;
 import com.vaadin.shared.ui.grid.GridStaticCellType;
@@ -4140,6 +4142,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
 
     private AutoScroller autoScroller = new AutoScroller(this);
 
+    private ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED;
+
     private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() {
 
         private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() {
@@ -5684,63 +5688,164 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
                         && staticRow instanceof HeaderRow
                         && ((HeaderRow) staticRow).isDefault()) {
 
+                    final DivElement resizeElement = Document.get()
+                            .createDivElement();
+                    resizeElement.addClassName(getStylePrimaryName()
+                            + "-column-resize-simple-indicator");
+
                     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;
-                                private double minCellWidth;
-
-                                @Override
-                                public void onUpdate(double deltaX,
-                                        double deltaY) {
-                                    col.setWidth(Math.max(minCellWidth,
-                                            initialWidth + deltaX));
-                                }
+                    final DragHandle dragger = new DragHandle(
+                            getStylePrimaryName() + "-column-resize-handle");
+                    dragger.addTo(td);
 
-                                @Override
-                                public void onStart() {
-                                    initialWidth = col.getWidthActual();
-
-                                    minCellWidth = escalator.getMinCellWidth(
-                                            getVisibleColumns().indexOf(col));
-                                    for (Column<?, T> c : getVisibleColumns()) {
-                                        if (selectionColumn == c) {
-                                            // Don't modify selection column.
-                                            continue;
-                                        }
-
-                                        if (c.getWidth() < 0) {
-                                            c.setWidth(c.getWidthActual());
-                                            fireEvent(
-                                                    new ColumnResizeEvent<>(c));
-                                        }
-                                    }
+                    // Common functionality for drag handle callback
+                    // implementations
+                    abstract class AbstractDHCallback
+                            implements DragHandleCallback {
+                        protected Column<?, T> col = getVisibleColumn(column);
+                        protected double initialWidth = 0;
+                        protected double minCellWidth;
+                        protected double width;
+
+                        protected void dragStarted() {
+                            initialWidth = col.getWidthActual();
+                            width = initialWidth;
+
+                            minCellWidth = escalator.getMinCellWidth(
+                                    getVisibleColumns().indexOf(col));
+                            for (Column<?, T> c : getVisibleColumns()) {
+                                if (selectionColumn == c) {
+                                    // Don't modify selection column.
+                                    continue;
+                                }
 
-                                    WidgetUtil.setTextSelectionEnabled(
-                                            getElement(), false);
+                                if (c.getWidth() < 0) {
+                                    c.setWidth(c.getWidthActual());
+                                    fireEvent(new ColumnResizeEvent<>(c));
                                 }
+                            }
 
-                                @Override
-                                public void onComplete() {
-                                    fireEvent(new ColumnResizeEvent<>(col));
+                            WidgetUtil.setTextSelectionEnabled(getElement(),
+                                    false);
+                        }
 
-                                    WidgetUtil.setTextSelectionEnabled(
-                                            getElement(), true);
-                                }
+                        protected void dragEnded() {
+                            WidgetUtil.setTextSelectionEnabled(getElement(),
+                                    true);
+                        }
+                    }
 
-                                @Override
-                                public void onCancel() {
-                                    col.setWidth(initialWidth);
+                    final DragHandleCallback simpleResizeMode = new AbstractDHCallback() {
+                        @Override
+                        protected void dragEnded() {
+                            super.dragEnded();
+                            dragger.getElement().removeChild(resizeElement);
+                        }
 
-                                    WidgetUtil.setTextSelectionEnabled(
-                                            getElement(), true);
-                                }
-                            });
-                    dragger.addTo(td);
+                        @Override
+                        public void onStart() {
+                            dragStarted();
+                            dragger.getElement().appendChild(resizeElement);
+                            resizeElement.getStyle().setLeft(
+                                    (dragger.getElement().getOffsetWidth()
+                                            - resizeElement.getOffsetWidth())
+                                            * .5,
+                                    Unit.PX);
+                            resizeElement.getStyle().setHeight(
+                                    col.grid.getOffsetHeight(), Unit.PX);
+                        }
+
+                        @Override
+                        public void onUpdate(double deltaX, double deltaY) {
+                            width = Math.max(minCellWidth,
+                                    initialWidth + deltaX);
+                            resizeElement.getStyle().setLeft(
+                                    (dragger.getElement().getOffsetWidth()
+                                            - resizeElement.getOffsetWidth())
+                                            * .5 + (width - initialWidth),
+                                    Unit.PX);
+                        }
+
+                        @Override
+                        public void onCancel() {
+                            dragEnded();
+                        }
+
+                        @Override
+                        public void onComplete() {
+                            dragEnded();
+
+                            col.setWidth(width);
+                            fireEvent(new ColumnResizeEvent<>(col));
+                        }
+                    };
+
+                    final DragHandleCallback animatedResizeMode = new AbstractDHCallback() {
+                        @Override
+                        public void onStart() {
+                            dragStarted();
+                        }
+
+                        @Override
+                        public void onUpdate(double deltaX, double deltaY) {
+                            width = Math.max(minCellWidth,
+                                    initialWidth + deltaX);
+                            col.setWidth(width);
+                        }
+
+                        @Override
+                        public void onCancel() {
+                            dragEnded();
+                            col.setWidth(initialWidth);
+                        }
+
+                        @Override
+                        public void onComplete() {
+                            dragEnded();
+                            col.setWidth(width);
+                            fireEvent(new ColumnResizeEvent<>(col));
+                        }
+                    };
+
+                    // DragHandle gets assigned a 'master callback' that
+                    // delegates
+                    // functionality to the correct case-specific implementation
+                    dragger.setCallback(new DragHandleCallback() {
+
+                        private DragHandleCallback currentCallback;
+
+                        @Override
+                        public void onStart() {
+                            switch (getColumnResizeMode()) {
+                            case SIMPLE:
+                                currentCallback = simpleResizeMode;
+                                break;
+                            case ANIMATED:
+                                currentCallback = animatedResizeMode;
+                                break;
+                            default:
+                                throw new UnsupportedOperationException(
+                                        "Support for current column resize mode is not yet implemented");
+                            }
+
+                            currentCallback.onStart();
+                        }
+
+                        @Override
+                        public void onUpdate(double deltaX, double deltaY) {
+                            currentCallback.onUpdate(deltaX, deltaY);
+                        }
+
+                        @Override
+                        public void onCancel() {
+                            currentCallback.onCancel();
+                        }
+
+                        @Override
+                        public void onComplete() {
+                            currentCallback.onComplete();
+                        }
+                    });
                 }
 
                 cellFocusHandler.updateFocusedCellStyle(cell, container);
@@ -5996,6 +6101,27 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
         fireEvent(new GridEnabledEvent(enabled));
     }
 
+    /**
+     * Sets the column resize mode to use. The default mode is
+     * {@link ColumnResizeMode.ANIMATED}.
+     *
+     * @param mode
+     *            a ColumnResizeMode value
+     */
+    public void setColumnResizeMode(ColumnResizeMode mode) {
+        columnResizeMode = mode;
+    }
+
+    /**
+     * Returns the current column resize mode. The default mode is
+     * {@link ColumnResizeMode.ANIMATED}.
+     *
+     * @return a ColumnResizeMode value
+     */
+    public ColumnResizeMode getColumnResizeMode() {
+        return columnResizeMode;
+    }
+
     @Override
     public void setStylePrimaryName(String style) {
         super.setStylePrimaryName(style);
index cf48586d01ee70caeadc9d47da3181261d6f7e22..cba6fc65654b1c1738b772e080d3ce4b12a82ad0 100644 (file)
@@ -389,7 +389,7 @@ public class GridConnector extends AbstractHasComponentsConnector
             } else {
                 Collection<Column<?, JsonObject>> errorColumns;
                 if (errorColumnsIds != null) {
-                    errorColumns = new ArrayList<Grid.Column<?, JsonObject>>();
+                    errorColumns = new ArrayList<>();
                     for (String colId : errorColumnsIds) {
                         errorColumns.add(columnIdToColumn.get(colId));
                     }
@@ -435,7 +435,7 @@ public class GridConnector extends AbstractHasComponentsConnector
         public void onColumnReorder(ColumnReorderEvent<JsonObject> event) {
             if (!columnsUpdatedFromState) {
                 List<Column<?, JsonObject>> columns = getWidget().getColumns();
-                final List<String> newColumnOrder = new ArrayList<String>();
+                final List<String> newColumnOrder = new ArrayList<>();
                 for (Column<?, JsonObject> column : columns) {
                     if (column instanceof CustomGridColumn) {
                         newColumnOrder.add(((CustomGridColumn) column).id);
@@ -494,8 +494,8 @@ public class GridConnector extends AbstractHasComponentsConnector
     private class CustomDetailsGenerator
             implements HeightAwareDetailsGenerator {
 
-        private final Map<String, ComponentConnector> idToDetailsMap = new HashMap<String, ComponentConnector>();
-        private final Map<String, Integer> idToRowIndex = new HashMap<String, Integer>();
+        private final Map<String, ComponentConnector> idToDetailsMap = new HashMap<>();
+        private final Map<String, Integer> idToRowIndex = new HashMap<>();
 
         @Override
         public Widget getDetails(int rowIndex) {
@@ -544,7 +544,7 @@ public class GridConnector extends AbstractHasComponentsConnector
         }
 
         public void updateConnectorHierarchy(List<ServerConnector> children) {
-            Set<String> connectorIds = new HashSet<String>();
+            Set<String> connectorIds = new HashSet<>();
             for (ServerConnector child : children) {
                 if (child instanceof ComponentConnector) {
                     connectorIds.add(child.getConnectorId());
@@ -553,7 +553,7 @@ public class GridConnector extends AbstractHasComponentsConnector
                 }
             }
 
-            Set<String> removedDetails = new HashSet<String>();
+            Set<String> removedDetails = new HashSet<>();
             for (Entry<String, ComponentConnector> entry : idToDetailsMap
                     .entrySet()) {
                 ComponentConnector connector = entry.getValue();
@@ -626,9 +626,9 @@ public class GridConnector extends AbstractHasComponentsConnector
     /**
      * Maps a generated column id to a grid column instance
      */
-    private Map<String, CustomGridColumn> columnIdToColumn = new HashMap<String, CustomGridColumn>();
+    private Map<String, CustomGridColumn> columnIdToColumn = new HashMap<>();
 
-    private List<String> columnOrder = new ArrayList<String>();
+    private List<String> columnOrder = new ArrayList<>();
 
     /**
      * {@link #columnsUpdatedFromState} is set to true when
@@ -642,7 +642,7 @@ public class GridConnector extends AbstractHasComponentsConnector
     private RpcDataSource dataSource;
 
     /* Used to track Grid editor columns with validation errors */
-    private final Map<Column<?, JsonObject>, String> columnToErrorMessage = new HashMap<Column<?, JsonObject>, String>();
+    private final Map<Column<?, JsonObject>, String> columnToErrorMessage = new HashMap<>();
 
     private ItemClickHandler itemClickHandler = new ItemClickHandler();
 
@@ -839,6 +839,11 @@ public class GridConnector extends AbstractHasComponentsConnector
             }
         }
 
+        // Column resize mode
+        if (stateChangeEvent.hasPropertyChanged("columnResizeMode")) {
+            getWidget().setColumnResizeMode(getState().columnResizeMode);
+        }
+
         // Header and footer
         if (stateChangeEvent.hasPropertyChanged("header")) {
             updateHeaderFromState(getState().header);
@@ -1101,7 +1106,7 @@ public class GridConnector extends AbstractHasComponentsConnector
     private void purgeRemovedColumns() {
 
         // Get columns still registered in the state
-        Set<String> columnsInState = new HashSet<String>();
+        Set<String> columnsInState = new HashSet<>();
         for (GridColumnState columnState : getState().columns) {
             columnsInState.add(columnState.id);
         }
@@ -1126,7 +1131,7 @@ public class GridConnector extends AbstractHasComponentsConnector
     }
 
     private void onSortStateChange() {
-        List<SortOrder> sortOrder = new ArrayList<SortOrder>();
+        List<SortOrder> sortOrder = new ArrayList<>();
 
         String[] sortColumns = getState().sortColumns;
         SortDirection[] sortDirs = getState().sortDirs;
@@ -1283,7 +1288,7 @@ public class GridConnector extends AbstractHasComponentsConnector
      * @return displayed error string
      */
     private String getColumnErrors() {
-        List<String> errors = new ArrayList<String>();
+        List<String> errors = new ArrayList<>();
 
         for (Grid.Column<?, JsonObject> c : getWidget().getColumns()) {
             if (!(c instanceof CustomGridColumn)) {
index ffaec8ad2077a3485dd04ef8f3fd4d753dcf38ec..8357a7649a5d523812ad1f2da15ece69781b31ae 100644 (file)
@@ -34,6 +34,7 @@ import com.google.gwt.user.client.Timer;
 import com.google.gwt.user.client.ui.TextBoxBase;
 import com.vaadin.client.ApplicationConnection;
 import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.DeferredWorker;
 import com.vaadin.client.WidgetUtil;
 import com.vaadin.client.ui.Field;
 import com.vaadin.shared.EventId;
@@ -47,7 +48,7 @@ import com.vaadin.v7.shared.ui.textfield.TextFieldConstants;
  */
 @Deprecated
 public class VTextField extends TextBoxBase implements Field, ChangeHandler,
-        FocusHandler, BlurHandler, KeyDownHandler {
+        FocusHandler, BlurHandler, KeyDownHandler, DeferredWorker {
 
     /**
      * The input node CSS classname.
@@ -530,4 +531,9 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler,
         }
         possibleInputError = false;
     }
+
+    @Override
+    public boolean isWorkPending() {
+        return scheduled;
+    }
 }
index 49a6c6b986abff7bdabe40fe685334f091150e1a..0ea348553a597e97be3d23ed2e034c0ec9ba9c28 100644 (file)
@@ -36,6 +36,7 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.core.shared.GWT;
 import com.google.gwt.dom.client.BrowserEvents;
 import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.EventTarget;
 import com.google.gwt.dom.client.NativeEvent;
@@ -179,6 +180,7 @@ import com.vaadin.v7.client.widgets.Escalator.SubPartArguments;
 import com.vaadin.v7.client.widgets.Grid.Editor.State;
 import com.vaadin.v7.client.widgets.Grid.StaticSection.StaticCell;
 import com.vaadin.v7.client.widgets.Grid.StaticSection.StaticRow;
+import com.vaadin.v7.shared.ui.grid.ColumnResizeMode;
 import com.vaadin.v7.shared.ui.grid.GridConstants;
 import com.vaadin.v7.shared.ui.grid.GridConstants.Section;
 import com.vaadin.v7.shared.ui.grid.GridStaticCellType;
@@ -4105,6 +4107,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
 
     private AutoScroller autoScroller = new AutoScroller(this);
 
+    private ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED;
+
     private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() {
 
         private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() {
@@ -5691,63 +5695,164 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
                         && staticRow instanceof HeaderRow
                         && ((HeaderRow) staticRow).isDefault()) {
 
+                    final DivElement resizeElement = Document.get()
+                            .createDivElement();
+                    resizeElement.addClassName(getStylePrimaryName()
+                            + "-column-resize-simple-indicator");
+
                     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;
-                                private double minCellWidth;
-
-                                @Override
-                                public void onUpdate(double deltaX,
-                                        double deltaY) {
-                                    col.setWidth(Math.max(minCellWidth,
-                                            initialWidth + deltaX));
-                                }
+                    final DragHandle dragger = new DragHandle(
+                            getStylePrimaryName() + "-column-resize-handle");
+                    dragger.addTo(td);
 
-                                @Override
-                                public void onStart() {
-                                    initialWidth = col.getWidthActual();
-
-                                    minCellWidth = escalator.getMinCellWidth(
-                                            getVisibleColumns().indexOf(col));
-                                    for (Column<?, T> c : getVisibleColumns()) {
-                                        if (selectionColumn == c) {
-                                            // Don't modify selection column.
-                                            continue;
-                                        }
-
-                                        if (c.getWidth() < 0) {
-                                            c.setWidth(c.getWidthActual());
-                                            fireEvent(
-                                                    new ColumnResizeEvent<>(c));
-                                        }
-                                    }
+                    // Common functionality for drag handle callback
+                    // implementations
+                    abstract class AbstractDHCallback
+                            implements DragHandleCallback {
+                        protected Column<?, T> col = getVisibleColumn(column);
+                        protected double initialWidth = 0;
+                        protected double minCellWidth;
+                        protected double width;
+
+                        protected void dragStarted() {
+                            initialWidth = col.getWidthActual();
+                            width = initialWidth;
+
+                            minCellWidth = escalator.getMinCellWidth(
+                                    getVisibleColumns().indexOf(col));
+                            for (Column<?, T> c : getVisibleColumns()) {
+                                if (selectionColumn == c) {
+                                    // Don't modify selection column.
+                                    continue;
+                                }
 
-                                    WidgetUtil.setTextSelectionEnabled(
-                                            getElement(), false);
+                                if (c.getWidth() < 0) {
+                                    c.setWidth(c.getWidthActual());
+                                    fireEvent(new ColumnResizeEvent<>(c));
                                 }
+                            }
 
-                                @Override
-                                public void onComplete() {
-                                    fireEvent(new ColumnResizeEvent<>(col));
+                            WidgetUtil.setTextSelectionEnabled(getElement(),
+                                    false);
+                        }
 
-                                    WidgetUtil.setTextSelectionEnabled(
-                                            getElement(), true);
-                                }
+                        protected void dragEnded() {
+                            WidgetUtil.setTextSelectionEnabled(getElement(),
+                                    true);
+                        }
+                    }
 
-                                @Override
-                                public void onCancel() {
-                                    col.setWidth(initialWidth);
+                    final DragHandleCallback simpleResizeMode = new AbstractDHCallback() {
+                        @Override
+                        protected void dragEnded() {
+                            super.dragEnded();
+                            dragger.getElement().removeChild(resizeElement);
+                        }
 
-                                    WidgetUtil.setTextSelectionEnabled(
-                                            getElement(), true);
-                                }
-                            });
-                    dragger.addTo(td);
+                        @Override
+                        public void onStart() {
+                            dragStarted();
+                            dragger.getElement().appendChild(resizeElement);
+                            resizeElement.getStyle().setLeft(
+                                    (dragger.getElement().getOffsetWidth()
+                                            - resizeElement.getOffsetWidth())
+                                            * .5,
+                                    Unit.PX);
+                            resizeElement.getStyle().setHeight(
+                                    col.grid.getOffsetHeight(), Unit.PX);
+                        }
+
+                        @Override
+                        public void onUpdate(double deltaX, double deltaY) {
+                            width = Math.max(minCellWidth,
+                                    initialWidth + deltaX);
+                            resizeElement.getStyle().setLeft(
+                                    (dragger.getElement().getOffsetWidth()
+                                            - resizeElement.getOffsetWidth())
+                                            * .5 + (width - initialWidth),
+                                    Unit.PX);
+                        }
+
+                        @Override
+                        public void onCancel() {
+                            dragEnded();
+                        }
+
+                        @Override
+                        public void onComplete() {
+                            dragEnded();
+
+                            col.setWidth(width);
+                            fireEvent(new ColumnResizeEvent<>(col));
+                        }
+                    };
+
+                    final DragHandleCallback animatedResizeMode = new AbstractDHCallback() {
+                        @Override
+                        public void onStart() {
+                            dragStarted();
+                        }
+
+                        @Override
+                        public void onUpdate(double deltaX, double deltaY) {
+                            width = Math.max(minCellWidth,
+                                    initialWidth + deltaX);
+                            col.setWidth(width);
+                        }
+
+                        @Override
+                        public void onCancel() {
+                            dragEnded();
+                            col.setWidth(initialWidth);
+                        }
+
+                        @Override
+                        public void onComplete() {
+                            dragEnded();
+                            col.setWidth(width);
+                            fireEvent(new ColumnResizeEvent<>(col));
+                        }
+                    };
+
+                    // DragHandle gets assigned a 'master callback' that
+                    // delegates
+                    // functionality to the correct case-specific implementation
+                    dragger.setCallback(new DragHandleCallback() {
+
+                        private DragHandleCallback currentCallback;
+
+                        @Override
+                        public void onStart() {
+                            switch (getColumnResizeMode()) {
+                            case SIMPLE:
+                                currentCallback = simpleResizeMode;
+                                break;
+                            case ANIMATED:
+                                currentCallback = animatedResizeMode;
+                                break;
+                            default:
+                                throw new UnsupportedOperationException(
+                                        "Support for current column resize mode is not yet implemented");
+                            }
+
+                            currentCallback.onStart();
+                        }
+
+                        @Override
+                        public void onUpdate(double deltaX, double deltaY) {
+                            currentCallback.onUpdate(deltaX, deltaY);
+                        }
+
+                        @Override
+                        public void onCancel() {
+                            currentCallback.onCancel();
+                        }
+
+                        @Override
+                        public void onComplete() {
+                            currentCallback.onComplete();
+                        }
+                    });
                 }
 
                 cellFocusHandler.updateFocusedCellStyle(cell, container);
@@ -5979,6 +6084,27 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
         return enabled;
     }
 
+    /**
+     * Sets the column resize mode to use. The default mode is
+     * {@link ColumnResizeMode.ANIMATED}.
+     *
+     * @param mode
+     *            a ColumnResizeMode value
+     */
+    public void setColumnResizeMode(ColumnResizeMode mode) {
+        columnResizeMode = mode;
+    }
+
+    /**
+     * Returns the current column resize mode. The default mode is
+     * {@link ColumnResizeMode.ANIMATED}.
+     *
+     * @return a ColumnResizeMode value
+     */
+    public ColumnResizeMode getColumnResizeMode() {
+        return columnResizeMode;
+    }
+
     @Override
     public void setEnabled(boolean enabled) {
         if (enabled == this.enabled) {
index 1055d1921dbc99189e722d87fd5d2d866e8759bd..553bbc753c34ce3deaac44dbbc61980e60ecd218 100644 (file)
@@ -99,6 +99,13 @@ public class FieldGroup implements Serializable {
     public void setItemDataSource(Item itemDataSource) {
         this.itemDataSource = itemDataSource;
 
+        bindFields();
+    }
+
+    /**
+     * Binds all fields to the properties in the item in use.
+     */
+    protected void bindFields() {
         for (Field<?> f : fieldToPropertyId.keySet()) {
             bind(f, fieldToPropertyId.get(f));
         }
@@ -254,20 +261,7 @@ public class FieldGroup implements Serializable {
         fieldToPropertyId.put(field, propertyId);
         propertyIdToField.put(propertyId, field);
         if (itemDataSource == null) {
-            // Clear any possible existing binding to clear the field
-            field.setPropertyDataSource(null);
-            boolean fieldReadOnly = field.isReadOnly();
-            if (!fieldReadOnly) {
-                field.clear();
-            } else {
-                // Temporarily make the field read-write so we can clear the
-                // value. Needed because setPropertyDataSource(null) does not
-                // currently clear the field
-                // (https://dev.vaadin.com/ticket/14733)
-                field.setReadOnly(false);
-                field.clear();
-                field.setReadOnly(true);
-            }
+            clearField(field);
 
             // Will be bound when data source is set
             return;
@@ -278,6 +272,29 @@ public class FieldGroup implements Serializable {
         configureField(field);
     }
 
+    /**
+     * Clears field and any possible existing binding.
+     *
+     * @param field
+     *            The field to be cleared
+     */
+    protected void clearField(Field<?> field) {
+        // Clear any possible existing binding to clear the field
+        field.setPropertyDataSource(null);
+        boolean fieldReadOnly = field.isReadOnly();
+        if (!fieldReadOnly) {
+            field.clear();
+        } else {
+            // Temporarily make the field read-write so we can clear the
+            // value. Needed because setPropertyDataSource(null) does not
+            // currently clear the field
+            // (https://dev.vaadin.com/ticket/14733)
+            field.setReadOnly(false);
+            field.clear();
+            field.setReadOnly(true);
+        }
+    }
+
     /**
      * Wrap property to transactional property.
      */
index f5399f60ce3a57d91c9ad92907bc7f68c36f4928..e48e58824fb3089aed0114c4b46e1ea9b32ece2d 100644 (file)
@@ -104,6 +104,7 @@ import com.vaadin.v7.event.SelectionEvent.SelectionListener;
 import com.vaadin.v7.event.SelectionEvent.SelectionNotifier;
 import com.vaadin.v7.server.communication.data.DataGenerator;
 import com.vaadin.v7.server.communication.data.RpcDataProviderExtension;
+import com.vaadin.v7.shared.ui.grid.ColumnResizeMode;
 import com.vaadin.v7.shared.ui.grid.EditorClientRpc;
 import com.vaadin.v7.shared.ui.grid.EditorServerRpc;
 import com.vaadin.v7.shared.ui.grid.GridClientRpc;
@@ -566,6 +567,35 @@ public class Grid extends AbstractComponent
             }
             return field;
         }
+
+        @Override
+        protected void bindFields() {
+            List<Field<?>> fields = new ArrayList<>(getFields());
+            Item itemDataSource = getItemDataSource();
+
+            if (itemDataSource == null) {
+                unbindFields(fields);
+            } else {
+                bindFields(fields, itemDataSource);
+            }
+        }
+
+        private void unbindFields(List<Field<?>> fields) {
+            for (Field<?> field : fields) {
+                clearField(field);
+                unbind(field);
+                field.setParent(null);
+            }
+        }
+
+        private void bindFields(List<Field<?>> fields, Item itemDataSource) {
+            for (Field<?> field : fields) {
+                if (itemDataSource
+                        .getItemProperty(getPropertyId(field)) != null) {
+                    bind(field, getPropertyId(field));
+                }
+            }
+        }
     }
 
     /**
@@ -2242,8 +2272,9 @@ public class Grid extends AbstractComponent
             Renderer<?> renderer = column.getRenderer();
 
             Item item = cell.getItem();
-            Object modelValue = item.getItemProperty(cell.getPropertyId())
-                    .getValue();
+            Property itemProperty = item.getItemProperty(cell.getPropertyId());
+            Object modelValue = itemProperty == null ? null
+                    : itemProperty.getValue();
 
             data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer
                     .encodeValue(modelValue, renderer, converter, getLocale()));
@@ -4583,6 +4614,12 @@ public class Grid extends AbstractComponent
     private boolean editorSaving = false;
     private FieldGroup editorFieldGroup = new CustomFieldGroup();
 
+    /**
+     * Poperty ID to Field mapping that stores editor fields set by
+     * {@link #setEditorField(Object, Field)}.
+     */
+    private Map<Object, Field<?>> editorFields = new HashMap<>();
+
     private CellStyleGenerator cellStyleGenerator;
     private RowStyleGenerator rowStyleGenerator;
 
@@ -5285,6 +5322,27 @@ public class Grid extends AbstractComponent
         return (GridState) super.getState(markAsDirty);
     }
 
+    /**
+     * Sets the column resize mode to use. The default mode is
+     * {@link ColumnResizeMode#ANIMATED}.
+     *
+     * @param mode
+     *            a ColumnResizeMode value
+     */
+    public void setColumnResizeMode(ColumnResizeMode mode) {
+        getState().columnResizeMode = mode;
+    }
+
+    /**
+     * Returns the current column resize mode. The default mode is
+     * {@link ColumnResizeMode#ANIMATED}.
+     *
+     * @return a ColumnResizeMode value
+     */
+    public ColumnResizeMode getColumnResizeMode() {
+        return getState(false).columnResizeMode;
+    }
+
     /**
      * Creates a new column based on a property id and appends it as the last
      * column.
@@ -6868,6 +6926,16 @@ public class Grid extends AbstractComponent
 
         Field<?> editor = editorFieldGroup.getField(propertyId);
 
+        // If field group has no field for this property, see if we have it
+        // stored
+        if (editor == null) {
+            editor = editorFields.get(propertyId);
+            if (editor != null) {
+                editorFieldGroup.bind(editor, propertyId);
+            }
+        }
+
+        // Otherwise try to build one
         try {
             if (editor == null) {
                 editor = editorFieldGroup.buildAndBind(propertyId);
@@ -6923,8 +6991,9 @@ public class Grid extends AbstractComponent
         editorFieldGroup.setItemDataSource(item);
 
         for (Column column : getColumns()) {
-            column.getState().editorConnector = getEditorField(
-                    column.getPropertyId());
+            column.getState().editorConnector = item
+                    .getItemProperty(column.getPropertyId()) == null ? null
+                            : getEditorField(column.getPropertyId());
         }
 
         editorActive = true;
@@ -6953,6 +7022,9 @@ public class Grid extends AbstractComponent
             field.setParent(this);
             editorFieldGroup.bind(field, propertyId);
         }
+
+        // Store field for this property for future reference
+        editorFields.put(propertyId, field);
     }
 
     /**
diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java
new file mode 100644 (file)
index 0000000..be6334f
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2000-2016 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.v7.tests.server.component.grid;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.v7.data.Item;
+import com.vaadin.v7.data.Property;
+import com.vaadin.v7.data.fieldgroup.FieldGroup;
+import com.vaadin.v7.data.util.AbstractInMemoryContainer;
+import com.vaadin.v7.data.util.BeanItem;
+import com.vaadin.v7.ui.Field;
+import com.vaadin.v7.ui.PasswordField;
+import com.vaadin.v7.ui.TextField;
+
+public class GridEditorMissingPropertyTest {
+
+    private static final String PROPERTY_NAME = "name";
+    private static final String PROPERTY_SIZE = "size";
+
+    private static final String FOLDER_NAME_BEFORE = "Folder name";
+    private static final String FOLDER_NAME_AFTER = "Modified folder name";
+    private static final String FILE_NAME_BEFORE = "File name";
+    private static final String FILE_NAME_AFTER = "Modified file name";
+    private static final String FILE_SIZE_BEFORE = "10kB";
+    private static final String FILE_SIZE_AFTER = "20MB";
+
+    private final Grid grid = new Grid();
+
+    // Test items
+    private final Folder folder = new Folder(FOLDER_NAME_BEFORE);
+    private final File file = new File(FILE_NAME_BEFORE, FILE_SIZE_BEFORE);
+
+    @Before
+    public void setup() throws SecurityException, NoSuchMethodException {
+        final BeanItem<Entry> folderItem = new BeanItem<>(folder);
+        final BeanItem<Entry> childItem = new BeanItem<>(file);
+
+        @SuppressWarnings("unchecked")
+        TestContainer container = new TestContainer(
+                Arrays.asList(folderItem, childItem),
+                Arrays.asList(PROPERTY_NAME, PROPERTY_SIZE));
+
+        grid.setContainerDataSource(container);
+        grid.setSelectionMode(Grid.SelectionMode.SINGLE);
+        grid.setEditorEnabled(true);
+    }
+
+    @Test
+    public void testBindFields() {
+        FieldGroup fieldGroup = grid.getEditorFieldGroup();
+
+        // Item with incomplete property set
+        fieldGroup.setItemDataSource(
+                grid.getContainerDataSource().getItem(folder));
+        grid.getColumn(PROPERTY_NAME).getEditorField(); // called in
+                                                        // grid.doEditItem
+        assertTrue("Properties in item should be bound",
+                fieldGroup.getBoundPropertyIds().contains(PROPERTY_NAME));
+        assertFalse("Properties not present in item should not be bound",
+                fieldGroup.getBoundPropertyIds().contains(PROPERTY_SIZE));
+        assertTrue("All of item's properties should be bound",
+                fieldGroup.getUnboundPropertyIds().isEmpty());
+
+        // Unbind all fields
+        fieldGroup.setItemDataSource(null);
+        assertTrue("No properties should be bound",
+                fieldGroup.getBoundPropertyIds().isEmpty());
+        assertTrue("No unbound properties should exist",
+                fieldGroup.getUnboundPropertyIds().isEmpty());
+
+        // Item with complete property set
+        fieldGroup
+                .setItemDataSource(grid.getContainerDataSource().getItem(file));
+        grid.getColumn(PROPERTY_NAME).getEditorField();
+        grid.getColumn(PROPERTY_SIZE).getEditorField();
+        assertTrue("Properties in item should be bound",
+                fieldGroup.getBoundPropertyIds().contains(PROPERTY_NAME));
+        assertTrue("Properties in item should be bound",
+                fieldGroup.getBoundPropertyIds().contains(PROPERTY_SIZE));
+        assertTrue("All of item's properties should be bound",
+                fieldGroup.getUnboundPropertyIds().isEmpty());
+
+        // Unbind all fields
+        fieldGroup.setItemDataSource(null);
+        assertTrue("No properties should be bound",
+                fieldGroup.getBoundPropertyIds().isEmpty());
+        assertTrue("No unbound properties should exist",
+                fieldGroup.getUnboundPropertyIds().isEmpty());
+    }
+
+    @Test
+    public void testSetEditorField() {
+        FieldGroup fieldGroup = grid.getEditorFieldGroup();
+        Field editorField = new PasswordField();
+
+        // Explicitly set editor field
+        fieldGroup.setItemDataSource(
+                grid.getContainerDataSource().getItem(folder));
+        grid.getColumn(PROPERTY_NAME).setEditorField(editorField);
+        assertTrue("Editor field should be the one that was previously set",
+                grid.getColumn(PROPERTY_NAME).getEditorField() == editorField);
+
+        // Reset item
+        fieldGroup.setItemDataSource(null);
+        fieldGroup
+                .setItemDataSource(grid.getContainerDataSource().getItem(file));
+        assertTrue("Editor field should be the one that was previously set",
+                grid.getColumn(PROPERTY_NAME).getEditorField() == editorField);
+    }
+
+    @Test
+    public void testEditCell() {
+        // Row with missing property
+        startEdit(folder);
+        assertEquals(folder, grid.getEditedItemId());
+        assertEquals(getEditedItem(),
+                grid.getEditorFieldGroup().getItemDataSource());
+
+        assertEquals(FOLDER_NAME_BEFORE,
+                grid.getColumn(PROPERTY_NAME).getEditorField().getValue());
+        try {
+            grid.getColumn(PROPERTY_SIZE).getEditorField();
+            fail("Grid.editorFieldGroup should throw BindException by default");
+        } catch (FieldGroup.BindException e) {
+            // BindException is thrown using the default FieldGroup
+        }
+        grid.cancelEditor();
+
+        // Row with all properties
+        startEdit(file);
+        assertEquals(file, grid.getEditedItemId());
+        assertEquals(getEditedItem(),
+                grid.getEditorFieldGroup().getItemDataSource());
+
+        assertEquals(FILE_NAME_BEFORE,
+                grid.getColumn(PROPERTY_NAME).getEditorField().getValue());
+        assertEquals(FILE_SIZE_BEFORE,
+                grid.getColumn(PROPERTY_SIZE).getEditorField().getValue());
+        grid.cancelEditor();
+    }
+
+    @Test
+    public void testCancelEditor() {
+        // Row with all properties
+        testCancel(file, PROPERTY_NAME, FILE_NAME_BEFORE, FILE_NAME_AFTER);
+        testCancel(file, PROPERTY_SIZE, FILE_SIZE_BEFORE, FILE_SIZE_AFTER);
+
+        // Row with missing property
+        testCancel(folder, PROPERTY_NAME, FOLDER_NAME_BEFORE,
+                FOLDER_NAME_AFTER);
+    }
+
+    private void testCancel(Object itemId, String propertyId,
+            String valueBefore, String valueAfter) {
+        startEdit(itemId);
+
+        TextField field = (TextField) grid.getColumn(propertyId)
+                .getEditorField();
+        field.setValue(valueAfter);
+
+        Property<?> datasource = field.getPropertyDataSource();
+
+        grid.cancelEditor();
+        assertFalse(grid.isEditorActive());
+        assertNull(grid.getEditedItemId());
+        assertFalse(field.isModified());
+        assertEquals("", field.getValue());
+        assertEquals(valueBefore, datasource.getValue());
+        assertNull(field.getPropertyDataSource());
+        assertNull(grid.getEditorFieldGroup().getItemDataSource());
+    }
+
+    @Test
+    public void testSaveEditor() throws Exception {
+        // Row with all properties
+        testSave(file, PROPERTY_SIZE, FILE_SIZE_BEFORE, FILE_SIZE_AFTER);
+
+        // Row with missing property
+        testSave(folder, PROPERTY_NAME, FOLDER_NAME_BEFORE, FOLDER_NAME_AFTER);
+    }
+
+    private void testSave(Object itemId, String propertyId, String valueBefore,
+            String valueAfter) throws Exception {
+        startEdit(itemId);
+        TextField field = (TextField) grid.getColumn(propertyId)
+                .getEditorField();
+
+        field.setValue(valueAfter);
+        assertEquals(valueBefore, field.getPropertyDataSource().getValue());
+
+        grid.saveEditor();
+        assertTrue(grid.isEditorActive());
+        assertFalse(field.isModified());
+        assertEquals(valueAfter, field.getValue());
+        assertEquals(valueAfter, getEditedProperty(propertyId).getValue());
+        grid.cancelEditor();
+    }
+
+    private Item getEditedItem() {
+        assertNotNull(grid.getEditedItemId());
+        return grid.getContainerDataSource().getItem(grid.getEditedItemId());
+    }
+
+    private Property<?> getEditedProperty(Object propertyId) {
+        return getEditedItem().getItemProperty(propertyId);
+    }
+
+    private void startEdit(Object itemId) {
+        grid.setEditorEnabled(true);
+        grid.editItem(itemId);
+        // Simulate succesful client response to actually start the editing.
+        grid.doEditItem();
+    }
+
+    private class TestContainer
+            extends AbstractInMemoryContainer<Object, String, BeanItem> {
+
+        private final List<BeanItem<Entry>> items;
+        private final List<String> pids;
+
+        public TestContainer(List<BeanItem<Entry>> items, List<String> pids) {
+            this.items = items;
+            this.pids = pids;
+        }
+
+        @Override
+        protected List<Object> getAllItemIds() {
+            List<Object> ids = new ArrayList<>();
+            for (BeanItem<Entry> item : items) {
+                ids.add(item.getBean());
+            }
+            return ids;
+        }
+
+        @Override
+        protected BeanItem<Entry> getUnfilteredItem(Object itemId) {
+            for (BeanItem<Entry> item : items) {
+                if (item.getBean().equals(itemId)) {
+                    return item;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public Collection<?> getContainerPropertyIds() {
+            return pids;
+        }
+
+        @Override
+        public Property getContainerProperty(Object itemId, Object propertyId) {
+            return getItem(itemId).getItemProperty(propertyId);
+        }
+
+        @Override
+        public Class<?> getType(Object propertyId) {
+            return String.class;
+        }
+    }
+
+    public class Entry {
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Entry(String name) {
+            this.name = name;
+        }
+    }
+
+    public class Folder extends Entry {
+
+        public Folder(String name) {
+            super(name);
+        }
+    }
+
+    public class File extends Entry {
+        private String size;
+
+        public File(String name, String size) {
+            super(name);
+            this.size = size;
+        }
+
+        public String getSize() {
+            return size;
+        }
+
+        public void setSize(String size) {
+            this.size = size;
+        }
+    }
+
+    private class Grid extends com.vaadin.v7.ui.Grid {
+        @Override
+        protected void doEditItem() {
+            super.doEditItem();
+        }
+    }
+}
diff --git a/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/ColumnResizeMode.java b/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/ColumnResizeMode.java
new file mode 100644 (file)
index 0000000..6f3de6a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2016 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.v7.shared.ui.grid;
+
+/**
+ * Collection of modes used for resizing columns in the Grid.
+ */
+public enum ColumnResizeMode {
+
+    /**
+     * When column resize mode is set to Animated, columns are resized as they
+     * are dragged.
+     */
+    ANIMATED,
+
+    /**
+     * When column resize mode is set to Simple, dragging to resize a column
+     * will show a marker, and the column will resize only after the mouse
+     * button or touch is released.
+     */
+    SIMPLE
+
+}
\ No newline at end of file
index 4befc1fe09ed6c4ee347db2684e2abc8d5d0fd92..a4c701c64b11bfe69cff36ea97568364d607d43a 100644 (file)
@@ -141,15 +141,20 @@ public class GridState extends TabIndexState {
         primaryStyleName = "v-grid";
     }
 
+    /**
+     * Column resize mode in grid.
+     */
+    public ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED;
+
     /**
      * Columns in grid.
      */
-    public List<GridColumnState> columns = new ArrayList<GridColumnState>();
+    public List<GridColumnState> columns = new ArrayList<>();
 
     /**
      * Column order in grid.
      */
-    public List<String> columnOrder = new ArrayList<String>();
+    public List<String> columnOrder = new ArrayList<>();
 
     public GridStaticSectionState header = new GridStaticSectionState();
 
index 1abf754584bded1e08c7c4439138bd7d4cd8bbcd..10a21c65d9e005e35708f359337f149caa85c8b6 100644 (file)
@@ -75,6 +75,8 @@
   }
 
   .#{$primaryStyleName}-body {
+    -ms-touch-action: none;
+    touch-action: none;
     z-index: 0;
     top: 0;
 
index fdbf2a0cf5f7309910f06f947411b9661b0f6833..2cbb1c86162a05dbbccf932459638c7ba68d825d 100644 (file)
@@ -276,6 +276,22 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co
     -ms-user-select: none;
     user-select: none;
   }
+  
+  .#{$primaryStyleName}-column-resize-simple-indicator {
+    position: absolute;
+    width: 3px;
+    top: 0px;
+    left: $v-grid-cell-padding-horizontal;
+    z-index: 9001;
+    background: #fff;
+    box-shadow: 0px 0px 5px #000;
+
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+  }
 
   // Footer
 
index 120640bd53e646e6700c466a517da97c06fba3aa..7b868ee542a7a66b14790e88c0d60b2ea9b787d3 100644 (file)
@@ -68,6 +68,7 @@ import com.vaadin.shared.Registration;
 import com.vaadin.shared.data.DataCommunicatorConstants;
 import com.vaadin.shared.data.sort.SortDirection;
 import com.vaadin.shared.ui.grid.AbstractGridExtensionState;
+import com.vaadin.shared.ui.grid.ColumnResizeMode;
 import com.vaadin.shared.ui.grid.ColumnState;
 import com.vaadin.shared.ui.grid.DetailsManagerState;
 import com.vaadin.shared.ui.grid.GridConstants;
@@ -79,6 +80,7 @@ import com.vaadin.shared.ui.grid.HeightMode;
 import com.vaadin.shared.ui.grid.SectionState;
 import com.vaadin.shared.util.SharedUtil;
 import com.vaadin.ui.Grid.FooterRow;
+import com.vaadin.ui.Grid.SelectionMode;
 import com.vaadin.ui.components.grid.AbstractSelectionModel;
 import com.vaadin.ui.components.grid.EditorComponentGenerator;
 import com.vaadin.ui.components.grid.EditorImpl;
@@ -845,7 +847,6 @@ public class Grid<T> extends AbstractListing<T>
             if (column != null && column.isResizable()) {
                 column.getState().width = pixels;
                 fireColumnResizeEvent(column, true);
-                markAsDirty();
             }
         }
     }
@@ -3465,6 +3466,27 @@ public class Grid<T> extends AbstractListing<T>
         return (GridState) super.getState(markAsDirty);
     }
 
+    /**
+     * Sets the column resize mode to use. The default mode is
+     * {@link ColumnResizeMode#ANIMATED}.
+     *
+     * @param mode
+     *            a ColumnResizeMode value
+     */
+    public void setColumnResizeMode(ColumnResizeMode mode) {
+        getState().columnResizeMode = mode;
+    }
+
+    /**
+     * Returns the current column resize mode. The default mode is
+     * {@link ColumnResizeMode#ANIMATED}.
+     *
+     * @return a ColumnResizeMode value
+     */
+    public ColumnResizeMode getColumnResizeMode() {
+        return getState(false).columnResizeMode;
+    }
+
     /**
      * Creates a new Editor instance. Can be overridden to create a custom
      * Editor. If the Editor is a {@link AbstractGridExtension}, it will be
@@ -3744,7 +3766,6 @@ public class Grid<T> extends AbstractListing<T>
             getDataCommunicator().setInMemorySorting(null);
             return;
         }
-
         sortOrder.addAll(order);
         sort(userOriginated);
     }
index f56a90be9514cad0a6a0fb0b59b69884a06100de..e4562f81ce287b91de1883b8fe915e901e04012f 100644 (file)
@@ -740,7 +740,7 @@ public class GridLayout extends AbstractLayout
 
         // Forget expands for removed columns
         if (columns < getColumns()) {
-            for (int i = columns - 1; i < getColumns(); i++) {
+            for (int i = columns; i < getColumns(); i++) {
                 columnExpandRatio.remove(i);
                 getState().explicitColRatios.remove(i);
             }
@@ -790,7 +790,7 @@ public class GridLayout extends AbstractLayout
         }
         // Forget expands for removed rows
         if (rows < getRows()) {
-            for (int i = rows - 1; i < getRows(); i++) {
+            for (int i = rows; i < getRows(); i++) {
                 rowExpandRatio.remove(i);
                 getState().explicitRowRatios.remove(i);
             }
index 1eabf2fb6250a93fa410d7fa59cb91612d73fe42..64b466612f9aeb299c3b8508b84d193ead3209ac 100644 (file)
@@ -75,6 +75,30 @@ public class GridLayoutTest {
         assertOrder(grid, new int[] { 0, 1, 2, 3 });
     }
 
+    @Test
+    public void removeRowsExpandRatiosPreserved() {
+        GridLayout gl = new GridLayout(3, 3);
+        gl.setRowExpandRatio(0, 0);
+        gl.setRowExpandRatio(1, 1);
+        gl.setRowExpandRatio(2, 2);
+
+        gl.setRows(2);
+        assertEquals(0, gl.getRowExpandRatio(0), 0);
+        assertEquals(1, gl.getRowExpandRatio(1), 0);
+    }
+
+    @Test
+    public void removeColsExpandRatiosPreserved() {
+        GridLayout gl = new GridLayout(3, 3);
+        gl.setColumnExpandRatio(0, 0);
+        gl.setColumnExpandRatio(1, 1);
+        gl.setColumnExpandRatio(2, 2);
+
+        gl.setColumns(2);
+        assertEquals(0, gl.getColumnExpandRatio(0), 0);
+        assertEquals(1, gl.getColumnExpandRatio(1), 0);
+    }
+
     private void assertContentPositions(GridLayout grid) {
         assertEquals(grid.getComponentCount(), children.length);
         int c = 0;
diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnResizeMode.java b/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnResizeMode.java
new file mode 100644 (file)
index 0000000..a19a2bb
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2016 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.shared.ui.grid;
+
+/**
+ * Collection of modes used for resizing columns in the Grid.
+ */
+public enum ColumnResizeMode {
+
+    /**
+     * When column resize mode is set to Animated, columns
+     * are resized as they are dragged.
+     */
+    ANIMATED,
+
+    /**
+     * When column resize mode is set to Simple, dragging to resize
+     * a column will show a marker, and the column will resize only
+     * after the mouse button or touch is released.
+     */
+    SIMPLE
+
+}
index a8abf82c759dfd6e4088e19779db8449c4fa70d5..1074b181d8da49d6cabaa2ea1ba38b1127b97a12 100644 (file)
@@ -99,6 +99,11 @@ public class GridState extends AbstractSingleSelectState {
         primaryStyleName = "v-grid";
     }
 
+    /**
+     * Column resize mode in grid.
+     */
+    public ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED;
+
     /** The state of the header section. */
     public SectionState header = new SectionState();
 
index c91c301fa7f0186ea6d4b7f49fabcdfb3d808721..895d9ab97537547e76c7655a72ef3e02d08506e6 100644 (file)
@@ -75,6 +75,8 @@
   }
 
   .#{$primaryStyleName}-body {
+    -ms-touch-action: none;
+    touch-action: none;
     z-index: 0;
     top: 0;
 
index 849ff7a1e4d53f0708f715d4562af9960d4394f4..b0279ecea2853b009b96cb48ca9e2786483f1945 100644 (file)
@@ -306,6 +306,22 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co
     -ms-user-select: none;
     user-select: none;
   }
+  
+  .#{$primaryStyleName}-column-resize-simple-indicator {
+    position: absolute;
+    width: 3px;
+    top: 0px;
+    left: $v-grid-cell-padding-horizontal;
+    z-index: 9001;
+    background: #fff;
+    box-shadow: 0px 0px 5px #000;
+
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+  }
 
   // Footer
 
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResize.java b/uitest/src/main/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResize.java
new file mode 100644 (file)
index 0000000..580f2d3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2000-2016 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.tests.components.grid;
+
+import java.util.stream.IntStream;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Grid;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class HorizontalScrollAfterResize extends AbstractTestUI {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        final Grid<Integer> grid = new Grid<>();
+        grid.setWidth("100%");
+        grid.setHeight("350px");
+        grid.setCaption("My Grid");
+
+        for (int i = 0; i < 10; i++) {
+            char ch = (char) ('a' + i);
+            grid.addColumn(item -> "test").setCaption("" + ch);
+        }
+
+        grid.setItems(IntStream.of(0, 100).mapToObj(Integer::valueOf));
+
+        addComponents(grid);
+    }
+
+    @Override
+    protected String getTestDescription() {
+        return "Don't add more than one scroll handler";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return 19189; // also 20254, 19622
+    }
+
+}
index c074bd4d14bbc247f86b70eb255c042c54eb2e85..e7ab23961b192cbbea527a0e552d4d9e5c3355e6 100644 (file)
@@ -22,6 +22,7 @@ import com.vaadin.event.selection.SingleSelectionEvent;
 import com.vaadin.server.VaadinRequest;
 import com.vaadin.shared.Registration;
 import com.vaadin.shared.data.sort.SortDirection;
+import com.vaadin.shared.ui.grid.ColumnResizeMode;
 import com.vaadin.shared.ui.grid.HeightMode;
 import com.vaadin.tests.components.AbstractTestUIWithLog;
 import com.vaadin.ui.Button;
@@ -233,6 +234,10 @@ public class GridBasics extends AbstractTestUIWithLog {
                 .getSelectionModel())
                         .addSingleSelectionListener(this::onSingleSelect);
 
+        grid.addColumnResizeListener(
+                event -> log("ColumnResizeEvent: isUserOriginated? "
+                        + event.isUserOriginated()));
+
         layout.addComponent(createMenu());
         layout.addComponent(grid);
         addComponent(layout);
@@ -345,6 +350,11 @@ public class GridBasics extends AbstractTestUIWithLog {
                     item -> grid.sort(col, SortDirection.DESCENDING));
         }
         columnsMenu.addItem("Clear sort", item -> grid.clearSortOrder());
+
+        columnsMenu.addItem("Simple resize mode",
+                item -> grid.setColumnResizeMode(item.isChecked()
+                        ? ColumnResizeMode.SIMPLE : ColumnResizeMode.ANIMATED))
+                .setCheckable(true);
     }
 
     private void createSizeMenu(MenuItem sizeMenu) {
diff --git a/uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java b/uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java
new file mode 100644 (file)
index 0000000..3db3e20
--- /dev/null
@@ -0,0 +1,125 @@
+package com.vaadin.tests.components.textfield;
+
+import com.vaadin.data.HasValue;
+import com.vaadin.data.HasValue.ValueChangeListener;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.ValueChangeMode;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.tests.util.TestUtils;
+import com.vaadin.ui.AbstractTextField;
+import com.vaadin.ui.TextArea;
+import com.vaadin.ui.TextField;
+
+public class TextChangeEvents extends AbstractTestUIWithLog {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+
+        TextField textField = new TextField("Default");
+
+        ValueChangeListener<String> listener = event -> log(
+                "Text change event for  " + event.getComponent().getCaption()
+                        + ", text content currently:'" + event.getValue()
+                        + "' Cursor at index:"
+                        + ((AbstractTextField) event.getSource())
+                                .getCursorPosition());
+
+        textField.addValueChangeListener(listener);
+        addComponent(textField);
+
+        TextField eager = new TextField("Eager");
+        eager.addValueChangeListener(listener);
+        eager.setValueChangeMode(ValueChangeMode.EAGER);
+        addComponent(eager);
+
+        TextField timeout = new TextField("Timeout 3s");
+        timeout.addValueChangeListener(listener);
+        timeout.setValueChangeMode(ValueChangeMode.EAGER);
+        timeout.setValueChangeTimeout(3000);
+        addComponent(timeout);
+
+        TextArea textArea = new TextArea("Default text area");
+        textArea.addValueChangeListener(listener);
+        addComponent(textArea);
+
+        TextArea textAreaTimeout = new TextArea("Timeout 3s");
+        textAreaTimeout.addValueChangeListener(listener);
+        textAreaTimeout.setValueChangeMode(ValueChangeMode.TIMEOUT);
+        textAreaTimeout.setValueChangeTimeout(3000);
+        addComponent(textAreaTimeout);
+
+        VaadinDeveloperNameField vd = new VaadinDeveloperNameField();
+        vd.addValueChangeListener(listener);
+        addComponent(vd);
+    }
+
+    @Override
+    protected String getTestDescription() {
+        return "Simple TextChangeEvent test cases.";
+    }
+
+    /**
+     * "Autosuggest"
+     *
+     * Known issue is timing if suggestion comes while typing more content. IMO
+     * we will not support this kind of features in default TextField, but
+     * hopefully make it easily extendable to perfect suggest feature. MT
+     * 2010-10
+     *
+     */
+    private class VaadinDeveloperNameField extends TextField
+            implements HasValue.ValueChangeListener<String> {
+        private String[] names = new String[] { "Matti Tahvonen",
+                "Marc Englund", "Joonas Lehtinen", "Jouni Koivuviita",
+                "Marko Grönroos", "Artur Signell" };
+
+        public VaadinDeveloperNameField() {
+            setCaption("Start typing 'old' Vaadin developers.");
+            addValueChangeListener(this);
+            setStyleName("nomatch");
+        }
+
+        @Override
+        public void attach() {
+            super.attach();
+            TestUtils.injectCSS(getUI(), ".match { background:green ;} "
+                    + ".nomatch {background:red;}");
+        }
+
+        @Override
+        public void valueChange(HasValue.ValueChangeEvent<String> event) {
+            boolean atTheEndOfText = event.getValue()
+                    .length() == getCursorPosition();
+            String match = findMatch(event.getValue());
+            if (match != null) {
+                setStyleName("match");
+                String curText = event.getValue();
+                int matchlenght = curText.length();
+                // autocomplete if garret is at the end of the text
+                if (atTheEndOfText) {
+                    suggest(match, matchlenght);
+                }
+            } else {
+                setStyleName("nomatch");
+            }
+        }
+
+        private void suggest(String match, int matchlenght) {
+            setValue(match);
+            setSelection(matchlenght, match.length() - matchlenght);
+        }
+
+        private String findMatch(String currentTextContent) {
+            if (currentTextContent.length() > 0) {
+                for (int i = 0; i < names.length; i++) {
+                    if (names[i].startsWith(currentTextContent)) {
+                        return names[i];
+                    }
+                }
+            }
+            return null;
+        }
+
+    }
+
+}
diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMissingProperty.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMissingProperty.java
new file mode 100644 (file)
index 0000000..99b2a53
--- /dev/null
@@ -0,0 +1,129 @@
+package com.vaadin.v7.tests.components.grid;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.v7.data.Property;
+import com.vaadin.v7.data.util.AbstractInMemoryContainer;
+import com.vaadin.v7.data.util.BeanItem;
+import com.vaadin.v7.ui.Grid;
+
+public class GridMissingProperty extends AbstractTestUI {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        final Grid grid = new Grid();
+
+        final Folder folder = new Folder("Folder name");
+        final BeanItem<Entry> folderItem = new BeanItem<>(folder);
+
+        final File file = new File("File name", "10kB");
+        final BeanItem<Entry> fileItem = new BeanItem<>(file);
+
+        @SuppressWarnings("unchecked")
+        TestContainer container = new TestContainer(
+                Arrays.asList(folderItem, fileItem),
+                Arrays.asList("name", "size"));
+
+        grid.setContainerDataSource(container);
+        grid.setSelectionMode(Grid.SelectionMode.SINGLE);
+        grid.setEditorEnabled(true);
+
+        addComponent(grid);
+    }
+
+    @Override
+    protected String getTestDescription() {
+        return "Grid Editor should not throw exception even when items are missing properties.";
+    }
+
+    private class TestContainer
+            extends AbstractInMemoryContainer<Object, String, BeanItem> {
+
+        private final List<BeanItem<Entry>> items;
+        private final List<String> pids;
+
+        public TestContainer(List<BeanItem<Entry>> items, List<String> pids) {
+            this.items = items;
+            this.pids = pids;
+        }
+
+        @Override
+        protected List<Object> getAllItemIds() {
+            List<Object> ids = new ArrayList<>();
+            for (BeanItem<Entry> item : items) {
+                ids.add(item.getBean());
+            }
+            return ids;
+        }
+
+        @Override
+        protected BeanItem<Entry> getUnfilteredItem(Object itemId) {
+            for (BeanItem<Entry> item : items) {
+                if (item.getBean().equals(itemId)) {
+                    return item;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public Collection<?> getContainerPropertyIds() {
+            return pids;
+        }
+
+        @Override
+        public Property getContainerProperty(Object itemId, Object propertyId) {
+            return getItem(itemId).getItemProperty(propertyId);
+        }
+
+        @Override
+        public Class<?> getType(Object propertyId) {
+            return String.class;
+        }
+    }
+
+    public class Entry {
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Entry(String name) {
+            this.name = name;
+        }
+    }
+
+    public class Folder extends Entry {
+
+        public Folder(String name) {
+            super(name);
+        }
+    }
+
+    public class File extends Entry {
+        private String size;
+
+        public File(String name, String size) {
+            super(name);
+            this.size = size;
+        }
+
+        public String getSize() {
+            return size;
+        }
+
+        public void setSize(String size) {
+            this.size = size;
+        }
+    }
+}
index 0e8f16889ca4e9105c46e42b8e43baa3a9db28a1..5c5acf0054f5b9eb571f237a4a93ca801a367284 100644 (file)
@@ -51,9 +51,10 @@ import com.vaadin.v7.data.Property.ValueChangeListener;
 import com.vaadin.v7.data.fieldgroup.FieldGroup.CommitException;
 import com.vaadin.v7.data.util.IndexedContainer;
 import com.vaadin.v7.event.ItemClickEvent;
-import com.vaadin.v7.event.SelectionEvent;
 import com.vaadin.v7.event.ItemClickEvent.ItemClickListener;
+import com.vaadin.v7.event.SelectionEvent;
 import com.vaadin.v7.event.SelectionEvent.SelectionListener;
+import com.vaadin.v7.shared.ui.grid.ColumnResizeMode;
 import com.vaadin.v7.shared.ui.grid.GridStaticCellType;
 import com.vaadin.v7.shared.ui.grid.HeightMode;
 import com.vaadin.v7.ui.Field;
@@ -1267,6 +1268,14 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> {
                         grid.getColumns().get(0).setMaximumWidth(30);
                     }
                 }, null);
+        createBooleanAction("Simple resize mode", "Columns", false,
+                new Command<Grid, Boolean>() {
+                    @Override
+                    public void execute(Grid g, Boolean value, Object data) {
+                        g.setColumnResizeMode(value ? ColumnResizeMode.SIMPLE
+                                : ColumnResizeMode.ANIMATED);
+                    }
+                });
     }
 
     private static String getColumnProperty(int c) {
diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/textfield/TextChangeEvents.java b/uitest/src/main/java/com/vaadin/v7/tests/components/textfield/TextChangeEvents.java
new file mode 100644 (file)
index 0000000..fc9ccfe
--- /dev/null
@@ -0,0 +1,136 @@
+package com.vaadin.v7.tests.components.textfield;
+
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.tests.util.Log;
+import com.vaadin.tests.util.TestUtils;
+import com.vaadin.v7.event.FieldEvents.TextChangeEvent;
+import com.vaadin.v7.event.FieldEvents.TextChangeListener;
+import com.vaadin.v7.ui.AbstractTextField.TextChangeEventMode;
+import com.vaadin.v7.ui.TextArea;
+import com.vaadin.v7.ui.TextField;
+
+public class TextChangeEvents extends TestBase {
+    Log l = new Log(10);
+
+    @Override
+    protected void setup() {
+
+        TextField tf = new TextField("Default");
+
+        TextChangeListener inputEventListener = new TextChangeListener() {
+
+            @Override
+            public void textChange(TextChangeEvent event) {
+                l.log("Text change event for  "
+                        + event.getComponent().getCaption()
+                        + ", text content currently:'" + event.getText()
+                        + "' Cursor at index:" + event.getCursorPosition());
+            }
+        };
+
+        tf.addListener(inputEventListener);
+
+        getLayout().addComponent(tf);
+
+        TextField eager = new TextField("Eager");
+        eager.addListener(inputEventListener);
+        eager.setTextChangeEventMode(TextChangeEventMode.EAGER);
+        getLayout().addComponent(eager);
+
+        TextField to = new TextField("Timeout 3s");
+        to.addListener(inputEventListener);
+        to.setTextChangeEventMode(TextChangeEventMode.TIMEOUT);
+        to.setTextChangeTimeout(3000);
+        getLayout().addComponent(to);
+
+        TextArea ta = new TextArea("Default text area");
+        ta.addListener(inputEventListener);
+        getLayout().addComponent(ta);
+
+        TextArea tat = new TextArea("Timeout 3s");
+        tat.addListener(inputEventListener);
+        tat.setTextChangeEventMode(TextChangeEventMode.TIMEOUT);
+        tat.setTextChangeTimeout(3000);
+        getLayout().addComponent(tat);
+
+        VaadinDeveloperNameField vd = new VaadinDeveloperNameField();
+        vd.addListener(inputEventListener);
+        getLayout().addComponent(vd);
+
+        getLayout().addComponent(l);
+    }
+
+    @Override
+    protected String getDescription() {
+        return "Simple TextChangeEvent test cases.";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return null;
+    }
+
+    /**
+     * "Autosuggest"
+     *
+     * Known issue is timing if suggestion comes while typing more content. IMO
+     * we will not support this kind of features in default TextField, but
+     * hopefully make it easily extendable to perfect suggest feature. MT
+     * 2010-10
+     *
+     */
+    private class VaadinDeveloperNameField extends TextField
+            implements TextChangeListener {
+        private String[] names = new String[] { "Matti Tahvonen",
+                "Marc Englund", "Joonas Lehtinen", "Jouni Koivuviita",
+                "Marko Grönroos", "Artur Signell" };
+
+        public VaadinDeveloperNameField() {
+            setCaption("Start typing 'old' Vaadin developers.");
+            addListener((TextChangeListener) this);
+            setStyleName("nomatch");
+        }
+
+        @Override
+        public void attach() {
+            super.attach();
+            TestUtils.injectCSS(getUI(), ".match { background:green ;} "
+                    + ".nomatch {background:red;}");
+        }
+
+        @Override
+        public void textChange(TextChangeEvent event) {
+            boolean atTheEndOfText = event.getText()
+                    .length() == getCursorPosition();
+            String match = findMatch(event.getText());
+            if (match != null) {
+                setStyleName("match");
+                String curText = event.getText();
+                int matchlenght = curText.length();
+                // autocomplete if garret is at the end of the text
+                if (atTheEndOfText) {
+                    suggest(match, matchlenght);
+                }
+            } else {
+                setStyleName("nomatch");
+            }
+        }
+
+        private void suggest(String match, int matchlenght) {
+            setValue(match);
+            setSelectionRange(matchlenght, match.length() - matchlenght);
+        }
+
+        private String findMatch(String currentTextContent) {
+            if (currentTextContent.length() > 0) {
+                for (int i = 0; i < names.length; i++) {
+                    if (names[i].startsWith(currentTextContent)) {
+                        return names[i];
+                    }
+                }
+            }
+            return null;
+        }
+    }
+
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResizeTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResizeTest.java
new file mode 100644 (file)
index 0000000..fcc9681
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2000-2016 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.tests.components.grid;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.Dimension;
+import org.openqa.selenium.Point;
+
+import com.vaadin.testbench.elements.GridElement;
+import com.vaadin.v7.tests.components.grid.basicfeatures.GridBasicFeaturesTest;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class HorizontalScrollAfterResizeTest extends GridBasicFeaturesTest {
+
+    /**
+     * The behavior without the fix differs across different browsers but
+     * scenario should work everywhere.
+     */
+    @Test
+    public void scrollAfterResize() {
+        getDriver().manage().window().setSize(new Dimension(600, 400));
+        openTestURL();
+        getDriver().manage().window().setSize(new Dimension(200, 400));
+
+        // First scroll to the right
+        scrollGridHorizontallyTo(600);
+        Point locationAfterFirstScroll = $(GridElement.class).first()
+                .getCell(0, 9).getLocation();
+
+        // resize back
+        getDriver().manage().window().setSize(new Dimension(600, 400));
+        // shrink again
+        getDriver().manage().window().setSize(new Dimension(200, 400));
+
+        // second scroll to the right
+        scrollGridHorizontallyTo(600);
+
+        Point lolocationAfterSecondScrollcation = $(GridElement.class).first()
+                .getCell(0, 9).getLocation();
+
+        // With the bug scrolling doesn't happen. Location should be the same as
+        // first time
+        Assert.assertEquals(locationAfterFirstScroll,
+                lolocationAfterSecondScrollcation);
+    }
+
+    @Override
+    protected Class<?> getUIClass() {
+        return HorizontalScrollAfterResize.class;
+    }
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java
new file mode 100644 (file)
index 0000000..f92366c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2000-2016 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.tests.components.grid.basicfeatures;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+import com.vaadin.testbench.By;
+import com.vaadin.testbench.customelements.GridElement;
+import com.vaadin.testbench.parallel.TestCategory;
+import com.vaadin.tests.components.grid.basics.GridBasicsTest;
+
+@TestCategory("grid")
+public class GridColumnResizeModeTest extends GridBasicsTest {
+
+    @Before
+    public void before() {
+        openTestURL();
+    }
+
+    @Test
+    public void testSimpleResizeModeToggle() throws Exception {
+
+        GridElement grid = getGridElement();
+
+        List<WebElement> handles = grid
+                .findElements(By.className("v-grid-column-resize-handle"));
+        WebElement handle = handles.get(1);
+
+        Actions drag1 = new Actions(getDriver()).moveToElement(handle)
+                .clickAndHold();
+        Actions drag2 = new Actions(getDriver()).moveByOffset(-50, 0);
+        Actions drag3 = new Actions(getDriver()).moveByOffset(100, 0);
+        Actions dragEndAction = new Actions(getDriver()).release()
+                .moveToElement(grid);
+
+        selectMenuPath("Component", "Columns", "Simple resize mode");
+        sleep(250);
+
+        drag1.perform();
+        sleep(500);
+        drag2.perform();
+        sleep(500);
+        drag3.perform();
+        sleep(500);
+
+        // Make sure we find at least one simple resize mode splitter
+        assertElementPresent(
+                By.className("v-grid-column-resize-simple-indicator"));
+
+        dragEndAction.perform();
+
+        // Make sure it went away
+        assertElementNotPresent(
+                By.className("v-grid-column-resize-simple-indicator"));
+
+        // See that we got a resize event
+        sleep(500);
+        Assert.assertTrue("Log shows resize event", getLogRow(0)
+                .contains("ColumnResizeEvent: isUserOriginated? true"));
+
+    }
+
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java b/uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java
new file mode 100644 (file)
index 0000000..5693400
--- /dev/null
@@ -0,0 +1,50 @@
+package com.vaadin.tests.components.textfield;
+
+import org.junit.Test;
+
+import com.vaadin.testbench.elements.TextAreaElement;
+import com.vaadin.testbench.elements.TextFieldElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class TextChangeEventsTest extends SingleBrowserTest {
+
+    @Test
+    public void textAreaWaitsForTextChangeEvents() {
+        openTestURL();
+
+        TextAreaElement taDefault = $(TextAreaElement.class)
+                .caption("Default text area").first();
+        taDefault.sendKeys("abc");
+        waitUntil(driver -> getLogRow(0).equals(
+                "1. Text change event for Default text area, text content currently:'abc' Cursor at index:3"));
+
+        TextAreaElement taTimeout = $(TextAreaElement.class)
+                .caption("Timeout 3s").first();
+        taTimeout.sendKeys("abc");
+        waitUntil(driver -> getLogRow(0).equals(
+                "2. Text change event for Timeout 3s, text content currently:'abc' Cursor at index:3"));
+    }
+
+    @Test
+    public void textFieldWaitsForTextChangeEvents() {
+        openTestURL();
+
+        TextFieldElement tfDefault = $(TextFieldElement.class)
+                .caption("Default").first();
+        tfDefault.sendKeys("abc");
+        waitUntil(driver -> getLogRow(0).equals(
+                "1. Text change event for Default, text content currently:'abc' Cursor at index:3"));
+
+        TextFieldElement tfEager = $(TextFieldElement.class).caption("Eager")
+                .first();
+        tfEager.sendKeys("abc");
+        waitUntil(driver -> getLogRow(0).contains(
+                "Text change event for Eager, text content currently:'abc' Cursor at index:3"));
+
+        TextFieldElement tfTimeout = $(TextFieldElement.class)
+                .caption("Timeout 3s").first();
+        tfTimeout.sendKeys("abc");
+        waitUntil(driver -> getLogRow(0).contains(
+                "Text change event for Timeout 3s, text content currently:'abc' Cursor at index:3"));
+    }
+}
diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMissingPropertyTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMissingPropertyTest.java
new file mode 100644 (file)
index 0000000..88262dc
--- /dev/null
@@ -0,0 +1,73 @@
+package com.vaadin.v7.tests.components.grid;
+
+import org.junit.Test;
+
+import com.vaadin.testbench.elements.GridElement;
+import com.vaadin.testbench.elements.GridElement.GridEditorElement;
+import com.vaadin.testbench.elements.TextFieldElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GridMissingPropertyTest extends SingleBrowserTest {
+
+    @Test
+    public void testCellEditable() {
+        openTestURL();
+        GridElement grid = $(GridElement.class).first();
+
+        // Row with missing property
+        grid.getCell(0, 0).doubleClick();
+        GridEditorElement editor = grid.getEditor();
+
+        assertTrue("Cell with property should be editable",
+                editor.isEditable(0));
+        assertFalse("Cell without property should not be editable",
+                editor.isEditable(1));
+
+        editor.cancel();
+
+        // Row with all properties
+        grid.getCell(1, 0).doubleClick();
+        editor = grid.getEditor();
+
+        assertTrue("Cell with property should be editable",
+                editor.isEditable(0));
+        assertTrue("Cell with property should be editable",
+                editor.isEditable(1));
+
+        editor.cancel();
+    }
+
+    @Test
+    public void testEditCell() {
+        openTestURL();
+        GridElement grid = $(GridElement.class).first();
+
+        GridEditorElement editor;
+        TextFieldElement editorField;
+
+        grid.getCell(0, 0).doubleClick();
+        editor = grid.getEditor();
+        editorField = editor.getField(0).wrap(TextFieldElement.class);
+        editorField.setValue("New Folder Name");
+        editor.save();
+        assertEquals("New Folder Name", grid.getCell(0, 0).getText());
+
+        grid.getCell(1, 0).doubleClick();
+        editor = grid.getEditor();
+        editorField = editor.getField(1).wrap(TextFieldElement.class);
+        editorField.setValue("10 MB");
+        editor.save();
+        assertEquals("10 MB", grid.getCell(1, 1).getText());
+
+        grid.getCell(1, 0).doubleClick();
+        editor = grid.getEditor();
+        editorField = editor.getField(0).wrap(TextFieldElement.class);
+        editorField.setValue("New File Name");
+        editor.save();
+        assertEquals("New File Name", grid.getCell(1, 0).getText());
+    }
+}
diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java
new file mode 100644 (file)
index 0000000..f287a2a
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2016 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.v7.tests.components.grid.basicfeatures;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+import com.vaadin.testbench.By;
+import com.vaadin.testbench.customelements.GridElement;
+import com.vaadin.testbench.parallel.TestCategory;
+
+@TestCategory("grid")
+public class GridColumnResizeModeTest extends GridBasicFeaturesTest {
+
+    @Before
+    public void before() {
+        openTestURL();
+    }
+
+    @Test
+    public void testSimpleResizeModeToggle() throws Exception {
+
+        GridElement grid = getGridElement();
+
+        List<WebElement> handles = grid
+                .findElements(By.className("v-grid-column-resize-handle"));
+        WebElement handle = handles.get(1);
+
+        Actions drag1 = new Actions(getDriver()).moveToElement(handle)
+                .clickAndHold();
+        Actions drag2 = new Actions(getDriver()).moveByOffset(-50, 0);
+        Actions drag3 = new Actions(getDriver()).moveByOffset(100, 0);
+        Actions dragEndAction = new Actions(getDriver()).release()
+                .moveToElement(grid);
+
+        selectMenuPath("Component", "Columns", "Simple resize mode");
+        sleep(250);
+
+        drag1.perform();
+        sleep(500);
+        drag2.perform();
+        sleep(500);
+        drag3.perform();
+        sleep(500);
+
+        // Make sure we find at least one simple resize mode splitter
+        assertElementPresent(
+                By.className("v-grid-column-resize-simple-indicator"));
+
+        dragEndAction.perform();
+
+        // Make sure it went away
+        assertElementNotPresent(
+                By.className("v-grid-column-resize-simple-indicator"));
+
+        // See that we got a resize event
+        sleep(500);
+        Assert.assertEquals("Log shows resize event", getLogRow(0),
+                "3. ColumnResizeEvent: isUserOriginated? true");
+
+    }
+
+}
\ No newline at end of file
diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/textfield/TextChangeEventsTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/textfield/TextChangeEventsTest.java
new file mode 100644 (file)
index 0000000..c6880fc
--- /dev/null
@@ -0,0 +1,58 @@
+package com.vaadin.v7.tests.components.textfield;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.testbench.elements.TextAreaElement;
+import com.vaadin.testbench.elements.TextFieldElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class TextChangeEventsTest extends SingleBrowserTest {
+
+    @Test
+    public void textAreaWaitsForTextChangeEvents() {
+        openTestURL();
+
+        TextAreaElement taDefault = $(TextAreaElement.class)
+                .caption("Default text area").first();
+        taDefault.sendKeys("abc");
+        Assert.assertEquals(
+                "1. Text change event for Default text area, text content currently:'abc' Cursor at index:3",
+                getLogRow(0));
+
+        TextAreaElement taTimeout = $(TextAreaElement.class)
+                .caption("Timeout 3s").first();
+        taTimeout.sendKeys("abc");
+        Assert.assertEquals(
+                "2. Text change event for Timeout 3s, text content currently:'abc' Cursor at index:3",
+                getLogRow(0));
+
+    }
+
+    @Test
+    public void textFieldWaitsForTextChangeEvents() {
+        openTestURL();
+
+        TextFieldElement tfDefault = $(TextFieldElement.class)
+                .caption("Default").first();
+        tfDefault.sendKeys("abc");
+        Assert.assertEquals(
+                "1. Text change event for Default, text content currently:'abc' Cursor at index:3",
+                getLogRow(0));
+
+        TextFieldElement tfEager = $(TextFieldElement.class).caption("Eager")
+                .first();
+        tfEager.sendKeys("abc");
+        Assert.assertEquals(
+                "2. Text change event for Eager, text content currently:'abc' Cursor at index:3",
+                getLogRow(0));
+
+        TextFieldElement tfTimeout = $(TextFieldElement.class)
+                .caption("Timeout 3s").first();
+        tfTimeout.sendKeys("abc");
+        Assert.assertEquals(
+                "3. Text change event for Timeout 3s, text content currently:'abc' Cursor at index:3",
+                getLogRow(0));
+
+    }
+}
\ No newline at end of file