Skip to content

Commit

Permalink
Merge pull request nus-cs2103-AY2425S1#89 from weijianwong/branch-arr…
Browse files Browse the repository at this point in the history
…ow-keys

Up arrow goes to previous command
  • Loading branch information
curtischang2510 authored Oct 22, 2024
2 parents b73cba5 + 58b7137 commit a18b5d9
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 0 deletions.
58 changes: 58 additions & 0 deletions src/main/java/tuteez/logic/commands/CommandHistory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package tuteez.logic.commands;

import java.util.ArrayList;
import java.util.List;

/**
* Represents the history of commands entered by the user.
*/
public class CommandHistory {
private final List<String> history;
private int currentIndex;

/**
* Creates a CommandHistory class to remember the history of commands the user entered.
* The current index is initially set to -1 to indicate that no commands have been accessed
* or selected from the history yet.
*/
public CommandHistory() {
this.history = new ArrayList<>();
this.currentIndex = -1;
}

/**
* Adds a new command to the history and resets the current index.
*/
public void add(String command) {
assert command != null;
history.add(command);
// Reset index to the end of the list.
currentIndex = history.size();
}

/**
* Returns the previous command in history, or null if no previous command exists.
*/
public String getPreviousCommand() {
assert currentIndex >= -1 && currentIndex <= history.size();
if (currentIndex > 0) {
currentIndex--;
return history.get(currentIndex);
}
return null; // No previous command available
}

/**
* Returns the next command in history, or an empty string if no next command exists.
*/
public String getNextCommand() {
assert currentIndex >= -1 && currentIndex <= history.size();
if (currentIndex < history.size() - 1) {
currentIndex++;
return history.get(currentIndex);
}
currentIndex = history.size();
return "";
}

}
36 changes: 36 additions & 0 deletions src/main/java/tuteez/ui/CommandBox.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;
import tuteez.logic.commands.CommandHistory;
import tuteez.logic.commands.CommandResult;
import tuteez.logic.commands.exceptions.CommandException;
import tuteez.logic.parser.exceptions.ParseException;
Expand All @@ -18,6 +21,8 @@ public class CommandBox extends UiPart<Region> {

private final CommandExecutor commandExecutor;

private final CommandHistory commandHistory;

@FXML
private TextField commandTextField;

Expand All @@ -27,8 +32,11 @@ public class CommandBox extends UiPart<Region> {
public CommandBox(CommandExecutor commandExecutor) {
super(FXML);
this.commandExecutor = commandExecutor;
this.commandHistory = new CommandHistory();
// calls #setStyleToDefault() whenever there is a change to the text of the command box.
commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault());

commandTextField.addEventFilter(KeyEvent.KEY_PRESSED, this::handleKeyPressed);
}

/**
Expand All @@ -43,12 +51,40 @@ private void handleCommandEntered() {

try {
commandExecutor.execute(commandText);
commandHistory.add(commandText);
commandTextField.setText("");
} catch (CommandException | ParseException e) {
setStyleToIndicateCommandFailure();
}
}

/**
* Handles key press events for navigating the command history.
*/
private void handleKeyPressed(KeyEvent event) {
if (event.getCode() == KeyCode.UP || event.getCode() == KeyCode.DOWN) {
String command = (event.getCode() == KeyCode.UP)
? commandHistory.getPreviousCommand()
: commandHistory.getNextCommand();

if (command != null) {
updateCommandField(command);
}

event.consume();
}
}

/**
* Updates the command text field and sets the caret position to the end.
*
* @param command The command to set in the text field.
*/
private void updateCommandField(String command) {
commandTextField.setText(command);
commandTextField.positionCaret(command.length());
}

/**
* Sets the command box style to use the default style.
*/
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/tuteez/ui/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextInputControl;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import tuteez.commons.core.GuiSettings;
Expand Down Expand Up @@ -38,6 +40,9 @@ public class MainWindow extends UiPart<Stage> {
@FXML
private StackPane commandBoxPlaceholder;

@FXML
private MenuBar menuBar;

@FXML
private MenuItem helpMenuItem;

Expand Down Expand Up @@ -66,6 +71,8 @@ public MainWindow(Stage primaryStage, Logic logic) {
setAccelerators();

helpWindow = new HelpWindow();

setupMenuBarListener();
}

public Stage getPrimaryStage() {
Expand Down Expand Up @@ -135,6 +142,22 @@ private void setWindowDefaultSize(GuiSettings guiSettings) {
}
}

/**
* Sets up the event listeners for the menu bar.
*/
private void setupMenuBarListener() {
menuBar.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> handleMenuBarClick());
}

/**
* Handles the click event for the menu bar to transfer focus away from command box to resultDisplay.
* This method is necessary to ensure that when the up and down arrow keys are recorded,
* the menu bar does not interfere with the command box's input.
*/
private void handleMenuBarClick() {
resultDisplayPlaceholder.requestFocus();
}

/**
* Opens the help window or focuses on it if it's already opened.
*/
Expand Down
89 changes: 89 additions & 0 deletions src/test/java/tuteez/logic/commands/CommandHistoryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package tuteez.logic.commands;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.lang.reflect.Field;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class CommandHistoryTest {
private CommandHistory commandHistory;

@BeforeEach
public void setUp() {
commandHistory = new CommandHistory();
}

@Test
public void add_nullCommand_throwsException() {
// Check that adding a null command throws an exception
assertThrows(AssertionError.class, () -> commandHistory.add(null));
}

@Test
public void add_correctlyAddsCommands() {
commandHistory.add("command1");
commandHistory.add("command2");

// Check if the commands have been added correctly
assertEquals("command2", commandHistory.getPreviousCommand());
assertEquals("command1", commandHistory.getPreviousCommand());

// Ensure currentIndex is reset to the end of the list after adding commands
commandHistory.add("command3");
assertEquals("command3", commandHistory.getPreviousCommand()); // Should return command3
}
@Test
public void getPreviousCommand_correctlyGetsPreviousCommands() {
commandHistory.add("command1");
commandHistory.add("command2");
commandHistory.add("command3");

// Check if the last command can be retrieved
assertEquals("command3", commandHistory.getPreviousCommand());
assertEquals("command2", commandHistory.getPreviousCommand());
assertEquals("command1", commandHistory.getPreviousCommand());
assertNull(commandHistory.getPreviousCommand());
}

@Test
public void getNextCommand_correctlyGetsNextCommands() {
commandHistory.add("command1");
commandHistory.add("command2");
assertEquals("command2", commandHistory.getPreviousCommand());
assertEquals("command1", commandHistory.getPreviousCommand());
assertEquals("command2", commandHistory.getNextCommand());
assertEquals("", commandHistory.getNextCommand()); // No more next commands
}

@Test
public void getNextCommand_invalidCurrentIndex_throwsAssertionError() throws Exception {
commandHistory.add("command1");
commandHistory.add("command2");

// Use reflection to set currentIndex to an invalid value (e.g., larger than history.size())
Field field = CommandHistory.class.getDeclaredField("currentIndex");
field.setAccessible(true);
field.set(commandHistory, 999); // Invalid index

// Expect an AssertionError when calling getNextCommand()
assertThrows(AssertionError.class, () -> commandHistory.getNextCommand());
}

@Test
public void getPreviousCommand_invalidCurrentIndex_throwsAssertionError() throws Exception {
commandHistory.add("command1");
commandHistory.add("command2");

// Use reflection to set currentIndex to an invalid value (e.g., negative beyond -1)
Field field = CommandHistory.class.getDeclaredField("currentIndex");
field.setAccessible(true);
field.set(commandHistory, -999); // Invalid index

// Expect an AssertionError when calling getPreviousCommand()
assertThrows(AssertionError.class, () -> commandHistory.getPreviousCommand());
}

}

0 comments on commit a18b5d9

Please sign in to comment.