soeren says

Font Book UI craziness

November 3rd, 2007

Today’s ‘What were they thinking?’ query goes to the team developing the UI for Font Book, the font manager that’s been a part of Mac OS X since 10.3. It’s a nice little utility, making it easy to install, activate and deactivate fonts, but also simply to simply browse and preview them – although in 10.5, the Finder’s icon and Cover Flow views have actually become capable of that as well. 10.5 also provides for auto-activation and a few other nifty features graphic designers will love to have standard in the OS. Still, Font Book can’t (and isn’t meant to) replace a professional font manager like FontExplorer X, which Linotype nicely enough has been providing for free, even though they could easily make quite a bit off money with it (like, say, $99.95).

But I digress. Among the functionality provided of Font Book is font validation. This checks a font file for various common errors, such as malformed or incomplete binary streams. As far as I recall, it even kicks in automatically when you try to install a font, warning you ahead that you may not want to do that.

Disappointed with Spotlight performance, I decided to force a re-index earlier (using sudo mdutil -E -a, for “erase index for all volumes, recreating it where appropriate”). mdworker, the background indexing tool, spits quite a few error message as it descends through the hierarchy, letting advanced users know of particular files it had trouble with. Among them:

11/3/07 8:42:15 PM mdworker[65051] FontImporter: Validation failed - "/Users/chucker/Library/Application Support/Linkinus/Styles/Mmm... IRC.lnkStyle/Contents/Resources/Fixedsys500c.ttf".

Ah, a broken font file. From an application I only really tried once, no less. (Mind you, I didn’t find Linkinus to be bad at all, just not sufficiently better compared to Colloquy to justify paying $20.) I could have just deleted the file, but I was curious enough to see the error. So I had it open in Font Book, and it showed me a preview (as did the Finder). Can’t be too broken. In Font Book’s file menu, we have “Validate Font” and “Validate File…”. But, what’s that? They’re both greyed out.

That’s right: it appears that, if you open Font Book through a font file, it won’t let you validate that font.

If, on the other hand, you open Font Book directly, then choose File → Validate File…, you’ll find that you’re suddenly able to select that same file in the Open dialog that appears and have it validated just fine.

The problem with the file, by the way? Indeed, Font Book did find one: “1 serious error” in “‘post’ table usability”. It’s a shame that this doesn’t tell me squat, and I’ve learnt a lot more about a really strange user interface omission or limitation than I have about this particular font. :-)

Posted in Chuckellania, Mac, Software, Usability

Share | No Comments

iPod Split Screen UI

September 13th, 2007

Gruber links to a piece lovingly titled “It’s Official: Apple’s Stupidest Interface Innovation Ever”.

Now, I can’t deny I expected this UI change to be rather controversial when it was first leaked. And I haven’t seen it in person, so I admittedly cannot judge it well. But I think Kirk may be exaggerating just one little bit.

Like others, I think a division of two-thirds/one-third might work a lot better than the current 50-50. But in general, I understand why they went with the split screen (to display just the traditional menu on such a high-resolution screen is so wasteful!), and do think it’s a neat idea. Again, though: I can’t really judge how distracting the (Ken Burns-like?) movement animation feels in real life. Perhaps I’d hate it too.

I just don’t think I would. On the contrary, I feel that cover art, when done well, can be quite pleasant to look at while navigating the menus.

Posted in Chuckellania, Usability, iPod

Share | No Comments

Totem and Codec-Buddy/codeina

March 26th, 2007

Through Planet GNOME, I found this little gem. Totem, GNOME’s bundled media player, has a subproject in development called Codec-Buddy, designed to aid the user in finding and installing missing codecs automatically when trying to play something. codeina, then, actually extends this functionality to also support commercial codec additions, so you can buy additional plug-ins straight through the interface. (At least, this is my understanding of the functionality split between Codec-Buddy as a whole and codeina in particular; correct me if I’m wrong.)

Naturally, there’s the typical whining (anonymously, no less):

As a user, I really don’t like the idea of being suggested to buy *non-free* software from within a *free* media player.

Or:

again and again trying to sell things

…but I think we can all ignore that.

This is a wonderful addition. QuickTime used to have a way to install additional components straight from the interface, but with no association to the actual file, and a rather lacking repository of components. Now, Perian tries to be the be-all-end-all of QuickTime decoding components (with the appropriate slogan “The swiss-army knife for QuickTime™”), but ideally, it would of course be a lot nicer if codecs could get downloaded as they are needed, rather than having to deal with monolithic packages like this – especially since Perian will likely never account for everything, such as Windows Media.

Consider the possibility of QuickTime Player (or, really, any player utilizing QuickTime, including NicePlayer or even iTunes) prompting you whether you’re okay with installing Flip4Mac as soon as you try to play a Windows Media file. Or prompting you whether you’d like to buy Apple’s MPEG-2 component as soon as you try to play from a VIDEO_TS folder. (If automated, this could even work on, gasp, the Apple TV.)

I applaud GNOME in this effort.

Posted in GNOME, OpenSource, Software, Usability

Share | No Comments

Edit conflicts

March 11th, 2007

Among the endless list of UI design problems we still haven’t solved well is collaborative document editing. There are some good solutions, especially as desktop apps, such as in SubEthaEdit. There are solutions that never really work well, such as in Microsoft Office. Finally, especially in web applications, users have to deal with solutions that ‘work’ only in the sense that the software points out problems, but doesn’t put any effort in solving them.

Take BugZilla or MediaWiki: if one person A submits an edited bug or page while another, B, is working on the old version (intending to submit yet another edited version), there will be an obvious discrepancy. Both apps point it out, but neither gives merging the changes a try.

In an ideal world, following the submitting of A’s version, B would be informed while editing (and not after trying to submit!) that A has since posted something new. B would then be shown the differences (MediaWiki already does this), and suggest changes to merge the two pieces of text together. Finally, most importantly, and by far the hardest to implement, A would be invited to work together with B in merging the versions.

This last part is important. If the software (e.g., MediaWiki) can contact a user (A) via, say, Windows Live Messenger and send them a link that lets A and B collaborate on a solution, the usual two risks would become unnecessary:

  1. Either, B would decide that it’s not worth going through A’s changes and conclude that the new version is good enough, even when it actually is not. A might even have appreciated B’s contributions.
  2. Or, B doesn’t feel the need to review what A did and overwrites all changes, submitting their own version anyway.

In either case, the result would be less than ideal. But if A were invited (and available, and accepting the invitation), A and B could collaborate, accomplishing an overall far superior version.

Not impossible at all to implement. But, not easy either.

P.S.: This is totally not a wink-wink-nudge-nudge.

Posted in Software, Usability, Web

Share | No Comments

Exponential Sliders in Cocoa (with Bindings)

January 27th, 2007

When I developed MenuTemperature 1.0, I ran into the following UI design problem: I wanted people to input the frequency of polling the CPU’s temperature, and I wanted to allow for a rather wide range of intervals. Polling twice a second should be just as possible as only polling, say, once every minute.

Three obvious options are there: you could have a text field allowing for numeral values to be input, hardcoded to be treated as seconds (MenuTemperature 1.5, for instance, does this, though it also adds a stepper to enter values more easily). Or you could have a popup menu with several predefined values, e.g. “twice a second”, “every 10 seconds”, “every minute” (for example, Safari does this for its RSS check interval, and Twitterrific for its refresh interval). Or you could even combine the two, so the user can type in “10″, then choose between “seconds”, “minutes” or “hours” (this was briefly considered for MenuTemperature but deemed needlessly complex). And then, there’s the aforementioned combination with a stepper. But, stepper aside, none of those are very “graphical”. Even the stepper doesn’t really give a visual representation of how your currently chosen amount relates to the possible minimum and maximum values; indeed, none of those controls even necessarily define such limits.

