1

I have a JavaFX TreeTableView. Under its root, there is a set of TreeItem (nodes) and each node may have its own child TreeItem (sub-nodes). Since there is no information to show for any nodes without sub-nodes, I want to remove those. But somehow, the simple remove action sometime changes the selection.

package sample;

import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        TreeItem<String> rootNode = new TreeItem<String>("Root");
        rootNode.setExpanded(true);
        rootNode.getChildren().setAll(new TreeItem<>("Node 0"),
                new TreeItem<>("Node 1"), new TreeItem<>("Node 2"),//);
                new TreeItem<>("Node 3"), new TreeItem<>("Node 4"));
        for (int i = 0; i < 2; i++) {
            TreeItem<String> node = rootNode.getChildren().get(i);
            node.setExpanded(true);
            node.getChildren().setAll(new TreeItem<>("Sub Node " + i + "-0"),
                    new TreeItem<>("Sub Node " + i + "-1"),
                    new TreeItem<>("Sub Node " + i + "-2"));
        }

        TreeTableColumn<String, String> column = new TreeTableColumn<>("Nodes");
        column.setCellValueFactory((TreeTableColumn.CellDataFeatures<String, String> p) -> {
            return new ReadOnlyStringWrapper(p.getValue().getValue());
        });
        column.setPrefWidth(200);

        TreeTableView<String> table = new TreeTableView<>(rootNode);
        table.getColumns().add(column);

        int selectIndex = 4;
        int removeIndex = 3;
        table.getSelectionModel().select(selectIndex);
        System.out.println("Selected index = " + table.getSelectionModel().getSelectedIndex());
        System.out.println("Selected item  = " + table.getSelectionModel().getSelectedItem().getValue());
        table.getRoot().getChildren().remove(removeIndex);
        System.out.println("Selected index = " + table.getSelectionModel().getSelectedIndex());
        System.out.println("Selected item  = " + table.getSelectionModel().getSelectedItem().getValue());

        primaryStage.setTitle("Tree Table View Selection");
        primaryStage.setScene(new Scene(table, 300, 275));
        primaryStage.show();
    }


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

The code as it is produces the following output:

Selected index = 4
Selected item  = Sub Node 0-2
Selected index = 3
Selected item  = Sub Node 0-1

However, if (selectedIndex, removeIndex) is changed to (3,4), then the output becomes:

Selected index = 3
Selected item  = Sub Node 0-1
Selected index = 3
Selected item  = Sub Node 0-1

No change in selected index.

In fact, in my limited testing case, as long as selectedIndex is less or equal to removeIndex, then the remove action does not change the selection. Otherwise it changes the selection.

Why does it happen this way? Is there a way to get around this problem?

Guangliang
  • 228
  • 2
  • 10

2 Answers2

1

It appears it is a bug in java. I reported it to bugs.java.com and it is now listed as JDK-8193442 and currently no fix yet.

A work around is to save the selectedItem before remove TreeItem, and call select after the removal. One need to watch out any changelistener on the selectedItemProperty, though.

Guangliang
  • 228
  • 2
  • 10
  • beware: the selection model implementations are ... suboptimal! On the bright side: they are improving, ... slowly ;) Your's might be related of https://bugs.openjdk.java.net/browse/JDK-8187596 (which was reported against a TreeView) – kleopatra Dec 13 '17 at 16:25
  • @kleopatra These two bugs look quite similar. They could be related. – Guangliang Dec 13 '17 at 20:37
0

What's happening here is that you have a selected index, not a selected item, and when you remove nodes from the tree, the selected index remains the same, but the item at that index is now different.

Your tree is indexed depth-first, I think, based on what you've printed out, so picture it's index to item map as an array that looks like this: ["Root", "Node 0", "Sub Node 0-0", "Sub Node 0-1", "Sub Node 0-2", "Node 1", "Sub Node 1-1" ... "Sub Node 4-2"].

When you remove from this tree, you're also removing from this (possibly imaginary) array. You remove something, and everything with a greater index than that is shifted down one spot.

In order to handle this, you need to simply do a check to properly update selectIndex after an item is removed, in order to match its new index. You could do something like the following:

if(selectIndex > removeIndex)
    selectIndex--;
MMAdams
  • 1,508
  • 15
  • 29
  • In the program, the original selection is on "Sub Node 0-2", which is at row 4. "Node 3" is at row 10. If the depth-first argument works, then removing "Node 3" should not have any effect on the selection of "Sub Node 0-2". – Guangliang Dec 12 '17 at 21:25