JavaFx Button-Event Bubbling


Question

I am working on a project using javafx within java8. I focused a weird situation: A panel ( of the class javafx.scene.layout.Pane) containing a button (javafx.scene.control.Button) and another pane. I expect a mouse-clicked event to be bubbled up to the parent. But this is only the case when I click on the pane and does not happen when I click on the button. Below is the code of a very simple example with this behaviour. Does anyone have a suggestion to solve this problem? I know I could create my own button based on a pane.. but this would just be a nasty workaround. Cheers

    package application;

    import javafx.animation.TranslateTransition;
    import javafx.application.Application;
    import javafx.event.Event;
    import javafx.event.EventHandler;
    import javafx.stage.Stage;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.Pane;
    import javafx.util.Duration;


    public class Main extends Application {

        double curScale;


        @Override
        public void start(Stage primaryStage) {
            try {
                BorderPane root = new BorderPane();
                Scene scene = new Scene(root,400,400);
                scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());


                Button btn = new Button("test");
                btn.setLayoutX(50);
                Pane p2 = new Pane();
                p2.setPrefSize(20,20);
                p2.setStyle("-fx-background-color: #440000");
                Pane p = new Pane();
                p.getChildren().add(btn);
                p.getChildren().add(p2);

                root.setTop(p);

                p.setOnMouseClicked(new EventHandler<Event>() {

                    @Override
                    public void handle(Event event) {
                        System.out.println("event source " + event.getSource() + " target : " + event.getTarget());

                    }
                });

                primaryStage.setScene(scene);
                primaryStage.show();
            } catch(Exception e) {
                e.printStackTrace();
            }
        }

        public static void main(String[] args) {
            launch(args);
        }
    }
1
2
5/21/2014 7:51:11 AM

Accepted Answer

By default, the button will consume the mouse click event and the pane will not.

If you want to intercept a button mouse click using a parent pane event handler, then you should use an event filter to do so (to intercept the event during the capturing phase rather than the bubbling phase).

Read up on event processing if you need to understand the capturing versus bubbling concepts.

8
5/21/2014 8:57:08 AM

Thanks to jewelsea's answer I came up with this working example:

package application;



import com.oracle.jrockit.jfr.EventToken;

import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventTarget;
import javafx.event.EventType;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;


    public class Main extends Application {

        double curScale;


        @Override
        public void start(Stage primaryStage) {
            try {
                BorderPane root = new BorderPane();
                Scene scene = new Scene(root,400,400);
                scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());


                Button btn = new Button("test");
                btn.setLayoutX(50);
                Pane p2 = new Pane();
                p2.setPrefSize(20,20);
                p2.setStyle("-fx-background-color: #440000");
                Pane p = new Pane();
                p.getChildren().add(btn);
                p.getChildren().add(p2);

                Pane p3 = new Pane();
                p3.getChildren().add(p);
                root.setTop(p3);

                p3.setOnMouseClicked(new EventHandler<Event>() {

                    @Override
                    public void handle(Event event) {
                        System.out.println("top pane got the clicked event");
                    }
                });


                // Define an event filter
                EventHandler filter = new EventHandler<Event>() {

                    @Override
                    public void handle(Event event) {
                        System.out.println("Filtering out event " + event.getEventType() +  "   source  " + event.getSource() + "   target " + event.getTarget());
                        Node node = (Node) event.getTarget();
                        try{
                            Button b = (Button)event.getSource();
                            node.fireEvent(new MyEvent( b.getText() ));
                        }catch(Exception e){
                            node.fireEvent(new MyEvent( "pane" ));
                        }

                event.consume();
                    }
                };

                btn.addEventFilter(MouseEvent.MOUSE_CLICKED, filter);
                p2.addEventFilter(MouseEvent.MOUSE_CLICKED, filter);

                p.addEventHandler(MyEvent.BUTTON_PRESSED, new MyEventHandler());

                primaryStage.setScene(scene);
                primaryStage.show();
            } catch(Exception e) {
                e.printStackTrace();
            }
        }

        public static void main(String[] args) {
            launch(args);
        }



    // The Handler
    class MyEventHandler implements EventHandler<MyEvent> {

        @Override
        public void handle(MyEvent event) {

            System.out.println("MyEvent " + event.data);

        }
    }

    // The Event
        static class MyEvent extends Event {

            public final static EventType<MyEvent> BUTTON_PRESSED = new EventType(ANY, "BUTTON_PRESSED");

            public String data;

            public MyEvent() {
                this(BUTTON_PRESSED);
            }

            public MyEvent(String what){
                    this(BUTTON_PRESSED);
                    data = what;
            }

            public MyEvent(EventType<? extends Event> arg0) {
                super(arg0);
            }
            public MyEvent(Object arg0, EventTarget arg1, EventType<? extends Event> arg2) {
                super(arg0, arg1, arg2);
            }  
        }
    }

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