37

I have two layers (= AnchorPanes) stacked one of the other with a StackPane. So both layer fill the whole scene. The problem is, that only the top layer receives mouse events.

Thats how the scene is build:

Layout of the Scene

Only Button B receives click events but Button A not.

Button A don't Receive Click Events

If I set Layer B to mouse transparent (layerB.setMouseTransparent(true)), Button A receives mouse events. But mouse transparent effects also all children, so Button B dont receives mouse events any more.

How to get Button A and Button B to receive mouse events?

Here is the full working source:

public class LayerTest extends Application {
    @Override
    public void start(final Stage primaryStage) throws Exception {
        final Node layerA = createLayerA();
        final Node layerB = createLayerB();
        final Parent root = new StackPane(layerA, layerB);
        final Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.setWidth(250);
        primaryStage.setHeight(200);
        primaryStage.show();
    }

    private Node createLayerA() {
        final AnchorPane layerA = new AnchorPane();
        final Button buttonA = new Button("Button A");
        AnchorPane.setLeftAnchor(buttonA, 10d);
        AnchorPane.setTopAnchor(buttonA, 10d);
        buttonA.setOnMouseClicked(e -> System.out.println("Button A clicked"));
        layerA.getChildren().setAll(buttonA);
        return layerA;
    }

    private Node createLayerB() {
        final AnchorPane layerB = new AnchorPane();
        final Button buttonB = new Button("Button B");
        AnchorPane.setRightAnchor(buttonB, 10d);
        AnchorPane.setBottomAnchor(buttonB, 10d);
        buttonB.setOnMouseClicked(e -> System.out.println("Button B clicked"));
        layerB.getChildren().setAll(buttonB);
        return layerB;
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Vertex
  • 2,682
  • 3
  • 29
  • 43
  • 2
    See [JavaFX 2 event dispatching to underlying nodes](http://stackoverflow.com/questions/15248716/javafx-2-event-dispatching-to-underlying-nodes) and [JavaFx, event interception/consumption](http://stackoverflow.com/questions/19498867/javafx-event-interception-consumption) for the hint. But changing your layout hierarchy will be more appropriate. All other workarounds will seem hackish and take some your hair down from your head in the future. – Uluk Biy Jul 07 '14 at 14:42
  • @UlukBiy is correct and you need to change the hierarchy of the Layouts. May be you can try using a single AnchorPane instead of using two of them. A similar problem is [explained here](http://stackoverflow.com/questions/24510845/stackpane-mouseclick-to-be-listened-by-both-its-children) – ItachiUchiha Jul 07 '14 at 16:52

2 Answers2

39

Solution

Add the following line to your sample code:

layerB.setPickOnBounds(false);

This will allow the mouse to interact with the visible elements you can see through the layers of your stacked elements.

If elements in the top layer overlap elements in the bottom layer clicking on the part of the top layer which overlaps the bottom layer will have the top layer consume the mouse event and the bottom layer will not receive it (which is probably what you want).

Alternate Interpretation

If you actually wanted to intercept and handle the mouse event in all layers then see the linked questions from Uluk's comments:

Method Description

A description of the setPickOnBounds method:

Defines how the picking computation is done for this node when triggered by a MouseEvent or a contains function call. If pickOnBounds is true, then picking is computed by intersecting with the bounds of this node, else picking is computed by intersecting with the geometric shape of this node.


Panes have no visible background by default, so why they should consume mouse events?

For modena.css, the default stylesheet that ships with JavaFX 8, Panes actually do have a very faint shaded background by default, so they can consume mouse events. To prevent this you can either set the background color of the pane to null or set the Pane to mouseTransparent.

This behavior changed between JavaFX 2 and JavaFX 8. JavaFX 2 shipped with a default stylesheet named caspian.css, which does not set a background for Panes.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Thank you for your detailed answer, it works fine :) I don't tried to set pick on bounds to false for my self, because the Java Doc said for Node, that the default value is false. But taking a look into the source code of Region, it turns out that is set to true by default. I think that the default value for layouts, i. e. Panes, should be false. Panes have no visible background by default, so why they should consume mouse events? – Vertex Jul 08 '14 at 08:43
  • Thanks. Setting top pane background color of the pane to null and `topPane.setPickOnBounds(false);` works fine – c0der Jul 10 '17 at 15:43
  • "For modena.css, the default stylesheet that ships with JavaFX 8, Panes actually do have a very faint shaded background by default, so they can consume mouse events" -- how does that work? Panes donot have a style class, so how can `modena.css` assign them a background? There are a few special ones like titled-pane and split-pane, but nothing for panes in general. – john16384 Aug 06 '20 at 23:35
  • @john16384 Good question. This was true at the time when I tested it many years ago. It may no longer be true now (I have not tested it recently). I think it occurs because a Pane is a Region. A Region is styleable and can have a background set. Modena has a [line in it](https://github.com/openjdk/jfx/blob/master/modules/javafx.controls/src/main/resources/com/sun/javafx/scene/control/skin/modena/modena.css#L379) that sets the background color on `.root`. I think this line may cause the regions to default to use that background color unless overridden. – jewelsea Aug 07 '20 at 03:57
3

This cant work becouse the Event is catched by LayerB. You can set LayerB to MouseTransparent

layerB.setMouseTransparent(true)

But im not sure that button b is mousetranparent too after this.

Just give it a try, ohterwise do you thoughts about a different layout.

Marcel
  • 1,606
  • 16
  • 29
  • 1
    Thank you! Yes you're right, the events get consumed by LayerB. But `layerB.setMouseTransparent(true)` doesn't work. The JavaDoc says, that the method affects not only the node it self but its children too. Because ButtonB is a child of LayerB, ButtonB wouldn't get mouse events. – Vertex Jul 08 '14 at 08:48