Skip to content
Snippets Groups Projects
Commit c4b0f23d authored by Magnus Eik's avatar Magnus Eik
Browse files

Implement PopupObserver for reduced coupling between popup view classes and PopupButtonController.

parent 599638d1
No related branches found
No related tags found
No related merge requests found
......@@ -9,6 +9,7 @@ import edu.ntnu.stud.chaosgame.model.transformations.AffineTransform2D;
import edu.ntnu.stud.chaosgame.model.transformations.JuliaTransform;
import edu.ntnu.stud.chaosgame.model.game.ChaosGameDescription;
import edu.ntnu.stud.chaosgame.model.transformations.Transform2D;
import edu.ntnu.stud.chaosgame.view.PopupObserver;
import edu.ntnu.stud.chaosgame.view.modificationpopups.AbstractPopup;
import edu.ntnu.stud.chaosgame.view.modificationpopups.Affine2DPopup;
import edu.ntnu.stud.chaosgame.view.modificationpopups.JuliaPopup;
......@@ -20,105 +21,128 @@ import java.util.ArrayList;
import java.util.List;
/**
* Controller class for handling the buttons relating to modifying the chaos game in the
* user interface.
* Controller class for handling the buttons related to modifying the chaos game in the user interface.
*/
public class PopupButtonController {
/**
* File handler for handling file operations.
*/
private final ChaosGameFileHandler fileHandler;
private PopupObserver observer;
/**
* Basic constructor for the class.
* Constructor for PopupButtonController.
*/
public PopupButtonController() {
this.fileHandler = new ChaosGameFileHandler();
this.fileHandler = new ChaosGameFileHandler();
}
/**
* Handles the update button in the Affine2DPopup.
* Sets the observer for the PopupButtonController.
*
* @param observer the observer to be set.
*/
public void setObserver(PopupObserver observer) {
this.observer = observer;
}
/**
* Handles the update button action for the Affine2DPopup.
*
* @param popup the Affine2DPopup to handle the button for.
*/
public void handleUpdateButton(Affine2DPopup popup) {
popup.getUpdateButton().setOnAction(event -> {
// Create new ChaosGameDescription based on the popup fields
String name = popup.getNameTextField().getText();
Vector2D minCoords = new Vector2D(
Double.parseDouble(popup.getMinXTextField().getText()),
Double.parseDouble(popup.getMinYTextField().getText())
);
Vector2D maxCoords = new Vector2D(
Double.parseDouble(popup.getMaxXTextField().getText()),
Double.parseDouble(popup.getMaxYTextField().getText())
);
List<Transform2D> transforms = new ArrayList<>();
for (int i = 0; i < popup.getMatrixTextFields().length; i++) {
double a00 = Double.parseDouble(popup.getMatrixTextFields()[i][0].getText());
double a01 = Double.parseDouble(popup.getMatrixTextFields()[i][1].getText());
double a10 = Double.parseDouble(popup.getMatrixTextFields()[i][2].getText());
double a11 = Double.parseDouble(popup.getMatrixTextFields()[i][3].getText());
double b0 = Double.parseDouble(popup.getVectorTextFields()[2 * i].getText());
double b1 = Double.parseDouble(popup.getVectorTextFields()[2 * i + 1].getText());
transforms.add(new AffineTransform2D(new Matrix2x2(a00, a01, a10, a11),
new Vector2D(b0, b1)));
}
// Create new ChaosGameDescription
ChaosGameDescription newDescription = new ChaosGameDescription(minCoords, maxCoords, transforms, name);
// Write new ChaosGameDescription to a file
try {
String filePath = Paths.get("src/main/resources/descriptions/saved_descriptions", name
+ ".txt").toString();
fileHandler.writeToFile(newDescription, filePath);
showConfirmationPopup(popup);
} catch (IOException e) {
PopupManager.displayError("Error", "Could not save the ChaosGameDescription to file.");
}
ChaosGameDescription newDescription = createChaosGameDescriptionFromAffine2DPopup(popup);
saveAndNotify(popup, newDescription);
});
}
/**
* Handles the update button in the JuliaPopup.
* Handles the update button action for the JuliaPopup.
*
* @param popup the JuliaPopup to handle the button for.
*/
public void handleUpdateButton(JuliaPopup popup) {
popup.getUpdateButton().setOnAction(event -> {
// Create new ChaosGameDescription based on the popup fields
String name = popup.getNameTextField().getText();
Vector2D minCoords = new Vector2D(
Double.parseDouble(popup.getMinXTextField().getText()),
Double.parseDouble(popup.getMinYTextField().getText())
);
Vector2D maxCoords = new Vector2D(
Double.parseDouble(popup.getMaxXTextField().getText()),
Double.parseDouble(popup.getMaxYTextField().getText())
);
double realPart = Double.parseDouble(popup.getRealPartTextField().getText());
double imagPart = Double.parseDouble(popup.getImagPartTextField().getText());
JuliaTransform transform = new JuliaTransform(new Complex(realPart, imagPart),1);
// Create new ChaosGameDescription
ChaosGameDescription newDescription = new ChaosGameDescription(minCoords, maxCoords, List.of(transform), name);
// Write new ChaosGameDescription to a file
try {
String filePath = Paths.get("src/main/resources/descriptions/saved_descriptions", name + ".txt").toString();
fileHandler.writeToFile(newDescription, filePath);
showConfirmationPopup(popup);
} catch (IOException e) {
PopupManager.displayError("Error", "Could not save the ChaosGameDescription to file.");
}
ChaosGameDescription newDescription = createChaosGameDescriptionFromJuliaPopup(popup);
saveAndNotify(popup, newDescription);
});
}
/**
* Creates a ChaosGameDescription from the data in an Affine2DPopup.
*
* @param popup the Affine2DPopup to extract data from.
* @return the created ChaosGameDescription.
*/
private ChaosGameDescription createChaosGameDescriptionFromAffine2DPopup(Affine2DPopup popup) {
String name = popup.getNameTextField().getText();
Vector2D minCoords = new Vector2D(
Double.parseDouble(popup.getMinXTextField().getText()),
Double.parseDouble(popup.getMinYTextField().getText())
);
Vector2D maxCoords = new Vector2D(
Double.parseDouble(popup.getMaxXTextField().getText()),
Double.parseDouble(popup.getMaxYTextField().getText())
);
List<Transform2D> transforms = new ArrayList<>();
for (int i = 0; i < popup.getMatrixTextFields().length; i++) {
double a00 = Double.parseDouble(popup.getMatrixTextFields()[i][0].getText());
double a01 = Double.parseDouble(popup.getMatrixTextFields()[i][1].getText());
double a10 = Double.parseDouble(popup.getMatrixTextFields()[i][2].getText());
double a11 = Double.parseDouble(popup.getMatrixTextFields()[i][3].getText());
double b0 = Double.parseDouble(popup.getVectorTextFields()[2 * i].getText());
double b1 = Double.parseDouble(popup.getVectorTextFields()[2 * i + 1].getText());
transforms.add(new AffineTransform2D(new Matrix2x2(a00, a01, a10, a11), new Vector2D(b0, b1)));
}
return new ChaosGameDescription(minCoords, maxCoords, transforms, name);
}
/**
* Creates a ChaosGameDescription from the data in a JuliaPopup.
*
* @param popup the JuliaPopup to extract data from.
* @return the created ChaosGameDescription.
*/
private ChaosGameDescription createChaosGameDescriptionFromJuliaPopup(JuliaPopup popup) {
String name = popup.getNameTextField().getText();
Vector2D minCoords = new Vector2D(
Double.parseDouble(popup.getMinXTextField().getText()),
Double.parseDouble(popup.getMinYTextField().getText())
);
Vector2D maxCoords = new Vector2D(
Double.parseDouble(popup.getMaxXTextField().getText()),
Double.parseDouble(popup.getMaxYTextField().getText())
);
double realPart = Double.parseDouble(popup.getRealPartTextField().getText());
double imagPart = Double.parseDouble(popup.getImagPartTextField().getText());
JuliaTransform transform = new JuliaTransform(new Complex(realPart, imagPart), 1);
return new ChaosGameDescription(minCoords, maxCoords, List.of(transform), name);
}
/**
* Saves the ChaosGameDescription to a file and shows a confirmation popup.
*
* @param popup the popup to show the confirmation for.
* @param newDescription the ChaosGameDescription to save.
*/
private void saveAndNotify(AbstractPopup popup, ChaosGameDescription newDescription) {
try {
String filePath = Paths.get("src/main/resources/descriptions/saved_descriptions", newDescription.getName() + ".txt").toString();
fileHandler.writeToFile(newDescription, filePath);
showConfirmationPopup(popup);
if (observer != null) {
observer.onUpdate(newDescription);
}
} catch (IOException e) {
PopupManager.displayError("Error", "Could not save the ChaosGameDescription to file.");
}
}
/**
* Shows a confirmation popup when the ChaosGameDescription has been updated and saved.
*
......@@ -130,12 +154,7 @@ public class PopupButtonController {
alert.showAndWait().ifPresent(response -> {
if (response == ButtonType.OK) {
popup.getPopupModifyStage().close();
GuiButtonController controller = (GuiButtonController) popup.getPopupModifyStage().getUserData();
if (controller != null) {
controller.updateDescriptionComboBox();
}
}
});
}
}
......@@ -16,7 +16,7 @@ public class Formatter {
*/
public static UnaryOperator<Change> floatFormatter = change -> {
String newText = change.getControlNewText();
if (newText.matches("([0-9]*[.])?[0-9]*")) {
if (newText.matches("-?([0-9]*[.])?[0-9]*")) {
return change;
}
return null;
......
package edu.ntnu.stud.chaosgame.view;
import edu.ntnu.stud.chaosgame.model.game.ChaosGameDescription;
public interface PopupObserver {
void onUpdate(ChaosGameDescription description);
}
\ No newline at end of file
......@@ -19,54 +19,11 @@ import javafx.stage.Stage;
*/
public abstract class AbstractPopup {
/**
* The stage for the popup.
*/
protected Stage popupModifyStage;
/**
* The layout of the popup.
*/
protected VBox layout;
/**
* The scene of the popup.
*/
protected Scene scene;
/**
* The text field for the name.
*/
protected TextField nameTextField;
/**
* The text field for the minimum x value.
*/
protected TextField minXTextField;
/**
* The text field for the minimum y value.
*/
protected TextField minYTextField;
/**
* The text field for the maximum x value.
*/
protected TextField maxXTextField;
/**
* The text field for the maximum y value.
*/
protected TextField maxYTextField;
/**
* The update button.
*/
protected TextField nameTextField, minXTextField, minYTextField, maxXTextField, maxYTextField;
protected Button updateButton;
/**
* The layout for the bottom part of the popup.
*/
protected VBox bottomLayout;
/**
......@@ -81,53 +38,52 @@ public abstract class AbstractPopup {
this.layout = layout;
this.scene = new Scene(layout);
this.popupModifyStage.setScene(scene);
initializeCommonElements();
}
/**
* Initialize the common elements of the popup.
* Initializes common elements of the popup.
*/
private void initializeCommonElements() {
nameTextField = new TextField();
HBox nameBox = new HBox(new Label("Name: "), nameTextField);
minXTextField = createNumericTextField();
minYTextField = createNumericTextField();
maxXTextField = createNumericTextField();
maxYTextField = createNumericTextField();
updateButton = new Button("Update");
minXTextField.setPrefWidth(50);
minYTextField.setPrefWidth(50);
maxXTextField.setPrefWidth(50);
maxYTextField.setPrefWidth(50);
GridPane minCoordsGrid = new GridPane();
minCoordsGrid.setHgap(10);
minCoordsGrid.add(new Label("Min Coordinates: "), 0, 0);
minCoordsGrid.add(minXTextField, 1, 0);
minCoordsGrid.add(minYTextField, 2, 0);
GridPane maxCoordsGrid = new GridPane();
maxCoordsGrid.setHgap(10);
maxCoordsGrid.add(new Label("Max Coordinates: "), 0, 0);
maxCoordsGrid.add(maxXTextField, 1, 0);
maxCoordsGrid.add(maxYTextField, 2, 0);
bottomLayout = new VBox(10, minCoordsGrid, maxCoordsGrid, updateButton);
bottomLayout.setAlignment(Pos.CENTER); // Center align the children
bottomLayout.setPadding(new Insets(10));
VBox.setVgrow(bottomLayout, Priority.ALWAYS);
layout.getChildren().addFirst(nameBox);
}
nameTextField = new TextField();
HBox nameBox = new HBox(new Label("Name: "), nameTextField);
minXTextField = createNumericTextField();
minYTextField = createNumericTextField();
maxXTextField = createNumericTextField();
maxYTextField = createNumericTextField();
updateButton = new Button("Update");
minXTextField.setPrefWidth(50);
minYTextField.setPrefWidth(50);
maxXTextField.setPrefWidth(50);
maxYTextField.setPrefWidth(50);
GridPane minCoordsGrid = new GridPane();
minCoordsGrid.setHgap(10);
minCoordsGrid.add(new Label("Min Coordinates: "), 0, 0);
minCoordsGrid.add(minXTextField, 1, 0);
minCoordsGrid.add(minYTextField, 2, 0);
GridPane maxCoordsGrid = new GridPane();
maxCoordsGrid.setHgap(10);
maxCoordsGrid.add(new Label("Max Coordinates: "), 0, 0);
maxCoordsGrid.add(maxXTextField, 1, 0);
maxCoordsGrid.add(maxYTextField, 2, 0);
bottomLayout = new VBox(10, minCoordsGrid, maxCoordsGrid, updateButton);
bottomLayout.setAlignment(Pos.CENTER);
bottomLayout.setPadding(new Insets(10));
VBox.setVgrow(bottomLayout, Priority.ALWAYS);
layout.getChildren().addFirst(nameBox);
}
/**
* Create a numeric text field.
* Creates a numeric text field with a float formatter.
*
* @return the numeric text field.
* @return the created numeric text field.
*/
protected TextField createNumericTextField() {
TextField numberField = new TextField();
......@@ -136,9 +92,9 @@ public abstract class AbstractPopup {
}
/**
* Initialize the layout of the popup.
* Initializes the layout of the popup.
*
* @return the layout of the popup.
* @return the initialized layout.
*/
public static VBox initializeLayout() {
VBox layout = new VBox();
......@@ -148,73 +104,73 @@ public abstract class AbstractPopup {
}
/**
* Display the popup.
* Displays the popup.
*/
public void display() {
popupModifyStage.show();
popupModifyStage.show();
}
/**
* Get the stage of the popup.
* Gets the stage of the popup.
*
* @return the stage of the popup.
*/
public Stage getPopupModifyStage() {
return popupModifyStage;
return popupModifyStage;
}
/**
* Get the text field for the name.
* Gets the text field for the name.
*
* @return the text field for the name.
*/
public TextField getNameTextField() {
return nameTextField;
return nameTextField;
}
/**
* Get the text field for the minimum x value.
* Gets the text field for the minimum x value.
*
* @return the text field for the minimum x value.
*/
public TextField getMinXTextField() {
return minXTextField;
return minXTextField;
}
/**
* Get the text field for the minimum y value.
* Gets the text field for the minimum y value.
*
* @return the text field for the minimum y value.
*/
public TextField getMinYTextField() {
return minYTextField;
return minYTextField;
}
/**
* Get the text field for the maximum x value.
* Gets the text field for the maximum x value.
*
* @return the text field for the maximum x value.
*/
public TextField getMaxXTextField() {
return maxXTextField;
return maxXTextField;
}
/**
* Get the text field for the maximum y value.
* Gets the text field for the maximum y value.
*
* @return the text field for the maximum y value.
*/
public TextField getMaxYTextField() {
return maxYTextField;
return maxYTextField;
}
/**
* Get the update button.
* Gets the update button.
*
* @return the update button.
*/
public Button getUpdateButton() {
return updateButton;
return updateButton;
}
}
......@@ -15,14 +15,7 @@ import edu.ntnu.stud.chaosgame.model.game.ChaosGameDescription;
*/
public class Affine2DPopup extends AbstractPopup {
/**
* The text fields for the matrix, represented as a 2D array.
*/
private final TextField[][] matrixTextFields;
/**
* The text fields for the vector, represented as a 1D array.
*/
private final TextField[] vectorTextFields;
/**
......@@ -33,8 +26,7 @@ public class Affine2DPopup extends AbstractPopup {
public Affine2DPopup(ChaosGameDescription gameDescription) {
super(AbstractPopup.initializeLayout());
// Initialize the TextFields for matrices and vectors based on the number of transforms
int transformCount = Math.min(gameDescription.getTransforms().size(), 4); // Ensure maximum of 4 transformations
int transformCount = Math.min(gameDescription.getTransforms().size(), 4);
matrixTextFields = new TextField[transformCount][4];
vectorTextFields = new TextField[transformCount * 2];
......@@ -49,47 +41,37 @@ public class Affine2DPopup extends AbstractPopup {
vectorTextFields[2 * i + 1].setPrefWidth(50);
}
// Add labels and text fields to GridPane
VBox matrixVBox = new VBox(10);
for (int i = 0; i < transformCount; i++) {
GridPane matrixGridPane = new GridPane();
matrixGridPane.setHgap(10);
matrixGridPane.setVgap(10);
matrixGridPane.setPadding(new Insets(10));
matrixGridPane.add(new Label("Matrix " + (i + 1) + " Row 1: "), 0, 0);
matrixGridPane.add(matrixTextFields[i][0], 1, 0);
matrixGridPane.add(matrixTextFields[i][1], 2, 0);
matrixGridPane.add(new Label("Matrix " + (i + 1) + " Row 2: "), 0, 1);
matrixGridPane.add(matrixTextFields[i][2], 1, 1);
matrixGridPane.add(matrixTextFields[i][3], 2, 1);
matrixGridPane.add(new Label("Vector " + (i + 1) + ": "), 0, 2);
matrixGridPane.add(vectorTextFields[2 * i], 1, 2);
matrixGridPane.add(vectorTextFields[2 * i + 1], 2, 2);
matrixVBox.getChildren().add(matrixGridPane);
}
VBox matrixVBox = new VBox(10);
for (int i = 0; i < transformCount; i++) {
GridPane matrixGridPane = new GridPane();
matrixGridPane.setHgap(10);
matrixGridPane.setVgap(10);
matrixGridPane.setPadding(new Insets(10));
// Add the GridPanes to the layout
layout.getChildren().addAll(matrixVBox);
matrixGridPane.add(new Label("Matrix " + (i + 1) + " Row 1: "), 0, 0);
matrixGridPane.add(matrixTextFields[i][0], 1, 0);
matrixGridPane.add(matrixTextFields[i][1], 2, 0);
// Add bottomLayout to the end
layout.getChildren().add(bottomLayout);
matrixGridPane.add(new Label("Matrix " + (i + 1) + " Row 2: "), 0, 1);
matrixGridPane.add(matrixTextFields[i][2], 1, 1);
matrixGridPane.add(matrixTextFields[i][3], 2, 1);
// Set the title for the popup window
popupModifyStage.setTitle("Affine2D Modification");
matrixGridPane.add(new Label("Vector " + (i + 1) + ": "), 0, 2);
matrixGridPane.add(vectorTextFields[2 * i], 1, 2);
matrixGridPane.add(vectorTextFields[2 * i + 1], 2, 2);
// Display the current values from ChaosGameDescription
displayCurrentValues(gameDescription);
matrixVBox.getChildren().add(matrixGridPane);
}
// Display the popup
display();
layout.getChildren().addAll(matrixVBox);
layout.getChildren().add(bottomLayout);
popupModifyStage.setTitle("Affine2D Modification");
displayCurrentValues(gameDescription);
display();
}
/**
* Display the current values from the ChaosGameDescription in the text fields.
* Displays the current values from the ChaosGameDescription in the text fields.
*
* @param gameDescription the ChaosGameDescription to display values from.
*/
......@@ -117,20 +99,20 @@ public class Affine2DPopup extends AbstractPopup {
}
/**
* Get the matrix text fields.
* Gets the matrix text fields.
*
* @return the matrix text fields.
*/
public TextField[][] getMatrixTextFields() {
return matrixTextFields;
return matrixTextFields;
}
/**
* Get the vector text fields.
* Gets the vector text fields.
*
* @return the vector text fields.
*/
public TextField[] getVectorTextFields() {
return vectorTextFields;
return vectorTextFields;
}
}
......@@ -13,14 +13,7 @@ import edu.ntnu.stud.chaosgame.model.transformations.JuliaTransform;
*/
public class JuliaPopup extends AbstractPopup {
/**
* The text field for the real part of the complex number.
*/
private final TextField realPartTextField;
/**
* The text field for the imaginary part of the complex number.
*/
private final TextField imagPartTextField;
/**
......@@ -31,11 +24,9 @@ public class JuliaPopup extends AbstractPopup {
public JuliaPopup(ChaosGameDescription gameDescription) {
super(AbstractPopup.initializeLayout());
// Initialize the TextFields
realPartTextField = createNumericTextField();
imagPartTextField = createNumericTextField();
// Add labels and text fields to a GridPane for better formatting
GridPane complexNumberGridPane = new GridPane();
complexNumberGridPane.setHgap(10);
complexNumberGridPane.setVgap(10);
......@@ -47,26 +38,17 @@ public class JuliaPopup extends AbstractPopup {
complexNumberGridPane.add(new Label("Imaginary Part: "), 0, 1);
complexNumberGridPane.add(imagPartTextField, 1, 1);
// Add the GridPane to the layout
layout.getChildren().add(complexNumberGridPane);
// Add bottomLayout to the end
layout.getChildren().add(bottomLayout);
// Set the title for the popup window
popupModifyStage.setTitle("Julia Set Modification");
// Display the current values from ChaosGameDescription
displayCurrentValues(gameDescription);
// Display the popup
display();
}
/**
* Display the current values from the ChaosGameDescription.
* Displays the current values from the ChaosGameDescription.
*
* @param gameDescription the chaos game description to be modified.
* @param gameDescription the ChaosGameDescription to display values from.
*/
private void displayCurrentValues(ChaosGameDescription gameDescription) {
if (gameDescription != null) {
......@@ -84,22 +66,24 @@ public class JuliaPopup extends AbstractPopup {
}
/**
* Get the real part text field.
* Gets the real part text field.
*
* @return the real part text field.
*/
public TextField getRealPartTextField() {
return realPartTextField;
return realPartTextField;
}
/**
* Get the imaginary part text field.
* Gets the imaginary part text field.
*
* @return the imaginary part text field.
*/
public TextField getImagPartTextField() {
return imagPartTextField;
return imagPartTextField;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment