Dmitry Nelepov
Dmitry Nelepov

Reputation: 7306

JDK 8 TreeView disclosure triangle vertical aligned not correctly

I make a custom tree cell which has height 40 px.

And here a problem disclosure triangle does not aligned center vertically.

enter image description here

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

Answers (4)

lolung
lolung

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

JOpolka
JOpolka

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

Chad
Chad

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

ShaggyInjun
ShaggyInjun

Reputation: 2973

Have you tried setting alignment property inside TreeCell ?

Upvotes: 0

Related Questions