I'm working on a JavaFX application which include a TableView with special cell, such that when the data in the cell is invalid, the cell turns red.
That's easy enough with css, but I'm having trouble covering all of the pseudo-classes of the TableCell.
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Test extends Application {
public static void main( String[] args ) throws Exception {
Application.launch( "Sportz Club" );
}
@Override
public void start( Stage stage ) throws Exception {
TableView<User> table = new TableView<>();
table.getStylesheets().add(
this.getClass().getResource( "test.css" ).toExternalForm() );
final TableColumn<User, String> nameColumn =
this.<String>unstyledColumn( "name" );
nameColumn.setText( "Name" );
table.getColumns().add( nameColumn );
final TableColumn<User, Integer> numColumn =
this.<Integer>unstyledColumn( "number" );
numColumn.setMinWidth(200);
numColumn.setText( "Number" );
table.getColumns().add( numColumn );
final TableColumn<User, Boolean> allowedColumn =
this.unstyledColumn( "allowed" );
allowedColumn .setText( "Allowed" );
allowedColumn .setMinWidth(200);
allowedColumn .setCellFactory( allowedCellFactory() );
table.getColumns().add( allowedColumn );
ObservableList<User> items = FXCollections.observableArrayList();
items.add( new User( "guy #1", 1, true ) );
items.add( new User( "guy #2", 2, true ) );
items.add( new User( "Steve", 3, false ) );
table.setItems( items );
stage.setScene( new Scene( table, 500, 500 ) );
stage.centerOnScreen();
stage.show();
}
private Callback<TableColumn<User, Boolean>, TableCell<User, Boolean>> allowedCellFactory() {
final Callback<TableColumn<User, Boolean>, TableCell<User, Boolean>> callback =
new Callback<TableColumn<User, Boolean>, TableCell<User, Boolean>>() {
@Override
public TableCell<User, Boolean> call(
TableColumn<User, Boolean> arg0 ) {
return new TableCell<User, Boolean>() {
@Override
protected void updateItem( Boolean item, boolean empty ) {
super.updateItem( item, empty );
if ( item == null || empty ) {
return;
}
if ( item ) {
getStyleClass().remove( "invalidCell" );
}
else {
getStyleClass().add( "invalidCell" );
}
setContentDisplay( ContentDisplay.TEXT_ONLY );
setText( item.toString() );
};
};
}
};
return callback;
}
private <S> TableColumn<User, S> unstyledColumn( String name ) {
TableColumn<User, S> column = new TableColumn<>();
column.setCellValueFactory( new PropertyValueFactory<User, S>( name ) );
return column;
}
public static class User {
StringProperty name;
IntegerProperty number;
BooleanProperty allowed;
public User( String name, int number, boolean allowed ) {
this.name = new ReadOnlyStringWrapper( name );
this.number = new ReadOnlyIntegerWrapper( number );
this.allowed = new ReadOnlyBooleanWrapper( allowed );
}
public StringProperty nameProperty() {
return name;
}
public IntegerProperty numberProperty() {
return number;
}
public BooleanProperty allowedProperty() {
return allowed;
}
}
}
This is complete code ^ But the point of interest is the allowedCellFactory method, which adds the "invalidCell" style class whenever the value in the cell is false.
Here is the css: (test.css, place in same package as code above)
.invalidCell {
-fx-background-color: #FFBBBB;
-fx-text-fill: black !important;
}
.invalidCell:odd {
-fx-background-color: #FFAAAA;
}
.invalidCell:hover {
-fx-background-color: #CCAACC;
}
.invalidCell:filled:selected:focused, .invalidCell:filled:selected, .invalidCell:selected {
-fx-background-insets: 0, 1.4;
-fx-background-color: linear-gradient(from 0% 0% to 0% 100%, #FF6666 0%, #FF2222 100%);
}
.table-view:focused .invalidCell:filled:focused:selected:hover {
-fx-background: -fx-accent;
-fx-background-color: yellow;
-fx-background-insets: 0, 1, 2;
-fx-text-fill: -fx-selection-bar-text;
}
The problem, as can be seen when running the application is that the "allowed" column does not change style when the rest of the row is hovered. When you hover over the allowed column itself, it changes color to match the :hover pseudoclass.
What I would like for it to do is for the allowed column to change along with all of the other columns when the row is hovered over.
Any ideas?