2

The JavaFX MouseEvent is only delivered to the top-most node which is not mouse transparent. I'm looking for a way to deliver a MouseEvent to multiple nodes (mouse translucent, maybe?).

In the example below, I have 2 partially overlapping circles. The top circle listens to MOUSE_CLICKED events to change its color. The bottom circle receives MOUSE_ENTERED and MOUSE_EXITED to update its hover property, changing its color while the mouse is over it.

When the top circle is mouse transparent, the bottom circle behaves as desired, but the top circle no longer receives MOUSE_CLICKED events. If the top circle is not mouse transparent, then the bottom circle sees MOUSE_EXITED when the mouse passes over the top circle, even if the mouse remains inside the bottom circle's shape.

It is possible to support both behaviors simultaneously?

public class MainApp extends Application {
  private final Random RND = new Random();

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

      Circle bottomCircle = new Circle(150, 150, 100, Color.BLUE);
      bottomCircle.fillProperty().bind(Bindings.when(bottomCircle.hoverProperty()).then(Color.AQUA).otherwise((Color.BLUE)));

      Circle topCircle = new Circle(200, 100, 40, randColor());
      topCircle.setOnMouseClicked((event) -> topCircle.setFill(randColor()));

      CheckBox mouseTransparencyCheckBox = new CheckBox("Top Circle Mouse Transparency");
      topCircle.mouseTransparentProperty().bind(mouseTransparencyCheckBox.selectedProperty());

      Pane pane = new Pane();
      pane.setPrefSize(300, 300);
      pane.getChildren().addAll(mouseTransparencyCheckBox, bottomCircle, topCircle);

      Scene scene = new Scene(pane);
      stage.setScene(scene);
      stage.show();
  }

  private Color randColor() {
    return Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
  }

  public static void main(String[] args) {
      launch(args);
  }
}
fastryan
  • 173
  • 1
  • 6
  • Here is half the answer: `topCircle.addEventHandler(EventType.ROOT, (event) -> { bottomCircle.fireEvent(new MouseEvent(MouseEvent.MOUSE_ENTERED, bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), MouseButton.NONE, 0, true, true, true, true, true, true, true, true, true, true, null)); });` – SedJ601 Mar 05 '19 at 21:39
  • https://stackoverflow.com/questions/15248716/javafx-2-event-dispatching-to-underlying-nodes – SedJ601 Mar 05 '19 at 21:46

1 Answers1

1

I figured it out using a little math. Catch all event's that happen on the top circle. If the event is Mouse entered, moved, or exited, see if the mouse is within the bottom circle.

import java.util.Random;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.EventType;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication349 extends Application
{

    private final Random RND = new Random();
    Circle bottomCircle = new Circle(150, 150, 100, Color.BLUE);
    Circle topCircle = new Circle(200, 100, 40, randColor());

    MouseEvent enteredBottomCircle = new MouseEvent(MouseEvent.MOUSE_ENTERED, bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), MouseButton.NONE, 1, true, true, true, true, true, true, true, true, true, true, null);
    MouseEvent exitedBottomCircle = new MouseEvent(MouseEvent.MOUSE_EXITED, bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), bottomCircle.getLayoutX(), bottomCircle.getLayoutY(), MouseButton.NONE, 1, true, true, true, true, true, true, true, true, true, true, null);

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

        bottomCircle.fillProperty().bind(Bindings.when(bottomCircle.hoverProperty()).then(Color.AQUA).otherwise((Color.BLUE)));

        topCircle.addEventHandler(EventType.ROOT, (event) -> {
            System.out.println(event.getEventType());

            if (event.getEventType() == MouseEvent.MOUSE_ENTERED || event.getEventType() == MouseEvent.MOUSE_MOVED || event.getEventType() == MouseEvent.MOUSE_EXITED) {
                MouseEvent event1 = (MouseEvent) event;

                if (Math.sqrt(Math.pow((event1.getSceneX() - bottomCircle.getCenterX()), 2) + Math.pow((event1.getSceneY() - bottomCircle.getCenterY()), 2)) < bottomCircle.getRadius()) {
                    System.out.println("entered bottom circle");
                    bottomCircle.fireEvent(enteredBottomCircle);
                }

                else {
                    System.out.println("exited bottom circle");
                    bottomCircle.fireEvent(exitedBottomCircle);
                }
            }

        });

        topCircle.setOnMouseClicked((event) -> topCircle.setFill(randColor()));

        CheckBox mouseTransparencyCheckBox = new CheckBox("Top Circle Mouse Transparency");
        topCircle.mouseTransparentProperty().bind(mouseTransparencyCheckBox.selectedProperty());

        Pane pane = new Pane();
        pane.setPrefSize(300, 300);
        pane.getChildren().addAll(mouseTransparencyCheckBox, bottomCircle, topCircle);

        Scene scene = new Scene(pane);
        stage.setScene(scene);
        stage.show();
    }

    private Color randColor()
    {
        return Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
    }

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

}
SedJ601
  • 12,173
  • 3
  • 41
  • 59