From f6dbc5015555ef2d0ee292a5be389c7ee5bc1677 Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Thu, 14 Dec 2023 18:29:03 -0500 Subject: [PATCH] Improved splash screen ergonomics. --- README.md | 9 ++- .../com/andrewlalis/perfin/PerfinApp.java | 39 +++------ .../StartupSplashScreenController.java | 81 ++++++++++++++----- .../perfin/view/SplashScreenStage.java | 38 +++++++++ src/main/resources/main.fxml | 29 +++++-- src/main/resources/startup-splash-screen.fxml | 20 +++-- 6 files changed, 155 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/andrewlalis/perfin/view/SplashScreenStage.java diff --git a/README.md b/README.md index 3bd145c..aa58898 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,11 @@ > *Per*sonal *Fin*ance -Personal accounting desktop app to track your finances using common file formats. +A personal accounting desktop app to track your finances using an approachable +interface and interoperable file formats for maximum compatibility. + +## About Perfin + +Perfin is a desktop app built with Java 21 and JavaFX, using the SQLite3 +database for most data storage. It's intended to be used by individuals to +track their finances across multiple accounts (savings, checking, credit, etc.). diff --git a/src/main/java/com/andrewlalis/perfin/PerfinApp.java b/src/main/java/com/andrewlalis/perfin/PerfinApp.java index 74a6e44..8dfb68d 100644 --- a/src/main/java/com/andrewlalis/perfin/PerfinApp.java +++ b/src/main/java/com/andrewlalis/perfin/PerfinApp.java @@ -1,23 +1,28 @@ package com.andrewlalis.perfin; +import com.andrewlalis.perfin.view.SplashScreenStage; import javafx.application.Application; import javafx.stage.Stage; -import javafx.stage.StageStyle; +import java.nio.file.Path; + +/** + * The class from which the JavaFX-based application starts. + */ public class PerfinApp extends Application { + public static final Path APP_DIR = Path.of(System.getProperty("user.home", "."), ".perfin"); + public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { + SplashScreenStage splashStage = new SplashScreenStage("Loading", SceneUtil.load("/startup-splash-screen.fxml")); + splashStage.show(); initMainScreen(stage); - Stage splashStage = showStartupSplashScreen(); - // Once the splash stage is hidden, show the main stage. - splashStage.showingProperty().not().addListener((v, old, hidden) -> { - if (hidden) { - showMainScreen(stage); - } + splashStage.stateProperty().addListener((v, oldState, state) -> { + if (state == SplashScreenStage.State.DONE) stage.show(); }); } @@ -26,24 +31,4 @@ public class PerfinApp extends Application { stage.setScene(SceneUtil.load("/main.fxml")); stage.setTitle("Perfin"); } - - private void showMainScreen(Stage stage) { - System.out.println("Showing the main application."); -// stage.setMaximized(true); - stage.show(); - } - - /** - * Shows a startup "splash" screen for a short time. - * @return The stage in which the splash screen is shown. - */ - private Stage showStartupSplashScreen() { - Stage stage = new Stage(); - stage.initStyle(StageStyle.UNDECORATED); - stage.setScene(SceneUtil.load("/startup-splash-screen.fxml")); - stage.setTitle("Loading"); - stage.setResizable(false); - stage.show(); - return stage; - } } \ No newline at end of file diff --git a/src/main/java/com/andrewlalis/perfin/control/StartupSplashScreenController.java b/src/main/java/com/andrewlalis/perfin/control/StartupSplashScreenController.java index b8a37aa..efdc7e8 100644 --- a/src/main/java/com/andrewlalis/perfin/control/StartupSplashScreenController.java +++ b/src/main/java/com/andrewlalis/perfin/control/StartupSplashScreenController.java @@ -1,36 +1,81 @@ package com.andrewlalis.perfin.control; +import com.andrewlalis.perfin.view.SplashScreenStage; import javafx.application.Platform; import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.stage.Stage; +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 Label content; + public BorderPane sceneRoot; + @FXML + public TextArea content; @FXML public void initialize() { - content.setText("Loading Perfin..."); Thread.ofVirtual().start(() -> { try { - Thread.sleep(1000); + printlnLater("Initializing application files..."); + if (!initAppDir()) { + Platform.runLater(() -> getSplashStage().setError()); + return; + } - Platform.runLater(() -> content.setText("Still loading...")); - Thread.sleep(1000); + printlnLater("Perfin initialized. Starting the app now."); + Thread.sleep(2000); - Platform.runLater(() -> content.setText("Almost done...")); - Thread.sleep(1000); - - Platform.runLater(() -> content.setText("Done!")); - Thread.sleep(500); - - System.out.println("Closing splash screen..."); - Stage stage = (Stage) content.getScene().getWindow(); - Platform.runLater(stage::close); - } catch (InterruptedException e) { - throw new RuntimeException(e); + 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/view/SplashScreenStage.java b/src/main/java/com/andrewlalis/perfin/view/SplashScreenStage.java new file mode 100644 index 0000000..159b0c7 --- /dev/null +++ b/src/main/java/com/andrewlalis/perfin/view/SplashScreenStage.java @@ -0,0 +1,38 @@ +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/resources/main.fxml b/src/main/resources/main.fxml index 2af8d64..7776e5a 100644 --- a/src/main/resources/main.fxml +++ b/src/main/resources/main.fxml @@ -1,11 +1,24 @@ - - - + + + + - - + minWidth="600.0" minHeight="400.0"> + + + + + + + + + + + + + + diff --git a/src/main/resources/startup-splash-screen.fxml b/src/main/resources/startup-splash-screen.fxml index e88644c..0a55d4d 100644 --- a/src/main/resources/startup-splash-screen.fxml +++ b/src/main/resources/startup-splash-screen.fxml @@ -1,11 +1,17 @@ + - + + + + + + + - - +
+