]> source.dussan.org Git - vaadin-framework.git/commitdiff
Grid now supports bare ItemSetChangeEvents (#13334)
authorHenrik Paul <henrik@vaadin.com>
Thu, 25 Sep 2014 12:49:21 +0000 (15:49 +0300)
committerHenrik Paul <henrik@vaadin.com>
Fri, 3 Oct 2014 10:07:23 +0000 (13:07 +0300)
Change-Id: Id87b2d7f50720bbfd5011520ea0be32fead48635

client/src/com/vaadin/client/data/AbstractRemoteDataSource.java
client/src/com/vaadin/client/data/DataChangeHandler.java
client/src/com/vaadin/client/data/RpcDataSourceConnector.java
client/src/com/vaadin/client/ui/grid/Grid.java
server/src/com/vaadin/data/RpcDataProviderExtension.java
shared/src/com/vaadin/shared/data/DataProviderRpc.java
uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java
uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridStructureTest.java

index b48a602679f4c5323e7b82bc4d0ff8627c9cb175..7a7743d9b25259e18a2ef94bad91f0a36c84386d 100644 (file)
@@ -550,4 +550,10 @@ public abstract class AbstractRemoteDataSource<T> implements DataSource<T> {
         }
         temporarilyPinnedRows = rows;
     }
+
+    protected void resetDataAndSize(int newSize) {
+        dropFromCache(getCachedRange());
+        cached = Range.withLength(0, 0);
+        dataChangeHandler.resetDataAndSize(newSize);
+    }
 }
index fe72fe673ae49bc095f3404eee52a91f7561c384..57e25ef11a76f2f0a105b3f820074e26dec408dc 100644 (file)
@@ -67,4 +67,16 @@ public interface DataChangeHandler {
      *            the number of available rows
      */
     public void dataAvailable(int firstRowIndex, int numberOfRows);
+
+    /**
+     * Resets all data and defines a new size for the data.
+     * <p>
+     * This should be used in the cases where the data has changed in some
+     * unverifiable way. I.e. "something happened". This will lead to a
+     * re-rendering of the current Grid viewport
+     * 
+     * @param estimatedNewDataSize
+     *            the estimated size of the new data set
+     */
+    public void resetDataAndSize(int estimatedNewDataSize);
 }
index 4c080f5086145689808c5fbbdb253a5ddf4100f8..deb3b5ed7c308b3e5bd2360f28d9c2243afef77b 100644 (file)
@@ -142,6 +142,11 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector {
             public void insertRowData(int firstRow, int count) {
                 dataSource.insertRowData(firstRow, count);
             }
+
+            @Override
+            public void resetDataAndSize(int size) {
+                dataSource.resetDataAndSize(size);
+            }
         });
     }
 
index 34b4ebe18e5ac0f43492eb4c9854a61cf01fbea9..7be14cb068c951734ce1a61f1b9ae936f93331f5 100644 (file)
@@ -1847,6 +1847,28 @@ public class Grid<T> extends ResizeComposite implements
                         numberOfItems);
                 fireEvent(new DataAvailableEvent(currentDataAvailable));
             }
+
+            @Override
+            public void resetDataAndSize(int newSize) {
+                RowContainer body = escalator.getBody();
+
+                /*
+                 * Because the data has simply changed and we don't really know
+                 * what, we'll simply remove everything and redraw everything.
+                 */
+
+                double prevScroll = escalator.getScrollTop();
+                body.removeRows(0, body.getRowCount());
+                body.insertRows(0, newSize);
+
+                /*
+                 * If data was removed or inserted above the scroll top, the
+                 * scroll position is kept locked, leading to data
+                 * "sliding under us". But we can't do anything about that,
+                 * since simply _something_ happened.
+                 */
+                escalator.setScrollTop(prevScroll);
+            }
         });
 
         int previousRowCount = escalator.getBody().getRowCount();
