Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a Tooltip transparent to mouse events?

Tags:

java

javafx

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?

like image 340
Chris_D_Turk Avatar asked Nov 09 '22 09:11

Chris_D_Turk


1 Answers

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();
};
like image 65
n247s Avatar answered Nov 14 '22 21:11

n247s