不转移焦点的JCheckBox助记符

JCheckBox mnemonic without transferring focus

根据标题,是否可以使用助记键更改JCheckBox选择状态而不将焦点转移到组件?

根据 GIF 的当前行为。
想要的行为是将焦点留在“类型”文本字段上。

问题是来自 BasicButtonListener.Actions 的这些行:

        if (key == PRESS) {
            ButtonModel model = b.getModel();
            model.setArmed(true);
            model.setPressed(true);
            if(!b.hasFocus()) {
                b.requestFocus();  // here it requests the focus
            }
        }

这深埋在复选框的键绑定处理中,并且此代码从未打算进行自定义。

解决方法是创建您自己的 class,它扩展 JCheckBox,特别是在处理键绑定时不请求焦点:

public class MyCheckBox extends JCheckBox {

    private boolean requestFocusAllowed = true;

    public MyCheckBox() {
    }

    @Override
    public void requestFocus() {
        if (requestFocusAllowed) {
            super.requestFocus();
        }
    }

    @Override
    protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
        requestFocusAllowed = false;
        try {
            return super.processKeyBinding(ks, e, condition, pressed);
        } finally {
            requestFocusAllowed = true;
        }
    }
}

鉴于助记词注册为按键绑定,在

的范围内
JComponent.WHEN_IN_FOCUSED_WINDOW

BasicButtonListener#updateMnemonicBinding


我们可以简单地用我们自己的键绑定操作覆盖。

private class MnemonicCheckBox : JCheckBox(...) {
  override fun setMnemonic(mnemonic: Int) {
    super.setMnemonic(mnemonic)

    if (mnemonic != 0) {
      overrideMnemonicActions()
    }
  }

  private fun overrideMnemonicActions() {
    val mnemonicMask = UIUtilities.getSystemMnemonicKeyMask()
    val altGraphDownMask = InputEvent.ALT_GRAPH_DOWN_MASK

    getInputMap(WHEN_IN_FOCUSED_WINDOW).apply {
      // Pressed
      put(KeyStroke.getKeyStroke(mnemonic, mnemonicMask), "mnemonicPressed")
      put(KeyStroke.getKeyStroke(mnemonic, mnemonicMask or altGraphDownMask), "mnemonicPressed")

      // Released
      put(KeyStroke.getKeyStroke(mnemonic, mnemonicMask, true), "mnemonicReleased")
      put(KeyStroke.getKeyStroke(mnemonic, mnemonicMask or altGraphDownMask, true), "mnemonicReleased")
    }

    actionMap.apply {
      put("mnemonicPressed", object : AbstractAction() {
        override fun actionPerformed(event: ActionEvent) {
          model.isArmed = true
          model.isPressed = true
        }
      })
      put("mnemonicReleased", object : AbstractAction() {
        override fun actionPerformed(event: ActionEvent) {
          model.isPressed = false
          model.isArmed = false
        }
      })
    }
  }
}

为了保持 BasicButtonListener.Actions 中表达的语义,我们覆盖了 pressedreleased 操作以更改 ButtonModel 状态。

示例: