Add: Sistema de notificaciones con colores de fondo temáticos

CARACTERÍSTICAS:
- Notificaciones con fondo personalizado por tema (15 temas)
- Soporte completo para temas estáticos y dinámicos
- Interpolación LERP de colores durante transiciones
- Actualización por frame durante animaciones de temas

IMPLEMENTACIÓN:

Theme System:
- Añadido getNotificationBackgroundColor() a interfaz Theme
- StaticTheme: Color fijo por tema
- DynamicTheme: Interpolación entre keyframes
- ThemeManager: LERP durante transiciones (PHASE 3)
- ThemeSnapshot: Captura color para transiciones suaves

Colores por Tema:
Estáticos (9):
  - SUNSET: Púrpura oscuro (120, 40, 80)
  - OCEAN: Azul marino (20, 50, 90)
  - NEON: Púrpura oscuro (60, 0, 80)
  - FOREST: Marrón tierra (70, 50, 30)
  - RGB: Gris claro (220, 220, 220)
  - MONOCHROME: Gris oscuro (50, 50, 50)
  - LAVENDER: Violeta oscuro (80, 50, 100)
  - CRIMSON: Rojo oscuro (80, 10, 10)
  - EMERALD: Verde oscuro (10, 80, 10)

Dinámicos (6, 20 keyframes totales):
  - SUNRISE: 3 keyframes (noche→alba→día)
  - OCEAN_WAVES: 2 keyframes (profundo→claro)
  - NEON_PULSE: 2 keyframes (apagado→encendido)
  - FIRE: 4 keyframes (brasas→llamas→inferno→llamas)
  - AURORA: 4 keyframes (verde→violeta→cian→violeta)
  - VOLCANIC: 4 keyframes (ceniza→erupción→lava→enfriamiento)

Notifier:
- Añadido SDL_Color bg_color a estructura Notification
- Método show() acepta parámetro bg_color
- renderBackground() usa color dinámico (no negro fijo)
- Soporte para cambios de color cada frame

Engine:
- Obtiene color de fondo desde ThemeManager
- Pasa bg_color al notifier en cada notificación
- Sincronizado con tema activo y transiciones

FIXES:
- TEXT_ABSOLUTE_SIZE cambiado de 16px a 12px (múltiplo nativo)
- Centrado de notificaciones corregido en F3 fullscreen
- updatePhysicalWindowSize() usa SDL_GetCurrentDisplayMode en F3
- Notificaciones centradas correctamente en ventana/F3/F4

🎨 Generated with Claude Code
This commit is contained in:
2025-10-10 07:17:06 +02:00
parent 68381dc92d
commit 0d1608712b
17 changed files with 818 additions and 12 deletions

View File

