Skip to main content

Style Swing components using CSS

Posted by enicholas on March 24, 2006 at 8:17 AM PST

Swing does a great job of separating data (the models) and controls (the components themselves), but it really doesn't do a good job of separating controls from presentation (the visual appearance of the controls).

Pluggable looks-and-feels help to a degree, but they are complicated to write and maintain, and on top of that they affect the appearance of every component of a particular type. There are lots of good reasons for having components of the same type have different styles attached to them, and the present-day solutions to doing this in Swing aren't fantastic.

CSS is a natural fit here. It was designed to solve exactly this problem, and on top of that it's a familiar technology which many Java programmers are already comfortable with. When writing the open-source JAXX framework, I chose to use CSS as its style language, and I think it brings unprecedented power and control to Java. Let's take a look at a simple example, the (slightly modified) Calculator from jaxxframework.org:

<Application title='Calculator'>
  <script source='Calculator.script'/>
 
  <Table fill='both' id='table'>
    <row>
      <cell columns='4'><JLabel id='display' text='0'/></cell>
    </row>
   
    <row>
      <cell columns='2'><JButton id='c' label='C' onActionPerformed='clear()' styleClass='clear'/></cell>     
      <cell><JButton id='ce'     label='CE' onActionPerformed='clearEntry()' styleClass='clear'/></cell>
      <cell><JButton id='equals' label='=' onActionPerformed='equal()' styleClass='operator'/></cell>
    </row>
   
    <row>
      <cell><JButton id='d7'   label='7' onActionPerformed='digit(7)' styleClass='digit'/></cell>
      <cell><JButton id='d8'   label='8' onActionPerformed='digit(8)' styleClass='digit'/></cell>
      <cell><JButton id='d9'   label='9' onActionPerformed='digit(9)' styleClass='digit'/></cell>
      <cell><JButton id='plus' label='+' onActionPerformed='add()' styleClass='operator'/></cell>
    </row>

    <row>
      <cell><JButton id='d4'       label='4' onActionPerformed='digit(4)'   styleClass='digit'/></cell>
      <cell><JButton id='d5'       label='5' onActionPerformed='digit(5)'   styleClass='digit'/></cell>
      <cell><JButton id='d6'       label='6' onActionPerformed='digit(6)'   styleClass='digit'/></cell>
      <cell><JButton id='subtract' label='-' onActionPerformed='subtract()' styleClass='operator'/></cell>
    </row>

    <row>
      <cell><JButton id='d1'       label='1' onActionPerformed='digit(1)' styleClass='digit'/></cell>
      <cell><JButton id='d2'       label='2' onActionPerformed='digit(2)' styleClass='digit'/></cell>
      <cell><JButton id='d3'       label='3' onActionPerformed='digit(3)' styleClass='digit'/></cell>
      <cell><JButton id='multiply' label='x' onActionPerformed='multiply()' styleClass='operator'/></cell>
    </row>

    <row>
      <cell><JButton id='d0'     label='0' onActionPerformed='digit(0)' styleClass='digit'/></cell>
      <cell><JButton id='sign'   label='+/-' onActionPerformed='toggleSign()' styleClass='operator'/></cell>
      <cell><JButton id='dot'    label='.' onActionPerformed='dot()' styleClass='digit'/></cell>
      <cell><JButton id='divide' label='&#x00F7;' onActionPerformed='divide()' styleClass='operator'/></cell>
    </row>
  </Table>
</Application>

The code that does all of the math is in a separate file, Calculator.script, which isn't relevant here. Hopefully this example is pretty easy to read even if you have never seen JAXX before. It's just creating a label, a bunch of buttons, and sticking them into a table. As it appears above, there is no style information at all -- no in-line styles, no stylesheet. Just plain raw components:

Calculator-unstyled.gif

Clearly, it needs some work. Let's start by adding a style tag:

<Application title='Calculator'>
  <style source='Calculator.css'/>
  <script source='Calculator.script'/>
...
</Application>

Now we need to create the Calculator.css file:

Application {
    lookAndFeel: system;
}

#display {
    background: #BCE5AD;
    opaque: true;
    horizontalAlignment: right;
    border: {BorderFactory.createBevelBorder(BevelBorder.LOWERED)};
    font-size: 22;
    font-weight: bold;
}

This stylesheet switches the look and feel to "system" (Microsoft Windows on this particular machine) and styles the component named "display" to have a background, a border, and different font settings. Here's what it looks like now:

Calculator-step1.gif

We're headed in the right direction now, but it needs more. Perhaps a bit of color, maybe some bigger buttons, different font...

Application {
    lookAndFeel: system;
}

#table {
    border: {BorderFactory.createEmptyBorder(4, 4, 4, 4)};
    font-face: "Trebuchet MS";
}

#display {
    background: #BCE5AD;
    opaque: true;
    horizontalAlignment: right;
    border: {BorderFactory.createBevelBorder(BevelBorder.LOWERED)};
    font-size: 22;
    font-weight: bold;
}

JButton {
    font-size: 18;
    width: 60;
    height: 35;
}

JButton.digit {
    foreground: blue;
}

JButton#dot {
    font-size: 20;
}

JButton.operator {
    font-size: 18;
    foreground: #009900;
}

JButton.clear {
    foreground: red;
}

This stylesheet controls the buttons based on their styleClass attributes, and in one case id, in order to style them by logical groupings. Here is the calculator with the final stylesheet in place:

Calculator-styled.gif

Compare the newly styled appearance against the unstyled appearance above. Okay, so it's not Monet, but I'm not a UI designer either. The fact that I could hand this program and this stylesheet to a UI designer and say "please make this prettier" -- and that the UI designer would actually be able to without my help -- is really exciting to me. I think CSS stylesheets and Swing are a perfect marriage, and I'm pleased with how the combination has turned out in JAXX.

If you want to compile and run these examples yourself, you will need to download JAXX from http://www.jaxxframework.org/.

Related Topics >>