Matt's Mind

Monday, November 22, 2004

SWT and Swing: Clash of philosophies

Most shoppers don't like to get in car that looks and smells terrible, even if it does have a nice engine.

-- From email discussing SWT vs Swing history


I’ve written about Swing vs SWT before, but for some reason I feel compelled to expand. This is probably because I needed to write a small Swing GUI last week and it once again prodded a sore point. The reason it’s a sore point is that, once upon a time, you wouldn’t have found a more ardent supporter of Swing than me. I started working with Swing in 1997, while it was in the development 0.x releases, and followed it as it went from com.sun.java.swing, tracked the arguments about whether it should be java.swing or javax.swing, and celebrated the final release as part of Java 2.

I know Swing inside and out. I’ve written big applications, and developed Swing application frameworks for esoteric things like integrating copy/paste/drag/drop between tree, table and list views, command integration for dynamically managing commands in menus, popups and toolbars. I know why you need to add components to JFrame.getContentPane (). I feel I can justly claim to know Swing.

And I went from being a Swing booster to a rebel.

Why? The conversion corresponded to a transition in my view on software, from focusing on technology to usability, from abstract to pragmatic when I first started to get feedback by people using my applications. I started to realize that excuses don’t cut it: if I want people to like and use my software then it needs to Just Work.

The software and frameworks I wrote back then were like Swing, big and intricate. Everything was abstracted to the nth degree. And they were so complex that, after a while, even I had trouble extending, debugging and using them. At the time I, arrogantly perhaps, assumed that the others having trouble with the software I wrote were just less smart and would “get it” eventually and realize the wisdom of my approach.

This is the reason why Swing failed to win the hearts and minds of developers: it was written by people with this mindset. It’s powerful and abstract. It has a complex inheritance hierarchy and an even more complex pluggable L&F system. And it’s hard to learn, hard to extend. It is consistent rather than intuitive. It obsessively uses models for everything, even buttons. And it tries to avoid any special cases so, for example, if you want a set of radio buttons to behave normally (ie have only one selected), you need to know to create a ButtonGroup and add the buttons to it, otherwise they'll just look like radio buttons. Because having radio buttons in the same panel automatically exclusive would be a special case and wouldn’t fit the clean Swing model.

And Swing often doesn't provide things that every GUI app is going to need: eg if you want to add a context menu then, up until Java 5.0, you had to create your own right-mouse-click listener and add the special right-click selection behavior expected in trees and tables yourself. This means that, until recently, every Swing programmer had to reinvent the same context menu logic, including having to ensure the menu doesn’t go off screen. Not to mention the lack of a font selection dialog box or spinner (introduced years late in 1.4).

Swing also rates platform integration as a very low priority. It even seems to go out of its way to make it harder to add platform-specific extensions: one of the things that finally convinced me to ditch Swing was when Sun gratuituously removed the last sane way to get a window handle on Windows in JDK 1.4.1. There was no technical reason, since I can see from the source code that it still uses the same way internally to get the handle. The alternative Sun provided, the JAWT library, was designed to allow native plugins and explicitly stops you getting a handle for anything but a Canvas, so I had to use a horrible hack and create an invisible Canvas in the frame and then walk up to the parent frame using native code. And after I got that working it turned out that the JRE didn’t actually put the JAWT DLL in the path, so I got linker errors when deploying: it was clear the Sun guys didn’t actually use any of this stuff themselves.

Many of the irritating things about Swing stem from the fact that it tries live in its own world: the lack of native anti-aliasing (ClearType or otherwise) stems from the fact that Java2D doesn’t integrate with the platform’s font renderer; the wrong default font probably stems from the fact that Java2D has it’s own TrueType renderer that doesn’t handle Tahoma properly; the sticky menus are due to the fact that it doesn’t support mouse capture; missing display effects like shadows, wrong borders because it’s just too hard to rewrite all this stuff. Collectively they induce a feeling of no confidence.

This seems to me to be a case of some overly zealous Win32 API programmers thinking they could do things a better way. Even though Eclipse looks responsive, IDEA has proven that you can do the same thing with Swing.

I just read that IDEA has anti-aliased fonts using Java2D layer, can you get that with Eclipse?

-- JavaLobby Swing-booster talking about SWT

Here’s the thing that differentiates the new me from the old me, and me from the guy quoted above: I now realize that the people using my software don’t care that I can do anti-aliased fonts. In fact, if my app is using anti-aliased fonts when none of the other apps are, then they may complain that it looks funny. They do care if they’re used to ClearType and the application doesn’t use it, and Swing apps are almost unique on Windows in that they end-run around ClearType: even my old NTEmacs, compiled 2 years before ClearType even existed, happily uses it. Before supporting anti-aliased fonts, Swing needs to get the basics right and do things like actually use the correct default system font on Windows.

Users care about things like menus not going away when they click outside them, or that tooltips appear in all sorts of odd places. They may care that the app uses several MB more of memory. They may not consciously notice that Swing widgets look subtly wrong (eg see WinLAF) and don't support the same shadows, animations, etc as the other apps, but they'll likely feel that your app looks a bit naff regardless. They definitely care that the File Open dialog doesn’t support their Favorites links, or their context menus, thumbnail image view or D&D from the desktop. They will also likely feel that the UI feels slow, even though Swing is way faster than it started out. I still don't why that is, but I experience it myself and I'm convinced it's real.

Programmers on the other hand care that laying out GUI’s with SWT's GridLayout is much easier by hand than GridbagLayout with its 8 or so parameters per layout info object.

Here’s a response further down:
> I don't know about the IDEA hacks, but the standard
> Swing in J2SE still doesn't use the font antialiasing
> feature of Java2D, after so many years..
>

Not true.

Try this to create an antialiased label:

_perspectiveHeaderLabel= new JLabel() {
protected void paintComponent(Graphics g)
{
((Graphics2D) g).setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

super.paintComponent(g);
}
};

-- From JavaLobby
Okay, so I just need to subclass every widget then I can enable anti-aliasing (not sure what you do if you use 3rd party components). So this isn’t useful, but that apparently isn’t what the author thinks is the point.

SWT takes a “make it as simple as possible, but no simpler” approach that I would once have derided. It is simpler than Swing. It is not simpler than it should be. It hits a sweet spot that Swing abandoned in its focus on software engineering geekery.

SWT doesn’t have pluggable table cell renderers or buttons you can put animated graphics on. It doesn’t have three ways to create a window, JFrame, JWindow, and JDialog: it has a Shell. Instead of 20-odd methods with 5+ arguments each on the strangely-named JOptionPane, it has MessageBox, which 95% of the time does what you need. MessageBox doesn't do HTML text, but instead it gets the basics right by making the correct alert sounds and wrapping the message text properly.

SWT has Table and Tree classes that look and feel native (because they are) down to the expansion animation and smooth scrolling. It doesn’t support pluggable renderers, but supports straightforward customization of text, icon and tooltip, which is all the majority apps usually need, without forcing you to deal with the horrors of Swing’s Component-based cell renderer approach.

In short, SWT tries to Get Out Of The Way (tm) and Just Work in the 95% case. And for that 5% case where you need animated graphics in an auto-scrolling TreeTable view: write your own or use one of the custom widgets that SWT already supports. Custom advanced widgets are just as straightforward in SWT as Swing (see some Azureus screenshots for an example). People who don't use SWT often don't realise that it's not limited to native widgets, and in fact has several "emulated" widgets in its default toolkit (eg CLabel and StyledText), it just defaults to native whenever possible rather than rebuilding the world.

OK, so now I feel I’ve got this off my chest. I bought into Swing in a big way and, after years of defending it and believing that Sun would live up to the expectations I had, it was a big deal to admit that I was wrong. Thankfully the designers of SWT seem to have the same philosophy as I do, and I don’t have to trust they’ll get it right eventually: SWT has already got it right enough, and now I can get on with writing the software.