当应用程序失去焦点时释放助记符

Release mnemonic when application loses focus

背景

由于 known bug, JavaFX applications that use a MenuBar 将在用户按 Alt+Tab 切换到另一个程序时保持选定的助记符(“锁定”)。当用户returns到JavaFX应用程序时,框架保留闩锁。

问题

如果用户随后按下与助记符对应的字母,则该字母被消耗并打开该菜单。

此行为不是用户对应用程序的期望:它会中断工作流程。相反,Alt+Tab 不应将应用程序置于可以打开菜单的状态。这是将 Alt 本身与 Alt+Tab 合并来触发菜单,这是一种概念上不同的操作。

其他questions寻求禁用助记符,但我们想清除闩锁,以便当用户returns进入应用程序时,按下字母不会触发打开菜单。

问题

当按下 Alt+Tab 时(即应用程序焦点丢失),您如何指示 JavaFX 应用程序清除锁定的助记符?

这个解决方案有几个部分:释放助记符和使用 Alt 按键。务必同时实施。

释放助记词

解决此错误的一种方法是向应用程序的 Stage 添加一个焦点侦听器,该侦听器会为所有已知的助记符触发按键释放事件。给定一个 Stage 实例,我们可以迭代所有主菜单助记符,如下所示:

stage.focusedProperty().addListener( ( c, lost, show ) -> {
  if( lost ) {
    for( final var mnemonics : stage.getScene().getMnemonics().values() ) {
      for( final var mnemonic : mnemonics ) {
        mnemonic.getNode().fireEvent( keyUp( ALT, false ) );
      }
    }
  }
  else if( show ) {
    // Make sure the menu does not capture focus.
    stage.getScene().focusOwnerProperty().get().requestFocus();
  }
} );

我们需要一些辅助方法来创建按键释放事件:

public static Event keyDown( final KeyCode code, final boolean shift ) {
  return keyEvent( KEY_PRESSED, code, shift );
}

public static Event keyUp( final KeyCode code, final boolean shift ) {
  return keyEvent( KEY_RELEASED, code, shift );
}

private static Event keyEvent(
  final EventType<KeyEvent> type, final KeyCode code, final boolean shift ) {
  return new KeyEvent(
    type, "", "", code, shift, false, false, false
  );
}

使用这些方法后,通过按 Alt+Tab 循环 windows 在返回到 JavaFX 应用程序后按助记键 (例如“文件”菜单的“f”)。

消耗事件

此外,让场景消费事件:

scene.addEventHandler( KEY_PRESSED, event -> {
  final var code = event.getCode();

  if( event.isAltDown() && (code == ALT_GRAPH || code == ALT) ) {
    event.consume();
  }
} );