ph73nt ph73nt - 3 months ago 19
Java Question

JavaFx Transparent window - yes please. Mouse transparent - no thanks

I would like to create a simple JavaFx class that shows the user a translucent rectangle (say an arbitrary 50% transparency) covering the users screen. It should simply allow me to get the Point of a mouse click event. This sounds trivial, but when I create transparent windows they always seem to be transparent to mouse events rather than just my requirement of semi-transparent visibility. The mouse event is never triggered.

I've used setMouseTransparent(false) on the rectangle and the root pane, but this makes no difference. I'd be really grateful if somebody could indicate any errors/misconceptions.

Here's the trivial test class I have created:

public class ClickScreen implements MouseListener {

private ClickScreenListener listener;
private Stage window;
private Point point;

public ClickScreen(ClickScreenListener listener) {

// Get screen size
Rectangle2D r = Screen.getPrimary().getBounds();

// Something to put stuff in
StackPane root = new StackPane();

// Translucent rectangle on the pane
Rectangle rectangle = new Rectangle(r.getWidth(), r.getHeight());
rectangle.setFill(Color.rgb(183, 183, 183, 0.5));
root.getChildren().add(rectangle);

Scene scene = new Scene(root, r.getWidth(), r.getHeight());
scene.setFill(null);

window = new Stage();
window.initStyle(StageStyle.TRANSPARENT);
window.setTitle("Click drop location");
window.setScene(scene);

this.listener = listener;

}

public Point getLocation(){
return point;
}

@Override
public void mouseClicked(MouseEvent e) {
point = e.getLocationOnScreen();
listener.screenClicked(point);
}
}


Edit:
A simpler example of the transparency issue I am experiencing is from this Hello World! example. When I mouse over the button, it's about 50:50 chance of clicking the button or just clicking "through" and giving focus to the underlying window (which is usually eclipse in my case). Would love you thoughts on this.

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

@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {

@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});

StackPane root = new StackPane();
root.getChildren().add(btn);

Scene scene = new Scene(root, 300, 250);
scene.setFill(null);

primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.show();
}
}

Answer

Check your Imports

You are using some kind of weird setup where you are mixing AWT/Swing classes and JavaFX classes, which really isn't advised (and doesn't work at all in the combination and manner you have used). Just be careful in your JavaFX programs not to import any java.awt.* or javax.swing.* classes unless you really know what you are doing in mixing code for two different toolkits.

Sample Solution

Here is a sample solution which imports only JavaFX classes and utilizes JavaFX events, but otherwise tries to stick to the coding/callback style of the sample code in your question. (The sample could be further simplified through use of Java 8 lambdas).

enter image description here

import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.*;

public class ClickListenerSample 
                 extends Application 
                 implements ClickScreenListener {

    private Label clickFeedbackLabel = new Label("");

    @Override public void start(Stage stage) {
        Button listen = new Button("listen");
        listen.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                new ClickScreen(ClickListenerSample.this);
            }
        });
        VBox layout = new VBox(10);
        layout.getChildren().setAll(
            listen,
            clickFeedbackLabel
        );
        layout.setPadding(new Insets(10));

        stage.setScene(new Scene(layout, 100, 80));
        stage.show();
    }

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

    @Override public void screenClicked(Point2D point) {
        clickFeedbackLabel.setText(point.getX() + ", " + point.getY());
    }
}

interface ClickScreenListener {
    void screenClicked(Point2D point);
}

class ClickScreen {
    private ClickScreenListener listener;
    private Stage window;
    private Point2D point;

    public ClickScreen(ClickScreenListener listener) {
        // Get screen size
        Rectangle2D r = Screen.getPrimary().getBounds();

        // Something to put stuff in
        StackPane root = new StackPane();
        root.setStyle("-fx-background-color: null;");

        // Translucent rectangle on the pane
        Rectangle rectangle = new Rectangle(r.getWidth(), r.getHeight());
        rectangle.setFill(Color.rgb(183, 183, 183, 0.5));
        root.getChildren().add(rectangle);

        Scene scene = new Scene(root, r.getWidth(), r.getHeight());
        scene.setFill(null);

        window = new Stage();
        window.initStyle(StageStyle.TRANSPARENT);
        window.setTitle("Click drop location");
        window.setScene(scene);

        scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                point = new Point2D(event.getScreenX(), event.getScreenY());
                listener.screenClicked(point);
                window.hide();
            }
        });

        window.show();

        this.listener = listener;
    }

    public Point2D getLocation(){
        return point;
    }
}
Comments