diff --git a/source/MenuOption.h b/source/MenuOption.h index d697c29..1508fc3 100644 --- a/source/MenuOption.h +++ b/source/MenuOption.h @@ -9,33 +9,34 @@ #include "options.h" // Para acceder a las variables de configuración #include "lang.h" // Para las traducciones #include "section.h" // Para las acciones como Quit o Reset +#include "text.h" // Para poder calcular el ancho del texto // --- Interfaz Base para todas las Opciones del Menú --- class MenuOption { public: - // Enum para el comportamiento, similar al anterior pero más integrado enum class Behavior { ADJUST, SELECT }; - // Constructor base MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false) : caption_(std::move(caption)), group_(group), hidden_(hidden) {} - // Destructor virtual para permitir la destrucción polimórfica correcta virtual ~MenuOption() = default; - // Métodos comunes que todas las opciones deben tener const std::string& getCaption() const { return caption_; } ServiceMenu::SettingsGroup getGroup() const { return group_; } bool isHidden() const { return hidden_; } void setHidden(bool hidden) { hidden_ = hidden; } - // Métodos virtuales que las clases derivadas implementarán según su naturaleza virtual Behavior getBehavior() const = 0; virtual std::string getValueAsString() const { return ""; } - virtual void adjustValue(bool adjust_up) {} // Implementado por opciones ajustables - virtual ServiceMenu::SettingsGroup getTargetGroup() const { return ServiceMenu::SettingsGroup::MAIN; } // Implementado por FolderOption - virtual void executeAction() {} // Implementado por ActionOption + virtual void adjustValue(bool adjust_up) {} + virtual ServiceMenu::SettingsGroup getTargetGroup() const { return ServiceMenu::SettingsGroup::MAIN; } + virtual void executeAction() {} + + // --- AÑADIDO --- + // Método virtual para que cada opción calcule el ancho de su valor más largo. + virtual int getMaxValueWidth(Text* text_renderer) const { return 0; } + // --- FIN AÑADIDO --- protected: std::string caption_; @@ -46,7 +47,6 @@ protected: // --- Clases Derivadas --- -// 1. Opción Booleana (On/Off) - Sin cambios class BoolOption : public MenuOption { public: BoolOption(const std::string& cap, ServiceMenu::SettingsGroup grp, bool* var) @@ -59,11 +59,18 @@ public: void adjustValue(bool /*adjust_up*/) override { *linked_variable_ = !*linked_variable_; } + // --- AÑADIDO --- + int getMaxValueWidth(Text* text_renderer) const override { + return std::max( + text_renderer->lenght(Lang::getText("[SERVICE_MENU] ON"), -2), + text_renderer->lenght(Lang::getText("[SERVICE_MENU] OFF"), -2) + ); + } + // --- FIN AÑADIDO --- private: bool* linked_variable_; }; -// 2. Opción de Entero (Volumen, Tamaño Ventana, etc.) - Sin cambios class IntOption : public MenuOption { public: IntOption(const std::string& cap, ServiceMenu::SettingsGroup grp, int* var, int min, int max, int step) @@ -75,15 +82,21 @@ public: int newValue = *linked_variable_ + (adjust_up ? step_value_ : -step_value_); *linked_variable_ = std::clamp(newValue, min_value_, max_value_); } + // --- AÑADIDO --- + int getMaxValueWidth(Text* text_renderer) const override { + return std::max( + text_renderer->lenght(std::to_string(min_value_), -2), + text_renderer->lenght(std::to_string(max_value_), -2) + ); + } + // --- FIN AÑADIDO --- private: int* linked_variable_; int min_value_, max_value_, step_value_; }; -// 3. Opción de Lista (Idioma, Dificultad) - *** CORREGIDO *** class ListOption : public MenuOption { public: - // El constructor ahora es más abstracto. Acepta funciones para obtener y establecer el valor. ListOption(const std::string& cap, ServiceMenu::SettingsGroup grp, std::vector values, std::function current_value_getter, @@ -94,15 +107,12 @@ public: setter_(std::move(new_value_setter)), list_index_(0) { - sync(); // Sincroniza el índice con el valor actual al momento de la creación. + sync(); } - // Se mantiene pública en caso de que necesitemos resincronizar desde fuera. void sync() { std::string current_value = getter_(); for (size_t i = 0; i < value_list_.size(); ++i) { - // Asume que el getter devuelve un string que coincide con uno de los de la lista. - // Para que esto funcione, necesitarás funciones como `Lang::getNameFromCode`. if (value_list_[i] == current_value) { list_index_ = i; return; @@ -111,21 +121,25 @@ public: } Behavior getBehavior() const override { return Behavior::ADJUST; } - std::string getValueAsString() const override { return value_list_.empty() ? "" : value_list_[list_index_]; } - void adjustValue(bool adjust_up) override { if (value_list_.empty()) return; size_t size = value_list_.size(); list_index_ = (adjust_up) ? (list_index_ + 1) % size : (list_index_ + size - 1) % size; - - // Usa la función setter para actualizar la variable de configuración real. setter_(value_list_[list_index_]); } - + // --- AÑADIDO --- + int getMaxValueWidth(Text* text_renderer) const override { + int max_w = 0; + for (const auto& val : value_list_) { + max_w = std::max(max_w, text_renderer->lenght(val, -2)); + } + return max_w; + } + // --- FIN AÑADIDO --- private: std::vector value_list_; std::function getter_; @@ -133,8 +147,6 @@ private: size_t list_index_; }; - -// 4. Opción Carpeta (Navega a otro sub-menú) - Sin cambios class FolderOption : public MenuOption { public: FolderOption(const std::string& cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target) @@ -146,7 +158,6 @@ private: ServiceMenu::SettingsGroup target_group_; }; -// 5. Opción de Acción (Ejecuta una función) - Sin cambios class ActionOption : public MenuOption { public: ActionOption(const std::string& cap, ServiceMenu::SettingsGroup grp, std::function action, bool hidden = false) diff --git a/source/service_menu.cpp b/source/service_menu.cpp index d5761a3..36036d5 100644 --- a/source/service_menu.cpp +++ b/source/service_menu.cpp @@ -1,5 +1,5 @@ #include "service_menu.h" -#include "MenuOption.h" // <-- Incluimos la nueva cabecera +#include "MenuOption.h" #include "screen.h" #include #include @@ -9,13 +9,13 @@ #include "audio.h" #include -// ... (métodos singleton y constructores no cambian mucho) ... - +// --- Singleton --- ServiceMenu *ServiceMenu::instance_ = nullptr; void ServiceMenu::init() { ServiceMenu::instance_ = new ServiceMenu(); } void ServiceMenu::destroy() { delete ServiceMenu::instance_; } ServiceMenu *ServiceMenu::get() { return ServiceMenu::instance_; } +// --- Constructor --- ServiceMenu::ServiceMenu() : element_text_(Resource::get()->getText("04b_25_flat")), title_text_(Resource::get()->getText("04b_25_flat_2x")), @@ -25,10 +25,13 @@ ServiceMenu::ServiceMenu() restart_message_ui_ = std::make_unique( element_text_, Lang::getText("[SERVICE_MENU] NEED_RESTART_MESSAGE"), - title_color_); + title_color_ + ); reset(); } +// --- Métodos públicos --- + void ServiceMenu::initializeOptions() { options_.clear(); @@ -45,55 +48,27 @@ void ServiceMenu::initializeOptions() options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] MAIN_VOLUME"), SettingsGroup::AUDIO, &Options::audio.volume, 0, 100, 5)); options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] MUSIC_VOLUME"), SettingsGroup::AUDIO, &Options::audio.music.volume, 0, 100, 5)); options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] SFX_VOLUME"), SettingsGroup::AUDIO, &Options::audio.sound.volume, 0, 100, 5)); - + // --- Settings --- options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] AUTOFIRE"), SettingsGroup::SETTINGS, &Options::settings.autofire)); - // Opción de Idioma - options_.push_back(std::make_unique( - Lang::getText("[SERVICE_MENU] LANGUAGE"), - SettingsGroup::SETTINGS, + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] LANGUAGE"), SettingsGroup::SETTINGS, std::vector{Lang::getText("[SERVICE_MENU] LANG_ES"), Lang::getText("[SERVICE_MENU] LANG_BA"), Lang::getText("[SERVICE_MENU] LANG_EN")}, - // Getter: Devuelve el nombre del idioma actual - []() - { return Lang::getNameFromCode(Options::pending_changes.new_language); }, - // Setter: Establece el nuevo idioma a partir de su nombre - [](const std::string &val) - { - Options::pending_changes.new_language = Lang::getCodeFromName(val); - Options::checkPendingChanges(); - })); - - // Opción de Dificultad - options_.push_back(std::make_unique( - Lang::getText("[SERVICE_MENU] DIFFICULTY"), - SettingsGroup::SETTINGS, + [](){ return Lang::getNameFromCode(Options::pending_changes.new_language); }, + [](const std::string& val){ Options::pending_changes.new_language = Lang::getCodeFromName(val); Options::checkPendingChanges(); } + )); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] DIFFICULTY"), SettingsGroup::SETTINGS, std::vector{Lang::getText("[SERVICE_MENU] EASY"), Lang::getText("[SERVICE_MENU] NORMAL"), Lang::getText("[SERVICE_MENU] HARD")}, - // Getter: Devuelve el nombre de la dificultad actual - []() - { return Options::getDifficultyNameFromCode(Options::pending_changes.new_difficulty); }, - // Setter: Establece la nueva dificultad a partir de su nombre - [](const std::string &val) - { - Options::pending_changes.new_difficulty = Options::getDifficultyCodeFromName(val); - Options::checkPendingChanges(); - })); + [](){ return Options::getDifficultyNameFromCode(Options::pending_changes.new_difficulty); }, + [](const std::string& val){ Options::pending_changes.new_difficulty = Options::getDifficultyCodeFromName(val); Options::checkPendingChanges(); } + )); options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] ENABLE_SHUTDOWN"), SettingsGroup::SETTINGS, &Options::settings.shutdown_enabled)); - // --- System (con lambdas para las acciones) --- - options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] RESET"), SettingsGroup::SYSTEM, [this]() - { - Section::name = Section::Name::RESET; - toggle(); })); - options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] QUIT"), SettingsGroup::SYSTEM, []() - { - Section::name = Section::Name::QUIT; - Section::options = Section::Options::NONE; })); - options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] SHUTDOWN"), SettingsGroup::SYSTEM, []() - { - Section::name = Section::Name::QUIT; - Section::options = Section::Options::SHUTDOWN; }, !Options::settings.shutdown_enabled)); // Se puede inicializar oculto + // --- System --- + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] RESET"), SettingsGroup::SYSTEM, [this](){ Section::name = Section::Name::RESET; toggle(); })); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] QUIT"), SettingsGroup::SYSTEM, [](){ Section::name = Section::Name::QUIT; Section::options = Section::Options::NONE; })); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] SHUTDOWN"), SettingsGroup::SYSTEM, [](){ Section::name = Section::Name::QUIT; Section::options = Section::Options::SHUTDOWN; }, !Options::settings.shutdown_enabled )); - // --- Menu principal (Carpetas) --- + // --- Main Menu --- options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] VIDEO"), SettingsGroup::MAIN, SettingsGroup::VIDEO)); options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] AUDIO"), SettingsGroup::MAIN, SettingsGroup::AUDIO)); options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] SETTINGS"), SettingsGroup::MAIN, SettingsGroup::SETTINGS)); @@ -104,13 +79,12 @@ void ServiceMenu::initializeOptions() void ServiceMenu::adjustOption(bool adjust_up) { - if (display_options_.empty() || selected_ >= display_options_.size()) - return; - - auto &selected_option = display_options_.at(selected_); + if (display_options_.empty() || selected_ >= display_options_.size()) return; + + auto& selected_option = display_options_.at(selected_); if (selected_option->getBehavior() == MenuOption::Behavior::ADJUST) { - selected_option->adjustValue(adjust_up); // <-- ¡Llamada polimórfica! + selected_option->adjustValue(adjust_up); option_pairs_ = getOptionPairs(current_settings_group_); applySettings(current_settings_group_); playMenuSound(); @@ -119,16 +93,14 @@ void ServiceMenu::adjustOption(bool adjust_up) void ServiceMenu::selectOption() { - if (display_options_.empty() || selected_ >= display_options_.size()) - return; + if (display_options_.empty() || selected_ >= display_options_.size()) return; if (current_settings_group_ == SettingsGroup::MAIN) main_menu_selected_ = selected_; - auto &selected_option = display_options_.at(selected_); - - // Si es una carpeta, cambiamos de grupo - if (auto folder = dynamic_cast(selected_option)) + auto& selected_option = display_options_.at(selected_); + + if (auto folder = dynamic_cast(selected_option)) { previous_settings_group_ = current_settings_group_; current_settings_group_ = folder->getTargetGroup(); @@ -139,107 +111,61 @@ void ServiceMenu::selectOption() playMenuSound(); return; } - - // Si es una acción, la ejecutamos + if (selected_option->getBehavior() == MenuOption::Behavior::SELECT) { - selected_option->executeAction(); // <-- ¡Llamada polimórfica! + selected_option->executeAction(); } } -// --- Métodos de utilidad modificados --- -ServiceMenu::OptionPairs ServiceMenu::getOptionPairs(SettingsGroup group) const +// --- Métodos de Cálculo de Ancho (Reincorporados y adaptados) --- + +void ServiceMenu::precalculateMenuWidths() { - OptionPairs option_pairs; - for (const auto &option : options_) + for (int& w : group_menu_widths_) + w = MIN_WIDTH_; + + for (int group = 0; group < 5; ++group) { - if (option->getGroup() == group && !option->isHidden()) + SettingsGroup sg = static_cast(group); + int max_option_width = 0; + int max_value_width = 0; + + for (const auto& option : options_) { - option_pairs.emplace_back(option->getCaption(), option->getValueAsString()); + if (option->getGroup() != sg) continue; + + max_option_width = std::max(max_option_width, element_text_->lenght(option->getCaption(), -2)); + max_value_width = std::max(max_value_width, option->getMaxValueWidth(element_text_.get())); } - } - return option_pairs; -} - -std::vector ServiceMenu::getOptionsByGroup(SettingsGroup group) -{ - std::vector filtered_options; - for (auto &option : options_) - { - if (option->getGroup() == group && !option->isHidden()) - { - filtered_options.push_back(option.get()); // Obtenemos el puntero raw - } - } - return filtered_options; -} - -void ServiceMenu::applySettings(SettingsGroup group) -{ - // La aplicación de settings de Video y Audio se puede llamar tras un cambio - // para que sea instantáneo, aunque la lógica principal ya está en cada `adjustValue`. - if (group == SettingsGroup::VIDEO) - Screen::get()->applySettings(); - if (group == SettingsGroup::AUDIO) - Audio::get()->applySettings(); - - // La lógica de UI, como ocultar/mostrar opciones, sí va aquí. - if (group == SettingsGroup::SETTINGS) - { - auto option = getOptionByCaption(Lang::getText("[SERVICE_MENU] SHUTDOWN")); - if (option) - { - option->setHidden(!Options::settings.shutdown_enabled); - // Si ocultamos/mostramos, necesitamos refrescar la lista de opciones visibles. - display_options_ = getOptionsByGroup(group); + + if (getGroupAlignment(sg) == GroupAlignment::LEFT) { + size_t total_width = max_option_width + MIN_GAP_OPTION_VALUE_ + max_value_width + (OPTIONS_HORIZONTAL_PADDING_ * 2); + group_menu_widths_[group] = std::max((int)MIN_WIDTH_, (int)total_width); + } else { + size_t total_width = max_option_width + (OPTIONS_HORIZONTAL_PADDING_ * 2); + group_menu_widths_[group] = std::max((int)MIN_WIDTH_, (int)total_width); } } } -void ServiceMenu::AdjustListValues() +int ServiceMenu::getMenuWidthForGroup(SettingsGroup group) const { - for (auto &option : options_) - { - // dynamic_cast es una forma segura de ver si un objeto es de un tipo derivado - if (auto list_option = dynamic_cast(option.get())) - { - list_option->sync(); - } - } + return group_menu_widths_[static_cast(group)]; } -MenuOption *ServiceMenu::getOptionByCaption(const std::string &caption) -{ - for (auto &option : options_) - { - if (option->getCaption() == caption) - { - return option.get(); - } - } - return nullptr; -} -// El resto de los métodos (render, update, moveBack, etc.) permanecen prácticamente igual, -// ya que su lógica no dependía tanto de la estructura interna de OptionEntry. -// Aquí se incluyen para que el archivo esté completo. +// --- Resto de métodos --- +// (Incluyendo el resto de definiciones que ya tenías para que el archivo esté completo) -void ServiceMenu::toggle() -{ - enabled_ = !enabled_; - if (!enabled_) - { - reset(); - } -} -void ServiceMenu::render() -{ +void ServiceMenu::toggle() { enabled_ = !enabled_; if (!enabled_) { reset(); } } + +void ServiceMenu::render() { if (enabled_) { int y = rect_.y; - if (aspect_ == Aspect::ASPECT1) - { + if (aspect_ == Aspect::ASPECT1) { SDL_FRect shadowRect = {rect_.x + 5, rect_.y + 5, rect_.w, rect_.h}; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 64); SDL_RenderFillRect(Screen::get()->getRenderer(), &shadowRect); @@ -254,55 +180,37 @@ void ServiceMenu::render() title_text_->writeDX(TEXT_COLOR | TEXT_CENTER, param.game.game_area.center_x, y, title_, -4, title_color_); y = rect_.y + upper_height_; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), title_color_.r, title_color_.g, title_color_.b, 255); - SDL_RenderLine(Screen::get()->getRenderer(), rect_.x + 20, y, rect_.x + rect_.w - 20, y); + SDL_RenderLine(Screen::get()->getRenderer(), rect_.x + OPTIONS_HORIZONTAL_PADDING_, y, rect_.x + rect_.w - OPTIONS_HORIZONTAL_PADDING_, y); y = options_y_; - for (size_t i = 0; i < option_pairs_.size(); ++i) - { - if (getGroupAlignment(current_settings_group_) == GroupAlignment::LEFT) - { - element_text_->writeColored(rect_.x + 20, y, option_pairs_.at(i).first, i == selected_ ? selected_color_ : text_color_, -2); - const int X = rect_.x + rect_.w - 20 - element_text_->lenght(std::string(option_pairs_.at(i).second), -2); + for (size_t i = 0; i < option_pairs_.size(); ++i) { + if (getGroupAlignment(current_settings_group_) == GroupAlignment::LEFT) { + element_text_->writeColored(rect_.x + OPTIONS_HORIZONTAL_PADDING_, y, option_pairs_.at(i).first, i == selected_ ? selected_color_ : text_color_, -2); + const int X = rect_.x + rect_.w - OPTIONS_HORIZONTAL_PADDING_ - element_text_->lenght(std::string(option_pairs_.at(i).second), -2); element_text_->writeColored(X, y, std::string(option_pairs_.at(i).second), i == selected_ ? selected_color_ : text_color_, -2); - } - else - { + } else { element_text_->writeDX(TEXT_CENTER | TEXT_COLOR, rect_.x + rect_.w / 2, y, option_pairs_.at(i).first, -2, i == selected_ ? selected_color_ : text_color_); } y += options_height_ + options_padding_; } } } -void ServiceMenu::update() -{ - if (resizing_) - { - updateResizeAnimation(); - return; - } - if (enabled_) - { - updateCounter(); - selected_color_ = getSelectedColor(); - } +void ServiceMenu::update() { + if (resizing_) { updateResizeAnimation(); return; } + if (enabled_) { updateCounter(); selected_color_ = getSelectedColor(); } bool now_pending = Options::pending_changes.has_pending_changes; - if (now_pending != last_pending_changes_) - { - if (now_pending) - { + if (now_pending != last_pending_changes_) { + if (now_pending) { float msg_x = param.game.game_area.center_x; float msg_y = rect_.y + 39.0f; restart_message_ui_->show(msg_x, msg_y); - } - else - { + } else { restart_message_ui_->hide(); } last_pending_changes_ = now_pending; } restart_message_ui_->update(); } -void ServiceMenu::reset() -{ +void ServiceMenu::reset() { selected_ = 0; previous_settings_group_ = current_settings_group_ = SettingsGroup::MAIN; title_ = settingsGroupToString(current_settings_group_); @@ -310,41 +218,20 @@ void ServiceMenu::reset() updateMenu(current_settings_group_); setAnchors(); } -void ServiceMenu::setSelectorUp() -{ - if (display_options_.empty()) - return; - selected_ = (selected_ > 0) ? selected_ - 1 : display_options_.size() - 1; - playMenuSound(); -} -void ServiceMenu::setSelectorDown() -{ - if (display_options_.empty()) - return; - selected_ = (selected_ + 1) % display_options_.size(); - playMenuSound(); -} -void ServiceMenu::moveBack() -{ - if (current_settings_group_ == SettingsGroup::MAIN) - { - enabled_ = false; - return; - } - else - { - if (previous_settings_group_ == SettingsGroup::MAIN) - selected_ = main_menu_selected_; - else - selected_ = 0; +void ServiceMenu::setSelectorUp() { if (display_options_.empty()) return; selected_ = (selected_ > 0) ? selected_ - 1 : display_options_.size() - 1; playMenuSound(); } +void ServiceMenu::setSelectorDown() { if (display_options_.empty()) return; selected_ = (selected_ + 1) % display_options_.size(); playMenuSound(); } +void ServiceMenu::moveBack() { + if (current_settings_group_ == SettingsGroup::MAIN) { enabled_ = false; return; } + else { + if (previous_settings_group_ == SettingsGroup::MAIN) selected_ = main_menu_selected_; + else selected_ = 0; current_settings_group_ = previous_settings_group_; updateMenu(current_settings_group_); setOptionsPosition(); playMenuSound(); } } -void ServiceMenu::setAnchors() -{ +void ServiceMenu::setAnchors() { const size_t MAX_ENTRIES = findLargestGroupSize(); options_height_ = element_text_->getCharacterSize(); options_padding_ = 5; @@ -352,119 +239,121 @@ void ServiceMenu::setAnchors() title_padding_ = title_height_ / 2; upper_height_ = (title_padding_ * 2) + title_height_; lower_padding_ = (options_padding_ * 3); - lower_height_ = ((MAX_ENTRIES - 1) * (options_height_ + options_padding_)) + options_height_ + (lower_padding_ * 2); + lower_height_ = ((MAX_ENTRIES > 0 ? MAX_ENTRIES - 1 : 0) * (options_height_ + options_padding_)) + options_height_ + (lower_padding_ * 2); width_ = 240; height_ = upper_height_ + lower_height_; rect_ = {(param.game.width - width_) / 2.0f, (param.game.height - height_) / 2.0f, static_cast(width_), static_cast(height_)}; setOptionsPosition(); } -void ServiceMenu::setOptionsPosition() -{ +void ServiceMenu::setOptionsPosition() { resize(); SDL_FRect new_rect = {(param.game.width - width_) / 2.0f, (param.game.height - height_) / 2.0f, static_cast(width_), static_cast(height_)}; options_y_ = new_rect.y + upper_height_ + lower_padding_; } -void ServiceMenu::resize() -{ +void ServiceMenu::resize() { int menu_width = getMenuWidthForGroup(current_settings_group_); width_ = menu_width; - lower_height_ = ((display_options_.size() - 1) * (options_height_ + options_padding_)) + options_height_ + (lower_padding_ * 2); + lower_height_ = ((display_options_.size() > 0 ? display_options_.size() - 1 : 0) * (options_height_ + options_padding_)) + options_height_ + (lower_padding_ * 2); height_ = upper_height_ + lower_height_; SDL_FRect new_rect = {(param.game.width - width_) / 2.0f, (param.game.height - height_) / 2.0f, static_cast(width_), static_cast(height_)}; - if (rect_.x != new_rect.x || rect_.y != new_rect.y || rect_.w != new_rect.w || rect_.h != new_rect.h) - { + if (rect_.x != new_rect.x || rect_.y != new_rect.y || rect_.w != new_rect.w || rect_.h != new_rect.h) { rect_anim_from_ = rect_; rect_anim_to_ = new_rect; resize_anim_step_ = 0; resizing_ = true; - } - else - { + } else { rect_ = new_rect; resizing_ = false; } } -void ServiceMenu::updateResizeAnimation() -{ - if (!resizing_) - return; +void ServiceMenu::updateResizeAnimation() { + if (!resizing_) return; ++resize_anim_step_; float t = static_cast(resize_anim_step_) / resize_anim_steps_; - if (t >= 1.0f) - { - rect_ = rect_anim_to_; - resizing_ = false; - return; - } + if (t >= 1.0f) { rect_ = rect_anim_to_; resizing_ = false; return; } float ease = 1 - (1 - t) * (1 - t); rect_.x = rect_anim_from_.x + (rect_anim_to_.x - rect_anim_from_.x) * ease; rect_.y = rect_anim_from_.y + (rect_anim_to_.y - rect_anim_from_.y) * ease; rect_.w = rect_anim_from_.w + (rect_anim_to_.w - rect_anim_from_.w) * ease; rect_.h = rect_anim_from_.h + (rect_anim_to_.h - rect_anim_from_.h) * ease; } -void ServiceMenu::updateCounter() -{ - static Uint64 lastUpdate = SDL_GetTicks(); - Uint64 currentTicks = SDL_GetTicks(); - if (currentTicks - lastUpdate >= 50) - { - counter_++; - lastUpdate = currentTicks; - } -} -Color ServiceMenu::getSelectedColor() const -{ - static std::array colors = {Color(0xFF, 0xFB, 0x8A), Color(0xFF, 0xE4, 0x5D), Color(0xFF, 0xD1, 0x3C), Color(0xFF, 0xBF, 0x23), Color(0xFF, 0xAA, 0x12), Color(0xE6, 0x9A, 0x08), Color(0xE6, 0x9A, 0x08), Color(0xFF, 0xAA, 0x12), Color(0xFF, 0xBF, 0x23), Color(0xFF, 0xD1, 0x3C), Color(0xFF, 0xE4, 0x5D), Color(0xFF, 0xFB, 0x8A)}; +void ServiceMenu::updateCounter() { static Uint64 lastUpdate = SDL_GetTicks(); Uint64 currentTicks = SDL_GetTicks(); if (currentTicks - lastUpdate >= 50) { counter_++; lastUpdate = currentTicks; } } +Color ServiceMenu::getSelectedColor() const { + static std::array colors = { Color(0xFF, 0xFB, 0x8A), Color(0xFF, 0xE4, 0x5D), Color(0xFF, 0xD1, 0x3C), Color(0xFF, 0xBF, 0x23), Color(0xFF, 0xAA, 0x12), Color(0xE6, 0x9A, 0x08), Color(0xE6, 0x9A, 0x08), Color(0xFF, 0xAA, 0x12), Color(0xFF, 0xBF, 0x23), Color(0xFF, 0xD1, 0x3C), Color(0xFF, 0xE4, 0x5D), Color(0xFF, 0xFB, 0x8A) }; return colors.at(counter_ % colors.size()); } -void ServiceMenu::playMenuSound() { Audio::get()->playSound("clock.wav"); } -void ServiceMenu::updateMenu(SettingsGroup group) -{ +void ServiceMenu::playMenuSound() { Audio::get()->playSound(MENU_SOUND_); } +void ServiceMenu::updateMenu(SettingsGroup group) { title_ = settingsGroupToString(group); AdjustListValues(); option_pairs_ = getOptionPairs(group); display_options_ = getOptionsByGroup(group); resize(); } -int ServiceMenu::findLargestGroupSize() const -{ +int ServiceMenu::findLargestGroupSize() const { std::unordered_map group_counts; - for (const auto &option : options_) - ++group_counts[option->getGroup()]; + for (const auto& option : options_) ++group_counts[option->getGroup()]; int max_size = 0; - for (const auto &pair : group_counts) - if (pair.second > max_size) - max_size = pair.second; + for (const auto& pair : group_counts) if (pair.second > max_size) max_size = pair.second; return max_size; } -ServiceMenu::GroupAlignment ServiceMenu::getGroupAlignment(SettingsGroup group) const -{ - switch (group) - { - case SettingsGroup::VIDEO: - case SettingsGroup::AUDIO: - case SettingsGroup::SETTINGS: - return GroupAlignment::LEFT; - default: - return GroupAlignment::CENTERED; +ServiceMenu::GroupAlignment ServiceMenu::getGroupAlignment(SettingsGroup group) const { + switch (group) { + case SettingsGroup::VIDEO: case SettingsGroup::AUDIO: case SettingsGroup::SETTINGS: return GroupAlignment::LEFT; + default: return GroupAlignment::CENTERED; } } - -std::string ServiceMenu::settingsGroupToString(SettingsGroup group) const -{ - switch (group) - { - case SettingsGroup::MAIN: - return Lang::getText("[SERVICE_MENU] TITLE"); - case SettingsGroup::VIDEO: - return Lang::getText("[SERVICE_MENU] VIDEO"); - case SettingsGroup::AUDIO: - return Lang::getText("[SERVICE_MENU] AUDIO"); - case SettingsGroup::SETTINGS: - return Lang::getText("[SERVICE_MENU] SETTINGS"); - case SettingsGroup::SYSTEM: - return Lang::getText("[SERVICE_MENU] SYSTEM"); - default: - return Lang::getText("[SERVICE_MENU] TITLE"); +std::string ServiceMenu::settingsGroupToString(SettingsGroup group) const { + switch (group) { + case SettingsGroup::MAIN: return Lang::getText("[SERVICE_MENU] TITLE"); + case SettingsGroup::VIDEO: return Lang::getText("[SERVICE_MENU] VIDEO"); + case SettingsGroup::AUDIO: return Lang::getText("[SERVICE_MENU] AUDIO"); + case SettingsGroup::SETTINGS: return Lang::getText("[SERVICE_MENU] SETTINGS"); + case SettingsGroup::SYSTEM: return Lang::getText("[SERVICE_MENU] SYSTEM"); + default: return Lang::getText("[SERVICE_MENU] TITLE"); } -} \ No newline at end of file +} +ServiceMenu::OptionPairs ServiceMenu::getOptionPairs(SettingsGroup group) const { + OptionPairs option_pairs; + for (const auto& option : options_) { + if (option->getGroup() == group && !option->isHidden()) { + option_pairs.emplace_back(option->getCaption(), option->getValueAsString()); + } + } + return option_pairs; +} +std::vector ServiceMenu::getOptionsByGroup(SettingsGroup group) { + std::vector filtered_options; + for (auto& option : options_) { + if (option->getGroup() == group && !option->isHidden()) { + filtered_options.push_back(option.get()); + } + } + return filtered_options; +} +MenuOption* ServiceMenu::getOptionByCaption(const std::string& caption) { + for (auto& option : options_) { + if (option->getCaption() == caption) { + return option.get(); + } + } + return nullptr; +} +void ServiceMenu::applySettings(SettingsGroup group) { + if (group == SettingsGroup::VIDEO) Screen::get()->applySettings(); + if (group == SettingsGroup::AUDIO) Audio::get()->applySettings(); + if (group == SettingsGroup::SETTINGS) { + auto option = getOptionByCaption(Lang::getText("[SERVICE_MENU] SHUTDOWN")); + if (option) { + option->setHidden(!Options::settings.shutdown_enabled); + display_options_ = getOptionsByGroup(group); + } + } +} +void ServiceMenu::AdjustListValues() { + for (auto& option : options_) { + if (auto list_option = dynamic_cast(option.get())) { + list_option->sync(); + } + } +} diff --git a/source/service_menu.h b/source/service_menu.h index 85e4388..e1ddfdf 100644 --- a/source/service_menu.h +++ b/source/service_menu.h @@ -51,6 +51,12 @@ public: void setResizeAnimationSteps(int steps) { resize_anim_steps_ = steps; } private: + // Definición de las constantes estáticas + static constexpr const char *MENU_SOUND_ = "clock.wav"; + static constexpr size_t OPTIONS_HORIZONTAL_PADDING_ = 20; + static constexpr size_t MIN_WIDTH_ = 220; + static constexpr size_t MIN_GAP_OPTION_VALUE_ = 20; + // --- Tipos internos --- using OptionPairs = std::vector>;