JavaFX 2.2 Mouseevent for invisible node

I'm trying to get MouseEvents for an invisible node in JavaFX 2.2. Think of it as an interactive but invisible area that should trigger an action, like when the mouse hovers it. The problem is that this is not a statically defined zone, but there are several zones (many) that can be moved and changed in the application. So there would be a lot of overhead for my use case to listen to mouse movement globally and do manual detection - say MouseMove-Events.

I am currently experimenting with transparent Rectangle ( new Rectangle(200, 100, Color.TRANSPARENT)

), but the actual / final application will use some kind of panel for it, because it is actually a drag-and-drop container for other components (when not filled with components, it has transparent areas, and MouseMoves should also be detected on these transparent areas).

I would also like some answers to help me better understand how JavaFX 2.2 normally handles MouseEvents based on node visibility.

My experiments showed the following general ideas:

  • Given the transparent scene: Mouse events will only be sent to foreign applications (visually below the scene) when the user clicks on the transparent area. It is not possible to dispatch a mouse event "to the OS" when the user clicks on a visible pixel in the scene. Right?

  • The panel on top of other nodes will by default swallow any MouseEvent, unless it is MouseTransparent or MouseClick (transparent) area is displayed.

  • pickOnBounds(true|false)

    to enable ( true

    ) constraint based on (rectangular) MouseEvents detection or disable it ( false

    ). Latter effectively handles mouse events for visible pixels / areas only. pickOnBounds(true)

    doesn't seem to work for completely invisible nodes. Right?

  • My experiments have shown that a node needs at least padding - new Color(1,1,1,0.004)

    to be considered visible. Lower alpha values ​​are considered invisible, which results in MouseEvents not being processed even if called pickOnBounds(true)

    .

Did I get it right? Then there would be no way for the invisible node to receive MouseEvents.

Or is there a special requirement for the job pickOnBounds

? Does it need to be called only after node or something similar has been shown? Any other suggestions?

+3


source to share


1 answer


In short: use Node.setOpacity (0.0)

The opacity property controls the visual transparency of the Node without affecting its ability to receive events, see APIdocs . Setting this property to zero provides the effect you were looking for (and I am): an invisible but mouse-sensitive "hot zone" - Node.

Unlike the Node.setVisible(false)

one I tried in the first place. This approach also disables event handling. From Node.setVisible () APIdocs:

Invisible nodes never receive mouse events or keyboard focus, and never maintain keyboard focus when they become invisible.

"Invisible" really means "after invocation setVisible(false)

" and should not be confused with opacity or fully transparent image pixels.

Due to lack of reputation, I cannot post the screenshot directly, so: link to the screenshot that shows the location of the hot zone in the example code below (Node's opacity is not set to 0 in the screenshot for obvious reasons).

The example uses a group as a hot zone that contains a rectangle and a circle to define the areas where mouse events are captured. The opacity property and mouse handler only need to be set on the group, not on the children.

This way you can build free-form hot zones. If you want to use an image with transparent areas as a hot zone, its property pickOnBounds

must be set to false

, so the actual content of the image is taken into account, not just the bounding box.

Hope it helps!

public class HotZoneTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        Scene scene = new Scene(root, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();

        Group hotZone = new Group();
        root.getChildren().add(hotZone);

        hotZone.getChildren().add(new Rectangle(10, 20, 100, 50));
        hotZone.getChildren().add(new Circle(50, 120, 20));

        hotZone.setOpacity(0.4); //set to 0.0 to make invisible

        EventHandler handler = new EventHandler() {
            @Override
            public void handle(Event e) {
                System.out.println("hotZone mouse event: " + e);
            }
        };
        hotZone.addEventHandler(MouseEvent.MOUSE_ENTERED, handler);
        hotZone.addEventHandler(MouseEvent.MOUSE_CLICKED, handler);
        hotZone.addEventHandler(MouseEvent.MOUSE_EXITED, handler);

    }

      

edit: regarding your specific sub-suggestions (all as far as I know I'm not an FX guru :))

It is not possible to dispatch a mouse event "to OS" when the user clicks on a visible pixel in the scene. Right?



Interesting, never tried it. Pure speculation on what might work: get the screen coordinates of the mouse event, take your window out of the way, use java.awt.Robot to move the OS cursor to the coordinates of the mouse event, shoot there if necessary, and move the window back. Beware: Sounds like a generic hack!

The panel on top of other nodes will by default swallow any MouseEvent unless it is MouseTransparent or MouseClick is mapped to an invisible (transparent) area.

The way I understand it; Not sure if the mouse is getting in and out. For those who can listen MOUSE_ENTERED_TARGET/MOUSE_EXITED_TARGET

, at least in the parent, to determine which one was injected / taken out. Register an event filter on the parent device and use the event there if you want the child not to receive the event.

pickOnBounds (true | false) to enable (true) constraint-based (rectangular) MouseEvents detection or disable it (false). the latter effectively handles mouse events for visible pixels / areas only.

Yes.

pickOnBounds (true) doesn't seem to work for completely invisible nodes.

True for nodes made invisible by calling setInvisible(true)

.

My experiments have shown that a Node requires at least padding - the new Color (1,1,1,0.004) is considered visible.

Can't comment, but the result of your experiment seems sound.

Then there would be no way to get invisible Node MouseEvents.

Usage .setOpacity(0.0)

makes Node "visually invisible" but still accepts events and honors setbickOnBounds

+2


source







All Articles