Feat/game-ui #182

Merged
j.kropff merged 6 commits from feat/game-ui into main 2026-03-14 17:05:03 +01:00
Showing only changes of commit c411e9c560 - Show all commits
@@ -1,23 +1,33 @@
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents; package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.URI;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.*; import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.*; import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle; 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 java.net.*;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
/** /**
* Experimenteller integrierter Browser für Casono. * Experimenteller integrierter Browser für Casono.
* *
@@ -49,10 +59,27 @@ import java.util.Set;
* - Automatische Cookie-Löschung beim Schließen * - Automatische Cookie-Löschung beim Schließen
*/ */
public class CasinoBrowserController { public class CasinoBrowserController {
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 =
org.apache.logging.log4j.LogManager.getLogger(CasinoBrowserController.class);
private static final int LOGO_HEIGHT = 40;
private static final int CORNER_RADIUS = 40;
private static final int HBOX_SPACIN = 10;
private static final int VBOX_SPACING = 10;
private static final int PADDING_SIZE = 10;
private static final int WINDOW_WIDTH = 1200;
private static final int WINDOW_HEIGHT = 800;
private static final int ALERT_WIDTH = 600;
private static final int ALERT_HEIGHT = 300;
private static final String LOGO_PATH = "/images/logoinverted.png";
private static final String LOGO_PATH_MAIN = "/images/logo.png";
static { static {
CookieHandler.setDefault(COOKIE_MANAGER); CookieHandler.setDefault(COOKIE_MANAGER);
@@ -104,7 +131,72 @@ public class CasinoBrowserController {
engine.setJavaScriptEnabled(false); engine.setJavaScriptEnabled(false);
// Popup Blocker configurePopupBlocker(engine);
webView.getStyleClass().add("web-view");
StackPane webContainer = createWebContainer(webView);
Rectangle clip = new Rectangle();
clip.setArcWidth(CORNER_RADIUS);
clip.setArcHeight(CORNER_RADIUS);
webView.setClip(clip);
webView.widthProperty().addListener((o, a, b) ->
clip.setWidth(b.doubleValue()));
webView.heightProperty().addListener((o, a, b) ->
clip.setHeight(b.doubleValue()));
ImageView browserLogo = loadLogos(stage);
TextField urlField = createUrlField(url);
Label securityLabel = createSecurityLabel();
Button jsToggle = createJsToggle(engine);
Button backBtn = createBackButton(engine);
Button fwdBtn = createForwardButton(engine);
Button reloadBtn = createReloadButton(engine);
Button closeBtn = createCloseButton(stage, webView);
configureUrlEvents(engine, urlField, securityLabel);
HBox taskbar = new HBox(HBOX_SPACIN, browserLogo, backBtn, fwdBtn,
reloadBtn, urlField, jsToggle, securityLabel, closeBtn
);
taskbar.getStyleClass().add("taskbar-browser");
taskbar.setAlignment(Pos.CENTER_LEFT);
Scene scene = createScene(taskbar, webContainer);
configureKeyEvents(scene, engine);
stage.setScene(scene);
stage.setTitle("Casono Browser");
loadUrlSafely(engine, url, securityLabel);
stage.show();
});
}
/**
* WebView Container
*
* @param webView WebView Inhalt
* @return StackPane Container
*/
public static StackPane createWebContainer(WebView webView) {
StackPane webContainer = new StackPane(webView);
webContainer.getStyleClass().add("browser-web-view");
VBox.setVgrow(webContainer, Priority.ALWAYS);
return webContainer;
}
/**
* Popup Blocker
*
* @param engine WebEngine nutzen
*/
public static void configurePopupBlocker(WebEngine engine) {
engine.setCreatePopupHandler(config -> { engine.setCreatePopupHandler(config -> {
Alert alert = new Alert(Alert.AlertType.WARNING); Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Popup blockiert"); alert.setTitle("Popup blockiert");
@@ -112,10 +204,13 @@ public class CasinoBrowserController {
alert.setContentText("Popup wurde aus Sicherheitsgründen blockiert."); alert.setContentText("Popup wurde aus Sicherheitsgründen blockiert.");
try { try {
Image logo = var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
new Image(CasinoBrowserController.class.getResourceAsStream("/images/logoinverted.png")); Image logo = new Image(stream);
Image logomain =
new Image(CasinoBrowserController.class.getResourceAsStream("/images/logo.png")); // Variable 'streamM' abgekürzt, um das 100-Zeichen-Limit (LineLength)
// einzuhalten
var streamM = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH_MAIN);
Image logomain = new Image(streamM);
if (logo != null) { if (logo != null) {
Stage alertStage = (Stage) alert.getDialogPane().getScene().getWindow(); Stage alertStage = (Stage) alert.getDialogPane().getScene().getWindow();
@@ -126,39 +221,36 @@ public class CasinoBrowserController {
if (logomain != null) { if (logomain != null) {
ImageView logoView = new ImageView(logomain); ImageView logoView = new ImageView(logomain);
logoView.setFitHeight(40); logoView.setFitHeight(LOGO_HEIGHT);
logoView.setPreserveRatio(true); logoView.setPreserveRatio(true);
alert.setGraphic(logoView); alert.setGraphic(logoView);
} }
} catch (Exception e) { } catch (Exception e) {
System.err.println("Logo konnte nicht geladen werden"); LOGGER.error("Logo konnte nicht geladen werden");
} }
alert.showAndWait(); alert.showAndWait();
return null; return null;
}); });
}
webView.getStyleClass().add("web-view"); /**
* Logos laden
StackPane webContainer = new StackPane(webView); *
webContainer.getStyleClass().add("browser-web-view"); * @param stage Fenster Stage
VBox.setVgrow(webContainer, Priority.ALWAYS); * @return ImageView Logo
*/
Rectangle clip = new Rectangle(); private static ImageView loadLogos(Stage stage) {
clip.setArcWidth(40);
clip.setArcHeight(40);
webView.setClip(clip);
webView.widthProperty().addListener((o,a,b)->clip.setWidth(b.doubleValue()));
webView.heightProperty().addListener((o,a,b)->clip.setHeight(b.doubleValue()));
ImageView browserLogo = new ImageView(); ImageView browserLogo = new ImageView();
try { try {
Image logo = var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
new Image(CasinoBrowserController.class.getResourceAsStream("/images/logoinverted.png")); Image logo = new Image(stream);
Image logomain =
new Image(CasinoBrowserController.class.getResourceAsStream("/images/logo.png")); // Variable 'streamM' abgekürzt, um das 100-Zeichen-Limit (LineLength)
// einzuhalten
var streamM = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH_MAIN);
Image logomain = new Image(streamM);
if (logo != null) { if (logo != null) {
stage.getIcons().add(logo); stage.getIcons().add(logo);
@@ -166,22 +258,49 @@ public class CasinoBrowserController {
if (logomain != null) { if (logomain != null) {
browserLogo.setImage(logomain); browserLogo.setImage(logomain);
browserLogo.setFitHeight(30); browserLogo.setFitHeight(LOGO_HEIGHT);
browserLogo.setPreserveRatio(true); browserLogo.setPreserveRatio(true);
} }
} catch (Exception e) { } catch (Exception e) {
System.err.println("Logo konnte nicht geladen werden"); LOGGER.error("Logo konnte nicht geladen werden");
} }
// URL Feld return browserLogo;
}
/**
* URL Feld
*
* @param url Start URL
* @return TextField Eingabe
*/
public static TextField createUrlField(String url) {
TextField urlField = new TextField(url); TextField urlField = new TextField(url);
urlField.getStyleClass().add("gray-input-field"); urlField.getStyleClass().add("gray-input-field");
HBox.setHgrow(urlField, Priority.ALWAYS); HBox.setHgrow(urlField, Priority.ALWAYS);
return urlField;
}
/**
* Sicherheits Label
*
* @return Label Anzeige
*/
public static Label createSecurityLabel() {
Label securityLabel = new Label("SICHER"); Label securityLabel = new Label("SICHER");
securityLabel.getStyleClass().add("security-label"); securityLabel.getStyleClass().add("security-label");
// JS Toggle return securityLabel;
}
/**
* JS Toggle
*
* @param engine WebEngine nutzen
* @return Button Umschalten
*/
public static Button createJsToggle(WebEngine engine){
Button jsToggle = new Button("JS EINSCHALTEN"); Button jsToggle = new Button("JS EINSCHALTEN");
jsToggle.getStyleClass().add("red-button"); jsToggle.getStyleClass().add("red-button");
@@ -201,30 +320,67 @@ public class CasinoBrowserController {
} }
}); });
// Navigation return jsToggle;
}
/**
* Zurück Button
*
* @param engine WebEngine nutzen
* @return Button Zurück
*/
public static Button createBackButton(WebEngine engine) {
Button backBtn = new Button("<"); Button backBtn = new Button("<");
backBtn.getStyleClass().add("gray-button"); backBtn.getStyleClass().add("gray-button");
backBtn.setOnAction(e -> { backBtn.setOnAction(e -> {
if(engine.getHistory().getCurrentIndex() > 0){ if (engine.getHistory().getCurrentIndex() > 0) {
engine.getHistory().go(-1); engine.getHistory().go(-1);
} }
}); });
return backBtn;
}
/**
* Vorwärts Button
*
* @param engine WebEngine nutzen
* @return Button Vorwärts
*/
public static Button createForwardButton(WebEngine engine) {
Button fwdBtn = new Button(">"); Button fwdBtn = new Button(">");
fwdBtn.getStyleClass().add("gray-button"); fwdBtn.getStyleClass().add("gray-button");
fwdBtn.setOnAction(e -> { fwdBtn.setOnAction(e -> {
if(engine.getHistory().getCurrentIndex() < if (engine.getHistory().getCurrentIndex() <
engine.getHistory().getEntries().size()-1){ engine.getHistory().getEntries().size() - 1) {
engine.getHistory().go(1); engine.getHistory().go(1);
} }
}); });
// Reload Button return fwdBtn;
}
/**
* Reload Button
*
* @param engine WebEngine nutzen
* @return Button Reload
*/
public static Button createReloadButton(WebEngine engine) {
Button reloadBtn = new Button(""); Button reloadBtn = new Button("");
reloadBtn.getStyleClass().add("gray-button"); reloadBtn.getStyleClass().add("gray-button");
reloadBtn.setOnAction(e -> engine.reload()); reloadBtn.setOnAction(e -> engine.reload());
return reloadBtn;
}
// Close /**
* Close Button
*
* @param stage Fenster Stage
* @param webView WebView Inhalt
* @return Button Schließen
*/
public static Button createCloseButton(Stage stage, WebView webView) {
Button closeBtn = new Button("X"); Button closeBtn = new Button("X");
closeBtn.getStyleClass().add("red-button"); closeBtn.getStyleClass().add("red-button");
closeBtn.setOnAction(e -> { closeBtn.setOnAction(e -> {
@@ -234,49 +390,60 @@ public class CasinoBrowserController {
}); });
// URL Enter return closeBtn;
}
/**
* URL Events
*
* @param engine WebEngine nutzen
* @param urlField URL Textfeld
* @param securityLabel Sicherheits Label
*/
public static void configureUrlEvents(WebEngine engine,
TextField urlField,
Label securityLabel) {
urlField.setOnAction(e -> urlField.setOnAction(e ->
loadUrlSafely(engine, urlField.getText(), securityLabel)); loadUrlSafely(engine, urlField.getText(), securityLabel));
engine.locationProperty().addListener((obs,o,n)->urlField.setText(n)); engine.locationProperty().addListener((obs, o, n) ->
urlField.setText(n));
}
// Taskbar /**
HBox taskbar = new HBox( * Scene erstellen
10, *
browserLogo, * @param taskbar Taskbar HBox
backBtn, * @param webContainer Web Container
fwdBtn, * @return Scene Fenster
reloadBtn, */
urlField, public static Scene createScene(HBox taskbar, StackPane webContainer) {
jsToggle, VBox root = new VBox(VBOX_SPACING, taskbar, webContainer);
securityLabel,
closeBtn
);
taskbar.getStyleClass().add("taskbar-browser");
taskbar.setAlignment(Pos.CENTER_LEFT);
// Root
VBox root = new VBox(10, taskbar, webContainer);
root.getStyleClass().add("browser-root"); root.getStyleClass().add("browser-root");
root.setPadding(new Insets(10)); root.setPadding(new Insets(PADDING_SIZE));
Scene scene = new Scene(root,1200,800);
var css = CasinoBrowserController.class.getResource("/ui-structure/casinogameui.css");
if(css != null) { Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT);
var css = CasinoBrowserController.class
.getResource("/ui-structure/casinogameui.css");
if (css != null) {
scene.getStylesheets().add(css.toExternalForm()); scene.getStylesheets().add(css.toExternalForm());
} }
// F5 Reload return scene;
scene.setOnKeyPressed(event -> {
switch (event.getCode()) {
case F5 -> engine.reload();
} }
});
stage.setScene(scene); /**
stage.setTitle("Casono Browser"); * Key Events
loadUrlSafely(engine, url, securityLabel); *
stage.show(); * @param scene Scene Fenster
* @param engine WebEngine nutzen
*/
public static void configureKeyEvents(Scene scene, WebEngine engine) {
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.F5) {
engine.reload();
}
}); });
} }
@@ -299,20 +466,20 @@ public class CasinoBrowserController {
*/ */
private static void loadUrlSafely(WebEngine engine, String url, Label securityLabel) { private static void loadUrlSafely(WebEngine engine, String url, Label securityLabel) {
try { try {
if(!url.startsWith("http")) { if (!url.startsWith("http")) {
url = "https://" + url; url = "https://" + url;
} }
URI uri = new URI(url); URI uri = new URI(url);
// HTTPS Pflicht // HTTPS Pflicht
if(!"https".equalsIgnoreCase(uri.getScheme())) { if (!"https".equalsIgnoreCase(uri.getScheme())) {
securityLabel.setText("BLOCKIERT"); securityLabel.setText("BLOCKIERT");
return; return;
} }
String host = uri.getHost(); String host = uri.getHost();
if(host == null) { if (host == null) {
securityLabel.setText("ERROR"); securityLabel.setText("ERROR");
return; return;
} }
@@ -321,44 +488,57 @@ public class CasinoBrowserController {
boolean trusted = TRUSTED_DOMAINS.stream().anyMatch(domain -> boolean trusted = TRUSTED_DOMAINS.stream().anyMatch(domain ->
host.equals(domain) || host.endsWith("." + domain)); host.equals(domain) || host.endsWith("." + domain));
if(!trusted) { if (!trusted) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION); if (!showUnknownWebsiteAlert(host)) {
alert.setTitle("Unbekannte Website");
alert.setHeaderText("Diese Website ist nicht bekannt");
alert.setContentText(
host + "\n\nDiese Seite ist nicht vom Casono Browser verifiziert.\nMöchten Sie sie trotzdem öffnen?"
);
Image logo = new Image(CasinoBrowserController.class.getResourceAsStream("/images/logoinverted.png"));
if (logo != null) {
Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
if(stage != null) {
stage.getIcons().add(logo);
}
ImageView logomain = new ImageView(logo);
logomain.setFitHeight(50);
logomain.setPreserveRatio(true);
alert.setGraphic(logomain);
}
alert.getDialogPane().setPrefSize(600,300);
Optional<ButtonType> result = alert.showAndWait();
if(result.isEmpty() || result.get() != ButtonType.OK) {
securityLabel.setText("BLOCKIERT"); securityLabel.setText("BLOCKIERT");
return; return;
} }
securityLabel.setText("UNBEKANNT"); securityLabel.setText("UNBEKANNT");
} else { } else {
securityLabel.setText("SICHER"); securityLabel.setText("SICHER");
} }
engine.load(uri.toString()); engine.load(uri.toString());
} } catch (Exception e) {
catch (Exception e) {
securityLabel.setText("ERROR"); securityLabel.setText("ERROR");
} }
} }
/**
* Zeigt Warnung an.
*
* @param host Website Host
* @return OK gedrückt
*/
private static boolean showUnknownWebsiteAlert(String host) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Unbekannte Website");
alert.setHeaderText("Diese Website ist nicht bekannt");
alert.setContentText(
host + "\n\nDiese Seite ist nicht vom " +
"Casono Browser verifiziert.\nMöchten Sie sie trotzdem öffnen?"
);
var stream = CasinoBrowserController.class.getResourceAsStream(LOGO_PATH);
Image logo = new Image(stream);
if (logo != null) {
Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
if (stage != null) {
stage.getIcons().add(logo);
}
ImageView logomain = new ImageView(logo);
logomain.setFitHeight(LOGO_HEIGHT);
logomain.setPreserveRatio(true);
alert.setGraphic(logomain);
}
alert.getDialogPane().setPrefSize(ALERT_WIDTH, ALERT_HEIGHT);
Optional<ButtonType> result = alert.showAndWait();
if (result.isEmpty() || result.get() != ButtonType.OK) {
return false;
}
return true;
}
} }