Twitter Logo Follow us on Twitter
Project Information About this project

RAP look and feel

Note: This is about the interaction design API for the workbench, not the theming API that controls the look of specific widgets

RAP provides an additional API to change the look and feel of workbench applications. This article is a step-by-step guide to using the so-called “interaction design API” to control the look of the WorkbenchWindow. All look and feel artifacts can be bundled into a separate plug-in and contributed to an existing application without touching the application itself.

Let's have a look at a RAP application with a customized look and feel:

RAP Application with Business theming

Getting started

These are the elements that we will be working with.

  • WorkbenchWindow components such as the ToolBar, MenuBar and the ViewToolbar.
  • Presentations for Stacks or, in other words, the look and feel of views and editors. You'll see later how the interaction design API allows you to make stack independent changes on-the-fly.
  • The graphical layout. When you change the look and feel of an application, you generally also need to change elements in the graphical layout, such as moving a menubar or the perspective switcher, or selecting a different font.
  • Activation by servlet name. You can leverage the servlets in RAP and use the servlet name to access the whole layout from a single point.
  • Reusable web components. Elements such as the header for an application are very likely to be reused in their web versions. Layout information can also be reused for web components. So a developer has not to develop many different web components, he can use the technique described bellow to create a component which can be reused in many applications.

Finally, one additional but important requirement for a good look and feel is to change interaction concepts to match the new UI. To assist in this, the API provides an automatic service called personalization. For instance it hides all ViewActions by default and makes it's visibility customizable.

WorkbenchWindow components

The best place to start is to create a PresentationFactory. You will need to create an extension for the org.eclipse.ui.presentationFactories extension point.

PresentationFactory Extensions

Here is the same as it appears in the plugin.xml.


  <extension point="org.eclipse.ui.presentationFactories">
    <factory
      class="org.eclipse.rap.presentation.example.PresentationFactoryImpl"
      id="org.eclipse.rap.presentation"
      name="RAP Presentation">
    </factory>
  </extension>
  

The presentationFactory contains the following attributes:

  • class
    The class refers to an implementation of org.eclipse.rap.ui.interactiondesign.PresentationFactory. It is very important not to extend from the original org.eclipse.ui.presentation.AbstractPresentationFactory as this class does not support the concepts described here.
  • name
    A human readable name for the PresentationFacotry implementation.

The org.eclipse.rap.ui.interactiondesign.PresentationFactory is abstract, so you will need to implement its abstract methods. Most are create methods for different Managers, for example, the MenuBar MenuManager or the Manager for the ToolBar. A ToolBar or MenuBar manager is a contribution manager which realizes itself and its items in a control. In these implementations you can change the look of the components such as styling a toolbar with icons and text or making it transparent.

Presentations for Stacks

If you are not familiar with the original AbstractPresentationFactory you might consider reading up on StackPresentations. You'll find a basic overview of the partstack in the Inside the Workbench article.
The look and feel of a partstack can be changed using StackPresentations. In the previous PresentationFactory the presentation had to be implemented programmatically. The interaction design API allows you to declare StackPresentations as an extension for the extension point org.eclipse.rap.ui.stackPresentations.
StackPresentation Extension

Here is the same as it appears in the plugin.xml.


  <extension point="org.eclipse.rap.ui.stackPresentations">
    <stackPresentation
      id="org.eclipse.rap.presentation.macBarStackPresentation"
      class="org.eclipse.rap.presentation.example.stacks.MacBarStackPresentation"
      name="MacBar"
      type="view"
      actionClass="org.eclipse.rap.presentation.example.configaction.ExampleConfigAction"
      actionIcon="icons/configAction.png"                        
      menuIcon="icons/menuIcon.gif">
    </stackPresentation>
  </extension>
  

