diff --git a/source/UIMessage.cpp b/source/UIMessage.cpp new file mode 100644 index 0000000..f20ec92 --- /dev/null +++ b/source/UIMessage.cpp @@ -0,0 +1,81 @@ +#include "UIMessage.h" +#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) +{ +} + +void UIMessage::show(float base_x, float base_y) +{ + 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 + y_offset_ = start_y_; + anim_step_ = 0; + animating_ = true; + visible_ = true; +} + +void UIMessage::hide() +{ + if (!visible_) return; + + start_y_ = y_offset_; + target_y_ = -8.0f; // Sube 8 píxeles para desaparecer + anim_step_ = 0; + animating_ = true; +} + +void UIMessage::update() +{ + if (animating_) + { + updateAnimation(); + } +} + +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); + 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) { + visible_ = false; + } + } +} + +void UIMessage::render() +{ + if (visible_) + { + text_renderer_->writeDX( + TEXT_COLOR | TEXT_CENTER, + base_x_, + base_y_ + y_offset_, + text_, + -2, + color_); + } +} + +bool UIMessage::isVisible() const +{ + return visible_; +} \ No newline at end of file diff --git a/source/UIMessage.h b/source/UIMessage.h new file mode 100644 index 0000000..be342a8 --- /dev/null +++ b/source/UIMessage.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include "text.h" +#include "utils.h" // Para Color + +class UIMessage +{ +public: + UIMessage(std::shared_ptr text_renderer, const std::string& message_text, const Color& color); + + void show(float base_x, float base_y); + void hide(); + void update(); + void render(); + + bool isVisible() const; + +private: + // Configuración + std::shared_ptr text_renderer_; + std::string text_; + Color color_; + + // Estado + bool visible_ = false; + bool animating_ = false; + float base_x_ = 0.0f; + float base_y_ = 0.0f; + float y_offset_ = 0.0f; + + // Animación + float start_y_ = 0.0f; + float target_y_ = 0.0f; + int anim_step_ = 0; + static constexpr int ANIMATION_STEPS = 12; + + void updateAnimation(); +}; \ No newline at end of file diff --git a/source/service_menu.cpp b/source/service_menu.cpp index b32b37c..dab8bf1 100644 --- a/source/service_menu.cpp +++ b/source/service_menu.cpp @@ -10,6 +10,7 @@ #include "audio.h" #include #include "lang.h" +#include "UIMessage.h" // Singleton ServiceMenu *ServiceMenu::instance_ = nullptr; @@ -30,6 +31,10 @@ ServiceMenu::ServiceMenu() current_settings_group_(SettingsGroup::MAIN), previous_settings_group_(current_settings_group_) { + restart_message_ui_ = std::make_unique( + element_text_, + Lang::getText("[SERVICE_MENU] NEED_RESTART_MESSAGE"), + title_color_); reset(); } @@ -68,20 +73,8 @@ void ServiceMenu::render() SDL_RenderRect(Screen::get()->getRenderer(), &rect_); // MENSAJE DE RESTART - if (restart_msg_visible_ || restart_msg_animating_) - { - // Al pintar el mensaje, calcula la posición base en ese frame: - float base_y = rect_.y + restart_msg_base_offset_; - float msg_y = base_y + restart_msg_y_; - element_text_->writeDX( - TEXT_COLOR | TEXT_CENTER, - param.game.game_area.center_x, - msg_y, - Lang::getText("[SERVICE_MENU] NEED_RESTART_MESSAGE"), - -2, - title_color_); - } - + 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_); @@ -134,32 +127,21 @@ void ServiceMenu::update() bool now_pending = Options::pending_changes.has_pending_changes; if (now_pending != last_pending_changes_) { - float y_offset = 39.0f; // O el que uses if (now_pending) - showRestartMessage(y_offset); + { + float msg_x = param.game.game_area.center_x; + float msg_y = rect_.y + 39.0f; // Posición Y base del mensaje + restart_message_ui_->show(msg_x, msg_y); + } else - hideRestartMessage(y_offset); + { + restart_message_ui_->hide(); + } last_pending_changes_ = now_pending; } // Animación del mensaje de reinicio - if (restart_msg_animating_) - { - ++restart_msg_anim_step_; - float t = static_cast(restart_msg_anim_step_) / restart_msg_anim_steps_; - // Ease out cubic - t = 1 - pow(1 - t, 3); - restart_msg_y_ = restart_msg_start_y_ + (restart_msg_target_y_ - restart_msg_start_y_) * t; - - if (restart_msg_anim_step_ >= restart_msg_anim_steps_) - { - restart_msg_y_ = restart_msg_target_y_; - restart_msg_animating_ = false; - // Si se oculta, asegúrate de que no se pinte más - if (!restart_msg_visible_) - restart_msg_y_ = restart_msg_target_y_; - } - } + restart_message_ui_->update(); } // Calcula y establece los anclajes y dimensiones del menú @@ -669,27 +651,4 @@ std::string ServiceMenu::settingsGroupToString(SettingsGroup group) const default: return Lang::getText("[SERVICE_MENU] TITLE"); } -} - -// Inicia la animación para mostrar el mensaje de reinicio -void ServiceMenu::showRestartMessage(float y_offset) -{ - restart_msg_start_y_ = -8.0f; // Empieza 8 píxeles arriba del destino - restart_msg_target_y_ = 0.0f; // El destino siempre es offset 0 respecto a base_y - restart_msg_y_ = restart_msg_start_y_; - restart_msg_anim_step_ = 0; - restart_msg_animating_ = true; - restart_msg_visible_ = true; - restart_msg_base_offset_ = y_offset; // Nueva variable para recordar el offset -} - -// Inicia la animación para ocultar el mensaje de reinicio -void ServiceMenu::hideRestartMessage(float y_offset) -{ - restart_msg_start_y_ = restart_msg_y_; - restart_msg_target_y_ = -6.0f; // Sube 8 píxeles arriba del destino - restart_msg_anim_step_ = 0; - restart_msg_animating_ = true; - restart_msg_visible_ = false; - restart_msg_base_offset_ = y_offset; } \ No newline at end of file diff --git a/source/service_menu.h b/source/service_menu.h index 0c03794..1df4c5c 100644 --- a/source/service_menu.h +++ b/source/service_menu.h @@ -8,6 +8,7 @@ #include "utils.h" #include "lang.h" #include "options.h" +#include "UIMessage.h" class Text; @@ -217,16 +218,8 @@ private: int resize_anim_steps_ = 8; // Total de pasos de la animación bool resizing_ = false; // Si está animando el resize - // --- Variables para el mensaje de reinicio --- - float restart_msg_y_ = 0.0f; - float restart_msg_target_y_ = 0.0f; - float restart_msg_start_y_ = 0.0f; - int restart_msg_anim_step_ = 0; - static constexpr int restart_msg_anim_steps_ = 6; // 8 pasos - bool restart_msg_animating_ = false; - bool restart_msg_visible_ = false; - - float restart_msg_base_offset_ = 0.0f; + std::unique_ptr restart_message_ui_; + bool last_pending_changes_ = false; int group_menu_widths_[5]; @@ -261,9 +254,6 @@ private: void precalculateMenuWidths(); int getMenuWidthForGroup(SettingsGroup group) const; - void showRestartMessage(float y); - void hideRestartMessage(float y); - // --- Patrón Singleton --- ServiceMenu(); // Constructor privado ~ServiceMenu() = default; // Destructor privado @@ -275,6 +265,4 @@ private: // --- Método para reproducir el sonido del menú --- void playMenuSound(); // Reproduce el sonido del menú - - bool last_pending_changes_ = false; // Último estado conocido de pending_changes }; \ No newline at end of file