-
Bug
-
Resolution: Fixed
-
P4
-
10
-
b14
To reproduce, compile and run the example below
- expand and collapse root by clicking into the expansion icon
- expected: graphic of root must be visible always
- actual: graphic not showing
Note that the actual # of expand/collapse or which children to expand/collapse and which graphics are effected is a bit unpredictable (read: could not find a pattern).
A bit of digging revealed that the treeItem's graphic is set as the graphic of the checkBox and appears to not be removed properly. So on re-use of the cell, the checkBox still contains the old graphic which leads to removing it from its real target.
While I don't know where exactly the cleanup of the box' graphic should happen, a quick hack seems to be to null the box' graphic in updateItem, see WCheckBoxTreeCell in the example below.
public class CheckBoxTreeCellWithGraphic extends Application {
@SuppressWarnings("unchecked")
private Parent createContent() {
TreeItem<String> root = new TreeItem<>("ROOT", new Label("really"));
root.getChildren().add(new TreeItem<>("firstChild"));
root.getChildren().add(new TreeItem<>("secondChild"));
root.getChildren().forEach(child -> {
child.getChildren().addAll(
new TreeItem<>(" of " + child.getValue(), new Label("grand1 "))
,new TreeItem<>(" of " + child.getValue(), new Label("grand2"))
);
child.setGraphic(new Button(child.getValue()));
});
TreeView<String> tree = new TreeView<>(root);
// core checkBoxTreeCell
tree.setCellFactory(CheckBoxTreeCell.forTreeView());
// hacked checkBoxTreeCell
// tree.setCellFactory(c -> new WCheckBoxTreeCell<>());
return new BorderPane(tree);
}
public static class WCheckBoxTreeCell<T> extends CheckBoxTreeCell<T> {
CheckBox checkBoxAlias;
public WCheckBoxTreeCell() {
InvalidationListener checkBoxGrabber = new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (checkBoxAlias == null) {
if (getGraphic() instanceof CheckBox) {
checkBoxAlias = (CheckBox) getGraphic();
graphicProperty().removeListener(this);
}
}
}
};
graphicProperty().addListener(checkBoxGrabber);
}
@Override
public void updateItem(T item, boolean empty) {
if (checkBoxAlias != null) {
checkBoxAlias.setGraphic(null);
}
super.updateItem(item, empty);
}
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(CheckBoxTreeCellWithGraphic.class.getName());
}
- expand and collapse root by clicking into the expansion icon
- expected: graphic of root must be visible always
- actual: graphic not showing
Note that the actual # of expand/collapse or which children to expand/collapse and which graphics are effected is a bit unpredictable (read: could not find a pattern).
A bit of digging revealed that the treeItem's graphic is set as the graphic of the checkBox and appears to not be removed properly. So on re-use of the cell, the checkBox still contains the old graphic which leads to removing it from its real target.
While I don't know where exactly the cleanup of the box' graphic should happen, a quick hack seems to be to null the box' graphic in updateItem, see WCheckBoxTreeCell in the example below.
public class CheckBoxTreeCellWithGraphic extends Application {
@SuppressWarnings("unchecked")
private Parent createContent() {
TreeItem<String> root = new TreeItem<>("ROOT", new Label("really"));
root.getChildren().add(new TreeItem<>("firstChild"));
root.getChildren().add(new TreeItem<>("secondChild"));
root.getChildren().forEach(child -> {
child.getChildren().addAll(
new TreeItem<>(" of " + child.getValue(), new Label("grand1 "))
,new TreeItem<>(" of " + child.getValue(), new Label("grand2"))
);
child.setGraphic(new Button(child.getValue()));
});
TreeView<String> tree = new TreeView<>(root);
// core checkBoxTreeCell
tree.setCellFactory(CheckBoxTreeCell.forTreeView());
// hacked checkBoxTreeCell
// tree.setCellFactory(c -> new WCheckBoxTreeCell<>());
return new BorderPane(tree);
}
public static class WCheckBoxTreeCell<T> extends CheckBoxTreeCell<T> {
CheckBox checkBoxAlias;
public WCheckBoxTreeCell() {
InvalidationListener checkBoxGrabber = new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (checkBoxAlias == null) {
if (getGraphic() instanceof CheckBox) {
checkBoxAlias = (CheckBox) getGraphic();
graphicProperty().removeListener(this);
}
}
}
};
graphicProperty().addListener(checkBoxGrabber);
}
@Override
public void updateItem(T item, boolean empty) {
if (checkBoxAlias != null) {
checkBoxAlias.setGraphic(null);
}
super.updateItem(item, empty);
}
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(CheckBoxTreeCellWithGraphic.class.getName());
}