stackPresentation contain the following attributes:

  • class
    To be backward compatible with Eclipse 3.4 or earlier, you can implement org.eclipse.ui.presentation.StackPresentation. But to use all the features of the interaction design API the org.eclipse.rap.ui.interactiondesign.ConfigurableStack. should be implemented. The ConfigurableStack is an abstract class and provides additional methods i.e. to get the part's ToolbarManager.
    The parts ToolbarManager contains all the actions of the parts toolbar and adds the personalization features. To create a clean UI all actions are invisible, but this visibility can be changed by the user during runtime.
    Another benefit of the ConfigurableStack is the ability to change the look of the stack during runtime without reloading the whole application. Therefore it provides a method called setCurrentStackPresentation( String id ). The id should be the id of the presentation defined in the extension. When you call this method, you will change the presentation of a whole stack to the presentation defined with the given id. The reloading happens automatically.
  • type
    There are three types of stacks. Editor-, view- and standaloneViewStacks. You can define a presentation for a specific type using the following arguments: view, standaloneView, editor.
  • actionClass
    This should be an implementation of org.eclipse.rap.ui.interactiondesign.ConfigurationAction. This is an abstract class providing methods for personalization. For example, you can use this to implement a popup dialog which allows the user to configure viewaction visibility or the stack's presentation. To apply configuration changes in your ConfigurableStack you will need to implement the Interface org.eclipse.rap.ui.interactiondesign.IConfigurationChangedListener and register it in the ConfigurationAction.
  • actionIcon
    A ConfigurationAction can contain an icon which can be used i.e. as a button's icon. The icon is associated with the ConfigurationAction.
  • menuIcon
    This is the icon for the view's menu and can be used to replace the standard triangle. To access this image use the getMenuIcon() Method inside the ConfigurableStack.

The graphical layout

RAP provides the ability to define graphical layouts where you have control over the elements such as the position of the workbench components like the toolbar, menubar or the perspective switcher. The WorkbenchWindowAdvisors createWindowContents( Shell ) method is one method, but the result of this technique is a fixed coupling between the look and feel and your application code.
You can now separate the application code and the look and feel by using the org.eclipse.rap.ui.interactiondesign.IWindowComposer Interface. An implementation of org.eclipse.rap.ui.interactiondesign.PresentationFactory defines the method createWindowComposer() which is called within the WorkbenchWindow. This is a simple replacement for the advisor's method that results in a loose coupling between application elements.
In order to organize graphical layout information such as images, colors, fonts and position data, the following registry was created. org.eclipse.rap.ui.interactiondesign.layout.LayoutRegistry
The LayoutRegistry is a singleton object which contains all existing layouts represented by org.eclipse.rap.ui.interactiondesign.model.Layout and org.eclipse.rap.ui.interactiondesign.model.LayoutSet. A Layout can be declared by creating an extension for the extension point org.eclipse.rap.ui.layouts.
Layout extension

And here is the same as it appears in the plugin.xml.


  <extension point="org.eclipse.rap.ui.layouts">
    <layout
      id="org.eclipse.presentation.example.layout"
      name="Example Layout">
        <layoutSet
          class="org.eclipse.rap.presentation.example.layoutset.HeaderLayoutSet2"
          id="header.layoutset"
          name="Header2">
        </layoutSet>
    </layout>
  </extension>
  

Layout contains the following attributes:

  • id
    This id can be used to get a Layout object from the LayoutRegistry.
  • name
    A human readable name for this layout.

Every Layout can have multiple layoutSets:
Element layoutSet
A layoutSet is a representation of the following class: org.eclipse.rap.ui.interactiondesign.layout.model.LayoutSet. The object contains the information described above. Every layoutSet has a maximum of one Layout as a parent and has the following attributes:

  • id
    This id can be used to get a LayoutSet object from a Layout using the getLayoutSet( String id ) method.
  • class
    This should be an implementation of the following interface:
    org.eclipse.rap.ui.interactiondesign.layout.ILayoutSetInitializer. This interface contains one method which is used to declared layout information for a LayoutSet object. All IlayoutSetInitializers will be called during the LayoutRegistry initialization.
  • overridesId
    This optional attribute should be an existing LayoutSet id. It's value will be used to override a LayoutSet. This is useful i.e. if a bundle defines a LayoutSet which contributes a Logo. Than another bundle can override this LayoutSet and contribute it's own logo.

