From 0ce8b0c89d92ca04a9826270a1d254bfa5d904b3 Mon Sep 17 00:00:00 2001 From: kske Date: Fri, 6 Nov 2020 08:58:13 +0100 Subject: [PATCH 1/5] Move SceneInfo to separate file --- .../data/shortcuts/EnvoyShortcutConfig.java | 2 +- .../data/shortcuts/GlobalKeyShortcuts.java | 2 +- .../java/envoy/client/ui/SceneContext.java | 39 ------------------ .../main/java/envoy/client/ui/SceneInfo.java | 40 +++++++++++++++++++ .../main/java/envoy/client/ui/Startup.java | 3 +- .../ui/chatscene/ChatSceneCommands.java | 6 +-- .../envoy/client/ui/controller/ChatScene.java | 6 ++- .../main/java/envoy/client/util/UserUtil.java | 2 +- 8 files changed, 51 insertions(+), 49 deletions(-) create mode 100644 client/src/main/java/envoy/client/ui/SceneInfo.java diff --git a/client/src/main/java/envoy/client/data/shortcuts/EnvoyShortcutConfig.java b/client/src/main/java/envoy/client/data/shortcuts/EnvoyShortcutConfig.java index 44504e1..1eb524d 100644 --- a/client/src/main/java/envoy/client/data/shortcuts/EnvoyShortcutConfig.java +++ b/client/src/main/java/envoy/client/data/shortcuts/EnvoyShortcutConfig.java @@ -6,7 +6,7 @@ import envoy.data.User.UserStatus; import envoy.client.data.Context; import envoy.client.helper.ShutdownHelper; -import envoy.client.ui.SceneContext.SceneInfo; +import envoy.client.ui.SceneInfo; import envoy.client.util.UserUtil; /** diff --git a/client/src/main/java/envoy/client/data/shortcuts/GlobalKeyShortcuts.java b/client/src/main/java/envoy/client/data/shortcuts/GlobalKeyShortcuts.java index 05ef3b7..3c4da5e 100644 --- a/client/src/main/java/envoy/client/data/shortcuts/GlobalKeyShortcuts.java +++ b/client/src/main/java/envoy/client/data/shortcuts/GlobalKeyShortcuts.java @@ -4,7 +4,7 @@ import java.util.*; import javafx.scene.input.KeyCombination; -import envoy.client.ui.SceneContext.SceneInfo; +import envoy.client.ui.SceneInfo; /** * Contains all keyboard shortcuts used throughout the application. diff --git a/client/src/main/java/envoy/client/ui/SceneContext.java b/client/src/main/java/envoy/client/ui/SceneContext.java index 8e42c33..b86d843 100644 --- a/client/src/main/java/envoy/client/ui/SceneContext.java +++ b/client/src/main/java/envoy/client/ui/SceneContext.java @@ -28,45 +28,6 @@ import envoy.client.event.*; */ public final class SceneContext implements EventListener { - /** - * Contains information about different scenes and their FXML resource files. - * - * @author Kai S. K. Engelbart - * @since Envoy Client v0.1-beta - */ - public enum SceneInfo { - - /** - * The main scene in which the chat screen is displayed. - * - * @since Envoy Client v0.1-beta - */ - CHAT_SCENE("/fxml/ChatScene.fxml"), - - /** - * The scene in which the settings screen is displayed. - * - * @since Envoy Client v0.1-beta - */ - SETTINGS_SCENE("/fxml/SettingsScene.fxml"), - - /** - * The scene in which the login screen is displayed. - * - * @since Envoy Client v0.1-beta - */ - LOGIN_SCENE("/fxml/LoginScene.fxml"); - - /** - * The path to the FXML resource. - */ - public final String path; - - SceneInfo(String path) { - this.path = path; - } - } - private final Stage stage; private final FXMLLoader loader = new FXMLLoader(); private final Stack sceneStack = new Stack<>(); diff --git a/client/src/main/java/envoy/client/ui/SceneInfo.java b/client/src/main/java/envoy/client/ui/SceneInfo.java new file mode 100644 index 0000000..801e947 --- /dev/null +++ b/client/src/main/java/envoy/client/ui/SceneInfo.java @@ -0,0 +1,40 @@ +package envoy.client.ui; + +/** + * Contains information about different scenes and their FXML resource files. + * + * @author Kai S. K. Engelbart + * @since Envoy Client v0.1-beta + */ +public enum SceneInfo { + + /** + * The main scene in which the chat screen is displayed. + * + * @since Envoy Client v0.1-beta + */ + CHAT_SCENE("/fxml/ChatScene.fxml"), + + /** + * The scene in which the settings screen is displayed. + * + * @since Envoy Client v0.1-beta + */ + SETTINGS_SCENE("/fxml/SettingsScene.fxml"), + + /** + * The scene in which the login screen is displayed. + * + * @since Envoy Client v0.1-beta + */ + LOGIN_SCENE("/fxml/LoginScene.fxml"); + + /** + * The path to the FXML resource. + */ + public final String path; + + SceneInfo(String path) { + this.path = path; + } +} \ No newline at end of file diff --git a/client/src/main/java/envoy/client/ui/Startup.java b/client/src/main/java/envoy/client/ui/Startup.java index 18468e3..2be9d39 100644 --- a/client/src/main/java/envoy/client/ui/Startup.java +++ b/client/src/main/java/envoy/client/ui/Startup.java @@ -20,7 +20,6 @@ import envoy.client.data.*; import envoy.client.data.shortcuts.EnvoyShortcutConfig; import envoy.client.helper.ShutdownHelper; import envoy.client.net.Client; -import envoy.client.ui.SceneContext.SceneInfo; import envoy.client.ui.controller.LoginScene; import envoy.client.util.IconUtil; @@ -226,7 +225,7 @@ public final class Startup extends Application { // Load ChatScene stage.setMinHeight(400); stage.setMinWidth(843); - context.getSceneContext().load(SceneContext.SceneInfo.CHAT_SCENE); + context.getSceneContext().load(SceneInfo.CHAT_SCENE); stage.centerOnScreen(); // Exit or minimize the stage when a close request occurs diff --git a/client/src/main/java/envoy/client/ui/chatscene/ChatSceneCommands.java b/client/src/main/java/envoy/client/ui/chatscene/ChatSceneCommands.java index 680074a..cba5b38 100644 --- a/client/src/main/java/envoy/client/ui/chatscene/ChatSceneCommands.java +++ b/client/src/main/java/envoy/client/ui/chatscene/ChatSceneCommands.java @@ -15,7 +15,7 @@ import envoy.util.EnvoyLog; import envoy.client.data.Context; import envoy.client.data.commands.*; import envoy.client.helper.ShutdownHelper; -import envoy.client.ui.SceneContext.SceneInfo; +import envoy.client.ui.SceneInfo; import envoy.client.ui.controller.ChatScene; import envoy.client.util.*; @@ -32,7 +32,7 @@ public final class ChatSceneCommands { private final SystemCommandBuilder builder = new SystemCommandBuilder(messageTextAreaCommands); - private static final String messageDependantCommandDescription = + private static final String messageDependentCommandDescription = " the given message. Use s/S to use the selected message. Otherwise expects a number relative to the uppermost completely visible message."; /** @@ -141,7 +141,7 @@ public final class ChatSceneCommands { else useRelativeMessage(command, action, additionalCheck, positionalArgument, false); }).setDefaults("s").setNumberOfArguments(1) - .setDescription(description.concat(messageDependantCommandDescription)).build(command); + .setDescription(description.concat(messageDependentCommandDescription)).build(command); } private void selectionNeighbor(Consumer action, Predicate additionalCheck, diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index 8dc48ef..74499c2 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -1,5 +1,7 @@ package envoy.client.ui.controller; +import static envoy.client.ui.SceneInfo.SETTINGS_SCENE; + import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; import java.io.*; @@ -32,7 +34,7 @@ import envoy.data.*; import envoy.data.Attachment.AttachmentType; import envoy.data.Message.MessageStatus; import envoy.event.*; -import envoy.event.contact.*; +import envoy.event.contact.UserOperation; import envoy.exception.EnvoyException; import envoy.util.EnvoyLog; @@ -445,7 +447,7 @@ public final class ChatScene implements EventListener, Restorable, KeyboardMappi */ @FXML private void settingsButtonClicked() { - sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE); + sceneContext.load(SETTINGS_SCENE); } /** diff --git a/client/src/main/java/envoy/client/util/UserUtil.java b/client/src/main/java/envoy/client/util/UserUtil.java index bc7d7fe..247866e 100644 --- a/client/src/main/java/envoy/client/util/UserUtil.java +++ b/client/src/main/java/envoy/client/util/UserUtil.java @@ -16,7 +16,7 @@ import envoy.util.EnvoyLog; import envoy.client.data.Context; import envoy.client.event.*; import envoy.client.helper.*; -import envoy.client.ui.SceneContext.SceneInfo; +import envoy.client.ui.SceneInfo; import envoy.client.ui.controller.ChatScene; /** From 4d4865570df849cb2b1b0a33f7984f8be9021cf4 Mon Sep 17 00:00:00 2001 From: kske Date: Fri, 6 Nov 2020 09:21:59 +0100 Subject: [PATCH 2/5] Make resizability a property of SceneInfo This removes a check hard coded into SceneContext that sets LoginScene to not resizable. --- .../src/main/java/envoy/client/ui/SceneContext.java | 5 +---- client/src/main/java/envoy/client/ui/SceneInfo.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/client/src/main/java/envoy/client/ui/SceneContext.java b/client/src/main/java/envoy/client/ui/SceneContext.java index b86d843..dcae7a9 100644 --- a/client/src/main/java/envoy/client/ui/SceneContext.java +++ b/client/src/main/java/envoy/client/ui/SceneContext.java @@ -77,11 +77,8 @@ public final class SceneContext implements EventListener { scene.getAccelerators() .putAll(((KeyboardMapping) controller).getKeyboardShortcuts()); - // The LoginScene is the only scene not intended to be resized - // As strange as it seems, this is needed as otherwise the LoginScene won't be - // displayed on some OS (...Debian...) + Platform.runLater(() -> stage.setResizable(sceneInfo.resizable)); stage.sizeToScene(); - Platform.runLater(() -> stage.setResizable(sceneInfo != SceneInfo.LOGIN_SCENE)); applyCSS(); stage.show(); } catch (final IOException e) { diff --git a/client/src/main/java/envoy/client/ui/SceneInfo.java b/client/src/main/java/envoy/client/ui/SceneInfo.java index 801e947..9f851e6 100644 --- a/client/src/main/java/envoy/client/ui/SceneInfo.java +++ b/client/src/main/java/envoy/client/ui/SceneInfo.java @@ -27,14 +27,24 @@ public enum SceneInfo { * * @since Envoy Client v0.1-beta */ - LOGIN_SCENE("/fxml/LoginScene.fxml"); + LOGIN_SCENE("/fxml/LoginScene.fxml", false); /** * The path to the FXML resource. */ public final String path; + /** + * Whether the scene should be resizable. + */ + public final boolean resizable; + SceneInfo(String path) { + this(path, true); + } + + SceneInfo(String path, boolean resizable) { this.path = path; + this.resizable = resizable; } } \ No newline at end of file From e3052a2133c82f393874269cadb364c81273c914 Mon Sep 17 00:00:00 2001 From: kske Date: Fri, 6 Nov 2020 17:27:54 +0100 Subject: [PATCH 3/5] 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(); } /** From 67ebc6be839c1cfd7caf5253e697064bb6864865 Mon Sep 17 00:00:00 2001 From: kske Date: Fri, 20 Nov 2020 14:01:00 +0100 Subject: [PATCH 4/5] Initialize scene with stage size in SceneContext This apparently fixes the rendering issues when switching scenes, while keeping the stage size constant (unless the user resizes the stage). --- .../main/java/envoy/client/ui/SceneContext.java | 3 +-- client/src/main/resources/fxml/ChatScene.fxml | 10 ++++------ client/src/main/resources/fxml/SettingsScene.fxml | 15 +++++++++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/client/src/main/java/envoy/client/ui/SceneContext.java b/client/src/main/java/envoy/client/ui/SceneContext.java index 0940c28..5af7fd0 100644 --- a/client/src/main/java/envoy/client/ui/SceneContext.java +++ b/client/src/main/java/envoy/client/ui/SceneContext.java @@ -66,12 +66,11 @@ public final class SceneContext implements EventListener { if (scene == null) { // One-time scene initialization - scene = new Scene(root); + scene = new Scene(root, stage.getWidth(), stage.getHeight()); applyCSS(); stage.setScene(scene); } else { scene.setRoot(root); - stage.sizeToScene(); } stage.setResizable(info.resizable); diff --git a/client/src/main/resources/fxml/ChatScene.fxml b/client/src/main/resources/fxml/ChatScene.fxml index f293d3e..88d5d0a 100644 --- a/client/src/main/resources/fxml/ChatScene.fxml +++ b/client/src/main/resources/fxml/ChatScene.fxml @@ -24,8 +24,8 @@ @@ -57,8 +57,7 @@ - + @@ -156,8 +155,7 @@ - + diff --git a/client/src/main/resources/fxml/SettingsScene.fxml b/client/src/main/resources/fxml/SettingsScene.fxml index 50a90ab..edca898 100644 --- a/client/src/main/resources/fxml/SettingsScene.fxml +++ b/client/src/main/resources/fxml/SettingsScene.fxml @@ -7,11 +7,16 @@ - + - + @@ -22,7 +27,8 @@ - + @@ -32,7 +38,8 @@ -