Externalized scene loading and management into SceneContext
This commit is contained in:
		@@ -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.<SettingsSceneController>getController().initializeData(sceneContext);
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								src/main/java/envoy/client/ui/SceneContext.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/main/java/envoy/client/ui/SceneContext.java
									
									
									
									
									
										Normal file
									
								
							@@ -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: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>SceneContext.java</strong><br>
 | 
			
		||||
 * Created: <strong>06.06.2020</strong><br>
 | 
			
		||||
 * 
 | 
			
		||||
 * @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<Scene>	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 <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 loader.getController(); }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<Message>	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.<ChatSceneController>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 <T>          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 <T extends Pane> 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.<T>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); }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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: <strong>envoy-client</strong><br>
 | 
			
		||||
@@ -15,20 +15,19 @@ import envoy.client.ui.Startup;
 | 
			
		||||
 */
 | 
			
		||||
public class SettingsSceneController {
 | 
			
		||||
 | 
			
		||||
	private Startup					startup;
 | 
			
		||||
	@FXML
 | 
			
		||||
	private ListView<SettingsPane>	settingsList;
 | 
			
		||||
	private ListView<SettingsPane> 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(); }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user