{"id":"gui-basics-scene-layout","slug":"scene-and-layout","title":"Scene, layout and components","text":"Step 1's window held one label. A window usually holds more: some text, a few buttons, a field to type in. JavaFX builds a window from parts inside parts:\n\n- The **Stage** is the window itself, with its frame and its title bar.\n- The stage shows a **Scene**: everything inside the frame.\n- The scene has one **root**, usually a **layout**. A layout holds other parts and decides where they go.\n- The layout holds the **components**: labels, buttons, text fields. JavaFX calls them **controls**.\n\nEverything that can be in a scene is a **node** (the class `Node`), layouts and controls alike. A scene's root must be a **Parent**, a node that can hold others, and every layout is one. JavaFX counts labels as Parents too, so step 1 could make a label the root, but a label can't hold your other components. For several components, the root is a layout.\n\n### FlowPane\n\nThe simplest layout is **FlowPane**. A café prints a slip for each order, with buttons for the size of the cup:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label order = new Label(\"Order 17: oat latte\");\n        Button small = new Button(\"Small\");\n        Button medium = new Button(\"Medium\");\n        Button large = new Button(\"Large\");\n\n        FlowPane slip = new FlowPane();\n        slip.getChildren().add(order);\n        slip.getChildren().add(small);\n        slip.getChildren().add(medium);\n        slip.getChildren().add(large);\n\n        Scene scene = new Scene(slip);\n        stage.setScene(scene);\n        stage.setTitle(\"Order slip\");\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\":\"Order slip\",\"scene\":{\"root\":{\"n\":1,\"type\":\"FlowPane\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Order 17: oat latte\"},{\"n\":3,\"type\":\"Button\",\"text\":\"Small\"},{\"n\":4,\"type\":\"Button\",\"text\":\"Medium\"},{\"n\":5,\"type\":\"Button\",\"text\":\"Large\"}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Order slip\\\"\\n  FlowPane\\n    Label \\\"Order 17: oat latte\\\"\\n    Button \\\"Small\\\"\\n    Button \\\"Medium\\\"\\n    Button \\\"Large\\\"\"}\n```\n\n- `new Button(\"Small\")` is a **button** with the text Small. The user can click it, but for now nothing happens when they do: module 49 makes a click run code.\n- A FlowPane puts its children side by side, from left to right, and goes on in a new row when the window is too narrow for them, the way words flow in a paragraph.\n- `slip.getChildren()` is the pane's list of children. It's a `List<Node>`, so `add`, `size`, `get`, `remove` and the others work as they do on an ArrayList. (Its type is `ObservableList<Node>`: a list that tells JavaFX when it changes, so the window can change with it.)\n- The order of the list is the order in the window. The label is first because it was added first.\n\nLayouts come from the package `javafx.scene.layout`, and controls from `javafx.scene.control`.\n\n### The window as text\n\nUnder each drawn window, \"The window as text\" lists everything in the window, a line for each part, indented under the part that holds it. For the order slip:\n\n```text\nWindow \"Order slip\"\n  FlowPane\n    Label \"Order 17: oat latte\"\n    Button \"Small\"\n    Button \"Medium\"\n    Button \"Large\"\n```\n\nThe first line is the window with its title. Then come the nodes, each with its class and its text. The challenges of part 13 compare exactly this with the window of the model solution: the classes, their order, what holds what, and every text. A window that looks the same but is built from other layouts doesn't pass, so the tasks always say which layouts to use.\n\n### Shorter ways to add\n\n`slip.getChildren().addAll(order, small, medium, large);` adds the four nodes in one call, in the order given. A FlowPane can also take its children in its constructor: `new FlowPane(order, small, medium, large)` makes the same slip.\n\nSince the children are a list, a loop can fill it too. The café's milks, from an array:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        String[] milks = {\"Whole\", \"Oat\", \"Soy\", \"None\"};\n        FlowPane slip = new FlowPane();\n        slip.getChildren().add(new Label(\"Milk:\"));\n        for (String milk : milks) {\n            slip.getChildren().add(new Button(milk));\n        }\n\n        stage.setScene(new Scene(slip));\n        stage.setTitle(\"Order slip\");\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\":\"Order slip\",\"scene\":{\"root\":{\"n\":1,\"type\":\"FlowPane\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Milk:\"},{\"n\":3,\"type\":\"Button\",\"text\":\"Whole\"},{\"n\":4,\"type\":\"Button\",\"text\":\"Oat\"},{\"n\":5,\"type\":\"Button\",\"text\":\"Soy\"},{\"n\":6,\"type\":\"Button\",\"text\":\"None\"}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Order slip\\\"\\n  FlowPane\\n    Label \\\"Milk:\\\"\\n    Button \\\"Whole\\\"\\n    Button \\\"Oat\\\"\\n    Button \\\"Soy\\\"\\n    Button \\\"None\\\"\"}\n```\n\nEach round of the loop creates a new Button, and that matters: a node can be in **one place** in a window at a time. Adding a node to a second pane takes it out of the first, and adding the same node to one pane twice stops the program with an IllegalArgumentException. Four buttons in a window are four Button objects.\n\n### A window from a file\n\nThe café keeps its milks in a file, so that the slip changes when the file does. `Files.readAllLines` (module 42) reads the file's lines into a list:\n\n```file milks.txt\nWhole\nOat\nSoy\nAlmond\n```\n\n```java\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        FlowPane slip = new FlowPane();\n        slip.getChildren().add(new Label(\"Milk:\"));\n        for (String milk : Files.readAllLines(Paths.get(\"milks.txt\"))) {\n            slip.getChildren().add(new Button(milk));\n        }\n\n        stage.setScene(new Scene(slip));\n        stage.setTitle(\"Order slip\");\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\":\"Order slip\",\"scene\":{\"root\":{\"n\":1,\"type\":\"FlowPane\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Milk:\"},{\"n\":3,\"type\":\"Button\",\"text\":\"Whole\"},{\"n\":4,\"type\":\"Button\",\"text\":\"Oat\"},{\"n\":5,\"type\":\"Button\",\"text\":\"Soy\"},{\"n\":6,\"type\":\"Button\",\"text\":\"Almond\"}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Order slip\\\"\\n  FlowPane\\n    Label \\\"Milk:\\\"\\n    Button \\\"Whole\\\"\\n    Button \\\"Oat\\\"\\n    Button \\\"Soy\\\"\\n    Button \\\"Almond\\\"\"}\n```\n\n`readAllLines` can throw an IOException, which is a checked exception. Application declares its `start` with `throws Exception`, so yours may say the same, and then reading needs no `try`. If the file can't be read, `start` stops with the exception, and the program ends with an error, as it does when an exception isn't caught.\n\n### The window's size\n\n`new Scene(slip, 360, 120)` gives the scene a width and a height, in pixels. Without them, JavaFX makes the window just big enough for what it holds. The challenges don't check sizes, so they leave them out.\n","task":"The café's slip for tea. Build this window: its title is `Tea order`, and the scene's root is a FlowPane that holds, in this order, the label `Order 21: green tea` and the buttons `Pot`, `Cup` and `To go`. No spacing, padding or sizes. The window it should show is drawn below.\n","kind":"code","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        FlowPane slip = new FlowPane();\n\n        stage.setScene(new Scene(slip));\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.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        FlowPane slip = new FlowPane();\n        slip.getChildren().add(new Label(\"Order 21: green tea\"));\n        slip.getChildren().add(new Button(\"Pot\"));\n        slip.getChildren().add(new Button(\"Cup\"));\n        slip.getChildren().add(new Button(\"To go\"));\n\n        stage.setScene(new Scene(slip));\n        stage.setTitle(\"Tea order\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Each node goes into `slip.getChildren()`, the pane's list of children, in the order it should appear. The title belongs to the stage.","`slip.getChildren().add(new Label(\"Order 21: green tea\"));`, then a `new Button(...)` for each button, and `stage.setTitle(\"Tea order\");`."],"tests":[{"name":"Window","stdin":"","expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"Tea order","scene":{"root":{"n":1,"type":"FlowPane","children":[{"n":2,"type":"Label","text":"Order 21: green tea"},{"n":3,"type":"Button","text":"Pot"},{"n":4,"type":"Button","text":"Cup"},{"n":5,"type":"Button","text":"To go"}]}}}],"problems":[]},"outline":"Window \"Tea order\"\n  FlowPane\n    Label \"Order 21: green tea\"\n    Button \"Pot\"\n    Button \"Cup\"\n    Button \"To go\""}],"require":[],"forbid":[],"more":[{"task":"The café's syrups are in the file `syrups.txt`, one per line. Build the slip that offers them: the window's title is `Syrups`, and its root is a FlowPane holding the label `Add a syrup:` and then a button for each line of the file, in the file's order. For the file shown below, the window should look like the drawing under it.\n","kind":"code","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        FlowPane slip = new FlowPane();\n\n        stage.setScene(new Scene(slip));\n        stage.setTitle(\"Syrups\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import java.nio.file.Files;\nimport java.nio.file.Paths;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) throws Exception {\n        FlowPane slip = new FlowPane();\n        slip.getChildren().add(new Label(\"Add a syrup:\"));\n        for (String syrup : Files.readAllLines(Paths.get(\"syrups.txt\"))) {\n            slip.getChildren().add(new Button(syrup));\n        }\n\n        stage.setScene(new Scene(slip));\n        stage.setTitle(\"Syrups\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Read the lines with `Files.readAllLines(Paths.get(\"syrups.txt\"))`, which needs `import java.nio.file.Files;` and `import java.nio.file.Paths;`. It can throw an IOException, so add `throws Exception` to the header of `start`.","Add the label first, then loop over the lines and add a `new Button(syrup)` for each one."],"tests":[{"name":"Test 1","stdin":"","files":{"syrups.txt":"Vanilla\nCaramel\nHazelnut\n"},"expect":"","hidden":false,"events":[],"window":{"windows":[{"title":"Syrups","scene":{"root":{"n":1,"type":"FlowPane","children":[{"n":2,"type":"Label","text":"Add a syrup:"},{"n":3,"type":"Button","text":"Vanilla"},{"n":4,"type":"Button","text":"Caramel"},{"n":5,"type":"Button","text":"Hazelnut"}]}}}],"problems":[]},"outline":"Window \"Syrups\"\n  FlowPane\n    Label \"Add a syrup:\"\n    Button \"Vanilla\"\n    Button \"Caramel\"\n    Button \"Hazelnut\""},{"name":"Test 2","stdin":"","files":{"syrups.txt":"Maple\nCinnamon\nGingerbread\nLavender\nMint\n"},"expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Syrups","scene":{"root":{"n":1,"type":"FlowPane","children":[{"n":2,"type":"Label","text":"Add a syrup:"},{"n":3,"type":"Button","text":"Maple"},{"n":4,"type":"Button","text":"Cinnamon"},{"n":5,"type":"Button","text":"Gingerbread"},{"n":6,"type":"Button","text":"Lavender"},{"n":7,"type":"Button","text":"Mint"}]}}}],"problems":[]},"outline":"Window \"Syrups\"\n  FlowPane\n    Label \"Add a syrup:\"\n    Button \"Maple\"\n    Button \"Cinnamon\"\n    Button \"Gingerbread\"\n    Button \"Lavender\"\n    Button \"Mint\""},{"name":"Test 3","stdin":"","files":{"syrups.txt":"Honey\n"},"expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Syrups","scene":{"root":{"n":1,"type":"FlowPane","children":[{"n":2,"type":"Label","text":"Add a syrup:"},{"n":3,"type":"Button","text":"Honey"}]}}}],"problems":[]},"outline":"Window \"Syrups\"\n  FlowPane\n    Label \"Add a syrup:\"\n    Button \"Honey\""}],"require":[],"forbid":[]},{"task":"The slip for order 30 is drafted in one FlowPane and then built in another. A node can be in one place at a time. `add(1, cup)` puts `cup` at index 1 of the list. What does the program print?\n","kind":"predict","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label note = new Label(\"No sugar\");\n        Button cup = new Button(\"Cup\");\n        FlowPane draft = new FlowPane(note, cup);\n        FlowPane slip = new FlowPane(new Label(\"Order 30: cocoa\"));\n        System.out.println(draft.getChildren().size() + \" \" + slip.getChildren().size());\n\n        slip.getChildren().add(note);\n        System.out.println(draft.getChildren().size() + \" \" + slip.getChildren().size());\n\n        slip.getChildren().add(1, cup);\n        System.out.println(slip.getChildren().indexOf(note) + \" \" + draft.getChildren().isEmpty());\n\n        stage.setScene(new Scene(slip));\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.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.FlowPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label note = new Label(\"No sugar\");\n        Button cup = new Button(\"Cup\");\n        FlowPane draft = new FlowPane(note, cup);\n        FlowPane slip = new FlowPane(new Label(\"Order 30: cocoa\"));\n        System.out.println(draft.getChildren().size() + \" \" + slip.getChildren().size());\n\n        slip.getChildren().add(note);\n        System.out.println(draft.getChildren().size() + \" \" + slip.getChildren().size());\n\n        slip.getChildren().add(1, cup);\n        System.out.println(slip.getChildren().indexOf(note) + \" \" + draft.getChildren().isEmpty());\n\n        stage.setScene(new Scene(slip));\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["At first `draft` holds the note and the cup, and `slip` holds one label. Adding `note` to `slip` takes it out of `draft`.","`add(1, cup)` takes the cup out of `draft` too and puts it between the order's label and the note, so the note moves to index 2."],"tests":[{"name":"Output","stdin":"","expect":"2 1\n1 2\n2 true","hidden":false}],"require":[],"forbid":[],"lines":["2 1","1 2","2 true"]}]}