diff options
author | Pekka Hyvönen <pekka@vaadin.com> | 2015-02-26 16:23:32 +0200 |
---|---|---|
committer | Pekka Hyvönen <pekka@vaadin.com> | 2015-02-27 10:21:22 +0200 |
commit | 31b188e370b60832ecc05df8ddd41a6884996aad (patch) | |
tree | fb4288a11a152da9045fcb0a3a89b596d04f189d | |
parent | fa50ea28870436c5dedb459e364d24f14f7ab001 (diff) | |
download | vaadin-framework-31b188e370b60832ecc05df8ddd41a6884996aad.tar.gz vaadin-framework-31b188e370b60832ecc05df8ddd41a6884996aad.zip |
Handle spanned header cells when dnd reordering columns in grid. (#16643)
Prevents dragging columns from outside spanned cells to inside them, on any row.
Currently prevents dragging columns inside spanned cells. Will change this later.
Change-Id: Ie832b3c404a3afbcce0374f8b5088dc8cb124fb8
4 files changed, 147 insertions, 26 deletions
diff --git a/client/src/com/vaadin/client/widget/grid/AutoScroller.java b/client/src/com/vaadin/client/widget/grid/AutoScroller.java index 60c9535ca7..71851d67c8 100644 --- a/client/src/com/vaadin/client/widget/grid/AutoScroller.java +++ b/client/src/com/vaadin/client/widget/grid/AutoScroller.java @@ -669,6 +669,7 @@ public class AutoScroller { } private double getFrozenColumnsWidth() { + // TODO handle the case where the checkbox column is present double value = 0; for (int i = 0; i < grid.getFrozenColumnCount(); i++) { value += grid.getColumn(i).getWidthActual(); diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index ebc2fcf97e..3fd119d9ea 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -25,7 +25,9 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -37,7 +39,6 @@ import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.EventTarget; import com.google.gwt.dom.client.NativeEvent; -import com.google.gwt.dom.client.NodeList; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.TableCellElement; @@ -416,6 +417,16 @@ public class Grid<T> extends ResizeComposite implements } /** + * Returns <code>true</code> if this row contains spanned cells. + * + * @since + * @return does this row contain spanned cells + */ + public boolean hasSpannedCells() { + return !cellGroups.isEmpty(); + } + + /** * Merges columns cells in a row * * @param columns @@ -2907,6 +2918,11 @@ public class Grid<T> extends ResizeComposite implements /** Tracks index of the column whose left side the drop would occur */ private int latestColumnDropIndex; /** + * Map of possible drop positions for the column and the corresponding + * column index. + */ + private final TreeMap<Double, Integer> possibleDropPositions = new TreeMap<Double, Integer>(); + /** * Makes sure that drag cancel doesn't cause anything unwanted like sort */ private HandlerRegistration columnSortPreventRegistration; @@ -2952,33 +2968,33 @@ public class Grid<T> extends ResizeComposite implements } private void updateDragDropMarker(final int clientX) { - RowContainer header = escalator.getHeader(); - NodeList<TableCellElement> cells = header.getRowElement( - eventCell.getRowIndex()).getCells(); - double dropMarkerLeft = 0 - escalator.getScrollLeft(); - latestColumnDropIndex = 0; - for (int i = 0; i < cells.getLength(); i++, latestColumnDropIndex++) { - TableCellElement cellElement = cells.getItem(i); - int cellX = cellElement.getAbsoluteLeft(); - int cellWidth = cellElement.getOffsetWidth(); - if (clientX < cellX || clientX < cellX + (cellWidth / 2)) { - break; - } else { - dropMarkerLeft += cellWidth; - } + final double scrollLeft = getScrollLeft(); + final double cursorXCoordinate = clientX + - escalator.getHeader().getElement().getAbsoluteLeft(); + final Entry<Double, Integer> cellEdgeOnRight = possibleDropPositions + .ceilingEntry(cursorXCoordinate); + final Entry<Double, Integer> cellEdgeOnLeft = possibleDropPositions + .floorEntry(cursorXCoordinate); + final double diffToRightEdge = cellEdgeOnRight == null ? Double.MAX_VALUE + : cellEdgeOnRight.getKey() - cursorXCoordinate; + final double diffToLeftEdge = cellEdgeOnLeft == null ? Double.MAX_VALUE + : cursorXCoordinate - cellEdgeOnLeft.getKey(); + + double dropMarkerLeft = 0 - scrollLeft; + if (diffToRightEdge > diffToLeftEdge) { + latestColumnDropIndex = cellEdgeOnLeft.getValue(); + dropMarkerLeft += cellEdgeOnLeft.getKey(); + } else { + latestColumnDropIndex = cellEdgeOnRight.getValue(); + dropMarkerLeft += cellEdgeOnRight.getKey(); } + dropMarkerLeft += autoScrollX; final double frozenColumnsWidth = getFrozenColumnsWidth(); - if (dropMarkerLeft < frozenColumnsWidth) { - latestColumnDropIndex = getFrozenColumnCount(); - if (getScrollLeft() == 0) { - dropMarkerLeft = frozenColumnsWidth; - } else { - dropMarkerLeft = -10000000; - } - } else if (dropMarkerLeft > header.getElement().getOffsetWidth() - || dropMarkerLeft < 0) { + if (dropMarkerLeft < frozenColumnsWidth + || dropMarkerLeft > escalator.getHeader().getElement() + .getOffsetWidth() || dropMarkerLeft < 0) { dropMarkerLeft = -10000000; } dropMarker.getStyle().setLeft(dropMarkerLeft, Unit.PX); @@ -3009,6 +3025,9 @@ public class Grid<T> extends ResizeComposite implements eventCell.getElement().addClassName("dragged"); // mark the floating cell, for styling & testing dragElement.addClassName("dragged-column-header"); + + calculatePossibleDropPositions(); + // start the auto scroll handler autoScroller.setScrollAreaPX(60); autoScroller.start(startingEvent, ScrollAxis.HORIZONTAL, @@ -3099,6 +3118,58 @@ public class Grid<T> extends ResizeComposite implements } return result; } + + private void calculatePossibleDropPositions() { + possibleDropPositions.clear(); + + if (!calculatePossibleDropPositionInsideSpannedHeader()) { + HashMap<Integer, Double> columnIndexToDropPositionMap = new HashMap<Integer, Double>(); + + final int frozenColumns = Math.max(0, getFrozenColumnCount()); + double position = getFrozenColumnsWidth(); + // add all columns except frozen columns + // TODO handle the case where the checkbox column is present + for (int i = frozenColumns; i < getColumnCount(); i++) { + columnIndexToDropPositionMap.put(i, position); + position += getColumn(i).getWidthActual(); + } + // add the right side of the last column as columns.size() + columnIndexToDropPositionMap.put(getColumnCount(), position); + + // can't drop inside a spanned header from outside it + // -> remove all column indexes that are inside a spanned cell + // in any header row + for (int c = frozenColumns; c < getColumnCount(); c++) { + for (int r = 0; r < getHeaderRowCount(); r++) { + HeaderRow headerRow = getHeaderRow(r); + if (headerRow.hasSpannedCells()) { + HeaderCell cell = headerRow.getCell(getColumn(c)); + assert cell != null : "Somehow got a null cell for row:cell " + + r + ":" + c; + int colspan = cell.getColspan(); + while (colspan > 1) { + c++; + colspan--; + columnIndexToDropPositionMap.remove(Integer + .valueOf(c)); + } + } + } + } + // finally lets flip the map, because while dragging we want the + // column index matching the X-coordinate + for (Entry<Integer, Double> entry : columnIndexToDropPositionMap + .entrySet()) { + possibleDropPositions.put(entry.getValue(), entry.getKey()); + } + } + } + + private boolean calculatePossibleDropPositionInsideSpannedHeader() { + // TODO if the dragged column is inside a spanned header on any row, + // then dragging is limited to inside that spanned cell + return false; + } }; /** @@ -5392,6 +5463,16 @@ public class Grid<T> extends ResizeComposite implements .getFrozenColumnCount()) { return false; } + // for now prevent dragging of spanned cells + if (eventCell.getElement().getColSpan() > 1) { + return false; + } + // for now prevent dragging of columns belonging to a spanned cell + for (int r = 0; r < getHeaderRowCount(); r++) { + if (getHeaderRow(r).getCell(eventCell.getColumn()).getColspan() > 1) { + return false; + } + } if (event.getTypeInt() == Event.ONMOUSEDOWN && event.getButton() == NativeEvent.BUTTON_LEFT || event.getTypeInt() == Event.ONTOUCHSTART) { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicClientFeaturesTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicClientFeaturesTest.java index d0e076fd3b..8e07729719 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicClientFeaturesTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicClientFeaturesTest.java @@ -15,6 +15,8 @@ */ package com.vaadin.tests.components.grid.basicfeatures; +import java.util.List; + import org.openqa.selenium.Dimension; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; @@ -83,10 +85,17 @@ public abstract class GridBasicClientFeaturesTest extends GridBasicFeaturesTest if (composite) { // Composite requires the basic client features widget for subparts return ((TestBenchElement) findElement(By - .vaadin("//TestWidgetComponent"))) - .wrap(GridElement.class); + .vaadin("//TestWidgetComponent"))).wrap(GridElement.class); } else { return super.getGridElement(); } } + + @Override + protected void assertColumnHeaderOrder(int... indices) { + List<TestBenchElement> headers = getGridHeaderRowCells(); + for (int i = 0; i < indices.length; i++) { + assertColumnHeader("HEADER (0," + indices[i] + ")", headers.get(i)); + } + } } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridColumnReorderTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridColumnReorderTest.java index ac267dc42e..6e5eda43f1 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridColumnReorderTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridColumnReorderTest.java @@ -205,6 +205,36 @@ public class GridColumnReorderTest extends GridBasicClientFeaturesTest { assertFocusedCell(4, 1); } + @Test + public void testColumnReorder_draggingHeaderRowThatHasColumnHeadersSpanned_cantDropInsideSpannedHeaderFromOutside() { + // given + toggleColumnReorder(); + selectMenuPath("Component", "Header", "Append row"); + selectMenuPath("Component", "Header", "Row 2", "Join columns 1, 2"); + assertColumnHeaderOrder(0, 1, 2, 3, 4); + + // when + dragAndDropColumnHeader(1, 3, 1, 80); + + // then + assertColumnHeaderOrder(0, 3, 1, 2, 4); + } + + @Test + public void testColumnReorder_anotherRowHasColumnHeadersSpanned_cantDropInsideSpannedHeaderFromOutside() { + // given + toggleColumnReorder(); + selectMenuPath("Component", "Header", "Append row"); + selectMenuPath("Component", "Header", "Row 2", "Join columns 1, 2"); + assertColumnHeaderOrder(0, 1, 2, 3, 4); + + // when + dragAndDropColumnHeader(0, 0, 2, 20); + + // then + assertColumnHeaderOrder(1, 2, 0, 3, 4); + } + private void toggleColumnReorder() { selectMenuPath("Component", "State", "Column Reordering"); } |