One of the nice things about RText is how configurable it is. Not only can you change the fonts and colors used in the editor, but you can also change the icon set and LookAndFeel of the application at runtime. This results in a huge amount of flexibility in the appearance of RText, although most people just stick to the defaults (system LookAndFeel, Eclipse icons, Visual Studio-ish editor color scheme), simply because those default settings look nice enough.
Lately though, I’ve been hankering to offer more built-in flexibility on the LookAndFeel side of things. For a long time, RText has shipped with the Office LookAndFeels, however those are only usable on Windows, and even then only look good on Windows XP and earlier (note to self: need to revisit these and tidy them up a little for Vista and 7!). Personally, I’ve been wanting to try out a “dark” LookAndFeel, so I did some hunting…
Turns out one of the skins of the Substance LookAndFeel is dark (“Graphite Glass”) and overall looks quite nice. So I decided to see what it would take to run RText with Substance. I thought it would be straight-forward, due to RText’s ability to load 3rd-parth LAF’s dynamically; however, it was a lot more work than I thought it would be.
First, the latest, greatest Substance release requires 2 jars – the main “substance.jar” and an extra library, “trident.jar”, which seemingly handles the animations in the LAF. RText’s LAF loading was hard-wired to assume a single jar per-LAF, so I had to modify the XML format to allow specifying multiple jars. No biggie.
Next I ran into what appears to be a bug in Trident, though I could be wrong. RText allows dynamic loading of 3rd-party LAFs by dropping them into a specific directory and adding an entry for them in an XML file. The magic behind loading them is done via a custom ClassLoader, which is used when setting a new LookAndFeel at runtime. Initially, I kept getting strange Exceptions about Trident-related stuff not being able to load resources that were in its own jar file. I dug into the source, and it appears that org.pushingpixels.trident.TridentConfig is hard-coded to use Thread.currentThread().getContextClassLoader() to load resources. I found that by changing this to use “getClass().getClassLoader()” instead, things worked beautifully. I’m unsusure of the full repercussions of this change; it may be that it breaks other scenarios, but I’m thinking that the context ClassLoader doesn’t work in situations like mine, where a custom ClassLoader is being used.
The next stumbling block was that, since Substance installs its own RootPaneUI and is capable of providing custom-drawn window decorations, it doesn’t play nicely with runtime LookAndFeel switching. Allowing it to draw window decorations is practically required to get the full effect of the LAF. Unfortunately, this means it doesn’t lend itself well to runtime LAF switching. In fact, as best as I can tell, if you’ve previously run your application with an LAF that doesn’t provide custom window decorations (such as Windows), then try to switch to a LAF that does (such as Substance), you’ll get runtime exceptions. For this reason, when doing a runtime LAF change, RText looks at the current LAF and the proposed one; if neither is Substance, the switch is allowed. If both are Substance (i.e. different skins), the switch is also allowed. But if one is a Substance skin and the other isn’t, the user is informed that the new LookAndFeel will only be installed after a restart of RText.
Once RText would finally run with a Substance skin enabled, a fourth problem emerged – custom components looked terrible. Specifically, RText’s main window has a title panel for its “docked windows,” and a custom tabbed pane UI for painting tabs, and these didn’t look too hot. I dug into the Substance API and found some calls you could make into the currently active SubstanceSkin, that seemed to provide some good colors for custom components. Things still aren’t perfect in this area, but it looks a lot better than it did previously.
So here’s a screenshot of how things currently look with the Substance “Graphite Glass” skin:
RText with Graphite Glass skin
Overall I don’t think it’s so bad. The editor colors could use some work, but otherwise I think things are okay. And here’s how things look with the “Moderate” skin:
RText with Moderate skin
The tabs at the bottom are a little dark and bleh, so some work to do there too.
There are other issues as well. Substance is really sort of an all-or-nothing LookAndFeel. It’s tough to have an application (like RText) where you can runtime switch to and from Substance and another LookAndFeel like Windows or Nimbus; if you’re going to use Substance and no other LAF though, it should be fine. One example being custom table/list renderers. Substance has a fade in/out effect for armed elements in these components, however, if you use any custom renderers extending Default(List|Tree)CellRenderer, the effect is lost. This is because this effect is only available if your custom renderers extend DefaultSubstance*CellRenderer, not plain ol’ Default*CellRenderer. Unfortunately, this is simply not possible in applications like RText, where Substance isn’t on the build path.