Varios arreglos a tot lo de definir botons dins del service menu i lo de triar mando

This commit is contained in:
2025-08-07 21:11:05 +02:00
parent d8a37c555f
commit fdfc976749
11 changed files with 223 additions and 233 deletions

View File

@@ -6,7 +6,7 @@
"[DEFINE_BUTTONS] TITLE": "define buttons title", "[DEFINE_BUTTONS] TITLE": "define buttons title",
"[DEFINE_BUTTONS] FIRE_LEFT": "Disparar cap a l'esquerra", "[DEFINE_BUTTONS] FIRE_LEFT": "Disparar cap a l'esquerra",
"[DEFINE_BUTTONS] FIRE_UP": "Disparar cap amunt amunt amunt amunt", "[DEFINE_BUTTONS] FIRE_UP": "Disparar cap amunt",
"[DEFINE_BUTTONS] FIRE_RIGHT": "Disparar cap a la dreta", "[DEFINE_BUTTONS] FIRE_RIGHT": "Disparar cap a la dreta",
"[DEFINE_BUTTONS] START": "Start", "[DEFINE_BUTTONS] START": "Start",
"[DEFINE_BUTTONS] SERVICE_MENU": "Menu de servei", "[DEFINE_BUTTONS] SERVICE_MENU": "Menu de servei",

View File

@@ -24,7 +24,7 @@ DefineButtons::DefineButtons()
// Crear la ventana de mensaje // Crear la ventana de mensaje
WindowMessage::Config config; WindowMessage::Config config;
config.bg_color = Color{20, 30, 50, 224}; // Fondo azul oscuro semi-transparente config.bg_color = Color{20, 30, 50, 240}; // Fondo azul oscuro semi-transparente
config.border_color = Color{100, 150, 200, 255}; // Borde azul claro config.border_color = Color{100, 150, 200, 255}; // Borde azul claro
config.title_color = Color{100, 150, 200, 255}; // Titulo azul claro config.title_color = Color{100, 150, 200, 255}; // Titulo azul claro
config.text_color = Color{220, 220, 220, 255}; // Texto gris claro config.text_color = Color{220, 220, 220, 255}; // Texto gris claro
@@ -157,8 +157,8 @@ void DefineButtons::checkEnd() {
if (window_message_) { if (window_message_) {
window_message_->clearTexts(); window_message_->clearTexts();
window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURATION_COMPLETE")); window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURATION_COMPLETE"));
window_message_->autoSize(); //window_message_->autoSize();
window_message_->centerOnScreen(); //window_message_->centerOnScreen();
} }
// Se deshabilitará desde el ServiceMenu después de un breve delay // Se deshabilitará desde el ServiceMenu después de un breve delay
@@ -178,26 +178,9 @@ void DefineButtons::updateWindowMessage() {
window_message_->clearTexts(); window_message_->clearTexts();
if (index_button_ < buttons_.size()) { if (index_button_ < buttons_.size()) {
// Mostrar progreso
/*std::string progress = "(" + std::to_string(index_button_ + 1) + "/" +
std::to_string(buttons_.size()) + ")";
window_message_->addText(progress);
window_message_->addText("");*/
// Instrucción actual // Instrucción actual
std::string instruction = Lang::getText("[DEFINE_BUTTONS] PRESS_BUTTON_FOR") + ":"; std::string instruction = Lang::getText("[DEFINE_BUTTONS] PRESS_BUTTON_FOR") + ":";
window_message_->addText(instruction); window_message_->addText(instruction);
window_message_->addText(buttons_.at(index_button_).label); window_message_->addText(buttons_.at(index_button_).label);
// Botones ya configurados
/*if (index_button_ > 0) {
window_message_->addText("");
window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURED") + ":");
for (size_t i = 0; i < index_button_; ++i) {
std::string configured = "✓ " + buttons_[i].label;
window_message_->addText(configured);
}
}*/
} }
} }

View File

