{"id":"gui-views-views-as-classes","slug":"views-as-classes","title":"Views as classes","text":"With a few views, `start` grows long, and the nodes and handlers of every view get mixed together. The cure is the one you used for long programs before: classes. Each view gets a class of its own, with a method that returns the view's root, ready to be put in a window. These lessons call that method `getView()`. The Application creates the view objects and switches between them.\n\n### The castle guide, in classes\n\nThe guide gets a page of history and a page for booking a tour. Each view is a class in its own file, and `Main` only builds the frame:\n\n```java\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        HistoryView history = new HistoryView();\n        ToursView tours = new ToursView(12);\n\n        Button toHistory = new Button(\"History\");\n        Button toTours = new Button(\"Tours\");\n        HBox menu = new HBox(10, toHistory, toTours);\n        menu.setPadding(new Insets(10));\n\n        BorderPane frame = new BorderPane();\n        frame.setTop(menu);\n        frame.setCenter(history.getView());\n        toHistory.setOnAction(event -> frame.setCenter(history.getView()));\n        toTours.setOnAction(event -> frame.setCenter(tours.getView()));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Castle guide\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n// ==== HistoryView.java ====\nimport javafx.geometry.Insets;\nimport javafx.scene.Parent;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\npublic class HistoryView {\n\n    public Parent getView() {\n        VBox view = new VBox(6);\n        view.setPadding(new Insets(10));\n        view.getChildren().add(new Label(\"1350: the first stone walls\"));\n        view.getChildren().add(new Label(\"1622: a fire, and a new north tower\"));\n        view.getChildren().add(new Label(\"1954: the castle opens to visitors\"));\n        return view;\n    }\n}\n// ==== ToursView.java ====\nimport javafx.geometry.Insets;\nimport javafx.scene.Parent;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.VBox;\n\npublic class ToursView {\n    private int placesLeft;\n    private VBox view;\n\n    public ToursView(int places) {\n        this.placesLeft = places;\n        Label left = new Label(\"Places left on the afternoon tour: \" + places);\n        TextField name = new TextField();\n        name.setPromptText(\"Your name\");\n        Button book = new Button(\"Book a place\");\n        Label message = new Label(\"\");\n        book.setOnAction(event -> {\n            if (this.placesLeft == 0) {\n                message.setText(\"Sorry, the tour is full.\");\n            } else {\n                this.placesLeft--;\n                left.setText(\"Places left on the afternoon tour: \" + this.placesLeft);\n                message.setText(\"Booked a place for \" + name.getText() + \".\");\n            }\n        });\n        this.view = new VBox(8, left, name, book, message);\n        this.view.setPadding(new Insets(10));\n    }\n\n    public Parent getView() {\n        return this.view;\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button \\\"Tours\\\"\",\"type TextField 1 \\\"Ada\\\"\",\"click Button \\\"History\\\"\",\"click Button \\\"Tours\\\"\",\"click Button \\\"Book a place\\\"\"],\"window\":{\"windows\":[{\"title\":\"Castle guide\",\"scene\":{\"root\":{\"n\":1,\"type\":\"BorderPane\",\"top\":{\"n\":2,\"type\":\"HBox\",\"padding\":[10,10,10,10],\"spacing\":10,\"children\":[{\"n\":3,\"type\":\"Button\",\"text\":\"History\",\"onAction\":true},{\"n\":4,\"type\":\"Button\",\"text\":\"Tours\",\"onAction\":true}]},\"center\":{\"n\":5,\"type\":\"VBox\",\"padding\":[10,10,10,10],\"spacing\":8,\"children\":[{\"n\":6,\"type\":\"Label\",\"text\":\"Places left on the afternoon tour: 11\"},{\"n\":7,\"type\":\"TextField\",\"text\":\"Ada\",\"promptText\":\"Your name\"},{\"n\":8,\"type\":\"Button\",\"text\":\"Book a place\",\"onAction\":true},{\"n\":9,\"type\":\"Label\",\"text\":\"Booked a place for Ada.\"}]}}}}],\"problems\":[]},\"outline\":\"Window \\\"Castle guide\\\"\\n  BorderPane\\n    top: HBox spacing 10, padding 10\\n      Button \\\"History\\\"\\n      Button \\\"Tours\\\"\\n    center: VBox spacing 8, padding 10\\n      Label \\\"Places left on the afternoon tour: 11\\\"\\n      TextField \\\"Ada\\\", prompt \\\"Your name\\\"\\n      Button \\\"Book a place\\\"\\n      Label \\\"Booked a place for Ada.\\\"\"}\n```\n\n- The view classes are ordinary classes: they don't extend Application, and each file has its own imports.\n- `getView()` returns a **Parent**. Parent is the class of every node that can hold others, every layout among them (module 48), so the method can return a VBox, a GridPane or a BorderPane, and the code that calls it doesn't need to know which. `setCenter` takes any node, and a Parent is one. (`new Scene(...)` wants a Parent too.)\n- `Main` creates each view object once, at the start, and its handlers only choose which view goes in the center.\n\n### Two ways to build a view\n\nLook at where each class makes its nodes.\n\n- **HistoryView** makes new nodes at every call of `getView()`. Each visit to the history page gets a fresh VBox with fresh labels. That's fine, since the page has nothing to remember.\n- **ToursView** makes its nodes once, in its constructor, and keeps the layout in an instance variable. `getView()` returns the same VBox every time. So the view keeps what happens in it: in the example, `Ada` was typed, the user went to the history page and back, and the name was still in the field when Book was clicked.\n\nToursView's handler is a lambda in the constructor. It changes `this.placesLeft`, an instance variable of the view object, which the effectively final rule allows (module 49), and it uses the local variables `left`, `name` and `message`, which never change.\n\nA view that builds new nodes in `getView()` can still show data that lasts, if it reads that data from an object that keeps it. Step 5 does that.\n\n### Views with setScene\n\nView classes work with `setScene` too. Make each scene once, at the start, such as `Scene toursScene = new Scene(tours.getView());`, and switch with `stage.setScene(toursScene)`. A handler that calls `new Scene(tours.getView())` at every click fails at the second click, when getView returns the same layout: a root can't be in two scenes (step 1).\n","task":"An art studio's app has the pages `Opening hours` and `My sketches`. `Main` and `HoursView` are ready. Write the class `SketchView` in `SketchView.java`:\n\n- a constructor without parameters\n- `getView()`, which returns a Parent: a VBox with the spacing 8 and the padding 10 on every side, holding the label `Sketches today: 0` and the button `Finished a sketch`\n- each click on the button adds one to the number in the label, like `Sketches today: 3`\n- the count must stay when the user goes to the opening hours and back, so `getView()` returns the same VBox at every call: make it in the constructor\n\nThe task card shows the window after a click on My sketches and three clicks on Finished a sketch.\n","kind":"code","seed":"// ==== SketchView.java ====\npublic class SketchView {\n\n}\n\n// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        HoursView hours = new HoursView();\n        SketchView sketches = new SketchView();\n\n        Button toHours = new Button(\"Opening hours\");\n        Button toSketches = new Button(\"My sketches\");\n        HBox menu = new HBox(10, toHours, toSketches);\n        menu.setPadding(new Insets(10));\n\n        BorderPane frame = new BorderPane();\n        frame.setTop(menu);\n        frame.setCenter(hours.getView());\n        toHours.setOnAction(event -> frame.setCenter(hours.getView()));\n        toSketches.setOnAction(event -> frame.setCenter(sketches.getView()));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Art studio\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== HoursView.java ====\nimport javafx.geometry.Insets;\nimport javafx.scene.Parent;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\npublic class HoursView {\n\n    public Parent getView() {\n        VBox view = new VBox(6, new Label(\"Weekdays 10 to 20\"), new Label(\"Weekends 12 to 18\"));\n        view.setPadding(new Insets(10));\n        return view;\n    }\n}\n","solution":"// ==== SketchView.java ====\nimport javafx.geometry.Insets;\nimport javafx.scene.Parent;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\npublic class SketchView {\n    private int sketches;\n    private VBox view;\n\n    public SketchView() {\n        Label count = new Label(\"Sketches today: 0\");\n        Button done = new Button(\"Finished a sketch\");\n        done.setOnAction(event -> {\n            this.sketches++;\n            count.setText(\"Sketches today: \" + this.sketches);\n        });\n        this.view = new VBox(8, count, done);\n        this.view.setPadding(new Insets(10));\n    }\n\n    public Parent getView() {\n        return this.view;\n    }\n}\n\n// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        HoursView hours = new HoursView();\n        SketchView sketches = new SketchView();\n\n        Button toHours = new Button(\"Opening hours\");\n        Button toSketches = new Button(\"My sketches\");\n        HBox menu = new HBox(10, toHours, toSketches);\n        menu.setPadding(new Insets(10));\n\n        BorderPane frame = new BorderPane();\n        frame.setTop(menu);\n        frame.setCenter(hours.getView());\n        toHours.setOnAction(event -> frame.setCenter(hours.getView()));\n        toSketches.setOnAction(event -> frame.setCenter(sketches.getView()));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Art studio\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== HoursView.java ====\nimport javafx.geometry.Insets;\nimport javafx.scene.Parent;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\npublic class HoursView {\n\n    public Parent getView() {\n        VBox view = new VBox(6, new Label(\"Weekdays 10 to 20\"), new Label(\"Weekends 12 to 18\"));\n        view.setPadding(new Insets(10));\n        return view;\n    }\n}\n","hints":["Give the class two instance variables, `private int sketches;` and `private VBox view;`. The constructor makes the label and the button, gives the button its handler, and stores `new VBox(8, count, done)` in `this.view`, then sets its padding.","The handler adds one to `this.sketches` and sets the label's text. `getView()` only returns `this.view`. The file needs imports for Insets, Parent, Button, Label and VBox."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"My sketches\"","click Button \"Finished a sketch\"","click Button \"Finished a sketch\"","click Button \"Finished a sketch\""],"window":{"windows":[{"title":"Art studio","scene":{"root":{"n":1,"type":"BorderPane","top":{"n":2,"type":"HBox","padding":[10,10,10,10],"spacing":10,"children":[{"n":3,"type":"Button","text":"Opening hours","onAction":true},{"n":4,"type":"Button","text":"My sketches","onAction":true}]},"center":{"n":5,"type":"VBox","padding":[10,10,10,10],"spacing":8,"children":[{"n":6,"type":"Label","text":"Sketches today: 3"},{"n":7,"type":"Button","text":"Finished a sketch","onAction":true}]}}}}],"problems":[]},"outline":"Window \"Art studio\"\n  BorderPane\n    top: HBox spacing 10, padding 10\n      Button \"Opening hours\"\n      Button \"My sketches\"\n    center: VBox spacing 8, padding 10\n      Label \"Sketches today: 3\"\n      Button \"Finished a sketch\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Art studio","scene":{"root":{"n":1,"type":"BorderPane","top":{"n":2,"type":"HBox","padding":[10,10,10,10],"spacing":10,"children":[{"n":3,"type":"Button","text":"Opening hours","onAction":true},{"n":4,"type":"Button","text":"My sketches","onAction":true}]},"center":{"n":5,"type":"VBox","padding":[10,10,10,10],"spacing":6,"children":[{"n":6,"type":"Label","text":"Weekdays 10 to 20"},{"n":7,"type":"Label","text":"Weekends 12 to 18"}]}}}}],"problems":[]},"outline":"Window \"Art studio\"\n  BorderPane\n    top: HBox spacing 10, padding 10\n      Button \"Opening hours\"\n      Button \"My sketches\"\n    center: VBox spacing 6, padding 10\n      Label \"Weekdays 10 to 20\"\n      Label \"Weekends 12 to 18\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"My sketches\""],"window":{"windows":[{"title":"Art studio","scene":{"root":{"n":1,"type":"BorderPane","top":{"n":2,"type":"HBox","padding":[10,10,10,10],"spacing":10,"children":[{"n":3,"type":"Button","text":"Opening hours","onAction":true},{"n":4,"type":"Button","text":"My sketches","onAction":true}]},"center":{"n":5,"type":"VBox","padding":[10,10,10,10],"spacing":8,"children":[{"n":6,"type":"Label","text":"Sketches today: 0"},{"n":7,"type":"Button","text":"Finished a sketch","onAction":true}]}}}}],"problems":[]},"outline":"Window \"Art studio\"\n  BorderPane\n    top: HBox spacing 10, padding 10\n      Button \"Opening hours\"\n      Button \"My sketches\"\n    center: VBox spacing 8, padding 10\n      Label \"Sketches today: 0\"\n      Button \"Finished a sketch\""},{"name":"Test 4","stdin":"","expect":"","hidden":true,"events":["click Button \"My sketches\"","click Button \"Finished a sketch\"","click Button \"Finished a sketch\"","click Button \"Opening hours\"","click Button \"My sketches\"","click Button \"Finished a sketch\""],"window":{"windows":[{"title":"Art studio","scene":{"root":{"n":1,"type":"BorderPane","top":{"n":2,"type":"HBox","padding":[10,10,10,10],"spacing":10,"children":[{"n":3,"type":"Button","text":"Opening hours","onAction":true},{"n":4,"type":"Button","text":"My sketches","onAction":true}]},"center":{"n":5,"type":"VBox","padding":[10,10,10,10],"spacing":8,"children":[{"n":6,"type":"Label","text":"Sketches today: 3"},{"n":7,"type":"Button","text":"Finished a sketch","onAction":true}]}}}}],"problems":[]},"outline":"Window \"Art studio\"\n  BorderPane\n    top: HBox spacing 10, padding 10\n      Button \"Opening hours\"\n      Button \"My sketches\"\n    center: VBox spacing 8, padding 10\n      Label \"Sketches today: 3\"\n      Button \"Finished a sketch\""}],"require":[],"forbid":[],"more":[{"task":"A seed shop's app has two views as classes, each a VBox that's made once, in the constructor. Each view holds the button that leads to the other one: `Main` makes the buttons and gives them to the views. The app starts on the seeds, and the first click on Order works, but a click on Seeds after it prints an exception that starts with `Exception in thread \"JavaFX Application Thread\" java.lang.IllegalArgumentException`, and the window stays on the order view.\n\nFix `Main`, so that the two buttons switch between the views as often as the user likes. The views, and what they show, stay as they are: make a scene for each view once, and switch between those two scenes. The task card shows the window after the clicks Order and Seeds.\n","kind":"code","seed":"// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Button toOrder = new Button(\"Order\");\n        Button toSeeds = new Button(\"Seeds\");\n        SeedView seeds = new SeedView(toOrder);\n        OrderView order = new OrderView(toSeeds);\n\n        toOrder.setOnAction(event -> stage.setScene(new Scene(order.getView())));\n        toSeeds.setOnAction(event -> stage.setScene(new Scene(seeds.getView())));\n\n        stage.setScene(new Scene(seeds.getView()));\n        stage.setTitle(\"Seed shop\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== SeedView.java ====\nimport javafx.scene.Parent;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\npublic class SeedView {\n    private VBox view;\n\n    public SeedView(Button toOrder) {\n        this.view = new VBox(8, new Label(\"Tomato seeds: 3 euros a bag\"), new Label(\"Pea seeds: 2 euros a bag\"), toOrder);\n    }\n\n    public Parent getView() {\n        return this.view;\n    }\n}\n\n// ==== OrderView.java ====\nimport javafx.scene.Parent;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.VBox;\n\npublic class OrderView {\n    private VBox view;\n\n    public OrderView(Button toSeeds) {\n        TextField what = new TextField();\n        what.setPromptText(\"What would you like?\");\n        Button send = new Button(\"Send order\");\n        Label message = new Label(\"\");\n        send.setOnAction(event -> message.setText(\"Ordered: \" + what.getText()));\n        this.view = new VBox(8, what, send, message, toSeeds);\n    }\n\n    public Parent getView() {\n        return this.view;\n    }\n}\n","solution":"// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Button toOrder = new Button(\"Order\");\n        Button toSeeds = new Button(\"Seeds\");\n        SeedView seeds = new SeedView(toOrder);\n        OrderView order = new OrderView(toSeeds);\n        Scene seedScene = new Scene(seeds.getView());\n        Scene orderScene = new Scene(order.getView());\n\n        toOrder.setOnAction(event -> stage.setScene(orderScene));\n        toSeeds.setOnAction(event -> stage.setScene(seedScene));\n\n        stage.setScene(seedScene);\n        stage.setTitle(\"Seed shop\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== SeedView.java ====\nimport javafx.scene.Parent;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\npublic class SeedView {\n    private VBox view;\n\n    public SeedView(Button toOrder) {\n        this.view = new VBox(8, new Label(\"Tomato seeds: 3 euros a bag\"), new Label(\"Pea seeds: 2 euros a bag\"), toOrder);\n    }\n\n    public Parent getView() {\n        return this.view;\n    }\n}\n\n// ==== OrderView.java ====\nimport javafx.scene.Parent;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.VBox;\n\npublic class OrderView {\n    private VBox view;\n\n    public OrderView(Button toSeeds) {\n        TextField what = new TextField();\n        what.setPromptText(\"What would you like?\");\n        Button send = new Button(\"Send order\");\n        Label message = new Label(\"\");\n        send.setOnAction(event -> message.setText(\"Ordered: \" + what.getText()));\n        this.view = new VBox(8, what, send, message, toSeeds);\n    }\n\n    public Parent getView() {\n        return this.view;\n    }\n}\n","hints":["`getView()` returns the same VBox every time, and the first `new Scene(seeds.getView())` already made it the root of a scene. A second `new Scene` with the same root is what throws.","Make the two scenes once, before the handlers: `Scene seedScene = new Scene(seeds.getView());` and `Scene orderScene = new Scene(order.getView());`. The handlers and the start of the window then use these variables."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"Order\"","click Button \"Seeds\""],"window":{"windows":[{"title":"Seed shop","scene":{"root":{"n":1,"type":"VBox","spacing":8,"children":[{"n":2,"type":"Label","text":"Tomato seeds: 3 euros a bag"},{"n":3,"type":"Label","text":"Pea seeds: 2 euros a bag"},{"n":4,"type":"Button","text":"Order","onAction":true}]}}}],"problems":[]},"outline":"Window \"Seed shop\"\n  VBox spacing 8\n    Label \"Tomato seeds: 3 euros a bag\"\n    Label \"Pea seeds: 2 euros a bag\"\n    Button \"Order\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":["click Button \"Order\"","type TextField 1 \"Two bags of peas\"","click Button \"Send order\"","click Button \"Seeds\"","click Button \"Order\""],"window":{"windows":[{"title":"Seed shop","scene":{"root":{"n":1,"type":"VBox","spacing":8,"children":[{"n":2,"type":"TextField","text":"Two bags of peas","promptText":"What would you like?"},{"n":3,"type":"Button","text":"Send order","onAction":true},{"n":4,"type":"Label","text":"Ordered: Two bags of peas"},{"n":5,"type":"Button","text":"Seeds","onAction":true}]}}}],"problems":[]},"outline":"Window \"Seed shop\"\n  VBox spacing 8\n    TextField \"Two bags of peas\", prompt \"What would you like?\"\n    Button \"Send order\"\n    Label \"Ordered: Two bags of peas\"\n    Button \"Seeds\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"Order\"","click Button \"Seeds\"","click Button \"Order\"","click Button \"Seeds\""],"window":{"windows":[{"title":"Seed shop","scene":{"root":{"n":1,"type":"VBox","spacing":8,"children":[{"n":2,"type":"Label","text":"Tomato seeds: 3 euros a bag"},{"n":3,"type":"Label","text":"Pea seeds: 2 euros a bag"},{"n":4,"type":"Button","text":"Order","onAction":true}]}}}],"problems":[]},"outline":"Window \"Seed shop\"\n  VBox spacing 8\n    Label \"Tomato seeds: 3 euros a bag\"\n    Label \"Pea seeds: 2 euros a bag\"\n    Button \"Order\""}],"require":[],"forbid":[]},{"task":"On a cleanup day, two teams collect bags of litter. `Main` is ready: it makes two views of the same class, `TeamView`, one for each team, and a menu with the buttons `Herons` and `Beavers` that shows a team's view in the center. Write the class `TeamView` in `TeamView.java`:\n\n- a constructor `TeamView(String team)`\n- `getView()`, which returns a Parent: a VBox with the spacing 8 and no padding, holding the label `Bags for Herons: 0` (with the team's name) and the button `Add a bag`\n- each click on the button adds a bag to that team only, like `Bags for Herons: 2`\n- each team keeps its count when the user switches to the other team and back\n\nThe task card shows the window after two clicks on Add a bag for the Herons, a click on Beavers and one more on Add a bag.\n","kind":"code","seed":"// ==== TeamView.java ====\npublic class TeamView {\n\n}\n\n// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        TeamView herons = new TeamView(\"Herons\");\n        TeamView beavers = new TeamView(\"Beavers\");\n\n        Button toHerons = new Button(\"Herons\");\n        Button toBeavers = new Button(\"Beavers\");\n        BorderPane frame = new BorderPane();\n        frame.setTop(new HBox(10, toHerons, toBeavers));\n        frame.setCenter(herons.getView());\n        toHerons.setOnAction(event -> frame.setCenter(herons.getView()));\n        toBeavers.setOnAction(event -> frame.setCenter(beavers.getView()));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Cleanup day\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"// ==== TeamView.java ====\nimport javafx.scene.Parent;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\n\npublic class TeamView {\n    private int bags;\n    private VBox view;\n\n    public TeamView(String team) {\n        Label score = new Label(\"Bags for \" + team + \": 0\");\n        Button add = new Button(\"Add a bag\");\n        add.setOnAction(event -> {\n            this.bags++;\n            score.setText(\"Bags for \" + team + \": \" + this.bags);\n        });\n        this.view = new VBox(8, score, add);\n    }\n\n    public Parent getView() {\n        return this.view;\n    }\n}\n\n// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.HBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        TeamView herons = new TeamView(\"Herons\");\n        TeamView beavers = new TeamView(\"Beavers\");\n\n        Button toHerons = new Button(\"Herons\");\n        Button toBeavers = new Button(\"Beavers\");\n        BorderPane frame = new BorderPane();\n        frame.setTop(new HBox(10, toHerons, toBeavers));\n        frame.setCenter(herons.getView());\n        toHerons.setOnAction(event -> frame.setCenter(herons.getView()));\n        toBeavers.setOnAction(event -> frame.setCenter(beavers.getView()));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Cleanup day\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Each TeamView object has its own instance variables, so `private int bags;` gives each team its own count. Make the label, the button and the VBox in the constructor, and keep the VBox in `private VBox view;`.","The handler can use the constructor's parameter `team`, which never changes: `score.setText(\"Bags for \" + team + \": \" + this.bags);`."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"Add a bag\"","click Button \"Add a bag\"","click Button \"Beavers\"","click Button \"Add a bag\""],"window":{"windows":[{"title":"Cleanup day","scene":{"root":{"n":1,"type":"BorderPane","top":{"n":2,"type":"HBox","spacing":10,"children":[{"n":3,"type":"Button","text":"Herons","onAction":true},{"n":4,"type":"Button","text":"Beavers","onAction":true}]},"center":{"n":5,"type":"VBox","spacing":8,"children":[{"n":6,"type":"Label","text":"Bags for Beavers: 1"},{"n":7,"type":"Button","text":"Add a bag","onAction":true}]}}}}],"problems":[]},"outline":"Window \"Cleanup day\"\n  BorderPane\n    top: HBox spacing 10\n      Button \"Herons\"\n      Button \"Beavers\"\n    center: VBox spacing 8\n      Label \"Bags for Beavers: 1\"\n      Button \"Add a bag\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Cleanup day","scene":{"root":{"n":1,"type":"BorderPane","top":{"n":2,"type":"HBox","spacing":10,"children":[{"n":3,"type":"Button","text":"Herons","onAction":true},{"n":4,"type":"Button","text":"Beavers","onAction":true}]},"center":{"n":5,"type":"VBox","spacing":8,"children":[{"n":6,"type":"Label","text":"Bags for Herons: 0"},{"n":7,"type":"Button","text":"Add a bag","onAction":true}]}}}}],"problems":[]},"outline":"Window \"Cleanup day\"\n  BorderPane\n    top: HBox spacing 10\n      Button \"Herons\"\n      Button \"Beavers\"\n    center: VBox spacing 8\n      Label \"Bags for Herons: 0\"\n      Button \"Add a bag\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"Beavers\"","click Button \"Add a bag\"","click Button \"Add a bag\"","click Button \"Add a bag\"","click Button \"Herons\""],"window":{"windows":[{"title":"Cleanup day","scene":{"root":{"n":1,"type":"BorderPane","top":{"n":2,"type":"HBox","spacing":10,"children":[{"n":3,"type":"Button","text":"Herons","onAction":true},{"n":4,"type":"Button","text":"Beavers","onAction":true}]},"center":{"n":5,"type":"VBox","spacing":8,"children":[{"n":6,"type":"Label","text":"Bags for Herons: 0"},{"n":7,"type":"Button","text":"Add a bag","onAction":true}]}}}}],"problems":[]},"outline":"Window \"Cleanup day\"\n  BorderPane\n    top: HBox spacing 10\n      Button \"Herons\"\n      Button \"Beavers\"\n    center: VBox spacing 8\n      Label \"Bags for Herons: 0\"\n      Button \"Add a bag\""},{"name":"Test 4","stdin":"","expect":"","hidden":true,"events":["click Button \"Add a bag\"","click Button \"Beavers\"","click Button \"Herons\"","click Button \"Add a bag\""],"window":{"windows":[{"title":"Cleanup day","scene":{"root":{"n":1,"type":"BorderPane","top":{"n":2,"type":"HBox","spacing":10,"children":[{"n":3,"type":"Button","text":"Herons","onAction":true},{"n":4,"type":"Button","text":"Beavers","onAction":true}]},"center":{"n":5,"type":"VBox","spacing":8,"children":[{"n":6,"type":"Label","text":"Bags for Herons: 2"},{"n":7,"type":"Button","text":"Add a bag","onAction":true}]}}}}],"problems":[]},"outline":"Window \"Cleanup day\"\n  BorderPane\n    top: HBox spacing 10\n      Button \"Herons\"\n      Button \"Beavers\"\n    center: VBox spacing 8\n      Label \"Bags for Herons: 2\"\n      Button \"Add a bag\""}],"require":[],"forbid":[]}]}