From 600d5b436d7ca33840b1b697082d140a5040bdf3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Patrik=20Lindstr=C3=B6m?= Date: Mon, 25 Aug 2014 14:44:12 +0300 Subject: [PATCH] Add keyboard sorting controls (#13334) Change-Id: Icb0ef5d70b5469cd87bdd079fe16f31b8cf769f1 --- .../src/com/vaadin/client/ui/grid/Grid.java | 56 +++++++++++++-- .../vaadin/client/ui/grid/sort/SortOrder.java | 20 ++++++ .../vaadin/shared/ui/grid/SortDirection.java | 21 +++++- .../basicfeatures/server/GridSortingTest.java | 69 +++++++++++++++++++ 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index aa5215bef8..34bff2fd45 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -1265,6 +1265,50 @@ public class Grid extends Composite implements sinkEvents(getHeader().getConsumedEvents()); sinkEvents(Arrays.asList(BrowserEvents.KEYDOWN, BrowserEvents.KEYUP, BrowserEvents.KEYPRESS)); + + // Make ENTER and SHIFT+ENTER in the header perform sorting + addKeyUpHandler(new HeaderKeyUpHandler() { + @Override + public void onKeyUp(GridKeyUpEvent event) { + if (event.getNativeKeyCode() != KeyCodes.KEY_ENTER) { + return; + } + + final Cell cell = event.getActiveCell(); + final GridColumn column = columns.get(cell.getColumn()); + + // If SHIFT is down, we modify multi-sorting order + if (event.isShiftKeyDown() && sortOrder != null) { + + final SortOrder so = getSortOrder(column); + + if (so != null) { + // Flip sort direction in-place + final int idx = sortOrder.indexOf(so); + sortOrder.set(idx, so.getOpposite()); + } else { + // Add a new sort rule to the end of the list + sortOrder.add(new SortOrder(column)); + } + + } else { + if (sortOrder.size() == 1 + && sortOrder.get(0).getColumn() == column) { + + // Reverse the sort order and re-sort + sortOrder.set(0, sortOrder.get(0).getOpposite()); + } else { + + // Manually re-set the sorting order + sortOrder.clear(); + sortOrder.add(new SortOrder(column)); + } + } + + // We've modified the sort order, re-sort it now. + setSortOrder(sortOrder, SortEventOriginator.USER); + } + }); } @Override @@ -2083,8 +2127,8 @@ public class Grid extends Composite implements private Point rowEventTouchStartingPoint; - private boolean handleHeaderDefaultRowEvent(Event event, RowContainer container, - final Cell cell) { + private boolean handleHeaderDefaultRowEvent(Event event, + RowContainer container, final Cell cell) { if (container != escalator.getHeader()) { return false; } @@ -2488,9 +2532,11 @@ public class Grid extends Composite implements private void setSortOrder(List order, SortEventOriginator originator) { - sortOrder.clear(); - if (order != null) { - sortOrder.addAll(order); + if (order != sortOrder) { + sortOrder.clear(); + if (order != null) { + sortOrder.addAll(order); + } } sort(originator); } diff --git a/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java b/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java index 682beda793..8878e18872 100644 --- a/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java +++ b/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java @@ -31,6 +31,17 @@ public class SortOrder { private final GridColumn column; private final SortDirection direction; + /** + * Create a sort order descriptor with a default sorting direction value of + * {@link SortDirection#ASCENDING}. + * + * @param column + * a grid column descriptor object + */ + public SortOrder(GridColumn column) { + this(column, SortDirection.ASCENDING); + } + /** * Create a sort order descriptor. * @@ -69,4 +80,13 @@ public class SortOrder { public SortDirection getDirection() { return direction; } + + /** + * Returns a new SortOrder object with the sort direction reversed. + * + * @return a new sort order object + */ + public SortOrder getOpposite() { + return new SortOrder(column, direction.getOpposite()); + } } diff --git a/shared/src/com/vaadin/shared/ui/grid/SortDirection.java b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java index 0b4eafc37f..9aed268d01 100644 --- a/shared/src/com/vaadin/shared/ui/grid/SortDirection.java +++ b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java @@ -28,10 +28,27 @@ public enum SortDirection implements Serializable { /** * Ascending (e.g. A-Z, 1..9) sort order */ - ASCENDING, + ASCENDING { + @Override + public SortDirection getOpposite() { + return DESCENDING; + } + }, /** * Descending (e.g. Z-A, 9..1) sort order */ - DESCENDING + DESCENDING { + @Override + public SortDirection getOpposite() { + return ASCENDING; + } + }; + + /** + * Get the sort direction that is the direct opposite to this one. + * + * @return a sort direction value + */ + public abstract SortDirection getOpposite(); } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSortingTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSortingTest.java index 024be65e83..74a5c6ed95 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSortingTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSortingTest.java @@ -191,6 +191,75 @@ public class GridSortingTest extends GridBasicFeaturesTest { } + private void sendKeys(CharSequence... seq) { + new Actions(getDriver()).sendKeys(seq).perform(); + } + + private void holdKey(Keys key) { + new Actions(getDriver()).keyDown(key).perform(); + } + + private void releaseKey(Keys key) { + new Actions(getDriver()).keyUp(key).perform(); + } + + private void assertLog(String expected) { + assertEquals(expected, getLogRow(0)); + } + + @Test + public void testKeyboardMultiColumnSorting() throws InterruptedException { + openTestURL(); + + // + // NOTE: This is a work-around to get the focus to the first header + // cell. + // We can't use element.focus() because TestBench (or, rather, Selenium + // beneath it) has rather interesting bugs regarding focus handling. + // + getGridElement().getCell(0, 0).click(); + sendKeys(Keys.ARROW_UP); + + // Sort ASCENDING on first column + sendKeys(Keys.ENTER); + assertLog("2. Sort order: [Column 0 ASCENDING] by USER"); + + // Move to next column + sendKeys(Keys.RIGHT); + + // Add this column to the existing sorting group + holdKey(Keys.SHIFT); + sendKeys(Keys.ENTER); + releaseKey(Keys.SHIFT); + assertLog("4. Sort order: [Column 0 ASCENDING, Column 1 ASCENDING] by USER"); + + // Move to next column + sendKeys(Keys.RIGHT); + + // Add a third column to the sorting group + holdKey(Keys.SHIFT); + sendKeys(Keys.ENTER); + releaseKey(Keys.SHIFT); + assertLog("6. Sort order: [Column 0 ASCENDING, Column 1 ASCENDING, Column 2 ASCENDING] by USER"); + + // Move back to the second column + sendKeys(Keys.LEFT); + + // Change sort direction of the second column to DESCENDING + holdKey(Keys.SHIFT); + sendKeys(Keys.ENTER); + releaseKey(Keys.SHIFT); + assertLog("8. Sort order: [Column 0 ASCENDING, Column 1 DESCENDING, Column 2 ASCENDING] by USER"); + + // Move back to the third column + sendKeys(Keys.RIGHT); + + // Reset sorting to third column, ASCENDING + sendKeys(Keys.ENTER); + assertLog("10. Sort order: [Column 2 ASCENDING] by USER"); + + } + private void sortBy(String column) { selectMenuPath("Component", "State", "Sort by column", column); } -- 2.39.5