{"id":"gui-basics-boxes-grids","slug":"boxes-and-grids","title":"HBox, VBox, spacing and GridPane","text":"A FlowPane wraps its children however the width allows. Often you want to decide yourself: these in a row, those in a column, everything in a grid. This step has the three layouts for that, and the settings that space things out.\n\n### HBox and VBox\n\nAn **HBox** puts its children in one row, from left to right, and a **VBox** in one column, from top to bottom. Neither wraps. A scoreboard for a football match:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label home = new Label(\"Harbor Rovers 2\");\n        Label away = new Label(\"Mill Town 1\");\n        Label time = new Label(\"67 minutes played\");\n        VBox board = new VBox(home, away, time);\n\n        stage.setScene(new Scene(board));\n        stage.setTitle(\"Scoreboard\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[],\"window\":{\"windows\":[{\"title\":\"Scoreboard\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Harbor Rovers 2\"},{\"n\":3,\"type\":\"Label\",\"text\":\"Mill Town 1\"},{\"n\":4,\"type\":\"Label\",\"text\":\"67 minutes played\"}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Scoreboard\\\"\\n  VBox\\n    Label \\\"Harbor Rovers 2\\\"\\n    Label \\\"Mill Town 1\\\"\\n    Label \\\"67 minutes played\\\"\"}\n```\n\nAn HBox is made the same way, `new HBox(home, away, time)`, and it would show the three labels side by side. Both have `getChildren()`, as FlowPane has.\n\n### Spacing\n\nWithout anything more, the children touch each other. **Spacing** is the room between two children, in pixels: `new VBox(10, home, away, time)` (the spacing comes first), or `board.setSpacing(10)` later. In the window as text it reads `VBox spacing 10`.\n\n### Padding\n\n**Padding** is room inside a pane, between its edge and its children. `setPadding` takes an **Insets** object, from `javafx.geometry`:\n\n- `new Insets(12)`: 12 pixels on all four sides\n- `new Insets(4, 12, 8, 12)`: top, right, bottom, left, in that order: clockwise, starting at the top\n\nThe window as text shows `padding 12` for the same padding on every side, and the four values, `padding 4 12 8 12`, otherwise. Every layout has `setPadding`.\n\n### Alignment\n\nWhen a pane has more room than its children need, **alignment** says where in it they sit. `setAlignment` takes one of the constants of **Pos**, also from `javafx.geometry`. The names say the place, up and down first, then across: `TOP_LEFT` (where HBox, VBox, FlowPane and GridPane put things unless told otherwise), `TOP_CENTER`, `TOP_RIGHT`, `CENTER_LEFT`, `CENTER`, `CENTER_RIGHT`, `BOTTOM_LEFT`, `BOTTOM_CENTER` and `BOTTOM_RIGHT`. (Three more, `BASELINE_LEFT`, `BASELINE_CENTER` and `BASELINE_RIGHT`, line up texts by the line their letters stand on.)\n\nThe scoreboard with all three:\n\n```java\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label home = new Label(\"Harbor Rovers 2\");\n        Label away = new Label(\"Mill Town 1\");\n        Label time = new Label(\"67 minutes played\");\n        VBox board = new VBox(10, home, away, time);\n        board.setPadding(new Insets(12));\n        board.setAlignment(Pos.CENTER);\n\n        stage.setScene(new Scene(board, 240, 140));\n        stage.setTitle(\"Scoreboard\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[],\"window\":{\"windows\":[{\"title\":\"Scoreboard\",\"scene\":{\"width\":240,\"height\":140,\"root\":{\"n\":1,\"type\":\"VBox\",\"padding\":[12,12,12,12],\"spacing\":10,\"alignment\":\"CENTER\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Harbor Rovers 2\"},{\"n\":3,\"type\":\"Label\",\"text\":\"Mill Town 1\"},{\"n\":4,\"type\":\"Label\",\"text\":\"67 minutes played\"}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Scoreboard\\\"\\n  VBox spacing 10, padding 12, alignment CENTER\\n    Label \\\"Harbor Rovers 2\\\"\\n    Label \\\"Mill Town 1\\\"\\n    Label \\\"67 minutes played\\\"\"}\n```\n\nThe scene here is bigger than the labels need, so the alignment shows: the labels sit in the middle.\n\n### GridPane\n\nA **GridPane** puts its children into the cells of a grid of rows and columns. `add(node, column, row)` puts a node into a cell. The **column comes first**, then the row, as x before y, and both count from 0. The grid has as many rows and columns as its cells need. `setHgap` sets the room between columns, and `setVgap` between rows.\n\nA phone's dial pad is a grid of buttons, and two nested loops make the digits 1 to 9:\n\n```java\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.GridPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        GridPane keypad = new GridPane();\n        keypad.setHgap(4);\n        keypad.setVgap(4);\n        keypad.setPadding(new Insets(8));\n\n        TextField number = new TextField();\n        number.setPromptText(\"Number\");\n        keypad.add(number, 0, 0, 3, 1);\n\n        for (int row = 1; row <= 3; row++) {\n            for (int column = 0; column < 3; column++) {\n                int digit = (row - 1) * 3 + column + 1;\n                keypad.add(new Button(\"\" + digit), column, row);\n            }\n        }\n        keypad.add(new Button(\"0\"), 1, 4);\n\n        stage.setScene(new Scene(keypad));\n        stage.setTitle(\"Phone\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[],\"window\":{\"windows\":[{\"title\":\"Phone\",\"scene\":{\"root\":{\"n\":1,\"type\":\"GridPane\",\"padding\":[8,8,8,8],\"hgap\":4,\"vgap\":4,\"children\":[{\"n\":2,\"type\":\"TextField\",\"column\":0,\"row\":0,\"columnSpan\":3,\"text\":\"\",\"promptText\":\"Number\"},{\"n\":3,\"type\":\"Button\",\"column\":0,\"row\":1,\"text\":\"1\"},{\"n\":4,\"type\":\"Button\",\"column\":1,\"row\":1,\"text\":\"2\"},{\"n\":5,\"type\":\"Button\",\"column\":2,\"row\":1,\"text\":\"3\"},{\"n\":6,\"type\":\"Button\",\"column\":0,\"row\":2,\"text\":\"4\"},{\"n\":7,\"type\":\"Button\",\"column\":1,\"row\":2,\"text\":\"5\"},{\"n\":8,\"type\":\"Button\",\"column\":2,\"row\":2,\"text\":\"6\"},{\"n\":9,\"type\":\"Button\",\"column\":0,\"row\":3,\"text\":\"7\"},{\"n\":10,\"type\":\"Button\",\"column\":1,\"row\":3,\"text\":\"8\"},{\"n\":11,\"type\":\"Button\",\"column\":2,\"row\":3,\"text\":\"9\"},{\"n\":12,\"type\":\"Button\",\"column\":1,\"row\":4,\"text\":\"0\"}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Phone\\\"\\n  GridPane hgap 4, vgap 4, padding 8\\n    (0, 0) span 3x1 TextField \\\"\\\", prompt \\\"Number\\\"\\n    (0, 1) Button \\\"1\\\"\\n    (1, 1) Button \\\"2\\\"\\n    (2, 1) Button \\\"3\\\"\\n    (0, 2) Button \\\"4\\\"\\n    (1, 2) Button \\\"5\\\"\\n    (2, 2) Button \\\"6\\\"\\n    (0, 3) Button \\\"7\\\"\\n    (1, 3) Button \\\"8\\\"\\n    (2, 3) Button \\\"9\\\"\\n    (1, 4) Button \\\"0\\\"\"}\n```\n\n- `keypad.add(number, 0, 0, 3, 1)` puts the field into column 0 of row 0, and lets it **span** 3 columns and 1 row: it's as wide as the three columns of buttons below it.\n- The outer loop goes through rows 1 to 3, and the inner loop through the columns of each row. In row 1 the digits are 1, 2 and 3, in row 2 they are 4, 5 and 6, and so on: `(row - 1) * 3 + column + 1`.\n- `\"\" + digit` turns the number into a String for the button's text.\n- The 0 sits alone in row 4, in the middle column. Cells that nothing is added to stay empty.\n\nIn the window as text, each node of a grid starts with its cell, column first, as in `add`: `(1, 4) Button \"0\"`. The cells are listed row by row, and a node that spans more than one cell says so: `(0, 0) span 3x1 TextField \"\"` is 3 columns wide and 1 row high. `GridPane.getColumnIndex(node)` and `GridPane.getRowIndex(node)` return the column and the row a node was added to.\n","task":"A scoreboard at the end of a match. Build it: the title is `Scoreboard`, and the root is a VBox with the spacing 6, the padding 10 at the top and the bottom and 20 on the left and the right, and the alignment `CENTER`. It holds the labels `Harbor Rovers 2`, `Mill Town 1` and `Full time`, in that order. No size for the scene. The window it should show is drawn below.\n","kind":"code","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        VBox board = new VBox();\n\n        stage.setScene(new Scene(board));\n        stage.setTitle(\"Scoreboard\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        VBox board = new VBox();\n        board.setSpacing(6);\n        board.setPadding(new Insets(10, 20, 10, 20));\n        board.setAlignment(Pos.CENTER);\n        board.getChildren().addAll(new Label(\"Harbor Rovers 2\"), new Label(\"Mill Town 1\"), new Label(\"Full time\"));\n\n        stage.setScene(new Scene(board));\n        stage.setTitle(\"Scoreboard\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["The gap between a VBox's children is its spacing, the place of its children is its alignment (a Pos constant), and the empty edge around them is its padding (an Insets). Insets and Pos need `import javafx.geometry.Insets;` and `import javafx.geometry.Pos;`.","`board.setSpacing(6);` and `board.setAlignment(Pos.CENTER);`. Insets takes the top, the right, the bottom and the left, in that order: `new Insets(10, 20, 10, 20)`."],"tests":[{"name":"Window","stdin":"","expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"Scoreboard","scene":{"root":{"n":1,"type":"VBox","padding":[10,20,10,20],"spacing":6,"alignment":"CENTER","children":[{"n":2,"type":"Label","text":"Harbor Rovers 2"},{"n":3,"type":"Label","text":"Mill Town 1"},{"n":4,"type":"Label","text":"Full time"}]}}}],"problems":[]},"outline":"Window \"Scoreboard\"\n  VBox spacing 6, padding 10 20 10 20, alignment CENTER\n    Label \"Harbor Rovers 2\"\n    Label \"Mill Town 1\"\n    Label \"Full time\""}],"require":[],"forbid":[],"more":[{"task":"A keypad for a gate, with the digits in the order of a calculator: `7`, `8` and `9` in the top row, `1`, `2` and `3` in the bottom row of digits. Build it: the title is `Gate`, and the root is a GridPane with the gaps 5 between both columns and rows, and the padding 10 on every side. It holds:\n\n- rows 0 to 2: the digit buttons, `7 8 9`, then `4 5 6`, then `1 2 3`, each row from column 0 to column 2\n- row 3: the buttons `Clear` in column 0, `0` in column 1 and `Open` in column 2\n\nMake the nine digit buttons with two nested `for` loops, one for the rows and one for the columns (the check counts the `new Button` in your code: one in the loops and three for row 3). The window it should show is drawn below.\n","kind":"code","seed":"import javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.GridPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        GridPane keypad = new GridPane();\n\n        stage.setScene(new Scene(keypad));\n        stage.setTitle(\"Gate\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.GridPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        GridPane keypad = new GridPane();\n        keypad.setHgap(5);\n        keypad.setVgap(5);\n        keypad.setPadding(new Insets(10));\n\n        for (int row = 0; row < 3; row++) {\n            for (int column = 0; column < 3; column++) {\n                int digit = (2 - row) * 3 + column + 1;\n                keypad.add(new Button(\"\" + digit), column, row);\n            }\n        }\n        keypad.add(new Button(\"Clear\"), 0, 3);\n        keypad.add(new Button(\"0\"), 1, 3);\n        keypad.add(new Button(\"Open\"), 2, 3);\n\n        stage.setScene(new Scene(keypad));\n        stage.setTitle(\"Gate\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Which digit starts row 0, which starts row 1, and which starts row 2? How much does the first digit change from one row to the next?","Row 2 starts with 1, row 1 with 4 and row 0 with 7: the first digit of a row is `(2 - row) * 3 + 1`. Add the column to it for the digit in each cell.","`keypad.add(new Button(\"\" + digit), column, row);` takes the column first. The gaps are `setHgap(5)` and `setVgap(5)`, and the padding `new Insets(10)`."],"tests":[{"name":"Window","stdin":"","expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"Gate","scene":{"root":{"n":1,"type":"GridPane","padding":[10,10,10,10],"hgap":5,"vgap":5,"children":[{"n":2,"type":"Button","column":0,"row":0,"text":"7"},{"n":3,"type":"Button","column":1,"row":0,"text":"8"},{"n":4,"type":"Button","column":2,"row":0,"text":"9"},{"n":5,"type":"Button","column":0,"row":1,"text":"4"},{"n":6,"type":"Button","column":1,"row":1,"text":"5"},{"n":7,"type":"Button","column":2,"row":1,"text":"6"},{"n":8,"type":"Button","column":0,"row":2,"text":"1"},{"n":9,"type":"Button","column":1,"row":2,"text":"2"},{"n":10,"type":"Button","column":2,"row":2,"text":"3"},{"n":11,"type":"Button","column":0,"row":3,"text":"Clear"},{"n":12,"type":"Button","column":1,"row":3,"text":"0"},{"n":13,"type":"Button","column":2,"row":3,"text":"Open"}]}}}],"problems":[]},"outline":"Window \"Gate\"\n  GridPane hgap 5, vgap 5, padding 10\n    (0, 0) Button \"7\"\n    (1, 0) Button \"8\"\n    (2, 0) Button \"9\"\n    (0, 1) Button \"4\"\n    (1, 1) Button \"5\"\n    (2, 1) Button \"6\"\n    (0, 2) Button \"1\"\n    (1, 2) Button \"2\"\n    (2, 2) Button \"3\"\n    (0, 3) Button \"Clear\"\n    (1, 3) Button \"0\"\n    (2, 3) Button \"Open\""}],"require":[{"pattern":"\\bfor\\s*\\(","min":2,"message":"Make the digit buttons with two nested for loops, one for the rows and one for the columns."},{"pattern":"\\bnew\\s+Button\\b","max":4,"message":"Make the nine digit buttons in the loops, with one `new Button` there, and the three buttons of row 3 on their own: four `new Button` in all."}],"forbid":[]},{"task":"A few questions about a grid, padding and a box's settings. `getAlignment()` returns a Pos, which prints as its name. What does the program print?\n","kind":"predict","seed":"import javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        GridPane keypad = new GridPane();\n        Button seven = new Button(\"7\");\n        keypad.add(seven, 0, 2);\n        keypad.add(new Button(\"8\"), 1, 2);\n        System.out.println(GridPane.getColumnIndex(seven) + \" \" + GridPane.getRowIndex(seven));\n\n        Insets padding = new Insets(4, 8, 12, 16);\n        keypad.setPadding(padding);\n        System.out.println(padding.getRight() + padding.getLeft());\n\n        VBox box = new VBox(keypad);\n        System.out.println(box.getAlignment() + \" \" + box.getSpacing());\n\n        stage.setScene(new Scene(box));\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        GridPane keypad = new GridPane();\n        Button seven = new Button(\"7\");\n        keypad.add(seven, 0, 2);\n        keypad.add(new Button(\"8\"), 1, 2);\n        System.out.println(GridPane.getColumnIndex(seven) + \" \" + GridPane.getRowIndex(seven));\n\n        Insets padding = new Insets(4, 8, 12, 16);\n        keypad.setPadding(padding);\n        System.out.println(padding.getRight() + padding.getLeft());\n\n        VBox box = new VBox(keypad);\n        System.out.println(box.getAlignment() + \" \" + box.getSpacing());\n\n        stage.setScene(new Scene(box));\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["`add(seven, 0, 2)` takes the column first: column 0, row 2.","Insets are top, right, bottom, left: the right is 8.0 and the left 16.0, and they are doubles. A VBox that was given nothing else has the alignment TOP_LEFT and the spacing 0.0."],"tests":[{"name":"Output","stdin":"","expect":"0 2\n24.0\nTOP_LEFT 0.0","hidden":false}],"require":[],"forbid":[],"lines":["0 2","24.0","TOP_LEFT 0.0"]}]}