NOW Hiring

Quick links

This tutorial gives an introduction into the CSS styling capabilities of Eclipse 4.

1. Eclipse and declarative styling

The visual appearance of your Eclipse application can be styled via external files similar to the CSS 2.1 standard. CSS selectors used in Eclipse are identifiers, which relate to widgets or other elements, for example predefined pseudo classes. Non-standard properties are prefixed with either swt- or eclipse-.

The following example shows a CSS file which defines a styling for an Eclipse application.

Label {
        font: Verdana 8px;
        color: black;
}
Composite Label {
        color: black;
}
Text {
        font: Verdana 8px;
}
/* Identifies only those SWT Text elements
appearing within a Composite */
Composite Text {
        background-color: white;
        color: black;
}
SashForm {
        background-color: #c1d5ef;
}
/* background uses a gradient */
.MTrimBar {
        background-color: #e3efff #c1d5ef;
        color: white;
        font: Verdana 8px;
}

Shell {
        background-color: #e3efff #c1d5ef 60%;
}

SWT has certain limitations concerning component styling. These limitations are based on the restrictions of the underlying operating system or limitation in SWT itself. For example, it is not possible to style menus and table headers because this is not supported by SWT. In addition some platforms do not allow the styling of certain widgets. For example, the SWT implementation for Windows does not allow to set the background color of the Button and the ScrollBar widget.

In most cases it is possible to overcome the styling restrictions of SWT in RCP applications. For example, you can use NatTable to display your tables. NatTable is a flexible table and tree implementation which allows full styling. For other components you can use custom widgets or use a PaintListener to draw on top of the widget.

2. Styling using themes

You can define themes for styling your application. This can be done via extensions in the plugin.xml file. To create new themes you define extensions for the org.eclipse.e4.ui.css.swt.theme extension point. Such an extension defines an ID for the style and a pointer to the CSS file.

You can define the default theme via the cssTheme property in your org.eclipse.core.runtime.products extension. This can also be used to define a fixed styling.

You need to set cssTheme otherwise the theming services is available at runtime.

The Eclipse platform provides the theme service with an instance of the IThemeEngine interface. Via this instance you can change the active theme at runtime. The switching of themes is demonstrated in the following handler code.

package com.example.e4.rcp.todo.handlers;

import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.ui.css.swt.theme.IThemeEngine;

public class ThemeSwitchHandler {
        private static final String DEFAULT_THEME = "com.example.e4.rcp.todo.default";
        private static final String RAINBOW_THEME = "com.example.e4.rcp.todo.rainbow";

        @Execute
        public void switchTheme(IThemeEngine engine) {
                if (!engine.getActiveTheme().getId().equals(DEFAULT_THEME)){
                        // second argument defines that change is
                        // persisted and restored on restart
                        engine.setTheme(DEFAULT_THEME, true);
                } else {
                        engine.setTheme(RAINBOW_THEME, true);
                }
        }
}

You can also specify a fixed styling of your application. This is done via a property for the org.eclipse.core.runtime.products extension point in the plugin.xml file.

The value of the applicationCSS property should point to your CSS file via an URI. The URI follows the platform:/plugin/BundleSymbolicName/path/file convention. The following example demonstrates such a URI:

platform:/plugin/com.example.e4.rcp.todo/css/default.css

The screenshot shows an example of the plugin.xml file with a defined applicationCSS property.

applicationCSS entered on the product extension

The corresponding file is shown below.

<?eclipse version="3.4"?>
<plugin>

 <extension
   id="product"
   point="org.eclipse.core.runtime.products">
  <product
           application="org.eclipse.e4.ui.workbench.swt.E4Application"
            name="to-do">
   <property
             name="appName"
             value="to-do">
   `
   <property
     name="applicationCSS"
     value="platform:/plugin/com.example.e4.rcp.todo/css/default.css">
   `
  </product>
 </extension>

</plugin>

3. CSS support for standard Eclipse elements

3.1. CSS attributes and selectors

The CSS attributes for SWT widgets which can be styled are listed under the following link: http://wiki.eclipse.org/E4/CSS/SWT_Mapping

Model elements of your Eclipse application, e.g., MPartStack or MPart can also be used a selectors for styling. The CSS selectors are based on the Java classes for the model elements. Some examples are given in the following table.

Table 1. Table Model elements and CSS selectors
Model Element CSS Selector

