How to delegate mouse events to all underlying overlapped panes in JavaFX?
Asked Answered
H

1

3

I have some top decoration pane, which I want to process/preprocess mouse events, but which should not consume them, i.e. all overlapped panes should work as if they were not overlapped by decoration pane.

How to do this? I failed with several tries.

Below is a code with 3 panes. Green one is "decorating". The task is to make it transparent to mouse events. Yellow and blue panes are worker panes. They should work as if they were not overlapped by green pane.

But green pane should receive mouse events nevertheless.

Commented lines indicate what I tries and comments say what appeared wrong:

import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 * Created by dims on 13.10.2016.
 */
public class DelegateEventsToOverlappedNodes extends Application{


   @Override
   public void start(Stage primaryStage) throws Exception {

      StackPane root = new StackPane();
      root.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY)));


      AnchorPane yellowPane = new AnchorPane();
      yellowPane.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
      yellowPane.setOnMouseClicked(new EventHandler<MouseEvent>() {
         @Override
         public void handle(MouseEvent event) {
            System.out.println("yellowPane clicked");
         }
      });


      root.getChildren().add(yellowPane);


      Pane bluePane = new Pane();
      bluePane.setBackground(new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
      bluePane.setOnMouseClicked(new EventHandler<MouseEvent>() {
         @Override
         public void handle(MouseEvent event) {
            System.out.println("bluePane clicked");
         }
      });

      AnchorPane.setLeftAnchor(bluePane, 200.);
      AnchorPane.setRightAnchor(bluePane, 200.);
      AnchorPane.setTopAnchor(bluePane, 200.);
      AnchorPane.setBottomAnchor(bluePane, 200.);


      yellowPane.getChildren().add(bluePane);




      AnchorPane greenPane = new AnchorPane();
      greenPane.setBackground(new Background(new BackgroundFill(Color.rgb(0, 255, 0, 0.9), CornerRadii.EMPTY, Insets.EMPTY)));
      greenPane.setOnMouseClicked(new EventHandler<MouseEvent>() {
         @Override
         public void handle(MouseEvent event) {
            System.out.println("greenPane clicked");
         }
      });


      // greenPane.setVisible(false); // green pane invisible at all

      // greenPane.setMouseTransparent(true); // green clicked doesn't occur

      // greenPane.addEventHandler(Event.ANY, event -> yellowPane.fireEvent(event)); // works for yellow pane, but not for blue sub pane



      root.getChildren().add(greenPane);


      Scene scene = new Scene(root, 800, 600);

      primaryStage.setScene(scene);
      primaryStage.setTitle("DelegateEventsToOverlappedNodes");
      primaryStage.show();

   }

   public static void main(String[] args) {
      DelegateEventsToOverlappedNodes.launch(args);
   }
}
Huntley answered 13/10, 2016 at 14:4 Comment(0)
D
0

You could set greenPane mouse transparent again and add an event filter to root to preprocess the mouse events here:

greenPane.setMouseTransparent(true);
root.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> System.out.println(event));

Filters receive events during the event capturing phase of event processing, whereas handlers are triggered during the event bubbling phase. You could also use an event handler, but I think a filter is more idiomatic in this case (read more here).

Your code:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class DelegateEventsToOverlappedNodes extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        // Yellow pane.
        AnchorPane yellowPane = new AnchorPane();
        yellowPane.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
        yellowPane.setOnMouseClicked(event -> System.out.println("yellowPane clicked"));

        // Blue pane.
        AnchorPane bluePane = new AnchorPane();
        bluePane.setBackground(new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
        bluePane.setOnMouseClicked(event -> System.out.println("bluePane clicked"));
        AnchorPane.setLeftAnchor(bluePane, 200.0);
        AnchorPane.setRightAnchor(bluePane, 200.0);
        AnchorPane.setTopAnchor(bluePane, 200.0);
        AnchorPane.setBottomAnchor(bluePane, 200.0);

        // Green pane.
        AnchorPane greenPane = new AnchorPane();
        greenPane.setBackground(new Background(new BackgroundFill(Color.rgb(0, 255, 0, 0.9), CornerRadii.EMPTY, Insets.EMPTY)));
        greenPane.setMouseTransparent(true);

        // Root pane.
        StackPane rootPane = new StackPane();
        rootPane.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY)));
        rootPane.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> System.out.println(event));

        // Layout and scene.
        yellowPane.getChildren().add(bluePane);
        rootPane.getChildren().addAll(yellowPane, greenPane);

        primaryStage.setScene(new Scene(rootPane, 800.0, 600.0));
        primaryStage.setTitle("DelegateEventsToOverlappedNodes");
        primaryStage.show();
    }

}

If you created greenPane only to preprocess events, there's no need for it anymore.

Dubrovnik answered 13/10, 2016 at 16:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.