How to get the menu item accelerator keys working

2019-07-22 12:27发布

问题:

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 ?

回答1:

I was mixing two different things:

The system property

System.setProperty("apple.laf.useScreenMenuBar", "true")

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.

JFrame frame = ...;
JMenuBar menu = ...;
frame.setJMenuBar(menu);

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 is null, 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:

  • It does not set the system property to true
  • It only sets the default menu bar, and forgets to set the menu bar on the JFrame. Because the JFrame itself has no menu bar, the default menu bar is shown which gave the impression that the setDefaultMenuBar 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 the JFrame.