tw-S
tw-S

Reputation: 1217

Checkboxes only on Leafs of TreeTableView in JavaFX

I have a TreeTableIView with 3 levels. I want to show Checkboxes on leafs only, while all other cells remain empty. So far, I have created 2 columns:

The first column holds the hierarchy:

      TreeTableColumn<AttributeRow, String> attributeColumn = new TreeTableColumn<>("Attributes");
        attributeColumn.setPrefWidth(200);
        attributeColumn.setCellValueFactory(
            (TreeTableColumn.CellDataFeatures<AttributeRow, String> param) -> 
                new ReadOnlyStringWrapper(param.getValue().getValue().getAttributeName())
        );

The second columns shall contain the CheckBoxes on leafs only:

           TreeTableColumn<AttributeRow, Boolean> companyColumn = new TreeTableColumn<>(companyGroup);
           companyColumn.setPrefWidth(200);
           companyColumn.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(companyColumn));
           companyColumn.setCellValueFactory(param -> {
               if (param.getValue().getValue().getCompanyGroups() == null) {
                   return new SimpleBooleanProperty(true);
               } else {                

                   return new SimpleBooleanProperty(false); 
               }
           });

This is what I want to achieve:

enter image description here

Is there a way to only show CheckBoxes on leafs, while the other cells are empty? Thanks!

Upvotes: 3

Views: 1369

Answers (1)

fabian
fabian

Reputation: 82461

You can use a custom rowFactory that checks, if the item it contains is a leaf or not and adds or removes a leaf pseudoclass:

private static final PseudoClass LEAF = PseudoClass.getPseudoClass("leaf");

...

treeTableView.setRowFactory(view -> new TreeTableRow<BoolItem>() {

    {
        ChangeListener<Boolean> listener = (observable, oldValue, newValue) -> {
            pseudoClassStateChanged(LEAF, newValue);
        };
        treeItemProperty().addListener((observable, oldItem, newItem) -> {
            if (oldItem != null) {
                oldItem.leafProperty().removeListener(listener);
            }
            if (newItem != null) {
                newItem.leafProperty().addListener(listener);
                listener.changed(null, null, newItem.isLeaf());
            } else {
                listener.changed(null, null, Boolean.FALSE);
            }
        });
    }

});

Furthemore use a custom cellFactory that in addition to creating the cell also assigns a style class to it that allows you to identify it as a cell that should be hidden, if it's not a leaf:

companyColumn.setCellFactory((TreeTableColumn<AttributeRow, Boolean> param) -> {
    CheckBoxTreeTableCell<AttributeRow, Boolean> cell = new CheckBoxTreeTableCell<>();
    cell.getStyleClass().add("hide-non-leaf");
    return cell;
});

This allows you to hide the appropriate cells from a CSS stylesheet:

.tree-table-row-cell .hide-non-leaf * {
    /* invisible by default */
    visibility: hidden;
}

.tree-table-row-cell:leaf .hide-non-leaf * {
    /* make visible, if leaf */
    visibility: visible;
}

Edit: For some reason javafx seems to set the visibility of the cell inline, which overrides the CSS style. Therefore you the children of the cells are made invisible instead of the cells themselves.

Upvotes: 7

Related Questions