Defining Visual Themes

Contents

Overview

The 11g release of the IDE framework introduces a themes engine. The themes engine makes it easy to alter the color scheme and visual design of a product based on the IDE framework without editing code.

Editing and assembling a theme is very straightforward, and primarily involves modifying text and image files, and assembling them into a jar which can be deployed directly into a product based on the IDE framework.

The focus of this document is on theme providers - those who are defining the visual characteristics of the product by creating or modifying theme definitions. A separate document, Implementing Visual Themes is of interest to extension developers who want to use the theme engine to allow the visual design of their components to be customized. Note: this document is not yet available in the JDeveloper Technology Preview.

Note on Availability: The themes engine is not available in the version of the IDE framework used as the basis for Oracle SQL Developer 1.0 or 1.1. The earliest versions of Oracle products which support the themes engine are:

Product Earliest Version Supported
JDeveloper Oracle JDeveloper 11g Technology Preview (11.1.1)
SQL Developer Oracle SQL Developer 2.0 (Not yet available)

You can determine whether a product based on the Oracle IDE Platform supports themes by looking for the directory ide/themes in its installation directory.

The theme engine and the syntax of themes.properties may undergo significant changes in the future as additional requirements evolve. There are therefore no guarantees that a theme which works in one version of an IDE-based product will continue to work in a future version of that product. Likewise, there's no guarantee that themes will be portable across products (e.g. that a theme for SQL Developer will work in JDeveloper).

Anatomy of a Theme

A theme is defined in a file using the Java Properties syntax called theme.properties. The themes engine provides access to information in this file to extension writers and components in the core IDE platform, and this information is used to render the visual appearance of components.

Most of the property keys in theme.properties have a dot separated hierarchy. This section explains some of the keys which may be used in this file.

Name

Every theme must have a name property, which defines the user-visible name of the theme as presented in the Themes drop down list on the Environment page in preferences. For example, here is a fragment from the fusion blue theme:

name=Fusion Blue (Default)

Every theme should have a unique name.

Color Scheme

A theme can control the look and feel color scheme used throughout the product by defining properties under the colors hierarchy.

The sub keys of colors correspond to values on the javax.swing.plaf.metal.MetalTheme API. You can take any method described in the javadoc for this class, remove the leading "get", convert the next letter to lowercase, and then use this as a subkey of colors to define that color value in the theme.

Here are some examples:

colors.control=#a0a0a0
colors.menuDisabledForeground=#505050

Parts and States

Most of the properties in theme.properties are concerned with the visual appearance of parts. A part is an area of the user interface which has been exposed to the theme engine by the IDE platform or an extension writer.

The term "part" is deliberately used instead of "component", since component has a very specific meaning in Swing / AWT applications, and the theme engine does not necessarily require a one-to-one correspondence between parts and components. Sometimes, a single component may be made up of multiple parts from the point of view of the theme. A good example of this is a scrollbar, which is made up of several parts such as the track, the thumb, the gripper, and the scroll buttons.

Typically, the appearance of a part changes depending on some state that it has. For example, a button part might have a normal state, a disabled state, a mouseover state, and a pressed state. The themes engine doesn't define specific parts and states - it can flexibly support whatever parts and states are required by the implementation.

By convention, keys other than the color scheme and the name in theme.properties follow this pattern:

part.state.propertyname

Here's an example which specifies that a part called button should be painted with a solid black background when disabled:

button.disabled.bg.type=matte
button.disabled.bg.color=#000000

Painters

Painters are an important feature of the themes engine. Parts are often drawn using painters. Theme providers can customize the appearance of parts by describing which painters to use for parts in various states.

A painter is specified by setting the a type sub property to one of the six permitted types described in Painter Types. Many of the painters require additional subproperties that define how the painter operates.

This section describes each of the painters and the properties they require.

Empty Painter (empty)

Required Properties:

None

Description:

The empty painter paints nothing at all. This can be useful when you want to make the background of a component invisible (transparent) in a particular state. A good example of this is a toolbar button when the mouse is not hovering over it.

Here's an example that sets the bg painter for the normal state of the button part to the empty painter:

button.normal.bg.type=empty

Centered Image Painter (center)

Required Properties:

NameTypeDescription
image ThemeImage An image to paint

Description:

