Tuesday, June 18, 2013

Tuesday fun...

What a productive week...
This morning I've found an interesting effect on the web that shows a rollover effect between images. I asked myself how much work could it be to create one of these effects in JavaFX...well it did not take long and it's worth to share it with you.

So here is a little screenshot of the effect in JavaFX...



As you might guess I take two images of the same size, cut them into pieces and animate these pieces. The most complicated part was getting the animation of both sides correctly. Ok ok, I agree an image doesn't really show the effect and therefor I captured a little screenvideo that you could find on YouTube.



I'm pretty sure that the code could need some more love but to be honest I was only interested in how long it would take to create the effect so feel free to improve it.

In the demo I simply load two images of the same size and split them into 20 tiles. In my code you could adjust the number of tiles and also the duration of the animation but that's it. After each animation I will toggle the background and the foreground image so this is the point where you might want to add a new image to the background that will come to the front after next animation. So you could use this effect for your dia show etc.

To make it easier for you I've put the code in a repo on bitbucket so fork it if you like...

And that's it again...so keep coding...

Monday, June 17, 2013

Taming the Nashorn...AGAIN...

Hi there,
Like I said some post ago I will make use of the Nashorn scripting capabilities more often in the future and so here is another example.
To be honest I'm looking in the Nashorn scripting stuff only because I would like to improve my workflow for the Raspberry Pi embedded development. Usually I create and test the jar files on my desktop computer before I copy them via scp to the Raspberry Pi. On the Pi I then execute them on the command line. In principle this is ok but if you would like to test new things you have to do the modification-compile-copy procedure very often and that takes time. So the idea is that as soon as a new JDK8 developer preview for the Raspberry Pi is out (and hopefully contains Nashorn) I could create a Java application that loads a JavaScript file at runtime and executes the code in the JavaScript file. With this scenario I could edit the JavaScript file with nano for example and simply start the jar again. This would improve the development workflow a lot. So because we don't have Nashorn on the Raspberry Pi at the moment I have to test it on my desktop where it is part of the JDK8 developer preview already.

Example:
Let's take a gauge from the Enzo library that will look like this...


The idea is to configure the gauge with default values if no JavaScript file is present and if there is a JavaScript file with a given name at the same place as the jar file it should be loaded and the configuration should be taken from the JavaScript file.
To realize that we first of all need a simple JavaFX application that contains the gauge.
So this is the code for that...

public class GaugeDemo extends Application {
    private Gauge gauge;

    @Override public void init() {
        gauge = new Gauge();
    }

    @Override public void start(Stage stage) {
        StackPane pane = new StackPane();
        pane.getChildren().add(gauge);

        Scene scene = new Scene(pane);

        stage.setTitle("Nashorn demo");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

Simple...isn't it...? Ok, now let's create a method that configures the gauge with some default values. In this case we will define things like a title, a unit, some sections and modify the colors of the sections. So the code will look like this...

private void setGaugeDefaults() {
    gauge.setTitle("Default");
    gauge.setUnit("°F");
    gauge.setMinValue(32);
    gauge.setMaxValue(212);
    gauge.setSections(new Section(104, 140),
                      new Section(140, 176),
                      new Section(176, 212));
    gauge.setStyle("-needle       : rgb(  0,   0, 255);" +
                   "-section0-fill: rgb(  0, 255,   0);" +
                   "-section1-fill: rgb(255, 255,   0);" +
                   "-section2-fill: rgb(255,   0,   0);");

}

Please keep in mind that calling setStyle() is not the best way to set a JavaFX CSS style but it's ok for this example!!!

If we call this method in the init() method after we have initialized the Gauge object the result will look like this...


So we now have the default style that should be used if no JavaScript file is present. So now we need a method to load a JavaScript file. We need the JavaScript file to be loaded into a String object that we could evaluate with the JavaScript engine. The code that we need to realize that looks like follows...

private String getScript(final String fileName) {
    StringBuilder scriptContent = new StringBuilder();
    try {
        Path         path  = Paths.get(fileName);
        List<String> lines = 
            Files.readAllLines(path, StandardCharsets.UTF_8);
        for(String line : lines) {
            scriptContent.append(line);
        }        
    } catch (IOException | URISyntaxException exception) {}
    return scriptContent.toString();

}

Now we are able to load a JavaScript file into a String so it's time to initialize the JavaScript engine...means unleash the Nashorn... :)
But before we can do that we need a JavaScript file. The idea is that we could pass the gauge object to the JavaScript file and modifiy the gauge within the JavaScript file.
So the JavaScript file will look like follows...

var ArrayList = java.util.ArrayList;
var Gauge     = Packages.eu.hansolo.enzo.gauge.Gauge;
var Section   = Packages.eu.hansolo.enzo.gauge.Section;

var obj = new Object();

obj.initGauge = function(gauge) {
    gauge.title      = 'Nashorn';
    gauge.unit       = '°C';
    gauge.minValue   = 0;
    gauge.maxValue   = 100;

    var sections     = new ArrayList();
    sections.add(new Section(40, 60));
    sections.add(new Section(60, 80));
    sections.add(new Section(80, 100));
    gauge.sections   = sections;

    gauge.style      = "-needle         : rgb(243,56,28);" +
                       "-section0-fill  : rgb(192, 215, 123);" +
                       "-section1-fill  : rgb(217, 191, 78);" +
                       "-section2-fill  : rgb(225, 75, 69);"

}

So the JavaScript above will initialize the gauge for measuring temperature in degree celsius where the setGaugeDefaults() method in our Java class will initialize the gauge for measuring the temperature in degree fahrenheit. Means it should be very easy to see if the script was loaded or not.
Because we could have more than one method in our JavaScript file that we might call in our Java code I created a method that loads the script once, get's the obj Object from the script an then we can use this script object to call the methods. So here is the method that will instantiate three member variables (the ScriptEngine, the so called Invocable and an Object that contains the JavaScript obj)...

private void initScript(final String script) {
    try {
        engine.eval(script);
        inv          = (Invocable) engine;
        scriptObject = engine.get("obj");
    } catch (ScriptException exception) { 
        script = ""
    }

}

Now we only have to add a method that will initialize the gauge object in dependence on the presence of a JavaScript file. So if the JavaScript file is present it should be taken to initialize the gauge otherwise the gauge should be initialized by calling the setGaugeDefaults() method. And here is the code...

private void initGauge() {
    gauge = new Gauge();
    if (script.isEmpty()) {
        // Default Gauge settings
        setGaugeDefaults();
    } else {
        // Settings from Script
        try {
            inv.invokeMethod(scriptObject, "initGauge", gauge );
        } catch (ScriptException | NoSuchMethodException | 
            RuntimeException exception) {
            // In case of an error set default values
            setGaugeDefaults();
        }
    }

}

With this method in place we are able to initialize our gauge by loading a JavaScript file during runtime...sweet.
So the complete Java code will look similar to this...

public class GaugeDemo extends Application {
    private static final String  SCRIPT_FILE_NAME = "config.js";
    private static final Charset ENCODING         = StandardCharsets.UTF_8;
    private ScriptEngineManager  manager;
    private ScriptEngine         engine;
    private String               script;
    private Invocable            inv;
    private Object               scriptObject;
    private Gauge                gauge;


    // ******************** Initialization ************************************
    @Override public void init() {
        manager = new ScriptEngineManager();
        engine  = manager.getEngineByName("nashorn");
        script  = getScript(SCRIPT_FILE_NAME);
        initScript(script);
        initGauge();
    }

    private void initScript(final String script) {
        try {
            engine.eval(script);
            inv          = (Invocable) engine;
            scriptObject = engine.get("obj");
        } catch (ScriptException exception) { 
            script = ""
        }
    }

    private void initGauge() {
        gauge = new Gauge();
        if (script.isEmpty()) {
            // Default Gauge settings
            setGaugeDefaults();
        } else {
            // Settings from Script
            try {
                inv.invokeMethod(scriptObject, "initGauge", gauge );
            } catch (ScriptException | NoSuchMethodException | 
                RuntimeException exception) {
                System.out.println("Error executing initGauge() in JavaScript");
                setGaugeDefaults();
            }
        }
    }


    // ******************** Methods *******************************************
    private void setGaugeDefaults() {
        gauge.setTitle("Default");
        gauge.setUnit("°F");
        gauge.setMinValue(32);
        gauge.setMaxValue(212);
        gauge.setSections(new Section(104, 140),
                          new Section(140, 176),
                          new Section(176, 212));
        gauge.setStyle("-needle         : rgb(0, 150,   0);" +
                       "-section0-fill  : rgb(0, 100, 150);" +
                       "-section1-fill  : rgb(0, 100, 200);" +
                       "-section2-fill  : rgb(0, 100, 255);");
    }

    private String getScript(final String fileName) {
        StringBuilder scriptContent = new StringBuilder();
        try {

            // ******** NEEDED FOR SCRIPT IN SAME FOLDER AS JAR ***************
            final URL    root       = GaugeDemo.class.getProtectionDomain()
                                               .getCodeSource().getLocation();
            final String scriptPath =
                (new File(root.toURI())).getParentFile().getPath() + 
                File.separator + fileName;
            // ****************************************************************            

            Path         path       = Paths.get(scriptPath);
            List<String> lines      = Files.readAllLines(path, ENCODING);
            for (String line : lines) {
                scriptContent.append(line);
            }
            System.out.println("Script: " + scriptPath + " loaded");
        } catch (IOException | URISyntaxException e) {}
        return scriptContent.toString();
    }


    // ******************** Start application *********************************
    @Override public void start(Stage stage) {        
        StackPane pane = new StackPane();
        pane.setPadding(new Insets(10, 10, 10, 10));
        pane.setPrefSize(400, 400);

        pane.getChildren().addAll(gauge);

        Scene scene = new Scene(pane);

        stage.setTitle("Nashorn JavaFX");
        stage.setScene(scene);
        stage.show();        
    }

    public static void main(String[] args) {
        launch(args);
    }

}

Be aware that in the code above the JavaScript file should be placed in the same folder as the jar. If you would like to load the JavaScript with an absolute path you simply could remove the block that is marked in the code and use the full path name for the SCRIPT_FILE_NAME constant.

If I start the code above the result looks like this...


As you can see the gauge was initialized by loading and evaluating the JavaScript file "config.js".

So let's hope the Nashorn will find it's way to the JDK8 ARM developer preview very soon... :)

I hope you enjoy playing with the Nashorn as much as I do...so keep coding...

Sunday, June 16, 2013

JavaFX BBQ details...

Hi there,

The 21st of June is coming closer and here are some more informations about the JavaFX BBQ (which will start around 17:00 - 18:00 o'clock), I've tried to put it in a for you readable format... :)

class JavaFxBBQ() {

  public JavaFxBBQ(boolean wouldLikeToAttend, 
                   boolean wouldLikeToHaveFun, 
                   boolean wouldLikeToEat, 
                   boolean wouldLikeToDrink, 
                   boolean wouldLikeToPresent, 
                   boolean useNavigationSystem, 
                   boolean useStreetMode,
                   boolean latLonMode) {
    
    if (wouldLikeToAttend && wouldLikeToHaveFun) {
      
      if (wouldLikeToEat) {
        bringYourOwnFood();
        if (needSalad) {
          bringYourOwnSalad();
        }
      }

      if (wouldLikeToDrink) {
        if (!beer || !weizen || !water) {
          bringYourOwnDrinks();
        }
      }

      if (wouldLikeToPresent) {
        bringYourComputer();
      }

      if (useNavigationSystem) {
        if (useStreetMode) {
          // use street and city
          putInNavigationSystem("Westfalenstrasse 93"
                                "48165 Münster");
        } else if (latLonMode) {
          // use latitude and longitude
          putInNavigationSystem(51.950085, 7.624097);
        }
      } else {
        // use public transfer from Münster Mainstation
        putInBrowser("http://www.stadtwerke-muenster.de/privatkunden/busverkehr/fahrplanauskunft/efa/fahrplanauskunft.html?name_origin=M%C3%BCnster+Hbf&nameInfo_origin=&name_destination=M%C3%BCnster+%28Westf%29%2C+Merkureck&nameInfo_destination=24047285");

        // use GoogleMaps to find the BBQ
        putInBrowser("https://maps.google.de/maps?f=q&source=s_q&hl=en&geocode=&q=Westfalenstrasse+93,+48165+M%C3%BCnster&aq=&sll=51.950085,7.624097&sspn=0.219646,0.457993&vpsrc=0&ie=UTF8&hq=&hnear=Westfalenstra%C3%9Fe+93,+48165+M%C3%BCnster,+Nordrhein-Westfalen&t=m&z=16"); 
      }     
    } else {
      doWhatEverYouWant();
    }
  }
}

If you have any further question...do not hesitate to contact me on twitter...

Enjoy your weekend and keep coding...

Thursday, June 13, 2013

JavaFX NixieClock on iPad

Hi there,

This is just a very short BlogPost about some fun that I had today. Two days ago I decided to take a look at RoboVM from Niklas Therning which makes it possible to compile JavaFX code for iOS devices. And I have to say THANX to Niklas who did a great job in creating an OpenSource project that is so easy to use and works out of the box even with the 0.3-SNAPSHOT release.
Well this doesn't mean you can now start to port your business application to iOS BUT it's possible to start playing around with it. Because not everything is OpenSource in JDK8 at the moment (Font's and Media are still missing) you can't really create a lot of stuff but as long as you don't use fonts and media stuff you are good to go. Ok ok, effects also do not work for me at the moment but I don't care.
Yesterday at the Eclipse Demo Camp in Munich (which was great, btw thanx to Kai Toedter for the great event) I met to Tom Schindl from Best Solution and we talked about the RoboVM thing and I told him that I've put the BrickBreaker demo already on my iPad. After that short talk and a demo of the BrickBreaker game on the iPad emulator during Tom's session made me think about creating my own little app and compile it for my iPad. Well the biggest problem was that I can't use fonts and so I decided to create a version of the NixieClock for the iPad.
Today during the trainride back from Munich I started from scratch and first created the NixieTube control, after that I created a clock from it and prepared an Eclipse project (because there is an Eclipse plugin for RoboVM which makes compiling and deploying your iOS app very easy) and shortly before I arrived at home I made the first test...it failed. I figured out that with the latest JDK8 developer preview b93 RoboVM has problems. After I've put the kids to bed I had some time to try it again with b92. To get it running I had to do the complete drawing in code instead of css and also had to remove all dropshadows from the control.
But in the end it worked and now I have my sweet little NixieClock running on my iPad :)

For those of you that don't know the clock here is a screen shot of the clock running on my Mac...



Like I said this version was created completely from scratch during my trainride from Munich to Münster and here is the same clock running on my iPad...



As you can see the iPad version is missing the glow effect around the numbers and you could also see some rendering artifacts on the 1 for example but overall it's really nice that I could now compile my JavaFX code for my iPad...

Here is also a little video of the clock running on the Mac and the iPad...

Well I won't share the code here because it might change very quickly again but you could easily follow the instructions on Niklas blog and have some fun... :)

That's it for today...so keep coding...

Tuesday, May 28, 2013

Taming the Nashorn (first impressions)...

Hi there,
JavaScript is everywhere these days and so it is also part of JDK8 dev. prev. with Project Nashorn which is a lightweight high-performance JavaScript runtime engine.
Because I never used Rhino (which is in principle the same just older and slower) I had no  idea how to use it and for what reason.
After I met Marcus Lagergren (@lagergren) at JAX in Mainz (Germany), Geecon in Krakow (Poland) and at Java Forum in Malmö (Sweden) I decided that I have to take a look at the Nashorn to get an idea on how to use it. Of course I'm especially interested on how to use Nashorn in combination with JavaFX. Lucky me that Nashorn is part of the weekly developer previews of JDK8 since a few weeks ago and because Jim Laskey (@wickund) blogged about this use case I was able to start with it yesterday.
After some starting problems I slowly begin to understand and would like to share it with you...
First of all I would like to see how I could use Nashorn with JavaScript in combination with my controls, so the idea is to visualize a Lcd control of my Enzo library with Nashorn.
For the following example you'll need the jdk8 developer preview (grab it here) and a current version of my Enzo library (grab it here).
And with a shell and a text editor you are good to go...really nice :)

So first of all we need to create a JavaScript file that should create a JavaFX application which should show the Lcd control and set's the value of the Lcd control every 3 seconds to a random value between 0 and 100.

This is the JavaFX code to realize that...

import eu.hansolo.enzo.lcd.Lcd;
import eu.hansolo.enzo.lcd.LcdBuilder;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.Random;


public class EnzoFX extends Application {
  private Lcd            lcd;
  private Random         random;
  private long           lastTimerCall;
  private double         charge;
  private AnimationTimer timer;

  @Override public void init() {
    // Initialize the AnimationTimer
    random        = new Random();
    lastTimerCall = System.nanoTime();
    charge        = 0;
    timer         = new AnimationTimer() {
      @Override public void handle(long now) {
        if (now > lastTimerCall + 3_000_000_000l) {
          lcd.setValue(random.nextDouble() * 100);
          lcd.setTrend(Lcd.Trend.values()[random.nextInt(5)]);
          charge += 0.02;
          if (charge > 1.0) charge = 0.0;
          lcd.setBatteryCharge(charge);
          lastTimerCall = now;
          System.out.println(lcd.getValue());
        }
      }
    };

    // Initialize the Enzo Lcd control
    lcd = LcdBuilder.create()
                    .styleClass(Lcd.STYLE_CLASS_STANDARD_GREEN)
                    .title("Room Temp")
                    .unit("°C")
                    .decimals(2)
                    .minMeasuredValueDecimals(2)
                    .maxMeasuredValueDecimals(2)
                    .unitVisible(true)
                    .batteryVisible(true)
                    .alarmVisible(true)
                    .minMeasuredValueVisible(true)
                    .maxMeasuredValueVisible(true)
                    .lowerRightTextVisible(true)
                    .formerValueVisible(true)
                    .trendVisible(true)
                    .lowerRightText("Info")
                    .valueAnimationEnabled(true)
                    .foregroundShadowVisible(true)
                    .crystalOverlayVisible(true)
                    .build();
  }

  @Override public void start(Stage stage) {
    // Prepare stage and add controls
    StackPane root = new StackPane();
    root.setPadding(new Insets(10, 10, 10, 10));
    root.getChildren().add(lcd);

    stage.setTitle("Enzo in JavaFX");
    stage.setScene(new Scene(root, 528, 192));
    stage.show();

    // Start the timer
    timer.start();
  }

  public static void main(String[] args) {
    launch(args);
  }
}

Ok, so now we know how to achieve this in JavaFX but now let's take a look at the JavaScript code that leads to the same result. Here you go...

var System         = java.lang.System;
var Random         = java.util.Random;
var StackPane      = javafx.scene.layout.StackPane;
var Scene          = javafx.scene.Scene;
var Insets         = javafx.geometry.Insets;
var AnimationTimer = javafx.animation.AnimationTimer;
var Lcd            = Packages.eu.hansolo.enzo.lcd.Lcd;


// Initialize the AnimationTimer
var random        = new Random();
var lastTimerCall = System.nanoTime();
var charge        = 0;
var timer         = new AnimationTimer() {
    handle: function(now) {
        if (now > lastTimerCall + 3000000000) {
            lcd.value = random.nextDouble() * 100;
            lcd.trend = Lcd.Trend.values()[random.nextInt(5)];
            charge += 0.02;
            if (charge > 1.0) charge = 0.0;
            lcd.batteryCharge = charge;
            lastTimerCall = now;
            print(lcd.value);
        }
    }
}


// Initialize the Enzo Lcd control
var lcd   = new Lcd();
lcd.styleClass.add(Lcd.STYLE_CLASS_STANDARD_GREEN);
lcd.title                    = "Room Temp";
lcd.unit                     = "°C";
lcd.decimals                 = 2;
lcd.minMeasuredValueDecimals = 2;
lcd.maxMeasuredValueDecimals = 2;
lcd.unitVisible              = true;
lcd.batteryVisible           = true;
lcd.alarmVisible             = true;
lcd.minMeasuredValueVisible  = true;
lcd.maxMeasuredValueVisible  = true;
lcd.lowerRightTextVisible    = true;
lcd.formerValueVisible       = true;
lcd.trendVisible             = true;
lcd.lowerRightText           = "Info";
lcd.valueAnimationEnabled    = true;
lcd.foregroundShadowVisible  = true;
lcd.crystalOverlayVisible    = true;


// Prepare the stage and add controls
var root     = new StackPane();
root.padding = new Insets(10, 10, 10, 10);
root.children.add(lcd);

$STAGE.title = "Enzo with Nashorn";
$STAGE.scene = new Scene(root, 528, 192);
$STAGE.show();

// Start the timer
timer.start();

The JavaScript code that I saved to a file "javafx.js" looks not that different from the JavaFX code except...it's JavaScript...and the resulting application will look like this...



You might ask yourself how you could run this code and here is the answer, all it takes is one call on the command line that looks like this:

/PATH/TO/JDK/jdk1.8.0.jdk/Contents/Home/jre/bin/jjs -cp /PATH/TO/LIBRARY/Enzo.jar -fx /PATH/TO/JS-FILE/javafx.js

Means you have to now the path to your jdk8 installation folder where you could find the jjs executable that is needed to start nashorn from the command line. In addition you have to add the Enzo.jar to the classpath so that you could use it within your JavaScript file and at last you have to call the JavaScript file with the -fx parameter which will start the application....BAM...that's all...sweet :)
I've put the commandline in a bash script so that I could call it by simple calling the bash script instead of typing the whole line over and over again.

If you start the javafx.js file you should see something like on this youtube video.

Ok you might say where is the advantage of using JavaScript to run a JavaFX application...just think about not starting an IDE, not compiling and building a JavaFX application just to make a short test. Isn't it be much nicer to simply have a small JavaScript, edit it with your default text editor and run it with from the command line with one single call...I really like that for testing things.

If you would like to know more about Nashorn you should definitely take a look at the Nashorn blog ,subscribe to the Nashorn mailing list and take a look at the Nashorn Wiki.

At this point I would say THANK YOU to Jim Laskey and Marcus Lagergren which helped me to getting this stuff done!!!

This is really a first test of using Nashorn with JavaFX but now that I know how to use it I will use more often...so stay tuned and keep coding...

Friday, May 24, 2013

CSS == Confusing Style Sheets ?

Hi there,

Finally I found some time to play around with CSS Pseudo classes and so called StyleableProperties in JavaFX. Because there is already a good description about this topics available on the web (OpenJFX Wiki) this post is more about examples of those topics than about the details. So to keep it short...

CSS PSEUDO CLASS
You might have seen something like follows when reading CSS files

.style-class {
    ...
}

.style-class:pseudo {
    ...
}

In the case above pseudo is a so called CSS pseudo class. The nice thing about these pseudo classes is that one could trigger them by using a setter on a property in JavaFX. Means if one calls the method setPseudo(true) in JavaFX, the component that uses the .style-class will now use the .style-class:pseudo for styling. This is really useful because you as a developer don't have to take care about switching the style on the component manually but JavaFX will take care about this.
As a simple example lets create a custom control by extending Region and create a CSS pseudo class for that control. This the code for the control.

public class CSSPseudo extends Region {
  private static final PseudoClass DEMO_PSEUDO_CLASS
    PseudoClass.getPseudoClass("demo");
  private BooleanProperty demo;

  public CSSPseudo() {      
    getStylesheets().add(getClass()
      .getResource("csspseudo.css").toExternalForm());
    getStyleClass().add("region");
    setPrefSize(50, 50);
  }

  public final boolean isDemo() {
    return null == demo ? false : demo.get();
  }
  public final void setDemo(final boolean ON) {
    demoProperty().set(ON);
  }
  public final BooleanProperty demoProperty() {
    if (null == demo) {
      demo = new BooleanPropertyBase(false) {
        @Override protected void invalidated() { 
          pseudoClassStateChanged(DEMO_PSEUDO_CLASS, get()); 
        }
        @Override public Object getBean() { return this; }
        @Override public String getName() { return "demo"; }
      };
    }
    return demo;
  }

}

