34be79192c
Afegeix submenu CONTROLS al menu de servei amb 2 items CYCLE per seleccionar el mando assignat a cada jugador (persistit per name + path) i 4 items ACTION per arrancar DefineInputs (teclat/mando per a P1/P2). Tambe afegeix: - Director: init/update/draw/destroy del singleton DefineInputs. - GlobalEvents: routing prioritari de tots els events a DefineInputs mentre l'overlay esta actiu. - Locale ca/en: claus del submenu CONTROLS i de l'overlay de rebind. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
147 lines
6.1 KiB
C++
147 lines
6.1 KiB
C++
// service_menu.hpp - Menu de servei (singleton)
|
|
// © 2026 JailDesigner
|
|
//
|
|
// Overlay de configuracio global accessible amb F12 des de qualsevol escena
|
|
// (LOGO, TITLE, GAME). Captura tots els KEY_DOWN excepte F1-F12 i ESC, que
|
|
// continuen arribant a GlobalEvents. Mentre esta obert, GameScene::update()
|
|
// fa early return per pausar el joc; LOGO i TITLE continuen renderitzant-se
|
|
// sota el menu.
|
|
//
|
|
// Arquitectura inspirada en aee_arcade service_menu.{hpp,cpp}: pila de
|
|
// pagines amb cursor, animacio open/close amb easing easeOutQuad i clipping
|
|
// del contingut mentre la caixa creix/decreix.
|
|
//
|
|
// API singleton equivalent a Notifier: init() al startup amb un renderer,
|
|
// get() retorna el punter, destroy() al teardown.
|
|
|
|
#pragma once
|
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "core/graphics/vector_text.hpp"
|
|
#include "core/rendering/render_context.hpp"
|
|
|
|
class SDLManager;
|
|
|
|
namespace System {
|
|
|
|
class DebugOverlay;
|
|
|
|
class ServiceMenu {
|
|
public:
|
|
// Tipus d'item de menu. En aquesta iteracio nomes s'usen SUBMENU i
|
|
// LABEL; la resta queden reservats per a iteracions futures (toggles
|
|
// de vsync/zoom, picker d'idioma, restart, exit...).
|
|
enum class Kind : std::uint8_t {
|
|
LABEL, // No interactiu, nomes es dibuixa
|
|
TOGGLE, // bool flip — reservat
|
|
CYCLE, // index amb modul — reservat
|
|
INT_RANGE, // step ± — reservat
|
|
SUBMENU, // pushPage en activar — usat
|
|
ACTION // call al lambda en activar — reservat
|
|
};
|
|
|
|
struct Item {
|
|
Kind kind = Kind::LABEL;
|
|
std::string label_key; // Clau de locale (s'ignora si label_text no esta buit)
|
|
std::string label_text; // Text literal (no locale). Util per a labels que no necessiten traduccio (resolucions, etc.)
|
|
bool selectable = true;
|
|
// SUBMENU / ACTION: callback en ENTER / RIGHT.
|
|
std::function<void()> on_activate;
|
|
// TOGGLE / CYCLE / INT_RANGE: text del valor actual (renderitzat a la dreta).
|
|
std::function<std::string()> get_value_text;
|
|
// TOGGLE / CYCLE / INT_RANGE: callback amb +1 (RIGHT/ENTER) o -1 (LEFT).
|
|
std::function<void(int)> on_change;
|
|
};
|
|
|
|
struct Page {
|
|
std::string title_key;
|
|
// Subtitol opcional, renderitzat sota el titol amb tipografia mes
|
|
// petita i color apagat. Es una funcio perque pot ser dinamic
|
|
// (versio+hash, etc.). Si esta buit, no es renderitza.
|
|
std::function<std::string()> subtitle_provider;
|
|
std::vector<Item> items;
|
|
std::size_t cursor = 0;
|
|
};
|
|
|
|
// Inicialitza el singleton amb el renderer global, l'SDLManager (video
|
|
// toggles: fullscreen, vsync, AA, postfx, zoom) i el DebugOverlay
|
|
// (toggle del HUD de debug a OPCIONS). Tots propietat del Director.
|
|
static void init(Rendering::Renderer* renderer, SDLManager* sdl, DebugOverlay* debug_overlay);
|
|
static void destroy();
|
|
[[nodiscard]] static auto get() -> ServiceMenu*;
|
|
|
|
// F12: alterna obrir/tancar amb animacio.
|
|
void toggle();
|
|
[[nodiscard]] auto isOpen() const -> bool;
|
|
|
|
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.
|
|
auto handleEvent(const SDL_Event& event) -> bool;
|
|
|
|
private:
|
|
ServiceMenu(Rendering::Renderer* renderer, SDLManager* sdl, DebugOverlay* debug_overlay);
|
|
|
|
void buildRootPage();
|
|
[[nodiscard]] auto buildVideoPage() -> Page;
|
|
[[nodiscard]] auto buildResolutionPage() const -> Page;
|
|
[[nodiscard]] static auto buildAudioPage() -> Page;
|
|
[[nodiscard]] auto buildOptionsPage() const -> Page;
|
|
[[nodiscard]] auto buildSystemPage() -> Page;
|
|
[[nodiscard]] auto buildControlsPage() -> Page;
|
|
// Pagina de confirmacio "ESTAS SEGUR? NO/SI". on_yes s'executa si
|
|
// l'usuari selecciona SI; el cursor per defecte apunta a NO.
|
|
void pushConfirmPage(const std::string& title_key, std::function<void()> on_yes);
|
|
void pushPage(Page page);
|
|
void popPage();
|
|
void moveCursor(int direction);
|
|
void activateCurrent();
|
|
// RIGHT (direction=+1) / LEFT (direction=-1). Per a TOGGLE/CYCLE/INT_RANGE
|
|
// crida on_change. Per a SUBMENU/ACTION nomes +1 (entra/activa).
|
|
void changeValue(int direction);
|
|
|
|
// Alçada objectiu de la caixa per a la pagina superior (sense animacio).
|
|
[[nodiscard]] auto computeTargetHeight() const -> float;
|
|
|
|
// Ample objectiu de la caixa per a la pagina superior (sense animacio).
|
|
// Pren com a base BOX_WIDTH_MIN i s'eixampla si algun text no hi cap.
|
|
[[nodiscard]] auto computeTargetWidth() const -> float;
|
|
|
|
// Y (top) de l'item index dins una caixa col·locada a box_y. Si la
|
|
// pagina te subtitol, els items es desplacen cap avall.
|
|
[[nodiscard]] static auto computeItemTopY(float box_y, std::size_t index, bool has_subtitle) -> float;
|
|
|
|
Rendering::Renderer* renderer_;
|
|
SDLManager* sdl_;
|
|
DebugOverlay* debug_overlay_;
|
|
Graphics::VectorText text_;
|
|
|
|
std::vector<Page> stack_;
|
|
bool open_ = false;
|
|
bool closing_ = false;
|
|
float open_anim_ = 0.0F; // 0..1 raw (sense easing)
|
|
float animated_h_ = 0.0F; // Alçada animada amb smoothing exponencial
|
|
float animated_w_ = 0.0F; // Ample animat (eixampla segons contingut)
|
|
|
|
// Estat del highlight (rectangle del cursor). Es lerpa cap a l'item
|
|
// actiu amb ease-out exponencial; quan el cursor "salta" (open o
|
|
// push/pop de pagina), s'enganxa directament al nou objectiu.
|
|
float highlight_y_ = 0.0F;
|
|
float highlight_h_ = 0.0F;
|
|
bool highlight_snap_ = true;
|
|
|
|
static std::unique_ptr<ServiceMenu> instance;
|
|
};
|
|
|
|
} // namespace System
|