Skip to main content

Proposal for uniform support of third-party components in custom look-and-feels

Posted by kirillcool on November 28, 2005 at 1:21 PM PST

In the recent years, Java Swing market has seen a surge in custom components that aim to provide UI widgets common in desktop applications yet missing from JDK. These components include date picker, task pane, tree table and many more. Among others, you can find those at SwingX and at JideSoft. The main problem with these components is the support of custom (third-party) look-and-feels.

The general mechanism employed in Swing is to declare a UI delegate ID string in a component class, say ToggleButtonUI and have UIManager contain an entry with this key. The entry value is a fully-qualified class name of the UI delegate, say javax.swing.plaf.metal.MetalToggleButtonUI. The look-and-feel class (either system or custom) specifies such pairs for all supported components. But what happens with third-party components that are not bundled with JDK?

Here the water gets a little murky. First, the component writer should follow the same guidelines that are used in core components, allowing specifying the UI delegate in the application code. But more important is another question - how does the look-and-feel know that you have additional components with pluggable UI delegates?

Unless you have access to the look-and-feel code, you have to update the UIManager from the outside code. This can either be some initialization function supplied with the components that you are using, or the direct manipulation of UIManager tables from your own code. And things get more complicated when you want to switch look-and-feel at runtime.

The Laf-Plugin project proposes an approach that shares the responsibility between the look-and-feel and the component writers. The approach borrows ideas from the plugin mechanisms developed in the last years, where the main library looks up the configuration files in the classpath, and the plugin writers follow the conventions dictated by the main library to specify additional behaviour.

The main LookAndFeel class calls PluginManager to get the details of
third-party UI components. The constructor of PluginManager gets three parameters:

  • the XML descriptor name
  • the main tag name (may be it's not needed really, but serves as a fault-protection mechanism)
  • the name of the tag that contains the fully-qualified class name of the plugin class.

The plugin class LafPlugin contains a number of functions:

  • initialize and reset - should be called by the main LookAndFeel
    class on initialization and theme switch.

  • getUiDelegates - returns a collection of all UI delegates for custom components.
    Can also return UI delegate for core Swing component, effectively overriding the UI delegate of the LAF

  • getFonts - returns font settings for custom components. The same as above -
    can override core settings for core Swing components

  • getDefaults - returns other settings for custom components. Same as above.

This way, the base look-and-feel library has no dependencies on third-party components,
and can have as many plugins as you wish. Plugin writer must comply with the settings of
the particular look-and-feel for XML name and XML tag names. In addition, she must
implement the LafPlugin interface with the above functions.

If you are a provider of custom component, you will need to provide the following for each LAF:

  1. XML descriptor.
  2. Class that implements LafPlugin interface.
  3. UI delegate for each custom component.

The laf-plugin.jar library (in Documents & Files section)
contains the runtime classes. You can use the following simple Ant task to
put the laf-plugin classes in your main LAF runtime library:

<target name="jar-bin" description="create runtime jar">
  <delete file="${substance.drop.dir}/substance.jar" />
  <unjar src="${substance.drop.dir}/laf-plugin.jar" dest="${substance.output.dir}/"/>
  <jar compress="true" destfile="${substance.drop.dir}/substance.jar"
        manifest="${substance.src.dir}/META-INF/MANIFEST.MF">
    <fileset dir="${substance.output.dir}/" excludes="test/**" />
    <fileset dir="${module.substance.basedir}/" includes="resources/**" />
  </jar>
</target>

This project was developed over the last month by Erik Vickroy and myself and has been successfully integrated into Substance and Liquid look-and-feels. Two examples of Substance plugins are the plugin for NetBeans components and the plugin for Ribbon components. The code itself is 1.4-compliant.

Update: the owners of Squareness and Pgs LAFs have expressed their design to adopt the proposed approach in the next versions of the respective libraries.

Related Topics >>