The center painter paints an image in the center of the part. It does not scale the image at all. If the image is larger than the current size of the part, then nothing is painted.

Example:

button.normal.bg.type=center
button.normal.bg.image=mybuttonimage.png

Safely Stretched Image Painter (stretch)

Required Properties:

NameTypeDescription
image ThemeImage An image to paint
sizingmargin ThemeInsets Describes the area of the image which may safely be stretched.

Description:

The stretch painter is a powerful painter that paints an image stretched to fit the area of the part. Rather than just crudely stretching the image uniformly across the whole area of the part, the sizingmargin insets are used to define the corners of the image (which are not stretched at all), and the top, left, bottom, and right edges (which are stretched only in one direction: horizontally for the top and bottom, and vertically for the left and right).

This stretching method allows the image to be scaled up to whatever size the part happens to be with a minimum of distortion.

The following diagram describes how to calculate the sizing margins for an image. Generally, it's advisable to make the sizing margins as small as possible while ensuring that they completely capture the corners which must not be resized.

image:Sizingmargins.png

Example:

button.normal.bg.type=stretch
button.normal.bg.image=mybuttonimage.png
button.normal.bg.sizingmargin=2 2 2 2

Gradient Painter (gradient)

Required Properties:

NameTypeDescription
startcolor ThemeColor The start color for the gradient.
endcolor ThemeColor The end color for the gradient.
direction ThemeDirection The direction to paint the gradient (horizontal or vertical).

Description:

The gradient painter paints a linear gradient starting at the top of the part or the left of the part depending on whether the direction is vertical or horizontal respectively. The gradient will transition from the startcolor to the endcolor

Example:

button.normal.bg.type=gradient
button.normal.bg.startcolor=#ff0000
button.normal.bg.endcolor=#ffffff
button.normal.bg.direction=vertical

Matte Painter (matte)

Required Properties:

NameTypeDescription
color ThemeColor The fill color to use.

Description:

The matte painter fills the entire content area of the part with a single, solid color, defined by the color property value.

Example:

button.normal.bg.type=matte
button.normal.bg.color=#ff0000

Edge Painter (edge)

Required Properties:

NameTypeDescription
image ThemeImage The image to paint
edge ThemeEdge The edge of the part along which to stretch the painted image.

Description:

The edge painter draws an image along a single edge of the part, stretching it horizontally if the top or bottom edge is specified, or vertically if the left or right edge is specified.

Example:

button.normal.bg.type=edge
button.normal.bg.image=myimage.png
button.normal.bg.edge=left

Value Types

The themes engine recognizes several value types, which are particular expressions that can appear as the value of a property in theme.properties. The syntax of value types is described in this document using grammar notation similar to that introduced in §2.4 of the Java Language Specification.

Colors

A ThemeColor value is specified as:

 LookAndFeelColorName:
       any symbolic color name in the look and feel UI defaults table
 ThemeColor:
       ui: LookAndFeelColorName
       DecodableString

Where DecodableString is defined in the javadoc for Integer.decode(String).

Some examples of valid color values are:

# The color red
firstcolor=#ff0000

# Whatever the UI defaults table defines as the window background color ("window")
secondcolor=ui:window

Insets

A ThemeInsets value is specified as:

 Space:
      ' ' Spaceopt
 TopPixels:
      Digits
 LeftPixels:
      Digits
 BottomPixels:
      Digits
 RightPixels:
      Digits
 ThemeInsets:
      TopPixels 
      TopPixels Space LeftPixels
      TopPixels Space LeftPixels Space BottomPixels
      TopPixels Space LeftPixels Space BottomPixels Space RightPixels

Where Digits is defined in §3.10.1 of the Java Language Specification.

Any omitted inset component is defaulted to 0.

Some examples of valid theme insets are:

zeroinsets=0 0 0 0
eveninsets=2 4 6 8
twoabove=2

Borders

A ThemeBorder value is specified as:

 BorderWidth:
       thin
       Digits px
 BorderStyle:
       solid
       none
       hidden
 BorderColor:
       DecodableString
 ThemeBorder:
       BorderWidth
       BorderWidth Space BorderStyle
       BorderWidth Space BorderStyle Space BorderColor

A thin BorderWidth is equivalent to 1px, i.e. a one pixel thick border.

