diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIngredientController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIngredientController.java index 108f13437ba6d6a412f927335b558b88cae4c6cd..fa34cebe38be07cba50000f7e9880dfbf81e6f6c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIngredientController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIngredientController.java @@ -1,6 +1,5 @@ package no.ntnu.idatt1002.demo.controller; -import javafx.animation.FadeTransition; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -8,7 +7,6 @@ import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; import javafx.scene.control.ListView; -import javafx.event.ActionEvent; import java.io.IOException; import java.net.URL; import java.util.Arrays; @@ -17,11 +15,21 @@ import java.util.ResourceBundle; import java.util.stream.Collectors; import javafx.scene.control.Button; import javafx.scene.control.TextField; -import javafx.util.Duration; import no.ntnu.idatt1002.demo.data.recipes.FileHandler; import no.ntnu.idatt1002.demo.data.recipes.FoodItem; import no.ntnu.idatt1002.demo.data.recipes.IngredientsAtHand; + +/** + * The AddIngredientController manages a dialog pane used to display a search-field along with a list of all the + * possible food types in the application according to the FoodItem enum class. The food types in the list may be + * selected and added to an up-to-date IngredientsAtHand object that is then written to file. The user may add + * several food types before pressing 'close' and exit the dialog. The food types that are not already contained + * in the IngredientsAtHand are listed underneath the list so that the user can keep track of the newly added + * food types. Upon opening the dialog, the search field is already selected and the user can start typing a search. + * When pressing 'Enter' on the keyboard, the search is performed and matches are displayed in the list. If the search + * field is left blank upon search, all food types are listed again. + */ public class AddIngredientController implements Initializable { private ObservableList<String> ingredients; @@ -44,48 +52,74 @@ public class AddIngredientController implements Initializable { private String statusText = "Added: "; + /** + * The initialize method of the controller takes in a URL (location) and ResourceBundle(resources) to + * initialize the controller once its root element has been processed. + * The method then sets the list items of the list, sets the focus on the search-field and makes sure that the + * label underneath the list that states the added food types to the user can wrap if it fills the full width + * of the window. + * @param url The location to resolve the relative paths to the root object. + * @param resourceBundle Resources used to localize the root object. + */ + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + listView.setItems(FXCollections.observableArrayList(Arrays.stream(FoodItem.values()).map(value -> value.label).toList())); + Platform.runLater(() -> searchBar.requestFocus()); + status.setWrapText(true); + } + + /** + * The addToFridge method reads an up-to-date instance of the IngredientsAtHand object from file and + * gets hold of the FoodItem constant that is currently selected in the list. If the selected FoodItem is not + * already at hand, it is added and the IngredientAtHand object is written to file. The label beneath the + * list is also updated with the name of the food type to keep track of what has been added. If the + * selected food type was already at hand, it is not added again and the label is not updated. + * + * @throws IOException If the method fails to write or read the Ingredients at hand object to/from file. + */ @FXML - void addToFridge(ActionEvent event) throws IOException { + void addToFridge() throws IOException { IngredientsAtHand ingredientsAtHand = FileHandler.readIngredientsAtHand("Fridge"); FoodItem item = FoodItem.valueOf(listView.getSelectionModel().getSelectedItem().replace(" ", "_").toUpperCase()); - assert ingredientsAtHand != null; - if(!ingredientsAtHand.atHand(item)) { - ingredientsAtHand.addIngredient(item); - FileHandler.writeIngredientsAtHand(ingredientsAtHand, "Fridge"); - - - if(status.isVisible() && status.getText().isBlank()) { - statusText += String.format("%s", item.label); - } else if (status.isVisible()){ - statusText += String.format(", %s", item.label); + if(item != null && ingredientsAtHand!= null ) { + if(!ingredientsAtHand.atHand(item)) { + ingredientsAtHand.addIngredient(item); + FileHandler.writeIngredientsAtHand(ingredientsAtHand, "Fridge"); + + if(status.isVisible() && status.getText().isBlank()) { + statusText += String.format("%s", item.label); + } else if (status.isVisible()){ + statusText += String.format(", %s", item.label); + } + status.setText(statusText); } - status.setText(statusText); // Only if not already in list!! - } } + /** + * The search method is fired whenever the 'Search' button is pressed by the user. It clears the list and then + * refills it by adding all the resulting values from the call to the method 'searchList'. + */ @FXML void search() { listView.getItems().clear(); listView.getItems().addAll(searchList(searchBar.getText(), - Arrays.stream(FoodItem.values()).toList().stream().map(value -> value.label).toArray(String[]::new))); // String[] + Arrays.stream(FoodItem.values()).toList().stream().map(value -> value.label).toArray(String[]::new))); } - + /** + * The searchList method takes in a string of what the user wrote in the search field and an array of + * Strings that represents every label of the FoodItem enum class. The search word from the user is + * trimmed and split by space and matched against all the constants of the FoodItem enums. Any matches are then + * to a List of strings and returned. + * @param searchWords A String of what the user wrote in the search field. + * @param listOfStrings A list of strings, in this case, each representing a constant of the FoodItem enum class. + * @return A list of strings food types of FoodItem that match the se search word(s). + */ private List<String> searchList(String searchWords, String[] listOfStrings) { String[] searchWordsArray = searchWords.trim().split(" "); - return Arrays.stream(listOfStrings).filter((in) -> { - return Arrays.stream(searchWordsArray).allMatch((word) -> - in.toLowerCase().contains(word.toLowerCase())); - }).collect(Collectors.toList()); - } - - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - listView.setItems(FXCollections.observableArrayList(Arrays.stream(FoodItem.values()).map(value -> value.label).toList())); - Platform.runLater(() -> searchBar.requestFocus()); - status.setWrapText(true); + return Arrays.stream(listOfStrings).filter((in) -> Arrays.stream(searchWordsArray).allMatch((word) -> + in.toLowerCase().contains(word.toLowerCase()))).collect(Collectors.toList()); } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AllRecipesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AllRecipesController.java index 8c4a90b13a60671d0c63c1c8ddf28e84a20012d6..a6bc85d369e4a2583c38d4202cd6003b8e6623af 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AllRecipesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AllRecipesController.java @@ -10,34 +10,101 @@ import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; -import javafx.scene.control.Button; import javafx.scene.control.ListView; import javafx.scene.input.MouseEvent; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.recipes.*; - import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.ResourceBundle; +/** + * The AllRecipesController manages the view named AllRecipes.fxml that displays a scrollable list of all the + * recipes that are stored in the recipe register at 'src/main/resources/recipes/Recipes.register'. The controller + * holds an instance of the IngredientsAtHand class and the RecipeRegister class and combines the two to + * fill the list with recipes sorted by the number of ingredients that are not currently in the 'fridge' of the + * user. Each recipe is also lister with a percentage value that shows how many percent of the recipe's + * ingredients are actually in the fridge. This information is included so that recipes with a lower number of + * ingredients are not given an unfair advantage over others where the user may have a significant number of + * the ingredients available. Each listed recipe may be clicked on by the user to open it in a new view in full + * detail. + * + * @author hannesofie + */ public class AllRecipesController implements Initializable { IngredientsAtHand ingredientsAtHand; RecipeRegister recipeRegister; - @FXML - private Button goBackBtn; - @FXML private ListView<String> allList; + private String selectedRecipeName; + - private ObservableList<String> recipes; + /** + * The initialize method of the controller takes in a URL (location) and ResourceBundle(resources) to + * initialize the controller once its root element has been processed. The method then reads and creates + * an object of the IngredientsAtHand class and one of the RecipesRegister class. + * If the recipe register exists, the list is filled with Strings for each recipe at the format: + * <Recipe name> - X missing ingredients (XX%) + * Finally, a MouseClick event and handler is attached to the list that gets the recipe name of the clicked + * list item and runs the method showRecipe() with that name as input to show the recipe in detail in the + * view Recipe.fxml. + * + * @param url The location to resolve the relative paths to the root object. + * @param resourceBundle Resources used to localize the root object. + */ + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { - private String selectedRecipeName; + ingredientsAtHand = FileHandler.readIngredientsAtHand("Fridge"); + recipeRegister = FileHandler.readRecipeRegister("Recipes"); + + ObservableList<String> recipes; + + if(recipeRegister == null) { + recipes = FXCollections.observableArrayList(new ArrayList<>()); + } else { + int numberOfRecipes = recipeRegister.getRecipes().size(); + + ArrayList<Recipe> sortedRecipes = recipeRegister.pickBestFits(numberOfRecipes, ingredientsAtHand); + + recipes = FXCollections.observableArrayList(sortedRecipes.stream().map(recipe -> String.format("# %s - %d missing ingredients (%2.0f %%)", recipe.getName(), recipe.getMissingIngredients(), percent(recipe.getIngredientList().size() - recipe.getMissingIngredients(), recipe.getIngredientList().size()))).toList()); + } + allList.setItems(recipes); + + allList.setOnMouseClicked(new EventHandler<>() { + + /** + * The handler method takes a MouseEvent(Mouse Click) and uses the list item that was subjected to the + * mouse click and extracts the recipe name. That name as a String is then passed to the method + * 'showRecipe' that loads the view Recipe.fxml to display this particular recipe in full detail. + * + * @param mouseEvent A mouse event, in this case a MouseClicked event. + */ + @Override + public void handle(MouseEvent mouseEvent) { + selectedRecipeName = allList.getSelectionModel() + .getSelectedItem().split("[-#]")[1].strip(); + try { + showRecipe(selectedRecipeName); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + + /** + * The goBack method is fired whenever the user presses the button 'Back To Suggestions'. It loads the location + * of the SuggestRecipes.fxml view and sets the new stage. + * @param event Action event that triggers this method, in this case a button click. + * @throws IOException If the method fails to load the location at the given file path. + */ @FXML private void goBack(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(); @@ -50,6 +117,12 @@ public class AllRecipesController implements Initializable { stage.show(); } + /** + * The showRecipe method takes in a recipe name as a String and navigates to the view Recipe.fxml after having + * the RecipeController set the data of the view according to this particular recipe. + * @param recipeName A case-sensitive string representation of the recipe to open in detail. + * @throws IOException If the method fails to load the location of the Recipe.fxml view. + */ private void showRecipe(String recipeName) throws IOException { FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/view/Recipe.fxml")); @@ -57,59 +130,33 @@ public class AllRecipesController implements Initializable { Recipe recipeOfInterest = recipeRegister.getRecipe(recipeName); Parent root = loader.load(); - RecipeController recipeController = loader.getController(); - recipeController.setData(recipeOfInterest); - - Stage stage = (Stage)allList.getParent().getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); + if(recipeOfInterest != null) { + recipeController.setData(recipeOfInterest); + Stage stage = (Stage)allList.getParent().getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); + } else { + System.out.println("The selected recipe is null."); + } } -private float percent(int a, int b) { - if(b != 0 && a != 0) { - return (float) a / b; + /** + * The percent method takes in two integers(a, b) and performs float-division(a/b) on then and multiplies the + * answer by 100 to get the percentage 'a' makes of the total 'b'. If either 'a' or 'b' is zero, zero is returned. + * In the current context; 'a' is an int representing a number of ingredients at hand and part of a recipe and 'b' + * is an int representing the total number of ingredients in the same recipe. + * @param a An int to divide by b and multiply by 100. + * @param b An int by which to divide 'a'. + * @return A float value presenting the percentage value of 'a' out of 'b'. + */ + private float percent(int a, int b) { + if (b != 0 && a != 0) { + return (a * 100f / b); } else { return 0; } - - -} - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - - ingredientsAtHand = FileHandler.readIngredientsAtHand("Fridge"); - recipeRegister = FileHandler.readRecipeRegister("Recipes"); - - int numberOfRecipes = recipeRegister.getRecipes().size(); - - ArrayList<Recipe> sortedRecipes = recipeRegister.pickBestFits(numberOfRecipes, ingredientsAtHand); - - recipes = FXCollections.observableArrayList(sortedRecipes.stream().map(recipe -> { - return String.format("# %s - %d missing ingredients (%.2f %%)", recipe.getName(), recipe.getMissingIngredients(), percent(recipe.getIngredientList().size() - recipe.getMissingIngredients(),recipe.getIngredientList().size())); - }).toList()); - - allList.setItems(recipes); - - allList.setOnMouseClicked(new EventHandler<MouseEvent>() { - - @Override - public void handle(MouseEvent mouseEvent) { - selectedRecipeName = allList.getSelectionModel() - .getSelectedItem().split("-|#")[1].strip(); - - try { - showRecipe(selectedRecipeName); - - } catch (IOException e) { - throw new RuntimeException(e); - } - - } - }); - } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetBarController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetBarController.java index 1444edbc381225e2a84a319490d525b9df616281..54aa19fa7527066b5c61d5026d931829b39b7041 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetBarController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetBarController.java @@ -1,15 +1,20 @@ package no.ntnu.idatt1002.demo.controller; import javafx.fxml.FXML; -import javafx.fxml.Initializable; import javafx.scene.control.Label; import javafx.scene.control.ProgressBar; import no.ntnu.idatt1002.demo.data.Budget.BudgetItem; -import java.net.URL; -import java.util.ResourceBundle; -public class BudgetBarController implements Initializable { +/** + * The BudgetBarController manages the component view BudgetBar.fxml that may be dynamically loaded to other views. + * The view consists of a HBox containing three elements; a label stating the name of the budget post (BudgetItem class), + * a progress bar showing how much money is left at this post according to the budget and the user's recorded expenses + * and a label stating how much money are left on the particular budget. + * + * @author hannesofie + */ +public class BudgetBarController { @FXML private Label nameTag; @@ -20,7 +25,15 @@ public class BudgetBarController implements Initializable { @FXML private ProgressBar miniBar; - + /** + * The setData method is called to fill the BudgetBar instance with information of a particular Budget + * post (BudgetItem), that is the name of the budget, it's progress bar and money left. The information needed + * for this are taken in as two parameters; the BudgetItem object to represent and a double value representing + * the money left of this budget. + * + * @param budgetItem A BudgetItem object to represent in the BudgetBar instance. + * @param leftovers A double value representing the money the user has left on this particular BudgetItem. + */ public void setData(BudgetItem budgetItem, double leftovers) { nameTag.setText(budgetItem.getBudgetCategory().label.substring(0,1).toUpperCase() + budgetItem.getBudgetCategory().label.substring(1)); @@ -40,12 +53,5 @@ public class BudgetBarController implements Initializable { miniBar.setStyle("-fx-accent: rgba(48,222,109,0.8);"); miniBar.setProgress(progress); } - - } - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - } - } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/CreateBudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/CreateBudgetController.java index e061094c62103ef3d9fb5ee147467c08e4987347..58d42d7f3f48b406f9fbeb0ed5b04ed42edc6ceb 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/CreateBudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/CreateBudgetController.java @@ -13,7 +13,6 @@ import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.stage.Stage; -import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudget; import no.ntnu.idatt1002.demo.data.Budget.FileHandlingSelectedBudget; import no.ntnu.idatt1002.demo.data.Economics.Expense; diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/FinanceController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/FinanceController.java index 36a538221a4bc5b5b24b698619a6b32cb62ca6d0..3b50b49919e4ad50e98764c41eee22bec59a858e 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/FinanceController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/FinanceController.java @@ -1,12 +1,7 @@ package no.ntnu.idatt1002.demo.controller; -import java.awt.event.ActionEvent; import java.io.IOException; -import java.time.LocalDate; import java.util.Optional; -import javafx.collections.ObservableList; -import javafx.scene.chart.PieChart; -import javafx.scene.chart.PieChart.Data; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.ButtonType; diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/IngredientTileController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/IngredientTileController.java index ba5e90e775dbf8900e64ea52e87af6b2e0bc371f..05937bc1e949e87ddd9f7221cae9e3de15c679a4 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/IngredientTileController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/IngredientTileController.java @@ -1,34 +1,35 @@ package no.ntnu.idatt1002.demo.controller; import javafx.fxml.FXML; -import javafx.fxml.Initializable; import javafx.scene.control.Label; -import javafx.scene.layout.Pane; import no.ntnu.idatt1002.demo.data.recipes.RecipeIngredient; -import java.net.URL; -import java.util.ResourceBundle; -public class IngredientTileController implements Initializable { +/** + * The IngredientTileController manages a simple pane component view called IngredientTile.fxml that is used to + * dynamically load ingredients into a view to present all details of a recipe. The data to create a component + * of this kind is set by 'setData' and takes RecipeIngredient objects as parameter. + * + * @author hannesofie + */ +public class IngredientTileController { @FXML private Label text; - @FXML - private Pane ingredientPane; - - + /** + * The setData method takes an RecipeIngredient object as parameter and sets the text of the label contained in + * the simple pane to a formatted string following this pattern: + * "# <Ingredient name> XX.X YY" + * where XX.X is the amount of the ingredient in the recipe and YY is the unit of measure. + * @param ingredient An RecipeIngredient object to format and create an Ingredient tile for. + */ public void setData(RecipeIngredient ingredient) { StringBuilder sb = new StringBuilder(); sb.append("# ").append(ingredient.getFoodType().label.substring(0,1).toUpperCase()) - .append(ingredient.getFoodType().label.substring(1)); - sb.append(" ").append(ingredient.getAmount()).append(" ").append(ingredient.getUnit().label); + .append(ingredient.getFoodType().label.substring(1)).append(" ") + .append(ingredient.getAmount()).append(" ") + .append(ingredient.getUnit().label); text.setText(String.valueOf(sb)); } - - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - - } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenu.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenu.java index eb1148294c03782bcfb88a0dbce059ab6d0e79ee..52283c5c72911e211dbb4fb7929f2c20ebfc8191 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenu.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenu.java @@ -3,12 +3,7 @@ package no.ntnu.idatt1002.demo.controller; import java.io.IOException; import java.time.LocalDate; import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.stream.Collectors; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; @@ -32,12 +27,10 @@ import no.ntnu.idatt1002.demo.data.Budget.BudgetItem; import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudget; import no.ntnu.idatt1002.demo.data.Budget.FileHandlingSelectedBudget; import no.ntnu.idatt1002.demo.data.Budget.GeneralBudget; -import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; import no.ntnu.idatt1002.demo.data.Economics.ExpenseRegister; import no.ntnu.idatt1002.demo.data.Economics.FileHandling; import no.ntnu.idatt1002.demo.data.Economics.IncomeRegister; import no.ntnu.idatt1002.demo.data.Economics.Overview; -import no.ntnu.idatt1002.demo.data.recipes.Recipe; public class MainMenu { diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/RecipeController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/RecipeController.java index c4ba3654e9cbe7b3f949a4d96249e44453dbf7a8..a112bf6b4b3149715c369fe2d6bcc8739f8fc640 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/RecipeController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/RecipeController.java @@ -5,7 +5,6 @@ import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; @@ -16,12 +15,16 @@ import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.recipes.*; - import java.io.IOException; -import java.net.URL; -import java.util.ResourceBundle; -public class RecipeController implements Initializable { +/** + * The RecipeController manages the view Recipe.fxml that displays one particular recipe in full detail. + * It has the recipe's name as headline, lists the ingredients of the recipe in a scroll pane to the left and the + * instructions in a scroll pane to the right. + * + * @author hannesofie + */ +public class RecipeController { @FXML private Label recipeName; @@ -29,37 +32,41 @@ public class RecipeController implements Initializable { @FXML private Text instructions; - @FXML - private Button goBackBtn; - @FXML private VBox ingredientList; - @FXML - private ObservableList<RecipeIngredient> ingredients; + private Button toSuggestionsBtn; @FXML - private Pane ingredientPane; + private Button allBtn; @FXML - private Button allRecipesBtn; - - - private Recipe recipe; - + private ObservableList<RecipeIngredient> ingredients; + /** + * The setData method takes a Recipe object as parameter and sets the information of the view accordingly. + * Specifically, the recipe name is set as headline, it's ingredients are stored in an ObservableList and it's + * instructions are set as text in the scroll pane to the right. Finally, the method 'setIngredientTiles' + * is called to fill the ingredient list with the component IngredientTile for each ingredient. + * @param recipeOfInterest A Recipe object to present in full detail on the Recipe.fxml view. + */ public void setData(Recipe recipeOfInterest) { - recipe = recipeOfInterest; - - recipeName.setText(recipe.getName()); - instructions.setText(recipe.getInstructions()); - ingredients = FXCollections.observableArrayList(recipe.getIngredientList()); - + if(recipeOfInterest != null) { + recipeName.setText(recipeOfInterest.getName()); + instructions.setText(recipeOfInterest.getInstructions()); + ingredients = FXCollections.observableArrayList(recipeOfInterest.getIngredientList()); + } else { + throw new IllegalArgumentException("The recipe of interest is null"); + } setIngredientTiles(); } + /** + * The setIngredientTiles method uses the ObservableList of RecipeIngredients and creates a pane according to + * the component view IngredientTile.fxml for each and adds them to the ingredient list of the Recipe.fxml view. + */ private void setIngredientTiles() { for(RecipeIngredient ri : ingredients) { @@ -67,7 +74,7 @@ public class RecipeController implements Initializable { try { Pane pane = loader.load(); - IngredientTileController ingredientTileController = loader.getController(); //Todo: is null + IngredientTileController ingredientTileController = loader.getController(); ingredientTileController.setData(ri); ingredientList.getChildren().add(pane); @@ -78,34 +85,28 @@ public class RecipeController implements Initializable { } } - + /** + * The method fires when one of the scene-switching buttons are clicked by the user. It then uses the identity of + * the clicked button to set the location of the scene switch between 'All Recipes' and 'To Suggested Recipes', + * and loads this scene. + * @param event The event that causes the method to be called. + * @throws IOException If the method is unable to load the locations at the provided paths. + */ @FXML - private void goBack(ActionEvent event) throws IOException { + private void switchScene(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(); - loader.setLocation(getClass().getResource("/view/SuggestRecipes.fxml")); - Parent root = loader.load(); - Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); - } - - @FXML - private void toAllRecipes(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(getClass().getResource("/view/AllRecipes.fxml")); + if (event.getSource() == allBtn) { + loader.setLocation(getClass().getResource("/view/AllRecipes.fxml")); + } else if (event.getSource() == toSuggestionsBtn) { + loader.setLocation(getClass().getResource("/view/SuggestRecipes.fxml")); + } Parent root = loader.load(); Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); Scene scene = new Scene(root); stage.setScene(scene); + stage.setResizable(false); stage.show(); } - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - // ingredients = FXCollections.observableArrayList(recipe.getIngredientList()); - - } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/RecipeTileController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/RecipeTileController.java index 0e832a026e76472bbc3fce207e30464082ff11ca..81e5e3b0584efc8c6b5c984e98dea75b42bbacae 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/RecipeTileController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/RecipeTileController.java @@ -8,7 +8,6 @@ import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.*; -import javafx.scene.layout.VBox; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.recipes.FileHandler; import no.ntnu.idatt1002.demo.data.recipes.Recipe; @@ -18,6 +17,13 @@ import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; + +/** + * The RecipeTileController manages the tile components that are used to dynamically display a set of recommended + * recipes in terms of their name and number of missing ingredients. Each tile is a VBox containing this information. + * + * @author hannesofie + */ public class RecipeTileController implements Initializable { @FXML @@ -26,12 +32,31 @@ public class RecipeTileController implements Initializable { @FXML private Label missingTag; - @FXML - private VBox recipeTile; - private RecipeRegister recipeRegister; + /** + * The initialize method of the controller takes in a URL (location) and ResourceBundle(resources) to + * initialize the controller once its root element has been processed. The method reads and creates an instance + * of the RecipeRegister class from file and makes sure that the name of the recipe will be able to wrap + * on the tile if it is longer than the tile width. + * @param url The location to resolve the relative paths to the root object. + * @param resourceBundle Resources used to localize the root object. + */ + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + recipeRegister = FileHandler.readRecipeRegister("Recipes"); + nameTag.setWrapText(true); + } + /** + * The tileClick method is triggered by an Action event fired once the user presses the tile. The event + * is handled by accessing the Recipe object represented by the pressed tile and setting the loader location + * to the Recipe.fxml view. It then loads the controller of the Recipe.fxml view and calls it's 'setData' + * method with the recipe as parameter. The scene is then changed to Recipe.fxml that shows the details of that + * particular recipe. + * @param event The action event triggered when the user presses a recipe tile. + * @throws IOException If the loader fails to set the location to the given path for Recipe.fxml. + */ @FXML private void tileClick(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(); @@ -43,7 +68,6 @@ public class RecipeTileController implements Initializable { Parent root = loader.load(); RecipeController recipeController = loader.getController(); - recipeController.setData(recipeOfInterest); Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); @@ -52,14 +76,13 @@ public class RecipeTileController implements Initializable { stage.show(); } + /** + * The setData method takes in a Recipe object and sets the labels of the Recipe tiles according to that + * recipe's name and number of missing ingredients. + * @param recipe A recipe object to represent as a recipe tile(RecipeTile.fxml) + */ public void setData(Recipe recipe) { nameTag.setText(recipe.getName()); missingTag.setText(Integer.toString(recipe.getMissingIngredients())); } - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - recipeRegister = FileHandler.readRecipeRegister("Recipes"); - nameTag.setWrapText(true); - } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java index 92f9f3b73934645fae914584ca0a54bf0c6015da..9a35491d23414bd45c14871380de3dc56fd2898c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java @@ -1,7 +1,6 @@ package no.ntnu.idatt1002.demo.controller; import java.io.IOException; - import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; @@ -15,7 +14,6 @@ import javafx.scene.control.Dialog; import javafx.stage.Modality; import javafx.stage.Screen; import javafx.stage.Stage; -import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudget; import no.ntnu.idatt1002.demo.data.Budget.FileHandlingSelectedBudget; import no.ntnu.idatt1002.demo.data.Economics.Income; diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/SelectBudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/SelectBudgetController.java index 35a213b509d5372a91de3e495306dab4363b6390..3feecc26f109cdfebd91109aa4242df3fd94a5d2 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/SelectBudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/SelectBudgetController.java @@ -12,7 +12,6 @@ import javafx.scene.control.ListView; import javafx.scene.input.MouseButton; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Budget.BudgetRegister; -import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudget; import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudgetArchive; import no.ntnu.idatt1002.demo.data.Budget.FileHandlingSelectedBudget; diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/SuggestRecipesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/SuggestRecipesController.java index cc60eafae54f69304a67833e1faf5f76b85a6c5f..812eebff257e7b205b911c57ea5332c56af7d93b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/SuggestRecipesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/SuggestRecipesController.java @@ -21,12 +21,22 @@ import java.util.ArrayList; import java.util.Optional; import java.util.ResourceBundle; +/** + * The controller manages the view SuggestRecipes.fxml that displays a list of food types that the user has available, + * called 'fridge' and four tiles showing the four recipes that requires the lowest number of ingredients + * that are currently not at hand. These 'recipe tiles' are dynamically loaded and update whenever food + * types are removed or added to the 'fridge'. Food types are removed from the fridge by selecting it from the + * fridge-list and pressing 'Remove' and added by pressing 'Add to Fridge' upon which a new window is opened. + * If the ingredients at hand list is empty or the recipe register is empty or not existing, the page will still load, + * but the 'fridge' will be empty and no recipe suggestions will appear. + * + * @author hannesofie + */ public class SuggestRecipesController implements Initializable { IngredientsAtHand ingredientsAtHand; RecipeRegister recipeRegister; - @FXML private Button showAllBtn; @@ -42,12 +52,41 @@ public class SuggestRecipesController implements Initializable { @FXML private Label missingList; - private ObservableList<String> fridge; - private final ArrayList<VBox> currentRecipeTiles = new ArrayList<>(4); + /** + * The initialize method of the controller takes in a URL (location) and ResourceBundle(resources) to + * initialize the controller once its root element has been processed. + * The method also provides the controller with an up do date IngredientsAtHand object by calling + * 'readIngredientsAtHand', as well as an up-to-date RecipeRegister by reading it from file. + * Once this data is in place, the method calls 'setRecipeTiles()' to populate the grid with tiles representing + * the four recipes requiring the least ingredients not at hand. + * + * @param url The location to resolve the relative paths to the root object. + * @param resourceBundle Resources used to localize the root object. + */ + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + readIngredientsAtHand(); + recipeRegister = FileHandler.readRecipeRegister("Recipes"); + if(recipeRegister == null) { + recipeRegister = new RecipeRegister(); + } + + missingList.setVisible(false); + setRecipeTiles(); + } + + /** + * The addIngredient method fires when the user presses the 'Add to Fridge' button of the UI. It then + * loads a pop-up dialog that appear over the current window. The dialog is managed by + * a different controller that adds and stores new food types in the fridge. Once the dialog is excited, this + * method updates the ingredientsAtHand object and the suggested recipes of this controller. + * + * @throws IOException If the method is unable to set the location based on the provided path of the dialog pane. + */ @FXML private void addIngredient() throws IOException { FXMLLoader loader = new FXMLLoader(); @@ -66,6 +105,13 @@ public class SuggestRecipesController implements Initializable { } } + /** + * The method fires once the user presses the 'Remove' button of the UI. It then validates that a recipe in the + * fridge-list was selected before going ahead and removing if from the IngredientsAtHand object. + * If it has removed an ingredient, it calls 'storeIngredientsAtHand' and 'setRecipeTiles' to store the change + * to file and update the recipe suggestions. + * @throws IOException If writing the altered IngredientsAtHand object to file fails. + */ @FXML private void removeFromFridge() throws IOException { String toRemove = fridgeList.getSelectionModel().getSelectedItem(); @@ -77,7 +123,16 @@ public class SuggestRecipesController implements Initializable { } - + /** + * The method calls the 'pickBestFits' method of the recipe register object with 4 as requested number of + * recipes and the ingredients at hand object as inputs. The resulting recipes are stored in an ArrayList. + * The recipes in the list are iterated through, and for each, a VBox object as defined in the view + * RecipeTile.fxml is created and filled with each recipe's data by calling 'setData' of the controller of + * recipeTile.fxml. Each VBox is equipped with some hover-effects by calling 'setHoverEffect' and then + * added to the grid of SuggestRecipes.fxml. The current tiles(VBox) are stored in an ArrayList to + * make sure to delete these VBox-object upon updating to prevent then from layering + * on top of each other. + */ private void setRecipeTiles() { int NUMBER_OF_TILES = 4; ArrayList<Recipe> recipes = recipeRegister.pickBestFits(NUMBER_OF_TILES, ingredientsAtHand); @@ -108,10 +163,8 @@ public class SuggestRecipesController implements Initializable { } setHoverEffect(vBox, r); - recipeGrid.add(vBox, i, j); - } catch (IOException e) { throw new RuntimeException(e); } @@ -121,7 +174,16 @@ public class SuggestRecipesController implements Initializable { } + /** + * This method is used to equip a VBox with a hover effect that displays a label at the bottom of the view that + * lists the missing ingredients of the recipe associated with the VBox whenever the mouse hovers over the VBox. + * If no ingredients are missing no label should be shown. + * @param vBox A VBox object to which the hover effect is attached. + * @param recipe The recipe associated with the VBox and therefore source of the content of the + * label displayed upon hovering. + */ private void setHoverEffect(VBox vBox, Recipe recipe) { + vBox.setOnMouseEntered(event -> { if(recipe.getMissingIngredients()==0) { missingList.setText(""); @@ -130,7 +192,6 @@ public class SuggestRecipesController implements Initializable { missingList.setText("Missing: " + String.join(", ", recipe.getMissingList())); missingList.setVisible(true); } - }); vBox.setOnMouseExited(event -> { @@ -140,6 +201,13 @@ public class SuggestRecipesController implements Initializable { } + /** + * The method fires when one of the scene-switching buttons are clicked by the user. It then uses the identity of + * the clicked button to set the location of the scene switch between 'All Recipes' and 'Main Menu', + * and loads this scene. + * @param event The event that causes the method to be called. + * @throws IOException If the method is unable to load the locations at the provided paths. + */ @FXML private void switchScene(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(); @@ -156,33 +224,39 @@ public class SuggestRecipesController implements Initializable { stage.show(); } - public void readIngredientsAtHand() { + /** + * The method reads and creates an ingredientsAtHand object by reading from the file with title "Fridge" + * by using the static class recipe.FileHandler. If the file is non-existent or empty, a new and empty IngredientsAtHand + * object is created and the fridge list is empty. Otherwise, the fridge list is filled with the food types + * at hand sorted alphabetically by calling the method 'updateFridge'. + */ + private void readIngredientsAtHand() { ingredientsAtHand = FileHandler.readIngredientsAtHand("Fridge"); if (ingredientsAtHand == null) { ingredientsAtHand = new IngredientsAtHand(); + fridgeList.setItems(FXCollections.observableArrayList(new ArrayList<>())); + } else { + updateFridge(); } - fridge = FXCollections.observableArrayList(ingredientsAtHand.getIngredientsAtHand().stream().map(foodItem -> foodItem.label).toList()); - fridgeList.setItems(fridge.sorted()); } - public void storeIngredientsAtHand() throws IOException { + /** + * The method makes use of the static class 'recipe.FileHandler' to write the IngredientsAtHand object of the + * controller to a file named 'Fridge.register'. Once the object is written to file, the fridge list is + * updated by calling 'updateFridge'. + * @throws IOException If the method fails to write the IngredientsAtHand object to file. + */ + private void storeIngredientsAtHand() throws IOException { FileHandler.writeIngredientsAtHand(ingredientsAtHand, "Fridge"); - fridge = FXCollections.observableArrayList(ingredientsAtHand.getIngredientsAtHand().stream().map(foodItem -> foodItem.label).toList()); - fridgeList.setItems(fridge.sorted()); + updateFridge(); } - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - - readIngredientsAtHand(); - recipeRegister = FileHandler.readRecipeRegister("Recipes"); - if(recipeRegister == null) { - recipeRegister = new RecipeRegister(); - } - //ObservableList<Recipe> recipes = FXCollections.observableArrayList(recipeRegister.getRecipes()); - missingList.setVisible(false); - - setRecipeTiles(); + /** + * The method 'updateFridge' supports other methods by setting the fridge's content based on the + * IngredientsAtHand object of the Controller and adding them to the fridge list in alphabetical order. + */ + private void updateFridge() { + ObservableList<String> fridge = FXCollections.observableArrayList(ingredientsAtHand.getIngredientsAtHand().stream().map(foodItem -> foodItem.label).toList()); + fridgeList.setItems(fridge.sorted()); } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java index 4ceaf8f63c2b03279deb7e2c3ff9c8040b70059d..ba766c728e10273905047e7198a6ba27edbbcaaa 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java @@ -2,7 +2,6 @@ package no.ntnu.idatt1002.demo.data.Budget; import java.util.Arrays; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; - import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java index 27a470035bb9749c496cb704cf5b4689b8826b80..004b34b4d9a1daedb7ce8ee40487cfbac526adc1 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java @@ -5,8 +5,6 @@ import java.io.FileWriter; import java.io.IOException; import java.io.*; import java.time.LocalDate; -import no.ntnu.idatt1002.demo.data.Budget.FileHandlingBudget; -import no.ntnu.idatt1002.demo.data.Budget.FileHandlingSelectedBudget; /** diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java index 9a38712eedada7b202fec8ee0266b5fdf89eea21..6f6aa4ed5cc41d38ec0df27ba1115ff5b52e4699 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java @@ -1,10 +1,7 @@ package no.ntnu.idatt1002.demo.data.Economics; -import java.time.Year; -import java.time.YearMonth; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; /** * ItemRegister is a generic class used diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Overview.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Overview.java index 4ed63a56b399c3daf710fcbab9ce2ab3c67bf989..01308b4a7df99e043821e333de92f0b4118d89ab 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Overview.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Overview.java @@ -1,6 +1,5 @@ package no.ntnu.idatt1002.demo.data.Economics; -import no.ntnu.idatt1002.demo.data.Budget.BudgetItem; import no.ntnu.idatt1002.demo.data.Budget.GeneralBudget; /** diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/recipes/RecipeRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/recipes/RecipeRegister.java index dd6954090704c381ca11224e6effb7df86dabc45..17ce164d66f90e15f9caf3c387bd73adffa2d4b4 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/recipes/RecipeRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/recipes/RecipeRegister.java @@ -2,7 +2,6 @@ package no.ntnu.idatt1002.demo.data.recipes; import java.util.ArrayList; import java.util.Comparator; -import java.util.List; import java.util.stream.Collectors; /** @@ -14,7 +13,7 @@ import java.util.stream.Collectors; */ public class RecipeRegister { - private ArrayList<Recipe> recipes = new ArrayList<>(); + private final ArrayList<Recipe> recipes = new ArrayList<>(); /** diff --git a/src/main/resources/budgets/Archive.archive b/src/main/resources/budgets/Archive.archive index 0ff85e98bc3837eb1b7dc2311235d6a835428b95..415bab20c3fe4b8bf68d40fed7fd72f6b4e0620c 100644 --- a/src/main/resources/budgets/Archive.archive +++ b/src/main/resources/budgets/Archive.archive @@ -1,3 +1,4 @@ APRILnew APRILsecond APRILreasonable +APRILpåske diff --git a/src/main/resources/budgets/SelectedBudget.current b/src/main/resources/budgets/SelectedBudget.current index 06b279097892ff167d38ffe92f919d36d811db9d..0e696bb67074785b3a96d841be79fcb03a645e39 100644 --- a/src/main/resources/budgets/SelectedBudget.current +++ b/src/main/resources/budgets/SelectedBudget.current @@ -1 +1 @@ -APRILreasonable \ No newline at end of file +APRILpåske \ No newline at end of file diff --git a/src/main/resources/style.css b/src/main/resources/style.css index 4b1575f2534246b3f287325317c8b1aed85c61d2..3a562b3cc890b50ba606a44f0d05284d3f4b276b 100644 --- a/src/main/resources/style.css +++ b/src/main/resources/style.css @@ -93,7 +93,7 @@ -fx-background-color: transparent; } .scroll-pane { - -fx-background-color: rgba(255, 255, 255, 0.25); + -fx-background-color: rgba(255, 255, 255, 0.5); -fx-border-width: 5; -fx-border-radius: 5; -fx-border-color: rgba(255, 255, 255, 0.75); diff --git a/src/main/resources/view/Recipe.fxml b/src/main/resources/view/Recipe.fxml index 45c0e797062d5dd8804f7ddca268d83cbc65c0c6..eb603d60c9b5a8ef8f7a0a1560ddc5b56beb95b3 100644 --- a/src/main/resources/view/Recipe.fxml +++ b/src/main/resources/view/Recipe.fxml @@ -18,11 +18,11 @@ <children> <Pane prefHeight="136.0" prefWidth="411.0"> <children> - <Button id="button-stye" fx:id="goBackBtn" layoutX="81.0" layoutY="30.0" mnemonicParsing="false" onAction="#goBack" styleClass="button-style" stylesheets="@../style.css" text="Recipe Suggestions"> + <Button id="button-stye" fx:id="toSuggestionsBtn" layoutX="81.0" layoutY="30.0" mnemonicParsing="false" onAction="#switchScene" styleClass="button-style" stylesheets="@../style.css" text="Recipe Suggestions"> <font> <Font size="14.0" /> </font></Button> - <Button fx:id="allRecipesBtn" layoutX="81.0" layoutY="68.0" mnemonicParsing="false" onAction="#toAllRecipes" styleClass="button-style" stylesheets="@../style.css" text="All Recipes" /> + <Button fx:id="allBtn" layoutX="81.0" layoutY="68.0" mnemonicParsing="false" onAction="#switchScene" styleClass="button-style" stylesheets="@../style.css" text="All Recipes" /> </children> </Pane> <Pane prefHeight="103.0" prefWidth="853.0"> @@ -40,9 +40,9 @@ <center> <HBox prefHeight="100.0" prefWidth="200.0" spacing="50.0" BorderPane.alignment="CENTER"> <children> - <ScrollPane prefHeight="431.0" prefWidth="271.0" stylesheets="@../style.css"> + <ScrollPane prefHeight="431.0" prefWidth="263.0" stylesheets="@../style.css"> <content> - <AnchorPane prefHeight="449.0" prefWidth="262.0" styleClass="ingredient-pane" stylesheets="@../style.css"> + <AnchorPane prefHeight="449.0" prefWidth="270.0" styleClass="ingredient-pane" stylesheets="@../style.css"> <children> <VBox fx:id="ingredientList" styleClass="ingredient-list" stylesheets="@../style.css"> <padding> @@ -52,7 +52,7 @@ </AnchorPane> </content> </ScrollPane> - <ScrollPane prefHeight="429.0" prefWidth="632.0"> + <ScrollPane prefHeight="429.0" prefWidth="632.0" stylesheets="@../style.css"> <content> <Pane id="recipe-instructions" stylesheets="@../style.css"> <children>