JavaFX looping over scenegraph controls
Asked Answered
B

3

8

How can I loop over the controls of the scene ? I tried with getChildrenUnmodifiable() but it returns only the first level of children.

public void rec(Node node){

    f(node);

    if (node instanceof Parent) {
        Iterator<Node> i = ((Parent) node).getChildrenUnmodifiable().iterator();

        while (i.hasNext()){
            this.rec(i.next());
        }
    }
}
Brain answered 22/10, 2012 at 12:1 Comment(0)
U
8

You need to scan recursively. For example:

private void scanInputControls(Pane parent) {
    for (Node component : parent.getChildren()) {
        if (component instanceof Pane) {
            //if the component is a container, scan its children
            scanInputControls((Pane) component);
        } else if (component instanceof IInputControl) {
            //if the component is an instance of IInputControl, add to list
            lstInputControl.add((IInputControl) component);
        }
    }
}
Unnecessary answered 22/10, 2012 at 12:37 Comment(0)
I
9

Here's a modified version of amru's answer that I am using, this method gives you the components of a specific type:

private <T> List<T> getNodesOfType(Pane parent, Class<T> type) {
    List<T> elements = new ArrayList<>();
    for (Node node : parent.getChildren()) {
        if (node instanceof Pane) {
            elements.addAll(getNodesOfType((Pane) node, type));
        } else if (type.isAssignableFrom(node.getClass())) {
            //noinspection unchecked
            elements.add((T) node);
        }
    }
    return Collections.unmodifiableList(elements);
}

To get all components:

List<Node> nodes = getNodesOfType(pane, Node.class);

To only get buttons:

List<Button> buttons= getNodesOfType(pane, Button.class);
Idolum answered 9/7, 2015 at 18:2 Comment(1)
This does not account for items within a ScrollPane since it extends Control, not Pane...Rockwell
U
8

You need to scan recursively. For example:

private void scanInputControls(Pane parent) {
    for (Node component : parent.getChildren()) {
        if (component instanceof Pane) {
            //if the component is a container, scan its children
            scanInputControls((Pane) component);
        } else if (component instanceof IInputControl) {
            //if the component is an instance of IInputControl, add to list
            lstInputControl.add((IInputControl) component);
        }
    }
}
Unnecessary answered 22/10, 2012 at 12:37 Comment(0)
M
0

you could also pass in a predicate, which allows any kind of selection.

Predicate<Node> p= n -> null ≃ n.getId() && n instanceof TextInputControl 

would get all TextFields and TextAreas.

You can pack it all in an interface, Java 8 style, and then you only need to implement Parent getRoot() in the Pane or other container.

@FunctionalInterface
public interface FXUIScraper {
    // abstract method.
    Parent getRoot();

    default List<Node> scrape( Predicate<Node> filter ) {
        Parent top = getRoot();

        List<Node> result = new ArrayList<>();
        scrape( filter, result, top );
        return result;
    }

    static void scrape( Predicate<Node> filter, List<Node> result, Parent top ) {
        ObservableList<Node> childrenUnmodifiable = top.getChildrenUnmodifiable();
        for ( Node node : childrenUnmodifiable ) {
            if ( filter.test( node ) ) {
                result.add( node );
            }
            if ( node instanceof Parent ) {
                scrape( filter, result, (Parent)node );
            }
        }
    }
}

Assuming that your Pane is called pane:

   FXUIScraper scraper = () ->pane;
   List<Node> textNodesWithId = 
        scraper.scrape(n -> null ≃ n.getId()
                    && n instanceof TextInputControl);

If you have meaningful ids like names of fields of an entity or key names in json object, it becomes trivial to process the result into a desired form. There is a project on github containing the fxuiscraper as a separate project.

Mongol answered 13/5, 2021 at 8:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.