Themes Tutorial

In Bindows, the look and feel of any visual component is determined by the currently active theme. The active theme can also be switched on the fly without having to restart the web application. Various themes are available for download from our online repository. A special theme called Default is provided with the Bindows framework. You can modify any of those themes and also create your own.

Scope of this tutorial

Use this tutorial to learn how to use themes, understand the Bindows theme architecture and the steps required to create your own theme as well as how themes relate to building custom components. Knowledge of CSS, JavaScript and Bindows is assumed.

Using Themes

First let's take a look at how themes can be used in a Bindows application.

In the Bindows package, there is a folder named html/themes/ that hold the different themes. The Bindows default theme is named Default and accordingly reside in the html/themes/Default/ folder. (Observe the capitilization of folders and theme names.)

In order to use other themes than default in your application, you include them by defining them in the adf:

<Application>
	<Theme name="OceanRounded"/>
	<Theme name="Ocean"/>
	<Window >
...

To invoke a theme at startup, you set it to be the default:

<Application>
	<Theme name="OceanRounded" default="true"/>
	<Theme name="Ocean"/>
	<Window >
...

and to switch to a(included) theme at runtime, you set it by calling application.getThemeManager().setDefaultTheme( oTheme ). There is a sample on how this can be done in ThemeTest.xml in the applauncher.

When you add more themes like seen above, the Default theme is no longer loaded by default. If you want to include also the Default theme you will have to explicitly include it. If you then want the Default theme to be loaded at startup you have to make the Default theme the default.

    <Application>
	<Theme name="Default" default="true"/>    

The Structure of Themes

A theme essentially consists of three files:

In addition to the above three files there is also an image sub folder containing all images used by the theme. All themes are stored in a special themes folder that Bindows looks inside when loading look and feel.

html/
   themes/
      Default/
         Images/
         menu.css
         theme.css
         theme.js
      MyTheme/
         Images/
         menu.css
         theme.css
         theme.js

When making your own images, Gif is usually a good choice of image since IE6 have problems compositing several png files with transparency.

There is an inheritance structure involved with themes as well. The following files serve as base files:

html/
   css/
      bimain.css
      olapgrid.css
      treeview.css

The file bimain.css defines style information that applies to the whole of Bindows and many components. This serves to reduce duplicate code when creating new themes. The rest of the files contain common style information for various complex Bindows components. Keep in mind that some of the styles defined here might be overridden by different themes. Further more, all themes derive from the Bindows default theme and creates their additions and modifications on top of it.

Creating and Modifying Themes

The easiest way to create a custom theme is to simply copy an existing one and start making changes. When copying an existing theme you first have to decide whether it should be a rounded theme or not. The rounded themes have some additional styling information in the css and some extra divs are generated to create the actual roundings on components. Performance-wise it shouldn't make any noticable difference unless you keep a very large amount of rounded components on the screen at the same time. Rounded versus non-rounded themes also need differently looking images. Simply copy the folder of an already existing theme that most closely resembles what you are trying to do. You will need to rename the theme with your own name. This includes the folder and several entries in the theme.js. Look in there; it should be fairly self-explanatory what needs to be renamed. All images that are used by the theme can be found in the images folder with sub folders. You can simply overwrite these with your own images.

The Default theme supplied with Bindows is a rounded theme. Non rounded themes are available for download at our web site.

If you are creating your own theme from scratch you will need to create the three files mentioned above and place them in a folder with the name of your theme. The theme.js file should create a new BiTheme object with the same name as the directory with the word "Theme" appended to the class name. Here one would usually derive from the default theme instead of creating the BiTheme object directly.

BiTheme Class

The theme.js file should define a theme object by extending BiTheme or BiDefaultTheme. The new theme is then instantiated and given to a special theme manager.

function MyTheme( )
{
   BiDefaultTheme.call( this, "MyTheme" );
}

_p = MyTheme.prototype = new BiDefaultTheme;
_p._className = "MyTheme";

// instantiate
application.getThemeManager().addTheme( new MyTheme );

Styling Components

CssClassNames and Appearance

Most components have a one-to-one relationship with an HTML element. Usually the CSS class name reflects the Bindows component's class name. For example, BiComponents have a class name called "bi-component" and BiComboBox components have a CSS class name of "bi-combo-box".

A component can also have one or many appearances. Those describe how to draw the component and are also mapped to a CSS class name. For example if you set the appearance to "button" then "button" is appended to the CSS class name of the component.

Let's show how this works using an example. We create a component and set its CSS class name and appearance.

var c = new BiComponent;
c.setCssClassName( "foo" );
c.setAppearance( "bar" );

This results in the following HTML:

<div id="..." class="foo bar"></div>

