#include "menu_renderer.h" #include "text.h" #include "menu_option.h" // Necesario para acceder a las opciones #include "screen.h" // Para param #include MenuRenderer::MenuRenderer(std::shared_ptr element_text, std::shared_ptr title_text) : element_text_(std::move(element_text)), title_text_(std::move(title_text)) { } void MenuRenderer::render(const ServiceMenu *menu_state) { // Dibuja la sombra if (menu_state->getAspect() == ServiceMenu::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); } // Dibuja el fondo const Uint8 ALPHA = menu_state->getAspect() == ServiceMenu::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_); // Dibuja el borde SDL_SetRenderDrawColor(Screen::get()->getRenderer(), title_color_.r, title_color_.g, title_color_.b, 255); SDL_RenderRect(Screen::get()->getRenderer(), &rect_); // Dibuja el título float y = rect_.y + title_padding_; title_text_->writeDX(TEXT_COLOR | TEXT_CENTER, param.game.game_area.center_x, y, menu_state->getTitle(), -4, title_color_); // Dibuja la línea separadora 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 + ServiceMenu::OPTIONS_HORIZONTAL_PADDING_, y, rect_.x + rect_.w - ServiceMenu::OPTIONS_HORIZONTAL_PADDING_, y); // Dibuja las opciones y = options_y_; const auto &option_pairs = menu_state->getOptionPairs(); for (size_t i = 0; i < option_pairs.size(); ++i) { const bool is_selected = (i == menu_state->getSelectedIndex()); const Color ¤t_color = is_selected ? selected_color_ : text_color_; if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) { element_text_->writeColored(rect_.x + ServiceMenu::OPTIONS_HORIZONTAL_PADDING_, y, option_pairs.at(i).first, current_color, -2); const int X = rect_.x + rect_.w - ServiceMenu::OPTIONS_HORIZONTAL_PADDING_ - element_text_->lenght(option_pairs.at(i).second, -2); element_text_->writeColored(X, y, option_pairs.at(i).second, current_color, -2); } else { element_text_->writeDX(TEXT_CENTER | TEXT_COLOR, rect_.x + rect_.w / 2, y, option_pairs.at(i).first, -2, current_color); } y += options_height_ + options_padding_; } } void MenuRenderer::update(const ServiceMenu *menu_state) { if (resizing_) { updateResizeAnimation(); } updateColorCounter(); selected_color_ = getAnimatedSelectedColor(); } void MenuRenderer::onLayoutChanged(const ServiceMenu *menu_state) { // Cuando la lógica del menú notifica un cambio, el renderer recalcula su layout precalculateMenuWidths(menu_state->getAllOptions(), menu_state); setAnchors(menu_state); resize(menu_state); } void MenuRenderer::setAnchors(const ServiceMenu *menu_state) { size_t max_entries = 0; for (int i = 0; i < 5; ++i) { size_t count = menu_state->countOptionsInGroup(static_cast(i)); if (count > max_entries) { max_entries = count; } } 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 > 0 ? max_entries - 1 : 0) * (options_height_ + options_padding_)) + options_height_ + (lower_padding_ * 2); width_ = ServiceMenu::MIN_WIDTH_; height_ = upper_height_ + lower_height_; rect_ = {(param.game.width - width_) / 2.0f, (param.game.height - height_) / 2.0f, (float)width_, (float)height_}; } void MenuRenderer::resize(const ServiceMenu *menu_state) { width_ = getMenuWidthForGroup(menu_state->getCurrentGroup()); const auto &display_options = menu_state->getDisplayOptions(); 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, (float)width_, (float)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_; rect_anim_to_ = new_rect; resize_anim_step_ = 0; resizing_ = true; } else { rect_ = new_rect; resizing_ = false; } options_y_ = new_rect.y + upper_height_ + lower_padding_; } void MenuRenderer::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; } 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 MenuRenderer::precalculateMenuWidths(const std::vector> &all_options, const ServiceMenu *menu_state) { for (int &w : group_menu_widths_) w = ServiceMenu::MIN_WIDTH_; for (int group = 0; group < 5; ++group) { auto sg = static_cast(group); int max_option_width = 0; int max_value_width = 0; for (const auto &option : all_options) { if (option->getGroup() != sg) continue; max_option_width = std::max(max_option_width, element_text_->lenght(option->getCaption(), -2)); if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) { max_value_width = std::max(max_value_width, option->getMaxValueWidth(element_text_.get())); } } size_t total_width = max_option_width + (ServiceMenu::OPTIONS_HORIZONTAL_PADDING_ * 2); if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) { total_width += ServiceMenu::MIN_GAP_OPTION_VALUE_ + max_value_width; } group_menu_widths_[group] = std::max((int)ServiceMenu::MIN_WIDTH_, (int)total_width); } } int MenuRenderer::getMenuWidthForGroup(ServiceMenu::SettingsGroup group) const { return group_menu_widths_[static_cast(group)]; } void MenuRenderer::updateColorCounter() { static Uint64 lastUpdate = SDL_GetTicks(); Uint64 currentTicks = SDL_GetTicks(); if (currentTicks - lastUpdate >= 50) { color_counter_++; lastUpdate = currentTicks; } } Color MenuRenderer::getAnimatedSelectedColor() { static const 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(color_counter_ % colors.size()); }