在 OS X 上使用屏幕菜单栏时如何使菜单项加速键起作用

How to get the menu item accelerator keys working when using the screen menu bar on OS X

在 OS X 上,从主 JFrame 中删除 JMenuBar 并改用屏幕菜单栏是有意义的。

我的印象是,在最新版本的 Apple JDK 中,这是使用

Application.getApplication().setDefaultMenuBar( JMenuBar );

呼唤。

使用此功能时,加速键似乎不再起作用。

以下程序说明了 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);
  }
}

该程序应该允许您按 cmd+t,然后会弹出一个对话框,确认该操作的快捷方式是否有效。

但是这不起作用。按下快捷键时菜单项高亮显示,但未执行操作。

com.apple.eawt.Applicationclass上没有找到相关的方法,忘记调用了。查看 rather outdated article on the Apple website 中的代码表明快捷方式应该可以正常工作。

注意使用

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

而不是 Application#setDefaultMenuBar 方法,它按预期工作。这是否意味着系统 属性 仍然是推荐的方法,我不应该使用 Application class 上的方法?

我混合了两种不同的东西:

系统属性

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

确定 Aqua 外观是否使用顶级 window(例如 JFrame)的菜单栏作为应用程序菜单栏。

将此设置为 true 将确保当您调用

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

当框架有焦点时,菜单栏将用作应用程序菜单栏,并且菜单栏永远不会被绘制为 JFrame 的一部分。 当另一个框架(或顶级组件)获得焦点时,应用程序菜单栏将更新以显示该组件的菜单栏。

例如,当您使用 JOptionPane.showXXX 方法之一显示模态对话框时,应用程序菜单栏将尝试显示该模态对话框的菜单栏。因为这是null,所以只要模态对话框没有关闭,应用程序菜单栏就会变成空的。

这就是 Application.setDefaultMenuBar 方法发挥作用的地方。如果您使用非 null 菜单栏调用此方法,则当没有菜单栏的顶级组件获得焦点时,该菜单栏将显示为应用程序菜单栏

因此,问题中的片段有 2 个问题:

  • 它没有将系统 属性 设置为 true
  • 只设置了默认菜单栏,忘记设置JFrame上的菜单栏了。因为 JFrame 本身没有菜单栏,所以显示默认菜单栏,给人的印象是 setDefaultMenuBar 方法是系统 属性 的替代方法。但显然不是这样。

结果是快捷方式不起作用,因为 JMenuBar 不包含在 JFrame 中。