Enter the slider: this control has clearly defined and visible limits of a minimum and a maximum; typically the left and right “end”. It has a knob, then, to scroll around and pick something that roughly matches the user’s desired value. It’s imprecise, in fact, but this doesn’t really matter. Who cares if it’s every 30 seconds, or every 31? The user does not. And when it does matter, the slider control even allows for predefined “markers”, which “lock” the value. That is, if the knob is near the marker, the marker’s value will take precedent over the “actually” selected one.

So this is one very simple and flexible control. As of 10.3(?), OS X actually supplies a circular variant that mimics the familiar volume knob on your Hi-Fi, but many argue this to be a bad idea, because you’ll typically interact with the slider using a mouse or trackpad, neither of which are designed for such “circular” input: the behaviour is quite unpredictable. (A few years ago, I had an audio app use such a control for pitch and speed, and I couldn’t figure out for a long time how to actually increase or decrease the value reliably, unless I realized that, even though the “tick” will go around the “knob”, your mouse has to go up and down. The highest value was therefore achieved by going all the way up, even though the “tick” was, then, in the bottom right. Extremely unintuitive. OS X’s implementation makes more sense, in that you do actually make the circular motion, but it’s still rather awkward to use.)

The rationale of the circular slider, beyond familiar with the aforementioned volume knobs, is that it takes up a lot less space. Compared to a linear slider with roughly the same length, a circular slider allows for far more “condensed” precision, i.e. many more values that can be chosen with this method.

I realize, of course, that much of this precision is superfluous. As already said, you don’t really need to be able to select 35 versus 36 seconds; you wouldn’t really notice the different anyway. But you would notice the difference between three or four seconds. In other words, your perception is of a percentual nature. Whereas four seconds take a third longer, the difference between 36 and 35 amounts to less than 3 percents. This is reflected as well in Safari: possible values for the interval of checking for RSS feed updates are 30 minutes, an hour (100% more) and an entire day (4700% more). What user would care to set it to seven hours?

A popup menu is still a suboptimal solution for this, I’d argue. It just wasn’t intended to be used to select values, but for, say, different behavioral modes. Apple’s three choices are rather arbitrary, and while the flexibility would be mostly lost for users, they arguably go overboard with their simplification. (Two hours?)

Going back to the slider control – the linear one – , it would therefore make sense if the values one can input with it reflect the actual needs of users. By the title, you can perhaps see where I’m going with this. Instead of a relationship of realValue(inputValue) = inputValue, a preferable solution would be realValue(inputValue) = einputValue, e being Euler’s number. Internally, then, we would also need to invert the conversion back, i.e. inputValue(realValue) = ln realValue. (I’m sure someone will reprimand me now for sucky math!) This would easily allow for wide ranges of values. With higher inputValues, the realValue would grow very quickly, and with lower ones, the realValue would be far more specific. Win-win.

There’s two considerations here: first, how do we implement this best? And second, seeing as users aren’t very used to such behaviour, how do we make it somewhat intuitive?

Thanks to Cocoa Bindings, the actual implementation is surprisingly simple. In my particular case, it was even simpler as I already was using Bindings for that particular slider. If you’re not, you need to switch to them; you want to anyway. (10.2 and older don’t support Bindings, but I would wager to say that supporting 10.2 users, by now, really costs more money that it brings in now. If you disagree, this post isn’t for you; implementing this without Bindings is certainly possible, but not without significantly more code. Bindings does things for you. Let it!) I’m also only writing about doing this programmatically, but you should figure out the Interface Builder part yourself.

Let’s go!

The first thing you want to do is create a value transformer. Even though this will do the meat of our conversion, it’s an extremely simple class. Here’s the header:

#import <Cocoa/Cocoa.h>

@interface ExponentialValueTransformer : NSValueTransformer {}
@end

And here’s the code:

#import "ExponentialValueTransformer.h"

