The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Introducing Java CSS

Posted by enicholas on July 17, 2008 at 2:12 PM PDT

How people configure Swing today

If you've ever written a non-trivial Swing program, you've probably written code that looks a lot like this:

JSlider slider = new JSlider(0, 100, currentValue);
slider.setMinorTickSpacing(5);
slider.setMajorTickSpacing(25);
slider.setPaintTicks(true);
slider.setForeground(DEFAULT_FOREGROUND);

That's an awful lot of configuration for one component, and worse: it's all compiled code, and therefore relatively inaccessible. The more experienced programmers in the audience are going to immediately (and quite rightly) complain about the setForeground() call -- that should be installed into the UIDefaults table -- and we should probably be using constants instead of those magic numbers.

But then we, in a sense, end up with an even bigger mess. Some of the slider's configuration comes from the UIDefaults table, some comes from constants defined well away from the slider itself, and some (like setPaintTicks(true)) is still buried directly in the slider's initialization code. And we still haven't fixed the real problem, that all of these values are still stored in compiled code and therefore not easy to get at or change.

Even-more-experienced coders will now suggest another change -- that we put the constants into a configuration file of some kind, rather than into code. So we perhaps write a configuration file containing something along the lines of:

default.font              = Dialog-12
default.foreground        = #000000
mySlider.minorTickSpacing = 5
mySlider.majorTickSpacing = 25
mySlider.paintTicks       = true

Problem solved! Right? Well, sort of. You've still got to write code to process this configuration file. You've got to work out how the values defined in the file actually get applied to components. slider.setMinorTickSpacing(getProperty("mySlider.minorTickSpacing")) is simple, but lame -- what happens if you add a new property to the configuration file? Unless you're specifically looking for it, it won't be respected. So you might go all the way to the logical extreme of using reflection to apply these properties by dynamically finding the relevant mutator methods.

I've seen configuration files along these lines (though the details of course differ) in many different Swing programs, and I don't think they're uncommon. I've written some myself. But there's a real problem with this approach -- you're reinventing the wheel. What you've created is in effect a very simple and limited stylesheet language. And if you're going to do that, why not just use a real stylesheet language?

Using CSS instead

There already exists a powerful, flexible stylesheet language that millions of people are familiar with. It's called CSS, and various (mostly very poor) implementations already exist for Java. With a proper CSS engine, you wouldn't need to bury the configuration in your code or burn it into proprietary configuration files. The configuration above might be expressed as:

* {
  font: Dialog-12;
  foreground: black;
}

JSlider#mySlider {
  minorTickSpacing: 5;
  majorTickSpacing: 25;
  paintTicks: true;
}

Admittedly, in this simple example there's not a clear benefit to using CSS instead of a proprietary file -- the syntax is a bit different, but the two files are about equally complex. The real benefit of CSS comes from all of the additional power it offers.

Want to only affect sliders which appear under MyColorChooser? No problem, use the selector "MyColorChooser JSlider". Or pick every label serving as a title with "JLabel.title". What about JButtons, but only when they are moused over? Use "JButton:mouseover".

What about even more complex selectors, such as "JSliders which are direct children of a MyPanel appearing in the currently active window, but only when they are set to their maximum value"? No problem, with a slight syntax extension "Window:active MyPanel > JSlider:{value == maximum}" does just the trick.

The Java CSS Engine

I mentioned above that the existing CSS implementations for Java are generally very poor. That's why I ended up creating a new one, which (hopefully) far exceeds previous CSS engines in many respects.

Click the launch button to see a simple demo of my CSS engine:

For comparison, here is the exact same application without a stylesheet:

The demo application also includes a stylesheet editor window, which allows you to update the stylesheet or turn it off altogether:

The stylesheet specifies some interesting rules, such as

JSlider#tip:{value <= 10} {
  background: red !over 0.3s;
}

The JSlider#tip portion of the selector means that this rule only applies to the JSlider named "tip". Okay, simple enough. The next part ":{value <= 10}" is what is called a programmatic pseudoclass -- basically just a boolean expression which controls whether or not the rule applies, taking the form of an EL expression evaluated using Beans Binding. So this rule applies only when the slider has a value of 10 or less.

One other unusual feature of this rule is the !over 0.3s expression. This signifies an animation, happening over a period of 0.3s. Try dragging the slider left and right to see the "good tip" and "bad tip" animations fade in and out. (Please note I'm not actually passing any judgment on tips. This is just a stupid demo. I suck at demos.)

Conclusion

Java CSS is a powerful, flexible engine for styling Swing components, supporting very powerful selectors, tons of pseudoclass states, and advanced features like animated state changes.

For full documentation or to download Java CSS, please visit the Java CSS Home Page.

Related Topics >> Java Desktop      
Comments
Comments are listed in date ascending order (oldest first)

I am baffled as to why you would bother since the Swing Application Framework provides this functionality with the added ability to hide the storage mechanism. http://jcp.org/en/jsr/detail?id=296

NICEEEEEEEEE !!! really nice ! after a month without any post on swing !! really really cool Ethan !! thanks for keeping swing shining and all java client side puzzles ! I'm waiting for the new java 6 update 10 final release to test it in my company !

This is really really interesting. To my eyes, this is one of the only (and better) ways to target existing designers/web designers for java on the client side, i strongly believe this would be great with for javafx (even though i really dislike fx script). Leveraging standards they already know seems the (obvious?) way to go. what about the licence, no classpath exception ? honestly, great job ethan, yet again!

oh ok i get it, thanks scenario :) i guess the question becomes: when you have the ability to change the licence, will you do it ? :)

is there a way to specify multiple classes as in html (where the class attribute is actually not a single value but a list of values) ?

Awesome!

excellent work!

That looks amazing. It's really cool. What about JavaFX support is it planned? Thank you Ethan!

@liquid: I apologize for the current restrictive license, but I expect this code to make an appearance under a more liberal license relatively soon. Also, there is no current way to specify more than one style class, but it would be trivial to add. @carcour: Partial JavaFX support is already in place, and the plan is absolutely to have rock-solid JavaFX support. The real holdup at this point is JavaFX reflection -- to make this work, I need to be able to (from Java) examine JavaFX objects and interact with their properties. Work on the JavaFX reflection package has started, but it is currently non-functional, so there is a limit to how well we can work with JavaFX objects.

nice.

Excellent ! We see some innovation from the Java team in making Java user-friendly for those coming from the web world. Will this be included in JDK 7 ?

That looks really great !! I will try it soon !!!

I just looked a little the documentation. It seems pretty simple to use. The only thing that is lacking for me (I know, I know, it's only 0.2, and it already do a lot !!) is the ability to add images on components that accept them.

@magnum: I'll add image support within the next couple of days. It's worth pointing out, though, that this is user-extensible -- all you would have to do is create a new TypeConverter for Images / Icons and register it with TypeManager.

This is cool, but how does it interact with the LookAndFeel layer? Does it ignore the look and feel and just set the properties manually? Is there support for setting the values that are in the UIdefaults?

Is it possible to do layout with this CSS ? Specifying padding, margins, and positioning, etc....

The CSS4SWING used by Jmatter is also similar approach but it takes a huge memory.

What's happening with JAXX? http://www.jaxxframework.org/wiki/Main_Page This is a great project but the last entry was in 2006? Does that mean it being superseded by something else? Please advise.

Hello, you maybe should put a forum in the javacss project page, it would allow users to post without registering to the project ;-)