MPart

.MPart

MPartStack

.MPartStack

MPartSashContainer

.MPartSashContainer

For example you could hide the minimize and maximize button of a MPartStack via the following CSS rule.

.MPartStack {
        swt-maximize-visible: false;
        swt-minimize-visible: false;
}

Eclipse also supports CSS pseudo classes but these are disabled by default. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=362532 for details. The following table lists several of the available pseudo classes.

Table 2. Table CSS pseudo classes in Eclipse
CSS pseudo classes Description

Button:checked

Selects checked buttons

:active

For example shell:active selects the active shell

:selected

Allows to style a selected element, e.g., a part in a PartStack.

3.2. Styling based on identifiers and classes

You can specify an identifier or a class on widgets in your source code and use these as selectors for styling. An identifier is supposed to be unique while a class can be assigned to several elements.

The following example demonstrates how to set the identifier and the class on SWT widgets.

// IStylingEngine is injected
@Inject IStylingEngine engine;

// more code....

Label label = new Label(parent, SWT.NONE);
Text text = new Text(parent, SWT.BORDER);

// set the ID, must be unique in the same window
engine.setID(label, "MyCSSTagForLabel");

// set the class, can be used several times
engine.setClassname(text, "error");

Sometimes the IStylingEngine cannot be accessed easily. For example, if your want to style existing dialog which are not created via dependency injection. In this case you can set a tag on SWT widget directly.

// more code....

Label label = new Label(parent, SWT.NONE);
Text text = new Text(parent, SWT.BORDER);

// set the ID, must be unique in the same window
text.setData("org.eclipse.e4.ui.css.id", "MyCSSTagForLabel");

// set the class, can be used several times
label.setData("org.eclipse.e4.ui.css.CssClassName", "MyCSSTagForLabel")

These ids or classes can be addressed in the CSS file via the # or the . selector.

#MyCSSTagForLabel{
        color: blue;
}

.error {
        border: 1px red;
}

3.3. Colors and gradients

Colors can be defined in different ways. For example, the color white can be described as white, rgb(255,255,255) or #ffffff.

Since Eclipse 4.3 also color constants from SWT can be used, e.g., SWT.COLOR_BLACK or SWT.COLOR_WIDGET_BACKGROUND.

Text {
        color: COLOR-CYAN;
        background-color: COLOR-WIDGET-BACKGROUND;
}

.MPart .InfoPane {
        color: COLOR-INFO-FOREGROUND;
        background-color: COLOR-INFO-BACKGROUND;
}
Note that the CSS value uses an "-" in between unlike the the SWT constants, which use an "_".

Styling supports gradients for the background color of user interface widgets. Linear and radial gradients are supported. The definition of gradients is demonstrated in the following listing.

/* linear gradient */
/* background-color: gradient linear color_1 [color_2]* [percentage]* */

/* for example */
background-color: gradient linear white black
background-color: gradient linear white black blue
background-color: gradient linear white black 50%
background-color: gradient linear white black 50% 20%

/* radial gradient */
/* background-color: gradient radial color_1 [color_2]* [percentage]* */

/* for example */
background-color: gradient radial white black
background-color: gradient radial white black 50%

If you use the optional percentage in the definition, then the color change will be done after the defined percentage. The following picture shows the result of different settings.

Different Gradient settings
Please note that the current gradient specification is not compliant with the CSS specification and that it might change in the future.

3.4. CSS imports

Eclipse supports also the import of existing stylesheets via the @import directive using the platform URI notation. If you import CSS files from another plug-in, it requires that the plug-in which contains the imported CSS file is included in your product configuration file.

For example the following stylesheet would import the existing dark theme of the org.eclipse.ui.themes plug-in from the Eclipse IDE.

@import url("platform:/plugin/org.eclipse.ui.themes/css/e4-dark.css");

/* add new CSS rules here. You can also override setting defined in the imported CSS
by using the same selector-property combination */

Using this import results in an application styling similar to the following screenshot.

Dark theming

4. CSS support for custom widgets

The CSS support offers two extension points for adding CSS capabilities to custom widgets:

  • org.eclipse.e4.ui.css.core.elementProvider

  • org.eclipse.e4.ui.css.core.propertyHandler

Those extension points are defined in the org.eclipse.e4.ui.css.core plug-in. You find some predefined implementations for SWT in the org.eclipse.e4.ui.css.swt plug-in.

