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:
Name | Type | Description |
---|---|---|
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:
Name | Type | Description |
---|---|---|
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.
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:
Name | Type | Description |
---|---|---|
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:
Name | Type | Description |
---|---|---|
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:
Name | Type | Description |
---|---|---|
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.