As you can see in the code the BooleanProperty looks in principle like a standard JavaFX BooleanProperty except the demoProperty() method. In this method the magic happens that if the property is invalid the style of the control will change to :demo in the CSS file.
The CSS file than looks like follows

.region {
    -fx-background-color: red;
}
.region:demo {
    -fx-background-color: green;

}

To test the behavior lets create a little Application and toggle the demo property between true and false which should lead to change of the Regions color between red and green. Here is the Application code

public class CSSDemo extends Application {
  private boolean        toggle;
  private long           lastTimerCall;
  private AnimationTimer timer;
  private CSSPseudo      control;

  @Override public void init() {
    toggle = true;
    lastTimerCall = System.nanoTime();
    timer = new AnimationTimer() {
      @Override public void handle(long now) {
        if (now > lastTimerCall + 1_000_000_000l) {
          toggle ^= true;
          control.setDemo(toggle);
          lastTimerCall = now;
        }
      }
    };
    control = new CSSPseudo();
  }

  @Override public void start(Stage stage) {
    StackPane pane = new StackPane();
    pane.setPadding(new Insets(10, 10, 10, 10));
    pane.getChildren().add(control);

    Scene scene = new Scene(pane);

    stage.setScene(scene);
    stage.show();

    timer.start();
  }

  public static void main(String[] args) {
    launch(args);
  }
}

If you start this application you should see a simple window with a rectangle that changes it's color between red and green every second.




Well and that's all it takes to switch a CSS style by using a JavaFX property...neat isn't it?

STYLEABLE PROPERTY
So now we now how to change a CSS style by using JavaFX properties but would it not be nice if one could set a CSS style (e.g. by loading a specific CSS file) and dependent on the CSS style a property in the code will set to the value given by the CSS style ?
And that's exactly what the StyleableProperties are for.
Means we would like to define the value of a JavaFX property by using a CSS variable.
Therefor it takes a bit more code...

First of all we now create a custom control by extending Control which means we also have to create a Skin for our control. So first of all we need the control class

public class CssStyleable extends Control {
  public static final Color     DEFAULT_CSS_COLOR = Color.RED;
  private ObjectProperty<Paint> cssColor;

  // ******************** Constructors **************************************
  public CssStyleable() {
    getStyleClass().add("css-styleable");
  }

  // ******************** Methods *******************************************
  public final Paint getCssColor() {
    return null == cssColor ? DEFAULT_CSS_COLOR : cssColor.get();
  }
  public final void setCssColor(Paint cssColor) {
    cssColorProperty().set(cssColor);
  }
  public final ObjectProperty<Paint> cssColorProperty() {
    if (null == cssColor) {
      cssColor = new StyleableObjectProperty<Paint>(DEFAULT_CSS_COLOR) {
        @Override public CssMetaData getCssMetaData() { 
          return StyleableProperties.CSS_COLOR
        }
        @Override public Object getBean() { return CssStyleable.this; }
        @Override public String getName() { return "cssColor"; }
      };
    }
    return cssColor;
  }

  // ******************** Style related *************************************
  @Override protected String getUserAgentStylesheet() {
    return getClass().getResource("cssstyleable.css").toExternalForm();
  }

  private static class StyleableProperties {
    private static final CssMetaData<CssStyleable, Paint> CSS_COLOR =
      new CssMetaData<CssStyleable, Paint>("-css-color"
        PaintConverter.getInstance(), DEFAULT_CSS_COLOR) {

      @Override public boolean isSettable(CssStyleable node) {
        return null == node.cssColor || !node.cssColor.isBound();
      }

      @Override public StyleableProperty<Paint> getStyleableProperty(
        CssStyleable node) {
        return (StyleableProperty) node.cssColorProperty();
      }

      @Override public Color getInitialValue(CssStyleable node) {
        return node.DEFAULT_CSS_COLOR;
      }
    };

