Midgard
Midgard

Reputation: 402

Set divider position of SplitPane

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

screenshot of result

It suggests that something resets it to the middle.

How can I achieve this?

Upvotes: 14

Views: 17772

Answers (9)

Timur Baysal
Timur Baysal

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

Florian
Florian

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

cooooool
cooooool

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

fabian
fabian

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

Mr. Brooks
Mr. Brooks

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

Oleks
Oleks

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

Thomas Andres
Thomas Andres

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

Enric Tordera
Enric Tordera

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

Mark
Mark

Reputation: 1470

Try

splitPane.setDividerPosition(0, percentage);

The parameters are setDividerPosition(int dividerIndex, double percentage)

Upvotes: -3

Related Questions