redesign. add: themes and text size

This commit is contained in:
ramollia
2025-10-06 21:08:36 +02:00
parent cfde1f8d7f
commit acc9972c80
44 changed files with 1637 additions and 757 deletions

View File

@@ -2,7 +2,7 @@ package org.toop.app;
import org.toop.app.layer.Layer;
import org.toop.app.layer.layers.MainLayer;
import org.toop.app.layer.layers.QuitLayer;
import org.toop.app.layer.layers.QuitPopup;
import org.toop.framework.asset.ResourceManager;
import org.toop.framework.asset.resources.CssAsset;
import org.toop.framework.audio.events.AudioEvents;
@@ -19,7 +19,9 @@ import java.util.Stack;
public final class App extends Application {
private static Stage stage;
private static Scene scene;
private static StackPane root;
private static Stack<Layer> stack;
private static int height;
private static int width;
@@ -32,17 +34,8 @@ public final class App extends Application {
@Override
public void start(Stage stage) throws Exception {
App.stage = stage;
final StackPane root = new StackPane();
App.root = root;
App.stack = new Stack<>();
AppSettings settings = new AppSettings();
settings.applySettings();
final Scene scene = new Scene(root);
scene.getStylesheets().add(ResourceManager.<CssAsset>get("app.css").getUrl());
stage.setTitle(AppContext.getString("appTitle"));
stage.setWidth(1080);
@@ -62,7 +55,9 @@ public final class App extends Application {
stage.show();
App.stage = stage;
App.scene = scene;
App.root = root;
App.stack = new Stack<>();
App.width = (int) stage.getWidth();
@@ -70,6 +65,9 @@ public final class App extends Application {
App.isQuitting = false;
final AppSettings settings = new AppSettings();
settings.applySettings();
new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
activate(new MainLayer());
}
@@ -102,7 +100,7 @@ public final class App extends Application {
}
public static void quitPopup() {
push(new QuitLayer());
push(new QuitPopup());
isQuitting = true;
}
@@ -127,6 +125,19 @@ public final class App extends Application {
reloadAll();
}
public static void setStyle(String theme, String layoutSize) {
final int stylesCount = scene.getStylesheets().size();
for (int i = 0; i < stylesCount; i++) {
scene.getStylesheets().removeLast();
}
scene.getStylesheets().add(ResourceManager.<CssAsset>get(theme + ".css").getUrl());
scene.getStylesheets().add(ResourceManager.<CssAsset>get(layoutSize + ".css").getUrl());
reloadAll();
}
public static int getWidth() {
return width;
}

View File

@@ -1,163 +1,11 @@
package org.toop.app.layer;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.Region;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import java.util.function.Consumer;
public abstract class Container {
public abstract Region getContainer();
public abstract void addNode(Node node);
public abstract void addNodes(Node... nodes);
public abstract void addContainer(Container container, boolean fill);
public Text addText(String cssClass, String x, boolean wrap) {
final Text element = new Text(x);
element.getStyleClass().add(cssClass);
if (wrap) {
addNode(new TextFlow(element));
} else {
addNode(element);
}
return element;
}
public Text addText(String x, boolean wrap) {
return addText("text", x, wrap);
}
public Label addButton(String cssClass, String x, Runnable runnable) {
final Label element = new Label(x);
element.getStyleClass().add(cssClass);
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
runnable.run();
});
addNode(element);
return element;
}
public Label addButton(String x, Runnable runnable) {
return addButton("button", x, runnable);
}
public Label addToggle(String cssClass, String x1, String x2, boolean toggled, Consumer<Boolean> consumer) {
final Label element = new Label(toggled ? x2 : x1);
element.getStyleClass().add(cssClass);
final BooleanProperty checked = new SimpleBooleanProperty(toggled);
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
checked.set(!checked.get());
if (checked.get()) {
element.setText(x1);
} else {
element.setText(x2);
}
consumer.accept(checked.get());
});
addNode(element);
return element;
}
public Label addToggle(String x1, String x2, boolean toggled, Consumer<Boolean> consumer) {
return addToggle("toggle", x1, x2, toggled, consumer);
}
public Slider addSlider(String cssClass, int max, int initial, Consumer<Integer> consumer) {
final Slider element = new Slider(0, max, initial);
element.getStyleClass().add(cssClass);
element.setMinorTickCount(0);
element.setMajorTickUnit(1);
element.setBlockIncrement(1);
element.setSnapToTicks(true);
element.setShowTickLabels(true);
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.valueProperty().addListener((_, _, newValue) -> {
consumer.accept(newValue.intValue());
});
addNode(element);
return element;
}
public Slider addSlider(int max, int initial, Consumer<Integer> consumer) {
return addSlider("slider", max, initial, consumer);
}
public TextField addInput(String cssClass, String input, Consumer<String> consumer) {
final TextField element = new TextField(input);
element.getStyleClass().add(cssClass);
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.textProperty().addListener((_, _, newValue) -> {
consumer.accept(newValue);
});
addNode(element);
return element;
}
public TextField addInput(String input, Consumer<String> consumer) {
return addInput("input", input, consumer);
}
public <T> ChoiceBox<T> addChoiceBox(String cssClass, Consumer<T> consumer) {
final ChoiceBox<T> element = new ChoiceBox<>();
element.getStyleClass().add(cssClass);
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.valueProperty().addListener((_, _, newValue) -> {
consumer.accept(newValue);
});
addNode(element);
return element;
}
public <T> ChoiceBox<T> addChoiceBox(Consumer<T> consumer) {
return addChoiceBox("choicebox", consumer);
}
public Separator addSeparator(String cssClass, boolean horizontal) {
final Separator element = new Separator(horizontal ? Orientation.HORIZONTAL : Orientation.VERTICAL);
element.getStyleClass().add(cssClass);
addNode(element);
return element;
}
public Separator addSeparator(boolean horizontal) {
return addSeparator("separator", horizontal);
}
}

View File

@@ -2,8 +2,6 @@ package org.toop.app.layer;
import org.toop.app.App;
import org.toop.app.canvas.GameCanvas;
import org.toop.framework.asset.ResourceManager;
import org.toop.framework.asset.resources.CssAsset;
import javafx.geometry.Pos;
import javafx.scene.layout.Region;
@@ -13,12 +11,11 @@ public abstract class Layer {
protected StackPane layer;
protected Region background;
protected Layer(String cssFile) {
protected Layer(String... backgroundStyles) {
layer = new StackPane();
layer.getStylesheets().add(ResourceManager.<CssAsset>get(cssFile).getUrl());
background = new Region();
background.getStyleClass().add("background");
background.getStyleClass().addAll(backgroundStyles);
background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
layer.getChildren().addLast(background);

View File

@@ -0,0 +1,131 @@
package org.toop.app.layer;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.text.Text;
import java.util.function.Consumer;
public final class NodeBuilder {
public static void addCss(Node node, String... cssClasses) {
node.getStyleClass().addAll(cssClasses);
}
public static void setCss(Node node, String... cssClasses) {
node.getStyleClass().removeAll();
node.getStyleClass().addAll(cssClasses);
}
public static Text header(String x) {
final Text element = new Text(x);
setCss(element, "text-primary", "text-header");
return element;
}
public static Text text(String x) {
final Text element = new Text(x);
setCss(element, "text-secondary", "text-normal");
return element;
}
public static Label button(String x, Runnable runnable) {
final Label element = new Label(x);
setCss(element, "button", "text-normal");
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
runnable.run();
});
return element;
}
public static Label toggle(String x1, String x2, boolean toggled, Consumer<Boolean> consumer) {
final Label element = new Label(toggled ? x2 : x1);
setCss(element, "toggle", "text-normal");
final BooleanProperty checked = new SimpleBooleanProperty(toggled);
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
checked.set(!checked.get());
if (checked.get()) {
element.setText(x1);
} else {
element.setText(x2);
}
consumer.accept(checked.get());
});
return element;
}
public static Slider slider(int max, int initial, Consumer<Integer> consumer) {
final Slider element = new Slider(0, max, initial);
setCss(element, "bg-slider-track");
element.setMinorTickCount(0);
element.setMajorTickUnit(1);
element.setBlockIncrement(1);
element.setSnapToTicks(true);
element.setShowTickLabels(true);
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.valueProperty().addListener((_, _, newValue) -> {
consumer.accept(newValue.intValue());
});
return element;
}
public static TextField input(String x, Consumer<String> consumer) {
final TextField element = new TextField(x);
setCss(element, "input", "text-normal");
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.textProperty().addListener((_, _, newValue) -> {
consumer.accept(newValue);
});
return element;
}
public static <T> ChoiceBox<T> choiceBox(Consumer<T> consumer) {
final ChoiceBox<T> element = new ChoiceBox<>();
setCss(element, "choice-box", "text-normal");
element.setOnMouseClicked(_ -> {
new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
});
element.valueProperty().addListener((_, _, newValue) -> {
consumer.accept(newValue);
});
return element;
}
public static Separator separator() {
final Separator element = new Separator(Orientation.HORIZONTAL);
setCss(element, "separator");
return element;
}
}

View File

@@ -0,0 +1,19 @@
package org.toop.app.layer;
import org.toop.app.App;
public abstract class Popup extends Layer {
protected Popup(boolean popOnBackground, String... backgroundStyles) {
super(backgroundStyles);
if (popOnBackground) {
background.setOnMouseClicked(_ -> {
App.pop();
});
}
}
protected Popup(boolean popOnBackground) {
this(popOnBackground, "bg-popup");
}
}

View File

@@ -11,13 +11,13 @@ import javafx.scene.layout.Region;
public final class HorizontalContainer extends Container {
private final HBox container;
public HorizontalContainer(String cssClass, int spacing) {
public HorizontalContainer(int spacing, String... cssClasses) {
container = new HBox(spacing);
container.getStyleClass().add(cssClass);
container.getStyleClass().addAll(cssClasses);
}
public HorizontalContainer(int spacing) {
this("horizontal_container", spacing);
this(spacing, "container");
}
@Override
@@ -26,8 +26,8 @@ public final class HorizontalContainer extends Container {
}
@Override
public void addNode(Node node) {
container.getChildren().addLast(node);
public void addNodes(Node... nodes) {
container.getChildren().addAll(nodes);
}
@Override

View File

@@ -11,13 +11,13 @@ import javafx.scene.layout.VBox;
public final class VerticalContainer extends Container {
private final VBox container;
public VerticalContainer(String cssClass, int spacing) {
public VerticalContainer(int spacing, String... cssClasses) {
container = new VBox(spacing);
container.getStyleClass().add(cssClass);
container.getStyleClass().addAll(cssClasses);
}
public VerticalContainer(int spacing) {
this("vertical_container", spacing);
this(spacing, "container");
}
@Override
@@ -26,8 +26,8 @@ public final class VerticalContainer extends Container {
}
@Override
public void addNode(Node node) {
container.getChildren().addLast(node);
public void addNodes(Node... nodes) {
container.getChildren().addAll(nodes);
}
@Override

View File

@@ -2,7 +2,8 @@ package org.toop.app.layer.layers;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.Popup;
import org.toop.app.layer.containers.HorizontalContainer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.local.AppContext;
@@ -10,13 +11,14 @@ import org.toop.local.AppContext;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.geometry.Pos;
import javafx.scene.text.Text;
import javafx.util.Duration;
public final class CreditsLayer extends Layer {
public final class CreditsPopup extends Popup {
private final int lineHeight = 100;
CreditsLayer() {
super("credits.css");
public CreditsPopup() {
super(true, "bg-primary");
reload();
}
@@ -35,22 +37,19 @@ public final class CreditsLayer extends Layer {
AppContext.getString("opengl") + ": Omar"
};
final Container creditsContainer = new HorizontalContainer(0);
final Text[] creditsHeaders = new Text[credits.length];
final Container animatedContainer = new VerticalContainer("animated_credits_container", lineHeight);
creditsContainer.addContainer(animatedContainer, true);
for (final String credit : credits) {
animatedContainer.addText("credit-text", credit, false);
for (int i = 0; i < credits.length; i++) {
creditsHeaders[i] = NodeBuilder.header(credits[i]);
}
final Container controlContainer = new VerticalContainer(5);
controlContainer.addButton(AppContext.getString("back"), () -> {
App.activate(new MainLayer());
});
final Container creditsContainer = new HorizontalContainer(0);
final Container animatedContainer = new VerticalContainer(lineHeight);
creditsContainer.addContainer(animatedContainer, true);
animatedContainer.addNodes(creditsHeaders);
addContainer(creditsContainer, Pos.CENTER, 0, 0, 50, 100);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
playCredits(animatedContainer, App.getHeight());
}

View File

@@ -3,16 +3,15 @@ package org.toop.app.layer.layers;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.game.othello.Othello;
import org.toop.game.tictactoe.TicTacToe;
import org.toop.local.AppContext;
import javafx.geometry.Pos;
public final class MainLayer extends Layer {
public MainLayer() {
super("main.css");
super("bg-primary");
reload();
}
@@ -20,30 +19,32 @@ public final class MainLayer extends Layer {
public void reload() {
popAll();
final Container gamesContainer = new VerticalContainer(5);
gamesContainer.addButton(AppContext.getString("tictactoe"), () -> {
final var tictactoeButton = NodeBuilder.button(AppContext.getString("tictactoe"), () -> {
App.activate(new MultiplayerLayer());
});
gamesContainer.addButton(AppContext.getString("othello"), () -> {
final var othelloButton = NodeBuilder.button(AppContext.getString("othello"), () -> {
App.activate(new MultiplayerLayer());
});
final Container controlContainer = new VerticalContainer(5);
controlContainer.addButton(AppContext.getString("credits"), () -> {
App.activate(new CreditsLayer());
final var creditsButton = NodeBuilder.button(AppContext.getString("credits"), () -> {
App.push(new CreditsPopup());
});
controlContainer.addButton(AppContext.getString("options"), () -> {
App.activate(new OptionsLayer());
final var optionsButton = NodeBuilder.button(AppContext.getString("options"), () -> {
App.push(new OptionsPopup());
});
controlContainer.addButton(AppContext.getString("quit"), () -> {
final var quitButton = NodeBuilder.button(AppContext.getString("quit"), () -> {
App.quitPopup();
});
final Container gamesContainer = new VerticalContainer(5);
gamesContainer.addNodes(tictactoeButton, othelloButton);
final Container controlContainer = new VerticalContainer(5);
controlContainer.addNodes(creditsButton, optionsButton, quitButton);
addContainer(gamesContainer, Pos.TOP_LEFT, 2, 2, 20, 0);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 20, 0);
}

View File

@@ -1,18 +1,18 @@
package org.toop.app.layer.layers;
import javafx.geometry.Pos;
import javafx.scene.Node;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.containers.HorizontalContainer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.app.layer.layers.game.TicTacToeLayer;
import org.toop.local.AppContext;
import javafx.geometry.Pos;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public final class MultiplayerLayer extends Layer {
private boolean isConnectionLocal = true;
@@ -29,7 +29,7 @@ public final class MultiplayerLayer extends Layer {
private String serverPort = "";
public MultiplayerLayer() {
super("multiplayer.css");
super("bg-primary");
reload();
}
@@ -37,89 +37,127 @@ public final class MultiplayerLayer extends Layer {
public void reload() {
popAll();
final Container mainContainer = new VerticalContainer(5);
final Container player1Container = new VerticalContainer(20);
final Container player2Container = new VerticalContainer(20);
mainContainer.addToggle(AppContext.getString("local"), AppContext.getString("server"), !isConnectionLocal, (server) -> {
isConnectionLocal = !server;
reload();
});
final Container playersContainer = new HorizontalContainer(50);
mainContainer.addContainer(playersContainer, true);
final Container player1Container = new VerticalContainer("player_container", 5);
playersContainer.addContainer(player1Container, true);
playersContainer.addText("VS", false);
final Container player2Container = new VerticalContainer("player_container", 5);
playersContainer.addContainer(player2Container, true);
mainContainer.addButton(isConnectionLocal? AppContext.getString("start") : AppContext.getString("connect"), () -> {
App.activate(new TicTacToeLayer(new GameInformation(
new String[] { player1Name, player2Name },
new boolean[] { isPlayer1Human, isPlayer2Human },
new int[] { computer1Difficulty, computer2Difficulty },
isConnectionLocal, "127.0.0.1", "7789")));
// serverIP, serverPort)));
});
player1Container.addToggle(AppContext.getString("human"), AppContext.getString("computer"), !isPlayer1Human, (computer) -> {
final var isPlayer1HumanToggle = NodeBuilder.toggle(AppContext.getString("human"), AppContext.getString("computer"), !isPlayer1Human, (computer) -> {
isPlayer1Human = !computer;
reload();
});
player1Container.addNodes(isPlayer1HumanToggle);
if (isPlayer1Human) {
player1Container.addText(AppContext.getString("playerName"), true);
player1Container.addInput(player1Name, (name) -> {
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
final var playerNameInput = NodeBuilder.input(player1Name, (name) -> {
player1Name = name;
});
player1Container.addNodes(playerNameText, playerNameInput);
} else {
player1Name = "PismBot" + LocalDateTime.now().getSecond();
player1Container.addText(AppContext.getString("computerDifficulty"), true);
player1Container.addSlider(10, computer1Difficulty, (difficulty) ->
player1Name = "Pism Bot #" + LocalDateTime.now().getSecond();
final var computerNameText = NodeBuilder.text(player1Name);
final var computerNameSeparator = NodeBuilder.separator();
final var computerDifficultyText = NodeBuilder.text(AppContext.getString("computerDifficulty"));
final var computerDifficultySlider = NodeBuilder.slider(10, computer1Difficulty, (difficulty) ->
computer1Difficulty = difficulty);
player1Container.addNodes(computerNameText, computerNameSeparator,
computerDifficultyText, computerDifficultySlider);
}
if (isConnectionLocal) {
player2Container.addToggle(AppContext.getString("human"), AppContext.getString("computer"), !isPlayer2Human, (computer) -> {
final var isPlayer2HumanToggle = NodeBuilder.toggle(AppContext.getString("human"), AppContext.getString("computer"), !isPlayer2Human, (computer) -> {
isPlayer2Human = !computer;
reload();
});
player2Container.addNodes(isPlayer2HumanToggle);
if (isPlayer2Human) {
player2Container.addText(AppContext.getString("playerName"), true);
player2Container.addInput(player2Name, (name) -> {
final var playerNameText = NodeBuilder.text(AppContext.getString("playerName"));
final var playerNameInput = NodeBuilder.input(player2Name, (name) -> {
player2Name = name;
});
player2Container.addNodes(playerNameText, playerNameInput);
} else {
player2Container.addText(AppContext.getString("computerDifficulty"), true);
player2Container.addSlider(10, computer2Difficulty, (difficulty) ->
player2Name = "Pism Bot #" + LocalDateTime.now().getSecond();
final var computerNameText = NodeBuilder.text(player2Name);
final var computerNameSeparator = NodeBuilder.separator();
final var computerDifficultyText = NodeBuilder.text(AppContext.getString("computerDifficulty"));
final var computerDifficultySlider = NodeBuilder.slider(10, computer1Difficulty, (difficulty) ->
computer2Difficulty = difficulty);
player2Container.addNodes(computerNameText, computerNameSeparator,
computerDifficultyText, computerDifficultySlider);
}
} else {
player2Container.addText(AppContext.getString("serverIP"), true);
player2Container.addInput(serverIP, (ip) -> {
final var serverIPText = NodeBuilder.text(AppContext.getString("serverIP"));
final var serverIPSeparator = NodeBuilder.separator();
final var serverIPInput = NodeBuilder.input(serverIP, (ip) -> {
serverIP = ip;
});
player2Container.addSeparator(true);
player2Container.addText(AppContext.getString("serverPort"), true);
player2Container.addInput(serverPort, (port) -> {
final var serverPortText = NodeBuilder.text(AppContext.getString("serverPort"));
final var serverPortInput = NodeBuilder.input(serverPort, (port) -> {
serverPort = port;
});
player2Container.addNodes(serverIPText, serverIPInput, serverIPSeparator,
serverPortText, serverPortInput);
}
final Container controlContainer = new VerticalContainer(5);
final var versusText = NodeBuilder.text("VS");
controlContainer.addButton(AppContext.getString("back"), () -> {
final var connectionTypeText = NodeBuilder.text("Connection type (translate)");
final var connectionTypeToggle = NodeBuilder.toggle(AppContext.getString("local"), AppContext.getString("server"), !isConnectionLocal, (server) -> {
isConnectionLocal = !server;
reload();
});
final var playButton = NodeBuilder.button(isConnectionLocal ? AppContext.getString("start") : AppContext.getString("connect"), () -> {
if (isConnectionLocal) {
App.activate(new TicTacToeLayer(new GameInformation(
new String[]{player1Name, player2Name},
new boolean[]{isPlayer1Human, isPlayer2Human},
new int[]{computer1Difficulty, computer2Difficulty},
isConnectionLocal, serverIP, serverPort)));
} else {
App.activate(new TicTacToeLayer(new GameInformation(
new String[]{player1Name, player2Name},
new boolean[]{isPlayer1Human, isPlayer2Human},
new int[]{computer1Difficulty, computer2Difficulty},
isConnectionLocal, serverIP, serverPort)));
}
});
final Container mainContainer = new VerticalContainer(10);
final Container playersContainer = new HorizontalContainer(5);
final Container connectionTypeContainer = new HorizontalContainer(10);
mainContainer.addContainer(playersContainer, true);
mainContainer.addContainer(connectionTypeContainer, false);
mainContainer.addNodes(playButton);
connectionTypeContainer.addNodes(connectionTypeText, connectionTypeToggle);
playersContainer.addContainer(player1Container, true);
playersContainer.addNodes(versusText);
playersContainer.addContainer(player2Container, true);
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
App.activate(new MainLayer());
});
final Container controlContainer = new VerticalContainer(0);
controlContainer.addNodes(backButton);
addContainer(mainContainer, Pos.CENTER, 0, 0, 75, 75);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
}

View File

@@ -1,105 +0,0 @@
package org.toop.app.layer.layers;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.framework.asset.resources.SettingsAsset;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.local.AppContext;
import javafx.geometry.Pos;
import javafx.scene.control.ChoiceBox;
import org.toop.local.AppSettings;
import java.util.Locale;
public final class OptionsLayer extends Layer {
AppSettings appSettings = new AppSettings();
SettingsAsset settings = appSettings.getPath();
private int currentVolume = settings.getVolume();
private boolean isWindowed = !(settings.getFullscreen());
OptionsLayer() {
super("options.css");
reload();
}
@Override
public void reload() {
popAll();
final Container optionsContainer = new VerticalContainer(5);
optionsContainer.addText(AppContext.getString("language"), false);
addLanguageBox(optionsContainer);
optionsContainer.addSeparator(true);
optionsContainer.addText(AppContext.getString("volume"), false);
addVolumeSlider(optionsContainer);
optionsContainer.addSeparator(true);
addFullscreenToggle(optionsContainer);
final Container mainContainer = new VerticalContainer(50);
mainContainer.addText(AppContext.getString("options"), false);
mainContainer.addContainer(optionsContainer, true);
final Container controlContainer = new VerticalContainer(5);
controlContainer.addButton(AppContext.getString("back"), () -> {
App.activate(new MainLayer());
});
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 60);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
}
private void addLanguageBox(Container container) {
assert AppContext.getLocalization() != null;
final ChoiceBox<Locale> languageBox = container.addChoiceBox((locale) -> {
if (locale == AppContext.getLocale()) {
return;
}
AppContext.setLocale(locale);
settings.setLocale(locale.toString());
App.reloadAll();
});
for (final Locale localeFile : AppContext.getLocalization().getAvailableLocales()) {
languageBox.getItems().add(localeFile);
}
languageBox.setConverter(new javafx.util.StringConverter<>() {
@Override
public String toString(Locale locale) {
return AppContext.getString(locale.getDisplayName().toLowerCase());
}
@Override
public Locale fromString(String string) {
return null;
}
});
languageBox.setValue(AppContext.getLocale());
}
private void addVolumeSlider(Container container) {
container.addSlider(100, currentVolume, (volume) -> {
currentVolume = volume;
settings.setVolume(volume);
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(volume.doubleValue())).asyncPostEvent();
});
}
private void addFullscreenToggle(Container container) {
container.addToggle(AppContext.getString("windowed"), AppContext.getString("fullscreen"), !isWindowed, (fullscreen) -> {
isWindowed = !fullscreen;
App.setFullscreen(fullscreen);
settings.setFullscreen(fullscreen);
});
}
}

View File

@@ -0,0 +1,171 @@
package org.toop.app.layer.layers;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.Popup;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.framework.asset.resources.SettingsAsset;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.local.AppContext;
import org.toop.local.AppSettings;
import javafx.geometry.Pos;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import java.util.Locale;
public final class OptionsPopup extends Popup {
AppSettings appSettings = new AppSettings();
SettingsAsset settings = appSettings.getPath();
private boolean isWindowed = !(settings.getFullscreen());
public OptionsPopup() {
super(true, "bg-primary");
reload();
}
@Override
public void reload() {
popAll();
final var languageHeader = NodeBuilder.header(AppContext.getString("language"));
final var languageSeparator = NodeBuilder.separator();
final var volumeHeader = NodeBuilder.header(AppContext.getString("volume"));
final var volumeSeparator = NodeBuilder.separator();
final var themeHeader = NodeBuilder.header(AppContext.getString("theme"));
final var themeSeparator = NodeBuilder.separator();
final var layoutSizeHeader = NodeBuilder.header(AppContext.getString("layoutSize"));
final var layoutSizeSeparator = NodeBuilder.separator();
final var optionsContainer = new VerticalContainer(5);
optionsContainer.addNodes(languageHeader, languageChoiceBox(), languageSeparator);
optionsContainer.addNodes(volumeHeader, volumeSlider(), volumeSeparator);
optionsContainer.addNodes(themeHeader, themeChoiceBox(), themeSeparator);
optionsContainer.addNodes(layoutSizeHeader, layoutSizeChoiceBox(), layoutSizeSeparator);
optionsContainer.addNodes(fullscreenToggle());
final Container mainContainer = new VerticalContainer(50, "");
mainContainer.addContainer(optionsContainer, true);
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
App.pop();
});
final Container controlContainer = new VerticalContainer(5);
controlContainer.addNodes(backButton);
addContainer(mainContainer, Pos.CENTER, 0, 0, 0, 0);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
}
private ChoiceBox<Locale> languageChoiceBox() {
assert AppContext.getLocalization() != null;
final ChoiceBox<Locale> languageChoiceBox = NodeBuilder.choiceBox((locale) -> {
if (locale == AppContext.getLocale()) {
return;
}
settings.setLocale(locale.toString());
AppContext.setLocale(locale);
App.reloadAll();
});
languageChoiceBox.setConverter(new javafx.util.StringConverter<>() {
@Override
public String toString(Locale locale) {
return AppContext.getString(locale.getDisplayName().toLowerCase());
}
@Override
public Locale fromString(String string) {
return null;
}
});
languageChoiceBox.getItems().addAll(AppContext.getLocalization().getAvailableLocales());
languageChoiceBox.setValue(AppContext.getLocale());
return languageChoiceBox;
}
private Slider volumeSlider() {
return NodeBuilder.slider(100, settings.getVolume(), (volume) -> {
settings.setVolume(volume);
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(volume.doubleValue())).asyncPostEvent();
});
}
private Label fullscreenToggle() {
return NodeBuilder.toggle(AppContext.getString("windowed"), AppContext.getString("fullscreen"), !isWindowed, (fullscreen) -> {
isWindowed = !fullscreen;
settings.setFullscreen(fullscreen);
App.setFullscreen(fullscreen);
});
}
private ChoiceBox<String> themeChoiceBox() {
final ChoiceBox<String> themeChoiceBox = NodeBuilder.choiceBox((theme) -> {
if (theme.equalsIgnoreCase(settings.getTheme())) {
return;
}
settings.setTheme(theme);
App.setStyle(theme, settings.getLayoutSize());
});
themeChoiceBox.setConverter(new javafx.util.StringConverter<>() {
@Override
public String toString(String theme) {
return AppContext.getString(theme);
}
@Override
public String fromString(String string) {
return null;
}
});
themeChoiceBox.getItems().addAll("dark", "light", "dark-hc", "light-hc");
themeChoiceBox.setValue(settings.getTheme());
return themeChoiceBox;
}
private ChoiceBox<String> layoutSizeChoiceBox() {
final ChoiceBox<String> layoutSizeChoiceBox = NodeBuilder.choiceBox((layoutSize) -> {
if (layoutSize.equalsIgnoreCase(settings.getLayoutSize())) {
return;
}
settings.setLayoutSize(layoutSize);
App.setStyle(settings.getTheme(), layoutSize);
});
layoutSizeChoiceBox.setConverter(new javafx.util.StringConverter<>() {
@Override
public String toString(String layoutSize) {
return AppContext.getString(layoutSize);
}
@Override
public String fromString(String string) {
return null;
}
});
layoutSizeChoiceBox.getItems().addAll("small", "medium", "large");
layoutSizeChoiceBox.setValue(settings.getLayoutSize());
return layoutSizeChoiceBox;
}
}

View File

@@ -2,16 +2,17 @@ package org.toop.app.layer.layers;
import org.toop.app.App;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.Popup;
import org.toop.app.layer.containers.HorizontalContainer;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.local.AppContext;
import javafx.geometry.Pos;
public final class QuitLayer extends Layer {
public QuitLayer() {
super("quit.css");
public final class QuitPopup extends Popup {
public QuitPopup() {
super(true);
reload();
}
@@ -19,21 +20,23 @@ public final class QuitLayer extends Layer {
public void reload() {
popAll();
final Container mainContainer = new VerticalContainer(30);
mainContainer.addText(AppContext.getString("quitSure"), false);
final var sureText = NodeBuilder.header(AppContext.getString("quitSure"));
final Container controlContainer = new HorizontalContainer(30);
mainContainer.addContainer(controlContainer, false);
controlContainer.addButton(AppContext.getString("yes"), () -> {
final var yesButton = NodeBuilder.button(AppContext.getString("yes"), () -> {
App.quit();
});
controlContainer.addButton(AppContext.getString("no"), () -> {
final var noButton = NodeBuilder.button(AppContext.getString("no"), () -> {
App.pop();
});
final Container controlContainer = new HorizontalContainer(30);
controlContainer.addNodes(yesButton, noButton);
final Container mainContainer = new VerticalContainer(30);
mainContainer.addNodes(sureText);
mainContainer.addContainer(controlContainer, false);
addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
}
}

View File

@@ -1,10 +1,13 @@
package org.toop.app.layer.layers.game;
import javafx.geometry.Pos;
import javafx.scene.paint.Color;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.canvas.TicTacToeCanvas;
import org.toop.app.layer.Container;
import org.toop.app.layer.Layer;
import org.toop.app.layer.NodeBuilder;
import org.toop.app.layer.containers.VerticalContainer;
import org.toop.app.layer.layers.MainLayer;
import org.toop.framework.eventbus.EventFlow;
@@ -14,13 +17,9 @@ import org.toop.game.tictactoe.TicTacToe;
import org.toop.game.tictactoe.TicTacToeAI;
import org.toop.local.AppContext;
import javafx.geometry.Pos;
import javafx.scene.paint.Color;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
public final class TicTacToeLayer extends Layer {
private TicTacToeCanvas canvas;
@@ -37,9 +36,9 @@ public final class TicTacToeLayer extends Layer {
private String player2Name = "";
public TicTacToeLayer(GameInformation information) {
super("game.css");
super("bg-primary");
canvas = new TicTacToeCanvas(Color.WHITE, (App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75, (cell) -> {
canvas = new TicTacToeCanvas(Color.LIME, (App.getHeight() / 100) * 75, (App.getHeight() / 100) * 75, (cell) -> {
try {
if (information.isConnectionLocal()) {
if (ticTacToe.getCurrentTurn() == 0) {
@@ -95,11 +94,13 @@ public final class TicTacToeLayer extends Layer {
}
}
final Container controlContainer = new VerticalContainer(5);
controlContainer.addButton(AppContext.getString("back"), () -> {
final var backButton = NodeBuilder.button(AppContext.getString("back"), () -> {
App.activate(new MainLayer());
});
final Container controlContainer = new VerticalContainer(5);
controlContainer.addNodes(backButton);
addContainer(controlContainer, Pos.BOTTOM_LEFT, 2, -2, 0, 0);
addGameCanvas(canvas, Pos.CENTER, 0, 0);
}
@@ -161,10 +162,12 @@ public final class TicTacToeLayer extends Layer {
class OnlineGameState {
public long clientId = -1;
public long receivedMove = -1;
public boolean firstPlayerIsMe = true;
public boolean firstPlayerIsMe = true;
}
AtomicBoolean firstPlayerIsMe = new AtomicBoolean(true);
AtomicBoolean gameHasStarted = new AtomicBoolean(false);
AtomicBoolean firstPlayerIsMe = new AtomicBoolean(true);
AtomicBoolean gameHasStarted = new AtomicBoolean(false);
private void serverGameThread(NetworkEvents.StartClientResponse event) {
boolean running = true;
final long clientId = event.clientId();
@@ -174,12 +177,12 @@ public final class TicTacToeLayer extends Layer {
//new EventFlow()
// .listen(NetworkEvents.GameMoveResponse.class,respEvent -> onMoveResponse(onlineGameState, respEvent));
new EventFlow()
.listen(this::yourTurnResponse)
.listen(this::handleChallengeResponse)
.listen(this::handleServerGameStart)
.listen(this::handleReceivedMessage)
.listen(this::onMoveResponse);
new EventFlow()
.listen(this::yourTurnResponse)
.listen(this::handleChallengeResponse)
.listen(this::handleServerGameStart)
.listen(this::handleReceivedMessage)
.listen(this::onMoveResponse);
new EventFlow().addPostEvent(new NetworkEvents.SendLogin(clientId, information.playerName()[0]))
.postEvent();
@@ -188,95 +191,94 @@ public final class TicTacToeLayer extends Layer {
.postEvent();
while (running) {
try {
Thread.sleep(250);
}catch (InterruptedException exception) {}
boolean hasStarted = gameHasStarted.get();
if (hasStarted) {
onlineGameState.firstPlayerIsMe = firstPlayerIsMe.get();
if (onlineGameState.firstPlayerIsMe) {
currentPlayerMove = 'X';
}
else {
currentPlayerMove = 'O';
}
if(!information.isPlayerHuman()[0]){
boolean myTurn = (onlineGameState.firstPlayerIsMe && ticTacToe.getCurrentTurn() % 2 == 0)
|| (!onlineGameState.firstPlayerIsMe && ticTacToe.getCurrentTurn() % 2 == 1);
if (myTurn) {
Game.Move move;
move = ticTacToeAI.findBestMove(ticTacToe, compurterDifficultyToDepth(10, 10));
new EventFlow().addPostEvent(new NetworkEvents.SendMove(clientId, (short) move.position()))
.postEvent();
}
}
else {
try {
final Game.Move wants = playerMoveQueue.take();
final Game.Move[] legalMoves = ticTacToe.getLegalMoves();
for (final Game.Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) {
new EventFlow().addPostEvent(new NetworkEvents.SendMove(clientId, (short) wants.position()))
.postEvent();
break;
}
}
} catch (InterruptedException exception) {
return;
}
}
}
try {
Thread.sleep(250);
} catch (InterruptedException exception) {
}
boolean hasStarted = gameHasStarted.get();
if (hasStarted) {
onlineGameState.firstPlayerIsMe = firstPlayerIsMe.get();
if (onlineGameState.firstPlayerIsMe) {
currentPlayerMove = 'X';
} else {
currentPlayerMove = 'O';
}
if (!information.isPlayerHuman()[0]) {
boolean myTurn = (onlineGameState.firstPlayerIsMe && ticTacToe.getCurrentTurn() % 2 == 0)
|| (!onlineGameState.firstPlayerIsMe && ticTacToe.getCurrentTurn() % 2 == 1);
if (myTurn) {
Game.Move move;
move = ticTacToeAI.findBestMove(ticTacToe, compurterDifficultyToDepth(10, 10));
new EventFlow().addPostEvent(new NetworkEvents.SendMove(clientId, (short) move.position()))
.postEvent();
}
} else {
try {
final Game.Move wants = playerMoveQueue.take();
final Game.Move[] legalMoves = ticTacToe.getLegalMoves();
for (final Game.Move legalMove : legalMoves) {
if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) {
new EventFlow().addPostEvent(new NetworkEvents.SendMove(clientId, (short) wants.position()))
.postEvent();
break;
}
}
} catch (InterruptedException exception) {
return;
}
}
}
}
}
private void drawSymbol(Game.Move move){
if (move.value() == 'X') {
canvas.drawX(Color.RED, move.position());
} else if (move.value() == 'O') {
canvas.drawO(Color.BLUE, move.position());
}
}
private void handleServerGameStart(NetworkEvents.GameMatchResponse resp) {
if(resp.playerToMove().equals(resp.opponent())){
firstPlayerIsMe.set(false);
}
else{
firstPlayerIsMe.set(true);
}
gameHasStarted.set(true);
}
private void onMoveResponse(NetworkEvents.GameMoveResponse resp) {
char playerChar;
if (resp.player().equals(information.playerName()[0]) && firstPlayerIsMe.get()
|| !resp.player().equals(information.playerName()[0]) && !firstPlayerIsMe.get()) {
playerChar = 'X';
}
else{
playerChar = 'O';
}
Game.Move move =new Game.Move(Integer.parseInt(resp.move()),playerChar);
Game.State state = ticTacToe.play(move);
if (state != Game.State.NORMAL) { //todo differentiate between future draw guaranteed and is currently a draw
gameHasStarted.set(false);
}
drawSymbol(move);
private void drawSymbol(Game.Move move) {
if (move.value() == 'X') {
canvas.drawX(Color.RED, move.position());
} else if (move.value() == 'O') {
canvas.drawO(Color.BLUE, move.position());
}
}
private void handleChallengeResponse(NetworkEvents.ChallengeResponse resp) {
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(resp.clientId(),Integer.parseInt(resp.challengeId())))
.postEvent();
}
private void handleServerGameStart(NetworkEvents.GameMatchResponse resp) {
if (resp.playerToMove().equals(resp.opponent())) {
firstPlayerIsMe.set(false);
} else {
firstPlayerIsMe.set(true);
}
gameHasStarted.set(true);
}
private void yourTurnResponse(NetworkEvents.YourTurnResponse response) {
private void onMoveResponse(NetworkEvents.GameMoveResponse resp) {
char playerChar;
if (resp.player().equals(information.playerName()[0]) && firstPlayerIsMe.get()
|| !resp.player().equals(information.playerName()[0]) && !firstPlayerIsMe.get()) {
playerChar = 'X';
} else {
playerChar = 'O';
}
Game.Move move = new Game.Move(Integer.parseInt(resp.move()), playerChar);
Game.State state = ticTacToe.play(move);
if (state != Game.State.NORMAL) { //todo differentiate between future draw guaranteed and is currently a draw
gameHasStarted.set(false);
}
drawSymbol(move);
}
//new EventFlow().addPostEvent(new NetworkEvents.SendCommand(response.clientId(),"CHALLENGE banaan tic-tac-toe")).postEvent();
//new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(),(short)2))
// .postEvent();
}
private void handleReceivedMessage(NetworkEvents.ReceivedMessage msg) {
System.out.println("Received Message: " + msg.message()); //todo add chat window
}
private void handleChallengeResponse(NetworkEvents.ChallengeResponse resp) {
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(resp.clientId(), Integer.parseInt(resp.challengeId())))
.postEvent();
}
private void yourTurnResponse(NetworkEvents.YourTurnResponse response) {
//new EventFlow().addPostEvent(new NetworkEvents.SendCommand(response.clientId(),"CHALLENGE banaan tic-tac-toe")).postEvent();
//new EventFlow().addPostEvent(new NetworkEvents.SendMove(response.clientId(),(short)2))
// .postEvent();
}
private void handleReceivedMessage(NetworkEvents.ReceivedMessage msg) {
System.out.println("Received Message: " + msg.message()); //todo add chat window
}
private void serverGameThreadResponseHandler(OnlineGameState ogs, NetworkEvents.ChallengeResponse msg) {
if (msg.clientId() != ogs.clientId) return;

View File

@@ -23,7 +23,7 @@ public class AppSettings {
AppContext.setLocale(Locale.of(settingsData.locale));
App.setFullscreen(settingsData.fullScreen);
new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(settingsData.volume)).asyncPostEvent();
App.setStyle(settingsAsset.getTheme(), settingsAsset.getLayoutSize());
}
public SettingsAsset getPath() {