  private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
  static {
    final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>
      (Control.getClassCssMetaData());
       Collections.addAll(styleables, CSS_COLOR);
       STYLEABLES = Collections.unmodifiableList(styleables);
    }
  }

  public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
    return StyleableProperties.STYLEABLES;
  }

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

}

The next thing we need is the Skin class for our control which looks like this

public class CssStyleableSkin extends SkinBase<CssStyleable> implements Skin<CssStyleable> {
  private static final double PREFERRED_SIZE = 64;
  private Pane                pane;
  private Region              region;

  // ******************** Constructors **************************************
  public CssStyleableSkin(final CssStyleable CONTROL) {
    super(CONTROL);
    pane = new Pane();

    getSkinnable().setPrefSize(PREFERRED_SIZE, PREFERRED_SIZE);
    initGraphics();
    registerListeners();
  }

  private void initGraphics() {
    region = new Region();
    region.getStyleClass().setAll("region");
    region.setPrefSize(PREFERRED_SIZE, PREFERRED_SIZE);
    pane.getChildren().setAll(region);
    getChildren().setAll(pane);
  }

  private void registerListeners() {
    getSkinnable().cssColorProperty().addListener(observable -> { 
      handleControlPropertyChanged("CSS_COLOR"); } );
  }

  // ******************** Methods *******************************************
  protected void handleControlPropertyChanged(final String PROPERTY) {
    if ("CSS_COLOR".equals(PROPERTY)) {
      System.out.println("CSS color property changed to "
                         getSkinnable().getCssColor());
    }
  }

}

As you could see in the code we attach an InvalidationListener to the cssColorProperty() of the CssStyleable control. That means as soon as this property gets invalid (by being changed to another value) the InvalidationListener will print the new color on the console.

Now we need the CSS file (cssstyleable.css) that will be used for our control. In this case it's really small and looks like follows

.css-styleable {
    -fx-skin  : "CssStyleableSkin";
    -css-color: red;
}

.css-styleable .region {
    -fx-background-color: -css-color;

}

As you can see it only defines the Skin file that should be loaded for the control and a the CSS variable named -css-color which is set to red;

With all the files in place we now just need a little application that demonstrates the StyleableProperty. Therefor we will create an app that simply toggles the CSS style of the Region between red and green by calling region.setStyle()

KEEP IN MIND that this CSS inlining should be avoided in an application due to performance but for this demo it's ok to use it.

So the code for the demo Application looks like this

public class CSSDemo extends Application {
  private boolean        toggle;
  private long           lastTimerCall;
  private AnimationTimer timer;
  private CssStyleable   control;


  @Override public void init() {
    toggle = true;
    lastTimerCall = System.nanoTime();
    timer = new AnimationTimer() {
      @Override public void handle(long now) {
        if (now > lastTimerCall + 1_000_000_000l) {
          toggle ^= true;
          control.setStyle(toggle ? "-css-color: red;" : "-css-color: green;");
          lastTimerCall = now;
        }
      }
    };
    control = new CssStyleable();
  }

  @Override public void start(Stage stage) {
    StackPane pane = new StackPane();
    pane.setPadding(new Insets(10, 10, 10, 10));
    pane.getChildren().add(control);

    Scene scene = new Scene(pane);

    stage.setScene(scene);
    stage.show();

    timer.start();
  }

  public static void main(String[] args) {
    launch(args);
  }

}

If you now start that application you should see the same as in the example before, a simple rectangle that changes it's color every second between red and green. In addition you should see something like follows on the the console

CSS color property changed to 0x008000ff
CSS color property changed to 0xff0000ff

CSS color property changed to 0x008000ff

And this indicates that the InvalidationListener in the Skin was triggered because the cssColorProperty() in the CssStyleable class was changed. So you could change a CSS style and the appropriate StyleableProperty in your code will be triggered so that you can react on that change. 

Like I mentioned at the beginning, this post won't explain all the details because there is a good explanation available but it will give you an easy example for the CSS pseudo class and StyleableProperty that you could try by yourself and use it for your controls etc.

And to be honest, I just needed to write it down somewhere so that I could take a look at it when I need it... :)

Keep coding...