From Photoshop to code – step 2, mapping design to UI toolkit
In this entry i’m going to talk about the second step in the process of taking the UI definitions from your designer and converting them to a live implementation. This process is illustrated by taking design of Cookbook UI (from the My Dream App contest) and turning it into a Swing application using Substance look-and-feel. The code behind the process is available in the Substance Samples project that aims to provide a collection of blueprints for creating visually rich UIs in Swing.
Overview of the application areas
In the first step we have identified the decoration and functional areas of Cookbook UI. The screenshot below shows the application decoration areas:
and the next screenshot shows the application functional areas:
As you can see, the application decoration and functional areas are not the same. During this phase we are going to map the application functional areas to Swing container hierarchy and the application decoration areas to Substance decoration areas.
Mapping functional areas to Swing
This phase will in most cases take you from the realm of pure design to the limitations of Swing. While the application functional areas will in most cases define the hierarchy of your Swing containers, the mapping itself might not be straightforward. Designer’s work on the UI mockup screens does not consider the implementation limitations of the specific UI toolkit. This is especially true for such a dynamic area as resizability behavior – what happens when the user resizes the application window (if he is allowed to do so by the application code)?
If the UI design is done in a pixel-based tool (such as Photoshop), the most obvious choice for a Swing implementor would be to use absolute (null) layout manager. This is usually considered a bad Swing practice. Instead, you would usually employ a combination of core, third-party and custom layout managers that handle the initial sizing of the components and the dynamic resizing behavior.
Mapping decoration areas to Substance
This phase will take you further away from the pure design to the limitations of Substance. If you are not familiar with the concept of decoration areas and painters in Substance, please first read the following documentation:
- Decoration painters
<span style="color: darkblue;"><a href="https://substance.dev.java.net/docs/api/SetDecorationType.html">setDecorationType()</a></span>
API.<span style="color: darkblue;"><a href="https://substance.dev.java.net/docs/api/GetDecorationType.html">getDecorationType()</a></span>
API.
The list of available Substance decoration areas is defined in the <span style="color: darkblue;">org.jvnet.substance.painter.decoration.DecorationAreaType</span>
enum.
As the documentation of <span style="color: darkblue;"><a href="https://substance.dev.java.net/docs/api/SetDecorationType.html">setDecorationType()</a></span>
specifies, the passed decoration area type is applied to the component itself and recursively on all its children, unless a child is marked with another decoration area type (with the same API). When is this behavior useful? We’re going to see a more complicated example in a short while, but in the meantime here is a simple one:
This screenshot shows a footer bar of the recipe list panel. As you can see, while the add / remove buttons are using the same colors as the footer bar itself, the text field is skinned with a different color scheme. In Substance API, you would mark the footer bar with <span style="color: darkblue;">FOOTER</span>
and the search text field with <span style="color: darkblue;">NONE</span>
. All the buttons would get the <span style="color: darkblue;">FOOTER</span>
from their parent (footer bar), while the search text field will use the color scheme bundle of the <span style="color: darkblue;">NONE</span>
decoration are type.
While not necessarily required, it is useful to remember that most core (and supported third-party) components are already marked to belong to a specific Substance decoration area. This comes especially handy for the header application area. The header portion of Cookbook UI can be mapped to a decorated title pane (<span style="color: darkblue;">PRIMARY_TITLE_PANE</span>
type) and an unfloatable toolbar (<span style="color: darkblue;">TOOLBAR</span>
type). While not necessarily required, reusing existing Swing components will make the implementation part easier.
What is another usage of using different decoration area types on a parent component and one of its children? The answer is quite simple – when the application functional and decoration areas are different (like is the case with Cookbook UI). The screenshot below shows the sidebar part of Cookbook:
There is a clear visual continuation between the category list and recipe list as far as the decoration areas go. The textures (dark mahogany and golden brush) flow seemlessly across the boundaries of the functional areas, and the top bar with evenly-spaced diffused lights further enforces this continuation. The final polish comes from the vertical separator that uses matching colors in the different decoration areas.
If, for the reasons stated above, you have decided to build your Swing container hierarchy based on the application functional areas, you can use the matching Substance decoration area types on the specific sub-panels (without mixing the different types in the same hierarchy path). However, using the <span style="color: darkblue;"><a href="https://substance.dev.java.net/docs/api/SetDecorationType.html">setDecorationType()</a></span>
API is only one part of the implementation. The second part comes from your custom decoration painter that is implementing the painting itself. While this topic is subject of the next phase, the ease of implementation is going to dictate mixing different decoration area types as shown in the next section.
Swing containers and Substance decoration areas
This section will show the Swing hierarchy behind the Substance-based implementation of Cookbook UI and the matching decoration area types. While most of the reasons behind this implementation are given in the sections above, one of them is part of the next phase (simplifying the implementation complexity of the custom decoration painter).
We’re going to start with the layout of the content pane:
Here, we’re using the core <span style="color: darkblue;">BorderLayout</span>
and add toolbar at <span style="color: darkblue;">NORTH</span>
, sidebar at <span style="color: darkblue;">WEST</span>
and main recipe panel at <span style="color: darkblue;">CENTER</span>
.
The next screenshot shows the children of the sidebar panel:
The top part is the only custom component which is not painted by Substance. This is done for purely implementation complexity considerations – it is much easier to override the <span style="color: darkblue;">paintComponent()</span>
method to emulate the light holder than to do it with Substance decoration painters. The <span style="color: darkblue;">CENTER</span>
part is another panel which is decorated with <span style="color: darkblue;">GENERAL</span>
area type. As the next phase will show, this makes it easier to implement the evenly-spaced diffused lights (the continuous look of the texture is not a factor in this particular decision).
Inside this <span style="color: darkblue;">GENERAL</span>
-decorated panels we have the <span style="color: darkblue;">BorderLayout</span>
with two panels:
Note that these two panels do not have any decoration area type installed on them – they get the <span style="color: darkblue;">GENERAL</span>
from the parent panel.
The last diagram shows the last level of Swing hierarchy as far as mapping the decoration areas goes:
Here, each (functional) panel has a footer bar and the main area. The footer bar is a panel decorated with <span style="color: darkblue;">FOOTER</span>
, thus overriding the decoration area type inherited from the grandparent panel. The main areas do not have any decoration area types installed on them, inheriting the grandparent panel settings.
The final piece comes by installing the <span style="color: darkblue;">NONE</span>
type on the search text field in the second footer bar, thus overriding the inherited <span style="color: darkblue;">FOOTER</span>
setting.