4

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);
    }
}
Community
  • 1
  • 1
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • `>` css syntax may help you. Meaning you can specify style of subcomponents – Eugene Ryzhikov Jan 23 '15 at 15:46
  • @eugener thanks for the suggestion, but doesn't make a difference (edited the css with the variant) – kleopatra Jan 23 '15 at 15:49
  • If I look into `modena.css` I see the following: `-fx-selection-bar: -fx-accent;` So why don't you use `-fx-background-color: -fx-accent;` directly instead if its working? – eckig Jan 23 '15 at 16:24
  • @eckig yeah, I know it looks like the same - but there seems to be a difference somehow/where. Why is the one working and the other not? I think there's a reason for the additional indirection (accent --> selection), so in order to go-for-the-highest-abstraction-available selection feels more appropriate Also note, that the text fill isn't updated with accent (it is with selection, even if it's not showing) ... Really strange – kleopatra Jan 23 '15 at 16:55

2 Answers2

3

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; 
}
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • silly me mixed up the rule with the color value - no wonder it didn't work ;-) Thanks again for your thorough explanation! – kleopatra Jan 26 '15 at 14:58
1

Again, I'm not 100% sure I understand your requirements, but following quick test works for me (Label is assigned to a graphic property of a Button):

.button {
    -fx-text-fill: red;
    -fx-background-color: yellow;
}

.button > .label {
    -fx-text-fill: blue;
    -fx-background-color: yellow;
}

.button:pressed > .label {
    -fx-text-fill: white;
    -fx-background-color: black;
}
Eugene Ryzhikov
  • 17,131
  • 3
  • 38
  • 60
  • yeah, I noticed that hard-coding is working (probably didn't make it clear enough, sorry) - but why isn't it with a "semantic" color? – kleopatra Jan 23 '15 at 16:51