The problem is that (f.i.) a button's action is fired / or not depending on whether a consuming keyEvent handler is registered after/before showing it. The example below has two buttons, both have added keyPressed handlers for ENTER which consume the key (sibling keyHandlers are registered only to see that they are notified as expected) and an action handler. The difference is that for before/after the handlers are registered before/after showing the stage.
To reproduce, compile and run the example below
- focus the before button
- press enter: the action is not fired
- focus the after button
- press enter: the action is fired
Whatever the expected behavior is (I lean to the behavior of first - that's the same as in pre-fx9 and Swing), it must be the same, sequence of listener registration must not make a difference.
Technically, the reason is the InputMap that is registered as eventHandler by any XXBehavior: its handle does nothing if the event is null or consumed
@Override public void handle(Event e) {
if (e == null || e.isConsumed()) return;
...
}
This handler is a sibling of the custom handlers, so served in sequence of registration. The inputMap is instantiated/registered at the time of skin instantiation. Consequently, its body is processed depending on whether a consuming handler (of the same type) is added before or after the skin creation.
The example:
public class AddEventHandler extends Application {
private Button before;
private Button after;
// non-mac only (mac has this binding deactivated by interceptor)
// private KeyCode consumeWith = KeyCode.ENTER;
// this should be active for all OS
private KeyCode consumeWith = KeyCode.SPACE;
protected void registerHandlers(Button button) {
button.addEventHandler(KEY_PRESSED, e -> {
if (e.getCode() == consumeWith) {
e.consume();
}
System.out.println(e.getCode() + " received in first");
});
button.addEventHandler(KEY_PRESSED, e -> {
System.out.println(e.getCode() + " received in second");
});
button.setOnKeyPressed(e -> {
System.out.println("singleton");
});
button.setOnAction(a -> {
System.out.println("action");
});
}
private Parent createContent() {
// some simple control that's focusable
before = new Button("handlers registered BEFORE showing");
registerHandlers(before);
after = new Button("handlers registered AFTER showing");
VBox content = new VBox(10, before, after);
return content;
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle(FXUtils.version());
stage.show();
registerHandlers(after);
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(AddEventHandler.class.getName());
}