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