Monday, April 26, 2010

Advanced JavaFX Control Styling

JavaFX v1.3 includes a brand new and much more powerful CSS-based styling and skinning engine. This article will take a peak under the covers and show you how to get the most out of JavaFX styling so you can develop a unique look for your applications. I'll even show you how to do some basic re-skinning of the core controls - changing not just their color scheme but also their shape! - all from the comfort and convenience of your style sheet. That is a lot of ground to cover so we better get started.

Understanding Regions

Warning!! The Region API is not public and is not considered a finished work. It can, and certainly will, change in future versions. Maybe even in JavaFX v1.3.1. The code presented here is for demonstration of the concepts underlying the new styling system in JavaFX 1.3. If you are considering using this information to build your own custom controls, be sure you understand the down side of using private APIs!

In v1.3 the core controls are made up of regions. Regions are basically a shape with one or more backgrounds and borders. All core control skins inherit from SkinBase which extends Region. Take the LabelSkin as an example. Its inheritance hierarchy looks like this:

LabelSkin -> SkinBase -> Region -> Stack

So the controls are regions, but they can also have zero or more sub-regions. Think of a scroll bar control. It is a region, but it also has sub-regions for the thumb button, the track, and the up and down arrow buttons.

Regions are tied into the new CSS system and can be fully specified, right down to their shape, from a style sheet.

Note that SkinBase does not inherit from Skin at any point. A SkinBase can be assigned to a custom control's skin variable through the use of the SkinAdapter class. The SkinAdapter will take care of passing along the control and behavior assignments to the SkinBase when SkinAdapter is set as the Control's skin.

Creating a Demonstration Control

I simply define a class that extends Control, assign the skin using the SkinAdapter, and override the styleClass variable.

public class DemoControl extends Control {
    // This is the class I'll use in style sheets
    override var styleClass = "demo-control";

    // SkinAdapter lets me use a Region as a Skin
    override var skin = SkinAdapter {
        rootRegion: DemoControlRegionSkin{}
    }
}

Next I define a Region-based skin by creating a class that extends SkinBase.

public class DemoControlRegionSkin extends SkinBase {
    // SkinAdapter will set up SkinBase's control and behavior vars
    var cc = bind control as DemoControl;

    // You should always give your control skins a default preferred size
    override function getPrefWidth( height ) { 100 }
    override function getPrefHeight( width ) { 100 }
}

JavaFX Styling Techniques

I think it's best to start with a basic example and build up from there. So I'll begin by creating a simple scene and adding our new demo control.

Stage {
    title: "JavaFX CSS Controls"
    scene: Scene {
        var stack:Stack;

        width: 300
        height: 300
        stylesheets: "{__DIR__}controlStyles.css"

        content: [
            stack = Stack {
                width: bind stack.scene.width
                height: bind stack.scene.height
                content: [
                      DemoControl {
                          layoutInfo: LayoutInfo {
                              width: 250
                              height: 250
                          }
                      }
                ]
            }
        ]
    }
}

I created a simple Scene that is 300 by 300 pixels and has a Stack container that fills it completely. I then created a new instance of my DemoControl and set it's preferred width and height to be 250 pixels. Note that this will override the preferred width and height of 100 that I entered into the control skin itself when I added those functions. By default a Stack will size its children to their preferred size and center them. Therefore, when this program is run we should see a 250 by 250 rectangle (a.k.a. a square) centered in the scene. I'll now add some minimal styling in controlStyles.css (which is added to the Scene above) so the Region's rectangle will be visible.

.scene {
    -fx-font: 16pt "Amble";
    -dean-slightlydarkergreen: derive( green, -25% );
}

.demo-control {
    -fx-background-color: skyblue;
    -fx-border-color: -dean-slightlydarkergreen;
    -fx-border-width: 2;
}

This small style sheet first sets a default font for the Scene. I haven't added any controls with text yet, but I will later and it's always nice to set these things up in one place. Below the font attribute, I demonstrate a feature of JavaFX styling: the ability to derive a new color from an existing one and store it for later use. In this case, I created a darker version of Green and named it -dean-slightlydarkergreen. Not only am I now immortalized in my own style sheet, but the DemoControl can reference this attribute later as shown above for the control's border color. Note that I use the same class string "demo-control" that I hard-coded into my DemoControl class. Let's see how this looks so far.

Gradients

You can also specify linear gradients in your style sheet. The image and code below shows a fancier version of the DemoControl.

#gradient.demo-control {
    -dean-lightnavy: derive(navy, 100%);
    -dean-navygrad: linear (0%,0%) to (100%,0%) stops (0%, -dean-lightnavy) (100%, navy);
    -fx-background-color: linear (0%,0%) to (0%,50%) stops (0%, derive(deepskyblue, 80%)) (30%, deepskyblue) reflect;
    -fx-border-color: -dean-navygrad null -dean-navygrad null;
    -fx-border-width: 8 0 8 0;
}

You can see by the CSS selector "#gradient.demo-control" that this style will be applied to DemoControls with "gradient" for an ID. I didn't have to change any JavaFX script code to make this work other than adding the id: "gradient" attribute to the DemoControl's object literal declaration in the Scene, thereby picking up this new style.

In this style, I also derive a new light navy color and then reference it again when I define my navy gradient (-dean-navygrad). This gradient is then used as the border color. I use it on the top and bottom while setting the left and right border colors to null. Similarly, I set the top and bottom border widths to 8 pixels while the left and right borders have a 0 width. Many style attributes of a Region have this ability to specify top, right, bottom, and left properties individually. On the other hand, if I were to simply say "-fx-border-width: 8" then all borders would be 8 pixels wide.

The Region's background color is a slightly more complicated linear gradient. It goes half way down the rectangle and is then reflected so the bottom half matches the top. Note that you can even embed "derive" functions inside of linear gradient definitions. More power!

Multiple Backgrounds

As I said at the beginning, Regions are made up of a shape and one or more backgrounds and borders. Let's take a look at how you define multiple backgrounds and borders for your region. Once again, I will make no changes to our source code other than the control's ID in the scene and a new style in the style sheet.

#multibg.demo-control {
    -fx-background-color: skyblue, mediumaquamarine;
    -fx-background-radius: 15 0 15 0;
    -fx-background-insets: 0, 20;
    -fx-border-color: navy, darkcyan;
    -fx-border-style: dotted;
    -fx-border-width: 2;
    -fx-border-radius: 15 0 15 0;
    -fx-border-insets: 0, 20;
}

Most of the style attributes of a Region can take multiple items in their declarations. You can see examples of this in the declarations of the -fx-background-color, -fx-background-insets, and the -fx-border-color attributes. In these cases, the first item is applied to the first background, the second item is applied to the second background and so on. Therefore we end up with a skyblue rectangle with navy border and a medium aquamarine rectangle with a dark cyan border. The first background has no insets (it is filled right to the border of the control), while the second rectangle is inset by 20 pixels on all sides.

In cases where there are multiple backgrounds but only one item specified, the first item will be applied to all backgrounds. For example, both rectangles have top-left and bottom-right rounded corners defined by the -fx-background-radius and -fx-border-radius attributes. Since only one set of radius attributes are defined, it will be applied to both backgrounds.

The thing to remember is that commas separate multiple items in a single attribute (as in the two items specified by -fx-background-color above), whereas spaces are used as the separators in an item that can take multiple values (as in the one item having 4 values specified by -fx-border-radius above). Crystal clear? Good. Let's take it up a notch.

Getting Fancier

Here's a twist: many of those attributes that can take multiple values in a single item (like -fx-border-radius) can also take multiple items. This allows you, for example, to specify a different set of border radius values for each background.

#multibgoppcorners.demo-control {
    -fx-background-color: skyblue, mediumaquamarine;
    -fx-background-radius: 15 0 15 0, 0 15 0 15;
    -fx-background-insets: 0, 20 40 20 40;
    -fx-border-color: navy, darkcyan;
    -fx-border-style: dotted, dashed;
    -fx-border-width: 2, 1;
    -fx-border-radius: 15 0 15 0, 0 15 0 15;
    -fx-border-insets: 0, 20 40 20 40;
}

Here I specify a completely different set of values for the -fx-background-radius, -fx-background-insets, -fx-border-radius, and -fx-border-insets attributes for each background. Remember, multiple values within a single item are separated by spaces while multiple items are separated by commas.

Styling Conclusions

In this section I've experimented with the most common style attributes that are used on Regions. You can also use the regular Node style attributes that I covered here. Of interest will be Node's -fx-opacity, -fx-effect, and -fx-cursor attributes. It should also be noted that Regions have a corresponding set of attributes for image-based backgrounds and borders (i.e. -fx-background-image and -fx-border-image and so on). I'm not going to cover them here, but I would expect Oracle to publish full documentation on these attributes very soon.

Shaping Controls

So far, I've just used the default rectangular shape but by using the style sheet I can make a region take on just about any shape that can be imagined. The shape of a Region is specified using SVG path notation. This notation uses a compact encoding to describe shapes using a series of commands and coordinates. Commands are represented by a single letter. For example, M is the move-to command. If the command letter is upper case then the coordinates that follow are considered absolute coordinates whereas the coordinates are interpreted as relative if the letter is lower case. So "M 0 1" means move to location 0,1 while "m 0 1" means move 1 unit in the y direction from the current location. See this page for details of the SVG path syntax.

Movement and Lines

Drawing shapes with one or more lines is extremely easy. The format of the commands is shown in the following table.

Move toM x y m x y
Line toL (x y)+ l (x y)+
Horizontal line toH x
Vertical line toV y
Close the current pathZ z

Suppose I wanted a triangular region instead of the default rectangle.

#line.demo-control {
    -fx-shape: "M 0,1 L 1,1 .5,0 Z";
    -fx-background-color: skyblue, blue;
    -fx-background-insets: 0, 20 20 10 20;
}
The coordinate system used in these paths is the same one used in JavaFX: x increases to the right, y increases down. So our shape is defined as:
  1. Move down one unit in the y direction
  2. Draw a poly-line to the coordinate 1,1 and then 0.5,0
  3. Close the path
By the way, the use of commas to separate the x,y coordinates in a SVG path command are purely optional. I like to use them because it makes the command more readable to me. I'm using capital letters so those coordinates are absolute, not relative to each other. By default, the shape is scaled to fill the area of the control, so the scale of the coordinates doesn't matter. I could have just as easily used 0, 50, and 100 rather than 0, 0.5 and 1.0. You can see that even though the shape has changed, our ability to specify multiple backgrounds with offsets hasn't. One thing to watch our for is that, with non-rectangular shapes, the insets may need to be adjusted to keep the inner shape centered. Here I had to adjust the bottom inset of the second background to make the triangles look centered. I don't know if this is a bug or a feature, but for JavaFX 1.3 be aware that you may need to play with the insets to get things to look exactly as you want. And although the coordinates of the shape are scaled, the insets are still specified in pixels.

Cubic Curves

The SVG path commands for drawing cubic Bezier curves are shown in the following table.

Cubic Bezier curve to Draws a curve from the current point to x,y. x1,y1 is the control point at the beginning of the curve. x2,y2 is the control point at the end of the curve.C (x1 y1 x2 y2 x y)+ c (x1 y1 x2 y2 x y)+
Cubic Bezier curve to (shorthand) Draws a curve from the current point to x,y. The control point at the beginning of the curve is the reflection of the second control point from the previous curve. x2,y2 is the control point at the end of the curve.S (x2 y2 x y)+ s (x2 y2 x y)+

So let's take a look at a region shaped with cubic curves.

#cubic.demo-control {
    -fx-shape: "M100,200 C100,100 250,100 250,200 S400,300 400,200 Z";
    -fx-background-color: skyblue, blue;
    -fx-background-insets: 0, 10;
}
This is admittedly a somewhat fanciful shape for a control. It is important to note that the control's clickable area still takes up the whole 250 by 250 pixels even though the Region's backgrounds take up a smaller area.

Quadratic Curves

