From e3052a2133c82f393874269cadb364c81273c914 Mon Sep 17 00:00:00 2001 From: kske Date: Fri, 6 Nov 2020 17:27:54 +0100 Subject: [PATCH] Reuse the same scene in SceneContext by switching root nodes --- .../java/envoy/client/ui/SceneContext.java | 95 ++++++++++--------- .../main/java/envoy/client/ui/Startup.java | 5 +- 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/client/src/main/java/envoy/client/ui/SceneContext.java b/client/src/main/java/envoy/client/ui/SceneContext.java index dcae7a9..0940c28 100644 --- a/client/src/main/java/envoy/client/ui/SceneContext.java +++ b/client/src/main/java/envoy/client/ui/SceneContext.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.util.Stack; import java.util.logging.Level; -import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.scene.*; import javafx.stage.Stage; @@ -29,11 +28,10 @@ import envoy.client.event.*; public final class SceneContext implements EventListener { private final Stage stage; - private final FXMLLoader loader = new FXMLLoader(); - private final Stack sceneStack = new Stack<>(); - private final Stack controllerStack = new Stack<>(); + private final Stack roots = new Stack<>(); + private final Stack controllers = new Stack<>(); - private static final Settings settings = Settings.getInstance(); + private Scene scene; /** * Initializes the scene context. @@ -49,41 +47,47 @@ public final class SceneContext implements EventListener { /** * Loads a new scene specified by a scene info. * - * @param sceneInfo specifies the scene to load + * @param info specifies the scene to load * @throws RuntimeException if the loading process fails * @since Envoy Client v0.1-beta */ - public void load(SceneInfo sceneInfo) { - EnvoyLog.getLogger(SceneContext.class).log(Level.FINER, "Loading scene " + sceneInfo); - loader.setRoot(null); - loader.setController(null); + public void load(SceneInfo info) { + EnvoyLog.getLogger(SceneContext.class).log(Level.FINER, "Loading scene " + info); try { - final var rootNode = - (Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path)); - final var scene = new Scene(rootNode); - final var controller = loader.getController(); - controllerStack.push(controller); - sceneStack.push(scene); - stage.setScene(scene); + // Load root node and controller + var loader = new FXMLLoader(); + Parent root = loader.load(getClass().getResourceAsStream(info.path)); + Object controller = loader.getController(); + roots.push(root); + controllers.push(controller); + + if (scene == null) { + + // One-time scene initialization + scene = new Scene(root); + applyCSS(); + stage.setScene(scene); + } else { + scene.setRoot(root); + stage.sizeToScene(); + } + + stage.setResizable(info.resizable); + + // Remove previous keyboard shortcuts + scene.getAccelerators().clear(); // Supply the global custom keyboard shortcuts for that scene scene.getAccelerators() - .putAll(GlobalKeyShortcuts.getInstance().getKeyboardShortcuts(sceneInfo)); + .putAll(GlobalKeyShortcuts.getInstance().getKeyboardShortcuts(info)); // Supply the scene specific keyboard shortcuts if (controller instanceof KeyboardMapping) scene.getAccelerators() .putAll(((KeyboardMapping) controller).getKeyboardShortcuts()); - - Platform.runLater(() -> stage.setResizable(sceneInfo.resizable)); - stage.sizeToScene(); - applyCSS(); - stage.show(); - } catch (final IOException e) { - EnvoyLog.getLogger(SceneContext.class).log(Level.SEVERE, - String.format("Could not load scene for %s: ", sceneInfo), e); + } catch (IOException e) { throw new RuntimeException(e); } } @@ -95,29 +99,30 @@ public final class SceneContext implements EventListener { */ public void pop() { - // Pop scene and controller - sceneStack.pop(); - controllerStack.pop(); + // Pop current root node and controller + roots.pop(); + controllers.pop(); // Apply new scene if present - if (!sceneStack.isEmpty()) { - final var newScene = sceneStack.peek(); - stage.setScene(newScene); - applyCSS(); - stage.sizeToScene(); - // If the controller implements the Restorable interface, - // the actions to perform on restoration will be executed here - final var controller = controllerStack.peek(); + if (!roots.isEmpty()) { + scene.setRoot(roots.peek()); + + // Invoke restore if controller is restorable + var controller = controllers.peek(); if (controller instanceof Restorable) ((Restorable) controller).onRestore(); + } else { + + // Remove the current scene entirely + scene = null; + stage.setScene(null); } - stage.show(); } private void applyCSS() { - if (!sceneStack.isEmpty()) { - final var styleSheets = stage.getScene().getStylesheets(); - final var themeCSS = "/css/" + settings.getCurrentTheme() + ".css"; + if (scene != null) { + var styleSheets = scene.getStylesheets(); + var themeCSS = "/css/" + Settings.getInstance().getCurrentTheme() + ".css"; styleSheets.clear(); styleSheets.addAll(getClass().getResource("/css/base.css").toExternalForm(), getClass().getResource(themeCSS).toExternalForm()); @@ -126,8 +131,8 @@ public final class SceneContext implements EventListener { @Event(eventType = Logout.class, priority = 150) private void onLogout() { - sceneStack.clear(); - controllerStack.clear(); + roots.clear(); + controllers.clear(); } @Event(priority = 150, eventType = ThemeChangeEvent.class) @@ -140,7 +145,7 @@ public final class SceneContext implements EventListener { * @return the controller used by the current scene * @since Envoy Client v0.1-beta */ - public T getController() { return (T) controllerStack.peek(); } + public T getController() { return (T) controllers.peek(); } /** * @return the stage in which the scenes are displayed @@ -152,5 +157,5 @@ public final class SceneContext implements EventListener { * @return whether the scene stack is empty * @since Envoy Client v0.2-beta */ - public boolean isEmpty() { return sceneStack.isEmpty(); } + public boolean isEmpty() { return roots.isEmpty(); } } diff --git a/client/src/main/java/envoy/client/ui/Startup.java b/client/src/main/java/envoy/client/ui/Startup.java index 2be9d39..ddbe0ef 100644 --- a/client/src/main/java/envoy/client/ui/Startup.java +++ b/client/src/main/java/envoy/client/ui/Startup.java @@ -93,7 +93,7 @@ public final class Startup extends Application { final var sceneContext = new SceneContext(stage); context.setSceneContext(sceneContext); - // Authenticate with token if present + // Authenticate with token if present or load login scene if (localDB.getAuthToken() != null) { logger.info("Attempting authentication with token..."); localDB.loadUserData(); @@ -102,8 +102,9 @@ public final class Startup extends Application { VERSION, localDB.getLastSync()))) sceneContext.load(SceneInfo.LOGIN_SCENE); } else - // Load login scene sceneContext.load(SceneInfo.LOGIN_SCENE); + + stage.show(); } /**