BLADE themes are simply a collection of css classes and javascript functions which modify css attributes, all of which give components a particular look and feel. In early versions of BLADE, changing a theme meant going in and editing the CSS and JS files directly. While its still possible to do this, the default BLADE themes use an XML-based format that allows inheritance between themes. There are two benefits to this approach. First, it simplifies extending existing themes. Second, by basing all rollover themes off a "root" theme, it simplifies extending the rollover widget while maintaining compatibility with existing themes.
As alluded to in the Architecture page, BLADE themes are broken up into elements and states. Consider the rollover, one of the simplest components. It has 4 elements: a root element (the table), and left, center, and right, the three table cells. It also has 3 states (actually closer to state transitions): render, the state for when it is initially loaded onto the page, rollon (during a mouseover), and rolloff (when someone mouses out).
Since the render state happens when the page is loaded, it can be defined using CSS attributes. Since the rollon and rolloff states are controlled by javascript, their attributes have to be defined using js's hacked up interpretation of css classes. Javascript doesn't like hyphens in variable names. As a result, all css attributes with hyhpens have to be converted to non-hyphen equivalents. Unfortunately, there isn't a clean, uniform way to do this. For example, background in javascript can be used to control either background-image or background-color.
As an example, lets look at the nblue theme:
The opening line specifies what type of widget we're defining a theme for, the theme name, and what theme we're extending. The root theme we extend defines a few attributes common to all rollovers: the cursor looks like a pointer, the caption is centered, etc. The next line opens the definition for the render state. The lines after that define each element and the attributes for those elements.<widget name="rollover" theme="nblue" extends="root"> <state name="render"> <element name="main"> <height value="30px"/> </element> <element name="left"> <width value="15px"/> <background-image value="url("$cr/images/buttons/nblue-left.gif")"/> </element> <element name="center"> <color value="white"/> <background-image value="url("$cr/images/buttons/nblue-center.gif")"/> </element> <element name="right"> <width value="15px"/> <background-image value="url("$cr/images/buttons/nblue-right.gif")"/> </element> </state> </widget>
The theme compiler also needs a list of all the states and elements that make up a widget. This is provided
through the just-under-top-level widget-definition
tags. The definition for the rollover widget
looks like this:
The<widget-definition name="rollover"> <elements> <element tagtype="table" name="main"/> <element tagtype="tr" name="main"/> <element tagtype="td" name="left"/> <element tagtype="td" name="center"/> <element tagtype="td" name="right"/> </elements> <states> <state name="render"/> <state name="rollon"/> <state name="rolloff"/> </states> </widget-definition>
elements
tag holds a list of all possible elements, and the states
tag holds a list
of all hte possible states. Note that the element tags also have a tagtype
attribute, which tells
the compiler what type of HTML tag the element corresponds to. In the event where the same element name corresponds
to multiple tagtypes (as seen here with the button element), you can place a tagtype attribute on the element
tag in the theme definition to indicate which element you're referring to. Since this is not done in the nblue theme
shown above, the height: 30
css attribute gets applied to both the table and tr tags.
The widget definitions and root themes probably aren't going to exist in the xml file your theme is in.
In order to support this, you can place import
and include
commands at the top of
the theme's xml file, like this:
Imported files act as if they are part of the current file. If a theme is defined in the imported file, it will be compiled alongside all the themes in the importing file. Imports are generally used to reference the widget definitions. Themes in included packages are not compiled, but can be extended.<import href="location of file"/> <include pkg="package name"/>
If theme A extends theme B, but A and B are in different directories, some serious problems arise. For example, B might have a background-image of background.gif. When A extends B, it will copy the background-image attribute as background.gif, even though there is no such file in A's folder. Blade solves this by using several variables in the value attribute: $cr/, $pr/, and $br/. $cr/ makes a reference to the CSS Root of the theme, and should be used before CSS background-images. $pr/ makes a reference to the package root of the theme, and should be used before image URLs and JS background images. $br/ references the BLADE root directory, and should be used for images that are in a fixed subdirectory from the BLADE root.
To get a theme to compile properly, two more things need to be done. First, a context tag needs to be added to the theme definition to tell the compiler which theme the definition represents. It looks like this:
Additionally, in the imports section of app/config.xml, you need to to add an import-theme statement that points to the new themepack.<context> <package-name value="package name"/> </context>
Actually compiling themes is the easy part. Go to the tools directory and run build.bat. It automatically reads config.xml, determines dependencies between themes based on the include statements, and then compiles the themes listed in config.xml. One important thing to keep in mind is that, in order for the compiler to work automatically, themes should avoid circular includes: if A includes B, B should not include A.