2006-04-26

Two "new" UI mistakes

As GBS (or was it Sting?) said, "we learn from history that we learn nothing from history".

When I first read about Microsoft's upcoming Vista and its new window manager, I was surprised to see mention of semi-transparent title bars. Surprised because earlier versions of Mac OS had this feature – one which seems like a reasonable idea until you try it – but newer versions of Mac OS have removed it, first partially (by making the title bar less transparent), and now completely (by making the title bar fully opaque again).

When Apple last year hubristically suggested that Redmond should "start their photocopiers", I doubt they expected Microsoft to still be catching up with ideas that have since been rejected.

Anyway, this misfeature seems to have survived into current Vista betas, and at least one reviewer hates it, and hates it for one of the reasons it didn't work on Mac OS. (There's irony in the linked-to author's anti-Apple jibe suggesting that "Apple can add stained glass windows to the next version of Mac OS X in response"; one can only assume that he, like Microsoft's developers, doesn't know that Apple already implemented and removed transparent title bars.)

If they're interested, the other reason why you realize that semi-transparent title bars suck when you comes to use them is that not only do they make it more difficult to recognize which window has the focus, they also make it more difficult to read the title bars because of the interference from everything beneath. This might be an even worse problem on Windows, where users tend to have all their windows maximized (and hence all their title bar text overlapping), compared to Mac OS users whose desktops tend to be far more random, reducing the chances of direct collision.

The cynic in me wonders if Microsoft doesn't see transparent title bars as a deliberate insurance policy to give users some reason to upgrade to whatever comes after Vista.

From the screenshots I've seen of Vista, Microsoft seem to have learned one important lesson from Apple, and it's a lesson Ubuntu need to learn. In Vista, the system tray (the bottom-right corner of the screen that fills up with icons for all the useless crapware that's installed on a Windows machine because some idiot developers seem to think that all users need quick access to such vital everyday features as screen rotation and virus scanning settings and mouse acceleration configuration and any number of other things that any normal human sets exactly once each time they install an OS) seems to have switched to silhouette icons rather than the current unintelligible low-res low-color (but gaudy) splodges we're used to from earlier versions of Windows.

Doubtless it will take years for the crapware vendors to follow Microsoft's lead – I'm pretty sure Microsoft explicitly says that most of their junk shouldn't sully the system tray at all – but it's a step in the right direction.

Ubuntu, though, needs to look at Mac OS' default "user" icon (a silhouette that's sufficiently abstract to be a generic carbon unit) and think hard about whether their default user icon – a black guy with a beard wearing a suit, in front of an old white woman in a baggy green sweater – is such a great idea. Those people are very much "not me" and very much "not users of my computer". Worse, there's a variant that's just the guy, that gdm(1) happily shows next to female usernames.

(Aren't there cultures that find beards and other facial hair offensive? I know I find the imputation that I might wear a beard pretty offensive.)

You can see the ugly beard icon in the "log out" dialog of Ubuntu 6.06 beta, by "Switch User". The suggested "tango" replacement is somewhat better, but still looks far too much like real people by giving them skin colors. The icon on the user-switching menu in A Look at GNOME 2.14 is infinitely preferable, and as asbtract as the little runner on the emergency exit signs in cinemas.

But what can you expect from a desktop environment whose default background is a big brown lake of shit? Unlike the beard icon, which I'm currently suffering in Debian, the "shit brown" leitmotif is Ubuntu's fault rather than GNOME's, which appears to be 100% skidmark-free. As an interesting experiment, you can check out user-submitted GNOME 2.14 screenshots and count how many people choose to have pleasing green or blue backgrounds compared to how many feature "oh no, I've crapped my pants" brown.

Yeah, I know: if I were doing work I wouldn't be able to see the desktop...

2006-04-25

Microsoft's snappily-named "Wheel Mouse Optical USB" ousts my "Mighty Mouse"

My girlfriend hates Apple's Mighty Mouse. While I merely disliked it enough to relegate it to the desktop I use less, she – perhaps because most days she spends more time on that machine than I do – regularly complained that it kept popping up a menu when she was just trying to click on a link.

Apple's idea sounded pretty silly to most geeks. Making a mouse that looks as if it has one button, but actually kind of has two, but where the buttons only work if you hold the mouse exactly right? At least geeks experience mild curiousity about how it works. Now imagine how Apple's idea sounded to a normal human. A mouse that looks like one thing, tries to be another, and doesn't really work?

"That's retarded".

Indeed. So the other day we finally went to Fry's and got a couple of Microsoft's cheapo black USB optical mice. Two because I'm anal enough to want identical mice.

The interesting thing is that one of the two supposedly identical mice feels much better than the other, and the one that feels better has a large enough gap between the top and bottom halves that light shines through. I guess that's the kind of build quality you get in a $15 mouse. The behavior if you have a habit, as I do, of resting the mouse with its left edge in the air is poor; lift it far enough and the pointer jumps to the right. And although the front light dims if you lift it away from a surface, the seemingly pointless rear light is on all the time, and very bright. I'm sure the whole black-with-red-glow thing looks cool in Bill's evil undersea headquarters, but I'm not sure what I think of it in my home.

Despite all this, we both much prefer these mice to Apple's ill-concieved Mighty Mouse. Which tells you all you need to know about the Mighty Mouse.

2006-04-24

The GTK LAF in Java 6

Up to and including Java 5, the GTK LAF was pretty much unusable. In my applications, initialization code would use the system LAF unless it was GTK, in which case it would use the Metal LAF in preference. The GTK LAF was so bad it made your application look worse than using the Metal LAF would. The Metal LAF managed to look less out of place on a GNOME desktop.

In Java 6, though, the GTK LAF has made the leap. There are lots of bugs still, but for the first time it's looking like a solveable problem.

I've got a big list of bugs, and I keep meaning to file them on the bug parade, but some of them are so blindingly obvious that I've been wondering what I can do to help fix the deeper problem. My answer is a Java imitation of Richard Stellingwerff's The Widget Factory. Here's a picture of the GTK+ original running on my Ubuntu 5.10 box:



And here's a picture of my JavaWidgetFactory running under Java 6 b81 on the same machine:



I won't show you Java 5. It's too sad.

I've committed JavaWidgetFactory to salma-hayek. There are a few limitations in my translation. Vertical JSeparators don't appear to work, so I haven't used them. Swing's progress bars and sliders don't let you configure them the wrong way round, so they don't appear in pairs in the Java version. I haven't tied the models because I couldn't be bothered. I didn't try to implement the titled borders because I don't think they're part of the LAF. I didn't get round to adding the menu or the toolbar. I didn't bother imitating the non-default column widths or getting any of the layout particularly close because it didn't seem to make much difference for the purpose I had in mind.

I also duplicated the flaw in the original that means the table is empty, so you can't see what rendered rows look like (Java really screws this up as up to and including Java 6 b81). Given the likely use of JavaWidgetFactory, I should probably have automatically opened a FatBits window, rather than expecting the user to run it themselves.

If you look closely at the pictures, you'll notice a number of bugs:

  • All four corners of the editable combo box are wrong. The left corners should be rounded rather than "snapped", and the right corners should join into the button (which shouldn't be rounded on its left edge).

  • The focus ring on the editable text components is wrong, but you can't see that from the screenshots.

  • The spinner's buttons are too narrow, and the right edge of the text area should be flush with the left edge of the button area. (The button area's edge is correct, so it looks like they're working on this.) [Fixed in 1.6.0-b82.]

  • The enabled toggle button's rollover color is wrong (it should be a solid color rather than the standard button rollover gradient), but you can't see that from the screenshots.

  • All disabled text components/subcomponents have the wrong border and background.

  • The non-editable combo box is completely wrong. The wrong icon is used, there should be just a subtle groove between the text and the icon, and the whole thing should be rendered in a button-like style.

  • The preferred size of JProgressBar doesn't match its native equivalent.

  • JSlider doesn't render the two sides of the slider in different colors like its native equivalent.

  • JSlider always has a text label, unlike its native equivalent.

  • JTable's header is rendered incorrectly, seemingly using the button renderer.

  • JTable's rows are rendered incorrectly, but you can't see that from the screenshots.

  • JScrollPane doesn't contain a table in the GTK way; the scrollbar should fill the top right corner.

  • JScrollPane doesn't seem to render unneeded scrollbars correctly, though GNOME seems to prescribe as-needed behavior, so this is perhaps an application error rather than a LAF problem.

  • JTabbedPane's tabs are slightly wrong. There's a single-pixel glitch at the start of each row of tabs, there are two dark gray lines between tabs rather than one, there are white lines on edges that shouldn't have them (at most one edge on a tab should have a white line; Java has one or two too many, depending on orientation).



There are a variety of problems with the various Swing dialogs, too, but they're outside the scope of this test. (The color chooser is really close, and only really spoiled by its buttons.)

Although the list above might seem very long, only three are really glaring even to the casual observer. The disabled text components/subcomponents having the wrong border and background, the JTable header renderer, and the non-editable JComboBox are the big three, I reckon, though obviously that depends on your particular program. FatBits, for example, doesn't use disabled text components, tables, or non-editable combo boxes, but it does use JSlider, so it's only affected by the JSlider problems.

Anyway, I hope that JavaWidgetFactory will be useful to people working on the GTK LAF, and maybe to people writing their own LAFs (the original GTK+ version was meant to help people writing GTK+ themes).

I've submitted Sun bug 6417110 for all the bugs mentioned above, and the other bugs I'd been meaning to report, and the dialog bugs too.

2006-04-01

A lesson about using env(1) in script #! lines

If you've read the "perlrun" man page, or you've just read a lot of shell scripts, you'll surely have come across "the env trick". The idea is that instead of using a hard-coded path to the interpreter, you use a hard-coded path to env(1) and let it find the interpreter on the user's path instead.

The supposed advantage of using the env trick (and the newer your scripting language is, the more useful the trick) is that although popular scripting languages gravitate towards /usr/bin/, they tend to start off in such lowly places as ~/bin/ and /usr/local/bin/. env(1), on the other hand, is as old as the hills, and (though POSIX doesn't guarantee it), it's always /usr/bin/env (except on systems you don't care about, according to '#!'-magic, details about the shebang mechanism which has only OpenServer 5.0.6 and Unicos 9.0.2 as problem cases).

So by using env(1), you make your script more portable, and you also make it possible for users to run their own versions of the interpreter by modifying their path.

So how come the env trick didn't take the world by storm? I've seen more scripts that didn't use it than ones that did. And in the past that's always been reason enough not to use the trick. "There must be something wrong with it, so I won't use it either, even if I don't know what's wrong."

Recently software.jessies.org moved server, and in doing so moved from Linux to Solaris. This meant, as you'd expect, some modification of our build system. Solaris has rather old-fashioned versions of various utilities, and even if you don't need any fancy modern features, you can forget about using options with meaningful names. Other than switching from a few long-name options to POSIX options, though, the main problem was that all our Ruby scripts expected Ruby to be in /usr/bin/, as it is on Cygwin, Linux, and Mac OS. Solaris doesn't ship with Ruby, and on our server it was installed in /usr/local/bin/.

Why not just put Ruby in /usr/bin/ on the Solaris box? Well, it's the usual conundrum you face when you combine the notions "cross-platform" and "consistency". You can either be consistent across the platforms, or consistent with each individual platform, and either choice is perfectly reasonable, but the two choices are often mutually exclusive. Here, if you modify your Solaris installation, you're more comfortable because it's then like Cygwin, Linux, and Mac OS, but you haven't really solved your original problem: you still don't work on Solaris. There's no "right" answer, and both alternatives suck in different ways. Had it been my server, I'd have gone for consistency with Linux and Mac OS. The guy whose server it is, though, went for consistency with Solaris.

So, we needed to do something. At this point, we thought the env trick would come to our rescue. We could think of two possible negatives:

1. Performance. There's going to be some tiny overhead in calling env(1) and searching the path, but our scripts don't get executed often enough for that to be a concern.

2. Security/Reproduceability. Using env(1) gives away a little bit of control over what executes your scripts. It's hard to see that relying on the exact interpreter isn't a mistake anyway, and it's certainly not something we were concerned about. What might have caused us trouble, though, is the potential reproduceability problems when you realize that you were testing with a different version of the interpreter than was being used in production.

Not caring about those, we switched to using env(1). So where we'd had:

#!/usr/bin/ruby -w

We now had:

#!/usr/bin/env ruby -w

Solaris was fixed, and Mac OS worked fine. Linux, though, was now broken:

/usr/bin/env: ruby -w: No such file or directory.

If you look at what POSIX has to say, or you look at the GNU info page, it's clear that env(1) supports multiple arguments. The problem is that env(1) isn't being passed multiple arguments, at least not on Linux. Linux's binfmt_script.c contains Linux's code for interpreting #! lines, and as you can see, it only allows a single argument after the interpreter name. So if your "interpreter" is env(1), you're going to have "ruby -w" as your single argument. Mac OS 10.3 (but not 10.4) has a similar problem.

[If you want to know almost everything about how #! behaves on different platforms, have a look at #! - the Unix truth as far as I know it by Andries Brouwer. The only thing it seems to be missing is the fact that POSIX doesn't even guarantee that #! does anything (though I don't know of a contemporary Unix where it isn't supported), and Mac OS seems a strange omission, even for 2002.]

So that's not going to work.

3. Passing arguments to the interpreter via env(1) is not portable. In particular, all versions of Linux and older versions of Mac OS are broken. And this is an ancient design decision that's unlikely to change, like the one that often forbids calling an interpreter that's itself a script from a #! line. And even if it were fixed today, there are a lot of machines out there that would still be broken, many of which will remain broken until the day they're unplugged from the wall.

We could try to get away with calling Ruby with no arguments. The $VERBOSE variable (along with the $-v and $-w variables) are all bound to the same switch inside the implementation that the -w command-line argument affects. So you could write:

#!/usr/bin/env ruby
$-w = true

But that doesn't mean the same thing, despite what I said above about it being bound to the same C variable in the implementation. Why not? Because by the time Ruby gets round to assigning true to $-w, it's already done a lot of work. In particular, it's compiled your entire script with the -w flag off. Which is almost certainly not what you wanted.

You could try switching to Python. I had expected that, unlike Perl and Ruby, which seem to quite like the idea that they might get to watch you shoot your foot off, Python might get more enjoyment out of preventing you from shooting your foot off. But even there, as far as I can tell, warnings are optional and have to be turned on. (Diagnostics is one area where Ruby is significantly worse than Perl. Not only do syntax errors refer to the lexical analyzer's implementation rather than sticking to user-visible terminology, talking about kEND instead of "end", for example, but Ruby has nothing corresponding to Perl's very useful "Name "handel" used only once: possible typo" error checking. Because obviously every single script needs to add methods and fields at run-time, so every script should pay to use that feature. Stroustrup needs to give scripting language authors a good hard beating with his cluestick. Trouble is, they'd probably enjoy it.)

So for now we've gone back to hard-coding /usr/bin/ruby, and we've put specific changes in place to provide the scripts as input to Ruby on Solaris, rather than rely on being able to execute them directly. Sad, but good enough. I guess the only real solutions would be to either find a scripting language where warnings are on by default, or rewrite the scripts on installation to contain a suitable path. The latter choice being of little use when you mainly run directly out of a checked-out repository and don't go through any kind of installation phase, as is the case for us.