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