AddRecipeView.java
package stud.ntnu.idatt1005.pantrypal.views;
import java.util.List;
import java.util.Objects;
import javafx.geometry.Pos;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import stud.ntnu.idatt1005.pantrypal.controllers.AddRecipeController;
import stud.ntnu.idatt1005.pantrypal.controllers.Observer;
import stud.ntnu.idatt1005.pantrypal.enums.ButtonEnum;
import stud.ntnu.idatt1005.pantrypal.enums.Route;
import stud.ntnu.idatt1005.pantrypal.models.Grocery;
import stud.ntnu.idatt1005.pantrypal.models.Recipe;
import stud.ntnu.idatt1005.pantrypal.registers.GroceryRegister;
import stud.ntnu.idatt1005.pantrypal.registers.StepRegister;
import stud.ntnu.idatt1005.pantrypal.utils.NodeUtils;
import stud.ntnu.idatt1005.pantrypal.utils.Sizing;
import stud.ntnu.idatt1005.pantrypal.views.components.AddGroceryListElement;
import stud.ntnu.idatt1005.pantrypal.views.components.GroceryListElement;
import stud.ntnu.idatt1005.pantrypal.views.components.StyledButton;
import stud.ntnu.idatt1005.pantrypal.views.components.StyledTextArea;
import stud.ntnu.idatt1005.pantrypal.views.components.StyledTextField;
/**
* The AddRecipeView class is responsible for creating and managing the view
* for adding a recipe to the cookbook, or modifying an existing one.
* It extends the View class and uses a AddRecipeController to interact with the models.
* Goal: Allow the user to add a new recipe or modify an existing one.
*/
public class AddRecipeView extends View {
private final StyledTextField name;
private final StyledTextArea description;
private final StyledTextField imageUrl;
/**
* Constructor for AddRecipeView. It initializes the AddRecipeView with an AddRecipeController.
* This constructor is used when adding a new recipe.
*
* @param controller The AddRecipeController associated with this render.
*/
public AddRecipeView(AddRecipeController controller) {
super(controller, Route.ADD_RECIPE, "/styles/add-recipe.css");
this.name = new StyledTextField("Name");
this.description = createDescription();
this.imageUrl = new StyledTextField("Image URL");
}
/**
* Constructor for AddRecipeView. It initializes the AddRecipeView with an AddRecipeController.
* This constructor is used when modifying an existing recipe.
*
* @param controller The AddRecipeController associated with this render.
* @param recipe The recipe to be modified.
*/
public AddRecipeView(AddRecipeController controller, Recipe recipe) {
super(controller, Route.ADD_RECIPE, "/styles/add-recipe.css");
this.name = new StyledTextField("Name");
this.description = createDescription();
this.name.setText(recipe.getKey());
this.description.setText(recipe.getDescription());
this.imageUrl = new StyledTextField("Image URL");
this.imageUrl.setText(recipe.getImagePath());
}
/**
* Renders the view for adding a recipe.
* The view consists of a form with fields for name, description, ingredients, and steps.
* The user can add ingredients and steps to the recipe.
* The user can submit the form to add the recipe to the cookbook.
*
* @param groceryRegister The register containing the groceries to be added to the recipe.
* @param stepRegister The register containing the steps to be added to the recipe.
*/
public void render(GroceryRegister groceryRegister, StepRegister stepRegister) {
// Create the overarching VBox
VBox form = new VBox();
form.getStyleClass().add("form");
form.setAlignment(Pos.CENTER);
form.setMaxHeight(Sizing.getScreenHeight() * 0.88);
// Create the scroll pane to hold the form
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
// Create the VBox to hold the form elements
VBox innerForm = new VBox();
// Create the title for the form
Text title = new Text("Add Recipe");
title.getStyleClass().add("title");
HBox titleBox = createTitleBox(title);
titleBox.setAlignment(Pos.TOP_LEFT);
// Create the grocery list with title and option to add groceries
Text groceryListTitle = new Text("Ingredients:");
groceryListTitle.getStyleClass().add("subtitle");
HBox groceryListTitleBox = createTitleBox(groceryListTitle);
ScrollPane groceryList = createGroceryList(groceryRegister);
AddGroceryListElement addGrocery = new AddGroceryListElement("");
for (Observer observer : observers) {
addGrocery.addObserver(observer);
}
// Create the step list with title and option to add steps
Text stepListTitle = new Text("Steps:");
stepListTitle.getStyleClass().add("subtitle");
HBox stepListTitleBox = createTitleBox(stepListTitle);
ScrollPane stepList = createStepList(stepRegister.getSteps());
// Create the button to add a step
HBox addStep = createAddStepBox();
// Create a border to separate the form from the button
Pane border = new Pane();
border.setMinHeight(10);
border.setStyle("-fx-border-width: 1 0 0 0; -fx-border-color: #000000;");
// Create the button to submit the form
StyledButton submit = new StyledButton("Add Recipe");
submit.setMaxWidth(Double.MAX_VALUE);
submit.setOnAction(e -> {
Recipe recipe = new Recipe(name.getText(), description.getText(),
groceryRegister, stepRegister, imageUrl.getText(), false);
notifyObservers(ButtonEnum.ADD, recipe);
});
innerForm.getChildren().addAll(titleBox, name, description, imageUrl,
groceryListTitleBox, groceryList, addGrocery,
stepListTitleBox, stepList, addStep,
border, submit);
scrollPane.setContent(innerForm);
form.getChildren().add(scrollPane);
this.getBorderPane().setCenter(form);
}
/**
* Creates a text area for the description of the recipe.
*
* @return A styled text area for the description of the recipe.
*/
private StyledTextArea createDescription() {
StyledTextArea descriptionField = new StyledTextArea("Description");
descriptionField.setMinHeight(Sizing.getScreenHeight() * 0.18);
descriptionField.setMaxHeight(Sizing.getScreenHeight() * 0.18);
descriptionField.setWrapText(true);
return descriptionField;
}
/**
* Creates a ScrollPane with the grocery list with the groceries in the grocery register.
*
* @param groceryRegister The register containing the groceries to be added to the recipe.
*
* @return A scroll pane containing the grocery list.
*/
private ScrollPane createGroceryList(GroceryRegister groceryRegister) {
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true);
scrollPane.setMinHeight(Sizing.getScreenHeight() * 0.3);
scrollPane.setMaxHeight(Sizing.getScreenHeight() * 0.3);
VBox groceryList = new VBox();
for (Grocery grocery : groceryRegister.getRegister().values()) {
GroceryListElement element = new GroceryListElement.GroceryListElementBuilder(grocery)
.text(grocery.getName())
.text(grocery.getShelf())
.quantity()
.build();
for (Observer observer : observers) {
element.addObserver(observer);
}
element.getPane().setMinWidth(Sizing.getScreenWidth() * 0.6);
groceryList.getChildren().add(element.getPane());
}
scrollPane.setContent(groceryList);
return scrollPane;
}
/**
* Creates a title box with the given text.
*
* @param text The text to be displayed in the title box.
*
* @return A title box with the given text.
*/
private HBox createTitleBox(Text text) {
HBox titleBox = new HBox();
Pane fillerPaneLeft = new Pane();
fillerPaneLeft.setMinWidth(10);
Pane fillerPaneRight = new Pane();
HBox.setHgrow(fillerPaneRight, Priority.ALWAYS);
titleBox.getChildren().addAll(fillerPaneLeft, text, fillerPaneRight);
return titleBox;
}
/**
* Creates a scroll pane with the steps in the step register.
*
* @param stepRegister The register containing the steps to be added to the recipe.
*
* @return A scroll pane containing the step list.
*/
private ScrollPane createStepList(List<String> stepRegister) {
int elementHeight = 20;
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true);
scrollPane.setMinHeight(Sizing.getScreenHeight() * 0.3);
scrollPane.setMaxHeight(Sizing.getScreenHeight() * 0.3);
VBox stepList = new VBox();
stepList.getStyleClass().add("step-list");
for (int i = 0; i < stepRegister.size(); i++) {
Text stepNumber = new Text(i + 1 + ".");
StackPane stepNumberPane = new StackPane(stepNumber);
stepNumberPane.setAlignment(Pos.CENTER);
stepNumberPane.setMaxHeight(elementHeight);
String step = stepRegister.get(i);
StackPane stepPane = new StackPane(new Text(step));
stepPane.setAlignment(Pos.CENTER);
stepPane.setMaxHeight(elementHeight);
StyledButton removeStep = new StyledButton("X", StyledButton.Variant.DELETE,
StyledButton.Size.MEDIUM);
removeStep.setOnAction(e -> notifyObservers(ButtonEnum.REMOVE, step));
removeStep.setMaxHeight(elementHeight);
BorderPane stepElement = new BorderPane();
stepElement.getStyleClass().add("step-element");
stepElement.setLeft(stepNumberPane);
stepElement.setCenter(stepPane);
stepElement.setRight(removeStep);
stepElement.setMaxHeight(elementHeight);
stepList.getChildren().add(stepElement);
}
scrollPane.setContent(stepList);
return scrollPane;
}
/**
* Creates a box with a text field for adding a step to the recipe.
*
* @return A box with a text field for adding a step to the recipe.
*/
private HBox createAddStepBox() {
HBox addStepBox = new HBox();
addStepBox.getStylesheets().add(Objects.requireNonNull(getClass()
.getResource("/styles/pantry.css")).toExternalForm());
addStepBox.setAlignment(Pos.CENTER);
NodeUtils.addClasses(addStepBox, "add-grocery-container");
StyledTextField stepField = new StyledTextField("Step");
addStepBox.getChildren().add(stepField);
StyledButton addStep = new StyledButton("Add Step");
addStep.setMaxWidth(Double.MAX_VALUE);
addStep.setOnAction(e -> {
if (!stepField.getText().isEmpty()) {
notifyObservers(ButtonEnum.ADD, stepField.getText());
stepField.clear();
}
});
addStepBox.getChildren().add(addStep);
return addStepBox;
}
/**
* Notifies the observers of the button that was pressed and the object affected.
*
* @param buttonEnum The button that was pressed.
* @param recipe The Recipe-object that was pressed.
*/
private void notifyObservers(ButtonEnum buttonEnum, Recipe recipe) {
for (Observer observer : observers) {
observer.update(buttonEnum, recipe);
}
}
/**
* Notifies the observers of the button that was pressed and the object affected.
*
* @param buttonEnum The button that was pressed.
* @param step The step that was added
*/
private void notifyObservers(ButtonEnum buttonEnum, String step) {
for (Observer observer : observers) {
observer.update(buttonEnum, step);
}
}
}