The quadratic Bezier curve commands are shown in the table below.
Quadratic Bezier curve to Draws a curve from the current point to x,y. x1,y1 is the control point of the curve.Q (x1 y1 x y)+ q (x1 y1 x y)+
Quadratic Bezier curve to (shorthand) Draws a curve from the current point to x,y. The control point at the beginning of the curve is the reflection of the control point from the previous curve.T (x y)+ t (x y)+
Let's take a look at a quadratic curve example.
#quadratic.demo-control {
    -fx-shape: "M0,200 Q100,50 200,200 T500,100";
    -fx-background-color: skyblue, blue;
    -fx-background-insets: 0, 10;
}
The shape of our region just keeps getting more interesting!

Elliptical Arcs

The arc command is easily the most confusing of the SVG path commands. Use of SVG graphical editors is highly recommended!
Elliptical arc to rx ry - The radius of the resulting arc x-axis-rotation - Rotation of the x in degrees large-arc-flag - If 1 then the largest arc between the start and end points will be drawn. If 0 then the smallest are will be drawn. sweep-flag - Indicates the direction in which the arc is drawn. A 1 specifies the positive direction, a 0 specifies the negative direction. x y - The end point of the resulting arc. These are absolute coordinates or are relative to the start point (the current location) depending on whether the A or a command is used.A (rx,ry x-axis-rotation large-arc-flag sweep-flag x,y)+ a (rx,ry x-axis-rotation large-arc-flag sweep-flag x,y)+
What are arcs good for? Lot's of things, of course, but circles are an obvious choice. However, drawing a circle with arc commands is not as trivial as you would imagine. Here is an example.
#arc.demo-control {
    -fx-shape: "M 50,30  m 0,25  a 1,1 0 0,0 0,-50  a 1,1 0 1,0 0,50";
    -fx-background-color: skyblue, blue;
    -fx-background-insets: 0, 10;
}
As you can see, you actually need to define two arcs: one for each 180 degree ellipse.

Final Thoughts on Shapes

The ability to change, not just the colors, but the actual shapes of the regions that make up the core controls gives application developers unprecedented control over the look of their applications. Need a slider with a gold star for the thumb track? Easy. Buttons that look like clouds? Very do-able. The only limit is your imagination. Just remember, as with many of the things that JavaFX makes easy for developers: with great power comes great responsibility!

Using Caspian Styles

One last thing I want to address is the subject of using that styles in caspian.css with your own controls. The Caspian class in JavaFX 1.2 contained several nice utility functions that would generate nice gradients that tied into the Caspian theme. That class no longer exists in JavaFX 1.3. To use the Caspian theme with your own controls, you will need to use the values defined in the caspian.css style sheet.

Let's return to the DemoControl and make it look like it fits in with the Caspian controls.

#caspian.demo-control {
    -fx-background-color: -fx-body-color;
    -fx-background-radius: 5;
    -fx-border-color: null;
}

As you can see, all I've done is use the -fx-body-color attribute from caspian.css as my background color, added a corner radius to match Caspian's, and turned off the border I would normally inherit from the default demo-control style. In order for the -fx-body-color attribute to be defined, the caspian.css has to be loaded into the scene. Instantiating any core control will take care of this. In this case, I not only instantiated a button, but I also added it to the scene's Stack container just for comparison.

Adding Borders

Now I'll go the whole nine yards and add Caspian-style borders as well. I can take these right out of the button style in caspian.css.
#caspianborders.demo-control {
    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
    -fx-background-radius: 5, 5, 4, 3;
    -fx-background-insets: 0 0 -1 0, 0, 1, 2;
    -fx-border-width: 0;
}
Here I have actually defined four different backgrounds for the DemoControl's region. The insets are used to make sure they appear properly as a shadow, two borders and the body. Using the Caspian-defined attributes for body, border and shadow color means that my DemoControl will respond to changes in the application-defined -fx-base attribute. This is the attribute you would set if you wanted to change the base color of all Caspian controls in the entire application. I can set -fx-base to a new color in the .scene style and both DemoControl and the button will pick up the change.
.scene {
     -fx-font: 16pt "Amble";
     -fx-base: dodgerblue;
}

