diff --git a/README.md b/README.md index 63cdba084..a81cf5242 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,38 @@ -# Duke +# Setting up -Duke is a Personal Assistant Chatbot that helps a person to keep track of various things. +**Prerequisites** -# Getting started +* JDK 11 +* Recommended: IntelliJ IDE +* Fork this repo to your GitHub account and clone the fork to your computer -1. Clone this repository with: +**Importing the project into IntelliJ** - > `git clone https://github.com/se-edu/duke.git` - -1. Extend Duke's functionality in `seedu.duke.Duke`. +* Open IntelliJ (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project dialog first). +* Set up the correct JDK version. + * Click `Configure` > `Structure for new Projects` (in older versions of Intellij:`Configure` > `Project Defaults` > `Project Structure`). + * If JDK 11 is listed in the drop down, select it. If it is not, click `New...` and select the directory where you installed JDK 11. + * Click `OK`. +* Click `Import Project`. +* Locate the project directory and click `OK`. +* Select `Create project from existing sources` and click `Next`. +* Rename the project if you want. Click `Next`. +* Ensure that your src folder is checked. Keep clicking `Next`. +* Click `Finish`. # Tutorials -Increments | Tutorial Name ----|--- -A-Gradle | [Gradle Tutorial 1 : Introduction to Gradle](tutorials/gradle-tutorial-1-introduction-to-gradle.md) -Level-10 | [JavaFX Tutorial 1: Introduction to JavaFX](tutorials/ui-tutorial-1-introduction-to-javafx.md) -Level-10 | [JavaFX Tutorial 2: Creating a GUI for Duke](tutorials/ui-tutorial-2-creating-a-gui-for-duke.md) -Level-10 | [JavaFX Tutorial 3: Interacting with the user](tutorials/ui-tutorial-3-interacting-with-the-user.md) -Level-10 | [JavaFX Tutorial 4: Introduction to FXML](tutorials/ui-tutorial-4-introduction-to-fxml.md) +Duke Increment | Tutorial +---------------|--------------- +`A-Gradle` | [Gradle Tutorial](tutorials/gradleTutorial.md) +`Level-10` | JavaFX tutorials:
→ [Part 1: Introduction to JavaFX][fx1]
→ [Part 2: Creating a GUI for Duke][fx2]
→ [Part 3: Interacting with the user][fx3]
→ [Part 4: Introduction to FXML][fx4] + +[fx1]: +[fx2]: +[fx3]: +[fx4]: + +# Feedback, Bug Reports + +* If you have feedback or bug reports, please post in [se-edu/duke issue tracker](https://github.com/se-edu/duke/issues). +* We welcome pull requests too. \ No newline at end of file diff --git a/tutorials/gradle-tutorial-1-introduction-to-gradle.md b/tutorials/gradle-tutorial-1-introduction-to-gradle.md deleted file mode 100644 index 68ab003bc..000000000 --- a/tutorials/gradle-tutorial-1-introduction-to-gradle.md +++ /dev/null @@ -1,163 +0,0 @@ -# Gradle Tutorial 1 - Setting up Gradle - -Gradle is a _build automation tool_ used to automate build processes. -There are many ways of integrating Gradle into a project. -In this guide, we will be using the _Gradle wrapper_. - -## Primer to Gradle - -As a developer, you write a build file that describes the project. - -A build file mainly consists of _plugins_, _tasks_ and _properties_. - -Plugins extend the functionality of Gradle. The `java` plugin adds support for `Java` projects. - -Tasks are reusable blocks of logic. -For example, the task `clean` simply deletes the project build directory. -Tasks can be composed of other tasks or be dependent on another task. -You might be surprised to find that a simple `check` will invoke another eight tasks at the very least! - -Properties change the behavior of tasks. -For instance, `mainClassName` of the `application` plugin is a compulsory property which tells Gradle which class is the entrypoint to your application. -As Gradle favors `convention over configuration`, there is not much to you need to configure if you follow the recommended directory structure. - -## Getting started - -1. Merge [this branch](https://github.com/se-edu/duke/tree/gradle). This will add the Gradle wrapper to your project. - -2. Navigate to the root directory of your project and type `gradlew run`. - -For users of IntelliJ IDEA, you can import the Gradle project by `Help > Find Action > Import Gradle Project`. - -![Import Gradle](assets/ImportGradle.png) - -After this, IntelliJ IDEA will identify your project as a Gradle project and you will gain access to the `Gradle Toolbar`. -Through the toolbar, you run Gradle tasks and view your project's dependencies. - -## Using Gradle - -Simply type `gradlew {taskName}` into the terminal and Gradle will run the task! -For example, you can type `gradlew tasks` and Gradle will show you a list of tasks available for your project. -Some plugins may add more helpful tasks so be sure to check the documentation! - -If you're using IntelliJ IDEA, you can click on the Gradle icon in the Gradle toolbar and create a new run configuration. - -![Gradle icon](assets/GradleIcon.png) - -Having a run configuration will save you a few keypresses in the long run. - -## Adding plugins - -Gradle plugins are reusable units of build logic. -Most common build tasks are provided as core plugins by Gradle. -Given below are instructions on how to use some useful plugins: - -### Checkstyle -To add support for _Checkstyle_, a tool to check if your code complies with coding standards. -Since Checkstyle is a core plugin, simply add the line `id 'checkstyle` into the `plugins` block. - -Your build file should look something like this now - -```groovy -plugins { - id 'java' - id 'application' - id 'checkstyle' -} -// ... code omitted for brevity ... -``` - -Checkstyle expects configuration files to for checkstyle to be in `./config/checkstyle/` by convention. -You can find the configuration files used in later projects [here](https://github.com/se-edu/addressbook-level3/tree/master/config/checkstyle). - -The plugin adds a few _tasks_ to your project. -Run `gradlew checkstyleMain checkstyleTest` to verify that you have set up Checkstyle properly. - -To find out the full list of tasks available to you, you can run `gradlew tasks --all`. - -### Shadow - -Shadow is a plugin that helps you package your application into an executable jar file. -Add the following line to your Gradle build file: - -```groovy -plugin { - //... - id 'com.github.johnrengelman.shadow' version '5.1.0' - //... -} -``` - -The plugin can be configured by setting some properties. -Let's try to produce a jar file with the name in format of `{baseName}-{version}.jar`. - -Add the following block to your build file: - -```groovy -//Publishes an executable jar to ./build/libs/ -shadowJar { - archiveBaseName = "duke" - archiveVersion = "0.1.3" - archiveClassifier = null - archiveAppendix = null -} -``` - -Now you can run the task `shadowJar` with the command `gradlew shadowJar`. -Are you able to execute your jar file with `java -jar {jarName}`? - -## Adding dependencies - -### JUnit 5 - -JUnit is a testing framework for Java. -It allows developers to write tests and run them. - -Add the following dependency to your build file: - -```groovy -dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter:5.5.0' -} -``` - -Then, configure Gradle to use JUnit by adding the following block to your build file: - -```groovy - -test { - useJUnitPlatform() -} -``` -By convention, tests belong in `src/test` folder. -Create a new `test` folder in under `src`. - -``` -src -├─main -│ ├─java -│ │ └─seedu -│ │ └─duke -│ └─resources -│ └─view -└─test - └─java - └─seedu - └─duke -``` - -If you have imported your Gradle project into IntelliJ IDEA, you will notice that IDEA is able to mark the test -directory as the Test root (colored in green by default) automatically. - -You can now write a test and run it with `gradlew test`. - -## Further reading - -Now that you have a general idea of how to accomplish basic tasks with Gradle, here's a list of material you can read - to further your understanding. - -- [Official Gradle Documentation](https://docs.gradle.org/current/userguide/userguide.html) -- [Google's checkstyle file](https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml) -- [Shadow](https://imperceptiblethoughts.com/shadow/introduction/) -- [Official JUnit Documentation](https://junit.org/junit5/docs/current/user-guide/#writing-tests) -- [AddressBook Level-4's build file](https://github.com/se-edu/addressbook-level4/blob/master/build.gradle) \ No newline at end of file diff --git a/tutorials/gradleTutorial.md b/tutorials/gradleTutorial.md new file mode 100644 index 000000000..7117662c4 --- /dev/null +++ b/tutorials/gradleTutorial.md @@ -0,0 +1,165 @@ +# Gradle Tutorial + +Gradle is a _build automation tool_ used to automate build processes. There are many ways of integrating Gradle into a project. In this guide, we will be using the _Gradle wrapper_. + +* [Introduction](#introduction) +* [Adding Gradle Support to Your Project](#adding-gradle-support-to-your-project) +* [Adding Plugins](#adding-plugins) + * [CheckStyle](#checkstyle) + * [Shadow](#shadow) +* [Adding Dependencies](#adding-dependencies) + * [JUnit](#junit) +* [Further Reading](#further-reading) + +## Introduction + +As a developer, you write a _build file_ that describes the project. A build file mainly consists of _plugins_, _tasks_ and _properties_. + +* **Plugins** extend the functionality of Gradle. For example, the `java` plugin adds support for `Java` projects. + +* **Tasks** are reusable blocks of logic. For example, the task `clean` simply deletes the project build directory. Tasks can be composed of other tasks or be dependent on another task. + +* **Properties** change the behavior of tasks. For instance, `mainClassName` of the `application` plugin is a compulsory property which tells Gradle which class is the entrypoint to your application. + As Gradle favors [_convention over configuration_](https://en.wikipedia.org/wiki/Convention_over_configuration), there is not much to you need to configure if you follow the recommended directory structure. + +## Adding Gradle Support to Your Project + +1. Pull the branch named `gradle`. Merge it to the `master` branch. This will add the Gradle wrapper to your project. + ``` + git checkout --track origin/gradle + git checkout master + git merge gradle + ``` +1. Open the `build.gradle` file in an editor. Update the following code block to point to the main class (i.e., the one containing the `main` method) of your application. The code below assumes your main class is `seedu.duke.Duke` + ```groovy + application { + mainClassName = "seedu.duke.Duke" + } + ``` +1. To check if Gradle has been added to the project correctly, open a terminal window, navigate to the root directory of your project and run the command `gradlew run`. This should result in Gradle running the main method of your project. + +:bulb: Simply run the command `gradlew {taskName}` in the terminal and Gradle will run the task! Here are some example commands: +* `gradlew tasks` (or `gradlew tasks --all`): shows a list of tasks available +* `gradlew run`: runs the main class of your project + +:bulb: Some plugins may add more helpful tasks so be sure to check their documentation! + +#### Using Gradle from within Intellij + +1. After adding support for Gradle, Intellij might automatically ask you (via a pop-up at the bottom right corner of the Window) whether to import the project as a Gradle project. In that case, go ahead and say yes. + +1. If the above didn't happen, import the Gradle project by `Help > Find Action > Import Gradle Project`. + ![Import Gradle](assets/ImportGradle.png) + +1. If the above didn't work either, close Intellij, delete the Intellij project files (i.e., `.idea` folder and `*.iml` files), and set up the project again, but instead of choosing `Create project from existing sources`, choose `Import project from external model` -> `Gradle`. + +After this, IntelliJ IDEA will identify your project as a Gradle project and you will gain access to the `Gradle Toolbar`. Through the toolbar, you run Gradle tasks and view your project's dependencies. + +You can click on the Gradle icon in the Gradle toolbar and create a new run configuration for running Gradle tasks without needing to type a `gradlew` command. + +![Gradle icon](assets/GradleIcon.png) + +## Adding Plugins + +Gradle plugins are reusable units of build logic. Most common build tasks are provided as core plugins by Gradle. Given below are instructions on how to use some useful plugins: + +### CheckStyle + +To add support for _Checkstyle_ (a tool to check if your code complies with a set of style rules), which comes as a core plugin, simply add the line `id 'checkstyle'` into the `plugins` block. + +Your build file should look something like this now: +```groovy +plugins { + id 'java' + id 'application' + id 'checkstyle' +} +// ... +``` + +Checkstyle expects configuration files for checkstyle to be in `./config/checkstyle/` by convention. +For example, you can copy the files found here to your project [this file from se-edu/addressbook-level3](https://github.com/se-edu/addressbook-level3/tree/master/config/checkstyle). + +The plugin adds a few _tasks_ to your project. Run `gradlew checkstyleMain checkstyleTest` to verify that you have set up Checkstyle properly (the command will check your main code and test code against the style rules). + +**Resources**: +* [Gradle documentation for CheckStyle plugin](https://docs.gradle.org/current/userguide/checkstyle_plugin.html) + +### Shadow + +Shadow is a plugin that packages an application into an executable jar file. To use it, first add the following line to your Gradle build file: +```groovy +plugins { + //... + id 'com.github.johnrengelman.shadow' version '5.1.0' + //... +} +``` + +The plugin can be configured by setting some properties. Let's try to produce a jar file with the name in format of `{baseName}-{version}.jar`. + +Add the following block to your build file: +```groovy +shadowJar { + archiveBaseName = "duke" + archiveVersion = "0.1.3" + archiveClassifier = null + archiveAppendix = null +} +``` + +Now you can run the command `gradlew shadowJar`.It publishes an executable jar to `./build/libs/`. You should be able to able to execute the created jar file by double-clicking it or using the command `java -jar {jarName}`? + +**Resources**: +* [Gradle documentation for Shadow plugin](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) +* [Gradle documentation for JUnit](https://docs.gradle.org/current/userguide/java_testing.html#using_junit5) +* [More about the Shadow plugin](https://imperceptiblethoughts.com/shadow/introduction/) + +## Adding Dependencies + +Gradle can automate the management of dependencies to third-party libraries. Given below are some examples. + +### JUnit + +JUnit is a testing framework for Java. It allows developers to write tests and run them. To manage JUnit dependency via Gradle, add the following to your build file: + +```groovy +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter:5.5.0' +} +``` + +Then, configure Gradle to use JUnit by adding the following block to your build file: + +```groovy +test { + useJUnitPlatform() +} +``` +By convention, java tests belong in `src/test/java` folder. Create a new `test/java` folder in under `src`. +``` +src +├─main +│ └─java +│ └─seedu/duke/Duke.java +└─test + └─java + └─seedu/duke/DukeTest.java +``` + +If you have imported your Gradle project into IntelliJ IDEA, you will notice that IDEA is able to mark the test directory as the _Test root_ (colored in green by default) automatically. + +You can now write a test (e.g., `test/java/seedu/duke/DukeTest.java`) and run it with `gradlew test`. + +**Resources**: +* [Gradle documentation for JUnit](https://docs.gradle.org/current/userguide/java_testing.html#using_junit5) + +## Further Reading + +Now that you have a general idea of how to accomplish basic tasks with Gradle, here's a list of material you can read to further your understanding. + +* [Official Gradle Documentation](https://docs.gradle.org/current/userguide/userguide.html) + +---------------------------------------------------------------------------------------- +**Authors:** +* Initial Version: Jeffry Lum \ No newline at end of file diff --git a/tutorials/javaFxTutorialPart1.md b/tutorials/javaFxTutorialPart1.md new file mode 100644 index 000000000..2f6331734 --- /dev/null +++ b/tutorials/javaFxTutorialPart1.md @@ -0,0 +1,96 @@ +# JavaFX Tutorial Part 1 – Introduction + +## JavaFX lifecycle of an application + +Imagine yourself as a director of a play. First you provision the props that you will feature in your play. These can be hand props for your actors to interact with or even set dressings just to liven up the background. You then decide where to layout the props for every scene. With a proper script in hand, you can finally approach a theatre and request for a stage. There on, it’s just a matter of pulling the curtains on your masterpiece. + +![Hierarchy of Objects in JavaFX](assets/JavaFxHierarchy.png) + +A JavaFX application is like a play you are directing. Instead of creating props, you create `Nodes` (`Nodes` are the fundamental building blocks of a JavaFX application), and place them onto a `Scene` (a scene is a graph of `Node`s). Then, you set your `Scene` on a `Stage` provided by JavaFX. When you call `Stage#show()` method, JavaFX renders a window with your `Stage` on it. + +## Setting up Java FX + +#### If you are not using Gradle + +1. Download the [JavaFX 11 SDK](https://gluonhq.com/products/javafx/) and unzip it. + +1. Import the `libs` folder from the SDK into your project (note: `JAVAFX_HOME` is the directory in which you have unzipped the JavaFX SDK). + + `File` > `Project Structure` > `Libraries` > `+` > `Java` > `{JAVAFX_HOME}/lib` + +1. From `Run` > `Edit Configurations`, add the following line into your `VM options` for each of the `main` classes. + + `--module-path {JAVAFX_HOME}/lib --add-modules javafx.controls,javafx.fxml`
+ e.g., `--module-path C:/javafx-sdk-11.0.2/lib --add-modules javafx.controls,javafx.fxml` + +#### If you are using Gradle + +Update your `build.gradle` to include the following lines: +```groovy +plugins { + id 'java' + id 'org.openjfx.javafxplugin' version '0.0.7' +} + +repositories { + mavenCentral() +} + +javafx { + version = "11.0.2" + modules = [ 'javafx.controls', 'javafx.fxml' ] +} +``` + +## Writing your first program + +As customary, let’s start off with a simple “Hello World” program. Create a new `HelloWorld.java` class in the `duke.seedu` package. Have the class extend `javafx.application.Application`. This requires you to override the `Application#start()` method and provide a concrete implementation. Notice that the method signature for `Application#start()` has a parameter `Stage`. This is the _primary stage_ that JavaFX provides. + +```java +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; + +public class HelloWorld extends Application { + + public static void main(String[] args) { + launch(); + } + + @Override + public void start(Stage stage) { + Label helloWorld = new Label("Hello World!"); // Creating a new Label control + Scene scene = new Scene(helloWorld); // Setting the scene to be our Label + + stage.setScene(scene); // Setting the stage to show our screen + stage.show(); // Render the stage. + } +} +``` + +Note how we have created a `Label` to contain the text that we want to show. We then create the `Scene` and set its content. Finally, we set the stage and show it. + +Run the program and you should see something like this: + +![Hello World](assets/HelloWorld.png) + +Congratulations! You have created your first GUI application! + +## Exercises + +1. We mentioned that `Node`s are the fundamental building blocks of JavaFX and used a `Label` as our root node in the HelloWorld application. + 1. What are some of the other types of `Node`s? + 1. How does JavaFX group them? + +1. `Node`s can be interacted with like Plain Old Java Objects (POJO). + 1. What properties of a `Label` can you change programmatically? + 1. Try changing the `Label` to have a font of Arial at size 50. + +1. You’ve learnt that a `Stage` can be thought of as a window. + 1. Can you have more than one `Stage` an application? + 1. Try creating another stage and showing it! What happens? + +---------------------------------------------------------------------------------------- +**Authors:** +* Initial Version: Jeffry Lum \ No newline at end of file diff --git a/tutorials/javaFxTutorialPart2.md b/tutorials/javaFxTutorialPart2.md new file mode 100644 index 000000000..f6831d9c8 --- /dev/null +++ b/tutorials/javaFxTutorialPart2.md @@ -0,0 +1,157 @@ +# JavaFX Tutorial Part 2 - Creating a GUI for Duke + +In this tutorial, we will be creating a GUI for Duke from scratch based on the following mockup. + +![Mockup for Duke](assets/DukeMockup.png) + +## JavaFX controls + +Controls are reusable UI elements. Refer to the [JavaFX's official documentation](https://openjfx.io/javadoc/11/javafx.controls/javafx/scene/control/package-summary.html) for a list of controls available. +From the mockup above, can you identify the controls that we will need to use? + +Mockup | Control +------ | :---: | +![ImageView](assets/MockupImageView.png) | ImageView +![Label](assets/MockupLabel.png) | Label +![Button](assets/MockupButton.png) | Button +![TextField](assets/MockupTextField.png) | TextField +![ScrollPane](assets/MockupScrollPane.png) | ScrollPane + +## Designing the Layout + +Now that we know what controls we need to implement our UI, let’s start programming! We quickly run into a problem: how do we show all of them on the screen at once? + +Each scene is initialized with a root `Node`. In the previous tutorial, our root `Node` was a `Label`. +What happens when we need to display more than one `Node` on the `Scene`? For that, we need to understand the JavaFX hierarchy. Recall from the previous tutorial: + +![Hierarchy of Objects in JavaFX](assets/JavaFxHierarchy.png) + +From the diagram, you see that the root `Node` can contain many other `Nodes` and similarly, each of those `Nodes` can contain many other `Nodes`. This means that if we can find a _container_ to set as our root `Node`, we can place all our other `Nodes` in it. + +But how do we get the exact layout we want in the UI? JavaFX provides that functionality in the form of **layout panes** in `javafx.scene.layouts`. Each layout pane follows a _layout policy_ to decide how to arrange its children. For example, the `VBox` lays out its children in a single vertical column and its counterpart, the `HBox` lays out its children in a single horizontal row. + +:bulb: A comprehensive list of layouts and how they behave is available here from the [official documentation](https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/layout/package-summary.html). + +One way to obtain the layout in the mockup is as follows. + +![Duke's layout](assets/DukeSceneGraph.png) + +To get that layout, we create a new `AnchorPane` and add our controls to it. Similarly, we create a new `VBox` to hold the contents of the `ScrollPane`. The code should look something like this: + +```java +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + + +public class Main extends Application { + + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + + public static void main(String[] args) { + launch(); + } + + @Override + public void start(Stage stage) { + //Step 1. Setting up required components + + //The container for the content of the chat to scroll. + scrollPane = new ScrollPane(); + dialogContainer = new VBox(); + scrollPane.setContent(dialogContainer); + + userInput = new TextField(); + sendButton = new Button("Send"); + + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + + scene = new Scene(mainLayout); + + stage.setScene(scene); + stage.show(); + + // more code to be added here later + } +} +``` + +Run the application and you should see something like this: + +![Duke's raw layout](assets/RawLayout.png) + +That is not what we were expecting, what did we forget to do? + +## Styling the Controls + +Almost every JavaFX object offer properties that you can set to customize its look and feel. For example, the `Stage` allows you to set its preferred size and title. Again, refer to the official JavaFX documentation for a comprehensive list of properties that you can modify. Here’s how you can get the application to look like the mockup: + +Add the following code to the bottom of the `start` method. You'll have to add `import javafx.scene.layout.Region;` to the imports too. + +```java + @Override + public void start(Stage stage) { + //Step 1. Formatting the window to look as expected. + + //... + + //Step 2. Formatting the window to look as expected + stage.setTitle("Duke"); + stage.setResizable(false); + stage.setMinHeight(600.0); + stage.setMinWidth(400.0); + + mainLayout.setPrefSize(400.0, 600.0); + + scrollPane.setPrefSize(385, 535); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + + dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); + + userInput.setPrefWidth(325.0); + + sendButton.setPrefWidth(55.0); + + AnchorPane.setTopAnchor(scrollPane, 1.0); + + AnchorPane.setBottomAnchor(sendButton, 1.0); + AnchorPane.setRightAnchor(sendButton, 1.0); + + AnchorPane.setLeftAnchor(userInput , 1.0); + AnchorPane.setBottomAnchor(userInput, 1.0); + + // more code to be added here later + } +``` + +Run the application again. It should now look like this: + +![Duke's Final layout](assets/FinalLayout.png) + +## Exercises + +1. In the tutorial, we used an `AnchorPane` to achieve the desired layout. + 1. Can you find other ways to obtain a similar layout? + 1. What are the advantages and disadvantages of your layout? + +1. Try interacting with the application + 1. What happens when you press the `Enter` key or left-click the send button? + 1. Why? + +---------------------------------------------------------------------------------------- +**Authors:** +* Initial Version: Jeffry Lum \ No newline at end of file diff --git a/tutorials/ui-tutorial-3-interacting-with-the-user.md b/tutorials/javaFxTutorialPart3.md similarity index 59% rename from tutorials/ui-tutorial-3-interacting-with-the-user.md rename to tutorials/javaFxTutorialPart3.md index 34a4ab7a4..04f573c41 100644 --- a/tutorials/ui-tutorial-3-interacting-with-the-user.md +++ b/tutorials/javaFxTutorialPart3.md @@ -1,25 +1,26 @@ # JavaFX Tutorial 3 – Interacting with the user -Picking up from where we left off last tutorial, we have successfully achieved the desired layout. -Now let’s make the application respond to user input. -You should substitute the provided `DukeStub` class with your completed Duke. +Picking up from where we left off last tutorial, we have successfully achieved the desired layout. Now let’s make the application respond to user input. + +:info: You should substitute the provided `DukeStub` class with your completed Duke. Rather than to do everything in one try, let’s iterate and build up towards our final goal. -## Iteration 1 – Echoing the user -JavaFX has an _event-driven architecture style_. -As such, we programmatically define handler methods to execute as a response to certain events. -When an event is detected, JavaFX will call the respective handlers. +## Iteration 1 – Echoing the User + +JavaFX has an _event-driven architecture style_. As such, we programmatically define _handler_ methods to execute as a response to certain _events_. When an event is detected, JavaFX will call the respective handlers. -For Duke, there are two events that we want to respond to, namely the user pressing `Enter` in the `TextField` and left-clicking the `Button`. -These are the `onAction` event for the `TextField` and the `onMouseClicked` event for the `Button`. +For Duke, there are two events that we want to respond to, namely the user pressing `Enter` in the `TextField` and left-clicking the `Button`. These are the `onAction` event for the `TextField` and the `onMouseClicked` event for the `Button`. -For now, let’s have the application add a new `Label` with the text from the `TextField`. +For now, let’s have the application add a new `Label` with the text from the `TextField`. Update the `Main` class as follows. You'll need to add an `import javafx.scene.control.Label;` too. ```java @Override public void start(Stage stage) { - // Code from part 1 and part 2 omitted for brevity - //Part 3. Add functionality to handle user input. + // Step 1 code here + + //Step 2 code here + + //Step 3. Add functionality to handle user input. sendButton.setOnMouseClicked((event) -> { dialogContainer.getChildren().add(getDialogLabel(userInput.getText())); userInput.clear(); @@ -49,28 +50,32 @@ Run the program and give it a whirl! ![Echo not scrolling as intended](assets/EchoNotScrolling.png) -At first glance everything appears to work perfectly. -However, when the `VBox` stretches beyond the confines of the `ScrollPane`, the `ScrollPane` does not scroll down automatically as expected. -We can remedy this by attaching a handler on the `VBox` to react to its own size changing and scrolling the `ScrollPane` down. +At first glance everything appears to work perfectly. However, when the `VBox` stretches beyond the confines of the `ScrollPane`, the `ScrollPane` does not scroll down automatically as expected. We can remedy this by attaching a handler on the `VBox` to react to its own size changing and scrolling the `ScrollPane` down. + +Update the `start` method as shown below. ```java public void start(Stage stage) { - // ... + // current code ... + //Scroll down to the end every time dialogContainer's height changes. - //v-value of 1.0 means 100% of the way down - dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); + dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); } ``` Verify that the `ScrollPane` scrolls as intended. -## Iteration 2 – Adding dialog boxes. -In the mockup of the UI, notice that the dialog boxes are composed of two different controls (`ImageView` and `Label`) and reused multiple times. -In situations like this, it is often beneficial to create our own custom control. -Doing this allows us to tightly couple the components together. +## Iteration 2 – Adding Dialog Boxes + +In the mockup of the UI, notice that the dialog boxes are composed of two different controls (`ImageView` and `Label`) and reused multiple times. In situations like this, it is often beneficial to create our own custom control. Let’s create our custom control `DialogBox`: ```java +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + public class DialogBox extends HBox { private Label text; @@ -90,24 +95,22 @@ public class DialogBox extends HBox { } ``` -We use the code in our main class just like any other control: +We use the code in our main class just like any other control. Here are the steps to update the code to use the custom control in `Main.java`. +First, add these imports: +```java +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +``` + +Next, add two images: ```java -@Override private Image user = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); private Image duke = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); +``` -public void start(Stage stage) { - //Part 3. Add functionality to handle user input. - sendButton.setOnMouseClicked((event) -> { - dialogContainer.getChildren().add(getDialogLabel(userInput.getText())); - }); - - userInput.setOnAction((event) -> { - dialogContainer.getChildren().add(getDialogLabel(userInput.getText())); - }); -} - +Add a new method to handle user input: +```java /** * Iteration 2: * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to @@ -124,21 +127,39 @@ private void handleUserInput() { } ``` +Update the event handler code in the `start` method to use the new `handleUserInput` method: +```java + +@Override +public void start(Stage stage) { + //... + + //Part 3. Add functionality to handle user input. + sendButton.setOnMouseClicked((event) -> { + handleUserInput(); + }); + + userInput.setOnAction((event) -> { + handleUserInput(); + }); +} +``` + Run the program and see how it works. ![DialogBoxes Iteration 2](assets/DialogBoxesIteration2.png) ## Iteration 3 – Adding custom behavior to DialogBox -One additional benefit of defining a custom control is that we can add behavior specific to our `DialogBox`. -Let’s add a method to flip a dialog box such that the image on the left to differentiate between user input and Duke’s output. + +One additional benefit of defining a custom control is that we can add behavior specific to our `DialogBox`. Let’s add a method to flip a dialog box such that the image on the left to differentiate between user input and Duke’s output. ```java +// Make the constructor private private DialogBox(Label l, ImageView iv) { - // ... - //Note how we changed the constructor to be private. + //... } + /** - * Iteration 3: * Flips the dialog box such that the ImageView is on the left and text on the right. */ private void flip() { @@ -159,7 +180,18 @@ public static DialogBox getDukeDialog(Label l, ImageView iv) { } ``` -Now, we can change the event handler to use our new `DialogBox`. +You'll need to update the imports as follows: +```java +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +``` + +Now, we can go back to the `Main` class and change the event handler to use our new `DialogBox`. ```java private void handleUserInput() { @@ -180,14 +212,18 @@ Run the application and play around with it. Congratulations! You have successfully implemented a fully functional GUI for Duke! -# Exercises -1. While the GUI looks similar to the mockup, there are still parts that need to be refined. -Try your hand at some of these tasks: - * Add padding between each DialogBox - * Add padding between each ImageView and its Label - * Clip the ImageView into a circle - * Add background color to each dialog box +## Exercises + +1. While the GUI looks similar to the mockup, there are still parts that need to be refined. Try your hand at some of these tasks: + * Add padding between each DialogBox + * Add padding between each ImageView and its Label + * Clip the ImageView into a circle + * Add background color to each dialog box 1. After attempting the changes, reflect critically on the following: - * What was the development workflow like? - * Is the code base well-organized? + * What was the development workflow like? + * Is the code base well-organized? + +---------------------------------------------------------------------------------------- +**Authors:** +* Initial Version: Jeffry Lum diff --git a/tutorials/ui-tutorial-4-introduction-to-fxml.md b/tutorials/javaFxTutorialPart4.md similarity index 57% rename from tutorials/ui-tutorial-4-introduction-to-fxml.md rename to tutorials/javaFxTutorialPart4.md index 828b78494..c5d478001 100644 --- a/tutorials/ui-tutorial-4-introduction-to-fxml.md +++ b/tutorials/javaFxTutorialPart4.md @@ -1,69 +1,61 @@ -# JavaFX Tutorial 4 – Introduction to FXML -This tutorial assumes that you have attempted the exercises at the end of tutorial 3. +# JavaFX Tutorial 4 – FXML + You will need to clone the code sample for this tutorial. While we have produced a fully functional prototype, there are a few major problems with our application. 1. The process of visually enhancing the GUI is long and painful: - > * Does the `TextField` need to be 330px or 325px wide? - > * How much padding is enough padding to look good? - - Every small change requires us to rebuild and run the application. + * Does the `TextField` need to be 330px or 325px wide? + * How much padding is enough padding to look good? + + Every small change requires us to rebuild and run the application. 1. Components are heavily dependent on each other: - > Why does `Main` need to know that `DialogBox` needs a `Label`? - > What happens if we change the `Label` to a custom `ColoredLabel` in the future? + Why does `Main` need to know that `DialogBox` needs a `Label`? + What happens if we change the `Label` to a custom `ColoredLabel` in the future? We need to minimize the amount of information each control needs to know about another. Otherwise, making changes in the future will break existing features. 1. The code is untidy and long: - > Why is all the code in one place? - - The `Main` class attempts to do it all. - Code for visual tweaks, listeners and even utility methods are all in one file. - This makes it difficult to find and make changes to existing code. - -# The solution -FXML is a XML-based language that allows us to define our user interface. -Properties of JavaFX objects can be defined in the FXML file. -For example: + Why is all the code in one place? + + The `Main` class attempts to do it all. + Code for visual tweaks, listeners and even utility methods are all in one file. + This makes it difficult to find and make changes to existing code. + +FXML is a XML-based language that allows us to define our user interface. Properties of JavaFX objects can be defined in the FXML file. For example: ```xml ``` -The FXML snippet define a TextField similar to the one that we programmatically defined previous in Tutorial 2. -Notice how concise FXML is compared to the plain Java version. +The FXML snippet define a TextField similar to the one that we programmatically defined previous in Tutorial 2. Notice how concise FXML is compared to the plain Java version. Let's return to Duke and convert it to use FXML instead. -# Rebuilding our scenes -Scene Builder is a tool developed by Oracle and currently maintained by Gluon. -It is a What-You-See-Is-What-You-Get GUI creation tool. -[Download](https://gluonhq.com/products/scene-builder/#download) the appropriate version for your OS and install it: +# Rebuilding the Scene using FXML + +1. Scene Builder is a tool developed by Oracle and currently maintained by Gluon. It is a What-You-See-Is-What-You-Get GUI creation tool. [Download](https://gluonhq.com/products/scene-builder/#download) the appropriate version for your OS and install it. -Let’s explore the provided FXML files in Scene Builder. -Running the tool brings up the main screen. -Select `Open Project` > `src/main/resources/view/MainWindow.fxml`. -Inspect each control and its properties. +1. Let’s explore the provided FXML files in Scene Builder. Running the tool brings up the main screen. +Select `Open Project` > `src/main/resources/view/MainWindow.fxml`. Inspect each control and its properties. -![SceneBuilder opening MainWindow.fxml](assets/SceneBuilder.png) + ![SceneBuilder opening MainWindow.fxml](assets/SceneBuilder.png) -On the right accordion pane, you can modify the properties of the control that you have selected. -Try changing the various settings and see what they do! +1. On the right accordion pane, you can modify the properties of the control that you have selected. Try changing the various settings and see what they do! -On the left accordion, you can see that we have set the controller class to `MainWindow`. +1. On the left accordion, you can see that we have set the controller class to `MainWindow`. We will get to that later. -![Controller for MainWindow](assets/MainWindowController.png) + ![Controller for MainWindow](assets/MainWindowController.png) -Let’s repeat the process for `DialogBox`. -The main difference here is that DialogBox checks `Use fx:root construct` and _does not define a controller class_. +1. Let’s repeat the process for `DialogBox`. + The main difference here is that DialogBox checks `Use fx:root construct` and _does not define a controller class_. + ![Settings for DialogBox](assets/DialogBoxController.png) -![Settings for DialogBox](assets/DialogBoxController.png) +## Using Controllers -# Structure of a Controller. Let's take a closer look at the `MainWindow` class that we specified as the controller in the FXML file: ```java @@ -107,17 +99,13 @@ public class MainWindow extends AnchorPane { As the controller, `MainWindow` is solely responsible for the logic behind the application. -The `@FXML` annotation marks a `private` or `protected` member and makes it accessible to FXML despite its modifier. -Without the annotation, we will have to make everything `public` and expose our UI to unwanted changes. +The `@FXML` annotation marks a `private` or `protected` member and makes it accessible to FXML despite its modifier. Without the annotation, we will have to make everything `public` and expose our UI to unwanted changes. -The `FXMLLoader` will map the a control with a `fx:id` defined in FXML to a variable with the same name in its controller. -Notice how in `MainWindow`, we can invoke `TextField#clear()` on `userInput` and access its content just as we did in the previous example. -Similarly, methods like private methods like `handleUserInput` can be used in FXML when annotated by `@FXML`. +The `FXMLLoader` will map the a control with a `fx:id` defined in FXML to a variable with the same name in its controller. Notice how in `MainWindow`, we can invoke `TextField#clear()` on `userInput` and access its content just as we did in the previous example. Similarly, methods like private methods like `handleUserInput` can be used in FXML when annotated by `@FXML`. -# Using FXML in our application +## Using FXML in our application -We load a FXML file using a `FXMLLoader`. -Note how we use `getResource` instead of using `InputStreams` from `java.io` as our resources will no longer be accessible through file IO when we eventually package our application into an executable `jar`. +We load a FXML file using a `FXMLLoader`. Note how we use `getResource` instead of using `InputStreams` from `java.io` as our resources will no longer be accessible through file IO when we eventually package our application into an executable `jar`. ```java @Override @@ -136,8 +124,7 @@ public void start(Stage stage) { Again, we can interact with the `AnchorPane` defined in the FXML as we would have if we created the `AnchorPane` ourselves. -For our custom `DialogBox`, we did not define a controller. -Looking at the code for the `DialogBox` class: +For our custom `DialogBox`, we did not define a controller. Looking at the code for the `DialogBox` class: ```java public class DialogBox extends HBox { @@ -168,8 +155,12 @@ From this point onwards we can interact with `DialogBox` as we have in the previ [todo]: # (Discussion on the fx:root pattern.) -# Exercise +## Exercises 1. Convert `MainWindow` to use the `fx:root` construct. 1. Extend `MainWindow` to have a `Stage` as a root Node. -1. Customize the appearance of the application further with CSS. \ No newline at end of file +1. Customize the appearance of the application further with CSS. + +---------------------------------------------------------------------------------------- +**Authors:** +* Initial Version: Jeffry Lum \ No newline at end of file diff --git a/tutorials/ui-tutorial-1-introduction-to-javafx.md b/tutorials/ui-tutorial-1-introduction-to-javafx.md deleted file mode 100644 index 32345b740..000000000 --- a/tutorials/ui-tutorial-1-introduction-to-javafx.md +++ /dev/null @@ -1,94 +0,0 @@ -# JavaFX Tutorial 1 – Hello World - -# Setting up -Follow the guide in the README. - - -# Basics of JavaFX: - -## An analogy -In JavaFX, you are a director and each application is a play. -First you provision the props that you will feature in your play. -These can be hand props for your actors to interact with or even set dressings just to liven up the background. -You then decide where to layout the props for every scene. -With a proper script in hand, you can finally approach a theatre and request for a stage. -There on, it’s just a matter of pulling the curtains on your masterpiece. - -## JavaFX lifecycle of an application -Instead of creating props, you create `Nodes` instead. -`Nodes` are the fundamental building blocks of a JavaFX application. -You create `Nodes` and place them onto a `Scene`. -A scene can be thought of as a graph, each `Scene` must have a root `Node` and each `Node` can have zero or more children. - -![Hierarchy of Objects in JavaFX](assets/JavaFxHierarchy.png) - -Don’t worry too much about this for now, you’ll see exactly how to work with the scene graph in a later tutorial. -Then, you set your `Scene` on a `Stage` provided by JavaFX. -When you call `Stage#show()` method, JavaFX renders a window with your `Stage` on it. - -# Writing your first program -As customary, let’s start off with a simple “Hello World” program. -Create a new `HelloWorld.java` class in the `duke.seedu` package. -Have the class extend `javafx.application.Application`. -This requires you to override the `Application#start()` method and provide a concrete implementation. -Notice that the method signature for `Application#start()` has a parameter `Stage`. -This is the _primary stage_ that JavaFX provides. - -```java -public class HelloWorld extends Application { - - public static void main(String[] args) { - launch(); - } - - @Override - public void start(Stage stage) { - // Our code goes here - } -} -``` - -Let’s create a `Label` to contain the text that we want to show. -We then create the `Scene` and set its content. -Finally, we can set the stage and show it. - -```java -public class HelloWorld extends Application { - - public static void main(String[] args) { - launch(); - } - - @Override - public void start(Stage stage) { - Label helloWorld = new Label("Hello World!"); // Creating a new Label control - Scene scene = new Scene(helloWorld); // Setting the scene to be our Label - - stage.setScene(scene); // Setting the stage to show our screen - stage.show(); // Render the stage. - } -} -``` - -Run the program and you should see something like this: - -![Hello World](assets/HelloWorld.png) - -Congratulations! You have created your first GUI application! - -# Exercises -1. We mentioned that `Node`s are the fundamental building blocks of JavaFX and used a `Label` as our root node in the HelloWorld application. - - 1. What are some of the other types of `Node`s? - 1. How does JavaFX group them? - -1. `Node`s can be interacted with like Plain Old Java Objects (POJO). - - 1. What properties of a `Label` can you change programmatically? - 1. Try changing the `Label` to have a font of Arial at size 50. - -1. You’ve learnt that a `Stage` can be thought of as a window. - - 1. Can you have more than one `Stage` an application? - 1. Try creating another stage and showing it! What happens? - \ No newline at end of file diff --git a/tutorials/ui-tutorial-2-creating-a-gui-for-duke.md b/tutorials/ui-tutorial-2-creating-a-gui-for-duke.md deleted file mode 100644 index 529971b8a..000000000 --- a/tutorials/ui-tutorial-2-creating-a-gui-for-duke.md +++ /dev/null @@ -1,157 +0,0 @@ -# JavaFX Tutorial 2 - Creating a GUI for Duke -We assume that you have finished the setting up process in the previous tutorial. -In this tutorial, we will be creating a graphical user interface for Duke from scratch based on the following mockup. - -![Mockup for Duke](assets/DukeMockup.png) - -# Introducing JavaFX controls -Controls are reusable UI elements. -Refer to the [JavaFX's official documentation](https://openjfx.io/javadoc/11/javafx.controls/javafx/scene/control/package-summary.html) for a list of controls available. -From the mockup above, can you identify the controls that we will need to use? - -Mockup | Control ---- | :---: | -![ImageView](assets/MockupImageView.png) | ImageView -![ImageView](assets/MockupLabel.png) | Label -![ImageView](assets/MockupButton.png) | Button -![ImageView](assets/MockupTextField.png) | TextField -![ImageView](assets/MockupScrollPane.png) | ScrollPane - -Now that we know what controls we need to implement our UI, let’s start programming! - -```java -public class Main extends Application { - - private ScrollPane scrollPane; - private TextField userInput; - private Button sendButton; - private Scene scene; - - public static void main(String[] args) { - launch(); - } - - @Override - public void start(Stage stage) { - //Part 1. Setting up required components - scrollPane = new ScrollPane(); - userInput = new TextField(); - sendButton = new Button("Send"); - - // What do we put into our scene as the root Node? - // scene = new Scene(???) - // stage.setScene(scene); - // stage.show(); - } -} -``` -We quickly run into a problem: how do we show all of them on the screen at once? - -# Introducing the Scenegraph API -Each scene is initialized with a root `Node`. -In the previous tutorial, our root `Node` was a `Label`. -What happens when we need to display more than one `Node` on the `Scene`? -For that, we need to understand the JavaFX hierarchy. -Recall from the previous tutorial: - -![Hierarchy of Objects in JavaFX](assets/JavaFxHierarchy.png) - -From the diagram, you see that the root `Node` can contain many other `Nodes` and similarly, each of those `Nodes` can contain many other `Nodes`. -This means that if we can find a _container_ to set as our root `Node`, we can place all our other `Nodes` in it. - -## Layout managers and containers -Fortunately for us, JavaFX provides that functionality in the form of layout panes in `javafx.scene.layouts`. -Each layout pane follows a layout policy to decide how to arrange its children. -For example, the `VBox` lays out its children in a single vertical column and its counterpart, the `HBox` lays out its children in a single horizontal row. -A comprehensive list of layouts and how they behave is available here from the [official documentation](https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/layout/package-summary.html). - -One way to obtain the layout in the mockup is as follows: - -![Duke's layout](assets/DukeSceneGraph.png) - -For the rest of the tutorial, let’s use this layout. - -## Putting it together -We create a new `AnchorPane` and add our controls to it. -Similarly, we create a new `VBox` to hold the contents of the `ScrollPane` -The code should look something like this: - -```java -@Override -public void start(Stage stage) { - //Part 1. Setting up required components - //The container for the content of the chat to scroll. - scrollPane = new ScrollPane(); - dialogContainer = new VBox(); - scrollPane.setContent(dialogContainer); - - userInput = new TextField(); - sendButton = new Button("Send"); - - AnchorPane mainLayout = new AnchorPane(); - mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); - - scene = new Scene(mainLayout); - - stage.setScene(scene); - stage.show(); -} -``` -Run the application and you should see something like this: - -![Duke's raw layout](assets/RawLayout.png) - -That is not what we were expecting, what did we forget to do? - -# Introducing styling and properties -Almost every JavaFX object offer properties that you can set to customize its look and feel. -For example, the `Stage` allows you to set its preferred size and title. -Again, refer to the official JavaFX documentation for a comprehensive list of properties that you can modify. -Here’s how you can get the application to look like the mockup: - -```java -//Part 2. Formatting the window to look as expected. -stage.setTitle("Duke"); -stage.setResizable(false); -stage.setMinHeight(600.0); -stage.setMinWidth(400.0); - -mainLayout.setPrefSize(400.0, 600.0); - -scrollPane.setPrefSize(385, 535); -scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); -scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); - -scrollPane.setVvalue(1.0); -scrollPane.setFitToWidth(true); - -dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); - -userInput.setPrefWidth(325.0); - -sendButton.setPrefWidth(55.0); - -AnchorPane.setTopAnchor(scrollPane, 1.0); - -AnchorPane.setBottomAnchor(sendButton, 1.0); -AnchorPane.setRightAnchor(sendButton, 1.0); - -AnchorPane.setLeftAnchor(userInput , 1.0); -AnchorPane.setBottomAnchor(userInput, 1.0); -``` - -Run the application again. - -![Duke's Final layout](assets/FinalLayout.png) - -Looking great! - -# Exercises -1. In the tutorial, we used an `AnchorPane` to achieve the desired layout. - - 1. Can you find other ways to obtain a similar layout? - 1. What are the advantages and disadvantages of your layout? - -1. Try interacting with the application - 1. What happens when you press the `Enter` key or left-click the send button? - 1. Why?