Adam
Adam

Reputation: 36703

JavaFX transparent window only receives mouse events over drawn pixels

I'd like a Stage that is the same size as the screen which is fully transparent and receives mouse events anywhere. In the example below I get mouse events only when the mouse is over the circle. I see this issue on both Windows XP and Windows 7 using Java 8u11

import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class TransparentTest extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage ignored) throws Exception {
        Stage stage = new Stage(StageStyle.TRANSPARENT);
        stage.setTitle("Transparent app test");

        Rectangle2D screenBounds = Screen.getPrimary().getBounds();
        stage.setX(0);
        stage.setY(0);
        stage.setWidth(screenBounds.getWidth());
        stage.setHeight(screenBounds.getHeight());

        Circle circle = new Circle(100);
        circle.setFill(Color.RED);
        Rectangle rectangle = new Rectangle(screenBounds.getWidth(),
                screenBounds.getHeight());
        rectangle.setFill(Color.TRANSPARENT);
        Scene scene = new Scene(new StackPane(circle, rectangle));
        scene.setFill(null);
        stage.setScene(scene);

        scene.setOnMouseMoved((e) -> {
            System.out.println("Mouse over rectangle " + e);
        });
        stage.show();
    }
}

Interestingly if I set the alpha part of the fill color to its absolute minimum then I get mouse events. However I'd prefer not to use this workaround and actually get to the bottom of the issue. My conclusion is somewhere in JavaFX or a Windows library there is some hit-detection code that filters mouse events based on the pixel value of the mouse event.

 rectangle.setFill(Color.rgb(0, 0, 0, 1d / 255d)); // receives mouse events        
 rectangle.setFill(Color.rgb(0, 0, 0, 0));         // does not receive mouse events

Research

Hit testing of a layered window is based on the shape and transparency of the window. This means that the areas of the window that are color-keyed or whose alpha value is zero will let the mouse messages through. If the layered window has the WS_EX_TRANSPARENT extended window style, the shape of the layered window will be ignored and the mouse events will be passed to the other windows underneath the layered window.

Upvotes: 3

Views: 1164

Answers (1)

Adam
Adam

Reputation: 36703

In summary only known solution is to set the background to be "not quite" transparent to fool JavaFX into sending events.

rectangle.setFill(Color.rgb(0, 0, 0, 1d / 255d)); // receives mouse events

Upvotes: 3

Related Questions