Skip to content

Commit

Permalink
Add JavaFX
Browse files Browse the repository at this point in the history
  • Loading branch information
Wong-ZZ committed Sep 3, 2020
1 parent 9853ede commit e4a9ed8
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 25 deletions.
14 changes: 14 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ repositories {
}

dependencies {
String javaFxVersion = '11'

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'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0'
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0'
}
Expand Down
3 changes: 1 addition & 2 deletions duke-test/ACTUAL.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

____________________________________________________________
Hello! I'm Duke
Hello! I'm Duke.
What can I do for you?
____________________________________________________________
____________________________________________________________
Expand Down
3 changes: 1 addition & 2 deletions duke-test/EXPECTED.TXT
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

____________________________________________________________
Hello! I'm Duke
Hello! I'm Duke.
What can I do for you?
____________________________________________________________
____________________________________________________________
Expand Down
84 changes: 84 additions & 0 deletions src/main/java/duke/DialogBox.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package duke;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

public class DialogBox extends HBox {

private Label text;
private ImageView displayPicture;

/**
* Creates and initialises a DialogBox object with preset styles.
*
* @param text Label a Label Node with the relevant dialog message.
* @param displayPicture ImageView the display picture of the user.
*/
public DialogBox(Label text, ImageView displayPicture) {
this.text = text;
this.displayPicture = displayPicture;
setDialogBoxStyle();
}

private void setDialogBoxStyle() {
text.setWrapText(true);
text.setPadding(new Insets(10, 10, 10, 10));
text.setTextFill(Color.web("#AAAAAA"));
double height = 100.0;
double width = 100.0;
displayPicture.setFitWidth(width);
displayPicture.setFitHeight(height);
this.setPadding(new Insets(10, 10, 10, 10));
Circle clip = new Circle(width / 2, height / 2, height / 2);
displayPicture.setClip(clip);

this.setBackground(new Background(new BackgroundFill(Color.web("#424242"), CornerRadii.EMPTY, Insets.EMPTY)));
this.setAlignment(Pos.TOP_RIGHT);
this.getChildren().addAll(text, displayPicture);
}

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

/**
* Creates a dialog box to display the user's input.
*
* @param l Label a Label node with message of the user.
* @param iv ImageView image of user.
* @return DialogBox a dialog box of the user's input.
*/
public static DialogBox getUserDialog(Label l, ImageView iv) {
return new DialogBox(l, iv);
}

/**
* Creates a dialog box to display duke's response.
*
* @param l Label a Label node with the response from Duke.
* @param iv ImageView image of Duke.
* @return DialogBox a dialog box of the Duke's response.
*/
public static DialogBox getDukeDialog(Label l, ImageView iv) {
DialogBox db = new DialogBox(l, iv);
db.flip();
return db;
}
}
167 changes: 154 additions & 13 deletions src/main/java/duke/Duke.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,75 @@
import duke.storage.Storage;
import duke.task.TaskArrayList;
import duke.ui.Ui;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

