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
private SelectMode selectMode = SelectMode.NONE; | private SelectMode selectMode = SelectMode.NONE; | ||||
private boolean multiSelectTouchDetectionEnabled = true; | |||||
public final HashSet<String> selectedRowKeys = new HashSet<String>(); | public final HashSet<String> selectedRowKeys = new HashSet<String>(); | ||||
/* | /* | ||||
} else { | } else { | ||||
selectMode = SelectMode.NONE; | selectMode = SelectMode.NONE; | ||||
} | } | ||||
if (uidl.hasAttribute("touchdetection")) { | |||||
multiSelectTouchDetectionEnabled = uidl | |||||
.getBooleanAttribute("touchdetection"); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
private void setMultiSelectMode(int multiselectmode) { | 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 | // 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; | this.multiselectmode = MULTISELECT_MODE_SIMPLE; | ||||
} else { | } else { | ||||
this.multiselectmode = multiselectmode; | this.multiselectmode = multiselectmode; |
private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT; | private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT; | ||||
private boolean multiSelectTouchDetectionEnabled = true; | |||||
private boolean rowCacheInvalidated; | private boolean rowCacheInvalidated; | ||||
private RowGenerator rowGenerator = null; | private RowGenerator rowGenerator = null; | ||||
if (isSelectable()) { | if (isSelectable()) { | ||||
target.addAttribute("selectmode", | target.addAttribute("selectmode", | ||||
(isMultiSelect() ? "multi" : "single")); | (isMultiSelect() ? "multi" : "single")); | ||||
if (isMultiSelect()) { | |||||
target.addAttribute("touchdetection", | |||||
isMultiSelectTouchDetectionEnabled()); | |||||
} | |||||
} else { | } else { | ||||
target.addAttribute("selectmode", "none"); | target.addAttribute("selectmode", "none"); | ||||
} | } | ||||
* <p> | * <p> | ||||
* Note, that on some clients the mode may not be respected. E.g. on touch | * 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 | * 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 | * @param mode | ||||
* The select mode of the table | * The select mode of the table | ||||
return multiSelectMode; | 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 | * Lazy loading accept criterion for Table. Accepted target rows are loaded | ||||
* from server once per drag and drop operation. Developer must override one | * from server once per drag and drop operation. Developer must override one |
@SuppressWarnings("serial") | @SuppressWarnings("serial") | ||||
public class CtrlShiftMultiselect extends TestBase { | public class CtrlShiftMultiselect extends TestBase { | ||||
protected final Table table = new Table("Multiselectable table"); | |||||
@Override | @Override | ||||
protected void setup() { | protected void setup() { | ||||
final Table table = new Table("Multiselectable table"); | |||||
table.setContainerDataSource(createContainer()); | table.setContainerDataSource(createContainer()); | ||||
table.setImmediate(true); | table.setImmediate(true); |
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; | |||||
} | |||||
} |
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; | |||||
} | |||||
} |
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(); | |||||
} | |||||
} |
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(); | |||||
} | |||||
} |