Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Mac OS X Integration for Java

The more your application fits in with the native environment, the less users have to learn unique behaviors for using your application. A great application looks and feels like an extension of the host platform. This chapter discusses a few details that can help you make your application look and feel like it is an integral part of Mac OS X.

Contents:

Making User Interface Decisions
AppleScript
System Properties


Making User Interface Decisions

J2SE’s cross-platform design demands a lot of flexibility in the user interface to accommodate multiple operating systems. The Aqua user interface, on the other hand, is streamlined to provide the absolute best user experience on just one platform. This section helps you to make decisions that let your Java applications approach the appearance and performance characteristics of native applications. In fact, following some of the suggestions presented here can probably help make your application appear and perform more like native applications on other platforms as well. The topics covered here represent just a small subset of design decision topics, but they are high-visibility issues often present in Java applications. The complete guidelines for the Aqua user interface can be found in Apple Human Interface Guidelines.

Menus

The appearance and behavior of menu items varies across platforms. Unfortunately, many Java programmers write their applications with only one platform in mind. This section discusses some common areas to improve how your Java menus are displayed and perform in Mac OS X.

The Menu Bar

“JMenuBars” discusses how to get the menu out of JFrames and into the Mac OS X menu bar. Having menus in the menu bar is highly encouraged, but it does not automatically provide the native experience of Mac OS X menus for two reasons:

In short, the menu bar is always present and, except that some items may be dimmed at times, always looks the same. Removing the menus from your windows and putting them in the menu bar is a great first step that retains cross-platform compatibility. Depending on how your application is designed, getting these other characteristics may require rethinking how you display menus with your code. One solution is to use a menu factory with an off screen JFrame that is always foremost when your application is active.

The Application Menu

Any Java application that uses AWT/Swing or is packaged in a double-clickable application bundle is automatically launched with an application menu similar to native applications in Mac OS X. This application menu, by default, contains the full name of the main class as the title. This name can be changed using the -Xdock:name command-line property, or it can be set in the information property list file for your application as the CFBundleName value. For more on Info.plist files, see “A Java Application’s Info.plist File.” According to the Aqua guidelines, the name you specify for the application menu should be no longer than 16 characters. Figure 1 shows an application menu.


Figure 1  Application menu for a Java application in Mac OS X

Application menu for a Java application in Mac OS X

The next step to customizing your application menu is to have your own handling code called when certain items in the application menu are chosen. Apple provides functionality for this in the com.apple.eawt package. The Application and ApplicationAdaptor classes provide a way to handle the Preferences, About, and Quit items.

For more information see Java 1.4 API: Apple Extensions and J2SE 5.0 Apple Extensions Reference. Examples of how to use these can also be found in a default Swing application project in Xcode. Just open a new project in Xcode by selecting Java Swing Application in the New Project Assistant. The resulting project uses all of these handlers.

If your application is to be deployed on other platforms, where Preferences, Quit, and About are elsewhere on the menu bar (in a File or Edit menu, for example), you may want to make this placement conditional based on the host platform’s operating system. Conditional placement is preferable to just adding a second instance of each of these menu items for Mac OS X. This minor modification can go a long way to making your Java application feel more like a native application in Mac OS X.

The Window Menu

Apple Human Interface Guidelines suggests that all Mac OS X applications should provide a Window menu to keep track of all currently open windows. A Window menu should contain a list of windows, with a checkmark next to the active window. Selection of a given Window menu item should result in the corresponding window being brought to the front. New windows should be added to the menu, and closed windows should be removed. The ordering of the menu items is typically the order in which the windows are opened. Apple Human Interface Guidelines has more specific guidance on the Window menu.

Accelerators (Keyboard Shortcuts)

Do not set menu item accelerators with an explicit javax.swing.KeyStroke specification. Modifier keys vary from platform to platform. Instead, use java.awt.Tookit.getMenuShortcutKeyMask to ask the system for the appropriate key rather than defining it yourself.

When calling this method, the current platform’s Toolkit implementation returns the proper mask for you. This single call checks for the current platform and then guesses which key is correct. In the case of adding a Copy item to a menu, using getMenuShortcutKeyMask means that you can replace the complexity of “The Contents of an Application Bundle” with the simplicity of Listing 2.

Listing 1  Explicitly setting accelerators based on the host platform

JMenuItem jmi = new JMenuItem("Copy");
    String vers = System.getProperty("os.name").toLowerCase();
    if (s.indexOf("windows") != -1) {
       jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.CTRL_MASK));
    } else if (s.indexOf("mac") != -1) {
       jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.META_MASK));
    }

Listing 2  Using getMenuShortcutKeyMask to set modifier keys

JMenuItem jmi = new JMenuItem("Copy");
jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));

The default modifier key in Mac OS X is the Command key. There may be additional modifier keys like Shift, Option, or Control, but the Command key is the primary key that alerts an application that a command, not regular input follows. Note that not all platforms provide this consistency in user interface behavior. When assigning keyboard shortcuts to items for menu items, make sure that you are not overriding any of the keyboard commands Macintosh users are accustomed to. See Apple Human Interface Guidelines for the definitive list of the most common and reserved keyboard shortcuts (keyboard equivalents).

Mnemonics

JMenuItems inherit the concept of mnemonics from JAbstractButton. In the context of menus, mnemonics are shortcuts to menus and their contents using a modifier key in conjunction with a single letter. When you set a mnemonic in a JMenuItem, Java underscores the mnemonic letter in the title of the JMenuItem or JMenu. The result looks something like that shown in Figure 2.

Note: Figure 2 is provided only to illustrate mnemonics. Although the figure illustrates the menu bar inside of the window, you should put your menus in the normal Mac OS X menu bar with the apple.laf.useScreenMenuBar system property, where mnemonics are never drawn. See Java System Properties.


Figure 2  Mnemonics in Mac OS X

Mnemonics in Mac OS X

This does not fit in with the Aqua guidelines for multiple reasons. Among them:

The problem is partially handled for you automatically if you use the apple.laf.useScreenMenuBar system property. A better solution though is for you to not to use mnemonics for Mac OS X. If you want mnemonics on another platform, just include a single setMnemonics() method that is conditionally called (based on the platform) when constructing your interface.

How then do you get the functionality of mnemonics without using Java’s mnemonics? If you have defined a keystroke sequence in the setAccelerator method for a menu item, that key sequence is automatically entered into your menus. For example, Listing 3 sets an accelerator of Command-Shift-P for a Page Setup menu. Figure 3 shows the result of this code along with similar settings for the other items. Note that the symbols representing the Command key and the Shift key are automatically included.

Listing 3  Setting an accelerator

JMenuItem saveAsItem = new JMenuItem("Save As...");
    saveAsItem.setAccelerator(
        KeyStroke.getKeyStroke(KeyEvent.VK_S, (java.awt.event.InputEvent.SHIFT_MASK | (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()))));
    saveAsItem.addActionListener(new ActionListener(  ) {
      public void actionPerformed(ActionEvent e) { System.out.println("Save As...") ; }
    });
    fileMenu.add(saveAsItem);

Figure 3  A File menu

A File menu

Though not quite the same as mnemonics, note that Mac OS X provides keyboard and assistive-device navigation to the menus. Preferences for these features are set in the Keyboard and Universal Access panes of System Preferences.

Note: Since the ALT_MASK modifier evaluates to the Option key on the Macintosh, Control-Alt masks set for Windows become Command-Option masks if you use getMenuShortcutKeyMask in conjunction with ALT_MASK.

Menu Item Icons and Special Characters

Like mnemonics, menu item icons are also available and functional via Swing in Mac OS X. They are not a standard part of the Aqua interface, although some applications do display them—most notably the Finder in the Go menu. You may want to consider applying these icons conditionally based on platform.

Aqua specifies a specific set of special characters to be used in menus. See the information on using special characters in menus in Apple Human Interface Guidelines

Contextual Menus

Contextual menus, which are called pop-up menus in Java, are fully supported. In Mac OS X, they are triggered by a Control-click or a right-click. Note that Control-click and right-click are a different type of MOUSE-EVENT, even through they have the same result. In Windows, the right mouse button is the standard trigger for contextual menus.