@@ -30,16 +30,7 @@ void check(const SDL_Event &event) {
break; break;
} }
if (ServiceMenu::get()->isEnabled()) { ServiceMenu::get()->handleEvent(event);
ServiceMenu::get()->handleEvent(event); // Método que vamos a crear
// Si DefineButtons está activo, no procesar más eventos
if (ServiceMenu::get()->isDefiningButtons()) {
return;
}
}
Mouse::handleEvent(event); Mouse::handleEvent(event);
static auto *input_ = Input::get(); static auto *input_ = Input::get();

View File

@@ -183,95 +183,6 @@ auto checkServiceButton() -> bool {
return false; return false;
} }
// Comprueba las entradas del menú de servicio
auto checkServiceInputs() -> bool {
if (!ServiceMenu::get()->isEnabled()) {
return false;
}
// Teclado
{
// Arriba
if (Input::get()->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->setSelectorUp();
return true;
}
// Abajo
if (Input::get()->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->setSelectorDown();
return true;
}
// Derecha
if (Input::get()->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->adjustOption(true);
return true;
}
// Izquierda
if (Input::get()->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->adjustOption(false);
return true;
}
// Aceptar
if (Input::get()->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->selectOption();
return true;
}
// Atras
if (Input::get()->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
ServiceMenu::get()->moveBack();
return true;
}
}
// Mandos
{
auto gamepads = Input::get()->getGamepads();
for (auto gamepad : gamepads) {
// Arriba
if (Input::get()->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->setSelectorUp();
return true;
}
// Abajo
if (Input::get()->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->setSelectorDown();
return true;
}
// Derecha
if (Input::get()->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->adjustOption(true);
return true;
}
// Izquierda
if (Input::get()->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->adjustOption(false);
return true;
}
// Aceptar
if (Input::get()->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->selectOption();
return true;
}
// Atras
if (Input::get()->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
ServiceMenu::get()->moveBack();
return true;
}
}
}
return false;
}
// Comprueba las entradas fuera del menú de servicio // Comprueba las entradas fuera del menú de servicio
auto checkInputs() -> bool { auto checkInputs() -> bool {
// Teclado // Teclado
@@ -370,7 +281,7 @@ auto check() -> bool {
if (checkServiceButton()) { if (checkServiceButton()) {
return true; return true;
} }
if (checkServiceInputs()) { if (ServiceMenu::get()->checkInput()) {
return true; return true;
} }
if (checkInputs()) { if (checkInputs()) {

View File

@@ -296,22 +296,28 @@ auto getGamepadInfo(Player::Id player_id) -> std::string {
// Asigna los mandos físicos basándose en la configuración actual. // Asigna los mandos físicos basándose en la configuración actual.
void GamepadManager::assignAndLinkGamepads() { void GamepadManager::assignAndLinkGamepads() {
// 1. Obtenemos todos los mandos físicos que están conectados ahora mismo. // 1. Obtenemos los mandos físicos conectados.
auto physical_gamepads = Input::get()->getGamepads(); auto physical_gamepads = Input::get()->getGamepads();
// 2. Reiniciamos las asignaciones actuales y guardamos los datos deseados de la config. // 2. Reiniciamos las asignaciones actuales.
std::array<std::string, MAX_PLAYERS> desired_paths; std::array<std::string, MAX_PLAYERS> desired_paths;
for (size_t i = 0; i < MAX_PLAYERS; ++i) { for (size_t i = 0; i < MAX_PLAYERS; ++i) {
desired_paths[i] = gamepads_[i].path; desired_paths[i] = gamepads_[i].path;
gamepads_[i].instance = nullptr; gamepads_[i].instance = nullptr;
} }
// 3. Vector para rastrear los mandos físicos que ya hemos asignado. // 3. Vector para rastrear los mandos ya asignados.
std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances; std::vector<std::shared_ptr<Input::Gamepad>> assigned_instances;
// --- Ejecutamos las pasadas de asignación --- // --- Ejecutamos las pasadas de asignación y limpieza ---
// Pasada 1: Intenta asignar por la ruta guardada.
assignGamepadsByPath(desired_paths, physical_gamepads, assigned_instances); assignGamepadsByPath(desired_paths, physical_gamepads, assigned_instances);
// Pasada 2: Asigna los mandos restantes a los jugadores libres.
assignRemainingGamepads(physical_gamepads, assigned_instances); assignRemainingGamepads(physical_gamepads, assigned_instances);
// Pasada 3: Limpia los datos de los slots que se quedaron sin mando.
clearUnassignedGamepadSlots();
} }
// --- PRIMERA PASADA: Intenta asignar mandos basándose en la ruta guardada --- // --- PRIMERA PASADA: Intenta asignar mandos basándose en la ruta guardada ---
@@ -328,7 +334,12 @@ void GamepadManager::assignGamepadsByPath(
// Buscamos un mando físico que coincida con la ruta y no esté ya asignado. // Buscamos un mando físico que coincida con la ruta y no esté ya asignado.
for (const auto& physical_gamepad : physical_gamepads) { for (const auto& physical_gamepad : physical_gamepads) {
if (physical_gamepad->path == desired_path && !isGamepadAssigned(physical_gamepad, assigned_instances)) { if (physical_gamepad->path == desired_path && !isGamepadAssigned(physical_gamepad, assigned_instances)) {
// Asignamos y actualizamos TODOS los datos.
gamepads_[i].instance = physical_gamepad; gamepads_[i].instance = physical_gamepad;
gamepads_[i].name = physical_gamepad->name; // <--- LA LÍNEA QUE FALTABA
// No es necesario actualizar la path aquí porque ya coincide.
assigned_instances.push_back(physical_gamepad); assigned_instances.push_back(physical_gamepad);
break; // Mando encontrado para este jugador, pasamos al siguiente. break; // Mando encontrado para este jugador, pasamos al siguiente.
} }
@@ -361,6 +372,21 @@ void GamepadManager::assignRemainingGamepads(
} }
} }
// --- TERCERA PASADA: Limpia la información "fantasma" de los slots no asignados ---
void GamepadManager::clearUnassignedGamepadSlots() {
// Recorremos los slots de jugador una última vez.
for (auto& gamepad_config : gamepads_) {
// Si un slot no tiene una instancia física enlazada (instance == nullptr),
// significa que no hay un mando para él.
if (gamepad_config.instance == nullptr) {
// Limpiamos sus datos de configuración para no mostrar información
// de un mando que ya no está conectado.
gamepad_config.name = "";
gamepad_config.path = "";
}
}
}
// Función auxiliar para comprobar si un mando físico ya está en la lista de asignados. // Función auxiliar para comprobar si un mando físico ya está en la lista de asignados.
// Devuelve 'true' si ya ha sido asignado, 'false' en caso contrario. // Devuelve 'true' si ya ha sido asignado, 'false' en caso contrario.
auto GamepadManager::isGamepadAssigned( auto GamepadManager::isGamepadAssigned(

View File

@@ -238,6 +238,7 @@ class GamepadManager {
void assignRemainingGamepads( void assignRemainingGamepads(
const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads, const std::vector<std::shared_ptr<Input::Gamepad>>& physical_gamepads,
std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances); std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances);
void clearUnassignedGamepadSlots();
[[nodiscard]] static auto isGamepadAssigned( [[nodiscard]] static auto isGamepadAssigned(
const std::shared_ptr<Input::Gamepad>& physical_gamepad, const std::shared_ptr<Input::Gamepad>& physical_gamepad,
const std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) -> bool; const std::vector<std::shared_ptr<Input::Gamepad>>& assigned_instances) -> bool;

View File

@@ -59,11 +59,6 @@ void MenuRenderer::render(const ServiceMenu *menu_state) {
std::string truncated_value = getTruncatedValue(option_pairs.at(i).second, available_width); std::string truncated_value = getTruncatedValue(option_pairs.at(i).second, available_width);
// Si la opción tiene Behavior::BOTH, añadir indicador visual
if (i < display_options.size() && display_options[i]->getBehavior() == MenuOption::Behavior::BOTH) {
truncated_value = "-" + truncated_value + "-";
}
element_text_->writeColored(rect_.x + ServiceMenu::OPTIONS_HORIZONTAL_PADDING, y, option_pairs.at(i).first, current_color, -2); 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); 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); element_text_->writeColored(X, y, truncated_value, current_color, -2);
@@ -72,11 +67,6 @@ void MenuRenderer::render(const ServiceMenu *menu_state) {
const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2); const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2);
std::string truncated_caption = getTruncatedValue(option_pairs.at(i).first, available_width); std::string truncated_caption = getTruncatedValue(option_pairs.at(i).first, available_width);
// Si la opción tiene Behavior::BOTH, añadir indicador visual
if (i < display_options.size() && display_options[i]->getBehavior() == MenuOption::Behavior::BOTH) {
truncated_caption = "-" + truncated_caption + "-";
}
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, y, truncated_caption, -2, current_color);
} }
y += options_height_ + options_padding_; y += options_height_ + options_padding_;

View File

@@ -3,6 +3,7 @@
#include <algorithm> // Para max #include <algorithm> // Para max
#include "audio.h" // Para Audio #include "audio.h" // Para Audio
#include "define_buttons.h" // Para DefineButtons
#include "difficulty.h" // Para getCodeFromName, getNameFromCode #include "difficulty.h" // Para getCodeFromName, getNameFromCode
#include "input.h" // Para Input #include "input.h" // Para Input
#include "lang.h" // Para getText, getCodeFromName, getNameFromCode #include "lang.h" // Para getText, getCodeFromName, getNameFromCode
@@ -16,7 +17,6 @@
#include "section.hpp" // Para Name, name, Options, options #include "section.hpp" // Para Name, name, Options, options
#include "ui/ui_message.h" // Para UIMessage #include "ui/ui_message.h" // Para UIMessage
#include "utils.h" // Para Zone #include "utils.h" // Para Zone
#include "define_buttons.h" // Para DefineButtons
// Singleton // Singleton
ServiceMenu *ServiceMenu::instance = nullptr; ServiceMenu *ServiceMenu::instance = nullptr;
@@ -39,6 +39,10 @@ ServiceMenu::ServiceMenu()
} }
void ServiceMenu::toggle() { 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
}
playBackSound(); playBackSound();
enabled_ = !enabled_; enabled_ = !enabled_;
if (!enabled_) { if (!enabled_) {
@@ -85,9 +89,9 @@ void ServiceMenu::update() {
// Si DefineButtons ha terminado, deshabilitarlo // Si DefineButtons ha terminado, deshabilitarlo
if (define_buttons_->isEnabled() && define_buttons_->isFinished()) { if (define_buttons_->isEnabled() && define_buttons_->isFinished()) {
// Pequeño delay antes de cerrar // Pequeño delay antes de cerrar
static int finish_delay = 0; static size_t finish_delay = 0;
finish_delay++; finish_delay++;
if (finish_delay > 60) { // 1 segundo a 60 FPS if (finish_delay > DEFINE_BUTTONS_FINISH_DELAY_FRAMES) {
define_buttons_->disable(); define_buttons_->disable();
finish_delay = 0; finish_delay = 0;
} }
@@ -552,64 +556,93 @@ void ServiceMenu::handleEvent(const SDL_Event &event) {
define_buttons_->checkEvents(event); define_buttons_->checkEvents(event);
return; // No procesar otros eventos mientras DefineButtons está activo return; // No procesar otros eventos mientras DefineButtons está activo
} }
}
// Procesar eventos normales del ServiceMenu bool ServiceMenu::checkInput() {
/*switch (event.type) { if (!enabled_) {
case SDL_EVENT_KEY_DOWN: return false;
switch (event.key.key) { }
case SDLK_ESCAPE:
case SDLK_BACKSPACE: if (define_buttons_ && define_buttons_->isEnabled()) {
moveBack(); return true;
break; }
case SDLK_RETURN:
case SDLK_KP_ENTER: static auto input = Input::get();
selectOption(); // --- Teclado ---
break; // Arriba
case SDLK_UP: if (input->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
setSelectorUp(); setSelectorUp();
break; return true;
case SDLK_DOWN:
setSelectorDown();
break;
case SDLK_LEFT:
adjustOption(false);
break;
case SDLK_RIGHT:
adjustOption(true);
break;
default:
break;
} }
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: // Abajo
switch (event.gbutton.button) { if (input->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
case SDL_GAMEPAD_BUTTON_SOUTH: setSelectorDown();
case SDL_GAMEPAD_BUTTON_BACK: return true;
moveBack(); }
break;
case SDL_GAMEPAD_BUTTON_EAST: // Derecha
case SDL_GAMEPAD_BUTTON_START: if (input->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
adjustOption(true);
return true;
}
// Izquierda
if (input->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
adjustOption(false);
return true;
}
// Aceptar
if (input->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
selectOption(); selectOption();
break; return true;
case SDL_GAMEPAD_BUTTON_DPAD_UP:
setSelectorUp();
break;
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
setSelectorDown();
break;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
adjustOption(false);
break;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
adjustOption(true);
break;
default:
break;
} }
break;
default: // Atras
break; if (input->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
}*/ moveBack();
return true;
}
// --- Mandos ---
auto gamepads = input->getGamepads();
for (auto gamepad : gamepads) {
// Arriba
if (input->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
setSelectorUp();
return true;
}
// Abajo
if (input->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
setSelectorDown();
return true;
}
// Derecha
if (input->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
adjustOption(true);
return true;
}
// Izquierda
if (input->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
adjustOption(false);
return true;
}
// Aceptar
if (input->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
selectOption();
return true;
}
// Atras
if (input->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_CHECK_KEYBOARD, gamepad)) {
moveBack();
return true;
}
}
return false;
} }

