diff --git a/source/text/textrenderer.cpp b/source/text/textrenderer.cpp index e8a2200..a39a742 100644 --- a/source/text/textrenderer.cpp +++ b/source/text/textrenderer.cpp @@ -361,3 +361,11 @@ int TextRenderer::getTextHeight() { return TTF_GetFontHeight(font_); } + +int TextRenderer::getGlyphHeight() { + if (!isInitialized()) { + return 0; + } + + return TTF_GetFontAscent(font_) - TTF_GetFontDescent(font_); +} diff --git a/source/text/textrenderer.hpp b/source/text/textrenderer.hpp index eea7b05..e6bdf25 100644 --- a/source/text/textrenderer.hpp +++ b/source/text/textrenderer.hpp @@ -42,9 +42,12 @@ public: // Útil para notificaciones y elementos UI de tamaño fijo int getTextWidthPhysical(const char* text); - // Obtiene la altura de la fuente + // Obtiene la altura de la fuente (incluye line_gap) int getTextHeight(); + // Obtiene la altura real del glifo (ascender + |descendente|, sin line_gap) + int getGlyphHeight(); + // Verifica si está inicializado correctamente bool isInitialized() const { return font_ != nullptr && renderer_ != nullptr; } diff --git a/source/ui/help_overlay.cpp b/source/ui/help_overlay.cpp index 5a747a8..5af0b12 100644 --- a/source/ui/help_overlay.cpp +++ b/source/ui/help_overlay.cpp @@ -30,7 +30,7 @@ HelpOverlay::HelpOverlay() {"SIMULACIÓN", ""}, {"1-8", "Escenarios (10 a 50.000 pelotas)"}, {"F", "Cambia entre figura y física"}, - {"B", "Modo Boids"}, + {"B", "Cambia entre boids y física"}, {"ESPACIO", "Impulso contra gravedad"}, {"G", "Activar / Desactivar gravedad"}, {"CURSORES", "Dirección de gravedad"}, @@ -47,9 +47,9 @@ HelpOverlay::HelpOverlay() // COLUMNA 2: MODOS {"MODOS", ""}, - {"D", "Modo demo"}, - {"L", "Modo demo lite"}, - {"K", "Modo logo"}, + {"D", "Activar / Desactivar modo demo"}, + {"L", "Activar / Desactivar modo demo lite"}, + {"K", "Activar / Desactivar modo logo"}, {"", ""}, // Separador // COLUMNA 2: VISUAL @@ -202,14 +202,14 @@ void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) { column2_width_ = max_col2_width; column3_width_ = max_col3_width; - // Ancho total: 3 columnas + 4 paddings (izq, entre cols x2, der) - max_width = max_col1_width + max_col2_width + max_col3_width + padding * 4; + // Gap entre columnas: doble del padding para dar más respiro + int col_gap = padding * 2; - // Altura: contar unidades de altura por columna (en "medias líneas" para precisión) - // - línea normal = 2 unidades, encabezado = 2 unidades (+2px ignorados aquí), separador = 1 unidad - int col1_half_lines = 0; - int col2_half_lines = 0; - int col3_half_lines = 0; + // Ancho total: 3 columnas + padding izq/der + 2 gaps entre columnas + max_width = max_col1_width + max_col2_width + max_col3_width + padding * 2 + col_gap * 2; + + // Calcular altura real simulando exactamente lo que hace el render + int col_heights[3] = {0, 0, 0}; current_column = 0; for (const auto& binding : key_bindings_) { @@ -218,23 +218,23 @@ void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) { continue; } - int units = (binding.key[0] == '\0') ? 1 : 2; // separador = 1, línea/encabezado = 2 - - if (current_column == 0) { - col1_half_lines += units; - } else if (current_column == 1) { - col2_half_lines += units; + if (binding.key[0] == '\0') { + col_heights[current_column] += line_height; // separador vacío + } else if (binding.description[0] == '\0') { + col_heights[current_column] += line_height; // encabezado } else { - col3_half_lines += units; + col_heights[current_column] += line_height; // línea normal } } - // Convertir a altura real: unidades * (line_height / 2) - int max_half_lines = std::max({col1_half_lines, col2_half_lines, col3_half_lines}); - int content_height = max_half_lines * line_height / 2; + int content_height = std::max({col_heights[0], col_heights[1], col_heights[2]}); - // Altura: título (2 líneas) + contenido + padding superior e inferior - total_height = line_height * 2 + content_height + padding * 2; + // Eliminar el line_gap de la última línea: ese gap es espacio entre líneas, + // pero la última línea no tiene siguiente, así que queda como padding muerto. + int glyph_height = text_renderer_->getGlyphHeight(); + int visual_content_height = content_height - (line_height - glyph_height); + + total_height = visual_content_height + padding * 2; } void HelpOverlay::calculateBoxDimensions() { @@ -341,31 +341,28 @@ void HelpOverlay::rebuildCachedTexture() { // Configuración de espaciado int line_height = text_renderer_->getTextHeight(); - // Padding dinámico basado en altura física: 25px para >= 600px, escalado proporcionalmente para menores int padding = (physical_height_ >= 600) ? 25 : std::max(10, physical_height_ / 24); + int col_gap = padding * 2; + + // Posición X de inicio de cada columna + int col_start[3]; + col_start[0] = padding; + col_start[1] = padding + column1_width_ + col_gap; + col_start[2] = padding + column1_width_ + col_gap + column2_width_ + col_gap; + + // Ancho de cada columna (para centrado interno) + int col_width[3] = {column1_width_, column2_width_, column3_width_}; - int current_x = padding; // Coordenadas relativas a la textura (0,0) int current_y = padding; int current_column = 0; - // Título principal - const char* title = "CONTROLES - ViBe3 Physics"; - int title_width = text_renderer_->getTextWidthPhysical(title); - text_renderer_->printAbsolute(box_width_ / 2 - title_width / 2, current_y, title, category_color); - current_y += line_height * 2; - int content_start_y = current_y; // Renderizar cada línea for (const auto& binding : key_bindings_) { if (strcmp(binding.key, "[new_col]") == 0 && binding.description[0] == '\0') { - if (current_column == 0) { - current_column = 1; - current_x = padding + column1_width_ + padding; - current_y = content_start_y; - } else if (current_column == 1) { - current_column = 2; - current_x = padding + column1_width_ + padding + column2_width_ + padding; + if (current_column < 2) { + current_column++; current_y = content_start_y; } continue; @@ -376,22 +373,30 @@ void HelpOverlay::rebuildCachedTexture() { continue; } + int cx = col_start[current_column]; + int cw = col_width[current_column]; + if (binding.description[0] == '\0') { if (binding.key[0] == '\0') { - // Separador vacío: avanzar media línea - current_y += line_height / 2; + // Separador vacío: avanzar una línea completa + current_y += line_height; } else { - // Encabezado de sección - text_renderer_->printAbsolute(current_x, current_y, binding.key, category_color); - current_y += line_height + 2; + // Encabezado de sección — centrado en la columna + int w = text_renderer_->getTextWidthPhysical(binding.key); + text_renderer_->printAbsolute(cx + (cw - w) / 2, current_y, binding.key, category_color); + current_y += line_height; } continue; } - // Tecla en category_color, descripción en content_color - text_renderer_->printAbsolute(current_x, current_y, binding.key, category_color); + // Par tecla+descripción — centrado como bloque en la columna int key_width = text_renderer_->getTextWidthPhysical(binding.key); - text_renderer_->printAbsolute(current_x + key_width + 10, current_y, binding.description, content_color); + int desc_width = text_renderer_->getTextWidthPhysical(binding.description); + int total_width = key_width + 10 + desc_width; + int line_x = cx + (cw - total_width) / 2; + + text_renderer_->printAbsolute(line_x, current_y, binding.key, category_color); + text_renderer_->printAbsolute(line_x + key_width + 10, current_y, binding.description, content_color); current_y += line_height; }