{"id":"gui-events-button-actions","slug":"button-actions","title":"Buttons that do something","text":"The windows of module 48 looked right, but their buttons did nothing when clicked. This module makes windows react: a click runs code that you write, and so can typing. As in module 48, each click or bit of typing on this page runs your program again from the start, with everything you did before it, and the code is ordinary JavaFX code.\n\n### An event handler\n\nWhen the user clicks a button, JavaFX makes an **event**: an object that describes what happened. It hands the event to the button's **event handler**, if the button has one. A handler is given to a button with `setOnAction`. A town's drawbridge gets a control panel:\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 status = new Label(\"The bridge is down.\");\n        Button raise = new Button(\"Raise the bridge\");\n        raise.setOnAction(event -> status.setText(\"The bridge is up.\"));\n\n        stage.setScene(new Scene(new VBox(status, raise)));\n        stage.setTitle(\"Drawbridge\");\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 \\\"Raise the bridge\\\"\"],\"window\":{\"windows\":[{\"title\":\"Drawbridge\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"The bridge is up.\"},{\"n\":3,\"type\":\"Button\",\"text\":\"Raise the bridge\",\"onAction\":true}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Drawbridge\\\"\\n  VBox\\n    Label \\\"The bridge is up.\\\"\\n    Button \\\"Raise the bridge\\\"\"}\n```\n\nThe window is drawn as it is after the clicks listed with it: here, one click on the button.\n\n- `setOnAction` takes an `EventHandler<ActionEvent>`. **EventHandler** is an interface of JavaFX with one method, `handle`, and a click on a button is an **ActionEvent**, so here the method is `void handle(ActionEvent event)`. An interface with one method can be implemented with a lambda (module 37), so the handler here is `event -> status.setText(\"The bridge is up.\")`: the lambda's body is what `handle` does.\n- The parameter `event` is the ActionEvent. This handler doesn't need it, but it must be there, since `handle` has that parameter. The name is yours to choose; `e` is common too.\n- Writing a lambda needs no import for EventHandler or ActionEvent: Java works out the lambda's type from `setOnAction`.\n- `start` runs once and builds the window. The line with `setOnAction` doesn't change the label: it only gives the button its handler, and the handler's code runs later, each time the button is clicked.\n- The lambda uses `status`, a variable of `start`. Step 2 looks at which variables a lambda can use.\n\n### A handler runs at every click\n\nThe front desk of a hotel gets a bell. Its handler prints, and so does `start`:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\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 bell = new Button(\"Ring the bell\");\n        bell.setOnAction(event -> System.out.println(\"Ding!\"));\n\n        stage.setScene(new Scene(new VBox(bell)));\n        stage.setTitle(\"Front desk\");\n        stage.show();\n        System.out.println(\"start is done\");\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button \\\"Ring the bell\\\"\",\"click Button \\\"Ring the bell\\\"\"],\"window\":{\"windows\":[{\"title\":\"Front desk\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"children\":[{\"n\":2,\"type\":\"Button\",\"text\":\"Ring the bell\",\"onAction\":true}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Front desk\\\"\\n  VBox\\n    Button \\\"Ring the bell\\\"\"}\n```\n\n```output\nstart is done\nDing!\nDing!\n```\n\n`start is done` comes first: `start` sets up the window and returns. Only then can the user click, and each click runs the handler once more.\n\n### Reading a text field in a handler\n\nA handler can use the other parts of the window: read a text field, set a label. A converter for a kitchen timer turns seconds into minutes:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\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        TextField seconds = new TextField();\n        seconds.setPromptText(\"Seconds\");\n        Button convert = new Button(\"Convert\");\n        Label result = new Label(\"\");\n        convert.setOnAction(event -> {\n            int total = Integer.valueOf(seconds.getText());\n            result.setText(total / 60 + \" min \" + total % 60 + \" s\");\n        });\n\n        stage.setScene(new Scene(new VBox(8, seconds, convert, result)));\n        stage.setTitle(\"Timer\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[\"type TextField 1 \\\"135\\\"\",\"click Button \\\"Convert\\\"\"],\"window\":{\"windows\":[{\"title\":\"Timer\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"spacing\":8,\"children\":[{\"n\":2,\"type\":\"TextField\",\"text\":\"135\",\"promptText\":\"Seconds\"},{\"n\":3,\"type\":\"Button\",\"text\":\"Convert\",\"onAction\":true},{\"n\":4,\"type\":\"Label\",\"text\":\"2 min 15 s\"}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Timer\\\"\\n  VBox spacing 8\\n    TextField \\\"135\\\", prompt \\\"Seconds\\\"\\n    Button \\\"Convert\\\"\\n    Label \\\"2 min 15 s\\\"\"}\n```\n\n- A handler of several statements has its body in braces, as a lambda in module 37 can.\n- `seconds.getText()` runs when the button is clicked, so it reads what the user has typed by then. Type another number and click again, and the handler reads the new one.\n- With the field empty, `Integer.valueOf(\"\")` throws a NumberFormatException. An exception in a handler doesn't end the program: JavaFX prints it (`Exception in thread \"JavaFX Application Thread\"` and the stack trace), and the window stays open for the next click. The checks in this module always type a number before they click.\n\n### Clicking from code\n\nA program can click its own buttons: `button.fire()` runs the button's handler, as a click does. Some programs in this module use it, so that you can see what their handlers do without clicking.\n","task":"A study room has a screen by its door. Fill in the three blanks, so that `Book` changes the label to `Room 4 is booked` and `Leave` changes it back to `Room 4 is free`. The task card shows the window after a click on Book.\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.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 sign = new Label(\"Room 4 is free\");\n        Button book = new Button(\"Book\");\n        Button leave = new Button(\"Leave\");\n        book.[[setOnAction]](event [[->]] sign.setText(\"Room 4 is booked\"));\n        leave.setOnAction(event -> sign.[[setText]](\"Room 4 is free\"));\n\n        stage.setScene(new Scene(new VBox(sign, new HBox(book, leave))));\n        stage.setTitle(\"Study room 4\");\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        Label sign = new Label(\"Room 4 is free\");\n        Button book = new Button(\"Book\");\n        Button leave = new Button(\"Leave\");\n        book.setOnAction(event -> sign.setText(\"Room 4 is booked\"));\n        leave.setOnAction(event -> sign.setText(\"Room 4 is free\"));\n\n        stage.setScene(new Scene(new VBox(sign, new HBox(book, leave))));\n        stage.setTitle(\"Study room 4\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["A button gets its handler with `setOnAction`, and a lambda has an arrow, `->`, between its parameter and its body.","The handler of Leave changes the label's text, as the one of Book does: `sign.setText(...)`."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"Book\""],"window":{"windows":[{"title":"Study room 4","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Room 4 is booked"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Book","onAction":true},{"n":5,"type":"Button","text":"Leave","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Study room 4\"\n  VBox\n    Label \"Room 4 is booked\"\n    HBox\n      Button \"Book\"\n      Button \"Leave\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":["click Button \"Book\"","click Button \"Leave\""],"window":{"windows":[{"title":"Study room 4","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Room 4 is free"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Book","onAction":true},{"n":5,"type":"Button","text":"Leave","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Study room 4\"\n  VBox\n    Label \"Room 4 is free\"\n    HBox\n      Button \"Book\"\n      Button \"Leave\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"Leave\"","click Button \"Book\"","click Button \"Book\""],"window":{"windows":[{"title":"Study room 4","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Room 4 is booked"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Book","onAction":true},{"n":5,"type":"Button","text":"Leave","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Study room 4\"\n  VBox\n    Label \"Room 4 is booked\"\n    HBox\n      Button \"Book\"\n      Button \"Leave\""}],"require":[],"forbid":[],"more":[{"task":"A club votes by a show of hands, and its secretary tries out a window for counting them. The program clicks its own buttons with `fire()`. 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.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 tally = new Label(\"Votes:\");\n        Button yes = new Button(\"Yes\");\n        Button no = new Button(\"No\");\n        yes.setOnAction(event -> tally.setText(tally.getText() + \" Y\"));\n        no.setOnAction(event -> tally.setText(tally.getText() + \" N\"));\n        System.out.println(tally.getText());\n        yes.fire();\n        no.fire();\n        yes.fire();\n        System.out.println(tally.getText());\n        no.setOnAction(event -> tally.setText(\"Votes: none\"));\n        no.fire();\n        yes.fire();\n        System.out.println(tally.getText());\n\n        stage.setScene(new Scene(new VBox(tally, new HBox(yes, no))));\n        stage.setTitle(\"Club vote\");\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        Label tally = new Label(\"Votes:\");\n        Button yes = new Button(\"Yes\");\n        Button no = new Button(\"No\");\n        yes.setOnAction(event -> tally.setText(tally.getText() + \" Y\"));\n        no.setOnAction(event -> tally.setText(tally.getText() + \" N\"));\n        System.out.println(tally.getText());\n        yes.fire();\n        no.fire();\n        yes.fire();\n        System.out.println(tally.getText());\n        no.setOnAction(event -> tally.setText(\"Votes: none\"));\n        no.fire();\n        yes.fire();\n        System.out.println(tally.getText());\n\n        stage.setScene(new Scene(new VBox(tally, new HBox(yes, no))));\n        stage.setTitle(\"Club vote\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["`setOnAction` only gives the button its handler: the label changes when the button fires, not before. Each handler adds to the end of the label's text.","A button has one handler for its action. The second `setOnAction` on `no` replaces the first one, so from then on `no` sets the text to `Votes: none`, and `yes` still adds ` Y` after it."],"tests":[{"name":"Output","stdin":"","expect":"Votes:\nVotes: Y N Y\nVotes: none Y","hidden":false}],"require":[],"forbid":[],"lines":["Votes:","Votes: Y N Y","Votes: none Y"]},{"task":"A children's party hands out candy. The window is built already: a text field for the number of candies (the prompt text `Candies`), one for the number of children (`Children`), the buttons `Share` and `Clear`, and an empty label. Give the buttons their handlers:\n\n- **Share** reads both numbers and sets the label to how many candies each child gets and how many are left over, like `Each child gets 7, and 3 are left over` for 45 candies and 6 children.\n- **Clear** empties both text fields and the label. `clear()` empties a text field.\n\nThe checks always type whole numbers, and at least 1 child, before they click Share. The task card shows the window after typing 45 and 6 and clicking Share.\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.control.TextField;\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        TextField candies = new TextField();\n        candies.setPromptText(\"Candies\");\n        TextField children = new TextField();\n        children.setPromptText(\"Children\");\n        Button share = new Button(\"Share\");\n        Button clear = new Button(\"Clear\");\n        Label result = new Label(\"\");\n\n        stage.setScene(new Scene(new VBox(candies, children, new HBox(share, clear), result)));\n        stage.setTitle(\"Sharing candy\");\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.control.TextField;\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        TextField candies = new TextField();\n        candies.setPromptText(\"Candies\");\n        TextField children = new TextField();\n        children.setPromptText(\"Children\");\n        Button share = new Button(\"Share\");\n        Button clear = new Button(\"Clear\");\n        Label result = new Label(\"\");\n        share.setOnAction(event -> {\n            int total = Integer.valueOf(candies.getText());\n            int kids = Integer.valueOf(children.getText());\n            result.setText(\"Each child gets \" + total / kids + \", and \" + total % kids + \" are left over\");\n        });\n        clear.setOnAction(event -> {\n            candies.clear();\n            children.clear();\n            result.setText(\"\");\n        });\n\n        stage.setScene(new Scene(new VBox(candies, children, new HBox(share, clear), result)));\n        stage.setTitle(\"Sharing candy\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["In Share's handler, read both fields with `getText()` and turn them into numbers with `Integer.valueOf(...)`. Integer division `/` gives each child's share, and `%` what's left over.","Clear's handler has three statements, so its body goes in braces: `candies.clear();`, `children.clear();` and `result.setText(\"\");`."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["type TextField 1 \"45\"","type TextField 2 \"6\"","click Button \"Share\""],"window":{"windows":[{"title":"Sharing candy","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"TextField","text":"45","promptText":"Candies"},{"n":3,"type":"TextField","text":"6","promptText":"Children"},{"n":4,"type":"HBox","children":[{"n":5,"type":"Button","text":"Share","onAction":true},{"n":6,"type":"Button","text":"Clear","onAction":true}]},{"n":7,"type":"Label","text":"Each child gets 7, and 3 are left over"}]}}}],"problems":[]},"outline":"Window \"Sharing candy\"\n  VBox\n    TextField \"45\", prompt \"Candies\"\n    TextField \"6\", prompt \"Children\"\n    HBox\n      Button \"Share\"\n      Button \"Clear\"\n    Label \"Each child gets 7, and 3 are left over\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":["type TextField 1 \"20\"","type TextField 2 \"4\"","click Button \"Share\"","set TextField 2 \"3\"","click Button \"Share\""],"window":{"windows":[{"title":"Sharing candy","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"TextField","text":"20","promptText":"Candies"},{"n":3,"type":"TextField","text":"3","promptText":"Children"},{"n":4,"type":"HBox","children":[{"n":5,"type":"Button","text":"Share","onAction":true},{"n":6,"type":"Button","text":"Clear","onAction":true}]},{"n":7,"type":"Label","text":"Each child gets 6, and 2 are left over"}]}}}],"problems":[]},"outline":"Window \"Sharing candy\"\n  VBox\n    TextField \"20\", prompt \"Candies\"\n    TextField \"3\", prompt \"Children\"\n    HBox\n      Button \"Share\"\n      Button \"Clear\"\n    Label \"Each child gets 6, and 2 are left over\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["type TextField 1 \"7\"","type TextField 2 \"9\"","click Button \"Share\"","click Button \"Clear\"","type TextField 1 \"31\"","type TextField 2 \"5\""],"window":{"windows":[{"title":"Sharing candy","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"TextField","text":"31","promptText":"Candies"},{"n":3,"type":"TextField","text":"5","promptText":"Children"},{"n":4,"type":"HBox","children":[{"n":5,"type":"Button","text":"Share","onAction":true},{"n":6,"type":"Button","text":"Clear","onAction":true}]},{"n":7,"type":"Label","text":""}]}}}],"problems":[]},"outline":"Window \"Sharing candy\"\n  VBox\n    TextField \"31\", prompt \"Candies\"\n    TextField \"5\", prompt \"Children\"\n    HBox\n      Button \"Share\"\n      Button \"Clear\"\n    Label \"\""},{"name":"Test 4","stdin":"","expect":"","hidden":true,"events":["type TextField 1 \"12\"","type TextField 2 \"5\"","click Button \"Share\"","click Button \"Clear\""],"window":{"windows":[{"title":"Sharing candy","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"TextField","text":"","promptText":"Candies"},{"n":3,"type":"TextField","text":"","promptText":"Children"},{"n":4,"type":"HBox","children":[{"n":5,"type":"Button","text":"Share","onAction":true},{"n":6,"type":"Button","text":"Clear","onAction":true}]},{"n":7,"type":"Label","text":""}]}}}],"problems":[]},"outline":"Window \"Sharing candy\"\n  VBox\n    TextField \"\", prompt \"Candies\"\n    TextField \"\", prompt \"Children\"\n    HBox\n      Button \"Share\"\n      Button \"Clear\"\n    Label \"\""}],"require":[],"forbid":[]}]}