The different triggers could result in fragmented and conditional code. One important aspect of both triggers is shared-the mouse click. To ensure that your program is interpreting the proper contextual–menu trigger, it is again a good idea to ask the AWT to do the interpreting for you with java.awt.event.MouseEvent.isPopupTrigger.

The method is defined in java.awt.event.MouseEvent because you need to activate the contextual menu through a java.awt.event.MouseListener on a given component when a mouse event on that component is detected. The important thing to determine is how and when to detect the proper event. In Mac OS X, the pop-up trigger is set on MOUSE_PRESSED. In Windows it is set on MOUSE_RELEASED. For portability, both cases should be considered, as shown in “A Java Application’s Info.plist File.”

Listing 4  Using ˜ to detect contextual-menu activation

JLabel label = new JLabel("I have a pop-up menu!");
 
label.addMouseListener(new MouseAdapter(){
    public void mousePressed(MouseEvent e) {
       evaluatePopup(e);
    }
 
    public void mouseReleased(MouseEvent e) {
       evaluatePopup(e);
    }
 
    private void evaluatePopup(MouseEvent e) {
       if (e.isPopupTrigger()) {
          // show the pop-up menu...
       }
    }
});

When designing contextual menus, keep in mind that a contextual menu should never be the only way a user can access something. Contextual menus provide convenient access to often-used commands associated with an item, not the primary or sole access.

Components

There are several key concepts to keep mind when designing your user interface. The following sections cover these.

Laying Out and Sizing Components

Do not explicitly set the x and y coordinates when placing components; instead use the AWT layout managers. The layout managers use abstracted location constants and determine the exact placement of these controls for a specific environment. Layout managers take into account the sizes of each individual component while maintaining their placement relative to one another within the container.

In general, do not set component sizes explicitly. Each look and feel has its own font styles and sizes. These font sizes affect the required size of the component containing the text. Moving explicitly sized components to a new look and feel with a larger font size can cause problems. The safest way to make your components a proper size in a portable manner is to change to or add another layout manager, or to set the component’s minimum and maximum size to its preferred size. The setSize() and getPreferredSize() methods are useful when following the latter approach.

Most layout managers and containers respect a component’s preferred size, usually making this call unnecessary. As your interface becomes more complicated however, you may find this call handy for containers with many child components.

Coloring Components

Because a given look and feel tends to have universal coloring and styling for most, if not all of its controls, you may be tempted to create custom components that match the look and feel of standard user interface classes. This is perfectly legal, but adds maintenance and portability costs. It is easy to set an explicit color that you think works well with the current look and feel. Changing to a different look and feel though may surprise you with an occasional non–standard component. To ensure that your custom control matches standard components, query the UIManager class for the desired colors. An example of this is a custom Window object that contains some standard lightweight components but wants to paint its uncovered background to match that of the rest of the application’s containers and windows. To do this, you can call

myPanel.setBackground(UIManager.getColor("window"))

This returns the color appropriate for the current look and feel. The other advantage of using such standard methods is that they provide more specialized backgrounds that are not easily reconstructed, such as the striped background used for Aqua containers and windows.

Windows and Dialogs

Mac OS X window coordinates and insets are compatible with the Java SDK. Window bounds refer to the outside of the window’s frame. The coordinates of the window put (0,0) at the top left of the title bar. The getInsets method returns the amount by which content needs to be inset in the window to avoid the window border. This should affect only applications that are performing precision positioning of windows, especially full-screen windows.

Windows behave differently in Mac OS X than they do on other platforms. For example, an application can be open without having any windows. Windows minimize to the Dock, and windows with variable content always have scroll bars. This section highlights the windows details you should be aware of and discusses how to deal with window behavior in Mac OS X.

Use of the Multiple Document Interface

The multiple document interface (MDI) model of the javax.swing.JDesktopPane class can provide a confusing user experience in Mac OS X. Windows minimized in a JDesktopPane move around as the JDesktopPane changes size. In JDesktopPane, windows minimize to the bottom of the pane while independent windows minimize to the Dock. Furthermore, JDesktopPane restricts users from moving windows where they want. They are forced to deal with two different scopes of windows, those within the JDesktopPane and the JDesktopPane itself. Normally Mac OS X users interact with applications through numerous free-floating, independent windows and a single menu bar at the top of the screen. Users can intersperse these windows with other application windows (from the same application or other applications) anywhere they want in their view, which includes the entire desktop. Users are not visually constrained to one area of the screen when using a particular application. When building applications for Mac OS X, try to avoid using javax.swing.JDesktopPanes.

There are times when there is not a simple way to solve window-related problems other than using a JDesktopPane. For example, you might have a Java application that requires a floating toolbar-like entity, referred to in Swing as an “internal utility window,” that needs to always remain in the foreground regardless of which document is active. Although Java currently has no means of providing this other than by using JDesktopPane, for new development you may want to consider designing a more platform-neutral user interface with a single dynamic container, similar to applications like JBuilder or LimeWire. If you are bringing an existing MDI-based application to the Macintosh from another platform and do not want to refactor the code, Mac OS X does support the MDI as specified in the J2SE 1.4 specification.

Windows With Scroll Bars (Using JScrollPanes)

In Mac OS X, scrollable document windows have a scroll bar regardless of whether or not there is enough content in the window to require scrolling. The scroller itself is present only when the size of the content exceeds the viewable area of the window. This prevents users from perceiving that the viewable area is changing size. By default, a Swing JFrame has no scroll bars, regardless of how it is resized. The easiest way to provide scrollable content in a frame is to place your frame’s components inside a JScrollPane, which can then be added to the parent frame. The default behavior of JScrollPane, however, is that scroll bars appear only if the content in the pane exceeds the size of the window. If you are using a JScrollPane in your application, you can set the JScrollPane’s scroll bar policy to always display the scroll bars, even when the content is smaller than the viewable size of the window. An example is shown in “Making a Java Application Bundle.”

Listing 5  Setting JScrollBar policies to be more like Aqua

JScrollPane jsp = new JScrollPane();
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
jsp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

With this setting the scroll bars are solid—with scrollers appearing when the content is larger than the viewable area. You might want to do this conditionally based on the host platform since the default policy, AS_NEEDED, may more closely resemble other platforms native behavior.

File Choosing Dialogs

The java.awt.FileDialog and javax.swing.JFileChooser classes are the two main mechanisms to create quick and easy access to the file system for Java applications. Although each has its advantages, java.awt.FileDialog provides considerable benefit in making your application behave more like a native application. The difference between the two is especially evident in Mac OS X as Figure 4 and Figure 5 show.


Figure 4  Dialog created with java.awt.FileDialog

Dialog created with java.awt.FileDialog


Figure 5  Dialog created with javax.swing.jFileChooser

Dialog created with javax.swing.jFileChooser

The column-view style of browsing is adopted automatically by the AWT FileDialog, while the Swing JFileChooser uses a navigation style different from that of native Mac OS X applications. Unless you need the functional advantages of JFileChooser you probably want to use java.awt.FileDialog. Since the FileDialog is modal and draws on top of other visible components, there is not the usual consequence of mixing Swing and AWT components.

When using the AWT FileDialog, you may want a user to select a directory instead of a file. In this case, use the apple.awt.fileDialogForDirectories property with the setProperty.invoke() method on your FileDialog instance.

Window Modified Indicator

On Mac OS X, it is common for windows to mark their close widget if the window’s content has changed since it was last saved. Using this makes your application behave more like a native application and conform to users expectations.

To use a window modified indicator, you need to use the Swing property windowModified. It can be set on any subclass of JComponent using the putClientProperty() method. The value of the property is either Boolean.TRUE or Boolean.FALSE.

For more on using the Window Modified indicator in your application, review Technical Q&A QA1146: Illustrating document window changes in Swing.

AppleScript

Mac OS X uses Apple events for interprocess communication. Apple events are high-level semantic events that an application can send to itself or other applications. AppleScript allows you to script actions based on these events. Without including native code in your Java application you may let users take some level of control of your application through AppleScript. Java applications can become AppleScript-aware in two ways:

For more on AppleScript, see Getting Started with AppleScript.

System Properties

The Mac OS X–specific system properties let you easily add native behavior to your Java applications with a minimum of work on your part. A complete list of supported Mac OS X system properties, including how to use them, is available in Java System Properties.



< Previous PageNext Page > Hide TOC


Last updated: 2006-05-23




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice