diff --git a/source/asset.cpp b/source/asset.cpp index 9d5a776..08bec10 100644 --- a/source/asset.cpp +++ b/source/asset.cpp @@ -1,10 +1,11 @@ #include "asset.h" #include -#include -#include + #include #include +#include +#include #include "utils.h" @@ -27,13 +28,14 @@ auto Asset::get() -> Asset * { void Asset::addToMap(const std::string &file_path, Type type, bool required, bool absolute) { std::string full_path = absolute ? file_path : executable_path_ + file_path; std::string filename = getFileName(full_path); - + // Verificar si ya existe el archivo if (file_list_.find(filename) != file_list_.end()) { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Warning: Asset '%s' already exists, overwriting", filename.c_str()); + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "Warning: Asset '%s' already exists, overwriting", + filename.c_str()); } - + file_list_.emplace(filename, Item{std::move(full_path), type, required}); } @@ -47,76 +49,81 @@ void Asset::add(const std::string &file_path, Type type, bool required, bool abs void Asset::loadFromFile(const std::string &config_file_path, const std::string &prefix, const std::string &system_folder) { std::ifstream file(config_file_path); if (!file.is_open()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Error: Cannot open config file: %s", config_file_path.c_str()); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Error: Cannot open config file: %s", + config_file_path.c_str()); return; } std::string line; int line_number = 0; - + while (std::getline(file, line)) { ++line_number; - + // Limpiar espacios en blanco al principio y final line.erase(0, line.find_first_not_of(" \t\r")); line.erase(line.find_last_not_of(" \t\r") + 1); - + // DEBUG: mostrar línea leída (opcional, puedes comentar esta línea) // SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Line %d: '%s'", line_number, line.c_str()); - + // Ignorar líneas vacías y comentarios if (line.empty() || line[0] == '#' || line[0] == ';') { continue; } - + // Dividir la línea por el separador '|' std::vector parts; std::istringstream iss(line); std::string part; - + while (std::getline(iss, part, '|')) { parts.push_back(part); } - + // Verificar que tenemos al menos tipo y ruta if (parts.size() < 2) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Warning: Malformed line %d in config file (insufficient fields)", line_number); + "Warning: Malformed line %d in config file (insufficient fields)", + line_number); continue; } - + try { std::string type_str = parts[0]; std::string path = parts[1]; - + // Valores por defecto bool required = true; bool absolute = false; - + // Si hay opciones en el tercer campo, parsearlas if (parts.size() >= 3) { parseOptions(parts[2], required, absolute); } - + // Reemplazar variables en la ruta path = replaceVariables(path, prefix, system_folder); - + // Parsear el tipo de asset Type type = parseAssetType(type_str); - + // Añadir al mapa addToMap(path, type, required, absolute); - + } catch (const std::exception &e) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Error parsing line %d in config file: %s", line_number, e.what()); + "Error parsing line %d in config file: %s", + line_number, + e.what()); } } - + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Loaded %d assets from config file", static_cast(file_list_.size())); - + "Loaded %d assets from config file", + static_cast(file_list_.size())); + file.close(); } @@ -126,7 +133,7 @@ auto Asset::get(const std::string &filename) const -> std::string { if (it != file_list_.end()) { return it->second.file; } - + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: file %s not found", filename.c_str()); return ""; } @@ -139,26 +146,27 @@ auto Asset::exists(const std::string &filename) const -> bool { // Comprueba que existen todos los elementos auto Asset::check() const -> bool { bool success = true; - + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES"); - + // Agrupar por tipo para mostrar organizado - std::unordered_map> by_type; - + std::unordered_map> by_type; + for (const auto &[filename, item] : file_list_) { if (item.required) { by_type[item.type].push_back(&item); } } - + // Verificar por tipo for (int type = 0; type < static_cast(Type::SIZE); ++type) { Type asset_type = static_cast(type); - + if (by_type.find(asset_type) != by_type.end()) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "\n>> %s FILES", getTypeName(asset_type).c_str()); - + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "\n>> %s FILES", + getTypeName(asset_type).c_str()); + bool type_success = true; for (const auto *item : by_type[asset_type]) { if (!checkFile(item->file)) { @@ -166,20 +174,20 @@ auto Asset::check() const -> bool { type_success = false; } } - + if (type_success) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, " All files are OK."); } } } - + // Resultado if (success) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES COMPLETED.\n"); } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES FAILED.\n"); } - + return success; } @@ -188,12 +196,13 @@ auto Asset::checkFile(const std::string &path) -> bool { std::ifstream file(path); bool success = file.good(); file.close(); - + if (!success) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Checking file: %s [ ERROR ]", getFileName(path).c_str()); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Checking file: %s [ ERROR ]", + getFileName(path).c_str()); } - + return success; } @@ -233,50 +242,60 @@ auto Asset::parseAssetType(const std::string &type_str) -> Type { // Devuelve el nombre del tipo de recurso auto Asset::getTypeName(Type type) -> std::string { switch (type) { - case Type::BITMAP: return "BITMAP"; - case Type::MUSIC: return "MUSIC"; - case Type::SOUND: return "SOUND"; - case Type::FONT: return "FONT"; - case Type::LANG: return "LANG"; - case Type::DATA: return "DATA"; - case Type::DEMODATA: return "DEMODATA"; - case Type::ANIMATION: return "ANIMATION"; - case Type::PALETTE: return "PALETTE"; - default: return "ERROR"; + case Type::BITMAP: + return "BITMAP"; + case Type::MUSIC: + return "MUSIC"; + case Type::SOUND: + return "SOUND"; + case Type::FONT: + return "FONT"; + case Type::LANG: + return "LANG"; + case Type::DATA: + return "DATA"; + case Type::DEMODATA: + return "DEMODATA"; + case Type::ANIMATION: + return "ANIMATION"; + case Type::PALETTE: + return "PALETTE"; + default: + return "ERROR"; } } // Devuelve la lista de recursos de un tipo auto Asset::getListByType(Type type) const -> std::vector { std::vector list; - + for (const auto &[filename, item] : file_list_) { if (item.type == type) { list.push_back(item.file); } } - + return list; } // Reemplaza variables en las rutas auto Asset::replaceVariables(const std::string &path, const std::string &prefix, const std::string &system_folder) -> std::string { std::string result = path; - + // Reemplazar ${PREFIX} size_t pos = 0; while ((pos = result.find("${PREFIX}", pos)) != std::string::npos) { result.replace(pos, 9, prefix); // 9 = longitud de "${PREFIX}" pos += prefix.length(); } - + // Reemplazar ${SYSTEM_FOLDER} pos = 0; while ((pos = result.find("${SYSTEM_FOLDER}", pos)) != std::string::npos) { result.replace(pos, 16, system_folder); // 16 = longitud de "${SYSTEM_FOLDER}" pos += system_folder.length(); } - + return result; } @@ -288,12 +307,12 @@ auto Asset::parseOptions(const std::string &options, bool &required, bool &absol std::istringstream iss(options); std::string option; - + while (std::getline(iss, option, ',')) { // Eliminar espacios option.erase(0, option.find_first_not_of(" \t")); option.erase(option.find_last_not_of(" \t") + 1); - + if (option == "optional") { required = false; } else if (option == "absolute") { diff --git a/source/asset.h b/source/asset.h index 1f44080..7d0f59f 100644 --- a/source/asset.h +++ b/source/asset.h @@ -2,8 +2,8 @@ #include #include -#include #include +#include // Clase Asset: gestor optimizado de recursos (singleton) class Asset { @@ -32,7 +32,7 @@ class Asset { // --- Métodos para la gestión de recursos --- void add(const std::string &file_path, Type type, bool required = true, bool absolute = false); void loadFromFile(const std::string &config_file_path, const std::string &prefix = "", const std::string &system_folder = ""); // Con soporte para variables - [[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original + [[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original [[nodiscard]] auto check() const -> bool; [[nodiscard]] auto getListByType(Type type) const -> std::vector; [[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia @@ -40,17 +40,17 @@ class Asset { private: // --- Estructura interna para almacenar información de cada recurso --- struct Item { - std::string file; // Ruta completa del archivo (mantener nombre original) - Type type; // Tipo de recurso - bool required; // Indica si el archivo es obligatorio + std::string file; // Ruta completa del archivo (mantener nombre original) + Type type; // Tipo de recurso + bool required; // Indica si el archivo es obligatorio - Item(std::string path, Type asset_type, bool is_required) - : file(std::move(path)), type(asset_type), required(is_required) {} + Item(std::string path, Type asset_type, bool is_required) + : file(std::move(path)), type(asset_type), required(is_required) {} }; // --- Variables internas --- std::unordered_map file_list_; // Mapa para búsqueda O(1) (mantener nombre original) - std::string executable_path_; // Ruta del ejecutable + std::string executable_path_; // Ruta del ejecutable // --- Métodos internos --- [[nodiscard]] static auto checkFile(const std::string &path) -> bool; diff --git a/source/director.cpp b/source/director.cpp index a141dc9..3236eeb 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -163,7 +163,7 @@ void Director::loadAssets() { // Cargar la configuración de assets (también aplicar el prefijo al archivo de configuración) std::string config_path = executable_path_ + prefix + "/data/config/assets.txt"; Asset::get()->loadFromFile(config_path, prefix, system_folder_); - + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Assets configuration loaded successfully"); // Si falta algun fichero, sale del programa diff --git a/source/options.h b/source/options.h index 6720512..32a88a0 100644 --- a/source/options.h +++ b/source/options.h @@ -247,7 +247,7 @@ class GamepadManager { private: static constexpr std::string_view UNASSIGNED_TEXT = "---"; static constexpr size_t MAX_PLAYERS = 2; - std::array gamepads_; // Punteros a las estructuras de mandos de Options + std::array gamepads_; // Punteros a las estructuras de mandos de Options std::vector> players_; // Punteros a los jugadores // Convierte Player::Id a índice del array diff --git a/source/player.cpp b/source/player.cpp index 79925f6..19364ef 100644 --- a/source/player.cpp +++ b/source/player.cpp @@ -460,26 +460,21 @@ void Player::setAnimation() { player_sprite_->setFlip(flipMode); break; } - case State::WAITING: player_sprite_->setCurrentAnimation("hello"); break; - case State::ROLLING: case State::CONTINUE_TIME_OUT: player_sprite_->setCurrentAnimation("rolling"); break; - case State::LYING_ON_THE_FLOOR_FOREVER: case State::ENTERING_NAME: case State::CONTINUE: player_sprite_->setCurrentAnimation("dead"); break; - case State::CELEBRATING: player_sprite_->setCurrentAnimation("celebration"); break; - default: break; } diff --git a/source/ui/menu_renderer.cpp b/source/ui/menu_renderer.cpp index fc17607..cdea0e4 100644 --- a/source/ui/menu_renderer.cpp +++ b/source/ui/menu_renderer.cpp @@ -13,25 +13,33 @@ // --- 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; + 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; +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; + type = Type::SHOWING; + target_width = to_w; + target_height = to_h; elapsed = 0.0F; active = true; } -void MenuRenderer::ShowHideAnimation::startHide() { type = Type::HIDING; +void MenuRenderer::ShowHideAnimation::startHide() { + type = Type::HIDING; elapsed = 0.0F; active = true; } -void MenuRenderer::ShowHideAnimation::stop() { type = Type::NONE; active = false; +void MenuRenderer::ShowHideAnimation::stop() { + type = Type::NONE; + active = false; elapsed = 0.0F; } @@ -62,7 +70,7 @@ void MenuRenderer::render(const ServiceMenu *menu_state) { SDL_SetRenderDrawColor(Screen::get()->getRenderer(), BORDER_COLOR.r, BORDER_COLOR.g, BORDER_COLOR.b, 255); 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 @@ -101,7 +109,7 @@ void MenuRenderer::render(const ServiceMenu *menu_state) { void MenuRenderer::update(const ServiceMenu *menu_state) { float delta_time = 1.0F / 60.0F; // Asumiendo 60 FPS updateAnimations(delta_time); - + if (visible_) { updateColorCounter(); param.service_menu.selected_color = getAnimatedSelectedColor(); @@ -110,7 +118,7 @@ void MenuRenderer::update(const ServiceMenu *menu_state) { // --- Nuevos métodos de control --- -void MenuRenderer::show(const ServiceMenu* menu_state) { +void MenuRenderer::show(const ServiceMenu *menu_state) { if (visible_) { return; } @@ -118,13 +126,13 @@ void MenuRenderer::show(const ServiceMenu* menu_state) { // 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; @@ -194,14 +202,13 @@ auto MenuRenderer::calculateNewRect(const ServiceMenu *menu_state) -> SDL_FRect 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_); - + 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); @@ -219,7 +226,7 @@ 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(); @@ -242,7 +249,7 @@ void MenuRenderer::updateAnimations(float 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; @@ -286,7 +293,6 @@ void MenuRenderer::updateResizeAnimation(float delta_time) { options_y_ = rect_.y + upper_height_ + lower_padding_; } - void MenuRenderer::updatePosition() { switch (position_mode_) { case PositionMode::CENTERED: diff --git a/source/ui/menu_renderer.h b/source/ui/menu_renderer.h index 8fc84c2..d8f388f 100644 --- a/source/ui/menu_renderer.h +++ b/source/ui/menu_renderer.h @@ -37,7 +37,7 @@ class MenuRenderer { // --- 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); @@ -77,27 +77,29 @@ class MenuRenderer { // --- 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; + 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(); + 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; + 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(); + void startShow(float to_w, float to_h); + void startHide(); + void stop(); } show_hide_animation_; // --- Anchos precalculados --- @@ -109,7 +111,7 @@ class MenuRenderer { auto calculateNewRect(const ServiceMenu *menu_state) -> SDL_FRect; void resize(const ServiceMenu *menu_state); void setSize(const ServiceMenu *menu_state); - + void updateAnimations(float delta_time); void updateResizeAnimation(float delta_time); void updateShowHideAnimation(float delta_time); diff --git a/source/ui/service_menu.cpp b/source/ui/service_menu.cpp index 4d71c94..b93d38b 100644 --- a/source/ui/service_menu.cpp +++ b/source/ui/service_menu.cpp @@ -49,7 +49,7 @@ void ServiceMenu::toggle() { } playBackSound(); - + if (!enabled_) { // Si está cerrado, abrir reset(); Options::gamepad_manager.assignAndLinkGamepads(); diff --git a/source/ui/window_message.cpp b/source/ui/window_message.cpp index d083c09..1192029 100644 --- a/source/ui/window_message.cpp +++ b/source/ui/window_message.cpp @@ -26,13 +26,11 @@ void WindowMessage::render() { SDL_Renderer* renderer = Screen::get()->getRenderer(); // Dibujar fondo con transparencia - SDL_SetRenderDrawColor(renderer, config_.bg_color.r, config_.bg_color.g, - config_.bg_color.b, config_.bg_color.a); + SDL_SetRenderDrawColor(renderer, config_.bg_color.r, config_.bg_color.g, config_.bg_color.b, config_.bg_color.a); SDL_RenderFillRect(renderer, &rect_); // Dibujar borde - SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, - config_.border_color.b, config_.border_color.a); + SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, config_.border_color.b, config_.border_color.a); SDL_RenderRect(renderer, &rect_); // Solo mostrar contenido si no estamos en animación de show/hide @@ -54,8 +52,7 @@ void WindowMessage::render() { // 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_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, @@ -91,34 +88,34 @@ void WindowMessage::update() { void WindowMessage::show() { if (visible_) { - return; // Ya visible + return; // Ya visible } - + visible_ = true; ensureTextFits(); - + // Detener cualquier animación anterior resize_animation_.stop(); - + // Iniciar animación de mostrar desde tamaño 0 show_hide_animation_.startShow(rect_.w, rect_.h); rect_.w = 0.0F; rect_.h = 0.0F; - updatePosition(); // Reposicionar con tamaño 0 + updatePosition(); // Reposicionar con tamaño 0 } void WindowMessage::hide() { if (!visible_) { - return; // Ya oculto + return; // Ya oculto } - + // Detener cualquier animación anterior resize_animation_.stop(); - + // Guardar el tamaño actual para la animación show_hide_animation_.target_width = rect_.w; show_hide_animation_.target_height = rect_.h; - + // Iniciar animación de ocultar hacia tamaño 0 show_hide_animation_.startHide(); } @@ -158,7 +155,7 @@ void WindowMessage::setPosition(float x, float y, PositionMode mode) { void WindowMessage::setSize(float width, float height) { rect_.w = width; rect_.h = height; - updatePosition(); // Reposicionar después de cambiar el tamaño + updatePosition(); // Reposicionar después de cambiar el tamaño } void WindowMessage::centerOnScreen() { @@ -167,18 +164,18 @@ void WindowMessage::centerOnScreen() { void WindowMessage::autoSize() { if (show_hide_animation_.active) { - return; // No redimensionar durante show/hide + return; // No redimensionar durante show/hide } - + if (resize_animation_.active) { - resize_animation_.stop(); // Detener animación anterior + resize_animation_.stop(); // Detener animación anterior } - + float old_width = rect_.w; float old_height = rect_.h; - + calculateAutoSize(); - + // Solo animar si hay cambio en el tamaño y la ventana está visible if (visible_ && (old_width != rect_.w || old_height != rect_.h)) { resize_animation_.start(old_width, old_height, rect_.w, rect_.h); @@ -186,21 +183,19 @@ void WindowMessage::autoSize() { rect_.w = old_width; rect_.h = old_height; } else { - updatePosition(); // Reposicionar después de ajustar el tamaño + updatePosition(); // Reposicionar después de ajustar el tamaño } } void WindowMessage::updateStyles() { - title_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.title_color, - config_.title_color, 0, -2); - text_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.text_color, - config_.text_color, 0, -2); + title_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.title_color, config_.title_color, 0, -2); + text_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.text_color, config_.text_color, 0, -2); } void WindowMessage::updatePosition() { switch (position_mode_) { case PositionMode::CENTERED: - rect_.x = anchor_.x- rect_.w / 2.0F; + rect_.x = anchor_.x - rect_.w / 2.0F; rect_.y = anchor_.y - rect_.h / 2.0F; break; case PositionMode::FIXED: @@ -217,10 +212,10 @@ void WindowMessage::updatePosition() { void WindowMessage::ensureTextFits() { float required_width = calculateContentWidth() + (config_.padding * 2) + config_.text_safety_margin; float required_height = calculateContentHeight() + (config_.padding * 2) + config_.text_safety_margin; - + // Verificar si el tamaño actual es suficiente if (rect_.w < required_width || rect_.h < required_height) { - autoSize(); // Recalcular tamaño automáticamente + autoSize(); // Recalcular tamaño automáticamente } } @@ -239,7 +234,7 @@ void WindowMessage::calculateAutoSize() { // Aplicar límites máximos basados en el tamaño de pantalla float max_width = getScreenWidth() * config_.max_width_ratio; float max_height = getScreenHeight() * config_.max_height_ratio; - + rect_.w = std::min(rect_.w, max_width); rect_.h = std::min(rect_.h, max_height); } @@ -264,7 +259,7 @@ auto WindowMessage::calculateContentHeight() const -> float { } auto WindowMessage::calculateContentWidth() const -> float { - float max_width = config_.min_width - (config_.padding * 2); // Ancho mínimo sin padding + float max_width = config_.min_width - (config_.padding * 2); // Ancho mínimo sin padding // Ancho del título if (!title_.empty()) { @@ -299,7 +294,7 @@ void WindowMessage::updateAnimation(float delta_time) { if (show_hide_animation_.active) { updateShowHideAnimation(delta_time); } - + if (resize_animation_.active) { updateResizeAnimation(delta_time); } @@ -309,9 +304,9 @@ void WindowMessage::updateShowHideAnimation(float delta_time) { if (!show_hide_animation_.active) { return; } - + show_hide_animation_.elapsed += delta_time; - + if (show_hide_animation_.isFinished(config_.animation_duration)) { // Animación terminada if (show_hide_animation_.type == ShowHideAnimation::Type::SHOWING) { @@ -324,13 +319,13 @@ void WindowMessage::updateShowHideAnimation(float delta_time) { rect_.h = 0.0F; visible_ = false; } - + show_hide_animation_.stop(); updatePosition(); } else { // Interpolar el tamaño float progress = easeOut(show_hide_animation_.getProgress(config_.animation_duration)); - + if (show_hide_animation_.type == ShowHideAnimation::Type::SHOWING) { // Crecer desde 0 hasta el tamaño objetivo rect_.w = show_hide_animation_.target_width * progress; @@ -340,8 +335,8 @@ void WindowMessage::updateShowHideAnimation(float delta_time) { rect_.w = show_hide_animation_.target_width * (1.0F - progress); rect_.h = show_hide_animation_.target_height * (1.0F - progress); } - - updatePosition(); // Mantener la posición centrada durante la animación + + updatePosition(); // Mantener la posición centrada durante la animación } } @@ -349,9 +344,9 @@ void WindowMessage::updateResizeAnimation(float delta_time) { if (!resize_animation_.active) { return; } - + resize_animation_.elapsed += delta_time; - + if (resize_animation_.isFinished(config_.animation_duration)) { // Animación terminada rect_.w = resize_animation_.target_width; @@ -361,13 +356,13 @@ void WindowMessage::updateResizeAnimation(float delta_time) { } else { // Interpolar el tamaño float progress = easeOut(resize_animation_.getProgress(config_.animation_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(); // Mantener la posición centrada durante la animación + + 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(); // Mantener la posición centrada durante la animación } } @@ -390,13 +385,13 @@ auto WindowMessage::getTruncatedText(const std::string& text, float available_wi 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 ""; @@ -406,12 +401,12 @@ auto WindowMessage::getTruncatedText(const std::string& text, float available_wi 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; @@ -419,6 +414,6 @@ auto WindowMessage::getTruncatedText(const std::string& text, float available_wi 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 77e1dc8..806dc31 100644 --- a/source/ui/window_message.h +++ b/source/ui/window_message.h @@ -7,7 +7,7 @@ #include #include "color.h" -#include "text.h" +#include "text.h" class WindowMessage { public: @@ -17,34 +17,34 @@ class WindowMessage { }; struct Config { - // Colores - Color bg_color; - Color border_color; - Color title_color; - Color text_color; - - // Espaciado y dimensiones - float padding{15.0f}; - float line_spacing{5.0f}; - float title_separator_spacing{10.0f}; // Espacio extra para separador del título + // Colores + Color bg_color; + Color border_color; + Color title_color; + Color text_color; - // Límites de tamaño - float min_width{200.0f}; - float min_height{100.0f}; - float max_width_ratio{0.8f}; // % máximo de ancho de pantalla - float max_height_ratio{0.8f}; // % máximo de alto de pantalla + // Espaciado y dimensiones + float padding{15.0f}; + float line_spacing{5.0f}; + float title_separator_spacing{10.0f}; // Espacio extra para separador del título - // Margen de seguridad para texto - float text_safety_margin{20.0f}; // Margen extra para evitar texto cortado + // Límites de tamaño + float min_width{200.0f}; + float min_height{100.0f}; + float max_width_ratio{0.8f}; // % máximo de ancho de pantalla + float max_height_ratio{0.8f}; // % máximo de alto de pantalla - // Animaciones - float animation_duration{0.3f}; // Duración en segundos para todas las animaciones + // Margen de seguridad para texto + float text_safety_margin{20.0f}; // Margen extra para evitar texto cortado - // Constructor con valores por defecto - Config() - : bg_color{40, 40, 60, 220}, border_color{100, 100, 120, 255}, title_color{255, 255, 255, 255}, text_color{200, 200, 200, 255} + // Animaciones + float animation_duration{0.3f}; // Duración en segundos para todas las animaciones - {} + // Constructor con valores por defecto + Config() + : bg_color{40, 40, 60, 220}, border_color{100, 100, 120, 255}, title_color{255, 255, 255, 255}, text_color{200, 200, 200, 255} + + {} }; WindowMessage( @@ -69,7 +69,7 @@ class WindowMessage { void setTexts(const std::vector& texts); void addText(const std::string& text); void clearTexts(); - + // Control de redimensionado automático void enableAutoResize(bool enabled) { auto_resize_enabled_ = enabled; } [[nodiscard]] auto isAutoResizeEnabled() const -> bool { return auto_resize_enabled_; } @@ -78,20 +78,29 @@ class WindowMessage { void setPosition(float x, float y, PositionMode mode = PositionMode::CENTERED); void setSize(float width, float height); void centerOnScreen(); - void autoSize(); // Ajusta automáticamente al contenido y reposiciona si es necesario + void autoSize(); // Ajusta automáticamente al contenido y reposiciona si es necesario // Configuración de colores void setBackgroundColor(const Color& color) { config_.bg_color = color; } void setBorderColor(const Color& color) { config_.border_color = color; } - void setTitleColor(const Color& color) { config_.title_color = color; updateStyles(); } - void setTextColor(const Color& color) { config_.text_color = color; updateStyles(); } + void setTitleColor(const Color& color) { + config_.title_color = color; + updateStyles(); + } + void setTextColor(const Color& color) { + config_.text_color = color; + updateStyles(); + } // Configuración de espaciado void setPadding(float padding) { config_.padding = padding; } void setLineSpacing(float spacing) { config_.line_spacing = spacing; } - + // Configuración avanzada - void setConfig(const Config& config) { config_ = config; updateStyles(); } + void setConfig(const Config& config) { + config_ = config; + updateStyles(); + } [[nodiscard]] auto getConfig() const -> const Config& { return config_; } // Getters @@ -102,15 +111,15 @@ class WindowMessage { private: std::shared_ptr text_renderer_; Config config_; - + // Estado de visibilidad y redimensionado bool visible_ = false; - bool auto_resize_enabled_ = true; // Por defecto habilitado - + bool auto_resize_enabled_ = true; // Por defecto habilitado + // Contenido std::string title_; std::vector texts_; - + // Posición y tamaño SDL_FRect rect_{0, 0, 300, 200}; PositionMode position_mode_ = PositionMode::CENTERED; @@ -118,94 +127,96 @@ class WindowMessage { // Animación de redimensionado struct ResizeAnimation { - bool active = false; - float start_width, start_height; - float target_width, target_height; - float elapsed = 0.0F; + bool active = false; + float start_width, start_height; + float target_width, target_height; + float elapsed = 0.0F; - void 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 stop() { - active = false; - elapsed = 0.0F; - } - - [[nodiscard]] auto isFinished(float duration) const -> bool { - return elapsed >= duration; - } - - [[nodiscard]] auto getProgress(float duration) const -> float { - return std::min(elapsed / duration, 1.0F); - } + void 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 stop() { + active = false; + elapsed = 0.0F; + } + + [[nodiscard]] auto isFinished(float duration) const -> bool { + return elapsed >= duration; + } + + [[nodiscard]] auto getProgress(float duration) const -> float { + return std::min(elapsed / duration, 1.0F); + } } resize_animation_; // Animación de mostrar/ocultar struct ShowHideAnimation { - enum class Type { NONE, SHOWING, HIDING }; - - Type type = Type::NONE; - bool active = false; - float target_width, target_height; // Tamaño final al mostrar - float elapsed = 0.0F; + enum class Type { NONE, + SHOWING, + HIDING }; - void startShow(float to_w, float to_h) { - type = Type::SHOWING; - target_width = to_w; - target_height = to_h; - elapsed = 0.0F; - active = true; - } - - void startHide() { - type = Type::HIDING; - elapsed = 0.0F; - active = true; - } - - void stop() { - type = Type::NONE; - active = false; - elapsed = 0.0F; - } - - [[nodiscard]] auto isFinished(float duration) const -> bool { - return elapsed >= duration; - } - - [[nodiscard]] auto getProgress(float duration) const -> float { - return std::min(elapsed / duration, 1.0F); - } + Type type = Type::NONE; + bool active = false; + float target_width, target_height; // Tamaño final al mostrar + float elapsed = 0.0F; + + void startShow(float to_w, float to_h) { + type = Type::SHOWING; + target_width = to_w; + target_height = to_h; + elapsed = 0.0F; + active = true; + } + + void startHide() { + type = Type::HIDING; + elapsed = 0.0F; + active = true; + } + + void stop() { + type = Type::NONE; + active = false; + elapsed = 0.0F; + } + + [[nodiscard]] auto isFinished(float duration) const -> bool { + return elapsed >= duration; + } + + [[nodiscard]] auto getProgress(float duration) const -> float { + return std::min(elapsed / duration, 1.0F); + } } show_hide_animation_; // Estilos Text::Style title_style_; Text::Style text_style_; - + // Métodos privados void calculateAutoSize(); - void updatePosition(); // Actualiza la posición según el modo y punto de anclaje - void updateStyles(); // Actualiza los estilos de texto cuando cambian los colores - void ensureTextFits(); // Verifica y ajusta para que todo el texto sea visible - void triggerAutoResize(); // Inicia redimensionado automático si está habilitado - void updateAnimation(float delta_time); // Actualiza la animación de redimensionado - void updateShowHideAnimation(float delta_time); // Actualiza la animación de mostrar/ocultar - void updateResizeAnimation(float delta_time); // Actualiza la animación de redimensionado - + void updatePosition(); // Actualiza la posición según el modo y punto de anclaje + void updateStyles(); // Actualiza los estilos de texto cuando cambian los colores + void ensureTextFits(); // Verifica y ajusta para que todo el texto sea visible + void triggerAutoResize(); // Inicia redimensionado automático si está habilitado + void updateAnimation(float delta_time); // Actualiza la animación de redimensionado + void updateShowHideAnimation(float delta_time); // Actualiza la animación de mostrar/ocultar + void updateResizeAnimation(float delta_time); // Actualiza la animación de redimensionado + // Función de suavizado (ease-out) [[nodiscard]] static auto easeOut(float t) -> 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 shouldShowContent() const -> bool; // Si mostrar el contenido (texto, líneas, etc.) - + [[nodiscard]] auto shouldShowContent() const -> bool; // Si mostrar el contenido (texto, líneas, etc.) + [[nodiscard]] auto calculateContentHeight() const -> float; [[nodiscard]] auto calculateContentWidth() const -> float; [[nodiscard]] static auto getScreenWidth() -> float;