Coverage Summary for Class: GUI (it.polimi.ingsw.gc14.View.GUI)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
| GUI |
0%
(0/1)
|
0%
(0/16)
|
0%
(0/22)
|
0%
(0/110)
|
package it.polimi.ingsw.gc14.View.GUI;
import it.polimi.ingsw.gc14.Controller.ClientController;
import it.polimi.ingsw.gc14.ErrorType;
import it.polimi.ingsw.gc14.Model.MiniModel;
import it.polimi.ingsw.gc14.View.IView;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
import static it.polimi.ingsw.gc14.Model.GamePackage.GameStages.ENDED;
import static it.polimi.ingsw.gc14.Model.GamePackage.GameStages.TOTEM_CHOICE;
/**
* JavaFX-based GUI implementation of {@link it.polimi.ingsw.gc14.View.IView IView}.
*
* <p>Manages the primary stage and switches between the login, totem choice,
* main game, and leaderboard scenes based on the current game state.
*/
public class GUI extends Application implements IView {
/** The primary JavaFX stage. */
private Stage primaryStage;
/** Latest mini model received from the server. */
private volatile MiniModel miniModel;
/** FXML loader for the login scene. */
private FXMLLoader loaderLogin;
/** The login scene. */
private Scene loginScene;
/** Controller for the login FXML scene. */
private LoginFXMLController controllerLogin;
/** FXML loader for the totem selection scene. */
private FXMLLoader loaderTotem;
/** The totem selection scene. */
private Scene totemScene;
/** Controller for the totem FXML scene. */
private TotemFXMLController controllerTotem;
/** FXML loader for the main game scene. */
private FXMLLoader loaderMain;
/** The main game scene. */
private Scene mainScene;
/** Controller for the main FXML scene. */
private MainFXMLController controllerMain;
/** FXML loader for the leaderboard scene. */
private FXMLLoader loaderLeaderboard;
/** The end-of-game leaderboard scene. */
private Scene leaderboardScene;
/** Controller for the leaderboard FXML scene. */
private LeaderboardFXMLController controllerLeaderboard;
/** Client controller injected before JavaFX startup. */
private ClientController controller;
/** When {@code true}, the app re-enters fullscreen automatically after ESC is pressed. */
private boolean autoReenterFullscreen = true;
/** Guards against overlapping fade transitions. */
private boolean isFading = false;
/**
* JavaFX entry point: loads all FXML scenes, wires up controllers,
* configures fullscreen behavior, and shows the login scene.
*
* @param stage the primary stage provided by the JavaFX runtime.
* @throws Exception if any FXML resource cannot be loaded.
*/
@Override
public void start(Stage stage) throws Exception {
this.primaryStage = stage;
loaderLogin = new FXMLLoader(getClass().getResource("/GUIScene/login.fxml"));
loginScene = new Scene(loaderLogin.load());
controllerLogin = loaderLogin.getController();
controllerLogin.setController(controller);
loaderTotem = new FXMLLoader(getClass().getResource("/GUIScene/totem.fxml"));
totemScene = new Scene(loaderTotem.load());
controllerTotem = loaderTotem.getController();
controllerTotem.setController(controller);
loaderMain = new FXMLLoader(getClass().getResource("/GUIScene/main.fxml"));
mainScene = new Scene(loaderMain.load());
controllerMain = loaderMain.getController();
controllerMain.setController(controller);
loaderLeaderboard = new FXMLLoader(getClass().getResource("/GUIScene/standing.fxml"));
leaderboardScene = new Scene(loaderLeaderboard.load());
controllerLeaderboard = loaderLeaderboard.getController();
controllerLeaderboard.setController(controller, () -> {
controllerLogin.updateLoginButton(true);
controllerLogin.showError("");
fadeToScene(loginScene);
});
wrapScene(loginScene);
wrapScene(totemScene);
wrapScene(mainScene);
wrapScene(leaderboardScene);
FireParticleSystem fireParticles = new FireParticleSystem(mainScene);
((StackPane) mainScene.getRoot()).getChildren().add(fireParticles.getPane());
fireParticles.start();
primaryStage.setScene(loginScene);
Rectangle2D tmp = Screen.getPrimary().getVisualBounds();
primaryStage.setWidth(tmp.getWidth());
primaryStage.setHeight(tmp.getHeight());
primaryStage.setX(tmp.getMinX());
primaryStage.setY(tmp.getMinY());
primaryStage.setResizable(false);
primaryStage.setFullScreenExitHint("");
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
primaryStage.fullScreenProperty().addListener((obs, was, isNow) -> {
if (!isNow && autoReenterFullscreen) {
Platform.runLater(() -> primaryStage.setFullScreen(true));
}
});
primaryStage.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ESCAPE) {
autoReenterFullscreen = false;
primaryStage.setFullScreen(false);
e.consume();
} else if (e.getCode() == KeyCode.F11) {
autoReenterFullscreen = true;
primaryStage.setFullScreen(true);
e.consume();
}
});
primaryStage.setTitle("Mesos");
primaryStage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
primaryStage.show();
Platform.runLater(() -> primaryStage.setFullScreen(true));
}
/**
* Updates the local mini model reference used by the GUI.
*
* @param miniModel the latest mini model received from the server.
*/
@Override
public void setModel(MiniModel miniModel) {
this.miniModel = miniModel;
}
/**
* Injects the client controller into this GUI.
*
* @param controller the client controller to use.
*/
public void setController(ClientController controller) {
this.controller=controller;
}
/**
* Re-renders the GUI on the JavaFX application thread, switching to the
* appropriate scene based on the current game stage.
*/
public void render() {
Platform.runLater(() -> {
if (miniModel == null) return;
if (controller.getMiniModel().currentState.getGameStage() == TOTEM_CHOICE) {
controllerTotem.render();
fadeToScene(totemScene);
} else if (controller.getMiniModel().currentState.getGameStage() == ENDED) {
controllerLeaderboard.render();
fadeToScene(leaderboardScene);
} else {
controllerMain.render();
fadeToScene(mainScene);
}
});
}
/**
* Wraps the current root of {@code scene} in a {@link StackPane} so that
* fade overlay rectangles can be layered over it during scene transitions.
*
* @param scene the scene whose root to wrap.
*/
private void wrapScene(Scene scene) {
Parent root = scene.getRoot();
scene.setRoot(new StackPane(root));
}
/**
* Performs a black-overlay crossfade from the current scene to {@code newScene}.
* No-op if already fading or if {@code newScene} is already displayed.
*
* @param newScene the scene to transition to.
*/
private void fadeToScene(Scene newScene) {
Scene currentScene = primaryStage.getScene();
if (currentScene == newScene || isFading) return;
isFading = true;
StackPane currentRoot = (StackPane) currentScene.getRoot();
Rectangle overlay = new Rectangle();
overlay.setFill(Color.BLACK);
overlay.setOpacity(0);
overlay.widthProperty().bind(currentRoot.widthProperty());
overlay.heightProperty().bind(currentRoot.heightProperty());
currentRoot.getChildren().add(overlay);
FadeTransition fadeOut = new FadeTransition(Duration.millis(500), overlay);
fadeOut.setFromValue(0);
fadeOut.setToValue(1);
fadeOut.setOnFinished(e -> {
currentRoot.getChildren().remove(overlay);
StackPane newRoot = (StackPane) newScene.getRoot();
Rectangle newOverlay = new Rectangle();
newOverlay.setFill(Color.BLACK);
newOverlay.setOpacity(1);
newOverlay.widthProperty().bind(newRoot.widthProperty());
newOverlay.heightProperty().bind(newRoot.heightProperty());
newRoot.getChildren().add(newOverlay);
primaryStage.setScene(newScene);
FadeTransition fadeIn = new FadeTransition(Duration.millis(500), newOverlay);
fadeIn.setFromValue(1);
fadeIn.setToValue(0);
fadeIn.setOnFinished(ev -> {
newRoot.getChildren().remove(newOverlay);
isFading = false;
});
fadeIn.play();
});
fadeOut.play();
}
/**
* Displays an error on the JavaFX application thread.
*
* <p>If the server crashed, returns to the login scene and re-enables the login button.
* Otherwise, delegates to the active scene's error display.
*
* @param error the error type.
* @param message a human-readable description of the error.
*/
@Override
public void showError(ErrorType error,String message) {
Platform.runLater(() -> {
if (error == ErrorType.SERVER_CRASHED) {
controllerLogin.updateLoginButton(true);
controllerLogin.showError(error);
fadeToScene(loginScene);
} else {
if (miniModel == null) return;
controllerMain.setError(true);
controllerMain.render();
}
});
}
}