4.1. The org.eclipse.e4.ui.css.core.elementProvider extension point

Under this extension you can specific for which entries in the CSS style sheet the IElementProvider is responsible. The org.eclipse.e4.ui.css.core.elementProvider extension defines an IElementProvider , which returns the widgets adapter(ElementAdapter). IElementProvider returns an org.w3c.dom.Element.

ElementAdapter type hierarchy

In general the returned org.w3c.dom.Element is an instance of an ElementAdapter. By the widget properties under the provider, you point to the class of the actual widget, like org.eclipse.swt.widgets.Control, for which the provider is responsible.

The SWTElementProvider for instance contains those ElementAdapter implementations, which are shown in the type hierarchy screenshot and looks like this:

/**
 * Returns the CSS class which is responsible for styling a SWT widget
 *
 * Registered via the "org.eclipse.e4.ui.css.core.elementProvider" extension
 * point for the SWT widgets
 *
 *
 *
 * {@link IElementProvider} SWT implementation to retrieve w3c Element
 * {@link SWTElement} linked to SWT widget.
 *
 */
public class SWTElementProvider implements IElementProvider {

        public static final IElementProvider INSTANCE = new SWTElementProvider();

        @Override
        public Element getElement(Object element, CSSEngine engine) {
                if (element instanceof Text) {
                        return new TextElement((Text) element, engine);
                }
                if (element instanceof Button) {
                        return new ButtonElement((Button) element, engine);
                }
                if (element instanceof Scale) {
                        return new ScaleElement((Scale) element, engine);
                }
                if (element instanceof Shell) {
                        return new ShellElement((Shell) element, engine);
                }
                if (element instanceof CTabFolder) {
                        return new CTabFolderElement((CTabFolder) element, engine);
                }
                if (element instanceof ToolBar) {
                        return new ToolBarElement((ToolBar) element, engine);
                }
                if (element instanceof Composite) {
                        return new CompositeElement((Composite) element, engine);
                }
                if (element instanceof Control) {
                        return new ControlElement((Control) element, engine);
                }
                if (element instanceof CTabItem) {
                        return new CTabItemElement((CTabItem) element, engine);
                }
                if (element instanceof TableItem) {
                        return new TableItemElement((TableItem) element, engine);
                }
                if (element instanceof ToolItem) {
                        return new ToolItemElement((ToolItem) element, engine);
                }
                if (element instanceof Item) {
                        return new ItemElement((Item) element, engine);
                }
                if (element instanceof Widget) {
                        return new WidgetElement((Widget) element, engine);
                }
                return null;
        }
}

See the following URL http://wiki.eclipse.org/Eclipse4/RCP/CSS for more information.

4.2. The org.eclipse.e4.ui.css.core.propertyHandler extension point

In the org.eclipse.e4.ui.css.core.propertyHandler extension point you define an adapter of type ElementAdapter for your widget which is used by the CSS engine. Here you specify an implementation of an ICSSPropertyHandler, where different property-names for the widget can be defined. This implementation is responsible for applying the CSS properties to the underlying widget.

There are three properties, which have to be defined for a org.eclipse.e4.ui.css.core.propertyHandler.

Table 3. Table Properties of the propertyHandler
Property Description

adapter

The ElementAdapter implementation adapts/wraps your custom widget for the CSS framework

composite

A boolean, which specifies whether the widget is a composite or not

handler

ICSSPropertyHandler implementation, which is responsible for setting a custom CSS property.

Here you can see a sample of the org.eclipse.e4.ui.css.swt projects propertyHandler extension point:

PropertyHandler extension point for org.eclipse.e4.ui.css.swt

These are the default ElementAdapter implementations for the adapter property in the screenshot above:

ElementAdapter type hierarchy

In the ICSSPropertyHandler implementation those property-names are read and applied to the actual widget.

The CSSPropertyBackgroundSWTHandler and it’s parent AbstractCSSPropertyBackgroundHandler are examples of such an implementation of the ICSSPropertyHandler. Here the background property is applied for certain widgets.

