From 09f938d15e438c778a285240c6c8f061c392a537 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 17 Jun 2025 10:58:28 +0200 Subject: [PATCH] ServiceMenu: trastejant cosetes --- source/MenuOption.h | 127 +++++++++------- source/UIMessage.cpp | 56 ++++--- source/UIMessage.h | 47 ++++-- source/service_menu.cpp | 319 +++++++++++++++++++++++++++------------- source/service_menu.h | 1 + 5 files changed, 359 insertions(+), 191 deletions(-) diff --git a/source/MenuOption.h b/source/MenuOption.h index 1508fc3..81cf0f0 100644 --- a/source/MenuOption.h +++ b/source/MenuOption.h @@ -4,7 +4,7 @@ #include #include #include -#include // para std::clamp +#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 @@ -13,16 +13,21 @@ // --- Interfaz Base para todas las Opciones del Menú --- -class MenuOption { +class MenuOption +{ public: - enum class Behavior { ADJUST, SELECT }; + enum class Behavior + { + ADJUST, + SELECT + }; MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false) : caption_(std::move(caption)), group_(group), hidden_(hidden) {} - + virtual ~MenuOption() = default; - const std::string& getCaption() const { return caption_; } + const std::string &getCaption() const { return caption_; } ServiceMenu::SettingsGroup getGroup() const { return group_; } bool isHidden() const { return hidden_; } void setHidden(bool hidden) { hidden_ = hidden; } @@ -33,10 +38,8 @@ public: 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 --- + virtual int getMaxValueWidth(Text *text_renderer) const { return 0; } protected: std::string caption_; @@ -44,63 +47,73 @@ protected: bool hidden_; }; - // --- Clases Derivadas --- -class BoolOption : public MenuOption { +class BoolOption : public MenuOption +{ public: - BoolOption(const std::string& cap, ServiceMenu::SettingsGroup grp, bool* var) + 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 { + std::string getValueAsString() const override + { return *linked_variable_ ? Lang::getText("[SERVICE_MENU] ON") : Lang::getText("[SERVICE_MENU] OFF"); } - void adjustValue(bool /*adjust_up*/) override { + void adjustValue(bool /*adjust_up*/) override + { *linked_variable_ = !*linked_variable_; } - // --- AÑADIDO --- - int getMaxValueWidth(Text* text_renderer) const override { + 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) - ); + text_renderer->lenght(Lang::getText("[SERVICE_MENU] OFF"), -2)); } - // --- FIN AÑADIDO --- + private: - bool* linked_variable_; + bool *linked_variable_; }; -class IntOption : public MenuOption { +class IntOption : public MenuOption +{ public: - IntOption(const std::string& cap, ServiceMenu::SettingsGroup grp, int* var, int min, int max, int step) + 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 { + 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_); } - // --- 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) - ); + int getMaxValueWidth(Text *text_renderer) const override + { + int max_width = 0; + + // Iterar por todos los valores posibles en el rango + for (int value = min_value_; value <= max_value_; value += step_value_) + { + int width = text_renderer->lenght(std::to_string(value), -2); + max_width = std::max(max_width, width); + } + + return max_width; } - // --- FIN AÑADIDO --- + private: - int* linked_variable_; + int *linked_variable_; int min_value_, max_value_, step_value_; }; -class ListOption : public MenuOption { +class ListOption : public MenuOption +{ public: - ListOption(const std::string& cap, ServiceMenu::SettingsGroup grp, + ListOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::vector values, std::function current_value_getter, - std::function new_value_setter) + std::function new_value_setter) : MenuOption(cap, grp), value_list_(std::move(values)), getter_(std::move(current_value_getter)), @@ -109,11 +122,14 @@ public: { sync(); } - - void sync() { + + void sync() + { std::string current_value = getter_(); - for (size_t i = 0; i < value_list_.size(); ++i) { - if (value_list_[i] == current_value) { + for (size_t i = 0; i < value_list_.size(); ++i) + { + if (value_list_[i] == current_value) + { list_index_ = i; return; } @@ -121,52 +137,61 @@ public: } Behavior getBehavior() const override { return Behavior::ADJUST; } - std::string getValueAsString() const override { + std::string getValueAsString() const override + { return value_list_.empty() ? "" : value_list_[list_index_]; } - void adjustValue(bool adjust_up) override { - if (value_list_.empty()) return; + 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; setter_(value_list_[list_index_]); } - // --- AÑADIDO --- - int getMaxValueWidth(Text* text_renderer) const override { + int getMaxValueWidth(Text *text_renderer) const override + { int max_w = 0; - for (const auto& val : value_list_) { + 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_; - std::function setter_; + std::function setter_; size_t list_index_; }; -class FolderOption : public MenuOption { +class FolderOption : public MenuOption +{ public: - FolderOption(const std::string& cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target) + 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_; }; -class ActionOption : public MenuOption { +class ActionOption : public MenuOption +{ public: - ActionOption(const std::string& cap, ServiceMenu::SettingsGroup grp, std::function action, bool hidden = false) + 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_(); + void executeAction() override + { + if (action_) + action_(); } + private: std::function action_; }; diff --git a/source/UIMessage.cpp b/source/UIMessage.cpp index f20ec92..d6a32f2 100644 --- a/source/UIMessage.cpp +++ b/source/UIMessage.cpp @@ -1,37 +1,40 @@ #include "UIMessage.h" -#include // Para pow +#include // Para pow #include "screen.h" // Para param y TEXT_CENTER -// Constructor -UIMessage::UIMessage(std::shared_ptr text_renderer, const std::string& message_text, const Color& color) - : text_renderer_(text_renderer), text_(message_text), color_(color) -{ -} +// Constructor: inicializa el renderizador, el texto y el color del mensaje +UIMessage::UIMessage(std::shared_ptr text_renderer, const std::string &message_text, const Color &color) + : text_renderer_(text_renderer), text_(message_text), color_(color) {} +// Muestra el mensaje en la posición base_x, base_y con animación de entrada desde arriba void UIMessage::show(float base_x, float base_y) { - if (visible_ && target_y_ == 0.0f) return; // Ya está visible y quieto + if (visible_ && target_y_ == 0.0f) + return; // Ya está visible y quieto base_x_ = base_x; base_y_ = base_y; - start_y_ = -8.0f; // Empieza 8 píxeles arriba - target_y_ = 0.0f; // La posición final es el base_y + start_y_ = DESP_; // Empieza 8 píxeles arriba de la posición base + target_y_ = 0.0f; // La posición final es la base y_offset_ = start_y_; anim_step_ = 0; animating_ = true; visible_ = true; } +// Oculta el mensaje con animación de salida hacia arriba void UIMessage::hide() { - if (!visible_) return; + if (!visible_) + return; - start_y_ = y_offset_; - target_y_ = -8.0f; // Sube 8 píxeles para desaparecer + start_y_ = y_offset_; // Comienza desde la posición actual + target_y_ = DESP_; // Termina 8 píxeles arriba de la base anim_step_ = 0; animating_ = true; } +// Actualiza el estado de la animación (debe llamarse cada frame) void UIMessage::update() { if (animating_) @@ -40,27 +43,35 @@ void UIMessage::update() } } +// Interpola la posición vertical del mensaje usando ease out cubic void UIMessage::updateAnimation() { anim_step_++; float t = static_cast(anim_step_) / ANIMATION_STEPS; - - // Ease Out Cubic para una animación más suave - t = 1 - pow(1 - t, 3); + + if (target_y_ > start_y_) + { + // Animación de entrada (ease out cubic) + t = 1 - pow(1 - t, 3); + } + else + { + // Animación de salida (ease in cubic) + t = pow(t, 3); + } + y_offset_ = start_y_ + (target_y_ - start_y_) * t; if (anim_step_ >= ANIMATION_STEPS) { y_offset_ = target_y_; animating_ = false; - - // Si el objetivo era ocultarlo (target_y negativo), lo marcamos como no visible - if (target_y_ < 0.0f) { + if (target_y_ < 0.0f) visible_ = false; - } } } +// Dibuja el mensaje en pantalla si está visible void UIMessage::render() { if (visible_) @@ -75,7 +86,14 @@ void UIMessage::render() } } +// Devuelve true si el mensaje está visible actualmente bool UIMessage::isVisible() const { return visible_; +} + +// Permite actualizar la posición base Y del mensaje (por ejemplo, si el menú se mueve) +void UIMessage::setBaseY(float new_base_y) +{ + base_y_ = new_base_y; } \ No newline at end of file diff --git a/source/UIMessage.h b/source/UIMessage.h index be342a8..1aa64ef 100644 --- a/source/UIMessage.h +++ b/source/UIMessage.h @@ -5,36 +5,51 @@ #include "text.h" #include "utils.h" // Para Color +// Clase para mostrar mensajes animados en la interfaz de usuario class UIMessage { public: - UIMessage(std::shared_ptr text_renderer, const std::string& message_text, const Color& color); + // Constructor: recibe el renderizador de texto, el mensaje y el color + UIMessage(std::shared_ptr text_renderer, const std::string &message_text, const Color &color); + // Muestra el mensaje en la posición base_x, base_y con animación de entrada void show(float base_x, float base_y); + + // Oculta el mensaje con animación de salida void hide(); + + // Actualiza el estado de la animación (debe llamarse cada frame) void update(); + + // Dibuja el mensaje en pantalla si está visible void render(); + // Indica si el mensaje está visible actualmente bool isVisible() const; + // Permite actualizar la posición base Y del mensaje (por ejemplo, si el menú se mueve) + void setBaseY(float new_base_y); + private: - // Configuración - std::shared_ptr text_renderer_; - std::string text_; - Color color_; + // --- Configuración --- + std::shared_ptr text_renderer_; // Renderizador de texto + std::string text_; // Texto del mensaje a mostrar + Color color_; // Color del texto - // Estado - bool visible_ = false; - bool animating_ = false; - float base_x_ = 0.0f; - float base_y_ = 0.0f; - float y_offset_ = 0.0f; + // --- Estado --- + bool visible_ = false; // Indica si el mensaje está visible + bool animating_ = false; // Indica si el mensaje está en proceso de animación + float base_x_ = 0.0f; // Posición X base donde se muestra el mensaje + float base_y_ = 0.0f; // Posición Y base donde se muestra el mensaje + float y_offset_ = 0.0f; // Desplazamiento vertical actual del mensaje (para animación) - // Animación - float start_y_ = 0.0f; - float target_y_ = 0.0f; - int anim_step_ = 0; - static constexpr int ANIMATION_STEPS = 12; + // --- Animación --- + float start_y_ = 0.0f; // Posición Y inicial de la animación + float target_y_ = 0.0f; // Posición Y objetivo de la animación + int anim_step_ = 0; // Paso actual de la animación + static constexpr int ANIMATION_STEPS = 8; // Número total de pasos de la animación + static constexpr float DESP_ = -8.0f; // Distancia a desplazarse + // Actualiza la interpolación de la animación (ease out/in cubic) void updateAnimation(); }; \ No newline at end of file diff --git a/source/service_menu.cpp b/source/service_menu.cpp index 36036d5..8593349 100644 --- a/source/service_menu.cpp +++ b/source/service_menu.cpp @@ -1,5 +1,5 @@ #include "service_menu.h" -#include "MenuOption.h" +#include "MenuOption.h" #include "screen.h" #include #include @@ -25,8 +25,7 @@ ServiceMenu::ServiceMenu() restart_message_ui_ = std::make_unique( element_text_, Lang::getText("[SERVICE_MENU] NEED_RESTART_MESSAGE"), - title_color_ - ); + title_color_); reset(); } @@ -48,25 +47,24 @@ 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)); - 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")}, - [](){ 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")}, - [](){ 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] LANGUAGE"), SettingsGroup::SETTINGS, std::vector{Lang::getText("[SERVICE_MENU] LANG_ES"), Lang::getText("[SERVICE_MENU] LANG_BA"), Lang::getText("[SERVICE_MENU] LANG_EN")}, []() + { 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")}, []() + { 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 --- - 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 )); + 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)); // --- Main Menu --- options_.push_back(std::make_unique(Lang::getText("[SERVICE_MENU] VIDEO"), SettingsGroup::MAIN, SettingsGroup::VIDEO)); @@ -79,9 +77,10 @@ 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); @@ -93,14 +92,15 @@ 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_); - - 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(); @@ -111,19 +111,18 @@ void ServiceMenu::selectOption() playMenuSound(); return; } - + if (selected_option->getBehavior() == MenuOption::Behavior::SELECT) { selected_option->executeAction(); } } - // --- Métodos de Cálculo de Ancho (Reincorporados y adaptados) --- void ServiceMenu::precalculateMenuWidths() { - for (int& w : group_menu_widths_) + for (int &w : group_menu_widths_) w = MIN_WIDTH_; for (int group = 0; group < 5; ++group) @@ -131,19 +130,23 @@ void ServiceMenu::precalculateMenuWidths() SettingsGroup sg = static_cast(group); int max_option_width = 0; int max_value_width = 0; - - for (const auto& option : options_) + + for (const auto &option : options_) { - if (option->getGroup() != sg) continue; - + 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())); } - - 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 { + + 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); } @@ -155,17 +158,25 @@ int ServiceMenu::getMenuWidthForGroup(SettingsGroup group) const return group_menu_widths_[static_cast(group)]; } - // --- 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::toggle() +{ + enabled_ = !enabled_; + if (!enabled_) + { + reset(); + } +} -void ServiceMenu::render() { +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); @@ -182,35 +193,53 @@ void ServiceMenu::render() { 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); y = options_y_; - for (size_t i = 0; i < option_pairs_.size(); ++i) { - if (getGroupAlignment(current_settings_group_) == GroupAlignment::LEFT) { + 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) { - 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 { + if (now_pending != last_pending_changes_) + { + if (now_pending) + { + const float MSG_X = param.game.game_area.center_x; + const float MSG_Y = rect_.y + RESET_TEXT_POS_Y_; + restart_message_ui_->show(MSG_X, MSG_Y); + } + 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_); @@ -218,20 +247,41 @@ 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; @@ -245,114 +295,173 @@ void ServiceMenu::setAnchors() { 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() > 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; + restart_message_ui_->setBaseY(rect_.y + RESET_TEXT_POS_Y_); } -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(MENU_SOUND_); } -void ServiceMenu::updateMenu(SettingsGroup group) { +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"); } } -ServiceMenu::OptionPairs ServiceMenu::getOptionPairs(SettingsGroup group) const { +ServiceMenu::OptionPairs ServiceMenu::getOptionPairs(SettingsGroup group) const +{ OptionPairs option_pairs; - for (const auto& option : options_) { - if (option->getGroup() == group && !option->isHidden()) { + 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()) { +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) { +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) { +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) { + 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())) { +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 e1ddfdf..c8c1cb2 100644 --- a/source/service_menu.h +++ b/source/service_menu.h @@ -56,6 +56,7 @@ private: static constexpr size_t OPTIONS_HORIZONTAL_PADDING_ = 20; static constexpr size_t MIN_WIDTH_ = 220; static constexpr size_t MIN_GAP_OPTION_VALUE_ = 20; + static constexpr float RESET_TEXT_POS_Y_ = 39.0f; // --- Tipos internos --- using OptionPairs = std::vector>;