Revision: 9408
Author: [email protected]
Date: Mon Dec 13 13:39:16 2010
Log: Cherry picking r9403 into release branch.
http://code.google.com/p/google-web-toolkit/source/detail?r=9408
Modified:
/releases/2.1/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ShowMorePagerPanel.java
/releases/2.1/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
/releases/2.1/user/test/com/google/gwt/user/cellview/client/AbstractCellTreeTestBase.java
/releases/2.1/user/test/com/google/gwt/user/cellview/client/CellBrowserTest.java
/releases/2.1/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
=======================================
---
/releases/2.1/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ShowMorePagerPanel.java
Tue Aug 17 10:14:36 2010
+++
/releases/2.1/samples/showcase/src/com/google/gwt/sample/showcase/client/content/cell/ShowMorePagerPanel.java
Mon Dec 13 13:39:16 2010
@@ -54,6 +54,9 @@
public ShowMorePagerPanel() {
initWidget(scrollable);
+ // Do not let the scrollable take tab focus.
+ scrollable.getElement().setTabIndex(-1);
+
// Handle scroll events.
scrollable.addScrollHandler(new ScrollHandler() {
public void onScroll(ScrollEvent event) {
=======================================
---
/releases/2.1/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
Wed Nov 24 05:29:07 2010
+++
/releases/2.1/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
Mon Dec 13 13:39:16 2010
@@ -181,6 +181,8 @@
boolean rowCountIsExact = false;
final List<T> rowData = new ArrayList<T>();
final Set<Integer> selectedRows = new HashSet<Integer>();
+ T selectedValue = null;
+ boolean viewTouched;
public DefaultState(int pageSize) {
this.pageSize = pageSize;
@@ -217,6 +219,10 @@
public List<T> getRowDataValues() {
return Collections.unmodifiableList(rowData);
}
+
+ public T getSelectedValue() {
+ return selectedValue;
+ }
public boolean isRowCountExact() {
return rowCountIsExact;
@@ -233,6 +239,10 @@
public boolean isRowSelected(int index) {
return selectedRows.contains(index);
}
+
+ public boolean isViewTouched() {
+ return viewTouched;
+ }
}
/**
@@ -271,6 +281,8 @@
this.pageStart = state.getPageStart();
this.rowCount = state.getRowCount();
this.rowCountIsExact = state.isRowCountExact();
+ this.selectedValue = state.getSelectedValue();
+ this.viewTouched = state.isViewTouched();
// Copy the row data.
int rowDataSize = state.getRowDataSize();
@@ -342,6 +354,11 @@
*/
List<T> getRowDataValues();
+ /**
+ * Get the value that is selected in the {...@link SelectionModel}.
+ */
+ T getSelectedValue();
+
/**
* Get a boolean indicating whether the row count is exact or an
estimate.
*/
@@ -354,6 +371,13 @@
* @return true if selected, false if not
*/
boolean isRowSelected(int index);
+
+ /**
+ * Check if the user interacted with the view at some point. Selection
is
+ * not bound to the keyboard selected row until the view is touched.
Once
+ * touched, selection is bound from then on.
+ */
+ boolean isViewTouched();
}
/**
@@ -727,10 +751,13 @@
if (KeyboardSelectionPolicy.DISABLED == keyboardSelectionPolicy) {
return;
}
+
+ // The user touched the view.
+ ensurePendingState().viewTouched = true;
/*
* Early exit if the keyboard selected row has not changed and the
keyboard
- * selected value is already set.
+ * selected value is already set.
*/
if (!forceUpdate && getKeyboardSelectedRow() == index
&& getKeyboardSelectedRowValue() != null) {
@@ -1162,24 +1189,38 @@
/*
* Update the SelectionModel based on the keyboard selected value.
This must
- * happen before we read the selection state.
+ * happen before we read the selection state. We only bind to selection
+ * after the user has interacted with the widget at least once. This
+ * prevents values from being selected by default.
*/
if (KeyboardSelectionPolicy.BOUND_TO_SELECTION ==
keyboardSelectionPolicy
- && selectionModel != null) {
- T oldValue = oldState.getRowDataSize() > 0
- ? oldState.getRowDataValue(oldState.getKeyboardSelectedRow()) :
null;
+ && selectionModel != null && pending.viewTouched) {
+ T oldValue = oldState.getSelectedValue();
Object oldKey = getRowValueKey(oldValue);
T newValue = rowDataCount > 0
? pending.getRowDataValue(pending.getKeyboardSelectedRow()) :
null;
Object newKey = getRowValueKey(newValue);
- if ((oldKey == null) ? newKey != null : !oldKey.equals(newKey)) {
+ /*
+ * Do not deselect the old value unless we have a new value to
select, or
+ * we will have a null selection event while we wait for
asynchronous data
+ * to load.
+ */
+ if (newKey != null && !newKey.equals(oldKey)) {
+ // Check both values for selection before setting selection, or the
+ // selection model may resolve state early.
+ boolean oldValueWasSelected = (oldValue == null) ? false
+ : selectionModel.isSelected(oldValue);
+ boolean newValueWasSelected = (newValue == null) ? false
+ : selectionModel.isSelected(newValue);
+
// Deselect the old value.
- if (oldValue != null && selectionModel.isSelected(oldValue)) {
+ if (oldValueWasSelected) {
selectionModel.setSelected(oldValue, false);
}
// Select the new value.
- if (newValue != null && !selectionModel.isSelected(newValue)) {
+ pending.selectedValue = newValue;
+ if (newValue != null && !newValueWasSelected) {
selectionModel.setSelected(newValue, true);
}
}
=======================================
---
/releases/2.1/user/test/com/google/gwt/user/cellview/client/AbstractCellTreeTestBase.java
Wed Dec 1 07:51:56 2010
+++
/releases/2.1/user/test/com/google/gwt/user/cellview/client/AbstractCellTreeTestBase.java
Mon Dec 13 13:39:16 2010
@@ -27,6 +27,7 @@
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ListDataProvider;
+import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.TreeViewModel;
@@ -49,6 +50,8 @@
*/
protected class MockTreeViewModel implements TreeViewModel {
+ private static final int MAX_DEPTH = 4;
+
/**
* The cell used to render all nodes in the tree.
*/
@@ -58,17 +61,31 @@
* The root data provider.
*/
private final ListDataProvider<String> rootDataProvider =
createDataProvider("");
+
+ /**
+ * The selection models at each level of the tree.
+ */
+ private final List<MultiSelectionModel<String>> selectionModels = new
ArrayList<MultiSelectionModel<String>>();
+
+ public MockTreeViewModel() {
+ for (int i = 0; i < MAX_DEPTH; i++) {
+ selectionModels.add(new MultiSelectionModel<String>());
+ }
+ }
public <T> NodeInfo<?> getNodeInfo(T value) {
if (value == ROOT_VALUE) {
- return new DefaultNodeInfo<String>(rootDataProvider, cell);
+ return new DefaultNodeInfo<String>(rootDataProvider, cell,
+ selectionModels.get(0), null);
} else if (value instanceof String) {
String prefix = (String) value;
- if (prefix.length() > 3) {
- throw new IllegalStateException(
- "Prefix should never exceed four characters.");
- }
- return new DefaultNodeInfo<String>(createDataProvider(prefix),
cell);
+ int depth = prefix.length();
+ if (depth >= MAX_DEPTH) {
+ throw new IllegalStateException("Prefix should never exceed "
+ + MAX_DEPTH + " characters.");
+ }
+ return new DefaultNodeInfo<String>(createDataProvider(prefix),
cell,
+ selectionModels.get(depth), null);
}
throw new IllegalArgumentException("Unrecognized value type");
}
@@ -77,12 +94,12 @@
if (value == ROOT_VALUE) {
return false;
} else if (value instanceof String) {
- String s = (String) value;
- if (s.length() > 4) {
+ int depth = ((String) value).length();
+ if (depth > MAX_DEPTH) {
throw new IllegalStateException(
"value should never exceed five characters.");
}
- return ((String) value).length() == 4;
+ return depth == MAX_DEPTH;
}
throw new IllegalArgumentException("Unrecognized value type");
}
@@ -90,6 +107,16 @@
public ListDataProvider<String> getRootDataProvider() {
return rootDataProvider;
}
+
+ /**
+ * Get the {...@link MultiSelectionModel} for the nodes at the specified
depth.
+ *
+ * @param depth the depth of the node
+ * @return the {...@link MultiSelectionModel} at that depth
+ */
+ public MultiSelectionModel<String> getSelectionModel(int depth) {
+ return selectionModels.get(depth);
+ }
/**
* Create a data provider that extends the prefix by one letter.
=======================================
---
/releases/2.1/user/test/com/google/gwt/user/cellview/client/CellBrowserTest.java
Wed Nov 10 04:31:36 2010
+++
/releases/2.1/user/test/com/google/gwt/user/cellview/client/CellBrowserTest.java
Mon Dec 13 13:39:16 2010
@@ -117,6 +117,37 @@
assertEquals(2, browser.treeNodes.get(0).getFocusedKey());
assertTrue(browser.treeNodes.get(0).isFocusedOpen());
}
+
+ /**
+ * Test that even when keyboard selection is bound to the selection
model, we
+ * do not automatically select items in child lists until the child list
is
+ * actually touched.
+ */
+ public void testSetKeyboardSelectionPolicyBound() {
+ CellBrowser browser = (CellBrowser) tree;
+
+ // Bind keyboard selection to the selection model.
+
browser.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.BOUND_TO_SELECTION);
+ assertEquals(KeyboardSelectionPolicy.BOUND_TO_SELECTION,
+ browser.getKeyboardSelectionPolicy());
+
+ // Select an item at depth 0. Nothing should be selected at depth 1.
+ BrowserCellList<?> list0 = browser.treeNodes.get(0).getDisplay();
+ list0.getPresenter().setKeyboardSelectedRow(1, false, false);
+ list0.getPresenter().flush();
+ browser.treeNodes.get(1).getDisplay().getPresenter().flush();
+ assertEquals(1, model.getSelectionModel(0).getSelectedSet().size());
+ assertEquals(0, model.getSelectionModel(1).getSelectedSet().size());
+
+ // Select an item at depth 1. Nothing should be selected at depth 2.
+ BrowserCellList<?> list1 = browser.treeNodes.get(1).getDisplay();
+ list1.getPresenter().setKeyboardSelectedRow(2, false, false);
+ list1.getPresenter().flush();
+ browser.treeNodes.get(2).getDisplay().getPresenter().flush();
+ assertEquals(1, model.getSelectionModel(0).getSelectedSet().size());
+ assertEquals(1, model.getSelectionModel(1).getSelectedSet().size());
+ assertEquals(0, model.getSelectionModel(2).getSelectedSet().size());
+ }
public void testSetKeyboardSelectionPolicyDisabled() {
CellBrowser browser = (CellBrowser) tree;
=======================================
---
/releases/2.1/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
Wed Nov 24 05:29:07 2010
+++
/releases/2.1/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
Mon Dec 13 13:39:16 2010
@@ -31,6 +31,7 @@
import com.google.gwt.view.client.MockHasData.MockRangeChangeHandler;
import com.google.gwt.view.client.MockHasData.MockRowCountChangeHandler;
import com.google.gwt.view.client.MockSelectionModel;
+import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.RangeChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent;
@@ -45,6 +46,49 @@
* Tests for {...@link HasDataPresenter}.
*/
public class HasDataPresenterTest extends GWTTestCase {
+
+ /**
+ * A mock {...@link SelectionChangeEvent.Handler} used for testing.
+ */
+ private static class MockSelectionChangeHandler implements
+ SelectionChangeEvent.Handler {
+
+ private boolean eventFired;
+
+ /**
+ * Assert that a {...@link SelectionChangeEvent} was fired and clear the
+ * boolean.
+ *
+ * @param expected the expected value
+ */
+ public void assertEventFired(boolean expected) {
+ assertEquals(expected, eventFired);
+ eventFired = false;
+ }
+
+ public void onSelectionChange(SelectionChangeEvent event) {
+ assertFalse(eventFired);
+ eventFired = true;
+ }
+ }
+
+ /**
+ * A mock {...@link SelectionModel} used for testing without used any GWT
client
+ * code.
+ *
+ * @param <T> the selection type
+ */
+ private class MockSingleSelectionModel<T> extends
SingleSelectionModel<T> {
+
+ public MockSingleSelectionModel(ProvidesKey<T> keyProvider) {
+ super(keyProvider);
+ }
+
+ @Override
+ public void fireSelectionChangeEvent() {
+ super.fireSelectionChangeEvent();
+ }
+ }
/**
* A mock view used for testing.
@@ -794,6 +838,16 @@
presenter.flush();
assertEquals(0, model.getSelectedSet().size());
+ // Clear the data and push new data. This should not select an item
because
+ // there has not been any user interaction.
+ presenter.setRowCount(0, true);
+ presenter.flush();
+ presenter.setRowCount(100, false);
+ presenter.flush();
+ populatePresenter(presenter);
+ presenter.flush();
+ assertEquals(0, model.getSelectedSet().size());
+
// Select an element.
presenter.setKeyboardSelectedRow(5, false, false);
presenter.flush();
@@ -809,14 +863,71 @@
// Select an element on another page.
presenter.setKeyboardSelectedRow(11, false, false);
presenter.flush();
- // Nothing is selected yet because we don't have data.
- assertEquals(0, model.getSelectedSet().size());
+ // The previous value is still selected because we don't have new data.
+ assertEquals(1, model.getSelectedSet().size());
+ assertTrue(model.isSelected("test 9"));
populatePresenter(presenter);
presenter.flush();
// Once data is pushed, the selection model should be populated.
assertEquals(1, model.getSelectedSet().size());
assertTrue(model.isSelected("test 10"));
}
+
+ /**
+ * Test that we only get one selection event when keyboard selection
changes.
+ */
+ public void testSetKeyboardSelectedRowFiresOneSelectionEvent() {
+ HasData<String> listView = new MockHasData<String>();
+ MockView<String> view = new MockView<String>();
+ HasDataPresenter<String> presenter = new
HasDataPresenter<String>(listView,
+ view, 10, null);
+ presenter.setVisibleRange(new Range(0, 10));
+ populatePresenter(presenter);
+ presenter.flush();
+
+ // Bind keyboard selection to the selection model.
+
presenter.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.BOUND_TO_SELECTION);
+ presenter.setKeyboardPagingPolicy(KeyboardPagingPolicy.CHANGE_PAGE);
+
+ // Add a selection model.
+ MockSingleSelectionModel<String> model = new
MockSingleSelectionModel<String>(
+ null);
+ presenter.setSelectionModel(model);
+ presenter.flush();
+ assertNull(model.getSelectedObject());
+
+ // Add an selection event handler.
+ MockSelectionChangeHandler handler = new MockSelectionChangeHandler();
+ model.addSelectionChangeHandler(handler);
+
+ // Select an element.
+ presenter.setKeyboardSelectedRow(5, false, false);
+ presenter.flush();
+ model.fireSelectionChangeEvent();
+ handler.assertEventFired(true);
+ assertEquals("test 5", model.getSelectedObject());
+
+ // Select another element.
+ presenter.setKeyboardSelectedRow(9, false, false);
+ presenter.flush();
+ model.fireSelectionChangeEvent();
+ handler.assertEventFired(true);
+ assertEquals("test 9", model.getSelectedObject());
+
+ // Select an element on another page.
+ presenter.setKeyboardSelectedRow(11, false, false);
+ presenter.flush();
+ model.fireSelectionChangeEvent();
+ // The previous value is still selected because we don't have new data.
+ handler.assertEventFired(false);
+ assertEquals("test 9", model.getSelectedObject());
+ populatePresenter(presenter);
+ presenter.flush();
+ model.fireSelectionChangeEvent();
+ // Once data is pushed, the selection model should be populated.
+ assertEquals("test 10", model.getSelectedObject());
+ handler.assertEventFired(true);
+ }
public void testSetKeyboardSelectedRowChangePage() {
HasData<String> listView = new MockHasData<String>();
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors