{"id":"gui-views-switching-scenes","slug":"switching-scenes","title":"Switching between scenes","text":"Each window in modules 48 and 49 showed one scene, set once in `start`. Most applications have more than one **view**: a main view, a page of settings, a help page, a form and then its result. The user moves between them by clicking, and the window shows one view at a time.\n\nThis module shows a few ways to build that. The simplest one uses what you know already: a stage shows a scene, and it can be given another scene at any time, also in an event handler.\n\n### Two scenes, one window\n\nA sailing app shows the distance to the next harbor, and it has a page of settings:\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.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label route = new Label(\"Next harbor: 12 km\");\n        Button toSettings = new Button(\"Settings\");\n        Scene routeScene = new Scene(new VBox(10, route, toSettings));\n\n        Label heading = new Label(\"Settings\");\n        Button back = new Button(\"Back\");\n        Scene settingsScene = new Scene(new VBox(10, heading, back));\n\n        toSettings.setOnAction(event -> stage.setScene(settingsScene));\n        back.setOnAction(event -> stage.setScene(routeScene));\n\n        stage.setScene(routeScene);\n        stage.setTitle(\"Sailing\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button \\\"Settings\\\"\"],\"window\":{\"windows\":[{\"title\":\"Sailing\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"spacing\":10,\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Settings\"},{\"n\":3,\"type\":\"Button\",\"text\":\"Back\",\"onAction\":true}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Sailing\\\"\\n  VBox spacing 10\\n    Label \\\"Settings\\\"\\n    Button \\\"Back\\\"\"}\n```\n\nThe window is drawn after a click on Settings, so it shows the settings scene. (The windows in the lesson text are drawn as the clicks listed with them leave them. When you run a program of your own, you can click its window, as in modules 48 and 49: Java Arena runs the program again from the start with all your clicks so far, and draws the scene it ends on.)\n\n- `start` builds two scenes, each with a layout and nodes of its own, and the window starts with the route scene.\n- The handler of Settings calls `stage.setScene(settingsScene)`. The window shows the other scene at once, in place of everything it showed before.\n- The title belongs to the stage, not to a scene, so it stays `Sailing`. A handler can change it too, with `stage.setTitle(...)`.\n- The lambdas use `stage`, `routeScene` and `settingsScene`: a parameter of `start` and two local variables. None of them changes after it gets its value, so they're effectively final, and a lambda may use them (module 49).\n- The route scene isn't gone while the settings show. The variable `routeScene` still refers to it, with its label and its button, and Back shows it again.\n\nThe window as text, and the checks of this module, see only the scene that the window shows at the end.\n\n### Changes in a scene that isn't showing\n\nThe settings get two buttons that choose how distances are shown:\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.HBox;\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 route = new Label(\"Next harbor: 12 km\");\n        Button toSettings = new Button(\"Settings\");\n        Scene routeScene = new Scene(new VBox(10, route, toSettings));\n\n        Label heading = new Label(\"Distances in kilometers\");\n        Button km = new Button(\"Kilometers\");\n        Button nautical = new Button(\"Nautical miles\");\n        Button back = new Button(\"Back\");\n        Scene settingsScene = new Scene(new VBox(10, heading, new HBox(10, km, nautical), back));\n\n        toSettings.setOnAction(event -> stage.setScene(settingsScene));\n        back.setOnAction(event -> stage.setScene(routeScene));\n        km.setOnAction(event -> {\n            heading.setText(\"Distances in kilometers\");\n            route.setText(\"Next harbor: 12 km\");\n        });\n        nautical.setOnAction(event -> {\n            heading.setText(\"Distances in nautical miles\");\n            route.setText(\"Next harbor: 6.5 nautical miles\");\n        });\n\n        stage.setScene(routeScene);\n        stage.setTitle(\"Sailing\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button \\\"Settings\\\"\",\"click Button \\\"Nautical miles\\\"\",\"click Button \\\"Back\\\"\"],\"window\":{\"windows\":[{\"title\":\"Sailing\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"spacing\":10,\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Next harbor: 6.5 nautical miles\"},{\"n\":3,\"type\":\"Button\",\"text\":\"Settings\",\"onAction\":true}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Sailing\\\"\\n  VBox spacing 10\\n    Label \\\"Next harbor: 6.5 nautical miles\\\"\\n    Button \\\"Settings\\\"\"}\n```\n\nThe handler of Nautical miles changes the label `route`, which is in the route scene, while the window shows the settings. That's fine: the nodes of a scene that isn't showing are ordinary objects, and they keep everything that's done to them. When Back shows the route scene again, its label has the new text.\n\nThat's why `start` builds each scene once and keeps it in a variable. Switching only chooses which scene the window shows, and nothing in the other scene is lost: not a label's text, and not what the user typed into a text field.\n\n### One scene, one root\n\nA scene's root belongs to that scene only. `new Scene(layout)` with a layout that is already the root of another scene stops with an IllegalArgumentException (\"is already set as root of another scene\"). So a handler that does `stage.setScene(new Scene(sameLayout))` works the first time and fails the second. Build each scene once, as above, and the problem doesn't come up.\n","task":"An e-reader has two scenes, the book and the settings. Fill in the three blanks, so that `Settings` shows the settings scene, `Back` shows the book again, and the window starts with the book. The task card shows the window after a click on Settings.\n","kind":"fill","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\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 book = new Label(\"The Glass Orchard, chapter 3 of 12\");\n        Button toSettings = new Button(\"Settings\");\n        Scene bookScene = new Scene(new VBox(10, book, toSettings));\n\n        Label heading = new Label(\"Settings\");\n        Button back = new Button(\"Back\");\n        Scene settingsScene = new Scene(new VBox(10, heading, back));\n\n        toSettings.setOnAction(event -> stage.[[setScene]](settingsScene));\n        back.setOnAction(event -> stage.setScene([[bookScene]]));\n\n        stage.setScene([[bookScene]]);\n        stage.setTitle(\"Reader\");\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.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label book = new Label(\"The Glass Orchard, chapter 3 of 12\");\n        Button toSettings = new Button(\"Settings\");\n        Scene bookScene = new Scene(new VBox(10, book, toSettings));\n\n        Label heading = new Label(\"Settings\");\n        Button back = new Button(\"Back\");\n        Scene settingsScene = new Scene(new VBox(10, heading, back));\n\n        toSettings.setOnAction(event -> stage.setScene(settingsScene));\n        back.setOnAction(event -> stage.setScene(bookScene));\n\n        stage.setScene(bookScene);\n        stage.setTitle(\"Reader\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Look at what `start` does to the stage at its end, just before `setTitle`: the first blank calls that same method.","The first blank is `setScene`. Back and the start of the program both show the scene with the book, `bookScene`."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"Settings\""],"window":{"windows":[{"title":"Reader","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"Settings"},{"n":3,"type":"Button","text":"Back","onAction":true}]}}}],"problems":[]},"outline":"Window \"Reader\"\n  VBox spacing 10\n    Label \"Settings\"\n    Button \"Back\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Reader","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"The Glass Orchard, chapter 3 of 12"},{"n":3,"type":"Button","text":"Settings","onAction":true}]}}}],"problems":[]},"outline":"Window \"Reader\"\n  VBox spacing 10\n    Label \"The Glass Orchard, chapter 3 of 12\"\n    Button \"Settings\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"Settings\"","click Button \"Back\""],"window":{"windows":[{"title":"Reader","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"The Glass Orchard, chapter 3 of 12"},{"n":3,"type":"Button","text":"Settings","onAction":true}]}}}],"problems":[]},"outline":"Window \"Reader\"\n  VBox spacing 10\n    Label \"The Glass Orchard, chapter 3 of 12\"\n    Button \"Settings\""},{"name":"Test 4","stdin":"","expect":"","hidden":true,"events":["click Button \"Settings\"","click Button \"Back\"","click Button \"Settings\""],"window":{"windows":[{"title":"Reader","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"Settings"},{"n":3,"type":"Button","text":"Back","onAction":true}]}}}],"problems":[]},"outline":"Window \"Reader\"\n  VBox spacing 10\n    Label \"Settings\"\n    Button \"Back\""}],"require":[],"forbid":[],"more":[{"task":"A phone has a home scene and a settings scene. The program clicks its own buttons with `fire()`, which runs a button's handler whether or not its scene is showing. What does it 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.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label status = new Label(\"Wi-Fi is on\");\n        Button open = new Button(\"Settings\");\n        Scene home = new Scene(new VBox(status, open));\n\n        Button off = new Button(\"Turn Wi-Fi off\");\n        Button back = new Button(\"Back\");\n        Scene settings = new Scene(new VBox(off, back));\n\n        open.setOnAction(event -> {\n            stage.setScene(settings);\n            stage.setTitle(\"Settings\");\n        });\n        off.setOnAction(event -> status.setText(\"Wi-Fi is off\"));\n        back.setOnAction(event -> {\n            stage.setScene(home);\n            stage.setTitle(\"Home\");\n        });\n\n        stage.setScene(home);\n        stage.setTitle(\"Home\");\n        System.out.println(stage.getTitle() + \": \" + status.getText());\n        open.fire();\n        off.fire();\n        System.out.println(stage.getTitle() + \" \" + (stage.getScene() == home));\n        back.fire();\n        System.out.println(stage.getTitle() + \": \" + status.getText());\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.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label status = new Label(\"Wi-Fi is on\");\n        Button open = new Button(\"Settings\");\n        Scene home = new Scene(new VBox(status, open));\n\n        Button off = new Button(\"Turn Wi-Fi off\");\n        Button back = new Button(\"Back\");\n        Scene settings = new Scene(new VBox(off, back));\n\n        open.setOnAction(event -> {\n            stage.setScene(settings);\n            stage.setTitle(\"Settings\");\n        });\n        off.setOnAction(event -> status.setText(\"Wi-Fi is off\"));\n        back.setOnAction(event -> {\n            stage.setScene(home);\n            stage.setTitle(\"Home\");\n        });\n\n        stage.setScene(home);\n        stage.setTitle(\"Home\");\n        System.out.println(stage.getTitle() + \": \" + status.getText());\n        open.fire();\n        off.fire();\n        System.out.println(stage.getTitle() + \" \" + (stage.getScene() == home));\n        back.fire();\n        System.out.println(stage.getTitle() + \": \" + status.getText());\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["`open.fire()` shows the settings scene and sets the title to `Settings`, so the window no longer shows `home`.","`off` changes the label in the home scene while the settings show. The label keeps its new text, and `back` shows the home scene with it."],"tests":[{"name":"Output","stdin":"","expect":"Home: Wi-Fi is on\nSettings false\nHome: Wi-Fi is off","hidden":false}],"require":[],"forbid":[],"lines":["Home: Wi-Fi is on","Settings false","Home: Wi-Fi is off"]},{"task":"A quiz with one question has three scenes. The start scene and the question scene are built already, each a VBox with the spacing 10:\n\n- start: the label `Space quiz` and the button `Start`\n- question: the label `Which planet is the largest?` and an HBox with the spacing 10 holding the buttons `Mars`, `Jupiter` and `Venus`\n\nBuild the third scene, the result: a VBox with the spacing 10 and no padding, holding an empty label and the button `Play again`. Then give the buttons their handlers:\n\n- `Start` shows the question scene.\n- `Jupiter` sets the result's label to `Right: Jupiter is the largest.` and shows the result scene.\n- `Mars` and `Venus` set it to `Not quite: the largest is Jupiter.` and show the result scene.\n- `Play again` shows the start scene.\n\nThe window's title stays `Space quiz`. The task card shows the window after Start and Jupiter.\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.HBox;\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        Button begin = new Button(\"Start\");\n        Scene startScene = new Scene(new VBox(10, new Label(\"Space quiz\"), begin));\n\n        Button mars = new Button(\"Mars\");\n        Button jupiter = new Button(\"Jupiter\");\n        Button venus = new Button(\"Venus\");\n        HBox answers = new HBox(10, mars, jupiter, venus);\n        Scene questionScene = new Scene(new VBox(10, new Label(\"Which planet is the largest?\"), answers));\n\n        stage.setScene(startScene);\n        stage.setTitle(\"Space quiz\");\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.HBox;\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        Button begin = new Button(\"Start\");\n        Scene startScene = new Scene(new VBox(10, new Label(\"Space quiz\"), begin));\n\n        Button mars = new Button(\"Mars\");\n        Button jupiter = new Button(\"Jupiter\");\n        Button venus = new Button(\"Venus\");\n        HBox answers = new HBox(10, mars, jupiter, venus);\n        Scene questionScene = new Scene(new VBox(10, new Label(\"Which planet is the largest?\"), answers));\n\n        Label result = new Label(\"\");\n        Button again = new Button(\"Play again\");\n        Scene resultScene = new Scene(new VBox(10, result, again));\n\n        begin.setOnAction(event -> stage.setScene(questionScene));\n        jupiter.setOnAction(event -> {\n            result.setText(\"Right: Jupiter is the largest.\");\n            stage.setScene(resultScene);\n        });\n        mars.setOnAction(event -> {\n            result.setText(\"Not quite: the largest is Jupiter.\");\n            stage.setScene(resultScene);\n        });\n        venus.setOnAction(event -> {\n            result.setText(\"Not quite: the largest is Jupiter.\");\n            stage.setScene(resultScene);\n        });\n        again.setOnAction(event -> stage.setScene(startScene));\n\n        stage.setScene(startScene);\n        stage.setTitle(\"Space quiz\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["The result scene is built like the other two: `Label result = new Label(\"\");`, a `Button` with the text `Play again`, and `new Scene(new VBox(10, result, again))`.","An answer's handler has two statements in braces: set the label's text, then `stage.setScene(resultScene);`."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"Start\"","click Button \"Jupiter\""],"window":{"windows":[{"title":"Space quiz","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"Right: Jupiter is the largest."},{"n":3,"type":"Button","text":"Play again","onAction":true}]}}}],"problems":[]},"outline":"Window \"Space quiz\"\n  VBox spacing 10\n    Label \"Right: Jupiter is the largest.\"\n    Button \"Play again\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Space quiz","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"Space quiz"},{"n":3,"type":"Button","text":"Start","onAction":true}]}}}],"problems":[]},"outline":"Window \"Space quiz\"\n  VBox spacing 10\n    Label \"Space quiz\"\n    Button \"Start\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"Start\"","click Button \"Mars\""],"window":{"windows":[{"title":"Space quiz","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"Not quite: the largest is Jupiter."},{"n":3,"type":"Button","text":"Play again","onAction":true}]}}}],"problems":[]},"outline":"Window \"Space quiz\"\n  VBox spacing 10\n    Label \"Not quite: the largest is Jupiter.\"\n    Button \"Play again\""},{"name":"Test 4","stdin":"","expect":"","hidden":true,"events":["click Button \"Start\"","click Button \"Venus\"","click Button \"Play again\""],"window":{"windows":[{"title":"Space quiz","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"Space quiz"},{"n":3,"type":"Button","text":"Start","onAction":true}]}}}],"problems":[]},"outline":"Window \"Space quiz\"\n  VBox spacing 10\n    Label \"Space quiz\"\n    Button \"Start\""},{"name":"Test 5","stdin":"","expect":"","hidden":true,"events":["click Button \"Start\"","click Button \"Jupiter\"","click Button \"Play again\"","click Button \"Start\"","click Button \"Venus\""],"window":{"windows":[{"title":"Space quiz","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"Not quite: the largest is Jupiter."},{"n":3,"type":"Button","text":"Play again","onAction":true}]}}}],"problems":[]},"outline":"Window \"Space quiz\"\n  VBox spacing 10\n    Label \"Not quite: the largest is Jupiter.\"\n    Button \"Play again\""},{"name":"Test 6","stdin":"","expect":"","hidden":true,"events":["click Button \"Start\"","click Button \"Mars\"","click Button \"Play again\"","click Button \"Start\""],"window":{"windows":[{"title":"Space quiz","scene":{"root":{"n":1,"type":"VBox","spacing":10,"children":[{"n":2,"type":"Label","text":"Which planet is the largest?"},{"n":3,"type":"HBox","spacing":10,"children":[{"n":4,"type":"Button","text":"Mars","onAction":true},{"n":5,"type":"Button","text":"Jupiter","onAction":true},{"n":6,"type":"Button","text":"Venus","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Space quiz\"\n  VBox spacing 10\n    Label \"Which planet is the largest?\"\n    HBox spacing 10\n      Button \"Mars\"\n      Button \"Jupiter\"\n      Button \"Venus\""}],"require":[],"forbid":[]}]}