From ec72b6fc6714be7aed912434655776b590c71d0b Mon Sep 17 00:00:00 2001 From: kske Date: Sat, 6 Jun 2020 18:30:09 +0200 Subject: [PATCH] Externalized scene loading and management into SceneContext --- .../envoy/client/ui/ChatSceneController.java | 28 ++--- .../java/envoy/client/ui/SceneContext.java | 98 +++++++++++++++++ src/main/java/envoy/client/ui/Startup.java | 104 ++---------------- .../ui/settings/SettingsSceneController.java | 15 ++- 4 files changed, 129 insertions(+), 116 deletions(-) create mode 100644 src/main/java/envoy/client/ui/SceneContext.java diff --git a/src/main/java/envoy/client/ui/ChatSceneController.java b/src/main/java/envoy/client/ui/ChatSceneController.java index d2a77d9..56fbe2c 100644 --- a/src/main/java/envoy/client/ui/ChatSceneController.java +++ b/src/main/java/envoy/client/ui/ChatSceneController.java @@ -11,7 +11,6 @@ import javafx.fxml.FXML; import javafx.scene.control.*; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; -import javafx.scene.layout.VBox; import envoy.client.data.Chat; import envoy.client.data.LocalDB; @@ -56,14 +55,13 @@ public final class ChatSceneController { @FXML private TextArea messageTextArea; - private LocalDB localDB; - private Client client; - private WriteProxy writeProxy; + private LocalDB localDB; + private Client client; + private WriteProxy writeProxy; + private SceneContext sceneContext; private Chat currentChat; - private Startup startup; - private static final Settings settings = Settings.getInstance(); private static final EventBus eventBus = EventBus.getInstance(); private static final Logger logger = EnvoyLog.getLogger(ChatSceneController.class); @@ -97,11 +95,11 @@ public final class ChatSceneController { eventBus.register(UserStatusChangeEvent.class, e -> Platform.runLater(() -> userList.refresh())); } - void initializeData(Startup startup, LocalDB localDB, Client client, WriteProxy writeProxy) { - this.startup = startup; - this.localDB = localDB; - this.client = client; - this.writeProxy = writeProxy; + void initializeData(SceneContext sceneContext, LocalDB localDB, Client client, WriteProxy writeProxy) { + this.sceneContext = sceneContext; + this.localDB = localDB; + this.client = client; + this.writeProxy = writeProxy; // TODO: handle offline mode userList.setItems(FXCollections.observableList(localDB.getUser().getContacts().stream().collect(Collectors.toList()))); @@ -131,8 +129,12 @@ public final class ChatSceneController { @FXML private void settingsButtonClicked() { - startup.changeScene("/fxml/SettingsScene.fxml", new VBox(), true); - Platform.runLater(() -> ((SettingsSceneController) startup.getCurrentController()).initializeData(startup)); + try { + sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE); + sceneContext.getController().initializeData(sceneContext); + } catch (IOException e) { + e.printStackTrace(); + } } @FXML diff --git a/src/main/java/envoy/client/ui/SceneContext.java b/src/main/java/envoy/client/ui/SceneContext.java new file mode 100644 index 0000000..1644955 --- /dev/null +++ b/src/main/java/envoy/client/ui/SceneContext.java @@ -0,0 +1,98 @@ +package envoy.client.ui; + +import java.io.IOException; +import java.util.Stack; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import envoy.client.data.Settings; +import envoy.client.event.ThemeChangeEvent; +import envoy.event.EventBus; + +/** + * Project: envoy-client
+ * File: SceneContext.java
+ * Created: 06.06.2020
+ * + * @author Kai S. K. Engelbart + * @since Envoy Client v0.1-beta + */ +public final class SceneContext { + + static enum SceneInfo { + + CHAT_SCENE("/fxml/ChatScene.fxml"), SETTINGS_SCENE("/fxml/SettingsScene.fxml"), CONTACT_SEARCH_SCENE("/fxml/ContactSearchScene.fxml"); + + 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<>(); + + private static final Settings settings = Settings.getInstance(); + + /** + * Initializes the scene context. + * + * @param stage the stage in which scenes will be displayed + * @since Envoy Client v0.1-beta + */ + public SceneContext(Stage stage) { + this.stage = stage; + EventBus.getInstance().register(ThemeChangeEvent.class, theme -> applyCSS()); + } + + /** + * Loads a new scene specified by a scene info. + * + * @param sceneInfo specifies the scene to load + * @throws IOException if the loading process fails + * @since Envoy Client v0.1-beta + */ + public void load(SceneInfo sceneInfo) throws IOException { + loader.setRoot(null); + loader.setController(null); + + final var rootNode = (Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path)); + final var scene = new Scene(rootNode); + + sceneStack.push(scene); + stage.setScene(scene); + applyCSS(); + stage.show(); + } + + /** + * Removes the current scene and displays the previous one. + * + * @since Envoy Client v0.1-beta + */ + public void pop() { + sceneStack.pop(); + stage.setScene(sceneStack.peek()); + applyCSS(); + stage.show(); + } + + private void applyCSS() { + if (!sceneStack.isEmpty()) { + final var styleSheets = stage.getScene().getStylesheets(); + final var themeCSS = "/css/" + (settings.isUsingDefaultTheme() ? settings.getCurrentThemeName() : "custom") + ".css"; + styleSheets.clear(); + styleSheets.addAll(getClass().getResource("/css/base.css").toExternalForm(), getClass().getResource(themeCSS).toExternalForm()); + } + } + + /** + * @param the type of the controller + * @return the controller used by the current scene + * @since Envoy Client v0.1-beta + */ + public T getController() { return loader.getController(); } +} diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index b6de986..acca4f1 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -8,23 +8,16 @@ import java.util.logging.Level; import java.util.logging.Logger; import javafx.application.Application; -import javafx.application.Platform; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Pane; import javafx.stage.Stage; import envoy.client.data.*; -import envoy.client.event.ThemeChangeEvent; import envoy.client.net.Client; import envoy.client.net.WriteProxy; import envoy.data.Message; import envoy.data.User; import envoy.data.User.UserStatus; -import envoy.event.EventBus; import envoy.exception.EnvoyException; import envoy.util.EnvoyLog; @@ -43,20 +36,14 @@ public final class Startup extends Application { private WriteProxy writeProxy; private Cache cache; - private FXMLLoader loader = new FXMLLoader(); - private Stage stage; - private Scene previousScene; - - private static final Settings settings = Settings.getInstance(); - private static final ClientConfig config = ClientConfig.getInstance(); - private static final Logger logger = EnvoyLog.getLogger(Startup.class); + private static final ClientConfig config = ClientConfig.getInstance(); + private static final Logger logger = EnvoyLog.getLogger(Startup.class); /** * {@inheritDoc} */ @Override public void start(Stage stage) throws Exception { - this.stage = stage; try { // Load the configuration from client.properties first final Properties properties = new Properties(); @@ -133,72 +120,17 @@ public final class Startup extends Application { .forEach(u -> u.setStatus(UserStatus.OFFLINE)); // Prepare stage and load ChatScene - changeScene("/fxml/ChatScene.fxml", new GridPane(), false); - Platform.runLater(() -> { ((ChatSceneController) loader.getController()).initializeData(this, localDB, client, writeProxy); }); + final var sceneContext = new SceneContext(stage); + sceneContext.load(SceneContext.SceneInfo.CHAT_SCENE); + sceneContext.getController().initializeData(sceneContext, localDB, client, writeProxy); stage.setTitle("Envoy"); stage.getIcons().add(IconUtil.load("/icons/envoy_logo.png")); stage.show(); - // TODO: Add capability to change custom CSS. In case of switching to a default - // theme, no further action is required - EventBus.getInstance().register(ThemeChangeEvent.class, theme -> applyCSS()); // Relay unread messages from cache if (cache != null && client.isOnline()) cache.relay(); } - /** - * Changes the scene of the stage. - * - * @param the type of the layout to use - * @param fxmlLocation the location of the fxml file - * @param layout the layout to use - * @param savePrevious if true, the previous stage will be stored in this - * instance of Startup, else the variable storing it will be - * set to null - * @since Envoy Client v0.1-beta - */ - public void changeScene(String fxmlLocation, T layout, boolean savePrevious) { - Platform.runLater(() -> { - try { - // Clearing the loader so that a new Scene can be initialised - loader = new FXMLLoader(); - final var rootNode = loader.load(getClass().getResourceAsStream(fxmlLocation)); - final var scene = new Scene(rootNode); - previousScene = savePrevious ? stage.getScene() : null; - // Setting the visual appearance - stage.setScene(scene); - applyCSS(); - stage.show(); - } catch (final IOException e) { - new Alert(AlertType.ERROR, "The screen could not be updated due to reasons. (...bad programming...)"); - System.err.println("input: FXMLLocation: " + fxmlLocation); - e.printStackTrace(); - logger.severe("Something happened (while loading the new scene from " + fxmlLocation + ")"); - } - }); - } - - /** - * Changes the visual scene back to the saved value. The currently active scene - * can be saved, but must not be. - * - * @param storeCurrent the old scene to store, if wanted. Can be null - * @since Envoy Client v0.1-beta - */ - public void restoreScene(boolean storeCurrent) { - Platform.runLater(() -> { - if (previousScene == null) throw new IllegalStateException("Someone tried restoring a null scene. (Something happened)"); - else { - // switching previous and current - final var temp = storeCurrent ? stage.getScene() : null; - stage.setScene(previousScene); - applyCSS(); - previousScene = temp; - stage.show(); - } - }); - } - /** * {@inheritDoc} */ @@ -218,29 +150,11 @@ public final class Startup extends Application { } /** - * @return the controller of the current scene or a {@link NullPointerException} - * if there is none + * Starts the application. + * + * @param args the command line arguments are processed by the + * {@link ClientConfig} * @since Envoy Client v0.1-beta */ - public Object getCurrentController() { - if (loader.getController() == null) throw new NullPointerException("Cannot deliver current controller as its undefined (duh!)"); - else return loader.getController(); - } - - /** - * Sets the CSS files used for each scene. Should be called when the theme - * changes. - * - * @since Envoy Client v0.1-beta - */ - public void applyCSS() { - final var styleSheets = stage.getScene().getStylesheets(); - styleSheets.clear(); - styleSheets.add(getClass().getResource("/css/base.css").toExternalForm()); - styleSheets.add(getClass().getResource("/css/" + (settings.isUsingDefaultTheme() ? settings.getCurrentThemeName() : "custom") + ".css") - .toExternalForm()); - } - - @SuppressWarnings("javadoc") public static void main(String[] args) { launch(args); } } diff --git a/src/main/java/envoy/client/ui/settings/SettingsSceneController.java b/src/main/java/envoy/client/ui/settings/SettingsSceneController.java index e3cc4fb..795a57f 100644 --- a/src/main/java/envoy/client/ui/settings/SettingsSceneController.java +++ b/src/main/java/envoy/client/ui/settings/SettingsSceneController.java @@ -3,7 +3,7 @@ package envoy.client.ui.settings; import javafx.fxml.FXML; import javafx.scene.control.*; -import envoy.client.ui.Startup; +import envoy.client.ui.SceneContext; /** * Project: envoy-client
@@ -15,20 +15,19 @@ import envoy.client.ui.Startup; */ public class SettingsSceneController { - private Startup startup; @FXML - private ListView settingsList; + private ListView settingsList; @FXML private TitledPane titledPane; + private SceneContext sceneContext; + /** - * initializes the object needed to reset the scene - * - * @param startup the instance of startup that stores the stage + * @param sceneContext enables the user to return to the chat scene * @since Envoy Client v0.1-beta */ - public void initializeData(Startup startup) { this.startup = startup; } + public void initializeData(SceneContext sceneContext) { this.sceneContext = sceneContext; } @FXML private void initialize() { @@ -54,5 +53,5 @@ public class SettingsSceneController { } @FXML - private void backButtonClicked() { startup.restoreScene(false); } + private void backButtonClicked() { sceneContext.pop(); } }