From 805c8fd6eadb0fb5e0b0a1771a1c641d49a587ea Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Dec 2025 13:32:32 +0100 Subject: [PATCH 01/15] feat: refactor the element tree to make inline recipe name change possible --- .../main/resources/client/scenes/FoodpalApplication.fxml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/src/main/resources/client/scenes/FoodpalApplication.fxml b/client/src/main/resources/client/scenes/FoodpalApplication.fxml index 57ad5f9..d1bfe0e 100644 --- a/client/src/main/resources/client/scenes/FoodpalApplication.fxml +++ b/client/src/main/resources/client/scenes/FoodpalApplication.fxml @@ -101,17 +101,16 @@
- + - + + +
From 874753790143877c3201c85dbaf4d94077de666d Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Dec 2025 17:46:52 +0100 Subject: [PATCH 06/15] chore: purge dead weight code --- client/src/main/java/client/Main.java | 9 +- client/src/main/java/client/MyModule.java | 2 - .../java/client/scenes/AddIngredientCtrl.java | 115 ------------------ .../main/java/client/scenes/AddNameCtrl.java | 112 ----------------- .../main/java/client/scenes/AddStepsCtrl.java | 114 ----------------- .../client/scenes/FoodpalApplicationCtrl.java | 48 -------- .../src/main/java/client/scenes/MainCtrl.java | 72 +---------- 7 files changed, 3 insertions(+), 469 deletions(-) delete mode 100644 client/src/main/java/client/scenes/AddIngredientCtrl.java delete mode 100644 client/src/main/java/client/scenes/AddNameCtrl.java delete mode 100644 client/src/main/java/client/scenes/AddStepsCtrl.java diff --git a/client/src/main/java/client/Main.java b/client/src/main/java/client/Main.java index e2f58d6..55d7fa7 100644 --- a/client/src/main/java/client/Main.java +++ b/client/src/main/java/client/Main.java @@ -17,10 +17,7 @@ package client; import static com.google.inject.Guice.createInjector; -import client.scenes.AddStepsCtrl; import client.scenes.MainCtrl; -import client.scenes.AddIngredientCtrl; -import client.scenes.AddNameCtrl; import client.scenes.FoodpalApplicationCtrl; import client.utils.ServerUtils; import com.google.inject.Injector; @@ -46,13 +43,9 @@ public class Main extends Application { System.err.println(msg); return; } - - var addName = FXML.load(AddNameCtrl.class, "client", "scenes", "AddName.fxml"); var foodpal = FXML.load(FoodpalApplicationCtrl.class, "client", "scenes", "FoodpalApplication.fxml"); - var addIngredient = FXML.load(AddIngredientCtrl.class, "client", "scenes", "AddIngredient.fxml"); - var addStep = FXML.load(AddStepsCtrl.class, "client", "scenes", "AddSteps.fxml"); var mainCtrl = INJECTOR.getInstance(MainCtrl.class); - mainCtrl.setup(primaryStage, addName, foodpal, addIngredient, addStep); + mainCtrl.setup(primaryStage, foodpal); } } \ No newline at end of file diff --git a/client/src/main/java/client/MyModule.java b/client/src/main/java/client/MyModule.java index a5dd197..4f3464e 100644 --- a/client/src/main/java/client/MyModule.java +++ b/client/src/main/java/client/MyModule.java @@ -23,7 +23,6 @@ import com.google.inject.Binder; import com.google.inject.Module; import com.google.inject.Scopes; -import client.scenes.AddNameCtrl; import client.scenes.MainCtrl; public class MyModule implements Module { @@ -31,7 +30,6 @@ public class MyModule implements Module { @Override public void configure(Binder binder) { binder.bind(MainCtrl.class).in(Scopes.SINGLETON); - binder.bind(AddNameCtrl.class).in(Scopes.SINGLETON); binder.bind(FoodpalApplicationCtrl.class).in(Scopes.SINGLETON); binder.bind(IngredientListCtrl.class).in(Scopes.SINGLETON); binder.bind(RecipeStepListCtrl.class).in(Scopes.SINGLETON); diff --git a/client/src/main/java/client/scenes/AddIngredientCtrl.java b/client/src/main/java/client/scenes/AddIngredientCtrl.java deleted file mode 100644 index 70d6924..0000000 --- a/client/src/main/java/client/scenes/AddIngredientCtrl.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2021 Delft University of Technology - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package client.scenes; - -import client.utils.LocaleAware; -import client.utils.LocaleManager; -import client.utils.ServerUtils; -import com.google.inject.Inject; -import commons.Recipe; -import jakarta.ws.rs.WebApplicationException; -import javafx.fxml.FXML; -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; -import javafx.stage.Modality; - -import java.io.IOException; - -public class AddIngredientCtrl implements LocaleAware { - - private final ServerUtils server; - private final MainCtrl mainCtrl; - private final FoodpalApplicationCtrl foodpalCtrl; - - private final LocaleManager localeManager; - - @FXML - public TextField ingredient; - - @FXML - public Button okButton; - - @FXML - public Button cancelButton; - - @FXML - public Label ingredientLabel; - - @Inject - public AddIngredientCtrl(ServerUtils server, MainCtrl mainCtrl, - FoodpalApplicationCtrl foodpalCtrl, LocaleManager localeManager) { - this.mainCtrl = mainCtrl; - this.server = server; - this.foodpalCtrl = foodpalCtrl; - this.localeManager = localeManager; - } - - @Override - public void updateText() { - ingredientLabel.setText(getLocaleString("add.ingredient.label")); - okButton.setText(getLocaleString("button.ok")); - cancelButton.setText(getLocaleString("button.cancel")); - } - - @Override - public LocaleManager getLocaleManager() { - return localeManager; - } - - public void cancel() throws IOException, InterruptedException { - clearFields(); - mainCtrl.showFoodpal(); - } - - public void ok() throws IOException, InterruptedException { - try { - Recipe selected = foodpalCtrl.getSelectedRecipe(); - server.addRecipeIngredient(selected, ingredient.getText()); - } catch (WebApplicationException e) { - - var alert = new Alert(Alert.AlertType.ERROR); - alert.initModality(Modality.APPLICATION_MODAL); - alert.setContentText(e.getMessage()); - alert.showAndWait(); - return; - } - - - clearFields(); - mainCtrl.showFoodpal(); - } - - private void clearFields() { - ingredient.clear(); - } - - public void keyPressed(KeyEvent e) throws IOException, InterruptedException { - switch (e.getCode()) { - case ENTER: - ok(); - break; - case ESCAPE: - cancel(); - break; - default: - break; - } - } - -} \ No newline at end of file diff --git a/client/src/main/java/client/scenes/AddNameCtrl.java b/client/src/main/java/client/scenes/AddNameCtrl.java deleted file mode 100644 index 73f86e6..0000000 --- a/client/src/main/java/client/scenes/AddNameCtrl.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2021 Delft University of Technology - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package client.scenes; - -import client.utils.LocaleAware; -import client.utils.LocaleManager; -import client.utils.ServerUtils; -import com.google.inject.Inject; - -import jakarta.ws.rs.WebApplicationException; -import javafx.fxml.FXML; -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; -import javafx.stage.Modality; - -import java.io.IOException; - -public class AddNameCtrl implements LocaleAware { - - private final ServerUtils server; - private final MainCtrl mainCtrl; - - private final LocaleManager localeManager; - - @FXML - public TextField recipeName; - - @FXML - public Label recipeNameLabel; - - @FXML - public Button cancelButton; - - @FXML - public Button okButton; - - @Inject - public AddNameCtrl(ServerUtils server, MainCtrl mainCtrl, LocaleManager localeManager) { - this.mainCtrl = mainCtrl; - this.server = server; - this.localeManager = localeManager; - } - - @Override - public void updateText() { - recipeNameLabel.setText(getLocaleString("add.recipe.label")); - okButton.setText(getLocaleString("button.ok")); - cancelButton.setText(getLocaleString("button.cancel")); - } - - @Override - public LocaleManager getLocaleManager() { - return localeManager; - } - - public void cancel() throws IOException, InterruptedException { - clearFields(); - mainCtrl.showFoodpal(); - } - - public void ok() throws IOException, InterruptedException { - try { - server.addRecipeName(recipeName.getText()); - } catch (WebApplicationException e) { - - var alert = new Alert(Alert.AlertType.ERROR); - alert.initModality(Modality.APPLICATION_MODAL); - alert.setContentText(e.getMessage()); - alert.showAndWait(); - return; - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - - clearFields(); - mainCtrl.showFoodpal(); - } - - private void clearFields() { - recipeName.clear(); - } - - public void keyPressed(KeyEvent e) throws IOException, InterruptedException { - switch (e.getCode()) { - case ENTER: - ok(); - break; - case ESCAPE: - cancel(); - break; - default: - break; - } - } - -} \ No newline at end of file diff --git a/client/src/main/java/client/scenes/AddStepsCtrl.java b/client/src/main/java/client/scenes/AddStepsCtrl.java deleted file mode 100644 index abd9f51..0000000 --- a/client/src/main/java/client/scenes/AddStepsCtrl.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2021 Delft University of Technology - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package client.scenes; - -import client.utils.LocaleAware; -import client.utils.LocaleManager; -import client.utils.ServerUtils; -import com.google.inject.Inject; -import commons.Recipe; -import jakarta.ws.rs.WebApplicationException; -import javafx.fxml.FXML; -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; -import javafx.stage.Modality; - -import java.io.IOException; - -public class AddStepsCtrl implements LocaleAware { - - private final ServerUtils server; - private final MainCtrl mainCtrl; - private final FoodpalApplicationCtrl foodpalCtrl; - private final LocaleManager localeManager; - - @FXML - public TextField preparationStep; - - @FXML - public Button okButton; - - @FXML - public Button cancelButton; - - @FXML - public Label preparationStepLabel; - - @Inject - public AddStepsCtrl(ServerUtils server, MainCtrl mainCtrl, - FoodpalApplicationCtrl foodpalCtrl, LocaleManager localeManager) { - this.mainCtrl = mainCtrl; - this.server = server; - this.foodpalCtrl = foodpalCtrl; - this.localeManager = localeManager; - } - - @Override - public void updateText() { - preparationStepLabel.setText(getLocaleString("add.step.label")); - okButton.setText(getLocaleString("button.ok")); - cancelButton.setText(getLocaleString("button.cancel")); - } - - @Override - public LocaleManager getLocaleManager() { - return localeManager; - } - - public void cancel() throws IOException, InterruptedException { - clearFields(); - mainCtrl.showFoodpal(); - } - - public void ok() throws IOException, InterruptedException { - try { - Recipe selected = foodpalCtrl.getSelectedRecipe(); - server.addRecipeStep(selected, preparationStep.getText()); - } catch (WebApplicationException e) { - - var alert = new Alert(Alert.AlertType.ERROR); - alert.initModality(Modality.APPLICATION_MODAL); - alert.setContentText(e.getMessage()); - alert.showAndWait(); - return; - } - - - clearFields(); - mainCtrl.showFoodpal(); - } - - private void clearFields() { - preparationStep.clear(); - } - - public void keyPressed(KeyEvent e) throws IOException, InterruptedException { - switch (e.getCode()) { - case ENTER: - ok(); - break; - case ESCAPE: - cancel(); - break; - default: - break; - } - } - -} \ No newline at end of file diff --git a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java index c0aecfc..e50a983 100644 --- a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java +++ b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java @@ -78,24 +78,6 @@ public class FoodpalApplicationCtrl implements LocaleAware { @FXML public Button printRecipeButton; - @FXML - public Label ingredientsLabel; - - @FXML - public Label preparationLabel; - - @FXML - private ListView ingredientsListView; - - @FXML - private Button addIngredientButton; - - @FXML - private ListView preparationListView; - - @FXML - private Button addPreparationStepButton; - @Inject public FoodpalApplicationCtrl( MainCtrl mainCtrl, @@ -311,28 +293,6 @@ public class FoodpalApplicationCtrl implements LocaleAware { editableTitleArea.getChildren().add(edit); } - /** - * You can add ingredients. you get send to a different scene that creates the ingredient - * It doesn't automatically refresh yet so before you can see the new ingredient you have to first click on a - * different recipe and come back after - */ - @FXML - private void addIngredient() { - System.out.println("Add ingredient clicked"); - mainCtrl.showAddIngredient(); - } - - /** - * You can add steps. you get send to a different scene that creates the Step - * It doesn't automatically refresh yet so before you can see the new step you have to first click on a - * different recipe and come back after - */ - @FXML - private void addPreparationStep() { - System.out.println("Add preparation step clicked"); - mainCtrl.showAddSteps(); - - } // Language buttons @FXML @@ -347,14 +307,6 @@ public class FoodpalApplicationCtrl implements LocaleAware { System.out.println("Recipe printed"); } - /** - * Get the recipe that was selected - * @return the selected recipe - */ - public Recipe getSelectedRecipe() { - return recipeList.getSelectionModel().getSelectedItem(); - } - /** * Clones a recipe, when clicking on the button "clone" */ diff --git a/client/src/main/java/client/scenes/MainCtrl.java b/client/src/main/java/client/scenes/MainCtrl.java index 0ad2f49..1f439c9 100644 --- a/client/src/main/java/client/scenes/MainCtrl.java +++ b/client/src/main/java/client/scenes/MainCtrl.java @@ -32,51 +32,23 @@ public class MainCtrl implements LocaleAware { @FXML private Stage primaryStage; - @FXML - private AddNameCtrl addNameCtrl; - private Scene addName; - @FXML private FoodpalApplicationCtrl foodpalCtrl; private Scene foodpal; - @FXML - private AddIngredientCtrl addIngredientCtrl; - private Scene addIngredient; - - @FXML - private AddStepsCtrl addStepsCtrl; - private Scene addStep; - - private String addNameTitle = "add.name.title"; - private String addIngredientTitle = "add.step.title"; - private String addStepTitle = "add.ingredient.title"; - @Inject public MainCtrl(LocaleManager localeManager) { this.localeManager = localeManager; } public void setup(Stage primaryStage, - Pair addName, - Pair foodpal, - Pair addIngredient, - Pair addStep) throws IOException, InterruptedException { + Pair foodpal) throws IOException, InterruptedException { this.primaryStage = primaryStage; - this.addNameCtrl = addName.getKey(); - this.addName = new Scene(addName.getValue()); - this.foodpalCtrl = foodpal.getKey(); this.foodpal = new Scene(foodpal.getValue()); - this.addIngredientCtrl = addIngredient.getKey(); - this.addIngredient = new Scene(addIngredient.getValue()); - - this.addStepsCtrl = addStep.getKey(); - this.addStep = new Scene(addStep.getValue()); - showFoodpal(); primaryStage.show(); @@ -85,9 +57,7 @@ public class MainCtrl implements LocaleAware { @Override public void updateText() { - addNameTitle = getLocaleString("add.recipe.title"); - addStepTitle = getLocaleString("add.step.title"); - addIngredientTitle = getLocaleString("add.ingredient.title"); + // nothing here, no actual text objects managed by MainCtrl } @Override @@ -95,47 +65,9 @@ public class MainCtrl implements LocaleAware { return localeManager; } - public void showAddName() { - primaryStage.setTitle(addNameTitle); - primaryStage.setScene(addName); - addName.setOnKeyPressed(e -> { - try { - addNameCtrl.keyPressed(e); - } catch (IOException | InterruptedException ex) { - throw new RuntimeException(ex); - } - }); - } - public void showFoodpal() { primaryStage.setTitle("FoodPal"); primaryStage.setScene(foodpal); foodpalCtrl.refresh(); } - - public void showAddIngredient() { - primaryStage.setTitle(addIngredientTitle); - primaryStage.setScene(addIngredient); - addIngredient.setOnKeyPressed(e -> { - try { - addIngredientCtrl.keyPressed(e); - } catch (IOException | InterruptedException ex) { - throw new RuntimeException(ex); - } - }); - - } - - public void showAddSteps() { - primaryStage.setTitle(addStepTitle); - primaryStage.setScene(addStep); - addStep.setOnKeyPressed(e -> { - try { - addStepsCtrl.keyPressed(e); - } catch (IOException | InterruptedException ex) { - throw new RuntimeException(ex); - } - }); - - } } \ No newline at end of file From 73324022b00fbf4a39310a758ab33d05b9fcc476 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Dec 2025 18:21:02 +0100 Subject: [PATCH 07/15] chore: cleanup debug sout (oops) --- .../src/main/java/client/scenes/recipe/IngredientListCtrl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java b/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java index a772913..6db8fb5 100644 --- a/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java +++ b/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java @@ -59,7 +59,6 @@ public class IngredientListCtrl implements Initializable { if (recipe == null) { this.ingredients = FXCollections.observableArrayList(new ArrayList<>()); } else { - System.out.println("refetched"); List ingredientList = recipe.getIngredients(); this.ingredients = FXCollections.observableArrayList(ingredientList); } From 30cbfbc45466be6beca90b8b572037402c768152 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Dec 2025 18:22:10 +0100 Subject: [PATCH 08/15] fix: rectify a NPE caused by wrong ListView model Apparently a thing can be selected but not focused. Oops. --- .../client/scenes/FoodpalApplicationCtrl.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java index e50a983..0e80969 100644 --- a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java +++ b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java @@ -92,37 +92,36 @@ public class FoodpalApplicationCtrl implements LocaleAware { this.ingredientListCtrl = ingredientListCtrl; this.stepListCtrl = stepListCtrl; } - @Override public void initializeComponents() { // TODO Reduce code duplication?? // Initialize callback for ingredient list updates this.ingredientListCtrl.setUpdateCallback(newList -> { - Recipe focusedRecipe = recipeList.getFocusModel().getFocusedItem(); - if (focusedRecipe == null) { + Recipe selectedRecipe = recipeList.getSelectionModel().getSelectedItem(); + if (selectedRecipe == null) { // edge case error for NPE. throw new NullPointerException("Null recipe whereas ingredients are edited"); } - focusedRecipe.setIngredients(newList); + selectedRecipe.setIngredients(newList); try { // propagate changes to server - server.updateRecipe(focusedRecipe); + server.updateRecipe(selectedRecipe); } catch (IOException | InterruptedException e) { - throw new UpdateException("Unable to update recipe to server for " + focusedRecipe); + throw new UpdateException("Unable to update recipe to server for " + selectedRecipe); } }); this.stepListCtrl.setUpdateCallback(newList -> { - Recipe focusedRecipe = recipeList.getFocusModel().getFocusedItem(); - if (focusedRecipe == null) { + Recipe selectedRecipe = recipeList.getSelectionModel().getSelectedItem(); + if (selectedRecipe == null) { // edge case error for NPE. throw new NullPointerException("Null recipe whereas ingredients are edited"); } - focusedRecipe.setPreparationSteps(newList); + selectedRecipe.setPreparationSteps(newList); try { // propagate changes to server - server.updateRecipe(focusedRecipe); + server.updateRecipe(selectedRecipe); } catch (IOException | InterruptedException e) { - throw new UpdateException("Unable to update recipe to server for " + focusedRecipe); + throw new UpdateException("Unable to update recipe to server for " + selectedRecipe); } }); // Show recipe name in the list @@ -284,6 +283,7 @@ public class FoodpalApplicationCtrl implements LocaleAware { try { server.updateRecipe(selected); refresh(); + recipeList.getSelectionModel().select(selected); } catch (IOException | InterruptedException e) { // throw a nice blanket UpdateException throw new UpdateException("Error occurred when updating recipe name!"); From ccb72481fd249ba3466fc811c4133e048970d31a Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Dec 2025 18:22:26 +0100 Subject: [PATCH 09/15] feat: resolve TODO autofocus edit box --- client/src/main/java/client/scenes/FoodpalApplicationCtrl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java index 0e80969..9ba7585 100644 --- a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java +++ b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java @@ -291,6 +291,7 @@ public class FoodpalApplicationCtrl implements LocaleAware { showName(edit.getText()); }); editableTitleArea.getChildren().add(edit); + edit.requestFocus(); } From 40d157cc6c8825703ba203c98c2e2e5d750b022a Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Dec 2025 21:09:12 +0100 Subject: [PATCH 10/15] fix: remove LocaleAware constraint from MainCtrl --- .../src/main/java/client/scenes/MainCtrl.java | 25 +------------------ .../test/java/client/scenes/MainCtrlTest.java | 3 +-- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/client/src/main/java/client/scenes/MainCtrl.java b/client/src/main/java/client/scenes/MainCtrl.java index 1f439c9..6499420 100644 --- a/client/src/main/java/client/scenes/MainCtrl.java +++ b/client/src/main/java/client/scenes/MainCtrl.java @@ -15,9 +15,6 @@ */ package client.scenes; -import client.utils.LocaleAware; -import client.utils.LocaleManager; -import jakarta.inject.Inject; import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.Scene; @@ -26,9 +23,7 @@ import javafx.util.Pair; import java.io.IOException; -public class MainCtrl implements LocaleAware { - private final LocaleManager localeManager; - +public class MainCtrl { @FXML private Stage primaryStage; @@ -36,11 +31,6 @@ public class MainCtrl implements LocaleAware { private FoodpalApplicationCtrl foodpalCtrl; private Scene foodpal; - @Inject - public MainCtrl(LocaleManager localeManager) { - this.localeManager = localeManager; - } - public void setup(Stage primaryStage, Pair foodpal) throws IOException, InterruptedException { @@ -51,20 +41,7 @@ public class MainCtrl implements LocaleAware { showFoodpal(); primaryStage.show(); - - initialize(); // Initialize LocaleManager stuff manually since MainCtrl isn't loaded from FXML. } - - @Override - public void updateText() { - // nothing here, no actual text objects managed by MainCtrl - } - - @Override - public LocaleManager getLocaleManager() { - return localeManager; - } - public void showFoodpal() { primaryStage.setTitle("FoodPal"); primaryStage.setScene(foodpal); diff --git a/client/src/test/java/client/scenes/MainCtrlTest.java b/client/src/test/java/client/scenes/MainCtrlTest.java index 63481c6..2709982 100644 --- a/client/src/test/java/client/scenes/MainCtrlTest.java +++ b/client/src/test/java/client/scenes/MainCtrlTest.java @@ -15,7 +15,6 @@ */ package client.scenes; -import client.utils.LocaleManager; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -24,7 +23,7 @@ public class MainCtrlTest { @BeforeEach public void setup() { - sut = new MainCtrl(new LocaleManager()); + sut = new MainCtrl(); } @Test From 5dd6c89f73463a1fddecea41c44fdce22d9c7cd9 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Dec 2025 21:39:55 +0100 Subject: [PATCH 11/15] feat: extract abstract OrderedEditableListCell Abstracts the editable list cell code to make it extendable. Also includes fix for index (now starts at 1) --- .../scenes/recipe/IngredientListCell.java | 68 +-------------- .../scenes/recipe/IngredientListCtrl.java | 2 +- .../recipe/OrderedEditableListCell.java | 78 +++++++++++++++++ .../scenes/recipe/RecipeStepListCell.java | 87 +------------------ 4 files changed, 86 insertions(+), 149 deletions(-) create mode 100644 client/src/main/java/client/scenes/recipe/OrderedEditableListCell.java diff --git a/client/src/main/java/client/scenes/recipe/IngredientListCell.java b/client/src/main/java/client/scenes/recipe/IngredientListCell.java index ac317d8..f420bff 100644 --- a/client/src/main/java/client/scenes/recipe/IngredientListCell.java +++ b/client/src/main/java/client/scenes/recipe/IngredientListCell.java @@ -1,78 +1,18 @@ package client.scenes.recipe; -import javafx.scene.control.ListCell; import javafx.scene.control.TextField; -import javafx.scene.input.KeyCode; /** - * A custom ListCell for displaying and editing ingredients in an + * A custom {@link OrderedEditableListCell} for displaying and editing ingredients in an * IngredientList. Allows inline editing of ingredient names. * * @see IngredientListCtrl */ -public class IngredientListCell extends ListCell { +public class IngredientListCell extends OrderedEditableListCell { // TODO: change all this to use actual Ingredient objects // and all that :) - @Override - protected void updateItem(String item, boolean empty) { - super.updateItem(item, empty); - - if (empty || item == null) { - this.setText(null); - } else { - this.setText(item); - } - } - - @Override - public void startEdit() { - super.startEdit(); - - TextField textField = new TextField(this.getItem()); - - // Commit edit on Enter key press - textField.setOnAction(event -> { - this.commitEdit(textField.getText()); - }); - - // Cancel edit on Escape key press - textField.setOnKeyReleased(event -> { - if (event.getCode() == KeyCode.ESCAPE) { - this.cancelEdit(); - } - }); - - this.setText(null); - this.setGraphic(textField); - } - - /** - * Check if the input is valid (non-empty). - * - * @param input The input string to validate. - * @return true if valid, false otherwise. - */ - private boolean isInputValid(String input) { - return input != null && !input.trim().isEmpty(); - } - - @Override - public void cancelEdit() { - super.cancelEdit(); - - this.setText(this.getItem()); - this.setGraphic(null); - } - - @Override - public void commitEdit(String newValue) { - this.setGraphic(null); - - if (!isInputValid(newValue)) { - newValue = this.getItem(); // Revert to old value if input is invalid - } - - super.commitEdit(newValue); + protected String fromInput(TextField inputField) { + return inputField.getText(); } } diff --git a/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java b/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java index 6db8fb5..aa33ed2 100644 --- a/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java +++ b/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java @@ -137,7 +137,7 @@ public class IngredientListCtrl implements Initializable { this.ingredientListView.setEditable(true); this.ingredientListView.setCellFactory( list -> { - return new RecipeStepListCell(); + return new IngredientListCell(); }); this.ingredientListView.setOnEditCommit(this::handleIngredientEdit); diff --git a/client/src/main/java/client/scenes/recipe/OrderedEditableListCell.java b/client/src/main/java/client/scenes/recipe/OrderedEditableListCell.java new file mode 100644 index 0000000..8716c3a --- /dev/null +++ b/client/src/main/java/client/scenes/recipe/OrderedEditableListCell.java @@ -0,0 +1,78 @@ +package client.scenes.recipe; + +import javafx.scene.control.ListCell; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; + +public abstract class OrderedEditableListCell extends ListCell { + /** + * Get the display text for the given item, prefixed with its index. + * Looks like "1. description". + * + * @param item The description. + * @return The display text. + */ + private String getDisplayText(String item) { + return (this.getIndex() + 1) + ". " + item; + } + @Override + protected void updateItem(T item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + this.setText(null); + } else { + this.setText(this.getDisplayText(item.toString())); + } + } + public void startEdit() { + super.startEdit(); + + TextField textField = new TextField(this.getItem().toString()); + + // Commit edit on Enter key press + textField.setOnAction(event -> { + this.commitEdit(fromInput(textField)); + }); + + // Cancel edit on Escape key press + textField.setOnKeyReleased(event -> { + if (event.getCode() == KeyCode.ESCAPE) { + this.cancelEdit(); + } + }); + + this.setText(null); + this.setGraphic(textField); + } + + protected abstract T fromInput(TextField inputField); + /** + * Check if the input is valid (non-empty). + * + * @param input The input string to validate. + * @return true if valid, false otherwise. + */ + private boolean isInputValid(String input) { + return input != null && !input.trim().isEmpty(); + } + + @Override + public void cancelEdit() { + super.cancelEdit(); + + this.setText(this.getItem().toString()); + this.setGraphic(null); + } + + @Override + public void commitEdit(T newValue) { + this.setGraphic(null); + + if (!isInputValid(newValue.toString())) { + newValue = this.getItem(); // Revert to old value if input is invalid + } + + super.commitEdit(newValue); + } +} diff --git a/client/src/main/java/client/scenes/recipe/RecipeStepListCell.java b/client/src/main/java/client/scenes/recipe/RecipeStepListCell.java index 6d6e992..bf57c13 100644 --- a/client/src/main/java/client/scenes/recipe/RecipeStepListCell.java +++ b/client/src/main/java/client/scenes/recipe/RecipeStepListCell.java @@ -1,96 +1,15 @@ package client.scenes.recipe; -import javafx.scene.control.ListCell; import javafx.scene.control.TextField; -import javafx.scene.input.KeyCode; - /** * A custom ListCell for displaying and editing ingredients in an * RecipeStepList. Allows inline editing of ingredient names. * * @see RecipeStepListCtrl */ -public class RecipeStepListCell extends ListCell { - /** - * Get the display text for the given item, prefixed with its index. - * Looks like "1. Step description". - * - * @param item The step description. - * @return The display text. - */ - private String getDisplayText(String item) { - return this.getIndex() + ". " + item; - } - - /** - * Get the display text for the current item. - * Looks like "1. Step description". - * - * @return The display text. - */ - private String getDisplayText() { - return this.getDisplayText(this.getItem()); - } - +public class RecipeStepListCell extends OrderedEditableListCell { @Override - protected void updateItem(String item, boolean empty) { - super.updateItem(item, empty); - - if (empty || item == null) { - this.setText(null); - } else { - this.setText(this.getDisplayText(item)); - } - } - - @Override - public void startEdit() { - super.startEdit(); - - TextField textField = new TextField(this.getItem()); - - // Commit edit on Enter key press - textField.setOnAction(event -> { - this.commitEdit(textField.getText()); - }); - - // Cancel edit on Escape key press - textField.setOnKeyReleased(event -> { - if (event.getCode() == KeyCode.ESCAPE) { - this.cancelEdit(); - } - }); - - this.setText(null); - this.setGraphic(textField); - } - - /** - * Check if the input is valid (non-empty). - * - * @param input The input string to validate. - * @return true if valid, false otherwise. - */ - private boolean isInputValid(String input) { - return input != null && !input.trim().isEmpty(); - } - - @Override - public void cancelEdit() { - super.cancelEdit(); - - this.setText(this.getDisplayText()); - this.setGraphic(null); - } - - @Override - public void commitEdit(String newValue) { - this.setGraphic(null); - - if (!isInputValid(newValue)) { - newValue = this.getItem(); // Revert to old value if input is invalid - } - - super.commitEdit(newValue); + protected String fromInput(TextField inputField) { + return inputField.getText(); } } From 603de94870622d9ea5b8a3a87b1a824d86deff6c Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Dec 2025 22:08:39 +0100 Subject: [PATCH 12/15] feat: bold font recipe name + i18n for controllers --- .../client/scenes/FoodpalApplicationCtrl.java | 9 +++-- .../scenes/recipe/IngredientListCtrl.java | 37 +++++++++++++++---- .../scenes/recipe/RecipeStepListCtrl.java | 37 ++++++++++++++----- .../client/scenes/recipe/IngredientList.fxml | 2 +- .../client/scenes/recipe/RecipeStepList.fxml | 2 +- 5 files changed, 64 insertions(+), 23 deletions(-) diff --git a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java index 9ba7585..cf5dc49 100644 --- a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java +++ b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java @@ -28,9 +28,9 @@ import javafx.scene.control.TextField; import javafx.scene.input.KeyCode; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; +import javafx.scene.text.Font; public class FoodpalApplicationCtrl implements LocaleAware { - private final MainCtrl mainCtrl; private final ServerUtils server; private final LocaleManager localeManager; private final IngredientListCtrl ingredientListCtrl; @@ -80,13 +80,11 @@ public class FoodpalApplicationCtrl implements LocaleAware { @Inject public FoodpalApplicationCtrl( - MainCtrl mainCtrl, ServerUtils server, LocaleManager localeManager, IngredientListCtrl ingredientListCtrl, RecipeStepListCtrl stepListCtrl ) { - this.mainCtrl = mainCtrl; this.server = server; this.localeManager = localeManager; this.ingredientListCtrl = ingredientListCtrl; @@ -149,8 +147,11 @@ public class FoodpalApplicationCtrl implements LocaleAware { refresh(); } private void showName(String name) { + final int NAME_FONT_SIZE = 20; editableTitleArea.getChildren().clear(); - editableTitleArea.getChildren().add(new Label(name)); + Label nameLabel = new Label(name); + nameLabel.setFont(new Font("System Bold", NAME_FONT_SIZE)); + editableTitleArea.getChildren().add(nameLabel); } @Override public void updateText() { diff --git a/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java b/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java index aa33ed2..859c28a 100644 --- a/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java +++ b/client/src/main/java/client/scenes/recipe/IngredientListCtrl.java @@ -1,17 +1,18 @@ package client.scenes.recipe; +import client.utils.LocaleAware; +import client.utils.LocaleManager; +import com.google.inject.Inject; import commons.Recipe; -import java.net.URL; import java.util.ArrayList; import java.util.List; -import java.util.ResourceBundle; import java.util.function.Consumer; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.fxml.Initializable; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.ListView.EditEvent; @@ -19,9 +20,15 @@ import javafx.scene.control.ListView.EditEvent; * Controller for the ingredient list view in the recipe detail scene. * Manages displaying, adding, editing, and deleting ingredients. * - * @see RecipeStepListCell The custom cell implementation. + * @see IngredientListCell The custom cell implementation. */ -public class IngredientListCtrl implements Initializable { +public class IngredientListCtrl implements LocaleAware { + private final LocaleManager localeManager; + @Inject + public IngredientListCtrl(LocaleManager localeManager) { + this.localeManager = localeManager; + } + /** *

* The list of ingredients currently displayed. @@ -43,6 +50,9 @@ public class IngredientListCtrl implements Initializable { @FXML public ListView ingredientListView; + @FXML + public Label ingredientsLabel; + @FXML public Button addIngredientButton; @FXML @@ -129,8 +139,21 @@ public class IngredientListCtrl implements Initializable { this.updateCallback.accept(this.ingredients); } - @FXML - public void initialize(URL location, ResourceBundle resources) { + @Override + public void updateText() { + System.out.println("updatetext called on ingredientListCtrl"); + ingredientsLabel.setText(getLocaleString("menu.label.ingredients")); + addIngredientButton.setText(getLocaleString("menu.button.add.ingredient")); + deleteIngredientButton.setText(getLocaleString("menu.button.remove.ingredient")); + } + + @Override + public LocaleManager getLocaleManager() { + return localeManager; + } + + @Override + public void initializeComponents() { // TODO: set up communication with the server // this would probably be best done with the callback (so this class doesn't // interact with the server at all) diff --git a/client/src/main/java/client/scenes/recipe/RecipeStepListCtrl.java b/client/src/main/java/client/scenes/recipe/RecipeStepListCtrl.java index 2839028..8bfcce4 100644 --- a/client/src/main/java/client/scenes/recipe/RecipeStepListCtrl.java +++ b/client/src/main/java/client/scenes/recipe/RecipeStepListCtrl.java @@ -1,17 +1,18 @@ package client.scenes.recipe; +import client.utils.LocaleAware; +import client.utils.LocaleManager; +import com.google.inject.Inject; import commons.Recipe; -import java.net.URL; import java.util.ArrayList; import java.util.List; -import java.util.ResourceBundle; import java.util.function.Consumer; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.fxml.Initializable; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.ListView.EditEvent; @@ -21,7 +22,16 @@ import javafx.scene.control.ListView.EditEvent; * * @see RecipeStepListCell The custom cell implementation. */ -public class RecipeStepListCtrl implements Initializable { +public class RecipeStepListCtrl implements LocaleAware { + private final LocaleManager localeManager; + + @FXML + public Label stepsLabel; + + @Inject + public RecipeStepListCtrl(LocaleManager localeManager) { + this.localeManager = localeManager; + } /** *

* The list of recipe steps currently displayed. @@ -129,18 +139,25 @@ public class RecipeStepListCtrl implements Initializable { this.updateCallback.accept(this.steps); } - @FXML - public void initialize(URL location, ResourceBundle resources) { - // TODO: set up communication with the server - // this would probably be best done with the callback (so this class doesn't - // interact with the server at all) + @Override + public void updateText() { + stepsLabel.setText(getLocaleString("menu.label.preparation")); + addStepButton.setText(getLocaleString("menu.button.add.step")); + deleteStepButton.setText(getLocaleString("menu.button.remove.step")); + } + @Override + public LocaleManager getLocaleManager() { + return localeManager; + } + + @Override + public void initializeComponents() { this.recipeStepListView.setEditable(true); this.recipeStepListView.setCellFactory( list -> { return new RecipeStepListCell(); }); - this.recipeStepListView.setOnEditCommit(this::handleIngredientEdit); this.addStepButton.setOnAction(this::handleIngredientAdd); this.deleteStepButton.setOnAction(this::handleIngredientDelete); diff --git a/client/src/main/resources/client/scenes/recipe/IngredientList.fxml b/client/src/main/resources/client/scenes/recipe/IngredientList.fxml index 1ad02e4..1a032ea 100644 --- a/client/src/main/resources/client/scenes/recipe/IngredientList.fxml +++ b/client/src/main/resources/client/scenes/recipe/IngredientList.fxml @@ -15,7 +15,7 @@ -