Reputation: 7306
I make a custom tree cell which has height 40 px.
And here a problem disclosure triangle does not aligned center vertically.
Here code of tree cell:
public static class TestObjectCell extends AnchorPane implements ISMPBVisualComponentWithData<TestObject>{
public Label label;
public TestObjectCell(){
label=new Label("label");
AnchorPane.setTopAnchor(label, 10.0);
this.getChildren().setAll(label);
this.setMinHeight(40);
this.setPrefHeight(40);
this.setMaxHeight(40);
}
@Override
public void setComponentData(TestObject object) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
As u can see at picture disclosure triangle aligned at top, how do align him vertical center of cell?
Upvotes: 4
Views: 910
Reputation: 461
I found a pretty neat solution by providing a custom cell factory that binds the height of the tree-disclosure-node to the cell height:
public class ArrowCenteringFactory<T> implements Callback<TreeView<T>, TreeCell<T>>
{
private Callback<TreeView<T>, TreeCell<T>> original = CheckBoxTreeCell.<T> forTreeView();
@Override
public TreeCell<T> call(TreeView<T> tree)
{
TreeCell<T> cell = original.call(tree);
bindDisclosureNodeHeightToCellHeight(cell);
return cell;
}
/**
* The disclosure node is monitored being able to bind its height to the cell height in the same way {@link TreeCellSkin#layoutChildren} called by {@link Control#layoutChildren}.
* The default tree-disclosure-node is created in {@link TreeViewSkin#createCell}.
*/
private void bindDisclosureNodeHeightToCellHeight(TreeCell<T> cell)
{
cell.disclosureNodeProperty().addListener((ChangeListener<Node>) (observable, oldValue, newValue) ->
{
if (oldValue instanceof Region)
{
Region old = (Region) oldValue;
old.prefHeightProperty().unbind();
}
if (newValue instanceof Region)
{
Region node = (Region) newValue;
node.prefHeightProperty()
.bind(Bindings.createDoubleBinding(() -> cell.snapSizeY(cell.getHeight()) - cell.snappedTopInset() - cell.snappedBottomInset(),
cell.heightProperty(), cell.insetsProperty()));
}
});
}
}
Because the default tree-disclosure-node is a StackPane
its .arrow
children will automatically be centered in the StackPane
.
You need to set the factory by calling
tree.setCellFactory(new ArrowCenteringFactory<>());
In general this should be fixed in TreeCellSkin#layoutChildren where the tree-disclosure-node is simply asked its preferred height and layout at the top y
.
Upvotes: 0
Reputation: 121
I had the same problem. I used the cell factory to translate the disclosure node of the TreeCell:
myTree.setCellFactory(treeView -> {
final TreeCell<Label> cell = new TreeCell<Label>() {
@Override public void updateItem(Label item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
final Node graphic = (getTreeItem() == null) ? null : getTreeItem().getGraphic();
setGraphic((graphic != null) ? graphic : (item != null) ? item.getGraphic() : null);
}
setText((empty || item == null) ? null : item.getText());
}
};
cell.disclosureNodeProperty().addListener((obs, oldNode, newNode) -> {
final StackPane pane = (StackPane) newNode;
newNode.translateYProperty().bind(cell.heightProperty().multiply(0.5).subtract(pane.heightProperty()));
});
return cell;
});
Surely there must be a better way to do this, but at least this worked for me.
Upvotes: 1
Reputation: 11
The only way that I have found to change the alignment of the disclosure node is to manually override the vertical padding using the -fx-padding
style:
.tree-disclosure-node {
-fx-padding: 0.85em 0.229em 0.333333em 0.229em;
}
This will allow you to get the arrow centered as needed, but you will need to modify this value if the row height ever changes.
Upvotes: 0