Skip to content

Commit 05d9921

Browse files
committed
Add GUI using JavaFX
1 parent 340a87b commit 05d9921

18 files changed

+289
-79
lines changed

build.gradle

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
plugins {
22
id 'java'
33
id 'application'
4+
id 'org.openjfx.javafxplugin' version '0.0.10'
45
id 'com.github.johnrengelman.shadow' version '5.1.0'
56
}
67

@@ -11,6 +12,20 @@ repositories {
1112
dependencies {
1213
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0'
1314
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0'
15+
16+
String javaFxVersion = '11'
17+
implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win'
18+
implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac'
19+
implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux'
20+
implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win'
21+
implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac'
22+
implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux'
23+
implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win'
24+
implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac'
25+
implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux'
26+
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win'
27+
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac'
28+
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux'
1429
}
1530

1631
test {
@@ -28,12 +43,7 @@ test {
2843
}
2944

3045
application {
31-
mainClassName = "duke.Duke"
32-
}
33-
34-
shadowJar {
35-
archiveBaseName = "duke"
36-
archiveClassifier = null
46+
mainClassName = "duke.Launcher"
3747
}
3848

3949
run{

src/main/java/duke/Duke.java

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,56 @@
11
package duke;
22

33
import duke.commands.Command;
4+
import duke.controllers.MainWindow;
45
import duke.tasks.*;
56
import duke.ui.Ui;
67
import duke.utils.InputParser;
78
import duke.utils.Storage;
9+
import javafx.application.Application;
10+
import javafx.fxml.FXMLLoader;
11+
import javafx.scene.Scene;
12+
import javafx.scene.layout.AnchorPane;
13+
import javafx.stage.Stage;
814

915
import java.io.File;
16+
import java.io.IOException;
17+
import java.net.URL;
1018
import java.util.Scanner;
1119

12-
public class Duke {
20+
public class Duke extends Application {
1321

1422
private TaskList taskList;
1523
private Storage storage;
16-
1724
private Ui ui;
18-
1925
private InputParser inputParser;
20-
private static final File saveFile = new File("savedata.txt");
21-
22-
public static void main(String[] args) {
23-
Duke duke = new Duke();
24-
duke.start();
25-
}
26+
private static final File SAVE_FILE = new File("savedata.txt");
27+
private static final URL MAIN_WINDOW_FXML = Duke.class.getResource("/view/MainWindow.fxml");
2628

27-
public Duke() {
29+
@Override
30+
public void start(Stage stage) throws Exception {
2831
inputParser = new InputParser();
2932
ui = new Ui();
30-
}
31-
32-
public void start() {
33-
Scanner sc = new Scanner(System.in);
34-
storage = new Storage(saveFile);
35-
ui.showLogo();
33+
storage = new Storage(SAVE_FILE);
3634
taskList = new TaskList(storage.loadFromFile());
37-
ui.showWelcome();
38-
loop(sc);
35+
loadMainWindow(stage);
3936
}
4037

41-
public void loop(Scanner sc) {
42-
while (sc.hasNext()) {
43-
try {
44-
String input = sc.nextLine().trim();
45-
Command cmd = inputParser.parse(input, taskList, storage, ui);
46-
cmd.execute();
47-
} catch (Exception e) {
48-
e.printStackTrace();
49-
}
38+
private void loadMainWindow(Stage stage) {
39+
try {
40+
FXMLLoader fxmlLoader = new FXMLLoader(MAIN_WINDOW_FXML);
41+
AnchorPane ap = fxmlLoader.load();
42+
Scene scene = new Scene(ap);
43+
stage.setScene(scene);
44+
fxmlLoader.<MainWindow>getController().setDuke(this);
45+
stage.show();
46+
} catch (IOException e) {
47+
e.printStackTrace();
5048
}
5149
}
50+
51+
public String getResponse(String input) {
52+
Command cmd = inputParser.parse(input, taskList, storage, ui);
53+
String response = cmd.execute();
54+
return response;
55+
}
5256
}

src/main/java/duke/Launcher.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package duke;
2+
3+
import javafx.application.Application;
4+
5+
public class Launcher {
6+
7+
public static void main(String[] args) {
8+
Application.launch(Duke.class, args);
9+
}
10+
11+
}

src/main/java/duke/commands/AddTaskCommand.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,21 @@ public AddTaskCommand(Storage storage, Ui ui, TaskList tasks, TaskType type, Str
2828
}
2929

3030
@Override
31-
public boolean execute() {
31+
public String execute() {
3232
try {
3333
Task newTask = TaskParser.stringToTask(type, taskString);
3434
tasks.addTask(newTask);
35-
ui.showAddTaskResponse(newTask, tasks);
3635
storage.saveToFile(tasks.getList());
36+
37+
String response = String.format(
38+
"Got it. I've added this task:\n %s\nNow you have %d tasks in the list.",
39+
newTask, tasks.getSize());
40+
return response;
3741
} catch (EmptyTaskDescException | EmptyTaskDateException | NoSuchTaskTypeException |
3842
UnrecognisedDateException e) {
39-
ui.showError(e);
40-
return false;
43+
44+
String response = String.format("Oops! %s", e.getMessage());
45+
return response;
4146
}
42-
return true;
4347
}
4448
}

src/main/java/duke/commands/Command.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
public abstract class Command {
44

5-
public abstract boolean execute();
5+
public abstract String execute();
66

77
}

src/main/java/duke/commands/DeleteTaskCommand.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,22 @@ public DeleteTaskCommand(Storage storage, Ui ui, TaskList tasks, String index) {
2121
}
2222

2323
@Override
24-
public boolean execute() {
24+
public String execute() {
2525
try {
2626
Task deletedTask = tasks.deleteTask(Integer.parseInt(index) - 1);
27-
ui.showDeleteTaskResponse(deletedTask, tasks);
2827
storage.saveToFile(tasks.getList());
29-
return true;
28+
29+
String response = String.format(
30+
"Noted. I've removed this task:\n %s\nNow you have %d tasks in the list",
31+
deletedTask, tasks.getSize()
32+
);
33+
return response;
3034
} catch (TaskNotFoundException e) {
31-
ui.showError(e);
32-
return false;
35+
String response = String.format("Oops! %s", e.getMessage());
36+
return response;
3337
} catch (NumberFormatException e) {
34-
ui.showInvalidFormatError(index);
35-
return false;
38+
String response = String.format("Oops! I could not recognise this format: %s", index);
39+
return response;
3640
}
3741
}
3842
}

src/main/java/duke/commands/ExitCommand.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ public ExitCommand(Ui ui) {
1111
}
1212

1313
@Override
14-
public boolean execute() {
15-
ui.showBye();
14+
public String execute() {
1615
System.exit(0);
17-
return true;
16+
return "";
1817
}
1918
}

src/main/java/duke/commands/FindTaskCommand.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,17 @@ public FindTaskCommand(TaskList tasks, Ui ui, String searchTerm) {
2020
}
2121

2222
@Override
23-
public boolean execute() {
23+
public String execute() {
2424
List<Task> matchingTasks = tasks.find(searchTerm);
25-
ui.showMatchingTasks(matchingTasks);
26-
return true;
25+
26+
StringBuilder sb = new StringBuilder();
27+
sb.append("Here are the matching tasks in your list:\n");
28+
int i = 1;
29+
for (Task task : matchingTasks) {
30+
sb.append(String.format("%d.%s\n", i, task.toString()));
31+
++i;
32+
}
33+
return sb.toString();
2734
}
2835

2936
}

src/main/java/duke/commands/MarkTaskCommand.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,21 @@ public MarkTaskCommand(Storage storage, Ui ui, TaskList tasks, String index) {
2222
}
2323

2424
@Override
25-
public boolean execute() {
25+
public String execute() {
2626
try {
2727
Task markedTask = tasks.markTask(Integer.parseInt(index) - 1);
28-
ui.showMarkTaskResponse(markedTask);
2928
storage.saveToFile(tasks.getList());
30-
return true;
29+
30+
String response = String.format(
31+
"Nice! I've marked this task as done:\n %s",
32+
markedTask);
33+
return response;
3134
} catch (TaskNotFoundException e) {
32-
ui.showError(e);
33-
return false;
35+
String response = String.format("Oops! %s", e.getMessage());
36+
return response;
3437
} catch (NumberFormatException e) {
35-
ui.showInvalidFormatError(index);
36-
return false;
38+
String response = String.format("Oops! I could not recognise this format: %s\n", index);
39+
return response;
3740
}
3841
}
3942

src/main/java/duke/commands/PrintTasksCommand.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ public PrintTasksCommand(Ui ui, TaskList tasks) {
1414
}
1515

1616
@Override
17-
public boolean execute() {
18-
ui.showTasks(tasks);
19-
return true;
17+
public String execute() {
18+
StringBuilder sb = new StringBuilder();
19+
sb.append("Here are the tasks in your list:\n").append(tasks.toString());
20+
return sb.toString();
2021
}
2122
}

src/main/java/duke/commands/UnmarkTaskCommand.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,21 @@ public UnmarkTaskCommand(Storage storage, Ui ui, TaskList tasks, String index) {
2222
}
2323

2424
@Override
25-
public boolean execute() {
25+
public String execute() {
2626
try {
2727
Task unmarkedTask = tasks.unmarkTask(Integer.parseInt(index) - 1);
28-
ui.showUnmarkTaskResponse(unmarkedTask);
2928
storage.saveToFile(tasks.getList());
30-
return true;
29+
30+
String response = String.format(
31+
"OK, I've marked this task as not done yet:\n %s`",
32+
unmarkedTask);
33+
return response;
3134
} catch (TaskNotFoundException e) {
32-
ui.showError(e);
33-
return false;
35+
String response = String.format("Oops! %s", e.getMessage());
36+
return response;
3437
} catch (NumberFormatException e) {
35-
ui.showInvalidFormatError(index);
36-
return false;
38+
String response = String.format("Oops! I could not recognise this format: %s\n", index);
39+
return response;
3740
}
3841
}
3942

src/main/java/duke/commands/UnrecognisedCommand.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ public UnrecognisedCommand(Ui ui) {
1111
}
1212

1313
@Override
14-
public boolean execute() {
15-
ui.showCommandUnknown();
16-
return true;
14+
public String execute() {
15+
String response = "Oops! I'm sorry, but I don't know what that means.";
16+
return response;
1717
}
1818
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package duke.controllers;
2+
3+
import java.io.IOException;
4+
import java.util.Collections;
5+
6+
import javafx.collections.FXCollections;
7+
import javafx.collections.ObservableList;
8+
import javafx.fxml.FXML;
9+
import javafx.fxml.FXMLLoader;
10+
import javafx.geometry.Pos;
11+
import javafx.scene.Node;
12+
import javafx.scene.control.Label;
13+
import javafx.scene.image.Image;
14+
import javafx.scene.image.ImageView;
15+
import javafx.scene.layout.Background;
16+
import javafx.scene.layout.BackgroundFill;
17+
import javafx.scene.layout.HBox;
18+
import javafx.scene.paint.Paint;
19+
import javafx.scene.shape.Circle;
20+
21+
/**
22+
* An example of a custom control using FXML.
23+
* This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label
24+
* containing text from the speaker.
25+
*/
26+
public class DialogBox extends HBox {
27+
@FXML
28+
private Label dialog;
29+
@FXML
30+
private ImageView displayPicture;
31+
32+
private DialogBox(String text, Image img) {
33+
try {
34+
FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml"));
35+
fxmlLoader.setController(this);
36+
fxmlLoader.setRoot(this);
37+
fxmlLoader.load();
38+
} catch (IOException e) {
39+
e.printStackTrace();
40+
}
41+
42+
dialog.setText(text);
43+
displayPicture.setImage(img);
44+
displayPicture.setClip(new Circle(50, 50, 45));
45+
}
46+
47+
/**
48+
* Flips the dialog box such that the ImageView is on the left and text on the right.
49+
*/
50+
private void flip() {
51+
ObservableList<Node> tmp = FXCollections.observableArrayList(this.getChildren());
52+
Collections.reverse(tmp);
53+
getChildren().setAll(tmp);
54+
setAlignment(Pos.TOP_LEFT);
55+
}
56+
57+
public static DialogBox getUserDialog(String text, Image img) {
58+
var db = new DialogBox(text, img);
59+
db.setStyle("-fx-background-color: azure;");
60+
return db;
61+
}
62+
63+
public static DialogBox getDukeDialog(String text, Image img) {
64+
var db = new DialogBox(text, img);
65+
db.setStyle("-fx-background-color: blanchedalmond;");
66+
db.flip();
67+
return db;
68+
}
69+
}

0 commit comments

Comments
 (0)