The default BorderStyle if not specified is solid. A none style border has no width (regardless of the BorderWidth value). A hidden style border reserves the space specified by BorderWidth, but is invisible (regardless of the color specified by the BorderColor value.

Some examples of valid borders are:

# A simple 1 pixel wide black border
firstborder=thin

# A solid 5 pixel red border
secondborder=5px solid #ff0000

# An invisible 2 pixel border
thirdborder=2px hidden

Fonts

Unlike other properties in theme.properties, fonts are usually derived from existing fonts provided by the look and feel. Normatively, a derived font is specified by property keys which end with font-weight and font-size.

A ThemeFontWeight value is specified as:

 ThemeFontWeight:
       normal
       bold

A ThemeFontSize value is specified as:

 ThemeFontSize:
       Digits pt
       Digits %

A ThemeFontSize using a pt value specifies an exact point value to use for the font size.

The default values for these properties retain the existing weight and size of the base font.

By contrast a percentage value specified using % will derive the font size from the size of the original font. If the original font was 9pt, a derived font size specified as 150% would be 1.5 x 9pt = 13.5pt.

Some examples of valid font properties:

# A bold font at 2pt.
myfont.font-weight=bold
myfont.font-size=12pt

# A font at 50% of its base size, retaining the weight of the base font.
myfont.font-size=50%

Images

A ThemeImage value is specified as:

 ResourceChar:
       any character that is valid as a classpath resource name
 ResourceName:
       ResourceChar ResourceNameopt
 ThemeImage:
       ResourceName

A ThemeImage value specifies the location of an image file. The value is interpreted as a resource path. It is appended to a string containing the value of the global imagebase property and resolved using ClassLoader.getResource(String). Themes are loaded in a special classloading environment that does not necessarily have visibility to images outside the theme jar; see Classloading for details.

An example of a valid ThemeImage:

mybackground=basic.png

Direction

A ThemeDirection is specified as:

 ThemeDirection:
       horizontal
       vertical

The ThemeDirection type is currently used only by the gradient painter.

Edge

A ThemeEdge is specified as:

 ThemeEdge:
       top
       left
       bottom
       right

The ThemeEdge type is currently used only by the edge painter.

 

Painter Types

Painters describe compound operations that are used to graphically render a component. The themes engine defines several painter types. See Painters for more information.

A ThemePainterType is specified as:

 ThemePainterType:
       empty
       stretch
       center
       gradient
       matte
       edge

Assembly and Deployment

A theme is packaged as a jar file with a simple structure. The theme.properties file must be located within the META-INF directory of the jar. The jar should contain any image resources referenced by image property values in theme.properties.

Here is an example of the contents of a simple theme jar:

mytheme.jar/
+- META-INF/
   +- theme.properties
+- org/
   +- acme/
      +- images/
         +- tb.png
         +- cb.png

You can use the jar tool to assemble a theme jar as follows. First change into a directory matching the structure shown above for the contents of the theme jar, then type:

jar cf mytheme.jar *

To deploy a theme, simply copy the theme jar into the ide/themes directory of a supported product. The theme will appear on the Environment page in Tools-Preferences after you restart the product.

Classloading

When the IDE starts, it will look for a jar file in ide/themes corresponding to the currently selected theme. The IDE stores the name of the theme jar in persistent preferences (so it's a good idea not to rename a theme once it has shipped). If it successfully locates the theme, it creates an isolated classloading environment containing just that single theme jar, then loads the theme properties file and initializes the look and feel of the product based on that theme.

The ide will also scan the other jar files in the ide/themes directory in order to populate the "Theme" drop down list on the Environment page in Tools->Preferences.

For theme writers, the primary repercussion of the way themes are loaded is that they cannot refer to images outside the theme jar. A theme jar must be completely self contained.

The Fusion Blue Theme

JDeveloper 11g ships with a default theme called "Fusion Blue". The easiest way to start creating your own theme is to copy the contents of the Fusion Blue theme and customize it. You can use the jar tool or a zip utility such as WinZip to extract the contents of the theme jar, which can be found in ide/themes/fusionblue.jar. This jar contains all of the images and the theme.properties file used by JDeveloper 11g out of the box.

If you create a theme based on Fusion Blue, remember to change its name to something else.