2025/12/08

JavaFX 04 Control

JavaFX UI control 物件是在 javafx.scene.control.* package 裡面

以功能來區分,可以這樣分類

功能 代表 Control
按鈕與開關 Button, ToggleButton, CheckBox, RadioButton, Hyperlink, MenuButton, SplitMenuButton
文字輸入 TextField, TextArea, PasswordField
選擇器與下拉 ChoiceBox, ComboBox, ColorPicker, DatePicker, Spinner
清單/樹格顯示 ListView, TreeView, TableView
對話與頁籤 TabPane, Dialog, MenuBar, Alert, TextInputDialog, ChoiceDialog, ContextMenu
滾動/分隔容器 ScrollPane, SplitPane, Separator
工具列與分頁 ToolBar, ButtonBar, Pagination, Accordion

Button

package javafx;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ControlButton extends Application {
    @Override
    public void start(Stage primaryStage) {
        // 1. Button
        Button button = new Button("Standard Button");
        button.setOnAction(e -> System.out.println("Button clicked"));

        // 2. ToggleButton
        ToggleButton toggleButton = new ToggleButton("Toggle Me");
        toggleButton.setOnAction(e -> System.out.println("Toggle is " + (toggleButton.isSelected() ? "ON" : "OFF")));

        // 3. CheckBox
        CheckBox checkBox = new CheckBox("Accept Terms");
        checkBox.setOnAction(e -> System.out.println("Checkbox: " + checkBox.isSelected()));

        // 4. RadioButton (in a ToggleGroup)
        RadioButton option1 = new RadioButton("Option A");
        RadioButton option2 = new RadioButton("Option B");
        ToggleGroup radioGroup = new ToggleGroup();
        option1.setToggleGroup(radioGroup);
        option2.setToggleGroup(radioGroup);

        option1.setOnAction(e -> System.out.println("Radio selected: Option A"));
        option2.setOnAction(e -> System.out.println("Radio selected: Option B"));

        // 5. Hyperlink
        Hyperlink link = new Hyperlink("Open Website");
        link.setOnAction(e -> System.out.println("Hyperlink clicked"));

        // 6. MenuButton
        MenuItem item1 = new MenuItem("Menu A");
        MenuItem item2 = new MenuItem("Menu B");
        MenuButton menuButton = new MenuButton("Choose Menu", null, item1, item2);
        item1.setOnAction(e -> System.out.println("Menu A clicked"));
        item2.setOnAction(e -> System.out.println("Menu B clicked"));

        // 7. SplitMenuButton
        MenuItem subItem1 = new MenuItem("Sub A");
        MenuItem subItem2 = new MenuItem("Sub B");
        SplitMenuButton splitMenuButton = new SplitMenuButton(subItem1, subItem2);
        splitMenuButton.setText("Action + Menu");
        splitMenuButton.setOnAction(e -> System.out.println("Main action clicked"));
        subItem1.setOnAction(e -> System.out.println("Sub A clicked"));
        subItem2.setOnAction(e -> System.out.println("Sub B clicked"));

        // Layout
        VBox root = new VBox(10,
                button,
                toggleButton,
                checkBox,
                option1,
                option2,
                link,
                menuButton,
                splitMenuButton
        );
        root.setPadding(new Insets(20));
        root.setAlignment(Pos.TOP_LEFT);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setTitle("JavaFX Buttons & Switches Sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Input

package javafx;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ControlInput extends Application {

    @Override
    public void start(Stage primaryStage) {
        // 1. TextField
        TextField textField = new TextField();
        textField.setPromptText("Enter your name");

        // 2. TextArea
        TextArea textArea = new TextArea();
        textArea.setPromptText("Enter your comments");
        textArea.setPrefRowCount(4);

        // 3. PasswordField
        PasswordField passwordField = new PasswordField();
        passwordField.setPromptText("Enter password");

        // Button to show entered data
        Button submitButton = new Button("Submit");
        submitButton.setOnAction(e -> {
            System.out.println("Name: " + textField.getText());
            System.out.println("Comments: " + textArea.getText());
            System.out.println("Password: " + passwordField.getText());
        });

        // Layout
        VBox root = new VBox(10,
                new Label("Name:"), textField,
                new Label("Comments:"), textArea,
                new Label("Password:"), passwordField,
                submitButton
        );
        root.setPadding(new Insets(20));
        root.setAlignment(Pos.TOP_LEFT);

        Scene scene = new Scene(root, 400, 300);
        primaryStage.setTitle("JavaFX Text Input Sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Selector

package javafx;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class ControlSelector extends Application {
    @Override
    public void start(Stage primaryStage) {
        // 1. ChoiceBox
        ChoiceBox<String> choiceBox = new ChoiceBox<>();
        choiceBox.getItems().addAll("Apple", "Banana", "Cherry");
        choiceBox.setValue("Banana");

        // 2. ComboBox
        ComboBox<String> comboBox = new ComboBox<>();
        comboBox.getItems().addAll("Red", "Green", "Blue");
        comboBox.setEditable(true);
        comboBox.setValue("Green");

        // 3. ColorPicker
        ColorPicker colorPicker = new ColorPicker(Color.CORNFLOWERBLUE);

        // 4. DatePicker
        DatePicker datePicker = new DatePicker();

        // 5. Spinner (Integer, 0~10)
        Spinner<Integer> spinner = new Spinner<>(0, 10, 5);
        spinner.setEditable(true);

        // Button to show selected values
        Button showButton = new Button("Show Values");
        showButton.setOnAction(e -> {
            System.out.println("ChoiceBox: " + choiceBox.getValue());
            System.out.println("ComboBox: " + comboBox.getValue());
            System.out.println("ColorPicker: " + colorPicker.getValue());
            System.out.println("DatePicker: " + datePicker.getValue());
            System.out.println("Spinner: " + spinner.getValue());
        });

        // Layout
        VBox root = new VBox(10,
                new Label("ChoiceBox:"), choiceBox,
                new Label("ComboBox:"), comboBox,
                new Label("ColorPicker:"), colorPicker,
                new Label("DatePicker:"), datePicker,
                new Label("Spinner:"), spinner,
                showButton
        );
        root.setPadding(new Insets(20));
        root.setAlignment(Pos.TOP_LEFT);

        Scene scene = new Scene(root, 400, 450);
        primaryStage.setTitle("JavaFX Selectors Sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

List

package javafx;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ControlList extends Application {

    @Override
    public void start(Stage primaryStage) {
        // 1. ListView
        ListView<String> listView = new ListView<>();
        listView.getItems().addAll("Item A", "Item B", "Item C");

        // 2. TreeView
        TreeItem<String> rootNode = new TreeItem<>("Root");
        rootNode.setExpanded(true);
        rootNode.getChildren().addAll(
                new TreeItem<>("Node 1"),
                new TreeItem<>("Node 2"),
                new TreeItem<>("Node 3")
        );
        TreeView<String> treeView = new TreeView<>(rootNode);

        // 3. TableView
        TableView<Person> tableView = new TableView<>();
        TableColumn<Person, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(data -> data.getValue().nameProperty());

        TableColumn<Person, String> emailCol = new TableColumn<>("Email");
        emailCol.setCellValueFactory(data -> data.getValue().emailProperty());

        tableView.getColumns().addAll(nameCol, emailCol);
        tableView.getItems().addAll(
                new Person("Alice", "alice@example.com"),
                new Person("Bob", "bob@example.com"),
                new Person("Carol", "carol@example.com")
        );

        // Layout
        VBox root = new VBox(10,
                new Label("ListView:"), listView,
                new Label("TreeView:"), treeView,
                new Label("TableView:"), tableView
        );
        root.setPrefWidth(400);
        root.setPrefHeight(600);
        Scene scene = new Scene(root, 500, 600);
        primaryStage.setTitle("JavaFX Control List Sample");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    // Inner class for TableView row
    public static class Person {
        private final SimpleStringProperty name;
        private final SimpleStringProperty email;

        public Person(String name, String email) {
            this.name = new SimpleStringProperty(name);
            this.email = new SimpleStringProperty(email);
        }

        public SimpleStringProperty nameProperty() { return name; }
        public SimpleStringProperty emailProperty() { return email; }
    }

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

Dialog

package javafx;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class ControlDialog extends Application {

    @Override
    public void start(Stage primaryStage) {
        // === MenuBar ===
        MenuBar menuBar = new MenuBar();
        Menu fileMenu = new Menu("File");
        MenuItem showAlert = new MenuItem("Show Alert");
        MenuItem showInput = new MenuItem("Show Input Dialog");
        MenuItem showChoice = new MenuItem("Show Choice Dialog");
        fileMenu.getItems().addAll(showAlert, showInput, showChoice);
        menuBar.getMenus().addAll(fileMenu);

        // === TabPane ===
        TabPane tabPane = new TabPane();
        Tab tab1 = new Tab("Home", new Label("This is the Home tab."));
        Tab tab2 = new Tab("Settings", new Label("This is the Settings tab."));
        tabPane.getTabs().addAll(tab1, tab2);

        // === ContextMenu ===
        ContextMenu contextMenu = new ContextMenu();
        MenuItem customDialog = new MenuItem("Open Custom Dialog");
        contextMenu.getItems().add(customDialog);

        Label contextTarget = new Label("Right-click me for ContextMenu");
        contextTarget.setOnMousePressed(e -> {
            if (e.getButton() == MouseButton.SECONDARY) {
                contextMenu.show(contextTarget, e.getScreenX(), e.getScreenY());
            }
        });

        // === Action: Alert ===
        showAlert.setOnAction(e -> {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setTitle("Information");
            alert.setHeaderText("This is an alert.");
            alert.setContentText("Everything is working fine.");
            alert.showAndWait();
        });

        // === Action: TextInputDialog ===
        showInput.setOnAction(e -> {
            TextInputDialog inputDialog = new TextInputDialog("Default");
            inputDialog.setTitle("Input Dialog");
            inputDialog.setHeaderText("Please enter your name:");
            Optional<String> result = inputDialog.showAndWait();
            result.ifPresent(name -> System.out.println("Entered: " + name));
        });

        // === Action: ChoiceDialog ===
        showChoice.setOnAction(e -> {
            List<String> choices = Arrays.asList("Dog", "Cat", "Bird");
            ChoiceDialog<String> choiceDialog = new ChoiceDialog<>("Cat", choices);
            choiceDialog.setTitle("Choice Dialog");
            choiceDialog.setHeaderText("Choose your favorite animal:");
            Optional<String> result = choiceDialog.showAndWait();
            result.ifPresent(choice -> System.out.println("Selected: " + choice));
        });

        // === Action: Custom Dialog ===
        customDialog.setOnAction(e -> {
            Dialog<String> dialog = new Dialog<>();
            dialog.setTitle("Custom Dialog");
            dialog.setHeaderText("This is a custom dialog.");

            ButtonType okButton = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE);
            dialog.getDialogPane().getButtonTypes().addAll(okButton, ButtonType.CANCEL);

            dialog.setContentText("This dialog returns a string.");
            dialog.setResultConverter(dialogButton -> {
                if (dialogButton == okButton) {
                    return "Confirmed";
                }
                return null;
            });

            Optional<String> result = dialog.showAndWait();
            result.ifPresent(r -> System.out.println("Dialog result: " + r));
        });

        // === Layout ===
        StackPane centerPane = new StackPane(contextTarget);
        BorderPane root = new BorderPane();
        root.setTop(menuBar);
        root.setCenter(tabPane);
        root.setBottom(centerPane);

        Scene scene = new Scene(root, 500, 400);
        primaryStage.setTitle("JavaFX ControlDialog Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Separator

package javafx;

import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class ControlSeparator extends Application {

    @Override
    public void start(Stage primaryStage) {
        // === ScrollPane: 可滾動內容 ===
        VBox scrollContent = new VBox(10);
        for (int i = 1; i <= 30; i++) {
            scrollContent.getChildren().add(new Label("Scrollable Item " + i));
        }
        ScrollPane scrollPane = new ScrollPane(scrollContent);
        scrollPane.setFitToWidth(true);

        // === Separator ===
        Separator horizontalSeparator = new Separator();
        Separator verticalSeparator = new Separator(Orientation.VERTICAL);

        // === SplitPane: 左右分隔區域 ===
        VBox leftPane = new VBox(5, new Label("Left Panel"), new Button("Left Action"));
        VBox rightPane = new VBox(5, new Label("Right Panel"), new Button("Right Action"));
        SplitPane splitPane = new SplitPane(leftPane, verticalSeparator, rightPane);
        splitPane.setDividerPositions(0.45f, 0.55f);

        // === 總佈局:ScrollPane 在上,分隔線,SplitPane 在下 ===
        VBox root = new VBox(10);
        root.getChildren().addAll(
                new Label("ScrollPane Area:"),
                scrollPane,
                horizontalSeparator,
                new Label("SplitPane Area:"),
                splitPane
        );
        root.setPrefHeight(500);

        Scene scene = new Scene(root, 600, 500);
        primaryStage.setTitle("JavaFX ControlSeparator Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Tool

package javafx;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class ControlTool extends Application {

    @Override
    public void start(Stage primaryStage) {
        // === ToolBar ===
        ToolBar toolBar = new ToolBar(
                new Button("New"),
                new Button("Open"),
                new Button("Save")
        );

        // === ButtonBar ===
        ButtonBar buttonBar = new ButtonBar();
        Button okBtn = new Button("OK");
        Button cancelBtn = new Button("Cancel");
        ButtonBar.setButtonData(okBtn, ButtonBar.ButtonData.OK_DONE);
        ButtonBar.setButtonData(cancelBtn, ButtonBar.ButtonData.CANCEL_CLOSE);
        buttonBar.getButtons().addAll(okBtn, cancelBtn);

        // === Pagination (5 pages) ===
        Pagination pagination = new Pagination(5, 0);
        pagination.setPageFactory(pageIndex -> {
            Label pageLabel = new Label("Page " + (pageIndex + 1));
            pageLabel.setStyle("-fx-font-size: 18px;");
            StackPane pageContent = new StackPane(pageLabel);
            pageContent.setPrefHeight(100);
            return pageContent;
        });

        // === Accordion ===
        TitledPane pane1 = new TitledPane("Section 1", new Label("Content of Section 1"));
        TitledPane pane2 = new TitledPane("Section 2", new Label("Content of Section 2"));
        Accordion accordion = new Accordion(pane1, pane2);

        // === Layout ===
        VBox root = new VBox(15);
        root.setPadding(new Insets(15));
        root.getChildren().addAll(
                toolBar,
                new Label("Accordion:"), accordion,
                new Label("Pagination:"), pagination,
                new Label("ButtonBar:"), buttonBar
        );

        Scene scene = new Scene(root, 500, 400);
        primaryStage.setTitle("JavaFX ControlTool Demo");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

2025/12/01

JavaFX 03 Layout

JavaFX Layout 在 javafx.layout package 裡面,有以下類別 HBox, VBox, Border Pane, Stack Pane, Text Flow, Anchor Pane, Title Pane, Grid Pane, Flow Panel,Pane 類別是所有類別的母類別。

create a layout

建立 layout 有以下步驟

  1. create node

  2. 以 layout 的類別產生 instance

  3. 設定 layout 屬性

  4. 將 nodes 加入 layout

create nodes

ex:

//Creating a text field 
TextField textField = new TextField();       

//Creating the play button 
Button playButton = new Button("Play");       

//Creating the stop button 
Button stopButton = new Button("stop");

產生 layout instance

HBox hbox = new HBox();

設定layout屬性

hbox.setSpacing(10);

將 nodes 加入 layout

//Creating a Group object  
hbox.getChildren().addAll(textField, playButton, stopButton);

Layouts

類別 抽象基底類別 說明
Region 抽象基底類別 支援背景、邊框、padding、最小/最大尺寸等,不直接用於排版。
Layout 類別 排版方式 特性 / 用途
Pane 手動設定每個子節點的位置 最基本容器,無自動排版功能。需搭配 setLayoutX/Y() 使用。
HBox 水平排版 將子節點由左至右排列,可設定間距、對齊與每個節點的伸縮比例。
VBox 垂直排版 將子節點由上至下排列,支援間距、對齊、節點高度調整等。
StackPane 子節點重疊排版 最後加入的節點在最上層,適合建立背景圖層或合成式 UI。
BorderPane 五區域:Top / Bottom / Left / Right / Center 適合標準應用主畫面分區(類似 HTML 的 frameset)。分成 Top/Bottom/Left/Right/Center 五個區域
GridPane 表格樣式的列/欄排列 可自由定義 row/column spans,類似 HTML table。
FlowPane 水平/垂直「流動」排列 當空間不夠時,自動換行/換列(類似 CSS flex-wrap: wrap)。
TilePane 固定格大小的網格排列 每個子節點占用相同大小的 tile 區塊,整齊對齊適合 icon 排列等用途。
AnchorPane 錨定(Anchor)子節點邊距 可將節點固定在上下左右的邊界距離,類似 CSS 的 absolute 定位。
ScrollPane 提供可滾動內容區塊 包含一個子節點,支援垂直與水平捲軸。

另外在 javafx.scene.control.TabPane,雖然名稱是 TabPane,但並不是一種 layout,而是 control 元件,TabPane 可建立一個多頁籤的畫面,每一個頁籤,都能容納一種內容節點,通常是 Pane

Example

以下實例,是利用 TabPan 製作這些 Pane layout 的 sample

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class LayoutExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TabPane tabPane = new TabPane();

        tabPane.getTabs().add(tab("Pane", createPaneExample()));
        tabPane.getTabs().add(tab("HBox", createHBoxExample()));
        tabPane.getTabs().add(tab("VBox", createVBoxExample()));
        tabPane.getTabs().add(tab("StackPane", createStackPaneExample()));
        tabPane.getTabs().add(tab("BorderPane", createBorderPaneExample()));
        tabPane.getTabs().add(tab("GridPane", createGridPaneExample()));
        tabPane.getTabs().add(tab("FlowPane", createFlowPaneExample()));
        tabPane.getTabs().add(tab("TilePane", createTilePaneExample()));
        tabPane.getTabs().add(tab("AnchorPane", createAnchorPaneExample()));
        tabPane.getTabs().add(tab("ScrollPane", createScrollPaneExample()));

        Scene scene = new Scene(tabPane, 600, 500);
        primaryStage.setTitle("JavaFX Layout Example");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Tab tab(String title, Pane content) {
        Tab tab = new Tab(title);
        tab.setContent(content);
        return tab;
    }

    private Pane createPaneExample() {
        Pane pane = new Pane();
        Rectangle rect = new Rectangle(50, 50, 100, 100);
        rect.setFill(Color.CORNFLOWERBLUE);
        pane.getChildren().add(rect);
        return pane;
    }

    private Pane createHBoxExample() {
        HBox hbox = new HBox(10, new Button("One"), new Button("Two"), new Button("Three"));
        hbox.setPadding(new Insets(10));
        hbox.setAlignment(Pos.CENTER);
        return hbox;
    }

    private Pane createVBoxExample() {
        VBox vbox = new VBox(10, new Label("Top"), new Button("Middle"), new TextField("Bottom"));
        vbox.setPadding(new Insets(10));
        vbox.setAlignment(Pos.CENTER);
        return vbox;
    }

    private Pane createStackPaneExample() {
        StackPane stackPane = new StackPane();
        Rectangle background = new Rectangle(200, 100, Color.LIGHTGRAY);
        Label label = new Label("On Top");
        stackPane.getChildren().addAll(background, label);
        stackPane.setPadding(new Insets(10));
        return stackPane;
    }

    private Pane createBorderPaneExample() {
        BorderPane borderPane = new BorderPane();
        borderPane.setTop(new Label("Top"));
        borderPane.setBottom(new Label("Bottom"));
        borderPane.setLeft(new Button("Left"));
        borderPane.setRight(new Button("Right"));
        borderPane.setCenter(new TextArea("Center"));
        borderPane.setPadding(new Insets(10));
        return borderPane;
    }

    private Pane createGridPaneExample() {
        GridPane grid = new GridPane();
        grid.setPadding(new Insets(10));
        grid.setHgap(10);
        grid.setVgap(10);
        grid.add(new Label("Name:"), 0, 0);
        grid.add(new TextField(), 1, 0);
        grid.add(new Label("Email:"), 0, 1);
        grid.add(new TextField(), 1, 1);
        return grid;
    }

    private Pane createFlowPaneExample() {
        FlowPane flow = new FlowPane(10, 10);
        flow.setPadding(new Insets(10));
        for (int i = 1; i <= 8; i++) {
            flow.getChildren().add(new Button("Button " + i));
        }
        return flow;
    }

    private Pane createTilePaneExample() {
        TilePane tile = new TilePane(10, 10);
        tile.setPadding(new Insets(10));
        tile.setPrefColumns(4);
        for (int i = 1; i <= 8; i++) {
            tile.getChildren().add(new Label("Tile " + i));
        }
        return tile;
    }

    private Pane createAnchorPaneExample() {
        AnchorPane anchor = new AnchorPane();
        Button button = new Button("Anchored Button");
        anchor.getChildren().add(button);
        AnchorPane.setTopAnchor(button, 20.0);
        AnchorPane.setLeftAnchor(button, 30.0);
        return anchor;
    }

    private Pane createScrollPaneExample() {
        VBox longVBox = new VBox(10);
        for (int i = 1; i <= 50; i++) {
            longVBox.getChildren().add(new Label("Line " + i));
        }
        ScrollPane scrollPane = new ScrollPane(longVBox);
        scrollPane.setFitToWidth(true);
        return new VBox(scrollPane);
    }

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

References

JavaFX GridPane Layout