Thursday, July 31, 2014

JavaFX StyleablePropertyFactory

javafx.css.StyleablePropertyFactory was just added to the code base for JavaFX 8u40, which you can download from the OpenJFX project. This factory class makes it easier to use CSS in your controls by dramatically reducing the StyleableProperty and CssMetaData boilerplate you have to write to make your JavaFX properties styleable. For more details on how to use CSS in your code, see the JavaFX javadoc for the javafx.css package and classes.

For background, consider an implementation of StyleableProperty and CssMetaData as it is now.

public class MyButton extends Button {

   public BooleanProperty fooProperty() { 
       return (BooleanProperty)foo; 
   }
   private StyleableProperty<Boolean> foo = 
       new SimpleStyleableBooleanProperty(fooMetaData);

   private static final CssMetaData<MyButton,Boolean> fooMetaData = 
        new CssMetaData<>("-my-foo", StyleConverter.getBooleanConverter()) {
           @Override 
           public boolean isSettable(MyButton node) { 
               return !node.fooProperty().isBound(); 
           }
           @Override 
           public StyleableProperty<Boolean> getStyleableProperty(MyButton node) {
               return node.foo; 
           }
        };

    static final List<CssMetaData<? extends Styleable, ?>> cssMetaData;
    static {
        List<CssMetaData<? extends Styleable, ?>> temp = 
            new ArrayList<>(Control.getClassCssMetaData());
        temp.addAll(Arrays.asList(fooMetaData));
        cssMetaData = Collections.unmodifiableList(temp);
    }

    @Override
    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
        return cssMetaData;    
    }          
}

From the high-level view, there are two pieces here. First is the StyleableProperty which is used by CSS to set the calculated value of a style on a property. The second piece is the CssMetaData which is used by CSS to (primarily) discover what css properties to look up for a node (e.g., -fx-fill),  and to get the StyleableProperty for setting the value (e.g., get the StyleableProperty that corresponds to -fx-fill).

With StyleablePropertyFactory, the same can be accomplished in fewer lines of code.

public class MyButton extends Button {

   private static final StyleablePropertyFactory<MyButton> FACTORY = 
       new StyleablePropertyFactory<>(Button.getClassCssMetaData());

   public BooleanProperty fooProperty() { 
       return (BooleanProperty)foo; 
   }
   private StyleableProperty<Boolean> foo = 
       new Factory.createStyleableBooleanProperty(this, "foo", "-my-foo");

    @Override
    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
        return cssMetaData;    
    }          
}