View File

@@ -34,6 +34,7 @@ class ServiceMenu {
static constexpr size_t MIN_WIDTH = 240; static constexpr size_t MIN_WIDTH = 240;
static constexpr size_t MIN_GAP_OPTION_VALUE = 30; static constexpr size_t MIN_GAP_OPTION_VALUE = 30;
static constexpr size_t SETTINGS_GROUP_SIZE = 6; static constexpr size_t SETTINGS_GROUP_SIZE = 6;
static constexpr size_t DEFINE_BUTTONS_FINISH_DELAY_FRAMES = 60 * 3; // 3 segundo a 60 FPS
// --- Métodos de singleton --- // --- Métodos de singleton ---
static void init(); static void init();
@@ -56,8 +57,9 @@ class ServiceMenu {
void moveBack(); void moveBack();
void checkEvents(const SDL_Event &event); // Nuevo método para eventos void checkEvents(const SDL_Event &event); // Nuevo método para eventos
// NUEVO: Método para manejar eventos (llamado desde GlobalEvents) // --- Método para manejar eventos (llamado desde GlobalEvents) ---
void handleEvent(const SDL_Event &event); void handleEvent(const SDL_Event &event);
bool checkInput();
// NUEVO: Getter para saber si DefineButtons está activo // NUEVO: Getter para saber si DefineButtons está activo
[[nodiscard]] auto isDefiningButtons() const -> bool { [[nodiscard]] auto isDefiningButtons() const -> bool {

View File

@@ -36,17 +36,20 @@ void WindowMessage::render() {
SDL_RenderRect(renderer, &rect_); SDL_RenderRect(renderer, &rect_);
float current_y = rect_.y + config_.padding; float current_y = rect_.y + config_.padding;
float available_width = getAvailableTextWidth();
// Dibujar título si existe // Dibujar título si existe
if (!title_.empty()) { if (!title_.empty()) {
std::string visible_title = getTruncatedText(title_, available_width);
text_renderer_->writeStyle( text_renderer_->writeStyle(
rect_.x + rect_.w / 2.0f, rect_.x + rect_.w / 2.0f,
current_y, current_y,
title_, visible_title,
title_style_); title_style_);
current_y += text_renderer_->getCharacterSize() + config_.title_separator_spacing; current_y += text_renderer_->getCharacterSize() + config_.title_separator_spacing;
// Línea separadora debajo del título // Línea separadora debajo del título (solo si hay título visible)
if (!visible_title.empty()) {
SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g,
config_.border_color.b, config_.border_color.a); config_.border_color.b, config_.border_color.a);
SDL_RenderLine(renderer, SDL_RenderLine(renderer,
@@ -55,14 +58,18 @@ void WindowMessage::render() {
rect_.x + rect_.w - config_.padding, rect_.x + rect_.w - config_.padding,
current_y - config_.title_separator_spacing / 2.0f); current_y - config_.title_separator_spacing / 2.0f);
} }
}
// Dibujar textos // Dibujar textos
for (const auto& text : texts_) { for (const auto& text : texts_) {
std::string visible_text = getTruncatedText(text, available_width);
if (!visible_text.empty()) {
text_renderer_->writeStyle( text_renderer_->writeStyle(
rect_.x + rect_.w / 2.0f, rect_.x + rect_.w / 2.0f,
current_y, current_y,
text, visible_text,
text_style_); text_style_);
}
current_y += text_renderer_->getCharacterSize() + config_.line_spacing; current_y += text_renderer_->getCharacterSize() + config_.line_spacing;
} }
} }
@@ -285,3 +292,45 @@ auto WindowMessage::easeOut(float t) const -> float {
// Función de suavizado ease-out cuadrática // Función de suavizado ease-out cuadrática
return 1.0f - (1.0f - t) * (1.0f - t); return 1.0f - (1.0f - t) * (1.0f - t);
} }
auto WindowMessage::getAvailableTextWidth() const -> float {
// Ancho disponible = ancho total - padding en ambos lados
return rect_.w - (config_.padding * 2.0f);
}
auto WindowMessage::getTruncatedText(const std::string& text, float available_width) const -> std::string {
if (text.empty()) {
return text;
}
// Si el texto completo cabe, devolverlo tal como está
int text_width = text_renderer_->length(text, -2);
if (text_width <= available_width) {
return text;
}
// Si no hay espacio suficiente, devolver string vacío
if (available_width < 10.0f) { // Mínimo espacio para al menos un carácter
return "";
}
// Buscar cuántos caracteres caben usando búsqueda binaria
int left = 0;
int right = text.length();
int best_length = 0;
while (left <= right) {
int mid = (left + right) / 2;
std::string partial = text.substr(0, mid);
int partial_width = text_renderer_->length(partial, -2);
if (partial_width <= available_width) {
best_length = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return text.substr(0, best_length);
}

View File

@@ -169,6 +169,10 @@ class WindowMessage {
// Función de suavizado (ease-out) // Función de suavizado (ease-out)
[[nodiscard]] auto easeOut(float t) const -> float; [[nodiscard]] auto easeOut(float t) const -> float;
// Métodos para manejo de texto durante animación
[[nodiscard]] auto getTruncatedText(const std::string& text, float available_width) const -> std::string;
[[nodiscard]] auto getAvailableTextWidth() const -> float;
[[nodiscard]] auto calculateContentHeight() const -> float; [[nodiscard]] auto calculateContentHeight() const -> float;
[[nodiscard]] auto calculateContentWidth() const -> float; [[nodiscard]] auto calculateContentWidth() const -> float;
[[nodiscard]] auto getScreenWidth() const -> float; [[nodiscard]] auto getScreenWidth() const -> float;