Compare commits

...
Author SHA1 Message Date
David Guler 1f049f86ab fix: fixed fxml version warning 2022-11-25 12:31:27 +01:00
David Guler bcb36b89c7 feature: added tutorial window 2022-11-23 21:55:35 +01:00
gulerdavandGitHub Enterprise f7105f041c Merge pull request #66 from schrom01/feature_guiOverhaul_M3
Feature gui overhaul m3
2022-11-21 12:56:57 +01:00
giavaphi b0369e3174 fix error edit task list 2022-11-21 12:53:28 +01:00
gulerdavandGitHub Enterprise 8c20dcaadb Merge pull request #65 from schrom01/refactor_task-id-semantics_M3
refactor: Made Task::id nullable/Optional for semantic reasons
2022-11-21 12:43:04 +01:00
David Guler 95b0f7e13d refactor: Made Task::id nullable/Optional for semantic reasons
better use of builder semantics in task and crop creation
2022-11-21 09:45:58 +01:00
giavaphiandGitHub Enterprise 2312149256 Merge pull request #64 from schrom01/fix_tasklist-not-updated_M3
fix: Properly saving tasks
2022-11-21 09:21:26 +01:00
David Guler ffc7f5174e fix: Properly saving tasks
Tasks were not properly saved by the JsonTaskList, resulting in the newly created tasks being discarded immediately. Fixed by putting them into the taskMap in saveTask()
2022-11-21 09:15:29 +01:00
giavaphi 8d3fbc06ad added task list editor date cell factory 2022-11-21 07:03:45 +01:00
giavaphi dcb97f1c55 add and edit task list 2022-11-21 06:47:27 +01:00
15 changed files with 585 additions and 147 deletions
@@ -1,5 +1,6 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
@@ -10,14 +11,18 @@ import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Pest;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
import java.io.IOException;
@@ -35,7 +40,12 @@ public class CropDetailController {
@Inject
private Garden garden;
@Inject
AppLoader appLoader;
private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
private final ListProperty<Task> taskListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
private final ListProperty<Pest> pestListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
@FXML
private ImageView imageView;
@@ -52,9 +62,6 @@ public class CropDetailController {
@FXML
private Label description_label;
@FXML
private VBox growthPhases_vbox;
@FXML
private Label location_label;
@@ -64,9 +71,6 @@ public class CropDetailController {
@FXML
private Button location_button;
@FXML
private VBox pests_vbox;
@FXML
private Label soil_label;
@@ -74,11 +78,17 @@ public class CropDetailController {
private Label spacing_label;
@FXML
private Button editTaskList_button;
private Button addTask_button;
@FXML
void editTaskList() {
private ListView<Task> taskList_listView;
@FXML
private ListView<Pest> pests_listView;
@FXML
void addTask() throws IOException {
createTaskDialog(true, null);
}
/**
@@ -128,25 +138,65 @@ public class CropDetailController {
}
area_label.setText("");
location_label.setText("");
createTaskLists(crop);
createPestList(plant);
setTaskListProperty(crop);
taskList_listView.itemsProperty().bind(taskListProperty);
pestListProperty.addAll(plant.pests());
pests_listView.itemsProperty().bind(pestListProperty);
} catch (HardinessZoneNotSetException | IOException e) {
throw new PlantNotFoundException();
}
setIconToButton(editTaskList_button, "editIcon.png");
setIconToButton(addTask_button, "addIcon.png");
setIconToButton(area_button, "areaIcon.png");
setIconToButton(location_button, "locationIcon.png");
setCellFactoryPests();
setCellFactoryTasks();
}
private void createTaskLists(Crop crop) {
private void setCellFactoryTasks() {
taskList_listView.setCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(Task task, boolean empty) {
super.updateItem(task, empty);
if (empty || task == null) {
setText(null);
setGraphic(null);
} else {
setText("");
setGraphic(createTaskHBox(task));
}
}
});
}
private void setCellFactoryPests() {
pests_listView.setCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(Pest pest, boolean empty) {
super.updateItem(pest, empty);
if (empty || pest == null) {
setText(null);
setGraphic(null);
} else {
setText("");
setGraphic(createPestHBox(pest));
}
}
});
}
private void setTaskListProperty(Crop crop) {
crop.getCropId().ifPresent(id -> {
List<Task> taskList;
try {
taskList = gardenSchedule.getTaskListForCrop(id);
for (Task task : taskList) {
Label label = new Label(task.getDescription());
growthPhases_vbox.getChildren().add(label);
}
taskListProperty.clear();
taskListProperty.addAll(taskList);
} catch (IOException e) {
// TODO: Alert
LOG.log(Level.SEVERE, "Could not get task list for crop", e.getCause());
@@ -154,22 +204,37 @@ public class CropDetailController {
});
}
private void createPestList(Plant plant) {
List<Pest> pests = plant.pests();
for (Pest pest : pests) {
Label label = new Label(pest.name() + ":");
label.setStyle("-fx-font-weight: bold");
HBox hBox = new HBox();
hBox.fillHeightProperty();
Label label1 = new Label(pest.description());
label1.setAlignment(Pos.TOP_LEFT);
label1.setWrapText(true);
label1.setMaxWidth(600);
label1.setMaxHeight(100);
Button button = new Button("Get Counter Measures");
hBox.getChildren().addAll(label1, button);
pests_vbox.getChildren().addAll(label, hBox);
}
private HBox createTaskHBox(Task task) {
HBox hBox = new HBox();
Label taskName = new Label(task.getName()+": ");
taskName.setStyle("-fx-font-weight: bold");
Label taskDescription = new Label(task.getDescription());
taskDescription.setWrapText(true);
taskDescription.setMaxWidth(2000);
HBox.setHgrow(taskDescription, Priority.ALWAYS);
Button edit = new Button();
Button delete = new Button();
setIconToButton(edit, "editIcon.png");
setIconToButton(delete, "deleteIcon.png");
edit.setOnAction(getEditTaskEvent(task));
hBox.getChildren().addAll(taskName, taskDescription, edit, delete);
return hBox;
}
private HBox createPestHBox(Pest pest) {
Label label = new Label(pest.name() + ": ");
label.setStyle("-fx-font-weight: bold");
HBox hBox = new HBox();
hBox.fillHeightProperty();
Label label1 = new Label(pest.description());
label1.setAlignment(Pos.TOP_LEFT);
label1.setWrapText(true);
label1.setMaxWidth(600);
Button button = new Button("Get Counter Measures");
hBox.getChildren().addAll(label, label1, button);
return hBox;
}
/**
@@ -184,4 +249,55 @@ public class CropDetailController {
imageView.setPreserveRatio(true);
button.setGraphic(imageView);
}
private EventHandler<ActionEvent> getEditTaskEvent(Task task) {
return (event) -> {
try {
createTaskDialog(false, task);
} catch (IOException e) {
e.printStackTrace();
}
};
}
private void createTaskDialog(boolean newTask, Task givenTask) throws IOException {
Dialog<Task> dialog = new Dialog<>();
dialog.setTitle("Set Task");
dialog.setHeaderText("Add/Edit Task:");
dialog.setResizable(false);
DialogPane dialogPane = dialog.getDialogPane();
ButtonType saveTask;
if(newTask) {
saveTask = new ButtonType("Add", ButtonBar.ButtonData.OK_DONE);
} else {
saveTask = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
}
dialogPane.getButtonTypes().addAll(saveTask, ButtonType.CANCEL);
if (appLoader.loadPaneToDialog("TaskFormular.fxml", dialogPane) instanceof TaskFormularController controller) {
controller.setCorp(this.crop);
if (!newTask) {
controller.setTaskValue(givenTask);
}
dialog.setResultConverter(button -> button.equals(saveTask) ? controller.returnResult() : null);
dialog.showAndWait()
.ifPresent(task -> {
if (newTask) {
try {
gardenSchedule.addTask(task);
setTaskListProperty(this.crop);
} catch (IOException e) {
e.printStackTrace();
}
} else {
//ToDo method to edit task
setTaskListProperty(this.crop);
}
});
}
}
}
@@ -11,6 +11,9 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
import java.util.logging.Level;
@@ -40,6 +43,9 @@ public class MainFXMLController {
@FXML
private Button tutorial_button;
private final Stage tutorialModal = new Stage();
/**
* go to home pane
*/
@@ -95,11 +101,23 @@ public class MainFXMLController {
}
/**
* go to Tutorial pane
* Show the tutorial window
*/
public void goToTutorial() {
showPaneAsMainView("Tutorial.fxml");
styleChangeButton(tutorial_button);
public void showTutorial() {
if (!tutorialModal.isShowing()) {
if (tutorialModal.getScene() == null) {
try {
appLoader.loadSceneToStage("Tutorial.fxml", tutorialModal);
tutorialModal.initModality(Modality.NONE);
tutorialModal.setResizable(false);
tutorialModal.sizeToScene();
} catch (IOException e) {
LOG.log(Level.SEVERE, "Could not load Tutorial");
}
}
tutorialModal.show();
}
tutorialModal.requestFocus();
}
/**
@@ -132,7 +150,6 @@ public class MainFXMLController {
appLoader.loadAndCacheFxml("MyGarden.fxml");
appLoader.loadAndCacheFxml("MySchedule.fxml");
appLoader.loadAndCacheFxml("Plants.fxml");
appLoader.loadAndCacheFxml("Tutorial.fxml");
}
private void styleChangeButton(Button button) {
@@ -153,12 +170,14 @@ public class MainFXMLController {
} catch (IOException e) {
LOG.log(Level.SEVERE, "Failed to load FXML-Pane!", e);
}
mainPane.getScene().getWindow().setOnCloseRequest(this::closeWindowHandler);
setIconToButton(home_button, "homeIcon.png");
setIconToButton(settings_button, "settingsIcon.png");
Settings.getInstance().getShowTutorialProperty().addListener((observable, oldValue, newValue) -> {
tutorial_button.setVisible(newValue);
});
tutorial_button.setVisible(Settings.getInstance().getShowTutorial());
tutorial_button.visibleProperty().bind(Settings.getInstance().getShowTutorialProperty());
}
private void closeWindowHandler(WindowEvent windowEvent) {
tutorialModal.close();
}
/**
@@ -173,7 +192,4 @@ public class MainFXMLController {
imageView.setPreserveRatio(true);
button.setGraphic(imageView);
}
}
@@ -0,0 +1,86 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.DateCell;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.util.Callback;
import java.net.URL;
import java.time.LocalDate;
import java.util.ResourceBundle;
public class TaskFormularController implements Initializable {
private Crop crop;
@FXML
private TextArea description_area;
@FXML
private DatePicker end_datePicker;
@FXML
private TextField interval_field;
@FXML
private DatePicker start_datePicker;
@FXML
private TextField taskName_field;
public Task returnResult() {
Task task = new Task(taskName_field.getText(), description_area.getText(),
start_datePicker.getValue(), end_datePicker.getValue(),
Integer.parseInt(interval_field.getText()), crop.getCropId().get());
return task;
}
public void setCorp(Crop crop) {
this.crop = crop;
}
public void setTaskValue(Task task) {
taskName_field.setText(task.getName());
description_area.setText(task.getDescription());
start_datePicker.setValue(task.getStartDate());
end_datePicker.setValue(task.getEndDate().orElse(null));
if(task.getInterval().orElse(null)!=null) {
interval_field.setText(task.getInterval().get().toString());
} else {
interval_field.setText(null);
}
}
private Callback<DatePicker, DateCell> getDayCellFactory() {
return (datePicker) -> new DateCell() {
private final LocalDate today = LocalDate.now();
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
if (item.compareTo(today) > 0 && item.compareTo(crop.getStartDate()) > 0) {
setDisable(false);
setStyle("-fx-background-color: #32CD32;");
}
}
};
}
@Override
public void initialize(URL location, ResourceBundle resources) {
start_datePicker.setDayCellFactory(getDayCellFactory());
start_datePicker.setEditable(false);
end_datePicker.setDayCellFactory(getDayCellFactory());
end_datePicker.setEditable(false);
}
}
@@ -1,6 +1,61 @@
package ch.zhaw.gartenverwaltung;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TutorialController {
@FXML
public Button previousPageButton;
@FXML
public Button nextPageButton;
@FXML
public StackPane tourPages;
public ImageView imgAddNewPlant;
public ImageView imgTaskList;
public ImageView imgSelectDate;
private int page = 0;
@FXML
public void initialize() {
switchViews();
setButtonAbilities();
Image placeholder = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
imgAddNewPlant.setImage(placeholder);
imgSelectDate.setImage(placeholder);
imgTaskList.setImage(placeholder);
}
public void viewNextPage() {
page++;
switchViews();
setButtonAbilities();
}
public void viewPreviousPage() {
page--;
switchViews();
setButtonAbilities();
}
private void setButtonAbilities() {
previousPageButton.setDisable(page <= 0);
nextPageButton.setDisable(page >= tourPages.getChildren().size() - 1);
}
private void switchViews() {
tourPages.getChildren().forEach(node -> node.setOpacity(0));
tourPages.getChildren().get(page).setOpacity(1);
}
public void closeTutorial() {
Stage root = (Stage) tourPages.getScene().getWindow();
root.close();
}
}
@@ -103,9 +103,9 @@ public class JsonTaskList implements TaskList {
if(taskMap.isEmpty()) {
loadTaskListFromFile();
}
if(task.getId() == 0) {
task.withId(idProvider.incrementAndGet());
}
long id = task.getId().orElse(idProvider.incrementAndGet());
taskMap.put(id, task.withId(id));
writeTaskListToFile();
}
@@ -121,8 +121,9 @@ public class JsonTaskList implements TaskList {
if(taskMap.isEmpty()) {
loadTaskListFromFile();
}
if(taskMap.containsKey(task.getId())){
taskMap.remove(task.getId());
Long taskId = task.getId().orElseThrow(IOException::new);
if(taskMap.containsKey(taskId)){
taskMap.remove(taskId);
writeTaskListToFile();
}
}
@@ -162,7 +163,7 @@ public class JsonTaskList implements TaskList {
taskMap = result.stream()
.collect(HashMap::new,
(res, task) -> res.put(task.getId(), task),
(res, task) -> res.put(task.getId().orElse(0L), task),
(existing, replacement) -> {});
}
@@ -9,7 +9,7 @@ import java.util.Optional;
* May be created using the builder pattern.
*/
public class Task {
private long id;
private Long id;
private final String name;
private final String description;
private final LocalDate startDate;
@@ -62,7 +62,7 @@ public class Task {
}
// Getters
public long getId() { return id; }
public Optional<Long> getId() { return Optional.ofNullable(id); }
public String getName() { return name; }
public String getDescription() { return description; }
public LocalDate getStartDate() { return startDate; }
@@ -34,6 +34,7 @@ public class TaskTemplate {
public void setRelativeEndDate(Integer relativeEndDate) {
this.relativeEndDate = relativeEndDate;
}
public void setInterval(Integer interval) {
this.interval = interval;
}
@@ -45,13 +46,10 @@ public class TaskTemplate {
}
public Task generateTask(LocalDate realStartDate, long cropId) {
Task task = new Task(name, description, realStartDate.plusDays(relativeStartDate), cropId);
if (relativeEndDate != null) {
task.withEndDate(realStartDate.plusDays(relativeEndDate));
}
if (interval != null) {
task.withInterval(interval);
}
return task;
LocalDate endDate = relativeEndDate != null ? realStartDate.plusDays(relativeEndDate) : null;
return new Task(name, description, realStartDate.plusDays(relativeStartDate), cropId)
.withInterval(interval)
.withEndDate(endDate);
}
}
@@ -3,6 +3,7 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
@@ -12,13 +13,11 @@
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="785.0" prefWidth="899.0"
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.CropDetailController">
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="785.0" prefWidth="899.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.CropDetailController">
<children>
<ScrollPane fitToWidth="true" prefHeight="759.0" prefWidth="664.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<content>
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="503.0" prefWidth="897.0">
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="781.0" prefWidth="897.0">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
@@ -85,22 +84,22 @@
<Insets bottom="10.0" />
</VBox.margin>
</Label>
<VBox fx:id="growthPhases_vbox" prefHeight="135.0" prefWidth="879.0">
<ListView fx:id="taskList_listView" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="877.0" VBox.vgrow="ALWAYS">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</VBox>
<Button fx:id="editTaskList_button" mnemonicParsing="false" onAction="#editTaskList" prefHeight="25.0" prefWidth="45.0">
</ListView>
<Button fx:id="addTask_button" mnemonicParsing="false" onAction="#addTask" prefHeight="25.0" prefWidth="45.0">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</Button>
<Label text="Pests:" />
<VBox fx:id="pests_vbox" prefHeight="200.0" prefWidth="100.0">
<ListView fx:id="pests_listView" maxHeight="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</VBox>
</ListView>
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
<children>
<Label text="Area:">
@@ -12,7 +12,7 @@
<Button fx:id="home_button" mnemonicParsing="false" onAction="#goToHome" prefHeight="38.0" prefWidth="40.0" HBox.hgrow="NEVER" />
<Button fx:id="myGarden_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="My Garden" />
<Button fx:id="mySchedule_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMySchedule" prefHeight="38.0" prefWidth="121.0" text="My Schedule" />
<Button fx:id="tutorial_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToTutorial" prefHeight="38.0" prefWidth="121.0" text="Tutorial" />
<Button fx:id="tutorial_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#showTutorial" prefHeight="38.0" prefWidth="121.0" text="Tutorial" />
<Pane maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
<Button fx:id="settings_button" maxHeight="1.7976931348623157E308" mnemonicParsing="false" onAction="#openSettings" prefHeight="38.0" prefWidth="40.0" HBox.hgrow="NEVER" />
</children>
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="259.0" prefWidth="390.0" xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.TaskFormularController">
<children>
<VBox layoutX="14.0" layoutY="14.0" prefHeight="272.0" prefWidth="390.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
<children>
<HBox alignment="CENTER_LEFT" prefHeight="35.0" prefWidth="560.0">
<children>
<Label maxWidth="1.7976931348623157E308" text="Task name:" HBox.hgrow="ALWAYS" />
<TextField fx:id="taskName_field" promptText="Task Name" />
</children>
</HBox>
<HBox prefHeight="77.0" prefWidth="350.0">
<children>
<Label maxWidth="1.7976931348623157E308" text="Description:" HBox.hgrow="ALWAYS" />
<TextArea fx:id="description_area" prefHeight="73.0" prefWidth="206.0" promptText="Description" />
</children>
</HBox>
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="30.0" prefHeight="35.0" prefWidth="560.0">
<children>
<Label maxWidth="1.7976931348623157E308" text="Start Date:" HBox.hgrow="ALWAYS" />
<DatePicker fx:id="start_datePicker" />
</children>
</HBox>
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="143.0" prefHeight="35.0" prefWidth="560.0">
<children>
<Label maxWidth="1.7976931348623157E308" text="End Date:" HBox.hgrow="ALWAYS" />
<DatePicker fx:id="end_datePicker" />
</children>
</HBox>
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="30.0" prefHeight="35.0" prefWidth="560.0">
<children>
<Label maxWidth="1.7976931348623157E308" text="Interval:" HBox.hgrow="ALWAYS" />
<TextField fx:id="interval_field" promptText="Interval (e.g. 0, 1, 3 ...)" />
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
@@ -1,27 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.text.TextFlow?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.TutorialController">
<children>
<VBox layoutX="7.0" layoutY="8.0" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<BorderPane maxHeight="470.0" maxWidth="724.0" minHeight="470.0" minWidth="724.0" prefHeight="470.0" prefWidth="724.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.TutorialController">
<bottom>
<HBox alignment="CENTER" prefHeight="0.0" prefWidth="724.0" BorderPane.alignment="CENTER">
<children>
<Label text="Tutorial">
<font>
<Font size="18.0" />
</font>
</Label>
<Label text="To be added" />
<CheckBox mnemonicParsing="false" text="Don't show again" />
<Separator prefWidth="50.0" visible="false" HBox.hgrow="ALWAYS" />
<ButtonBar prefHeight="40.0" prefWidth="200.0">
<buttons>
<Button cancelButton="true" contentDisplay="CENTER" graphicTextGap="5.0" mnemonicParsing="false" text="Close" onAction="#closeTutorial"/>
<Button fx:id="previousPageButton" mnemonicParsing="false" text="Previous" onAction="#viewPreviousPage"/>
<Button fx:id="nextPageButton" defaultButton="true" mnemonicParsing="false" text="Next" onAction="#viewNextPage" />
</buttons>
</ButtonBar>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
</VBox>
</children>
</AnchorPane>
</HBox>
</bottom>
<center>
<StackPane fx:id="tourPages" prefHeight="150.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
<VBox layoutX="30.0" layoutY="26.0" opacity="0.0" prefHeight="200.0" prefWidth="100.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Crops">
<font>
<Font size="24.0" />
</font>
</Text>
<Separator prefWidth="200.0">
<VBox.margin>
<Insets bottom="15.0" top="10.0" />
</VBox.margin>
</Separator>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="In the &quot;My Garden&quot; tab, you can see all of the plants you're planning on growing.">
<VBox.margin>
<Insets bottom="20.0" />
</VBox.margin>
</Text>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<TextFlow lineSpacing="4.0" prefHeight="90.0" prefWidth="500.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="To get started, click on the &quot;Add new Plant&quot; button in the bottom left. This will open a list of all the plants you can grow.&#10;Use the search bar at the top, the filters on the left, or simply browse the list until you've found a plant you want to grow in your garden." wrappingWidth="345.80316162109375" />
</children>
<HBox.margin>
<Insets bottom="20.0" />
</HBox.margin>
</TextFlow>
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
<ImageView fx:id="imgAddNewPlant" fitHeight="98.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
</children>
</HBox>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<TextFlow lineSpacing="4.0" prefHeight="200.0" prefWidth="500.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Once you've found what you're looking for, click the &quot;Select Sow/Harvest Date&quot; button at the bottom of the window.&#10;In the subsequently shown dialog, you can select the date you'd like to sow or harvest the crop. The date selector shows possible dates in green. Confirm your selection with the &quot;Save&quot; button." />
</children>
</TextFlow>
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
<ImageView fx:id="imgSelectDate" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
</children>
</HBox>
</children>
<opaqueInsets>
<Insets />
</opaqueInsets>
</VBox>
<VBox prefHeight="200.0" prefWidth="100.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Tasks">
<font>
<Font size="24.0" />
</font>
</Text>
<Separator prefWidth="200.0">
<VBox.margin>
<Insets bottom="15.0" top="10.0" />
</VBox.margin>
</Separator>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="In the &quot;My Schedule&quot; tab, you can see all of the tasks you need to complete for a successful harvest.">
<VBox.margin>
<Insets bottom="20.0" />
</VBox.margin>
</Text>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<TextFlow lineSpacing="4.0" prefHeight="90.0" prefWidth="500.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="You can view the tasks for a specific crop by selecting it from the left sidebar.&#10;To see all tasks again, click the &quot;Clear Filter&quot; button at the bottom." wrappingWidth="345.80316162109375" />
</children>
<HBox.margin>
<Insets bottom="20.0" />
</HBox.margin>
</TextFlow>
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
<ImageView fx:id="imgTaskList" fitHeight="98.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
</children>
</HBox>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<TextFlow lineSpacing="4.0" prefHeight="200.0" prefWidth="500.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="You can also add your own custom tasks, by clicking the &quot;Add Task&quot; button.&#10;In the subsequently shown dialog, you can enter the corresponding details.&#10;&#10;Note: If you want to make a task recurring, you need to set both an interval (in days) AND an end date, so the task won't repeat for all eternity." />
</children>
</TextFlow>
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
<ImageView fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
</children></HBox>
</children>
<opaqueInsets>
<Insets />
</opaqueInsets>
</VBox>
</children>
<BorderPane.margin>
<Insets />
</BorderPane.margin>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</StackPane>
</center>
</BorderPane>
@@ -184,15 +184,14 @@
"litersPerSqM": 15,
"interval": 4,
"notes": [
]
},
"taskTemplates": [
{
"name": "hilling",
"name": "Plant Sets",
"relativeStartDate": 0,
"relativeEndDate": 0,
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
"description": "Plant the sets about 5cm deep into the soil.",
"interval": null,
"isOptional": false
}
@@ -232,7 +231,6 @@
"litersPerSqM": 0,
"interval": null,
"notes": [
]
},
"taskTemplates": [
@@ -1,56 +1,56 @@
[
{
"id" : 1,
"name" : "sow plant",
"description": "Plant the seeds, crops in de bed.",
"startDate" : "2022-05-01",
"endDate" : "2022-05-01",
"interval" : 0,
"cropId" : 0
"id": 1,
"name": "sow plant",
"description": "Plant the seeds, crops in de bed.",
"startDate": "2022-05-01",
"endDate": "2022-05-01",
"interval": 0,
"cropId": 0
},
{
"id" : 2,
"name" : "water plant",
"id": 2,
"name": "water plant",
"description": "water the plant, so that the soil is wet around the plant.",
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 2,
"cropId" : 0
"startDate": "2022-05-01",
"endDate": "2022-09-01",
"interval": 2,
"cropId": 0
},
{
"id" : 3,
"name" : "fertilize plant",
"id": 3,
"name": "fertilize plant",
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
"startDate" : "2022-06-01",
"endDate" : "2022-08-01",
"interval" : 28,
"cropId" : 0
"startDate": "2022-06-01",
"endDate": "2022-08-01",
"interval": 28,
"cropId": 0
},
{
"id" : 4,
"name" : "covering plant",
"id": 4,
"name": "covering plant",
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
"startDate" : "2022-07-01",
"endDate" : "2022-07-01",
"interval" : 0,
"cropId" : 0
"startDate": "2022-07-01",
"endDate": "2022-07-01",
"interval": 0,
"cropId": 0
},
{
"id" : 5,
"name" : "look after plant",
"id": 5,
"name": "look after plant",
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 5,
"cropId" : 0
"startDate": "2022-05-01",
"endDate": "2022-09-01",
"interval": 5,
"cropId": 0
},
{
"id" : 6,
"name" : "harvest plant",
"id": 6,
"name": "harvest plant",
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
"startDate" : "2022-09-01",
"endDate" : "2022-09-01",
"interval" : 0,
"cropId" : 0
"startDate": "2022-09-01",
"endDate": "2022-09-01",
"interval": 0,
"cropId": 0
}
]
@@ -72,7 +72,8 @@ public class JsonTaskListTest {
@Test
@DisplayName("Remove task.")
void removeTask() {
Task task = new Task("Dummy", "Dummy", LocalDate.parse("2022-05-31", formatter), 1).withId(2);
Task task = new Task("Dummy", "Dummy", LocalDate.parse("2022-05-31", formatter), 1)
.withId(2);
try {
testDatabase.removeTask(task);
List<Task> taskList;
@@ -55,8 +55,8 @@ public class GardenPlanModelTest {
new GrowthPhase(MonthDay.of(2, 8), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
new GrowthPhase(MonthDay.of(10, 2), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
exampleCropOnion = new Crop(examplePlantOnion.id(), LocalDate.of(2023,3,1));
exampleCropOnion.withId(3);
exampleCropOnion = new Crop(examplePlantOnion.id(), LocalDate.of(2023, 3, 1))
.withId(3);
examplePlantCarrot = new Plant(
1,
"Early Carrot",
@@ -67,18 +67,18 @@ public class GardenPlanModelTest {
"sandy to loamy, loose soil, free of stones",
new ArrayList<>(),
List.of(new GrowthPhase(MonthDay.of(4, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
exampleCropCarrot = new Crop(examplePlantCarrot.id(), LocalDate.now());
exampleCropCarrot.withId(5);
exampleCropCarrot = new Crop(examplePlantCarrot.id(), LocalDate.now())
.withId(5);
exampleCrop1 = new Crop(1, LocalDate.of(2023,2,25));
exampleCrop1.withId(0);
exampleCrop1.withArea(0.5);
exampleCrop2 = new Crop(1,LocalDate.of(2023,3,1));
exampleCrop2.withId(1);
exampleCrop2.withArea(0.5);
exampleCrop3 = new Crop(0,LocalDate.of(2023,3,1));
exampleCrop3.withId(2);
exampleCrop3.withArea(1.0);
exampleCrop1 = new Crop(1, LocalDate.of(2023, 2, 25))
.withId(0)
.withArea(0.5);
exampleCrop2 = new Crop(1, LocalDate.of(2023, 3, 1))
.withId(1)
.withArea(0.5);
exampleCrop3 = new Crop(0, LocalDate.of(2023, 3, 1))
.withId(2)
.withArea(1.0);
exampleCrops = new ArrayList<>();
exampleCrops.add(exampleCrop1);
@@ -107,7 +107,7 @@ public class GardenPlanModelTest {
@Test
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023, 3, 1));
Optional<Crop> exampleCrop = model.getCrop(2L);
assertTrue(exampleCrop.isPresent());
assertEquals(model.getCrops().get(2), exampleCrop.get());
@@ -119,6 +119,6 @@ public class GardenPlanModelTest {
exampleCrop1.withId(2);
exampleCrop1.withArea(1.5);
model.removeCrop(exampleCrop1);
assertEquals(2,model.getCrops().size());
assertEquals(2, model.getCrops().size());
}
}