fix: menu.cpp enums UPPER_CASE i statics sense sufix
This commit is contained in:
+187
-187
@@ -49,20 +49,20 @@ namespace Menu {
|
|||||||
static constexpr float HEIGHT_RATE = 12.0F; // smoothing exponencial de l'alçada (~150 ms al 90%)
|
static constexpr float HEIGHT_RATE = 12.0F; // smoothing exponencial de l'alçada (~150 ms al 90%)
|
||||||
|
|
||||||
// --- Items ---
|
// --- Items ---
|
||||||
enum class ItemKind : std::uint8_t { Toggle,
|
enum class ItemKind : std::uint8_t { TOGGLE,
|
||||||
Cycle,
|
CYCLE,
|
||||||
IntRange,
|
INT_RANGE,
|
||||||
Submenu,
|
SUBMENU,
|
||||||
KeyBind,
|
KEY_BIND,
|
||||||
Action };
|
ACTION };
|
||||||
|
|
||||||
struct Item {
|
struct Item {
|
||||||
const char* label;
|
const char* label;
|
||||||
ItemKind kind;
|
ItemKind kind;
|
||||||
std::function<std::string()> getValue; // opcional
|
std::function<std::string()> get_value; // opcional
|
||||||
std::function<void(int dir)> change; // per Toggle/Cycle/IntRange
|
std::function<void(int dir)> change; // per TOGGLE/CYCLE/INT_RANGE
|
||||||
std::function<void()> enter; // per Submenu i Action
|
std::function<void()> enter; // per SUBMENU i ACTION
|
||||||
SDL_Scancode* scancode{nullptr}; // per KeyBind
|
SDL_Scancode* scancode{nullptr}; // per KEY_BIND
|
||||||
std::function<bool()> visible; // nullptr ⇒ sempre visible
|
std::function<bool()> visible; // nullptr ⇒ sempre visible
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,12 +78,12 @@ namespace Menu {
|
|||||||
// Troba el pròxim ítem visible en direcció `dir` (±1) a partir de `from`.
|
// Troba el pròxim ítem visible en direcció `dir` (±1) a partir de `from`.
|
||||||
// Si cap és visible retorna `from`.
|
// Si cap és visible retorna `from`.
|
||||||
static auto nextVisibleCursor(const Page& p, int from, int dir) -> int {
|
static auto nextVisibleCursor(const Page& p, int from, int dir) -> int {
|
||||||
const int n = static_cast<int>(p.items.size());
|
const int N = static_cast<int>(p.items.size());
|
||||||
if (n <= 0) {
|
if (N <= 0) {
|
||||||
return from;
|
return from;
|
||||||
}
|
}
|
||||||
for (int i = 1; i <= n; ++i) {
|
for (int i = 1; i <= N; ++i) {
|
||||||
int idx = ((from + dir * i) % n + n) % n;
|
int idx = ((from + dir * i) % N + N) % N;
|
||||||
if (isVisible(p.items[idx])) {
|
if (isVisible(p.items[idx])) {
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
@@ -92,35 +92,35 @@ namespace Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Estat ---
|
// --- Estat ---
|
||||||
static std::vector<Page> stack_;
|
static std::vector<Page> stack;
|
||||||
static std::unique_ptr<Text> font_;
|
static std::unique_ptr<Text> font;
|
||||||
static float open_anim_{0.0F}; // 0 = tancat, 1 = obert
|
static float open_anim{0.0F}; // 0 = tancat, 1 = obert
|
||||||
static float animated_h_{0.0F}; // alçada actual animada (smoothing cap al target visible)
|
static float animated_h{0.0F}; // alçada actual animada (smoothing cap al target visible)
|
||||||
static Uint32 last_ticks_{0};
|
static Uint32 last_ticks{0};
|
||||||
static SDL_Scancode* capturing_{nullptr}; // != null → esperant tecla per assignar
|
static SDL_Scancode* capturing{nullptr}; // != null → esperant tecla per assignar
|
||||||
static bool closing_{false}; // true mentre l'animació de tancament és en curs
|
static bool closing{false}; // true mentre l'animació de tancament és en curs
|
||||||
|
|
||||||
// --- Transició entre pàgines ---
|
// --- Transició entre pàgines ---
|
||||||
static constexpr float TRANSITION_SPEED = 5.5F; // ~180 ms
|
static constexpr float TRANSITION_SPEED = 5.5F; // ~180 ms
|
||||||
static Page transition_outgoing_{.title = "", .items = {}, .cursor = 0};
|
static Page transition_outgoing{.title = "", .items = {}, .cursor = 0};
|
||||||
static bool transition_active_{false};
|
static bool transition_active{false};
|
||||||
static float transition_progress_{1.0F};
|
static float transition_progress{1.0F};
|
||||||
static int transition_dir_{+1}; // +1 endavant, -1 enrere
|
static int transition_dir{+1}; // +1 endavant, -1 enrere
|
||||||
|
|
||||||
// Helpers per triggerar transicions
|
// Helpers per triggerar transicions
|
||||||
static void pushPage(Page newPage) {
|
static void pushPage(Page new_page) {
|
||||||
transition_outgoing_ = stack_.back();
|
transition_outgoing = stack.back();
|
||||||
stack_.push_back(std::move(newPage));
|
stack.push_back(std::move(new_page));
|
||||||
transition_active_ = true;
|
transition_active = true;
|
||||||
transition_progress_ = 0.0F;
|
transition_progress = 0.0F;
|
||||||
transition_dir_ = +1;
|
transition_dir = +1;
|
||||||
}
|
}
|
||||||
static void popPage() {
|
static void popPage() {
|
||||||
transition_outgoing_ = stack_.back();
|
transition_outgoing = stack.back();
|
||||||
stack_.pop_back();
|
stack.pop_back();
|
||||||
transition_active_ = true;
|
transition_active = true;
|
||||||
transition_progress_ = 0.0F;
|
transition_progress = 0.0F;
|
||||||
transition_dir_ = -1;
|
transition_dir = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Helpers ---
|
// --- Helpers ---
|
||||||
@@ -138,11 +138,11 @@ namespace Menu {
|
|||||||
|
|
||||||
static auto buildRoot() -> Page {
|
static auto buildRoot() -> Page {
|
||||||
Page p{.title = Locale::get("menu.titles.root"), .items = {}, .cursor = 0};
|
Page p{.title = Locale::get("menu.titles.root"), .items = {}, .cursor = 0};
|
||||||
p.items.push_back({Locale::get("menu.items.video"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildVideo()); }, nullptr});
|
p.items.push_back({Locale::get("menu.items.video"), ItemKind::SUBMENU, nullptr, nullptr, [] { pushPage(buildVideo()); }, nullptr});
|
||||||
p.items.push_back({Locale::get("menu.items.audio"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildAudio()); }, nullptr});
|
p.items.push_back({Locale::get("menu.items.audio"), ItemKind::SUBMENU, nullptr, nullptr, [] { pushPage(buildAudio()); }, nullptr});
|
||||||
p.items.push_back({Locale::get("menu.items.controls"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildControls()); }, nullptr});
|
p.items.push_back({Locale::get("menu.items.controls"), ItemKind::SUBMENU, nullptr, nullptr, [] { pushPage(buildControls()); }, nullptr});
|
||||||
p.items.push_back({Locale::get("menu.items.game"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildGame()); }, nullptr});
|
p.items.push_back({Locale::get("menu.items.game"), ItemKind::SUBMENU, nullptr, nullptr, [] { pushPage(buildGame()); }, nullptr});
|
||||||
p.items.push_back({Locale::get("menu.items.system"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildSystem()); }, nullptr});
|
p.items.push_back({Locale::get("menu.items.system"), ItemKind::SUBMENU, nullptr, nullptr, [] { pushPage(buildSystem()); }, nullptr});
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ namespace Menu {
|
|||||||
|
|
||||||
// Zoom i fullscreen: sense sentit a WASM (el navegador posseix el canvas)
|
// Zoom i fullscreen: sense sentit a WASM (el navegador posseix el canvas)
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
p.items.push_back({Locale::get("menu.items.zoom"), ItemKind::IntRange, [] {
|
p.items.push_back({Locale::get("menu.items.zoom"), ItemKind::INT_RANGE, [] {
|
||||||
char buf[16];
|
char buf[16];
|
||||||
std::snprintf(buf, sizeof(buf), "%dX", Screen::get()->getZoom());
|
std::snprintf(buf, sizeof(buf), "%dX", Screen::get()->getZoom());
|
||||||
return std::string(buf); }, [](int dir) {
|
return std::string(buf); }, [](int dir) {
|
||||||
@@ -159,15 +159,15 @@ namespace Menu {
|
|||||||
} else if (dir > 0) { Screen::get()->incZoom();
|
} else if (dir > 0) { Screen::get()->incZoom();
|
||||||
} }, nullptr, nullptr});
|
} }, nullptr, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.screen"), ItemKind::Toggle, [] { return std::string(Screen::get()->isFullscreen() ? Locale::get("menu.values.fullscreen") : Locale::get("menu.values.windowed")); }, [](int) { Screen::get()->toggleFullscreen(); }, nullptr, nullptr, nullptr});
|
p.items.push_back({Locale::get("menu.items.screen"), ItemKind::TOGGLE, [] { return std::string(Screen::get()->isFullscreen() ? Locale::get("menu.values.fullscreen") : Locale::get("menu.values.windowed")); }, [](int) { Screen::get()->toggleFullscreen(); }, nullptr, nullptr, nullptr});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Opcions visuals generals (sempre visibles)
|
// Opcions visuals generals (sempre visibles)
|
||||||
p.items.push_back({Locale::get("menu.items.aspect_4_3"), ItemKind::Toggle, [] { return yesNo(Options::video.aspect_ratio_4_3); }, [](int) { Screen::get()->toggleAspectRatio(); }, nullptr, nullptr, nullptr});
|
p.items.push_back({Locale::get("menu.items.aspect_4_3"), ItemKind::TOGGLE, [] { return yesNo(Options::video.aspect_ratio_4_3); }, [](int) { Screen::get()->toggleAspectRatio(); }, nullptr, nullptr, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.vsync"), ItemKind::Toggle, [] { return onOff(Options::video.vsync); }, [](int) { Screen::get()->toggleVSync(); }, nullptr, nullptr, nullptr});
|
p.items.push_back({Locale::get("menu.items.vsync"), ItemKind::TOGGLE, [] { return onOff(Options::video.vsync); }, [](int) { Screen::get()->toggleVSync(); }, nullptr, nullptr, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.scaling_mode"), ItemKind::Cycle, [] {
|
p.items.push_back({Locale::get("menu.items.scaling_mode"), ItemKind::CYCLE, [] {
|
||||||
switch (Options::video.scaling_mode) {
|
switch (Options::video.scaling_mode) {
|
||||||
case Options::ScalingMode::DISABLED: return std::string(Locale::get("menu.values.scaling_disabled"));
|
case Options::ScalingMode::DISABLED: return std::string(Locale::get("menu.values.scaling_disabled"));
|
||||||
case Options::ScalingMode::STRETCH: return std::string(Locale::get("menu.values.scaling_stretch"));
|
case Options::ScalingMode::STRETCH: return std::string(Locale::get("menu.values.scaling_stretch"));
|
||||||
@@ -177,30 +177,30 @@ namespace Menu {
|
|||||||
}
|
}
|
||||||
return std::string(Locale::get("menu.values.scaling_integer")); }, [](int dir) { Screen::get()->cycleScalingMode(dir); }, nullptr, nullptr, nullptr});
|
return std::string(Locale::get("menu.values.scaling_integer")); }, [](int dir) { Screen::get()->cycleScalingMode(dir); }, nullptr, nullptr, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.texture_filter"), ItemKind::Cycle, [] { return std::string(Options::video.texture_filter == Options::TextureFilter::LINEAR
|
p.items.push_back({Locale::get("menu.items.texture_filter"), ItemKind::CYCLE, [] { return std::string(Options::video.texture_filter == Options::TextureFilter::LINEAR
|
||||||
? Locale::get("menu.values.linear")
|
? Locale::get("menu.values.linear")
|
||||||
: Locale::get("menu.values.nearest")); }, [](int dir) { Screen::get()->cycleTextureFilter(dir); }, nullptr, nullptr, nullptr});
|
: Locale::get("menu.values.nearest")); }, [](int dir) { Screen::get()->cycleTextureFilter(dir); }, nullptr, nullptr, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.internal_resolution"), ItemKind::IntRange, [] {
|
p.items.push_back({Locale::get("menu.items.internal_resolution"), ItemKind::INT_RANGE, [] {
|
||||||
char buf[16];
|
char buf[16];
|
||||||
std::snprintf(buf, sizeof(buf), "%dX", Options::video.internal_resolution);
|
std::snprintf(buf, sizeof(buf), "%dX", Options::video.internal_resolution);
|
||||||
return std::string(buf); }, [](int dir) { Screen::get()->changeInternalResolution(dir); }, nullptr, nullptr, nullptr});
|
return std::string(buf); }, [](int dir) { Screen::get()->changeInternalResolution(dir); }, nullptr, nullptr, nullptr});
|
||||||
|
|
||||||
// Bloc shaders: no disponible a WASM (NO_SHADERS, sense SDL3 GPU a WebGL2)
|
// Bloc shaders: no disponible a WASM (NO_SHADERS, sense SDL3 GPU a WebGL2)
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
p.items.push_back({Locale::get("menu.items.shader"), ItemKind::Toggle, [] { return onOff(Options::video.shader_enabled); }, [](int) { Screen::get()->toggleShaders(); }, nullptr, nullptr, nullptr});
|
p.items.push_back({Locale::get("menu.items.shader"), ItemKind::TOGGLE, [] { return onOff(Options::video.shader_enabled); }, [](int) { Screen::get()->toggleShaders(); }, nullptr, nullptr, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.shader_type"), ItemKind::Cycle, [] { return std::string(Screen::get()->getActiveShaderName()); }, [](int dir) {
|
p.items.push_back({Locale::get("menu.items.shader_type"), ItemKind::CYCLE, [] { return std::string(Screen::get()->getActiveShaderName()); }, [](int dir) {
|
||||||
if (dir < 0) { Screen::get()->prevShaderType();
|
if (dir < 0) { Screen::get()->prevShaderType();
|
||||||
} else { Screen::get()->nextShaderType();
|
} else { Screen::get()->nextShaderType();
|
||||||
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
|
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.preset"), ItemKind::Cycle, [] { return std::string(Screen::get()->getCurrentPresetName()); }, [](int dir) {
|
p.items.push_back({Locale::get("menu.items.preset"), ItemKind::CYCLE, [] { return std::string(Screen::get()->getCurrentPresetName()); }, [](int dir) {
|
||||||
if (dir < 0) { Screen::get()->prevPreset();
|
if (dir < 0) { Screen::get()->prevPreset();
|
||||||
} else { Screen::get()->nextPreset();
|
} else { Screen::get()->nextPreset();
|
||||||
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
|
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.supersampling"), ItemKind::Toggle, [] { return onOff(Options::video.supersampling); }, [](int) { Screen::get()->toggleSupersampling(); }, nullptr, nullptr, [] {
|
p.items.push_back({Locale::get("menu.items.supersampling"), ItemKind::TOGGLE, [] { return onOff(Options::video.supersampling); }, [](int) { Screen::get()->toggleSupersampling(); }, nullptr, nullptr, [] {
|
||||||
if (!Options::video.shader_enabled) { return false;
|
if (!Options::video.shader_enabled) { return false;
|
||||||
}
|
}
|
||||||
const char* name = Screen::get()->getActiveShaderName();
|
const char* name = Screen::get()->getActiveShaderName();
|
||||||
@@ -208,7 +208,7 @@ namespace Menu {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Informació de render
|
// Informació de render
|
||||||
p.items.push_back({Locale::get("menu.items.render_info"), ItemKind::Cycle, [] {
|
p.items.push_back({Locale::get("menu.items.render_info"), ItemKind::CYCLE, [] {
|
||||||
switch (Options::render_info.position) {
|
switch (Options::render_info.position) {
|
||||||
case Options::RenderInfoPosition::OFF: return std::string(Locale::get("menu.values.off"));
|
case Options::RenderInfoPosition::OFF: return std::string(Locale::get("menu.values.off"));
|
||||||
case Options::RenderInfoPosition::TOP: return std::string(Locale::get("menu.values.top"));
|
case Options::RenderInfoPosition::TOP: return std::string(Locale::get("menu.values.top"));
|
||||||
@@ -216,7 +216,7 @@ namespace Menu {
|
|||||||
}
|
}
|
||||||
return std::string(Locale::get("menu.values.off")); }, [](int dir) { Overlay::cycleRenderInfo(dir); }, nullptr, nullptr, nullptr});
|
return std::string(Locale::get("menu.values.off")); }, [](int dir) { Overlay::cycleRenderInfo(dir); }, nullptr, nullptr, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.uptime"), ItemKind::Toggle, [] { return onOff(Options::render_info.show_time); }, [](int) { Options::render_info.show_time = !Options::render_info.show_time; }, nullptr, nullptr, [] { return Options::render_info.position != Options::RenderInfoPosition::OFF; }});
|
p.items.push_back({Locale::get("menu.items.uptime"), ItemKind::TOGGLE, [] { return onOff(Options::render_info.show_time); }, [](int) { Options::render_info.show_time = !Options::render_info.show_time; }, nullptr, nullptr, [] { return Options::render_info.position != Options::RenderInfoPosition::OFF; }});
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@@ -241,34 +241,34 @@ namespace Menu {
|
|||||||
|
|
||||||
static auto buildControls() -> Page {
|
static auto buildControls() -> Page {
|
||||||
Page p{.title = Locale::get("menu.titles.controls"), .items = {}, .cursor = 0};
|
Page p{.title = Locale::get("menu.titles.controls"), .items = {}, .cursor = 0};
|
||||||
p.items.push_back({Locale::get("menu.items.move_up"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.up});
|
p.items.push_back({Locale::get("menu.items.move_up"), ItemKind::KEY_BIND, nullptr, nullptr, nullptr, &Options::keys_game.up});
|
||||||
p.items.push_back({Locale::get("menu.items.move_down"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.down});
|
p.items.push_back({Locale::get("menu.items.move_down"), ItemKind::KEY_BIND, nullptr, nullptr, nullptr, &Options::keys_game.down});
|
||||||
p.items.push_back({Locale::get("menu.items.move_left"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.left});
|
p.items.push_back({Locale::get("menu.items.move_left"), ItemKind::KEY_BIND, nullptr, nullptr, nullptr, &Options::keys_game.left});
|
||||||
p.items.push_back({Locale::get("menu.items.move_right"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.right});
|
p.items.push_back({Locale::get("menu.items.move_right"), ItemKind::KEY_BIND, nullptr, nullptr, nullptr, &Options::keys_game.right});
|
||||||
p.items.push_back({Locale::get("menu.items.menu_key"), ItemKind::KeyBind, nullptr, nullptr, nullptr, KeyConfig::scancodePtr("menu_toggle")});
|
p.items.push_back({Locale::get("menu.items.menu_key"), ItemKind::KEY_BIND, nullptr, nullptr, nullptr, KeyConfig::scancodePtr("menu_toggle")});
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto buildAudio() -> Page {
|
static auto buildAudio() -> Page {
|
||||||
Page p{.title = Locale::get("menu.titles.audio"), .items = {}, .cursor = 0};
|
Page p{.title = Locale::get("menu.titles.audio"), .items = {}, .cursor = 0};
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.master_enable"), ItemKind::Toggle, [] { return onOff(Options::audio.enabled); }, [](int) {
|
p.items.push_back({Locale::get("menu.items.master_enable"), ItemKind::TOGGLE, [] { return onOff(Options::audio.enabled); }, [](int) {
|
||||||
Options::audio.enabled = !Options::audio.enabled;
|
Options::audio.enabled = !Options::audio.enabled;
|
||||||
Options::applyAudio(); }, nullptr});
|
Options::applyAudio(); }, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.master_volume"), ItemKind::IntRange, [] { return volPct(Options::audio.volume); }, [](int dir) { stepVolume(Options::audio.volume, dir); }, nullptr});
|
p.items.push_back({Locale::get("menu.items.master_volume"), ItemKind::INT_RANGE, [] { return volPct(Options::audio.volume); }, [](int dir) { stepVolume(Options::audio.volume, dir); }, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.music"), ItemKind::Toggle, [] { return onOff(Options::audio.music.enabled); }, [](int) {
|
p.items.push_back({Locale::get("menu.items.music"), ItemKind::TOGGLE, [] { return onOff(Options::audio.music.enabled); }, [](int) {
|
||||||
Options::audio.music.enabled = !Options::audio.music.enabled;
|
Options::audio.music.enabled = !Options::audio.music.enabled;
|
||||||
Options::applyAudio(); }, nullptr});
|
Options::applyAudio(); }, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.music_volume"), ItemKind::IntRange, [] { return volPct(Options::audio.music.volume); }, [](int dir) { stepVolume(Options::audio.music.volume, dir); }, nullptr});
|
p.items.push_back({Locale::get("menu.items.music_volume"), ItemKind::INT_RANGE, [] { return volPct(Options::audio.music.volume); }, [](int dir) { stepVolume(Options::audio.music.volume, dir); }, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.sounds"), ItemKind::Toggle, [] { return onOff(Options::audio.sound.enabled); }, [](int) {
|
p.items.push_back({Locale::get("menu.items.sounds"), ItemKind::TOGGLE, [] { return onOff(Options::audio.sound.enabled); }, [](int) {
|
||||||
Options::audio.sound.enabled = !Options::audio.sound.enabled;
|
Options::audio.sound.enabled = !Options::audio.sound.enabled;
|
||||||
Options::applyAudio(); }, nullptr});
|
Options::applyAudio(); }, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.sounds_volume"), ItemKind::IntRange, [] { return volPct(Options::audio.sound.volume); }, [](int dir) { stepVolume(Options::audio.sound.volume, dir); }, nullptr});
|
p.items.push_back({Locale::get("menu.items.sounds_volume"), ItemKind::INT_RANGE, [] { return volPct(Options::audio.sound.volume); }, [](int dir) { stepVolume(Options::audio.sound.volume, dir); }, nullptr});
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@@ -276,11 +276,11 @@ namespace Menu {
|
|||||||
static auto buildGame() -> Page {
|
static auto buildGame() -> Page {
|
||||||
Page p{.title = Locale::get("menu.titles.game"), .items = {}, .cursor = 0};
|
Page p{.title = Locale::get("menu.titles.game"), .items = {}, .cursor = 0};
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.use_new_logo"), ItemKind::Toggle, [] { return yesNo(Options::game.use_new_logo); }, [](int) { Options::game.use_new_logo = !Options::game.use_new_logo; }, nullptr});
|
p.items.push_back({Locale::get("menu.items.use_new_logo"), ItemKind::TOGGLE, [] { return yesNo(Options::game.use_new_logo); }, [](int) { Options::game.use_new_logo = !Options::game.use_new_logo; }, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.show_title_credits"), ItemKind::Toggle, [] { return yesNo(Options::game.show_title_credits); }, [](int) { Options::game.show_title_credits = !Options::game.show_title_credits; }, nullptr});
|
p.items.push_back({Locale::get("menu.items.show_title_credits"), ItemKind::TOGGLE, [] { return yesNo(Options::game.show_title_credits); }, [](int) { Options::game.show_title_credits = !Options::game.show_title_credits; }, nullptr});
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.show_preload"), ItemKind::Toggle, [] { return yesNo(Options::game.show_preload); }, [](int) { Options::game.show_preload = !Options::game.show_preload; }, nullptr});
|
p.items.push_back({Locale::get("menu.items.show_preload"), ItemKind::TOGGLE, [] { return yesNo(Options::game.show_preload); }, [](int) { Options::game.show_preload = !Options::game.show_preload; }, nullptr});
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@@ -289,7 +289,7 @@ namespace Menu {
|
|||||||
Page p{.title = Locale::get("menu.titles.system"), .items = {}, .cursor = 0};
|
Page p{.title = Locale::get("menu.titles.system"), .items = {}, .cursor = 0};
|
||||||
p.subtitle = std::string("v") + Texts::VERSION + " (" + Version::GIT_HASH + ")";
|
p.subtitle = std::string("v") + Texts::VERSION + " (" + Version::GIT_HASH + ")";
|
||||||
|
|
||||||
p.items.push_back({Locale::get("menu.items.restart"), ItemKind::Action, nullptr, nullptr, [] {
|
p.items.push_back({Locale::get("menu.items.restart"), ItemKind::ACTION, nullptr, nullptr, [] {
|
||||||
if (Director::get()) {
|
if (Director::get()) {
|
||||||
Director::get()->requestRestart();
|
Director::get()->requestRestart();
|
||||||
}
|
}
|
||||||
@@ -298,7 +298,7 @@ namespace Menu {
|
|||||||
nullptr});
|
nullptr});
|
||||||
|
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
p.items.push_back({Locale::get("menu.items.exit_game"), ItemKind::Action, nullptr, nullptr, [] {
|
p.items.push_back({Locale::get("menu.items.exit_game"), ItemKind::ACTION, nullptr, nullptr, [] {
|
||||||
if (Director::get()) {
|
if (Director::get()) {
|
||||||
Director::get()->requestQuit();
|
Director::get()->requestQuit();
|
||||||
}
|
}
|
||||||
@@ -314,11 +314,11 @@ namespace Menu {
|
|||||||
|
|
||||||
// Alpha blending per pixel sobre el buffer ARGB (ABGR en memòria)
|
// Alpha blending per pixel sobre el buffer ARGB (ABGR en memòria)
|
||||||
static void blendRect(Uint32* buf, int x, int y, int w, int h, Uint32 src_argb, Uint8 src_alpha) {
|
static void blendRect(Uint32* buf, int x, int y, int w, int h, Uint32 src_argb, Uint8 src_alpha) {
|
||||||
const Uint8 sa = src_alpha;
|
const Uint8 SA = src_alpha;
|
||||||
const Uint8 sr = src_argb & 0xFF;
|
const Uint8 SR = src_argb & 0xFF;
|
||||||
const Uint8 sg = (src_argb >> 8) & 0xFF;
|
const Uint8 SG = (src_argb >> 8) & 0xFF;
|
||||||
const Uint8 sb = (src_argb >> 16) & 0xFF;
|
const Uint8 SB = (src_argb >> 16) & 0xFF;
|
||||||
const Uint8 inv = 255 - sa;
|
const Uint8 INV = 255 - SA;
|
||||||
for (int row = y; row < y + h; row++) {
|
for (int row = y; row < y + h; row++) {
|
||||||
if (row < 0 || row >= SCREEN_H) {
|
if (row < 0 || row >= SCREEN_H) {
|
||||||
continue;
|
continue;
|
||||||
@@ -332,9 +332,9 @@ namespace Menu {
|
|||||||
Uint8 dr = dst & 0xFF;
|
Uint8 dr = dst & 0xFF;
|
||||||
Uint8 dg = (dst >> 8) & 0xFF;
|
Uint8 dg = (dst >> 8) & 0xFF;
|
||||||
Uint8 db = (dst >> 16) & 0xFF;
|
Uint8 db = (dst >> 16) & 0xFF;
|
||||||
Uint8 r = (sr * sa + dr * inv) / 255;
|
Uint8 r = (SR * SA + dr * INV) / 255;
|
||||||
Uint8 g = (sg * sa + dg * inv) / 255;
|
Uint8 g = (SG * SA + dg * INV) / 255;
|
||||||
Uint8 b = (sb * sa + db * inv) / 255;
|
Uint8 b = (SB * SA + db * INV) / 255;
|
||||||
*p = 0xFF000000U | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
|
*p = 0xFF000000U | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,8 +365,8 @@ namespace Menu {
|
|||||||
// body = (N-1) * ITEM_SPACING + charH — així BOTTOM_PAD és el buit real
|
// body = (N-1) * ITEM_SPACING + charH — així BOTTOM_PAD és el buit real
|
||||||
// sota el text del darrer ítem, no un buit extra per sobre d'un "slot" buit.
|
// sota el text del darrer ítem, no un buit extra per sobre d'un "slot" buit.
|
||||||
static auto boxHeight(const Page& page) -> int {
|
static auto boxHeight(const Page& page) -> int {
|
||||||
const int n = static_cast<int>(std::count_if(page.items.begin(), page.items.end(), [](const auto& it) { return isVisible(it); }));
|
const int N = static_cast<int>(std::count_if(page.items.begin(), page.items.end(), [](const auto& it) { return isVisible(it); }));
|
||||||
int body = (n == 0) ? 8 : ((n - 1) * ITEM_SPACING) + 8;
|
int body = (N == 0) ? 8 : ((N - 1) * ITEM_SPACING) + 8;
|
||||||
int header = HEADER_H + (page.subtitle.empty() ? 0 : SUBTITLE_H);
|
int header = HEADER_H + (page.subtitle.empty() ? 0 : SUBTITLE_H);
|
||||||
return header + body + BOTTOM_PAD;
|
return header + body + BOTTOM_PAD;
|
||||||
}
|
}
|
||||||
@@ -374,89 +374,89 @@ namespace Menu {
|
|||||||
// --- API pública ---
|
// --- API pública ---
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
font_ = std::make_unique<Text>("fonts/8bithud.fnt", "fonts/8bithud.gif");
|
font = std::make_unique<Text>("fonts/8bithud.fnt", "fonts/8bithud.gif");
|
||||||
stack_.clear();
|
stack.clear();
|
||||||
open_anim_ = 0.0F;
|
open_anim = 0.0F;
|
||||||
closing_ = false;
|
closing = false;
|
||||||
last_ticks_ = SDL_GetTicks();
|
last_ticks = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy() {
|
void destroy() {
|
||||||
font_.reset();
|
font.reset();
|
||||||
stack_.clear();
|
stack.clear();
|
||||||
closing_ = false;
|
closing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Actiu": accepta input. Durant l'animació de tancament la pila encara
|
// "Actiu": accepta input. Durant l'animació de tancament la pila encara
|
||||||
// té pàgines però ja no ha de processar tecles.
|
// té pàgines però ja no ha de processar tecles.
|
||||||
auto isOpen() -> bool {
|
auto isOpen() -> bool {
|
||||||
return !stack_.empty() && !closing_;
|
return !stack.empty() && !closing;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Visible": encara hi ha caixa per pintar (incloent close animation).
|
// "Visible": encara hi ha caixa per pintar (incloent close animation).
|
||||||
auto isVisible() -> bool {
|
auto isVisible() -> bool {
|
||||||
return !stack_.empty();
|
return !stack.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggle() {
|
void toggle() {
|
||||||
if (closing_ && !stack_.empty()) {
|
if (closing && !stack.empty()) {
|
||||||
// Cancel·la el tancament en curs — continua l'animació cap a "obert"
|
// Cancel·la el tancament en curs — continua l'animació cap a "obert"
|
||||||
// des del valor actual d'open_anim_.
|
// des del valor actual d'open_anim.
|
||||||
closing_ = false;
|
closing = false;
|
||||||
last_ticks_ = SDL_GetTicks();
|
last_ticks = SDL_GetTicks();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
stack_.push_back(buildRoot());
|
stack.push_back(buildRoot());
|
||||||
open_anim_ = 0.0F;
|
open_anim = 0.0F;
|
||||||
closing_ = false;
|
closing = false;
|
||||||
animated_h_ = static_cast<float>(boxHeight(stack_.back()));
|
animated_h = static_cast<float>(boxHeight(stack.back()));
|
||||||
last_ticks_ = SDL_GetTicks();
|
last_ticks = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// close() no buida la pila immediatament: marca closing_ i deixa que
|
// close() no buida la pila immediatament: marca closing i deixa que
|
||||||
// render() faça decréixer open_anim_ fins a 0. En aquell moment es neteja
|
// render() faça decréixer open_anim fins a 0. En aquell moment es neteja
|
||||||
// l'estat. Si es crida estant ja tancat o tancant-se, no-op.
|
// l'estat. Si es crida estant ja tancat o tancant-se, no-op.
|
||||||
void close() {
|
void close() {
|
||||||
if (stack_.empty() || closing_) {
|
if (stack.empty() || closing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
closing_ = true;
|
closing = true;
|
||||||
capturing_ = nullptr;
|
capturing = nullptr;
|
||||||
transition_active_ = false;
|
transition_active = false;
|
||||||
transition_progress_ = 1.0F;
|
transition_progress = 1.0F;
|
||||||
last_ticks_ = SDL_GetTicks();
|
last_ticks = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto isCapturing() -> bool {
|
auto isCapturing() -> bool {
|
||||||
return capturing_ != nullptr;
|
return capturing != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void captureKey(SDL_Scancode sc) {
|
void captureKey(SDL_Scancode sc) {
|
||||||
if (capturing_ == nullptr) {
|
if (capturing == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sc == SDL_SCANCODE_ESCAPE) {
|
if (sc == SDL_SCANCODE_ESCAPE) {
|
||||||
// Cancel·la
|
// Cancel·la
|
||||||
capturing_ = nullptr;
|
capturing = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*capturing_ = sc;
|
*capturing = sc;
|
||||||
capturing_ = nullptr;
|
capturing = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleKey(SDL_Scancode sc) {
|
void handleKey(SDL_Scancode sc) {
|
||||||
if (!isOpen()) {
|
if (!isOpen()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Page& page = stack_.back();
|
Page& page = stack.back();
|
||||||
if (page.items.empty()) {
|
if (page.items.empty()) {
|
||||||
// Pàgina buida — només backspace surt
|
// Pàgina buida — només backspace surt
|
||||||
if (sc == SDL_SCANCODE_BACKSPACE) {
|
if (sc == SDL_SCANCODE_BACKSPACE) {
|
||||||
if (stack_.size() > 1) {
|
if (stack.size() > 1) {
|
||||||
popPage();
|
popPage();
|
||||||
} else {
|
} else {
|
||||||
close();
|
close();
|
||||||
@@ -477,32 +477,32 @@ namespace Menu {
|
|||||||
page.cursor = nextVisibleCursor(page, page.cursor, +1);
|
page.cursor = nextVisibleCursor(page, page.cursor, +1);
|
||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_LEFT:
|
case SDL_SCANCODE_LEFT:
|
||||||
if (page.items[page.cursor].kind != ItemKind::Submenu &&
|
if (page.items[page.cursor].kind != ItemKind::SUBMENU &&
|
||||||
page.items[page.cursor].change) {
|
page.items[page.cursor].change) {
|
||||||
page.items[page.cursor].change(-1);
|
page.items[page.cursor].change(-1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_RIGHT:
|
case SDL_SCANCODE_RIGHT:
|
||||||
if (page.items[page.cursor].kind != ItemKind::Submenu &&
|
if (page.items[page.cursor].kind != ItemKind::SUBMENU &&
|
||||||
page.items[page.cursor].change) {
|
page.items[page.cursor].change) {
|
||||||
page.items[page.cursor].change(+1);
|
page.items[page.cursor].change(+1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_RETURN:
|
case SDL_SCANCODE_RETURN:
|
||||||
case SDL_SCANCODE_KP_ENTER:
|
case SDL_SCANCODE_KP_ENTER:
|
||||||
if (page.items[page.cursor].kind == ItemKind::Submenu ||
|
if (page.items[page.cursor].kind == ItemKind::SUBMENU ||
|
||||||
page.items[page.cursor].kind == ItemKind::Action) {
|
page.items[page.cursor].kind == ItemKind::ACTION) {
|
||||||
if (page.items[page.cursor].enter) {
|
if (page.items[page.cursor].enter) {
|
||||||
page.items[page.cursor].enter();
|
page.items[page.cursor].enter();
|
||||||
}
|
}
|
||||||
} else if (page.items[page.cursor].kind == ItemKind::KeyBind) {
|
} else if (page.items[page.cursor].kind == ItemKind::KEY_BIND) {
|
||||||
capturing_ = page.items[page.cursor].scancode;
|
capturing = page.items[page.cursor].scancode;
|
||||||
} else if (page.items[page.cursor].change) {
|
} else if (page.items[page.cursor].change) {
|
||||||
page.items[page.cursor].change(+1);
|
page.items[page.cursor].change(+1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_BACKSPACE:
|
case SDL_SCANCODE_BACKSPACE:
|
||||||
if (stack_.size() > 1) {
|
if (stack.size() > 1) {
|
||||||
popPage();
|
popPage();
|
||||||
} else {
|
} else {
|
||||||
close();
|
close();
|
||||||
@@ -514,8 +514,8 @@ namespace Menu {
|
|||||||
// Després de qualsevol acció, si el cursor quedara sobre un ítem ocult
|
// Després de qualsevol acció, si el cursor quedara sobre un ítem ocult
|
||||||
// (possible si una acció ha canviat la visibilitat pròpia de l'ítem actual,
|
// (possible si una acció ha canviat la visibilitat pròpia de l'ítem actual,
|
||||||
// edge case defensiu), salta al següent visible.
|
// edge case defensiu), salta al següent visible.
|
||||||
if (!stack_.empty()) {
|
if (!stack.empty()) {
|
||||||
Page& top = stack_.back();
|
Page& top = stack.back();
|
||||||
if (!top.items.empty() && !isVisible(top.items[top.cursor])) {
|
if (!top.items.empty() && !isVisible(top.items[top.cursor])) {
|
||||||
top.cursor = nextVisibleCursor(top, top.cursor, +1);
|
top.cursor = nextVisibleCursor(top, top.cursor, +1);
|
||||||
}
|
}
|
||||||
@@ -527,12 +527,12 @@ namespace Menu {
|
|||||||
// clip_x_min/clip_x_max limiten on es dibuixa text i la línia separadora.
|
// clip_x_min/clip_x_max limiten on es dibuixa text i la línia separadora.
|
||||||
static void renderPageContent(Uint32* pixel_data, const Page& page, int box_x, int box_y, int x_offset, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) {
|
static void renderPageContent(Uint32* pixel_data, const Page& page, int box_x, int box_y, int x_offset, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) {
|
||||||
// Títol
|
// Títol
|
||||||
int title_w = font_->width(page.title);
|
int title_w = font->width(page.title);
|
||||||
int title_x = box_x + ((BOX_W - title_w) / 2) + x_offset;
|
int title_x = box_x + ((BOX_W - title_w) / 2) + x_offset;
|
||||||
font_->drawClipped(pixel_data, title_x, box_y + TITLE_PAD_Y, page.title, TITLE_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, title_x, box_y + TITLE_PAD_Y, page.title, TITLE_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
|
|
||||||
// Línia sota el títol (també lliscada) — clippada manualment
|
// Línia sota el títol (també lliscada) — clippada manualment
|
||||||
int title_line_y = box_y + TITLE_PAD_Y + font_->charHeight() + 2;
|
int title_line_y = box_y + TITLE_PAD_Y + font->charHeight() + 2;
|
||||||
if (title_line_y >= clip_y_min && title_line_y < clip_y_max) {
|
if (title_line_y >= clip_y_min && title_line_y < clip_y_max) {
|
||||||
int line_x = box_x + 4 + x_offset;
|
int line_x = box_x + 4 + x_offset;
|
||||||
int line_w = BOX_W - 8;
|
int line_w = BOX_W - 8;
|
||||||
@@ -546,17 +546,17 @@ namespace Menu {
|
|||||||
// Subtítol opcional (sota la línia del títol, abans dels items)
|
// Subtítol opcional (sota la línia del títol, abans dels items)
|
||||||
int items_y = title_line_y + 4;
|
int items_y = title_line_y + 4;
|
||||||
if (!page.subtitle.empty()) {
|
if (!page.subtitle.empty()) {
|
||||||
int sub_w = font_->width(page.subtitle.c_str());
|
int sub_w = font->width(page.subtitle.c_str());
|
||||||
int sub_x = box_x + ((BOX_W - sub_w) / 2) + x_offset;
|
int sub_x = box_x + ((BOX_W - sub_w) / 2) + x_offset;
|
||||||
font_->drawClipped(pixel_data, sub_x, items_y, page.subtitle.c_str(), LABEL_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, sub_x, items_y, page.subtitle.c_str(), LABEL_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
items_y += SUBTITLE_H;
|
items_y += SUBTITLE_H;
|
||||||
}
|
}
|
||||||
// Compta visibles — si cap, dibuixa placeholder (caixa totalment col·lapsada però oberta)
|
// Compta visibles — si cap, dibuixa placeholder (caixa totalment col·lapsada però oberta)
|
||||||
const int visible_count = static_cast<int>(std::count_if(page.items.begin(), page.items.end(), [](const auto& it) { return isVisible(it); }));
|
const int VISIBLE_COUNT = static_cast<int>(std::count_if(page.items.begin(), page.items.end(), [](const auto& it) { return isVisible(it); }));
|
||||||
if (visible_count == 0) {
|
if (VISIBLE_COUNT == 0) {
|
||||||
const char* empty_text = Locale::get("menu.values.empty");
|
const char* empty_text = Locale::get("menu.values.empty");
|
||||||
int ew = font_->width(empty_text);
|
int ew = font->width(empty_text);
|
||||||
font_->drawClipped(pixel_data, box_x + ((BOX_W - ew) / 2) + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, box_x + ((BOX_W - ew) / 2) + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,30 +571,30 @@ namespace Menu {
|
|||||||
bool selected = (static_cast<int>(i) == page.cursor);
|
bool selected = (static_cast<int>(i) == page.cursor);
|
||||||
Uint32 label_color = selected ? CURSOR_COLOR : LABEL_COLOR;
|
Uint32 label_color = selected ? CURSOR_COLOR : LABEL_COLOR;
|
||||||
|
|
||||||
// Action: sense valor a la dreta — centrem el label amb el cursor just a l'esquerra.
|
// ACTION: sense valor a la dreta — centrem el label amb el cursor just a l'esquerra.
|
||||||
if (item.kind == ItemKind::Action) {
|
if (item.kind == ItemKind::ACTION) {
|
||||||
int lw = font_->width(item.label);
|
int lw = font->width(item.label);
|
||||||
int lx = box_x + ((BOX_W - lw) / 2) + x_offset;
|
int lx = box_x + ((BOX_W - lw) / 2) + x_offset;
|
||||||
if (selected) {
|
if (selected) {
|
||||||
font_->drawClipped(pixel_data, lx - font_->width("> "), y, ">", CURSOR_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, lx - font->width("> "), y, ">", CURSOR_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
}
|
}
|
||||||
font_->drawClipped(pixel_data, lx, y, item.label, label_color, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, lx, y, item.label, label_color, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
font_->drawClipped(pixel_data, box_x + 4 + x_offset, y, ">", CURSOR_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, box_x + 4 + x_offset, y, ">", CURSOR_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
font_->drawClipped(pixel_data, box_x + ITEM_PAD_X + x_offset, y, item.label, label_color, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, box_x + ITEM_PAD_X + x_offset, y, item.label, label_color, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
|
|
||||||
if (item.kind == ItemKind::Submenu) {
|
if (item.kind == ItemKind::SUBMENU) {
|
||||||
const char* arrow = ">>";
|
const char* arrow = ">>";
|
||||||
int aw = font_->width(arrow);
|
int aw = font->width(arrow);
|
||||||
Uint32 ac = selected ? CURSOR_COLOR : VALUE_COLOR;
|
Uint32 ac = selected ? CURSOR_COLOR : VALUE_COLOR;
|
||||||
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - aw + x_offset, y, arrow, ac, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - aw + x_offset, y, arrow, ac, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
} else if (item.kind == ItemKind::KeyBind) {
|
} else if (item.kind == ItemKind::KEY_BIND) {
|
||||||
bool this_capturing = (capturing_ == item.scancode);
|
bool this_capturing = (capturing == item.scancode);
|
||||||
const char* text = nullptr;
|
const char* text = nullptr;
|
||||||
if (this_capturing) {
|
if (this_capturing) {
|
||||||
text = Locale::get("menu.values.press_key");
|
text = Locale::get("menu.values.press_key");
|
||||||
@@ -606,83 +606,83 @@ namespace Menu {
|
|||||||
if ((text == nullptr) || (*text == 0)) {
|
if ((text == nullptr) || (*text == 0)) {
|
||||||
text = Locale::get("menu.values.unknown");
|
text = Locale::get("menu.values.unknown");
|
||||||
}
|
}
|
||||||
int tw = font_->width(text);
|
int tw = font->width(text);
|
||||||
Uint32 tc = 0;
|
Uint32 tc = 0;
|
||||||
if (this_capturing) {
|
if (this_capturing) {
|
||||||
tc = 0xFF00FFFF;
|
tc = 0xFF00FFFF;
|
||||||
} else {
|
} else {
|
||||||
tc = selected ? CURSOR_COLOR : VALUE_COLOR;
|
tc = selected ? CURSOR_COLOR : VALUE_COLOR;
|
||||||
}
|
}
|
||||||
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - tw + x_offset, y, text, tc, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - tw + x_offset, y, text, tc, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
} else if (item.getValue) {
|
} else if (item.get_value) {
|
||||||
std::string value = item.getValue();
|
std::string value = item.get_value();
|
||||||
int value_w = font_->width(value.c_str());
|
int value_w = font->width(value.c_str());
|
||||||
Uint32 value_color = selected ? CURSOR_COLOR : VALUE_COLOR;
|
Uint32 value_color = selected ? CURSOR_COLOR : VALUE_COLOR;
|
||||||
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - value_w + x_offset, y, value.c_str(), value_color, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
font->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - value_w + x_offset, y, value.c_str(), value_color, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void render(Uint32* pixel_data) {
|
void render(Uint32* pixel_data) {
|
||||||
if (!isVisible() || !font_ || (pixel_data == nullptr)) {
|
if (!isVisible() || !font || (pixel_data == nullptr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delta time
|
// Delta time
|
||||||
Uint32 now = SDL_GetTicks();
|
Uint32 now = SDL_GetTicks();
|
||||||
float dt = static_cast<float>(now - last_ticks_) / 1000.0F;
|
float dt = static_cast<float>(now - last_ticks) / 1000.0F;
|
||||||
last_ticks_ = now;
|
last_ticks = now;
|
||||||
if (closing_) {
|
if (closing) {
|
||||||
open_anim_ -= CLOSE_SPEED * dt;
|
open_anim -= CLOSE_SPEED * dt;
|
||||||
if (open_anim_ <= 0.0F) {
|
if (open_anim <= 0.0F) {
|
||||||
// Animació de tancament completada — buida l'estat de veritat.
|
// Animació de tancament completada — buida l'estat de veritat.
|
||||||
open_anim_ = 0.0F;
|
open_anim = 0.0F;
|
||||||
stack_.clear();
|
stack.clear();
|
||||||
animated_h_ = 0.0F;
|
animated_h = 0.0F;
|
||||||
closing_ = false;
|
closing = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (open_anim_ < 1.0F) {
|
} else if (open_anim < 1.0F) {
|
||||||
open_anim_ += OPEN_SPEED * dt;
|
open_anim += OPEN_SPEED * dt;
|
||||||
open_anim_ = std::min(open_anim_, 1.0F);
|
open_anim = std::min(open_anim, 1.0F);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avança transició
|
// Avança transició
|
||||||
if (transition_active_) {
|
if (transition_active) {
|
||||||
transition_progress_ += TRANSITION_SPEED * dt;
|
transition_progress += TRANSITION_SPEED * dt;
|
||||||
if (transition_progress_ >= 1.0F) {
|
if (transition_progress >= 1.0F) {
|
||||||
transition_progress_ = 1.0F;
|
transition_progress = 1.0F;
|
||||||
transition_active_ = false;
|
transition_active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Page& page = stack_.back();
|
const Page& page = stack.back();
|
||||||
const int current_h = boxHeight(page);
|
const int CURRENT_H = boxHeight(page);
|
||||||
|
|
||||||
// Smoothing exponencial de l'alçada cap al target (pàgina actual + ítems visibles).
|
// Smoothing exponencial de l'alçada cap al target (pàgina actual + ítems visibles).
|
||||||
// Permet que el menú reaccione amb animació quan una opció canvia la visibilitat
|
// Permet que el menú reaccione amb animació quan una opció canvia la visibilitat
|
||||||
// d'altres ítems en calent (p. ex. shader=off → shader_type/preset/supersampling).
|
// d'altres ítems en calent (p. ex. shader=off → shader_type/preset/supersampling).
|
||||||
if (animated_h_ <= 0.0F) {
|
if (animated_h <= 0.0F) {
|
||||||
animated_h_ = static_cast<float>(current_h);
|
animated_h = static_cast<float>(CURRENT_H);
|
||||||
} else {
|
} else {
|
||||||
float diff = static_cast<float>(current_h) - animated_h_;
|
float diff = static_cast<float>(CURRENT_H) - animated_h;
|
||||||
if (std::fabs(diff) < 0.5F) {
|
if (std::fabs(diff) < 0.5F) {
|
||||||
animated_h_ = static_cast<float>(current_h);
|
animated_h = static_cast<float>(CURRENT_H);
|
||||||
} else {
|
} else {
|
||||||
float t = HEIGHT_RATE * dt;
|
float t = HEIGHT_RATE * dt;
|
||||||
t = std::min(t, 1.0F);
|
t = std::min(t, 1.0F);
|
||||||
animated_h_ += diff * t;
|
animated_h += diff * t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float eased = Easing::outQuad(open_anim_);
|
float eased = Easing::outQuad(open_anim);
|
||||||
|
|
||||||
// Calcula alçada (amb transició si escau)
|
// Calcula alçada (amb transició si escau)
|
||||||
int target_h = static_cast<int>(animated_h_);
|
int target_h = static_cast<int>(animated_h);
|
||||||
if (transition_active_) {
|
if (transition_active) {
|
||||||
int outgoing_h = boxHeight(transition_outgoing_);
|
int outgoing_h = boxHeight(transition_outgoing);
|
||||||
float tp = Easing::outQuad(transition_progress_);
|
float tp = Easing::outQuad(transition_progress);
|
||||||
target_h = Easing::lerpInt(outgoing_h, static_cast<int>(animated_h_), tp);
|
target_h = Easing::lerpInt(outgoing_h, static_cast<int>(animated_h), tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caixa creix verticalment durant l'obertura
|
// Caixa creix verticalment durant l'obertura
|
||||||
@@ -696,17 +696,17 @@ namespace Menu {
|
|||||||
blendRect(pixel_data, box_x, box_y, BOX_W, box_h, BG_COLOR, alpha);
|
blendRect(pixel_data, box_x, box_y, BOX_W, box_h, BG_COLOR, alpha);
|
||||||
|
|
||||||
// El contingut només apareix quan la caixa és prou gran
|
// El contingut només apareix quan la caixa és prou gran
|
||||||
if (open_anim_ >= 0.9F) {
|
if (open_anim >= 0.9F) {
|
||||||
int clip_x_min = box_x + 1;
|
int clip_x_min = box_x + 1;
|
||||||
int clip_x_max = box_x + BOX_W - 1;
|
int clip_x_max = box_x + BOX_W - 1;
|
||||||
int clip_y_min = box_y + 1;
|
int clip_y_min = box_y + 1;
|
||||||
int clip_y_max = box_y + box_h - 1;
|
int clip_y_max = box_y + box_h - 1;
|
||||||
|
|
||||||
if (transition_active_) {
|
if (transition_active) {
|
||||||
float tp = Easing::outQuad(transition_progress_);
|
float tp = Easing::outQuad(transition_progress);
|
||||||
int out_offset = static_cast<int>(-transition_dir_ * BOX_W * tp);
|
int out_offset = static_cast<int>(-transition_dir * BOX_W * tp);
|
||||||
int new_offset = static_cast<int>(transition_dir_ * BOX_W * (1.0F - tp));
|
int new_offset = static_cast<int>(transition_dir * BOX_W * (1.0F - tp));
|
||||||
renderPageContent(pixel_data, transition_outgoing_, box_x, box_y, out_offset, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
renderPageContent(pixel_data, transition_outgoing, box_x, box_y, out_offset, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
renderPageContent(pixel_data, page, box_x, box_y, new_offset, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
renderPageContent(pixel_data, page, box_x, box_y, new_offset, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
} else {
|
} else {
|
||||||
renderPageContent(pixel_data, page, box_x, box_y, 0, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
renderPageContent(pixel_data, page, box_x, box_y, 0, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
|
||||||
|
|||||||
Reference in New Issue
Block a user