feat: rediseño HUD ayuda — 3 columnas, colores diferenciados, font -1pt

- Reorganizar key_bindings_ en 3 columnas (SIMULACIÓN+FIGURAS3D / VISUAL+PANTALLA / MODOS+DEBUG)
- Añadir F6 (escalado entero) y corregir F5 (Toggle PostFX), X (ciclar presets)
- Teclas usan category_color, descripciones usan content_color
- Separadores vacíos avanzan media línea (fix secciones pegadas)
- Font size del overlay reducido en 1pt respecto al resto de la UI
- calculateTextDimensions y rebuildCachedTexture actualizados para 3 columnas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 19:06:48 +01:00
parent 200672756c
commit b9b5f0b29f
3 changed files with 73 additions and 64 deletions

View File

@@ -18,20 +18,21 @@ HelpOverlay::HelpOverlay()
box_y_(0),
column1_width_(0),
column2_width_(0),
column3_width_(0),
cached_texture_(nullptr),
last_category_color_({0, 0, 0, 255}),
last_content_color_({0, 0, 0, 255}),
last_bg_color_({0, 0, 0, 255}),
texture_needs_rebuild_(true) {
// Llenar lista de controles (organizados por categoría, equilibrado en 2 columnas)
// Llenar lista de controles (organizados por categoría, equilibrado en 3 columnas)
key_bindings_ = {
// COLUMNA 1: SIMULACIÓN
{"SIMULACIÓN", ""},
{"1-8", "Escenarios (10 a 50,000 pelotas)"},
{"F", "Toggle Física - Última Figura"},
{"B", "Modo Boids (enjambre)"},
{"1-8", "Escenarios (10 a 50.000 pelotas)"},
{"F", "Cambia entre figura y física"},
{"B", "Modo Boids"},
{"ESPACIO", "Impulso contra gravedad"},
{"G", "Toggle Gravedad ON/OFF"},
{"G", "Activar / Desactivar gravedad"},
{"CURSORES", "Dirección de gravedad"},
{"", ""}, // Separador
@@ -39,40 +40,42 @@ HelpOverlay::HelpOverlay()
{"FIGURAS 3D", ""},
{"Q/W/E/R", "Esfera/Lissajous/Hélice/Toroide"},
{"T/Y/U/I", "Cubo/Cilindro/Icosaedro/Átomo"},
{"O", "Forma PNG"},
{"Num+/-", "Escalar figura"},
{"Num*", "Reset escala"},
{"Num/", "Toggle profundidad"},
{"Num/", "Activar / Desactivar profundidad"},
{"[new_col]", ""}, // CAMBIO DE COLUMNA -> COLUMNA 2
// COLUMNA 2: MODOS
{"MODOS", ""},
{"D", "Modo demo"},
{"L", "Modo demo lite"},
{"K", "Modo logo"},
{"", ""}, // Separador
// COLUMNA 1: VISUAL
// COLUMNA 2: VISUAL
{"VISUAL", ""},
{"C", "Tema siguiente"},
{"Shift+C", "Tema anterior"},
{"NumEnter", "Página de temas"},
{"N", "Cambiar sprite"},
{"[new_col]", ""}, // Separador -> CAMBIO DE COLUMNA
// COLUMNA 2: PANTALLA
{"PANTALLA", ""},
{"F1/F2", "Zoom out/in (ventana)"},
{"F3", "Fullscreen letterbox"},
{"F4", "Fullscreen real"},
{"F5", "Escalado (F3 activo)"},
{"V", "Toggle V-Sync"},
{"", ""}, // Separador
// COLUMNA 2: MODOS
{"MODOS", ""},
{"D", "Modo DEMO"},
{"Shift+D", "Pausar tema dinámico"},
{"L", "Modo DEMO LITE"},
{"K", "Modo LOGO (easter egg)"},
{"N", "Cambiar tamaño de pelota"},
{"X", "Ciclar presets PostFX"},
{"[new_col]", ""}, // CAMBIO DE COLUMNA -> COLUMNA 3
// COLUMNA 3: PANTALLA
{"PANTALLA", ""},
{"F1", "Disminuye ventana"},
{"F2", "Aumenta ventana"},
{"F3", "Pantalla completa"},
{"F4", "Pantalla completa real"},
{"F5", "Activar / Desactivar PostFX"},
{"F6", "Cambia el escalado de pantalla"},
{"V", "Activar / Desactivar V-Sync"},
{"", ""}, // Separador
// COLUMNA 2: DEBUG/AYUDA
// COLUMNA 3: DEBUG/AYUDA
{"DEBUG/AYUDA", ""},
{"F12", "Toggle info debug"},
{"F12", "Activar / Desactivar info debug"},
{"H", "Esta ayuda"},
{"ESC", "Salir"}};
}
@@ -157,12 +160,13 @@ void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) {
// Calcular ancho máximo por columna
int max_col1_width = 0;
int max_col2_width = 0;
int max_col3_width = 0;
int current_column = 0;
for (const auto& binding : key_bindings_) {
// Cambio de columna
if (strcmp(binding.key, "[new_col]") == 0) {
current_column = 1;
current_column++;
continue;
}
@@ -186,48 +190,51 @@ void HelpOverlay::calculateTextDimensions(int& max_width, int& total_height) {
// Actualizar máximo de columna correspondiente
if (current_column == 0) {
max_col1_width = std::max(max_col1_width, line_width);
} else {
} else if (current_column == 1) {
max_col2_width = std::max(max_col2_width, line_width);
} else {
max_col3_width = std::max(max_col3_width, line_width);
}
}
// Almacenar anchos de columnas en miembros para uso posterior
column1_width_ = max_col1_width;
column2_width_ = max_col2_width;
column3_width_ = max_col3_width;
// Ancho total: 2 columnas + 3 paddings (izq, medio, der)
max_width = max_col1_width + max_col2_width + padding * 3;
// Ancho total: 3 columnas + 4 paddings (izq, entre cols x2, der)
max_width = max_col1_width + max_col2_width + max_col3_width + padding * 4;
// Altura: contar líneas REALES en cada columna
int col1_lines = 0;
int col2_lines = 0;
// 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;
current_column = 0;
for (const auto& binding : key_bindings_) {
// Cambio de columna
if (strcmp(binding.key, "[new_col]") == 0) {
current_column = 1;
current_column++;
continue;
}
// Separador vacío no cuenta como línea
if (binding.key[0] == '\0') {
continue;
}
int units = (binding.key[0] == '\0') ? 1 : 2; // separador = 1, línea/encabezado = 2
// Contar línea (ya sea encabezado o contenido)
if (current_column == 0) {
col1_lines++;
col1_half_lines += units;
} else if (current_column == 1) {
col2_half_lines += units;
} else {
col2_lines++;
col3_half_lines += units;
}
}
// Usar la columna más larga para calcular altura
int max_column_lines = std::max(col1_lines, col2_lines);
// 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;
// Altura: título (2 líneas) + contenido + padding superior e inferior
total_height = line_height * 2 + max_column_lines * line_height + padding * 2;
total_height = line_height * 2 + content_height + padding * 2;
}
void HelpOverlay::calculateBoxDimensions() {
@@ -354,34 +361,35 @@ void HelpOverlay::rebuildCachedTexture() {
if (strcmp(binding.key, "[new_col]") == 0 && binding.description[0] == '\0') {
if (current_column == 0) {
current_column = 1;
current_x = padding + column1_width_ + padding; // Usar ancho real de columna 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;
current_y = content_start_y;
}
continue;
}
// CHECK PADDING INFERIOR ANTES de escribir la línea (AMBAS COLUMNAS)
// Verificar si la PRÓXIMA línea cabrá dentro del box con padding inferior
// CHECK PADDING INFERIOR ANTES de escribir la línea
if (current_y + line_height >= box_height_ - padding) {
if (current_column == 0) {
// Columna 0 llena: cambiar a columna 1
current_column = 1;
current_x = padding + column1_width_ + padding;
current_y = content_start_y;
} else {
// Columna 1 llena: omitir resto de texto (no cabe)
// Preferible omitir que sobresalir del overlay
continue;
}
continue;
}
if (binding.description[0] == '\0') {
text_renderer_->printAbsolute(current_x, current_y, binding.key, category_color);
current_y += line_height + 2;
if (binding.key[0] == '\0') {
// Separador vacío: avanzar media línea
current_y += line_height / 2;
} else {
// Encabezado de sección
text_renderer_->printAbsolute(current_x, current_y, binding.key, category_color);
current_y += line_height + 2;
}
continue;
}
text_renderer_->printAbsolute(current_x, current_y, binding.key, content_color);
// Tecla en category_color, descripción en content_color
text_renderer_->printAbsolute(current_x, current_y, binding.key, category_color);
int key_width = text_renderer_->getTextWidthPhysical(binding.key);
text_renderer_->printAbsolute(current_x + key_width + 10, current_y, binding.description, content_color);

View File

@@ -76,6 +76,7 @@ class HelpOverlay {
// Anchos individuales de cada columna (para evitar solapamiento)
int column1_width_;
int column2_width_;
int column3_width_;
// Sistema de caché para optimización de rendimiento
SDL_Texture* cached_texture_; // Textura cacheada del overlay completo

View File

@@ -100,7 +100,7 @@ void UIManager::initialize(SDL_Renderer* renderer, ThemeManager* theme_manager,
// Crear y configurar sistema de ayuda (overlay)
help_overlay_ = new HelpOverlay();
help_overlay_->initialize(renderer, theme_manager_, physical_width, physical_height, current_font_size_);
help_overlay_->initialize(renderer, theme_manager_, physical_width, physical_height, std::max(9, current_font_size_ - 1));
// Inicializar FPS counter
fps_last_time_ = SDL_GetTicks();
@@ -195,7 +195,7 @@ void UIManager::updatePhysicalWindowSize(int width, int height) {
// Actualizar help overlay con font size actual Y nuevas dimensiones (atómicamente)
if (help_overlay_) {
help_overlay_->updateAll(current_font_size_, width, height);
help_overlay_->updateAll(std::max(9, current_font_size_ - 1), width, height);
}
// Actualizar otros componentes de UI con nuevas dimensiones