// Main class that initializes the program.
public class Duke {
public class Duke extends Application {
private static final String START_MSG = "Hello! I'm Duke.\nWhat can I do for you?";
private final Scanner sc = new Scanner(System.in);
private final CommandExecutor exe = new DukeCommandExecutor();
private final Ui ui = new Ui();
private final Path savePath;
private final Storage storage;
private final TaskArrayList taskList;

// JavaFX related fields
private AnchorPane mainLayout;
private ScrollPane scrollPane;
private VBox dialogContainer;
private TextField userInput;
private Button sendButton;
private Scene scene;
private final Image user = new Image(this.getClass().getResourceAsStream("/images/User.jpg"));
private final Image duke = new Image(this.getClass().getResourceAsStream("/images/Duke.jpg"));

/**
* Takes in a path which decides where the save file is stored and initializes the program.
* Initialises the Duke chatbot with a default save path.
*
* @param filePath Path path to save file.
* @throws IOException if an error occurs while performing IO operations.
* @throws IOException If an IO error occurs while loading the save file.
*/
public Duke(Path filePath) throws IOException {
this.storage = new DukeStorage(filePath);
public Duke() throws IOException {
this.savePath = Paths.get("data", "duke.txt");
this.storage = new DukeStorage(savePath);
this.taskList = new TaskArrayList(storage);
}

private void handleStart() {
ui.print("Hello! I'm Duke\nWhat can I do for you?");
/**
* Initialises the Duke chatbot with the given save path.
*
* @throws IOException If an IO error occurs while loading the save file.
*/
public Duke(Path savePath) throws IOException {
this.savePath = savePath;
this.storage = new DukeStorage(savePath);
this.taskList = new TaskArrayList(storage);
}

/**
* Starts the program by calling the relevant initialization processes then starts taking in user inputs.
* Starts the program on the cli.
*/
public void run() throws InvalidSaveFileException {
try {
initialise();
loadSave();
} catch (DukeException e) {
e.printStackTrace();
throw new InvalidSaveFileException("An error has occurred while loading the save file!.");
throw new InvalidSaveFileException("An error has occurred while loading the save file!");
}

ui.print(START_MSG);
while (true) {
String input = sc.nextLine().trim();
try {
Expand All @@ -63,8 +97,115 @@ public void run() throws InvalidSaveFileException {
}
}

private void initialise() throws DukeException {
handleStart();
@Override
public void start(Stage stage) throws InvalidSaveFileException {
try {
loadSave();
} catch (DukeException e) {
e.printStackTrace();
throw new InvalidSaveFileException("An error has occurred while loading the save file!");
}

createNodes(stage);
initialiseNodesStyles(stage);
initialiseNodesEvents(stage);
generateStartMsgDialog();
}

private void createNodes(Stage stage) {
scrollPane = new ScrollPane();
dialogContainer = new VBox();
scrollPane.setContent(dialogContainer);

userInput = new TextField();
sendButton = new Button("Send");

mainLayout = new AnchorPane();
mainLayout.getChildren().addAll(scrollPane, userInput, sendButton);

scene = new Scene(mainLayout);

stage.setScene(scene);
stage.show();
}

private void initialiseNodesStyles(Stage stage) {
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);
scrollPane.setStyle("-fx-background: #212121;");

dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE);
dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0));
dialogContainer.setSpacing(20);

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);
}

private void initialiseNodesEvents(Stage stage) {
sendButton.setOnMouseClicked((event) -> handleUserInput(stage));

userInput.setOnAction((event) -> handleUserInput(stage));
}

private void generateStartMsgDialog() {
Label startMsg = new Label(getResponse(START_MSG));
dialogContainer.getChildren().addAll(DialogBox.getDukeDialog(startMsg, new ImageView(duke)));
}

private void handleUserInput(Stage stage) {
String input = userInput.getText();
if (input.equals("")) {
return;
}
String response;
try {
response = exe.execute(input, taskList);
} catch (DukeException e) {
response = e.getMessage();
}

Label userText = new Label(input);
Label dukeText = new Label(getResponse(response));
dialogContainer.getChildren().addAll(
DialogBox.getUserDialog(userText, new ImageView(user)),
DialogBox.getDukeDialog(dukeText, new ImageView(duke))
);
userInput.clear();

if (exe.shouldExit()) {
userInput.setDisable(true);
PauseTransition delay = new PauseTransition(Duration.seconds(1));
delay.setOnFinished(event -> stage.close());
delay.play();
}
}

private String getResponse(String input) {
return "Duke:\n" + input;
}

private void loadSave() throws DukeException {
List<String> loadedLines = storage.getSavedLines();
for (String line: loadedLines) {
exe.loadSaveString(line, taskList);
Expand Down
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(Duke.class);
}
}
2 changes: 1 addition & 1 deletion src/main/java/duke/command/DoneCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static String execute(String in, TaskList taskList) throws InvalidCommand
taskList.update(index);
return "Nice! I've marked this task as done\n " + task.toString();
} catch (NumberFormatException | IndexOutOfBoundsException e) {
throw new InvalidCommandException("Please input a valid index");
throw new InvalidCommandException("Please input a valid index.");
}
}
}
11 changes: 6 additions & 5 deletions src/main/java/duke/storage/DukeStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,19 @@ public class DukeStorage implements Storage {
public DukeStorage(Path filePath) throws IOException {
this.filePath = filePath;
try {
createIfNotExist();
loadSaveFile();
createSaveFile();
loadData();
} catch (IOException e) {
throw new IOException("An error has occurred when trying to create the save file!");
}
}

private void createIfNotExist() throws IOException {
private void createSaveFile() throws IOException {
int len = filePath.getNameCount();
Path directoriesToCreate = filePath.subpath(0, len - 1);
Files.createDirectories(directoriesToCreate);
if (!java.nio.file.Files.exists(filePath)) {
boolean saveFileExists = java.nio.file.Files.exists(filePath);
if (!saveFileExists) {
new File(filePath.toString()).createNewFile();
}
}
Expand Down Expand Up @@ -103,7 +104,7 @@ public ArrayList<String> getSavedLines() {
return saveLines;
}

private String[] loadSaveFile() {
private String[] loadData() {
// Prevent saving while loading
isActive = false;
String[] result = new String[0];
Expand Down
Binary file added src/main/resources/images/Duke.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/main/resources/images/User.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions src/test/java/duke/DukeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import duke.exception.InvalidSaveFileException;

public class DukeTest {
private static final Path TEST_FILE_PATH = Paths.get("test", "data", "duke.txt");
private static final Path TEST_FILE_PATH = Paths.get("temp-data", "duke.txt");
private static final ByteArrayOutputStream OUT_CONTENT = new ByteArrayOutputStream();
private static final PrintStream ORIGINAL_OUT = System.out;
private static final InputStream SYSIN_BACKUP = System.in;
Expand Down Expand Up @@ -51,7 +51,6 @@ private static void clearPath() {
static void setUpStreams() throws IOException {
clearPath();
System.setOut(new PrintStream(OUT_CONTENT));
System.out.println();
BufferedReader reader = new BufferedReader(new FileReader(Paths.get("duke-test", "input.txt").toString()));
String[] input = reader.lines().toArray(String[]::new);
reader.close();
Expand Down

0 comments on commit e4a9ed8

Please sign in to comment.