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;
.map(this::getColumn).toArray(size -> new Column[size]));
}
+ @OnStateChange("columnResizeMode")
+ void updateColumnResizeMode() {
+ getWidget().setColumnResizeMode(getState().columnResizeMode);
+ }
+
/**
* Updates the grid header section on state change.
*/
}
}
- 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());
}
}
public void onFocus(FocusEvent event) {
addStyleDependentName(CLASSNAME_FOCUS);
}
+
}
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.
* @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();
}
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.
*
@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;
}
@Override
public void onDragCancel() {
removeDraggingStyle();
- userCallback.onCancel();
+ if (userCallback != null) {
+ userCallback.onCancel();
+ }
}
private void addDraggingStyle() {
});
}
+ /**
+ * 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.
*
// 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;
+ }
}
}
boolean offsetSizeBecomesGreaterThanScrollSize = showsScrollHandle()
&& newOffsetSizeIsGreaterThanScrollSize;
if (offsetSizeBecomesGreaterThanScrollSize && getScrollPos() != 0) {
+ if (offsetSizeTemporaryScrollHandler != null) {
+ offsetSizeTemporaryScrollHandler.removeHandler();
+ }
// must be a field because Java insists.
offsetSizeTemporaryScrollHandler = addScrollHandler(
new ScrollHandler() {
*/
boolean delayedSizeSet = !BrowserInfo.get().isFirefox();
if (delayedSizeSet) {
+ if (scrollSizeTemporaryScrollHandler != null) {
+ scrollSizeTemporaryScrollHandler.removeHandler();
+ }
scrollSizeTemporaryScrollHandler = addScrollHandler(
new ScrollHandler() {
@Override
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"
/*-{
return this.targetTouches[0].pageY;
}-*/;
+
+ public native String getPointerType()
+ /*-{
+ return this.pointerType;
+ }-*/;
}
private final Escalator escalator;
}
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();
}
};
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);
}
}
+ // 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);
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
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";
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
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
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();
+ }
}
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;
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;
private AutoScroller autoScroller = new AutoScroller(this);
+ private ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED;
+
private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() {
private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() {
&& 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);
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);
} 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));
}
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);
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) {
}
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());
}
}
- Set<String> removedDetails = new HashSet<String>();
+ Set<String> removedDetails = new HashSet<>();
for (Entry<String, ComponentConnector> entry : idToDetailsMap
.entrySet()) {
ComponentConnector connector = entry.getValue();
/**
* 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
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();
}
}
+ // Column resize mode
+ if (stateChangeEvent.hasPropertyChanged("columnResizeMode")) {
+ getWidget().setColumnResizeMode(getState().columnResizeMode);
+ }
+
// Header and footer
if (stateChangeEvent.hasPropertyChanged("header")) {
updateHeaderFromState(getState().header);
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);
}
}
private void onSortStateChange() {
- List<SortOrder> sortOrder = new ArrayList<SortOrder>();
+ List<SortOrder> sortOrder = new ArrayList<>();
String[] sortColumns = getState().sortColumns;
SortDirection[] sortDirs = getState().sortDirs;
* @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)) {
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;
*/
@Deprecated
public class VTextField extends TextBoxBase implements Field, ChangeHandler,
- FocusHandler, BlurHandler, KeyDownHandler {
+ FocusHandler, BlurHandler, KeyDownHandler, DeferredWorker {
/**
* The input node CSS classname.
}
possibleInputError = false;
}
+
+ @Override
+ public boolean isWorkPending() {
+ return scheduled;
+ }
}
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;
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;
private AutoScroller autoScroller = new AutoScroller(this);
+ private ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED;
+
private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() {
private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() {
&& 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);
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) {
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));
}
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;
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.
*/
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;
}
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));
+ }
+ }
+ }
}
/**
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()));
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;
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.
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);
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;
field.setParent(this);
editorFieldGroup.bind(field, propertyId);
}
+
+ // Store field for this property for future reference
+ editorFields.put(propertyId, field);
}
/**
--- /dev/null
+/*
+ * 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();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
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();
}
.#{$primaryStyleName}-body {
+ -ms-touch-action: none;
+ touch-action: none;
z-index: 0;
top: 0;
-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
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;
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;
if (column != null && column.isResizable()) {
column.getState().width = pixels;
fireColumnResizeEvent(column, true);
- markAsDirty();
}
}
}
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
getDataCommunicator().setInMemorySorting(null);
return;
}
-
sortOrder.addAll(order);
sort(userOriginated);
}
// 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);
}
}
// 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);
}
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;
--- /dev/null
+/*
+ * 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
+
+}
primaryStyleName = "v-grid";
}
+ /**
+ * Column resize mode in grid.
+ */
+ public ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED;
+
/** The state of the header section. */
public SectionState header = new SectionState();
}
.#{$primaryStyleName}-body {
+ -ms-touch-action: none;
+ touch-action: none;
z-index: 0;
top: 0;
-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
--- /dev/null
+/*
+ * 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
+ }
+
+}
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;
.getSelectionModel())
.addSingleSelectionListener(this::onSingleSelect);
+ grid.addColumnResizeListener(
+ event -> log("ColumnResizeEvent: isUserOriginated? "
+ + event.isUserOriginated()));
+
layout.addComponent(createMenu());
layout.addComponent(grid);
addComponent(layout);
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) {
--- /dev/null
+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;
+ }
+
+ }
+
+}
--- /dev/null
+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;
+ }
+ }
+}
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;
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) {
--- /dev/null
+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;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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"));
+
+ }
+
+}
--- /dev/null
+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"));
+ }
+}
--- /dev/null
+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());
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+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