The AbstractCSSPropertyBackgroundHandler class delegates to the methods for the property-names. These are applied in the propertyHandler extension point, to its subclasses like the CSSPropertyBackgroundSWTHandler.

        @Override
        public boolean applyCSSProperty(Object element, String property,
                        CSSValue value, String pseudo, CSSEngine engine) throws Exception {
                if ("background".equals(property)) {
                        applyCSSPropertyBackground(element, value, pseudo, engine);
                }
                if ("background-attachment".equals(property)) {
                        applyCSSPropertyBackgroundAttachment(element, value, pseudo, engine);
                }
                if ("background-color".equals(property)) {
                        applyCSSPropertyBackgroundColor(element, value, pseudo, engine);
                }
                if ("background-image".equals(property)) {
                        applyCSSPropertyBackgroundImage(element, value, pseudo, engine);
                }
                if ("background-position".equals(property)) {
                        applyCSSPropertyBackgroundPosition(element, value, pseudo, engine);
                }
                if ("background-repeat".equals(property)) {
                        applyCSSPropertyBackgroundRepeat(element, value, pseudo, engine);
                }
                return false;
        }

The following listing is the applyCSSPropertyBackgroundColor method of the CSSPropertyBackgroundSWTHandler. It shows how the background-color property-name is applied for several widgets:

@Override
        public void applyCSSPropertyBackgroundColor(Object element, CSSValue value,
                        String pseudo, CSSEngine engine) throws Exception {
                Widget widget = (Widget) ((WidgetElement) element).getNativeWidget();
                if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
                        Color newColor = (Color) engine.convert(value, Color.class, widget
                                        .getDisplay());
                        if (widget instanceof CTabItem) {
                                CTabFolder folder = ((CTabItem) widget).getParent();
                                if ("selected".equals(pseudo)) {
                                        // tab folder selection manages gradients
                                        CSSSWTColorHelper.setSelectionBackground(folder, newColor);
                                } else {
                                        CSSSWTColorHelper.setBackground(folder, newColor);
                                }
                        } else if (widget instanceof Control) {
                                GradientBackgroundListener.remove((Control) widget);
                                CSSSWTColorHelper.setBackground((Control) widget, newColor);
                                CompositeElement.setBackgroundOverriddenByCSSMarker(widget);
                        }
                } else if (value.getCssValueType() == CSSValue.CSS_VALUE_LIST) {
                        Gradient grad = (Gradient) engine.convert(value, Gradient.class,
                                        widget.getDisplay());
                        if (grad == null) {
                                return; // warn?
                        }
                        if (widget instanceof CTabItem) {
                                CTabFolder folder = ((CTabItem) widget).getParent();
                                Color[] colors = CSSSWTColorHelper.getSWTColors(grad,
                                                folder.getDisplay(), engine);
                                int[] percents = CSSSWTColorHelper.getPercents(grad);

                                if ("selected".equals(pseudo)) {
                                        folder.setSelectionBackground(colors, percents, true);
                                } else {
                                        folder.setBackground(colors, percents, true);
                                }

                        } else if (widget instanceof Control) {
                                GradientBackgroundListener.handle((Control) widget, grad);
                                CompositeElement.setBackgroundOverriddenByCSSMarker(widget);
                        }
                }
        }

5. CSS Tools

5.1. CSS Spy

The CSS spy tool helps to see the possible styling options for selected elements. CSS spy is part of the e4 tooling project and can be installed from its update site.

You can open the CSS spy via the Shift+Alt+F5 shortcut.

CSS spy allows you to see the available properties and the current style settings.

The CSS Spy in action

5.2. CSS Scratchpad

The CSS Scratchpad allows to change CSS rules interactively. It is also part of the e4 tooling project.

You open it via the Ctrl+Shift+Alt+F6 shortcut.

If you click the Apply button, the entered CSS is applied at runtime to your application.

6. Exercise: Styling with CSS files

In this exercise you style your application with a CSS file.

6.1. Create a CSS file

Create a folder called css in your com.example.e4.rcp.todo plug-in and create the following default.css file.

Label {
        font: Verdana 8px;
        color: black;
}
Composite Label {
        color: black;
}
Text {
        font: Verdana 8px;
}
/* Identifies only those SWT Text elements
appearing within a Composite */
Composite Text {
        background-color: white;
        color: black;
}
SashForm {
        background-color: #c1d5ef;
}
/* background uses a gradient */
.MTrimBar {
        background-color: #e3efff #c1d5ef;
        color: white;
        font: Verdana 8px;
}

Shell {
        background-color: #e3efff #c1d5ef 60%;
}

