Skip to content

Commit

Permalink
Start Level 10. GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
QianChangru committed Sep 10, 2023
1 parent 2a192a3 commit 9ff4bbc
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 65 deletions.
15 changes: 15 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ repositories {
dependencies {
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.10.0'
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.0'

String javaFxVersion = '17.0.7'

implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win'
implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac'
implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux'
implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win'
implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac'
implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux'
implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win'
implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac'
implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux'
}

test {
Expand Down
62 changes: 62 additions & 0 deletions src/main/java/duke/DialogBox.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package duke;

import java.io.IOException;
import java.util.Collections;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;

/**
* An example of a custom control using FXML.
* This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label
* containing text from the speaker.
*/
public class DialogBox extends HBox {
@FXML
private Label dialog;
@FXML
private ImageView displayPicture;

private DialogBox(String text, Image img) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setRoot(this);
fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}

dialog.setText(text);
displayPicture.setImage(img);
}

/**
* Flips the dialog box such that the ImageView is on the left and text on the right.
*/
private void flip() {
ObservableList<Node> tmp = FXCollections.observableArrayList(this.getChildren());
Collections.reverse(tmp);
getChildren().setAll(tmp);
setAlignment(Pos.TOP_LEFT);
}

public static DialogBox getUserDialog(String text, Image img) {
return new DialogBox(text, img);
}

public static DialogBox getDukeDialog(String text, Image img) {
var db = new DialogBox(text, img);
db.flip();
return db;
}
}

24 changes: 23 additions & 1 deletion src/main/java/duke/Duke.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package duke;

import java.util.Scanner;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.VBox;

import java.io.IOException;
import java.util.Scanner;

/**
* Encapsulates the chat bot.
Expand All @@ -13,6 +19,10 @@ public class Duke {
private TaskList tasks;
private Ui ui;

public Duke() {
this("data/tasks.txt");
}

public Duke(String filePath) {
ui = new Ui();
storage = new Storage(filePath);
Expand Down Expand Up @@ -46,4 +56,16 @@ public void run() {
public static void main(String[] args) {
new Duke("data/tasks.txt").run();
}


/**
* You should have your own function to generate a response to user input.
* Replace this stub with your completed method.
*/
protected String getResponse(String input) {
Parser parser = new Parser(input, this.storage, this.tasks);
String response = parser.parse();
return response;
}

}
12 changes: 12 additions & 0 deletions src/main/java/duke/Launcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package duke;

import javafx.application.Application;

/**
* A launcher class to workaround classpath issues.
*/
public class Launcher {
public static void main(String[] args) {
Application.launch(Main.class, args);
}
}
32 changes: 32 additions & 0 deletions src/main/java/duke/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package duke;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;


