feat(service-menu): submenu RESOLUCIO amb canvi en calent de l'offscreen
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
#include "core/audio/audio.hpp"
|
||||
#include "core/config/engine_config.hpp"
|
||||
#include "core/defaults/audio.hpp"
|
||||
#include "core/defaults/rendering.hpp"
|
||||
#include "core/defaults/service_menu.hpp"
|
||||
#include "core/locale/locale.hpp"
|
||||
#include "core/rendering/sdl_manager.hpp"
|
||||
@@ -64,6 +65,18 @@ namespace {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Resol el text del label d'un item: prioritza label_text (literal) sobre
|
||||
// label_key (locale). Retorna cadena buida si tots dos son buits.
|
||||
auto resolveLabel(const System::ServiceMenu::Item& item) -> std::string {
|
||||
if (!item.label_text.empty()) {
|
||||
return item.label_text;
|
||||
}
|
||||
if (item.label_key.empty()) {
|
||||
return {};
|
||||
}
|
||||
return Locale::get().text(item.label_key);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace System {
|
||||
@@ -123,6 +136,7 @@ namespace System {
|
||||
return ServiceMenu::Item{
|
||||
.kind = ServiceMenu::Kind::SUBMENU,
|
||||
.label_key = label_key,
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = std::move(on_activate),
|
||||
.get_value_text = {},
|
||||
@@ -145,7 +159,7 @@ namespace System {
|
||||
stack_.push_back(std::move(root));
|
||||
}
|
||||
|
||||
auto ServiceMenu::buildVideoPage() const -> Page {
|
||||
auto ServiceMenu::buildVideoPage() -> Page {
|
||||
// Helper: localitza ON/OFF per a TOGGLE items.
|
||||
auto on_off_text = [](bool v) -> std::string {
|
||||
return Locale::get().text(v ? "service_menu.value_on" : "service_menu.value_off");
|
||||
@@ -160,6 +174,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::INT_RANGE,
|
||||
.label_key = "service_menu.video_zoom",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [sdl] { return std::format("{:.1f}X", sdl->getScaleFactor()); },
|
||||
@@ -174,6 +189,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::TOGGLE,
|
||||
.label_key = "service_menu.video_fullscreen",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [sdl, on_off_text] { return on_off_text(sdl->isFullscreen()); },
|
||||
@@ -183,15 +199,29 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::TOGGLE,
|
||||
.label_key = "service_menu.video_vsync",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [on_off_text] { return on_off_text(ConfigYaml::engine_config.rendering.vsync != 0); },
|
||||
.on_change = [sdl](int) { sdl->toggleVSync(); },
|
||||
},
|
||||
// RESOLUCIO (sub-submenu amb els 5 presets; mostra l'actual com a valor)
|
||||
Item{
|
||||
.kind = Kind::SUBMENU,
|
||||
.label_key = "service_menu.video_resolution",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = [this] { pushPage(buildResolutionPage()); },
|
||||
.get_value_text = [] { return std::format("{}X{}",
|
||||
ConfigYaml::engine_config.rendering.render_width,
|
||||
ConfigYaml::engine_config.rendering.render_height); },
|
||||
.on_change = {},
|
||||
},
|
||||
// ANTIALIAS
|
||||
Item{
|
||||
.kind = Kind::TOGGLE,
|
||||
.label_key = "service_menu.video_aa",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [on_off_text] { return on_off_text(ConfigYaml::engine_config.rendering.antialias != 0); },
|
||||
@@ -201,6 +231,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::TOGGLE,
|
||||
.label_key = "service_menu.video_postfx",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [sdl, on_off_text] { return on_off_text(sdl->isPostFxEnabled()); },
|
||||
@@ -210,6 +241,36 @@ namespace System {
|
||||
return page;
|
||||
}
|
||||
|
||||
auto ServiceMenu::buildResolutionPage() const -> Page {
|
||||
Page page;
|
||||
page.title_key = "service_menu.video_resolution";
|
||||
// El cursor arrenca sobre el preset actual perquè l'usuari vegi quin
|
||||
// esta seleccionat sense buscar-lo.
|
||||
const int CURR_W = ConfigYaml::engine_config.rendering.render_width;
|
||||
const int CURR_H = ConfigYaml::engine_config.rendering.render_height;
|
||||
std::size_t cursor = 0;
|
||||
SDLManager* sdl = sdl_;
|
||||
for (std::size_t i = 0; i < Defaults::Rendering::RESOLUTION_PRESETS.size(); ++i) {
|
||||
const auto& preset = Defaults::Rendering::RESOLUTION_PRESETS[i];
|
||||
if (preset.w == CURR_W && preset.h == CURR_H) {
|
||||
cursor = i;
|
||||
}
|
||||
const int PW = preset.w;
|
||||
const int PH = preset.h;
|
||||
page.items.push_back(Item{
|
||||
.kind = Kind::ACTION,
|
||||
.label_key = {},
|
||||
.label_text = std::format("{}X{}", PW, PH),
|
||||
.selectable = true,
|
||||
.on_activate = [sdl, PW, PH] { sdl->setRenderResolution(PW, PH); },
|
||||
.get_value_text = {},
|
||||
.on_change = {},
|
||||
});
|
||||
}
|
||||
page.cursor = cursor;
|
||||
return page;
|
||||
}
|
||||
|
||||
auto ServiceMenu::buildAudioPage() -> Page {
|
||||
auto on_off_text = [](bool v) -> std::string {
|
||||
return Locale::get().text(v ? "service_menu.value_on" : "service_menu.value_off");
|
||||
@@ -229,6 +290,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::TOGGLE,
|
||||
.label_key = "service_menu.audio_master",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [on_off_text] {
|
||||
@@ -243,6 +305,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::INT_RANGE,
|
||||
.label_key = "service_menu.audio_master_volume",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [] {
|
||||
@@ -258,6 +321,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::TOGGLE,
|
||||
.label_key = "service_menu.audio_music",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [on_off_text] {
|
||||
@@ -272,6 +336,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::INT_RANGE,
|
||||
.label_key = "service_menu.audio_music_volume",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [] {
|
||||
@@ -287,6 +352,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::TOGGLE,
|
||||
.label_key = "service_menu.audio_sound",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [on_off_text] {
|
||||
@@ -301,6 +367,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::INT_RANGE,
|
||||
.label_key = "service_menu.audio_sound_volume",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [] {
|
||||
@@ -330,6 +397,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::CYCLE,
|
||||
.label_key = "service_menu.options_language",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [] { return Locale::get().text("language." + ConfigYaml::engine_config.locale); },
|
||||
@@ -344,6 +412,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::TOGGLE,
|
||||
.label_key = "service_menu.options_show_info",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = {},
|
||||
.get_value_text = [debug, on_off_text] { return on_off_text(debug != nullptr && debug->isVisible()); },
|
||||
@@ -369,6 +438,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::ACTION,
|
||||
.label_key = "service_menu.system_restart",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = [this] {
|
||||
pushConfirmPage("service_menu.confirm_restart", [] {
|
||||
@@ -385,6 +455,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::ACTION,
|
||||
.label_key = "service_menu.exit",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = [this] {
|
||||
pushConfirmPage("service_menu.confirm_exit", [] {
|
||||
@@ -409,6 +480,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::ACTION,
|
||||
.label_key = "service_menu.confirm_no",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = [this] { popPage(); },
|
||||
.get_value_text = {},
|
||||
@@ -417,6 +489,7 @@ namespace System {
|
||||
Item{
|
||||
.kind = Kind::ACTION,
|
||||
.label_key = "service_menu.confirm_yes",
|
||||
.label_text = {},
|
||||
.selectable = true,
|
||||
.on_activate = std::move(yes_callback),
|
||||
.get_value_text = {},
|
||||
@@ -572,10 +645,8 @@ namespace System {
|
||||
content_w = std::max(content_w, Graphics::VectorText::getTextWidth(page.subtitle_provider(), SUBTITLE_SCALE, TEXT_SPACING));
|
||||
}
|
||||
for (const Item& item : page.items) {
|
||||
const std::string LABEL = item.label_key.empty()
|
||||
? std::string{}
|
||||
: Locale::get().text(item.label_key);
|
||||
if (item.label_key.empty() && item.get_value_text) {
|
||||
const std::string LABEL = resolveLabel(item);
|
||||
if (LABEL.empty() && item.get_value_text) {
|
||||
content_w = std::max(content_w, Graphics::VectorText::getTextWidth(item.get_value_text(), ITEM_SCALE, TEXT_SPACING));
|
||||
} else if (item.get_value_text) {
|
||||
const float LABEL_W = Graphics::VectorText::getTextWidth(LABEL, ITEM_SCALE, TEXT_SPACING);
|
||||
@@ -810,15 +881,13 @@ namespace System {
|
||||
for (std::size_t i = 0; i < page.items.size(); ++i) {
|
||||
const Item& item = page.items[i];
|
||||
const SDL_Color COL = (i == page.cursor) ? CURSOR_COLOR : LABEL_COLOR;
|
||||
// Salta el Locale lookup si label_key esta buit (item nomes-valor).
|
||||
const std::string LABEL = item.label_key.empty()
|
||||
? std::string{}
|
||||
: Locale::get().text(item.label_key);
|
||||
// resolveLabel prioritza label_text (literal) sobre label_key (locale).
|
||||
const std::string LABEL = resolveLabel(item);
|
||||
const float ITEM_TOP = computeItemTopY(BOX_Y, i, HAS_SUBTITLE);
|
||||
const float ITEM_CY = ITEM_TOP + (static_cast<float>(ITEM_HEIGHT) * 0.5F);
|
||||
|
||||
if (item.label_key.empty() && item.get_value_text) {
|
||||
// Item nomes-valor (sense label_key): el text del valor es
|
||||
if (LABEL.empty() && item.get_value_text) {
|
||||
// Item nomes-valor (sense label): el text del valor es
|
||||
// renderitza centrat com a label decoratiu. Util per a items
|
||||
// d'informacio com la versio/hash a SISTEMA.
|
||||
text_.renderCentered(item.get_value_text(),
|
||||
|
||||
Reference in New Issue
Block a user