How to style a table cell in JavaFX2 using CSS (without removing hover/selection etc.)

css, java, javafx-2

Solution

While not exactly what I wanted, I eventually found a way to create a CSS that doesn't contain too much redundancy.

Many thanks to @James_D for helping out!

Here's the updated `my.css`:

.cell-renderer-changed {
    my-bg: rgba(255, 248, 33, .5);
    -fx-background-color: my-bg;
}

.cell-renderer-error {
    my-bg: rgba(255, 159, 160, .5);
    -fx-background-color: my-bg;
}

.cell-renderer-error-and-changed {
    my-bg: rgba(255, 205, 158, .5);
    -fx-background-color: my-bg;
}

/* Restore default behaviour from caspian.css */
.cell-renderer-changed:hover, .cell-renderer-error:hover, .cell-renderer-error-and-changed:hover {
    -fx-background-color: -fx-table-cell-border-color, -fx-cell-hover-color;
    -fx-background-insets: 0, 0 0 1 0;
}

.cell-renderer-changed:selected, .cell-renderer-error:selected, .cell-renderer-error-and-changed:selected {
    -fx-background: -fx-accent;
    -fx-background-color: -fx-selection-bar;
    -fx-text-fill: -fx-selection-bar-text;
    -fx-background-insets: 0 0 1 0;
}

.cell-renderer-changed:focused:selected, .cell-renderer-error:focused:selected, .cell-renderer-error-and-changed:focused:selected {
    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
    -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2;
    -fx-background: -fx-accent;
    -fx-text-fill: -fx-selection-bar-text;
}

.cell-renderer-changed:focused, .cell-renderer-error:focused, .cell-renderer-error-and-changed:focused {
    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, my-bg;
    /*-fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, transparent;*/
    -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2;
}

Problem

I'm trying to style individual table cells in an JavaFX2 `TableView` (to indicate that a table cell value is changed, contains an error, etc). To achieve this, I have created a custom `TableCell` that sets the proper CSS class, based on it's content. This works to some degree, but the problem is that I now lose all effects, the hover, the selection color gradient etc. (i.e. the pseudo classes). How can I style a table cell using CSS, without requiring re-defining all the possible combinations of pseudo-classes? I've read this answer but the solution does not work for me. If I set `-fx-control-inner-background` to the background color I want, I get hover and selection color like I want, but the initial color is transparent, which is obviously not what I want (I have commented out the attempts in the CSS below, you can uncomment and try for yourself). If you can explain why, and provide a solution, I'd be happy to accept that. I've also seen this answer, but I really don't want to crate duplicate definitions for all the pseudo-classes (besides, it only seems to work with row selection). Below is an SSCCE that demonstrates the issue. Screen shot (this looks like what I want, except the hover and selection doesn't work): TableViewSample.java (based on Table View tutorial code): ``` import javafx.application.Application; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.SelectionMode; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; import javafx.util.Callback; public class TableViewSample extends Application { public static final String CSS_ORIGINAL = "cell-renderer-original"; public static final String CSS_CHANGED = "cell-renderer-changed"; public static final String CSS_ERROR = "cell-renderer-error"; public static final String CSS_ERROR_AND_CHANGED = "cell-renderer-error-and-changed"; private TableView<Person> table = new TableView<>(); private final ObservableList<Person> data = FXCollections.observableArrayList( new Person("Jacob", "Smith", "jacob.smith@example.com"), new Person("Isabella", "Johnson", "isabella.johnson@example.com"), new Person("Ethan", "Williams", "ethan.williams@example.com"), new Person("Emma", "Jones", "emma.jones@example.com"), new Person("Michael", "Brown", "michael.brown@example.com") ); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(450); stage.setHeight(500); Label label = new Label("Address Book"); label.setFont(new Font("Arial", 20)); table.setEditable(true); // Cell selection only (I need this) table.getSelectionModel().setCellSelectionEnabled(true); table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name"); firstNameCol.setMinWidth(100); firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName")); TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name"); lastNameCol.setMinWidth(100); lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName")); TableColumn<Person, String> emailCol = new TableColumn<>("Email"); emailCol.setMinWidth(200); emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email")); // Install custom cell renderer emailCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() { @Override public TableCell<Person, String> call(final TableColumn<Person, String> personStringTableColumn) { return new BackgroundTableCell(); } }); table.setItems(data); table.getColumns().addAll(firstNameCol, lastNameCol, emailCol); VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table); ((Group) scene.getRoot()).getChildren().addAll(vbox); scene.getStylesheets().add(getClass().getResource("/my.css").toExternalForm()); stage.setScene(scene); stage.show(); } public static class Person { private final SimpleStringProperty firstName; private final SimpleStringProperty lastName; private final SimpleStringProperty email; private Person(String fName, String lName, String email) { this.firstName = new SimpleStringProperty(fName); this.lastName = new SimpleStringProperty(lName); this.email = new SimpleStringProperty(email); } public String getFirstName() { return firstName.get(); } public void setFirstName(String fName) { firstName.set(fName); } public String getLastName() { return lastName.get(); } public void setLastName(String fName) { lastName.set(fName); } public String getEmail() { return email.get(); } public void setEmail(String fName) { email.set(fName); } } // My custom background cell renderer private static class BackgroundTableCell extends TableCell<Person, String> { @Override protected void updateItem(final String item, final boolean empty) { super.updateItem(item, empty); setText(empty ? "" : item); getStyleClass().removeAll(CSS_ORIGINAL, CSS_CHANGED, CSS_ERROR, CSS_ERROR_AND_CHANGED); updateStyles(empty ? null : item); } private void updateStyles(String item) { if (item == null) { return; } if (item.startsWith("i") || item.startsWith("j")) { getStyleClass().add(CSS_CHANGED); } else if (item.startsWith("e")) { getStyleClass().add(CSS_ERROR); } else if (item.startsWith("m")) { getStyleClass().add(CSS_ERROR_AND_CHANGED); } } } } ``` my.css: ``` .cell-renderer-changed { -fx-background-color: rgba(255, 248, 33, .4); /*-fx-control-inner-background: rgb(255, 248, 33);*/ -fx-accent: derive(-fx-control-inner-background, -40%); -fx-cell-hover-color: #cce3f4; -fx-cell-focus-inner-border: #85b9de; } .cell-renderer-error { -fx-background-color: rgba(255, 159, 160, .4); /*-fx-control-inner-background: rgb(255, 159, 160);*/ -fx-accent: derive(-fx-control-inner-background, -40%); -fx-cell-hover-color: #cce3f4; -fx-cell-focus-inner-border: #85b9de; } .cell-renderer-error-and-changed { -fx-background-color: rgba(255, 205, 158, .4); /*-fx-control-inner-background: rgb(255, 205, 158);*/ -fx-accent: derive(-fx-control-inner-background, -40%); -fx-cell-hover-color: #cce3f4; -fx-cell-focus-inner-border: #85b9de; } ```

Original source

Related problems