Merge branch 'refactor/recipe-details' into 'main'
refactor: put recipe detail controller into its own file Closes #38 See merge request cse1105/2025-2026/teams/csep-team-76!36
This commit is contained in:
commit
560a00cd2a
4 changed files with 377 additions and 199 deletions
|
|
@ -7,9 +7,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import client.exception.UpdateException;
|
||||
import client.scenes.recipe.IngredientListCtrl;
|
||||
import client.scenes.recipe.RecipeStepListCtrl;
|
||||
import client.scenes.recipe.RecipeDetailCtrl;
|
||||
|
||||
import client.utils.Config;
|
||||
import client.utils.ConfigService;
|
||||
|
|
@ -30,24 +28,18 @@ import javafx.scene.control.Button;
|
|||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.ToggleButton;
|
||||
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 ServerUtils server;
|
||||
private final WebSocketUtils webSocketUtils;
|
||||
private final LocaleManager localeManager;
|
||||
private final IngredientListCtrl ingredientListCtrl;
|
||||
private final RecipeStepListCtrl stepListCtrl;
|
||||
|
||||
private SearchBarCtrl searchBarCtrl;
|
||||
@FXML
|
||||
private RecipeDetailCtrl recipeDetailController;
|
||||
|
||||
public VBox detailsScreen;
|
||||
public HBox editableTitleArea;
|
||||
@FXML
|
||||
private SearchBarCtrl searchBarController;
|
||||
|
||||
// everything in the left lane
|
||||
@FXML
|
||||
|
|
@ -65,25 +57,10 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
@FXML
|
||||
public Button cloneRecipeButton;
|
||||
|
||||
@FXML
|
||||
private Button favouriteButton;
|
||||
|
||||
// stores the users favourites and load and saves them
|
||||
private Config config;
|
||||
private final ConfigService configService;
|
||||
|
||||
|
||||
// === CENTER: RECIPE DETAILS ===
|
||||
|
||||
@FXML
|
||||
private Button editRecipeTitleButton;
|
||||
|
||||
@FXML
|
||||
public Button removeRecipeButton2;
|
||||
|
||||
@FXML
|
||||
public Button printRecipeButton;
|
||||
|
||||
@FXML
|
||||
private ToggleButton favouritesOnlyToggle;
|
||||
|
||||
|
|
@ -95,20 +72,17 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
ServerUtils server,
|
||||
WebSocketUtils webSocketUtils,
|
||||
LocaleManager localeManager,
|
||||
IngredientListCtrl ingredientListCtrl,
|
||||
RecipeStepListCtrl stepListCtrl,
|
||||
ConfigService configService
|
||||
) {
|
||||
this.server = server;
|
||||
this.webSocketUtils = webSocketUtils;
|
||||
this.localeManager = localeManager;
|
||||
this.ingredientListCtrl = ingredientListCtrl;
|
||||
this.stepListCtrl = stepListCtrl;
|
||||
|
||||
this.configService = configService;
|
||||
|
||||
initializeWebSocket();
|
||||
}
|
||||
|
||||
private void initRecipeList() {
|
||||
// Show recipe name in the list
|
||||
recipeList.setCellFactory(list -> new ListCell<>() {
|
||||
|
|
@ -126,20 +100,14 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
// When your selection changes, update details in the panel
|
||||
recipeList.getSelectionModel().selectedItemProperty().addListener(
|
||||
(obs, oldRecipe, newRecipe) -> {
|
||||
showRecipeDetails(newRecipe);
|
||||
updateFavouriteButton(newRecipe);
|
||||
this.recipeDetailController.setCurrentlyViewedRecipe(newRecipe);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Inject
|
||||
void setSearchBarCtrl(SearchBarCtrl searchBarCtrl) {
|
||||
this.searchBarCtrl = searchBarCtrl;
|
||||
}
|
||||
|
||||
private void initializeSearchBar() {
|
||||
// Refresh on search bar change
|
||||
this.searchBarCtrl.setOnSearch(recipes -> {
|
||||
this.searchBarController.setOnSearch(recipes -> {
|
||||
// Don't lose selection on refresh
|
||||
Recipe currentlySelected = recipeList.getSelectionModel().getSelectedItem();
|
||||
int newIndex = -1;
|
||||
|
|
@ -178,42 +146,15 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
showRecipeDetails(recipeInList);
|
||||
this.recipeDetailController.setCurrentlyViewedRecipe(recipeInList);
|
||||
}); // runLater as it's on another non-FX thread.
|
||||
});
|
||||
});
|
||||
}
|
||||
private void initStepsIngredientsList() {
|
||||
// Initialize callback for ingredient list updates
|
||||
this.ingredientListCtrl.setUpdateCallback(newList -> {
|
||||
Recipe selectedRecipe = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selectedRecipe == null) { // edge case error for NPE.
|
||||
throw new NullPointerException("Null recipe whereas ingredients are edited");
|
||||
}
|
||||
selectedRecipe.setIngredients(newList);
|
||||
try { // propagate changes to server
|
||||
server.updateRecipe(selectedRecipe);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new UpdateException("Unable to update recipe to server for " + selectedRecipe);
|
||||
}
|
||||
});
|
||||
this.stepListCtrl.setUpdateCallback(newList -> {
|
||||
Recipe selectedRecipe = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selectedRecipe == null) { // edge case error for NPE.
|
||||
throw new NullPointerException("Null recipe whereas ingredients are edited");
|
||||
}
|
||||
selectedRecipe.setPreparationSteps(newList);
|
||||
try { // propagate changes to server
|
||||
server.updateRecipe(selectedRecipe);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new UpdateException("Unable to update recipe to server for " + selectedRecipe);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeComponents() {
|
||||
config = configService.getConfig();
|
||||
initStepsIngredientsList();
|
||||
initRecipeList();
|
||||
|
||||
// Double-click to go to detail screen
|
||||
|
|
@ -223,25 +164,18 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
openSelectedRecipe();
|
||||
}
|
||||
});
|
||||
|
||||
this.initializeSearchBar();
|
||||
refresh();
|
||||
updateFavouriteButton(recipeList.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
private void showName(String name) {
|
||||
final int NAME_FONT_SIZE = 20;
|
||||
editableTitleArea.getChildren().clear();
|
||||
Label nameLabel = new Label(name);
|
||||
nameLabel.setFont(new Font("System Bold", NAME_FONT_SIZE));
|
||||
editableTitleArea.getChildren().add(nameLabel);
|
||||
|
||||
this.recipeDetailController.setCurrentlyViewedRecipe(recipeList.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateText() {
|
||||
addRecipeButton.setText(getLocaleString("menu.button.add.recipe"));
|
||||
removeRecipeButton.setText(getLocaleString("menu.button.remove.recipe"));
|
||||
cloneRecipeButton.setText(getLocaleString("menu.button.clone"));
|
||||
editRecipeTitleButton.setText(getLocaleString("menu.button.edit"));
|
||||
removeRecipeButton2.setText(getLocaleString("menu.button.remove.recipe"));
|
||||
printRecipeButton.setText(getLocaleString("menu.button.print"));
|
||||
recipesLabel.setText(getLocaleString("menu.label.recipes"));
|
||||
}
|
||||
|
||||
|
|
@ -250,21 +184,12 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
return localeManager;
|
||||
}
|
||||
|
||||
private void showRecipeDetails(Recipe recipe) {
|
||||
if (recipe == null) {
|
||||
return;
|
||||
}
|
||||
showName(recipe.getName());
|
||||
ingredientListCtrl.refetchFromRecipe(recipe);
|
||||
stepListCtrl.refetchFromRecipe(recipe);
|
||||
}
|
||||
|
||||
// Button handlers
|
||||
@FXML
|
||||
public void refresh() {
|
||||
List<Recipe> recipes;
|
||||
try {
|
||||
recipes = server.getRecipesFiltered(searchBarCtrl.getFilter());
|
||||
recipes = server.getRecipesFiltered(searchBarController.getFilter());
|
||||
} catch (IOException | InterruptedException e) {
|
||||
recipes = Collections.emptyList();
|
||||
System.err.println("Failed to load recipes: " + e.getMessage());
|
||||
|
|
@ -303,11 +228,24 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
printError("Error occurred when adding recipe!");
|
||||
}
|
||||
// Calls edit after the recipe has been created, seamless name editing
|
||||
editRecipeTitle();
|
||||
// editRecipeTitle();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void removeSelectedRecipe() throws IOException, InterruptedException {
|
||||
private void openSelectedRecipe() {
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.recipeDetailController.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the selected recipe from the server and refreshes the recipe list.
|
||||
*/
|
||||
@FXML
|
||||
public void removeSelectedRecipe() throws IOException, InterruptedException {
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
return;
|
||||
|
|
@ -321,52 +259,6 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
refresh();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void openSelectedRecipe() {
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
return;
|
||||
}
|
||||
detailsScreen.visibleProperty().set(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revised edit recipe control flow, deprecates the use of a separate AddNameCtrl.
|
||||
* This is automagically called when a new recipe is created, making for a more seamless UX.
|
||||
*/
|
||||
@FXML
|
||||
private void editRecipeTitle() {
|
||||
editableTitleArea.getChildren().clear();
|
||||
TextField edit = new TextField();
|
||||
edit.setOnKeyPressed(event -> {
|
||||
if (event.getCode() != KeyCode.ENTER) {
|
||||
return;
|
||||
}
|
||||
String newName = edit.getText();
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
// edge case, prob won't happen but best to handle it later
|
||||
throw new NullPointerException("No recipe selected while name was changed!");
|
||||
}
|
||||
selected.setName(newName);
|
||||
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!");
|
||||
}
|
||||
showName(edit.getText());
|
||||
});
|
||||
editableTitleArea.getChildren().add(edit);
|
||||
edit.requestFocus();
|
||||
}
|
||||
@FXML
|
||||
private void makePrintable() {
|
||||
System.out.println("Recipe printed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a recipe, when clicking on the button "clone".
|
||||
*/
|
||||
|
|
@ -381,48 +273,19 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
recipeList.getSelectionModel().select(cloned);
|
||||
}
|
||||
|
||||
private void updateFavouriteButton(Recipe recipe) {
|
||||
if (recipe == null) {
|
||||
favouriteButton.setDisable(true);
|
||||
favouriteButton.setText("☆");
|
||||
return;
|
||||
}
|
||||
|
||||
favouriteButton.setDisable(false);
|
||||
favouriteButton.setText(
|
||||
config.isFavourite(recipe.getId()) ? "★" : "☆"
|
||||
);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void toggleFavourite() {
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
long id = selected.getId();
|
||||
|
||||
if (config.isFavourite(id)) {
|
||||
config.removeFavourite(id);
|
||||
} else {
|
||||
config.addFavourite(id);
|
||||
}
|
||||
|
||||
configService.save();
|
||||
|
||||
//instant ui update
|
||||
applyRecipeFilterAndKeepSelection();
|
||||
recipeList.refresh();
|
||||
updateFavouriteButton(selected);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void toggleFavouritesView() {
|
||||
applyRecipeFilterAndKeepSelection();
|
||||
}
|
||||
|
||||
private void applyRecipeFilterAndKeepSelection() {
|
||||
/**
|
||||
* Applies the recipe filter (favourites only or all) while keeping the current selection if possible.
|
||||
* If the previously selected recipe is no longer visible after applying the filter,
|
||||
* the first recipe in the filtered list will be selected instead.
|
||||
* If there are no recipes after filtering, no selection will be made.
|
||||
*
|
||||
*/
|
||||
public void applyRecipeFilterAndKeepSelection() {
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
Long selectedId = selected == null ? null : selected.getId();
|
||||
|
||||
|
|
@ -452,8 +315,8 @@ public class FoodpalApplicationCtrl implements LocaleAware {
|
|||
recipeList.getSelectionModel().selectFirst();
|
||||
}
|
||||
|
||||
updateFavouriteButton(recipeList.getSelectionModel().getSelectedItem());
|
||||
detailsScreen.visibleProperty().set(!recipeList.getItems().isEmpty());
|
||||
this.recipeDetailController.refreshFavouriteButton();
|
||||
this.recipeDetailController.setVisible(!recipeList.getItems().isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
300
client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java
Normal file
300
client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
package client.scenes.recipe;
|
||||
|
||||
import client.exception.UpdateException;
|
||||
import client.scenes.FoodpalApplicationCtrl;
|
||||
import client.utils.Config;
|
||||
import client.utils.ConfigService;
|
||||
import client.utils.LocaleAware;
|
||||
import client.utils.LocaleManager;
|
||||
import client.utils.ServerUtils;
|
||||
import com.google.inject.Inject;
|
||||
import commons.Recipe;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListView;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Controller for the recipe detail view.
|
||||
* Manages displaying and editing recipe details such as name, ingredients, and
|
||||
* steps.
|
||||
*/
|
||||
public class RecipeDetailCtrl implements LocaleAware {
|
||||
private final LocaleManager localeManager;
|
||||
private final ServerUtils server;
|
||||
private final FoodpalApplicationCtrl appCtrl;
|
||||
private final ConfigService configService;
|
||||
|
||||
@FXML
|
||||
private IngredientListCtrl ingredientListController;
|
||||
|
||||
@FXML
|
||||
private RecipeStepListCtrl stepListController;
|
||||
|
||||
private Recipe recipe;
|
||||
|
||||
@Inject
|
||||
public RecipeDetailCtrl(LocaleManager localeManager, ServerUtils server,
|
||||
FoodpalApplicationCtrl appCtrl,
|
||||
ConfigService configService) {
|
||||
this.localeManager = localeManager;
|
||||
this.server = server;
|
||||
this.appCtrl = appCtrl;
|
||||
this.configService = configService;
|
||||
}
|
||||
|
||||
@FXML
|
||||
private VBox detailsScreen;
|
||||
|
||||
@FXML
|
||||
private HBox editableTitleArea;
|
||||
|
||||
@FXML
|
||||
private Button editRecipeTitleButton;
|
||||
|
||||
@FXML
|
||||
private Button removeRecipeButton;
|
||||
|
||||
@FXML
|
||||
private Button printRecipeButton;
|
||||
|
||||
@FXML
|
||||
private Button favouriteButton;
|
||||
|
||||
private ListView<Recipe> getParentRecipeList() {
|
||||
return this.appCtrl.recipeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for a frequently needed operation to retrieve the
|
||||
* currently selected recipe.
|
||||
*
|
||||
* @return The currently selected recipe in the parent recipe list.
|
||||
*/
|
||||
private Optional<Recipe> getSelectedRecipe() {
|
||||
return Optional.ofNullable(
|
||||
this.getParentRecipeList().getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for a frequently needed operation to retrieve the global
|
||||
* application configuration.
|
||||
*
|
||||
* @return The application configuration.
|
||||
*/
|
||||
private Config getConfig() {
|
||||
return this.configService.getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the recipe list from the server.
|
||||
*
|
||||
* @throws IOException Upon invalid recipe response.
|
||||
* @throws InterruptedException Upon request interruption.
|
||||
*
|
||||
* @see FoodpalApplicationCtrl#refresh()
|
||||
*/
|
||||
private void refresh() throws IOException, InterruptedException {
|
||||
this.appCtrl.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the recipe details screen.
|
||||
*
|
||||
* @param visible Whether the details screen should be visible.
|
||||
*/
|
||||
public void setVisible(boolean visible) {
|
||||
this.detailsScreen.setVisible(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the currently viewed recipe in the detail view.
|
||||
*
|
||||
* @param recipe The recipe to view.
|
||||
*/
|
||||
public void setCurrentlyViewedRecipe(Recipe recipe) {
|
||||
if (recipe == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.recipe = recipe;
|
||||
|
||||
this.showName(recipe.getName());
|
||||
this.ingredientListController.refetchFromRecipe(recipe);
|
||||
this.stepListController.refetchFromRecipe(recipe);
|
||||
this.refreshFavouriteButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a callback that takes in the selected recipe and a helper type
|
||||
* and updates the recipe based on that.
|
||||
* Also, posts the update to the server.
|
||||
*
|
||||
* @param recipeConsumer The helper function to use when updating the recipe -
|
||||
* how to update it
|
||||
* @return The created callback to use in a setUpdateCallback or related
|
||||
* function
|
||||
*/
|
||||
private <T> Consumer<T> createUpdateRecipeCallback(BiConsumer<Recipe, T> recipeConsumer) {
|
||||
return recipes -> {
|
||||
Recipe selectedRecipe = this.getSelectedRecipe().orElseThrow(
|
||||
() -> new NullPointerException(
|
||||
"Null recipe whereas ingredients are edited"));
|
||||
|
||||
recipeConsumer.accept(selectedRecipe, recipes);
|
||||
|
||||
try { // propagate changes to server
|
||||
server.updateRecipe(selectedRecipe);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new UpdateException("Unable to update recipe to server for " +
|
||||
selectedRecipe);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the ingredient and step list controllers with update callbacks.
|
||||
*/
|
||||
private void initStepsIngredientsList() {
|
||||
// code does NOT get nicer than this :pray:
|
||||
|
||||
// Initialize callback for ingredient list updates
|
||||
this.ingredientListController.setUpdateCallback(
|
||||
this.createUpdateRecipeCallback(Recipe::setIngredients));
|
||||
// Ditto, for step list updates
|
||||
this.stepListController.setUpdateCallback(
|
||||
this.createUpdateRecipeCallback(Recipe::setPreparationSteps));
|
||||
}
|
||||
|
||||
/**
|
||||
* Revised edit recipe control flow, deprecates the use of a separate
|
||||
* AddNameCtrl. This is automagically called when a new recipe is created,
|
||||
* making for a more seamless UX.
|
||||
*/
|
||||
@FXML
|
||||
private void editRecipeTitle() {
|
||||
this.editableTitleArea.getChildren().clear();
|
||||
TextField edit = new TextField();
|
||||
|
||||
edit.setOnKeyPressed(event -> {
|
||||
if (event.getCode() != KeyCode.ENTER) {
|
||||
return;
|
||||
}
|
||||
|
||||
String newName = edit.getText();
|
||||
this.recipe.setName(newName);
|
||||
|
||||
try {
|
||||
server.updateRecipe(this.recipe);
|
||||
this.refresh();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
// throw a nice blanket UpdateException
|
||||
throw new UpdateException("Error occurred when updating recipe name!");
|
||||
}
|
||||
|
||||
this.showName(edit.getText());
|
||||
});
|
||||
|
||||
editableTitleArea.getChildren().add(edit);
|
||||
edit.requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the selected recipe from the server and refresh the recipe list.
|
||||
* Internally calls {@link FoodpalApplicationCtrl#removeSelectedRecipe()}.
|
||||
*/
|
||||
@FXML
|
||||
private void removeSelectedRecipe() throws IOException, InterruptedException {
|
||||
this.appCtrl.removeSelectedRecipe();
|
||||
}
|
||||
|
||||
@FXML
|
||||
/**
|
||||
* Print the currently viewed recipe.
|
||||
*/
|
||||
private void printRecipe() {
|
||||
// TODO: actually make it print?
|
||||
System.out.println("Recipe printed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the favourite button state based on whether the currently viewed
|
||||
* recipe is marked as a favourite in the application configuration.
|
||||
*/
|
||||
public void refreshFavouriteButton() {
|
||||
if (recipe == null) {
|
||||
favouriteButton.setDisable(true);
|
||||
favouriteButton.setText("☆");
|
||||
return;
|
||||
}
|
||||
|
||||
favouriteButton.setDisable(false);
|
||||
favouriteButton.setText(this.getConfig().isFavourite(recipe.getId()) ? "★"
|
||||
: "☆");
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the favourite status of the currently viewed recipe in the
|
||||
* application configuration and writes the changes to disk.
|
||||
*/
|
||||
@FXML
|
||||
private void toggleFavourite() {
|
||||
long id = this.recipe.getId();
|
||||
|
||||
Config config = this.getConfig();
|
||||
if (config.isFavourite(id)) {
|
||||
config.removeFavourite(id);
|
||||
} else {
|
||||
config.addFavourite(id);
|
||||
}
|
||||
|
||||
configService.save();
|
||||
|
||||
// instant ui update
|
||||
appCtrl.applyRecipeFilterAndKeepSelection();
|
||||
this.getParentRecipeList().refresh();
|
||||
refreshFavouriteButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the title area to show the given name.
|
||||
*
|
||||
* @param name The name to show.
|
||||
*/
|
||||
private void showName(String name) {
|
||||
final int NAME_FONT_SIZE = 20;
|
||||
|
||||
editableTitleArea.getChildren().clear();
|
||||
|
||||
Label nameLabel = new Label(name);
|
||||
nameLabel.setFont(new Font("System Bold", NAME_FONT_SIZE));
|
||||
|
||||
editableTitleArea.getChildren().add(nameLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateText() {
|
||||
editRecipeTitleButton.setText(getLocaleString("menu.button.edit"));
|
||||
removeRecipeButton.setText(getLocaleString("menu.button.remove.recipe"));
|
||||
printRecipeButton.setText(getLocaleString("menu.button.print"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocaleManager getLocaleManager() {
|
||||
return this.localeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeComponents() {
|
||||
initStepsIngredientsList();
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
<Font name="System Bold" size="15.0" />
|
||||
</font></Label>
|
||||
|
||||
<fx:include source="SearchBar.fxml" />
|
||||
<fx:include source="SearchBar.fxml" fx:id="searchBar" />
|
||||
|
||||
<ListView fx:id="recipeList" />
|
||||
|
||||
|
|
@ -61,27 +61,7 @@
|
|||
|
||||
<!-- CENTER: RECIPE DETAILS -->
|
||||
<center>
|
||||
<VBox spacing="20" visible="false" fx:id="detailsScreen">
|
||||
<padding>
|
||||
<Insets bottom="10" left="10" right="10" top="10" />
|
||||
</padding>
|
||||
|
||||
<!-- Recipe title row -->
|
||||
<HBox spacing="10">
|
||||
<HBox fx:id="editableTitleArea">
|
||||
</HBox>
|
||||
<Button fx:id="editRecipeTitleButton" onAction="#editRecipeTitle" text="Edit" />
|
||||
<Button fx:id="removeRecipeButton2" mnemonicParsing="false" onAction="#removeSelectedRecipe" text="Remove Recipe" />
|
||||
<Button fx:id="printRecipeButton" mnemonicParsing="false" onAction="#makePrintable" text="Print Recipe" />
|
||||
<Button fx:id="favouriteButton" onAction="#toggleFavourite" text="☆" />
|
||||
</HBox>
|
||||
|
||||
<!-- Ingredients -->
|
||||
<fx:include source="recipe/IngredientList.fxml" />
|
||||
|
||||
<!-- Preparation -->
|
||||
<fx:include source="recipe/RecipeStepList.fxml" />
|
||||
</VBox>
|
||||
<fx:include source="recipe/RecipeDetailView.fxml" fx:id="recipeDetail" />
|
||||
</center>
|
||||
|
||||
</BorderPane>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.*?>
|
||||
<?import java.util.*?>
|
||||
<?import javafx.scene.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
|
||||
<VBox
|
||||
xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="client.scenes.recipe.RecipeDetailCtrl"
|
||||
spacing="20" visible="false" fx:id="detailsScreen">
|
||||
|
||||
<padding>
|
||||
<Insets bottom="10" left="10" right="10" top="10" />
|
||||
</padding>
|
||||
|
||||
<!-- Recipe title row -->
|
||||
<HBox spacing="10">
|
||||
<HBox fx:id="editableTitleArea">
|
||||
</HBox>
|
||||
<Button fx:id="editRecipeTitleButton" onAction="#editRecipeTitle" text="Edit" />
|
||||
<Button fx:id="removeRecipeButton" mnemonicParsing="false" onAction="#removeSelectedRecipe" text="Remove Recipe" />
|
||||
<Button fx:id="printRecipeButton" mnemonicParsing="false" onAction="#printRecipe" text="Print Recipe" />
|
||||
<Button fx:id="favouriteButton" onAction="#toggleFavourite" text="☆" />
|
||||
</HBox>
|
||||
|
||||
<!-- Ingredients -->
|
||||
<fx:include source="IngredientList.fxml" fx:id="ingredientList" />
|
||||
|
||||
<!-- Preparation -->
|
||||
<fx:include source="RecipeStepList.fxml" fx:id="stepList" />
|
||||
</VBox>
|
||||
Loading…
Add table
Add a link
Reference in a new issue