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; /** * Manages a stack of scenes. The most recently added scene is displayed inside * a stage. When a scene is removed from the stack, its predecessor is * displayed. *

* When a scene is loaded, the style sheet for the current theme is applied to * it. *

* 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 { /** * Contains information about different scenes and their FXML resource files. * * @author Kai S. K. Engelbart * @since Envoy Client v0.1-beta */ public static enum SceneInfo { /** * The main scene in which chats are displayed. * * @since Envoy Client v0.1-beta */ CHAT_SCENE("/fxml/ChatScene.fxml"), /** * The scene in which settings are displayed. * * @since Envoy Client v0.1-beta */ SETTINGS_SCENE("/fxml/SettingsScene.fxml"), /** * The scene in which the contact search is displayed. * * @since Envoy Client v0.1-beta */ CONTACT_SEARCH_SCENE("/fxml/ContactSearchScene.fxml"), /** * The scene in which the group creation screen is displayed. * * @since Envoy Client v0.1-beta */ GROUP_CREATION_SCENE("/fxml/GroupCreationScene.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<>(); 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 RuntimeException if the loading process fails * @since Envoy Client v0.1-beta */ public void load(SceneInfo sceneInfo) { loader.setRoot(null); loader.setController(null); try { final var rootNode = (Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path)); final var scene = new Scene(rootNode); sceneStack.push(scene); stage.setScene(scene); applyCSS(); stage.sizeToScene(); stage.show(); } catch (IOException e) { throw new RuntimeException(e); } } /** * Removes the current scene and displays the previous one. * * @since Envoy Client v0.1-beta */ public void pop() { sceneStack.pop(); if (!sceneStack.isEmpty()) { stage.setScene(sceneStack.peek()); applyCSS(); stage.sizeToScene(); } stage.show(); } private void applyCSS() { if (!sceneStack.isEmpty()) { final var styleSheets = stage.getScene().getStylesheets(); final var themeCSS = "/css/" + settings.getCurrentTheme() + ".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(); } /** * @return the stage in which the scenes are displayed * @since Envoy Client v0.1-beta */ public Stage getStage() { return stage; } }