Making visually stable UIs
I’m pretty sure you’ve seen user interfaces that look something like this:
Let’s ignore for the moment that progress bar would be a better fit for this particular application.
Are you equally annoyed as me about the text after the
percentage indicator moving on every increment? Great, in this post,
I’ll explain this one weird trick two simple tricks to ensure that the text following the
percentage stays locked in place. If you find this okay, don’t bother
reading on and keep making annoying UIs.
We can break down the unwanted jitter of the trailing text into two distinct phenomena:
- Not all figures have equal width
- 10 has one more digit than 9
Let’s deal with them one after the other.
Making all figures equal in width
Fonts used in user interfaces these days are usual proportional, i.e.
every letter takes up as much space as the font author intended so an
m
is wider than an i
. In some fonts, this extends to figures, so a
5
is wider than a 1
. While this may look better in context if the
number isn’t changing this however is one cause for the text wobbling.
Since this an issue for tables as well where digits should line up
regardless of their value, fonts may have proportional and tabular
figures. As the name tells, tabular figures are all the same width, so
a 1
takes up the same space as a 5
. To make use of this an other
typographic details such as kerning, the OpenType font format supports
so-called features that allow the user to pick a particular style
suiting their needs. The one of interest for our use case of course is
the number spacing.
One might think, that such OpenType features are
only supported by desktop publishing software and other
graphics-related applications, but the Gtk’s text rendering stack
supports OpenType font features out-of the box. So all we have to do to
fix the wobbly text is to set the tnum 1
font feature. The code to to
this is a bit unwieldy unfortunately as that part of the API isn’t
supported by Gtkmm so we need to fall back to C:
void label_set_tnum(Gtk::Label *la)
{
auto attributes_list = pango_attr_list_new();
auto attribute_font_features = pango_attr_font_features_new("tnum 1");
pango_attr_list_insert(attributes_list, attribute_font_features);
gtk_label_set_attributes(la->gobj(), attributes_list);
pango_attr_list_unref(attributes_list);
}
Similar code is used to set the attribute on numerical entries and treeviews. Other modern UI frameworks such as WPF support this as well, so no excuse for wobbly text there. If you’re stuck with QT (which to my astonishment doesn’t support OpenType features) or plain old win32, the best advice I have is to pick a font that uses tabular figures by default.
With this fixed, this is what our example from the beginning looks like:
Making the number’s width constant
The only thing to make the trailing text in our example rock-solid is
to make sure that 1
and 10
take up amount equal amounts of space. A
knee-jerk reaction might be to use your standard libraries facilities
for padding numbers with spaces to a constant string length, so
let’s see what this gets us:
Well, not all that better. Not too surprising given that a regular space is
less wide than a digit. Fortunately Unicode got us covered and provides
U+2007 FIGURE SPACE
which is supposed to be same width as tabular
digit. After applying this to our example we’ve finally achieved visual
perfection:
Everything is locked in place as it should have always been!
Bonus: negative numbers
Suppose we’re writing some sort of CAD application and would like to add a coordinate readout. Taking what we know from above into consideration, this is what we might get:
Oh no! The text moves slightly when the readout transitions from
negative to positive since a regular ASCII -
takes up less space than a
+
. Once more, Unicode to the rescue: U+2212 MINUS SIGN
is the same
with as +
. With that fixed, this is what we’re at:
Nice, finally no wobbly text for negative numbers.
Closing remarks
As we’ve discovered, the usual number formatting options available in today’s programming languages are inadequate for properly typesetting changing numbers in a visually acceptable way. This is quite unfortunate as doing the typographically correct thing requires the programmer to recognize that something’s wrong in the first place and then add a non-insignificant amount of code to rectify this issue.
A downside of having gone down the number typesetting rabbithole is that I spot blunders in number formatting almost everywhere I look:
- Digital dashboards in cars
- Clock display on the LED passenger information system used in the Class 430 EMUs
- Spotify’s desktop app
- Big-Dollar EDA software
The entirety of the Gtk by default, despite of it appering to be fixedNot anymore after fixing it myself.- Test equipment
To me at least, improper number typesetting is a sign of shoddy workmanship on the same level as wobbly knobs or poor surface finish.