Added scene router, main view for the entire app, and more preparations for a multi-page application.
This commit is contained in:
parent
974fbe0b6e
commit
2959947acc
|
@ -1,10 +1,14 @@
|
|||
package com.andrewlalis.perfin;
|
||||
|
||||
import com.andrewlalis.perfin.control.MainViewController;
|
||||
import com.andrewlalis.perfin.view.SceneRouter;
|
||||
import com.andrewlalis.perfin.view.SplashScreenStage;
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The class from which the JavaFX-based application starts.
|
||||
|
@ -29,7 +33,12 @@ public class PerfinApp extends Application {
|
|||
|
||||
private void initMainScreen(Stage stage) {
|
||||
stage.hide();
|
||||
stage.setScene(SceneUtil.load("/accounts-view.fxml"));
|
||||
Scene mainViewScene = SceneUtil.load("/main-view.fxml", (Consumer<MainViewController>) c -> {
|
||||
c.router = new SceneRouter(c.mainContainer::setCenter)
|
||||
.map("accounts", "/accounts-view.fxml")
|
||||
.map("edit-account", "/edit-account.fxml");
|
||||
});
|
||||
stage.setScene(mainViewScene);
|
||||
stage.setTitle("Perfin");
|
||||
}
|
||||
}
|
|
@ -4,13 +4,17 @@ import com.andrewlalis.perfin.SceneUtil;
|
|||
import com.andrewlalis.perfin.model.Account;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.view.BindingUtil;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.FlowPane;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class AccountsViewController {
|
||||
|
@ -18,27 +22,42 @@ public class AccountsViewController {
|
|||
public BorderPane mainContainer;
|
||||
@FXML
|
||||
public FlowPane accountsPane;
|
||||
@FXML
|
||||
public Label noAccountsLabel;
|
||||
|
||||
private final ObservableList<Account> accountsList = FXCollections.observableArrayList();
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
// Sync the size of the accounts pane to its container.
|
||||
accountsPane.minWidthProperty().bind(mainContainer.widthProperty());
|
||||
accountsPane.prefWidthProperty().bind(mainContainer.widthProperty());
|
||||
accountsPane.prefWrapLengthProperty().bind(mainContainer.widthProperty());
|
||||
accountsPane.maxWidthProperty().bind(mainContainer.widthProperty());
|
||||
|
||||
// Map each account in our list to an account tile element.
|
||||
BindingUtil.mapContent(accountsPane.getChildren(), accountsList, account -> SceneUtil.loadNode(
|
||||
"/account-tile.fxml",
|
||||
(Consumer<AccountTileController>) c -> c.setAccount(account)
|
||||
));
|
||||
|
||||
// Show the "no accounts" label when the accountsList is empty.
|
||||
var listProp = new SimpleListProperty<>(accountsList);
|
||||
noAccountsLabel.visibleProperty().bind(listProp.emptyProperty());
|
||||
noAccountsLabel.managedProperty().bind(noAccountsLabel.visibleProperty());
|
||||
accountsPane.visibleProperty().bind(listProp.emptyProperty().not());
|
||||
accountsPane.managedProperty().bind(accountsPane.visibleProperty());
|
||||
|
||||
// Populate the list of accounts once a profile is available.
|
||||
Profile.whenLoaded(profile -> {
|
||||
populateAccounts(profile.getDataSource().getAccountRepository().findAll());
|
||||
accountsList.setAll(profile.getDataSource().getAccountRepository().findAll());
|
||||
});
|
||||
}
|
||||
|
||||
private void populateAccounts(List<Account> accounts) {
|
||||
this.accountsList.clear();
|
||||
this.accountsList.addAll(accounts);
|
||||
@FXML
|
||||
public void createNewAccount() {
|
||||
Stage mainStage = (Stage) mainContainer.getScene().getWindow();
|
||||
Scene editAccountScene = SceneUtil.load("/edit-account.fxml");
|
||||
mainStage.setScene(editAccountScene);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package com.andrewlalis.perfin.control;
|
||||
|
||||
import com.andrewlalis.perfin.view.SceneRouter;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
public class MainViewController {
|
||||
public SceneRouter router;
|
||||
@FXML
|
||||
public BorderPane mainContainer;
|
||||
@FXML
|
||||
public HBox mainFooter;
|
||||
|
||||
@FXML
|
||||
public void goToAccounts() {
|
||||
router.goTo("accounts");
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void goToEditAccounts() {
|
||||
router.goTo("edit-account");
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.andrewlalis.perfin.data;
|
|||
|
||||
import com.andrewlalis.perfin.model.Account;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -9,4 +10,5 @@ public interface AccountRepository {
|
|||
long insert(Account account);
|
||||
List<Account> findAll();
|
||||
Optional<Account> findById(long id);
|
||||
BigDecimal deriveCurrentBalance(long id);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.andrewlalis.perfin.data.DbUtil;
|
|||
import com.andrewlalis.perfin.model.Account;
|
||||
import com.andrewlalis.perfin.model.AccountType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
@ -39,6 +40,12 @@ public record JdbcAccountRepository(Connection conn) implements AccountRepositor
|
|||
return DbUtil.findById(conn, "SELECT * FROM account WHERE id = ?", id, JdbcAccountRepository::parseAccount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal deriveCurrentBalance(long id) {
|
||||
// TODO: Implement this!
|
||||
return BigDecimal.valueOf(0, 4);
|
||||
}
|
||||
|
||||
private static Account parseAccount(ResultSet rs) throws SQLException {
|
||||
long id = rs.getLong("id");
|
||||
LocalDateTime createdAt = DbUtil.utcLDTFromTimestamp(rs.getTimestamp("created_at"));
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package com.andrewlalis.perfin.view;
|
||||
|
||||
import com.andrewlalis.perfin.SceneUtil;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.layout.Pane;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class SceneRouter {
|
||||
private final Consumer<Parent> setter;
|
||||
private final Map<String, Parent> routeMap = new HashMap<>();
|
||||
public Object context = null;
|
||||
|
||||
public SceneRouter(Pane pane) {
|
||||
this(p -> pane.getChildren().setAll(p));
|
||||
}
|
||||
|
||||
public SceneRouter(Consumer<Parent> setter) {
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
public SceneRouter map(String route, Parent scene) {
|
||||
routeMap.put(route, scene);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SceneRouter map(String route, String fxml) {
|
||||
return map(route, SceneUtil.loadNode(fxml));
|
||||
}
|
||||
|
||||
public void goTo(String route, Object context) {
|
||||
Parent node = routeMap.get(route);
|
||||
if (node == null) throw new IllegalArgumentException("Route " + route + " is not mapped to any node.");
|
||||
this.context = context;
|
||||
Platform.runLater(() -> setter.accept(node));
|
||||
}
|
||||
|
||||
public void goTo(String route) {
|
||||
goTo(route, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getContext() {
|
||||
return (T) context;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.FlowPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<BorderPane
|
||||
minHeight="400.0"
|
||||
minWidth="600.0"
|
||||
|
@ -12,20 +16,26 @@
|
|||
fx:id="mainContainer"
|
||||
stylesheets="@style/accounts-view.css"
|
||||
>
|
||||
<top>
|
||||
<MenuBar BorderPane.alignment="CENTER">
|
||||
<Menu mnemonicParsing="false" text="File">
|
||||
<MenuItem mnemonicParsing="false" text="Close" />
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="Edit">
|
||||
<MenuItem mnemonicParsing="false" text="Delete" />
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="Help">
|
||||
<MenuItem mnemonicParsing="false" text="About" />
|
||||
</Menu>
|
||||
</MenuBar>
|
||||
</top>
|
||||
<!-- <top>-->
|
||||
<!-- <MenuBar BorderPane.alignment="CENTER">-->
|
||||
<!-- <Menu mnemonicParsing="false" text="File">-->
|
||||
<!-- <MenuItem mnemonicParsing="false" text="Close" />-->
|
||||
<!-- </Menu>-->
|
||||
<!-- <Menu mnemonicParsing="false" text="Edit">-->
|
||||
<!-- <MenuItem mnemonicParsing="false" text="Delete" />-->
|
||||
<!-- </Menu>-->
|
||||
<!-- <Menu mnemonicParsing="false" text="Help">-->
|
||||
<!-- <MenuItem mnemonicParsing="false" text="About" />-->
|
||||
<!-- </Menu>-->
|
||||
<!-- </MenuBar>-->
|
||||
<!-- </top>-->
|
||||
<center>
|
||||
<VBox>
|
||||
<HBox styleClass="actionsBar">
|
||||
<Button text="Add an Account" onAction="#createNewAccount"/>
|
||||
</HBox>
|
||||
<FlowPane fx:id="accountsPane" BorderPane.alignment="TOP_LEFT" vgap="5" hgap="5"/>
|
||||
<Label fx:id="noAccountsLabel" BorderPane.alignment="TOP_LEFT" text="No accounts have been added to this profile."/>
|
||||
</VBox>
|
||||
</center>
|
||||
</BorderPane>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<BorderPane xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<top>
|
||||
<Label text="Edit Account"/>
|
||||
</top>
|
||||
<center>
|
||||
<GridPane BorderPane.alignment="CENTER">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" />
|
||||
</columnConstraints>
|
||||
<Label GridPane.columnIndex="0" GridPane.rowIndex="0" text="Name"/>
|
||||
<TextField GridPane.columnIndex="1" GridPane.rowIndex="0" text="Bleh"/>
|
||||
|
||||
<Label GridPane.columnIndex="0" GridPane.rowIndex="1" text="Account Number"/>
|
||||
<TextField GridPane.columnIndex="1" GridPane.rowIndex="1" text="1234"/>
|
||||
|
||||
<Label GridPane.columnIndex="0" GridPane.rowIndex="2" text="Currency"/>
|
||||
<ChoiceBox GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||
</ChoiceBox>
|
||||
</GridPane>
|
||||
</center>
|
||||
<bottom>
|
||||
<FlowPane BorderPane.alignment="CENTER_RIGHT">
|
||||
<Button text="Save" />
|
||||
<Button text="Cancel" />
|
||||
</FlowPane>
|
||||
</bottom>
|
||||
</BorderPane>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<BorderPane
|
||||
xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:id="mainContainer"
|
||||
fx:controller="com.andrewlalis.perfin.control.MainViewController"
|
||||
stylesheets="@style/main-view.css"
|
||||
>
|
||||
<top>
|
||||
<HBox fx:id="mainHeader">
|
||||
<Label text="TODO: Add breadcrumb navigation/header here!"/>
|
||||
<Button text="Go to accounts!" onAction="#goToAccounts"/>
|
||||
<Button text="Go to edit accounts!" onAction="#goToEditAccounts"/>
|
||||
</HBox>
|
||||
</top>
|
||||
<bottom>
|
||||
<HBox fx:id="mainFooter" spacing="5">
|
||||
<Label text="Perfin v0.0.1"/>
|
||||
<Label text="Copyright Bleh"/>
|
||||
</HBox>
|
||||
</bottom>
|
||||
</BorderPane>
|
|
@ -1,3 +1,11 @@
|
|||
#accountsPane {
|
||||
-fx-padding: 5px;
|
||||
}
|
||||
|
||||
#noAccountsLabel {
|
||||
-fx-padding: 5px;
|
||||
}
|
||||
|
||||
.actionsBar {
|
||||
-fx-padding: 5px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#mainHeader {
|
||||
-fx-padding: 3px;
|
||||
}
|
||||
|
||||
#mainFooter {
|
||||
-fx-padding: 3px;
|
||||
}
|
Loading…
Reference in New Issue