HEAD PREVIOUS

Chapter 17
Styling the UI with CSS

Cascading Stylesheets (CSS) is a web technology. It is supported by modern web browsers to style user interfaces. CSS is simple and elegant. It is also powerful. It has an aspect-like design in that styles for parts of a user interface can be specified in a manner that is orthogonal from the rest of an application: it is a cross-cutting concern.
JMatter comes with built-in support for a subset of the CSS that I believe to be applicable to Java's Swing technology. By default, JMatter will automatically load the file resources/styles.css on startup, and interpret and apply the rules contained within that stylesheet. Stylesheets consist of rules, and rules consist of selectors and declarations.
Let us begin with a description of the CSS engine itself and specifically what subset of CSS is supported, and how it is adapted to Swing. We will then turn our attention to the default stylesheet.

17.1  JMatter's CSS Engine

17.1.1  Selectors

JMatter's CSS implementation supports both simple and composite selectors. Simple selectors match a component by any of the following, or a combination thereof:
  1. its type
  2. a semantic style name (a css class name)
  3. an id
In the web world, a component type is designated by its corresponding tag name, such as <h1> or <p>. In Swing it's designated by its class name, for example: JLabel.
Composite selectors allow the expression of a path. They're composed of two or more simple selectors, related through a combinator:
  1. the Child combinator (denoted by the character >)
  2. the Sibling combinator (denoted by the character +)
  3. the Descendant combinator (denoted with a space)

17.1.2  Adapting Selectors for Swing

By default, you can reference any Swing component class that is a member of the package javax.swing without qualifying it. Some examples include: JPanel, JLabel, and JButton.
Any component that is assignable from the referenced class name will be matched. That is, if you write a subclass of JLabel, and write a CSS rule like this:
JLabel
{
  background-color: blue;
}
The rule will apply to instances of the subclass as well (this is not true the other way around of course).
A parsing ambiguity arises if one wished to reference a fully-qualified class name in a selector. Let's say we had a rule such as:
JLabel.special
{
  background-color: blue;
}
If the component type were referenced fully-qualified:
javax.swing.JLabel.special
{
  background-color: blue;
}
It would be less clear (though not impossible to disambiguate) that we were referencing a component marked with a css class name. JMatter's CSS implementation therefore does not allow fully-qualified class names in selectors.
This CSS implementation adopts and leverages CSS3's namespaces (thanks to Tiago Silveira) module to resolve this issue. CSS3 namespaces were designed to resolve the issue of possibly clashing namespaces in different xml vocabularies.
To reference a type in a different package, you must define a namespace for it, like so:
@namespace l2f com.l2fprod.common.swing
l2f - JOutlookBar
{
  color: olive;
}
The two main items to note here are:
  1. A @namespace css rule is defined and aliased as l2f.
  2. To reference a type selector in that namespace, concatenate the namespace id and type name with the vertical bar delimiter ( - ).

17.1.3  Assigning Components CSS class names

On the web, components are given semantic style names by annotating them with an html attribute, as in: class="title". JMatter's CSS implementation provides an API method for assigning a css class name to a component:
ComponentStyle.addClass(componentRef, "title");
Since components can have more than a single semantic class name, one can invoke the above method repeatedly. There's also a corresponding method for removing a class assignment:
ComponentStyle.removeClass(componentRef, "title");
One can likewise assign an id to a component with:
ComponentStyle.setIdent(componentRef, "btn103");
The above are all static methods.
Application developers needn't concern themselves with this because the work associated with defining style names and annotating a user interface is a part of the task of writing that user interface. In the case of JMatter, the user interface is already written and is already annotated. To customize the style of a JMatter application, all you need to concern yourself with (besides knowing what semantic names to reference) is editing the stylesheet itself.
You might however want to take a look at the Sympster demo application where a custom view for a type is written that leverages CSS.

17.1.4  Values

Of the myriad of ways in which measures can be specified in CSS, JMatter supports only a subset:
  1. Font sizes must be specified in pt's
  2. Borders, margins, and padding must be specified in px's
  3. Colors can be specified either using the sixteen predefined color names, the six-digit hex notation, or the shortened three-digit hex notation. The rgb() function is not supported at this time
  4. Only the line styles dotted, dashed, and solid are supported at this time
  5. Only the generic font family names serif, sans-serif, and monospace are supported at this time

17.1.5  Properties

A subset of the CSS defined properties is supported at this time. They are:
cursor
color
background-color
background-image
font-family
font-size
font-weight
font-style
font
border-color, border-left-color, border-right-color, border-top-color, border-bottom-color
border-style, border-left-style, border-right-style, border-top-style, border-bottom-style
border-width, border-left-width, border-right-width, border-top-width, border-bottom-width
border
margin, margin-left, margin-right, margin-top, margin-bottom
padding, padding-left, padding-right, padding-top, padding-bottom
JMatter's CSS implementation also supports these CSS3 properties, but only on SwingX components that allow for the delegation of background painting:
opacity
border-radius
border-top-left-radius, border-top-right-radius, 
 border-bottom-left-radius, border-bottom-right-radius

17.1.6  CSS Inheritance

It is important to mention that this engine supports CSS inheritance: that [inheritable] properties inherited from containing components are applied as well.

17.2  The JMatter Stylesheet

