Merge remote-tracking branch 'origin/main' into feature/backend-websockets
This commit is contained in:
commit
cfacc37739
37 changed files with 2333 additions and 254 deletions
|
|
@ -17,15 +17,14 @@ package client;
|
|||
|
||||
import static com.google.inject.Guice.createInjector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import client.scenes.AddStepsCtrl;
|
||||
import client.scenes.MainCtrl;
|
||||
import client.scenes.AddIngredientCtrl;
|
||||
import client.scenes.AddNameCtrl;
|
||||
import client.scenes.FoodpalApplicationCtrl;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import client.scenes.AddQuoteCtrl;
|
||||
import client.scenes.MainCtrl;
|
||||
import client.scenes.QuoteOverviewCtrl;
|
||||
import client.utils.ServerUtils;
|
||||
import client.utils.ServerUtilsExample;
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
|
|
@ -34,24 +33,26 @@ public class Main extends Application {
|
|||
private static final Injector INJECTOR = createInjector(new MyModule());
|
||||
private static final MyFXML FXML = new MyFXML(INJECTOR);
|
||||
|
||||
public static void main(String[] args) throws URISyntaxException, IOException {
|
||||
public static void main(String[] args){
|
||||
launch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
|
||||
var serverUtils = INJECTOR.getInstance(ServerUtils.class);
|
||||
var serverUtils = INJECTOR.getInstance(ServerUtilsExample.class);
|
||||
if (!serverUtils.isServerAvailable()) {
|
||||
var msg = "Server needs to be started before the client, but it does not seem to be available. Shutting down.";
|
||||
System.err.println(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
var overview = FXML.load(QuoteOverviewCtrl.class, "client", "scenes", "QuoteOverview.fxml");
|
||||
var add = FXML.load(AddQuoteCtrl.class, "client", "scenes", "AddQuote.fxml");
|
||||
var addName = FXML.load(AddNameCtrl.class, "client", "scenes", "AddName.fxml");
|
||||
var foodpal = FXML.load(FoodpalApplicationCtrl.class, "client", "scenes", "FoodpalApplication.fxml");
|
||||
var addIngredient = FXML.load(AddIngredientCtrl.class, "client", "scenes", "AddIngredient.fxml");
|
||||
var addStep = FXML.load(AddStepsCtrl.class, "client", "scenes", "AddSteps.fxml");
|
||||
|
||||
var mainCtrl = INJECTOR.getInstance(MainCtrl.class);
|
||||
mainCtrl.initialize(primaryStage, overview, add);
|
||||
mainCtrl.setup(primaryStage, addName, foodpal, addIngredient, addStep);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,20 +15,23 @@
|
|||
*/
|
||||
package client;
|
||||
|
||||
import client.scenes.FoodpalApplicationCtrl;
|
||||
import client.utils.LocaleManager;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Scopes;
|
||||
|
||||
import client.scenes.AddQuoteCtrl;
|
||||
import client.scenes.AddNameCtrl;
|
||||
import client.scenes.MainCtrl;
|
||||
import client.scenes.QuoteOverviewCtrl;
|
||||
|
||||
public class MyModule implements Module {
|
||||
|
||||
@Override
|
||||
public void configure(Binder binder) {
|
||||
binder.bind(MainCtrl.class).in(Scopes.SINGLETON);
|
||||
binder.bind(AddQuoteCtrl.class).in(Scopes.SINGLETON);
|
||||
binder.bind(QuoteOverviewCtrl.class).in(Scopes.SINGLETON);
|
||||
binder.bind(AddNameCtrl.class).in(Scopes.SINGLETON);
|
||||
binder.bind(FoodpalApplicationCtrl.class).in(Scopes.SINGLETON);
|
||||
|
||||
binder.bind(LocaleManager.class).in(Scopes.SINGLETON);
|
||||
}
|
||||
}
|
||||
115
client/src/main/java/client/scenes/AddIngredientCtrl.java
Normal file
115
client/src/main/java/client/scenes/AddIngredientCtrl.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2021 Delft University of Technology
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package client.scenes;
|
||||
|
||||
import client.utils.LocaleAware;
|
||||
import client.utils.LocaleManager;
|
||||
import client.utils.ServerUtils;
|
||||
import com.google.inject.Inject;
|
||||
import commons.Recipe;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.stage.Modality;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AddIngredientCtrl implements LocaleAware {
|
||||
|
||||
private final ServerUtils server;
|
||||
private final MainCtrl mainCtrl;
|
||||
private final FoodpalApplicationCtrl foodpalCtrl;
|
||||
|
||||
private final LocaleManager localeManager;
|
||||
|
||||
@FXML
|
||||
public TextField ingredient;
|
||||
|
||||
@FXML
|
||||
public Button okButton;
|
||||
|
||||
@FXML
|
||||
public Button cancelButton;
|
||||
|
||||
@FXML
|
||||
public Label ingredientLabel;
|
||||
|
||||
@Inject
|
||||
public AddIngredientCtrl(ServerUtils server, MainCtrl mainCtrl,
|
||||
FoodpalApplicationCtrl foodpalCtrl, LocaleManager localeManager) {
|
||||
this.mainCtrl = mainCtrl;
|
||||
this.server = server;
|
||||
this.foodpalCtrl = foodpalCtrl;
|
||||
this.localeManager = localeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateText() {
|
||||
ingredientLabel.setText(getLocaleString("add.ingredient.label"));
|
||||
okButton.setText(getLocaleString("button.ok"));
|
||||
cancelButton.setText(getLocaleString("button.cancel"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocaleManager getLocaleManager() {
|
||||
return localeManager;
|
||||
}
|
||||
|
||||
public void cancel() throws IOException, InterruptedException {
|
||||
clearFields();
|
||||
mainCtrl.showFoodpal();
|
||||
}
|
||||
|
||||
public void ok() throws IOException, InterruptedException {
|
||||
try {
|
||||
Recipe selected = foodpalCtrl.getSelectedRecipe();
|
||||
server.addRecipeIngredient(selected, ingredient.getText());
|
||||
} catch (WebApplicationException e) {
|
||||
|
||||
var alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.initModality(Modality.APPLICATION_MODAL);
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
clearFields();
|
||||
mainCtrl.showFoodpal();
|
||||
}
|
||||
|
||||
private void clearFields() {
|
||||
ingredient.clear();
|
||||
}
|
||||
|
||||
public void keyPressed(KeyEvent e) throws IOException, InterruptedException {
|
||||
switch (e.getCode()) {
|
||||
case ENTER:
|
||||
ok();
|
||||
break;
|
||||
case ESCAPE:
|
||||
cancel();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
112
client/src/main/java/client/scenes/AddNameCtrl.java
Normal file
112
client/src/main/java/client/scenes/AddNameCtrl.java
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2021 Delft University of Technology
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package client.scenes;
|
||||
|
||||
import client.utils.LocaleAware;
|
||||
import client.utils.LocaleManager;
|
||||
import client.utils.ServerUtils;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.stage.Modality;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AddNameCtrl implements LocaleAware {
|
||||
|
||||
private final ServerUtils server;
|
||||
private final MainCtrl mainCtrl;
|
||||
|
||||
private final LocaleManager localeManager;
|
||||
|
||||
@FXML
|
||||
public TextField recipeName;
|
||||
|
||||
@FXML
|
||||
public Label recipeNameLabel;
|
||||
|
||||
@FXML
|
||||
public Button cancelButton;
|
||||
|
||||
@FXML
|
||||
public Button okButton;
|
||||
|
||||
@Inject
|
||||
public AddNameCtrl(ServerUtils server, MainCtrl mainCtrl, LocaleManager localeManager) {
|
||||
this.mainCtrl = mainCtrl;
|
||||
this.server = server;
|
||||
this.localeManager = localeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateText() {
|
||||
recipeNameLabel.setText(getLocaleString("add.recipe.label"));
|
||||
okButton.setText(getLocaleString("button.ok"));
|
||||
cancelButton.setText(getLocaleString("button.cancel"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocaleManager getLocaleManager() {
|
||||
return localeManager;
|
||||
}
|
||||
|
||||
public void cancel() throws IOException, InterruptedException {
|
||||
clearFields();
|
||||
mainCtrl.showFoodpal();
|
||||
}
|
||||
|
||||
public void ok() throws IOException, InterruptedException {
|
||||
try {
|
||||
server.addRecipeName(recipeName.getText());
|
||||
} catch (WebApplicationException e) {
|
||||
|
||||
var alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.initModality(Modality.APPLICATION_MODAL);
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
return;
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
clearFields();
|
||||
mainCtrl.showFoodpal();
|
||||
}
|
||||
|
||||
private void clearFields() {
|
||||
recipeName.clear();
|
||||
}
|
||||
|
||||
public void keyPressed(KeyEvent e) throws IOException, InterruptedException {
|
||||
switch (e.getCode()) {
|
||||
case ENTER:
|
||||
ok();
|
||||
break;
|
||||
case ESCAPE:
|
||||
cancel();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
114
client/src/main/java/client/scenes/AddStepsCtrl.java
Normal file
114
client/src/main/java/client/scenes/AddStepsCtrl.java
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2021 Delft University of Technology
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package client.scenes;
|
||||
|
||||
import client.utils.LocaleAware;
|
||||
import client.utils.LocaleManager;
|
||||
import client.utils.ServerUtils;
|
||||
import com.google.inject.Inject;
|
||||
import commons.Recipe;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.stage.Modality;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AddStepsCtrl implements LocaleAware {
|
||||
|
||||
private final ServerUtils server;
|
||||
private final MainCtrl mainCtrl;
|
||||
private final FoodpalApplicationCtrl foodpalCtrl;
|
||||
private final LocaleManager localeManager;
|
||||
|
||||
@FXML
|
||||
public TextField preparationStep;
|
||||
|
||||
@FXML
|
||||
public Button okButton;
|
||||
|
||||
@FXML
|
||||
public Button cancelButton;
|
||||
|
||||
@FXML
|
||||
public Label preparationStepLabel;
|
||||
|
||||
@Inject
|
||||
public AddStepsCtrl(ServerUtils server, MainCtrl mainCtrl,
|
||||
FoodpalApplicationCtrl foodpalCtrl, LocaleManager localeManager) {
|
||||
this.mainCtrl = mainCtrl;
|
||||
this.server = server;
|
||||
this.foodpalCtrl = foodpalCtrl;
|
||||
this.localeManager = localeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateText() {
|
||||
preparationStepLabel.setText(getLocaleString("add.step.label"));
|
||||
okButton.setText(getLocaleString("button.ok"));
|
||||
cancelButton.setText(getLocaleString("button.cancel"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocaleManager getLocaleManager() {
|
||||
return localeManager;
|
||||
}
|
||||
|
||||
public void cancel() throws IOException, InterruptedException {
|
||||
clearFields();
|
||||
mainCtrl.showFoodpal();
|
||||
}
|
||||
|
||||
public void ok() throws IOException, InterruptedException {
|
||||
try {
|
||||
Recipe selected = foodpalCtrl.getSelectedRecipe();
|
||||
server.addRecipeStep(selected, preparationStep.getText());
|
||||
} catch (WebApplicationException e) {
|
||||
|
||||
var alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.initModality(Modality.APPLICATION_MODAL);
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
clearFields();
|
||||
mainCtrl.showFoodpal();
|
||||
}
|
||||
|
||||
private void clearFields() {
|
||||
preparationStep.clear();
|
||||
}
|
||||
|
||||
public void keyPressed(KeyEvent e) throws IOException, InterruptedException {
|
||||
switch (e.getCode()) {
|
||||
case ENTER:
|
||||
ok();
|
||||
break;
|
||||
case ESCAPE:
|
||||
cancel();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
276
client/src/main/java/client/scenes/FoodpalApplicationCtrl.java
Normal file
276
client/src/main/java/client/scenes/FoodpalApplicationCtrl.java
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
package client.scenes;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import client.utils.LocaleAware;
|
||||
import client.utils.LocaleManager;
|
||||
import client.utils.ServerUtils;
|
||||
import commons.Recipe;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
|
||||
public class FoodpalApplicationCtrl implements LocaleAware {
|
||||
private final MainCtrl mainCtrl;
|
||||
private final ServerUtils server;
|
||||
private final LocaleManager localeManager;
|
||||
|
||||
// everything in the top bar ===
|
||||
@FXML
|
||||
private Button flagEnButton; //already here for advanced stuff
|
||||
|
||||
@FXML
|
||||
private Button flagNlButton; //already here for advanced stuff
|
||||
|
||||
@FXML
|
||||
private Button flagPlButton; //already here for advanced stuff
|
||||
|
||||
// everything in the left lane
|
||||
@FXML
|
||||
public Label recipesLabel;
|
||||
|
||||
@FXML
|
||||
private ListView<Recipe> recipeList;
|
||||
|
||||
@FXML
|
||||
private Button addRecipeButton;
|
||||
|
||||
@FXML
|
||||
private Button removeRecipeButton;
|
||||
|
||||
@FXML
|
||||
public Button cloneRecipeButton;
|
||||
|
||||
// === CENTER: RECIPE DETAILS ===
|
||||
@FXML
|
||||
private Label recipeNameLabel;
|
||||
|
||||
@FXML
|
||||
private Button editRecipeTitleButton;
|
||||
|
||||
@FXML
|
||||
public Button removeRecipeButton2;
|
||||
|
||||
@FXML
|
||||
public Button printRecipeButton;
|
||||
|
||||
@FXML
|
||||
public Label ingredientsLabel;
|
||||
|
||||
@FXML
|
||||
public Label preparationLabel;
|
||||
|
||||
@FXML
|
||||
private ListView<String> ingredientsListView;
|
||||
|
||||
@FXML
|
||||
private Button addIngredientButton;
|
||||
|
||||
@FXML
|
||||
private ListView<String> preparationListView;
|
||||
|
||||
@FXML
|
||||
private Button addPreparationStepButton;
|
||||
|
||||
@Inject
|
||||
public FoodpalApplicationCtrl(MainCtrl mainCtrl, ServerUtils server, LocaleManager localeManager) {
|
||||
this.mainCtrl = mainCtrl;
|
||||
this.server = server;
|
||||
this.localeManager = localeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeComponents() {
|
||||
// Show recipe name in the list
|
||||
recipeList.setCellFactory(list -> new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(Recipe item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(empty || item == null ? "" : item.getName());
|
||||
}
|
||||
});
|
||||
|
||||
// When your selection changes, update details in the panel
|
||||
recipeList.getSelectionModel().selectedItemProperty().addListener(
|
||||
(obs, oldRecipe, newRecipe) -> showRecipeDetails(newRecipe)
|
||||
);
|
||||
|
||||
// Double-click to go to detail screen
|
||||
recipeList.setOnMouseClicked(event -> {
|
||||
final int DOUBLE_CLICK = 2; //to not get magic number:P
|
||||
if (event.getClickCount() == DOUBLE_CLICK) {
|
||||
openSelectedRecipe();
|
||||
}
|
||||
});
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
@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"));
|
||||
ingredientsLabel.setText(getLocaleString("menu.label.ingredients"));
|
||||
preparationLabel.setText(getLocaleString("menu.label.preparation"));
|
||||
|
||||
addIngredientButton.setText(getLocaleString("menu.button.add.ingredient"));
|
||||
addPreparationStepButton.setText(getLocaleString("menu.button.add.step"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocaleManager getLocaleManager() {
|
||||
return localeManager;
|
||||
}
|
||||
|
||||
// till the all the code from everyone is implemented for now to not have errors
|
||||
private void showRecipeDetails(Recipe recipe) {
|
||||
if (recipe == null) {
|
||||
recipeNameLabel.setText("");
|
||||
ingredientsListView.getItems().clear();
|
||||
preparationListView.getItems().clear();
|
||||
return;
|
||||
}
|
||||
|
||||
recipeNameLabel.setText(recipe.getName());
|
||||
ingredientsListView.getItems().setAll(recipe.getIngredients());
|
||||
preparationListView.getItems().setAll(recipe.getPreparationSteps());
|
||||
}
|
||||
|
||||
// Button handlers
|
||||
@FXML
|
||||
public void refresh() {
|
||||
// TODO: someone else doing this
|
||||
|
||||
List<Recipe> recipes;
|
||||
try {
|
||||
recipes = server.getRecipes();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
recipes = Collections.emptyList();
|
||||
System.err.println("Failed to load recipes: " + e.getMessage());
|
||||
}
|
||||
|
||||
recipeList.getItems().setAll(recipes);
|
||||
|
||||
// Select first recipe in the list by default
|
||||
if (!recipes.isEmpty()) {
|
||||
recipeList.getSelectionModel().selectFirst();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipe, by going to a different scene, there you insert the title and bam recipe created
|
||||
*/
|
||||
@FXML
|
||||
private void addRecipe() {
|
||||
// Navigate to "create recipe" screen (should be like a pop-up or new screen or just another place in the app)
|
||||
mainCtrl.showAddName();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void removeSelectedRecipe() throws IOException, InterruptedException {
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
server.deleteRecipe(selected.getId());
|
||||
|
||||
recipeList.getItems().remove(selected);
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void openSelectedRecipe() {
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
return;
|
||||
}
|
||||
// Let MainCtrl open the full detail screen
|
||||
mainCtrl.showAddName(); //I had showrecipedetail but intelij says showoverview
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void editRecipeTitle() {
|
||||
// TODO: someone else todo
|
||||
// For now reuse openSelectedRecipe()
|
||||
openSelectedRecipe();
|
||||
}
|
||||
|
||||
/**
|
||||
* You can add ingredients. you get send to a different scene that creates the ingredient
|
||||
* It doesn't automatically refresh yet so before you can see the new ingredient you have to first click on a
|
||||
* different recipe and come back after
|
||||
*/
|
||||
@FXML
|
||||
private void addIngredient() {
|
||||
System.out.println("Add ingredient clicked");
|
||||
mainCtrl.showAddIngredient();
|
||||
}
|
||||
|
||||
/**
|
||||
* You can add steps. you get send to a different scene that creates the Step
|
||||
* It doesn't automatically refresh yet so before you can see the new step you have to first click on a
|
||||
* different recipe and come back after
|
||||
*/
|
||||
@FXML
|
||||
private void addPreparationStep() {
|
||||
System.out.println("Add preparation step clicked");
|
||||
mainCtrl.showAddSteps();
|
||||
|
||||
}
|
||||
|
||||
// Language buttons
|
||||
@FXML
|
||||
private void switchLocale(ActionEvent event) {
|
||||
Button button = (Button)event.getSource();
|
||||
String lang = (String)button.getUserData();
|
||||
localeManager.setLocale(Locale.of(lang));
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void makePrintable() {
|
||||
System.out.println("Recipe printed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recipe that was selected
|
||||
* @return the selected recipe
|
||||
*/
|
||||
public Recipe getSelectedRecipe() {
|
||||
return recipeList.getSelectionModel().getSelectedItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a recipe, when clicking on the button "clone"
|
||||
*/
|
||||
public void cloneRecipe() throws IOException, InterruptedException {
|
||||
Recipe selected = recipeList.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
server.cloneRecipe(selected.getId());
|
||||
refresh();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -15,43 +15,127 @@
|
|||
*/
|
||||
package client.scenes;
|
||||
|
||||
import client.utils.LocaleAware;
|
||||
import client.utils.LocaleManager;
|
||||
import jakarta.inject.Inject;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class MainCtrl {
|
||||
import java.io.IOException;
|
||||
|
||||
public class MainCtrl implements LocaleAware {
|
||||
private final LocaleManager localeManager;
|
||||
|
||||
@FXML
|
||||
private Stage primaryStage;
|
||||
|
||||
private QuoteOverviewCtrl overviewCtrl;
|
||||
private Scene overview;
|
||||
@FXML
|
||||
private AddNameCtrl addNameCtrl;
|
||||
private Scene addName;
|
||||
|
||||
private AddQuoteCtrl addCtrl;
|
||||
private Scene add;
|
||||
@FXML
|
||||
private FoodpalApplicationCtrl foodpalCtrl;
|
||||
private Scene foodpal;
|
||||
|
||||
@FXML
|
||||
private AddIngredientCtrl addIngredientCtrl;
|
||||
private Scene addIngredient;
|
||||
|
||||
@FXML
|
||||
private AddStepsCtrl addStepsCtrl;
|
||||
private Scene addStep;
|
||||
|
||||
private String addNameTitle = "add.name.title";
|
||||
private String addIngredientTitle = "add.step.title";
|
||||
private String addStepTitle = "add.ingredient.title";
|
||||
|
||||
@Inject
|
||||
public MainCtrl(LocaleManager localeManager) {
|
||||
this.localeManager = localeManager;
|
||||
}
|
||||
|
||||
public void setup(Stage primaryStage,
|
||||
Pair<AddNameCtrl, Parent> addName,
|
||||
Pair<FoodpalApplicationCtrl, Parent> foodpal,
|
||||
Pair<AddIngredientCtrl, Parent> addIngredient,
|
||||
Pair<AddStepsCtrl, Parent> addStep) throws IOException, InterruptedException {
|
||||
|
||||
public void initialize(Stage primaryStage, Pair<QuoteOverviewCtrl, Parent> overview,
|
||||
Pair<AddQuoteCtrl, Parent> add) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.overviewCtrl = overview.getKey();
|
||||
this.overview = new Scene(overview.getValue());
|
||||
|
||||
this.addCtrl = add.getKey();
|
||||
this.add = new Scene(add.getValue());
|
||||
this.addNameCtrl = addName.getKey();
|
||||
this.addName = new Scene(addName.getValue());
|
||||
|
||||
showOverview();
|
||||
this.foodpalCtrl = foodpal.getKey();
|
||||
this.foodpal = new Scene(foodpal.getValue());
|
||||
|
||||
this.addIngredientCtrl = addIngredient.getKey();
|
||||
this.addIngredient = new Scene(addIngredient.getValue());
|
||||
|
||||
this.addStepsCtrl = addStep.getKey();
|
||||
this.addStep = new Scene(addStep.getValue());
|
||||
|
||||
showFoodpal();
|
||||
primaryStage.show();
|
||||
|
||||
initialize(); // Initialize LocaleManager stuff manually since MainCtrl isn't loaded from FXML.
|
||||
}
|
||||
|
||||
public void showOverview() {
|
||||
primaryStage.setTitle("Quotes: Overview");
|
||||
primaryStage.setScene(overview);
|
||||
overviewCtrl.refresh();
|
||||
@Override
|
||||
public void updateText() {
|
||||
addNameTitle = getLocaleString("add.recipe.title");
|
||||
addStepTitle = getLocaleString("add.step.title");
|
||||
addIngredientTitle = getLocaleString("add.ingredient.title");
|
||||
}
|
||||
|
||||
public void showAdd() {
|
||||
primaryStage.setTitle("Quotes: Adding Quote");
|
||||
primaryStage.setScene(add);
|
||||
add.setOnKeyPressed(e -> addCtrl.keyPressed(e));
|
||||
@Override
|
||||
public LocaleManager getLocaleManager() {
|
||||
return localeManager;
|
||||
}
|
||||
|
||||
public void showAddName() {
|
||||
primaryStage.setTitle(addNameTitle);
|
||||
primaryStage.setScene(addName);
|
||||
addName.setOnKeyPressed(e -> {
|
||||
try {
|
||||
addNameCtrl.keyPressed(e);
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void showFoodpal() {
|
||||
primaryStage.setTitle("FoodPal");
|
||||
primaryStage.setScene(foodpal);
|
||||
foodpalCtrl.refresh();
|
||||
}
|
||||
|
||||
public void showAddIngredient() {
|
||||
primaryStage.setTitle(addIngredientTitle);
|
||||
primaryStage.setScene(addIngredient);
|
||||
addIngredient.setOnKeyPressed(e -> {
|
||||
try {
|
||||
addIngredientCtrl.keyPressed(e);
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void showAddSteps() {
|
||||
primaryStage.setTitle(addStepTitle);
|
||||
primaryStage.setScene(addStep);
|
||||
addStep.setOnKeyPressed(e -> {
|
||||
try {
|
||||
addStepsCtrl.keyPressed(e);
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 Delft University of Technology
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package client.scenes;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import client.utils.ServerUtils;
|
||||
import commons.Quote;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.scene.control.TableView;
|
||||
|
||||
public class QuoteOverviewCtrl implements Initializable {
|
||||
|
||||
private final ServerUtils server;
|
||||
private final MainCtrl mainCtrl;
|
||||
|
||||
private ObservableList<Quote> data;
|
||||
|
||||
@FXML
|
||||
private TableView<Quote> table;
|
||||
@FXML
|
||||
private TableColumn<Quote, String> colFirstName;
|
||||
@FXML
|
||||
private TableColumn<Quote, String> colLastName;
|
||||
@FXML
|
||||
private TableColumn<Quote, String> colQuote;
|
||||
|
||||
@Inject
|
||||
public QuoteOverviewCtrl(ServerUtils server, MainCtrl mainCtrl) {
|
||||
this.server = server;
|
||||
this.mainCtrl = mainCtrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
colFirstName.setCellValueFactory(q -> new SimpleStringProperty(q.getValue().person.firstName));
|
||||
colLastName.setCellValueFactory(q -> new SimpleStringProperty(q.getValue().person.lastName));
|
||||
colQuote.setCellValueFactory(q -> new SimpleStringProperty(q.getValue().quote));
|
||||
}
|
||||
|
||||
public void addQuote() {
|
||||
mainCtrl.showAdd();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
var quotes = server.getQuotes();
|
||||
data = FXCollections.observableList(quotes);
|
||||
table.setItems(data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
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
|
||||
* IngredientList. Allows inline editing of ingredient names.
|
||||
*
|
||||
* @see IngredientListCtrl
|
||||
*/
|
||||
public class IngredientListCell extends ListCell<String> {
|
||||
// TODO: change all this to use actual Ingredient objects
|
||||
// and all that :)
|
||||
|
||||
@Override
|
||||
protected void updateItem(String item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
this.setText(null);
|
||||
} else {
|
||||
this.setText(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.getItem());
|
||||
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 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 {
|
||||
/**
|
||||
* <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;
|
||||
|
||||
/**
|
||||
* A callback function that is called when the ingredient list is updated.
|
||||
*/
|
||||
private Function<List<String>, Void> updateCallback;
|
||||
|
||||
@FXML
|
||||
ListView<String> ingredientListView;
|
||||
|
||||
@FXML
|
||||
Button addIngredientButton;
|
||||
@FXML
|
||||
Button deleteIngredientButton;
|
||||
|
||||
/**
|
||||
* Set the recipe and refetch data from it.
|
||||
* This replaces the current ingredient 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.ingredients = FXCollections.observableArrayList(new ArrayList<>());
|
||||
} else {
|
||||
List<String> ingredientList = recipe.getIngredients();
|
||||
this.ingredients = FXCollections.observableArrayList(ingredientList);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
public void setUpdateCallback(Function<List<String>, Void> callback) {
|
||||
this.updateCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the ingredient list view.
|
||||
*/
|
||||
private void refresh() {
|
||||
ingredientListView.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ingredient addition. Automatically calls update callback.
|
||||
*
|
||||
* @param event The action event.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param event The edit event.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
42
client/src/main/java/client/utils/LocaleAware.java
Normal file
42
client/src/main/java/client/utils/LocaleAware.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package client.utils;
|
||||
|
||||
import javafx.fxml.Initializable;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public interface LocaleAware extends Initializable {
|
||||
|
||||
/**
|
||||
* Updates all text when locale changes.
|
||||
* Must be implemented by each controller.
|
||||
*/
|
||||
void updateText();
|
||||
|
||||
/**
|
||||
* Returns the injected LocaleManager.
|
||||
* Must be implemented by each controller.
|
||||
*/
|
||||
LocaleManager getLocaleManager();
|
||||
|
||||
@Override
|
||||
default void initialize(URL location, ResourceBundle resources) {
|
||||
updateText();
|
||||
|
||||
getLocaleManager().getBundleProperty().addListener((_, _, _) -> updateText());
|
||||
|
||||
initializeComponents();
|
||||
}
|
||||
|
||||
default void initialize() {
|
||||
initialize(null, null);
|
||||
}
|
||||
|
||||
default String getLocaleString(String key) {
|
||||
return getLocaleManager().getBundle().getString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional function for initialization of components. Runs after localization.
|
||||
*/
|
||||
default void initializeComponents() {}
|
||||
}
|
||||
47
client/src/main/java/client/utils/LocaleManager.java
Normal file
47
client/src/main/java/client/utils/LocaleManager.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package client.utils;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class LocaleManager {
|
||||
private final ObjectProperty<Locale> currentLocale = new SimpleObjectProperty<>(Locale.ENGLISH);
|
||||
private final ObjectProperty<ResourceBundle> currentBundle = new SimpleObjectProperty<>();
|
||||
|
||||
private static final String RESOURCE_BUNDLE_PATH = "locale/lang";
|
||||
|
||||
public LocaleManager() {
|
||||
// TODO: Set currentLocale to config value instead of EN default.
|
||||
updateBundle();
|
||||
currentLocale.addListener((_, _, _) -> updateBundle());
|
||||
}
|
||||
|
||||
private void updateBundle() {
|
||||
ResourceBundle bundle = ResourceBundle.getBundle(RESOURCE_BUNDLE_PATH,
|
||||
currentLocale.get(),
|
||||
ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_PROPERTIES)
|
||||
);
|
||||
currentBundle.set(bundle);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setLocale(Locale locale) {
|
||||
currentLocale.set(locale);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Locale getLocale() {
|
||||
return currentLocale.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public ResourceBundle getBundle() {
|
||||
return currentBundle.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public ObjectProperty<ResourceBundle> getBundleProperty() {
|
||||
return currentBundle;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +1,147 @@
|
|||
/*
|
||||
* Copyright 2021 Delft University of Technology
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package client.utils;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import commons.Recipe;
|
||||
import jakarta.ws.rs.ProcessingException;
|
||||
import jakarta.ws.rs.client.ClientBuilder;
|
||||
import org.glassfish.jersey.client.ClientConfig;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
||||
import org.glassfish.jersey.client.ClientConfig;
|
||||
|
||||
import commons.Quote;
|
||||
import jakarta.ws.rs.ProcessingException;
|
||||
import jakarta.ws.rs.client.ClientBuilder;
|
||||
import jakarta.ws.rs.client.Entity;
|
||||
import jakarta.ws.rs.core.GenericType;
|
||||
|
||||
public class ServerUtils {
|
||||
private static final String SERVER = "http://localhost:8080/api";
|
||||
private final HttpClient client;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final int statusOK = 200;
|
||||
|
||||
private static final String SERVER = "http://localhost:8080/";
|
||||
public ServerUtils() {
|
||||
client = HttpClient.newHttpClient();
|
||||
}
|
||||
|
||||
public void getQuotesTheHardWay() throws IOException, URISyntaxException {
|
||||
var url = new URI("http://localhost:8080/api/quotes").toURL();
|
||||
var is = url.openConnection().getInputStream();
|
||||
var br = new BufferedReader(new InputStreamReader(is));
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
System.out.println(line);
|
||||
/**
|
||||
* Gets all the recipes from the backend
|
||||
* @return a JSON string with all the recipes
|
||||
*/
|
||||
public List<Recipe> getRecipes() throws IOException, InterruptedException {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(SERVER + "/recipes"))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if(response.statusCode() != statusOK){
|
||||
throw new IOException("No recipe to get. Server responds with " + response.body());
|
||||
}
|
||||
|
||||
|
||||
return objectMapper.readValue(response.body(), new TypeReference<List<Recipe>>() {
|
||||
});// JSON string-> List<Recipe> (Jackson)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a single recipe based on its id
|
||||
* @param id every recipe has it's unique id
|
||||
* @return a singe recipe with the given id
|
||||
*/
|
||||
public Recipe findId(long id) throws IOException, InterruptedException {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(SERVER +"/recipe/" + id))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if(response.statusCode() != statusOK){
|
||||
throw new IOException("failed finding recipe with id: "+ id + " body: " + response.body());
|
||||
}
|
||||
|
||||
return objectMapper.readValue(response.body(),Recipe.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipe to the backend
|
||||
* @param newRecipe the recipe to be added
|
||||
* @return a recipe
|
||||
*/
|
||||
public Recipe addRecipe(Recipe newRecipe) throws IOException, InterruptedException {
|
||||
//Make sure the name of the newRecipe is unique
|
||||
List<Recipe> allRecipes = getRecipes();
|
||||
|
||||
|
||||
int version = 1;
|
||||
String originalName = newRecipe.getName();
|
||||
while (allRecipes.stream().anyMatch(r -> r.getName().equals(newRecipe.getName()))) {
|
||||
String newName = originalName + "(" + version++ + ")";
|
||||
newRecipe.setName(newName);
|
||||
}
|
||||
|
||||
String json = objectMapper.writeValueAsString(newRecipe);
|
||||
|
||||
//Recipe to backend
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(SERVER + "/recipe/new"))
|
||||
.header("Content-Type", "application/json")
|
||||
.PUT(HttpRequest.BodyPublishers.ofString(json))
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if(response.statusCode() != statusOK){
|
||||
throw new IOException("Failed to add Recipe: " + newRecipe.toDetailedString() + "body: " + response.body());
|
||||
}
|
||||
|
||||
return objectMapper.readValue(response.body(),Recipe.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a recipe
|
||||
* @param id the recipe that get deleted
|
||||
*/
|
||||
public void deleteRecipe(long id) throws IOException, InterruptedException {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(SERVER + "/recipe/" + id))
|
||||
.DELETE()
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if(response.statusCode() != statusOK){
|
||||
throw new IOException("Failed removing recipe with id: " + id + "body: " + response.body());
|
||||
}
|
||||
}
|
||||
|
||||
public List<Quote> getQuotes() {
|
||||
return ClientBuilder.newClient(new ClientConfig()) //
|
||||
.target(SERVER).path("api/quotes") //
|
||||
.request(APPLICATION_JSON) //
|
||||
.get(new GenericType<List<Quote>>() {});
|
||||
}
|
||||
/**
|
||||
* Clones a recipe
|
||||
* @param id the id of the recipe to be cloned
|
||||
* @return a duplicated recipe of the given recipe
|
||||
*/
|
||||
public Recipe cloneRecipe(long id) throws IOException, InterruptedException {
|
||||
//Get the recipe
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(SERVER + "/recipe/" + id))
|
||||
.GET() //Needs to be changed to POST() when api is changed
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
public Quote addQuote(Quote quote) {
|
||||
return ClientBuilder.newClient(new ClientConfig()) //
|
||||
.target(SERVER).path("api/quotes") //
|
||||
.request(APPLICATION_JSON) //
|
||||
.post(Entity.entity(quote, APPLICATION_JSON), Quote.class);
|
||||
//200 is the status code for success, other codes can mean there is no recipe to clone
|
||||
if(response.statusCode() != statusOK){
|
||||
throw new IOException("Failed to get recipe to clone with id: " + id + "body: " + response.body());
|
||||
}
|
||||
// recipe exists so you can make a "new" recipe aka the clone
|
||||
Recipe recipe = objectMapper.readValue(response.body(), Recipe.class);
|
||||
|
||||
recipe.setId(null); // otherwise the id is the same as the original, and that's wrong
|
||||
|
||||
return addRecipe(recipe);
|
||||
}
|
||||
|
||||
public boolean isServerAvailable() {
|
||||
|
|
@ -74,4 +157,44 @@ public class ServerUtils {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addRecipeName(String name) throws IOException, InterruptedException {
|
||||
Recipe newRecipe = new Recipe();
|
||||
newRecipe.setName(name);
|
||||
addRecipe(newRecipe);
|
||||
|
||||
}
|
||||
|
||||
public void addRecipeIngredient(Recipe recipe, String ingredient) throws IOException, InterruptedException {
|
||||
List<String> ingredients = new ArrayList<>(recipe.getIngredients());
|
||||
ingredients.add(ingredient);
|
||||
recipe.setIngredients(ingredients);
|
||||
|
||||
updateRecipe(recipe);
|
||||
}
|
||||
|
||||
public void updateRecipe(Recipe recipe) throws IOException, InterruptedException {
|
||||
String json = objectMapper.writeValueAsString(recipe);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(SERVER + "/recipe/" + recipe.getId()))
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString((json))) // Needs to be changed to PUT() when api changed
|
||||
.build();
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if(response.statusCode() != statusOK){
|
||||
throw new IOException("Failed to update recipe: " + recipe.toDetailedString() + "body: " + response.body());
|
||||
}
|
||||
|
||||
objectMapper.readValue(response.body(), Recipe.class);
|
||||
}
|
||||
|
||||
public void addRecipeStep(Recipe recipe, String preparationStep) throws IOException, InterruptedException {
|
||||
List<String> preparationSteps = new ArrayList<>(recipe.getPreparationSteps());
|
||||
preparationSteps.add(preparationStep);
|
||||
recipe.setPreparationSteps(preparationSteps);
|
||||
|
||||
updateRecipe(recipe);
|
||||
}
|
||||
}
|
||||
72
client/src/main/java/client/utils/ServerUtilsExample.java
Normal file
72
client/src/main/java/client/utils/ServerUtilsExample.java
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2021 Delft University of Technology
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package client.utils;
|
||||
|
||||
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.ws.rs.core.GenericType;
|
||||
import org.glassfish.jersey.client.ClientConfig;
|
||||
|
||||
import commons.Quote;
|
||||
import jakarta.ws.rs.ProcessingException;
|
||||
import jakarta.ws.rs.client.ClientBuilder;
|
||||
import jakarta.ws.rs.client.Entity;
|
||||
|
||||
public class ServerUtilsExample {
|
||||
|
||||
private static final String SERVER = "http://localhost:8080/";
|
||||
|
||||
// public void getQuotesTheHardWay() throws IOException, URISyntaxException {
|
||||
// var url = new URI("http://localhost:8080/api/quotes").toURL();
|
||||
// var is = url.openConnection().getInputStream();
|
||||
// var br = new BufferedReader(new InputStreamReader(is));
|
||||
// String line;
|
||||
// while ((line = br.readLine()) != null) {
|
||||
// System.out.println(line);
|
||||
// }
|
||||
// }
|
||||
|
||||
public List<Quote> getQuotes() {
|
||||
return ClientBuilder.newClient(new ClientConfig()) //
|
||||
.target(SERVER).path("api/quotes") //
|
||||
.request(APPLICATION_JSON) //
|
||||
.get(new GenericType<List<Quote>>() {});
|
||||
}
|
||||
|
||||
public Quote addQuote(Quote quote) {
|
||||
return ClientBuilder.newClient(new ClientConfig()) //
|
||||
.target(SERVER).path("api/quotes") //
|
||||
.request(APPLICATION_JSON) //
|
||||
.post(Entity.entity(quote, APPLICATION_JSON), Quote.class);
|
||||
}
|
||||
|
||||
public boolean isServerAvailable() {
|
||||
try {
|
||||
ClientBuilder.newClient(new ClientConfig()) //
|
||||
.target(SERVER) //
|
||||
.request(APPLICATION_JSON) //
|
||||
.get();
|
||||
} catch (ProcessingException e) {
|
||||
if (e.getCause() instanceof ConnectException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
15
client/src/main/resources/client/scenes/AddIngredient.fxml
Normal file
15
client/src/main/resources/client/scenes/AddIngredient.fxml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="94.0" prefWidth="470.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.AddIngredientCtrl">
|
||||
<children>
|
||||
<Button fx:id="okButton" layoutX="417.0" layoutY="54.0" mnemonicParsing="false" onAction="#ok" text="Ok" />
|
||||
<Button fx:id="cancelButton" layoutX="349.0" layoutY="54.0" mnemonicParsing="false" onAction="#cancel" text="Cancel" />
|
||||
<TextField fx:id="ingredient" layoutX="120.0" layoutY="24.0" prefHeight="18.0" prefWidth="292.0" />
|
||||
<Label fx:id="ingredientLabel" layoutX="28.0" layoutY="29.0" text="Ingredient Name" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
15
client/src/main/resources/client/scenes/AddName.fxml
Normal file
15
client/src/main/resources/client/scenes/AddName.fxml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="94.0" prefWidth="470.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.AddNameCtrl">
|
||||
<children>
|
||||
<Button fx:id="okButton" layoutX="417.0" layoutY="54.0" mnemonicParsing="false" onAction="#ok" text="Ok" />
|
||||
<Button fx:id="cancelButton" layoutX="349.0" layoutY="54.0" mnemonicParsing="false" onAction="#cancel" text="Cancel" />
|
||||
<TextField fx:id="recipeName" layoutX="109.0" layoutY="24.0" prefHeight="18.0" prefWidth="292.0" />
|
||||
<Label fx:id="recipeNameLabel" layoutX="28.0" layoutY="29.0" text="Recipe Name" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="146.0" prefWidth="470.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.AddQuoteCtrl">
|
||||
<children>
|
||||
<Button layoutX="419.0" layoutY="105.0" mnemonicParsing="false" onAction="#ok" text="Ok" />
|
||||
<Button layoutX="350.0" layoutY="105.0" mnemonicParsing="false" onAction="#cancel" text="Cancel" />
|
||||
<TextField fx:id="firstName" layoutX="109.0" layoutY="24.0" prefHeight="26.0" prefWidth="79.0" />
|
||||
<TextField fx:id="lastName" layoutX="286.0" layoutY="24.0" />
|
||||
<TextField fx:id="quote" layoutX="109.0" layoutY="61.0" prefHeight="26.0" prefWidth="345.0" />
|
||||
<Label layoutX="216.0" layoutY="29.0" text="Last Name" />
|
||||
<Label layoutX="28.0" layoutY="29.0" text="First Name" />
|
||||
<Label layoutX="28.0" layoutY="66.0" text="Quote" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
15
client/src/main/resources/client/scenes/AddSteps.fxml
Normal file
15
client/src/main/resources/client/scenes/AddSteps.fxml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="94.0" prefWidth="470.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.scenes.AddStepsCtrl">
|
||||
<children>
|
||||
<Button fx:id="okButton" layoutX="417.0" layoutY="54.0" mnemonicParsing="false" onAction="#ok" text="Ok" />
|
||||
<Button fx:id="cancelButton" layoutX="349.0" layoutY="54.0" mnemonicParsing="false" onAction="#cancel" text="Cancel" />
|
||||
<TextField fx:id="preparationStep" layoutX="120.0" layoutY="24.0" prefHeight="18.0" prefWidth="292.0" />
|
||||
<Label fx:id="preparationStepLabel" layoutX="28.0" layoutY="29.0" text="Preparation step" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue