Skip to content

Instantly share code, notes, and snippets.

@jondef
Created March 31, 2021 18:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jondef/80a662ae5c74fcd56f260d66e0d8a0a4 to your computer and use it in GitHub Desktop.
Save jondef/80a662ae5c74fcd56f260d66e0d8a0a4 to your computer and use it in GitHub Desktop.
package ch.zhaw.prog2.fxmlcalculator;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
/**
* Main-Application. Opens the first window (MainWindow) and the common ValueHandler
* @author
* @version 1.0
*/
public class Main extends Application {
private ValueHandler valueHandler;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
valueHandler = new ValueHandler();
mainWindow(primaryStage);
}
private void mainWindow(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("MainWindow.fxml"));
Scene scene = new Scene(root);
primaryStage.setTitle("Return on investment calculator");
primaryStage.setScene(scene);
primaryStage.setMinWidth(250);
primaryStage.setMinHeight(350);
primaryStage.show();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.input.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.prog2.fxmlcalculator.MainWindowController">
<BorderPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<top>
<MenuBar fx:id="menuBar" BorderPane.alignment="TOP_LEFT">
<Menu text="Clear">
<CheckMenuItem fx:id="checkmenuitem_clear_initial_amount" text="Initial amount" />
<CheckMenuItem fx:id="checkmenuitem_return_in_percent" text="Return in %" />
<CheckMenuItem fx:id="checkmenuitem_annual_cost" mnemonicParsing="false" text="Annual Costs" />
<CheckMenuItem fx:id="checkmenuitem_number_years" mnemonicParsing="false" text="Number of years" />
<SeparatorMenuItem />
<MenuItem fx:id="menuitem_clear_values" text="Clear values" />
<SeparatorMenuItem />
<MenuItem fx:id="menuitem_clear_results" text="Clear results" />
</Menu>
<Menu text="?">
<MenuItem fx:id="menuitem_show_help" text="Show Help">
<accelerator>
<KeyCodeCombination alt="UP" code="F1" control="UP" meta="UP" shift="UP" shortcut="UP" />
</accelerator>
</MenuItem>
</Menu>
</MenuBar>
</top>
<center>
<VBox spacing="10.0">
<HBox alignment="CENTER_LEFT">
<Label minWidth="100.0" text="Initial amount" />
<TextField fx:id="textfield_initial_amount" />
</HBox>
<HBox alignment="CENTER_LEFT">
<Label contentDisplay="CENTER" minWidth="100.0" text="Return rate in %" />
<TextField fx:id="textfield_return_rate" />
</HBox>
<HBox alignment="CENTER_LEFT">
<Label minWidth="100.0" text="Annual cost" />
<TextField fx:id="textfield_annual_cost" />
</HBox>
<HBox alignment="CENTER_LEFT">
<Label minWidth="100.0" text="Number of years" />
<TextField fx:id="textfield_number_of_years" />
</HBox>
<VBox alignment="CENTER_LEFT" VBox.vgrow="ALWAYS">
<Label text="Results" />
<TextArea fx:id="textarea_results" editable="false" VBox.vgrow="ALWAYS" />
</VBox>
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</BorderPane.margin>
</VBox>
</center>
<bottom>
<HBox alignment="CENTER" spacing="10">
<Button fx:id="button_calculate" text="Calculate" />
<Button fx:id="button_close" text="Close" />
<Button fx:id="button_open_results" text="Results" />
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" />
</BorderPane.margin>
</HBox>
</bottom>
</BorderPane>
</AnchorPane>
package ch.zhaw.prog2.fxmlcalculator;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
import java.io.IOException;
/**
* Controller for the MainWindow. One controller per mask (or FXML file)
* Contains everything the controller has to reach in the view (controls)
* and all methods the view calls based on events.
* @author
* @version 1.0
*/
public class MainWindowController {
@FXML private MenuBar menuBar;
@FXML private CheckMenuItem checkmenuitem_clear_initial_amount;
@FXML private CheckMenuItem checkmenuitem_return_in_percent;
@FXML private CheckMenuItem checkmenuitem_annual_cost;
@FXML private CheckMenuItem checkmenuitem_number_years;
@FXML private MenuItem menuitem_clear_values;
@FXML private MenuItem menuitem_clear_results;
@FXML private MenuItem menuitem_show_help;
@FXML private TextArea textarea_results;
@FXML private TextField textfield_initial_amount;
@FXML private TextField textfield_return_rate;
@FXML private TextField textfield_annual_cost;
@FXML private TextField textfield_number_of_years;
@FXML private Button button_calculate;
@FXML private Button button_close;
@FXML private Button button_open_results;
private ValueHandler valueHandler;
/**
* Constructor
*/
public MainWindowController() {
valueHandler = new ValueHandler();
}
// called by the FXML loader after the labels declared above are injected:
public void initialize() {
button_close.setOnAction((event) -> {
Stage stage = (Stage) button_close.getScene().getWindow();
stage.close();
});
button_calculate.setOnAction((event) -> {
String initialAmount = textfield_initial_amount.getText();
String returnRate = textfield_return_rate.getText();
String annualCost = textfield_annual_cost.getText();
String numberOfYears = textfield_number_of_years.getText();
valueHandler.checkAndCalculateResult(initialAmount, returnRate, annualCost, numberOfYears);
if (valueHandler.areValuesOk()) {
textarea_results.setStyle("-fx-border-color: green; -fx-focus-color: green");
textarea_results.setText(valueHandler.getResultBound());
} else {
textarea_results.setStyle("-fx-border-color: red; -fx-focus-color: red");
textarea_results.setText(valueHandler.getResultBound());
}
});
// menu bar connections
menuitem_clear_values.setOnAction((event) -> {
if (checkmenuitem_clear_initial_amount.isSelected()) {
textfield_initial_amount.clear();
}
if (checkmenuitem_return_in_percent.isSelected()) {
textfield_return_rate.clear();
}
if (checkmenuitem_annual_cost.isSelected()) {
textfield_annual_cost.clear();
}
if (checkmenuitem_number_years.isSelected()) {
textfield_number_of_years.clear();
}
});
menuitem_clear_results.setOnAction(event -> textarea_results.clear());
menuitem_show_help.setOnAction(event -> {
textarea_results.setStyle("-fx-border-color: blue; -fx-focus-color:blue");
textarea_results.setText(valueHandler.getHelpText());
});
valueHandler.resultBoundProperty().addListener((observable, oldValue, newValue) -> {
});
button_open_results.setOnAction(event -> {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("ResultWindow.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
Stage stageOfNewWindow = new Stage();
stageOfNewWindow.setTitle("Results");
stageOfNewWindow.setScene(scene);
stageOfNewWindow.setMinWidth(410);
stageOfNewWindow.setMinHeight(280);
// stageOfNewWindow.initOwner(textarea_results.getScene().getWindow());
// stageOfNewWindow.initModality(Modality.WINDOW_MODAL);
((ResultWindowController) loader.getController()).setValueHandler(valueHandler);
// we need to fire the calculate button to update the text on the new window
button_calculate.fire();
stageOfNewWindow.show();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
} // initialize
}
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.input.KeyCodeCombination?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="250.0" minWidth="400.0" prefHeight="250.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.prog2.fxmlcalculator.ResultWindowController">
<center>
<VBox minWidth="300.0" BorderPane.alignment="TOP_CENTER">
<padding>
<Insets left="10.0" right="10.0"/>
</padding>
<VBox spacing="5.0" VBox.vgrow="ALWAYS">
<VBox.margin>
<Insets top="10.0"/>
</VBox.margin>
<Label text="Results">
<font>
<Font name="System Bold" size="12.0"/>
</font>
</Label>
<TextArea fx:id="textarea_results" editable="false" minHeight="100.0" VBox.vgrow="ALWAYS"/>
</VBox>
</VBox>
</center>
<bottom>
<HBox alignment="BOTTOM_RIGHT" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</BorderPane.margin>
<Button fx:id="closeForm" mnemonicParsing="false" onMouseClicked="#closeWindow" text="Close">
<HBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</HBox.margin>
</Button>
</HBox>
</bottom>
</BorderPane>
package ch.zhaw.prog2.fxmlcalculator;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
/**
* Controller for the MainWindow. One controller per mask (or FXML file)
* Contains everything the controller has to reach in the view (controls)
* and all methods the view calls based on events.
* @author
* @version 1.0
*/
public class ResultWindowController {
@FXML private TextArea textarea_results;
@FXML
private void closeWindow() {
Stage stage = (Stage) textarea_results.getScene().getWindow();
stage.close();
}
public void setValueHandler(ValueHandler valueHandler) {
valueHandler.resultBoundProperty().addListener((observable, oldValue, newValue) -> {
if (valueHandler.areValuesOk()) {
textarea_results.setStyle("-fx-border-color: green; -fx-focus-color: green");
textarea_results.setText(valueHandler.getResultBound());
textarea_results.layout();
} else {
textarea_results.setStyle("-fx-border-color: red; -fx-focus-color: red");
textarea_results.setText(valueHandler.getResultBound());
}
});
}
}
package ch.zhaw.prog2.fxmlcalculator;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* Handles the values from the input form. Offers the {@link resultBound} StringProperty to listen from
* a GUI (bind to a field in the GUI)
* @author bles
* @version 1.1
* MODEL
*/
public class ValueHandler {
private double initialAmount;
private double returnInPercent;
private double annualCost;
private int numberOfYears;
private boolean valuesOk = false;
// Solution with bound properties
private final StringProperty resultBound = new SimpleStringProperty();
public ValueHandler() {
}
/**
* Check the input values are valid (can be improved)
* If not ok, fill error message to the results and return false
* If ok, set the date fields and return true
*/
private String checkValues(String initialAmount, String returnInPercent, String annualCost, String numberOfYears) {
StringBuilder sb = new StringBuilder();
valuesOk = true;
if ("".equals(initialAmount) || Double.parseDouble(initialAmount)<=0) {
sb.append("Please specify a positive initial amount!\n");
valuesOk = false;
} else {
this.initialAmount = Double.parseDouble(initialAmount);
}
if ("".equals(returnInPercent)) {
sb.append("Please specify the annual return rate in %!\n");
valuesOk = false;
} else {
this.returnInPercent = Double.parseDouble(returnInPercent)/100;
}
if ("".equals(annualCost) || Double.parseDouble(annualCost)<0) {
sb.append("Please specify the annual cost!\n");
valuesOk = false;
} else {
this.annualCost = Double.parseDouble(annualCost);
}
if ("".equals(numberOfYears) ||
Double.parseDouble(numberOfYears) < 1 ||
Double.parseDouble(numberOfYears) > 99 ||
Math.round(Double.parseDouble(numberOfYears))!=Double.parseDouble(numberOfYears)) {
sb.append("Please enter a time period in years!");
valuesOk = false;
} else {
this.numberOfYears = Integer.parseInt(numberOfYears);
}
return sb.toString();
}
/**
* Calculates the result
* @return the result as a String
*/
private String calculateResult() {
StringBuilder resultSB = new StringBuilder();
double val = initialAmount;
for(int i = 1; i <= numberOfYears; i++) {
resultSB.append("After ");
resultSB.append(i).append(" year(s): ");
val = val * (1 + returnInPercent) - annualCost;
resultSB.append(Math.round(val)).append("\n");
}
return resultSB.toString();
}
/**
* Result String can be "", if no calculation or check is done
* @return String with the result of the value checking or the calculation
*/
public String getResultBound() {
return resultBound.get();
}
/**
* Set the result to the string in the parameter
* @param infoText
*/
public void setResultBound(String infoText) {
resultBound.set(infoText);
}
/**
* If the values checked by {@link checkValues} are ok, the return is true
* @return true, if ok
*/
public boolean areValuesOk() {
return valuesOk;
}
// Solution with bound properties
/**
* The property to bind
* @return
*/
public StringProperty resultBoundProperty() {
return resultBound;
}
/**
* Checks and calculates the result. All values as String (from the Text-Fields)
* If the check fails, an error message is set to the bound result property
* @param initialAmount
* @param returnInPercent
* @param annualCost
* @param numberOfYears
*/
public void checkAndCalculateResult(String initialAmount, String returnInPercent, String annualCost, String numberOfYears) {
setResultBound(checkValues(initialAmount, returnInPercent, annualCost, numberOfYears));
if(valuesOk) {
setResultBound(calculateResult());
}
}
public String getHelpText() {
return "Enter valid values to\n" +
"- Initial amount (>0)\n" +
"- Return in % (can be +/- or 0)\n" +
"- Annual costs (>0)\n" +
"- Number of years (>0)\n" +
"\n" +
"Calculate display the annual balance development!";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment