diff --git a/source/MenuOption.h b/source/MenuOption.h new file mode 100644 index 0000000..fd30697 --- /dev/null +++ b/source/MenuOption.h @@ -0,0 +1,181 @@ +#pragma once + +#include +#include +#include +#include +#include // para std::clamp +#include "service_menu.h" // Necesitamos las enums como SettingsGroup +#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 + +// --- 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 + +protected: + std::string caption_; + ServiceMenu::SettingsGroup group_; + bool hidden_; +}; + +// --- Clases Derivadas --- + +// 1. Opción Booleana (On/Off) +class BoolOption : public MenuOption +{ +public: + BoolOption(const std::string &cap, ServiceMenu::SettingsGroup grp, bool *var) + : MenuOption(cap, grp), linked_variable_(var) {} + + Behavior getBehavior() const override { return Behavior::ADJUST; } + std::string getValueAsString() const override + { + return *linked_variable_ ? Lang::getText("[SERVICE_MENU] ON") : Lang::getText("[SERVICE_MENU] OFF"); + } + void adjustValue(bool /*adjust_up*/) override + { + *linked_variable_ = !*linked_variable_; + } + +private: + bool *linked_variable_; +}; + +// 2. Opción de Entero (Volumen, Tamaño Ventana, etc.) +class IntOption : public MenuOption +{ +public: + IntOption(const std::string &cap, ServiceMenu::SettingsGroup grp, int *var, int min, int max, int step) + : MenuOption(cap, grp), linked_variable_(var), min_value_(min), max_value_(max), step_value_(step) {} + + Behavior getBehavior() const override { return Behavior::ADJUST; } + std::string getValueAsString() const override { return std::to_string(*linked_variable_); } + void adjustValue(bool adjust_up) override + { + int newValue = *linked_variable_ + (adjust_up ? step_value_ : -step_value_); + *linked_variable_ = std::clamp(newValue, min_value_, max_value_); + } + +private: + int *linked_variable_; + int min_value_, max_value_, step_value_; +}; + +// 3. Opción de Lista (Idioma, Dificultad) +class ListOption : public MenuOption +{ +public: + ListOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::string *var, std::vector values) + : MenuOption(cap, grp), linked_variable_(var), value_list_(std::move(values)), list_index_(0) {} + + void sync() + { // Sincroniza el índice con el valor actual de la variable + for (size_t i = 0; i < value_list_.size(); ++i) + { + std::string code; + if (linked_variable_ == &Options::pending_changes.new_language) + { + code = Lang::getCodeFromName(value_list_[i]); + } + else if (linked_variable_ == &Options::pending_changes.new_difficulty) + { + code = Options::getDifficultyCodeFromName(value_list_[i]); + } + if (code == *linked_variable_) + { + list_index_ = i; + return; + } + } + } + + 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; + + // Actualiza la variable real y comprueba cambios pendientes + if (linked_variable_ == &Options::pending_changes.new_language) + { + *linked_variable_ = Lang::getCodeFromName(value_list_[list_index_]); + } + else if (linked_variable_ == &Options::pending_changes.new_difficulty) + { + *linked_variable_ = Options::getDifficultyCodeFromName(value_list_[list_index_]); + } + Options::checkPendingChanges(); + } + +private: + std::string *linked_variable_; + std::vector value_list_; + size_t list_index_; +}; + +// 4. Opción Carpeta (Navega a otro sub-menú) +class FolderOption : public MenuOption +{ +public: + FolderOption(const std::string &cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target) + : MenuOption(cap, grp), target_group_(target) {} + + Behavior getBehavior() const override { return Behavior::SELECT; } + ServiceMenu::SettingsGroup getTargetGroup() const override { return target_group_; } + +private: + ServiceMenu::SettingsGroup target_group_; +}; + +// 5. Opción de Acción (Ejecuta una función) +class ActionOption : public MenuOption +{ +public: + // Usamos std::function para poder pasar cualquier función/lambda + ActionOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::function action, bool hidden = false) + : MenuOption(cap, grp, hidden), action_(std::move(action)) {} + + Behavior getBehavior() const override { return Behavior::SELECT; } + void executeAction() override + { + if (action_) + action_(); + } + +private: + std::function action_; +}; diff --git a/source/service_menu.cpp b/source/service_menu.cpp index dab8bf1..e1eabf7 100644 --- a/source/service_menu.cpp +++ b/source/service_menu.cpp @@ -1,30 +1,21 @@ #include "service_menu.h" +#include "MenuOption.h" // <-- Incluimos la nueva cabecera #include "screen.h" #include #include -#include #include "text.h" #include "resource.h" -#include "options.h" -#include "section.h" // Para Name, name, Options, options, AttractMode +#include "section.h" #include "audio.h" #include -#include "lang.h" -#include "UIMessage.h" -// Singleton +// ... (métodos singleton y constructores no cambian mucho) ... + ServiceMenu *ServiceMenu::instance_ = nullptr; - -// Inicializa la instancia única del singleton void ServiceMenu::init() { ServiceMenu::instance_ = new ServiceMenu(); } - -// Libera la instancia única del singleton void ServiceMenu::destroy() { delete ServiceMenu::instance_; } - -// Devuelve la instancia única del singleton ServiceMenu *ServiceMenu::get() { return ServiceMenu::instance_; } -// Constructor de ServiceMenu ServiceMenu::ServiceMenu() : element_text_(Resource::get()->getText("04b_25_flat")), title_text_(Resource::get()->getText("04b_25_flat_2x")), @@ -38,7 +29,177 @@ ServiceMenu::ServiceMenu() reset(); } -// Alterna la visibilidad del menú de servicio +void ServiceMenu::initializeOptions() +{ + options_.clear(); + + // --- Video --- + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] FULLSCREEN"), SettingsGroup::VIDEO, &Options::video.fullscreen)); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] WINDOW_SIZE"), SettingsGroup::VIDEO, &Options::window.size, 1, Options::window.max_size, 1)); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] SHADERS"), SettingsGroup::VIDEO, &Options::video.shaders)); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] VSYNC"), SettingsGroup::VIDEO, &Options::video.v_sync)); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] INTEGER_SCALE"), SettingsGroup::VIDEO, &Options::video.integer_scale)); + + // --- Audio --- + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] AUDIO"), SettingsGroup::AUDIO, &Options::audio.enabled)); + 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)); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] LANGUAGE"), SettingsGroup::SETTINGS, &Options::pending_changes.new_language, + std::vector{Lang::getText("[SERVICE_MENU] LANG_ES"), Lang::getText("[SERVICE_MENU] LANG_BA"), Lang::getText("[SERVICE_MENU] LANG_EN")})); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] DIFFICULTY"), SettingsGroup::SETTINGS, &Options::pending_changes.new_difficulty, + std::vector{Lang::getText("[SERVICE_MENU] EASY"), Lang::getText("[SERVICE_MENU] NORMAL"), Lang::getText("[SERVICE_MENU] HARD")})); + 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 + + // --- Menu principal (Carpetas) --- + 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)); + options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] SYSTEM"), SettingsGroup::MAIN, SettingsGroup::SYSTEM)); + + precalculateMenuWidths(); +} + +void ServiceMenu::adjustOption(bool adjust_up) +{ + 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! + option_pairs_ = getOptionPairs(current_settings_group_); + applySettings(current_settings_group_); + playMenuSound(); + } +} + +void ServiceMenu::selectOption() +{ + 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)) + { + previous_settings_group_ = current_settings_group_; + current_settings_group_ = folder->getTargetGroup(); + title_ = settingsGroupToString(current_settings_group_); + updateMenu(current_settings_group_); + selected_ = 0; + setOptionsPosition(); + playMenuSound(); + return; + } + + // Si es una acción, la ejecutamos + if (selected_option->getBehavior() == MenuOption::Behavior::SELECT) + { + selected_option->executeAction(); // <-- ¡Llamada polimórfica! + } +} + +// --- Métodos de utilidad modificados --- + +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()); // 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); + } + } +} + +void ServiceMenu::AdjustListValues() +{ + 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(); + } + } +} + +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. + void ServiceMenu::toggle() { enabled_ = !enabled_; @@ -47,74 +208,50 @@ void ServiceMenu::toggle() reset(); } } - -// Dibuja el menú de servicio en pantalla void ServiceMenu::render() { if (enabled_) { int y = rect_.y; - - // SOMBRA 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); } - - // FONDO const Uint8 ALPHA = aspect_ == Aspect::ASPECT1 ? 255 : 255; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), bg_color_.r, bg_color_.g, bg_color_.b, ALPHA); SDL_RenderFillRect(Screen::get()->getRenderer(), &rect_); - - // BORDE SDL_SetRenderDrawColor(Screen::get()->getRenderer(), title_color_.r, title_color_.g, title_color_.b, 255); SDL_RenderRect(Screen::get()->getRenderer(), &rect_); - - // MENSAJE DE RESTART restart_message_ui_->render(); - - // TITULO y += title_padding_; title_text_->writeDX(TEXT_COLOR | TEXT_CENTER, param.game.game_area.center_x, y, title_, -4, title_color_); - // if (Options::pending_changes.has_pending_changes) - // element_text_->writeDX(TEXT_COLOR | TEXT_CENTER, param.game.game_area.center_x, y + 25, Lang::getText("[SERVICE_MENU] NEED_RESTART_MESSAGE"), -2, title_color_); - - // LINEA 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 + OPTIONS_HORIZONTAL_PADDING_, y, rect_.x + rect_.w - OPTIONS_HORIZONTAL_PADDING_, y); - - // OPCIONES + SDL_RenderLine(Screen::get()->getRenderer(), rect_.x + 20, y, rect_.x + rect_.w - 20, y); y = options_y_; for (size_t i = 0; i < option_pairs_.size(); ++i) { if (getGroupAlignment(current_settings_group_) == GroupAlignment::LEFT) { - // Nombre de la opción - element_text_->writeColored(rect_.x + OPTIONS_HORIZONTAL_PADDING_, y, option_pairs_.at(i).first, i == selected_ ? selected_color_ : text_color_, -2); - // Valor de la opción - const int X = rect_.x + rect_.w - OPTIONS_HORIZONTAL_PADDING_ - element_text_->lenght(std::string(option_pairs_.at(i).second), -2); + 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); element_text_->writeColored(X, y, std::string(option_pairs_.at(i).second), i == selected_ ? selected_color_ : text_color_, -2); } else { - // Nombre de la opción 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_; } } } - -// Actualiza el estado del menú de servicio (colores, animaciones, etc.) void ServiceMenu::update() { if (resizing_) { updateResizeAnimation(); - // No actualizar colores ni animaciones mientras se redimensiona return; } if (enabled_) @@ -122,15 +259,13 @@ void ServiceMenu::update() updateCounter(); selected_color_ = getSelectedColor(); } - - // Detecta cambios en el estado de pending_changes bool now_pending = Options::pending_changes.has_pending_changes; 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; // Posición Y base del mensaje + float msg_y = rect_.y + 39.0f; restart_message_ui_->show(msg_x, msg_y); } else @@ -139,66 +274,78 @@ void ServiceMenu::update() } last_pending_changes_ = now_pending; } - - // Animación del mensaje de reinicio restart_message_ui_->update(); } - -// Calcula y establece los anclajes y dimensiones del menú +void ServiceMenu::reset() +{ + selected_ = 0; + previous_settings_group_ = current_settings_group_ = SettingsGroup::MAIN; + title_ = settingsGroupToString(current_settings_group_); + initializeOptions(); + 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; + current_settings_group_ = previous_settings_group_; + updateMenu(current_settings_group_); + setOptionsPosition(); + playMenuSound(); + } +} void ServiceMenu::setAnchors() { const size_t MAX_ENTRIES = findLargestGroupSize(); - options_height_ = element_text_->getCharacterSize(); options_padding_ = 5; - title_height_ = title_text_->getCharacterSize(); 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); - width_ = 240; height_ = upper_height_ + lower_height_; - rect_ = { - (param.game.width - width_) / 2, - (param.game.height - height_) / 2, - static_cast(width_), - static_cast(height_)}; - + rect_ = {(param.game.width - width_) / 2.0f, (param.game.height - height_) / 2.0f, static_cast(width_), static_cast(height_)}; setOptionsPosition(); } - -// Establce la posición donde empezar a escribir las opciones del menu void ServiceMenu::setOptionsPosition() { resize(); - // options_y_ = rect_.y + upper_height_ + lower_padding_; - - SDL_FRect new_rect = { - (param.game.width - width_) / 2, - (param.game.height - height_) / 2, - static_cast(width_), - static_cast(height_)}; + 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_; } - -// Cambia el tamaño de la ventana de menu void ServiceMenu::resize() { - // Usa el ancho precalculado para el grupo actual 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); height_ = upper_height_ + lower_height_; - SDL_FRect new_rect = { - (param.game.width - width_) / 2, - (param.game.height - height_) / 2, - static_cast(width_), - static_cast(height_)}; - - // Si el rect actual es diferente al nuevo, inicia animación + 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) { rect_anim_from_ = rect_; @@ -212,7 +359,6 @@ void ServiceMenu::resize() resizing_ = false; } } - void ServiceMenu::updateResizeAnimation() { if (!resizing_) @@ -225,15 +371,12 @@ void ServiceMenu::updateResizeAnimation() resizing_ = false; return; } - // EaseOutQuad 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; } - -// Actualiza el contador interno para animaciones o efectos visuales void ServiceMenu::updateCounter() { static Uint64 lastUpdate = SDL_GetTicks(); @@ -244,295 +387,31 @@ void ServiceMenu::updateCounter() lastUpdate = currentTicks; } } - -// Devuelve el color actual del elemento seleccionado (animado) Color ServiceMenu::getSelectedColor() const { - static std::array colors = { - Color(0xFF, 0xFB, 0x8A), // Amarillo suave - Color(0xFF, 0xE4, 0x5D), // Dorado medio - Color(0xFF, 0xD1, 0x3C), // Amarillo pastel intenso - Color(0xFF, 0xBF, 0x23), // Amarillo anaranjado - Color(0xFF, 0xAA, 0x12), // Amarillo cálido - Color(0xE6, 0x9A, 0x08), // Mostaza oscuro - Color(0xE6, 0x9A, 0x08), // Mostaza oscuro (regreso, cierre) - Color(0xFF, 0xAA, 0x12), // Amarillo cálido (regreso) - Color(0xFF, 0xBF, 0x23), // Amarillo anaranjado (regreso) - Color(0xFF, 0xD1, 0x3C), // Amarillo pastel intenso (regreso) - Color(0xFF, 0xE4, 0x5D), // Dorado medio (regreso) - Color(0xFF, 0xFB, 0x8A) // Amarillo suave (regreso) - }; - - const size_t index = counter_ % colors.size(); - - return colors.at(index); + 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()); } - -// Método privado para reproducir el sonido del menú -void ServiceMenu::playMenuSound() -{ - Audio::get()->playSound(MENU_SOUND_); -} - -// Mueve el selector hacia arriba en la lista de opciones -void ServiceMenu::setSelectorUp() -{ - if (display_options_.empty()) - return; - selected_ = (selected_ > 0) ? selected_ - 1 : display_options_.size() - 1; - playMenuSound(); -} - -// Mueve el selector hacia abajo en la lista de opciones -void ServiceMenu::setSelectorDown() -{ - if (display_options_.empty()) - return; - selected_ = (selected_ + 1) % display_options_.size(); - playMenuSound(); -} - -// Ajusta el valor de la opción seleccionada (si es ajustable) -void ServiceMenu::adjustOption(bool adjust_up) -{ - if (display_options_.empty() || selected_ >= display_options_.size()) - { - return; - } - - if (display_options_.at(selected_)->behavior == OptionBehavior::ADJUST) - { - display_options_.at(selected_)->adjustValue(adjust_up); - option_pairs_ = getOptionPairs(current_settings_group_); - applySettings(current_settings_group_); - playMenuSound(); - } -} - -// Ejecuta la acción de la opción seleccionada o navega a un submenú -void ServiceMenu::selectOption() -{ - if (display_options_.empty() || selected_ >= display_options_.size()) - return; - - // Si estamos en el menú principal, guarda la selección actual - if (current_settings_group_ == SettingsGroup::MAIN) - main_menu_selected_ = selected_; - - // Carpeta - if (display_options_.at(selected_)->type == ValueType::FOLDER) - { - previous_settings_group_ = current_settings_group_; - current_settings_group_ = display_options_.at(selected_)->target_group; - title_ = settingsGroupToString(current_settings_group_); - updateMenu(current_settings_group_); - selected_ = 0; - setOptionsPosition(); - playMenuSound(); - return; - } - - // Opción - if (display_options_.at(selected_)->behavior == OptionBehavior::SELECT) - { - if (display_options_.at(selected_)->caption == Lang::getText("[SERVICE_MENU] RESET")) - { - Section::name = Section::Name::RESET; - toggle(); - return; - } - else if (display_options_.at(selected_)->caption == Lang::getText("[SERVICE_MENU] QUIT")) - { - Section::name = Section::Name::QUIT; - Section::options = Section::Options::NONE; - return; - } - else if (display_options_.at(selected_)->caption == Lang::getText("[SERVICE_MENU] SHUTDOWN")) - { - Section::name = Section::Name::QUIT; - Section::options = Section::Options::SHUTDOWN; - return; - } - return; - } -} - -// Vuelve al grupo de opciones anterior o cierra el menú si está en el principal -void ServiceMenu::moveBack() -{ - if (current_settings_group_ == SettingsGroup::MAIN) - { - enabled_ = false; - return; - } - else - { - // Si vamos a volver al menú principal, restaura la selección guardada - 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(); - } -} - -// Inicializa todas las opciones del menú de servicio -void ServiceMenu::initializeOptions() -{ - options_.clear(); - - // Video - options_.emplace_back(Lang::getText("[SERVICE_MENU] FULLSCREEN"), SettingsGroup::VIDEO, OptionBehavior::ADJUST, &Options::video.fullscreen, ValueType::BOOL); - options_.emplace_back(Lang::getText("[SERVICE_MENU] WINDOW_SIZE"), SettingsGroup::VIDEO, OptionBehavior::ADJUST, &Options::window.size, ValueType::INT, 1, Options::window.max_size, 1); - options_.emplace_back(Lang::getText("[SERVICE_MENU] SHADERS"), SettingsGroup::VIDEO, OptionBehavior::ADJUST, &Options::video.shaders, ValueType::BOOL); - options_.emplace_back(Lang::getText("[SERVICE_MENU] VSYNC"), SettingsGroup::VIDEO, OptionBehavior::ADJUST, &Options::video.v_sync, ValueType::BOOL); - options_.emplace_back(Lang::getText("[SERVICE_MENU] INTEGER_SCALE"), SettingsGroup::VIDEO, OptionBehavior::ADJUST, &Options::video.integer_scale, ValueType::BOOL); - - // Audio - options_.emplace_back(Lang::getText("[SERVICE_MENU] AUDIO"), SettingsGroup::AUDIO, OptionBehavior::ADJUST, &Options::audio.enabled, ValueType::BOOL); - options_.emplace_back(Lang::getText("[SERVICE_MENU] MAIN_VOLUME"), SettingsGroup::AUDIO, OptionBehavior::ADJUST, &Options::audio.volume, ValueType::INT, 0, 100, 5); - options_.emplace_back(Lang::getText("[SERVICE_MENU] MUSIC_VOLUME"), SettingsGroup::AUDIO, OptionBehavior::ADJUST, &Options::audio.music.volume, ValueType::INT, 0, 100, 5); - options_.emplace_back(Lang::getText("[SERVICE_MENU] SFX_VOLUME"), SettingsGroup::AUDIO, OptionBehavior::ADJUST, &Options::audio.sound.volume, ValueType::INT, 0, 100, 5); - - // Settings - options_.emplace_back(Lang::getText("[SERVICE_MENU] AUTOFIRE"), SettingsGroup::SETTINGS, OptionBehavior::ADJUST, &Options::settings.autofire, ValueType::BOOL); - options_.emplace_back( - Lang::getText("[SERVICE_MENU] LANGUAGE"), - SettingsGroup::SETTINGS, - OptionBehavior::ADJUST, - &Options::pending_changes.new_language, - std::vector{ - Lang::getText("[SERVICE_MENU] LANG_ES"), - Lang::getText("[SERVICE_MENU] LANG_BA"), - Lang::getText("[SERVICE_MENU] LANG_EN")}); - options_.emplace_back( - Lang::getText("[SERVICE_MENU] DIFFICULTY"), - SettingsGroup::SETTINGS, - OptionBehavior::ADJUST, - &Options::pending_changes.new_difficulty, - std::vector{ - Lang::getText("[SERVICE_MENU] EASY"), - Lang::getText("[SERVICE_MENU] NORMAL"), - Lang::getText("[SERVICE_MENU] HARD")}); - options_.emplace_back(Lang::getText("[SERVICE_MENU] ENABLE_SHUTDOWN"), SettingsGroup::SETTINGS, OptionBehavior::ADJUST, &Options::settings.shutdown_enabled, ValueType::BOOL); - - // System - options_.emplace_back(Lang::getText("[SERVICE_MENU] RESET"), SettingsGroup::SYSTEM, OptionBehavior::SELECT, nullptr, ValueType::NONE); - options_.emplace_back(Lang::getText("[SERVICE_MENU] QUIT"), SettingsGroup::SYSTEM, OptionBehavior::SELECT, nullptr, ValueType::NONE); - options_.emplace_back(Lang::getText("[SERVICE_MENU] SHUTDOWN"), SettingsGroup::SYSTEM, OptionBehavior::SELECT, nullptr, ValueType::NONE, !Options::settings.shutdown_enabled); - - // Menu principal - options_.emplace_back(Lang::getText("[SERVICE_MENU] VIDEO"), SettingsGroup::MAIN, OptionBehavior::SELECT, SettingsGroup::VIDEO); - options_.emplace_back(Lang::getText("[SERVICE_MENU] AUDIO"), SettingsGroup::MAIN, OptionBehavior::SELECT, SettingsGroup::AUDIO); - options_.emplace_back(Lang::getText("[SERVICE_MENU] SETTINGS"), SettingsGroup::MAIN, OptionBehavior::SELECT, SettingsGroup::SETTINGS); - options_.emplace_back(Lang::getText("[SERVICE_MENU] SYSTEM"), SettingsGroup::MAIN, OptionBehavior::SELECT, SettingsGroup::SYSTEM); - - // Al terminar de inicializar las opciones, recalcula los anchos de menú - precalculateMenuWidths(); -} - -// Devuelve las opciones del grupo como pares (nombre, valor) -ServiceMenu::OptionPairs ServiceMenu::getOptionPairs(ServiceMenu::SettingsGroup group) const -{ - OptionPairs option_pairs; - - for (const auto &option : options_) - { - if (option.group == group && !option.hidden) - { - option_pairs.emplace_back(option.caption, option.getValueAsString()); - } - } - - return option_pairs; -} - -// Devuelve las opciones del grupo como un vector de OptionEntry -std::vector ServiceMenu::getOptionsByGroup(SettingsGroup group) -{ - std::vector filtered_options; - for (auto &option : options_) - { - if (option.group == group && !option.hidden) - { - filtered_options.push_back(&option); - } - } - return filtered_options; -} - -// Aplica la configuración correspondiente al grupo seleccionado -void ServiceMenu::applySettings(ServiceMenu::SettingsGroup group) -{ - switch (group) - { - case SettingsGroup::VIDEO: - Screen::get()->applySettings(); - break; - case SettingsGroup::AUDIO: - Audio::get()->applySettings(); - break; - case SettingsGroup::SETTINGS: - { - auto option = getOptionEntryByCaption(Lang::getText("[SERVICE_MENU] SHUTDOWN")); - if (option != nullptr) - { - option->hidden = !Options::settings.shutdown_enabled; - } - break; - } - default: - break; - } -} - -// Actualiza las opciones mostradas según el grupo seleccionado +void ServiceMenu::playMenuSound() { Audio::get()->playSound("clock.wav"); } void ServiceMenu::updateMenu(SettingsGroup group) { title_ = settingsGroupToString(group); AdjustListValues(); option_pairs_ = getOptionPairs(group); display_options_ = getOptionsByGroup(group); - // Recalcula el ancho del menú al cambiar de grupo resize(); } - -// Reinicia el menú al estado inicial (grupo principal y opción seleccionada) -void ServiceMenu::reset() -{ - selected_ = 0; - previous_settings_group_ = current_settings_group_ = SettingsGroup::MAIN; - title_ = settingsGroupToString(current_settings_group_); - initializeOptions(); - updateMenu(current_settings_group_); - setAnchors(); -} - -// Calcula la altura total del menú en píxeles -int ServiceMenu::calculateMenuHeight() const -{ - return ((4 + findLargestGroupSize() + 1) * options_padding_) - 5; -} - -// Devuelve el tamaño (número de opciones) del grupo más grande int ServiceMenu::findLargestGroupSize() const { std::unordered_map group_counts; for (const auto &option : options_) - ++group_counts[option.group]; - + ++group_counts[option->getGroup()]; int max_size = 0; for (const auto &pair : group_counts) if (pair.second > max_size) max_size = pair.second; return max_size; } - -// Devuelve la alineación de las opciones para el grupo dado ServiceMenu::GroupAlignment ServiceMenu::getGroupAlignment(SettingsGroup group) const { switch (group) @@ -546,94 +425,6 @@ ServiceMenu::GroupAlignment ServiceMenu::getGroupAlignment(SettingsGroup group) } } -// Devuelve un puntero a OptionEntry a partir del caption, o nullptr si no se encuentra -ServiceMenu::OptionEntry *ServiceMenu::getOptionEntryByCaption(const std::string &caption) -{ - for (auto &option : options_) - { - if (option.caption == caption) - { - return &option; - } - } - return nullptr; -} - -// Pone el texto que corresponde al valor de la variable en las opciones de tipo lista -void ServiceMenu::AdjustListValues() -{ - { // Idioma - auto option = getOptionEntryByCaption(Lang::getText("[SERVICE_MENU] LANGUAGE")); - for (size_t i = 0; i < option->value_list.size(); ++i) - { - if (Lang::getCodeFromName(option->value_list[i]) == Options::pending_changes.new_language) - { - option->list_index = i; - } - } - } - - { // Dificultad - auto option = getOptionEntryByCaption(Lang::getText("[SERVICE_MENU] DIFFICULTY")); - for (size_t i = 0; i < option->value_list.size(); ++i) - { - if (Options::getDifficultyCodeFromName(option->value_list[i]) == Options::pending_changes.new_difficulty) - { - option->list_index = i; - } - } - } -} - -void ServiceMenu::precalculateMenuWidths() -{ - // Inicializa todos los anchos al mínimo - for (int &w : group_menu_widths_) - w = MIN_WIDTH_; - - // Para cada grupo - for (int group = 0; group < 5; ++group) - { - SettingsGroup sg = static_cast(group); - int max_option_width = 0; - int max_value_width = 0; - for (const auto &option : options_) - { - if (option.group != sg) - continue; - // Opción más larga - max_option_width = std::max(max_option_width, element_text_->lenght(option.caption, -2)); - // Valor más largo de todos los posibles valores de todas las opciones - switch (option.type) - { - case ValueType::BOOL: - max_value_width = std::max({max_value_width, - element_text_->lenght(Lang::getText("[SERVICE_MENU] ON"), -2), - element_text_->lenght(Lang::getText("[SERVICE_MENU] OFF"), -2)}); - break; - case ValueType::INT: - max_value_width = std::max({max_value_width, - element_text_->lenght(std::to_string(option.min_value), -2), - element_text_->lenght(std::to_string(option.max_value), -2)}); - break; - case ValueType::LIST: - for (const auto &val : option.value_list) - max_value_width = std::max(max_value_width, element_text_->lenght(val, -2)); - break; - default: - break; - } - } - size_t total_width = max_option_width + MIN_GAP_OPTION_VALUE_ + max_value_width + (OPTIONS_HORIZONTAL_PADDING_ * 2); - group_menu_widths_[group] = std::max(MIN_WIDTH_, total_width); - } -} - -int ServiceMenu::getMenuWidthForGroup(SettingsGroup group) const -{ - return group_menu_widths_[static_cast(group)]; -} - std::string ServiceMenu::settingsGroupToString(SettingsGroup group) const { switch (group) diff --git a/source/service_menu.h b/source/service_menu.h index 1df4c5c..85e4388 100644 --- a/source/service_menu.h +++ b/source/service_menu.h @@ -6,263 +6,137 @@ #include #include #include "utils.h" -#include "lang.h" -#include "options.h" #include "UIMessage.h" +// --- Forward Declarations --- class Text; +class MenuOption; // <-- Añadido class ServiceMenu { public: - // --- Métodos de Singleton --- - static void init(); // Inicializa la instancia única de ServiceMenu - static void destroy(); // Libera la instancia única de ServiceMenu - static ServiceMenu *get(); // Devuelve el puntero a la instancia única + // Enums públicas para que MenuOption.h pueda usarlas. + enum class Aspect + { + ASPECT1, + ASPECT2 + }; + enum class SettingsGroup + { + VIDEO, + AUDIO, + SETTINGS, + SYSTEM, + MAIN + }; + enum class GroupAlignment + { + CENTERED, + LEFT + }; - // --- Métodos principales --- - void toggle(); // Muestra u oculta el menú de servicio - void render(); // Dibuja el menú de servicio en pantalla - void update(); // Actualiza el estado del menú de servicio - void reset(); // Reinicia el menú al estado inicial - - // --- Métodos de control de navegación --- - void setSelectorUp(); // Mueve el selector hacia arriba - void setSelectorDown(); // Mueve el selector hacia abajo - void adjustOption(bool adjust_up); // Ajusta el valor de la opción seleccionada - void selectOption(); // Selecciona la opción actual - void moveBack(); // Vuelve al grupo de opciones anterior - - // --- Getters --- - bool isEnabled() const { return enabled_; } // Indica si el menú de servicio está activo - - // --- Métodos para animación de resize --- + static void init(); + static void destroy(); + static ServiceMenu *get(); + void toggle(); + void render(); + void update(); + void reset(); + void setSelectorUp(); + void setSelectorDown(); + void adjustOption(bool adjust_up); + void selectOption(); + void moveBack(); + bool isEnabled() const { return enabled_; } void setResizeAnimationSteps(int steps) { resize_anim_steps_ = steps; } private: // --- Tipos internos --- using OptionPairs = std::vector>; - // --- Constantes --- - static constexpr const char *MENU_SOUND_ = "clock.wav"; // Sonido al navegar por el menú - static constexpr size_t OPTIONS_HORIZONTAL_PADDING_ = 20; // Relleno horizontal de las opciones - static constexpr size_t MIN_WIDTH_ = 240; // Anchura mínima del menu - static constexpr size_t MIN_GAP_OPTION_VALUE_ = 30; // Espacio mínimo entre una opción y su valor - - // --- Enumeraciones internas --- - enum class Aspect - { - ASPECT1, // Fondo opaco y proyecta sombra - ASPECT2 // Fondo translúcido - }; - - enum class SettingsGroup - { - VIDEO, // Configuraciones de vídeo - AUDIO, // Opciones de audio - SETTINGS, // Opciones de juego - SYSTEM, // Opciones del sistema - MAIN // Menú principal - }; - - enum class OptionBehavior - { - ADJUST, // Modificable con izquierda/derecha - SELECT // Activable con ENTER - }; - - enum class ValueType - { - BOOL, // Valor booleano - INT, // Valor entero - LIST, // Lista de valores - FOLDER, // Referencia a otro grupo - NONE // Sin valor asociado - }; - - enum class GroupAlignment - { - CENTERED, // Opciones centradas - LEFT // Opciones alineadas a la izquierda - }; - - // --- Estructura de opción del menú --- - struct OptionEntry - { - std::string caption; // Texto visible en el menú - SettingsGroup group; // Categoría de la opción - OptionBehavior behavior; // Cómo se interactúa con la opción - void *linked_variable; // Puntero a la variable que controla la opción - ValueType type; // Tipo de la variable - bool hidden = false; // Indica si la opción está oculta en el menú - - int min_value; // Valor mínimo (solo aplicable si type == INT) - int max_value; // Valor máximo (solo aplicable si type == INT) - int step_value; // Incremento al modificar la opción (solo aplicable si type == INT) - std::vector value_list; // Lista de valores posibles (solo aplicable si type == LIST) - size_t list_index; // Índice del valor seleccionado dentro de value_list - - SettingsGroup target_group; // Grupo al que hace referencia la opción si es de tipo FOLDER - - // Constructor para opciones de tipo BOOL, NONE, FOLDER - OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, void *var, ValueType t, bool hid = false) - : caption(cap), group(grp), behavior(beh), linked_variable(var), type(t), hidden(hid), - min_value(0), max_value(0), step_value(0), list_index(0), target_group(SettingsGroup::SYSTEM) {} - - // Constructor para opciones de tipo INT - OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, void *var, ValueType t, int min, int max, int step, bool hid = false) - : caption(cap), group(grp), behavior(beh), linked_variable(var), type(t), hidden(hid), - min_value(min), max_value(max), step_value(step), list_index(0), target_group(SettingsGroup::SYSTEM) {} - - // Constructor para opciones de tipo LIST - OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, void *var, std::vector values, bool hid = false) - : caption(cap), group(grp), behavior(beh), linked_variable(var), type(ValueType::LIST), hidden(hid), - min_value(0), max_value(0), step_value(0), value_list(values), list_index(0), target_group(SettingsGroup::SYSTEM) {} - - // Constructor para opciones de tipo FOLDER - OptionEntry(std::string cap, SettingsGroup grp, OptionBehavior beh, SettingsGroup tgtGrp, bool hid = false) - : caption(cap), group(grp), behavior(beh), linked_variable(nullptr), type(ValueType::FOLDER), hidden(hid), - min_value(0), max_value(0), step_value(0), list_index(0), target_group(tgtGrp) {} - - // Método para modificar el valor de la opción - void adjustValue(bool adjust_up) - { - if (linked_variable) - { - if (type == ValueType::INT) - { - int &value = *(static_cast(linked_variable)); - int newValue = adjust_up ? value + step_value : value - step_value; - value = std::clamp(newValue, min_value, max_value); - } - else if (type == ValueType::BOOL) - { - bool &value = *(static_cast(linked_variable)); - value = !value; - } - else if (type == ValueType::LIST && !value_list.empty()) - { - list_index = adjust_up ? (list_index + 1) % value_list.size() - : (list_index - 1 + value_list.size()) % value_list.size(); - - // Idioma - if (linked_variable == &Options::pending_changes.new_language) - { - Options::pending_changes.new_language = Lang::getCodeFromName(value_list[list_index]); - Options::checkPendingChanges(); - } - - // Dificultad - if (linked_variable == &Options::pending_changes.new_difficulty) - { - Options::pending_changes.new_difficulty = Options::getDifficultyCodeFromName(value_list[list_index]); - Options::checkPendingChanges(); - } - } - } - } - - // Método para obtener el valor como string - std::string getValueAsString() const - { - switch (type) - { - case ValueType::BOOL: - return (*(static_cast(linked_variable))) ? Lang::getText("[SERVICE_MENU] ON") : Lang::getText("[SERVICE_MENU] OFF"); - case ValueType::INT: - return std::to_string(*(static_cast(linked_variable))); - case ValueType::LIST: - return value_list.empty() ? "" : value_list[list_index]; - default: - return ""; - } - } - }; - // --- Variables internas --- - bool enabled_ = false; // Indica si el menú de servicio está activo - SDL_FRect rect_; // Rectángulo que define el área del menú de servicio - std::shared_ptr element_text_; // Objeto para escribir el texto de los elementos - std::shared_ptr title_text_; // Objeto para escribir el texto del título - size_t selected_ = 0; // Índice del elemento del menú seleccionado - Uint32 counter_ = 0; // Contador interno - std::vector options_; // Listado con todas las opciones del menú de servicio - std::vector display_options_; // Opciones actualmente mostradas en pantalla (punteros) - OptionPairs option_pairs_; // Opciones actuales del menú (filtradas por grupo) - SettingsGroup current_settings_group_; // Grupo de opciones actualmente activo - SettingsGroup previous_settings_group_; // Grupo de opciones anterior - Aspect aspect_ = Aspect::ASPECT1; // Estilo visual del menú - std::string title_; // Titulo a mostrar en el menu - size_t main_menu_selected_ = 0; // Recuerda la selección del menú principal + bool enabled_ = false; + SDL_FRect rect_; + std::shared_ptr element_text_; + std::shared_ptr title_text_; + size_t selected_ = 0; + Uint32 counter_ = 0; - // --- Variables de aspecto --- - Color bg_color_ = SERV_MENU_BG_COLOR; // Color de fondo - Color title_color_ = SERV_MENU_TITLE_COLOR; // Color del título del menú - Color text_color_ = SERV_MENU_TEXT_COLOR; // Color del texto de los elementos - Color selected_color_ = SERV_MENU_SELECTED_COLOR; // Color del elemento seleccionado - size_t width_; // Ancho del menú - size_t height_; // Alto del menú - size_t options_height_; // Altura de cada elemento del menu - size_t options_padding_; // Espaciado vertical alrededor de cada elemento del menu - size_t options_y_; // Posicion del primer elemento del menu - size_t title_height_; // Altura del texto de titulo del menu - size_t title_padding_; // Espaciado vertical alrededor del titulo - size_t upper_height_; // Altura de la parte de arriba del menu: la del titulo - size_t lower_height_; // Altira de la parte baja del menu: la que tiene las opciones - size_t lower_padding_; // Espaciado vertical mínimo entre los bordes y el contenido de la zona inferior + // Usamos punteros únicos para gestionar la vida de los objetos polimórficos. + std::vector> options_; + // Mostramos punteros raw (no propietarios) de las opciones filtradas. + std::vector display_options_; - // --- Variables para animación de resize --- - SDL_FRect rect_anim_from_{}; // Estado inicial de la animación - SDL_FRect rect_anim_to_{}; // Estado objetivo de la animación - int resize_anim_step_ = 0; // Paso actual de la animación - int resize_anim_steps_ = 8; // Total de pasos de la animación - bool resizing_ = false; // Si está animando el resize + OptionPairs option_pairs_; + SettingsGroup current_settings_group_; + SettingsGroup previous_settings_group_; + Aspect aspect_ = Aspect::ASPECT1; + std::string title_; + size_t main_menu_selected_ = 0; + // Variables de aspecto y layout... + Color bg_color_ = SERV_MENU_BG_COLOR; + Color title_color_ = SERV_MENU_TITLE_COLOR; + Color text_color_ = SERV_MENU_TEXT_COLOR; + Color selected_color_ = SERV_MENU_SELECTED_COLOR; + size_t width_; + size_t height_; + size_t options_height_; + size_t options_padding_; + size_t options_y_; + size_t title_height_; + size_t title_padding_; + size_t upper_height_; + size_t lower_height_; + size_t lower_padding_; + + // Variables de animación de resize... + SDL_FRect rect_anim_from_{}; + SDL_FRect rect_anim_to_{}; + int resize_anim_step_ = 0; + int resize_anim_steps_ = 8; + bool resizing_ = false; + + // Mensaje de reinicio std::unique_ptr restart_message_ui_; bool last_pending_changes_ = false; + // Anchos precalculados int group_menu_widths_[5]; - // --- Métodos internos: Anclaje y aspecto --- - void setAnchors(); // Establece el valor de las variables de anclaje - Color getSelectedColor() const; // Devuelve el color del elemento seleccionado - void setOptionsPosition(); // Establce la posición donde empezar a escribir las opciones del menu - void resize(); // Cambia el tamaño de la ventana de menu + // --- Métodos internos --- - // --- Métodos internos: Gestión de opciones --- - void initializeOptions(); // Crea todas las opciones del menú de servicio - OptionPairs getOptionPairs(SettingsGroup group) const; // Devuelve las opciones como pares de strings para un grupo - std::vector getOptionsByGroup(SettingsGroup group); // Devuelve punteros a las opciones de un grupo - - // --- Métodos internos: Lógica de menú --- - void applySettings(SettingsGroup group); // Aplica la configuración de un grupo - void updateMenu(SettingsGroup group); // Actualiza las opciones mostradas según el grupo - void AdjustListValues(); // Ajusta los valores de las opciones tipo lista - - // --- Métodos internos: Utilidades --- - void updateCounter(); // Actualiza el contador interno - int calculateMenuHeight() const; // Calcula la altura del menú - int findLargestGroupSize() const; // Devuelve el tamaño del grupo más grande - GroupAlignment getGroupAlignment(SettingsGroup group) const; // Devuelve la alineación del grupo - OptionEntry *getOptionEntryByCaption(const std::string &caption); // Devuelve un puntero a OptionEntry a partir del caption - std::string settingsGroupToString(SettingsGroup group) const; - - // --- Métodos internos: Animación de resize --- + // Anclaje y aspecto + void setAnchors(); + Color getSelectedColor() const; + void setOptionsPosition(); + void resize(); void updateResizeAnimation(); - // --- Métodos internos: Cálculo de anchos --- + // Gestión de opciones + void initializeOptions(); + OptionPairs getOptionPairs(SettingsGroup group) const; + std::vector getOptionsByGroup(SettingsGroup group); + MenuOption *getOptionByCaption(const std::string &caption); + + // Lógica de menú + void applySettings(SettingsGroup group); + void updateMenu(SettingsGroup group); + void AdjustListValues(); + + // Utilidades + void updateCounter(); + int findLargestGroupSize() const; + GroupAlignment getGroupAlignment(SettingsGroup group) const; + std::string settingsGroupToString(SettingsGroup group) const; void precalculateMenuWidths(); int getMenuWidthForGroup(SettingsGroup group) const; + void playMenuSound(); // --- Patrón Singleton --- - ServiceMenu(); // Constructor privado - ~ServiceMenu() = default; // Destructor privado - ServiceMenu(const ServiceMenu &) = delete; // Evita la copia - ServiceMenu &operator=(const ServiceMenu &) = delete; // Evita la asignación - - // --- Instancia Singleton --- - static ServiceMenu *instance_; // Instancia única del menú de servicio - - // --- Método para reproducir el sonido del menú --- - void playMenuSound(); // Reproduce el sonido del menú -}; \ No newline at end of file + ServiceMenu(); + ~ServiceMenu() = default; + ServiceMenu(const ServiceMenu &) = delete; + ServiceMenu &operator=(const ServiceMenu &) = delete; + static ServiceMenu *instance_; +};