Petter Thowsen Petter Thowsen - 20 days ago 9
Java Question

JavaFX Click through overlaying stackpane

schematic

As you can see in the drawing above, I have a stackpane containing two elements, a BorderPane (which again contains a canvas and a statusbar) and another stackpane (which contains some other UI things).

I'd like to be able to click through from invisible areas of the green stackpane to the yellow borderpane but still allow clicking on actual UI stuff on the green stackpane (where there is clickable things like buttons etc.).

How do you do this?

Answer

You can use stackPane.setPickOnBounds(false);. This means that the stack pane will only be identified as the target of a mouse action if the point on which it was clicked was not transparent (instead of the default behavior, which is to identify it as the target of the mouse action if the mouse click is within its bounds).

Here is an SSCCE:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ClickThroughStackPane extends Application {

    @Override
    public void start(Stage primaryStage) {
        Canvas canvas = new Canvas(400,400);
        canvas.setOnMouseClicked(e -> System.out.println("Mouse click: canvas"));
        HBox statusBar = new HBox(new Label("Status"));
        statusBar.setOnMouseClicked(e -> System.out.println("Mouse click: statusBar"));
        BorderPane borderPane = new BorderPane(canvas, statusBar, null, null, null);

        Button button = new Button("Click");
        button.setOnAction(e -> System.out.println("Button pressed"));
        StackPane stack = new StackPane(button);

        stack.setPickOnBounds(false);

        StackPane root = new StackPane(borderPane, stack);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

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

Note that the top stack pane seems unnecessary, as you could simply add the UI elements it contains directly to the underlying stack pane. The previous example could simply be rewritten:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ClickThroughStackPane extends Application {

    @Override
    public void start(Stage primaryStage) {
        Canvas canvas = new Canvas(400,400);
        canvas.setOnMouseClicked(e -> System.out.println("Mouse click: canvas"));
        HBox statusBar = new HBox(new Label("Status"));
        statusBar.setOnMouseClicked(e -> System.out.println("Mouse click: statusBar"));
        BorderPane borderPane = new BorderPane(canvas, statusBar, null, null, null);

        Button button = new Button("Click");
        button.setOnAction(e -> System.out.println("Button pressed"));

        StackPane root = new StackPane(borderPane, button);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

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