diff --git a/core/src/main/java/group07/beatbattle/controller/SettingsController.java b/core/src/main/java/group07/beatbattle/controller/SettingsController.java index 5361686..9f1e9be 100644 --- a/core/src/main/java/group07/beatbattle/controller/SettingsController.java +++ b/core/src/main/java/group07/beatbattle/controller/SettingsController.java @@ -1,17 +1,79 @@ package group07.beatbattle.controller; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Preferences; import group07.beatbattle.BeatBattle; +import group07.beatbattle.model.GameMode; +import group07.beatbattle.view.LobbyView; public class SettingsController { private final BeatBattle game; + private final GameMode mode; + private final String sessionId; + private final String gamePin; + private final String playerId; + private final String displayName; + private final boolean isHost; - public SettingsController(BeatBattle game) { + private String language; + private int turns; + private final Preferences prefs; + + + public SettingsController( + BeatBattle game, + GameMode mode, + String sessionId, + String gamePin, + String playerId, + String displayName, + boolean isHost + ) { this.game = game; + this.mode = mode; + this.sessionId = sessionId; + this.gamePin = gamePin; + this.playerId = playerId; + this.displayName = displayName; + this.isHost = isHost; + + this.prefs = Gdx.app.getPreferences("settings"); + this.language = prefs.getString("language", "EN"); + } + + public String getLanguage() { + return language; + } + + public void onLanguageChanged(String language) { + this.language = language; + Gdx.app.log("Settings", "Language changed to: " + language); + } + + public void onTurnsChanged(int turns) { + this.turns = turns; } - // TODO: onLanguageChanged(String language) - // TODO: onTurnsChanged(int turns) - // TODO: onApply() - // TODO: onBack() + public void onApply() { + prefs.putString("language", language); + prefs.putInteger("turns", turns); + prefs.flush(); + Gdx.app.log("Settings", "Settings saved"); + onBack(); + } + + public void onBack() { + Gdx.app.log("Settings", "Back pressed"); + game.setScreen(new LobbyView( + game, + mode, + sessionId, + gamePin, + playerId, + displayName, + isHost, + new LobbyController(game) + )); + } } diff --git a/core/src/main/java/group07/beatbattle/states/SettingsState.java b/core/src/main/java/group07/beatbattle/states/SettingsState.java index 3e1ec3e..b250882 100644 --- a/core/src/main/java/group07/beatbattle/states/SettingsState.java +++ b/core/src/main/java/group07/beatbattle/states/SettingsState.java @@ -1,22 +1,42 @@ package group07.beatbattle.states; import group07.beatbattle.BeatBattle; -import group07.beatbattle.controller.SettingsController; +import group07.beatbattle.model.GameMode; +import group07.beatbattle.view.SettingsView; + public class SettingsState extends State { - private final SettingsController settingsController; + private final SettingsView view; - public SettingsState(BeatBattle game, SettingsController settingsController) { + public SettingsState( + BeatBattle game, + GameMode mode, + String sessionId, + String gamePin, + String playerId, + String displayName, + boolean isHost + ) { super(game); - this.settingsController = settingsController; + this.view = new SettingsView( + game, + mode, + sessionId, + gamePin, + playerId, + displayName, + isHost + ); } @Override public void enter() { - // TODO: game.setScreen(new SettingsView(game, settingsController)); + game.setScreen(view); } @Override - public void exit() {} + public void exit() { + + } } diff --git a/core/src/main/java/group07/beatbattle/ui/components/ApplyButton.java b/core/src/main/java/group07/beatbattle/ui/components/ApplyButton.java new file mode 100644 index 0000000..465bace --- /dev/null +++ b/core/src/main/java/group07/beatbattle/ui/components/ApplyButton.java @@ -0,0 +1,48 @@ +package group07.beatbattle.ui.components; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; +import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; + +public class ApplyButton extends TextButton { + + private static Texture sharedTexture; + + private static TextButtonStyle createStyle(BitmapFont font) { + Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888); + + pixmap.setColor(0.23f, 0.23f, 0.34f, 1f); // same as StartGameButton + pixmap.fill(); + + sharedTexture = new Texture(pixmap); + pixmap.dispose(); + + TextureRegionDrawable drawable = + new TextureRegionDrawable(new TextureRegion(sharedTexture)); + + TextButtonStyle style = new TextButtonStyle(); + style.up = drawable; + style.down = drawable; + style.font = font; + style.fontColor = Color.WHITE; + + return style; + } + + public ApplyButton(BitmapFont font) { + super("Apply", createStyle(font)); + + pad(40f, 80f, 40f, 80f); + } + + public static void disposeSharedResources() { + if (sharedTexture != null) { + sharedTexture.dispose(); + sharedTexture = null; + } + } +} diff --git a/core/src/main/java/group07/beatbattle/ui/components/LanguageSelector.java b/core/src/main/java/group07/beatbattle/ui/components/LanguageSelector.java new file mode 100644 index 0000000..aeafe76 --- /dev/null +++ b/core/src/main/java/group07/beatbattle/ui/components/LanguageSelector.java @@ -0,0 +1,168 @@ +package group07.beatbattle.ui.components; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.Touchable; +import com.badlogic.gdx.scenes.scene2d.ui.Label; +import com.badlogic.gdx.scenes.scene2d.ui.List; +import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane; +import com.badlogic.gdx.scenes.scene2d.ui.Table; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; +import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; +import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; +import com.badlogic.gdx.utils.Align; + +public class LanguageSelector { + + private static final String DEFAULT_LANGUAGE = "EN"; + private static final int VISIBLE_ROWS_HEIGHT = 200; + private static final float FIELD_HEIGHT = 96f; + + private static Texture fieldBg; + private static Texture popupBg; + private static Texture selectionBg; + private static Texture scrollTrackBg; + private static Texture scrollKnobBg; + + private final Table root; + private final TextButton valueButton; + private final List list; + private final ScrollPane scrollPane; + private final Table popupContainer; + + private boolean popupOpen = false; + private String selectedLanguage = DEFAULT_LANGUAGE; + + public LanguageSelector(BitmapFont font, String initialLanguage) { + this.root = new Table(); + + ensureTextures(); + + Label.LabelStyle labelStyle = new Label.LabelStyle(); + labelStyle.font = font; + labelStyle.fontColor = Color.LIGHT_GRAY; + + Label label = new Label("Language", labelStyle); + label.setAlignment(Align.left); + + TextButton.TextButtonStyle buttonStyle = new TextButton.TextButtonStyle(); + buttonStyle.up = new TextureRegionDrawable(new TextureRegion(fieldBg)); + buttonStyle.down = new TextureRegionDrawable(new TextureRegion(selectionBg)); + buttonStyle.over = new TextureRegionDrawable(new TextureRegion(selectionBg)); + buttonStyle.font = font; + buttonStyle.fontColor = Color.WHITE; + + this.selectedLanguage = initialLanguage; + this.valueButton = new TextButton(initialLanguage, buttonStyle); + this.valueButton.getLabel().setAlignment(Align.center); + + List.ListStyle listStyle = new List.ListStyle(); + listStyle.font = font; + listStyle.fontColorUnselected = Color.LIGHT_GRAY; + listStyle.fontColorSelected = Color.WHITE; + listStyle.background = new TextureRegionDrawable(new TextureRegion(popupBg)); + listStyle.selection = new TextureRegionDrawable(new TextureRegion(selectionBg)); + + this.list = new List<>(listStyle); + this.list.setItems("EN", "NO"); + this.list.setSelected(initialLanguage); + + ScrollPane.ScrollPaneStyle scrollStyle = new ScrollPane.ScrollPaneStyle(); + scrollStyle.background = new TextureRegionDrawable(new TextureRegion(popupBg)); + scrollStyle.vScroll = new TextureRegionDrawable(new TextureRegion(scrollTrackBg)); + scrollStyle.vScrollKnob = new TextureRegionDrawable(new TextureRegion(scrollKnobBg)); + + this.scrollPane = new ScrollPane(list, scrollStyle); + this.scrollPane.setScrollingDisabled(true, false); + this.scrollPane.setFadeScrollBars(false); + + this.popupContainer = new Table(); + this.popupContainer.setVisible(false); + this.popupContainer.setTouchable(Touchable.disabled); + + valueButton.addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + setPopupOpen(!popupOpen); + } + }); + + list.addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + String sel = list.getSelected(); + if (sel != null) { + selectedLanguage = sel; + valueButton.setText(sel); + } + setPopupOpen(false); + } + }); + + root.add(label).left().padBottom(8f).row(); + root.add(valueButton).expandX().fillX().height(FIELD_HEIGHT).row(); + + popupContainer.add(scrollPane).expandX().fillX().height(VISIBLE_ROWS_HEIGHT); + root.add(popupContainer).expandX().fillX().padTop(8f).row(); + } + + public Actor getActor() { + return root; + } + + public String getSelectedLanguage() { + return selectedLanguage; + } + + private void setPopupOpen(boolean open) { + popupOpen = open; + popupContainer.setVisible(open); + popupContainer.setTouchable(open ? Touchable.enabled : Touchable.disabled); + if (open) { + scrollPane.layout(); + scrollPane.setScrollPercentY(0f); + } + } + + private static void ensureTextures() { + if (fieldBg == null) { + Pixmap p = new Pixmap(1, 1, Pixmap.Format.RGBA8888); + p.setColor(0.16f, 0.16f, 0.24f, 1f); + p.fill(); + fieldBg = new Texture(p); + p.dispose(); + } + if (popupBg == null) { + Pixmap p = new Pixmap(1, 1, Pixmap.Format.RGBA8888); + p.setColor(0.12f, 0.12f, 0.18f, 1f); + p.fill(); + popupBg = new Texture(p); + p.dispose(); + } + if (selectionBg == null) { + Pixmap p = new Pixmap(1, 1, Pixmap.Format.RGBA8888); + p.setColor(0.30f, 0.30f, 0.55f, 1f); + p.fill(); + selectionBg = new Texture(p); + p.dispose(); + } + if (scrollTrackBg == null) { + Pixmap p = new Pixmap(14, 14, Pixmap.Format.RGBA8888); + p.setColor(0.20f, 0.20f, 0.28f, 1f); + p.fill(); + scrollTrackBg = new Texture(p); + p.dispose(); + } + if (scrollKnobBg == null) { + Pixmap p = new Pixmap(14, 14, Pixmap.Format.RGBA8888); + p.setColor(0.75f, 0.75f, 0.88f, 1f); + p.fill(); + scrollKnobBg = new Texture(p); + p.dispose(); + } + } +} diff --git a/core/src/main/java/group07/beatbattle/view/GameRoundView.java b/core/src/main/java/group07/beatbattle/view/GameRoundView.java index c273e8b..53940e2 100644 --- a/core/src/main/java/group07/beatbattle/view/GameRoundView.java +++ b/core/src/main/java/group07/beatbattle/view/GameRoundView.java @@ -15,6 +15,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; +import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Scaling; import com.badlogic.gdx.utils.viewport.ScreenViewport; @@ -84,11 +85,9 @@ public GameRoundView(BeatBattle game, RoundController controller) { Table root = new Table(); root.setFillParent(true); - root.pad(40f); // --- Top bar: leave button + timer --- Table topBar = new Table(); - topBar.setFillParent(false); TextButton leaveButton = makeButton("X", defaultTex); leaveButton.addListener(new ChangeListener() { @@ -99,11 +98,13 @@ public void changed(ChangeEvent event, Actor actor) { }); timerLabel = new Label(String.valueOf((int) Math.ceil(controller.getTotalTime())), timerStyle()); - topBar.add(leaveButton).width(120f).height(100f).left(); - topBar.add(timerLabel).expandX().center(); - topBar.add().width(120f); // spacer to balance left button + timerLabel.setAlignment(Align.center); - root.add(topBar).fillX().padBottom(60f).row(); + topBar.add(leaveButton).width(120f).height(100f).padLeft(40f); + topBar.add(timerLabel).expandX().center().padTop(60f); + topBar.add().width(120f).padRight(40f); // Spacer + + root.add(topBar).fillX().row(); // --- Song info --- Label.LabelStyle infoStyle = new Label.LabelStyle(); @@ -111,8 +112,9 @@ public void changed(ChangeEvent event, Actor actor) { infoStyle.fontColor = Color.LIGHT_GRAY; Label songLabel = new Label("Now Playing...", infoStyle); + songLabel.setAlignment(Align.center); songLabel.setWrap(true); - root.add(songLabel).fillX().center().padBottom(60f).row(); + root.add(songLabel).fillX().center().padTop(40f).padBottom(60f).row(); // --- Answer buttons 2x2 grid --- Table grid = new Table(); @@ -126,10 +128,8 @@ public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) { if (answered) return; answered = true; - // Highlight selected button and scale label to suggest bold btn.getStyle().up = new TextureRegionDrawable(new TextureRegion(selectedTex)); btn.getLabel().setFontScale(1.15f); - // Gray out all other buttons for (TextButton other : optionButtons) { if (other != btn) { other.getStyle().up = new TextureRegionDrawable(new TextureRegion(disabledTex)); diff --git a/core/src/main/java/group07/beatbattle/view/LobbyView.java b/core/src/main/java/group07/beatbattle/view/LobbyView.java index 8ae4ecc..6e910e7 100644 --- a/core/src/main/java/group07/beatbattle/view/LobbyView.java +++ b/core/src/main/java/group07/beatbattle/view/LobbyView.java @@ -188,7 +188,15 @@ public void changed(ChangeEvent event, Actor actor) { settingsButton.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { - game.setScreen(new SettingsView(game, mode)); + game.setScreen(new SettingsView( + game, + mode, + sessionId, + gamePin, + playerId, + hostName, + isHost + )); } }); diff --git a/core/src/main/java/group07/beatbattle/view/SettingsView.java b/core/src/main/java/group07/beatbattle/view/SettingsView.java index 919674c..532a25c 100644 --- a/core/src/main/java/group07/beatbattle/view/SettingsView.java +++ b/core/src/main/java/group07/beatbattle/view/SettingsView.java @@ -2,25 +2,137 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.ScreenAdapter; +import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.utils.viewport.ScreenViewport; +import com.badlogic.gdx.scenes.scene2d.Actor; +import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import group07.beatbattle.BeatBattle; +import group07.beatbattle.controller.SettingsController; import group07.beatbattle.model.GameMode; +import group07.beatbattle.ui.components.BackButton; +import group07.beatbattle.ui.components.ApplyButton; +import group07.beatbattle.ui.components.LanguageSelector; +import group07.beatbattle.ui.components.RoundSelector; + public class SettingsView extends ScreenAdapter { private final BeatBattle game; - private final GameMode mode; + private final SettingsController controller; private final Stage stage; - public SettingsView(BeatBattle game, GameMode mode) { + private final Label.LabelStyle titleStyle; + + private final GameMode mode; + + public SettingsView( + BeatBattle game, + GameMode mode, + String sessionId, + String gamePin, + String playerId, + String displayName, + boolean isHost + ) { this.game = game; this.mode = mode; + this.controller = new SettingsController( + game, + mode, + sessionId, + gamePin, + playerId, + displayName, + isHost + ); this.stage = new Stage(new ScreenViewport()); + + titleStyle = new Label.LabelStyle(); + titleStyle.font = game.getOrbitronFont(); + titleStyle.fontColor = Color.WHITE; + + Table root = new Table(); + root.setFillParent(true); + root.top(); + + root.add(createHeader()).expandX().fillX().padTop(20).row(); + root.add(createContent()).expand().center().row(); + root.add(createFooter()).expandX().bottom().padBottom(40); + + stage.addActor(root); + } + + private Table createHeader() { + Table header = new Table(); + + BackButton backButton = new BackButton("Back", game.getMontserratFont()); + + backButton.addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + controller.onBack(); + + } + }); + + header.add(backButton) + .width(200) + .height(100) + .expandX() + .left() + .padLeft(20); + + return header; + } + + private Table createFooter() { + return new Table(); // empty for now (keeps structure consistent) + } + + private Table createContent() { + Table table = new Table(); + table.center(); + + Label title = new Label("Settings", titleStyle); + + LanguageSelector languageSelector = new LanguageSelector(game.getMontserratFont(), controller.getLanguage()); + + RoundSelector roundSelector = new RoundSelector(game.getMontserratFont()); + + ApplyButton applyButton = new ApplyButton(game.getMontserratFont()); + + table.add(title).padBottom(60).row(); + + table.add(languageSelector.getActor()) + .width(500) + .padBottom(60) + .row(); + + table.add(roundSelector.getActor()) + .width(500).padBottom(60).row(); + + table.add(applyButton) + .width(600) + .height(150); + + applyButton.addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + controller.onLanguageChanged(languageSelector.getSelectedLanguage()); + controller.onTurnsChanged(roundSelector.getSelectedRounds()); + controller.onApply(); + } + }); + + return table; } + + @Override public void show() { Gdx.input.setInputProcessor(stage); @@ -28,8 +140,21 @@ public void show() { @Override public void render(float delta) { + Gdx.gl.glClearColor(0.08f, 0.08f, 0.12f, 1f); // same as others Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + stage.act(delta); stage.draw(); } + + @Override + public void resize(int width, int height) { + stage.getViewport().update(width, height, true); + } + + @Override + public void dispose() { + stage.dispose(); + } + }