Added account tile, more model definitions, and improved scene and node loading.
This commit is contained in:
parent
050f9c8a6b
commit
53ff257323
17
pom.xml
17
pom.xml
|
@ -28,9 +28,20 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.xerial</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>sqlite-jdbc</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>3.44.1.0</version>
|
<version>2.16.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
<version>2.16.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<version>2.2.224</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,39 @@
|
||||||
package com.andrewlalis.perfin;
|
package com.andrewlalis.perfin;
|
||||||
|
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class SceneUtil {
|
public class SceneUtil {
|
||||||
public static Scene load(String fxml, Object controller) {
|
public static <T> Parent loadNode(String fxml, Consumer<T> controllerConfig) {
|
||||||
FXMLLoader loader = new FXMLLoader(SceneUtil.class.getResource(fxml));
|
FXMLLoader loader = new FXMLLoader(SceneUtil.class.getResource(fxml));
|
||||||
if (controller != null) {
|
|
||||||
if (loader.getController() != null) {
|
|
||||||
throw new IllegalStateException("Cannot set loader for resource " + fxml + " because it has declared one already.");
|
|
||||||
}
|
|
||||||
loader.setController(controller);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return new Scene(loader.load());
|
Parent p = loader.load();
|
||||||
|
if (controllerConfig != null) {
|
||||||
|
T controller = loader.getController();
|
||||||
|
if (controller == null) throw new NullPointerException("Could not get controller from " + fxml);
|
||||||
|
controllerConfig.accept(controller);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Parent loadNode(String fxml) {
|
||||||
|
return loadNode(fxml, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Scene load(String fxml, Consumer<T> controllerConfig) {
|
||||||
|
return new Scene(loadNode(fxml, controllerConfig));
|
||||||
|
}
|
||||||
|
|
||||||
public static Scene load(String fxml) {
|
public static Scene load(String fxml) {
|
||||||
return load(fxml, null);
|
return load(fxml, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.andrewlalis.perfin.control;
|
||||||
|
|
||||||
|
import com.andrewlalis.perfin.model.Account;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.event.EventType;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
public class AccountTileController {
|
||||||
|
private Account account;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public VBox container;
|
||||||
|
@FXML
|
||||||
|
public Label accountNumberLabel;
|
||||||
|
@FXML
|
||||||
|
public Label accountBalanceLabel;
|
||||||
|
@FXML
|
||||||
|
public VBox accountNameBox;
|
||||||
|
@FXML
|
||||||
|
public Label accountNameLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
ObservableValue<Boolean> accountNameTextPresent = accountNameLabel.textProperty().map(t -> t != null && !t.isBlank());
|
||||||
|
accountNameBox.visibleProperty().bind(accountNameTextPresent);
|
||||||
|
accountNameBox.managedProperty().bind(accountNameTextPresent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccount(Account account) {
|
||||||
|
this.account = account;
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
accountNumberLabel.setText(account.getAccountNumber());
|
||||||
|
accountBalanceLabel.setText(account.getCurrency().getSymbol() + " " + account.getCurrentBalance().toPlainString());
|
||||||
|
accountNameLabel.setText(account.getName());
|
||||||
|
container.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
|
||||||
|
System.out.println("Clicked on " + account.getAccountNumber());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,47 @@
|
||||||
package com.andrewlalis.perfin.control;
|
package com.andrewlalis.perfin.control;
|
||||||
|
|
||||||
|
import com.andrewlalis.perfin.SceneUtil;
|
||||||
|
import com.andrewlalis.perfin.model.Account;
|
||||||
|
import com.andrewlalis.perfin.model.AccountType;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.FlowPane;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Currency;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class MainController {
|
public class MainController {
|
||||||
|
@FXML
|
||||||
|
public BorderPane mainContainer;
|
||||||
|
@FXML
|
||||||
|
public FlowPane accountsPane;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
accountsPane.minWidthProperty().bind(mainContainer.widthProperty());
|
||||||
|
accountsPane.prefWidthProperty().bind(mainContainer.widthProperty());
|
||||||
|
accountsPane.prefWrapLengthProperty().bind(mainContainer.widthProperty());
|
||||||
|
accountsPane.maxWidthProperty().bind(mainContainer.widthProperty());
|
||||||
|
List<Account> tempAccounts = List.of(
|
||||||
|
new Account(AccountType.CHECKING, "1234-4324-4321-4143", BigDecimal.valueOf(3745.01), "Main Checking", Currency.getInstance("USD")),
|
||||||
|
new Account(AccountType.CHECKING, "1234-4324-4321-4143", BigDecimal.valueOf(3745.01), "Main Checking", Currency.getInstance("USD")),
|
||||||
|
new Account(AccountType.CHECKING, "1234-4324-4321-4143", BigDecimal.valueOf(3745.01), "Main Checking", Currency.getInstance("USD")),
|
||||||
|
new Account(AccountType.CHECKING, "1234-4324-4321-4143", BigDecimal.valueOf(3745.01), "Main Checking", Currency.getInstance("USD"))
|
||||||
|
);
|
||||||
|
populateAccounts(tempAccounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateAccounts(List<Account> accounts) {
|
||||||
|
accountsPane.getChildren().clear();
|
||||||
|
for (var account : accounts) {
|
||||||
|
Parent node = SceneUtil.loadNode(
|
||||||
|
"/account-tile.fxml",
|
||||||
|
(Consumer<AccountTileController>) c -> c.setAccount(account)
|
||||||
|
);
|
||||||
|
accountsPane.getChildren().add(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class StartupSplashScreenController {
|
||||||
}
|
}
|
||||||
|
|
||||||
printlnLater("Perfin initialized. Starting the app now.");
|
printlnLater("Perfin initialized. Starting the app now.");
|
||||||
Thread.sleep(50000);
|
Thread.sleep(500);
|
||||||
|
|
||||||
Platform.runLater(() -> getSplashStage().setDone());
|
Platform.runLater(() -> getSplashStage().setDone());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.andrewlalis.perfin.model;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Currency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of a physical account of some sort (checking, savings,
|
||||||
|
* credit-card, etc.).
|
||||||
|
*/
|
||||||
|
public class Account {
|
||||||
|
private long id;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
private AccountType type;
|
||||||
|
private String accountNumber;
|
||||||
|
private BigDecimal currentBalance;
|
||||||
|
private String name;
|
||||||
|
private Currency currency;
|
||||||
|
|
||||||
|
public Account(AccountType type, String accountNumber, BigDecimal currentBalance, String name, Currency currency) {
|
||||||
|
this.type = type;
|
||||||
|
this.accountNumber = accountNumber;
|
||||||
|
this.currentBalance = currentBalance;
|
||||||
|
this.name = name;
|
||||||
|
this.currency = currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccountNumber() {
|
||||||
|
return accountNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getCurrentBalance() {
|
||||||
|
return currentBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Currency getCurrency() {
|
||||||
|
return currency;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.andrewlalis.perfin.model;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Currency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single entry depicting a credit or debit to an account.
|
||||||
|
* <p>
|
||||||
|
* The following rules apply in determining the type of an entry:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>A <em>debit</em> indicates an increase in assets or decrease in liability.</li>
|
||||||
|
* <li>A <em>credit</em> indicates a decrease in assets or increase in liability.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class AccountEntry {
|
||||||
|
public enum Type {
|
||||||
|
CREDIT,
|
||||||
|
DEBIT
|
||||||
|
}
|
||||||
|
|
||||||
|
private long id;
|
||||||
|
private LocalDateTime timestamp;
|
||||||
|
private long accountId;
|
||||||
|
private BigDecimal amount;
|
||||||
|
private Type type;
|
||||||
|
private Currency currency;
|
||||||
|
|
||||||
|
public AccountEntry(long id, LocalDateTime timestamp, long accountId, BigDecimal amount, Type type, Currency currency) {
|
||||||
|
this.id = id;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.accountId = accountId;
|
||||||
|
this.amount = amount;
|
||||||
|
this.type = type;
|
||||||
|
this.currency = currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountEntry(long accountId, BigDecimal amount, Type type, Currency currency) {
|
||||||
|
this.accountId = accountId;
|
||||||
|
this.amount = amount;
|
||||||
|
this.type = type;
|
||||||
|
this.currency = currency;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.andrewlalis.perfin.model;
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
CHECKING,
|
||||||
|
SAVINGS,
|
||||||
|
CREDIT_CARD
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.andrewlalis.perfin.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A profile is essentially a complete set of data that the application can
|
||||||
|
* operate on, sort of like a save file or user account. The profile contains
|
||||||
|
* a set of accounts, transaction records, attached documents, historical data,
|
||||||
|
* and more. A profile can be imported or exported easily from the application,
|
||||||
|
* and can be encrypted for additional security. Each profile also has its own
|
||||||
|
* settings.
|
||||||
|
* Practically, each profile is stored as its own isolated database file, with
|
||||||
|
* a name corresponding to the profile's name.
|
||||||
|
*/
|
||||||
|
public class Profile {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
}
|
|
@ -3,9 +3,6 @@ package com.andrewlalis.perfin.view;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javafx.scene.shape.Rectangle;
|
|
||||||
import javafx.stage.Modality;
|
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.StageStyle;
|
import javafx.stage.StageStyle;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ module com.andrewlalis.perfin {
|
||||||
requires javafx.fxml;
|
requires javafx.fxml;
|
||||||
requires javafx.graphics;
|
requires javafx.graphics;
|
||||||
|
|
||||||
requires org.xerial.sqlitejdbc;
|
requires com.fasterxml.jackson.databind;
|
||||||
|
|
||||||
exports com.andrewlalis.perfin to javafx.graphics;
|
exports com.andrewlalis.perfin to javafx.graphics;
|
||||||
opens com.andrewlalis.perfin.control to javafx.fxml;
|
opens com.andrewlalis.perfin.control to javafx.fxml;
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<VBox
|
||||||
|
styleClass="account-tile-container"
|
||||||
|
prefHeight="100.0"
|
||||||
|
prefWidth="200.0"
|
||||||
|
stylesheets="@style/account-tile.css"
|
||||||
|
xmlns="http://javafx.com/javafx/17.0.2-ea"
|
||||||
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="com.andrewlalis.perfin.control.AccountTileController"
|
||||||
|
fx:id="container"
|
||||||
|
>
|
||||||
|
<Label styleClass="main-label" text="Account Info" />
|
||||||
|
<Separator prefWidth="200.0" />
|
||||||
|
<Label text="Account Number" styleClass="property-label"/>
|
||||||
|
<Label fx:id="accountNumberLabel" styleClass="property-value" text="Account Number placeholder" />
|
||||||
|
<Label text="Account Balance" styleClass="property-label"/>
|
||||||
|
<Label fx:id="accountBalanceLabel" styleClass="property-value" text="account balance placeholder" />
|
||||||
|
<VBox fx:id="accountNameBox">
|
||||||
|
<Label text="Account Name" styleClass="property-label"/>
|
||||||
|
<Label fx:id="accountNameLabel" styleClass="property-value" text="account name placeholder"/>
|
||||||
|
</VBox>
|
||||||
|
</VBox>
|
|
@ -1,24 +1,31 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.FlowPane?>
|
||||||
|
<BorderPane
|
||||||
<BorderPane xmlns="http://javafx.com/javafx/17.0.2-ea"
|
minHeight="400.0"
|
||||||
xmlns:fx="http://javafx.com/fxml/1"
|
minWidth="600.0"
|
||||||
fx:controller="com.andrewlalis.perfin.control.MainController"
|
xmlns="http://javafx.com/javafx/17.0.2-ea"
|
||||||
minWidth="600.0" minHeight="400.0">
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="com.andrewlalis.perfin.control.MainController"
|
||||||
|
fx:id="mainContainer"
|
||||||
|
stylesheets="@style/main.css"
|
||||||
|
>
|
||||||
<top>
|
<top>
|
||||||
<MenuBar BorderPane.alignment="CENTER">
|
<MenuBar BorderPane.alignment="CENTER">
|
||||||
<Menu mnemonicParsing="false" text="File">
|
<Menu mnemonicParsing="false" text="File">
|
||||||
<MenuItem mnemonicParsing="false" text="Close"/>
|
<MenuItem mnemonicParsing="false" text="Close" />
|
||||||
</Menu>
|
</Menu>
|
||||||
<Menu mnemonicParsing="false" text="Edit">
|
<Menu mnemonicParsing="false" text="Edit">
|
||||||
<MenuItem mnemonicParsing="false" text="Delete"/>
|
<MenuItem mnemonicParsing="false" text="Delete" />
|
||||||
</Menu>
|
</Menu>
|
||||||
<Menu mnemonicParsing="false" text="Help">
|
<Menu mnemonicParsing="false" text="Help">
|
||||||
<MenuItem mnemonicParsing="false" text="About"/>
|
<MenuItem mnemonicParsing="false" text="About" />
|
||||||
</Menu>
|
</Menu>
|
||||||
</MenuBar>
|
</MenuBar>
|
||||||
</top>
|
</top>
|
||||||
|
<center>
|
||||||
|
<FlowPane fx:id="accountsPane" BorderPane.alignment="TOP_LEFT" vgap="5" hgap="5"/>
|
||||||
|
</center>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
xmlns="http://javafx.com/javafx/17.0.2-ea"
|
xmlns="http://javafx.com/javafx/17.0.2-ea"
|
||||||
xmlns:fx="http://javafx.com/fxml/1"
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
fx:controller="com.andrewlalis.perfin.control.StartupSplashScreenController"
|
fx:controller="com.andrewlalis.perfin.control.StartupSplashScreenController"
|
||||||
stylesheets="/startup-splash-screen.css"
|
stylesheets="@style/startup-splash-screen.css"
|
||||||
>
|
>
|
||||||
<center>
|
<center>
|
||||||
<TextArea fx:id="content" wrapText="true" editable="false" focusTraversable="false"/>
|
<TextArea fx:id="content" wrapText="true" editable="false" focusTraversable="false"/>
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
.account-tile-container {
|
||||||
|
-fx-border-color: lightgray;
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
-fx-border-style: solid;
|
||||||
|
-fx-padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-tile-container:hover {
|
||||||
|
-fx-cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-label {
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-font-size: large;
|
||||||
|
}
|
||||||
|
|
||||||
|
.property-label {
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.property-value {
|
||||||
|
-fx-font-family: monospace;
|
||||||
|
-fx-font-size: large;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
#accountsPane {
|
||||||
|
-fx-padding: 5px;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
#sceneRoot {
|
#sceneRoot {
|
||||||
-fx-background-image: url("images/splash-screen.png");
|
-fx-background-image: url("../images/splash-screen.png");
|
||||||
-fx-background-repeat: stretch;
|
-fx-background-repeat: stretch;
|
||||||
-fx-background-size: 400 200;
|
-fx-background-size: 400 200;
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
Loading…
Reference in New Issue