This is a little bit novel – using FXExperience for a post that isn’t the weekly links! 🙂 I wish I could post here more often, but time constraints pretty much stop that in its tracks (even the weekly links take longer than I would like). Anyway, I often get asked about node picking in JavaFX, and if the Node.impl_pickNode(...) API will ever lose the ‘impl’ and become proper API.

The answer is: I don’t know – picking API is not ‘mine’ to worry about. Frankly, I’m barely qualified to discuss picking in any depth, as it is not something I use. Despite this, I will give my unqualified summary for those of you who are unfamiliar with what picking is: it is basically about getting the best matching (i.e. top-most) Node under a given x, y coordinate.

A few months back I was working in the Scene Builder code base to remove dependencies on these impl methods (and to generally clean it up to work on JDK 9 given all the changes due to project Jigsaw (i.e. modularity) and new APIs in JavaFX 9). One thing I had to replace was the use of Node.impl_pickNode(...). I ended up writing the code below, and given that I’m asked about this functionality a lot, I thought I would post it here in case others found it useful.

I should be very clear that this is not a performant implementation, and it is built with my naivety regarding picking (so I am happy to be sent suggestions for improving it). Nevertheless, I hope it helps.

public static Node pick(Node node, double sceneX, double sceneY) {
    Point2D p = node.sceneToLocal(sceneX, sceneY, true /* rootScene */);

    // check if the given node has the point inside it, or else we drop out
    if (!node.contains(p)) return null;

    // at this point we know that _at least_ the given node is a valid
    // answer to the given point, so we will return that if we don't find
    // a better child option
    if (node instanceof Parent) {
        // we iterate through all children in reverse order, and stop when we find a match.
        // We do this as we know the elements at the end of the list have a higher
        // z-order, and are therefore the better match, compared to children that
        // might also intersect (but that would be underneath the element).
        Node bestMatchingChild = null;
        List<Node> children = ((Parent)node).getChildrenUnmodifiable();
        for (int i = children.size() - 1; i >= 0; i--) {
            Node child = children.get(i);
            p = child.sceneToLocal(sceneX, sceneY, true /* rootScene */);
            if (child.isVisible() && !child.isMouseTransparent() && child.contains(p)) {
                bestMatchingChild = child;
                break;
            }
        }

        if (bestMatchingChild != null) {
            return pick(bestMatchingChild, sceneX, sceneY);
        }
    }

    return node;
}