Feat/ui switch lobby game #222

Merged
jona.walpert merged 12 commits from feat/ui-switch-lobby-game into main 2026-04-01 12:34:31 +02:00
17 changed files with 127 additions and 56 deletions
@@ -41,6 +41,11 @@ public class ChatController {
chatScrollPane.vvalueProperty().bind(chatVBox.heightProperty()); chatScrollPane.vvalueProperty().bind(chatVBox.heightProperty());
} }
/** Standardkonstruktor. Wird von FXML verwendet. */
public ChatController() {
// default constructor for FXML
}
/** /**
* Diese Methode wird vom Senden-Button oder Enter ausgelöst. Sie gibt die eigene Nachricht an * Diese Methode wird vom Senden-Button oder Enter ausgelöst. Sie gibt die eigene Nachricht an
* das Netzwerkprotokoll weiter. * das Netzwerkprotokoll weiter.
@@ -15,11 +15,22 @@ import javafx.scene.layout.VBox;
*/ */
public class CasinoGameController { public class CasinoGameController {
/** Standardkonstruktor. Wird von FXML verwendet. */
public CasinoGameController() {
// default constructor for FXML
}
@FXML private Label welcomeText; @FXML private Label welcomeText;
@FXML private VBox casinoTable; @FXML private VBox casinoTable;
// TODO: Test-Logik: wird durch echte Spielinteraktionen ersetzt, // TODO: Test-Logik: wird durch echte Spielinteraktionen ersetzt,
// sobald die GameEngine fertig ist // sobald die GameEngine fertig ist
/**
* Temporäre Test-Methode, die bei Klick auf den Tisch eine Platzhalteraktion ausführt.
*
* <p>Wird in der finalen Implementierung durch die Spiel-Logik ersetzt.
*/
@FXML @FXML
public void onTableClick() { public void onTableClick() {
welcomeText.setText("Einsatz akzeptiert!"); welcomeText.setText("Einsatz akzeptiert!");
@@ -17,6 +17,11 @@ import javafx.stage.Stage;
*/ */
public class CasinoGameUI extends Application { public class CasinoGameUI extends Application {
/** Standardkonstruktor. */
public CasinoGameUI() {
// default no-arg constructor
}
private static final int DEFAULT_WIDTH = 1200; private static final int DEFAULT_WIDTH = 1200;
private static final int DEFAULT_HEIGHT = 800; private static final int DEFAULT_HEIGHT = 800;
@@ -29,7 +34,7 @@ public class CasinoGameUI extends Application {
@Override @Override
public void start(Stage stage) throws IOException { public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = FXMLLoader fxmlLoader =
new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/casinogameui.fxml")); new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/Casinogameui.fxml"));
Scene scene = new Scene(fxmlLoader.load(), DEFAULT_WIDTH, DEFAULT_HEIGHT); Scene scene = new Scene(fxmlLoader.load(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
stage.setTitle("Casono (GAME)"); stage.setTitle("Casono (GAME)");
@@ -27,6 +27,8 @@ import javafx.scene.shape.Rectangle;
import javafx.scene.web.WebEngine; import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView; import javafx.scene.web.WebView;
import javafx.stage.Stage; import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** /**
* Experimenteller integrierter Browser für Casono. * Experimenteller integrierter Browser für Casono.
@@ -50,13 +52,17 @@ import javafx.stage.Stage;
*/ */
public class CasinoBrowserController { public class CasinoBrowserController {
/** Standardkonstruktor. Initialisiert den CasinoBrowserController. */
public CasinoBrowserController() {
// Intentionally left blank; controller initialization is FXML-driven.
}
private static final Set<String> TRUSTED_DOMAINS = new HashSet<>(); private static final Set<String> TRUSTED_DOMAINS = new HashSet<>();
private static final CookieManager COOKIE_MANAGER = private static final CookieManager COOKIE_MANAGER =
new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER); new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER);
private static final org.apache.logging.log4j.Logger LOGGER = private static final Logger LOGGER = LogManager.getLogger(CasinoBrowserController.class);
org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class);
private static final int LOGO_HEIGHT = 40; private static final int LOGO_HEIGHT = 40;
private static final int CORNER_RADIUS = 40; private static final int CORNER_RADIUS = 40;
@@ -354,8 +360,9 @@ public class CasinoBrowserController {
fwdBtn.getStyleClass().add("gray-button"); fwdBtn.getStyleClass().add("gray-button");
fwdBtn.setOnAction( fwdBtn.setOnAction(
e -> { e -> {
if (engine.getHistory().getCurrentIndex() int currentIndex = engine.getHistory().getCurrentIndex();
< engine.getHistory().getEntries().size() - 1) { int lastIndex = engine.getHistory().getEntries().size() - 1;
if (currentIndex < lastIndex) {
engine.getHistory().go(1); engine.getHistory().go(1);
} }
}); });
@@ -423,7 +430,7 @@ public class CasinoBrowserController {
Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT); Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT);
var css = CasinoBrowserController.class.getResource("/ui-structure/casinogameui.css"); var css = CasinoBrowserController.class.getResource("/ui-structure/Casinogameui.css");
if (css != null) { if (css != null) {
scene.getStylesheets().add(css.toExternalForm()); scene.getStylesheets().add(css.toExternalForm());
@@ -512,10 +519,11 @@ public class CasinoBrowserController {
alert.setTitle("Unbekannte Website"); alert.setTitle("Unbekannte Website");
alert.setHeaderText("Diese Website ist nicht bekannt"); alert.setHeaderText("Diese Website ist nicht bekannt");
alert.setContentText( String content =
host host
+ "\n\nDiese Seite ist nicht vom " + "\n\nDiese Seite ist nicht vom Casono Browser verifiziert.\n"
+ "Casono Browser verifiziert.\nMöchten Sie sie trotzdem öffnen?"); + "Möchten Sie sie trotzdem öffnen?";
alert.setContentText(content);
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH); var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
Image logo = new Image(stream); Image logo = new Image(stream);
@@ -1,12 +1,14 @@
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents; package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents;
import javafx.application.Platform; import ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui.Casinomainui;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** /**
* Controller für die interaktive Taskleiste innerhalb der Poker-UI. * Controller für die interaktive Taskleiste innerhalb der Poker-UI.
@@ -16,8 +18,12 @@ import javafx.scene.layout.HBox;
*/ */
public class TaskbarController { public class TaskbarController {
private static final org.apache.logging.log4j.Logger LOGGER = /** Standardkonstruktor. Wird von FXML verwendet. */
org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class); public TaskbarController() {
// default constructor for FXML
}
private static final Logger LOGGER = LogManager.getLogger(CasinoBrowserController.class);
@FXML private HBox taskbar; @FXML private HBox taskbar;
@FXML private TextField taskbarInput; @FXML private TextField taskbarInput;
@@ -92,15 +98,21 @@ public class TaskbarController {
processBet(); processBet();
} }
/**
* Wird aufgerufen, wenn der Exit-Button in der Taskleiste gedrückt wird.
*
* <p>TODO: Logik implementieren, um zur Lobby zurückzukehren, ohne die gesamte Anwendung zu
* schließen (kein System.exit/Platform.exit).
*/
@FXML @FXML
private void onExitButtonClick() { private void onExitButtonClick() {
Platform.exit(); javafx.application.Platform.runLater(
() -> {
// Close game stage
javafx.stage.Stage currentStage =
(javafx.stage.Stage) taskbar.getScene().getWindow();
currentStage.close();
// Lobby-UI starten
try {
new Casinomainui().start(new javafx.stage.Stage());
} catch (Exception e) {
LOGGER.error("Fehler beim Starten der Lobby-UI: {}", e.getMessage());
}
});
} }
/** /**
@@ -28,6 +28,7 @@ public class CasinomainuiController {
private LobbyButtonGridManager gridManager; private LobbyButtonGridManager gridManager;
private int nextButtonId = 1; private int nextButtonId = 1;
/** Default constructor for dependency injection by FXMLLoader. */
public CasinomainuiController() { public CasinomainuiController() {
// Default constructor // Default constructor
} }
@@ -39,7 +40,7 @@ public class CasinomainuiController {
subtitleLabel.setText("Texas Hold'em Poker"); subtitleLabel.setText("Texas Hold'em Poker");
logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm())); logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm()));
translationManager = new LobbyButtonTranslationManager(); translationManager = LobbyButtonTranslationManager.getInstance();
gridManager = gridManager =
new LobbyButtonGridManager(new javafx.scene.layout.GridPane(), translationManager); new LobbyButtonGridManager(new javafx.scene.layout.GridPane(), translationManager);
casinoTable.getChildren().clear(); casinoTable.getChildren().clear();
@@ -17,6 +17,8 @@ import org.apache.logging.log4j.Logger;
* ButtonID to LobbyID. * ButtonID to LobbyID.
*/ */
public class LobbyButtonGridManager { public class LobbyButtonGridManager {
private static final double BUTTON_WIDTH_MARGIN = 20.0;
private static final double BUTTON_MIN_SIZE = 10.0;
private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class); private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class);
/** GridPane for the button grid. */ /** GridPane for the button grid. */
@@ -46,7 +48,8 @@ public class LobbyButtonGridManager {
public LobbyButtonGridManager( public LobbyButtonGridManager(
GridPane gridPane, LobbyButtonTranslationManager translationManager) { GridPane gridPane, LobbyButtonTranslationManager translationManager) {
this.gridPane = gridPane; this.gridPane = gridPane;
this.translationManager = translationManager; // Singleton immer verwenden
this.translationManager = LobbyButtonTranslationManager.getInstance();
} }
/** /**
@@ -65,8 +68,21 @@ public class LobbyButtonGridManager {
int buttonId = entry.getKey(); int buttonId = entry.getKey();
Button btn = new Button(); Button btn = new Button();
btn.setId("lobbyBtn-" + buttonId); btn.setId("lobbyBtn-" + buttonId);
btn.setGraphic( ImageView imageView =
new ImageView(new Image(getClass().getResourceAsStream(BUTTON_IMAGE_PATH)))); new ImageView(new Image(getClass().getResourceAsStream(BUTTON_IMAGE_PATH)));
imageView.setPreserveRatio(true);
// Dynamische Breite: Bindung an die Zellengröße
imageView
.fitWidthProperty()
.bind(gridPane.widthProperty().divide(COLS).subtract(BUTTON_WIDTH_MARGIN));
imageView.setSmooth(true);
btn.setGraphic(imageView);
btn.setMaxWidth(Double.MAX_VALUE);
btn.setMaxHeight(Double.MAX_VALUE);
btn.setMinWidth(BUTTON_MIN_SIZE);
btn.setMinHeight(BUTTON_MIN_SIZE);
GridPane.setHgrow(btn, javafx.scene.layout.Priority.ALWAYS);
GridPane.setVgrow(btn, javafx.scene.layout.Priority.ALWAYS);
btn.setOnAction( btn.setOnAction(
e -> { e -> {
Integer lobbyId = translationManager.getLobbyIdForButton(buttonId); Integer lobbyId = translationManager.getLobbyIdForButton(buttonId);
@@ -99,8 +115,22 @@ public class LobbyButtonGridManager {
* @param lobbyId The lobbyId to join * @param lobbyId The lobbyId to join
*/ */
public void joinLobby(int lobbyId) { public void joinLobby(int lobbyId) {
// TODO: Replace with actual join logic // Game-UI starten und Lobby-UI schließen
LOGGER.info("Joining lobby: {}", lobbyId); LOGGER.info("Joining lobby: {}", lobbyId);
javafx.application.Platform.runLater(
() -> {
// Lobby-Stage schließen
javafx.stage.Stage currentStage =
(javafx.stage.Stage) gridPane.getScene().getWindow();
currentStage.close();
// Game-UI starten
try {
new ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI()
.start(new javafx.stage.Stage());
} catch (Exception e) {
LOGGER.error("Fehler beim Starten der Game-UI: {}", e.getMessage());
}
});
} }
/** /**
@@ -8,14 +8,31 @@ import java.util.Map;
* nur Laufzeitdatenstruktur. * nur Laufzeitdatenstruktur.
*/ */
public class LobbyButtonTranslationManager { public class LobbyButtonTranslationManager {
// Singleton-Instanz
private static LobbyButtonTranslationManager instance;
// Singleton-Zugriff
/**
* Liefert die Singleton-Instanz des Managers.
*
* @return die einzige Instanz von {@code LobbyButtonTranslationManager}
*/
public static LobbyButtonTranslationManager getInstance() {
if (instance == null) {
instance = new LobbyButtonTranslationManager();
}
return instance;
}
/** Maximale Anzahl an Buttons/Lobbys */ /** Maximale Anzahl an Buttons/Lobbys */
private static final int MAX_BUTTONS = 8; private static final int MAX_BUTTONS = 8;
/** Zuordnung ButtonID → LobbyID */ /** Zuordnung ButtonID → LobbyID */
private final Map<Integer, Integer> buttonIdToLobbyId = new HashMap<>(); private final Map<Integer, Integer> buttonIdToLobbyId = new HashMap<>();
/** Konstruktor: initialisiert die Zuordnung leer. */ /** Privater Konstruktor für Singleton-Pattern */
public LobbyButtonTranslationManager() { private LobbyButtonTranslationManager() {
// Zuordnung bleibt leer beim Start // Zuordnung bleibt leer beim Start
} }
@@ -5,12 +5,12 @@
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.image.*?> <?import javafx.scene.image.*?>
<!-- Hauptcontainer: Verknüpft die UI mit dem CasinoGameController und lädt casinogameui.css --> <!-- Hauptcontainer: Verknüpft die UI mit dem CasinoGameController und lädt Casinogameui.css -->
<AnchorPane xmlns="http://javafx.com/javafx/21" <AnchorPane xmlns="http://javafx.com/javafx/21"
xmlns:fx="http://javafx.com/fxml/1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameController" fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameController"
styleClass="background" styleClass="background"
stylesheets="@casinogameui.css"> stylesheets="@Casinogameui.css">
<GridPane prefWidth="1200" prefHeight="800" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0"> <GridPane prefWidth="1200" prefHeight="800" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0">
<columnConstraints> <columnConstraints>
@@ -78,14 +78,14 @@
</VBox> </VBox>
<!-- TODO: Platzhalter: Chat-Box in Spalte 2: --> <!-- TODO: Platzhalter: Chat-Box in Spalte 2: -->
<fx:include source="components/chatbox.fxml" GridPane.columnIndex="2"/> <fx:include source="components/Chatbox.fxml" GridPane.columnIndex="2"/>
</children> </children>
</GridPane> </GridPane>
<!-- verschiebbare Taskbar --> <!-- verschiebbare Taskbar -->
<AnchorPane> <AnchorPane>
<fx:include fx:id="taskbarInclude" source="gameuicomponents/taskbar.fxml"/> <fx:include fx:id="taskbarInclude" source="gameuicomponents/Taskbar.fxml"/>
</AnchorPane> </AnchorPane>
<!-- Gegner-Status 1: Eines von drei Panels, das Icon, Name und Kontostand des Mitspielers anzeigt --> <!-- Gegner-Status 1: Eines von drei Panels, das Icon, Name und Kontostand des Mitspielers anzeigt -->
@@ -99,7 +99,7 @@
</columnConstraints> </columnConstraints>
<children> <children>
<fx:include source="gameuicomponents/playerstatus.fxml" GridPane.columnIndex="1"/> <fx:include source="gameuicomponents/Playerstatus.fxml" GridPane.columnIndex="1"/>
</children> </children>
</GridPane> </GridPane>
@@ -114,7 +114,7 @@
</columnConstraints> </columnConstraints>
<children> <children>
<fx:include source="gameuicomponents/playerstatus.fxml" GridPane.columnIndex="1"/> <fx:include source="gameuicomponents/Playerstatus.fxml" GridPane.columnIndex="1"/>
</children> </children>
</GridPane> </GridPane>
@@ -129,7 +129,7 @@
</columnConstraints> </columnConstraints>
<children> <children>
<fx:include source="gameuicomponents/playerstatus.fxml" GridPane.columnIndex="1"/> <fx:include source="gameuicomponents/Playerstatus.fxml" GridPane.columnIndex="1"/>
</children> </children>
</GridPane> </GridPane>
</AnchorPane> </AnchorPane>
@@ -106,20 +106,7 @@
</VBox> </VBox>
<!-- RECHTER BEREICH (Info-Box) --> <!-- RECHTER BEREICH (Info-Box) -->
<VBox GridPane.columnIndex="2" alignment="CENTER"> <fx:include source="components/Chatbox.fxml" GridPane.columnIndex="2"/>
<padding>
<Insets top="30" right="30" bottom="30" left="10"/>
</padding>
<VBox VBox.vgrow="ALWAYS" styleClass="floating-chat-box" spacing="10" maxWidth="Infinity">
<Label text="== INFO ==" styleClass="chat-header" alignment="CENTER" maxWidth="Infinity"/>
<Region minHeight="4" style="-fx-background-color: #ffffff;" />
<VBox spacing="15" VBox.vgrow="ALWAYS" styleClass="info-content">
<Label text="> CREDITS: 1000" styleClass="info-text"/>
<Label text="> JACKPOT: 9999" styleClass="info-text"/>
<Label text="> SYSTEM: OK" styleClass="info-text"/>
</VBox>
</VBox>
</VBox>
</children> </children>
</GridPane> </GridPane>
</AnchorPane> </AnchorPane>
@@ -21,7 +21,7 @@
xmlns:fx="http://javafx.com/fxml/1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui.ChatController" fx:controller="ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui.ChatController"
alignment="CENTER" alignment="CENTER"
stylesheets="@chatui.css"> stylesheets="@Chatui.css">
<!-- Innenabstand: Schafft oben, rechts und unten 30 Pixel Platz, links nur 10 Pixel (asymmetrisch) --> <!-- Innenabstand: Schafft oben, rechts und unten 30 Pixel Platz, links nur 10 Pixel (asymmetrisch) -->
<padding> <padding>
@@ -13,7 +13,7 @@ class LobbyButtonGridManagerTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
gridPane = new GridPane(); gridPane = new GridPane();
translationManager = new LobbyButtonTranslationManager(); translationManager = LobbyButtonTranslationManager.getInstance();
translationManager.getButtonIdToLobbyId().clear(); translationManager.getButtonIdToLobbyId().clear();
gridManager = new LobbyButtonGridManager(gridPane, translationManager); gridManager = new LobbyButtonGridManager(gridPane, translationManager);
} }
@@ -23,10 +23,4 @@ class LobbyButtonGridManagerTest {
int lobbyId = gridManager.createLobby(); int lobbyId = gridManager.createLobby();
assertTrue(lobbyId > 0); assertTrue(lobbyId > 0);
} }
@Test
void testJoinLobbyPlaceholder() {
// check if exception is thrown
assertDoesNotThrow(() -> gridManager.joinLobby(123));
}
} }
@@ -6,11 +6,12 @@ import java.util.Map;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
class LobbyButtonTranslationManagerTest { class LobbyButtonTranslationManagerTest {
LobbyButtonTranslationManager manager; LobbyButtonTranslationManager manager;
@BeforeEach @BeforeEach
void setUp() { void setUp() {
manager = new LobbyButtonTranslationManager(); manager = LobbyButtonTranslationManager.getInstance();
manager.getButtonIdToLobbyId().clear(); manager.getButtonIdToLobbyId().clear();
} }