diff --git a/data/lang/ba_BA.json b/data/lang/ba_BA.json index 5fa3f17..0779d74 100644 --- a/data/lang/ba_BA.json +++ b/data/lang/ba_BA.json @@ -6,7 +6,7 @@ "[DEFINE_BUTTONS] TITLE": "define buttons title", "[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] START": "Start", "[DEFINE_BUTTONS] SERVICE_MENU": "Menu de servei", diff --git a/source/define_buttons.cpp b/source/define_buttons.cpp index b7b54bb..2a5971a 100644 --- a/source/define_buttons.cpp +++ b/source/define_buttons.cpp @@ -24,7 +24,7 @@ DefineButtons::DefineButtons() // Crear la ventana de mensaje 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.title_color = Color{100, 150, 200, 255}; // Titulo azul claro config.text_color = Color{220, 220, 220, 255}; // Texto gris claro @@ -157,8 +157,8 @@ void DefineButtons::checkEnd() { if (window_message_) { window_message_->clearTexts(); window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURATION_COMPLETE")); - window_message_->autoSize(); - window_message_->centerOnScreen(); + //window_message_->autoSize(); + //window_message_->centerOnScreen(); } // Se deshabilitará desde el ServiceMenu después de un breve delay @@ -178,26 +178,9 @@ void DefineButtons::updateWindowMessage() { window_message_->clearTexts(); 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 std::string instruction = Lang::getText("[DEFINE_BUTTONS] PRESS_BUTTON_FOR") + ":"; window_message_->addText(instruction); 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); - } - }*/ } } \ No newline at end of file diff --git a/source/global_events.cpp b/source/global_events.cpp index 7005dc4..3bc9af9 100644 --- a/source/global_events.cpp +++ b/source/global_events.cpp @@ -5,7 +5,7 @@ #include "input.h" #include "mouse.h" // Para handleEvent #include "screen.h" -#include "section.hpp" // Para Name, Options, name, options +#include "section.hpp" // Para Name, Options, name, options #include "ui/service_menu.h" // Para ServiceMenu namespace GlobalEvents { @@ -30,16 +30,7 @@ void check(const SDL_Event &event) { break; } - if (ServiceMenu::get()->isEnabled()) { - ServiceMenu::get()->handleEvent(event); // Método que vamos a crear - - // Si DefineButtons está activo, no procesar más eventos - if (ServiceMenu::get()->isDefiningButtons()) { - return; - } - } - - + ServiceMenu::get()->handleEvent(event); Mouse::handleEvent(event); static auto *input_ = Input::get(); diff --git a/source/global_inputs.cpp b/source/global_inputs.cpp index 2975b9b..c8f96ff 100644 --- a/source/global_inputs.cpp +++ b/source/global_inputs.cpp @@ -183,95 +183,6 @@ auto checkServiceButton() -> bool { 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 auto checkInputs() -> bool { // Teclado @@ -370,7 +281,7 @@ auto check() -> bool { if (checkServiceButton()) { return true; } - if (checkServiceInputs()) { + if (ServiceMenu::get()->checkInput()) { return true; } if (checkInputs()) { diff --git a/source/options.cpp b/source/options.cpp index 73798ae..5358ae4 100644 --- a/source/options.cpp +++ b/source/options.cpp @@ -296,22 +296,28 @@ auto getGamepadInfo(Player::Id player_id) -> std::string { // Asigna los mandos físicos basándose en la configuración actual. 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(); - // 2. Reiniciamos las asignaciones actuales y guardamos los datos deseados de la config. + // 2. Reiniciamos las asignaciones actuales. std::array desired_paths; for (size_t i = 0; i < MAX_PLAYERS; ++i) { desired_paths[i] = gamepads_[i].path; 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> 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); + + // Pasada 2: Asigna los mandos restantes a los jugadores libres. 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 --- @@ -322,15 +328,20 @@ void GamepadManager::assignGamepadsByPath( for (size_t i = 0; i < MAX_PLAYERS; ++i) { const std::string& desired_path = desired_paths[i]; if (desired_path.empty()) { - continue; // No hay ruta guardada para este slot. + continue; // No hay ruta guardada para este slot. } // Buscamos un mando físico que coincida con la ruta y no esté ya asignado. for (const auto& physical_gamepad : physical_gamepads) { if (physical_gamepad->path == desired_path && !isGamepadAssigned(physical_gamepad, assigned_instances)) { + + // Asignamos y actualizamos TODOS los datos. 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); - 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. // Devuelve 'true' si ya ha sido asignado, 'false' en caso contrario. auto GamepadManager::isGamepadAssigned( diff --git a/source/options.h b/source/options.h index c290df4..fe004bd 100644 --- a/source/options.h +++ b/source/options.h @@ -238,6 +238,7 @@ class GamepadManager { void assignRemainingGamepads( const std::vector>& physical_gamepads, std::vector>& assigned_instances); + void clearUnassignedGamepadSlots(); [[nodiscard]] static auto isGamepadAssigned( const std::shared_ptr& physical_gamepad, const std::vector>& assigned_instances) -> bool; diff --git a/source/ui/menu_renderer.cpp b/source/ui/menu_renderer.cpp index 54d4fb8..0bde407 100644 --- a/source/ui/menu_renderer.cpp +++ b/source/ui/menu_renderer.cpp @@ -59,11 +59,6 @@ void MenuRenderer::render(const ServiceMenu *menu_state) { 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); 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); @@ -72,11 +67,6 @@ void MenuRenderer::render(const ServiceMenu *menu_state) { const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2); 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); } y += options_height_ + options_padding_; diff --git a/source/ui/service_menu.cpp b/source/ui/service_menu.cpp index d08266c..510f244 100644 --- a/source/ui/service_menu.cpp +++ b/source/ui/service_menu.cpp @@ -2,21 +2,21 @@ #include // Para max -#include "audio.h" // Para Audio -#include "difficulty.h" // Para getCodeFromName, getNameFromCode -#include "input.h" // Para Input -#include "lang.h" // Para getText, getCodeFromName, getNameFromCode -#include "menu_option.h" // Para MenuOption, ListOption, ActionOption, BoolOption, FolderOption, IntOption -#include "menu_renderer.h" // Para MenuRenderer -#include "options.h" // Para PendingChanges, pending_changes, checkPendingChanges, GamepadManager, Video, gamepad_manager, video, Audio, Settings, audio, settings, Gamepad, Window, window, Music, Sound -#include "param.h" // Para Param, param, ParamGame, ParamServiceMenu -#include "player.h" // Para Player -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.hpp" // Para Name, name, Options, options -#include "ui/ui_message.h" // Para UIMessage -#include "utils.h" // Para Zone -#include "define_buttons.h" // Para DefineButtons +#include "audio.h" // Para Audio +#include "define_buttons.h" // Para DefineButtons +#include "difficulty.h" // Para getCodeFromName, getNameFromCode +#include "input.h" // Para Input +#include "lang.h" // Para getText, getCodeFromName, getNameFromCode +#include "menu_option.h" // Para MenuOption, ListOption, ActionOption, BoolOption, FolderOption, IntOption +#include "menu_renderer.h" // Para MenuRenderer +#include "options.h" // Para PendingChanges, pending_changes, checkPendingChanges, GamepadManager, Video, gamepad_manager, video, Audio, Settings, audio, settings, Gamepad, Window, window, Music, Sound +#include "param.h" // Para Param, param, ParamGame, ParamServiceMenu +#include "player.h" // Para Player +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.hpp" // Para Name, name, Options, options +#include "ui/ui_message.h" // Para UIMessage +#include "utils.h" // Para Zone // Singleton ServiceMenu *ServiceMenu::instance = nullptr; @@ -39,6 +39,10 @@ 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 + } + playBackSound(); enabled_ = !enabled_; if (!enabled_) { @@ -81,13 +85,13 @@ void ServiceMenu::update() { // Actualizar DefineButtons if (define_buttons_) { define_buttons_->update(); - + // Si DefineButtons ha terminado, deshabilitarlo if (define_buttons_->isEnabled() && define_buttons_->isFinished()) { // Pequeño delay antes de cerrar - static int finish_delay = 0; + static size_t finish_delay = 0; finish_delay++; - if (finish_delay > 60) { // 1 segundo a 60 FPS + if (finish_delay > DEFINE_BUTTONS_FINISH_DELAY_FRAMES) { define_buttons_->disable(); finish_delay = 0; } @@ -283,7 +287,7 @@ void ServiceMenu::initializeOptions() { }, [this]() { // Acción: configurar botones del mando del jugador 1 - auto* gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER1); + auto *gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER1); if (gamepad && gamepad->instance) { define_buttons_->enable(gamepad); } @@ -301,7 +305,7 @@ void ServiceMenu::initializeOptions() { }, [this]() { // Acción: configurar botones del mando del jugador 2 - auto* gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER2); + auto *gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER2); if (gamepad && gamepad->instance) { define_buttons_->enable(gamepad); } @@ -550,66 +554,95 @@ void ServiceMenu::handleEvent(const SDL_Event &event) { // Si DefineButtons está activo, que maneje todos los eventos if (define_buttons_ && define_buttons_->isEnabled()) { define_buttons_->checkEvents(event); - return; // No procesar otros eventos mientras DefineButtons está activo + return; // No procesar otros eventos mientras DefineButtons está activo + } +} + +bool ServiceMenu::checkInput() { + if (!enabled_) { + return false; } - // Procesar eventos normales del ServiceMenu - /*switch (event.type) { - case SDL_EVENT_KEY_DOWN: - switch (event.key.key) { - case SDLK_ESCAPE: - case SDLK_BACKSPACE: - moveBack(); - break; - case SDLK_RETURN: - case SDLK_KP_ENTER: - selectOption(); - break; - case SDLK_UP: - setSelectorUp(); - break; - 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: - switch (event.gbutton.button) { - case SDL_GAMEPAD_BUTTON_SOUTH: - case SDL_GAMEPAD_BUTTON_BACK: - moveBack(); - break; - case SDL_GAMEPAD_BUTTON_EAST: - case SDL_GAMEPAD_BUTTON_START: - selectOption(); - break; - 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: - break; - }*/ + if (define_buttons_ && define_buttons_->isEnabled()) { + return true; + } + + static auto input = Input::get(); + // --- Teclado --- + // Arriba + if (input->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) { + setSelectorUp(); + return true; + } + + // Abajo + if (input->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) { + setSelectorDown(); + return true; + } + + // Derecha + 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(); + return true; + } + + // Atras + 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; } \ No newline at end of file diff --git a/source/ui/service_menu.h b/source/ui/service_menu.h index 068140a..1c1f8d9 100644 --- a/source/ui/service_menu.h +++ b/source/ui/service_menu.h @@ -34,6 +34,7 @@ class ServiceMenu { static constexpr size_t MIN_WIDTH = 240; static constexpr size_t MIN_GAP_OPTION_VALUE = 30; 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 --- static void init(); @@ -56,8 +57,9 @@ class ServiceMenu { void moveBack(); 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); + bool checkInput(); // NUEVO: Getter para saber si DefineButtons está activo [[nodiscard]] auto isDefiningButtons() const -> bool { diff --git a/source/ui/window_message.cpp b/source/ui/window_message.cpp index 11f6f88..c26183f 100644 --- a/source/ui/window_message.cpp +++ b/source/ui/window_message.cpp @@ -36,33 +36,40 @@ void WindowMessage::render() { SDL_RenderRect(renderer, &rect_); float current_y = rect_.y + config_.padding; + float available_width = getAvailableTextWidth(); // Dibujar título si existe if (!title_.empty()) { + std::string visible_title = getTruncatedText(title_, available_width); text_renderer_->writeStyle( rect_.x + rect_.w / 2.0f, current_y, - title_, + visible_title, title_style_); current_y += text_renderer_->getCharacterSize() + config_.title_separator_spacing; - // Línea separadora debajo del título - SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, - config_.border_color.b, config_.border_color.a); - SDL_RenderLine(renderer, - rect_.x + config_.padding, - current_y - config_.title_separator_spacing / 2.0f, - rect_.x + rect_.w - config_.padding, - current_y - config_.title_separator_spacing / 2.0f); + // 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, + config_.border_color.b, config_.border_color.a); + SDL_RenderLine(renderer, + rect_.x + config_.padding, + current_y - config_.title_separator_spacing / 2.0f, + rect_.x + rect_.w - config_.padding, + current_y - config_.title_separator_spacing / 2.0f); + } } // Dibujar textos for (const auto& text : texts_) { - text_renderer_->writeStyle( - rect_.x + rect_.w / 2.0f, - current_y, - text, - text_style_); + std::string visible_text = getTruncatedText(text, available_width); + if (!visible_text.empty()) { + text_renderer_->writeStyle( + rect_.x + rect_.w / 2.0f, + current_y, + visible_text, + text_style_); + } current_y += text_renderer_->getCharacterSize() + config_.line_spacing; } } @@ -284,4 +291,46 @@ void WindowMessage::updateAnimation(float delta_time) { auto WindowMessage::easeOut(float t) const -> float { // Función de suavizado ease-out cuadrática 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); } \ No newline at end of file diff --git a/source/ui/window_message.h b/source/ui/window_message.h index f7ac318..b89aef5 100644 --- a/source/ui/window_message.h +++ b/source/ui/window_message.h @@ -169,6 +169,10 @@ class WindowMessage { // Función de suavizado (ease-out) [[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 calculateContentWidth() const -> float; [[nodiscard]] auto getScreenWidth() const -> float;