ngongocthinh159
ngongocthinh159

Reputation: 21

Javafx event handler overlap

I have a pane, inside the pane I have a circle and a label. Everytime mouse enter or exit the circle, the label will change to "enter" or "exit". And the label will move accordingly to the mouse position (mouse scenceX and sceneY).

The problem is when I enter the mouse inside the circle, the label will change to "enter" but will immediately change to "exit" then.

I think there is an overlap eventhandler somewhere in my code, but I don't know why. This is my code:

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Pane pane = new Pane();
        pane.setStyle("-fx-background-color: grey;");
        pane.setPrefSize(300,300);

        Circle circle = new Circle();
        circle.setFill(Color.TRANSPARENT);
        int radius = 50;
        circle.setRadius(radius);
        circle.setStroke(Color.BLACK);
        circle.setStrokeWidth(2);
        circle.setLayoutX(100);
        circle.setLayoutY(60);
        pane.getChildren().add(circle);

        Label label = new Label("Mouse point is outside the circle");
        pane.getChildren().add(label);

        circle.addEventHandler(MouseEvent.MOUSE_EXITED, e -> {
            label.setText("Mouse point is outside the circle");
            System.out.println("exit");
        });
        circle.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> {
            label.setText("Mouse point is inside the circle");
            System.out.println("enter");
        });
        pane.addEventFilter(MouseEvent.MOUSE_MOVED, e -> {
            label.setLayoutX(e.getSceneX());
            label.setLayoutY(e.getSceneY());
        });

        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(pane));
        primaryStage.show();
    }


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

Upvotes: 2

Views: 69

Answers (2)

James_D
James_D

Reputation: 209714

As explained in another answer, when the label moves, it moves so that the mouse is now inside the label; this means it is no longer hovering directly over the circle, so an "exited" event is fired on the circle.

The simplest fix is just to make the label ignore the mouse, which can be done using the mouseTransparent property:

    Label label = new Label("Mouse point is outside the circle");
    label.setMouseTransparent(true);
    pane.getChildren().add(label);

Upvotes: 1

Abra
Abra

Reputation: 20924

I'm only guessing but I believe the problem is that when the event filter for pane is executed, the result is that the mouse pointer moves from being inside circle to inside label. Then when you move the mouse, it leaves label and enters circle which causes the MOUSE_ENTERED event handler to fire. After that handler fires, the event filter fires which moves the mouse pointer out of circle and into label which causes MOUSE_EXITED event handler to fire. That's why you see enter and exit repeatedly when you move the mouse pointer around inside circle. You don't see enter nor exit at all when you move the mouse pointer around pane when it is outside of circle.

In order to fix it, I removed MOUSE_ENTERED and MOUSE_EXITED and inside the event filter, I check whether the mouse pointer is inside the bounds of circle and set label text accordingly.

Here is the code. (Note that I replaced addEventFilter with setOnMouseMoved.)

import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Pane pane = new Pane();
        pane.setStyle("-fx-background-color: grey;");
        pane.setPrefSize(300, 300);

        Label label = new Label("Mouse point is outside the circle");

        Circle circle = new Circle();
        circle.setFill(Color.TRANSPARENT);
        int radius = 50;
        circle.setRadius(radius);
        circle.setStroke(Color.BLACK);
        circle.setStrokeWidth(2);
        circle.setLayoutX(100);
        circle.setLayoutY(60);
        Bounds boundingBox = circle.getBoundsInParent();
        double maxX = boundingBox.getMaxX();
        double minX = boundingBox.getMinX();
        double maxY = boundingBox.getMaxY();
        double minY = boundingBox.getMinY();

        pane.getChildren().add(circle);
        pane.getChildren().add(label);
        pane.setOnMouseMoved(e -> {
            double x = e.getSceneX();
            double y = e.getSceneY();
            if (minX < x && x < maxX && minY < y && y < maxY) {
                label.setText("Mouse point is inside the circle");
            }
            else {
                label.setText("Mouse point is outside the circle");
            }
            label.setLayoutX(e.getSceneX());
            label.setLayoutY(e.getSceneY());
        });
        primaryStage.setTitle("Hello World");
        Scene scene = new Scene(pane);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Upvotes: 1

Related Questions