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;
// Selection
if (stateChangeEvent.hasPropertyChanged("selectionMode")) {
onSelectionModeChange();
+ updateSelectDeselectAllowed();
+ } else if (stateChangeEvent
+ .hasPropertyChanged("singleSelectDeselectAllowed")) {
+ updateSelectDeselectAllowed();
}
+
if (stateChangeEvent.hasPropertyChanged("selectedKeys")) {
updateSelectionFromState();
}
}
}
+ private void updateSelectDeselectAllowed() {
+ SelectionModel<JsonObject> model = getWidget().getSelectionModel();
+ if (model instanceof SelectionModel.Single<?>) {
+ ((SelectionModel.Single<?>) model)
+ .setDeselectAllowed(getState().singleSelectDeselectAllowed);
+ }
+ }
+
private void updateColumnOrderFromState(List<String> stateColumnOrder) {
CustomGridColumn[] columns = new CustomGridColumn[stateColumnOrder
.size()];
private Grid<T> grid;
private HandlerRegistration clickHandler;
+ private boolean deselectAllowed = true;
private class RowClickHandler implements BodyClickHandler {
T row = (T) event.getTargetCell().getRow();
if (!grid.isSelected(row)) {
grid.select(row);
+ } else if (deselectAllowed) {
+ grid.deselect(row);
}
}
}
public void removeHandler() {
clickHandler.removeHandler();
}
+
+ /**
+ * Sets whether clicking the currently selected row should deselect the row.
+ *
+ * @param deselectAllowed
+ * <code>true</code> to allow deselecting the selected row;
+ * otherwise <code>false</code>
+ */
+ public void setDeselectAllowed(boolean deselectAllowed) {
+ this.deselectAllowed = deselectAllowed;
+ }
}
*/
public T getSelectedRow();
+ /**
+ * Sets whether it's allowed to deselect the selected row through the
+ * UI. Deselection is allowed by default.
+ *
+ * @param deselectAllowed
+ * <code>true</code> if the selected row can be deselected
+ * without selecting another row instead; otherwise
+ * <code>false</code>.
+ */
+ public void setDeselectAllowed(boolean deselectAllowed);
+
+ /**
+ * Sets whether it's allowed to deselect the selected row through the
+ * UI.
+ *
+ * @return <code>true</code> if deselection is allowed; otherwise
+ * <code>false</code>
+ */
+ public boolean isDeselectAllowed();
+
}
/**
/** Event handling for selection by clicking cells */
private ClickSelectHandler<T> clickSelectHandler;
+ private boolean deselectAllowed = true;
+
@Override
public boolean isSelected(T row) {
return selectedRow != null
if (this.grid != null) {
spaceSelectHandler = new SpaceSelectHandler<T>(grid);
clickSelectHandler = new ClickSelectHandler<T>(grid);
+ updateHandlerDeselectAllowed();
} else {
spaceSelectHandler.removeHandler();
clickSelectHandler.removeHandler();
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);
+ }
+ }
+
}
protected void setSelected(Grid<T> 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);
}
}
}
private Grid<T> grid;
private HandlerRegistration spaceUpHandler;
private HandlerRegistration spaceDownHandler;
+ private boolean deselectAllowed = true;
/**
* Constructor for SpaceSelectHandler. This constructor will add all
spaceDownHandler.removeHandler();
spaceUpHandler.removeHandler();
}
+
+ /**
+ * Sets whether pressing space for the currently selected row should
+ * deselect the row.
+ *
+ * @param deselectAllowed
+ * <code>true</code> to allow deselecting the selected row;
+ * otherwise <code>false</code>
+ */
+ public void setDeselectAllowed(boolean deselectAllowed) {
+ this.deselectAllowed = deselectAllowed;
+ }
}
\ No newline at end of file
* <code>null</code> 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
+ * <code>true</code> if the selected row can be
+ * deselected without selecting another row instead;
+ * otherwise <code>false</code>.
+ */
+ public void setDeselectAllowed(boolean deselectAllowed);
+
+ /**
+ * Sets whether it's allowed to deselect the selected row through
+ * the UI.
+ *
+ * @return <code>true</code> if deselection is allowed; otherwise
+ * <code>false</code>
+ */
+ public boolean isDeselectAllowed();
}
/**
public void reset() {
deselect(getSelectedRow());
}
+
+ @Override
+ public void setDeselectAllowed(boolean deselectAllowed) {
+ grid.getState().singleSelectDeselectAllowed = deselectAllowed;
+ }
+
+ @Override
+ public boolean isDeselectAllowed() {
+ return grid.getState(false).singleSelectDeselectAllowed;
+ }
}
/**
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];
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;
private int containerDelay = 0;
+ private boolean singleSelectAllowDeselect = true;
+
private IndexedContainer ds;
private Grid grid;
private SelectionListener selectionListener = new SelectionListener() {
grid.setSelectionMode(selectionMode);
if (selectionMode == SelectionMode.SINGLE) {
grid.addSelectionListener(selectionListener);
+
+ ((SelectionModel.Single) grid.getSelectionModel())
+ .setDeselectAllowed(singleSelectAllowDeselect);
} else {
grid.removeSelectionListener(selectionListener);
}
}
});
+
+ createBooleanAction("Single select allow deselect", "State",
+ singleSelectAllowDeselect, new Command<Grid, Boolean>() {
+ @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() {
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;
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",
}
+ @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);
}
}
}
+ @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");
}
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;
}
}, 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() {