As per title, is it possible to change a JCheckBox
selection state using a mnemonic key without transferring the focus to the component?
Current behavior as per GIF.
The wanted behavior would be the focus remaining on the "Type" text field.
As per title, is it possible to change a JCheckBox
selection state using a mnemonic key without transferring the focus to the component?
Current behavior as per GIF.
The wanted behavior would be the focus remaining on the "Type" text field.
The problem are these lines from the 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
}
}
This is buried deep in the handling of the key binding for checkbox and this code was never meant to be customized.
A workaround would be to create your own class that extends JCheckBox
and specifically doesn't request the focus while a key binding is processed:
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;
}
}
}
Given that the mnemonic is registered as a key binding, in the scope of
JComponent.WHEN_IN_FOCUSED_WINDOW
See BasicButtonListener#updateMnemonicBinding
We can simply override the key binding actions with our own ones.
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
}
})
}
}
}
To maintain the semantic expressed in BasicButtonListener.Actions
, we have overridden both the pressed
and released
actions to change the ButtonModel
state.
Example: