Java Hacking
In my recent work on PhotoStuff, I’ve been working in Java on the Mac. PhotoStuff is full of features, but I often find the interface frustrating and confusing. This is especially true on the Mac where many of the UI elements end up being in the wrong place and looking very un-Mac-like.
From my own experience, trying to use an application that appear very un-Mac-like immediately turns me off of the application. Every little thing, no matter how small, contributes to the off-putting feeling that what I’m using was clearly not designed for use on the Mac. When it comes to Mac applications, it often doesn’t matter how great the features are if the interface doesn’t live up to the standards of the community. Knowing this, it’s worth spending some time to make some (mostly simple) changes to PhotoStuff to make it act a bit more Mac-like.
Having been away from Java for the better part of a decade, and finding some things rather tricky or unintuitive, I thought I’d take a minute to write down some of the changes I made. These are things that I didn’t find especially obvious when I first tried to do them, and most of the issues have to do with menus.
Application Name
Unless explicitly specified, the application name that shows up as the title of the application menu is a class name. My menu bar is crowded enough without displaying something awful like “org.mindswap.markup.Razor”. The fix this, the system propertycom.apple.mrj.application.apple.menu.about.name
needs to be set to the application name.
Global Menu Bars
I can’t stand the look of a Java application that has in-window menu bars. It just seems so wrong. So I went looking for how to get the existing JMenu code to display as a proper Mac menu bar. Technical Note TN2042 (Tailoring Java 1.3.1 Applications for Mac OS X) looked promising at first, telling me to set com.apple.macos.useScreenMenuBar
. This didn’t work. Unsupported System Properties tells me that this property was replaced with apple.laf.useScreenMenuBar
in Java 1.4.1. I had to set this property on the command line (or in the application bundle’s Info.plist — see below), since setting it at runtime didn’t seem to have any effect.
Menu Item Locations
On the Mac, there are already places for menu items such as About, Preferences, and Quit. They belong in the application menu, not in Help, Edit, or File. To solve this, com.apple.mrj.MRJApplicationUtils provides the registerAboutHandler
, registerPrefsHandler
, and registerQuitHandler
methods. Using these methods gets the menu items in the right place (although Eclipse tells me things like “The method registerQuitHandler(MRJQuitHandler) from the type MRJApplicationUtils is deprecated.” Hopefully it keeps working.).
The final step here is to check that if we’re running on a Mac we don’t duplicate these items in both the Application menu and their other (Windows) locations. An isMac()
method and a few conditional blocks in the JMenu construction code solves this.
Keyboard Shortcuts
Keyboard shortcuts are great, but they should follow system conventions so that users don’t have to memorize new shortcuts for existing functionality, going against years of muscle memory. PhotoStuff has shortcuts for almost every menu item, but most of them followed the Windows convention of using the Control modifier. To fix this, the explicit KeyStroke.getKeyStroke(KeyEvent.VKO, KeyEvent.CTRLMASK)
becomes the more flexible getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())
. The getMenuShortcutKeyMask()
will presumably work for any system that uses its own convention for menu modifiers, not just the ones that we’ve thought about ahead of time.
Bundling and Packaging
Finally, the bundling of the application has a huge impact on the impression of the application. The Mac Developer Tools comes with the handy Jar Bundler.app that can turn a set of jar files into a native application bundle.
Using Jar Bundler, you can get a single application icon instead of a folder full of supporting libraries, folders, and the application jar. Compare:
This also makes copying the application easier because an application bundle acts like a single file. With the application bundle in hand, we can put it into a disk image, and we’ve got something that looks a lot more like a Mac application than when we started.
I suppose it’s still in beta, but until I navigate the release process here at MINDSWAP, and since it’s open source anyway, here’s a local copy of PhotoStuff including the changes I describe above: PhotoStuff.dmg
Links
A couple of the pages that I found useful:
Comments
True, it’s already a bundle, but I’m not sure it would ever act as a first class Mac application without the Jar Bundling step. Partly because a jar file will never appear or act as an application. But also because the Jar Bundling allows you to specify properties in the PList such as JVM command line arguments (memory allocation) and application creator and type, among others.
I’d love to be proved wrong, and am sure there are things about this area that I don’t know, or think I know but am totally wrong about. But my intuition is that you’ll always need to bundle the jar files into a proper Mac bundle to have it appear as a first class application.
Posted by: kasei on November 3rd, 2006 2:49 AM
Strictly-speaking, isn’t a .jar file already a bundle (in modified .zip format) which contains all the stuff needed for the app? I don’t know about on J2SE but on J2ME you put all your app icons, app provisioning info files, etc. into the .jar, and if the same is true for J2SE then you’d have a single self-contained app bundle on ANY platform without needing to do an OSX-specific build or whatever.
Posted by: fluffy on November 3rd, 2006 2:28 AM