This repository has been archived on 2021-12-05. You can view files and clone it, but cannot push or open issues or pull requests.
envoy/client/src/main/java/envoy/client/ui/SceneContext.java

161 lines
4.0 KiB
Java

package envoy.client.ui;
import java.io.IOException;
import java.util.Stack;
import java.util.logging.Level;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.Stage;
import dev.kske.eventbus.core.*;
import envoy.util.EnvoyLog;
import envoy.client.data.Settings;
import envoy.client.data.shortcuts.*;
import envoy.client.event.*;
/**
* 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.
* <p>
* When a scene is loaded, the style sheet for the current theme is applied to it.
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
*/
public final class SceneContext {
private final Stage stage;
private final Stack<Parent> roots = new Stack<>();
private final Stack<Object> controllers = new Stack<>();
private Scene scene;
/**
* 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().registerListener(this);
}
/**
* Loads a new scene specified by a scene info.
*
* @param info specifies the scene to load
* @throws RuntimeException if the loading process fails
* @since Envoy Client v0.1-beta
*/
public void load(SceneInfo info) {
EnvoyLog.getLogger(SceneContext.class).log(Level.FINER, "Loading scene " + info);
try {
// 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, stage.getWidth(), stage.getHeight());
applyCSS();
stage.setScene(scene);
} else {
scene.setRoot(root);
}
// Remove previous keyboard shortcuts
scene.getAccelerators().clear();
// Supply the global custom keyboard shortcuts for that scene
scene.getAccelerators()
.putAll(GlobalKeyShortcuts.getInstance().getKeyboardShortcuts(info));
// Supply the scene specific keyboard shortcuts
if (controller instanceof KeyboardMapping)
scene.getAccelerators()
.putAll(((KeyboardMapping) controller).getKeyboardShortcuts());
} 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() {
// Pop current root node and controller
roots.pop();
controllers.pop();
// Apply new scene if present
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);
}
}
private void applyCSS() {
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());
}
}
@Event(Logout.class)
@Priority(150)
private void onLogout() {
roots.clear();
controllers.clear();
}
@Event(ThemeChangeEvent.class)
@Priority(150)
private void onThemeChange() {
applyCSS();
}
/**
* @param <T> the type of the controller
* @return the controller used by the current scene
* @since Envoy Client v0.1-beta
*/
public <T> T getController() { return (T) controllers.peek(); }
/**
* @return the stage in which the scenes are displayed
* @since Envoy Client v0.1-beta
*/
public Stage getStage() { return stage; }
/**
* @return whether the scene stack is empty
* @since Envoy Client v0.2-beta
*/
public boolean isEmpty() { return roots.isEmpty(); }
}