JDK-8244826 : Collapsing row outside of viewport with navigation key makes TreeTableView blank
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: openjfx11,openjfx14
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2020-05-08
  • Updated: 2020-12-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
Windows 10 / jdk11.0.1 

A DESCRIPTION OF THE PROBLEM :
Given a TreeTableView with several rows and children, when a row with children is expanded while it's not visible because the user scrolled, when the row is collapsed using the left navigation key, then the TreeTableView goes blank.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- On a TreeTableView, select a child row (i.e: "child1" from SSCCE) and expand it using right navigation key
- Keep the selection on that row and scroll down to the bottom of the table. The "child1" row is still selected and expanded but not visible 
- Use left navigation key to collapse "child1"

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The row should collapse and the table should continue to displays the rows we were looking at after the scroll. 
ACTUAL -
The table goes blank, the rows are not visible anymore. If the user scrolls the rows appear again.

---------- BEGIN SOURCE ----------

public class CollapseBug extends Application {
    @Override
    public void start(Stage stage) {
        stage.setScene(new Scene(new StackPane(createTree()), 800, 500));
        stage.show();
    }

    private Node createTree() {
        TreeTableView<String> treeTable = new TreeTableView<>();
        treeTable.setMinWidth(500);
        TreeTableColumn<String, String> column = new TreeTableColumn<>("column");
        column.setMinWidth(500);
        treeTable.getColumns().add(column);
        column.setCellValueFactory(value -> new SimpleStringProperty(value.getValue().getValue()));

        treeTable.setOnKeyPressed(event -> {
            ObservableList<TreeTablePosition<String, ?>> selectedCells = treeTable.getSelectionModel().getSelectedCells();
            if (!selectedCells.isEmpty()) {
                int row = selectedCells.get(0).getRow();
                VirtualFlow<?> virtualFlow = getVirtualFlow(treeTable);
                if (virtualFlow != null && virtualFlow.getFirstVisibleCell() != null) {
                    int first = virtualFlow.getFirstVisibleCell().getIndex();
                    int last = virtualFlow.getLastVisibleCell().getIndex();
                    if (row < first || row > last) {
                        System.out.println(row + " is outside visible rows [" + first + ";" + last + "]");
//                        treeTable.refresh(); // Uncomment this line for the workaround to work
                    }
                }
            }
        });

        return createData(treeTable);
    }

    private TreeTableView<String> createData(TreeTableView<String> treeTable) {
        TreeItem<String> root = new TreeItem<>("root");
        treeTable.setRoot(root);
        for (int i = 0; i < 50; i++) {
            root.getChildren().add(new TreeItem<String>("child" + i));
            for (int j = 0; j < 50; j++) {
                root.getChildren().get(i).getChildren().add(new TreeItem<String>("child" + i + "." + j));
            }
        }
        return treeTable;
    }

    private VirtualFlow<?> getVirtualFlow(TreeTableView<?> table) {
        return (VirtualFlow<?>)((TreeTableViewSkin<?>)table.getSkin()).getChildren().get(1);
    }

    public static void main(String[] args) {
        launch(args);
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
In the SSCCE provided, I do a treeTable.refresh() when the collapse event occurs outside of the visible rows. It seems to fix the bug (tested with JavaFx 11.0.2, 14.0.1 and 15-ea+4)
Sometimes it requires a few tries spamming left & right arrow keys to reproduce the issue, sometimes it is reproduced on first try.

FREQUENCY : often



Comments
yet another possible variant - this time going blank after adding items - is https://stackoverflow.com/q/65304453/203657 - working in current dev, misbehaving in fx11.
17-12-2020

Jeanette is right: I can confirm that this issue is no longer reproducible running the SSCCE with JavaFX 16-ea+3, as that includes the fix from JDK-8252811, while it is reproducible with JavaFX 16-ea+2.
23-10-2020

This might be (a regression?) introduced with JDK-8147483 in TreeTableViewSkin.updateItemCount: if (newCount != oldCount) { // The following line is (perhaps temporarily) disabled to // resolve two issues: JDK-8155798 and JDK-8147483. // A unit test exists in TreeTableViewTest to ensure that // the performance issue covered in JDK-8147483 doesn't regress. // requestRebuildCells(); } else { needCellsReconfigured = true; } with uncommenting the requestRebuildCells the misbehavior disappears (at the expense of a failing test for that issue and possible performance issues again). Technically, the issue is that flow.setCellCount clears sheet's children and flow's layout has no clue that it has to re-add/configure them again (in certain corner cases which I was unable to nail ;). So technically, this appears to be fixed implicitly with fixing JDK-8252811: as the cells are never removed from the sheet, layout happens as needed. Probably need make sure that's not only smearing over the issue here, though.
18-09-2020

a question on SO might be another variant of the same issue (didn't dig, though): https://stackoverflow.com/q/63836998/203657 - treeTable going blank when collapsing last expanded item
11-09-2020

I can reproduce this.
12-05-2020