feat(service_menu): navegacio amb mando (dpad, stick, fire = enter, accelerate = back)
ServiceMenu::handleEvent ara accepta tambe SDL_EVENT_GAMEPAD_BUTTON_DOWN i SDL_EVENT_GAMEPAD_AXIS_MOTION. Mapeig: dpad UP/DOWN/LEFT/RIGHT mouen el cursor, el boto FIRE configurat per qualsevol jugador equival a ENTER (activa l'item), ACCELERATE equival a BACK (popPage). El stick esquerre fa nav amb edge-detect: cal tornar a centre per disparar una altra entrada. GlobalEvents::forwardToServiceMenu envia tots aquests events al menu quan esta obert. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,25 +23,32 @@ namespace GlobalEvents {
|
||||
|
||||
namespace {
|
||||
|
||||
// Reenvia el KEY_DOWN al menu de servei si esta obert i la tecla no
|
||||
// es F1-F12 ni ESC (que sempre passen com a globals). Retorna true si
|
||||
// el menu l'ha consumit.
|
||||
// Reenvia events al menu de servei si esta obert. Accepta:
|
||||
// - KEY_DOWN (excepte F1-F12 i ESC, que sempre passen com a globals)
|
||||
// - GAMEPAD_BUTTON_DOWN (per navegacio amb dpad + FIRE/ACCELERATE)
|
||||
// - GAMEPAD_AXIS_MOTION (per navegacio amb stick)
|
||||
// Retorna true si l'event s'ha entregat al menu.
|
||||
auto forwardToServiceMenu(const SDL_Event& event) -> bool {
|
||||
if (event.type != SDL_EVENT_KEY_DOWN) {
|
||||
return false;
|
||||
}
|
||||
auto* menu = System::ServiceMenu::get();
|
||||
if (menu == nullptr || !menu->isOpen()) {
|
||||
return false;
|
||||
}
|
||||
const SDL_Scancode SC = event.key.scancode;
|
||||
const bool PASSTHROUGH = (SC == SDL_SCANCODE_ESCAPE) ||
|
||||
(SC >= SDL_SCANCODE_F1 && SC <= SDL_SCANCODE_F12);
|
||||
if (PASSTHROUGH) {
|
||||
return false;
|
||||
if (event.type == SDL_EVENT_KEY_DOWN) {
|
||||
const SDL_Scancode SC = event.key.scancode;
|
||||
const bool PASSTHROUGH = (SC == SDL_SCANCODE_ESCAPE) ||
|
||||
(SC >= SDL_SCANCODE_F1 && SC <= SDL_SCANCODE_F12);
|
||||
if (PASSTHROUGH) {
|
||||
return false;
|
||||
}
|
||||
menu->handleEvent(event);
|
||||
return true;
|
||||
}
|
||||
menu->handleEvent(event);
|
||||
return true;
|
||||
if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ||
|
||||
event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
|
||||
menu->handleEvent(event);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Si l'overlay de redefinicio esta actiu, engoleix tots els events.
|
||||
|
||||
@@ -718,10 +718,52 @@ namespace System {
|
||||
}
|
||||
}
|
||||
|
||||
auto ServiceMenu::handleEvent(const SDL_Event& event) -> bool {
|
||||
if (!open_ || stack_.empty() || event.type != SDL_EVENT_KEY_DOWN) {
|
||||
namespace {
|
||||
|
||||
// Llindar de stick per a navegacio de menu (mig camp del rang ±32767).
|
||||
// Mes baix que el del joc (30000) per a una resposta mes agil al menu.
|
||||
constexpr Sint16 MENU_STICK_THRESHOLD = 16384;
|
||||
|
||||
// Retorna true si el codi de boto SDL coincideix amb l'accio
|
||||
// configurada per algun dels dos jugadors (es a dir, el boto te el
|
||||
// mateix codi al binding de FIRE o ACCELERATE del pad emissor).
|
||||
auto buttonMatchesAction(SDL_JoystickID which, int button, InputAction action) -> bool {
|
||||
const auto* input = Input::get();
|
||||
if (input == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto pad = input->getPlayerGamepad(i);
|
||||
if (!pad || pad->instance_id != which) {
|
||||
continue;
|
||||
}
|
||||
auto it = pad->bindings.find(action);
|
||||
if (it != pad->bindings.end() && it->second.button == button) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto ServiceMenu::handleEvent(const SDL_Event& event) -> bool {
|
||||
if (!open_ || stack_.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (event.type == SDL_EVENT_KEY_DOWN) {
|
||||
return handleKeyDown(event);
|
||||
}
|
||||
if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
|
||||
return handleGamepadButton(event);
|
||||
}
|
||||
if (event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
|
||||
return handleGamepadAxis(event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ServiceMenu::handleKeyDown(const SDL_Event& event) -> bool {
|
||||
switch (event.key.scancode) {
|
||||
case SDL_SCANCODE_UP:
|
||||
moveCursor(-1);
|
||||
@@ -747,6 +789,68 @@ namespace System {
|
||||
}
|
||||
}
|
||||
|
||||
auto ServiceMenu::handleGamepadButton(const SDL_Event& event) -> bool {
|
||||
const int BTN = static_cast<int>(event.gbutton.button);
|
||||
if (BTN == SDL_GAMEPAD_BUTTON_DPAD_UP) {
|
||||
moveCursor(-1);
|
||||
return true;
|
||||
}
|
||||
if (BTN == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
|
||||
moveCursor(+1);
|
||||
return true;
|
||||
}
|
||||
if (BTN == SDL_GAMEPAD_BUTTON_DPAD_LEFT) {
|
||||
changeValue(-1);
|
||||
return true;
|
||||
}
|
||||
if (BTN == SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
|
||||
changeValue(+1);
|
||||
return true;
|
||||
}
|
||||
// Botons d'accio per al pad emissor: FIRE = ENTER, ACCELERATE = BACK.
|
||||
if (buttonMatchesAction(event.gbutton.which, BTN, InputAction::SHOOT)) {
|
||||
activateCurrent();
|
||||
return true;
|
||||
}
|
||||
if (buttonMatchesAction(event.gbutton.which, BTN, InputAction::THRUST)) {
|
||||
popPage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ServiceMenu::handleGamepadAxis(const SDL_Event& event) -> bool {
|
||||
const auto AXIS = static_cast<SDL_GamepadAxis>(event.gaxis.axis);
|
||||
const Sint16 VAL = event.gaxis.value;
|
||||
if (AXIS == SDL_GAMEPAD_AXIS_LEFTX) {
|
||||
const bool LEFT_NOW = VAL < -MENU_STICK_THRESHOLD;
|
||||
const bool RIGHT_NOW = VAL > MENU_STICK_THRESHOLD;
|
||||
if (LEFT_NOW && !stick_left_held_) {
|
||||
changeValue(-1);
|
||||
}
|
||||
if (RIGHT_NOW && !stick_right_held_) {
|
||||
changeValue(+1);
|
||||
}
|
||||
stick_left_held_ = LEFT_NOW;
|
||||
stick_right_held_ = RIGHT_NOW;
|
||||
return true;
|
||||
}
|
||||
if (AXIS == SDL_GAMEPAD_AXIS_LEFTY) {
|
||||
const bool UP_NOW = VAL < -MENU_STICK_THRESHOLD;
|
||||
const bool DOWN_NOW = VAL > MENU_STICK_THRESHOLD;
|
||||
if (UP_NOW && !stick_up_held_) {
|
||||
moveCursor(-1);
|
||||
}
|
||||
if (DOWN_NOW && !stick_down_held_) {
|
||||
moveCursor(+1);
|
||||
}
|
||||
stick_up_held_ = UP_NOW;
|
||||
stick_down_held_ = DOWN_NOW;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ServiceMenu::computeTargetHeight() const -> float {
|
||||
if (stack_.empty()) {
|
||||
return 0.0F;
|
||||
|
||||
@@ -85,13 +85,22 @@ namespace System {
|
||||
void update(float delta_time);
|
||||
void draw() const;
|
||||
|
||||
// Processa el KEY_DOWN. Retorna true si l'ha consumit (UP/DOWN/ENTER/
|
||||
// RIGHT/BACKSPACE/LEFT mentre esta obert). false en qualsevol altre cas.
|
||||
// Processa events de navegacio. Retorna true si l'event s'ha consumit.
|
||||
// Accepta:
|
||||
// - SDL_EVENT_KEY_DOWN: UP/DOWN/ENTER/RIGHT/LEFT/BACKSPACE.
|
||||
// - SDL_EVENT_GAMEPAD_BUTTON_DOWN: DPAD per nav, FIRE = ENTER,
|
||||
// ACCELERATE = BACK. La resta de botons s'ignoren.
|
||||
// - SDL_EVENT_GAMEPAD_AXIS_MOTION: stick X/Y amb edge-detect.
|
||||
auto handleEvent(const SDL_Event& event) -> bool;
|
||||
|
||||
private:
|
||||
ServiceMenu(Rendering::Renderer* renderer, SDLManager* sdl, DebugOverlay* debug_overlay);
|
||||
|
||||
// Sub-handlers de handleEvent. Privats, no son part de l'API publica.
|
||||
auto handleKeyDown(const SDL_Event& event) -> bool;
|
||||
auto handleGamepadButton(const SDL_Event& event) -> bool;
|
||||
auto handleGamepadAxis(const SDL_Event& event) -> bool;
|
||||
|
||||
void buildRootPage();
|
||||
[[nodiscard]] auto buildVideoPage() -> Page;
|
||||
[[nodiscard]] auto buildResolutionPage() const -> Page;
|
||||
@@ -140,6 +149,15 @@ namespace System {
|
||||
float highlight_h_ = 0.0F;
|
||||
bool highlight_snap_ = true;
|
||||
|
||||
// Edge-detect de stick analogic per a navegacio. Una sola activacio
|
||||
// per direccio: cal tornar a centre (sota el llindar) per disparar
|
||||
// una altra. Compartit entre tots els pads — qualsevol jugador pot
|
||||
// navegar el menu.
|
||||
bool stick_left_held_ = false;
|
||||
bool stick_right_held_ = false;
|
||||
bool stick_up_held_ = false;
|
||||
bool stick_down_held_ = false;
|
||||
|
||||
static std::unique_ptr<ServiceMenu> instance;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user