From ff79398de401d05c0cb00dde6cdb248ebe553b18 Mon Sep 17 00:00:00 2001 From: Sverre Halvorsen <sverrgha@stud.ntnu.no> Date: Tue, 21 May 2024 22:52:21 +0200 Subject: [PATCH 1/6] Feat: Implement ButtonEventHandler to handle button events --- .../idatt2003/view/ButtonEventHandler.java | 64 ++++++ .../edu/ntnu/idatt2003/view/MainPageView.java | 215 +++++++----------- 2 files changed, 151 insertions(+), 128 deletions(-) create mode 100644 src/main/java/edu/ntnu/idatt2003/view/ButtonEventHandler.java diff --git a/src/main/java/edu/ntnu/idatt2003/view/ButtonEventHandler.java b/src/main/java/edu/ntnu/idatt2003/view/ButtonEventHandler.java new file mode 100644 index 0000000..fa8f5ae --- /dev/null +++ b/src/main/java/edu/ntnu/idatt2003/view/ButtonEventHandler.java @@ -0,0 +1,64 @@ +package edu.ntnu.idatt2003.view; + +import edu.ntnu.idatt2003.controller.MainPageController; +import edu.ntnu.idatt2003.view.MainPageView; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import javafx.stage.Stage; + +import java.io.File; +import java.util.List; + +public class ButtonEventHandler { + private final MainPageController controller; + private final MainPageView view; + + protected ButtonEventHandler(MainPageController controller, MainPageView view) { + this.controller = controller; + this.view = view; + } + + protected void handleRunSteps(int steps) { + controller.runSteps(steps); + } + protected void handleReset() { + controller.runSteps(-1); + } + + protected void handleSave(String fractalName, List<String[]> transformations, + String[] startVector, String[] endVector) { + controller.addCustomFractal(startVector, endVector, transformations, fractalName); + } + protected void handleCancel() { + view.render(); + } + + protected void handleSaveLocally() { + FileChooser fileChooser = new FileChooser(); + fileChooser.getExtensionFilters().add( + new FileChooser.ExtensionFilter("Text Files", "*.txt")); + controller.saveToLocalDirectory(fileChooser.showSaveDialog(null)); + } + + /** + * Opens a file chooser dialog that enables the user to upload a custom + * text file with a chaos game description. The file is then uploaded + * by the controller. If an exception occurs, an alert is shown. + */ + protected void handleUploadFile() { + FileChooser fileChooser = new FileChooser(); + fileChooser.getExtensionFilters().add( + new FileChooser.ExtensionFilter("Text Files", "*.txt")); + + Stage fileChooserStage = new Stage(); + File file = fileChooser.showOpenDialog(fileChooserStage); + if (file != null) { + controller.uploadFile(file); + } + } + + public void handleAddTransformation(VBox vbox, String textBoxText) { + vbox.getChildren().add(view.createTextBoxWithTextField(textBoxText, 55, 20, "a00", "a01", "a10", "a11", "v0", "v1")); + } + +} diff --git a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java index b8a6cd5..015bc7e 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java +++ b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java @@ -8,12 +8,12 @@ import edu.ntnu.idatt2003.view.components.ChaosImage; import edu.ntnu.idatt2003.view.components.StyledButton; import edu.ntnu.idatt2003.view.components.StyledComboBox; import edu.ntnu.idatt2003.view.components.StyledTextField; -import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Objects; + import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.Scene; @@ -29,8 +29,6 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; -import javafx.stage.FileChooser; -import javafx.stage.Stage; /** * The MainPageView class is the main page of the application. @@ -45,6 +43,7 @@ public class MainPageView extends Scene implements ChaosGameObserver { private static final int COMPONENT_HEIGHT = 40; private static final int BUTTON_WIDTH = (int) (Sizes.SCREEN_WIDTH) / BUTTON_COUNT; private static final int DEFAULT_SPACING = 10; + private final ButtonEventHandler buttonEventHandler; private TextField x0Field; private TextField x1Field; private TransformationType selectedTransformation; @@ -60,7 +59,7 @@ public class MainPageView extends Scene implements ChaosGameObserver { .getResource("/styles/mainPage.css")).toExternalForm()); this.getStylesheets().add(Objects.requireNonNull(getClass() .getResource("/styles/components.css")).toExternalForm()); - + this.buttonEventHandler = new ButtonEventHandler(mainPageController, this); root = (BorderPane) this.getRoot(); this.selectedTransformation = TransformationType.AFFINE; this.controller = mainPageController; @@ -188,8 +187,8 @@ public class MainPageView extends Scene implements ChaosGameObserver { * @param promptTexts The prompt texts for the text fields. * @return The HBox containing the text box and text fields. */ - private HBox createTextBoxWithTextField(String text, int width, - int height, String... promptTexts) { + public HBox createTextBoxWithTextField(String text, int width, + int height, String... promptTexts) { HBox container = new HBox(DEFAULT_SPACING); container.getChildren().add(createTextBox(text)); for (String promptText : promptTexts) { @@ -235,14 +234,14 @@ public class MainPageView extends Scene implements ChaosGameObserver { createComboBox(), createCustomComboBox(), new StyledButton("10 Steps", BUTTON_WIDTH, COMPONENT_HEIGHT, - e -> controller.runSteps(10)), + e -> buttonEventHandler.handleRunSteps(10)), new StyledButton("100 Steps", BUTTON_WIDTH, COMPONENT_HEIGHT, - e -> controller.runSteps(100)), + e -> buttonEventHandler.handleRunSteps(100)), new StyledButton("1000 Steps", BUTTON_WIDTH, COMPONENT_HEIGHT, - e -> controller.runSteps(1000)), + e -> buttonEventHandler.handleRunSteps(1000)), createStepsTextField(), new StyledButton("Reset", BUTTON_WIDTH, COMPONENT_HEIGHT, - e -> controller.runSteps(-1)) + e -> buttonEventHandler.handleReset()) ); return buttonContainer; } @@ -280,23 +279,6 @@ public class MainPageView extends Scene implements ChaosGameObserver { return customMenu; } - /** - * Opens a file chooser dialog that enables the user to upload a custom - * text file with a chaos game description. The file is then uploaded - * by the controller. If an exception occurs, an alert is shown. - */ - private void uploadFile() { - FileChooser fileChooser = new FileChooser(); - fileChooser.getExtensionFilters().add( - new FileChooser.ExtensionFilter("Text Files", "*.txt")); - - Stage fileChooserStage = new Stage(); - File file = fileChooser.showOpenDialog(fileChooserStage); - if (file != null) { - controller.uploadFile(file); - } - } - /** * Shows an alert with a specified message to give feedback to the user. * @@ -368,23 +350,90 @@ public class MainPageView extends Scene implements ChaosGameObserver { new HBox( DEFAULT_SPACING, new StyledButton("Save", 20, - e -> saveFractal(fractalName, transformationComboBox, - transformationVbox, startVectorField, endVectorField)), - new StyledButton("Cancel", 20, e -> render()), + e -> buttonEventHandler.handleSave( + fractalName.getText(), + getTransformationList(transformationComboBox.getValue(), transformationVbox), + getInputVector(startVectorField), + getInputVector(endVectorField) + ) + ), + new StyledButton("Cancel", 20, e -> buttonEventHandler.handleCancel()), new StyledButton("Save current locally", 20, - e -> { - FileChooser fileChooser = new FileChooser(); - fileChooser.getExtensionFilters().add( - new FileChooser.ExtensionFilter("Text Files", "*.txt")); - controller.saveToLocalDirectory(fileChooser.showSaveDialog(null)); - }), - new StyledButton("Add File", 100, 20, e -> uploadFile()) + e -> buttonEventHandler.handleSaveLocally()), + new StyledButton("Add File", 100, 20, e -> buttonEventHandler.handleUploadFile()) ) ); StackPane.setAlignment(addPanel, Pos.BOTTOM_LEFT); return addPanel; } + /** + * Retrieves input information based on the selected transformation type. + * + * @param selectedTransformation the selected transformation type. + * @param transformationVbox the HBox containing the input fields for transformations. + * @return a list of Transform2D objects. + */ + private List<String[]> getTransformationList(MainPageView.TransformationType selectedTransformation, + VBox transformationVbox) { + if (selectedTransformation == MainPageView.TransformationType.JULIA) { + return getJuliaTransformation(transformationVbox); + } else if (selectedTransformation == MainPageView.TransformationType.AFFINE) { + return getAffineTransformation(transformationVbox); + } + return new ArrayList<>(); + } + + /** + * Retrieves input information for the Julia transformation. + * + * @param transformationVbox the HBox containing the input fields for the Julia + * transformation. + * @return a list of Transform2D objects for the Julia transformation. + */ + private List<String[]> getJuliaTransformation(VBox transformationVbox) { + List<String[]> list = new ArrayList<>(); + if (!transformationVbox.getChildren().isEmpty()) { + HBox juliaFields = (HBox) transformationVbox.getChildren().get(0); + list.add(new String[]{((TextField) juliaFields.getChildren().get(1)).getText(), + ((TextField) juliaFields.getChildren().get(2)).getText()}); + } + return list; + } + + /** + * Retrieves input information for the Affine transformation. + * + * @param transformationInputField the HBox containing the input fields for the Affine + * transformation. + * @return a list of Transform2D objects for the Affine transformation. + */ + private List<String[]> getAffineTransformation(VBox transformationInputField) { + List<String[]> list = new ArrayList<>(); + if (!transformationInputField.getChildren().isEmpty()) { + for (Node node : transformationInputField.getChildren() + .subList(1, transformationInputField.getChildren().size())) { + HBox matrixFields = (HBox) node; + String[] coordinateList = new String[matrixFields.getChildren().size() - 1]; + for (int i = 1; i < matrixFields.getChildren().size(); i++) { + coordinateList[i - 1] = ((TextField) matrixFields.getChildren().get(i)).getText(); + } + list.add(coordinateList); + } + } + return list; + } + + /** + * Retrieves the input vector from an HBox. + * + * @param vectorBox the HBox containing the input fields for the vector. + * @return a Vector2d object representing the input vector. + */ + private String[] getInputVector(HBox vectorBox) { + return new String[]{((TextField) vectorBox.getChildren().get(1)).getText(), + ((TextField) vectorBox.getChildren().get(2)).getText()}; + } private ComboBox<TransformationType> createAddComboBox() { StyledComboBox<TransformationType> transformMenu = new StyledComboBox<>("Transformation", @@ -416,8 +465,7 @@ public class MainPageView extends Scene implements ChaosGameObserver { if (transformationComboBox.getValue() == TransformationType.AFFINE) { vbox.getChildren().add( new StyledButton("Add Transformation", 250, 20, - e -> vbox.getChildren().add(createTextBoxWithTextField(textBoxText, - 55, 20, "a00", "a01", "a10", "a11", "v0", "v1")) + e -> buttonEventHandler.handleAddTransformation(vbox, "Transformation:") ) ); } @@ -453,95 +501,6 @@ public class MainPageView extends Scene implements ChaosGameObserver { JULIA, AFFINE } - /** - * Saves the fractal with the given parameters. - * - * @param fractalName the name of the fractal. - * @param transformationComboBox the ComboBox for selecting transformation types. - * @param transformationVbox the HBox containing the input fields for transformations. - * @param startVectorField the TextField for the start vector. - * @param endVectorField the TextField for the end vector. - */ - private void saveFractal(TextField fractalName, - ComboBox<TransformationType> transformationComboBox, - VBox transformationVbox, HBox startVectorField, - HBox endVectorField) { - List<String[]> transformations = getInputInformation(transformationComboBox.getValue(), - transformationVbox); - String[] startVector = getInputVector(startVectorField); - String[] endVector = getInputVector(endVectorField); - controller.addCustomFractal( - startVector, endVector, transformations, fractalName.getText()); - } - - /** - * Retrieves input information based on the selected transformation type. - * - * @param selectedTransformation the selected transformation type. - * @param transformationVbox the HBox containing the input fields for transformations. - * @return a list of Transform2D objects. - */ - private List<String[]> getInputInformation(TransformationType selectedTransformation, - VBox transformationVbox) { - if (selectedTransformation == TransformationType.JULIA) { - return getJuliaTransformation(transformationVbox); - } else if (selectedTransformation == TransformationType.AFFINE) { - return getAffineTransformation(transformationVbox); - } - return new ArrayList<>(); - } - - /** - * Retrieves input information for the Julia transformation. - * - * @param transformationVbox the HBox containing the input fields for the Julia - * transformation. - * @return a list of Transform2D objects for the Julia transformation. - */ - private List<String[]> getJuliaTransformation(VBox transformationVbox) { - List<String[]> list = new ArrayList<>(); - if (!transformationVbox.getChildren().isEmpty()) { - HBox juliaFields = (HBox) transformationVbox.getChildren().get(0); - list.add(new String[]{((TextField) juliaFields.getChildren().get(1)).getText(), - ((TextField) juliaFields.getChildren().get(2)).getText()}); - } - return list; - } - - /** - * Retrieves input information for the Affine transformation. - * - * @param transformationInputField the HBox containing the input fields for the Affine - * transformation. - * @return a list of Transform2D objects for the Affine transformation. - */ - private List<String[]> getAffineTransformation(VBox transformationInputField) { - List<String[]> list = new ArrayList<>(); - if (!transformationInputField.getChildren().isEmpty()) { - for (Node node : transformationInputField.getChildren() - .subList(1, transformationInputField.getChildren().size())) { - HBox matrixFields = (HBox) node; - String[] coordinateList = new String[matrixFields.getChildren().size() - 1]; - for (int i = 1; i < matrixFields.getChildren().size(); i++) { - coordinateList[i - 1] = ((TextField) matrixFields.getChildren().get(i)).getText(); - } - list.add(coordinateList); - } - } - return list; - } - - /** - * Retrieves the input vector from an HBox. - * - * @param vectorBox the HBox containing the input fields for the vector. - * @return a Vector2d object representing the input vector. - */ - private String[] getInputVector(HBox vectorBox) { - return new String[]{((TextField) vectorBox.getChildren().get(1)).getText(), - ((TextField) vectorBox.getChildren().get(2)).getText()}; - } - /** * Creates a VBox containing a Pane that tracks mouse movement and two TextFields * that display the normalized mouse coordinates within the Pane. -- GitLab From af128407f6454366d482c7f8a198398470924c99 Mon Sep 17 00:00:00 2001 From: Sverre Halvorsen <sverrgha@stud.ntnu.no> Date: Tue, 21 May 2024 23:32:23 +0200 Subject: [PATCH 2/6] Feat: Implement TextBoxFactory to create text boxes --- .../edu/ntnu/idatt2003/view/MainPageView.java | 18 ++-------- .../view/components/TextBoxFactory.java | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 src/main/java/edu/ntnu/idatt2003/view/components/TextBoxFactory.java diff --git a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java index 015bc7e..49f8842 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java +++ b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java @@ -1,5 +1,7 @@ package edu.ntnu.idatt2003.view; +import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox; + import edu.ntnu.idatt2003.controller.MainPageController; import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory; import edu.ntnu.idatt2003.model.ChaosGameObserver; @@ -161,22 +163,6 @@ public class MainPageView extends Scene implements ChaosGameObserver { return spacing; } - /** - * Creates a text box with the specified text. The text box is styled with the - * "text-box" style class. The text box is a StackPane with a Label as a child. - * The text box is configured to grow horizontally. - * - * @param text The text to display in the text box. - * @return The text box. - */ - private StackPane createTextBox(String text) { - StackPane textBox = new StackPane(); - textBox.getStyleClass().add("text-box"); - HBox.setHgrow(textBox, Priority.ALWAYS); - textBox.getChildren().add(new Label(text)); - return textBox; - } - /** * Creates a HBox containing a text box with the specified text and as many text fields * as specified is given prompt text to, which gets the size given as parameters. diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxFactory.java b/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxFactory.java new file mode 100644 index 0000000..cd9c2ca --- /dev/null +++ b/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxFactory.java @@ -0,0 +1,35 @@ +package edu.ntnu.idatt2003.view.components; + +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; + +/** + * A factory for creating text boxes. The text boxes are styled with the "text-box" + * style class. + * Goal: Creating text boxes. + */ +public class TextBoxFactory { + + /** + * Private constructor to prevent instantiation. + */ + private TextBoxFactory() { + } + /** + * Creates a text box with the specified text. The text box is styled with the + * "text-box" style class. The text box is a StackPane with a Label as a child. + * The text box is configured to grow horizontally. + * + * @param text The text to display in the text box. + * @return The text box. + */ + public static StackPane createTextBox(String text) { + StackPane textBox = new StackPane(); + textBox.getStyleClass().add("text-box"); + HBox.setHgrow(textBox, Priority.ALWAYS); + textBox.getChildren().add(new Label(text)); + return textBox; + } +} -- GitLab From 61eeafeb1ff74cf10ba4ce9deeaffcaf7c9fcde9 Mon Sep 17 00:00:00 2001 From: Sverre Halvorsen <sverrgha@stud.ntnu.no> Date: Tue, 21 May 2024 23:59:08 +0200 Subject: [PATCH 3/6] Feat: Implement TextBoxFactoryAndTextFieldContainerFactory to create containers with a text box and text fields --- .../edu/ntnu/idatt2003/view/MainPageView.java | 60 ++++------------ .../TextBoxAndTextFieldContainerFactory.java | 70 +++++++++++++++++++ 2 files changed, 82 insertions(+), 48 deletions(-) create mode 100644 src/main/java/edu/ntnu/idatt2003/view/components/TextBoxAndTextFieldContainerFactory.java diff --git a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java index 49f8842..6776abf 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java +++ b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java @@ -1,6 +1,7 @@ package edu.ntnu.idatt2003.view; import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox; +import static edu.ntnu.idatt2003.view.components.TextBoxAndTextFieldContainerFactory.createTextBoxWithTextFieldsContainer; import edu.ntnu.idatt2003.controller.MainPageController; import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory; @@ -163,45 +164,6 @@ public class MainPageView extends Scene implements ChaosGameObserver { return spacing; } - /** - * Creates a HBox containing a text box with the specified text and as many text fields - * as specified is given prompt text to, which gets the size given as parameters. - * - * @param text The text to display in the text box. - * @param width The width of the text field. - * @param height The height of the text field. - * @param promptTexts The prompt texts for the text fields. - * @return The HBox containing the text box and text fields. - */ - public HBox createTextBoxWithTextField(String text, int width, - int height, String... promptTexts) { - HBox container = new HBox(DEFAULT_SPACING); - container.getChildren().add(createTextBox(text)); - for (String promptText : promptTexts) { - container.getChildren().add(new StyledTextField(promptText, width, height)); - } - return container; - } - - /** - * Creates a HBox containing a text box with the specified text and as many text fields - * as the length of the doubles array, which gets the height and with given as parameters. - * - * @param text The text to display in the text box. - * @param width The width of the text field. - * @param height The height of the text field. - * @param coords The text for the StyledTextField. - * @return The HBox containing the text box and text fields. - */ - private HBox createTextBoxWithTextField(String text, int width, int height, double[] coords) { - HBox container = new HBox(DEFAULT_SPACING); - container.getChildren().add(createTextBox(text)); - for (double coordinate : coords) { - container.getChildren().add(new StyledTextField(coordinate, width, height)); - } - return container; - } - /** * Creates a button container with a ComboBox to change the type * of transformation, buttons for running steps/resetting, the @@ -308,14 +270,15 @@ public class MainPageView extends Scene implements ChaosGameObserver { HBox startVectorField; HBox endVectorField; if (controller.isAddingCustomFractal()) { - startVectorField = createTextBoxWithTextField("Start vector:", 100, 20, + startVectorField = createTextBoxWithTextFieldsContainer( + DEFAULT_SPACING, "Start vector:", 100, 20, "x0", "x1"); - endVectorField = createTextBoxWithTextField("End vector:", 100, 20, + endVectorField = createTextBoxWithTextFieldsContainer(DEFAULT_SPACING, "End vector:", 100, 20, "x0", "x1"); } else { - startVectorField = createTextBoxWithTextField("Min vector:", 100, 20, + startVectorField = createTextBoxWithTextFieldsContainer(DEFAULT_SPACING, "Min vector:", 100, 20, controller.getMinCoordsX()); - endVectorField = createTextBoxWithTextField("Max vector:", 100, 20, + endVectorField = createTextBoxWithTextFieldsContainer(DEFAULT_SPACING, "Max vector:", 100, 20, controller.getMaxCoordsX()); } @@ -451,19 +414,20 @@ public class MainPageView extends Scene implements ChaosGameObserver { if (transformationComboBox.getValue() == TransformationType.AFFINE) { vbox.getChildren().add( new StyledButton("Add Transformation", 250, 20, - e -> buttonEventHandler.handleAddTransformation(vbox, "Transformation:") + e -> buttonEventHandler.handleAddTransformation(vbox, + "Transformation:", DEFAULT_SPACING) ) ); } if (!controller.isAddingCustomFractal()) { for (double[] coords : controller.getTransformList()) { - vbox.getChildren().add(createTextBoxWithTextField(textBoxText, - 55, 20, coords)); + vbox.getChildren().add(createTextBoxWithTextFieldsContainer(DEFAULT_SPACING, + textBoxText, 55, 20, coords)); } } else if (controller.isAddingCustomFractal() && transformationComboBox.getValue() == TransformationType.JULIA) { - vbox.getChildren().add(createTextBoxWithTextField(textBoxText, - 55, 20, "c0", "c1")); + vbox.getChildren().add(createTextBoxWithTextFieldsContainer(DEFAULT_SPACING, + textBoxText, 55, 20, "c0", "c1")); } return vbox; } diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxAndTextFieldContainerFactory.java b/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxAndTextFieldContainerFactory.java new file mode 100644 index 0000000..c3f6907 --- /dev/null +++ b/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxAndTextFieldContainerFactory.java @@ -0,0 +1,70 @@ +package edu.ntnu.idatt2003.view.components; + +import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox; + +import javafx.scene.layout.HBox; + +/** + * A factory for creating containers containing a text box and text fields. + * Goal: Create containers containing a text box and text fields. + */ +public class TextBoxAndTextFieldContainerFactory { + + /** + * Private constructor to prevent instantiation. + */ + private TextBoxAndTextFieldContainerFactory() { + } + + /** + * Creates a HBox containing a text box with the specified text and as many text fields + * as the length of the doubles array, which gets the height and with given as parameters. + * + * @param spacing Spacing between the elements. + * @param textBoxText The text for the text box. + * @param textFieldWidth The width of the text fields. + * @param textFieldHeight The height of the text field. + * @param textFieldCoords The coordinates for the TextFields. + * @return The HBox containing the text box and text fields. + */ + public static HBox createTextBoxWithTextFieldsContainer(int spacing, + String textBoxText, + int textFieldWidth, + int textFieldHeight, + double[] textFieldCoords) { + HBox container = new HBox(spacing); + container.getChildren().add(createTextBox(textBoxText)); + for (double coordinate : textFieldCoords) { + container.getChildren().add(new StyledTextField(coordinate, + textFieldWidth, textFieldHeight)); + } + return container; + } + + /** + * Creates a HBox containing a text box with the specified text and as many text fields + * as specified is given prompt text to, which gets the size given as parameters. + * + * @param spacing Spacing between the elements. + * @param textBoxText The text to display in the text box. + * @param textFieldWidth The width of the text field. + * @param textFieldHeight The height of the text field. + * @param promptTexts The prompt texts for the text fields. + * @return The HBox containing the text box and text fields. + */ + public static HBox createTextBoxWithTextFieldsContainer(int spacing, + String textBoxText, + int textFieldWidth, + int textFieldHeight, + String... promptTexts) { + HBox container = new HBox(spacing); + container.getChildren().add(createTextBox(textBoxText)); + for (String promptText : promptTexts) { + container.getChildren().add(new StyledTextField(promptText, + textFieldWidth, textFieldHeight)); + } + return container; + } + + +} -- GitLab From 3d833d20b4c363cc2895fff5012da9472b483111 Mon Sep 17 00:00:00 2001 From: Sverre Halvorsen <sverrgha@stud.ntnu.no> Date: Wed, 22 May 2024 09:10:42 +0200 Subject: [PATCH 4/6] Refactor: Refactor ComboBoxFactory into a factory-class --- .../idatt2003/view/ButtonEventHandler.java | 64 ---------- .../edu/ntnu/idatt2003/view/EventHandler.java | 83 ++++++++++++ .../edu/ntnu/idatt2003/view/MainPageView.java | 120 ++++++++---------- .../view/components/ComboBoxFactory.java | 66 ++++++++++ .../view/components/StyledComboBox.java | 30 ----- 5 files changed, 203 insertions(+), 160 deletions(-) delete mode 100644 src/main/java/edu/ntnu/idatt2003/view/ButtonEventHandler.java create mode 100644 src/main/java/edu/ntnu/idatt2003/view/EventHandler.java create mode 100644 src/main/java/edu/ntnu/idatt2003/view/components/ComboBoxFactory.java delete mode 100644 src/main/java/edu/ntnu/idatt2003/view/components/StyledComboBox.java diff --git a/src/main/java/edu/ntnu/idatt2003/view/ButtonEventHandler.java b/src/main/java/edu/ntnu/idatt2003/view/ButtonEventHandler.java deleted file mode 100644 index fa8f5ae..0000000 --- a/src/main/java/edu/ntnu/idatt2003/view/ButtonEventHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -package edu.ntnu.idatt2003.view; - -import edu.ntnu.idatt2003.controller.MainPageController; -import edu.ntnu.idatt2003.view.MainPageView; -import javafx.scene.layout.VBox; -import javafx.stage.FileChooser; -import javafx.stage.Stage; - -import java.io.File; -import java.util.List; - -public class ButtonEventHandler { - private final MainPageController controller; - private final MainPageView view; - - protected ButtonEventHandler(MainPageController controller, MainPageView view) { - this.controller = controller; - this.view = view; - } - - protected void handleRunSteps(int steps) { - controller.runSteps(steps); - } - protected void handleReset() { - controller.runSteps(-1); - } - - protected void handleSave(String fractalName, List<String[]> transformations, - String[] startVector, String[] endVector) { - controller.addCustomFractal(startVector, endVector, transformations, fractalName); - } - protected void handleCancel() { - view.render(); - } - - protected void handleSaveLocally() { - FileChooser fileChooser = new FileChooser(); - fileChooser.getExtensionFilters().add( - new FileChooser.ExtensionFilter("Text Files", "*.txt")); - controller.saveToLocalDirectory(fileChooser.showSaveDialog(null)); - } - - /** - * Opens a file chooser dialog that enables the user to upload a custom - * text file with a chaos game description. The file is then uploaded - * by the controller. If an exception occurs, an alert is shown. - */ - protected void handleUploadFile() { - FileChooser fileChooser = new FileChooser(); - fileChooser.getExtensionFilters().add( - new FileChooser.ExtensionFilter("Text Files", "*.txt")); - - Stage fileChooserStage = new Stage(); - File file = fileChooser.showOpenDialog(fileChooserStage); - if (file != null) { - controller.uploadFile(file); - } - } - - public void handleAddTransformation(VBox vbox, String textBoxText) { - vbox.getChildren().add(view.createTextBoxWithTextField(textBoxText, 55, 20, "a00", "a01", "a10", "a11", "v0", "v1")); - } - -} diff --git a/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java b/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java new file mode 100644 index 0000000..23315b6 --- /dev/null +++ b/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java @@ -0,0 +1,83 @@ +package edu.ntnu.idatt2003.view; + +import edu.ntnu.idatt2003.controller.MainPageController; +import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory; +import javafx.scene.control.ComboBox; +import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import javafx.stage.Stage; + +import java.io.File; +import java.util.List; + +import static edu.ntnu.idatt2003.view.components.TextBoxAndTextFieldContainerFactory.createTextBoxWithTextFieldsContainer; + +public class EventHandler { + private final MainPageController controller; + private final MainPageView view; + + protected EventHandler(MainPageController controller, MainPageView view) { + this.controller = controller; + this.view = view; + } + + protected void handleRunSteps(int steps) { + controller.runSteps(steps); + } + + protected void handleReset() { + controller.runSteps(-1); + } + + protected void handleSave(String fractalName, List<String[]> transformations, + String[] startVector, String[] endVector) { + controller.addCustomFractal(startVector, endVector, transformations, fractalName); + } + + protected void handleCancel() { + view.render(); + } + + protected void handleSaveLocally() { + FileChooser fileChooser = new FileChooser(); + fileChooser.getExtensionFilters().add( + new FileChooser.ExtensionFilter("Text Files", "*.txt")); + controller.saveToLocalDirectory(fileChooser.showSaveDialog(null)); + } + + /** + * Opens a file chooser dialog that enables the user to upload a custom + * text file with a chaos game description. The file is then uploaded + * by the controller. If an exception occurs, an alert is shown. + */ + protected void handleUploadFile() { + FileChooser fileChooser = new FileChooser(); + fileChooser.getExtensionFilters().add( + new FileChooser.ExtensionFilter("Text Files", "*.txt")); + + Stage fileChooserStage = new Stage(); + File file = fileChooser.showOpenDialog(fileChooserStage); + if (file != null) { + controller.uploadFile(file); + } + } + + public void handleAddTransformation(VBox vbox, String textBoxText, int spacing) { + vbox.getChildren().add(createTextBoxWithTextFieldsContainer(spacing, + textBoxText, 55, 20, "a00", "a01", + "a10", "a11", "v0", "v1")); + } + + protected <T> void handleChangeFractal(ComboBox<T> box) { + if (box.getValue() instanceof ChaosGameDescriptionFactory.DescriptionTypeEnum descriptionTypeEnum) { + controller.changeFractal(descriptionTypeEnum); + } else if (box.getValue() instanceof String string) { + controller.changeFractal(string); + } + } + + protected <T> void handleEditTransformationChoice(ComboBox<T> box) { + view.setSelectedTransformation((MainPageView.TransformationType) box.getValue()); + controller.changeFractal("add new"); + } +} diff --git a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java index 6776abf..3c327e3 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java +++ b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java @@ -1,7 +1,8 @@ package edu.ntnu.idatt2003.view; -import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox; +import static edu.ntnu.idatt2003.view.components.ComboBoxFactory.createComboBox; import static edu.ntnu.idatt2003.view.components.TextBoxAndTextFieldContainerFactory.createTextBoxWithTextFieldsContainer; +import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox; import edu.ntnu.idatt2003.controller.MainPageController; import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory; @@ -9,14 +10,12 @@ import edu.ntnu.idatt2003.model.ChaosGameObserver; import edu.ntnu.idatt2003.utils.Sizes; import edu.ntnu.idatt2003.view.components.ChaosImage; import edu.ntnu.idatt2003.view.components.StyledButton; -import edu.ntnu.idatt2003.view.components.StyledComboBox; import edu.ntnu.idatt2003.view.components.StyledTextField; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Objects; - import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.Scene; @@ -46,7 +45,7 @@ public class MainPageView extends Scene implements ChaosGameObserver { private static final int COMPONENT_HEIGHT = 40; private static final int BUTTON_WIDTH = (int) (Sizes.SCREEN_WIDTH) / BUTTON_COUNT; private static final int DEFAULT_SPACING = 10; - private final ButtonEventHandler buttonEventHandler; + private final EventHandler eventHandler; private TextField x0Field; private TextField x1Field; private TransformationType selectedTransformation; @@ -62,7 +61,7 @@ public class MainPageView extends Scene implements ChaosGameObserver { .getResource("/styles/mainPage.css")).toExternalForm()); this.getStylesheets().add(Objects.requireNonNull(getClass() .getResource("/styles/components.css")).toExternalForm()); - this.buttonEventHandler = new ButtonEventHandler(mainPageController, this); + this.eventHandler = new EventHandler(mainPageController, this); root = (BorderPane) this.getRoot(); this.selectedTransformation = TransformationType.AFFINE; this.controller = mainPageController; @@ -179,30 +178,25 @@ public class MainPageView extends Scene implements ChaosGameObserver { StackPane.setAlignment(buttonContainer, Pos.TOP_CENTER); buttonContainer.getChildren().addAll( - createComboBox(), - createCustomComboBox(), + createComboBox("Fractals", BUTTON_WIDTH, COMPONENT_HEIGHT, + Arrays.asList(ChaosGameDescriptionFactory.DescriptionTypeEnum.values()), + (box, e) -> eventHandler.handleChangeFractal(box)), + createComboBox("Custom fractals", BUTTON_WIDTH, COMPONENT_HEIGHT, + controller.getCustomFractalNames(), + (box, e) -> eventHandler.handleChangeFractal(box)), new StyledButton("10 Steps", BUTTON_WIDTH, COMPONENT_HEIGHT, - e -> buttonEventHandler.handleRunSteps(10)), + e -> eventHandler.handleRunSteps(10)), new StyledButton("100 Steps", BUTTON_WIDTH, COMPONENT_HEIGHT, - e -> buttonEventHandler.handleRunSteps(100)), + e -> eventHandler.handleRunSteps(100)), new StyledButton("1000 Steps", BUTTON_WIDTH, COMPONENT_HEIGHT, - e -> buttonEventHandler.handleRunSteps(1000)), + e -> eventHandler.handleRunSteps(1000)), createStepsTextField(), new StyledButton("Reset", BUTTON_WIDTH, COMPONENT_HEIGHT, - e -> buttonEventHandler.handleReset()) + e -> eventHandler.handleReset()) ); return buttonContainer; } - private ComboBox<ChaosGameDescriptionFactory.DescriptionTypeEnum> createComboBox() { - StyledComboBox<ChaosGameDescriptionFactory.DescriptionTypeEnum> transformMenu = - new StyledComboBox<>("Fractals", BUTTON_WIDTH, COMPONENT_HEIGHT, - Arrays.asList(ChaosGameDescriptionFactory.DescriptionTypeEnum.values()) - ); - transformMenu.setOnAction(e -> controller.changeFractal(transformMenu.getValue())); - return transformMenu; - } - private StyledTextField createStepsTextField() { StyledTextField stepsField = new StyledTextField("Steps", BUTTON_WIDTH, COMPONENT_HEIGHT); stepsField.getStyleClass().set(0, "button"); @@ -213,20 +207,6 @@ public class MainPageView extends Scene implements ChaosGameObserver { return stepsField; } - /** - * Creates a StyledComboBox with setOnAction to change the fractal based on - * the selected value. - * - * @return The ComboBox with custom fractals. - */ - private ComboBox<String> createCustomComboBox() { - StyledComboBox<String> customMenu = new StyledComboBox<>("Custom fractals", - BUTTON_WIDTH, COMPONENT_HEIGHT, controller.getCustomFractalNames()); - customMenu.getItems().add("Add new"); - customMenu.setOnAction(e -> controller.changeFractal(customMenu.getValue())); - return customMenu; - } - /** * Shows an alert with a specified message to give feedback to the user. * @@ -266,20 +246,27 @@ public class MainPageView extends Scene implements ChaosGameObserver { VBox addPanel = createMainPanel(); TextField fractalName = new StyledTextField("Fractal name", 210, 20); fractalName.setText(controller.getCurrentFractalName()); - ComboBox<TransformationType> transformationComboBox = createAddComboBox(); + ComboBox<TransformationType> transformationComboBox = createComboBox("Transformation", + 210, COMPONENT_HEIGHT, + Arrays.asList(TransformationType.values()), + (box, e) -> eventHandler.handleEditTransformationChoice(box), + getTransformationComboBoxValue()); HBox startVectorField; HBox endVectorField; if (controller.isAddingCustomFractal()) { startVectorField = createTextBoxWithTextFieldsContainer( DEFAULT_SPACING, "Start vector:", 100, 20, "x0", "x1"); - endVectorField = createTextBoxWithTextFieldsContainer(DEFAULT_SPACING, "End vector:", 100, 20, - "x0", "x1"); + endVectorField = createTextBoxWithTextFieldsContainer( + DEFAULT_SPACING, "End vector:", 100, + 20, "x0", "x1"); } else { - startVectorField = createTextBoxWithTextFieldsContainer(DEFAULT_SPACING, "Min vector:", 100, 20, - controller.getMinCoordsX()); - endVectorField = createTextBoxWithTextFieldsContainer(DEFAULT_SPACING, "Max vector:", 100, 20, - controller.getMaxCoordsX()); + startVectorField = createTextBoxWithTextFieldsContainer( + DEFAULT_SPACING, "Min vector:", 100, + 20, controller.getMinCoordsX()); + endVectorField = createTextBoxWithTextFieldsContainer( + DEFAULT_SPACING, "Max vector:", 100, + 20, controller.getMaxCoordsX()); } VBox transformationVbox = createTransformationVbox(transformationComboBox); @@ -299,23 +286,36 @@ public class MainPageView extends Scene implements ChaosGameObserver { new HBox( DEFAULT_SPACING, new StyledButton("Save", 20, - e -> buttonEventHandler.handleSave( + e -> eventHandler.handleSave( fractalName.getText(), - getTransformationList(transformationComboBox.getValue(), transformationVbox), + getTransformationList( + transformationComboBox.getValue(), + transformationVbox), getInputVector(startVectorField), getInputVector(endVectorField) ) ), - new StyledButton("Cancel", 20, e -> buttonEventHandler.handleCancel()), + new StyledButton("Cancel", 20, e -> eventHandler.handleCancel()), new StyledButton("Save current locally", 20, - e -> buttonEventHandler.handleSaveLocally()), - new StyledButton("Add File", 100, 20, e -> buttonEventHandler.handleUploadFile()) + e -> eventHandler.handleSaveLocally()), + new StyledButton("Add File", 100, 20, e -> eventHandler.handleUploadFile()) ) ); StackPane.setAlignment(addPanel, Pos.BOTTOM_LEFT); return addPanel; } + + private TransformationType getTransformationComboBoxValue() { + if (controller.isAddingCustomFractal()) { + return selectedTransformation; + } else if (controller.fractalIsJulia()) { + return TransformationType.JULIA; + } else { + return TransformationType.AFFINE; + } + } + /** * Retrieves input information based on the selected transformation type. * @@ -323,8 +323,9 @@ public class MainPageView extends Scene implements ChaosGameObserver { * @param transformationVbox the HBox containing the input fields for transformations. * @return a list of Transform2D objects. */ - private List<String[]> getTransformationList(MainPageView.TransformationType selectedTransformation, - VBox transformationVbox) { + private List<String[]> getTransformationList(MainPageView.TransformationType + selectedTransformation, + VBox transformationVbox) { if (selectedTransformation == MainPageView.TransformationType.JULIA) { return getJuliaTransformation(transformationVbox); } else if (selectedTransformation == MainPageView.TransformationType.AFFINE) { @@ -384,23 +385,6 @@ public class MainPageView extends Scene implements ChaosGameObserver { ((TextField) vectorBox.getChildren().get(2)).getText()}; } - private ComboBox<TransformationType> createAddComboBox() { - StyledComboBox<TransformationType> transformMenu = new StyledComboBox<>("Transformation", - 210, COMPONENT_HEIGHT, Arrays.asList(TransformationType.values())); - transformMenu.setOnAction(e -> { - selectedTransformation = transformMenu.getValue(); - controller.changeFractal("add new"); - }); - if (controller.isAddingCustomFractal()) { - transformMenu.setValue(this.selectedTransformation); - } else if (controller.fractalIsJulia()) { - transformMenu.setValue(TransformationType.JULIA); - } else { - transformMenu.setValue(TransformationType.AFFINE); - } - return transformMenu; - } - /** * Creates a VBox containing input fields for transformations. * @@ -414,7 +398,7 @@ public class MainPageView extends Scene implements ChaosGameObserver { if (transformationComboBox.getValue() == TransformationType.AFFINE) { vbox.getChildren().add( new StyledButton("Add Transformation", 250, 20, - e -> buttonEventHandler.handleAddTransformation(vbox, + e -> eventHandler.handleAddTransformation(vbox, "Transformation:", DEFAULT_SPACING) ) ); @@ -490,4 +474,8 @@ public class MainPageView extends Scene implements ChaosGameObserver { x0Field.setText(String.format(Locale.ENGLISH, "%.5f", x)); x1Field.setText(String.format(Locale.ENGLISH, "%.5f", y)); } + + public void setSelectedTransformation(TransformationType transformationType) { + selectedTransformation = transformationType; + } } diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/ComboBoxFactory.java b/src/main/java/edu/ntnu/idatt2003/view/components/ComboBoxFactory.java new file mode 100644 index 0000000..a8056b8 --- /dev/null +++ b/src/main/java/edu/ntnu/idatt2003/view/components/ComboBoxFactory.java @@ -0,0 +1,66 @@ +package edu.ntnu.idatt2003.view.components; + +import java.util.List; +import java.util.function.BiConsumer; +import javafx.event.ActionEvent; +import javafx.scene.control.ComboBox; + +/** + * A styled combo box with a specified prompt text, width, height and values. + * The combo box has a style class "combo-box" and the values are set to the specified values. + * Goal: Create a styled combo box with a specified prompt text, width, height and values. + */ +public class ComboBoxFactory { + + /** + * Private constructor to prevent instantiation. + */ + private ComboBoxFactory() { + } + + /** + * Creates a styled combo box with a specified prompt text, width, height and values. + * The combo box has a style class "combo-box" and the values are set to the specified values. + * + * @param promptText The text to display in the combo box when it is empty. + * @param width The preferred width of the combo box. + * @param height The preferred height of the combo box. + * @param values The values to set in the combo box. + */ + public static <T> ComboBox<T> createComboBox(String promptText, int width, int height, List<T> values, + BiConsumer<ComboBox<T>, ActionEvent> eventHandler) { + return createComboBoxWithStyle(promptText, width, height, values, eventHandler); + } + + public static <T> ComboBox<T> createComboBox(String promptText, int width, int height, List<T> values, + BiConsumer<ComboBox<T>, ActionEvent> eventHandler, T defaultValue) { + ComboBox<T> comboBox = createComboBoxWithStyle(promptText, width, height, values, eventHandler); + comboBox.setValue(defaultValue); + return comboBox; + } + + /** + * Creates a styled combo box with a specified prompt text, width, height, values and events + * to be used in other methods in class. + * + * @param promptText The text to display in the combo box when it is empty. + * @param width The preferred width of the combo box. + * @param height The preferred height of the combo box. + * @param values The values to set in the combo box. + * @param eventHandler The event handler to set on the combo box. + * @return The styled combo box. + */ + private static <T> ComboBox<T> createComboBoxWithStyle(String promptText, int width, int height, List<T> values, + BiConsumer<ComboBox<T>, ActionEvent> eventHandler) { + ComboBox<T> comboBox = new ComboBox<>(); + comboBox.setPromptText(promptText); + comboBox.setPromptText(promptText); + comboBox.setPrefSize(width, height); + comboBox.getStyleClass().add("combo-box"); + comboBox.setOnAction(e -> eventHandler.accept(comboBox, e)); + if (values != null) { + comboBox.getItems().addAll(values); + } + return comboBox; + } +} diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/StyledComboBox.java b/src/main/java/edu/ntnu/idatt2003/view/components/StyledComboBox.java deleted file mode 100644 index 0f17b9e..0000000 --- a/src/main/java/edu/ntnu/idatt2003/view/components/StyledComboBox.java +++ /dev/null @@ -1,30 +0,0 @@ -package edu.ntnu.idatt2003.view.components; - -import java.util.List; -import javafx.scene.control.ComboBox; - -/** - * A styled combo box with a specified prompt text, width, height and values. - * The combo box has a style class "combo-box" and the values are set to the specified values. - * Goal: Create a styled combo box with a specified prompt text, width, height and values. - */ -public class StyledComboBox<T> extends ComboBox<T> { - /** - * Creates a styled combo box with a specified prompt text, width, height and values. - * The combo box has a style class "combo-box" and the values are set to the specified values. - * - * @param promptText The text to display in the combo box when it is empty. - * @param width The preferred width of the combo box. - * @param height The preferred height of the combo box. - * @param values The values to set in the combo box. - */ - public StyledComboBox(String promptText, int width, int height, List<T> values) { - super(); - this.setPromptText(promptText); - this.setPrefSize(width, height); - this.getStyleClass().add("combo-box"); - if (values != null) { - this.getItems().addAll(values); - } - } -} -- GitLab From 829ea05972e7de1f15d5c163119d69715de53cff Mon Sep 17 00:00:00 2001 From: Sverre Halvorsen <sverrgha@stud.ntnu.no> Date: Wed, 22 May 2024 09:49:24 +0200 Subject: [PATCH 5/6] Refactor: Refactor TextFieldFactory into a factory-class --- .../edu/ntnu/idatt2003/view/EventHandler.java | 86 +++++++++++++++++-- .../edu/ntnu/idatt2003/view/MainPageView.java | 21 ++--- .../view/components/StyledTextField.java | 43 ---------- .../TextBoxAndTextFieldContainerFactory.java | 5 +- .../view/components/TextFieldFactory.java | 73 ++++++++++++++++ 5 files changed, 163 insertions(+), 65 deletions(-) delete mode 100644 src/main/java/edu/ntnu/idatt2003/view/components/StyledTextField.java create mode 100644 src/main/java/edu/ntnu/idatt2003/view/components/TextFieldFactory.java diff --git a/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java b/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java index 23315b6..a293c70 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java +++ b/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java @@ -1,43 +1,96 @@ package edu.ntnu.idatt2003.view; +import static edu.ntnu.idatt2003.view.components.TextBoxAndTextFieldContainerFactory.createTextBoxWithTextFieldsContainer; + import edu.ntnu.idatt2003.controller.MainPageController; import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory; +import java.io.File; +import java.util.List; import javafx.scene.control.ComboBox; +import javafx.scene.control.TextField; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Stage; -import java.io.File; -import java.util.List; - -import static edu.ntnu.idatt2003.view.components.TextBoxAndTextFieldContainerFactory.createTextBoxWithTextFieldsContainer; +/** + * An event handler for the main page view. The event handler is responsible for + * handling events from the main page view and calling the appropriate methods + * in the main page controller. + */ public class EventHandler { private final MainPageController controller; private final MainPageView view; + /** + * Creates a new event handler with the specified controller and view. + * The event handler is responsible for handling events from the main page + * view and calling the appropriate methods in the main page controller. + * + * @param controller The controller to call methods on. + * @param view The view to handle events from. + */ protected EventHandler(MainPageController controller, MainPageView view) { this.controller = controller; this.view = view; } + /** + * Handles the event of the user pressing a run steps button. The event handler + * calls the controller's runSteps method with the number of steps to run. + * + * @param steps The number of steps to run. + */ protected void handleRunSteps(int steps) { controller.runSteps(steps); } + /** + * Handles the event of the user pressing a reset button. The event handler + * calls the controller's runSteps method with -1 as the number of steps to run. + */ protected void handleReset() { controller.runSteps(-1); } + /** + * Handles the event of the user running steps from a TextField. The event handler + * calls the controller's runCustomSteps method with the steps to run. + * + * @param stepsField The text field containing the steps to run. + */ + protected void handleRunCustomSteps(TextField stepsField) { + controller.runCustomSteps(stepsField.getText()); + stepsField.clear(); + } + + /** + * Handles the event of the user pressing a save button. The event handler + * calls the controller's save-method with the fractal name, transformations, + * + * @param fractalName The name of the fractal. + * @param transformations The transformations of the fractal. + * @param startVector The start vector of the fractal. + * @param endVector The end vector of the fractal. + */ protected void handleSave(String fractalName, List<String[]> transformations, String[] startVector, String[] endVector) { controller.addCustomFractal(startVector, endVector, transformations, fractalName); } + /** + * Handles the event of the user pressing a cancel button. The event handler + * calls the view's render method to render the main page view. + */ protected void handleCancel() { view.render(); } + /** + * Handles the event of the user pressing a save locally button. The event handler + * asks for a location to save from user and calls the controller's saveToLocalDirectory + * method with the location to save to. + */ protected void handleSaveLocally() { FileChooser fileChooser = new FileChooser(); fileChooser.getExtensionFilters().add( @@ -62,20 +115,43 @@ public class EventHandler { } } + /** + * Handles the event of the user pressing addTransformation button. It adds a new + * text box with text fields for the user to input the transformation matrix and vector + * to the vbox. + * + * @param vbox the vbox to add the text box with fields to. + * @param textBoxText the text to display in the text box. + * @param spacing the spacing between the elements. + */ public void handleAddTransformation(VBox vbox, String textBoxText, int spacing) { vbox.getChildren().add(createTextBoxWithTextFieldsContainer(spacing, textBoxText, 55, 20, "a00", "a01", "a10", "a11", "v0", "v1")); } + /** + * Handles the event of the user using a ComboBox to change fractal. It calls the controller's + * changeFractal method with the selected fractal. + * + * @param box The ComboBox containing the fractal to change to. + */ protected <T> void handleChangeFractal(ComboBox<T> box) { - if (box.getValue() instanceof ChaosGameDescriptionFactory.DescriptionTypeEnum descriptionTypeEnum) { + if (box.getValue() + instanceof ChaosGameDescriptionFactory.DescriptionTypeEnum descriptionTypeEnum) { controller.changeFractal(descriptionTypeEnum); } else if (box.getValue() instanceof String string) { controller.changeFractal(string); } } + /** + * Handles the event of the user using the edit ComboBox. The event handler + * sets the selected transformation in the view. The controller then changes + * the fractal to "add new". + * + * @param box The ComboBox containing the transformationType. + */ protected <T> void handleEditTransformationChoice(ComboBox<T> box) { view.setSelectedTransformation((MainPageView.TransformationType) box.getValue()); controller.changeFractal("add new"); diff --git a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java index 3c327e3..163d6ea 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java +++ b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java @@ -3,6 +3,7 @@ package edu.ntnu.idatt2003.view; import static edu.ntnu.idatt2003.view.components.ComboBoxFactory.createComboBox; import static edu.ntnu.idatt2003.view.components.TextBoxAndTextFieldContainerFactory.createTextBoxWithTextFieldsContainer; import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox; +import static edu.ntnu.idatt2003.view.components.TextFieldFactory.createTextField; import edu.ntnu.idatt2003.controller.MainPageController; import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory; @@ -10,7 +11,6 @@ import edu.ntnu.idatt2003.model.ChaosGameObserver; import edu.ntnu.idatt2003.utils.Sizes; import edu.ntnu.idatt2003.view.components.ChaosImage; import edu.ntnu.idatt2003.view.components.StyledButton; -import edu.ntnu.idatt2003.view.components.StyledTextField; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -134,8 +134,8 @@ public class MainPageView extends Scene implements ChaosGameObserver { private HBox createDynamicJuliaContainer() { HBox dynamicJuliaContainer = new HBox(DEFAULT_SPACING); dynamicJuliaContainer.setAlignment(Pos.BOTTOM_CENTER); - x0Field = new StyledTextField("x: ", 100, 20); - x1Field = new StyledTextField("y: ", 100, 20); + x0Field = createTextField("x: ", 100, 20); + x1Field = createTextField("y: ", 100, 20); if (controller.fractalIsJulia()) { VBox juliaInformationContainer = new VBox(DEFAULT_SPACING); HBox.setHgrow(juliaInformationContainer, Priority.ALWAYS); @@ -190,23 +190,14 @@ public class MainPageView extends Scene implements ChaosGameObserver { e -> eventHandler.handleRunSteps(100)), new StyledButton("1000 Steps", BUTTON_WIDTH, COMPONENT_HEIGHT, e -> eventHandler.handleRunSteps(1000)), - createStepsTextField(), + createTextField("Steps", BUTTON_WIDTH, COMPONENT_HEIGHT, + (box, e) -> eventHandler.handleRunCustomSteps(box)), new StyledButton("Reset", BUTTON_WIDTH, COMPONENT_HEIGHT, e -> eventHandler.handleReset()) ); return buttonContainer; } - private StyledTextField createStepsTextField() { - StyledTextField stepsField = new StyledTextField("Steps", BUTTON_WIDTH, COMPONENT_HEIGHT); - stepsField.getStyleClass().set(0, "button"); - stepsField.setOnAction(e -> { - controller.runCustomSteps(stepsField.getText()); - stepsField.clear(); - }); - return stepsField; - } - /** * Shows an alert with a specified message to give feedback to the user. * @@ -244,7 +235,7 @@ public class MainPageView extends Scene implements ChaosGameObserver { */ public VBox createAddFractalPanel() { VBox addPanel = createMainPanel(); - TextField fractalName = new StyledTextField("Fractal name", 210, 20); + TextField fractalName = createTextField("Fractal name", 210, 20); fractalName.setText(controller.getCurrentFractalName()); ComboBox<TransformationType> transformationComboBox = createComboBox("Transformation", 210, COMPONENT_HEIGHT, diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/StyledTextField.java b/src/main/java/edu/ntnu/idatt2003/view/components/StyledTextField.java deleted file mode 100644 index b74a242..0000000 --- a/src/main/java/edu/ntnu/idatt2003/view/components/StyledTextField.java +++ /dev/null @@ -1,43 +0,0 @@ -package edu.ntnu.idatt2003.view.components; - -import javafx.scene.control.TextField; - -import java.util.Locale; - -/** - * A styled text field with a specified prompt text, width and height. - * The text field has a style class "text-field". - * Goal: Create a styled text field with a specified prompt text, width and height. - */ -public class StyledTextField extends TextField { - /** - * Creates a styled text field with a specified prompt text, width and height. - * The text field has a style class "text-field". - * - * @param promptText The text to display in the text field when it is empty. - * @param width The preferred width of the text field. - * @param height The preferred height of the text field. - */ - public StyledTextField(String promptText, int width, int height) { - super(); - this.setPromptText(promptText); - this.setPrefSize(width, height); - this.getStyleClass().add("text-field"); - } - - /** - * Creates a styled text field with a specified coordinate, width and height. - * The coordinate is formatted to two decimal places, and is set as the text - * of the text field. - * - * @param coordinate the coordinate to display in the text field. - * @param width the preferred width of the text field. - * @param height the preferred height of the text field. - */ - public StyledTextField(double coordinate, int width, int height) { - super(String.format(Locale.US, "%.2f", coordinate)); - this.setPrefSize(width, height); - this.getStyleClass().add("text-field"); - } - -} diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxAndTextFieldContainerFactory.java b/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxAndTextFieldContainerFactory.java index c3f6907..9c9d383 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxAndTextFieldContainerFactory.java +++ b/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxAndTextFieldContainerFactory.java @@ -1,6 +1,7 @@ package edu.ntnu.idatt2003.view.components; import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox; +import static edu.ntnu.idatt2003.view.components.TextFieldFactory.createTextField; import javafx.scene.layout.HBox; @@ -35,7 +36,7 @@ public class TextBoxAndTextFieldContainerFactory { HBox container = new HBox(spacing); container.getChildren().add(createTextBox(textBoxText)); for (double coordinate : textFieldCoords) { - container.getChildren().add(new StyledTextField(coordinate, + container.getChildren().add(createTextField(coordinate, textFieldWidth, textFieldHeight)); } return container; @@ -60,7 +61,7 @@ public class TextBoxAndTextFieldContainerFactory { HBox container = new HBox(spacing); container.getChildren().add(createTextBox(textBoxText)); for (String promptText : promptTexts) { - container.getChildren().add(new StyledTextField(promptText, + container.getChildren().add(createTextField(promptText, textFieldWidth, textFieldHeight)); } return container; diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/TextFieldFactory.java b/src/main/java/edu/ntnu/idatt2003/view/components/TextFieldFactory.java new file mode 100644 index 0000000..e07ecb2 --- /dev/null +++ b/src/main/java/edu/ntnu/idatt2003/view/components/TextFieldFactory.java @@ -0,0 +1,73 @@ +package edu.ntnu.idatt2003.view.components; + +import java.util.Locale; +import java.util.function.BiConsumer; +import javafx.event.ActionEvent; +import javafx.scene.control.TextField; + +/** + * A styled text field with a specified prompt text, width and height. + * The text field has a style class "text-field". + * Goal: Create a styled text field with a specified prompt text, width and height. + */ +public class TextFieldFactory { + + /** + * Private constructor to prevent instantiation. + */ + private TextFieldFactory() { + } + + /** + * Creates a styled text field with a specified prompt text, width and height. + * The text field has a style class "text-field". + * + * @param promptText The text to display in the text field when it is empty. + * @param width The preferred width of the text field. + * @param height The preferred height of the text field. + */ + public static TextField createTextField(String promptText, int width, int height) { + TextField textField = new TextField(); + textField.setPromptText(promptText); + textField.setPrefSize(width, height); + textField.getStyleClass().add("text-field"); + return textField; + } + + /** + * Creates a styled text field with a specified coordinate, width and height. + * The coordinate is formatted to two decimal places, and is set as the text + * of the text field. + * + * @param coordinate the coordinate to display in the text field. + * @param width the preferred width of the text field. + * @param height the preferred height of the text field. + */ + public static TextField createTextField(double coordinate, int width, int height) { + TextField textField = new TextField(String.format(Locale.US, "%.2f", coordinate)); + textField.setPrefSize(width, height); + textField.getStyleClass().add("text-field"); + return textField; + } + + /** + * Creates a styled text field with a specified coordinate, width and height, in addition to this + * it also takes an BiConsumer as a parameter to set the action event. + * + * @param promptText The text to display in the text field when it is empty. + * @param width The preferred width of the text field. + * @param height The preferred height of the text field. + * @param eventHandler The event handler to set on the text field. + * @return The styled text field with action event. + */ + public static TextField createTextField(String promptText, int width, int height, + BiConsumer<TextField, ActionEvent> eventHandler) { + TextField textField = new TextField(); + textField.setPromptText(promptText); + textField.setPrefSize(width, height); + textField.getStyleClass().add("button"); + textField.setOnAction(e -> eventHandler.accept(textField, e)); + return textField; + } + +} -- GitLab From e91ade2710e63458735bab222b8095ed3e16578f Mon Sep 17 00:00:00 2001 From: Sverre Halvorsen <sverrgha@stud.ntnu.no> Date: Wed, 22 May 2024 10:17:22 +0200 Subject: [PATCH 6/6] Refactor: Abstract creation of mouseHoverBox to new component-class MouseHoverBoxFactory --- .../edu/ntnu/idatt2003/view/EventHandler.java | 23 ++++++++ .../edu/ntnu/idatt2003/view/MainPageView.java | 53 ++----------------- .../view/components/MouseHoverBoxFactory.java | 41 ++++++++++++++ .../view/components/TextBoxFactory.java | 1 + 4 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 src/main/java/edu/ntnu/idatt2003/view/components/MouseHoverBoxFactory.java diff --git a/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java b/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java index a293c70..5a79806 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java +++ b/src/main/java/edu/ntnu/idatt2003/view/EventHandler.java @@ -6,8 +6,11 @@ import edu.ntnu.idatt2003.controller.MainPageController; import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory; import java.io.File; import java.util.List; +import java.util.Locale; import javafx.scene.control.ComboBox; import javafx.scene.control.TextField; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Stage; @@ -156,4 +159,24 @@ public class EventHandler { view.setSelectedTransformation((MainPageView.TransformationType) box.getValue()); controller.changeFractal("add new"); } + + /** + * Handles the mouse event on hovering over the mouse box. The event handler + * calls the controller's changeJuliaTransformationDynamic method with the + * x and y coordinates of the mouse, and sets the text of the x0Field and x1Field + * to the new coordinates. + * + * @param x0Field TextField to display the x coordinate. + * @param x1Field TextField to display the y coordinate. + * @param box The mouse-hover-box + * @param e The mouse event. + */ + protected void handleMouseHoverOnMouseBox(TextField x0Field, TextField x1Field, + Pane box, MouseEvent e) { + double x = (e.getX() / box.getWidth()) * 2 - 1; + double y = (e.getY() / box.getHeight()) * 2 - 1; + controller.changeJuliaTransformationDynamic(x, y); + x0Field.setText(String.format(Locale.ENGLISH, "%.5f", x)); + x1Field.setText(String.format(Locale.ENGLISH, "%.5f", y)); + } } diff --git a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java index 163d6ea..6a5e295 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java +++ b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java @@ -1,6 +1,7 @@ package edu.ntnu.idatt2003.view; import static edu.ntnu.idatt2003.view.components.ComboBoxFactory.createComboBox; +import static edu.ntnu.idatt2003.view.components.MouseHoverBoxFactory.createMouseHoverBox; import static edu.ntnu.idatt2003.view.components.TextBoxAndTextFieldContainerFactory.createTextBoxWithTextFieldsContainer; import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox; import static edu.ntnu.idatt2003.view.components.TextFieldFactory.createTextField; @@ -14,7 +15,6 @@ import edu.ntnu.idatt2003.view.components.StyledButton; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Locale; import java.util.Objects; import javafx.geometry.Pos; import javafx.scene.Node; @@ -22,11 +22,9 @@ import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; @@ -46,8 +44,6 @@ public class MainPageView extends Scene implements ChaosGameObserver { private static final int BUTTON_WIDTH = (int) (Sizes.SCREEN_WIDTH) / BUTTON_COUNT; private static final int DEFAULT_SPACING = 10; private final EventHandler eventHandler; - private TextField x0Field; - private TextField x1Field; private TransformationType selectedTransformation; /** @@ -134,8 +130,8 @@ public class MainPageView extends Scene implements ChaosGameObserver { private HBox createDynamicJuliaContainer() { HBox dynamicJuliaContainer = new HBox(DEFAULT_SPACING); dynamicJuliaContainer.setAlignment(Pos.BOTTOM_CENTER); - x0Field = createTextField("x: ", 100, 20); - x1Field = createTextField("y: ", 100, 20); + TextField x0Field = createTextField("x: ", 100, 20); + TextField x1Field = createTextField("y: ", 100, 20); if (controller.fractalIsJulia()) { VBox juliaInformationContainer = new VBox(DEFAULT_SPACING); HBox.setHgrow(juliaInformationContainer, Priority.ALWAYS); @@ -146,7 +142,8 @@ public class MainPageView extends Scene implements ChaosGameObserver { ); dynamicJuliaContainer.getChildren().addAll( juliaInformationContainer, - mouseBox() + createMouseHoverBox("Hover over me!", 125, + (box, e) -> eventHandler.handleMouseHoverOnMouseBox(x0Field, x1Field, box, e)) ); } return dynamicJuliaContainer; @@ -426,46 +423,6 @@ public class MainPageView extends Scene implements ChaosGameObserver { JULIA, AFFINE } - /** - * Creates a VBox containing a Pane that tracks mouse movement and two TextFields - * that display the normalized mouse coordinates within the Pane. - * The Pane is 400x400 in size. When the mouse is moved within the Pane, the - * coordinates are normalized to the range [-1, 1] and the values are updated - * in the TextFields. - * - * @return a VBox containing the Pane and the TextFields - */ - private Pane mouseBox() { - StackPane box = new StackPane(); - box.setPrefWidth(125); - box.getStyleClass().add("mouse-box"); - - box.getChildren().add(new Label("Hover over me!")); - - box.setOnMouseMoved(e -> { - double mouseX = e.getX(); - double mouseY = e.getY(); - double normalizedX = (mouseX / box.getWidth()) * 2 - 1; - double normalizedY = (mouseY / box.getHeight()) * 2 - 1; - updateValues(normalizedX, normalizedY); - }); - - return box; - } - - /** - * Updates the displayed values in the TextFields and triggers a change in the - * Julia transformation dynamically. - * - * @param x the normalized x-coordinate in the range [-1, 1] - * @param y the normalized y-coordinate in the range [-1, 1] - */ - private void updateValues(double x, double y) { - controller.changeJuliaTransformationDynamic(x, y); - x0Field.setText(String.format(Locale.ENGLISH, "%.5f", x)); - x1Field.setText(String.format(Locale.ENGLISH, "%.5f", y)); - } - public void setSelectedTransformation(TransformationType transformationType) { selectedTransformation = transformationType; } diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/MouseHoverBoxFactory.java b/src/main/java/edu/ntnu/idatt2003/view/components/MouseHoverBoxFactory.java new file mode 100644 index 0000000..a7ce3ee --- /dev/null +++ b/src/main/java/edu/ntnu/idatt2003/view/components/MouseHoverBoxFactory.java @@ -0,0 +1,41 @@ +package edu.ntnu.idatt2003.view.components; + +import java.util.function.BiConsumer; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; + +/** + * A factory for creating mouse hover boxes. The mouse hover boxes are styled with the "mouse-box" + * style class, and has a label in the center. + * Goal: Create mouse hover boxes. + */ +public class MouseHoverBoxFactory { + /** + * Private constructor to prevent instantiation. + */ + private MouseHoverBoxFactory() { + } + + /** + * Creates a mouse hover box with the specified text and width, and event. The mouse hover box + * is styled with the "mouse-box" style class, and has a label in the center. The box grows + * vertically to fill available space, and has a preferred width. The mouse hover + * box is configured to call the specified event handler when the mouse is moved over it. + * + * @param text The text to display in the mouse hover box. + * @param width The preferred width of the mouse hover box. + * @param eventHandler The event handler to call when the mouse is moved over the mouse hover box. + * @return The mouse hover box. + */ + public static Pane createMouseHoverBox(String text, double width, + BiConsumer<Pane, MouseEvent> eventHandler) { + Pane box = new StackPane(); + box.setPrefWidth(width); + box.getStyleClass().add("mouse-box"); + box.getChildren().add(new Label(text)); + box.setOnMouseMoved(e -> eventHandler.accept(box, e)); + return box; + } +} diff --git a/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxFactory.java b/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxFactory.java index cd9c2ca..bf49e67 100644 --- a/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxFactory.java +++ b/src/main/java/edu/ntnu/idatt2003/view/components/TextBoxFactory.java @@ -17,6 +17,7 @@ public class TextBoxFactory { */ private TextBoxFactory() { } + /** * Creates a text box with the specified text. The text box is styled with the * "text-box" style class. The text box is a StackPane with a Label as a child. -- GitLab