September 26th, 2007

Resolution independent Swing, part I – alignment and baseline

I first touched on the subject of resolution-independent Swing UIs and automatic support for high-DPI monitors about six months ago. This area has been pioneered by Karsten in his Looks family of look-and-feels. The code from Looks was used as a starting point of supporting resolution independence in Substance look-and-feel, and the last two versions provided the basic support, including automatic scaling of fonts, insets and other UI elements (such as check boxes, arrow icons, scroll bars and others).

When Mikael Grev has contacted me about two weeks ago about an article that he was going to write about the support for high DPI in his layout manager (MigLayout), he has provided a long-needed push for me to resume working on this part of Substance (thanks, Mikael). So, what was missing?

Quite a lot, actually, and the past two weeks have seen numerous improvements in this area across all the core Swing components. In this part, i’m going to talk about two area, alignment and baseline.

The first support for these two areas under Substance was introduced in this blog entry that showed how text components, combos and spinners have the same height (alignment). In addition, the inner texts are aligned as well (baseline). Here is a screenshot from that entry:

As it turns out, this is not an easy thing to achieve even for a fixed font size. To understand why, you need to understand how comboboxes and spinners are implemented.

  • Text components (JTextField, JFormattedTextField, JPasswordField) are simple – they have a border, insets and the actual document that is displayed. The component bounds are computed from the document size and the insets.
  • Non-editable comboboxes are more complicated. They have border, insets, arrow button and a cell renderer that shows the selected value. The cell renderer by default is a label with insets. The component bounds are computed from the label size, label insets, the combo insets and button bounds.
  • Editable comboboxes have border, insets, arrow button and a text field (editor). The text field also has border (in most cases it would a non-painting one) and insets. The component bounds are computed from the text field document size, text field insets, the combo insets and button bounds.
  • Spinners have border, insets, up button, down button and a text field (editor). The text field also has border (in most cases it would a non-painting one) and insets. The component bounds are computed from the text field document size, text field insets, spinner insets and button bounds.

So, how do you get all these components have the same height and the same text alignment? That’s the black magic of playing with the pixel insets in each one of the UI delegates. To get a small taste of what it feels like to be a look-and-feel developer, see the this source file from the Looks package.

And now imagine taking that implementation and scaling it to arbitrary font sizes (click the image to see more font sizes):

Now that is a whole new challenge – how to compute the insets of all the inner parts of comboboxes and spinners so that they scale with the resolution settings and remain globally aligned. Not to mention that the texts need to be aligned as well – here is a screenshot of three component rows under font sizes 22, 23 and 24 pixels (click to see full-size image):

Stay tuned for the next part when i will talk about scaling other visual parts of Swing UIs. Once again, thanks to Mikael for the challenge :)