On Mon, 4 Jul 2022 13:52:40 GMT, Johan Vos <[email protected]> wrote:
> In order to fix the issues reported in JDK-8089589, the VirtualFlow now uses
> the real sizes of the elements that are displayed. This allows for a more
> natural way of scrolling through lists that contain items of very different
> sizes.
> The algorithm that is used, does not calculate the size of each individual
> cell upfront, as that would be a major performance overhead (especially for
> large lists). Instead, we estimate the total size and the size of the
> individual cells based on the real measured size of a (growing) number of
> cells.
>
> There are a number of key variables that depend on this estimated size:
> * position
> * aboluteOffset
> * currentIndex
>
> As a consequence, when the estimated size is changing, the absoluteOffset may
> change which may lead to a new currentIndex. If this happens during a layout
> phase, or a "complex" operation (e.g. scroll to an item and select it), this
> may lead to visually unexpected artifacts. While the rendering becomes more
> "correct" when the estimated size is improving, it is more important that we
> do not create visual confusion.
>
> The difficulty is that there are a large number of ways to manipulate the
> VirtualFlow, and different entry points may have different expectations about
> which variables should be kept constant (or only changed gradually).
>
> In this PR, we focus on consistency in the scrollTo method, where we want to
> keep the top-cell constant. In case the estimated size is improved during the
> scrollTo execution, or the next layoutChildren invocation, this implies
> keeping the result of computeCurrentIndex() constant so that after scrolling
> to a cell and selecting it, the selected cell is indeed the one that is
> scrolled to.
>
> This PR contains a number of tests for this scrollTo behaviour, that should
> also be considered as regression test in case we want to add more invariants
> later.
The fix looks good to me, although I did leave a question about one of the
boundary checks. This will need additional testing. Several of the
`assertEquals` calls have the expected and actual args backwards. I also left a
few minor formatting comments (I didn't note all of them).
modules/javafx.controls/src/main/java/javafx/scene/control/skin/VirtualFlow.java
line 2321:
> 2319: */
> 2320: void getCellSizesInExpectedViewport(int index) {
> 2321: double estlength = getOrCreateCellSize(index);
Minor: indentation.
modules/javafx.controls/src/main/java/javafx/scene/control/skin/VirtualFlow.java
line 2323:
> 2321: double estlength = getOrCreateCellSize(index);
> 2322: int i = index;
> 2323: while ((estlength < viewportLength) && (++i < getCellCount())){
Minor: need space before `{`
modules/javafx.controls/src/main/java/javafx/scene/control/skin/VirtualFlow.java
line 2328:
> 2326: if (estlength < viewportLength) {
> 2327: int j = index;
> 2328: while ((estlength < viewportLength) && (--j > 0)) {
Is the `> 0` intentional or should it be `>= 0`?
modules/javafx.controls/src/main/java/javafx/scene/control/skin/VirtualFlow.java
line 3098:
> 3096: estSize = estimatedSize / itemCount;
> 3097:
> 3098: if (keepRatio ) {
Minor: there is an extra space before the `)`
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewKeyInputTest.java
line 1837:
> 1835: }
> 1836:
> 1837: @Ignore // there is no guarantee that there will be 8 selected
> items (can be 7 as well)
Ideally we would have an open bug ID for an `@Ignore`d test. In this case, it
might or might not be worth filing one to fix the test.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2189:
> 2187:
> 2188: @Test public void testUnfixedCellScrollResize() {
> 2189: final ObservableList<Integer> items =
> FXCollections.observableArrayList(300,300,70,20);
Minor: add spaces after the commas.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2197:
> 2195: public void updateItem(Integer item, boolean empty) {
> 2196: super.updateItem(item, empty);
> 2197: if (!empty && (item!=null)) {
Minor: space around the `!=`
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2212:
> 2210: IndexedCell<Integer> cell =
> VirtualFlowTestUtils.getCell(listView, i);
> 2211: if ((cell != null) && (cell.getItem() == 20)) {
> 2212: assertEquals("Last cell doesn't end at listview end",
> cell.getLayoutY(), viewportLength - 20,1.);
Expected and actual args are backwards.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2216:
> 2214: }
> 2215: if ((cell != null) && (cell.getItem() == 70)) {
> 2216: assertEquals("Secondlast cell doesn't end properly",
> cell.getLayoutY(), viewportLength - 20 - 70,1.);
Expected and actual args are backwards.
Minor: space after the last comma.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2224:
> 2222: // resize cells and make sure they align after scrolling
> 2223: ObservableList<Integer> list =
> FXCollections.observableArrayList();
> 2224: list.addAll(300,300,20,21);
Expected and actual args are backwards.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2233:
> 2231: IndexedCell<Integer> cell =
> VirtualFlowTestUtils.getCell(listView, i);
> 2232: if ((cell != null) && (cell.getItem() == 21)) {
> 2233: assertEquals("Last cell doesn't end at listview end",
> cell.getLayoutY(), viewportLength - 21,1.);
Expected and actual args are backwards.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2237:
> 2235: }
> 2236: if ((cell != null) && (cell.getItem() == 20)) {
> 2237: assertEquals("Secondlast cell doesn't end properly",
> cell.getLayoutY(), viewportLength - 21 - 20,1.);
Expected and actual args are backwards.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2265:
> 2263: Toolkit.getToolkit().firePulse();
> 2264: int cc = VirtualFlowTestUtils.getCellCount(listView);
> 2265: assertEquals(cc, 15);
Expected and actual args are backwards.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2374:
> 2372: double viewportLength = listViewHeight - 2; // it would be
> better to calculate this from listView but there is no API for this
> 2373:
> 2374: for(int height: heights) {
Minor: space after `for`
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2400:
> 2398: assertTrue("Our cell must be visible!",
> scrollToCell.isVisible());
> 2399:
> 2400: if(lastCell.isVisible() && sumOfHeights >= viewportLength) {
Minor: space after `if`
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2406:
> 2404: if(sumOfHeights < viewportLength) {
> 2405: // If we have less cells then space, then all cells are
> shown, and the position of the last cell, is the sum of the height of the
> previous cells.
> 2406: assertEquals("Last cell should be at the bottom, if we
> scroll to it", lastCell.getLayoutY(), sumOfHeights - lastCellSize,1.);
Expected and actual args are backwards.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2410:
> 2408: if(shouldScrollToBottom && sumOfHeights > viewportLength) {
> 2409: // If we scroll to the bottom, then the last cell should be
> exactly at the bottom
> 2410: // assertEquals("", lastCell.getLayoutY(), viewportLength -
> lastCellSize,1.);
Expected and actual args are backwards.
modules/javafx.controls/src/test/java/test/javafx/scene/control/ListViewTest.java
line 2414:
> 2412: if(!shouldScrollToBottom && sumOfHeights > viewportLength) {
> 2413: // If we don't scroll to the bottom, and the cells are
> bigger than the available space, then our cell should be at the top.
> 2414: assertEquals("Our cell mut be at the top",
> scrollToCell.getLayoutY(), 0,1.);
Expected and actual args are backwards.
-------------
PR: https://git.openjdk.org/jfx/pull/808