Fire Button's onAction with Enter in JavaFX


Question

I'm a newbie to JavaFx. In my JavaFX application I have set onAction property and it works fine when I press the button using mouse. I want to fire the same even when user press Enter on button. I know I can use a even handler to do that. But when I read the onAction JavaDoc it says that this event get fire by a key press.

Property description:

The button's action, which is invoked whenever the button is fired. This may be due to the user clicking on the button with the mouse, or by a touch event, or by a key press, or if the developer programmatically invokes the fire() method.

But when I press Enter key nothing happens. Is it error in documentation? Are there any other way to achieve that without adding alistener to the button?

P.S

After the comments I checked with space key then it get fired. But I want to set that to Enter key. I have many buttons. I tried button.setDefaultButton(true); but it is not get fired. I think that is becacuse there are more than one button. If I set it just to a single button it works fine. How to set that to multiple buttons?

1
12
9/10/2014 8:07:22 AM

Accepted Answer

You can dynamically change the default button property of the currently focused button by using binding

btn.defaultButtonProperty().bind(btn.focusedProperty());
9
9/10/2014 8:47:45 AM

Why do I always answer three years old questions...?! ;-) Anyway, someone might find it useful (including myself).

To override the Enter key press behavior I use the function below calling it in the scene's key press event filter:

public static void overrideEnterKeyPressEvent(KeyEvent evt) {
    EventTarget eventTarget = evt.getTarget();

    if ((eventTarget instanceof TextArea) || (eventTarget instanceof TableView)) {
        return;
    }

    if (eventTarget instanceof Button) {
        Platform.runLater(() -> {
            KeyEvent newEventPressed = new KeyEvent(KeyEvent.KEY_PRESSED, " ", " ", KeyCode.SPACE, false, false, false, false);
            Event.fireEvent(eventTarget, newEventPressed);
            KeyEvent newEventReleased = new KeyEvent(KeyEvent.KEY_RELEASED, " ", " ", KeyCode.SPACE, false, false, false, false);
            Event.fireEvent(eventTarget, newEventReleased);
        });
        evt.consume();
        return;
    }

    Platform.runLater(() -> {
        KeyEvent tabEvent = new KeyEvent(KeyEvent.KEY_PRESSED, "", "\t", KeyCode.TAB, evt.isShiftDown(), false, false, false);
        Event.fireEvent(eventTarget, tabEvent);
    });
    evt.consume();
}

Based on the event's target the function works as follows. For a TextArea or TableView, it's a NoOp. For a button, it consumes the Enter press event and fires Space key press and release events. And for all the other controls, it also consumes the Enter press event and fires a Tab event so pressing Enter moves focus to the next control just like Tab.

Then you just register an event filter for the whole scene:

    scene.addEventFilter(KeyEvent.KEY_PRESSED, this::onSceneKeyPressedFilter);

And the event filter looks like:

private void onSceneKeyPressedFilter(KeyEvent evt) {
    switch (evt.getCode()) {
        case ENTER:
            if (evt.isControlDown() && FxTools.isAncestorNodeTargeted(evt.getTarget(), fxHBoxInputAp)) {
                return; //let the events for the fxHBoxInputAp pane pass through
            }
            overrideEnterKeyPressEvent(evt);
            break;
        ...
        default:
            break;
    }
}

----- edit because I forgot to include the isAncestorNodeTargeted() function; thanks for the comment, Robert -----

public static boolean isDescendantOf(Node node, Node ancestor) {
    while ((node != null) && (node != ancestor)) {
        node = node.getParent();
    }
    return (node != null);
}


public static boolean isAncestorNodeTargeted(EventTarget target, Node ancestor) {
    return (target instanceof Node) ? isDescendantOf((Node) target, ancestor) : false;
}

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon