Added a toggle in Table (and thus TreeTable) where you can explicitly disable multiselect touch screen detection. This allows you to work around issues on hybrid devices that have both a touch screen and a keyboard where you don't want automatic simple multiselection applied. Fixes #11601, slightly modified cherry-pick of #11641tags/8.12.0.alpha1
@@ -349,6 +349,8 @@ public class VScrollTable extends FlowPanel | |||
private SelectMode selectMode = SelectMode.NONE; | |||
private boolean multiSelectTouchDetectionEnabled = true; | |||
public final HashSet<String> selectedRowKeys = new HashSet<String>(); | |||
/* | |||
@@ -1502,6 +1504,10 @@ public class VScrollTable extends FlowPanel | |||
} else { | |||
selectMode = SelectMode.NONE; | |||
} | |||
if (uidl.hasAttribute("touchdetection")) { | |||
multiSelectTouchDetectionEnabled = uidl | |||
.getBooleanAttribute("touchdetection"); | |||
} | |||
} | |||
} | |||
@@ -1951,9 +1957,10 @@ public class VScrollTable extends FlowPanel | |||
} | |||
private void setMultiSelectMode(int multiselectmode) { | |||
if (BrowserInfo.get().isTouchDevice()) { | |||
if (BrowserInfo.get().isTouchDevice() | |||
&& multiSelectTouchDetectionEnabled) { | |||
// Always use the simple mode for touch devices that do not have | |||
// shift/ctrl keys | |||
// shift/ctrl keys (unless this feature is explicitly disabled) | |||
this.multiselectmode = MULTISELECT_MODE_SIMPLE; | |||
} else { | |||
this.multiselectmode = multiselectmode; |
@@ -588,6 +588,8 @@ public class Table extends AbstractSelect implements Action.Container, | |||
private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT; | |||
private boolean multiSelectTouchDetectionEnabled = true; | |||
private boolean rowCacheInvalidated; | |||
private RowGenerator rowGenerator = null; | |||
@@ -3775,6 +3777,10 @@ public class Table extends AbstractSelect implements Action.Container, | |||
if (isSelectable()) { | |||
target.addAttribute("selectmode", | |||
(isMultiSelect() ? "multi" : "single")); | |||
if (isMultiSelect()) { | |||
target.addAttribute("touchdetection", | |||
isMultiSelectTouchDetectionEnabled()); | |||
} | |||
} else { | |||
target.addAttribute("selectmode", "none"); | |||
} | |||
@@ -5188,7 +5194,10 @@ public class Table extends AbstractSelect implements Action.Container, | |||
* <p> | |||
* Note, that on some clients the mode may not be respected. E.g. on touch | |||
* based devices CTRL/SHIFT base selection method is invalid, so touch based | |||
* browsers always use the {@link MultiSelectMode#SIMPLE}. | |||
* browsers always use the {@link MultiSelectMode#SIMPLE} unless touch multi | |||
* select is explicitly disabled. | |||
* | |||
* @see #setMultiSelectTouchDetectionEnabled(boolean) | |||
* | |||
* @param mode | |||
* The select mode of the table | |||
@@ -5207,6 +5216,31 @@ public class Table extends AbstractSelect implements Action.Container, | |||
return multiSelectMode; | |||
} | |||
/** | |||
* Default behavior on touch-reporting devices is to switch from CTRL/SHIFT | |||
* based multi-selection to simple mode, but you can use this method to | |||
* explicitly disable the touch device detection. Thus you can keep using | |||
* keyboard-based multi selection on hybrid devices that have both a touch | |||
* screen and a keyboard. | |||
* | |||
* @param multiSelectTouchDetectionEnabled | |||
* Whether to enable or disable touch screen detection | |||
*/ | |||
public void setMultiSelectTouchDetectionEnabled( | |||
boolean multiSelectTouchDetectionEnabled) { | |||
this.multiSelectTouchDetectionEnabled = multiSelectTouchDetectionEnabled; | |||
markAsDirty(); | |||
} | |||
/** | |||
* Returns if touch screen detection is used to toggle multi select mode. | |||
* | |||
* @return If touch screen detection for multi select is enabled | |||
*/ | |||
public boolean isMultiSelectTouchDetectionEnabled() { | |||
return multiSelectTouchDetectionEnabled; | |||
} | |||
/** | |||
* Lazy loading accept criterion for Table. Accepted target rows are loaded | |||
* from server once per drag and drop operation. Developer must override one |
@@ -10,9 +10,10 @@ import com.vaadin.v7.ui.Table.TableDragMode; | |||
@SuppressWarnings("serial") | |||
public class CtrlShiftMultiselect extends TestBase { | |||
protected final Table table = new Table("Multiselectable table"); | |||
@Override | |||
protected void setup() { | |||
final Table table = new Table("Multiselectable table"); | |||
table.setContainerDataSource(createContainer()); | |||
table.setImmediate(true); |
@@ -0,0 +1,41 @@ | |||
package com.vaadin.tests.components.table; | |||
import java.util.Set; | |||
import com.vaadin.v7.data.Property; | |||
import com.vaadin.v7.ui.Label; | |||
public class CtrlShiftMultiselectTouchDetectionDisabled | |||
extends CtrlShiftMultiselect { | |||
protected Label label; | |||
@Override | |||
protected void setup() { | |||
super.setup(); | |||
label = new Label("0"); | |||
label.setId("count"); | |||
label.setCaption("Amount of selected items"); | |||
table.setMultiSelectTouchDetectionEnabled(false); | |||
table.addValueChangeListener(new Property.ValueChangeListener() { | |||
@Override | |||
public void valueChange(Property.ValueChangeEvent event) { | |||
Property property = event.getProperty(); | |||
Set set = (Set) property.getValue(); | |||
label.setValue("" + set.size()); | |||
} | |||
}); | |||
addComponent(label); | |||
} | |||
@Override | |||
protected String getDescription() { | |||
return "Allow disabling multi selection's touch screen detection for hybrid devices"; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return 11601; | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
package com.vaadin.tests.components.treetable; | |||
import static com.vaadin.server.Sizeable.Unit.PIXELS; | |||
import java.util.Set; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUI; | |||
import com.vaadin.v7.data.Property; | |||
import com.vaadin.v7.ui.Label; | |||
import com.vaadin.v7.ui.TreeTable; | |||
public class TreeTableMultiselect extends AbstractTestUI { | |||
protected final TreeTable tt = new TreeTable("Multiselectable treetable"); | |||
protected Label label; | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
label = new Label("0"); | |||
label.setCaption("Amount of selected items"); | |||
label.setId("count"); | |||
tt.setImmediate(true); | |||
tt.addContainerProperty("Foo", String.class, ""); | |||
tt.setColumnWidth("Foo", 100); | |||
tt.addContainerProperty("Bar", String.class, ""); | |||
tt.setColumnWidth("Bar", 100); | |||
tt.setHeight(400, PIXELS); | |||
Object item1 = tt.addItem(new Object[] { "Foo", "Bar" }, null); | |||
Object item2 = tt.addItem(new Object[] { "Foo2", "Bar2" }, null); | |||
Object item3 = tt.addItem(new Object[] { "Foo3", "Bar3" }, null); | |||
tt.setParent(item2, item1); | |||
tt.setParent(item3, item1); | |||
tt.setCollapsed(item1, false); | |||
tt.setSelectable(true); | |||
tt.setMultiSelect(true); | |||
tt.setMultiSelectTouchDetectionEnabled(false); | |||
tt.setWidth("400px"); | |||
tt.setHeight("400px"); | |||
tt.addValueChangeListener(new Property.ValueChangeListener() { | |||
@Override | |||
public void valueChange(Property.ValueChangeEvent event) { | |||
Property property = event.getProperty(); | |||
Set set = (Set) property.getValue(); | |||
label.setValue("" + set.size()); | |||
} | |||
}); | |||
addComponent(tt); | |||
addComponent(label); | |||
} | |||
@Override | |||
public String getTestDescription() { | |||
return "Allow disabling multi selection's touch screen detection for hybrid devices"; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return 11601; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
package com.vaadin.tests.components.table; | |||
import static org.junit.Assert.assertEquals; | |||
import org.junit.Test; | |||
import org.openqa.selenium.Keys; | |||
import org.openqa.selenium.interactions.Actions; | |||
import com.vaadin.testbench.elements.LabelElement; | |||
import com.vaadin.testbench.elements.TableElement; | |||
import com.vaadin.tests.tb3.SingleBrowserTest; | |||
public class CtrlShiftMultiselectTouchDetectionDisabledTest | |||
extends SingleBrowserTest { | |||
@Override | |||
protected boolean requireWindowFocusForIE() { | |||
return true; | |||
} | |||
@Test | |||
public void testSelectedCount() { | |||
openTestURL(); | |||
clickRow(3); | |||
new Actions(driver).keyDown(Keys.SHIFT).perform(); | |||
clickRow(8); | |||
new Actions(driver).keyUp(Keys.SHIFT).perform(); | |||
new Actions(driver).release().perform(); | |||
LabelElement labelElement = $(LabelElement.class).id("count"); | |||
assertEquals("Unexpected amount of selected rows", "6", | |||
labelElement.getText()); | |||
} | |||
private void clickRow(int index) { | |||
TableElement tableElement = $(TableElement.class).first(); | |||
tableElement.getRow(index).click(); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.vaadin.tests.components.treetable; | |||
import static org.junit.Assert.assertEquals; | |||
import org.junit.Test; | |||
import org.openqa.selenium.Keys; | |||
import org.openqa.selenium.interactions.Actions; | |||
import com.vaadin.testbench.elements.LabelElement; | |||
import com.vaadin.testbench.elements.TreeTableElement; | |||
import com.vaadin.tests.tb3.SingleBrowserTest; | |||
public class TreeTableMultiselectTest extends SingleBrowserTest { | |||
@Override | |||
protected boolean requireWindowFocusForIE() { | |||
return true; | |||
} | |||
@Test | |||
public void testSelectedCount() { | |||
openTestURL(); | |||
clickRow(0); | |||
new Actions(driver).keyDown(Keys.SHIFT).perform(); | |||
clickRow(2); | |||
new Actions(driver).keyUp(Keys.SHIFT).perform(); | |||
new Actions(driver).release().perform(); | |||
LabelElement labelElement = $(LabelElement.class).id("count"); | |||
assertEquals("Unexpected amount of selected rows", "3", | |||
labelElement.getText()); | |||
} | |||
private void clickRow(int index) { | |||
TreeTableElement treeTable = $(TreeTableElement.class).first(); | |||
treeTable.getRow(index).click(); | |||
} | |||
} |