Skip to main content

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.

Comments

Folks, does anybody know what happend ...

Folks,

does anybody know what happend to javacss.dev.java.net ?

Thx.,

H.

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

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.

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

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

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?

@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.

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.

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

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 ?

nice.

@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.

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

excellent work!

Awesome!

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) ?

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 ? :)

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!

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 !

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