Skip to content
Advertisement

How to force control skin to be created so I can use getParent?

In the following minimal example when the parents of tabs and tabs2 are printing out, they are both null.

From this question I’ve come to understand because while the two TabPanes have been added to the SplitPane, because the skin of the TabPanes hasn’t been created yet, getScene and getParent will return null.

So the question is, how can I get access to their parents at the point in code where I am trying to print it out? I presume I need to force the creation of the skin?

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TabPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;


public class MainApp extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Button button = new Button("click");
        StackPane root = new StackPane(button);

        button.setOnAction( event -> {
            SplitPane sp = new SplitPane();
            TabPane tabs1 = new TabPane();
            TabPane tabs2 = new TabPane();
            sp.getItems().addAll(tabs1, tabs2);
            root.getChildren().add(sp);

            System.out.println(tabs1.getParent());
            System.out.println(tabs2.getParent());
        });

        Scene scene = new Scene(root);
        stage.setTitle("JavaFX and Gradle");
        stage.setScene(scene);
        stage.show();
}

public static void main(String[] args) {
    launch(args);
}
}

Advertisement

Answer

May be instead of trying to rely on the node rendering, you can consider trying to change your logic by keeping references to parent nodes. That way you dont need to rely on node rendering.

Below are the changes i tried and it works as expected.

#Change 1:

Add the below instance variable to DetachableTabPane.java, to let know in which split pane this is set. That way you dont need to loop through all the nodes, to find the parent SplitPane.

private TabSplitPane parentSplitPane;

#Change 2:

Create a custom SplitPane that register itself to its children. So you dont need to worry to set it everytime when you add a DetachableTabPane to SplitPane.

class TabSplitPane extends SplitPane {
    public TabSplitPane() {
        getItems().addListener((ListChangeListener) e -> {
            if (e.next()) {
                e.getAddedSubList().stream().filter(o -> o instanceof DetachableTabPane).forEach(tp -> ((DetachableTabPane) tp).parentSplitPane = TabSplitPane.this);
                e.getRemoved().stream().filter(o -> o instanceof DetachableTabPane).forEach(tp -> ((DetachableTabPane) tp).parentSplitPane = null);
            }
        });
    }
}

# Change 3:

Update the placeTab method as below. This way you directly deal with the SplitPane instance associated to the DetachableTabPane and not worrying about when the node(s) will be rendered.

public void placeTab(Tab tab, Pos pos) {
    boolean addToLast = pos == Pos.CENTER_RIGHT || pos == Pos.BOTTOM_CENTER;
    DetachableTabPane dt = detachableTabPaneFactory.create(this);
    dt.getTabs().add(tab);

    Orientation requestedOrientation = Orientation.HORIZONTAL;
    if (pos == Pos.BOTTOM_CENTER || pos == Pos.TOP_CENTER) {
        requestedOrientation = Orientation.VERTICAL;
    }

    TabSplitPane targetSplitPane = parentSplitPane;
    // If there is no splitPane parent... Create one!!
    if (targetSplitPane == null) {
        targetSplitPane = new TabSplitPane();
        targetSplitPane.setOrientation(requestedOrientation);

        Pane parent = (Pane) getParent();
        int indexInParent = parent.getChildren().indexOf(DetachableTabPane.this);
        parent.getChildren().remove(DetachableTabPane.this);
        if (addToLast) {
            targetSplitPane.getItems().addAll(DetachableTabPane.this, dt);
        } else {
            targetSplitPane.getItems().addAll(dt, DetachableTabPane.this);
        }
        parent.getChildren().add(indexInParent, targetSplitPane);

    } 
    //  If the orientation is changed... create a new split pane.
    else if (targetSplitPane.getOrientation() != requestedOrientation) {
        TabSplitPane parent = targetSplitPane;
        int indexInParent = parent.getItems().indexOf(DetachableTabPane.this);
        parent.getItems().remove(DetachableTabPane.this);

        targetSplitPane = new TabSplitPane();
        targetSplitPane.setOrientation(requestedOrientation);
        if (addToLast) {
            targetSplitPane.getItems().addAll(DetachableTabPane.this, dt);
        } else {
            targetSplitPane.getItems().addAll(dt, DetachableTabPane.this);
        }
        parent.getItems().add(indexInParent, targetSplitPane);

    } else {
        if (addToLast) {
            parentSplitPane.getItems().add(dt);
        } else {
            int indexInParent = targetSplitPane.getItems().indexOf(DetachableTabPane.this);
            parentSplitPane.getItems().add(indexInParent, dt);
        }
    }
}
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement