feat: integrate steps list updates

uses JavaFX includes to incorporate a nested controller
This commit is contained in:
Zhongheng Liu 2025-12-04 17:46:38 +01:00
commit 0bec62b9a3
Signed by: steven
GPG key ID: F69B980899C1C09D
4 changed files with 36 additions and 31 deletions

View file

@ -17,6 +17,7 @@ package client;
import client.scenes.FoodpalApplicationCtrl; import client.scenes.FoodpalApplicationCtrl;
import client.scenes.recipe.IngredientListCtrl; import client.scenes.recipe.IngredientListCtrl;
import client.scenes.recipe.RecipeStepListCtrl;
import client.utils.LocaleManager; import client.utils.LocaleManager;
import com.google.inject.Binder; import com.google.inject.Binder;
import com.google.inject.Module; import com.google.inject.Module;
@ -33,6 +34,7 @@ public class MyModule implements Module {
binder.bind(AddNameCtrl.class).in(Scopes.SINGLETON); binder.bind(AddNameCtrl.class).in(Scopes.SINGLETON);
binder.bind(FoodpalApplicationCtrl.class).in(Scopes.SINGLETON); binder.bind(FoodpalApplicationCtrl.class).in(Scopes.SINGLETON);
binder.bind(IngredientListCtrl.class).in(Scopes.SINGLETON); binder.bind(IngredientListCtrl.class).in(Scopes.SINGLETON);
binder.bind(RecipeStepListCtrl.class).in(Scopes.SINGLETON);
binder.bind(LocaleManager.class).in(Scopes.SINGLETON); binder.bind(LocaleManager.class).in(Scopes.SINGLETON);
} }
} }

View file

@ -8,6 +8,7 @@ import java.util.Locale;
import client.exception.UpdateException; import client.exception.UpdateException;
import client.scenes.recipe.IngredientListCtrl; import client.scenes.recipe.IngredientListCtrl;
import client.scenes.recipe.RecipeStepListCtrl;
import client.utils.DefaultRecipeFactory; import client.utils.DefaultRecipeFactory;
import client.utils.LocaleAware; import client.utils.LocaleAware;
import client.utils.LocaleManager; import client.utils.LocaleManager;
@ -33,6 +34,7 @@ public class FoodpalApplicationCtrl implements LocaleAware {
private final ServerUtils server; private final ServerUtils server;
private final LocaleManager localeManager; private final LocaleManager localeManager;
private final IngredientListCtrl ingredientListCtrl; private final IngredientListCtrl ingredientListCtrl;
private final RecipeStepListCtrl stepListCtrl;
public VBox detailsScreen; public VBox detailsScreen;
public HBox editableTitleArea; public HBox editableTitleArea;
@ -99,16 +101,19 @@ public class FoodpalApplicationCtrl implements LocaleAware {
MainCtrl mainCtrl, MainCtrl mainCtrl,
ServerUtils server, ServerUtils server,
LocaleManager localeManager, LocaleManager localeManager,
IngredientListCtrl ingredientListCtrl IngredientListCtrl ingredientListCtrl,
RecipeStepListCtrl stepListCtrl
) { ) {
this.mainCtrl = mainCtrl; this.mainCtrl = mainCtrl;
this.server = server; this.server = server;
this.localeManager = localeManager; this.localeManager = localeManager;
this.ingredientListCtrl = ingredientListCtrl; this.ingredientListCtrl = ingredientListCtrl;
this.stepListCtrl = stepListCtrl;
} }
@Override @Override
public void initializeComponents() { public void initializeComponents() {
// TODO Reduce code duplication??
// Initialize callback for ingredient list updates // Initialize callback for ingredient list updates
this.ingredientListCtrl.setUpdateCallback(newList -> { this.ingredientListCtrl.setUpdateCallback(newList -> {
Recipe focusedRecipe = recipeList.getFocusModel().getFocusedItem(); Recipe focusedRecipe = recipeList.getFocusModel().getFocusedItem();
@ -124,6 +129,20 @@ public class FoodpalApplicationCtrl implements LocaleAware {
throw new UpdateException("Unable to update recipe to server for " + focusedRecipe); throw new UpdateException("Unable to update recipe to server for " + focusedRecipe);
} }
}); });
this.stepListCtrl.setUpdateCallback(newList -> {
Recipe focusedRecipe = recipeList.getFocusModel().getFocusedItem();
if (focusedRecipe == null) {
// edge case error for NPE.
throw new NullPointerException("Null recipe whereas ingredients are edited");
}
focusedRecipe.setPreparationSteps(newList);
try {
// propagate changes to server
server.updateRecipe(focusedRecipe);
} catch (IOException | InterruptedException e) {
throw new UpdateException("Unable to update recipe to server for " + focusedRecipe);
}
});
// Show recipe name in the list // Show recipe name in the list
recipeList.setCellFactory(list -> new ListCell<>() { recipeList.setCellFactory(list -> new ListCell<>() {
@Override @Override
@ -163,11 +182,13 @@ public class FoodpalApplicationCtrl implements LocaleAware {
printRecipeButton.setText(getLocaleString("menu.button.print")); printRecipeButton.setText(getLocaleString("menu.button.print"));
recipesLabel.setText(getLocaleString("menu.label.recipes")); recipesLabel.setText(getLocaleString("menu.label.recipes"));
// TODO propagate ResourceBundle lang changes to nested controller
// ingredientsLabel.setText(getLocaleString("menu.label.ingredients")); // ingredientsLabel.setText(getLocaleString("menu.label.ingredients"));
preparationLabel.setText(getLocaleString("menu.label.preparation")); // preparationLabel.setText(getLocaleString("menu.label.preparation"));
// addIngredientButton.setText(getLocaleString("menu.button.add.ingredient")); // addIngredientButton.setText(getLocaleString("menu.button.add.ingredient"));
addPreparationStepButton.setText(getLocaleString("menu.button.add.step")); // addPreparationStepButton.setText(getLocaleString("menu.button.add.step"));
} }
@Override @Override
@ -177,12 +198,11 @@ public class FoodpalApplicationCtrl implements LocaleAware {
private void showRecipeDetails(Recipe recipe) { private void showRecipeDetails(Recipe recipe) {
if (recipe == null) { if (recipe == null) {
preparationListView.getItems().clear();
return; return;
} }
showName(recipe.getName()); showName(recipe.getName());
ingredientListCtrl.refetchFromRecipe(recipe); ingredientListCtrl.refetchFromRecipe(recipe);
preparationListView.getItems().setAll(recipe.getPreparationSteps()); stepListCtrl.refetchFromRecipe(recipe);
} }
// Button handlers // Button handlers

View file

@ -5,7 +5,7 @@ import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.function.Function; import java.util.function.Consumer;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
@ -29,7 +29,7 @@ public class RecipeStepListCtrl implements Initializable {
* As the step list in {@link Recipe} is immutable, * As the step list in {@link Recipe} is immutable,
* this copies that list upon initialization to this mutable list. * this copies that list upon initialization to this mutable list.
* <br> * <br>
* Please use the {@link #setUpdateCallback(Function)} function to listen for * Please use the {@link #setUpdateCallback(Consumer)} function to listen for
* changes and update the recipe accordingly. * changes and update the recipe accordingly.
* </p> * </p>
*/ */
@ -38,7 +38,7 @@ public class RecipeStepListCtrl implements Initializable {
/** /**
* A callback function that is called when the step list is updated. * A callback function that is called when the step list is updated.
*/ */
private Function<List<String>, Void> updateCallback; private Consumer<List<String>> updateCallback;
@FXML @FXML
ListView<String> recipeStepListView; ListView<String> recipeStepListView;
@ -72,7 +72,7 @@ public class RecipeStepListCtrl implements Initializable {
* *
* @param callback The function to call upon each update. * @param callback The function to call upon each update.
*/ */
public void setUpdateCallback(Function<List<String>, Void> callback) { public void setUpdateCallback(Consumer<List<String>> callback) {
this.updateCallback = callback; this.updateCallback = callback;
} }
@ -91,7 +91,7 @@ public class RecipeStepListCtrl implements Initializable {
private void handleIngredientAdd(ActionEvent event) { private void handleIngredientAdd(ActionEvent event) {
this.steps.add("Step " + (this.steps.size() + 1)); this.steps.add("Step " + (this.steps.size() + 1));
this.refresh(); this.refresh();
this.updateCallback.apply(this.steps); this.updateCallback.accept(this.steps);
var select = this.recipeStepListView.getSelectionModel(); var select = this.recipeStepListView.getSelectionModel();
select.select(this.steps.size() - 1); select.select(this.steps.size() - 1);
@ -108,7 +108,7 @@ public class RecipeStepListCtrl implements Initializable {
this.steps.set(index, newValue); this.steps.set(index, newValue);
this.refresh(); this.refresh();
this.updateCallback.apply(this.steps); this.updateCallback.accept(this.steps);
} }
/** /**
@ -126,7 +126,7 @@ public class RecipeStepListCtrl implements Initializable {
this.steps.remove(selectedIndex); this.steps.remove(selectedIndex);
this.refresh(); this.refresh();
this.updateCallback.apply(this.steps); this.updateCallback.accept(this.steps);
} }
@FXML @FXML

View file

@ -116,27 +116,10 @@
</HBox> </HBox>
<!-- Ingredients --> <!-- Ingredients -->
<fx:include fx:id="ingredientsListContainer" source="recipe/IngredientList.fxml" /> <fx:include source="recipe/IngredientList.fxml" />
<!-- Preparation --> <!-- Preparation -->
<Label fx:id="preparationLabel" text="Preparation"> <fx:include source="recipe/RecipeStepList.fxml" />
<font>
<Font name="System Bold" size="14.0" />
</font>
</Label>
<ListView fx:id="preparationListView" prefHeight="200.0" />
<Button fx:id="addPreparationStepButton" onAction="#addPreparationStep" text="Add Step" />
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="890.6666870117188" minWidth="10.0" prefWidth="854.6666870117188" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="445.3333740234375" minWidth="10.0" prefWidth="57.33331298828125" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
</VBox> </VBox>
</center> </center>