Refactored styling in most views and components to follow base CSS.

This commit is contained in:
Andrew Lalis 2024-01-05 10:59:44 -05:00
parent a914197634
commit 02d392d6c7
19 changed files with 226 additions and 95 deletions

View File

@ -10,8 +10,13 @@ import com.andrewlalis.perfin.view.StartupSplashScreen;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
@ -21,6 +26,7 @@ import java.util.function.Consumer;
* The class from which the JavaFX-based application starts. * The class from which the JavaFX-based application starts.
*/ */
public class PerfinApp extends Application { public class PerfinApp extends Application {
private static final Logger log = LoggerFactory.getLogger(PerfinApp.class);
public static final Path APP_DIR = Path.of(System.getProperty("user.home", "."), ".perfin"); public static final Path APP_DIR = Path.of(System.getProperty("user.home", "."), ".perfin");
public static PerfinApp instance; public static PerfinApp instance;
@ -36,6 +42,7 @@ public class PerfinApp extends Application {
@Override @Override
public void start(Stage stage) { public void start(Stage stage) {
instance = this; instance = this;
loadFonts();
var splashScreen = new StartupSplashScreen(List.of( var splashScreen = new StartupSplashScreen(List.of(
PerfinApp::defineRoutes, PerfinApp::defineRoutes,
PerfinApp::initAppDir, PerfinApp::initAppDir,
@ -97,4 +104,27 @@ public class PerfinApp extends Application {
throw e; throw e;
} }
} }
private static void loadFonts() {
List<String> fontResources = List.of(
"/font/JetBrainsMono-2.304/fonts/ttf/JetBrainsMono-Medium.ttf",
"/font/Roboto/Roboto-Regular.ttf",
"/font/Roboto/Roboto-Bold.ttf",
"/font/Roboto/Roboto-Italic.ttf",
"/font/Roboto/Roboto-BoldItalic.ttf"
);
for (String res : fontResources) {
URL resourceUrl = PerfinApp.class.getResource(res);
if (resourceUrl == null) {
log.warn("Font resource {} was not found.", res);
} else {
Font font = Font.loadFont(PerfinApp.class.getResource(res).toExternalForm(), 10);
if (font == null) {
log.warn("Failed to load font {}.", res);
} else {
log.debug("Loaded font: Family = {}, Name = {}, Style = {}.", font.getFamily(), font.getName(), font.getStyle());
}
}
}
}
} }

View File

@ -25,7 +25,7 @@ public class MainViewController {
breadCrumb -> { breadCrumb -> {
Label label = new Label("> " + breadCrumb.route()); Label label = new Label("> " + breadCrumb.route());
if (breadCrumb.current()) { if (breadCrumb.current()) {
label.setStyle("-fx-font-weight: bold"); label.getStyleClass().add("bold-text");
} }
return label; return label;
} }

View File

@ -66,22 +66,15 @@ public class ProfilesViewController {
for (String profileName : profileNames) { for (String profileName : profileNames) {
boolean isCurrent = profileName.equals(currentProfile); boolean isCurrent = profileName.equals(currentProfile);
AnchorPane profilePane = new AnchorPane(); AnchorPane profilePane = new AnchorPane();
profilePane.setStyle(""" profilePane.getStyleClass().add("tile");
-fx-border-color: lightgray;
-fx-border-radius: 5px;
-fx-padding: 5px;
""");
Text nameTextElement = new Text(profileName); Text nameTextElement = new Text(profileName);
nameTextElement.setStyle("-fx-font-size: large;"); nameTextElement.getStyleClass().add("large-font");
TextFlow nameLabel = new TextFlow(nameTextElement); TextFlow nameLabel = new TextFlow(nameTextElement);
if (isCurrent) { if (isCurrent) {
nameTextElement.setStyle("-fx-font-size: large; -fx-font-weight: bold;"); nameTextElement.getStyleClass().addAll("large-font", "bold-text");
Text currentProfileIndicator = new Text(" Currently Selected Profile"); Text currentProfileIndicator = new Text(" Currently Selected Profile");
currentProfileIndicator.setStyle(""" currentProfileIndicator.getStyleClass().addAll("small-font", "secondary-color-fill");
-fx-font-size: small;
-fx-fill: grey;
""");
nameLabel.getChildren().add(currentProfileIndicator); nameLabel.getChildren().add(currentProfileIndicator);
} }
AnchorPane.setLeftAnchor(nameLabel, 0.0); AnchorPane.setLeftAnchor(nameLabel, 0.0);

View File

@ -39,6 +39,16 @@ public class Account extends IdEntity {
return "..." + accountNumber.substring(accountNumber.length() - suffixLength); return "..." + accountNumber.substring(accountNumber.length() - suffixLength);
} }
public String getAccountNumberGrouped(int groupSize, char separator) {
StringBuilder sb = new StringBuilder();
int idx = 0;
while (idx < accountNumber.length()) {
sb.append(accountNumber.charAt(idx++));
if (idx % groupSize == 0 && idx < accountNumber.length()) sb.append(separator);
}
return sb.toString();
}
public String getShortName() { public String getShortName() {
String numberSuffix = getAccountNumberSuffix(); String numberSuffix = getAccountNumberSuffix();
return name + " (" + numberSuffix + ")"; return name + " (" + numberSuffix + ")";

View File

@ -87,9 +87,10 @@ public class AccountEntry extends IdEntity {
* @return The effective value of this entry, either positive or negative. * @return The effective value of this entry, either positive or negative.
*/ */
public BigDecimal getEffectiveValue(AccountType accountType) { public BigDecimal getEffectiveValue(AccountType accountType) {
return switch (accountType) { if (accountType.areDebitsPositive()) {
case CHECKING, SAVINGS -> type == Type.DEBIT ? amount : amount.negate(); return type == Type.DEBIT ? amount : amount.negate();
case CREDIT_CARD -> type == Type.DEBIT ? amount.negate() : amount; } else {
}; return type == Type.DEBIT ? amount.negate() : amount;
}
} }
} }

View File

@ -4,14 +4,20 @@ package com.andrewlalis.perfin.model;
* Represents the different possible account types in Perfin. * Represents the different possible account types in Perfin.
*/ */
public enum AccountType { public enum AccountType {
CHECKING("Checking"), CHECKING("Checking", true),
SAVINGS("Savings"), SAVINGS("Savings", true),
CREDIT_CARD("Credit Card"); CREDIT_CARD("Credit Card", false);
private final String name; private final String name;
private final boolean debitsPositive;
AccountType(String name) { AccountType(String name, boolean debitsPositive) {
this.name = name; this.name = name;
this.debitsPositive = debitsPositive;
}
public boolean areDebitsPositive() {
return debitsPositive;
} }
@Override @Override

View File

@ -23,7 +23,7 @@ public class AccountComboBoxCellFactory implements Callback<ListView<Account>, L
public AccountListCell(String emptyCellText) { public AccountListCell(String emptyCellText) {
this.emptyCellText = emptyCellText; this.emptyCellText = emptyCellText;
label.setStyle("-fx-text-fill: black;"); label.getStyleClass().add("normal-color-text-fill");
} }
@Override @Override

View File

@ -53,7 +53,10 @@ public class StartupSplashScreen extends Stage implements Consumer<String> {
textArea.setFocusTraversable(false); textArea.setFocusTraversable(false);
Scene scene = new Scene(root, 400.0, 200.0); Scene scene = new Scene(root, 400.0, 200.0);
scene.getStylesheets().add(StartupSplashScreen.class.getResource("/style/startup-splash-screen.css").toExternalForm()); scene.getStylesheets().addAll(
StartupSplashScreen.class.getResource("/style/base.css").toExternalForm(),
StartupSplashScreen.class.getResource("/style/startup-splash-screen.css").toExternalForm()
);
return scene; return scene;
} }

View File

@ -12,14 +12,10 @@ import javafx.scene.layout.BorderPane;
*/ */
public abstract class AccountHistoryItemTile extends BorderPane { public abstract class AccountHistoryItemTile extends BorderPane {
public AccountHistoryItemTile(AccountHistoryItem item) { public AccountHistoryItemTile(AccountHistoryItem item) {
setStyle(""" getStyleClass().add("tile");
-fx-border-color: lightgray;
-fx-border-radius: 5px;
-fx-padding: 5px;
""");
Label timestampLabel = new Label(DateUtil.formatUTCAsLocalWithZone(item.getTimestamp())); Label timestampLabel = new Label(DateUtil.formatUTCAsLocalWithZone(item.getTimestamp()));
timestampLabel.setStyle("-fx-font-size: small;"); timestampLabel.getStyleClass().add("small-font");
setTop(timestampLabel); setTop(timestampLabel);
} }

View File

@ -1,8 +1,11 @@
package com.andrewlalis.perfin.view.component; package com.andrewlalis.perfin.view.component;
import com.andrewlalis.perfin.data.util.CurrencyUtil;
import com.andrewlalis.perfin.model.Account; import com.andrewlalis.perfin.model.Account;
import com.andrewlalis.perfin.model.AccountType; import com.andrewlalis.perfin.model.AccountType;
import com.andrewlalis.perfin.model.MoneyValue;
import com.andrewlalis.perfin.model.Profile; import com.andrewlalis.perfin.model.Profile;
import javafx.application.Platform;
import javafx.geometry.HPos; import javafx.geometry.HPos;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Label; import javafx.scene.control.Label;
@ -14,6 +17,7 @@ import javafx.scene.layout.Priority;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import java.math.BigDecimal;
import java.util.Map; import java.util.Map;
import static com.andrewlalis.perfin.PerfinApp.router; import static com.andrewlalis.perfin.PerfinApp.router;
@ -30,14 +34,7 @@ public class AccountTile extends BorderPane {
public AccountTile(Account account) { public AccountTile(Account account) {
setPrefWidth(350.0); setPrefWidth(350.0);
setStyle(""" getStyleClass().addAll("tile", "hand-cursor");
-fx-border-color: lightgray;
-fx-border-width: 1px;
-fx-border-style: solid;
-fx-border-radius: 5px;
-fx-padding: 5px;
-fx-cursor: hand;
""");
setTop(getHeader(account)); setTop(getHeader(account));
setBottom(getFooter(account)); setBottom(getFooter(account));
@ -48,7 +45,7 @@ public class AccountTile extends BorderPane {
private Node getHeader(Account account) { private Node getHeader(Account account) {
Text title = new Text("Account #" + account.id); Text title = new Text("Account #" + account.id);
title.setStyle("-fx-font-size: large; -fx-font-weight: bold;"); title.getStyleClass().addAll("large-font", "bold-text");
return title; return title;
} }
@ -56,7 +53,7 @@ public class AccountTile extends BorderPane {
Label currencyLabel = new Label(account.getCurrency().getCurrencyCode()); Label currencyLabel = new Label(account.getCurrency().getCurrencyCode());
Label typeLabel = new Label(account.getType().toString() + " Account"); Label typeLabel = new Label(account.getType().toString() + " Account");
HBox footerHBox = new HBox(currencyLabel, typeLabel); HBox footerHBox = new HBox(currencyLabel, typeLabel);
footerHBox.setStyle("-fx-font-size: x-small; -fx-spacing: 3px;"); footerHBox.getStyleClass().addAll("std-spacing", "small-font");
return footerHBox; return footerHBox;
} }
@ -73,15 +70,33 @@ public class AccountTile extends BorderPane {
valueConstraints.setHalignment(HPos.RIGHT); valueConstraints.setHalignment(HPos.RIGHT);
propertiesPane.getColumnConstraints().setAll(keyConstraints, valueConstraints); propertiesPane.getColumnConstraints().setAll(keyConstraints, valueConstraints);
Label accountNameLabel = newPropertyValue(account.getName()); Label accountNameLabel = new Label(account.getName());
accountNameLabel.setWrapText(true); accountNameLabel.setWrapText(true);
accountNameLabel.getStyleClass().add("italic-text");
Label accountTypeLabel = newPropertyValue(account.getType().toString()); Label accountNumberLabel = new Label(account.getAccountNumber());
accountNumberLabel.getStyleClass().add("mono-font");
Label accountTypeLabel = new Label(account.getType().toString());
accountTypeLabel.setTextFill(ACCOUNT_TYPE_COLORS.get(account.getType())); accountTypeLabel.setTextFill(ACCOUNT_TYPE_COLORS.get(account.getType()));
accountTypeLabel.setStyle("-fx-font-weight: bold;"); accountTypeLabel.getStyleClass().add("bold-text");
Label balanceLabel = newPropertyValue("Computing balance..."); Label balanceLabel = new Label("Computing balance...");
balanceLabel.getStyleClass().addAll("mono-font");
balanceLabel.setDisable(true); balanceLabel.setDisable(true);
Thread.ofVirtual().start(() -> Profile.getCurrent().getDataSource().useAccountRepository(repo -> {
BigDecimal balance = repo.deriveCurrentBalance(account.id);
String text = CurrencyUtil.formatMoney(new MoneyValue(balance, account.getCurrency()));
Platform.runLater(() -> {
balanceLabel.setText(text);
if (account.getType().areDebitsPositive() && balance.compareTo(BigDecimal.ZERO) < 0) {
balanceLabel.getStyleClass().add("negative-color-text-fill");
} else if (!account.getType().areDebitsPositive() && balance.compareTo(BigDecimal.ZERO) < 0) {
balanceLabel.getStyleClass().add("positive-color-text-fill");
}
balanceLabel.setDisable(false);
});
}));
Profile.getCurrent().getDataSource().getAccountBalanceText(account, text -> { Profile.getCurrent().getDataSource().getAccountBalanceText(account, text -> {
balanceLabel.setText(text); balanceLabel.setText(text);
balanceLabel.setDisable(false); balanceLabel.setDisable(false);
@ -91,7 +106,7 @@ public class AccountTile extends BorderPane {
newPropertyLabel("Account Name"), newPropertyLabel("Account Name"),
accountNameLabel, accountNameLabel,
newPropertyLabel("Account Number"), newPropertyLabel("Account Number"),
newPropertyValue(account.getAccountNumber()), accountNumberLabel,
newPropertyLabel("Account Type"), newPropertyLabel("Account Type"),
accountTypeLabel, accountTypeLabel,
newPropertyLabel("Current Balance"), newPropertyLabel("Current Balance"),
@ -102,18 +117,7 @@ public class AccountTile extends BorderPane {
private static Label newPropertyLabel(String text) { private static Label newPropertyLabel(String text) {
Label lbl = new Label(text); Label lbl = new Label(text);
lbl.setStyle(""" lbl.getStyleClass().add("bold-text");
-fx-font-weight: bold;
""");
return lbl;
}
private static Label newPropertyValue(String text) {
Label lbl = new Label(text);
lbl.setStyle("""
-fx-font-family: monospace;
-fx-font-size: large;
""");
return lbl; return lbl;
} }
} }

View File

@ -27,7 +27,7 @@ public class AttachmentPreview extends BorderPane {
public AttachmentPreview(Attachment attachment) { public AttachmentPreview(Attachment attachment) {
BorderPane contentContainer = new BorderPane(); BorderPane contentContainer = new BorderPane();
Label nameLabel = new Label(attachment.getFilename()); Label nameLabel = new Label(attachment.getFilename());
nameLabel.setStyle("-fx-font-size: small;"); nameLabel.getStyleClass().add("small-font");
VBox nameContainer = new VBox(nameLabel); VBox nameContainer = new VBox(nameLabel);
nameContainer.setPrefHeight(LABEL_SIZE); nameContainer.setPrefHeight(LABEL_SIZE);
nameContainer.setMaxHeight(LABEL_SIZE); nameContainer.setMaxHeight(LABEL_SIZE);

View File

@ -27,42 +27,30 @@ import static com.andrewlalis.perfin.PerfinApp.router;
*/ */
public class TransactionTile extends BorderPane { public class TransactionTile extends BorderPane {
public final BooleanProperty selected = new SimpleBooleanProperty(false); public final BooleanProperty selected = new SimpleBooleanProperty(false);
private static final String UNSELECTED_STYLE = """
-fx-border-color: lightgray;
-fx-border-width: 1px;
-fx-border-style: solid;
-fx-border-radius: 5px;
-fx-padding: 5px;
-fx-cursor: hand;
""";
private static final String SELECTED_STYLE = """
-fx-border-color: white;
-fx-border-width: 1px;
-fx-border-style: solid;
-fx-border-radius: 5px;
-fx-padding: 5px;
-fx-cursor: hand;
""";
public TransactionTile(Transaction transaction) { public TransactionTile(Transaction transaction) {
setStyle(UNSELECTED_STYLE); getStyleClass().addAll("tile", "hand-cursor");
setTop(getHeader(transaction)); setTop(getHeader(transaction));
setCenter(getBody(transaction)); setCenter(getBody(transaction));
setBottom(getFooter(transaction)); setBottom(getFooter(transaction));
styleProperty().bind(selected.map(value -> value ? SELECTED_STYLE : UNSELECTED_STYLE)); selected.addListener((observable, oldValue, newValue) -> {
if (newValue) {
getStyleClass().add("tile-border-selected");
} else {
getStyleClass().remove("tile-border-selected");
}
});
} }
private Node getHeader(Transaction transaction) { private Node getHeader(Transaction transaction) {
Label currencyLabel = new Label(CurrencyUtil.formatMoney(transaction.getMoneyAmount())); Label currencyLabel = new Label(CurrencyUtil.formatMoney(transaction.getMoneyAmount()));
currencyLabel.setStyle("-fx-font-family: monospace;"); currencyLabel.getStyleClass().add("mono-font");
HBox headerHBox = new HBox( HBox headerHBox = new HBox(
currencyLabel currencyLabel
); );
headerHBox.setStyle(""" headerHBox.getStyleClass().addAll("std-spacing");
-fx-spacing: 3px;
""");
return headerHBox; return headerHBox;
} }
@ -77,14 +65,14 @@ public class TransactionTile extends BorderPane {
Hyperlink link = new Hyperlink(acc.getShortName()); Hyperlink link = new Hyperlink(acc.getShortName());
link.setOnAction(event -> router.navigate("account", acc)); link.setOnAction(event -> router.navigate("account", acc));
Text prefix = new Text("Credited from"); Text prefix = new Text("Credited from");
prefix.setFill(Color.RED); prefix.getStyleClass().add("negative-color-fill");
Platform.runLater(() -> bodyVBox.getChildren().add(new TextFlow(prefix, link))); Platform.runLater(() -> bodyVBox.getChildren().add(new TextFlow(prefix, link)));
}); });
accounts.ifDebit(acc -> { accounts.ifDebit(acc -> {
Hyperlink link = new Hyperlink(acc.getShortName()); Hyperlink link = new Hyperlink(acc.getShortName());
link.setOnAction(event -> router.navigate("account", acc)); link.setOnAction(event -> router.navigate("account", acc));
Text prefix = new Text("Debited to"); Text prefix = new Text("Debited to");
prefix.setFill(Color.GREEN); prefix.getStyleClass().add("positive-color-fill");
Platform.runLater(() -> bodyVBox.getChildren().add(new TextFlow(prefix, link))); Platform.runLater(() -> bodyVBox.getChildren().add(new TextFlow(prefix, link)));
}); });
}); });
@ -96,10 +84,7 @@ public class TransactionTile extends BorderPane {
HBox footerHBox = new HBox( HBox footerHBox = new HBox(
timestampLabel timestampLabel
); );
footerHBox.setStyle(""" footerHBox.getStyleClass().addAll("std-spacing", "small-font");
-fx-spacing: 3px;
-fx-font-size: small;
""");
return footerHBox; return footerHBox;
} }

View File

@ -37,7 +37,7 @@
<VBox> <VBox>
<Label text="Current Balance" styleClass="bold-text" fx:id="balanceLabel"/> <Label text="Current Balance" styleClass="bold-text" fx:id="balanceLabel"/>
<Text <Text
style="-fx-font-size: x-small; -fx-fill: grey" styleClass="small-font,secondary-color-fill"
wrappingWidth="${balanceLabel.width}" wrappingWidth="${balanceLabel.width}"
>Computed using the last recorded balance and all transactions since.</Text> >Computed using the last recorded balance and all transactions since.</Text>
</VBox> </VBox>
@ -65,7 +65,7 @@
<Label text="History" styleClass="bold-text,std-padding"/> <Label text="History" styleClass="bold-text,std-padding"/>
<VBox> <VBox>
<ScrollPane fitToHeight="true" fitToWidth="true"> <ScrollPane fitToHeight="true" fitToWidth="true">
<VBox fx:id="historyItemsVBox" style="-fx-padding: 10px; -fx-spacing: 10px;"/> <VBox fx:id="historyItemsVBox" styleClass="padding-extra-1,spacing-extra-1"/>
</ScrollPane> </ScrollPane>
<AnchorPane> <AnchorPane>
<Button <Button

View File

@ -19,7 +19,7 @@
<center> <center>
<VBox> <VBox>
<ScrollPane fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS"> <ScrollPane fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS">
<FlowPane fx:id="accountsPane" BorderPane.alignment="TOP_LEFT" vgap="5" hgap="5" styleClass="std-padding"/> <FlowPane fx:id="accountsPane" BorderPane.alignment="TOP_LEFT" vgap="10" hgap="10" styleClass="padding-extra-1"/>
</ScrollPane> </ScrollPane>
<Label fx:id="noAccountsLabel" BorderPane.alignment="TOP_LEFT" styleClass="std-padding" text="No accounts have been added to this profile."/> <Label fx:id="noAccountsLabel" BorderPane.alignment="TOP_LEFT" styleClass="std-padding" text="No accounts have been added to this profile."/>
</VBox> </VBox>

View File

@ -27,7 +27,7 @@
</VBox> </VBox>
<VBox> <VBox>
<Label text="Description" labelFor="${descriptionField}" styleClass="bold-text"/> <Label text="Description" labelFor="${descriptionField}" styleClass="bold-text"/>
<Label text="Maximum of 255 characters." styleClass="small-text"/> <Label text="Maximum of 255 characters." styleClass="small-font"/>
<TextArea <TextArea
fx:id="descriptionField" fx:id="descriptionField"
styleClass="mono-font" styleClass="mono-font"
@ -57,7 +57,7 @@
<!-- Container for attachments --> <!-- Container for attachments -->
<VBox fx:id="attachmentsVBox"> <VBox fx:id="attachmentsVBox">
<Label text="Attachments" styleClass="bold-text"/> <Label text="Attachments" styleClass="bold-text"/>
<Label text="Attach receipts, invoices, or other content to this transaction." styleClass="small-text" wrapText="true"/> <Label text="Attach receipts, invoices, or other content to this transaction." styleClass="small-font" wrapText="true"/>
<!-- FileSelectionArea inserted here! --> <!-- FileSelectionArea inserted here! -->
</VBox> </VBox>
</VBox> </VBox>

View File

@ -18,7 +18,7 @@
<Button text="Transactions" onAction="#goToTransactions"/> <Button text="Transactions" onAction="#goToTransactions"/>
<Button text="Profiles" onAction="#viewProfiles"/> <Button text="Profiles" onAction="#viewProfiles"/>
</HBox> </HBox>
<HBox fx:id="breadcrumbHBox" styleClass="std-spacing,small-text"/> <HBox fx:id="breadcrumbHBox" styleClass="std-spacing,small-font"/>
</VBox> </VBox>
</top> </top>
<bottom> <bottom>

View File

@ -1,5 +1,19 @@
.root {
-fx-font-family: "Roboto", sans-serif;
-fx-font-size: 14px;
-fx-text-fill: rgb(26, 26, 26);
}
.mono-font { .mono-font {
-fx-font-family: monospace; -fx-font-family: "JetBrains Mono", monospace;
}
.large-font {
-fx-font-size: 18px;
}
.small-font {
-fx-font-size: 10px;
} }
.error-text { .error-text {
@ -11,8 +25,8 @@
-fx-font-weight: bold; -fx-font-weight: bold;
} }
.small-text { .italic-text {
-fx-font-size: small; -fx-font-style: italic;
} }
.large-text { .large-text {
@ -22,14 +36,62 @@
.std-padding { .std-padding {
-fx-padding: 3px; -fx-padding: 3px;
} }
.padding-extra {
-fx-padding: 6px;
}
.padding-extra-1 {
-fx-padding: 10px;
}
.std-spacing { .std-spacing {
-fx-spacing: 3px; -fx-spacing: 3px;
} }
.spacing-extra { .spacing-extra {
-fx-spacing: 6px; -fx-spacing: 6px;
} }
.spacing-extra-1 {
-fx-spacing: 10px;
}
.hand-cursor {
-fx-cursor: hand;
}
/* Standard "tile" styling. */
.tile {
-fx-border-color: lightgray;
-fx-border-width: 2px;
-fx-border-style: solid;
-fx-border-radius: 5px;
-fx-padding: 5px;
}
.tile-border-selected {
-fx-border-color: darkgray;
}
/* Standard colors */
.normal-color-text-fill {
-fx-text-fill: rgb(26, 26, 26);
}
.secondary-color-fill {
-fx-fill: rgb(99, 99, 99);
}
.negative-color-text-fill {
-fx-text-fill: rgb(247, 37, 69);
}
.negative-color-fill {
-fx-fill: rgb(247, 37, 69);
}
.positive-color-text-fill {
-fx-text-fill: rgb(43, 196, 77);
}
.positive-color-fill {
-fx-fill: rgb(43, 196, 77);
}
/* DEBUG BORDERS */ /* DEBUG BORDERS */
.debug-border-1 { .debug-border-1 {

View File

@ -28,7 +28,7 @@
</top> </top>
<center> <center>
<ScrollPane fitToHeight="true" fitToWidth="true"> <ScrollPane fitToHeight="true" fitToWidth="true">
<VBox fx:id="transactionsVBox" styleClass="std-padding,spacing-extra"/> <VBox fx:id="transactionsVBox" styleClass="padding-extra-1,spacing-extra-1"/>
</ScrollPane> </ScrollPane>
</center> </center>
</BorderPane> </BorderPane>

View File

@ -0,0 +1,41 @@
package com.andrewlalis.perfin.model;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.Currency;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class AccountTest {
@Test
public void testGetAccountNumberSuffix() {
assertEquals("...1234", getTestAccountWithNumber("4328-1234").getAccountNumberSuffix());
}
@Test
public void testGetAccountNumberGrouped() {
assertEquals(
"1234-5678-9101-1121",
getTestAccountWithNumber("1234567891011121")
.getAccountNumberGrouped(4, '-')
);
assertEquals(
"123-456-7",
getTestAccountWithNumber("1234567")
.getAccountNumberGrouped(3, '-')
);
}
private Account getTestAccountWithNumber(String num) {
return new Account(
1,
LocalDateTime.now(),
false,
AccountType.CHECKING,
num,
"Testing Account",
Currency.getInstance("USD")
);
}
}