@implementation ExponentialValueTransformer
+ (Class) transformedValueClass {
	return [NSNumber class];
}

+ (BOOL) allowsReverseTransformation {
	return YES;
}

- (id) transformedValue: (id) value {
	return [NSNumber numberWithFloat:log([value floatValue])];
}

- (id) reverseTransformedValue: (id) value {
	return [NSNumber numberWithFloat:exp([value floatValue])];
}
@end

(Yes, that’s it.)

Fortunately for us, C provides log() and exp(), but naturally, they cannot work with NSNumber objects, only with C floats. No problem, though; we simply cast to a float using floatValue, run the C function on that, and create (and return, and have it autorelease when appropriate) a new NSNumber object.

I'm assuming you use this with your userDefaults, i.e. you wish to store a setting. In that case, transformedValue gets called for pulling the value out of the NSUserDefaults database (more specifically, normally your app's property list file in Library/Preferences), so we need to transform it back to display the slider correctly. (You will typically call log ln instead, or "logarithmus naturalis", or "natural logarithm" for the English-inclined.)

And once the slider gets dragged, reverseTransformedValue stores the new value: "e to the power of x", which, lucky for us, happens to be the exact inverse function. So even though we internally store and use a completely different value, the user will never know; he or she interacts with the direct slider value only.

To hook up the transformer, you will want to register it, typically somewhere in your application delegate ([NSApp delegate]), or at some other part in the code that gets called early enough. I'm using NSApplication's +load method for a number of reasons, and this probably can't hurt either, but it's a little unusual, to say the least. Of course, you should import the header of your new value transformer first:

#import "NearExponentialValueTransformer.h"

Registration, then, looks like this:

	ExponentialValueTransformer * exponentialValueTransformer = [[[ExponentialValueTransformer alloc] init] autorelease];
    [NSValueTransformer setValueTransformer:exponentialValueTransformer forName:@"ExponentialValueTransformer"];

If you're using Interface Builder, you should be able to just create a slider or choose an existing one, bind its value to some key path, and choose this value transformer from a popup menu. Programmatically, it looks something like this:

	NSSlider * refreshSlider = [[NSSlider alloc] initWithFrame:sliderFrame];

	[refreshSlider setMinValue:log(60.0)];
	[refreshSlider setMaxValue:log(3600.0)];

	[refreshSlider bind:@"value" toObject:[NSUserDefaultsController sharedUserDefaultsController]
		withKeyPath:@"values.refreshInterval"
		options:[NSDictionary dictionaryWithObject:@"ExponentialValueTransformer" forKey:NSValueTransformerNameBindingOption]];

	[view addSubview:refreshSlider];

Two gotchas: first, the min and max values above need to be prepended by the log() function, as shown. (You could also put the result of that right into the code, but that's just plain ugly.) And second, I lost a needless amount of time thinking that this argument to options: should also work:

[NSDictionary dictionaryWithObject:@"ExponentialValueTransformer" forKey:NSValueTransformerNameBindingOption]

In some cases, Apple allows both interchangeably; here they didn't, so nothing ever happened because I changed the wrong setting.

Congratulations: you now have a slider which allows the user to get more specific with smaller values, and more wide-ranged with bigger ones. I would wager to say that this is the behaviour many users would like.

Only one thing left: add a text label so the user actually knows what value the slider represents. Fairly simple, though, so I'll leave this up to the reader. This should fix the lack of intuitiveness, especially when you turn on "Continuously send action while sliding"; when in doubt, the user can just look at the bare number instead.

To give you an idea of the range achieved internally compared to the range perceived by the user: above, I set the minValue to ln 60, and the maxValue to ln 3600. That's a big internal range; 3600 is 60 times 60! Yet, for the slider, the values actually only go from about 4.094345 to roughly 8.188689, i.e. only twice that. Nifty!

Posted in Cocoa / Objective-C, Mac, Programming, Usability

Share | No Comments