This will then match to the following CSS rules. Be careful here because Internet Explorer does not fully support multiple CSS class names.

.foo {
   color: red;
}

.bar {
   color: blue;
}

Don't use rules like this:

.foo .bar {
   color: green;
}

because Internet Explorer incorrectly breaks on them.

Often when you build custom Bindows components they are composed out of several different Bindows components. A BiComboBox for example is comprised of BiComponent that holds a BiTextField a couple of BiButtons and a BiList. After these three components have been styled, most of the BiComboBox is already styled.

Appearance States

To support interactive states for the appearance something called appearance states are used. Examples of the most common appearance states are:

Some components add other appearance states, such as "selected" and others.

You can make your component react to state changes by adding appearance listeners for it in your theme's theme.js file. (Bindows defines this in theme.js of the default theme so that other themes inherit this behavior).

 this.addAppearance( "tool-bar-button", ["hover", "active", "checked"] ); 

In the above example; when the mouse hovers over the component, this will result in the addition of a CSS class on the html element.

<div id="..." class="bi-tool-bar-button tool-bar-button-hover"></div>

If you want your state to be registered regardless of the theme, you can do it on the component level as well.

var c = new BiComponent;
c.setCssClassName( "foo" );
c.setAppearance( "bar" );
application.getThemeManager().addState( c, "baz" );

This results in the following HTML:

<div id="..." class="foo bar bar-baz"></div>

This will then match the following CSS rules.

.foo {
   color: red;
}

.bar {
   color: blue;
}

.bar-baz {
   background-color: lightblue;
}

This allows you to define different look for different states in a consistent manner.

JavaScript Aspects

Appearance Properties

Not every size and image can be defined in the CSS file. Therefore the theme has a way to define special appearance properties that you can later get hold of from JavaScript code inside your component. See BiDefaultTheme( in themes/Default/theme.js in your package ) for a list of currently used appearance properties.

function MyTheme( )
{
   BiDefaultTheme.call( this, "MyTheme" );

   ...
   this.setAppearanceProperty( "split-pane-horizontal-divider", "preferredWidth", 7 );
   ...
   this.setAppearanceProperty( "grid", "default-icon", imgBase + "file.gif" );
   ...
}

Then you can get hold of the appearance property for the current theme from code like this:

var uri = application.getTheme().getAppearanceProperty("grid", "default-icon");

Listening to Theme Changes and Invoking JavaScript

If the user of your application can switch themes it might be useful to be able to detect when the active theme changes. Any JavaScript that must run due to a theme change should occur in two places; themeComponent and unthemeComponent (abstract methods of BiTheme) in your theme.js file. To make it so that these methods are called for your component; you should make your component "theme aware" by placing the following line in the constructor:

this.makeThemeAware();

Whenever the theme changes the applyTheme method is called on all theme aware components. Each component is then unthemed and themed as needed. This is taken care of by two methods that you must declare in theme.js.

_p.themeComponent = function(oComponent) {
   if (oComponent instanceof MyComponent)
      oComponent.setSpecialImage("special.gif");
};

_p.unthemeComponent = function(oComponent) {
   if (oComponent instanceof MyComponent)
     oComponent.setSpecialImage("default.gif");

};
  

In the above code when the active theme changes to our new theme, themeComponent is called with an instance of an object that could be of type "MyComponent" and if that is the case we change some image on it. Later on when the user switches to another theme we will have to set the image back to its default and this happens in the unthemeComponent method. This is a great way to collect all theme specific code in one logical place.

The Theme Manager

In Bindows there is a singleton class called BiThemeManager. The theme manager holds all the themes and it allows you to add/remove themes, loop thought all themes, get and set the default theme as well as modify CSS styles. You can get hold of BiThemeManager, from anywhere in your JavaScript, through the public BiApplication singleton object in the following way.

var tm = application.getThemeManager();

Dynamic Style Modification from Code

In some cases you might want to dynamically change the style of some object such as a grid where every other row should have a different background color. In this case you can override a special method called getRowStyle on the datamodel. This method is called by Bindows whenever a grid is updated. Returning style information from this method effectively adds it as an inline style on the HTML element which causes the styles you supply to take precedence over the theme styles. Keep in mind though that the inline style will also take precedence over any appearance states and might interfere with how those works. Also be aware of that some components and some situations require you to call an update method before these changes take effect.

dm.getRowStyle = function (y) {
   if (y % 2)
      return "font-size:24px;background-color: #f7f7f7;";
   return "font-size:24px;";
};

The above method is used for components that, for performance reasons, don't derive from BiComponent. In most cases though you are dealing with a BiComponent derivate and can use the methods getStyleProperty and setStyleProperty but also getHtmlProperty and setHtmlProperty.