diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 82e10abd195..5b49e8900b0 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -403,6 +403,49 @@ This method of implementation closely follows the _Observer Pattern_, which prom **Alternative 2** was chosen as it was neater to implement and performs statistic calculations only when absolutely necessary, this preventing unnecessary wastage of computational resources. + +### \[Implemented\] Category Autocomplete feature + +The category autocompletion feature in FastTrack is implemented using two `UI` classes, namely the `SuggestionListPanel` and the `CommandBox`. + +The `SuggestionListPanel` is a JavaFX `UI` component responsible for displaying the suggestion list for the category autocomplete feature. It receives two parameters in its constructor, namely an `ObservableList` and a `CommandBox`. +The `ObservableList` contains all available categories in FastTrack, and the `CommandBox` is the `UI` component that contains a text field which allows users to enter commands, as well as for setting text to autofill. + +To filter the categories based on the user's input, the `SuggestionListPanel` makes use of a `FilteredList`. This filtered list is used as the data source for the `ListView` `UI` component of the `SuggestionListPanel`. +The `FilteredList` displays filtered categories based on whether the category name matches the user's input. + +Upon initialization, the `SuggestionListPanel` sets up autocomplete handlers for the suggestion list by calling the `SuggestionListPanel#initialiseAutocompleteHandlers()` method. This method registers listeners for focus changes, key presses, and autocomplete filters, which work together to ensure the autocomplete feature works responsively. + +When the user enters a command, they can trigger the autocomplete feature by typing `c/` followed by a few characters. These characters can then be used to filter the categories, and the most likely suggestions are displayed in the suggestion list. +To load the filter for autocompletion, the `SuggestionListPanel#loadAutocompleteFilter()` method within `SuggestionListPanel#initialiseAutocompleteHandlers()` sets up a listener for changes in the text field of the `CommandBox`. +If the user types `c/` to start entering a category name, the `SuggestionListPanel#getAutocompleteSuggestions()` method is called to retrieve the most likely suggestions based on the user's input so far. +This method filters the categories based on the user's input and updates the filtered categories as the items in the `ListView` `UI` component of the `SuggestionListPanel`. + +When the suggestion list is visible, the user can navigate through the suggestions using the `UP` and `DOWN` arrow keys. + +The `CommandBox#initialiseAutocompleteHandler()` method adds a key press event filter to the command text field that listens for the `UP` arrow key. + +When the user presses the `UP` arrow key, the focus is given to the `SuggestionListPanel` if it is visible (i.e. the user had typed in `c/`), and the `SuggestionListPanel#selectLastItemOnFocus()` method selects the last item in the suggestion list by default. When the user presses the `DOWN` arrow key, the `SuggestionListPanel#handleDownArrowKey()` method is called to check if the user is about to navigate out of the suggestion list and is responsible for returning the focus back to the `CommandBox`. + +To select a category from the suggestion list, the user can either navigate through the list using the arrow keys and press the `ENTER` key or press the `TAB` key to select the bottom-most suggestion in the list. +When the user makes a selection using the `ENTER` key, a callback function within the `SuggestionListPanel#addKeyPressListener()` method is called. This function updates the suggested text in the `CommandBox` by calling the `SuggestionListPanel#updateSuggestedText()` method, which sets the text in the `CommandBox` and subsequently hides the suggestion list. + +If the user makes the selection by pressing the `TAB` key, the `CommandBox#initialiseAutocompleteHandler()` method simulates a `UP` arrow key press followed by an `ENTER` key press, which also causes the first suggestion in the list to be selected. + + +#### Design considerations: + +**Aspect: How the autocomplete feature should be implemented**: + +* **Alternative 1 (Current choice):** Add the autocomplete logic within the `SuggestionListPanel` and `CommandBox` classes itself + * Pros: This design is more convenient and allows the methods within each class to focus specifically on their own behaviors, without explicit knowledge of the autocomplete feature. + * Cons: Currently, the `SuggestionListPanel` is tightly coupled to the `CommandBox` through its constructor parameters. This means that more modifications would need to be made if a new text input component was introduced. + +* **Alternative 2 :** Create a new `AutocompleteLogic` class which uses the _Observer Pattern_ to listen to and propagate changes across the `SuggestionListPanel` and `CommandBox` components. + * Pros: Enforces loose coupling by the _Observer Pattern_ + * Cons: Tedious to implement, and the added flexibility might be unnecessary since the autocomplete feature is not likely to be further extended upon. + + ### \[Proposed\] Undo/redo feature #### Proposed Implementation @@ -483,6 +526,7 @@ The following activity diagram summarizes what happens when a user executes a ne _{more aspects and alternatives to be added}_ + ### \[Proposed\] Data archiving _{Explain here how the data archiving feature will be implemented}_ diff --git a/docs/diagrams/activity_diagrams/addRecurringExpenseActivityDiagram.puml b/docs/diagrams/activity_diagrams/addRecurringExpenseActivityDiagram.puml new file mode 100644 index 00000000000..7f72164fbb3 --- /dev/null +++ b/docs/diagrams/activity_diagrams/addRecurringExpenseActivityDiagram.puml @@ -0,0 +1,31 @@ +@startuml +start +:User executes "addrec" command; +:"addrec" command is parsed; + +if () then ([arguments present & in valid format]) + if () then ([end date later than start date]) + :RecurringExpenseManager object created; + :AddRecurringExpenseCommand object created and executed; + if () then ([recurring expense is unique) + :Search category list for instance matching category name; + if () then ([matching instance found]) + :Link matching category to RecurringExpenseManager; + else ([else]) + :Add category to category list; + endif; + :Add RecurringExpenseManager object to recurring expense list; + :Generate new expenses from recurring expense; + :Display success message; + else ([else]) + :Error message displayed specifying duplicate recurring expense; + endif + else ([else]) + :Error message displayed specifying end date was earlier than start date; + endif + +else ([else]) + :Error message displayed specifying an error in the command format; +endif +stop +@enduml diff --git a/docs/diagrams/activity_diagrams/deleteRecurringExpenseActivityDiagram.puml b/docs/diagrams/activity_diagrams/deleteRecurringExpenseActivityDiagram.puml new file mode 100644 index 00000000000..603de8d0947 --- /dev/null +++ b/docs/diagrams/activity_diagrams/deleteRecurringExpenseActivityDiagram.puml @@ -0,0 +1,18 @@ +@startuml +start +:User executes "delrec" command; +:"delrec" command is parsed; + +if () then ([index is present]) + if () then ([index is within range]) + :Get RecurringExpenseManager instance to edit; + :Delete RecurringExpenseManager from recurring expense list; + :Display success message; + else ([else]) + :Error message displayed specifying index out of range; + endif; +else ([else]) + :Error message displayed specifying an error in the command format; +endif +stop +@enduml diff --git a/docs/diagrams/activity_diagrams/editRecurringExpenseActivityDiagram.puml b/docs/diagrams/activity_diagrams/editRecurringExpenseActivityDiagram.puml new file mode 100644 index 00000000000..1653cc6dd45 --- /dev/null +++ b/docs/diagrams/activity_diagrams/editRecurringExpenseActivityDiagram.puml @@ -0,0 +1,24 @@ +@startuml +start +:User executes "edrec" command; +:"edrec" command is parsed; + +if () then ([at least one argument is present]) + if () then ([arguments in valid format and index within range]) + :Get RecurringExpenseManager instance to edit; + :Search category list for instance matching category name; + if () then ([matching instance found]) + :Link matching category to RecurringExpenseManager; + :Update necessary attributes in RecurringExpenseManager; + :Display success message; + else ([else]) + :Error message displayed specifying nonexistent category; + endif; + else ([else]) + :Error message displayed specifying an error in the command format or index out of range; + endif +else ([else]) + :Error message displayed specifying no attributes given to edit; +endif +stop +@enduml diff --git a/docs/images/activity_diagrams/addRecurringExpenseActivityDiagram.png b/docs/images/activity_diagrams/addRecurringExpenseActivityDiagram.png new file mode 100644 index 00000000000..b3ccdb00438 Binary files /dev/null and b/docs/images/activity_diagrams/addRecurringExpenseActivityDiagram.png differ diff --git a/docs/images/activity_diagrams/deleteRecurringExpenseActivityDiagram.png b/docs/images/activity_diagrams/deleteRecurringExpenseActivityDiagram.png new file mode 100644 index 00000000000..b66c5b1537b Binary files /dev/null and b/docs/images/activity_diagrams/deleteRecurringExpenseActivityDiagram.png differ diff --git a/docs/images/activity_diagrams/editRecurringExpenseActivityDiagram.png b/docs/images/activity_diagrams/editRecurringExpenseActivityDiagram.png new file mode 100644 index 00000000000..8265b1262b4 Binary files /dev/null and b/docs/images/activity_diagrams/editRecurringExpenseActivityDiagram.png differ