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:

  1. Not all figures have equal width
  2. 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 fixed Not 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.