Search code examples
javafxtreetableview

JavaFx treetableview setStyle for rows with NO children


In TreeTableView I need to find and setStyle rows with NO children. In below code example, problematic code is in method: markRows.

public class Controller {
    public TreeTableView<MyTreeObject> fuses_ttv;
    
    private ArrayList<MyTreeObject> data = new ArrayList<>();

    private void createTreeTableView(){}

    private void markRows(){
        fuses_ttv.setRowFactory(row -> new TreeTableRow<MyTreeObject>(){
            @Override
            protected void updateItem(MyTreeObject item, boolean empty) {
                super.updateItem(item, empty);
                if (item==null){
                    setStyle(null);
                } else if (item.getType().equals("FRC")){
                    setStyle("-fx-background-color: lightslategray;");
                } else if(item.getType().equals("wire")){
                    setStyle("-fx-background-color: lightyellow;");
                } //***** else if (ROW HAS NOW CHILDREN) - HOW TO DO IT????? ******
            }
        });
    }
}

Like in picture below - rows with SLOT "A1" and "A2" have no children. How to identify such rows? Thanks in advance for any help.

Need setStyle rows with "A1" and "A2" - no children rows.


Solution

  • In JavaFX 19 and later you can do:

        fuses_ttv.setRowFactory(row -> new TreeTableRow<MyTreeObject>(){
    
            {
                treeItemProperty().flatMap(TreeItem::leafProperty)
                    .orElse(false)
                    .addListener((obs, wasLeaf, isLeaf) -> {
                        if (isLeaf) {
                            // set style for leaf (no children)
                        } else {
                            // set style for non-leaf (has children)
                        }
                    });
            }
    
            @Override
            protected void updateItem(MyTreeObject item, boolean empty) {
                super.updateItem(item, empty);
                if (item==null){
                    setStyle(null);
                } else if (item.getType().equals("FRC")){
                    setStyle("-fx-background-color: lightslategray;");
                } else if(item.getType().equals("wire")){
                    setStyle("-fx-background-color: lightyellow;");
                } //***** else if (ROW HAS NOW CHILDREN) - HOW TO DO IT????? ******
            }
        });
    

    I would actually recommend setting custom PseudoClasses, and an external style sheet, instead of using inline styles.

    Here is a complete working example:

    import javafx.application.Application;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.css.PseudoClass;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.HBox;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    
    public class TreeTableStyleExample extends Application {
    
        private int itemCount ;
        @Override
        public void start(Stage stage) throws IOException {
            TreeTableView<Integer> table = new TreeTableView<>();
            TreeTableColumn<Integer, Number> column = new TreeTableColumn<>("Item");
            table.getColumns().add(column);
            column.setCellValueFactory(data -> new SimpleIntegerProperty(data.getValue().getValue()));
            column.setCellFactory(ttv -> new TreeTableCell<>() {
                @Override
                protected void updateItem(Number item, boolean empty) {
                    super.updateItem(item, empty);
                    if (empty || item == null) {
                        setText("");
                    } else {
                        setText("Item "+item);
                    }
                }
            });
    
            PseudoClass leaf = PseudoClass.getPseudoClass("leaf");
            PseudoClass odd = PseudoClass.getPseudoClass("odd-value");
            PseudoClass even = PseudoClass.getPseudoClass("even-value");
            table.setRowFactory( ttv -> new TreeTableRow<>() {
                {
                    treeItemProperty().flatMap(TreeItem::leafProperty).orElse(false)
                            .addListener((obs, wasLeaf, isNowLeaf) -> pseudoClassStateChanged(leaf, isNowLeaf));
                }
    
                @Override
                protected void updateItem(Integer item, boolean empty) {
                    super.updateItem(item, empty);
                    if (item == null || empty) {
                        pseudoClassStateChanged(odd, false);
                        pseudoClassStateChanged(even, false);
                    } else {
                        pseudoClassStateChanged(odd, item % 2 == 1);
                        pseudoClassStateChanged(even, item % 2 == 0);
                    }
                }
            });
    
            table.setRoot(buildTable(20));
    
            Button add = new Button("Add item");
            add.disableProperty().bind(Bindings.isEmpty(table.getSelectionModel().getSelectedItems()));
            add.setOnAction(e -> {
                TreeItem<Integer> treeItem = new TreeItem<>(++itemCount);
                treeItem.setExpanded(true);
                table.getSelectionModel().getSelectedItem().getChildren().add(treeItem);
            });
            Button remove = new Button("Remove");
            remove.disableProperty().bind(Bindings.isEmpty(table.getSelectionModel().getSelectedItems())
                    .or(Bindings.equal(table.getSelectionModel().selectedItemProperty(), table.getRoot())));
            remove.setOnAction(e -> {
                TreeItem<Integer> selection = table.getSelectionModel().getSelectedItem();
                selection.getParent().getChildren().remove(selection);
            });
    
            HBox controls = new HBox(5, add, remove);
            controls.setAlignment(Pos.CENTER);
            controls.setPadding(new Insets(5));
    
            BorderPane root = new BorderPane(table);
            root.setBottom(controls);
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
            stage.setScene(scene);
            stage.show();
        }
    
        private TreeItem<Integer> buildTable(int numItems) {
            Random rng = new Random();
            TreeItem<Integer> root = new TreeItem<>(1);
            root.setExpanded(true);
            List<TreeItem> items = new ArrayList<>();
            items.add(root);
            for (itemCount = 2; itemCount <= numItems ; itemCount++) {
                TreeItem<Integer> item = new TreeItem<>(itemCount);
                item.setExpanded(true);
                items.get(rng.nextInt(items.size())).getChildren().add(item);
                items.add(item);
            }
            return root ;
        }
    
        public static void main(String[] args) {
            launch();
        }
    }
    

    with style.css:

    .tree-table-row-cell:odd-value {
        -fx-background: lightslategray ;
    }
    
    .tree-table-row-cell:even-value {
        -fx-background: lightyellow;
    }
    
    .tree-table-row-cell:leaf {
        -fx-background: lightgreen ;
    }
    

    Sample output:

    enter image description here