Final Conclusion

I really like the power and flexibility of the new CSS support in JavaFX 1.3. Styling and skinning controls is now easier than ever. If you need to, you can create an entirely unique look for your application without having to change any lines of JavaFX source code. It brings us a true separation of presentation and program logic. All in all, I give it a gold star. A gold star button even!
#star.button {
     -fx-shape: "M 50,5 L 37,40 5,40 30,60 20,95 50,75 80,95 70,60 95,40 63,40 Z";
     -fx-base: goldenrod
}

References

I found these things handy while learning about all of this:
  1. The caspian.css style sheet located in com/sun/javafx/scene/control/skin/caspian/caspian.css in the javafx-ui-controls.jar packaged with the JavaFX SDK.
  2. The Style Editor example at: http://www.javafx.com/samples/StyleEditor/index.html

23 comments:

  1. Why those components so simple? Look old article about user defined datepicker component:

    http://java.dzone.com/tips/javafx-component-creation

    ReplyDelete
  2. I felt it is little confusing. The relation between components,
    skin -> SkinAdapter -> DemoControlRegionSkin -> etc etc

    It reminds me the days when I was developing SWING LookAndFeel, all those complex API for minor change's.

    With JavaFX 1.3 I expected a easy and (understandable way) of specifying CSS styles to components something like in Adobe Flex.

    something like

    Button {style:"myCSS"}

    OR

    MyButton extends Button {
    style:"myCSS"
    }

    and use it

    scene: Scene {
    content: [ MyButton {} ] }

    May be I need to read it again to understand.

    ReplyDelete
  3. Interesting, indeed. Too bad it's based on private APIs.

    ReplyDelete
  4. Great helper as ever - thanks for your digging :-)

    Now - anyone actually tell me what this is from the stylesheet as seems to affect a number of the bits of interest to me and I dont have access:
    /* TODO RT-7542 replace this with ".choice-box *.popup-menu *.menu-item" once bug is fixed */

    ReplyDelete
  5. Sergey: This article is intended as an exploration of the new CSS skinning support in v1.3 (the code in your article is still pre-1.0 by the way). More complex controls are certainly possible, but we have to start somewhere when figuring out what this new system is capable of.

    Kishore: This article is admittedly more from the perspective of a control author than an app developer. But what you want to do is certainly possible. You can create your own style sheet with a .button selector and change the -fx-base (background color), the -fx-font, the -fx-opacity, etc. Then all buttons in your scene will pick up the new style.

    Goddard: They are private for now. Authors of custom controls do need to start understanding them because this is the way styling will work from now on. When the JavaFX team is satisfied with the API it will go public. You can learn it then, if you prefer. :-)

    And I would argue that learning the underlying bits also gives app developers a better understanding of what they can and cannot do in their stylesheets.

    David: There are apparently still some issues with the more complex selector statements. For now, you'll have to find a different way to select the menu items in the choice box's popup menu.

    ReplyDelete
  6. Nice stuff, We need more tutorial like this.

    ReplyDelete
  7. Great post, many thanks!

    But: you are showing only how to set the predefined -fx properties. In 1.2 I could set in CSS values of the public properties/variables in my custom skin. This doesn't work anymore, at least not in the same way. Hopefully it's only the syntax that changed:) If so, can you advise?

    ReplyDelete
  8. Dean, thx a lot for this post. The writing style and depth of information is excellent.

    I was playing around with the JavaFX CSS on the weekend and found it fun but cryptic. With the info in your post, now it is just fun :-)

    The multi-background stuff and the differences between spaces and commas was confusing me, but now it makes sense.

    Those without a web design background who need a css refresher, check out => http://www.w3schools.com/css/default.asp

    I wish that some of the standard css names (non -fx prefixed) like margin or padding worked out of the box in JavaFX. It would also be great if some of the non-standard extensions such as (https://developer.mozilla.org/en/CSS_Reference/Mozilla_Extensions) worked too. That way, it would be easier to port existing stylesheets to Java and existing stylesheet editors, like netbeans, could do better syntax checking and highlighting.

    It would also be nice to able to completely layout a scene using css in addition to easily styling a control. Perhaps this is already possible with the existing 1.3 implementation...

    The JavaFX css system is confusing, a bit complicated, non-w3c standard and undocumented, but I do prefer the css system for doing a lot of things over coding a lot of styling and layout stuff in JavaFX script, especially if it allows you to cut the compile/run steps out of the change, compile, run, test cycle.

    Kishore: I think you can do exactly what you want with JavaFX css - your demo code will just work.

    Ah, long comment, perhaps I should just start my own blog . . . there is a lot more to cover on this topic ;-)

    ReplyDelete
  9. @Dean,

    I haven't explore much of CSS in my application. Had the plan to use in JavaFX 1.3.
    Got little confused.

    Thanks Dean

    @jsmith

    I must have explored a little and tried some CSS code before posting this :) Thanks Jsmith.

    ReplyDelete
  10. COOOOL

    but the one thin I miss from Caspian is States. For example when you are making a component to set different styles for different states of the component.

    P.S.
    Dean, I think Oracle should hire you to right a JavaFx tutorials.

    ReplyDelete
  11. Hmm - played with examples and lots of thoughts to play with, but ... this is great for the simple example but how do we actually create a skin with multiple regions?

    e.g. a very simple case of a slider on a bar (as already provided by javafx but just for illustration here) then I assume the bar would be one region and the slider another identified by id? How are the two parts defined?

    Trying to work it out, as would like to apply to xpicker, but any hints would be very useful ( and yes, I acknowledge caveats with implementation) :-)

    ReplyDelete
  12. @D It looks like States are gone, at least for now. Which is really, really disappointing. And I appreciate the thought, but I'd rather write code than tutorials. And I'd rather write about what interests me; not an option you usually get once it turns into a job. :-)

    @David: Sub-regions are next on my list. Hopefully I'll get time this weekend to dig into that and write up what I find (unless someone beats me to it *cough* Oracle *cough*). :-)

    Dean

    ReplyDelete
  13. @Jan: I just realized that I missed your question. Sorry about that. You are correct, you cannot style arbitrary public variables any more (at least I can confirm that I can't get it to work, either).

    I suspect that this functionality was sacrificed for performance in the new CSS engine. But I'm hoping they will soon find a way to restore it.

    Dean

    ReplyDelete
  14. Dean, have you ever looked Sergey's codes about styling? He's right about lack of bind support in css. Also in his example, it cannot be given colors programmatically to css styled controls.
    I tried the following workaround. But


    override var style = bind "-fx-base:{toColorString(coColor)};";

    function toColorString(coColor:Color) {
    "rgba({coColor.red*255 as Integer},{coColor.green*255 as Integer},{coColor.blue*255 as Integer}, {coColor.opacity})"
    }


    But style from css overrides style from node. it should be vice versa, shouldnt it?

    ReplyDelete
  15. ihsan,

    Yes and I think Sergey's misunderstanding when you would use CSS. CSS is not meant to be a replacement for writing the logic of your control. You still need to do that in JavaFX.

    CSS is only meant to allow people to easily change the look of *existing* controls. If a control doesn't *behave* the way you want, no amount of CSS will change that. See my response to him in the other post.

    If you do want to programmatically change the color of a button with CSS you would simply create two different styles:

    .button {
    -fx-base: blue;
    }

    #red-button {
    -fx-base: red;
    }

    These styles turn all buttons blue. But any button (any control, actually) with the ID of "red-button" will be red. From here you just change the ID of the button:

    btn = Button {
    text: "I'm a Button"
    action: function() {
    if (btn.id == "") btn.id = "red-button" else btn.id = ""
    }
    }

    Now every time you click the button it changes between blue and red.

    Tip: If you find yourself trying to write logic using CSS hacks, you're probably not using CSS as God, or Jasper, intended. :-)

    Dean

    ReplyDelete
  16. Hi, dean.
    I know and see the aim of css. Normally, in web culture, there was and is a trend to create ui in css. In 1.2, I created my software api in a way that user can change the theme by both css (I love css approach for its simplicity) and skin. It was working well except the bug for dynamically created nodes. For 1.3, I have to redesign the skinning architecture of my software. Again I want to benefit from both static (css) and dynamic (skin) way of styling. For a consistent api, I need to learn every aspects of styling and estimate possible changes that can be made by fx-team. In this sense, your postings are very fulfilling, and I'm forwarding my thanks. If you looked at Sergey's codes, in fact the css button is very simple and short. If there were a way of overriding css styling in some parts, it would be very useful. Maybe there is. But I couldnt find a way yet.

    ReplyDelete
  17. Hi Dean.
    Is there any way to style HBox and VBox? I'd like to make a round border and background color for my application.
    Thanks.

    ReplyDelete
  18. Dean and ishan,

    Just to expand on Dean's comment about states: they can be easily achieved based on a status variable. Suppose you have the states red, green and blue. Then the corresponding CSS may be:

    .button#red {-fx-base: red;}
    .button#green {-fx-base: green;}
    .button#blue {-fx-base: blue;}

    As JavaFX doesn't give us more functional defs, (yet?), I like to define constants as Java enums, e.g., for the above states I'd define Status.java:

    public enum Status {red, green, blue}

    Then a button styled based on the status is simple:

    Button {text: "Chameleon"
    var status = Status.red;
    id: bind "{status}";
    }

    For instance, if we introduce a next() method to go from one state to another in the Status.java:

    public enum Status {
    red {
    public Status next () {return green;}
    },
    green {
    public Status next () {return blue;}
    },
    blue {
    public Status next () {return red;}
    };
    abstract public Status next ();
    }

    Then the button that changes style when clicked becomes simply:

    Button {text: "Chameleon"
    var status = Status.red;
    id: bind "{status}";
    action: function () {status = status.next()}
    }

    Of course we could also bind the id to an outside status variable...

    Actually, I'd find it much more appropriate to use styleClass rather than id attribute, i.e.:
    styleClass: bind "{status}";
    After all, a widget should not be identified by its style; it's even a good idea to have unique ids, e.g. for look-ups. But I didn't get styleClass to work with -fx-base, only with -fx-text-fill, and only partially. (Do you have any idea why - a bug?)

    ReplyDelete
  19. Thanks for the comment, but I still need to find the way to style HBox and VBox.

    ReplyDelete
  20. Thanks Dean for this interesting post.
    One question about the use of caspian style.
    Do you make a copy of the caspian css file and then customize it or do you load both caspian.css file and your own css ?

    ReplyDelete
  21. The caspian style sheet is loaded by default. So you can just make your own style sheet and just start overriding the caspian styles. No need to copy the entire style sheet.

    Although the caspian style sheet is a good reference when creating your own styles.

    ReplyDelete
  22. Dean I have a question, about new SkinBase-way of implementation of new JavaFx controls.

    Right now I'm busy with implementation custom Dialogs for my application and it looks like this:

    var alert:JFxDialog = JFxDialog {
    titleText: "Warining about error!",
    icon:ImageView{image:Image{width:25, height:25, url:"{__DIR__}assets/Ico_Alert_32bit.png"}},
    content:Label {text:"hello window"},
    options:[DialogOption.OK_OPTION, DialogOption.CANCEL_OPTION]
    }

    as you can see I use options for creating buttons in the bottom of the dialog.

    So, the question is how(where) I can create those buttons in my dialog skin? Cause when I try to read a sequence of my options and it has a size of 0.

    And one more question :)
    In javafx 1.2 we had a skin reference in control and we could invoke smth in skin. Right now we use SkinBase and SkinAdapter in order to use CSS support, but in this case we don't have a reference to our Skinbase extended class. How I can get this reference?

    Thank you in advance.

    ReplyDelete
  23. HTML5 and CSS3 Design are best subjects among for web developers. These layouts are another stepping-stone to make the web encounter more pleasant and grafting.

    ReplyDelete

Please Note: All comments are moderated. That's why you won't see your comment appear right away. If it's not some stupid piece of spam, it will appear soon.

Note: Only a member of this blog may post a comment.