ServiceMenu: afegida animació de apertura/tancament
This commit is contained in:
@@ -1,21 +1,41 @@
|
||||
#include "menu_renderer.h"
|
||||
|
||||
#include <algorithm> // Para max, min
|
||||
#include <utility> // Para pair, move
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "color.h"
|
||||
#include "menu_option.h"
|
||||
#include "param.h"
|
||||
#include "screen.h"
|
||||
#include "text.h"
|
||||
#include "utils.h"
|
||||
|
||||
// --- Implementación de las estructuras de animación ---
|
||||
|
||||
void MenuRenderer::ResizeAnimation::start(float from_w, float from_h, float to_w, float to_h) {
|
||||
start_width = from_w; start_height = from_h;
|
||||
target_width = to_w; target_height = to_h;
|
||||
elapsed = 0.0f; active = true;
|
||||
}
|
||||
void MenuRenderer::ResizeAnimation::stop() { active = false; elapsed = 0.0f; }
|
||||
|
||||
void MenuRenderer::ShowHideAnimation::startShow(float to_w, float to_h) {
|
||||
type = Type::SHOWING; target_width = to_w; target_height = to_h;
|
||||
elapsed = 0.0f; active = true;
|
||||
}
|
||||
void MenuRenderer::ShowHideAnimation::startHide() { type = Type::HIDING; elapsed = 0.0f; active = true; }
|
||||
void MenuRenderer::ShowHideAnimation::stop() { type = Type::NONE; active = false; elapsed = 0.0f; }
|
||||
|
||||
#include "color.h" // Para Color, generateMirroredCycle, ColorCycleStyle
|
||||
#include "menu_option.h" // Para MenuOption
|
||||
#include "param.h" // Para Param, param, ParamServiceMenu, ParamGame
|
||||
#include "screen.h" // Para Screen
|
||||
#include "text.h" // Para Text, Text::CENTER, Text::COLOR
|
||||
#include "utils.h" // Para Zone, truncateWithEllipsis
|
||||
|
||||
MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text)
|
||||
: element_text_(std::move(element_text)), title_text_(std::move(title_text)) {
|
||||
initializeMaxSizes();
|
||||
setPosition(param.game.game_area.center_x, param.game.game_area.center_y, PositionMode::CENTERED);
|
||||
}
|
||||
|
||||
void MenuRenderer::render(const ServiceMenu *menu_state) {
|
||||
if (!visible_) return;
|
||||
|
||||
// Dibuja la sombra
|
||||
if (param.service_menu.drop_shadow) {
|
||||
SDL_FRect shadow_rect = {rect_.x + 5, rect_.y + 5, rect_.w, rect_.h};
|
||||
@@ -33,9 +53,11 @@ void MenuRenderer::render(const ServiceMenu *menu_state) {
|
||||
SDL_RenderRect(Screen::get()->getRenderer(), &rect_);
|
||||
SDL_RenderRect(Screen::get()->getRenderer(), &border_rect_);
|
||||
|
||||
// Solo renderizar contenido si la animación lo permite
|
||||
if (shouldShowContent()) {
|
||||
// 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, param.service_menu.title_color);
|
||||
title_text_->writeDX(Text::COLOR | Text::CENTER, rect_.x + rect_.w / 2.0f, y, menu_state->getTitle(), -4, param.service_menu.title_color);
|
||||
|
||||
// Dibuja la línea separadora
|
||||
y = rect_.y + upper_height_;
|
||||
@@ -51,52 +73,86 @@ void MenuRenderer::render(const ServiceMenu *menu_state) {
|
||||
const Color ¤t_color = IS_SELECTED ? param.service_menu.selected_color : param.service_menu.text_color;
|
||||
|
||||
if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) {
|
||||
// Para opciones alineadas a la izquierda, truncamos el valor si es necesario
|
||||
const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2) -
|
||||
element_text_->length(option_pairs.at(i).first, -2) -
|
||||
ServiceMenu::MIN_GAP_OPTION_VALUE;
|
||||
|
||||
const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2) - element_text_->length(option_pairs.at(i).first, -2) - ServiceMenu::MIN_GAP_OPTION_VALUE;
|
||||
std::string truncated_value = getTruncatedValue(option_pairs.at(i).second, available_width);
|
||||
|
||||
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_->length(truncated_value, -2);
|
||||
element_text_->writeColored(X, y, truncated_value, current_color, -2);
|
||||
} else {
|
||||
// Para opciones centradas, también truncamos si es necesario
|
||||
const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2);
|
||||
std::string truncated_caption = getTruncatedValue(option_pairs.at(i).first, available_width);
|
||||
|
||||
element_text_->writeDX(Text::CENTER | Text::COLOR, rect_.x + rect_.w / 2, y, truncated_caption, -2, current_color);
|
||||
element_text_->writeDX(Text::CENTER | Text::COLOR, rect_.x + rect_.w / 2.0f, y, truncated_caption, -2, current_color);
|
||||
}
|
||||
y += options_height_ + options_padding_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuRenderer::update(const ServiceMenu *menu_state) {
|
||||
if (resizing_) {
|
||||
updateResizeAnimation();
|
||||
}
|
||||
float delta_time = 1.0f / 60.0f; // Asumiendo 60 FPS
|
||||
updateAnimations(delta_time);
|
||||
|
||||
if (visible_) {
|
||||
updateColorCounter();
|
||||
param.service_menu.selected_color = getAnimatedSelectedColor();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Nuevos métodos de control ---
|
||||
|
||||
void MenuRenderer::show(const ServiceMenu* menu_state) {
|
||||
if (visible_) return;
|
||||
visible_ = true;
|
||||
|
||||
// Calcula el tamaño final y lo usa para la animación
|
||||
SDL_FRect target_rect = calculateNewRect(menu_state);
|
||||
|
||||
// Detener cualquier animación anterior
|
||||
resize_animation_.stop();
|
||||
|
||||
// Iniciar animación de mostrar
|
||||
show_hide_animation_.startShow(target_rect.w, target_rect.h);
|
||||
|
||||
// El tamaño inicial es cero para la animación
|
||||
rect_.w = 0.0f;
|
||||
rect_.h = 0.0f;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
void MenuRenderer::hide() {
|
||||
if (!visible_ || show_hide_animation_.type == ShowHideAnimation::Type::HIDING) return;
|
||||
|
||||
// Detener animación de resize si la hubiera
|
||||
resize_animation_.stop();
|
||||
|
||||
// Guardar tamaño actual para la animación de ocultar
|
||||
show_hide_animation_.target_width = rect_.w;
|
||||
show_hide_animation_.target_height = rect_.h;
|
||||
show_hide_animation_.startHide();
|
||||
}
|
||||
|
||||
void MenuRenderer::setPosition(float x, float y, PositionMode mode) {
|
||||
anchor_x_ = x;
|
||||
anchor_y_ = y;
|
||||
position_mode_ = mode;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
// --- Métodos de layout ---
|
||||
|
||||
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::setLayout(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);
|
||||
setSize(menu_state);
|
||||
}
|
||||
|
||||
void MenuRenderer::initializeMaxSizes() {
|
||||
// Establecemos los límites máximos basados en el tamaño de la pantalla
|
||||
// Dejamos un margen del 10% en cada lado para que el menú no ocupe toda la pantalla
|
||||
max_menu_width_ = static_cast<size_t>(param.game.game_area.rect.w * 0.9F);
|
||||
max_menu_height_ = static_cast<size_t>(param.game.game_area.rect.h * 0.9F);
|
||||
}
|
||||
@@ -104,10 +160,7 @@ void MenuRenderer::initializeMaxSizes() {
|
||||
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<ServiceMenu::SettingsGroup>(i));
|
||||
if (count > max_entries) {
|
||||
max_entries = count;
|
||||
}
|
||||
max_entries = std::max(max_entries, menu_state->countOptionsInGroup(static_cast<ServiceMenu::SettingsGroup>(i)));
|
||||
}
|
||||
|
||||
options_height_ = element_text_->getCharacterSize();
|
||||
@@ -124,110 +177,152 @@ void MenuRenderer::setAnchors(const ServiceMenu *menu_state) {
|
||||
|
||||
auto MenuRenderer::calculateNewRect(const ServiceMenu *menu_state) -> SDL_FRect {
|
||||
width_ = std::min(static_cast<size_t>(getMenuWidthForGroup(menu_state->getCurrentGroup())), max_menu_width_);
|
||||
|
||||
const auto &display_options = menu_state->getDisplayOptions();
|
||||
lower_height_ = ((!display_options.empty() ? display_options.size() - 1 : 0) * (options_height_ + options_padding_)) + options_height_ + (lower_padding_ * 2);
|
||||
height_ = std::min(upper_height_ + lower_height_, max_menu_height_);
|
||||
|
||||
return {(param.game.width - width_) / 2.0F, (param.game.height - height_) / 2.0F, (float)width_, (float)height_};
|
||||
SDL_FRect new_rect = {0, 0, (float)width_, (float)height_};
|
||||
|
||||
// La posición x, y se establecerá en `updatePosition`
|
||||
return new_rect;
|
||||
}
|
||||
|
||||
|
||||
void MenuRenderer::resize(const ServiceMenu *menu_state) {
|
||||
SDL_FRect new_rect = calculateNewRect(menu_state);
|
||||
|
||||
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;
|
||||
if (rect_.w != new_rect.w || rect_.h != new_rect.h) {
|
||||
// En lugar de la animación antigua, usamos la nueva
|
||||
resize_animation_.start(rect_.w, rect_.h, new_rect.w, new_rect.h);
|
||||
} else {
|
||||
rect_ = setRect(new_rect);
|
||||
resizing_ = false;
|
||||
// Si no hay cambio de tamaño, solo actualizamos la posición
|
||||
updatePosition();
|
||||
}
|
||||
options_y_ = new_rect.y + upper_height_ + lower_padding_;
|
||||
}
|
||||
|
||||
void MenuRenderer::setSize(const ServiceMenu *menu_state) {
|
||||
rect_ = setRect(calculateNewRect(menu_state));
|
||||
resizing_ = false;
|
||||
options_y_ = rect_.y + upper_height_ + lower_padding_;
|
||||
}
|
||||
|
||||
void MenuRenderer::updateResizeAnimation() {
|
||||
if (!resizing_) {
|
||||
return;
|
||||
}
|
||||
++resize_anim_step_;
|
||||
float t = static_cast<float>(resize_anim_step_) / resize_anim_steps_;
|
||||
if (t >= 1.0F) {
|
||||
rect_ = setRect(rect_anim_to_);
|
||||
resizing_ = false;
|
||||
return;
|
||||
}
|
||||
float ease = 1 - (1 - t) * (1 - t);
|
||||
SDL_FRect rect =
|
||||
{rect_anim_from_.x + (rect_anim_to_.x - rect_anim_from_.x) * ease,
|
||||
rect_anim_from_.y + (rect_anim_to_.y - rect_anim_from_.y) * ease,
|
||||
rect_anim_from_.w + (rect_anim_to_.w - rect_anim_from_.w) * ease,
|
||||
rect_anim_from_.h + (rect_anim_to_.h - rect_anim_from_.h) * ease};
|
||||
rect_ = setRect(rect);
|
||||
void MenuRenderer::setSize(const ServiceMenu *menu_state) {
|
||||
SDL_FRect new_rect = calculateNewRect(menu_state);
|
||||
rect_.w = new_rect.w;
|
||||
rect_.h = new_rect.h;
|
||||
|
||||
show_hide_animation_.stop();
|
||||
resize_animation_.stop();
|
||||
|
||||
updatePosition();
|
||||
options_y_ = rect_.y + upper_height_ + lower_padding_;
|
||||
border_rect_ = {rect_.x - 1, rect_.y + 1, rect_.w + 2, rect_.h - 2};
|
||||
}
|
||||
|
||||
void MenuRenderer::precalculateMenuWidths(const std::vector<std::unique_ptr<MenuOption>> &all_options, const ServiceMenu *menu_state) {
|
||||
for (int &w : group_menu_widths_) {
|
||||
w = ServiceMenu::MIN_WIDTH;
|
||||
// --- Métodos de animación y posición ---
|
||||
|
||||
void MenuRenderer::updateAnimations(float delta_time) {
|
||||
if (show_hide_animation_.active) {
|
||||
updateShowHideAnimation(delta_time);
|
||||
}
|
||||
if (resize_animation_.active) {
|
||||
updateResizeAnimation(delta_time);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuRenderer::updateShowHideAnimation(float delta_time) {
|
||||
show_hide_animation_.elapsed += delta_time;
|
||||
float duration = show_hide_animation_.duration;
|
||||
|
||||
if (show_hide_animation_.elapsed >= duration) {
|
||||
if (show_hide_animation_.type == ShowHideAnimation::Type::SHOWING) {
|
||||
rect_.w = show_hide_animation_.target_width;
|
||||
rect_.h = show_hide_animation_.target_height;
|
||||
} else if (show_hide_animation_.type == ShowHideAnimation::Type::HIDING) {
|
||||
rect_.w = 0.0f; rect_.h = 0.0f; visible_ = false;
|
||||
}
|
||||
show_hide_animation_.stop();
|
||||
updatePosition();
|
||||
} else {
|
||||
float progress = easeOut(show_hide_animation_.elapsed / duration);
|
||||
if (show_hide_animation_.type == ShowHideAnimation::Type::SHOWING) {
|
||||
rect_.w = show_hide_animation_.target_width * progress;
|
||||
rect_.h = show_hide_animation_.target_height * progress;
|
||||
} else if (show_hide_animation_.type == ShowHideAnimation::Type::HIDING) {
|
||||
rect_.w = show_hide_animation_.target_width * (1.0f - progress);
|
||||
rect_.h = show_hide_animation_.target_height * (1.0f - progress);
|
||||
}
|
||||
updatePosition();
|
||||
}
|
||||
options_y_ = rect_.y + upper_height_ + lower_padding_;
|
||||
}
|
||||
|
||||
void MenuRenderer::updateResizeAnimation(float delta_time) {
|
||||
resize_animation_.elapsed += delta_time;
|
||||
float duration = resize_animation_.duration;
|
||||
|
||||
if (resize_animation_.elapsed >= duration) {
|
||||
rect_.w = resize_animation_.target_width;
|
||||
rect_.h = resize_animation_.target_height;
|
||||
resize_animation_.stop();
|
||||
updatePosition();
|
||||
} else {
|
||||
float progress = easeOut(resize_animation_.elapsed / duration);
|
||||
rect_.w = resize_animation_.start_width + (resize_animation_.target_width - resize_animation_.start_width) * progress;
|
||||
rect_.h = resize_animation_.start_height + (resize_animation_.target_height - resize_animation_.start_height) * progress;
|
||||
updatePosition();
|
||||
}
|
||||
options_y_ = rect_.y + upper_height_ + lower_padding_;
|
||||
}
|
||||
|
||||
|
||||
void MenuRenderer::updatePosition() {
|
||||
switch (position_mode_) {
|
||||
case PositionMode::CENTERED:
|
||||
rect_.x = anchor_x_ - rect_.w / 2.0f;
|
||||
rect_.y = anchor_y_ - rect_.h / 2.0f;
|
||||
break;
|
||||
case PositionMode::FIXED:
|
||||
rect_.x = anchor_x_;
|
||||
rect_.y = anchor_y_;
|
||||
break;
|
||||
}
|
||||
// Actualizar el rectángulo del borde junto con el principal
|
||||
border_rect_ = {rect_.x - 1, rect_.y + 1, rect_.w + 2, rect_.h - 2};
|
||||
}
|
||||
|
||||
// Resto de métodos (sin cambios significativos)
|
||||
|
||||
void MenuRenderer::precalculateMenuWidths(const std::vector<std::unique_ptr<MenuOption>> &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<ServiceMenu::SettingsGroup>(group);
|
||||
int max_option_width = 0;
|
||||
int max_value_width = 0;
|
||||
int max_option_width = 0, max_value_width = 0;
|
||||
for (const auto &option : all_options) {
|
||||
if (option->getGroup() != sg) {
|
||||
continue;
|
||||
}
|
||||
if (option->getGroup() != sg) continue;
|
||||
max_option_width = std::max(max_option_width, element_text_->length(option->getCaption(), -2));
|
||||
if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) {
|
||||
// Para calcular el ancho máximo, necesitamos considerar la truncación
|
||||
int max_available_value_width = static_cast<int>(max_menu_width_) - max_option_width -
|
||||
(ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2) -
|
||||
ServiceMenu::MIN_GAP_OPTION_VALUE;
|
||||
|
||||
int actual_value_width = getTruncatedValueWidth(option->getValueAsString(), max_available_value_width);
|
||||
max_value_width = std::max(max_value_width, actual_value_width);
|
||||
int max_available_value_width = static_cast<int>(max_menu_width_) - max_option_width - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2) - ServiceMenu::MIN_GAP_OPTION_VALUE;
|
||||
max_value_width = std::max(max_value_width, getTruncatedValueWidth(option->getValueAsString(), max_available_value_width));
|
||||
}
|
||||
}
|
||||
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::min(std::max((int)ServiceMenu::MIN_WIDTH, (int)total_width),
|
||||
static_cast<int>(max_menu_width_));
|
||||
if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) total_width += ServiceMenu::MIN_GAP_OPTION_VALUE + max_value_width;
|
||||
group_menu_widths_[group] = std::min(std::max((int)ServiceMenu::MIN_WIDTH, (int)total_width), static_cast<int>(max_menu_width_));
|
||||
}
|
||||
}
|
||||
|
||||
auto MenuRenderer::getMenuWidthForGroup(ServiceMenu::SettingsGroup group) const -> int {
|
||||
return group_menu_widths_[static_cast<int>(group)];
|
||||
}
|
||||
|
||||
auto MenuRenderer::getMenuWidthForGroup(ServiceMenu::SettingsGroup group) const -> int { return group_menu_widths_[static_cast<int>(group)]; }
|
||||
void MenuRenderer::updateColorCounter() {
|
||||
static Uint64 last_update_ = SDL_GetTicks();
|
||||
Uint64 current_ticks = SDL_GetTicks();
|
||||
if (current_ticks - last_update_ >= 50) {
|
||||
if (SDL_GetTicks() - last_update_ >= 50) {
|
||||
color_counter_++;
|
||||
last_update_ = current_ticks;
|
||||
last_update_ = SDL_GetTicks();
|
||||
}
|
||||
}
|
||||
|
||||
auto MenuRenderer::getAnimatedSelectedColor() const -> Color {
|
||||
static auto color_cycle_ = generateMirroredCycle(param.service_menu.selected_color, ColorCycleStyle::HUE_WAVE);
|
||||
return color_cycle_.at(color_counter_ % color_cycle_.size());
|
||||
}
|
||||
|
||||
auto MenuRenderer::setRect(SDL_FRect rect) -> SDL_FRect {
|
||||
border_rect_ = {rect.x - 1, rect.y + 1, rect.w + 2, rect.h - 2};
|
||||
return rect;
|
||||
}
|
||||
|
||||
auto MenuRenderer::getTruncatedValueWidth(const std::string &value, int available_width) const -> int {
|
||||
int value_width = element_text_->length(value, -2);
|
||||
if (value_width <= available_width) {
|
||||
@@ -279,3 +374,6 @@ auto MenuRenderer::getTruncatedValue(const std::string &value, int available_wid
|
||||
|
||||
return truncated;
|
||||
}
|
||||
|
||||
auto MenuRenderer::easeOut(float t) const -> float { return 1.0f - (1.0f - t) * (1.0f - t); }
|
||||
auto MenuRenderer::shouldShowContent() const -> bool { return !show_hide_animation_.active; }
|
||||
@@ -1,28 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_FRect, Uint32
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <array> // Para array
|
||||
#include <cstddef> // Para size_t
|
||||
#include <memory> // Para shared_ptr, unique_ptr
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "color.h" // Para Color
|
||||
#include "ui/service_menu.h" // Para ServiceMenu
|
||||
#include "color.h"
|
||||
#include "ui/service_menu.h"
|
||||
|
||||
class MenuOption;
|
||||
// Forward declarations
|
||||
class Text;
|
||||
|
||||
class MenuRenderer {
|
||||
public:
|
||||
// --- Nuevo: Enum para el modo de posicionamiento ---
|
||||
enum class PositionMode {
|
||||
CENTERED, // La ventana se centra en el punto especificado
|
||||
FIXED // La esquina superior izquierda coincide con el punto
|
||||
};
|
||||
|
||||
MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text);
|
||||
|
||||
// Métodos principales de la vista
|
||||
// --- Métodos principales de la vista ---
|
||||
void render(const ServiceMenu *menu_state);
|
||||
void update(const ServiceMenu *menu_state);
|
||||
|
||||
// --- Nuevos: Métodos de control de visibilidad y animación ---
|
||||
void show(const ServiceMenu *menu_state);
|
||||
void hide();
|
||||
[[nodiscard]] auto isVisible() const -> bool { return visible_; }
|
||||
[[nodiscard]] auto isFullyVisible() const -> bool { return visible_ && !show_hide_animation_.active && !resize_animation_.active; }
|
||||
[[nodiscard]] auto isAnimating() const -> bool { return resize_animation_.active || show_hide_animation_.active; }
|
||||
|
||||
// --- Nuevos: Métodos de configuración de posición ---
|
||||
void setPosition(float x, float y, PositionMode mode);
|
||||
|
||||
// Método para notificar al renderer que el layout puede haber cambiado
|
||||
void onLayoutChanged(const ServiceMenu *menu_state);
|
||||
void setLayout(const ServiceMenu *menu_state);
|
||||
@@ -49,17 +64,41 @@ class MenuRenderer {
|
||||
size_t lower_height_ = 0;
|
||||
size_t lower_padding_ = 0;
|
||||
Uint32 color_counter_ = 0;
|
||||
bool visible_ = false;
|
||||
|
||||
// --- Posicionamiento ---
|
||||
PositionMode position_mode_ = PositionMode::CENTERED;
|
||||
float anchor_x_ = 0.0f;
|
||||
float anchor_y_ = 0.0f;
|
||||
|
||||
// --- Límites de tamaño máximo ---
|
||||
size_t max_menu_width_ = 0;
|
||||
size_t max_menu_height_ = 0;
|
||||
|
||||
// --- Variables para 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;
|
||||
// --- Estructuras de Animación (inspirado en WindowMessage) ---
|
||||
struct ResizeAnimation {
|
||||
bool active = false;
|
||||
float start_width, start_height;
|
||||
float target_width, target_height;
|
||||
float elapsed = 0.0f;
|
||||
float duration = 0.2f;
|
||||
|
||||
void start(float from_w, float from_h, float to_w, float to_h);
|
||||
void stop();
|
||||
} resize_animation_;
|
||||
|
||||
struct ShowHideAnimation {
|
||||
enum class Type { NONE, SHOWING, HIDING };
|
||||
Type type = Type::NONE;
|
||||
bool active = false;
|
||||
float target_width, target_height;
|
||||
float elapsed = 0.0f;
|
||||
float duration = 0.25f;
|
||||
|
||||
void startShow(float to_w, float to_h);
|
||||
void startHide();
|
||||
void stop();
|
||||
} show_hide_animation_;
|
||||
|
||||
// --- Anchos precalculados ---
|
||||
std::array<int, ServiceMenu::SETTINGS_GROUP_SIZE> group_menu_widths_ = {};
|
||||
@@ -70,7 +109,12 @@ class MenuRenderer {
|
||||
auto calculateNewRect(const ServiceMenu *menu_state) -> SDL_FRect;
|
||||
void resize(const ServiceMenu *menu_state);
|
||||
void setSize(const ServiceMenu *menu_state);
|
||||
void updateResizeAnimation();
|
||||
|
||||
void updateAnimations(float delta_time);
|
||||
void updateResizeAnimation(float delta_time);
|
||||
void updateShowHideAnimation(float delta_time);
|
||||
void updatePosition();
|
||||
|
||||
void precalculateMenuWidths(const std::vector<std::unique_ptr<MenuOption>> &all_options, const ServiceMenu *menu_state);
|
||||
[[nodiscard]] auto getMenuWidthForGroup(ServiceMenu::SettingsGroup group) const -> int;
|
||||
[[nodiscard]] auto getAnimatedSelectedColor() const -> Color;
|
||||
@@ -78,4 +122,6 @@ class MenuRenderer {
|
||||
auto setRect(SDL_FRect rect) -> SDL_FRect;
|
||||
[[nodiscard]] auto getTruncatedValueWidth(const std::string &value, int available_width) const -> int;
|
||||
[[nodiscard]] auto getTruncatedValue(const std::string &value, int available_width) const -> std::string;
|
||||
[[nodiscard]] auto easeOut(float t) const -> float;
|
||||
[[nodiscard]] auto shouldShowContent() const -> bool;
|
||||
};
|
||||
@@ -31,7 +31,9 @@ ServiceMenu::ServiceMenu()
|
||||
auto element_text = Resource::get()->getText("04b_25_flat");
|
||||
auto title_text = Resource::get()->getText("04b_25_flat_2x");
|
||||
|
||||
// El renderer ahora se inicializa con su configuración
|
||||
renderer_ = std::make_unique<MenuRenderer>(this, element_text, title_text);
|
||||
|
||||
restart_message_ui_ = std::make_unique<UIMessage>(element_text, Lang::getText("[SERVICE_MENU] NEED_RESTART_MESSAGE"), param.service_menu.title_color);
|
||||
define_buttons_ = std::make_unique<DefineButtons>();
|
||||
|
||||
@@ -39,44 +41,53 @@ ServiceMenu::ServiceMenu()
|
||||
}
|
||||
|
||||
void ServiceMenu::toggle() {
|
||||
if (define_buttons_ && define_buttons_->isEnabled()) {
|
||||
return; // No se puede mostrar u ocultar el menu de servicio si se estan definiendo los botones
|
||||
}
|
||||
if (define_buttons_ && define_buttons_->isEnabled()) return;
|
||||
if (isAnimating() && !define_buttons_->isEnabled()) return; // No permitir toggle durante una animación
|
||||
|
||||
playBackSound();
|
||||
enabled_ = !enabled_;
|
||||
|
||||
if (enabled_) {
|
||||
// Primero resetea el estado y luego muestra la animación
|
||||
//reset();
|
||||
Options::gamepad_manager.assignAndLinkGamepads();
|
||||
renderer_->show(this);
|
||||
} else {
|
||||
reset();
|
||||
// Al cerrar, solo inicia la animación de ocultar
|
||||
renderer_->hide();
|
||||
// NO llames a reset() aquí
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceMenu::render() {
|
||||
if (!enabled_) {
|
||||
return;
|
||||
}
|
||||
// Condición corregida: renderiza si está habilitado O si se está animando
|
||||
if (enabled_ || isAnimating()) {
|
||||
renderer_->render(this);
|
||||
} else {
|
||||
return; // Si no está ni habilitado ni animándose, no dibujes nada.
|
||||
}
|
||||
|
||||
// El mensaje de reinicio se dibuja por encima, así que lo gestionamos aquí
|
||||
// El mensaje de reinicio y otros elementos solo deben aparecer si está completamente visible,
|
||||
// no durante la animación.
|
||||
if (enabled_ && !isAnimating()) {
|
||||
const float MSG_X = param.game.game_area.center_x;
|
||||
const float MSG_Y = renderer_->getRect().y + 39.0F;
|
||||
restart_message_ui_->setPosition(MSG_X, MSG_Y);
|
||||
restart_message_ui_->render();
|
||||
|
||||
// Renderizar DefineButtons si está activo (se dibuja por encima de todo)
|
||||
if (define_buttons_ && define_buttons_->isEnabled()) {
|
||||
define_buttons_->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceMenu::update() {
|
||||
if (!enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// El renderer siempre se actualiza para manejar sus animaciones
|
||||
renderer_->update(this);
|
||||
|
||||
if (!enabled_) return;
|
||||
|
||||
// Lógica de actualización del mensaje de reinicio y botones
|
||||
bool now_pending = Options::pending_changes.has_pending_changes;
|
||||
if (now_pending != last_pending_changes_) {
|
||||
now_pending ? restart_message_ui_->show() : restart_message_ui_->hide();
|
||||
@@ -84,11 +95,8 @@ void ServiceMenu::update() {
|
||||
}
|
||||
restart_message_ui_->update();
|
||||
|
||||
// Actualizar DefineButtons
|
||||
if (define_buttons_) {
|
||||
define_buttons_->update();
|
||||
|
||||
// Si DefineButtons ha terminado y está listo para cerrar completamente
|
||||
if (define_buttons_->isEnabled() && define_buttons_->isReadyToClose()) {
|
||||
define_buttons_->disable();
|
||||
}
|
||||
@@ -102,7 +110,22 @@ void ServiceMenu::reset() {
|
||||
previous_settings_group_ = SettingsGroup::MAIN;
|
||||
initializeOptions();
|
||||
updateMenu();
|
||||
renderer_->setLayout(this); // Notifica al renderer para que calcule el layout inicial
|
||||
renderer_->setLayout(this);
|
||||
}
|
||||
|
||||
void ServiceMenu::moveBack() {
|
||||
// Si estamos en una subpantalla, no llamamos a toggle
|
||||
if (current_settings_group_ != SettingsGroup::MAIN) {
|
||||
playBackSound();
|
||||
current_settings_group_ = previous_settings_group_;
|
||||
selected_ = (current_settings_group_ == SettingsGroup::MAIN) ? main_menu_selected_ : 0;
|
||||
updateMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
// Si estamos en la pantalla principal, llamamos a toggle() para cerrar con animación.
|
||||
// toggle() ya reproduce el sonido, por lo que no es necesario aquí.
|
||||
toggle();
|
||||
}
|
||||
|
||||
// --- Lógica de Navegación ---
|
||||
@@ -159,17 +182,6 @@ void ServiceMenu::selectOption() {
|
||||
playSelectSound();
|
||||
}
|
||||
|
||||
void ServiceMenu::moveBack() {
|
||||
playBackSound();
|
||||
if (current_settings_group_ == SettingsGroup::MAIN) {
|
||||
enabled_ = false;
|
||||
return;
|
||||
}
|
||||
current_settings_group_ = previous_settings_group_;
|
||||
selected_ = (current_settings_group_ == SettingsGroup::MAIN) ? main_menu_selected_ : 0;
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
// --- Lógica Interna ---
|
||||
|
||||
void ServiceMenu::updateDisplayOptions() {
|
||||
@@ -554,16 +566,13 @@ void ServiceMenu::handleEvent(const SDL_Event &event) {
|
||||
}
|
||||
|
||||
bool ServiceMenu::checkInput() {
|
||||
if (!enabled_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (define_buttons_ && define_buttons_->isEnabled()) {
|
||||
// --- Guardas ---
|
||||
// No procesar input si el menú no está habilitado, si se está animando o si se definen botones
|
||||
if (!enabled_ || isAnimating() || (define_buttons_ && define_buttons_->isEnabled())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static auto input = Input::get();
|
||||
|
||||
using Action = Input::Action;
|
||||
|
||||
const std::vector<std::pair<Action, std::function<void()>>> actions = {
|
||||
@@ -595,3 +604,12 @@ bool ServiceMenu::checkInput() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- Nuevo Getter ---
|
||||
auto ServiceMenu::isAnimating() const -> bool {
|
||||
return renderer_ && renderer_->isAnimating();
|
||||
}
|
||||
|
||||
auto ServiceMenu::isDefiningButtons() const -> bool {
|
||||
return define_buttons_ && define_buttons_->isEnabled();
|
||||
}
|
||||
@@ -1,35 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // Para size_t
|
||||
#include <memory> // Para unique_ptr
|
||||
#include <string> // Para basic_string, string
|
||||
#include <utility> // Para pair
|
||||
#include <vector> // Para vector
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "ui_message.h" // Para UIMessage
|
||||
#include "ui_message.h"
|
||||
#include "define_buttons.h"
|
||||
|
||||
class MenuOption;
|
||||
class MenuRenderer;
|
||||
//class DefineButtons; // Forward declaration
|
||||
|
||||
class ServiceMenu {
|
||||
public:
|
||||
enum class SettingsGroup {
|
||||
CONTROLS,
|
||||
VIDEO,
|
||||
AUDIO,
|
||||
SETTINGS,
|
||||
SYSTEM,
|
||||
MAIN
|
||||
};
|
||||
|
||||
enum class GroupAlignment {
|
||||
CENTERED,
|
||||
LEFT
|
||||
};
|
||||
|
||||
// --- Constantes públicas que el Renderer podría necesitar ---
|
||||
// ... (enums y constantes sin cambios)
|
||||
enum class SettingsGroup { CONTROLS, VIDEO, AUDIO, SETTINGS, SYSTEM, MAIN };
|
||||
enum class GroupAlignment { CENTERED, LEFT };
|
||||
static constexpr size_t OPTIONS_HORIZONTAL_PADDING = 20;
|
||||
static constexpr size_t MIN_WIDTH = 240;
|
||||
static constexpr size_t MIN_GAP_OPTION_VALUE = 30;
|
||||
@@ -55,17 +42,17 @@ class ServiceMenu {
|
||||
void selectOption();
|
||||
void moveBack();
|
||||
|
||||
// --- Método para manejar eventos (llamado desde GlobalEvents) ---
|
||||
// --- Método para manejar eventos ---
|
||||
void handleEvent(const SDL_Event &event);
|
||||
bool checkInput();
|
||||
|
||||
// NUEVO: Getter para saber si DefineButtons está activo
|
||||
[[nodiscard]] auto isDefiningButtons() const -> bool {
|
||||
return define_buttons_ && define_buttons_->isEnabled();
|
||||
}
|
||||
// --- Getters para el estado ---
|
||||
[[nodiscard]] auto isDefiningButtons() const -> bool;
|
||||
[[nodiscard]] auto isAnimating() const -> bool; // Nuevo getter
|
||||
|
||||
// --- Getters para que el Renderer pueda leer el estado ---
|
||||
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
|
||||
// ... (resto de getters sin cambios)
|
||||
[[nodiscard]] auto getTitle() const -> const std::string & { return title_; }
|
||||
[[nodiscard]] auto getCurrentGroup() const -> SettingsGroup { return current_settings_group_; }
|
||||
[[nodiscard]] auto getCurrentGroupAlignment() const -> GroupAlignment;
|
||||
@@ -76,26 +63,19 @@ class ServiceMenu {
|
||||
[[nodiscard]] auto countOptionsInGroup(SettingsGroup group) const -> size_t;
|
||||
|
||||
private:
|
||||
// --- Lógica de estado del menú (Modelo) ---
|
||||
// ... (resto de miembros privados sin cambios)
|
||||
bool enabled_ = false;
|
||||
std::vector<std::unique_ptr<MenuOption>> options_;
|
||||
std::vector<MenuOption *> display_options_;
|
||||
std::vector<std::pair<std::string, std::string>> option_pairs_;
|
||||
|
||||
SettingsGroup current_settings_group_;
|
||||
SettingsGroup previous_settings_group_;
|
||||
std::string title_;
|
||||
size_t selected_ = 0;
|
||||
size_t main_menu_selected_ = 0;
|
||||
|
||||
// --- Mensaje de reinicio ---
|
||||
std::unique_ptr<UIMessage> restart_message_ui_;
|
||||
bool last_pending_changes_ = false;
|
||||
|
||||
// --- Configuración de botones ---
|
||||
std::unique_ptr<DefineButtons> define_buttons_;
|
||||
|
||||
// --- La Vista ---
|
||||
std::unique_ptr<MenuRenderer> renderer_;
|
||||
|
||||
// --- Métodos de lógica interna ---
|
||||
|
||||
Reference in New Issue
Block a user