Coverage Summary for Class: TotemFXMLController (it.polimi.ingsw.gc14.View.GUI)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
| TotemFXMLController |
0%
(0/1)
|
0%
(0/16)
|
0%
(0/24)
|
0%
(0/90)
|
package it.polimi.ingsw.gc14.View.GUI;
import it.polimi.ingsw.gc14.Controller.ClientController;
import it.polimi.ingsw.gc14.Model.Totems;
import javafx.animation.*;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Screen;
import javafx.util.Duration;
import java.util.Locale;
import java.util.Objects;
/**
* FXML controller for the totem selection scene.
*
* <p>Displays the available totems and lets the current player choose one;
* other players see a waiting banner.
*/
public class TotemFXMLController {
/** Background image displayed behind the totem selection grid. */
@FXML private ImageView backgroundImage;
/** HBox containing the totem card widgets. */
@FXML private HBox mainHBox;
/** Banner label showing whose turn it is to choose a totem. */
@FXML private Label turnBanner;
/** Button that submits the selected totem to the server. */
@FXML private Button confirmButton;
/** Client controller for sending the totem choice. */
private ClientController controller;
/** Index of the currently selected totem, or {@code -1} if none selected. */
private int selectedIndex = -1;
/** The {@link StackPane} frame of the currently selected totem card, or {@code null}. */
private StackPane selectedFrame = null;
/**
* Injects the client controller into this FXML controller.
*
* @param controller the client controller to use.
*/
public void setController(ClientController controller) {
this.controller = controller;
}
/** Initializes the scene: sets the background image to fill the screen. */
@FXML
public void initialize() {
Image img = new Image(Objects.requireNonNull(getClass().getResourceAsStream("/GUIImages/Background.png")));
backgroundImage.setImage(img);
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
backgroundImage.setFitWidth(screenBounds.getWidth());
backgroundImage.setFitHeight(screenBounds.getHeight());
}
/**
* Renders the totem selection cards with entry animations.
* Disables interaction for players who are not the current chooser.
*/
public void render() {
mainHBox.getChildren().clear();
selectedIndex = -1;
selectedFrame = null;
if (controller.getMiniModel().currentState.getCurrentPlayer() == null) return;
String chooser = controller.getMiniModel().currentState.getCurrentPlayer().getUserName();
boolean myTurn = chooser.equals(controller.getMyUsername());
updateBanner(chooser, myTurn);
updateConfirmButton(false);
for (int i = 0; i < controller.getMiniModel().availableTotems.size(); i++) {
Totems totem = controller.getMiniModel().availableTotems.get(i);
VBox card = buildCard(totem, i, myTurn);
mainHBox.getChildren().add(card);
card.setOpacity(0);
card.setTranslateY(14);
PauseTransition delay = new PauseTransition(Duration.millis(80 + i * 65));
delay.setOnFinished(e -> {
FadeTransition ft = new FadeTransition(Duration.millis(320), card);
ft.setToValue(1);
TranslateTransition tt = new TranslateTransition(Duration.millis(320), card);
tt.setToY(0);
new ParallelTransition(ft, tt).play();
});
delay.play();
}
}
/** Updates the turn banner text and style based on whether it is the local player's turn. */
private void updateBanner(String chooser, boolean myTurn) {
if (myTurn) {
turnBanner.setText("✦ It's your turn to choose ✦");
turnBanner.setStyle(
"-fx-font-family: 'Cinzel'; -fx-font-size: 13; -fx-font-weight: 'bold'; -fx-letter-spacing: 3;" +
"-fx-text-fill: #000000;" +
"-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
"-fx-border-color: #000000; -fx-border-width: 1;" +
"-fx-border-radius: 3; -fx-background-radius: 3;" +
"-fx-padding: 10 28 10 28;" +
"-fx-effect: dropshadow(gaussian, rgba(244,192,90,0.3), 18, 0, 0, 0);"
);
} else {
turnBanner.setText("Waiting for " + chooser + " to choose his totem…");
turnBanner.setStyle(
"-fx-font-family: 'Cinzel'; -fx-font-size: 11; -fx-letter-spacing: 2;" +
"-fx-text-fill: #000000;" +
"-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
"-fx-border-color: #000000; -fx-border-width: 1;" +
"-fx-border-radius: 3; -fx-background-radius: 3;" +
"-fx-padding: 10 28 10 28;"
);
}
}
/** Builds a totem card widget; if {@code interactive}, wires click/hover handlers and selection logic. */
private VBox buildCard(Totems totem, int idx, boolean interactive) {
ImageView img = new ImageView(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/GUIImages/Totems/totem_" + String.valueOf(totem).toLowerCase(Locale.ROOT) + ".png"))));
img.setFitHeight(150);
img.setPreserveRatio(true);
StackPane frame = new StackPane(img);
frame.setPrefSize(100, 145);
frame.setBackground(Background.EMPTY);
img.setStyle(frameStyle(false));
Region base = new Region();
base.setPrefSize(100, 10);
Label name = new Label(capitalize(totem));
name.setStyle(nameStyle(false));
VBox card = new VBox(4, frame, base, name);
card.setAlignment(Pos.CENTER);
if (!interactive) {
card.setOpacity(0.35);
card.setCursor(Cursor.DEFAULT);
return card;
}
card.setCursor(Cursor.HAND);
card.setOnMouseEntered(e -> {
if (frame != selectedFrame) {
ScaleTransition st = new ScaleTransition(Duration.millis(140), card);
st.setToX(1.06); st.setToY(1.06); st.play();
}
});
card.setOnMouseExited(e -> {
if (frame != selectedFrame) {
ScaleTransition st = new ScaleTransition(Duration.millis(140), card);
st.setToX(1.0); st.setToY(1.0); st.play();
}
});
card.setOnMouseClicked(e -> {
mainHBox.getChildren().forEach(n -> {
if (n instanceof VBox v) {
StackPane f = (StackPane) v.getChildren().get(0);
Label l = (Label) v.getChildren().get(2);
f.setStyle(frameStyle(false));
l.setStyle(nameStyle(false));
ScaleTransition st = new ScaleTransition(Duration.millis(130), v);
st.setToX(1.0); st.setToY(1.0); st.play();
}
});
frame.setStyle(frameStyle(true));
name.setStyle(nameStyle(true));
selectedFrame = frame;
selectedIndex = idx;
ScaleTransition pop = new ScaleTransition(Duration.millis(160), card);
pop.setToX(1.08); pop.setToY(1.08);
pop.setAutoReverse(true); pop.setCycleCount(2); pop.play();
updateConfirmButton(true);
});
return card;
}
/** Enables or disables the confirm button and updates its visual opacity to reflect the state. */
private void updateConfirmButton(boolean enabled) {
confirmButton.setDisable(!enabled);
confirmButton.setStyle(
"-fx-font-family: 'Cinzel'; -fx-font-size: 12; -fx-font-weight: bold;" +
"-fx-letter-spacing: 4; -fx-text-fill: #0c0601;" +
"-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
"-fx-padding: 12 48 12 48; -fx-background-radius: 2;" +
"-fx-opacity: " + (enabled ? "1.0" : "0.3") + ";" +
"-fx-cursor: " + (enabled ? "hand" : "default") + ";"
);
}
/** Returns the CSS style string for the totem card frame, highlighting it when {@code selected}. */
private String frameStyle(boolean selected) {
return selected ? "-fx-effect: dropshadow(gaussian, #ffffff, 20, 0.33, 0, 0);" : "";
}
/** Returns the CSS style string for the totem name label, brightening it when {@code selected}. */
private String nameStyle(boolean selected) {
return "-fx-font-family: 'Cinzel'; -fx-font-size: 10; -fx-letter-spacing: 2;" +
"-fx-text-fill: " + (selected ? "#FFD700" : "#ffffff") + ";";
}
/** Returns the totem name with only the first letter capitalized. */
private String capitalize(Totems t) {
String s = t.name().toLowerCase(Locale.ROOT);
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
/** Submits the selected totem index to the controller when the confirm button is clicked. */
@FXML
private void onConfirm() {
if (selectedIndex >= 0) {
controller.totemChoice(selectedIndex);
}
}
}