Daniel H. Steinberg, presenter at the O'Reilly Mac OS X Conference
One of the earlier promises of Java was that you could write an application once and run it on any platform with a JVM. For Swing applications, the various looks and feels solve the big issues. Bring a 100 percent Swing application to a Mac OS X box and your application automatically benefits from many of the features of Aqua's look and feel. Run the same application on a Windows machine and the application looks mostly like a native Windows application.
Fortunately, the big changes don't require much work on your part. Apple takes care of porting the look and feel of your favorite widgets and provides you with runtime properties to customize and fine-tune the appearance and placement. But what about the small things. Take a look at the File menu and menu bar in this application:
At first blush this looks great to someone developing Java applications on a Windows box. The menubar is at the top of the screen. The keyboard accelerators are the Command key and not the Control key that you'd expect on a Windows box. Boy, it sure looks right.
There are, however, tell-tale signs that this is just a port of a Windows application. For one thing, the Exit at the bottom looks really out of place. No Mac application would use Exit. A Mac application would use Quit and in Mac OS X it would be the last menu item in the Application menu. What is meant by the Send item? It may sound as if I'm splitting hairs and not appreciating the hard work done to bring this application this far. Remember that Mac users and Windows users have different expectations for their menus. If you want to write a cross-platform Java application that feels right to both audiences, you're going to need to address these differences. This advice also applies to Mac developers who are hoping their Java application will be adopted by Windows users. You have to be familiar with the culture you're trying to reach. In this article, we'll take a look at some basic techniques for customizing your menus for both platforms at once. As always, suggestions for future articles and comments can be sent to me at DSteinberg@core.com.
Comparing Mac and Windows Menu Bars for Native Apps
As a first step, you should set system properties to help improve the Java experience on a Mac. For example, set the system property
true to display a menu bar assigned with the method
setJMenuBar() at the top of the screen instead of at the top of the JFrame that owns it. You can read more about this and other runtime properties in the recent technote from Apple available in tech note 2031.
For the most part, runtime properties don't require changes to the underlying code. It's true that to take advantage of the different look and feels on different platforms you can't hardcode the look and feel in. Also, as noted above, if you have a
JFrame myFrame then you can only move the
JMenuBar myMenuBar if it has been associated with
myFrame using code like
myFrame.setJMenuBar(myMenuBar). If you specify the name to be displayed in the application menu for the about option using
com.apple.mrj.application.apple.menu.about.name you'll need to enable it by creating the code for an
To fine-tune the menu bars for the different platforms we're going to have to create different versions of the menu bar and of some of the menus and menu items. To see where we're heading, let's take a look at the File menus for the TextEdit application on Mac OS X and for the WordPad application in Windows 2000. Here's a flash from the past from the ghost of standardized tests gone by:
|Compare and contrast the these pictures. Indeed the look is different.|
Next to the Apple and Application menus in the TextEdit example, the menus include File, Edit, Format, Window, and Help. The WordPad menus are similar but not identical. There you see menus for File, Edit, View, Insert, Format, and Help. In this article, we'll only customize the File menus for both and leave the others as unimplemented menus. So we'll create a Mac and a Windows version of the menu bar. Each will have their custom version of the File menu. The other menus will just be JMenus with the labels set appropriately.
Take another look at the File menus and you'll notice that there are common elements. Both versions have New, Open, Save, Save As, Print, and Page Setup menu items. We'll put these in a package that we'll call
commonGUI. Even in these common elements there appear to be differences that we'll need to account for. You'll see that the TextEdit menu uses the command key for accelerators while the WordPad menu uses the control key. This difference is very easy to accommodate as the actual key being modified is the same for both menus (N for New, O for Open, and so on). There is a subtle difference with the New menu item. On the Mac the label reads "New" and on Windows it reads "New...". We'll just create a New menu item in the commonGUI package and extend it in the MacGUI package. Similarly, the Page Setup and Save As menu items have corresponding keyboard accelerators on the Mac but not on Windows. We'll again create a base class that we extend. For those menu items that exist only on the Windows or Mac version, we'll create a corresponding class within the WindowsGUI and MacGUI packages.
Note: Of course, there are many ways to solve any programming problem. You might look at the duplicated code and recognize that we could have used Resource bundles for the menu items that were common to both platforms. We'll save that solution for another article.
As you support more menus and menu items you may find it appropriate to introduce Abstract Factories and Factory Methods. You'll find these described in the Gang of Four book on Design Patterns. I'm following the lead of Joshua Kerievsky who advises that we don't begin with Design Patterns but refactor towards them when the need becomes apparent. You can read the latest draft of his "Refactoring to Patterns" book on the Refactoring page of the industrial logic site.