Skip to main content

JavaFX in Style

Posted by javakiddy on December 31, 2008 at 12:21 PM PST

One of the most touted parts of the new JavaFX API is the ability to skin UI controls using CSS-like stylesheets. However the current 1.0 release seems to be rather light on skin-aware controls, while documentation and examples seem to be rarer than a woman at a Star Trek convention. (That's my derogatory stereotyping quota used up for this year!)

Not that lack of documentation ever stopped anyone, of course!

A few days back I was playing about with some code, trying to unlock how the stylesheet support might work. All of a sudden a forum posting by tamerkarakan turning up, containing some hastily written (but insightful)
notes — presumably the rewards of his own digging around in the code and trial-and-error testing.

I thought I'd work his findings up into a more complete, practical, example. What follows is a step-by-step guide to creating your own style-aware JavaFX control, including an external stylesheet which can transform the look of the component without need for recompilation of the JavaFX Script code.


Step one is to create a new type of control which supports a skin. For this example I thought we'd build something pretty simple, a basic progress bar.

package skindemo;
import javafx.scene.control.Control;

public class Progress extends Control
{   public var minimum:Number = 0;
    public var maximum:Number = 100;
    public var value:Number = 50;

    {   skin = ProgressSkin{};

The class extends javafx.scene.control.Control rather than javafx.scene.CustomNode, giving us access to an inherited field, skin. This is where we will plug our scene graph code, which actually draws the control. This class just houses our control's public properties.

Step two is to create the skin itself, giving our control a UI.

package skintest;
import javafx.scene.Group;
import javafx.scene.control.Skin;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.paint.*;
import javafx.scene.shape.Rectangle;

public class ProgressSkin extends Skin
{   public var boxCount:Integer = 10;
    public var boxWidth:Number = 15;
    public var boxHeight:Number = 20;
    public var boxHGap:Number = 2;
    public var unsetHighColor:Color = Color.YELLOW;
    public var unsetMidColor:Color = Color.GREEN;
    public var unsetLowColor:Color = Color.DARKGREEN;
    public var setHighColor:Color = Color.ORANGE;
    public var setMidColor:Color = Color.RED;
    public var setLowColor:Color =  Color.DARKRED;
    def boxValue:Integer = bind
    {   var p:Progress = control as Progress;
        var v:Number = (p.value-p.minimum) /
        (boxCount*v) as Integer;

    {   def border:Number = bind boxWidth/10;
        def arc:Number = bind boxWidth/2;
        def lgUnset:LinearGradient = bind makeLG
        def lgSet:LinearGradient = bind makeLG
        scene = HBox
        {   spacing: bind boxHGap;
            content: bind for(i in [0..<boxCount])
            {   Group
                {   content:
                    [   Rectangle
                        {   width: bind boxWidth;
                            height: bind boxHeight;
                            arcWidth: bind arc;
                            arcHeight: bind arc;
                            fill: bind
                                if(i<boxValue) setLowColor
                                else unsetLowColor;
                        } ,
                        {   x: bind border;
                            y: bind border;
                            width: bind boxWidth-border*2;
                            height: bind boxHeight-border*2;
                            arcWidth: bind arc;
                            arcHeight: bind arc;
                            fill: bind
                                if(i<boxValue) lgSet
                                else lgUnset;                   
    function makeLG(c1:Color,c2:Color,c3:Color) : LinearGradient
    {   LinearGradient
        {   endX: 0;  endY: 1;
            proportional: true;
            [   Stop { offset:0;    color: c2; } ,
                Stop { offset:0.25; color: c1; } ,
                Stop { offset:0.50; color: c2; } ,
                Stop { offset:0.85;    color: c3; }

Our skin extends (unsurprisingly) Skin. This is where our UI code for the Progress control actually lives. We create our scene graph and plug it into the inherited scene variable. The public properties at the head of the file will be controllable through a stylesheet, as we'll see shortly.

Step three creates a test application for our new Progress control.

package skintest;
import javafx.animation.*;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;

var val:Number = 0;
{   scene: Scene
    {   content: VBox
        {   spacing:10;
            translateX: 5;  translateY: 5;
            [   Progress
                {   minimum: 0;  maximum: 100;
                    value: bind val;   
                } ,
                {   id: "testId";
                    minimum: 0;  maximum: 100;
                    value: bind val;   
                } ,
                {   styleClass: "testClass";
                    minimum: 0;  maximum: 100;
                    value: bind val;   
        stylesheets: [ "{__DIR__}../Test.css" ]
        fill:  Color.BLACK;
        width: 200;  height: 100;   
    title: "CSS Test";
    visible: true;

{   repeatCount: Timeline.INDEFINITE;
    autoReverse: true;
    [   at(0s)   { val => 0 } ,
        at(0.1s) { val => 0 tween Interpolator.LINEAR } ,
        at(0.9s) { val => 100 tween Interpolator.LINEAR } ,
        at(1s)   { val => 100 }

Here we create three instances of our control. The second instance has a specific id, and the third has been assigned a style class. The first has neither. The significance of this will be apparent when we look at the stylesheet, next.

Step four is to create a stylesheet.

{   boxWidth: 15;
    boxHGap: 2;
    setHighColor: yellow;
    setMidColor: red;
    setLowColor: darkred;
    unsetHighColor: cyan;
    unsetMidColor: blue;
    unsetLowColor: darkblue;

{   boxWidth: 25;
    boxHeight: 30;
    boxCount: 7;
    boxHGap: 1;
    unsetHighColor: white;
    unsetMidColor: silver;
    unsetLowColor: dimgray;

{   boxWidth: 7;
    boxHGap: 2;
    boxCount: 20;
    setHighColor: yellow;
    setMidColor: limegreen;
    setLowColor: darkgreen;

The above is an external stylesheet file, with three example style rules. The first rule will match any instance of the skintest.Progress control (all of them, in effect); the second rule will match only that Progress control which has the id "testId"; while the third rule matches any Progress control with the style class "testClass".

In each case we are able to manipulate the class properties, change its size, the number of boxes, its colours, etc — all without recompiling the JavaFX Script code.

The source code for this mini-project is available here. Have fun (and don't forget to buy "JavaFX in Action" when it comes out! :)

Related Topics >>


Where does it follow???? Where is the blog????

I cant biliv I signed up for this website and I still cant get to read the full blog.. I am stuck on abstract.

We are working hard on getting this all right for one of the next couple JavaFX releases. Before we go all out documenting what's there and the how to guides we want to make sure we have it all right. Skining a button is nice and simple but it gets more complicated very quickly with a CheckBox for example what happens if you want a different tick shape or let alone a scrollbar. This is where the CSS approach gets tough so we need to get the right balance of control between code, CSS and graphics like FXD files. Its exciting seeing people playing with our work and doing cool things and would to love to hear people thoughts on skinning in FX. After spending 2 year writing the Nimbus Look and Feel I have learnt all the limitations of LAFS and skinning in Swing and want make sure we get it right in FX. Hopefully we will have more cool stuff for you to experiment with soon. I will blog stuff on my blog when I have cool stuff to share. On the question of license for Swing CSS there are plans to make it more official but I am not sure where that is going yet but we understand that people will love to get their hands on it. If i ever get the time I would love to make Nimbus CSS theme-able like JavaFX Controls but that might have to be something that the community does, it would be cool though to have a single CSS file theming a Swing/FX hybrid application.

@Dean -- no problem, feel free. :)

Simon, I just wanted to let you know that I've posted an article related to this one at We have a Web Start link as well. If you're not cool with us including your code in a running example, please let me know and we'll remove the Web Start link. Regards, Dean

Speaking of CSS styling, any chance Sun will change the license on the JavaCSS project: from pure GPL to GPL with classpath exception. so that we can style regular Swing components with CSS as well? I don't understand why such an important project is released under such an unfriendly license....

The code isn't really production quality. For a start I used two grouped rectangles, when a single rectangle with a stroke would have done just as well. Also, the code pre-supposes a gradient fill with four stops, while it would be better to allow the stylesheet to plug its own paint in. Still, it serves its purpose as a demo. I've already crafted a much stronger version, which will probably end up in the book.

If you include package names in the selector, it makes sense to put quotes around it, because the package dots may be confused with the 'class' selector dots. Example: "skintest.Progress".testClass {}

Simon, Why do we need to encapsulate the package & class in quotes ala "org.coffeejolts.javafx.SkinnableClass"? It doesn't make any sense to me. Was there a reason for this?

By the way, the {__DIR__}../Test.css type of resource access didn't play well in a JAR file last time I tried. Thanks, Jim Weaver

By the way, you don't actually need to include the package name in your selector. You also don't need the quotes, but the Netbeans CSS editor likes the file better with them. This is a really nice looking control. Great job. Have you had much luck getting pseudo-class selectors like :hover and :disabled to work? Dean

Brilliant, Simon! Thanks to you and tamerkarakan for your detective work! Jim Weaver

Simon, Great post! I was going to do a similar write up but you beat me to it. Would you consider contributing this control to the JFXtras project at:

Everybody says great post. WHERE IS THE POST??

I cant find the post and I am very frustrated by now. I am loosing my cool just trying to read a post. I should have tried to figure it myself. But I dont blame the sitemaster. Creating great features without documentation or with poor documentation. How are we supposed to compete with Microsoft and Flash? It takes days for me to get somewhere in YawaFX and sites like this one don't help either