Reputation: 402
I want to set the divider of a SplitPane to a certain default position. This does not work, the divider stays in the middle:
public void start(Stage primaryStage) throws Exception{
SplitPane splitPane = new SplitPane(new Pane(), new Pane());
// Report changes to the divider position
splitPane.getDividers().get(0).positionProperty().addListener(
o -> System.out.println(splitPane.getDividerPositions()[0])
);
// Doesn't work:
splitPane.setDividerPositions(0.8);
// The docs seem to recommend the following (with floats instead of
// doubles, and with one number more than there are dividers, which is
// weird), but it doesn't work either:
//splitPane.setDividerPositions(0.8f, 0.2f);
primaryStage.setScene(new Scene(splitPane));
primaryStage.setMaximized(true);
primaryStage.show();
}
The output:
0.8
0.5
It suggests that something resets it to the middle.
How can I achieve this?
Upvotes: 14
Views: 17772
Reputation: 151
Platform.runlater(()->splitpane.setDividerPosition(0,0.8));
Absolutely does the trick for me. This sets the first split position of my horizontal splitpane to 80% of the parents width when opening the window.
While runlater() in many cases can lead JavaFX to do a little visible jitter at times, depending on the complexity of your GUI, in my case I haven't seen this happen.
Upvotes: 0
Reputation: 61
As pointed out by others the issue is that the divider position is reset when the Stage is maximized.
You can prevent this by setting ResizableWithParent
to false
.
Example
Let's say you have a SplitPane
with two nested containers inside. Here is the fxml extract:
<SplitPane fx:id="splitPane" dividerPositions="0.25">
<VBox fx:id="leftSplitPaneContainer" />
<FlowPane fx:id="rightSplitPaneContainer"/>
</SplitPane>
And here is the extract from the controller class:
@FXML
private SplitPane splitPane;
@FXML
private VBox leftSplitPaneContainer;
@FXML
private FlowPane rightSplitPaneContainer;
Then you simply can call SplitPane.setResizableWithParent()
on both containers to prevent resetting the divider position:
public void initialize(){
SplitPane.setResizableWithParent(leftSplitPaneContainer, false);
SplitPane.setResizableWithParent(rightSplitPaneContainer, false);
}
The divider position will now remain at 0.25 even if you maximize the window.
No complicated listeners or overwriting of SplitPaneSkin
involved.
Upvotes: 2
Reputation: 425
Since you usually have at least something in splitpane, eg. vbox, just set min and max width and it will automatically set divider.
Upvotes: 0
Reputation: 82451
The issue seems to be that the divider position is reset when the SplitPane
width is set during when the Stage
is maximized. Set the divider positions afterwards by listening to the window's showing property as follows:
primaryStage.showingProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
splitPane.setDividerPositions(0.8);
observable.removeListener(this);
}
}
});
Upvotes: 15
Reputation: 41
Here is my result:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.*;
/**
* SplitPane, Dialogbox example
* @author Pataki István
*/
public class SimpleDocking extends Application {
private double splitPosition = 0;
private SplitPane rootPane = new SplitPane();
private MyDialog dialog;
private BorderPane dockedArea;
@Override
public void start(final Stage stage) throws Exception {
rootPane.setOrientation(Orientation.VERTICAL);
rootPane.setBorder(new Border(new BorderStroke(
Color.GREEN,
BorderStrokeStyle.SOLID,
new CornerRadii(5),
new BorderWidths(3))
));
dockedArea = new BorderPane(new TextArea("Some docked content"));
final FlowPane centerArea = new FlowPane();
final Button undockButton = new Button("Undock");
centerArea.getChildren().add(undockButton);
rootPane.getItems().addAll(centerArea, dockedArea);
stage.setScene(new Scene(rootPane, 300, 300));
stage.show();
dialog = new MyDialog(stage);
undockButton.disableProperty().bind(dialog.showingProperty());
undockButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
handler(stage);
}
});
}
private void handler(Stage stage) {
splitPosition = rootPane.getDividerPositions()[0];
rootPane.getItems().remove(dockedArea);
dialog.setOnHidden(windowEvent -> {
rootPane.getItems().add(dockedArea);
rootPane.setDividerPositions(splitPosition);
});
dialog.setContent(dockedArea);
dialog.show(stage);
}
private class MyDialog extends Popup {
private BorderPane root;
private MyDialog(Window parent) {
root = new BorderPane();
root.setPrefSize(200, 200);
root.setStyle("-fx-border-width: 1; -fx-border-color: gray");
root.setTop(buildTitleBar());
setX(parent.getX() + 50);
setY(parent.getY() + 50);
getContent().add(root);
}
public void setContent(Node content) {
root.setCenter(content);
}
private Node buildTitleBar() {
BorderPane pane = new BorderPane();
pane.setStyle("-fx-background-color: burlywood; -fx-padding: 5");
final Delta dragDelta = new Delta();
pane.setOnMousePressed(mouseEvent -> {
dragDelta.x = getX() - mouseEvent.getScreenX();
dragDelta.y = getY() - mouseEvent.getScreenY();
});
pane.setOnMouseDragged(mouseEvent -> {
setX(mouseEvent.getScreenX() + dragDelta.x);
setY(mouseEvent.getScreenY() + dragDelta.y);
});
Label title = new Label("My Dialog");
title.setStyle("-fx-text-fill: midnightblue;");
pane.setLeft(title);
Button closeButton = new Button("X");
closeButton.setOnAction(actionEvent -> hide());
pane.setRight(closeButton);
return pane;
}
}
private static class Delta {
double x, y;
}
public static void main(String[] args) throws Exception {
launch();
}
}
This is works like you wish.
Upvotes: 0
Reputation: 1051
You could just wrap the call setDividerPositions
with
Platform.runLater(new Runnable() { @Override public void run() { splitPane.setDividerPositions(0.8); } });
This is not 100% reliable solution because run()
method is performed in JFX thread at unspecified time but it works properly for simple initialization cases.
Upvotes: 0
Reputation: 106
I had the same problem and the above solutions where not working reliably for me. So I created a custom skin for SplitPane:
public class DumbSplitPaneSkin extends SplitPaneSkin {
public DumbSplitPaneSkin(SplitPane splitPane) {
super(splitPane);
}
@Override
protected void layoutChildren(double x, double y, double w, double h) {
double[] dividerPositions = getSkinnable().getDividerPositions();
super.layoutChildren(x, y, w, h);
getSkinnable().setDividerPositions(dividerPositions);
}
}
This skin can be used via css or by overriding SplitPane.createDefaultSkin(). You can also set programatically as splitPane.setSkin(new DumbSplitPaneSkin(splitPane));
Upvotes: 2
Reputation: 99
During Stage
initialization, window size changes several times until layout is completed. Every change modifies divider positions. If you want to control divider positions, they have to be set after Stage
is fully initialized:
private boolean m_stageShowing = false;
@Override
public void start(Stage primaryStage) throws Exception {
SplitPane splitPane = new SplitPane(new Pane(), new Pane());
ChangeListener<Number> changeListener = new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
splitPane.setDividerPositions(0.8);
if (m_stageShowing) {
observable.removeListener(this);
}
}
};
splitPane.widthProperty().addListener(changeListener);
splitPane.heightProperty().addListener(changeListener);
primaryStage.setScene(new Scene(splitPane));
primaryStage.setMaximized(true);
primaryStage.show();
m_stageShowing = true;
}
Upvotes: 2
Reputation: 1470
Try
splitPane.setDividerPosition(0, percentage);
The parameters are setDividerPosition(int dividerIndex, double percentage)
Upvotes: -3