The following is an example use case for a declarative layout.
A button should display its text in a specific font. Instead of defining the font directly in the source code, you can define a layout and a layoutSet over the extension described above. Let start by creating two id's.
For the Layout = org.eclipse.layout
For the layoutSet = org.eclipse.layoutSet
To define the font you can call the LayoutSet.addFont( String key, Font font ) method in your IlayoutSetInitializer implementation. We use "fontKey" as the key value. Here is the source code for this font definition:


    Button button = new Button( composite, SWT.NONE );
    LayoutRegistry registry = LayoutRegistry.getInstance();
    registry.setActiveLayout( "org.eclipse.layout" );
    Layout layout = registry.getActiveLayout();
    LayoutSet set = layout.getLayoutSet( "org.eclipse.layoutSet" );
    button.setFont( set.getFont( "fontKey" );
  

An alternative to accomplish this is theming, but the declarative layout give you more options such as defining images or position data. Another big advantage of using the declarative layout is that you can change a layout during runtime. And, to do this is just a two step process.

  1. Define two layouts with layoutSets. The layoutSets must have the same ids as in their equivalent and the key values for the information need to be identical.
  2. Call the setActiveLayout( String id ) method within the LayoutRegistry. This sets the active layout to the one with the given id. The PresentationFactory will be called automatically and prompt a rebuild of the styled components.

Activation by servlet name

Different layouts can be activated from a central point by using the servlet name. To activate a PresentationFactory and a Layout by a servlet name, you can use the org.eclipse.rap.ui.branding extension point.

Branding extension

And here is the same as it appears in the plugin.xml.


  <extension point="org.eclipse.rap.ui.branding">
    <branding
      defaultEntrypointId="org.eclipse.rap.demo.entrypoint1"
      favicon="icons/perspective.gif"
      id="org.eclipse.rap.presentation.macBarBranding"
      servletName="api"
      themeId="org.eclipse.rap.presentation.macBarTheme"
      title="Interactiondesign API">
      <presentationFactory
        defaultLayoutId="org.eclipse.rap.presentation.defaultlayout"
        id="org.eclipse.rap.presentation"
        name="ConfigurablePF">
        viewactionsVisible=true;
        <defaultStackPresentation
          id="org.eclipse.rap.presentation.navigationPaneStackPresentation"
          name="Default">
        </defaultStackPresentation>
        <stackPresentation
          id="org.eclipse.rap.presentation.macBarStackPresentation"
          name="topLeftMapping"
          partId="topLeft">
        </stackPresentation>
      </presentationFactory>
    </branding>
  </extension>
  

As you can see, the presentationFactory is an element of the branding extension and has the following attributes:

  • id
    This is the id of the PresentationFactory which should be loaded when the servlet name defined in the branding has been called.
  • defaultLayoutId
    This optional argument represents the id of the Layout which should be activated when the servlet name has been called. If no id is defined, the standard workbench layout should be loaded.
  • viewActionsVisible
    This boolean value is responsible for making all view contribution items visible by default (true) or to let the user decide which items are visible (false).

The presentationFactory element can have two different sub elements.

  1. defaultStackPresentation
    This represents the StackPresentation which is loaded for all parts if nothing else is defined. You need to specify the id of the StackPresentation if a different presentation should be loaded.
  2. stackPresentation
    This element couples a stackPresentation to a specific part. The id attribute should be the id of the stackPresentation to load. The partId represents the id of a part which has been defined in a perspective.

Reusable web components

To style the look and feel of a RAP application many web components are also required. For example, you may want to place the menubar on a header with rounded corners or place the statusline in a nicely styled footer. You can accomplish this with SWT Widgets but if you want to reuse these components, you'll need to use also org.eclipse.rap.ui.interactiondesign.layout.ElementBuilder. An ElementBuilder is an abstract class which works hand in hand with the declarative layout. You can extend it to build your own web components.
This is a two step process.

  1. Extend org.eclipse.rap.ui.interactiondesign.ElementBuilder and implement its abstract methods. The most important method is build(). You should build your component with SWT widgets when build() is called. Every ElementBuilder is associated to a LayoutSet id which will get from the LayoutRegsitry during the instantiation of the ElementBuilder. During the instantiation the builder register itself in the LayoutRegistry. If a new Layout is activated the dispose() methods from all registered ElementBuilders will be called. This means that you need to be sure you dispose your component correctly in this method. If not, switching the Layout on-the-fly may not work correctly.
  2. The second step is to instantiate an ElementBuilder. As an example, YourBuilder is instantiated by the following snippet:
          Composite parent = new Composite( aShell, SWT.NONE );
          ElementBuilder builder = new YourBuilder( parent, "org.eclipse.layoutSet" );
          
    Be sure that your ElementBuilder implementation is initialized with a layoutSet you've associated with it.

You may recognize the idea behind the ElementBuilder - it is based on the builder design pattern. This separates the component from your application code allowing you to reuse the component in different places.

This completes our tour of the interaction design API. For questions please feel free to visit the RAP newsgroup. We'd also be glad to hear about your experiences with this API and to have a look at the great new look and feels you're building for your apps.