The Synth Look and Feel

A Skinnable Look
and Feel for 1.5

by Scott Violet

March 26th, 2004

One of Swing's early design goals was to enable applications
to support a multitude of look and feels without changing
code. This feature allows developers to seamlessly run their
applications with numerous look and feels. While a handful
of custom look and feels have been developed  most
notably the INCORS Alloy
Look and Feel and L2F's Skin Look and
Feel  creating a new look and feel requires programming
and an extensive knowledge of Swing's pluggable look-and-feel
architecture, limiting this feature mostly to experts.

This article provides an overview of a new look and feel,
Synth, which can be completely customized without writing
code, enabling even non-programmers to create new look and
feels. The Synth look and feel is part of version 1.5 of the JavaTM 2
Platform, Standard Edition (J2SETM).

Writing code like this is fun the first couple of times,
but it soon becomes obvious there needs to be a better way
to provide a custom image-based look and feel without writing
code.

Goals

After reviewing numerous skinnable toolkits we decided
upon the following goals:

Enable creating a custom look without writing any code.

Allow appearance to be configured from images.

Provide the ability to customize the look of a component
based on its name property.

Provide a centralized point for overriding the look of
all components.

Enable custom rendering based on images.

It is also worth mentioning what Synth does not provide:

No way to specify layout.

No default look! Synth is an empty canvas. It has no
default bindings and, unless customized, does not paint
anything.

Example

For the anxious, here's a quick example. The following
XML code, taken from example1.xml, defines a style
named textfield and binds it to all text fields
in Synth. The result is that text fields look like the one
in the Motivation section.

This section assumes that you understand Swing's Pluggable
look-and-feel architecture, which is described in the Swing
Connection article Swing Architecture
Overview. The text in this section is rife with links
to the Synth
API documentation and to the Synth File
Format document, which describes the XML file format
for Synth.

The previous XML code illustrates a number of the core
concepts of Synth. It defines a style and a painter for
that style, and then binds that style to a set of
components.

The style element
defines a SynthStyle object. SynthStyle is
very similar to UIDefaults, in that a SynthStyle consists
of a set of style-related properties: fonts, insets, opacity,
painters, and so on. Each component becomes associated with
at least one SynthStyle. The ComponentUIs
for Synth use a SynthStyle to obtain all style-related
information. For example, each ComponentUI in
Synth does the following to install the font:

The previous XML example creates a style with the ID textfield,
a background of white, and insets of 5, 6, 6, and 7 pixels.
It also registers a painter for the paintTextFieldBorder method.

The bind element
is used to associate a SynthStyle with a set
of components. You can bind a style either using component
types or the value of the component name property.
To bind to component types, you specify type equals "region",
as in the previous XML example. To bind to component names,
you specify that type equals "name".

The previous example binds the textfield style
to all text fields  all components corresponding to
the TEXT_FIELD constant defined in the Region class.

The Region class defines constants corresponding
to each kind of component and sometimes to parts of components.
For example, Region defines the constants INTERNAL_FRAME and INTERNAL_FRAME_TITLE_PANE.
You can find all the constants in the Region API
documentation.

To bind a style to a particular kind of component, you
use the name of the Region constant for that
component, minus the underscores, and using any capitalization
you like. For example, you can specify all text fields by
specifying a type of "region" and a key of "textfield", "TEXTFIELD",
or "TextField".

Each ComponentUI in Synth obtains a SynthStyle at
two distinct points: when the ComponentUI is
first created, and when the component's name property changes.
The ComponentUI uses a SynthStyleFactory to
obtain a SynthStyle. SynthStyleFactory is
an abstract class that contains the single method getStyle.
The load method of SynthLookAndFeel creates
a custom SynthStyleFactory that returns SynthStyles
based on the contents of the file that was loaded.

Each ComponentUI may match multiple styles.
For example, in the following XML code, all components match
the style all. Text fields match both the all and textfield styles.
Refer to the documentation of the bind element
for details on how multiple matches are resolved into a single
style.

Each SynthStyle has a SynthPainter. SynthPainter is
used to render the distinct parts of each Swing component.
For example, it has methods such as paintPanelBackground, paintPanelBorder, paintProgressBarBackground, paintProgressBarBorder,
and paintProgressBarForeground. In the previous
example the imagePainter element
specified that an image-based SynthPainter should
be used to paint the text field's border.

As its name implies, an imagePainter element
creates a SynthPainter that paints from an image.
An image painter is commonly used to paint a border from
a small image, where the top, left, bottom, and right edges
are stretched or tiled. An image painter breaks the source
image into 9 distinct areas: top, top right, right, bottom
right, bottom, bottom left, left, top left, and center. Each
of the these areas is drawn into the destination. The stretch attribute
dictates whether the top, left, bottom, and right edges are
tiled or stretched. You can specify whether the center area
should be painted with the paintCenter attribute.
The following image shows the nine areas:

Synth's file format allows for embedding arbitrary objects
by way of the long-term
persistence for JavaBeansTM mechanism.
This ability is particularly useful in providing your own
painters beyond the image-based ones we provide. For example,
the following XML code specifies that a gradient should be
rendered in the background of text fields:

While we hope Synth's file format accommodates most developers'
needs, you can also programmatically use Synth. Rather than
using the load method you'll need to create
a custom SynthStyleFactory and
install it by way of SynthLookAndFeel's setStyleFactory method.
For example: