JavaFX 2 BorderPane Use Full Space

JavaFX 2 BorderPane Use Full Space

I´m just facing a little issue that I can´t solve by myself. I try to place a vBox including a TextField and a HTML-Editor in my BorderPane, but the full space is not used. Another problem is that if I shrink the window, the html-editor overlaps with my left option window.

private void initEditor()
{
editor = new HTMLEditor();
editor.setId("editor");
editor.lookup(".top-toolbar").setDisable(true);
editor.lookup(".top-toolbar").setManaged(false);
((ToolBar) editor.lookup(".bottom-toolbar")).getItems().addAll(FXCollections.observableArrayList(((ToolBar)editor.lookup(".top-toolbar")).getItems()));

editorBox = new VBox();
TextField field = new TextField();
field.setPrefHeight(36);
field.setId("editor-title");
editorBox.setFillWidth(true);
editorBox.getChildren().addAll(field, editor);
    root.setCenter(editorBox);
}

OK, so a few things going wrong here, I'll try to address them provide some advice and a sample solution.

I try to place a vBox including a TextField and a HTML-Editor in my BorderPane, but the full space is not used.

You need to use the VBox.setVgrow(editor, Priority.ALWAYS) method to get the HTMLEditor to take up any extra space in the VBox. Also, make sure that the HTMLeditor has an unbounded max height so that it can grow to fit the available area, for example editor.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE). Calling editorBox.setFillWidth(true) is redundant as the default value for the fillWidth property is true.

But even if you do all that, (as of 2.2b13) there is a bug in WebPane sizing which will cause you problems. Internally the WebPane is implemented as a GridPane containing the Toolbar and an editable WebView. By default, WebView has a preferred size of 800x600. The HtmlEditor control does not set the GridPane constraints for the WebView to allow it to be sized past it's preferred size.

To work around this, you can lookup the WebView via a css lookup after it has been added to an active Scene and adjust the GridPane constraints manually. Here is some code to do that:

stage.show();
...
WebView webview = (WebView) editor.lookup("WebView");
GridPane.setHgrow(webview, Priority.ALWAYS);
GridPane.setVgrow(webview, Priority.ALWAYS);

I shrink the window, the html-editor overlaps with my left option window.

Explicitly set the minimum width setting for the center pane in your BorderPane so that it won't overflow over the outer edge Panes.

editorBox.setMinSize(0, 0);

You need to do this because the BorderPane documentation states:

BorderPane does not clip its content by default, so it is possible that childrens' bounds may extend outside its own bounds if a child's min size prevents it from being fit within it space.


As an aside the lookup calls in your code are suspicious. Normally you can't lookup nodes by css ID until the node has been added to an active scene on a displayed stage and the JavaFX system has had a chance to run a CSS layout pass on the node - otherwise the lookup will just return null.


For debugging JavaFX layout issues, the ScenicView application is invaluable.


Here is a complete example to generate a layout similar to the image linked in your question, but without the issues you mention.

import com.javafx.experiments.scenicview.ScenicView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.*;
import javafx.stage.*;

public class HtmlEditorInBorderPane extends Application {
  public static void main(String[] args) { launch(args); }
  @Override public void start(final Stage stage) throws IOException {
    // option pane.
    VBox optionPane = new VBox(10);
    MenuBar menuBar = new MenuBar();
    menuBar.getMenus().addAll(new Menu("School"), new Menu("Social"), new Menu("Network"));
    TreeItem<String> root = new TreeItem<>("Private Notes");
    root.setExpanded(false);
    root.getChildren().addAll(new TreeItem<>("Layout"), new TreeItem<>("is not"), new TreeItem<>("easy"));
    TreeView<String> notes = new TreeView<>(root);
    optionPane.getChildren().addAll(menuBar, new Label("Kaiser Notes"), notes);
    optionPane.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");

    // editor pane.
    HTMLEditor editor = new HTMLEditor();
    VBox editorBox = new VBox(10);
    TextField textField = new TextField();
    editorBox.getChildren().addAll(textField, editor);
    editorBox.setStyle("-fx-background-color: mistyrose; -fx-padding: 10;");
    editor.setHtmlText(getSampleText());

    // option layout constraints
    VBox.setVgrow(notes, Priority.ALWAYS);

    // editor layout constraints
    textField.setMinHeight(Control.USE_PREF_SIZE); // stop the textfield from getting squashed when the scene is sized small.
    VBox.setVgrow(editor, Priority.ALWAYS);        // tells the editor to fill available vertical space.
    editorBox.setMinSize(0, 0);                    // stops the editor from overflowing it's bounds in a BorderPane.

    // layout the scene.
    BorderPane layout = new BorderPane();
    layout.setLeft(optionPane);
    layout.setCenter(editorBox);
    stage.setScene(new Scene(layout));
    stage.show();

    // addition of static layout grow constraints for the htmleditor embedded webview.
    WebView webview = (WebView) editor.lookup("WebView");
    GridPane.setHgrow(webview, Priority.ALWAYS);  // allow the webview to grow beyond it's preferred width of 800.
    GridPane.setVgrow(webview, Priority.ALWAYS);  // allow the webview to grow beyond it's preferred height of 600.
  }

