The issue is that the initial indentation of children below the last expanded parent is incorrect (too small).
Below is a code example which:
- creates and shows an empty treeView
- for visual clarity: has a style rule for the disclosure node with large left padding and yellow background
- builds a treeItem hierarchy of nested items that is attached to the tree _after_ showing
- expected: every child has a larger indentation than its parent
- actual: some have a smaller indentation
- click on any item to select it and see the broken layout to jump to the expected location
notes:
- layout is as expected if the data is added before showing
- the jumping can be seen also without custom style, but is slight in that case
- the mis-location seems to start at the first child of the last expanded parent (and all items below that)
- the issue was brought up at SO https://stackoverflow.com/q/72625590/203657
- digging done in that question reveals a diffferent state of the isNeedsLayout flag of cells when comparing fx8/fx18
The broken layout is a regression: working as expected in fx8, fx11, broken in fx17. Might be technically introduced withJDK-8252811 (fixed after fx15) - reverting that change of VirtualFlow made the layout look okay. On the other hand, this might be a bit accidental (the layout flag looks inconsistent since fx11), feels like there's a cell layout update path that's not fully covered. Also (as noted in the question/answer on SO) the indenting code in TreeCellSkin looks fishy.
The example:
public class TreeViewLayoutBug extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
final TreeView<String> tree = new TreeView<>();
BorderPane root = new BorderPane(tree);
final Scene scene = new Scene(root, 250, 300);
scene.getStylesheets().add(this.getClass().getResource("treeviewlayoutissue.css").toExternalForm());
String version = System.getProperty("javafx.runtime.version");
primaryStage.setTitle("TreeView " + version);
primaryStage.setScene(scene);
primaryStage.show();
// set data after showing the tree
addData(tree);
}
private void addData(TreeView<String> tree) {
// build the item hierarchy
final TreeItem<String> rootNode = new TreeItem<>("root");
rootNode.setExpanded(true);
final TreeItem<String> firstChild = new TreeItem<>("Child A");
final TreeItem<String> secondChild = new TreeItem<>("Child B");
rootNode.getChildren().addAll(firstChild, secondChild);
final TreeItem<String> grandChild = new TreeItem<>("GrandChild A");
firstChild.getChildren().addAll(grandChild);
// variants of added grand/grand children to see intial broken layout
// moving to first child of last expanded parent
// secondChild.getChildren().add(new TreeItem<>("GrandChild B"));
//
// final List<TreeItem<String>> groups = Stream.of("Red", "Green", "Yellow", "Blue").map(TreeItem::new).collect(Collectors.toList());
// groups.forEach(itm -> grandChild.getChildren().add(itm));
firstChild.setExpanded(true);
secondChild.setExpanded(true);
grandChild.setExpanded(true);
// set root - note: doing at the end to not have effect of showRoot
tree.setRoot(rootNode);
}
public static void main(String[] args) {
launch(args);
}
}
The style:
.tree-cell > .tree-disclosure-node{
-fx-padding: 4 6 4 50;
-fx-background-color: yellow;
}
Below is a code example which:
- creates and shows an empty treeView
- for visual clarity: has a style rule for the disclosure node with large left padding and yellow background
- builds a treeItem hierarchy of nested items that is attached to the tree _after_ showing
- expected: every child has a larger indentation than its parent
- actual: some have a smaller indentation
- click on any item to select it and see the broken layout to jump to the expected location
notes:
- layout is as expected if the data is added before showing
- the jumping can be seen also without custom style, but is slight in that case
- the mis-location seems to start at the first child of the last expanded parent (and all items below that)
- the issue was brought up at SO https://stackoverflow.com/q/72625590/203657
- digging done in that question reveals a diffferent state of the isNeedsLayout flag of cells when comparing fx8/fx18
The broken layout is a regression: working as expected in fx8, fx11, broken in fx17. Might be technically introduced with
The example:
public class TreeViewLayoutBug extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
final TreeView<String> tree = new TreeView<>();
BorderPane root = new BorderPane(tree);
final Scene scene = new Scene(root, 250, 300);
scene.getStylesheets().add(this.getClass().getResource("treeviewlayoutissue.css").toExternalForm());
String version = System.getProperty("javafx.runtime.version");
primaryStage.setTitle("TreeView " + version);
primaryStage.setScene(scene);
primaryStage.show();
// set data after showing the tree
addData(tree);
}
private void addData(TreeView<String> tree) {
// build the item hierarchy
final TreeItem<String> rootNode = new TreeItem<>("root");
rootNode.setExpanded(true);
final TreeItem<String> firstChild = new TreeItem<>("Child A");
final TreeItem<String> secondChild = new TreeItem<>("Child B");
rootNode.getChildren().addAll(firstChild, secondChild);
final TreeItem<String> grandChild = new TreeItem<>("GrandChild A");
firstChild.getChildren().addAll(grandChild);
// variants of added grand/grand children to see intial broken layout
// moving to first child of last expanded parent
// secondChild.getChildren().add(new TreeItem<>("GrandChild B"));
//
// final List<TreeItem<String>> groups = Stream.of("Red", "Green", "Yellow", "Blue").map(TreeItem::new).collect(Collectors.toList());
// groups.forEach(itm -> grandChild.getChildren().add(itm));
firstChild.setExpanded(true);
secondChild.setExpanded(true);
grandChild.setExpanded(true);
// set root - note: doing at the end to not have effect of showRoot
tree.setRoot(rootNode);
}
public static void main(String[] args) {
launch(args);
}
}
The style:
.tree-cell > .tree-disclosure-node{
-fx-padding: 4 6 4 50;
-fx-background-color: yellow;
}