RecipeView.java
package stud.ntnu.idatt1005.pantrypal.views;
import static javafx.stage.Screen.getPrimary;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import stud.ntnu.idatt1005.pantrypal.controllers.CookbookController;
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.utils.ColorPalette;
import stud.ntnu.idatt1005.pantrypal.utils.Sizing;
import stud.ntnu.idatt1005.pantrypal.views.components.FavoriteButton;
import stud.ntnu.idatt1005.pantrypal.views.components.StyledButton;
/**
* This class represents the RecipeView in the application. It extends the View class and sets the
* scene for the stage. The RecipeView is responsible for displaying a single recipe in
* the application.
* It includes the recipe overview, steps, and groceries.
*/
public class RecipeView extends View {
/**
* The recipe to be displayed in the view.
*/
private final Recipe recipe;
/**
* Constructor for RecipeView.
*
* @param controller The controller for the view.
* @param recipe The recipe to be displayed in the view.
*/
public RecipeView(CookbookController controller, Recipe recipe) {
super(controller, Route.RECIPE, "/styles/recipe.css");
this.recipe = recipe;
setUpView();
}
/**
* Sets up the view for the recipe.
* Includes the recipe overview, steps, and groceries.
*/
private void setUpView() {
VBox recipeContainer = new VBox(20);
recipeContainer.setPadding(new Insets(20));
recipeContainer.getStyleClass().add("recipe-container");
recipeContainer.setMaxWidth(getPrimary().getVisualBounds().getWidth() * 0.7);
recipeContainer.getChildren().addAll(
createRecipeOverview(),
createSeparator(Sizing.getRecipeBoxSize()[0]),
setUpRecipeStepsAndGroceries()
);
recipeContainer.setAlignment(Pos.TOP_CENTER);
getBorderPane().setCenter(recipeContainer);
}
/**
* Creates the recipe overview. That is, the image, name, description, and buttons
* for adding groceries to shopping list and to favorite the recipe.
*
* @return The recipe overview
*/
private HBox createRecipeOverview() {
HBox recipeOverview = new HBox(80);
recipeOverview.setMaxSize(Sizing.getRecipeBoxSize()[0], Sizing.getRecipeBoxSize()[1]);
Rectangle image = new Rectangle(Sizing.getRecipeBoxSize()[0] * 0.4,
Sizing.getRecipeBoxSize()[1]);
String imagePath = recipe.getImagePath();
if (imagePath != null && !recipe.getImagePath().isEmpty()) {
ImagePattern imagePattern;
try {
imagePattern = new ImagePattern(new Image(imagePath));
} catch (Exception e) {
imagePattern = new ImagePattern(new Image("images/PantryPalLogo.png"));
}
image.setFill(imagePattern);
} else {
image.setFill(ColorPalette.GRAY);
}
recipeOverview.getChildren().addAll(
image,
setUpOverviewText()
);
recipeOverview.setAlignment(Pos.TOP_CENTER);
return recipeOverview;
}
/**
* Sets up the text-part of the recipe overview. To get an overview of the recipe.
* Includes the recipe name, description, and buttons for adding groceries and to
* favorite the recipe.
*
* @return The overview text
*/
private VBox setUpOverviewText() {
VBox textContainer = new VBox(20);
textContainer.setMinHeight(Sizing.getRecipeBoxSize()[1]);
Text header = new Text(this.recipe.getKey());
header.setFont(new Font("Times new roman", 60));
Text description = new Text();
if (recipe.getDescription() != null && !recipe.getDescription().isEmpty()) {
description.setText(recipe.getDescription());
} else {
description = new Text("Recipe description - Lorem ipsum dolor sit amet, "
+ "consectetur adipiscing elit. Morbi malesuada nulla diam, quis vulputate augue "
+ "porta sed. Sed semper neque ac tempus molestie. Suspendisse ultricies erat nunc, "
+ "ut fringilla leo porta id. Vivamus euismod fringilla leo.");
}
description.setWrappingWidth(400);
textContainer.getChildren().addAll(header, description, createOverviewButtons());
textContainer.setAlignment(Pos.TOP_LEFT);
return textContainer;
}
/**
* Creates the buttons for the recipe overview.
* The buttons are used to add groceries to the shopping list and to favorite the recipe.
*
* @return The overview buttons
*/
private HBox createOverviewButtons() {
StyledButton addNeededGroceriesButton = new StyledButton("Add groceries",
StyledButton.Variant.SOLID, StyledButton.Size.MEDIUM);
addNeededGroceriesButton.getStyleClass().add("overview-buttons");
addNeededGroceriesButton.setOnAction(e ->
notifyObservers(ButtonEnum.ADD_TO_SHOPPING_LIST, this.recipe));
FavoriteButton favoriteButton = new FavoriteButton(recipe.getIsFavorite());
favoriteButton.setOnMouseClicked(e -> {
notifyObservers(ButtonEnum.EDIT_FAVORITE, this.recipe);
favoriteButton.toggleStarColor();
});
StyledButton editButton = new StyledButton("Edit",
StyledButton.Variant.SOLID, StyledButton.Size.MEDIUM);
editButton.setOnMouseClicked(e -> notifyObservers(ButtonEnum.EDIT_RECIPE, this.recipe));
StyledButton deleteButton = new StyledButton("Delete",
StyledButton.Variant.DANGER, StyledButton.Size.MEDIUM);
deleteButton.setOnMouseClicked(e -> notifyObservers(ButtonEnum.REMOVE, this.recipe));
HBox overviewButtons = new HBox(20);
overviewButtons.getChildren().addAll(
addNeededGroceriesButton,
favoriteButton,
editButton,
deleteButton);
overviewButtons.setAlignment(Pos.CENTER_LEFT);
overviewButtons.setPadding(new Insets(0, 0, 0, 0));
return overviewButtons;
}
/**
* Creates a separator.
*
* @param size The size of the separator
* @return The separator
*/
private Rectangle createSeparator(double size) {
Rectangle separator = new Rectangle();
separator.setWidth(size);
separator.setHeight(1);
separator.setFill(ColorPalette.BLACK);
return separator;
}
/**
* Sets up the recipe steps and groceries section of the view.
* The section contains the steps and groceries needed for the recipe. And
* displays them in at the bottom of the view.
*
* @return The container with steps and groceries
*/
private HBox setUpRecipeStepsAndGroceries() {
HBox stepsAndGroceries = new HBox();
stepsAndGroceries.setMinWidth(Sizing.getRecipeBoxSize()[0]);
VBox stepsContainer = setUpRecipeSteps();
VBox groceriesContainer = setUpRecipeGroceries();
HBox.setHgrow(stepsContainer, Priority.ALWAYS);
HBox.setHgrow(groceriesContainer, Priority.ALWAYS);
stepsAndGroceries.getChildren().addAll(
stepsContainer,
groceriesContainer
);
return stepsAndGroceries;
}
/**
* Sets up the recipe steps section.
*
* @return The container with steps
*/
private VBox setUpRecipeSteps() {
VBox stepsContainer = new VBox(20);
stepsContainer.getStyleClass().add("steps-container");
HBox numberedStep;
for (String step : recipe.getRecipeSteps()) {
Text stepText = new Text(step);
stepText.setFont(new Font(25));
numberedStep = new HBox(20);
numberedStep.setAlignment(Pos.CENTER_LEFT);
numberedStep.getChildren().addAll(
setUpStepNumber(recipe.getRecipeSteps().indexOf(step) + 1),
stepText
);
stepsContainer.getChildren().add(numberedStep);
}
return stepsContainer;
}
/**
* Sets up the step number component for a step.
* The step number component is a circle with a number inside.
*
* @param number The number of the step
* @return The step number container
*/
private StackPane setUpStepNumber(int number) {
Circle stepNumber = new Circle(25);
stepNumber.setFill(ColorPalette.GRAY);
Text stepNumberText = new Text(String.valueOf(number));
stepNumberText.setFont(new Font(25));
StackPane stepNumberContainer = new StackPane();
stepNumberContainer.getChildren().addAll(stepNumber, stepNumberText);
return stepNumberContainer;
}
/**
* Sets up the recipe groceries section.
* The section contains the groceries needed for the recipe.
*
* @return The container with groceries
*/
private VBox setUpRecipeGroceries() {
VBox groceriesContainer = new VBox(20);
groceriesContainer.setMaxWidth(Sizing.getRecipeBoxSize()[0] * 0.3);
Text groceriesHeader = new Text("Ingredients:");
groceriesContainer.getChildren().add(groceriesHeader);
groceriesHeader.getStyleClass().add("groceries-header");
for (Grocery grocery : recipe.getRecipeGroceries().getRegister().values()) {
groceriesContainer.getChildren().add(createGroceryTextWithSeparator(grocery));
}
groceriesContainer.setAlignment(Pos.TOP_LEFT);
return groceriesContainer;
}
/**
* Creates a grocery text with a separator. To be used in the groceries section.
*
* @param grocery The grocery to be displayed
* @return The grocery text with a separator
*/
private VBox createGroceryTextWithSeparator(Grocery grocery) {
HBox groceryText = new HBox();
groceryText.getStyleClass().add("grocery-text");
Text nameText = new Text(grocery.getKey());
nameText.getStyleClass().add("sub-groceries-text");
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
Text quantityText = new Text(grocery.getQuantity() + " " + grocery.getUnit());
quantityText.getStyleClass().add("sub-groceries-text");
quantityText.setTextAlignment(TextAlignment.RIGHT);
groceryText.getChildren().addAll(
nameText,
spacer,
quantityText
);
VBox groceryTextWithSeparator = new VBox(10);
groceryTextWithSeparator.getChildren().addAll(
groceryText,
createSeparator(Sizing.getRecipeBoxSize()[0] * 0.3)
);
return groceryTextWithSeparator;
}
}