index 55952f92316670b8e32fc93d0642084720da8aa3..99ff57089ccd8ea3558978dba15ded98ac2d7cbd 100644 (file)
@@ -567,6 +567,8 @@ public class RpcDataProviderExtension extends AbstractExtension {
 
     private final ActiveRowHandler activeRowHandler = new ActiveRowHandler();
 
+    private DataProviderRpc rpc;
+
     private final ItemSetChangeListener itemListener = new ItemSetChangeListener() {
         @Override
         public void containerItemSetChange(ItemSetChangeEvent event) {
@@ -586,17 +588,49 @@ public class RpcDataProviderExtension extends AbstractExtension {
             }
 
             else {
-                Range visibleRows = activeRowHandler.activeRange;
-                List<?> itemIds = container.getItemIds(visibleRows.getStart(),
-                        visibleRows.length());
 
-                keyMapper.removeActiveRows(keyMapper.activeRange);
-                keyMapper.addActiveRows(visibleRows, visibleRows.getStart(),
-                        itemIds);
+                /*
+                 * Clear everything we have in view, and let the client
+                 * re-request for whatever it needs.
+                 * 
+                 * Why this shortcut? Well, since anything could've happened, we
+                 * don't know what has happened. There are a lot of use-cases we
+                 * can cover at once with this carte blanche operation:
+                 * 
+                 * 1) Grid is scrolled somewhere in the middle and all the
+                 * rows-inview are removed. We need a new pageful.
+                 * 
+                 * 2) Grid is scrolled somewhere in the middle and none of the
+                 * visible rows are removed. We need no new rows.
+                 * 
+                 * 3) Grid is scrolled all the way to the bottom, and the last
+                 * rows are being removed. Grid needs to scroll up and request
+                 * for more rows at the top.
+                 * 
+                 * 4) Grid is scrolled pretty much to the bottom, and the last
+                 * rows are being removed. Grid needs to be aware that some
+                 * scrolling is needed, but not to compensate for all the
+                 * removed rows. And it also needs to request for some more rows
+                 * to the top.
+                 * 
+                 * 5) Some ranges of rows are removed from view. We need to
+                 * collapse the gaps with existing rows and load the missing
+                 * rows.
+                 * 
+                 * 6) The ultimate use case! Grid has 1.5 pages of rows and
+                 * scrolled a bit down. One page of rows is removed. We need to
+                 * make sure that new rows are loaded, but not all old slots are
+                 * occupied, since the page can't be filled with new row data.
+                 * It also needs to be scrolled to the top.
+                 * 
+                 * So, it's easier (and safer) to do the simple thing instead of
+                 * taking all the corner cases into account.
+                 */
 
-                pushRows(visibleRows.getStart(), itemIds);
-                activeRowHandler.setActiveRows(visibleRows.getStart(),
-                        visibleRows.length());
+                activeRowHandler.activeRange = Range.withLength(0, 0);
+                activeRowHandler.valueChangeListeners.clear();
+                rpc.resetDataAndSize(event.getContainer().size());
+                getState().containerSize = event.getContainer().size();
             }
         }
     };
@@ -613,6 +647,7 @@ public class RpcDataProviderExtension extends AbstractExtension {
      */
     public RpcDataProviderExtension(Indexed container) {
         this.container = container;
+        rpc = getRpcProxy(DataProviderRpc.class);
 
         registerRpc(new DataRequestRpc() {
             private Collection<String> allTemporarilyPinnedKeys = new ArrayList<String>();
@@ -711,7 +746,7 @@ public class RpcDataProviderExtension extends AbstractExtension {
         for (int i = 0; i < itemIds.size(); ++i) {
             rows.set(i, getRowData(propertyIds, itemIds.get(i)));
         }
-        getRpcProxy(DataProviderRpc.class).setRowData(firstRow, rows.toJson());
+        rpc.setRowData(firstRow, rows.toJson());
     }
 
     private JsonValue getRowData(Collection<?> propertyIds, Object itemId) {
@@ -766,7 +801,7 @@ public class RpcDataProviderExtension extends AbstractExtension {
      */
     private void insertRowData(int index, int count) {
         getState().containerSize += count;
-        getRpcProxy(DataProviderRpc.class).insertRowData(index, count);
+        rpc.insertRowData(index, count);
 
         activeRowHandler.insertRows(index, count);
     }
@@ -783,7 +818,7 @@ public class RpcDataProviderExtension extends AbstractExtension {
      */
     private void removeRowData(int firstIndex, int count) {
         getState().containerSize -= count;
-        getRpcProxy(DataProviderRpc.class).removeRowData(firstIndex, count);
+        rpc.removeRowData(firstIndex, count);
 
         for (int i = 0; i < count; i++) {
             Object itemId = keyMapper.itemIdAtIndex(firstIndex + i);
@@ -809,7 +844,7 @@ public class RpcDataProviderExtension extends AbstractExtension {
         JsonValue row = getRowData(container.getContainerPropertyIds(), itemId);
         JsonArray rowArray = Json.createArray();
         rowArray.set(0, row);
-        getRpcProxy(DataProviderRpc.class).setRowData(index, rowArray.toJson());
+        rpc.setRowData(index, rowArray.toJson());
     }
 
     @Override
index 21e299e68b723cecc437d2b6c1c4bc4b71894c41..043818d57304ff7c7475470ed8774746340b5bb0 100644 (file)
@@ -73,4 +73,16 @@ public interface DataProviderRpc extends ClientRpc {
      *            the number of rows inserted at <code>firstRowIndex</code>
      */
     public void insertRowData(int firstRowIndex, int count);
+
+    /**
+     * Resets all data and defines a new size for the data.
+     * <p>
+     * This should be used in the cases where the data has changed in some
+     * unverifiable way. I.e. "something happened". This will lead to a
+     * re-rendering of the current Grid viewport
+     * 
+     * @param size
+     *            the size of the new data set
+     */
+    public void resetDataAndSize(int size);
 }
index cdf3508bea4d41f96e637c5e689fe14d124da8c2..dc5b48107eb81a0056f9084f838a42732e42f890 100644 (file)
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Random;
 
+import com.vaadin.data.Container.Filter;
 import com.vaadin.data.Item;
 import com.vaadin.data.Property;
 import com.vaadin.data.util.IndexedContainer;
@@ -209,6 +210,28 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> {
 
         addHeightActions();
 
+        createClickAction("Column 1 starts with \"(23\"", "Filter",
+                new Command<Grid, Void>() {
+                    @Override
+                    public void execute(Grid grid, Void value, Object data) {
+                        ds.addContainerFilter(new Filter() {
+
+                            @Override
+                            public boolean passesFilter(Object itemId, Item item)
+                                    throws UnsupportedOperationException {
+                                return item.getItemProperty("Column 1")
+                                        .getValue().toString()
+                                        .startsWith("(23");
+                            }
+
+                            @Override
+                            public boolean appliesToProperty(Object propertyId) {
+                                return propertyId.equals("Column 1");
+                            }
+                        });
+                    }
+                }, null);
+
         return grid;
     }
 
index b7711851672317cee6a647f518540aef2032a26f..207a381be827fab549f34b6081ebfa9e854f0fe9 100644 (file)
@@ -20,14 +20,17 @@ import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.util.List;
 
 import org.junit.Test;
 import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
 import org.openqa.selenium.WebElement;
 
 import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.tests.components.grid.GridElement.GridCellElement;
 import com.vaadin.testbench.elements.NotificationElement;
 import com.vaadin.tests.components.grid.GridElement;
 import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeatures;
@@ -229,6 +232,29 @@ public class GridStructureTest extends GridBasicFeaturesTest {
         assertTrue(verticalScrollbarIsPresent());
     }
 
+    @Test
+    public void testBareItemSetChange() throws Exception {
+        openTestURL();
+
+        selectMenuPath("Component", "Filter", "Column 1 starts with \"(23\"");
+
+        boolean foundElements = false;
+        for (int row = 0; row < 100; row++) {
+            try {
+                GridCellElement cell = getGridElement().getCell(row, 1);
+                foundElements = true;
+                assertTrue("Unexpected cell contents. "
+                        + "Did the ItemSetChange work after all?", cell
+                        .getText().startsWith("(23"));
+            } catch (NoSuchElementException e) {
+                assertTrue("No rows were found", foundElements);
+                return;
+            }
+        }
+        fail("unexpected amount of rows post-filter. Did the ItemSetChange work after all?");
+    }
+
+
     @Test
     public void testRemoveLastColumn() {
         setDebug(true);