JDK-8231245 : Controls' behavior must not depend on sequence of handler registration
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: jfx11
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2019-09-19
  • Updated: 2024-11-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Relates :  
Description
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());
    
    }
 
Comments
It's even more inconsistent than you might think. If you register your handler AFTER, but then apply a new Skin (or the same Skin), then you get the BEFORE situation. This is because the old Skin is removed, all its handlers are removed, and then all handlers are re-added. But as handlers are processed in registration order, these handlers will now be last. The fundamental problem is that Skins/Behaviors are using the same infrastructure as the user handlers. The user expects to be the sole user of these functions and doesn't expect interference from Skins/Behaviors. For property listeners, this problem is less pronounced because listeners are always called and earlier listeners don't influence the "result". Handlers however can consume events, so their order is of real importance.
07-11-2024

changed the example to use SPACE (vs. ENTER which is not active on Mac) - now should be reproducible on Mac
07-10-2019

ah.. yes. I tested on Mac. I will test on Windows/Linux and reopen if reproducible.
01-10-2019

.. are you on a mac, by any chance? Just seeing that the enter binding is active on non-mac systems only ..
01-10-2019

quick closer :) Weird that you can't reproduce it, for me it's behaving like described in fx11, fx12 and my current dev . With added version output: String version = "java: " + System.getProperty("java.version")+ "-" + System.getProperty("java.vm.version") + " (" + System.getProperty("os.arch") + ")" + "\n fx: " + System.getProperty("javafx.runtime.version") ; System.out.println(version); the log on running and enter -> tab -> enter is: java: 11-11+28 (amd64) fx: 11+26 ENTER received in first ENTER received in second singleton TAB received in first TAB received in second singleton ENTER received in first ENTER received in second singleton action and: java: 12.0.2-12.0.2+10 (amd64) fx: 12.0.1+2 ENTER received in first ENTER received in second singleton TAB received in first TAB received in second singleton ENTER received in first ENTER received in second singleton action dev: java: 12.0.2-12.0.2+10 (amd64) fx: 14-internal+0-2019-09-04-133237 ENTER received in first ENTER received in second singleton TAB received in first TAB received in second singleton ENTER received in first ENTER received in second singleton action
01-10-2019

I will reopen it based on response.
01-10-2019

Tested with openjfx11 & openjfx13 - I am never seeing the "action" message being printed for any of the button on focusing and ENTER key press. am I missing something here?
27-09-2019