On OS X, it makes sense to remove the JMenuBar
from your main JFrame
and use the screen menu bar instead.
I was under the impression that in recent versions of the Apple JDK this is done using the
Application.getApplication().setDefaultMenuBar( JMenuBar );
call.
When using this, it seems like the accelerator keys are no longer working.
The following program illustrates the problem on OS X:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.apple.eawt.Application;
public class MacMenuBarShortcutTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
showUI();
}
});
}
private static void showUI(){
JFrame testFrame = new JFrame("TestFrame");
JLabel content = new JLabel("Press cmd-t to test whether the action is triggered");
testFrame.getContentPane().add(content);
JMenuBar menuBar = new JMenuBar();
Action action = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "It works!");
}
};
action.putValue(Action.NAME, "Test action");
action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_T, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
JMenu menu = new JMenu("Menu");
menu.add(new JMenuItem(action));
menuBar.add(menu);
Application.getApplication().setDefaultMenuBar(menuBar);
testFrame.setVisible(true);
testFrame.pack();
testFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
The program should allow you to press cmd+t and a dialog will pop up, confirming the shortcut for the action works.
This is however not working. The menu item gets highlighted when pressing the short cut, but the action is not executed.
I did not found any relevant methods on the com.apple.eawt.Application
class that I forgot to call. Going over the code in a rather outdated article on the Apple website suggested that shortcuts should be working though.
Note that when using
System.setProperty("apple.laf.useScreenMenuBar", "true")
instead of the Application#setDefaultMenuBar
method, it works as expected. Does this mean the system property is still the recommended way, and I shouldn't be using the method on the Application
class ?
I was mixing two different things:
The system property
determines whether the Aqua look and feel uses the menubar of your top-level window (e.g. a
JFrame
) as application menu bar.Setting this to
true
will ensure that when you call e.g.the menu bar will be used as application menu bar when the frame has focus, and that the menu bar will never be painted as part of the
JFrame
. When another frame (or top level component) gains focus, the application menu bar will be updated to show the menu bar of that component.For example when you show a modal dialog using one of the
JOptionPane.showXXX
methods, the application menu bar will try to show the menu bar of that modal dialog. As this isnull
, the application menu bar will become empty as long as that modal dialog is not closed.This is where the
Application.setDefaultMenuBar
method comes into play. If you call this method with a non-null
menu bar, that menu bar will be shown as application menu bar when a top level component without menu bar gains focus.As such, the snippet in the question has 2 problems:
JFrame
. Because theJFrame
itself has no menu bar, the default menu bar is shown which gave the impression that thesetDefaultMenuBar
method was an alternative for the system property. But this is clearly not the case.The result is that the shortcuts are not working because the
JMenuBar
is not contained in theJFrame
.