title: JavaFX 2.2 : Styling a table cell and handling row pseudo-classes (hover, selected, etc)
body: <p>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.</p>
  
  <p>That's easy enough with css, but I'm having trouble covering all of the pseudo-classes of the TableCell.</p>
  
  <pre><code>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&lt;User&gt; table = new TableView&lt;&gt;();
          table.getStylesheets().add(
              this.getClass().getResource( "test.css" ).toExternalForm() );
  
          final TableColumn&lt;User, String&gt; nameColumn =
          this.&lt;String&gt;unstyledColumn( "name" );
          nameColumn.setText( "Name" );
          table.getColumns().add( nameColumn );
  
          final TableColumn&lt;User, Integer&gt; numColumn =
          this.&lt;Integer&gt;unstyledColumn( "number" );
          numColumn.setMinWidth(200);
          numColumn.setText( "Number" );
          table.getColumns().add( numColumn );
  
          final TableColumn&lt;User, Boolean&gt; allowedColumn =
          this.unstyledColumn( "allowed" );
          allowedColumn .setText( "Allowed" );
          allowedColumn .setMinWidth(200);
          allowedColumn .setCellFactory( allowedCellFactory() );
          table.getColumns().add( allowedColumn );
  
          ObservableList&lt;User&gt; 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&lt;TableColumn&lt;User, Boolean&gt;, TableCell&lt;User, Boolean&gt;&gt; allowedCellFactory() {
          final Callback&lt;TableColumn&lt;User, Boolean&gt;, TableCell&lt;User, Boolean&gt;&gt; callback =
              new Callback&lt;TableColumn&lt;User, Boolean&gt;, TableCell&lt;User, Boolean&gt;&gt;() {
                  @Override
                  public TableCell&lt;User, Boolean&gt; call(
                      TableColumn&lt;User, Boolean&gt; arg0 ) {
                      return new TableCell&lt;User, Boolean&gt;() {
                          @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 &lt;S&gt; TableColumn&lt;User, S&gt; unstyledColumn( String name ) {
          TableColumn&lt;User, S&gt; column = new TableColumn&lt;&gt;();
          column.setCellValueFactory( new PropertyValueFactory&lt;User, S&gt;( 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;
          }
  
      }
  }
  </code></pre>
  
  <p>This is complete code ^
  But the point of interest is the <strong>allowedCellFactory</strong> method, which adds the "invalidCell" style class whenever the value in the cell is false.</p>
  
  <p>Here is the css: (test.css, place in same package as code above)</p>
  
  <pre><code>.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;
  }
  </code></pre>
  
  <p>The problem, as can be seen when running the application is that the "allowed" column does not change style when the rest of the <strong>row</strong> is hovered.  When you hover over the allowed column itself, it changes color to match the :hover pseudoclass.</p>
  
  <p>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.</p>
  
  <p>Any ideas?</p>