From affa81a3e8b7b7fad84cd5007ba6e657af26b775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=E1=BA=BF=20H=C6=B0ng?= Date: Wed, 1 Oct 2025 10:17:04 +0700 Subject: [PATCH] feat(ui/button): implement more events & simulators This commit implements events onHoverEnter/onHoverExit. Simulators have .click() to simulate click, .hoverEnter()/.hoverExit() and so on. --- .../vibecoders/moongazer/scenes/MainMenu.java | 63 ++++++++++++++++++ .../org/vibecoders/moongazer/ui/UIButton.java | 64 +++++++++++++++++-- 2 files changed, 123 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/vibecoders/moongazer/scenes/MainMenu.java b/app/src/main/java/org/vibecoders/moongazer/scenes/MainMenu.java index 9c4b039..f4af480 100644 --- a/app/src/main/java/org/vibecoders/moongazer/scenes/MainMenu.java +++ b/app/src/main/java/org/vibecoders/moongazer/scenes/MainMenu.java @@ -10,9 +10,12 @@ import org.vibecoders.moongazer.managers.Assets; import org.vibecoders.moongazer.ui.UITextButton; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.scenes.scene2d.InputEvent; +import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.video.VideoPlayer; import com.badlogic.gdx.video.VideoPlayerCreator; @@ -20,8 +23,10 @@ public class MainMenu extends Scene { private VideoPlayer videoPlayer; private FileHandle videoFileHandle; private Texture titleTexture; + private UITextButton[] buttons; private float titleY, titleX, titleWidth, titleHeight; private boolean videoPrepared = false; + private int currentChoice = -1; public MainMenu(Game game) { super(game); @@ -55,6 +60,7 @@ public class MainMenu extends Scene { UITextButton loadButton = new UITextButton("Load", font); UITextButton settingsButton = new UITextButton("Settings", font); UITextButton exitButton = new UITextButton("Exit", font); + buttons = new UITextButton[] { playButton, loadButton, settingsButton, exitButton }; int buttonWidth = 300; int buttonHeight = 80; @@ -77,6 +83,7 @@ public class MainMenu extends Scene { exitButton.setSize(buttonWidth, buttonHeight); exitButton.setPosition(centerX, startY - spacing * 3); + // Mouse click handlers playButton.onClick(() -> log.debug("Play clicked")); loadButton.onClick(() -> log.debug("Load clicked")); settingsButton.onClick(() -> { @@ -95,9 +102,65 @@ public class MainMenu extends Scene { root.addActor(settingsButton.getActor()); root.addActor(exitButton.getActor()); + // Keyboard navigation handling + initKeyboardHandling(); game.stage.addActor(root); } + private void initKeyboardHandling() { + for (int i = 0; i < buttons.length; i++) { + final int index = i; + buttons[i].onHoverEnter(() -> { + log.trace("Button hover enter: {}", index); + if (currentChoice != index) { + if (currentChoice != -1) { + buttons[currentChoice].hoverExit(); + } + currentChoice = index; + } + }); + buttons[i].onHoverExit(() -> { + if (currentChoice == index) { + currentChoice = -1; + } + }); + } + + root.addListener(new InputListener() { + @Override + public boolean keyDown(InputEvent event, int keycode) { + log.trace("Key pressed: {}", keycode); + switch (keycode) { + case Input.Keys.UP: + currentChoice = (currentChoice - 1 + 4) % 4; + break; + case Input.Keys.DOWN: + currentChoice = (currentChoice + 1) % 4; + break; + case Input.Keys.RIGHT: + case Input.Keys.ENTER: + if (currentChoice != -1) { + buttons[currentChoice].click(); + } + break; + default: + break; + } + if (currentChoice != -1) { + log.trace("Current choice: {}", currentChoice); + for (int i = 0; i < buttons.length; i++) { + if (i == currentChoice) { + buttons[i].hoverEnter(); + } else { + buttons[i].hoverExit(); + } + } + } + return true; + } + }); + } + private void startVideoOnce() { if (videoPlayer == null || videoPrepared) return; diff --git a/app/src/main/java/org/vibecoders/moongazer/ui/UIButton.java b/app/src/main/java/org/vibecoders/moongazer/ui/UIButton.java index 5e43a97..62d1d8a 100644 --- a/app/src/main/java/org/vibecoders/moongazer/ui/UIButton.java +++ b/app/src/main/java/org/vibecoders/moongazer/ui/UIButton.java @@ -1,12 +1,15 @@ package org.vibecoders.moongazer.ui; +import com.badlogic.gdx.Input; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.EventListener; import com.badlogic.gdx.scenes.scene2d.ui.Button; +import com.badlogic.gdx.scenes.scene2d.InputEvent; +import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; public abstract class UIButton { - protected Actor actor; - protected Button button; + public Actor actor; + public Button button; public Actor getActor() { return actor; @@ -24,12 +27,65 @@ public abstract class UIButton { actor.addListener(eventListener); } + public void click() { + // Thx ChatGPT + InputEvent down = new InputEvent(); + down.setType(InputEvent.Type.touchDown); + down.setButton(Input.Buttons.LEFT); + down.setStageX(button.getX()); + down.setStageY(button.getY()); + button.fire(down); + + InputEvent up = new InputEvent(); + up.setType(InputEvent.Type.touchUp); + up.setButton(Input.Buttons.LEFT); + up.setStageX(button.getX()); + up.setStageY(button.getY()); + button.fire(up); + } + + public void hoverEnter() { + InputEvent e = new InputEvent(); + e.setType(InputEvent.Type.enter); + e.setPointer(-1); + button.fire(e); + } + + public void hoverExit() { + InputEvent e = new InputEvent(); + e.setType(InputEvent.Type.exit); + e.setPointer(-1); + button.fire(e); + } + public void onClick(Runnable action) { - button.addListener(new com.badlogic.gdx.scenes.scene2d.utils.ClickListener() { + button.addListener(new ClickListener() { @Override - public void clicked(com.badlogic.gdx.scenes.scene2d.InputEvent event, float x, float y) { + public void clicked(InputEvent event, float x, float y) { action.run(); } }); } + + public void onHoverEnter(Runnable action) { + button.addListener(new ClickListener() { + @Override + public void enter(InputEvent event, float x, float y, int pointer, Actor fromActor) { + if (pointer == -1) { + action.run(); + } + } + }); + } + + public void onHoverExit(Runnable action) { + button.addListener(new ClickListener() { + @Override + public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) { + if (pointer == -1) { + action.run(); + } + } + }); + } }