diff --git a/pom.xml b/pom.xml
index 8504fba..44a2705 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,6 +13,7 @@
21
UTF-8
21.0.1
+ com.andrewlalis.perfin.PerfinApp
@@ -60,6 +61,46 @@
com.andrewlalis.perfin.PerfinApp
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.6.0
+
+
+
+ com.andrewlalis.perfin.PerfinApp
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.6.0
+
+
+ package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/lib
+
+
+
+
\ No newline at end of file
diff --git a/run-jar.sh b/run-jar.sh
new file mode 100755
index 0000000..ab86ff2
--- /dev/null
+++ b/run-jar.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+shouldBuild=0
+for i in "$@" ; do
+ if [[ $i == "build" ]] ; then
+ shouldBuild=1
+ break
+ fi
+done
+
+if [ $shouldBuild == 1 ]; then
+ mvn clean
+ mvn package
+fi
+
+java \
+ --add-modules=javafx.controls,com.andrewlalis.javafx_scene_router \
+ --module-path=target/lib/ \
+ -jar target/perfin-*-jar-with-dependencies.jar
diff --git a/src/main/java/com/andrewlalis/perfin/PerfinApp.java b/src/main/java/com/andrewlalis/perfin/PerfinApp.java
index fe678a0..e9d6061 100644
--- a/src/main/java/com/andrewlalis/perfin/PerfinApp.java
+++ b/src/main/java/com/andrewlalis/perfin/PerfinApp.java
@@ -2,12 +2,17 @@ package com.andrewlalis.perfin;
import com.andrewlalis.javafx_scene_router.AnchorPaneRouterView;
import com.andrewlalis.javafx_scene_router.SceneRouter;
-import com.andrewlalis.perfin.view.SplashScreenStage;
+import com.andrewlalis.perfin.model.Profile;
+import com.andrewlalis.perfin.view.StartupSplashScreen;
import javafx.application.Application;
+import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.List;
+import java.util.function.Consumer;
/**
* The class from which the JavaFX-based application starts.
@@ -26,33 +31,57 @@ public class PerfinApp extends Application {
@Override
public void start(Stage stage) {
- SplashScreenStage splashStage = new SplashScreenStage("Loading", SceneUtil.load("/startup-splash-screen.fxml"));
- splashStage.show();
- defineRoutes();
- initMainScreen(stage);
- splashStage.stateProperty().addListener((v, oldState, state) -> {
- if (state == SplashScreenStage.State.DONE) stage.show();
- if (state == SplashScreenStage.State.ERROR) System.out.println("ERROR!");
- });
+ var splashScreen = new StartupSplashScreen(List.of(
+ PerfinApp::defineRoutes,
+ PerfinApp::initAppDir,
+ c -> initMainScreen(stage, c),
+ PerfinApp::loadProfile
+ ));
+ splashScreen.showAndWait();
+ if (splashScreen.isStartupSuccessful()) {
+ stage.show();
+ }
}
- private void initMainScreen(Stage stage) {
- stage.hide();
- Scene mainViewScene = SceneUtil.load("/main-view.fxml");
- stage.setScene(mainViewScene);
- stage.setTitle("Perfin");
+ private void initMainScreen(Stage stage, Consumer msgConsumer) throws Exception {
+ msgConsumer.accept("Initializing main screen.");
+ Platform.runLater(() -> {
+ stage.hide();
+ Scene mainViewScene = SceneUtil.load("/main-view.fxml");
+ stage.setScene(mainViewScene);
+ stage.setTitle("Perfin");
+ });
}
private static void mapResourceRoute(String route, String resource) {
router.map(route, PerfinApp.class.getResource(resource));
}
- private static void defineRoutes() {
- mapResourceRoute("accounts", "/accounts-view.fxml");
- mapResourceRoute("account", "/account-view.fxml");
- mapResourceRoute("edit-account", "/edit-account.fxml");
- mapResourceRoute("transactions", "/transactions-view.fxml");
- mapResourceRoute("create-transaction", "/create-transaction.fxml");
- mapResourceRoute("transaction", "/transaction-view.fxml");
+ private static void defineRoutes(Consumer msgConsumer) throws Exception {
+ msgConsumer.accept("Initializing application views.");
+ Platform.runLater(() -> {
+ mapResourceRoute("accounts", "/accounts-view.fxml");
+ mapResourceRoute("account", "/account-view.fxml");
+ mapResourceRoute("edit-account", "/edit-account.fxml");
+ mapResourceRoute("transactions", "/transactions-view.fxml");
+ mapResourceRoute("create-transaction", "/create-transaction.fxml");
+ });
+ }
+
+ private static void initAppDir(Consumer msgConsumer) throws Exception {
+ msgConsumer.accept("Validating application files.");
+ if (Files.notExists(APP_DIR)) {
+ msgConsumer.accept(APP_DIR + " doesn't exist yet. Creating it now.");
+ Files.createDirectory(APP_DIR);
+ } else if (Files.exists(APP_DIR) && Files.isRegularFile(APP_DIR)) {
+ msgConsumer.accept(APP_DIR + " is a file, when it should be a directory. Deleting it and creating new directory.");
+ Files.delete(APP_DIR);
+ Files.createDirectory(APP_DIR);
+ }
+ }
+
+ private static void loadProfile(Consumer msgConsumer) throws Exception {
+ msgConsumer.accept("Loading the most recent profile.");
+ Profile.loadLast();
}
}
\ No newline at end of file
diff --git a/src/main/java/com/andrewlalis/perfin/control/AccountsViewController.java b/src/main/java/com/andrewlalis/perfin/control/AccountsViewController.java
index ff083d0..35923da 100644
--- a/src/main/java/com/andrewlalis/perfin/control/AccountsViewController.java
+++ b/src/main/java/com/andrewlalis/perfin/control/AccountsViewController.java
@@ -1,7 +1,7 @@
package com.andrewlalis.perfin.control;
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
-import com.andrewlalis.perfin.control.component.AccountTile;
+import com.andrewlalis.perfin.view.component.AccountTile;
import com.andrewlalis.perfin.data.CurrencyUtil;
import com.andrewlalis.perfin.data.pagination.PageRequest;
import com.andrewlalis.perfin.data.pagination.Sort;
diff --git a/src/main/java/com/andrewlalis/perfin/control/StartupSplashScreenController.java b/src/main/java/com/andrewlalis/perfin/control/StartupSplashScreenController.java
deleted file mode 100644
index 06703c5..0000000
--- a/src/main/java/com/andrewlalis/perfin/control/StartupSplashScreenController.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.andrewlalis.perfin.control;
-
-import com.andrewlalis.perfin.model.Profile;
-import com.andrewlalis.perfin.view.SplashScreenStage;
-import javafx.application.Platform;
-import javafx.fxml.FXML;
-import javafx.scene.control.TextArea;
-import javafx.scene.layout.BorderPane;
-
-import java.io.IOException;
-import java.nio.file.Files;
-
-import static com.andrewlalis.perfin.PerfinApp.APP_DIR;
-
-/**
- * A controller for the application's splash screen that shows initially on
- * startup. While the splash screen is shown, we do any complicated loading
- * tasks so that the application starts properly, and give the user periodic
- * updates as we go.
- */
-public class StartupSplashScreenController {
- @FXML
- public BorderPane sceneRoot;
- @FXML
- public TextArea content;
-
- @FXML
- public void initialize() {
- Thread.ofVirtual().start(() -> {
- try {
- printlnLater("Initializing application files...");
- if (!initAppDir()) {
- Thread.sleep(3000);
- Platform.runLater(() -> getSplashStage().setError());
- return;
- }
-
- printlnLater("Loading the last profile...");
- try {
- Profile.loadLast();
- } catch (Exception e) {
- printlnLater("Failed to load profile: " + e.getMessage());
- Thread.sleep(3000);
- Platform.runLater(() -> getSplashStage().setError());
- return;
- }
-
-
- printlnLater("Perfin initialized. Starting the app now.");
- Thread.sleep(500);
-
- Platform.runLater(() -> getSplashStage().setDone());
- } catch (Exception e) {
- e.printStackTrace(System.err);
- printlnLater("An error occurred while starting: " + e.getMessage() + "\nThe application will now exit.");
- Platform.runLater(() -> getSplashStage().setError());
- }
- });
- }
-
- private void println(String text) {
- content.appendText(text + "\n");
- }
-
- private void printlnLater(String text) {
- Platform.runLater(() -> println(text));
- }
-
- private SplashScreenStage getSplashStage() {
- return (SplashScreenStage) sceneRoot.getScene().getWindow();
- }
-
- private boolean initAppDir() {
- if (Files.notExists(APP_DIR)) {
- printlnLater(APP_DIR + " doesn't exist yet. Creating it now.");
- try {
- Files.createDirectory(APP_DIR);
- } catch (IOException e) {
- printlnLater("Could not create directory " + APP_DIR + "; " + e.getMessage());
- return false;
- }
- } else if (Files.exists(APP_DIR) && Files.isRegularFile(APP_DIR)) {
- printlnLater(APP_DIR + " is a file, when it should be a directory. Deleting it and creating new directory.");
- try {
- Files.delete(APP_DIR);
- Files.createDirectory(APP_DIR);
- } catch (IOException e) {
- printlnLater("Could not delete file and create directory " + APP_DIR + "; " + e.getMessage());
- return false;
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/com/andrewlalis/perfin/control/TransactionViewController.java b/src/main/java/com/andrewlalis/perfin/control/TransactionViewController.java
index 22e8008..658c3b6 100644
--- a/src/main/java/com/andrewlalis/perfin/control/TransactionViewController.java
+++ b/src/main/java/com/andrewlalis/perfin/control/TransactionViewController.java
@@ -1,6 +1,6 @@
package com.andrewlalis.perfin.control;
-import com.andrewlalis.perfin.control.component.AttachmentPreview;
+import com.andrewlalis.perfin.view.component.AttachmentPreview;
import com.andrewlalis.perfin.data.CurrencyUtil;
import com.andrewlalis.perfin.data.DateUtil;
import com.andrewlalis.perfin.model.CreditAndDebitAccounts;
@@ -15,6 +15,8 @@ import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.skin.ScrollPaneSkin;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.TextFlow;
@@ -70,6 +72,10 @@ public class TransactionViewController {
Platform.runLater(() -> attachmentsList.setAll(attachments));
});
});
+ attachmentsHBox.setMinHeight(AttachmentPreview.HEIGHT);
+ attachmentsHBox.setPrefHeight(AttachmentPreview.HEIGHT);
+ ((ScrollPane) attachmentsHBox.getParent().getParent().getParent()).minHeightProperty().bind(attachmentsHBox.heightProperty().map(n -> n.doubleValue() + 2));
+ ((ScrollPane) attachmentsHBox.getParent().getParent().getParent()).prefHeightProperty().bind(attachmentsHBox.heightProperty().map(n -> n.doubleValue() + 2));
BindingUtil.mapContent(attachmentsHBox.getChildren(), attachmentsList, AttachmentPreview::new);
}
diff --git a/src/main/java/com/andrewlalis/perfin/control/TransactionsViewController.java b/src/main/java/com/andrewlalis/perfin/control/TransactionsViewController.java
index 03bff07..d5c864c 100644
--- a/src/main/java/com/andrewlalis/perfin/control/TransactionsViewController.java
+++ b/src/main/java/com/andrewlalis/perfin/control/TransactionsViewController.java
@@ -3,8 +3,8 @@ package com.andrewlalis.perfin.control;
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
import com.andrewlalis.perfin.Pair;
import com.andrewlalis.perfin.SceneUtil;
-import com.andrewlalis.perfin.control.component.DataSourcePaginationControls;
-import com.andrewlalis.perfin.control.component.TransactionTile;
+import com.andrewlalis.perfin.view.component.DataSourcePaginationControls;
+import com.andrewlalis.perfin.view.component.TransactionTile;
import com.andrewlalis.perfin.data.pagination.Page;
import com.andrewlalis.perfin.data.pagination.PageRequest;
import com.andrewlalis.perfin.data.pagination.Sort;
diff --git a/src/main/java/com/andrewlalis/perfin/view/SplashScreenStage.java b/src/main/java/com/andrewlalis/perfin/view/SplashScreenStage.java
deleted file mode 100644
index 159b0c7..0000000
--- a/src/main/java/com/andrewlalis/perfin/view/SplashScreenStage.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.andrewlalis.perfin.view;
-
-import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.value.ObservableValue;
-import javafx.scene.Scene;
-import javafx.stage.Stage;
-import javafx.stage.StageStyle;
-
-public class SplashScreenStage extends Stage {
- public enum State {
- LOADING,
- DONE,
- ERROR
- }
-
- private final SimpleObjectProperty stateProperty = new SimpleObjectProperty<>(State.LOADING);
-
- public SplashScreenStage(String title, Scene scene) {
- setTitle(title);
- setResizable(false);
- initStyle(StageStyle.UNDECORATED);
- setScene(scene);
- }
-
- public void setDone() {
- stateProperty.set(State.DONE);
- close();
- }
-
- public void setError() {
- stateProperty.set(State.ERROR);
- close();
- }
-
- public ObservableValue stateProperty() {
- return this.stateProperty;
- }
-}
diff --git a/src/main/java/com/andrewlalis/perfin/view/StartupSplashScreen.java b/src/main/java/com/andrewlalis/perfin/view/StartupSplashScreen.java
new file mode 100644
index 0000000..ee9a56c
--- /dev/null
+++ b/src/main/java/com/andrewlalis/perfin/view/StartupSplashScreen.java
@@ -0,0 +1,82 @@
+package com.andrewlalis.perfin.view;
+
+import com.andrewlalis.perfin.data.ThrowableConsumer;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.control.TextArea;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A splash screen that is shown as the application starts up, and does some
+ * tasks before the main application can start.
+ */
+public class StartupSplashScreen extends Stage implements Consumer {
+ private final List>> tasks;
+ private boolean startupSuccessful = false;
+
+ private final TextArea textArea = new TextArea();
+
+ public StartupSplashScreen(List>> tasks) {
+ this.tasks = tasks;
+ setTitle("Starting Perfin...");
+ setResizable(false);
+ initStyle(StageStyle.UNDECORATED);
+
+ setScene(buildScene());
+ setOnShowing(event -> runTasks());
+ }
+
+ public boolean isStartupSuccessful() {
+ return startupSuccessful;
+ }
+
+ @Override
+ public void accept(String message) {
+ Platform.runLater(() -> textArea.appendText(message + "\n"));
+ }
+
+ private Scene buildScene() {
+ BorderPane root = new BorderPane(textArea);
+ root.setId("sceneRoot");
+ root.setPrefWidth(400.0);
+ root.setPrefHeight(200.0);
+
+ textArea.setId("content");
+ textArea.setWrapText(true);
+ textArea.setEditable(false);
+ textArea.setFocusTraversable(false);
+
+ Scene scene = new Scene(root, 400.0, 200.0);
+ scene.getStylesheets().add(StartupSplashScreen.class.getResource("/style/startup-splash-screen.css").toExternalForm());
+ return scene;
+ }
+
+ private void runTasks() {
+ Thread.ofVirtual().start(() -> {
+ for (var task : tasks) {
+ try {
+ task.accept(this);
+ Thread.sleep(100);
+ } catch (Exception e) {
+ accept("Startup failed: " + e.getMessage());
+ e.printStackTrace(System.err);
+ Platform.runLater(this::close);
+ return;
+ }
+ }
+ accept("Startup successful!");
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ startupSuccessful = true;
+ Platform.runLater(this::close);
+ });
+ }
+}
diff --git a/src/main/java/com/andrewlalis/perfin/control/component/AccountTile.java b/src/main/java/com/andrewlalis/perfin/view/component/AccountTile.java
similarity index 98%
rename from src/main/java/com/andrewlalis/perfin/control/component/AccountTile.java
rename to src/main/java/com/andrewlalis/perfin/view/component/AccountTile.java
index 02d67da..b43245c 100644
--- a/src/main/java/com/andrewlalis/perfin/control/component/AccountTile.java
+++ b/src/main/java/com/andrewlalis/perfin/view/component/AccountTile.java
@@ -1,4 +1,4 @@
-package com.andrewlalis.perfin.control.component;
+package com.andrewlalis.perfin.view.component;
import com.andrewlalis.perfin.model.Account;
import com.andrewlalis.perfin.model.AccountType;
diff --git a/src/main/java/com/andrewlalis/perfin/control/component/AttachmentPreview.java b/src/main/java/com/andrewlalis/perfin/view/component/AttachmentPreview.java
similarity index 64%
rename from src/main/java/com/andrewlalis/perfin/control/component/AttachmentPreview.java
rename to src/main/java/com/andrewlalis/perfin/view/component/AttachmentPreview.java
index 824d4d6..3e97082 100644
--- a/src/main/java/com/andrewlalis/perfin/control/component/AttachmentPreview.java
+++ b/src/main/java/com/andrewlalis/perfin/view/component/AttachmentPreview.java
@@ -1,4 +1,4 @@
-package com.andrewlalis.perfin.control.component;
+package com.andrewlalis.perfin.view.component;
import com.andrewlalis.perfin.model.TransactionAttachment;
import javafx.scene.control.Label;
@@ -18,20 +18,27 @@ import java.util.Set;
* like its name, type, and a preview image if possible.
*/
public class AttachmentPreview extends BorderPane {
+ public static final double IMAGE_SIZE = 64.0;
+ public static final double LABEL_SIZE = 18.0;
+ public static final double HEIGHT = IMAGE_SIZE + LABEL_SIZE;
+
public AttachmentPreview(TransactionAttachment attachment) {
Label nameLabel = new Label(attachment.getFilename());
- Label typeLabel = new Label(attachment.getContentType());
- typeLabel.setStyle("-fx-font-size: x-small;");
- setBottom(new VBox(nameLabel, typeLabel));
+ nameLabel.setStyle("-fx-font-size: small;");
+ VBox nameContainer = new VBox(nameLabel);
+ nameContainer.setPrefHeight(LABEL_SIZE);
+ nameContainer.setMaxHeight(LABEL_SIZE);
+ nameContainer.setMinHeight(LABEL_SIZE);
+ setBottom(nameContainer);
- Rectangle placeholder = new Rectangle(64.0, 64.0);
+ Rectangle placeholder = new Rectangle(IMAGE_SIZE, IMAGE_SIZE);
placeholder.setFill(Color.WHITE);
setCenter(placeholder);
Set imageTypes = Set.of("image/png", "image/jpeg", "image/gif", "image/bmp");
if (imageTypes.contains(attachment.getContentType())) {
try (var in = Files.newInputStream(attachment.getPath())) {
- Image img = new Image(in, 64.0, 64.0, true, true);
+ Image img = new Image(in, IMAGE_SIZE, IMAGE_SIZE, true, true);
setCenter(new ImageView(img));
} catch (IOException e) {
e.printStackTrace();
diff --git a/src/main/java/com/andrewlalis/perfin/control/component/DataSourcePaginationControls.java b/src/main/java/com/andrewlalis/perfin/view/component/DataSourcePaginationControls.java
similarity index 98%
rename from src/main/java/com/andrewlalis/perfin/control/component/DataSourcePaginationControls.java
rename to src/main/java/com/andrewlalis/perfin/view/component/DataSourcePaginationControls.java
index dc07992..3911533 100644
--- a/src/main/java/com/andrewlalis/perfin/control/component/DataSourcePaginationControls.java
+++ b/src/main/java/com/andrewlalis/perfin/view/component/DataSourcePaginationControls.java
@@ -1,4 +1,4 @@
-package com.andrewlalis.perfin.control.component;
+package com.andrewlalis.perfin.view.component;
import com.andrewlalis.perfin.data.pagination.Page;
import com.andrewlalis.perfin.data.pagination.PageRequest;
diff --git a/src/main/java/com/andrewlalis/perfin/control/component/TransactionTile.java b/src/main/java/com/andrewlalis/perfin/view/component/TransactionTile.java
similarity index 98%
rename from src/main/java/com/andrewlalis/perfin/control/component/TransactionTile.java
rename to src/main/java/com/andrewlalis/perfin/view/component/TransactionTile.java
index 5d4c874..24a3cea 100644
--- a/src/main/java/com/andrewlalis/perfin/control/component/TransactionTile.java
+++ b/src/main/java/com/andrewlalis/perfin/view/component/TransactionTile.java
@@ -1,4 +1,4 @@
-package com.andrewlalis.perfin.control.component;
+package com.andrewlalis.perfin.view.component;
import com.andrewlalis.perfin.data.CurrencyUtil;
import com.andrewlalis.perfin.data.DateUtil;
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 00a482a..f70e4dc 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -12,6 +12,8 @@ module com.andrewlalis.perfin {
exports com.andrewlalis.perfin to javafx.graphics;
exports com.andrewlalis.perfin.view to javafx.graphics;
exports com.andrewlalis.perfin.model to javafx.graphics;
+
opens com.andrewlalis.perfin.control to javafx.fxml;
- opens com.andrewlalis.perfin.control.component to javafx.fxml;
+ opens com.andrewlalis.perfin.view to javafx.fxml;
+ opens com.andrewlalis.perfin.view.component to javafx.fxml;
}
\ No newline at end of file
diff --git a/src/main/resources/startup-splash-screen.fxml b/src/main/resources/startup-splash-screen.fxml
deleted file mode 100644
index ff47cdf..0000000
--- a/src/main/resources/startup-splash-screen.fxml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/main/resources/transaction-view.fxml b/src/main/resources/transaction-view.fxml
index beecfec..0eb5e2a 100644
--- a/src/main/resources/transaction-view.fxml
+++ b/src/main/resources/transaction-view.fxml
@@ -41,7 +41,6 @@
-