6.2. Define the applicationCSS property

You can point to your CSS file via your product configuration file. Open your product and select the Customization tab. Use the CSS File entry to select your CSS file.

Selecting the extension

6.3. Validating

Start your application. The application should be styled according to your CSS file.

After you started the application via the product, the applicationCSS property is added to the plugin.xml file for the org.eclipse.core.runtime.products extension of your application. Check the content of the plugin.xml file

Change the CSS style sheet and restart your application to see the effect.

6.4. Adjust the build.properties file

Add the created CSS file to your build.properties file in the com.example.e4.rcp.todo plug-in to make it available in an exported application.

7. Exercise: Dynamic style switching with themes

In this exercise you use the IThemeEngine to introduce the ability to switch the application styling at runtime.

7.1. Add dependencies

Add the org.eclipse.e4.ui.css.swt.theme plug-in as dependency to your application plug-in.

7.2. Create a CSS file

Create the following css/rainbow.css file.

Shell {
    swt-background-mode: default;
    background-color: red orange yellow green orange 30% 60% 75% 95%
}

.MPartStack {
    font-size: 22px;
    color: orange;
}

7.3. Remove the applicationCSS property

Remove the applicationCSS property from your product extension. For this select the property and press the Remove button as depicted in the following screenshot.

Remove applicationCSS

7.4. Create the theme extensions

Select the Extensions tab in the plugin.xml editor and create two extensions for the org.eclipse.e4.ui.css.swt.theme extension point.

You create an extension in the Extensions tab by pressing the Add…​ button.

Add button in extension points

Select the org.eclipse.e4.ui.css.swt.theme extension point and press the Finish button.

Selecting

Right-click on the org.eclipse.e4.ui.css.swt.theme extension to create a new theme. The following screenshots show the extensions which you should create.

theme10
theme20

The relevant part in the plugin.xml file should look like the following.

 <extension
         point="org.eclipse.e4.ui.css.swt.theme">
      <theme
            basestylesheeturi="css/default.css"
            id="com.example.e4.rcp.todo.default"
            label="Default Theme">
      </theme>
      <theme
            basestylesheeturi="css/rainbow.css"
            id="com.example.e4.rcp.todo.rainbow"
            label="Rainbow">
      </theme>
</extension>

Add the cssTheme property to your product and point to the com.example.e4.rcp.todo.default theme.

theme30

Ensure that the cssTheme property is correctly set. Without this initial setting the IThemeEngine does not work.

7.5. Validating

Start your application and ensure that the application is styled according to the cssTheme property setting.

7.6. Implement a new menu entry

Create a new Java class, which allows you to switch between the themes.

package com.example.e4.rcp.todo.handlers;

import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.ui.css.swt.theme.IThemeEngine;

public class ThemeSwitchHandler {
        private static final String DEFAULT_THEME = "com.example.e4.rcp.todo.default";
        private static final String RAINBOW_THEME = "com.example.e4.rcp.todo.rainbow";

        @Execute
        public void switchTheme(IThemeEngine engine) {
                if (!engine.getActiveTheme().getId().equals(DEFAULT_THEME)){
                        // second argument defines that change is
                        // persisted and restored on restart
                        engine.setTheme(DEFAULT_THEME, true);
                } else {
                        engine.setTheme(RAINBOW_THEME, true);
                }
        }
}

Define a new command and a handler in your application model for switching the style. Assign the above class to the handler.

Add a menu entry to your application model which uses your handler for switching the style.

7.7. Validate theme switching

Start the application and select your new menu entry. Afterwards the styling of your application should use the rainbow theme.

theme40

Changes in styling may require a restart of your application. The CSS engines resets most of the SWT properties but unfortunately not all widget reacts to this reset.

7.8. Optional: Reusing the dark theme of Eclipse

You can also reuse other CSS themes. To use the dark theme of the Eclipse IDE, add the org.eclipse.ui.themes plug-in to your feature.

Create the following file called dark.css in your css folder.

@import url("platform:/plugin/org.eclipse.ui.themes/css/e4-dark.css");

Create a new theme pointing to the following new CSS file. Start your application and validate that you can switch to the dark theme.

7.9. Adjust the build.properties file

Ensure that all CSS files are selected in your build.properties file in the com.example.e4.rcp.todo plug-in to make them available in an exported application.

8. Exercise: Styling the widgets created by the life cycle class

In this exercise you will ensure that the widget created by the life cycle handler of your application are also styled.

8.1. Implement styling

Change your Manager class to the following to one of your CSS files for the widgets created in this class.

package com.example.e4.rcp.todo.lifecycle;

import javax.inject.Inject;

import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.extensions.Preference;
import org.eclipse.e4.ui.internal.workbench.E4Workbench;
import org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine;
import org.eclipse.e4.ui.workbench.IWorkbench;
import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.osgi.service.prefs.BackingStoreException;

import com.example.e4.rcp.todo.dialogs.PasswordDialog;
import com.example.e4.rcp.todo.preferences.PreferenceConstants;

public class Manager {

        // We add the nodePath in case you move the lifecycle handler to
        // another plug-in later
        @Inject
        @Preference(nodePath = PreferenceConstants.NODEPATH,
                                value = PreferenceConstants.USER_PREF_KEY)
        private String user;

        @PostContextCreate
        public void postContextCreate(@Preference IEclipsePreferences prefs,
                        IApplicationContext appContext, Display display, IEclipseContext context) {

                final Shell shell = new Shell(SWT.SHELL_TRIM);
                PasswordDialog dialog = new PasswordDialog(shell);
                if (user != null) {
                        dialog.setUser(user);
                }

                // close the static splash screen
                appContext.applicationRunning();

                // position the shell
                 setLocation(display, shell);

                 String cssURI = "platform:/plugin/com.example.e4.rcp.todo/css/rainbow.css";
             context.set(E4Workbench.CSS_URI_ARG, cssURI);
             PartRenderingEngine.initializeStyling(shell.getDisplay(), context);
                // open the dialog
                if (dialog.open() != Window.OK) {
                        // close the application
                        System.exit(-1);
                } else {
                        // get the user from the dialog
                        String userValue = dialog.getUser();
                        // store the user values in the preferences
                        prefs.put(PreferenceConstants.USER_PREF_KEY, userValue );
                        try {
                                prefs.flush();
                        } catch (BackingStoreException e) {
                                e.printStackTrace();
                        }
                }

        }

        private void setLocation(Display display, Shell shell) {
                Monitor monitor = display.getPrimaryMonitor();
                Rectangle monitorRect = monitor.getBounds();
                Rectangle shellRect = shell.getBounds();
                int x = monitorRect.x + (monitorRect.width - shellRect.width) / 2;
                int y = monitorRect.y + (monitorRect.height - shellRect.height) / 2;
                shell.setLocation(x, y);
        }
}

8.2. Validating

Start your application and ensure the splash screen is styled according to your settings.

Styled splash screen

9. Exercise: Define a custom CSS selector and CSS property

In this exercise you will implement your custom CSS selector and property for styling the header of an user profile widget. The aim is to apply custom styling to the custom widget, called UserProfileWidget.

You will define a new UserProfileWidget CSS selector and a new header-background-color CSS property. The result may look like that, where the user profile widget is embedded in a Tooltip:

UserProfileWidget sample

9.1. Add dependencies

Add the org.eclipse.e4.ui.css.core and the org.eclipse.e4.ui.css.swt plug-ins as dependency to your application.

9.2. Adjust custom CSS to your CSS file

Add the following CSS to your CSS file, which is used in your application plug-in.

UserProfileWidget { (1)
   header-background-color: red; (2)
}
1 Custom CSS selector
2 Custom CSS property

9.3. Create the user profile widget

In order to learn how to implement customizations for CSS we also need a custom widget, which should be styled. So we implement a really simple custom widget, which is a Composite that consists of three other Composites for the header, image and description area. For the following widget you should create a widget package in your plug-in and place the UserProfileWidget class into it.

package com.vogella.rcp.css.custom.widget;

import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;

public class UserProfileWidget extends Composite {
        private ResourceManager resourceManager;

        private String text = "";
        private String headerText = "";
        private ImageDescriptor imageDescriptor;

        private Label imageLabel;
        private Label headerLabel;
        private Label labelText;

        private Composite head;

        public UserProfileWidget(Composite parent, int style) {
                super(parent, style);
                GridLayoutFactory.swtDefaults().numColumns(2).applyTo(this);

                head = new Composite(this, SWT.NONE);
                GridLayoutFactory.fillDefaults().applyTo(head);
                GridDataFactory.fillDefaults().span(2, 1).grab(true, false).applyTo(head);
                headerLabel = new Label(head, SWT.NONE);
                headerLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
                headerLabel.setText(getHeaderText());

                Composite imageComposite = new Composite(this, SWT.NONE);
                GridLayoutFactory.fillDefaults().applyTo(imageComposite);
                GridDataFactory.fillDefaults().applyTo(imageComposite);
                imageLabel = new Label(imageComposite, SWT.NONE);
                ImageDescriptor imgDescriptor = getImage();
                if (imgDescriptor != null) {
                        imageLabel.setImage(getResourceManager().createImage(imgDescriptor));
                }

                Composite textComposite = new Composite(this, SWT.NONE);
                GridLayoutFactory.fillDefaults().applyTo(textComposite);
                GridDataFactory.fillDefaults().grab(true, true).applyTo(textComposite);
                labelText = new Label(textComposite, SWT.NONE);
                labelText.setText(getText());
        }

        protected ResourceManager getResourceManager() {
                if (null == resourceManager) {
                        resourceManager = new LocalResourceManager(JFaceResources.getResources(), this);
                }
                return resourceManager;
        }

        protected String getText() {
                return text;
        }

        public void setText(String text) {
                this.text = text;
                if(labelText != null) {
                        labelText.setText(text);
                }
        }

        protected String getHeaderText() {
                return headerText;
        }

        public void setHeaderText(String headerText) {
                this.headerText = headerText;
                if (headerLabel != null) {
                        headerLabel.setText(headerText);
                }
        }

        protected ImageDescriptor getImage() {
                return imageDescriptor;
        }

        public void setImage(ImageDescriptor imageDescriptor) {
                this.imageDescriptor = imageDescriptor;
                if (imageLabel != null) {
                        imageLabel.setImage(getResourceManager().createImage(imageDescriptor));
                }
        }

        public Color getHeaderColor() {
                return head.getBackground();
        }

        public void setHeaderColor(Color color) {
                head.setBackground(color);
                headerLabel.setBackground(color);
        }
}

The most important method for our custom CSS here is the last setHeaderColor() method, which we will use for our header-background-color CSS property. In order to test this widget you can place it on a new part in your application or in a tooltip as it is depicted in the screenshot above. Afterwards you should set a certain header-text, an image and a description-text to the UserProfileWidget.

9.4. Adding the org.eclipse.e4.ui.css.core.propertyHandler extension point

Select the Extensions tab in the editor for the plugin.xml file and add the org.eclipse.e4.ui.css.core.propertyHandler extension point. You create an extension in the Extensions tab by pressing the Add…​ button.

Add button in extension points

Select the org.eclipse.e4.ui.css.core.propertyHandler extension point and press the Finish button.

Selecting
propertyHandler handler definition

By clicking on the adapter hyperlink of the handler in the plugin.xml a "New Java Class" dialog appears, where you define the CompositeElement as superclass. Call the class itself UserProfileElementAdapter and place it into a css package.

new class dialog user profile element adapter

The UserProfileElementAdapter class should look like this:

package com.vogella.rcp.css.custom.css;

import org.eclipse.e4.ui.css.core.engine.CSSEngine;
import org.eclipse.e4.ui.css.swt.dom.CompositeElement;

import com.vogella.rcp.css.custom.widget.UserProfileWidget;

public class UserProfileElementAdapter extends CompositeElement {

        public UserProfileElementAdapter(UserProfileWidget composite, CSSEngine engine) {
                super(composite, engine);
        }
}

In this case we only have a custom composite, which needs to be adapted. The propertyHandler needs a handler, which has to be an implementation of the ICSSPropertyHandler interface. Therefore we need to click on the handler hyperlink in the plugin.xml and the following "New Java Class" dialog appears:

new class dialog user profile css handler

The AbstractCSSPropertySWTHandler is an abstraction of the ICSSPropertyHandler interface for SWT widgets. It handles the cast to a SWT Control, which is then passed to the applyCSSProperty and retrieveCSSProperty methods. In the UserProfileCSSHandler implementation we apply the header-background-color to our UserProfileWidget.

package com.vogella.rcp.css.custom.css;

import org.eclipse.e4.ui.css.core.dom.properties.ICSSPropertyHandler;
import org.eclipse.e4.ui.css.core.dom.properties.converters.ICSSValueConverter;
import org.eclipse.e4.ui.css.core.engine.CSSEngine;
import org.eclipse.e4.ui.css.swt.properties.AbstractCSSPropertySWTHandler;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Control;
import org.w3c.dom.css.CSSValue;

import com.vogella.rcp.css.custom.widget.UserProfileWidget;

public class UserProfileCSSHandler extends AbstractCSSPropertySWTHandler implements ICSSPropertyHandler {

        private static final String HEADER_COLOR = "header-background-color";

        @Override
        protected void applyCSSProperty(Control control, String property, CSSValue value, String pseudo, CSSEngine engine)
                        throws Exception {
                if (control instanceof UserProfileWidget) {
                        UserProfileWidget userProfileWidget = (UserProfileWidget) control;
                        if (HEADER_COLOR.equalsIgnoreCase(property) && (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE)) {
                                Color newColor = (Color) engine.convert(value, Color.class, control.getDisplay());
                                userProfileWidget.setHeaderColor(newColor);
                        }
                }
        }

        @Override
        protected String retrieveCSSProperty(Control control, String property, String pseudo, CSSEngine engine)
                        throws Exception {
                if (control instanceof UserProfileWidget) {
                        UserProfileWidget userProfileWidget = (UserProfileWidget) control;
                        if (HEADER_COLOR.equalsIgnoreCase(property)) {
                                ICSSValueConverter cssValueConverter = engine.getCSSValueConverter(String.class);
                                return cssValueConverter.convert(userProfileWidget.getHeaderColor(), engine, null);
                        }
                }
                return null;
        }

}

At first we check, if the given Control is a UserProfileWidget and cast it. Then we check, if the property, which should be applied, is our header-background-color. In case these checks are valid the CSSEngine can be used to convert the given CSSValue in our case to a Color. Finally we set this color for the header of our custom widget.

If you are using a different UI toolkit than SWT you must only implement the ICSSPropertyHandler interface, rather than the AbstractCSSPropertySWTHandler. This interface passes an java.lang.Object and does not check for a SWT Control .

The last thing we need to do for the propertyHandler extension is to define for which property-name this handler is responsible:

property handler property name

9.5. Adding the org.eclipse.e4.ui.css.core.elementProvider extension point

Now the second and last extension point has to be added:

element provider extension

After adding the org.eclipse.e4.ui.css.core.elementProvider extension point we can add an IElementProvider to it. For this, click on the class hyperlink and give the class the name UserProfileWidgetElementProvider.

element provider provider definition

The widget property you can see in the screenshot above points to the full qualified name of the UserProfileWidget (com.vogella.rcp.css.custom.widget.UserProfileWidget). The widget property may be used several times, so that one IElementProvider implementation can be in charge of several widgets. The UserProfileWidgetElementProvider class we just created by clicking the class hyperlink in the plugin.xml implements the IElementProvider interface and should look like this:

package com.vogella.rcp.css.custom.css;

import org.eclipse.e4.ui.css.core.dom.IElementProvider;
import org.eclipse.e4.ui.css.core.engine.CSSEngine;
import org.w3c.dom.Element;

import com.vogella.rcp.css.custom.widget.UserProfileWidget;

public class UserProfileWidgetElementProvider implements IElementProvider {

        @Override
        public Element getElement(Object element, CSSEngine engine) {
                if(element instanceof UserProfileWidget) {
                        return new UserProfileElementAdapter((UserProfileWidget) element, engine);
                }

                return null;
        }

}

In this IElementProvider implementation we return the UserProfileElementAdapter, which we previously defined, when we attached this adapter to the propertyHandler.

9.6. Validating

Now we can check, if our custom CSS definitions are applied to the UserProfileWidget. Make sure that you have done all the steps and then start your application.

  • The UserProfileWidget has been applied to a Part and a sample header-text, image and description-text has been set for the widget.

  • The CSS snippet has been added to the active CSS file

  • You have configured the org.eclipse.e4.ui.css.core.propertyHandler and org.eclipse.e4.ui.css.core.elementProvider extension points correctly

10. About this website

11. CSS styling resources

11.1. vogella GmbH training and consulting support

TRAINING SERVICE & SUPPORT

The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”.

The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.

Copyright © 2012-2016 vogella GmbH. Free use of the software examples is granted under the terms of the EPL License. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

See Licence.