Let's study the file resources/styles.css one piece at a time.
First comes an import declaration:
@namespace jm com.u2d.view.swing
This allows me to reference custom components from JMatter's Swing view mechanism in selectors. Here are some examples:
JDesktopPane
{
  background-color: #7684FF;
}
jm - FormPane
{
  background-color: #fffaf0;
}
The first rule customizes the background color of the desktop pane. The selector matches a standard swing component. The second rule customizes the background color of form panes (using an off-beige color).
Here are two more rules:
.command
{
  font-style: italic;
}
.default-button
{
  font-weight: bold;
}
Views of commands in JMatter are annotated with the CSS class name command. I have chosen to emphasize that commands are active by italicizing their views (views of commands are either JMenuItem's or JButton's).
Furthermore, Swing (and forms) has the notion of a single command being designated as the default command on a form. Default buttons are given a little extra attention, such as interpreting pressing of the Enter key on the form as implying the invocation of the default command. Here you can specify how its view should be styled.
Note how both rules .command and .default-button match for default commands so that their properties are combined. This CSS engine will make sure to apply the more specific rule in case of a conflict.
Here is a rule that styles the text in title views:
.list-title, .instance-title
{
  color: #400;
  font-weight: bold;
  font-size: 16pt;
}
We are applying the same set of declarations to both list titles and instance titles, so we grouped the selectors together.
Here are two more semantic styles:
.required
{
   color: blue;
   font-weight: bold;
}
.validation-msg
{
   color: red;
   font-style: italic;
}
Here we specify how to style the captions for required fields and validation error messages. Feel free to customize these (and any other styles) as you see fit.
And finally, this rule styles the large semi-transparent message panel that appears for 2-3 seconds to acknowledge various actions, for example when one creates and persists a new instance to database:
.feedback-pane
{
  color: white;
  background-color: black;
  font-size: 24pt;
  font-weight: bold;
  padding: 30px;
  border-radius: 24px;
  opacity: 0.7;
}
Styling JMatter UIs is now much more accessible thanks to CSS. Since CSS is a technology you might already be familiar with, and since this implementation adheres strictly to the specification (i.e. it doesn't invent its own properties), you should be able to jump in right away without a learning curve.

17.2.1  The complete stylesheet

Below is a copy of the complete stylesheet, for reference.
@namespace jm com.u2d.view.swing
@namespace types com.u2d.view.swing.atom
JDesktopPane
{
  background-color: #7684FF;
}
jm - FormPane
{
  background-color: #fffaf0;
  padding: 5px;
}
.command
{
  font-style: italic;
}
.list-title, .instance-title, .title
{
  color: #400;
  font-weight: bold;
  font-size: 16pt;
}
.list-title-panel, .instance-title-panel
{
}
jm - FieldCaption
{
  color: #2f4f4f;
  font-weight: normal;
}
.required
{
  color: #483d8b;
  font-weight: bold;
}
.validation-msg
{
   color: red;
   font-style: italic;
}
.default-button
{
  font-weight: bold;
}
.feedback-pane
{
  color: white;
  background-color: black;
  font-size: 24pt;
  font-weight: bold;
  padding: 30px;
  border-radius: 24px;
  opacity: 0.7;
}
#aboutPnl
{
  margin: 5px;
}
types - URIRenderer
{
  cursor: pointer;
}

17.3  Using CSS4Swing in Standalone Swing Applications

The CSS engine used by JMatter was developed by Eitan Suez, for the purpose of enhancing JMatter. However, the implementation is agnostic to the JMatter framework, and can be used with any old Swing application.
That is, if you're working on a Swing-related project and wished you could style your application with CSS, well, now you can.
First you'll need to add these libraries to your classpath, which you'll find in jmatter/lib/runtime/swingvm:
  1. css4swing.jar
  2. antlr-runtime-3.0.1.jar
  3. swingx.jar
This chapter has already documented how to write stylesheets, and how to apply semantic style names or id's to various components.
The only other requirement is to insert this statement before you start building your Swing user interface:
CSSEngine.initialize();
By default, the engine will look for the resource styles.css in your classpath. Alternatively, you can specify a different origin for your stylesheet with this method:
CSSEngine.initialize(InputStream is);
Here is a sample class that provides a little more context, and a more complete illustration:
package com.u2d.css4swing;
import com.l2fprod.common.swing.JOutlookBar;
import com.u2d.css4swing.style.ComponentStyle;
import javax.swing.*;
/**
 * Created by IntelliJ IDEA.
 * User: eitan
 * Date: Jan 30, 2007
 * Time: 11:48:24 AM
 */
public class SampleTest
      extends JPanel
{
   public SampleTest()
   {
      JLabel label = new JLabel("Eitan is testing..");
      ComponentStyle.addClass(label, "required");
      add(label);
      
      JLabel lbl2 = new JLabel("Ident-Designated");
      ComponentStyle.setIdent(lbl2, "theone");
      add(lbl2);
      
      FieldCaption caption = new FieldCaption("A label subclass..");
      add(caption);
      
      JButton button = new JButton("Button 1 (required)..");
      ComponentStyle.addClass(button, "required");
      add(button);
      JButton button2 = new JButton("Button 2 (not required)..");
      add(button2);
      _bar = new JOutlookBar();
      ComponentStyle.addClass(_bar, "left-sidebar");
      _bar.addTab("Hello", new JLabel("Hi"));
      _bar.addTab("Bye", new JLabel("Bye"));
      add(_bar);
   }
   
   JOutlookBar _bar;
   
   public static void main(String[] args)
   {
      CSSEngine.initialize();
      
      SwingUtilities.invokeLater(new Runnable()
      {
         public void run()
         {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            SampleTest st = new SampleTest();
            f.setContentPane(st);
            f.setBounds(100,100,500,500);
            f.setVisible(true);
         }
      });
   }
}

17.4  Future Plans

Additional enhancements for CSS support in JMatter are planned. Here is a tentative list:
  1. Produce multiple alternative stylesheets that can be used as themes to skin JMatter applications
  2. Support all border line styles defined by the CSS specification
  3. Complete support for color value syntax, including the rgb() function and the CSS3 rgba() function for supporting transparency
  4. Possibly adding support for CSS @import statements, as a simple include mechanism
  5. Possibly adding support for applicable CSS pseudo classes and pseudo elements
  6. Perhaps expose the ability to apply effects in a Swing application through CSS
If you produce a stylesheet that you find does a good job at styling the JMatter user interface, please consider contributing it back to the project as a JMatter theme.

HEAD NEXT