@@ -96,6 +96,8 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
// SDL ya inicializado arriba para validación
{
// Crear ventana principal (fullscreen si se especifica)
// NOTA: SDL_WINDOW_HIGH_PIXEL_DENSITY removido por incompatibilidad con STRETCH mode (F4)
// El DPI se detectará manualmente con SDL_GetWindowSizeInPixels()
Uint32 window_flags = SDL_WINDOW_OPENGL;
if (fullscreen) {
window_flags |= SDL_WINDOW_FULLSCREEN;
@@ -285,11 +287,14 @@ void Engine::update() {
updateShape();
}
// Actualizar texto (sin cambios en la lógica)
// Actualizar texto (OBSOLETO: sistema antiguo, se mantiene por compatibilidad temporal)
if (show_text_) {
show_text_ = !(SDL_GetTicks() - text_init_time_ > TEXT_DURATION);
}
// Actualizar sistema de notificaciones
notifier_.update(current_time);
// Actualizar Modo DEMO (auto-play)
updateDemoMode();
@@ -874,6 +879,10 @@ void Engine::render() {
float text_scale_x = static_cast<float>(physical_window_width_) / static_cast<float>(current_screen_width_);
float text_scale_y = static_cast<float>(physical_window_height_) / static_cast<float>(current_screen_height_);
// SISTEMA DE TEXTO ANTIGUO DESHABILITADO
// Reemplazado completamente por el sistema de notificaciones (Notifier)
// El doble renderizado causaba que aparecieran textos duplicados detrás de las notificaciones
/*
if (show_text_) {
// Obtener datos del tema actual (delegado a ThemeManager)
int text_color_r, text_color_g, text_color_b;
@@ -898,6 +907,7 @@ void Engine::render() {
text_renderer_.printPhysical(theme_x, theme_y, theme_name_es, text_color_r, text_color_g, text_color_b, text_scale_x, text_scale_y);
}
}
*/
// Debug display (solo si está activado con tecla H)
if (show_debug_) {
@@ -992,6 +1002,9 @@ void Engine::render() {
}
}
// Renderizar notificaciones (siempre al final, sobre todo lo demás)
notifier_.render();
SDL_RenderPresent(renderer_);
}
@@ -1033,13 +1046,47 @@ void Engine::setText() {
// Suprimir textos durante modos demo
if (current_app_mode_ != AppMode::MANUAL) return;
// Generar texto de número de pelotas
int num_balls = BALL_COUNT_SCENARIOS[scenario_];
std::string notification_text;
if (num_balls == 1) {
text_ = "1 pelota";
notification_text = "1 Pelota";
} else if (num_balls < 1000) {
notification_text = std::to_string(num_balls) + " Pelotas";
} else {
text_ = std::to_string(num_balls) + " pelotas";
// Formato con separador de miles para números grandes
notification_text = std::to_string(num_balls / 1000) + "," +
(num_balls % 1000 < 100 ? "0" : "") +
(num_balls % 1000 < 10 ? "0" : "") +
std::to_string(num_balls % 1000) + " Pelotas";
}
text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2; // Centrar texto
// Obtener color del tema actual para la notificación
int text_r, text_g, text_b;
theme_manager_->getCurrentThemeTextColor(text_r, text_g, text_b);
SDL_Color notification_color = {
static_cast<Uint8>(text_r),
static_cast<Uint8>(text_g),
static_cast<Uint8>(text_b),
255
};
// Obtener color de fondo de la notificación desde el tema
int bg_r, bg_g, bg_b;
theme_manager_->getCurrentNotificationBackgroundColor(bg_r, bg_g, bg_b);
SDL_Color notification_bg_color = {
static_cast<Uint8>(bg_r),
static_cast<Uint8>(bg_g),
static_cast<Uint8>(bg_b),
255
};
// Mostrar notificación
notifier_.show(notification_text, NOTIFICATION_DURATION, notification_color, notification_bg_color);
// Sistema antiguo (mantener temporalmente para compatibilidad)
text_ = notification_text;
text_pos_ = (current_screen_width_ - text_renderer_.getTextWidth(text_.c_str())) / 2;
show_text_ = true;
text_init_time_ = SDL_GetTicks();
}
@@ -1136,6 +1183,9 @@ void Engine::toggleFullscreen() {
fullscreen_enabled_ = !fullscreen_enabled_;
SDL_SetWindowFullscreen(window_, fullscreen_enabled_);
// Actualizar dimensiones físicas después del cambio
updatePhysicalWindowSize();
}
void Engine::toggleRealFullscreen() {
@@ -1388,13 +1438,27 @@ void Engine::zoomOut() {
void Engine::updatePhysicalWindowSize() {
if (real_fullscreen_enabled_) {
// En fullscreen real, usar resolución del display
// En fullscreen real (F4), usar resolución del display
physical_window_width_ = current_screen_width_;
physical_window_height_ = current_screen_height_;
} else if (fullscreen_enabled_) {
// En fullscreen F3, obtener tamaño REAL del display (no del framebuffer lógico)
// SDL_GetRenderOutputSize() falla en F3 (devuelve tamaño lógico 960x720)
// Necesitamos el tamaño FÍSICO real de la pantalla
int num_displays = 0;
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
if (displays != nullptr && num_displays > 0) {
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
if (dm != nullptr) {
physical_window_width_ = dm->w;
physical_window_height_ = dm->h;
}
SDL_free(displays);
}
} else {
// En modo ventana, obtener tamaño real de la ventana (lógica * zoom)
// En modo ventana, obtener tamaño FÍSICO real del framebuffer
int window_w = 0, window_h = 0;
SDL_GetWindowSize(window_, &window_w, &window_h);
SDL_GetWindowSizeInPixels(window_, &window_w, &window_h);
physical_window_width_ = window_w;
physical_window_height_ = window_h;
}
@@ -1407,8 +1471,35 @@ void Engine::updatePhysicalWindowSize() {
// Reinicializar TextRenderers con nuevo tamaño de fuente
text_renderer_.cleanup();
text_renderer_debug_.cleanup();
text_renderer_notifier_.cleanup();
text_renderer_.init(renderer_, TEXT_FONT_PATH, font_size, TEXT_ANTIALIASING);
text_renderer_debug_.init(renderer_, TEXT_FONT_PATH, font_size, TEXT_ANTIALIASING);
// TextRenderer para notificaciones: Detectar DPI y ajustar tamaño
// En pantallas Retina/HiDPI, el texto necesita ser más grande para ser legible
int logical_w = 0, logical_h = 0;
SDL_GetWindowSize(window_, &logical_w, &logical_h);
// Usar physical_window_width_ que ya contiene el tamaño real del framebuffer
// (calculado arriba con SDL_GetRenderOutputSize o current_screen_width_)
int pixels_w = physical_window_width_;
// Calcular escala DPI (1.0 normal, 2.0 Retina, 3.0 en algunos displays)
float dpi_scale = (logical_w > 0) ? static_cast<float>(pixels_w) / static_cast<float>(logical_w) : 1.0f;
// Ajustar tamaño de fuente base (16px) por escala DPI
// Retina macOS: 16px * 2.0 = 32px (legible)
// Normal: 16px * 1.0 = 16px
int notification_font_size = static_cast<int>(TEXT_ABSOLUTE_SIZE * dpi_scale);
if (notification_font_size < 12) notification_font_size = 12; // Mínimo legible
text_renderer_notifier_.init(renderer_, TEXT_FONT_PATH, notification_font_size, TEXT_ANTIALIASING);
// Inicializar/actualizar Notifier con nuevas dimensiones
// NOTA: init() es seguro de llamar múltiples veces, solo actualiza punteros y dimensiones
// Esto asegura que el notifier tenga las referencias correctas tras resize/fullscreen
notifier_.init(renderer_, &text_renderer_notifier_, physical_window_width_, physical_window_height_);
}
// ============================================================================