JavaFX-CSS: How to “move” style of parent to a child?


Tags: java,javafx-8,treecell,javafx-css

Problem :

The trigger for this was a quick experiment to solve TreeItem selection width: the requirement is to highlight only the text, not the whole tree cell. Nothing easier than that (said Frederik :)

  • implement a custom TreeCell with a Label as graphic (and configure the label with the item as needed)
  • remove the selection style (mostly the highlight background from the cell
  • add the highlight style to the label

Something like (a runnable example using this is at the end):

public static class MyTreeCell extends TreeCell<String> {

    private Label label;

    public MyTreeCell() {
        getStyleClass().add("tree-text-only");
    }
    @Override
    public void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        if (empty) {
            setText(null);
            setGraphic(null);
        } else {
            if (label == null) {
                label = new Label();
            }
            label.setText(item);
            setGraphic(label);
        }
    }
}

The css:

/* remove the highlight from cell as a whole, 
   c&p'd the unselected style from modena */
.tree-text-only:filled:selected {
    -fx-background: -fx-control-inner-background; 
    -fx-background-color: -fx-background;
}
/* c&p'd selected style */
/* using > doesn't make a different to the behaviour */ 
/* .tree-text-only:filled:selected > .label { */

.tree-text-only:filled:selected .label {
    /* following is the selection color from themes, doesn't show */
    -fx-background: -fx-selection-bar; 
    /* hard-coded color does show */
    /*   -fx-background-color: -fx-accent ; */
    /* auto-adjust text fill */
    /* no effect for hard-coded color, showing nothing for selection bar */
    -fx-text-fill: -fx-text-background-color;
}

Expected behaviour

  • label has highlighted background when using selection-bar
  • text auto-adjusts its fill

Actual behaviour:

  • using the "semantic" (?) highlight color selection-bar, the background color of the label is not changed to the highlight color but the text fill is changed to the "white", so the text doesn't show
  • using a hard-coded color (any, even the accent as above) changes the label's background but the text fill isn't updated

The obvious question: How to make it work as expected?

For convenience, a runnable example:

public class TreeCellExample extends Application {

    public static class MyTreeCell extends TreeCell<String> {

        private Label label;

        public MyTreeCell() {
            getStyleClass().add("tree-text-only");
        }
        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
                setGraphic(null);
            } else {
                if (label == null) {
                    label = new Label();
                }
                label.setText(item);
                setGraphic(label);
            }
        }
    }

    private Parent getContent() {
        TreeItem root = createSubTree("root");
        root.setExpanded(true);
        TreeView tree = new TreeView(root);
        tree.getStylesheets().add(
                getClass().getResource("treetextonly.css").toExternalForm());
        tree.setCellFactory(p -> new MyTreeCell());
        BorderPane pane = new BorderPane(tree);
        return pane;
    }

    ObservableList rawItems = FXCollections.observableArrayList(
            "9-item", "8-item", "7-item", "6-item", 
            "5-item", "4-item", "3-item", "2-item", "1-item");

    protected TreeItem createSubTree(Object value) {
        TreeItem child = new TreeItem(value);
        child.getChildren().setAll((List<TreeItem>) rawItems.stream()
                .map(TreeItem::new)
                .collect(Collectors.toList()));
        return child;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Scene scene = new Scene(getContent());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}


Solution :

The reason why it doesn't work is because .label doesn't have a rule where -fx-background is applied, so any value assigned to it won't affect any of the Label properties.

What's more, Label doesn't use a -fx-background-color property.

So an easy solution would be adding it:

.tree-text-only > .label {
    -fx-background-color: -fx-background; 
}

Now you can apply all the style using only this "semantic" colors.

Note I've added also the case where the control doesn't have the focus:

/* 
 * remove the highlight from cell as a whole 
 */

/* Selected rows */
.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-text-only:filled:selected {
    -fx-background: -fx-control-inner-background; 
}
/* Selected when control is not focused */
.tree-text-only:filled:selected {
    -fx-background: -fx-control-inner-background; 
}
/* focused cell (keyboard navigation) */
.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-text-only:focused {
    -fx-cell-focus-inner-border: -fx-control-inner-background; 
}

/* 
 * highlight only the label
 */

// Add background color rule
.tree-text-only > .label {
    -fx-background-color: -fx-background; 
}
/* Selected rows */
.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-text-only:filled:selected > .label {
    -fx-background: -fx-selection-bar; 
}
/* Selected when control is not focused */
.tree-text-only:filled:selected > .label {
    -fx-background: -fx-selection-bar-non-focused;
}
/* focused cell (keyboard navigation) */
.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-text-only:focused > .label {
    -fx-background: -fx-selection-bar; 
}

    CSS Howto..

    How to replace css class value using angularjs values?

    How to select element by order ? - Css

    How to preload style image to use it when server is down?

    How to read CSS property of Psuedo class in javascript

    How to make both text and image change on rollover via CSS?

    How can I cut long text according to available div area size?

    How to apply custom css to split post into multiple page

    How to fit css textbox to content?

    How to set CSS hover on input

    css how to force to ul,ol to show numbers or disc, after reset the list style.?

    How to make entire div clickable with css only

    How to adjust font awesome down arrow via css

    How to stretch individual background images in CSS

    How to position boxes like a posterboard with CSS?

    how to turn off the break word in css? [duplicate]

    How can the css dottled border be implemented?

    How to attach for every Array-Element in jQuery different Animationeffects? And animate one by one forwards and backwards with 2 Buttons(back&next)?

    CSS how to height step arrow

    How can I make my icon
    s show up next to each other, rather than underneath each other?

    CSS :hover - How to get lower DIVs to :hover as well?

    How to scroll an html element both horizontally and vertically with top and side labels scrolling correctly

    how to repeat a small background image througout the page?

    Integration of CSS, Javascript and html ,,,howto

    JQuery UI Elements' Image not shown

    how do I override user agent — using buttons from angularjs

    jquery width toggle animation is showing vertical cells during animation takes place

    How to display 'fit' image in CSS or HTML?

    How can a URL fragment affect a CSS layout?

    How to make a Sass mixin declare a non-nested selector on base level?

    How would I make these two elements on the same line?