Presentation is loading. Please wait.

Presentation is loading. Please wait.

Lecture 13.

Similar presentations


Presentation on theme: "Lecture 13."— Presentation transcript:

1 Lecture 13

2 Menus MenuItems are placed in Menus, which are grouped in MenuBars (think of the typical File/Edit/View menu bar) MenuItems trigger events that can be handled with EventHandlers or by using setters from the GUI component classes setAccelerator() provides hotkeys as an alternative to using the mouse; very useful for accessibility.

3 Menu Demo package application; import javafx.application.Application;
import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.stage.Popup; import javafx.stage.Stage; public class JavaFXMenuDemo extends Application { @Override public void start(Stage stage) { GridPane gp = new GridPane(); Scene scene = new Scene(gp, 300, 300); scene.getStylesheets().add( getClass().getResource("application.css").toExternalForm()); stage.setTitle("Menu Sample"); MenuBar menuBar = setUpMenuBar(stage); gp.getChildren().add(menuBar); stage.setScene(scene); stage.show(); } private MenuBar setUpMenuBar(Stage stage) { MenuBar menuBar = new MenuBar(); Menu fileMenu = setUpFileMenu(); Menu helpMenu = setUpHelpMenu(stage); menuBar.getMenus().add(fileMenu); menuBar.getMenus().add(helpMenu); return menuBar;

4 Menu Demo private Menu setUpFileMenu() {
Menu fileMenu = new Menu("File"); MenuItem quitItem = new MenuItem("Quit"); quitItem.setOnAction(e -> { System.exit(0); }); quitItem.setAccelerator(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN)); fileMenu.getItems().add(quitItem); return fileMenu; } private Menu setUpHelpMenu(Stage stage) { Menu helpMenu = new Menu("Help"); MenuItem aboutItem = new MenuItem("About"); aboutItem.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent arg0) { Popup popup = new Popup(); // popup.setX(100); VBox v = new VBox(); v.getStyleClass().add("about"); Text t = new Text("Copyright 2014 by John Hurley"); t.getStyleClass().add("text"); Button okButton = new Button("OK"); v.getChildren().addAll(t, okButton); okButton.setOnAction(e -> { popup.hide(); popup.getContent().addAll(v); popup.show(stage); aboutItem.setAccelerator(new KeyCodeCombination(KeyCode.A, helpMenu.getItems().add(aboutItem); return helpMenu;

5 Menu Demo .about{ -fx-background-color: #00BBBB;
-fx-alignment: center; -fx-padding: 10; -fx-border-style: solid; }

6 FileChooser FileChooser is a dialog box that lets the user choose a file to open, save as , etc. FileChooser returns a selected file or an array of selected files FileChooser.showOpenDialog() starts in the users' default directory Other starting directories can be specified using setInitialDirectory(). showOpenMultipleDialog() allows multiple file selections. This may or may not be appropriate for your task. You can limit the files shown by their file name extensions this way: fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("BMP", "*.bmp"), new FileChooser.ExtensionFilter("JPG", "*.jpg"), new FileChooser.ExtensionFilter("PNG", "*.png"));

7 FileChooser You can often show the contents of a file using a Desktop object: Desktop desktop = Desktop.getDesktop(); desktop.open(file); The user’s OS will try to choose a way to open the file (for example, using Paint or a PDF reader), according the the OS’s defaults and user settings. However, this is platform-dependent and may cause results that don’t work well with your application. It is much better to code your own ways to deal with opened files The example on the next page will work with text files only!

8 FileChooser package application; import java.io.File;
import java.io.IOException; import java.util.Scanner; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.stage.FileChooser; import javafx.stage.Popup; import javafx.stage.Stage; public class JavaFXFileChooserDemo extends Application { @Override public void start(Stage stage) { stage.setTitle("Text File Dumper"); VBox vb = new VBox(); Scene scene = new Scene(vb, 300, 300); scene.getStylesheets().add( getClass().getResource("application.css").toExternalForm()); stage.setTitle("File Chooser Sample"); Text text = new Text(); MenuBar menuBar = setUpMenuBar(stage, text); vb.getChildren().add(menuBar); vb.getChildren().add(text); stage.setScene(scene); stage.show(); } private MenuBar setUpMenuBar(Stage stage, Text text) { MenuBar menuBar = new MenuBar(); Menu fileMenu = setUpFileMenu(stage, text); Menu helpMenu = setUpHelpMenu(stage); menuBar.getMenus().add(fileMenu); menuBar.getMenus().add(helpMenu); return menuBar;

9 FileChooser private Menu setUpFileMenu(Stage stage, Text text) {
Menu fileMenu = new Menu("File"); MenuItem openItem = new MenuItem("Open"); openItem.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent arg0) { FileChooser fileChooser = new FileChooser(); File file = fileChooser.showOpenDialog(stage); if (file != null) { text.setText(getFileContents(file)); } }); openItem.setAccelerator(new KeyCodeCombination(KeyCode.O, KeyCombination.CONTROL_DOWN)); fileMenu.getItems().add(openItem); MenuItem quitItem = new MenuItem("Quit"); quitItem.setOnAction(e -> { System.exit(0); quitItem.setAccelerator(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN)); fileMenu.getItems().add(quitItem); return fileMenu; private Menu setUpHelpMenu(Stage stage) { Menu helpMenu = new Menu("Help"); MenuItem aboutItem = new MenuItem("About"); aboutItem.setOnAction(new EventHandler<ActionEvent>() { Popup popup = new Popup(); // popup.setX(100); VBox v = new VBox(); v.getStyleClass().add("about"); Text t = new Text("Copyright 2014 by John Hurley"); t.getStyleClass().add("text"); Button okButton = new Button("OK"); v.getChildren().addAll(t, okButton); okButton.setOnAction(e -> { popup.hide(); popup.getContent().addAll(v); popup.show(stage); aboutItem.setAccelerator(new KeyCodeCombination(KeyCode.A, helpMenu.getItems().add(aboutItem); return helpMenu;

10 FileChooser private String getFileContents(File file) {
StringBuilder sb = new StringBuilder(); try { Scanner reader; reader = new Scanner(file); // ... Loop as long as there are input lines. String line = null; while (reader.hasNextLine()) { if(!(sb.length()==0)) sb.append("\n"); line = reader.nextLine(); sb.append(line); } reader.close(); } catch (IOException ex) { ex.printStackTrace(); return sb.toString();

11 Image and ImageView Image class constructor uses a URI, not a file or standard file path, so construct the Image like this: String imageFileURI = file.toURI().toURL().toExternalForm(); i = new Image(imageFileURI); ImageView iv = new ImageView(i); Use with the Image class

12 package application; import java.io.File; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.geometry.Rectangle2D; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; import javafx.scene.control.ScrollPane; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.stage.FileChooser; import javafx.stage.Popup; import javafx.stage.Stage; public class JavaFXImageDemo extends Application { VBox vb; ScrollPane scrollPane; StackPane stackPane; @Override public void start(Stage stage) { vb = new VBox(); scrollPane = new ScrollPane(); stackPane = new StackPane(); scrollPane.setPrefHeight(1500); scrollPane.setContent(stackPane); StackPane.setAlignment(scrollPane, Pos.BASELINE_CENTER); //scrollPane.setFitToHeight(true); //scrollPane.setFitToWidth(true); Scene scene = new Scene(vb, 500,500); scene.getStylesheets().add( getClass().getResource("application.css").toExternalForm()); stage.setTitle("FX Image Demo");

13 MenuBar menuBar = setUpMenuBar(stage);
vb.getChildren().add(menuBar); vb.getChildren().add(scrollPane); stage.setScene(scene); stage.show(); } private MenuBar setUpMenuBar(Stage stage) { MenuBar menuBar = new MenuBar(); Menu fileMenu = setUpFileMenu(stage); Menu helpMenu = setUpHelpMenu(stage); menuBar.getMenus().add(fileMenu); menuBar.getMenus().add(helpMenu); return menuBar; private Menu setUpFileMenu(Stage stage) { Menu fileMenu = new Menu("File"); MenuItem openItem = new MenuItem("Open"); openItem.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent arg0) { FileChooser fileChooser = new FileChooser(); File file = fileChooser.showOpenDialog(stage); if (file != null) { Image i; try { String imageFileURI = file.toURI().toURL() .toExternalForm(); i = new Image(imageFileURI); ImageView iv = new ImageView(i); stackPane.getChildren().clear(); stackPane.getChildren().add(iv); stackPane.autosize(); } catch (Exception e) { e.printStackTrace(); }); openItem.setAccelerator(new KeyCodeCombination(KeyCode.O, KeyCombination.CONTROL_DOWN));

14 fileMenu.getItems().add(openItem);
MenuItem quitItem = new MenuItem("Quit"); quitItem.setOnAction(e -> { System.exit(0); }); quitItem.setAccelerator(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN)); fileMenu.getItems().add(quitItem); return fileMenu; } private Menu setUpHelpMenu(Stage stage) { Menu helpMenu = new Menu("Help"); MenuItem aboutItem = new MenuItem("About"); aboutItem.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent arg0) { Popup popup = new Popup(); // popup.setX(100); VBox v = new VBox(); v.getStyleClass().add("about"); Text t = new Text("Copyright 2014 by John Hurley"); t.getStyleClass().add("text"); Button okButton = new Button("OK"); v.getChildren().addAll(t, okButton); okButton.setOnAction(e -> { popup.hide(); popup.getContent().addAll(v); popup.show(stage); aboutItem.setAccelerator(new KeyCodeCombination(KeyCode.A, helpMenu.getItems().add(aboutItem); return helpMenu; public static void main(String[] args) { Application.launch(args);

15 Audio Audio uses streams that are related to the stream types we have already used for binary file i/o in fact, we are just doing a specialized kind of binary file i/o The next example will only work for .wav, but you can find open- source libraries that can handle mp3, etc. AudioInputStream is just what it sounds like Line: an audio feed LineListener is an event handler. The key method waitUntilDone() helps you keep the stream open until the file is finished playing Clip is an object that can load an audio file in advance rather than as the file loads for smooth performance This example uses a hard-coded file name. Put a .wav in your project and change the filename. In a real app, of course, you would have the user choose the file.

16 Audio package week10; import javax.sound.sampled.*;
import java.io.File; import java.io.IOException; import javax.sound.sampled.LineEvent.Type; public class WavPlayer { private void playClip(File clipFile) throws IOException, UnsupportedAudioFileException, LineUnavailableException, InterruptedException { AudioListener listener = new AudioListener(); AudioInputStream audioInputStream = AudioSystem .getAudioInputStream(clipFile); try { Clip clip = AudioSystem.getClip(); clip.addLineListener(listener); clip.open(audioInputStream); clip.start(); listener.waitUntilDone(); } finally { clip.close(); } audioInputStream.close();

17 Audio public static void main(String[] args) {
WavPlayer a = new WavPlayer(); try { a.playClip(new File("transporter.wav")); } catch (Exception e) { e.printStackTrace(); } private class AudioListener implements LineListener { private boolean done = false; @Override public synchronized void update(LineEvent event) { Type eventType = event.getType(); if (eventType == Type.STOP || eventType == Type.CLOSE) { done = true; public synchronized void waitUntilDone() throws InterruptedException { while (!done) { wait();

18 JavaFX Graphs JavaFX includes several classes for graphs. Here is an example of LineChart: public class LineChartSample extends Application { @Override public void start(Stage stage) { stage.setTitle("Line Chart Sample"); //defining the axes final NumberAxis xAxis = new NumberAxis(); final NumberAxis yAxis = new NumberAxis(); xAxis.setLabel("Number of Month"); //creating the chart final LineChart<Number,Number> lineChart = new LineChart<Number,Number>(xAxis,yAxis); lineChart.setTitle("Stock Monitoring, 2010"); //defining a series XYChart.Series series = new XYChart.Series(); series.setName("My portfolio"); //populating the series with data series.getData().add(new XYChart.Data<Integer, Integer>(1, 23)); series.getData().add(new XYChart.Data<Integer, Integer>(2, 14)); series.getData().add(new XYChart.Data<Integer, Integer>(3, 15)); series.getData().add(new XYChart.Data<Integer, Integer>(4, 24)); series.getData().add(new XYChart.Data<Integer, Integer>(5, 34)); series.getData().add(new XYChart.Data<Integer, Integer>(6, 36)); series.getData().add(new XYChart.Data<Integer, Integer>(7, 22)); series.getData().add(new XYChart.Data<Integer, Integer>(8, 45)); series.getData().add(new XYChart.Data<Integer, Integer>(9, 43)); series.getData().add(new XYChart.Data<Integer, Integer>(10, 17)); series.getData().add(new XYChart.Data<Integer, Integer>(11, 29)); series.getData().add(new XYChart.Data<Integer, Integer>(12, 25)); Scene scene = new Scene(lineChart,800,600); lineChart.getData().add(series); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args);

19 JavaFX Graphs Here is an example of BarChart:
public class BarChartSample extends Application { // final static String austria = "Austria"; final static String brazil = "Brazil"; final static String france = "France"; final static String italy = "Italy"; final static String usa = "USA"; @Override public void start(Stage stage) { stage.setTitle("Bar Chart Sample"); final CategoryAxis xAxis = new CategoryAxis(); final NumberAxis yAxis = new NumberAxis(); final BarChart<String,Number> bc = new BarChart<String,Number>(xAxis,yAxis); bc.setTitle("Country Summary"); xAxis.setLabel("Country"); yAxis.setLabel("Value"); XYChart.Series series1 = new XYChart.Series(); series1.setName("2003"); series1.getData().add(new XYChart.Data(austria, )); series1.getData().add(new XYChart.Data(brazil, )); series1.getData().add(new XYChart.Data(france, 10000)); series1.getData().add(new XYChart.Data(italy, )); series1.getData().add(new XYChart.Data(usa, 12000)); XYChart.Series series2 = new XYChart.Series(); series2.setName("2004"); series2.getData().add(new XYChart.Data(austria, )); series2.getData().add(new XYChart.Data(brazil, )); series2.getData().add(new XYChart.Data(france, )); series2.getData().add(new XYChart.Data(italy, )); series2.getData().add(new XYChart.Data(usa, )); XYChart.Series series3 = new XYChart.Series(); series3.setName("2005"); series3.getData().add(new XYChart.Data(austria, )); series3.getData().add(new XYChart.Data(brazil, )); series3.getData().add(new XYChart.Data(france, )); series3.getData().add(new XYChart.Data(italy, )); series3.getData().add(new XYChart.Data(usa, )); Scene scene = new Scene(bc,800,600); bc.getData().addAll(series1, series2, series3); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args);

20 Celsius Converter: Version 1
public class CelsiusConverter extends Application{ @Override public void start(Stage primaryStage) { GridPane gp = new GridPane(); Scene sc = new Scene(gp); sc.getStylesheets().add("styles/style.css"); Label lblFahr = new Label("Fahrenheit Value: "); TextField tfFahr = new TextField(); Label lblCels = new Label("Celsius Value:"); Label lblCelsAns = new Label(); Button btnCalc = new Button("Convert"); btnCalc.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<Event>(){ public void handle(Event event) { double cels = (Double.parseDouble(tfFahr.getText())-32)*5/9; lblCelsAns.setText(String.valueOf(cels)); } }); gp.getStyleClass().add("pane"); gp.add(lblFahr, 0, 0); gp.add(tfFahr, 2, 0); gp.add(lblCels, 0, 1); gp.add(lblCelsAns, 2, 1); gp.add(btnCalc, 1, 2); primaryStage.setScene(sc); primaryStage.show();

21 CelsiusConverter .pane{ -fx-font-size: 250%; -fx-padding: 20px; }

22 Separate Application Logic From The GUI
The initial EventHandler examples showed simple responses coded in the handlers. It is better to separate out any substantial processing into separate methods, in other classes, following the general principle of separation of concerns. We may want to reuse code to handle various different events. We may want to use the same functionality with a different kind of GUI When we need to change the processing code, it is easier to work with if it is not mixed with GUI code. It's hard to unit test GUIs, so we should get the code that might have subtle bugs away from the GUI.

23 Separate Application Logic From The GUI
Don’t put any substantial processing in the Event Handlers and don’t access the GUI from the processing code. Either use separately-defined classes for the GUI app and the processing; or In simple cases, use an inner class with processing methods.

24 Celsius Converter 2.0 Let's move the conversion calculation to this class: package application; public class CelsiusCalculator { private final double CONVFACTOR = 5.0/9.0; private final int FSTART = 32; public double fToC(double f){ return (f-FSTART) * CONVFACTOR; }

25 Unit Tests package test; import static org.junit.Assert.assertEquals;
import org.junit.Test; import application.CelsiusCalculator; public class CelsiusCalculatorTester { @Test public void testFreezing() { CelsiusCalculator calc = new CelsiusCalculator(); double freezingF = 32; double freezingC = 0; double freezingResult = calc.fToC(freezingF); // assertEquals for two doubles takes a delta as the third argument! assertEquals(freezingResult, freezingC, 0.1); } public void testBoiling() { double boilingF = 212; double boilingC = 100; double boilingResult = calc.fToC(boilingF); assertEquals(boilingResult, boilingC, 0.1); public void testNegForty() { double negForty = -40; double negFortyResult = calc.fToC(negForty); assertEquals(negFortyResult, negForty, 0.1); Unit Tests

26 CelsiusConverterGUI package application;
import javafx.application.Application; import javafx.event.Event; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.input.MouseEvent; import javafx.scene.layout.GridPane; import javafx.stage.Stage; public class CelsiusConverterGUI extends Application{ @Override public void start(Stage primaryStage) { CelsiusCalculator calc = new CelsiusCalculator(); GridPane gp = new GridPane(); Scene sc = new Scene(gp); sc.getStylesheets().add("styles/style.css"); Label lblFahr = new Label("Fahrenheit Value: "); TextField tfFahr = new TextField(); Label lblCels = new Label("Celsius Value:"); Label lblCelsAns = new Label(); Button btnCalc = new Button("Convert"); btnCalc.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<Event>(){ public void handle(Event event) { double cels = calc.fToC(Double.parseDouble(tfFahr.getText())); lblCelsAns.setText(String.valueOf(cels)); } }); gp.getStyleClass().add("pane"); gp.add(lblFahr, 0, 0); gp.add(tfFahr, 2, 0); gp.add(lblCels, 0, 1); gp.add(lblCelsAns, 2, 1); gp.add(btnCalc, 1, 2); primaryStage.setScene(sc); primaryStage.show();

27 Separate Application Logic From The GUI
As your applications get larger, you will need to think about how to organize them into packages. Eclipse creates a Main.java and application.css for JavaFX in the application package. There are other reasonable ways to structure a GUI app, but here is one good way. See the course web page for code

28 FXML JavaFX also offers a way to code UI controls in XML, while writing the event handlers and other application logic in Java. The form of XML used is called FXML. Even if you have never seen XML before, you will understand it easily if you have taken CS120, since XML is very similar to HTML. This type of Java UI coding uses the Model-View-Controller design pattern The Model consists of backend logic (there is none in the simple example that follows, but imagine code operating on data structures, eg lists of Students, and interacting with a database.) The view is the UI coded in XML The Controllers are classes that handle UI events This way of coding UI is similar to many web and mobile GUI coding frameworks, including the Java web programming frameworks Spring and Seam, Microsoft’s ASP.NET (C# or Visual Basic), and many others. It is also very much like the Android app framework.

29

30 package application; // import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("../ui/fxmldemo.fxml")); Scene scene = new Scene(root, 600, 275); scene.getStylesheets().add("application/application.css"); stage.setTitle("Monster Attack Monitor"); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args);

31 GridPane{-fx-background-color: #FFDD66;}

32 package controllers; public class FXMLDemoController { @FXML private Text results; @FXML private TextField nameField; @FXML private TextField locationField; @FXML private TextField damagesField; private List<MonsterAttack> attacks = new ArrayList<MonsterAttack>(); @FXML protected void handleSubmitButtonAction(ActionEvent event) { String name = nameField.getText(); String location = locationField.getText(); double damages = Double.parseDouble(damagesField.getText()); MonsterAttack m = new MonsterAttack(name, location, damages); attacks.add(m); String s = generateListOutput(); results.setText(s); } private String generateListOutput(){ StringBuilder sb = new StringBuilder(); for (MonsterAttack m: attacks) sb.append(m.toString() + '\n'); return sb.toString();

33 package model; public class MonsterAttack { private String monsterName; private String attackLocation; private double damages; public MonsterAttack(String monsterName, String attackLocation, double damages) { super(); this.monsterName = monsterName; this.attackLocation = attackLocation; this.damages = damages; } public String toString(){ return "Attack by " + monsterName + " on " + attackLocation + " caused damages of $" + damages + "million.";

34 <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?> <?import java.net.*?> <?import javafx.geometry.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.text.*?> <GridPane fx:controller="controllers.FXMLDemoController" xmlns:fx=" alignment="center" hgap="10" vgap="10"> <padding> <Insets top="25" right="25" bottom="10" left="25" /> </padding> <Text text="Monster Attack Monitor" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2" /> <Label text="Monster Name:" GridPane.columnIndex="0" GridPane.rowIndex="1" /> <TextField fx:id="nameField" GridPane.columnIndex="1" GridPane.rowIndex="1" /> <Label text="Location:" GridPane.columnIndex="0" GridPane.rowIndex="2" /> <TextField fx:id="locationField" GridPane.columnIndex="1" GridPane.rowIndex="2" /> <Label text="Estimated Damages:" GridPane.columnIndex="0" GridPane.rowIndex="3" /> <TextField fx:id="damagesField" GridPane.columnIndex="1" GridPane.rowIndex="3" /> <HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4"> <Button text="Sign In" onAction="#handleSubmitButtonAction" /> </HBox> <Text fx:id="results" GridPane.columnIndex="0" GridPane.rowIndex="5" GridPane.columnSpan="2" /> </GridPane>


Download ppt "Lecture 13."

Similar presentations


Ads by Google