Issue
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);
}
}
Solution
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);
}
}
}
Answered By - Sai Dandem
Answer Checked By - Willingham (JavaFixing Volunteer)