diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index f543a35..4389088 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -1,13 +1,14 @@ package org.toop.app; import javafx.application.Platform; -import javafx.scene.paint.Color; -import org.toop.Main; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; + import org.toop.app.widget.Primitive; -import org.toop.app.widget.Widget; import org.toop.app.widget.WidgetContainer; import org.toop.app.widget.complex.LoadingWidget; import org.toop.app.widget.display.SongDisplay; +import org.toop.app.widget.popup.EscapePopup; import org.toop.app.widget.popup.QuitPopup; import org.toop.app.widget.view.MainView; import org.toop.framework.audio.*; @@ -30,6 +31,8 @@ import javafx.scene.Scene; import javafx.scene.layout.StackPane; import javafx.stage.Stage; +import java.util.Objects; + public final class App extends Application { private static Stage stage; private static Scene scene; @@ -37,8 +40,6 @@ public final class App extends Application { private static int height; private static int width; - private static boolean isQuitting; - public static void run(String[] args) { launch(args); } @@ -78,10 +79,10 @@ public final class App extends Application { App.width = (int)stage.getWidth(); App.height = (int)stage.getHeight(); - App.isQuitting = false; - AppSettings.applySettings(); + setKeybinds(root); + LoadingWidget loading = new LoadingWidget(Primitive.text( "Loading...", false), 0, 0, Integer.MAX_VALUE, false, false // Just set a high default ); @@ -130,6 +131,27 @@ public final class App extends Application { } + private void setKeybinds(StackPane root) { + root.addEventHandler(KeyEvent.KEY_PRESSED,event -> { + if (event.getCode() == KeyCode.ESCAPE) { + escapePopup(); + } + }); + } + + public void escapePopup() { + if (!Objects.requireNonNull( + WidgetContainer.find(widget -> widget instanceof QuitPopup || widget instanceof EscapePopup) + ).isEmpty()) { + WidgetContainer.removeFirst(QuitPopup.class); + WidgetContainer.removeFirst(EscapePopup.class); + return; + } + + EscapePopup escPopup = new EscapePopup(); + escPopup.show(Pos.CENTER); + } + private void setOnLoadingSuccess(LoadingWidget loading) { loading.setOnSuccess(() -> { initSystems(); @@ -140,7 +162,12 @@ public final class App extends Application { WidgetContainer.add(Pos.BOTTOM_RIGHT, new SongDisplay()); stage.setOnCloseRequest(event -> { event.consume(); - startQuit(); + + if (WidgetContainer.getAllWidgets().stream().anyMatch(e -> e instanceof QuitPopup)) return; + + QuitPopup a = new QuitPopup(); + a.show(Pos.CENTER); + }); }); } @@ -177,19 +204,6 @@ public final class App extends Application { } } - public static void startQuit() { - if (isQuitting) { - return; - } - - WidgetContainer.add(Pos.CENTER, new QuitPopup()); - isQuitting = true; - } - - public static void stopQuit() { - isQuitting = false; - } - public static void quit() { stage.close(); System.exit(0); // TODO: This is like dropping a nuke diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index d6c4552..536a33c 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -31,7 +31,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public final class Server { - // TODO: Keep track of listeners. Remove them on Server connection close so reference is deleted. private String user = ""; private long clientId = -1; @@ -47,7 +46,7 @@ public final class Server { private ScheduledExecutorService scheduler; - private EventFlow eventFlow = new EventFlow(); + private EventFlow connectFlow; public static GameInformation.Type gameToType(String game) { if (game.equalsIgnoreCase("tic-tac-toe")) { @@ -97,9 +96,8 @@ public final class Server { ); loading.setOnFailure(() -> { - WidgetContainer.getCurrentView().transitionPrevious(); - a.unsubscribe("connecting"); - a.unsubscribe("startclient"); + if (WidgetContainer.getCurrentView() == loading) WidgetContainer.getCurrentView().transitionPrevious(); + a.unsubscribeAll(); WidgetContainer.add( Pos.CENTER, new ErrorPopup(AppContext.getString("connecting-failed") + " " + ip + ":" + port) @@ -112,7 +110,8 @@ public final class Server { return; } - WidgetContainer.getCurrentView().transitionPrevious(); + primary = new ServerView(user, this::sendChallenge); + WidgetContainer.getCurrentView().transitionNextCustom(primary, "disconnect", this::disconnect); a.unsubscribe("connecting"); a.unsubscribe("startclient"); @@ -122,11 +121,11 @@ public final class Server { new EventFlow().addPostEvent(new NetworkEvents.SendLogin(clientId, user)).postEvent(); - primary = new ServerView(user, this::sendChallenge, this::disconnect); - WidgetContainer.getCurrentView().transitionNext(primary); - startPopulateScheduler(); populateGameList(); + + primary.removeViewFromPreviousChain(loading); + }, false, "startclient") .listen( NetworkEvents.ConnectTry.class, @@ -146,20 +145,24 @@ public final class Server { ) .postEvent(); - eventFlow.listen(NetworkEvents.ChallengeResponse.class, this::handleReceivedChallenge, false) - .listen(NetworkEvents.GameMatchResponse.class, this::handleMatchResponse, false) - .listen(NetworkEvents.GameResultResponse.class, this::handleGameResult, false) - .listen(NetworkEvents.GameMoveResponse.class, this::handleReceivedMove, false) - .listen(NetworkEvents.YourTurnResponse.class, this::handleYourTurn, false); + a.listen(NetworkEvents.ChallengeResponse.class, this::handleReceivedChallenge, false, "challenge") + .listen(NetworkEvents.GameMatchResponse.class, this::handleMatchResponse, false, "match-response") + .listen(NetworkEvents.GameResultResponse.class, this::handleGameResult, false, "game-result") + .listen(NetworkEvents.GameMoveResponse.class, this::handleReceivedMove, false, "game-move") + .listen(NetworkEvents.YourTurnResponse.class, this::handleYourTurn, false, "your-turn"); + + connectFlow = a; } private void sendChallenge(String opponent) { if (!isPolling) return; - new SendChallengePopup(this, opponent, (playerInformation, gameType) -> { + var a = new SendChallengePopup(this, opponent, (playerInformation, gameType) -> { new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType)).postEvent(); isSingleGame.set(true); }); + + a.show(Pos.CENTER); } private void handleMatchResponse(NetworkEvents.GameMatchResponse response) { @@ -195,7 +198,7 @@ public final class Server { /*switch (type){ case TICTACTOE ->{ - players[myTurn] = new ArtificialPlayer<>(new TicTacToeAIR(), user); + players[myTurn] = new ArtificialPlayer<>(new TicTacToeAIR(9), user); } case REVERSI ->{ players[myTurn] = new ArtificialPlayer<>(new ReversiAIR(), user); @@ -254,11 +257,13 @@ public final class Server { String challengerName = extractQuotedValue(response.challengerName()); String gameType = extractQuotedValue(response.gameType()); final String finalGameType = gameType; - new ChallengePopup(challengerName, gameType, (playerInformation) -> { + var a = new ChallengePopup(challengerName, gameType, (playerInformation) -> { final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", "")); new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent(); isSingleGame.set(true); }); + + a.show(Pos.CENTER); } private void sendMessage(String message) { @@ -269,7 +274,9 @@ public final class Server { new EventFlow().addPostEvent(new NetworkEvents.CloseClient(clientId)).postEvent(); isPolling = false; stopScheduler(); - primary.transitionPrevious(); + connectFlow.unsubscribeAll(); + + WidgetContainer.getCurrentView().transitionPrevious(); } private void forfeitGame() { diff --git a/app/src/main/java/org/toop/app/widget/Widget.java b/app/src/main/java/org/toop/app/widget/Widget.java index 5f7a269..7278a05 100644 --- a/app/src/main/java/org/toop/app/widget/Widget.java +++ b/app/src/main/java/org/toop/app/widget/Widget.java @@ -2,19 +2,27 @@ package org.toop.app.widget; import javafx.geometry.Pos; import javafx.scene.Node; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public interface Widget { + Logger logger = LogManager.getLogger(Widget.class); + Node getNode(); default void show(Pos position) { + logger.debug("Showing Widget: {} at position: {}", this.getNode(), position.toString()); WidgetContainer.add(position, this); } default void hide() { + logger.debug("Hiding Widget: {}", this.getNode()); WidgetContainer.remove(this); } default void replace(Pos position, Widget widget) { + logger.debug("Replacing Widget: {}, with widget: {}, to position: {}", + this.getNode(), widget.getNode(), position.toString()); widget.show(position); hide(); } diff --git a/app/src/main/java/org/toop/app/widget/WidgetContainer.java b/app/src/main/java/org/toop/app/widget/WidgetContainer.java index fe54d45..44dbc9c 100644 --- a/app/src/main/java/org/toop/app/widget/WidgetContainer.java +++ b/app/src/main/java/org/toop/app/widget/WidgetContainer.java @@ -1,5 +1,7 @@ package org.toop.app.widget; +import javafx.collections.ObservableList; +import javafx.scene.Node; import org.toop.app.widget.complex.PopupWidget; import org.toop.app.widget.complex.ViewWidget; @@ -7,6 +9,10 @@ import javafx.application.Platform; import javafx.geometry.Pos; import javafx.scene.layout.StackPane; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + public final class WidgetContainer { private static StackPane root; private static ViewWidget currentView; @@ -38,7 +44,7 @@ public final class WidgetContainer { root.getChildren().addFirst(view.getNode()); currentView = view; } else if (widget instanceof PopupWidget popup) { - currentView.add(Pos.CENTER, popup); + currentView.add(Pos.CENTER, (Widget) popup); } else { root.getChildren().add(widget.getNode()); } @@ -52,13 +58,61 @@ public final class WidgetContainer { Platform.runLater(() -> { if (widget instanceof PopupWidget popup) { - currentView.remove(popup); + currentView.remove((Widget) popup); } else { root.getChildren().remove(widget.getNode()); } }); } + public static void remove(Class widgetClass) { + if (root == null || currentView == null) return; + + Platform.runLater(() -> + currentView.getChildren().removeIf(widget -> widget.getClass().isAssignableFrom(widgetClass)) + ); + } + + public static void removeFirst(Class widgetClass) { + if (root == null || currentView == null) return; + + Platform.runLater(() -> { + for (Node widget : currentView.getChildren()) { + if (widgetClass.isAssignableFrom(widget.getClass())) { + currentView.getChildren().remove(widget); + break; + } + } + }); + } + + public static List find(Class widgetClass) { + if (root == null || currentView == null) return null; + + return getAllWidgets() + .stream() + .filter(widget -> widget.getClass().isAssignableFrom(widgetClass)) + .toList(); + } + + public static List find(Predicate predicate) { + if (root == null || currentView == null) return null; + + return getAllWidgets() + .stream() + .filter(predicate) + .toList(); + } + + public static Widget findFirst(Class widgetClass) { + if (root == null || currentView == null) return null; + + return getAllWidgets() + .stream() + .filter(widget -> widget.getClass().isAssignableFrom(widgetClass)) + .findFirst().orElse(null); + } + public static ViewWidget getCurrentView() { return currentView; } @@ -74,4 +128,22 @@ public final class WidgetContainer { currentView = view; }); } + + public static List getAllWidgets() { + final List children = new ArrayList<>(); + + for (var child : root.getChildren()) { + if (child instanceof Widget widget) { + children.add(widget); + } + } + + for (var child : currentView.getNode().getChildren()) { + if (child instanceof Widget widget) { + children.add(widget); + } + } + + return children; + } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/widget/complex/LoadingWidget.java b/app/src/main/java/org/toop/app/widget/complex/LoadingWidget.java index 241c4ce..61f4007 100644 --- a/app/src/main/java/org/toop/app/widget/complex/LoadingWidget.java +++ b/app/src/main/java/org/toop/app/widget/complex/LoadingWidget.java @@ -2,6 +2,7 @@ package org.toop.app.widget.complex; import javafx.application.Platform; import javafx.geometry.Pos; +import javafx.scene.Node; import javafx.scene.control.Control; import javafx.scene.control.ProgressBar; import javafx.scene.control.ProgressIndicator; @@ -144,11 +145,11 @@ public class LoadingWidget extends ViewWidget implements Update { // TODO make o if (successTrigger.call()) { triggerSuccess(); - this.remove(this); + this.remove((Node) this); return; } else if (failureTrigger.call()) { triggerFailure(); - this.remove(this); + this.remove((Node) this); return; } diff --git a/app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java b/app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java index 90ec2a5..c69edf4 100644 --- a/app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java +++ b/app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java @@ -5,10 +5,13 @@ import org.toop.app.widget.Primitive; import javafx.scene.Node; import javafx.scene.layout.VBox; +import javafx.scene.text.Text; public class PlayerInfoWidget { private final GameInformation.Player information; private final VBox container; + private Text playerName; + private boolean hasSet; public PlayerInfoWidget(GameInformation.Player information) { this.information = information; @@ -16,6 +19,7 @@ public class PlayerInfoWidget { buildToggle().getNode(), buildContent() ); + this.playerName = null; } private ToggleWidget buildToggle() { @@ -33,51 +37,69 @@ public class PlayerInfoWidget { } private Node buildContent() { - if (information.isHuman) { - var nameInput = new LabeledInputWidget( - "name", - "enter-your-name", - information.name, - newName -> information.name = newName - ); + if (information.isHuman) { + var nameInput = new LabeledInputWidget( + "name", + "enter-your-name", + information.name, + newName -> information.name = newName + ); - return nameInput.getNode(); - } else { - if (information.name == null || information.name.isEmpty()) { - information.name = "Pism Bot"; - } + return nameInput.getNode(); + } else { + var AIBox = Primitive.vbox( + makeAIButton(0, 1, "zwartepiet"), + makeAIButton(2, 1, "sinterklaas"), + makeAIButton(9, 1, "santa") + ); - var playerName = Primitive.text(""); - playerName.setText(information.name); + this.playerName = Primitive.text(""); + playerName.setText(information.name); - var nameDisplay = Primitive.vbox( - Primitive.text("name"), - playerName - ); + var nameDisplay = Primitive.vbox( + Primitive.text("name"), + playerName + ); - var difficultySlider = new LabeledSliderWidget( - "computer-difficulty", - 0, 5, - information.computerDifficulty, - newVal -> information.computerDifficulty = newVal - ); + if (!hasSet) { + doDefault(); + hasSet = true; + } - var thinkTimeSlider = new LabeledSliderWidget( - "computer-think-time", - 0, 5, - information.computerThinkTime, - newVal -> information.computerThinkTime = newVal - ); + return Primitive.vbox( + AIBox, + nameDisplay + ); - return Primitive.vbox( - nameDisplay, - difficultySlider.getNode(), - thinkTimeSlider.getNode() - ); - } - } + } + } public Node getNode() { return container; } + + private Node makeAIButton(int depth, int thinktime, String name) { + return Primitive.button(name, () -> { + information.name = getName(name); + information.computerDifficulty = depth; + information.computerThinkTime = thinktime; + this.playerName.setText(getName(name)); + }); + } + + private String getName(String name) { + return switch (name) { + case "sinterklaas" -> "Sint. R. Klaas"; + case "zwartepiet" -> "Zwarte Piet"; + case "santa" -> "Santa"; + default -> "Default"; + }; + } + + private void doDefault() { + information.name = getName("zwartepiet"); + information.computerDifficulty = 0; + information.computerThinkTime = 1; + this.playerName.setText(getName("zwartepiet")); + } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/widget/complex/StackWidget.java b/app/src/main/java/org/toop/app/widget/complex/StackWidget.java index 1bb70ff..3027c2e 100644 --- a/app/src/main/java/org/toop/app/widget/complex/StackWidget.java +++ b/app/src/main/java/org/toop/app/widget/complex/StackWidget.java @@ -7,22 +7,19 @@ import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.layout.StackPane; -public abstract class StackWidget implements Widget { - private final StackPane container; - +public abstract class StackWidget extends StackPane implements Widget { public StackWidget(String cssClass) { - container = new StackPane(); - container.getStyleClass().add(cssClass); + this.getStyleClass().add(cssClass); } public void add(Pos position, Node node) { Platform.runLater(() -> { - if (container.getChildren().contains(node)) { + if (this.getChildren().contains(node)) { return; } StackPane.setAlignment(node, position); - container.getChildren().add(node); + this.getChildren().add(node); }); } @@ -32,7 +29,7 @@ public abstract class StackWidget implements Widget { public void remove(Node node) { Platform.runLater(() -> { - container.getChildren().remove(node); + this.getChildren().remove(node); }); } @@ -41,7 +38,7 @@ public abstract class StackWidget implements Widget { } @Override - public Node getNode() { - return container; + public StackPane getNode() { + return this; } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/widget/complex/ViewWidget.java b/app/src/main/java/org/toop/app/widget/complex/ViewWidget.java index fd0dbb1..217ac86 100644 --- a/app/src/main/java/org/toop/app/widget/complex/ViewWidget.java +++ b/app/src/main/java/org/toop/app/widget/complex/ViewWidget.java @@ -37,6 +37,19 @@ public abstract class ViewWidget extends StackWidget { view.add(Pos.BOTTOM_LEFT, Primitive.vbox(backButton)); } + public void transitionNextCustom(ViewWidget view, String key, Runnable runnable) { + view.previous = this; + + replace(Pos.CENTER, view); + + var customButton = Primitive.button(key, () -> { + runnable.run(); + view.transitionPrevious(); + }); + + view.add(Pos.BOTTOM_LEFT, Primitive.vbox(customButton)); + } + public void transitionPrevious() { if (previous == null) { return; @@ -46,6 +59,38 @@ public abstract class ViewWidget extends StackWidget { previous = null; } + public void removeIndexFromPreviousChain(int index) { + ViewWidget view = this; + + while (index > 0 && view != null) { + index--; + + if (index == 0) { + if (view.previous != null && view.previous.previous != null) { + view.previous = view.previous.previous; + } + } + + view = view.previous; + } + } + + public void removeViewFromPreviousChain(ViewWidget view) { + ViewWidget prev = previous; + int index = 0; + + while (prev != null) { + index++; + + if (prev == view) { + removeIndexFromPreviousChain(index); + break; + } + + prev = prev.previous; + } + } + public void reload(ViewWidget view) { view.previous = previous; replace(Pos.CENTER, view); diff --git a/app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java b/app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java index 1a2f755..cd4d87c 100644 --- a/app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java +++ b/app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java @@ -8,6 +8,7 @@ import org.toop.app.widget.complex.PopupWidget; import java.util.function.Consumer; import javafx.geometry.Pos; +import org.toop.local.AppContext; public final class ChallengePopup extends PopupWidget { private final GameInformation.Player playerInformation; @@ -28,19 +29,22 @@ public final class ChallengePopup extends PopupWidget { private void setupLayout() { var challengeText = Primitive.text("you-were-challenged-by"); - var challengerHeader = Primitive.header(""); - challengerHeader.setText(challenger); + var challengerHeader = Primitive.header(challenger, false); - var gameText = Primitive.text("to-a-game-of"); - gameText.setText(gameText.getText() + " " + game); + var toAGameOfText = Primitive.text("to-a-game-of"); + var gameHeader = Primitive.header(game, false); - var acceptButton = Primitive.button("accept", () -> onAccept.accept(playerInformation)); + var acceptButton = Primitive.button("accept", () -> { + onAccept.accept(playerInformation); + this.hide(); + }); var denyButton = Primitive.button("deny", () -> hide()); var leftSection = Primitive.vbox( challengeText, challengerHeader, - gameText, + toAGameOfText, + gameHeader, Primitive.separator(), Primitive.hbox( acceptButton, diff --git a/app/src/main/java/org/toop/app/widget/popup/EscapePopup.java b/app/src/main/java/org/toop/app/widget/popup/EscapePopup.java new file mode 100644 index 0000000..e36c82a --- /dev/null +++ b/app/src/main/java/org/toop/app/widget/popup/EscapePopup.java @@ -0,0 +1,31 @@ +package org.toop.app.widget.popup; + +import javafx.geometry.Pos; +import org.toop.app.App; +import org.toop.app.widget.Primitive; +import org.toop.app.widget.WidgetContainer; +import org.toop.app.widget.complex.ConfirmWidget; +import org.toop.app.widget.complex.PopupWidget; +import org.toop.app.widget.view.OptionsView; + +public class EscapePopup extends PopupWidget { + public EscapePopup() { + var con = Primitive.button("Continue", this::hide, false); // TODO, localize + + var qui = Primitive.button("quit", () -> { + hide(); + WidgetContainer.add(Pos.CENTER, new QuitPopup()); + }); + + if (!(WidgetContainer.getCurrentView().getClass().isAssignableFrom(OptionsView.class))) { + var opt = Primitive.button("options", () -> { + hide(); + WidgetContainer.getCurrentView().transitionNext(new OptionsView()); + }); + add(Pos.CENTER, Primitive.vbox(con, opt, qui)); + } else { + add(Pos.CENTER, Primitive.vbox(con, qui)); + } + + } +} diff --git a/app/src/main/java/org/toop/app/widget/popup/QuitPopup.java b/app/src/main/java/org/toop/app/widget/popup/QuitPopup.java index 264e936..593a7d8 100644 --- a/app/src/main/java/org/toop/app/widget/popup/QuitPopup.java +++ b/app/src/main/java/org/toop/app/widget/popup/QuitPopup.java @@ -15,14 +15,12 @@ public class QuitPopup extends PopupWidget { }); confirmWidget.addButton("no", () -> { - App.stopQuit(); hide(); }); add(Pos.CENTER, confirmWidget); setOnPop(() -> { - App.stopQuit(); hide(); }); } diff --git a/app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java b/app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java index 6299fdc..bc42486 100644 --- a/app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java +++ b/app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java @@ -34,7 +34,7 @@ public final class SendChallengePopup extends PopupWidget { // --- Left side: challenge text and buttons --- var challengeText = Primitive.text("challenge"); - var opponentHeader = Primitive.header(opponent); + var opponentHeader = Primitive.header(opponent, false); var gameText = Primitive.text("to-a-game-of"); @@ -61,7 +61,7 @@ public final class SendChallengePopup extends PopupWidget { var sendButton = Primitive.button( "send", - () -> onSend.accept(playerInformation, gameChoice.getValue()) + () -> { onSend.accept(playerInformation, gameChoice.getValue()); this.hide(); } ); var cancelButton = Primitive.button("cancel", () -> hide()); diff --git a/app/src/main/java/org/toop/app/widget/view/GameView.java b/app/src/main/java/org/toop/app/widget/view/GameView.java index b866ef4..84cdf14 100644 --- a/app/src/main/java/org/toop/app/widget/view/GameView.java +++ b/app/src/main/java/org/toop/app/widget/view/GameView.java @@ -1,34 +1,44 @@ package org.toop.app.widget.view; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.text.Font; import org.toop.app.widget.Primitive; import org.toop.app.widget.complex.ViewWidget; import org.toop.app.widget.popup.GameOverPopup; - import java.util.function.Consumer; - import javafx.application.Platform; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.text.Text; -import org.toop.app.widget.tutorial.BaseTutorialWidget; import org.toop.app.widget.tutorial.Connect4TutorialWidget; import org.toop.app.widget.tutorial.ReversiTutorialWidget; import org.toop.app.widget.tutorial.TicTacToeTutorialWidget; +import org.toop.local.AppContext; public final class GameView extends ViewWidget { - private final Text currentPlayerHeader; - private final Text currentMoveHeader; - private final Text nextPlayerHeader; + private final Text playerHeader; + private final Text turnHeader; + private final Text player1Header; + private final Text player2Header; + private Circle player1Icon; + private Circle player2Icon; private final Button forfeitButton; private final Button exitButton; private final Button tutorialButton; private final TextField chatInput; + private final Text keyThingy; + private boolean hasSet = false; public GameView(Runnable onForfeit, Runnable onExit, Consumer onMessage, String gameType) { - currentPlayerHeader = Primitive.header(""); - currentMoveHeader = Primitive.header(""); - nextPlayerHeader = Primitive.header(""); + playerHeader = Primitive.header(""); + turnHeader = Primitive.header(""); + keyThingy = Primitive.text("turnof"); + player1Header = Primitive.header(""); + player2Header = Primitive.header(""); + player1Icon = new Circle(); + player2Icon = new Circle(); if (onForfeit != null) { forfeitButton = Primitive.button("forfeit", () -> onForfeit.run()); @@ -66,17 +76,11 @@ public final class GameView extends ViewWidget { } private void setupLayout() { - var playerInfo = Primitive.vbox( - currentPlayerHeader, - Primitive.hbox( - Primitive.separator(), - currentMoveHeader, - Primitive.separator() - ), - nextPlayerHeader - ); + var turnInfo = Primitive.vbox( + turnHeader + ); - add(Pos.TOP_RIGHT, playerInfo); + add(Pos.TOP_CENTER, turnInfo); var buttons = Primitive.vbox( forfeitButton, @@ -94,21 +98,86 @@ public final class GameView extends ViewWidget { } } - public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) { + public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer, char GameType) { Platform.runLater(() -> { - currentPlayerHeader.setText(currentPlayer); - currentMoveHeader.setText(currentMove); - nextPlayerHeader.setText(nextPlayer); - - if (isMe) { - currentPlayerHeader.getStyleClass().add("my-turn"); - } else { - currentPlayerHeader.getStyleClass().remove("my-turn"); - } + if (!(hasSet)) { + playerHeader.setText(currentPlayer + " vs. " + nextPlayer); + hasSet = true; + setPlayerHeaders(isMe, currentPlayer, nextPlayer, GameType); + } + //TODO idk if theres any way to check this? only EN uses 's and the rest doesnt. if theres a better way to do this pls let me know + if (AppContext.getLocale().toLanguageTag().equals("en")) { + turnHeader.setText(currentPlayer + keyThingy.getText()); + } }); } public void gameOver(boolean iWon, String winner) { new GameOverPopup(iWon, winner).show(Pos.CENTER); } + + private void setPlayerHeaders(boolean isMe, String currentPlayer, String nextPlayer, char GameType) { + if (GameType == 'T') { + if (isMe) { + player1Header.setText("X: " + currentPlayer); + player2Header.setText("O: " + nextPlayer); + } + else { + player1Header.setText("X: " + nextPlayer); + player2Header.setText("O: " + currentPlayer); + } + setPlayerInfoTTT(); + } + else if (GameType == 'R') { + if (isMe) { + player1Header.setText(currentPlayer); + player2Header.setText(nextPlayer); + } + else { + player1Header.setText(nextPlayer); + player2Header.setText(currentPlayer); + } + setPlayerInfoReversi(); + } + } + + private void setPlayerInfoTTT() { + var playerInfo = Primitive.vbox( + playerHeader, + Primitive.separator(), + player1Header, + player2Header + ); + + add(Pos.TOP_RIGHT, playerInfo); + } + + private void setPlayerInfoReversi() { + var player1box = Primitive.hbox( + player1Icon, + player1Header + ); + + player1box.getStyleClass().add("hboxspacing"); + + var player2box = Primitive.hbox( + player2Icon, + player2Header + ); + + player2box.getStyleClass().add("hboxspacing"); + + var playerInfo = Primitive.vbox( + playerHeader, + Primitive.separator(), + player1box, + player2box + ); + + player1Icon.setRadius(player1Header.fontProperty().map(Font::getSize).getValue()); + player2Icon.setRadius(player2Header.fontProperty().map(Font::getSize).getValue()); + player1Icon.setFill(Color.BLACK); + player2Icon.setFill(Color.WHITE); + add(Pos.TOP_RIGHT, playerInfo); + } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/widget/view/MainView.java b/app/src/main/java/org/toop/app/widget/view/MainView.java index 15e5c03..ee93350 100644 --- a/app/src/main/java/org/toop/app/widget/view/MainView.java +++ b/app/src/main/java/org/toop/app/widget/view/MainView.java @@ -4,6 +4,7 @@ import org.toop.app.App; import org.toop.app.widget.Primitive; import org.toop.app.widget.complex.ViewWidget; import javafx.geometry.Pos; +import org.toop.app.widget.popup.QuitPopup; public class MainView extends ViewWidget { public MainView() { @@ -24,7 +25,8 @@ public class MainView extends ViewWidget { }); var quitButton = Primitive.button("quit", () -> { - App.startQuit(); + var a = new QuitPopup(); + a.show(Pos.CENTER); }); add(Pos.CENTER, Primitive.vbox( diff --git a/app/src/main/java/org/toop/app/widget/view/ServerView.java b/app/src/main/java/org/toop/app/widget/view/ServerView.java index 94da1e2..7365e74 100644 --- a/app/src/main/java/org/toop/app/widget/view/ServerView.java +++ b/app/src/main/java/org/toop/app/widget/view/ServerView.java @@ -14,14 +14,12 @@ import javafx.scene.control.ListView; public final class ServerView extends ViewWidget { private final String user; private final Consumer onPlayerClicked; - private final Runnable onDisconnect; private final ListView