  // get some dummy lorem ipsum sample text.
  private String getSampleText() throws IOException {
    StringBuilder builder = new StringBuilder();
    try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("http://www.lorem-ipsum-text.com/").openStream()))) {
      String string;
      while ((string = in.readLine()) != null) {
        builder.append(string);
      }
    }
    return builder.toString();
  }
}

2 Tips for Sizing and Aligning Nodes. This topic describes techniques for controlling the size and alignment of nodes when placed in a JavaFX layout pane. A main advantage of using the built-in JavaFX layout panes is that the size and alignment of nodes is handled by the pane.


    GridPane gridPane = (GridPane) editor.getChildrenUnmodifiable().get(0);
    RowConstraints row1 = new RowConstraints();
    row1.setVgrow(Priority.NEVER);
    RowConstraints row2 = new RowConstraints();
    row2.setVgrow(Priority.NEVER);
    RowConstraints row3 = new RowConstraints();
    row3.setVgrow(Priority.ALWAYS);
    gridPane.getRowConstraints().addAll(row1, row2, row3);

Layout components can be combined. A typical setup involves using the BorderPane layout as the base with other layouts inside it. In the example below, the top of the BorderPane contains a HBox used for horizontal layout and a VBox used for vertical layouts. A text box has been placed placed in the center.


How to set and resize ScrollPane with FXML:

package anchorscroll;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;

public class FXMLDocumentController implements Initializable {

    private Label label;
    @FXML
    private VBox vp;
    @FXML
    private ScrollPane sp;
    @FXML

    private Pane panel;
    @FXML
    private double Width;
    @FXML
    private double Height;

    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
        label.setText("Hello World!");
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
        Width = panel.getWidth();
        Height = panel.getHeight();
        sp.setPrefWidth(Width - 60);
        sp.setPrefHeight(Height - 60);

        panel.widthProperty().addListener(p -> {
            Width = panel.getWidth();
            Height = panel.getHeight();
            System.out.println("Width " + Width + " Height " + Height);
            sp.setPrefWidth(Width - 60);
            sp.setPrefHeight(Height - 60);
        });

        panel.heightProperty().addListener(p -> {
            Width = panel.getWidth();
            Height = panel.getHeight();
            System.out.println("Width " + Width + " Height " + Height);
            sp.setPrefWidth(Width - 60);
            sp.setPrefHeight(Height - 60);
        });

        VBox v = new VBox();
        for (int i = 0; i < 100; i++) {
            v.getChildren().add(new Button("Hello " + i));
        }
        sp.setContent(v);

        VBox p = new VBox();
        try {
            p = FXMLLoader.load(getClass().getResource("NewMessage.fxml")); // "/fxml/Register.fxml"
            sp.setContent(p);
        } catch (IOException ex) {
            Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

}

!!! NewMessage.fxml need start with VBox panel !!!

Comment or delete this 2 lines if u don't need load FXML:

p = FXMLLoader.load(getClass().getResource("NewMessage.fxml"));
sp.setContent(p);

And layout:

<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<Pane fx:id="panel" prefHeight="400.0" prefWidth="600.0" stylesheets="@Styles.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="anchorscroll.FXMLDocumentController">
    <children>
        <HBox prefHeight="60.0" prefWidth="600.0">
            <children>
                <Label text="Label" />
            </children>
        </HBox>
        <VBox fx:id="vp" layoutY="60.0" prefHeight="540.0" prefWidth="60.0">
            <children>
                <Button mnemonicParsing="false" prefWidth="50.0" text="Button" />
            </children>
        </VBox>
        <ScrollPane fx:id="sp" fitToHeight="true" fitToWidth="true" layoutX="60.0" layoutY="60.0" prefHeight="340.0" prefWidth="540.0" vbarPolicy="ALWAYS" />
    </children>
</Pane>

Layout image (topBar, leftBar, scrollContentPane)

BorderPane lays out children in top, left, right, bottom, and center positions. The top and bottom children will be resized to their preferred heights and extend the width of the border pane. The left and right children will be resized And the center node will be resized to fill the available space in the middle.


I was also facing the same kind of issue. My anchorPane inside borderPane was not filling up. Setting a large number for prefSize worked for me. So when border pane grows, it tries to provide as much size as possible to its child

anchorPane.setMinSize(25, 25);
anchorPane.setPrefSize(25000, 25000);

Use layout panes to easily manage the user interface for your JavaFX application. Learn how to use the JavaFX Layout API and built-in layout containers (BorderPane, GridPane, FlowPane, TilePane, HBox, VBox, StackPane) to lay out and style the interface the for your JavaFX application.


With this configuration when the parent changes the available space it will use the final method setWidth()/setHeight() to update the width/height your component can use. So, since these methods are final you can´t override it to "know" when a change occurs.


1 Why Use FXML. This tutorial provides a basic description of FXML and the benefits of using it to create user interfaces. FXML is an XML-based language that provides the structure for building a user interface separate from the application logic of your code.


Java FX resource bundle and internalization - implementation 0