Recently I had the problem that my ‘well-conceived’ view design did not scale down reasonably. I realized that the flexibility to arrange viewparts in stacks of arbitrary sizes in an Eclipse workbench window does not exactly ease the development of appealing UIs. At least if you strive for more than a set of actions placed around a list, table or a tree.

As luck would have it I also spent some time on responsive web design since we plan an overhauling of codeaffine.com. It was then when I had the idea to ‘rescue’ my UI concept by making it more responsive to size adjustments. As the result looks quite promising I decided to share the experience (and the code as GitHub gist) although the stuff is somewhat experimental .

Setting up the Stage

First you might watch this short video (10 sec) that demonstrates what I mean by ‘responsive to size adjustments’:

The example shows a fictive task management view that is composed of various custom controls. You can see a use case selection, a list-detail representation, and an action bar. At the beginning, these controls are arranged vertically. But as the width expands the alignment changes. The selection bar is shown at the left, the list-detail section in the middle and the action bar moves to the right.

Furthermore, the controls change their appearance gradually. The selection bar displays now a list of the available use cases. The action bar switches the action items to vertical alignment and – as space increases – additionally, shows labels for each item. Finally, the width expansion reaches saturation and from this point, the layout stays fixed.

How is it implemented?

For me, the obvious point to start with were SWT Layouts. But a straightforward solution based on one of the layouts I am aware of seemed difficult. So I tried my luck with a handcrafted one.

The idea was to use a scale of width ranges as a measurement unit for the layout algorithm. All controls are associated with an individual storage for related layout data. The storage allows configuring different settings for each range. Now the algorithm can pick up the appropriate configuration for the actual composite width and layout the controls accordingly .

Given this it seemed natural to call the custom layout ScaleLayout accompanied by an enumeration Scale and a layout data implementation ScaleData . The following code snippet shows how to use the latter:

class ViewContentScaleConfig { private final ScaleData selectorData; private final ScaleData seperatorData; private final ScaleData detailData; [...] void configure() { layoutSuperCompact(); layoutCompact(); [...] } private void layoutCompact() { selectorData.on( Scale.COMPACT ) .setMargin( 5, 3, 0, 0 ); seperatorData.on( Scale.COMPACT ) .setWidth( 1 ).setMargin( 5, 0, 0, 0 ); detailData.on( Scale.COMPACT ) .setMargin( 3, 3, 2, 2 ); } private void layoutSuperCompact() { selectorData.on( Scale.SUPER_COMPACT ) .tie( seperatorData ).setMargin( 3, 0, 3, 0 ); seperatorData.on( Scale.SUPER_COMPACT ) .tie( detailData ).setHeight( 1 ); detailData.on( Scale.SUPER_COMPACT ) .setMargin( 0, 8, 0, 0 ); } }

The ScaleData facilitates a fluent attribute configuration, which I guess is quite self-explanatory. More noteworthy is the tie call which concats layout datas for a given Scale value, because this defines how a ScaleLayout arranges the controls within a certain range.

The Layout Algorithm

Without any ties all controls are aligned seamlessly in a horizontal row, each control taking the available client-area height and its preferred width as size. The last in line grabs the residual width of the client area.

To change this a control can be tied to another. Note that only one tie per Scale and ScaleData is allowed. In fact this organizes the controls in one or more disjointed lists.

Now the list-head-controls will be lined up horizontally. In addition, the controls of a particular list will be arranged subsequently below the head control. This is done in a way that the list controls appears as a column. The width of such a column is determined by the control with the largest preferred width. Height calculations for the different column controls follow the same rules as explained above for widths .

As shown in the example snippet the default size calculations of a control can be overridden by hints. But even with that information, the attentive reader might wonder how this construct with a fixed scale enumeration can possibly work within nested UI compositions.

Scale Provider

The missing piece to make the ScaleLayout actually work is a reference point. The scale has to be related to a certain composite of your UI. Changing the width of this ‘base’ composite might change the placement in the ‘scale’. And this information has to be propagated throughout the composition structure. The root composite of a viewpart is obviously a good choice to serve as reference point.

For this purpose there is a class called ScaleProvider which takes a composite – the reference point – as constructor parameter and a ScaleLayout instance in turn takes a ScaleProvider as constructor parameter. This is how the layout algorithm gets its frame of reference.

Moreover the ScaleProvider offers the possibility of listener registration. This e.g. enables to map the appearance modes of a control to Scale values. The principle is illustrated by the use case selector in the video above. The selector implementation can be switched between two modes: STANDARD and COMPACT . The following snippet shows how the mode changes are triggered by scale change events:

class SelectorModeUpdater implements ScaleListener { private final InfoSelector selector; SelectorModeUpdater( InfoSelector selector ) { this.selector = selector; } public void scaleChanged( ScaleEvent event ) { if( Scale.SUPER_COMPACT == event.getNewScale() ) { selector.setMode( Mode.COMPACT ); } else { selector.setMode( Mode.STANDARD ); } } }

A similar mapping is used to trigger the appearance changes of the action bar.

Conclusion

The responsive UI approach improves the appearance of my view design considerably as it now adopts to different sizes. However, I have to point out that the ScaleLayout has some drawbacks that should be solved before it can serve as a general purpose solution. First, the layout algorithm is probably a bit too simplistic. Second, the fixed scale approach is inflexible and induces a kind of semantic coupling. Third the ScaleData configuration tend to get a bit verbose…

But things may evolve in subsequent iterations and for an inventive soul this might serve as a good inspiration or starting point. And by the way in this regard I am very curious about the solution Holger and Jordi will present for RAP/Tabris in their talk Creating Responsive SWT Applications with RAP at EclipseCon 2014. In particular as they also cover resource handling which I omitted completely in this post.