feat: recipe step controller, cell and fxml, add docs for everything, slightly refactor IngredientListCtrl
This commit is contained in:
parent
14b2b2259a
commit
1cb54d0592
6 changed files with 426 additions and 90 deletions
|
|
@ -2,8 +2,18 @@ package client.scenes.recipe;
|
||||||
|
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom ListCell for displaying and editing ingredients in an
|
||||||
|
* IngredientList. Allows inline editing of ingredient names.
|
||||||
|
*
|
||||||
|
* @see IngredientListCtrl
|
||||||
|
*/
|
||||||
public class IngredientListCell extends ListCell<String> {
|
public class IngredientListCell extends ListCell<String> {
|
||||||
|
// TODO: change all this to use actual Ingredient objects
|
||||||
|
// and all that :)
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateItem(String item, boolean empty) {
|
protected void updateItem(String item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
|
|
@ -28,12 +38,8 @@ public class IngredientListCell extends ListCell<String> {
|
||||||
|
|
||||||
// Cancel edit on Escape key press
|
// Cancel edit on Escape key press
|
||||||
textField.setOnKeyReleased(event -> {
|
textField.setOnKeyReleased(event -> {
|
||||||
switch (event.getCode()) {
|
if (event.getCode() == KeyCode.ESCAPE) {
|
||||||
case ESCAPE:
|
this.cancelEdit();
|
||||||
this.cancelEdit();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -41,6 +47,12 @@ public class IngredientListCell extends ListCell<String> {
|
||||||
this.setGraphic(textField);
|
this.setGraphic(textField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the input is valid (non-empty).
|
||||||
|
*
|
||||||
|
* @param input The input string to validate.
|
||||||
|
* @return true if valid, false otherwise.
|
||||||
|
*/
|
||||||
private boolean isInputValid(String input) {
|
private boolean isInputValid(String input) {
|
||||||
return input != null && !input.trim().isEmpty();
|
return input != null && !input.trim().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,99 +15,136 @@ import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
import javafx.scene.control.ListView.EditEvent;
|
import javafx.scene.control.ListView.EditEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the ingredient list view in the recipe detail scene.
|
||||||
|
* Manages displaying, adding, editing, and deleting ingredients.
|
||||||
|
*
|
||||||
|
* @see RecipeStepListCell The custom cell implementation.
|
||||||
|
*/
|
||||||
public class IngredientListCtrl implements Initializable {
|
public class IngredientListCtrl implements Initializable {
|
||||||
private ObservableList<String> ingredients;
|
/**
|
||||||
private Function<List<String>, Void> updateCallback;
|
* <p>
|
||||||
|
* The list of ingredients currently displayed.
|
||||||
|
* <br>
|
||||||
|
* As the ingredient list in {@link Recipe} is immutable,
|
||||||
|
* this copies that list upon initialization to this mutable list.
|
||||||
|
* <br>
|
||||||
|
* Please use the {@link #setUpdateCallback(Function)} function to listen for
|
||||||
|
* changes and update the recipe accordingly.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private ObservableList<String> ingredients;
|
||||||
|
|
||||||
@FXML ListView<String> ingredientListView;
|
/**
|
||||||
|
* A callback function that is called when the ingredient list is updated.
|
||||||
|
*/
|
||||||
|
private Function<List<String>, Void> updateCallback;
|
||||||
|
|
||||||
@FXML Button addIngredientButton;
|
@FXML
|
||||||
@FXML Button deleteIngredientButton;
|
ListView<String> ingredientListView;
|
||||||
|
|
||||||
/**
|
@FXML
|
||||||
* Set the recipe and refetch data from it.
|
Button addIngredientButton;
|
||||||
*
|
@FXML
|
||||||
* @param recipe The recipe to fetch data from.
|
Button deleteIngredientButton;
|
||||||
*/
|
|
||||||
public void refetchFromRecipe(Recipe recipe) {
|
/**
|
||||||
if (recipe == null) {
|
* Set the recipe and refetch data from it.
|
||||||
this.ingredients = FXCollections.observableArrayList(new ArrayList<>());
|
* This replaces the current ingredient list.
|
||||||
} else {
|
* Only use this once per recipe when initializing the detail view.
|
||||||
List<String> ingredientList = recipe.getIngredients();
|
*
|
||||||
this.ingredients = FXCollections.observableArrayList(ingredientList);
|
* @param recipe The recipe to fetch data from.
|
||||||
|
*/
|
||||||
|
public void refetchFromRecipe(Recipe recipe) {
|
||||||
|
if (recipe == null) {
|
||||||
|
this.ingredients = FXCollections.observableArrayList(new ArrayList<>());
|
||||||
|
} else {
|
||||||
|
List<String> ingredientList = recipe.getIngredients();
|
||||||
|
this.ingredients = FXCollections.observableArrayList(ingredientList);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ingredientListView.setItems(this.ingredients);
|
||||||
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ingredientListView.setItems(this.ingredients);
|
/**
|
||||||
this.refresh();
|
* Set a callback that's called when the ingredient list changes.
|
||||||
}
|
*
|
||||||
|
* @param callback The function to call upon each update.
|
||||||
/**
|
*/
|
||||||
* Set a callback that's called when the ingredient list changes.
|
public void setUpdateCallback(Function<List<String>, Void> callback) {
|
||||||
*
|
this.updateCallback = callback;
|
||||||
* @param callback The function to call upon each update.
|
|
||||||
*/
|
|
||||||
public void setUpdateCallback(Function<List<String>, Void> callback) {
|
|
||||||
this.updateCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refresh() { ingredientListView.refresh(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle ingredient addition. Automatically calls update callback.
|
|
||||||
*/
|
|
||||||
private void handleIngredientAdd(ActionEvent event) {
|
|
||||||
this.ingredients.add("Ingredient " + (this.ingredients.size() + 1));
|
|
||||||
this.refresh();
|
|
||||||
this.updateCallback.apply(this.ingredients);
|
|
||||||
|
|
||||||
var select = this.ingredientListView.getSelectionModel();
|
|
||||||
select.select(this.ingredients.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle ingredient edits. Automatically calls update callback.
|
|
||||||
*/
|
|
||||||
private void handleIngredientEdit(EditEvent<String> event) {
|
|
||||||
int index = event.getIndex();
|
|
||||||
String newValue = event.getNewValue();
|
|
||||||
|
|
||||||
this.ingredients.set(index, newValue);
|
|
||||||
this.refresh();
|
|
||||||
this.updateCallback.apply(this.ingredients);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle ingredient deletion. Automatically calls update callback.
|
|
||||||
*/
|
|
||||||
private void handleIngredientDelete(ActionEvent event) {
|
|
||||||
var select = this.ingredientListView.getSelectionModel();
|
|
||||||
int selectedIndex = select.getSelectedIndex();
|
|
||||||
// No index is selected, don't do anything
|
|
||||||
if (selectedIndex < 0) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ingredients.remove(selectedIndex);
|
/**
|
||||||
this.refresh();
|
* Refresh the ingredient list view.
|
||||||
this.updateCallback.apply(this.ingredients);
|
*/
|
||||||
}
|
private void refresh() {
|
||||||
|
ingredientListView.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
/**
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
* Handle ingredient addition. Automatically calls update callback.
|
||||||
// TODO: set up communication with the server
|
*
|
||||||
// this would probably be best done with the callback (so this class doesn't
|
* @param event The action event.
|
||||||
// interact with the server at all)
|
*/
|
||||||
|
private void handleIngredientAdd(ActionEvent event) {
|
||||||
|
this.ingredients.add("Ingredient " + (this.ingredients.size() + 1));
|
||||||
|
this.refresh();
|
||||||
|
this.updateCallback.apply(this.ingredients);
|
||||||
|
|
||||||
this.ingredientListView.setEditable(true);
|
var select = this.ingredientListView.getSelectionModel();
|
||||||
this.ingredientListView.setCellFactory(
|
select.select(this.ingredients.size() - 1);
|
||||||
list -> { return new IngredientListCell(); });
|
}
|
||||||
|
|
||||||
this.ingredientListView.setOnEditCommit(
|
/**
|
||||||
event -> handleIngredientEdit(event));
|
* Handle ingredient edits. Automatically calls update callback.
|
||||||
this.addIngredientButton.setOnAction(event -> handleIngredientAdd(event));
|
*
|
||||||
this.deleteIngredientButton.setOnAction(
|
* @param event The edit event.
|
||||||
event -> handleIngredientDelete(event));
|
*/
|
||||||
|
private void handleIngredientEdit(EditEvent<String> event) {
|
||||||
|
int index = event.getIndex();
|
||||||
|
String newValue = event.getNewValue();
|
||||||
|
|
||||||
this.refresh();
|
this.ingredients.set(index, newValue);
|
||||||
}
|
this.refresh();
|
||||||
|
this.updateCallback.apply(this.ingredients);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle ingredient deletion. Automatically calls update callback.
|
||||||
|
*
|
||||||
|
* @param event The action event.
|
||||||
|
*/
|
||||||
|
private void handleIngredientDelete(ActionEvent event) {
|
||||||
|
var select = this.ingredientListView.getSelectionModel();
|
||||||
|
int selectedIndex = select.getSelectedIndex();
|
||||||
|
// No index is selected, don't do anything
|
||||||
|
if (selectedIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ingredients.remove(selectedIndex);
|
||||||
|
this.refresh();
|
||||||
|
this.updateCallback.apply(this.ingredients);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
// TODO: set up communication with the server
|
||||||
|
// this would probably be best done with the callback (so this class doesn't
|
||||||
|
// interact with the server at all)
|
||||||
|
|
||||||
|
this.ingredientListView.setEditable(true);
|
||||||
|
this.ingredientListView.setCellFactory(
|
||||||
|
list -> {
|
||||||
|
return new RecipeStepListCell();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.ingredientListView.setOnEditCommit(this::handleIngredientEdit);
|
||||||
|
this.addIngredientButton.setOnAction(this::handleIngredientAdd);
|
||||||
|
this.deleteIngredientButton.setOnAction(this::handleIngredientDelete);
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
package client.scenes.recipe;
|
||||||
|
|
||||||
|
import javafx.scene.control.ListCell;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom ListCell for displaying and editing ingredients in an
|
||||||
|
* RecipeStepList. Allows inline editing of ingredient names.
|
||||||
|
*
|
||||||
|
* @see RecipeStepListCtrl
|
||||||
|
*/
|
||||||
|
public class RecipeStepListCell extends ListCell<String> {
|
||||||
|
/**
|
||||||
|
* Get the display text for the given item, prefixed with its index.
|
||||||
|
* Looks like "1. Step description".
|
||||||
|
*
|
||||||
|
* @param item The step description.
|
||||||
|
* @return The display text.
|
||||||
|
*/
|
||||||
|
private String getDisplayText(String item) {
|
||||||
|
return this.getIndex() + ". " + item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the display text for the current item.
|
||||||
|
* Looks like "1. Step description".
|
||||||
|
*
|
||||||
|
* @return The display text.
|
||||||
|
*/
|
||||||
|
private String getDisplayText() {
|
||||||
|
return this.getDisplayText(this.getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateItem(String item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
|
||||||
|
if (empty || item == null) {
|
||||||
|
this.setText(null);
|
||||||
|
} else {
|
||||||
|
this.setText(this.getDisplayText(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startEdit() {
|
||||||
|
super.startEdit();
|
||||||
|
|
||||||
|
TextField textField = new TextField(this.getItem());
|
||||||
|
|
||||||
|
// Commit edit on Enter key press
|
||||||
|
textField.setOnAction(event -> {
|
||||||
|
this.commitEdit(textField.getText());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel edit on Escape key press
|
||||||
|
textField.setOnKeyReleased(event -> {
|
||||||
|
if (event.getCode() == KeyCode.ESCAPE) {
|
||||||
|
this.cancelEdit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setText(null);
|
||||||
|
this.setGraphic(textField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the input is valid (non-empty).
|
||||||
|
*
|
||||||
|
* @param input The input string to validate.
|
||||||
|
* @return true if valid, false otherwise.
|
||||||
|
*/
|
||||||
|
private boolean isInputValid(String input) {
|
||||||
|
return input != null && !input.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelEdit() {
|
||||||
|
super.cancelEdit();
|
||||||
|
|
||||||
|
this.setText(this.getDisplayText());
|
||||||
|
this.setGraphic(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commitEdit(String newValue) {
|
||||||
|
this.setGraphic(null);
|
||||||
|
|
||||||
|
if (!isInputValid(newValue)) {
|
||||||
|
newValue = this.getItem(); // Revert to old value if input is invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
super.commitEdit(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
package client.scenes.recipe;
|
||||||
|
|
||||||
|
import commons.Recipe;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.control.ListView.EditEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the step list view in the recipe detail scene.
|
||||||
|
* Manages displaying, adding, editing, and deleting steps.
|
||||||
|
*
|
||||||
|
* @see RecipeStepListCell The custom cell implementation.
|
||||||
|
*/
|
||||||
|
public class RecipeStepListCtrl implements Initializable {
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The list of recipe steps currently displayed.
|
||||||
|
* <br>
|
||||||
|
* As the step list in {@link Recipe} is immutable,
|
||||||
|
* this copies that list upon initialization to this mutable list.
|
||||||
|
* <br>
|
||||||
|
* Please use the {@link #setUpdateCallback(Function)} function to listen for
|
||||||
|
* changes and update the recipe accordingly.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private ObservableList<String> steps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback function that is called when the step list is updated.
|
||||||
|
*/
|
||||||
|
private Function<List<String>, Void> updateCallback;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
ListView<String> recipeStepListView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
Button addStepButton;
|
||||||
|
@FXML
|
||||||
|
Button deleteStepButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the recipe and refetch data from it.
|
||||||
|
* This replaces the current step list.
|
||||||
|
* Only use this once per recipe when initializing the detail view.
|
||||||
|
*
|
||||||
|
* @param recipe The recipe to fetch data from.
|
||||||
|
*/
|
||||||
|
public void refetchFromRecipe(Recipe recipe) {
|
||||||
|
if (recipe == null) {
|
||||||
|
this.steps = FXCollections.observableArrayList(new ArrayList<>());
|
||||||
|
} else {
|
||||||
|
List<String> stepList = recipe.getPreparationSteps();
|
||||||
|
this.steps = FXCollections.observableArrayList(stepList);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recipeStepListView.setItems(this.steps);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a callback that's called when the step list changes.
|
||||||
|
*
|
||||||
|
* @param callback The function to call upon each update.
|
||||||
|
*/
|
||||||
|
public void setUpdateCallback(Function<List<String>, Void> callback) {
|
||||||
|
this.updateCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh the step list view.
|
||||||
|
*/
|
||||||
|
private void refresh() {
|
||||||
|
recipeStepListView.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle step addition. Automatically calls update callback.
|
||||||
|
*
|
||||||
|
* @param event The action event.
|
||||||
|
*/
|
||||||
|
private void handleIngredientAdd(ActionEvent event) {
|
||||||
|
this.steps.add("Step " + (this.steps.size() + 1));
|
||||||
|
this.refresh();
|
||||||
|
this.updateCallback.apply(this.steps);
|
||||||
|
|
||||||
|
var select = this.recipeStepListView.getSelectionModel();
|
||||||
|
select.select(this.steps.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle step edits. Automatically calls update callback.
|
||||||
|
*
|
||||||
|
* @param event The edit event.
|
||||||
|
*/
|
||||||
|
private void handleIngredientEdit(EditEvent<String> event) {
|
||||||
|
int index = event.getIndex();
|
||||||
|
String newValue = event.getNewValue();
|
||||||
|
|
||||||
|
this.steps.set(index, newValue);
|
||||||
|
this.refresh();
|
||||||
|
this.updateCallback.apply(this.steps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle step deletion. Automatically calls update callback.
|
||||||
|
*
|
||||||
|
* @param event The action event.
|
||||||
|
*/
|
||||||
|
private void handleIngredientDelete(ActionEvent event) {
|
||||||
|
var select = this.recipeStepListView.getSelectionModel();
|
||||||
|
int selectedIndex = select.getSelectedIndex();
|
||||||
|
// No index is selected, don't do anything
|
||||||
|
if (selectedIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.steps.remove(selectedIndex);
|
||||||
|
this.refresh();
|
||||||
|
this.updateCallback.apply(this.steps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
// TODO: set up communication with the server
|
||||||
|
// this would probably be best done with the callback (so this class doesn't
|
||||||
|
// interact with the server at all)
|
||||||
|
|
||||||
|
this.recipeStepListView.setEditable(true);
|
||||||
|
this.recipeStepListView.setCellFactory(
|
||||||
|
list -> {
|
||||||
|
return new RecipeStepListCell();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.recipeStepListView.setOnEditCommit(this::handleIngredientEdit);
|
||||||
|
this.addStepButton.setOnAction(this::handleIngredientAdd);
|
||||||
|
this.deleteStepButton.setOnAction(this::handleIngredientDelete);
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<AnchorPane xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.recipe.IngredientListCtrl">
|
<AnchorPane xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.recipe.RecipeStepListCtrl">
|
||||||
<children>
|
<children>
|
||||||
<VBox minHeight="200.0" minWidth="200.0">
|
<VBox minHeight="200.0" minWidth="200.0">
|
||||||
<children>
|
<children>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.recipe.RecipeStepListCtrl">
|
||||||
|
<children>
|
||||||
|
<VBox minHeight="200.0" minWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="20.0">
|
||||||
|
<children>
|
||||||
|
<Label text="Steps">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="5.0" top="5.0" />
|
||||||
|
</padding>
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="16.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<HBox alignment="CENTER" spacing="10.0">
|
||||||
|
<children>
|
||||||
|
<Button fx:id="addStepButton" mnemonicParsing="false" text="Add" />
|
||||||
|
<Button fx:id="deleteStepButton" mnemonicParsing="false" text="Delete" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets left="10.0" right="10.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
<ListView fx:id="recipeStepListView" prefHeight="200.0" prefWidth="200.0" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue