From: Leif Åstrand Date: Thu, 5 Feb 2015 19:04:36 +0000 (+0200) Subject: Add API for controlling deselection for single select model (#16567) X-Git-Tag: 7.5.0.alpha1~121 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ca435cd30b6bdf1a24636e37c10a43545804991f;p=vaadin-framework.git Add API for controlling deselection for single select model (#16567) Change-Id: Ieb245205b3a311a4563f39bc48baadc44e218b61 --- diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index f8aa044a8d..f263b47642 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -57,6 +57,7 @@ import com.vaadin.client.widget.grid.events.SelectAllHandler; import com.vaadin.client.widget.grid.selection.AbstractRowHandleSelectionModel; import com.vaadin.client.widget.grid.selection.SelectionEvent; import com.vaadin.client.widget.grid.selection.SelectionHandler; +import com.vaadin.client.widget.grid.selection.SelectionModel; import com.vaadin.client.widget.grid.selection.SelectionModelMulti; import com.vaadin.client.widget.grid.selection.SelectionModelNone; import com.vaadin.client.widget.grid.selection.SelectionModelSingle; @@ -536,7 +537,12 @@ public class GridConnector extends AbstractHasComponentsConnector implements // Selection if (stateChangeEvent.hasPropertyChanged("selectionMode")) { onSelectionModeChange(); + updateSelectDeselectAllowed(); + } else if (stateChangeEvent + .hasPropertyChanged("singleSelectDeselectAllowed")) { + updateSelectDeselectAllowed(); } + if (stateChangeEvent.hasPropertyChanged("selectedKeys")) { updateSelectionFromState(); } @@ -567,6 +573,14 @@ public class GridConnector extends AbstractHasComponentsConnector implements } } + private void updateSelectDeselectAllowed() { + SelectionModel model = getWidget().getSelectionModel(); + if (model instanceof SelectionModel.Single) { + ((SelectionModel.Single) model) + .setDeselectAllowed(getState().singleSelectDeselectAllowed); + } + } + private void updateColumnOrderFromState(List stateColumnOrder) { CustomGridColumn[] columns = new CustomGridColumn[stateColumnOrder .size()]; diff --git a/client/src/com/vaadin/client/widget/grid/selection/ClickSelectHandler.java b/client/src/com/vaadin/client/widget/grid/selection/ClickSelectHandler.java index 0a1154e787..c6bc52dd1c 100644 --- a/client/src/com/vaadin/client/widget/grid/selection/ClickSelectHandler.java +++ b/client/src/com/vaadin/client/widget/grid/selection/ClickSelectHandler.java @@ -30,6 +30,7 @@ public class ClickSelectHandler { private Grid grid; private HandlerRegistration clickHandler; + private boolean deselectAllowed = true; private class RowClickHandler implements BodyClickHandler { @@ -38,6 +39,8 @@ public class ClickSelectHandler { T row = (T) event.getTargetCell().getRow(); if (!grid.isSelected(row)) { grid.select(row); + } else if (deselectAllowed) { + grid.deselect(row); } } } @@ -60,4 +63,15 @@ public class ClickSelectHandler { public void removeHandler() { clickHandler.removeHandler(); } + + /** + * Sets whether clicking the currently selected row should deselect the row. + * + * @param deselectAllowed + * true to allow deselecting the selected row; + * otherwise false + */ + public void setDeselectAllowed(boolean deselectAllowed) { + this.deselectAllowed = deselectAllowed; + } } diff --git a/client/src/com/vaadin/client/widget/grid/selection/SelectionModel.java b/client/src/com/vaadin/client/widget/grid/selection/SelectionModel.java index 37f6fb48c3..ec36ab52e8 100644 --- a/client/src/com/vaadin/client/widget/grid/selection/SelectionModel.java +++ b/client/src/com/vaadin/client/widget/grid/selection/SelectionModel.java @@ -116,6 +116,26 @@ public interface SelectionModel { */ public T getSelectedRow(); + /** + * Sets whether it's allowed to deselect the selected row through the + * UI. Deselection is allowed by default. + * + * @param deselectAllowed + * true if the selected row can be deselected + * without selecting another row instead; otherwise + * false. + */ + public void setDeselectAllowed(boolean deselectAllowed); + + /** + * Sets whether it's allowed to deselect the selected row through the + * UI. + * + * @return true if deselection is allowed; otherwise + * false + */ + public boolean isDeselectAllowed(); + } /** diff --git a/client/src/com/vaadin/client/widget/grid/selection/SelectionModelSingle.java b/client/src/com/vaadin/client/widget/grid/selection/SelectionModelSingle.java index 20eb3c1e63..38605db12c 100644 --- a/client/src/com/vaadin/client/widget/grid/selection/SelectionModelSingle.java +++ b/client/src/com/vaadin/client/widget/grid/selection/SelectionModelSingle.java @@ -40,6 +40,8 @@ public class SelectionModelSingle extends AbstractRowHandleSelectionModel /** Event handling for selection by clicking cells */ private ClickSelectHandler clickSelectHandler; + private boolean deselectAllowed = true; + @Override public boolean isSelected(T row) { return selectedRow != null @@ -66,6 +68,7 @@ public class SelectionModelSingle extends AbstractRowHandleSelectionModel if (this.grid != null) { spaceSelectHandler = new SpaceSelectHandler(grid); clickSelectHandler = new ClickSelectHandler(grid); + updateHandlerDeselectAllowed(); } else { spaceSelectHandler.removeHandler(); clickSelectHandler.removeHandler(); @@ -148,4 +151,25 @@ public class SelectionModelSingle extends AbstractRowHandleSelectionModel return false; } } + + @Override + public void setDeselectAllowed(boolean deselectAllowed) { + this.deselectAllowed = deselectAllowed; + updateHandlerDeselectAllowed(); + } + + @Override + public boolean isDeselectAllowed() { + return deselectAllowed; + } + + private void updateHandlerDeselectAllowed() { + if (spaceSelectHandler != null) { + spaceSelectHandler.setDeselectAllowed(deselectAllowed); + } + if (clickSelectHandler != null) { + clickSelectHandler.setDeselectAllowed(deselectAllowed); + } + } + } diff --git a/client/src/com/vaadin/client/widget/grid/selection/SpaceSelectHandler.java b/client/src/com/vaadin/client/widget/grid/selection/SpaceSelectHandler.java index 7a1bf2dc06..3e04a6dfac 100644 --- a/client/src/com/vaadin/client/widget/grid/selection/SpaceSelectHandler.java +++ b/client/src/com/vaadin/client/widget/grid/selection/SpaceSelectHandler.java @@ -79,10 +79,10 @@ public class SpaceSelectHandler { protected void setSelected(Grid grid, int rowIndex) { T row = grid.getDataSource().getRow(rowIndex); - if (grid.isSelected(row)) { - grid.deselect(row); - } else { + if (!grid.isSelected(row)) { grid.select(row); + } else if (deselectAllowed) { + grid.deselect(row); } } } @@ -91,6 +91,7 @@ public class SpaceSelectHandler { private Grid grid; private HandlerRegistration spaceUpHandler; private HandlerRegistration spaceDownHandler; + private boolean deselectAllowed = true; /** * Constructor for SpaceSelectHandler. This constructor will add all @@ -121,4 +122,16 @@ public class SpaceSelectHandler { spaceDownHandler.removeHandler(); spaceUpHandler.removeHandler(); } + + /** + * Sets whether pressing space for the currently selected row should + * deselect the row. + * + * @param deselectAllowed + * true to allow deselecting the selected row; + * otherwise false + */ + public void setDeselectAllowed(boolean deselectAllowed) { + this.deselectAllowed = deselectAllowed; + } } \ No newline at end of file diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 150f672835..bbae501782 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -641,6 +641,26 @@ public class Grid extends AbstractComponent implements SelectionNotifier, * null if nothing is selected */ Object getSelectedRow(); + + /** + * Sets whether it's allowed to deselect the selected row through + * the UI. Deselection is allowed by default. + * + * @param deselectAllowed + * true if the selected row can be + * deselected without selecting another row instead; + * otherwise false. + */ + public void setDeselectAllowed(boolean deselectAllowed); + + /** + * Sets whether it's allowed to deselect the selected row through + * the UI. + * + * @return true if deselection is allowed; otherwise + * false + */ + public boolean isDeselectAllowed(); } /** @@ -814,6 +834,16 @@ public class Grid extends AbstractComponent implements SelectionNotifier, public void reset() { deselect(getSelectedRow()); } + + @Override + public void setDeselectAllowed(boolean deselectAllowed) { + grid.getState().singleSelectDeselectAllowed = deselectAllowed; + } + + @Override + public boolean isDeselectAllowed() { + return grid.getState(false).singleSelectDeselectAllowed; + } } /** diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index 9d94a2cb8e..7018df1413 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -132,6 +132,9 @@ public class GridState extends AbstractComponentState { public SharedSelectionMode selectionMode; + /** Whether single select mode can be cleared through the UI */ + public boolean singleSelectDeselectAllowed = true; + /** Keys of the currently sorted columns */ public String[] sortColumns = new String[0]; diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index fe4a31d9e7..0c335f58b2 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -57,6 +57,7 @@ import com.vaadin.ui.Grid.MultiSelectionModel; import com.vaadin.ui.Grid.RowReference; import com.vaadin.ui.Grid.RowStyleGenerator; import com.vaadin.ui.Grid.SelectionMode; +import com.vaadin.ui.Grid.SelectionModel; import com.vaadin.ui.renderer.DateRenderer; import com.vaadin.ui.renderer.HtmlRenderer; import com.vaadin.ui.renderer.NumberRenderer; @@ -82,6 +83,8 @@ public class GridBasicFeatures extends AbstractComponentTest { private int containerDelay = 0; + private boolean singleSelectAllowDeselect = true; + private IndexedContainer ds; private Grid grid; private SelectionListener selectionListener = new SelectionListener() { @@ -320,6 +323,9 @@ public class GridBasicFeatures extends AbstractComponentTest { grid.setSelectionMode(selectionMode); if (selectionMode == SelectionMode.SINGLE) { grid.addSelectionListener(selectionListener); + + ((SelectionModel.Single) grid.getSelectionModel()) + .setDeselectAllowed(singleSelectAllowDeselect); } else { grid.removeSelectionListener(selectionListener); } @@ -488,6 +494,20 @@ public class GridBasicFeatures extends AbstractComponentTest { } }); + + createBooleanAction("Single select allow deselect", "State", + singleSelectAllowDeselect, new Command() { + @Override + public void execute(Grid c, Boolean value, Object data) { + singleSelectAllowDeselect = value.booleanValue(); + + SelectionModel model = c.getSelectionModel(); + if (model instanceof SelectionModel.Single) { + ((SelectionModel.Single) model) + .setDeselectAllowed(singleSelectAllowDeselect); + } + } + }); } protected void createHeaderActions() { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientSelectionTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientSelectionTest.java index d4c10da626..dccf2c0974 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientSelectionTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientSelectionTest.java @@ -19,6 +19,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.interactions.Actions; import com.vaadin.testbench.By; import com.vaadin.testbench.elements.GridElement.GridCellElement; @@ -74,7 +76,7 @@ public class GridClientSelectionTest extends GridBasicClientFeaturesTest { assertTrue("Multi Selection Model should have select all checkbox", header.isElementPresent(By.tagName("input"))); - setSelectionModelSingle(); + setSelectionModelSingle(true); header = getGridElement().getHeaderCell(0, 0); assertFalse( "Check box shouldn't have been in header for Single Selection Model", @@ -88,15 +90,133 @@ public class GridClientSelectionTest extends GridBasicClientFeaturesTest { } + @Test + public void testDeselectAllowedMouseInput() { + openTestURL(); + + setSelectionModelSingle(true); + + getGridElement().getCell(5, 1).click(); + + assertTrue("Row 5 should be selected after clicking", isRowSelected(5)); + + getGridElement().getCell(7, 1).click(); + + assertFalse("Row 5 should be deselected after clicking another row", + isRowSelected(5)); + assertTrue("Row 7 should be selected after clicking", isRowSelected(7)); + + getGridElement().getCell(7, 1).click(); + + assertFalse("Row should be deselected after clicking again", + isRowSelected(7)); + } + + @Test + public void testDeselectAllowedKeyboardInput() { + openTestURL(); + + setSelectionModelSingle(true); + + getGridElement().getHeaderCell(0, 1).click(); + + new Actions(getDriver()).sendKeys(Keys.ARROW_DOWN).perform(); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertTrue("Row 0 should be selected after pressing space", + isRowSelected(0)); + + new Actions(getDriver()).sendKeys(Keys.ARROW_DOWN).perform(); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertFalse( + "Row 0 should be deselected after pressing space another row", + isRowSelected(0)); + assertTrue("Row 1 should be selected after pressing space", + isRowSelected(1)); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertFalse("Row should be deselected after pressing space again", + isRowSelected(1)); + } + + @Test + public void testDeselectNotAllowedMouseInput() { + openTestURL(); + + setSelectionModelSingle(false); + + getGridElement().getCell(5, 1).click(); + + assertTrue("Row 5 should be selected after clicking", isRowSelected(5)); + + getGridElement().getCell(7, 1).click(); + + assertFalse("Row 5 should be deselected after clicking another row", + isRowSelected(5)); + assertTrue("Row 7 should be selected after clicking", isRowSelected(7)); + + getGridElement().getCell(7, 1).click(); + + assertTrue("Row should remain selected after clicking again", + isRowSelected(7)); + } + + @Test + public void testDeselectNotAllowedKeyboardInput() { + openTestURL(); + + setSelectionModelSingle(false); + + getGridElement().getHeaderCell(0, 1).click(); + new Actions(getDriver()).sendKeys(Keys.ARROW_DOWN).perform(); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertTrue("Row 0 should be selected after pressing space", + isRowSelected(0)); + + new Actions(getDriver()).sendKeys(Keys.ARROW_DOWN).perform(); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertFalse( + "Row 0 should be deselected after pressing space another row", + isRowSelected(0)); + assertTrue("Row 1 should be selected after pressing space", + isRowSelected(1)); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertTrue("Row should remain selected after pressing space again", + isRowSelected(1)); + } + + private boolean isRowSelected(int index) { + boolean selected = getGridElement().getRow(index).isSelected(); + return selected; + } + private void setSelectionModelMulti() { - selectMenuPath("Component", "State", "Selection mode", "multi"); + setSelectionModel("multi"); } - private void setSelectionModelSingle() { - selectMenuPath("Component", "State", "Selection mode", "single"); + private void setSelectionModelSingle(boolean deselectAllowed) { + String mode = "single"; + if (!deselectAllowed) { + mode += " (no deselect)"; + } + setSelectionModel(mode); } private void setSelectionModelNone() { - selectMenuPath("Component", "State", "Selection mode", "none"); + setSelectionModel("none"); + } + + private void setSelectionModel(String model) { + selectMenuPath("Component", "State", "Selection mode", model); } } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java index 3dbf613ba0..b4eb473d4b 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java @@ -270,6 +270,41 @@ public class GridSelectionTest extends GridBasicFeaturesTest { } + @Test + public void testToggleDeselectAllowed() { + openTestURL(); + + setSelectionModelSingle(); + // Deselect allowed already enabled + + getGridElement().getCell(5, 1).click(); + getGridElement().getCell(5, 1).click(); + assertFalse("Row should be not selected after two clicks", getRow(5) + .isSelected()); + + selectMenuPath("Component", "State", "Single select allow deselect"); + getGridElement().getCell(5, 1).click(); + getGridElement().getCell(5, 1).click(); + assertTrue("Row should be selected after two clicks", getRow(5) + .isSelected()); + + selectMenuPath("Component", "State", "Single select allow deselect"); + getGridElement().getCell(5, 1).click(); + assertFalse("Row should be not selected after another click", getRow(5) + .isSelected()); + + // Also verify that state is updated together with the model + setSelectionModelNone(); + selectMenuPath("Component", "State", "Single select allow deselect"); + setSelectionModelSingle(); + + getGridElement().getCell(5, 1).click(); + getGridElement().getCell(5, 1).click(); + + assertTrue("Row should stay selected after two clicks", getRow(5) + .isSelected()); + } + private void setSelectionModelMulti() { selectMenuPath("Component", "State", "Selection mode", "multi"); } diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeaturesWidget.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeaturesWidget.java index 232a3a780e..0452aa65d1 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeaturesWidget.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeaturesWidget.java @@ -66,6 +66,7 @@ import com.vaadin.client.widget.grid.events.HeaderKeyPressHandler; import com.vaadin.client.widget.grid.events.HeaderKeyUpHandler; import com.vaadin.client.widget.grid.events.ScrollEvent; import com.vaadin.client.widget.grid.events.ScrollHandler; +import com.vaadin.client.widget.grid.selection.SelectionModel; import com.vaadin.client.widget.grid.selection.SelectionModel.None; import com.vaadin.client.widgets.Grid; import com.vaadin.client.widgets.Grid.Column; @@ -468,6 +469,15 @@ public class GridBasicClientFeaturesWidget extends } }, selectionModePath); + addMenuCommand("single (no deselect)", new ScheduledCommand() { + @Override + public void execute() { + grid.setSelectionMode(SelectionMode.SINGLE); + ((SelectionModel.Single) grid.getSelectionModel()) + .setDeselectAllowed(false); + } + }, selectionModePath); + addMenuCommand("none", new ScheduledCommand() { @Override public void execute() {