Prepare Custom Widgets for Theming

If you contribute a custom widget, you might want to allow your clients to customize its look and feel. To do so, you have to register the custom widget with the extension point org.eclipse.rap.ui.themeableWidgets and provide a couple of resources that are relevant for the RWT theming. Those resources must conform to a naming convention to be found by RAP. Once a custom widget is registered with the extension point, its theming resources are located by their name.

Extension Point themeableWidgets

To make RWT's theming engine consider a custom widget in the theming, it needs to know this widget. Thus, the custom widget's class has to be registered with the extension point org.eclipse.rap.ui.themeableWidgets. Here is an example of a themeable widget extension:


    <extension
        point="org.eclipse.rap.ui.themeableWidgets">
      <widget
          class="my.custom.controls.XButton">
      </widget>
    </extension>
  

Resources that are relevant for Theming

The custom widget must provide the theme-relevant resources in a package which is named after the schema <internal-package>.<widget>kit where <internal-package> is the package name with the path segment "internal" inserted and <widget> is the lower case class name of the custom widget. For example, if your custom widget is my.custom.XButton, you must create a package my.custom.internal.xbuttonkit for your files. If you are already familiar with the concept of RWT's life-cycle adapters (LCAs) you already know this convention, as those reside in the same package.

Theming relevant resources include:

For more complex widgets you might also want to pass some extra JavaScript resources to the client. Those additional resources are not automatically discovered and must be registered manually.

Theme Definition File

The theme definition file is an XML-file with the name <Widget>.theme.xml. This file defines elements and themeable properties for the widget. Its contents must conform with the schema that is outlined by the following example:


    <theme>
      <element name="XButton"
          description="Custom buttons">
  
        <property name="color"
            description="Text color for custom buttons" />
  
        <property name="border"
            description="Border for custom buttons" />
  
        ...
      </element>
    </theme>
  

The root element is named theme and contains one or more element definitions. The main element's name should match the widget's class name. Nested sub-elements may also be defined, their name should also begin with the widget's class name and add the sub-component's name with a dash, e.g. XButton-FocusIndicator.

Default Theme File

The default theme file is a CSS-file with the name <Widget>.default.css. This file must set a default value for each themeable property defined in the theme definition file. This default value becomes active when the default theme is used and whenever a custom theme does not specify a value for this property. This ensures that the themeable property can never become undefined. Here's an example:


    XButton {
      color: #c0c0c0;
      border: 1px solid #c0c0c0;
    }
  

If the file contains non-ASCII characters, it has to be UTF-8 encoded.

Appearances File

The appearances file contains JavaScript code to be included in the qooxdoo appearance theme. It must have the name <Widget>.appearances.js. For details on the qooxdoo theming, refer to http://qooxdoo.org/documentation/0.7/theme_support, especially the section on appearances.

RAP currently cuts out a section to include in the qooxdoo appearance theme. Therefore, the relevant part must be enclosed in two lines that contain the words BEGIN TEMPLATE and END TEMPLATE as shown in the example below. However, in order to be upward compatible, and to allow editing with JavaScript editors, this file should contain valid JavaScript.


  appearances = {
  // BEGIN TEMPLATE //
  
    "my-custom" : {
      style : function( states ) {
        var tv = new org.eclipse.swt.theme.ThemeValues( states );
        return {
          color : tv.getCssColor( "XButton", "color" ),
          border : tv.getCssBorder( "XButton", "border" ),
          ...
        }
      }
    }
  
  // END TEMPLATE //
  };
  

The above example shows the general pattern for appearance files in RAP. A ThemeValues object is created from the current states as passed to the style function. This object can then be queried for matching CSS property values. The ThemeValues#getCssXXX methods take the CSS element name as first parameter and the CSS property as second one. The return value will be a JavaScript representation of the property value that applies to widgets with these states. SWT style flags, states and widget variants are already taken into account by these functions.

Theme Adapter

This is a class that implements the interface org.eclipse.rwt.theme.IControlThemeAdapter. The name must match the pattern <Widget>ThemeAdapter. You must provide such a class if your custom widget's default values for background color, foreground color, font, or border width differ from the defaults of its superclass.

What's the use of the Theme Adapter?

Your custom widget will always be an (indirect) descendant of org.eclipse.swt.widgets.Control. This class defines a couple of getter methods, which must be aware of the widget's default values, which in turn depend on the current theme. Namely, this includes the methods

If a custom value has been set for one of these properties using the respective setter method, the getters will simply return this value. But if either no custom value has been set or the value has explicitly been set to null, the widget displays its default from the theme and the getters must reflect the actual state of the widget. For example, if your custom widget has a light gray background by default, the method getBackground must return this color instead of null. The value can depend on the particular instance of the widget, e.g. a widget created with the style flag SWT.BORDER might be displayed with a different border than another widget that doesn't have this flag. In order to provide the Control class with the necessary information, you have to implement the theme adapter.