Change-Id: Id87b2d7f50720bbfd5011520ea0be32fead48635tags/7.4.0.beta1
} | } | ||||
temporarilyPinnedRows = rows; | temporarilyPinnedRows = rows; | ||||
} | } | ||||
protected void resetDataAndSize(int newSize) { | |||||
dropFromCache(getCachedRange()); | |||||
cached = Range.withLength(0, 0); | |||||
dataChangeHandler.resetDataAndSize(newSize); | |||||
} | |||||
} | } |
* the number of available rows | * the number of available rows | ||||
*/ | */ | ||||
public void dataAvailable(int firstRowIndex, int numberOfRows); | 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); | |||||
} | } |
public void insertRowData(int firstRow, int count) { | public void insertRowData(int firstRow, int count) { | ||||
dataSource.insertRowData(firstRow, count); | dataSource.insertRowData(firstRow, count); | ||||
} | } | ||||
@Override | |||||
public void resetDataAndSize(int size) { | |||||
dataSource.resetDataAndSize(size); | |||||
} | |||||
}); | }); | ||||
} | } | ||||
numberOfItems); | numberOfItems); | ||||
fireEvent(new DataAvailableEvent(currentDataAvailable)); | 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(); | int previousRowCount = escalator.getBody().getRowCount(); |
private final ActiveRowHandler activeRowHandler = new ActiveRowHandler(); | private final ActiveRowHandler activeRowHandler = new ActiveRowHandler(); | ||||
private DataProviderRpc rpc; | |||||
private final ItemSetChangeListener itemListener = new ItemSetChangeListener() { | private final ItemSetChangeListener itemListener = new ItemSetChangeListener() { | ||||
@Override | @Override | ||||
public void containerItemSetChange(ItemSetChangeEvent event) { | public void containerItemSetChange(ItemSetChangeEvent event) { | ||||
} | } | ||||
else { | 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(); | |||||
} | } | ||||
} | } | ||||
}; | }; | ||||
*/ | */ | ||||
public RpcDataProviderExtension(Indexed container) { | public RpcDataProviderExtension(Indexed container) { | ||||
this.container = container; | this.container = container; | ||||
rpc = getRpcProxy(DataProviderRpc.class); | |||||
registerRpc(new DataRequestRpc() { | registerRpc(new DataRequestRpc() { | ||||
private Collection<String> allTemporarilyPinnedKeys = new ArrayList<String>(); | private Collection<String> allTemporarilyPinnedKeys = new ArrayList<String>(); | ||||
for (int i = 0; i < itemIds.size(); ++i) { | for (int i = 0; i < itemIds.size(); ++i) { | ||||
rows.set(i, getRowData(propertyIds, itemIds.get(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) { | private JsonValue getRowData(Collection<?> propertyIds, Object itemId) { | ||||
*/ | */ | ||||
private void insertRowData(int index, int count) { | private void insertRowData(int index, int count) { | ||||
getState().containerSize += count; | getState().containerSize += count; | ||||
getRpcProxy(DataProviderRpc.class).insertRowData(index, count); | |||||
rpc.insertRowData(index, count); | |||||
activeRowHandler.insertRows(index, count); | activeRowHandler.insertRows(index, count); | ||||
} | } | ||||
*/ | */ | ||||
private void removeRowData(int firstIndex, int count) { | private void removeRowData(int firstIndex, int count) { | ||||
getState().containerSize -= count; | getState().containerSize -= count; | ||||
getRpcProxy(DataProviderRpc.class).removeRowData(firstIndex, count); | |||||
rpc.removeRowData(firstIndex, count); | |||||
for (int i = 0; i < count; i++) { | for (int i = 0; i < count; i++) { | ||||
Object itemId = keyMapper.itemIdAtIndex(firstIndex + i); | Object itemId = keyMapper.itemIdAtIndex(firstIndex + i); | ||||
JsonValue row = getRowData(container.getContainerPropertyIds(), itemId); | JsonValue row = getRowData(container.getContainerPropertyIds(), itemId); | ||||
JsonArray rowArray = Json.createArray(); | JsonArray rowArray = Json.createArray(); | ||||
rowArray.set(0, row); | rowArray.set(0, row); | ||||
getRpcProxy(DataProviderRpc.class).setRowData(index, rowArray.toJson()); | |||||
rpc.setRowData(index, rowArray.toJson()); | |||||
} | } | ||||
@Override | @Override |
* the number of rows inserted at <code>firstRowIndex</code> | * the number of rows inserted at <code>firstRowIndex</code> | ||||
*/ | */ | ||||
public void insertRowData(int firstRowIndex, int count); | 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); | |||||
} | } |
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Random; | import java.util.Random; | ||||
import com.vaadin.data.Container.Filter; | |||||
import com.vaadin.data.Item; | import com.vaadin.data.Item; | ||||
import com.vaadin.data.Property; | import com.vaadin.data.Property; | ||||
import com.vaadin.data.util.IndexedContainer; | import com.vaadin.data.util.IndexedContainer; | ||||
addHeightActions(); | 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; | return grid; | ||||
} | } | ||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | import static org.junit.Assert.assertFalse; | ||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assert.fail; | |||||
import java.util.List; | import java.util.List; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.openqa.selenium.By; | import org.openqa.selenium.By; | ||||
import org.openqa.selenium.NoSuchElementException; | |||||
import org.openqa.selenium.WebElement; | import org.openqa.selenium.WebElement; | ||||
import com.vaadin.testbench.TestBenchElement; | import com.vaadin.testbench.TestBenchElement; | ||||
import com.vaadin.tests.components.grid.GridElement.GridCellElement; | |||||
import com.vaadin.testbench.elements.NotificationElement; | import com.vaadin.testbench.elements.NotificationElement; | ||||
import com.vaadin.tests.components.grid.GridElement; | import com.vaadin.tests.components.grid.GridElement; | ||||
import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeatures; | import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeatures; | ||||
assertTrue(verticalScrollbarIsPresent()); | 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 | @Test | ||||
public void testRemoveLastColumn() { | public void testRemoveLastColumn() { | ||||
setDebug(true); | setDebug(true); |