Chris_D_Turk
Chris_D_Turk

Reputation: 452

How to make a Tooltip transparent to mouse events?

A javafx.scene.Node has the ability to make it transparent to mouse events, so that it won't be selected as target for such events:

Node.mouseTransparentProperty()
If true, this node (together with all its children) is completely transparent to mouse events. When choosing target for mouse event, nodes with mouseTransparent set to true and their subtrees won't be taken into account.

Unfortunately this features is not yet implemented for javafx.scene.control.Tooltip.
There is an open feature request for that - but there doesn't seem to be a lot of activity on that topic.

My question is: Is there any workaround for this? How can I make a Tooltip mouse-transparent to route mouse events to the underlying control?

Upvotes: 5

Views: 849

Answers (1)

n247s
n247s

Reputation: 1912

If someone is still looking for a solution. I found a hacky way in javafx-8 (using internal API!!). Its propably patched in javafx-8+, so from a maintainablility standpoint not a good option, but at least something:

    public static boolean correctNativeMouseEvent(MouseEvent event, Scene exclude)
    {
        Scene targetScene = getTargetScreen(event, exclude);
        if(targetScene != null)
        {
            PickResultChooser chooser = new PickResultChooser();

            targetScene.getRoot().impl_pickNode(new PickRay(event.getScreenX() - targetScene.getWindow().getX() - targetScene.getX(),
                    event.getScreenY() - targetScene.getWindow().getY() - targetScene.getY(),
                    1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), chooser);

            PickResult res = chooser.toPickResult();
            if(res != null)
            {
                Point2D pos = res.getIntersectedNode().localToScene(res.getIntersectedPoint().getX(), res.getIntersectedPoint().getY());

                MouseEvent newEvent = new MouseEvent(null, null, event.getEventType(), pos.getX(), pos.getY(),
                        event.getScreenX(), event.getScreenY(),
                        event.getButton(), event.getClickCount(),
                        event.isShiftDown(), event.isControlDown(), event.isAltDown(), event.isMetaDown(),
                        event.isPrimaryButtonDown(), event.isMiddleButtonDown(), event.isSecondaryButtonDown(),
                        event.isSynthesized(), event.isPopupTrigger(), event.isStillSincePress(), res);

                targetScene.impl_processMouseEvent(newEvent);
                return true;
            }
        }
        return false;
    }

    static Scene getTargetScreen(MouseEvent event, Scene exclude)
    {
        double x = event.getScreenX();
        double y = event.getScreenY();

        double sx, sy, sw, sh;

        Iterator<Window> itr = Window.impl_getWindows();

        if(itr.hasNext())
        {
            for(Window w = itr.next(); itr.hasNext(); w = itr.next())
            {
                sx = w.getX();
                sy = w.getY();
                sw = w.getWidth();
                sh = w.getHeight();

                if(sx < x && x < sx + sw
                        && sy < y && y < sy + sh
                        && w.getScene() != exclude)
                    return w.getScene();
            }
        }
        return null;
    }

upon creating a tooltip you just add the following:

Tooltip tp = new Tooltip();
// use filter to catch before anything can be consumed
tp.addEventFilter(MouseEvent.ANY, E -> {
    // now correct the event
    correctNativeMouseEvent(E, tp.getScene());
    // although it is optionally, I would recommend to just consume the event anyways
    E.consume();
};

Upvotes: 1

Related Questions