/**
* A GUI for Duke using FXML.
*/
public class Main extends Application {

private Duke duke = new Duke();

@Override
public void start(Stage stage) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml"));
AnchorPane ap = fxmlLoader.load();
Scene scene = new Scene(ap);
stage.setScene(scene);
fxmlLoader.<MainWindow>getController().setDuke(duke);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
52 changes: 52 additions & 0 deletions src/main/java/duke/MainWindow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package duke;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;

/**
* Controller for MainWindow. Provides the layout for the other controls.
*/
public class MainWindow extends AnchorPane {
@FXML
private ScrollPane scrollPane;
@FXML
private VBox dialogContainer;
@FXML
private TextField userInput;
@FXML
private Button sendButton;

private Duke duke;

private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png"));
private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png"));

@FXML
public void initialize() {
scrollPane.vvalueProperty().bind(dialogContainer.heightProperty());
}

public void setDuke(Duke d) {
duke = d;
}

/**
* Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to
* the dialog container. Clears the user input after processing.
*/
@FXML
private void handleUserInput() {
String input = userInput.getText();
String response = duke.getResponse(input);
dialogContainer.getChildren().addAll(
DialogBox.getUserDialog(input, userImage),
DialogBox.getDukeDialog(response, dukeImage)
);
userInput.clear();
}
}
19 changes: 10 additions & 9 deletions src/main/java/duke/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,45 @@ public Parser(String command, Storage storage, TaskList taskList) {
/**
* Parses the user input and initiate following operations.
*/
public void parse() {
public String parse() {
try{
if (command.startsWith("list")) {
this.taskList.listTasks();
return this.taskList.listTasks();
} else if (command.startsWith("mark")) {
this.taskList.markTask(Integer.valueOf(command.split(" ")[1]) - 1);
return this.taskList.markTask(Integer.valueOf(command.split(" ")[1]) - 1);
} else if (command.startsWith("todo")) {
if (command.split(" ", 2).length == 1) {
throw new DukeException(" OOPS!!! The description of a todo cannot be empty.");
}
ToDo newToDo = new ToDo(command.split(" ", 2)[1]);
this.taskList.addTask(newToDo);
return this.taskList.addTask(newToDo);
} else if (command.startsWith("deadline")) {
LocalDate deadline = LocalDate.parse(command.split(" /by ", 2)[1]);
String name = command.split(" /by ", 2)[0].split(" ", 2)[1];
Deadline newDeadline = new Deadline(name, deadline);
this.taskList.addTask(newDeadline);
return this.taskList.addTask(newDeadline);
} else if (command.startsWith("event")) {
LocalDate startTime = LocalDate.parse(command.split(" /from ", 2)[1]
.split(" /to ", 2)[0]);
LocalDate endTime = LocalDate.parse(command.split(" /to ", 2)[1]);
String name = command.split(" /from ", 2)[0].split(" ", 2)[1];
Event newEvent = new Event(name, startTime, endTime);
this.taskList.addTask(newEvent);
return this.taskList.addTask(newEvent);
} else if (command.startsWith("delete")) {
this.taskList.deleteTask(Integer.valueOf(command.split(" ")[1]) - 1);
return this.taskList.deleteTask(Integer.valueOf(command.split(" ")[1]) - 1);
} else if (command.startsWith("bye")) {
this.isEnd = true;
Ui.farewellMessage();
return Ui.farewellMessage();
} else if (command.startsWith("find")) {
String keyword = command.split(" ", 2)[1];
this.taskList.findTask(keyword);
return this.taskList.findTask(keyword);
} else {
throw new DukeException(" OOPS!!! I'm sorry, but I don't know what that means :-(");
}
} catch (DukeException e) {
Ui.printException(e);
}
return "Invalid command.";
}

/**
Expand Down
20 changes: 10 additions & 10 deletions src/main/java/duke/TaskList.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,56 +22,56 @@ public TaskList(ArrayList<Task> tasks, Storage storage) {
/**
* Lists all tasks in the task list.
*/
public void listTasks() {
Ui.listTasks(this.tasks);
public String listTasks() {
return Ui.listTasks(this.tasks);
}

/**
* Adds one task to the task list.
*
* @param t The task to be added.
*/
public void addTask(Task t) {
public String addTask(Task t) {
this.tasks.add(t);
Ui.addTask(t, this.tasks);
this.storage.addTheLastTaskToFile(this.tasks);
return Ui.addTask(t, this.tasks);
}

/**
* Deletes the task at the specified position in the task list.
*
* @param num The position starts from 0.
*/
public void deleteTask(int num) {
public String deleteTask(int num) {
Task re = tasks.remove(num);
this.storage.rewriteFile(tasks);
Ui.deleteTask(re, tasks);
return Ui.deleteTask(re, tasks);
}

/**
* Marks the task at the specified position in the task list.
*
* @param num The position starts from 0.
*/
public void markTask(int num) {
public String markTask(int num) {
Task t = tasks.get(num);
t.markAsDone();
Ui.markTask(t);
this.storage.rewriteFile(tasks);
return Ui.markTask(t);
}

/**
* Searches all Tasks with the keyword.
*
* @param keyword The keyword.
*/
public void findTask(String keyword) {
public String findTask(String keyword) {
ArrayList<Task> result = new ArrayList<>();
for (int i = 0; i < this.tasks.size(); i++) {
if (tasks.get(i).toString().contains(keyword)) {
result.add(tasks.get(i));
}
}
Ui.listMatchingTasks(result);
return Ui.listMatchingTasks(result);
}
}
Loading

0 comments on commit 9ff4bbc

Please sign in to comment.