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:
@@ -25,7 +25,7 @@ if (NOT SDL3_ttf_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Archivos fuente (excluir main_old.cpp)
|
# Archivos fuente (excluir main_old.cpp)
|
||||||
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/shapes/*.cpp source/themes/*.cpp source/text/*.cpp)
|
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/shapes/*.cpp source/themes/*.cpp source/text/*.cpp source/ui/*.cpp)
|
||||||
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
||||||
|
|
||||||
# Comprobar si se encontraron archivos fuente
|
# Comprobar si se encontraron archivos fuente
|
||||||
|
|||||||
@@ -22,9 +22,18 @@ constexpr int WINDOW_DECORATION_HEIGHT = 30; // Altura estimada de decoraciones
|
|||||||
constexpr float GRAVITY_FORCE = 0.2f; // Fuerza de gravedad (píxeles/frame²)
|
constexpr float GRAVITY_FORCE = 0.2f; // Fuerza de gravedad (píxeles/frame²)
|
||||||
|
|
||||||
// Configuración de interfaz
|
// Configuración de interfaz
|
||||||
constexpr Uint64 TEXT_DURATION = 2000; // Duración del texto informativo (ms)
|
constexpr Uint64 TEXT_DURATION = 2000; // Duración del texto informativo (ms) - OBSOLETO, usar NOTIFICATION_DURATION
|
||||||
constexpr float THEME_TRANSITION_DURATION = 0.5f; // Duración de transiciones LERP entre temas (segundos)
|
constexpr float THEME_TRANSITION_DURATION = 0.5f; // Duración de transiciones LERP entre temas (segundos)
|
||||||
|
|
||||||
|
// Configuración de notificaciones (sistema Notifier)
|
||||||
|
constexpr int TEXT_ABSOLUTE_SIZE = 12; // Tamaño fuente base en píxeles físicos (múltiplo de 12px, tamaño nativo de la fuente)
|
||||||
|
constexpr Uint64 NOTIFICATION_DURATION = 2000; // Duración default de notificaciones (ms)
|
||||||
|
constexpr Uint64 NOTIFICATION_SLIDE_TIME = 300; // Duración animación entrada (ms)
|
||||||
|
constexpr Uint64 NOTIFICATION_FADE_TIME = 200; // Duración animación salida (ms)
|
||||||
|
constexpr float NOTIFICATION_BG_ALPHA = 0.7f; // Opacidad fondo semitransparente (0.0-1.0)
|
||||||
|
constexpr int NOTIFICATION_PADDING = 10; // Padding interno del fondo (píxeles físicos)
|
||||||
|
constexpr int NOTIFICATION_TOP_MARGIN = 20; // Margen superior desde borde pantalla (píxeles físicos)
|
||||||
|
|
||||||
// Configuración de pérdida aleatoria en rebotes
|
// Configuración de pérdida aleatoria en rebotes
|
||||||
constexpr float BASE_BOUNCE_COEFFICIENT = 0.75f; // Coeficiente base IGUAL para todas las pelotas
|
constexpr float BASE_BOUNCE_COEFFICIENT = 0.75f; // Coeficiente base IGUAL para todas las pelotas
|
||||||
constexpr float BOUNCE_RANDOM_LOSS_PERCENT = 0.1f; // 0-10% pérdida adicional aleatoria en cada rebote
|
constexpr float BOUNCE_RANDOM_LOSS_PERCENT = 0.1f; // 0-10% pérdida adicional aleatoria en cada rebote
|
||||||
@@ -65,6 +74,9 @@ struct DynamicThemeKeyframe {
|
|||||||
float bg_top_r, bg_top_g, bg_top_b;
|
float bg_top_r, bg_top_g, bg_top_b;
|
||||||
float bg_bottom_r, bg_bottom_g, bg_bottom_b;
|
float bg_bottom_r, bg_bottom_g, bg_bottom_b;
|
||||||
|
|
||||||
|
// Color de fondo de notificaciones
|
||||||
|
int notif_bg_r, notif_bg_g, notif_bg_b;
|
||||||
|
|
||||||
// Colores de pelotas en este keyframe
|
// Colores de pelotas en este keyframe
|
||||||
std::vector<Color> ball_colors;
|
std::vector<Color> ball_colors;
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
// SDL ya inicializado arriba para validación
|
// SDL ya inicializado arriba para validación
|
||||||
{
|
{
|
||||||
// Crear ventana principal (fullscreen si se especifica)
|
// 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;
|
Uint32 window_flags = SDL_WINDOW_OPENGL;
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
window_flags |= SDL_WINDOW_FULLSCREEN;
|
window_flags |= SDL_WINDOW_FULLSCREEN;
|
||||||
@@ -285,11 +287,14 @@ void Engine::update() {
|
|||||||
updateShape();
|
updateShape();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar texto (sin cambios en la lógica)
|
// Actualizar texto (OBSOLETO: sistema antiguo, se mantiene por compatibilidad temporal)
|
||||||
if (show_text_) {
|
if (show_text_) {
|
||||||
show_text_ = !(SDL_GetTicks() - text_init_time_ > TEXT_DURATION);
|
show_text_ = !(SDL_GetTicks() - text_init_time_ > TEXT_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actualizar sistema de notificaciones
|
||||||
|
notifier_.update(current_time);
|
||||||
|
|
||||||
// Actualizar Modo DEMO (auto-play)
|
// Actualizar Modo DEMO (auto-play)
|
||||||
updateDemoMode();
|
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_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_);
|
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_) {
|
if (show_text_) {
|
||||||
// Obtener datos del tema actual (delegado a ThemeManager)
|
// Obtener datos del tema actual (delegado a ThemeManager)
|
||||||
int text_color_r, text_color_g, text_color_b;
|
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);
|
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)
|
// Debug display (solo si está activado con tecla H)
|
||||||
if (show_debug_) {
|
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_);
|
SDL_RenderPresent(renderer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1033,13 +1046,47 @@ void Engine::setText() {
|
|||||||
// Suprimir textos durante modos demo
|
// Suprimir textos durante modos demo
|
||||||
if (current_app_mode_ != AppMode::MANUAL) return;
|
if (current_app_mode_ != AppMode::MANUAL) return;
|
||||||
|
|
||||||
|
// Generar texto de número de pelotas
|
||||||
int num_balls = BALL_COUNT_SCENARIOS[scenario_];
|
int num_balls = BALL_COUNT_SCENARIOS[scenario_];
|
||||||
|
std::string notification_text;
|
||||||
if (num_balls == 1) {
|
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 {
|
} 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;
|
show_text_ = true;
|
||||||
text_init_time_ = SDL_GetTicks();
|
text_init_time_ = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
@@ -1136,6 +1183,9 @@ void Engine::toggleFullscreen() {
|
|||||||
|
|
||||||
fullscreen_enabled_ = !fullscreen_enabled_;
|
fullscreen_enabled_ = !fullscreen_enabled_;
|
||||||
SDL_SetWindowFullscreen(window_, fullscreen_enabled_);
|
SDL_SetWindowFullscreen(window_, fullscreen_enabled_);
|
||||||
|
|
||||||
|
// Actualizar dimensiones físicas después del cambio
|
||||||
|
updatePhysicalWindowSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::toggleRealFullscreen() {
|
void Engine::toggleRealFullscreen() {
|
||||||
@@ -1388,13 +1438,27 @@ void Engine::zoomOut() {
|
|||||||
|
|
||||||
void Engine::updatePhysicalWindowSize() {
|
void Engine::updatePhysicalWindowSize() {
|
||||||
if (real_fullscreen_enabled_) {
|
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_width_ = current_screen_width_;
|
||||||
physical_window_height_ = current_screen_height_;
|
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 {
|
} 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;
|
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_width_ = window_w;
|
||||||
physical_window_height_ = window_h;
|
physical_window_height_ = window_h;
|
||||||
}
|
}
|
||||||
@@ -1407,8 +1471,35 @@ void Engine::updatePhysicalWindowSize() {
|
|||||||
// Reinicializar TextRenderers con nuevo tamaño de fuente
|
// Reinicializar TextRenderers con nuevo tamaño de fuente
|
||||||
text_renderer_.cleanup();
|
text_renderer_.cleanup();
|
||||||
text_renderer_debug_.cleanup();
|
text_renderer_debug_.cleanup();
|
||||||
|
text_renderer_notifier_.cleanup();
|
||||||
|
|
||||||
text_renderer_.init(renderer_, TEXT_FONT_PATH, font_size, TEXT_ANTIALIASING);
|
text_renderer_.init(renderer_, TEXT_FONT_PATH, font_size, TEXT_ANTIALIASING);
|
||||||
text_renderer_debug_.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_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
||||||
#include "text/textrenderer.h" // for TextRenderer
|
#include "text/textrenderer.h" // for TextRenderer
|
||||||
#include "theme_manager.h" // for ThemeManager
|
#include "theme_manager.h" // for ThemeManager
|
||||||
|
#include "ui/notifier.h" // for Notifier
|
||||||
|
|
||||||
// Modos de aplicación mutuamente excluyentes
|
// Modos de aplicación mutuamente excluyentes
|
||||||
enum class AppMode {
|
enum class AppMode {
|
||||||
@@ -54,9 +55,11 @@ class Engine {
|
|||||||
|
|
||||||
// UI y debug
|
// UI y debug
|
||||||
bool show_debug_ = false;
|
bool show_debug_ = false;
|
||||||
bool show_text_ = true;
|
bool show_text_ = true; // OBSOLETO: usar notifier_ en su lugar
|
||||||
TextRenderer text_renderer_; // Sistema de renderizado de texto para display (centrado)
|
TextRenderer text_renderer_; // Sistema de renderizado de texto para display (centrado)
|
||||||
TextRenderer text_renderer_debug_; // Sistema de renderizado de texto para debug (HUD)
|
TextRenderer text_renderer_debug_; // Sistema de renderizado de texto para debug (HUD)
|
||||||
|
TextRenderer text_renderer_notifier_; // Sistema de renderizado de texto para notificaciones (tamaño fijo)
|
||||||
|
Notifier notifier_; // Sistema de notificaciones estilo iOS/Android
|
||||||
|
|
||||||
// Sistema de zoom dinámico
|
// Sistema de zoom dinámico
|
||||||
int current_window_zoom_ = DEFAULT_WINDOW_ZOOM;
|
int current_window_zoom_ = DEFAULT_WINDOW_ZOOM;
|
||||||
|
|||||||
@@ -152,6 +152,68 @@ void TextRenderer::printPhysical(int logical_x, int logical_y, const std::string
|
|||||||
printPhysical(logical_x, logical_y, text.c_str(), r, g, b, scale_x, scale_y);
|
printPhysical(logical_x, logical_y, text.c_str(), r, g, b, scale_x, scale_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextRenderer::printAbsolute(int physical_x, int physical_y, const char* text, SDL_Color color) {
|
||||||
|
if (!isInitialized() || text == nullptr || text[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear superficie con el texto renderizado
|
||||||
|
SDL_Surface* text_surface = nullptr;
|
||||||
|
|
||||||
|
if (use_antialiasing_) {
|
||||||
|
text_surface = TTF_RenderText_Blended(font_, text, strlen(text), color);
|
||||||
|
} else {
|
||||||
|
text_surface = TTF_RenderText_Solid(font_, text, strlen(text), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text_surface == nullptr) {
|
||||||
|
SDL_Log("Error al renderizar texto: %s", SDL_GetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear textura desde la superficie
|
||||||
|
SDL_Texture* text_texture = SDL_CreateTextureFromSurface(renderer_, text_surface);
|
||||||
|
|
||||||
|
if (text_texture == nullptr) {
|
||||||
|
SDL_Log("Error al crear textura: %s", SDL_GetError());
|
||||||
|
SDL_DestroySurface(text_surface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurar alpha blending si el color tiene transparencia
|
||||||
|
if (color.a < 255) {
|
||||||
|
SDL_SetTextureBlendMode(text_texture, SDL_BLENDMODE_BLEND);
|
||||||
|
SDL_SetTextureAlphaModFloat(text_texture, color.a / 255.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preparar rectángulo de destino en coordenadas físicas absolutas
|
||||||
|
SDL_FRect dest_rect;
|
||||||
|
dest_rect.x = static_cast<float>(physical_x);
|
||||||
|
dest_rect.y = static_cast<float>(physical_y);
|
||||||
|
dest_rect.w = static_cast<float>(text_surface->w);
|
||||||
|
dest_rect.h = static_cast<float>(text_surface->h);
|
||||||
|
|
||||||
|
// Deshabilitar temporalmente presentación lógica para renderizar en píxeles físicos
|
||||||
|
int logical_w = 0, logical_h = 0;
|
||||||
|
SDL_RendererLogicalPresentation presentation_mode;
|
||||||
|
SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &presentation_mode);
|
||||||
|
|
||||||
|
// Renderizar sin presentación lógica (coordenadas absolutas)
|
||||||
|
SDL_SetRenderLogicalPresentation(renderer_, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
|
||||||
|
SDL_RenderTexture(renderer_, text_texture, nullptr, &dest_rect);
|
||||||
|
|
||||||
|
// Restaurar presentación lógica
|
||||||
|
SDL_SetRenderLogicalPresentation(renderer_, logical_w, logical_h, presentation_mode);
|
||||||
|
|
||||||
|
// Limpiar recursos
|
||||||
|
SDL_DestroyTexture(text_texture);
|
||||||
|
SDL_DestroySurface(text_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextRenderer::printAbsolute(int physical_x, int physical_y, const std::string& text, SDL_Color color) {
|
||||||
|
printAbsolute(physical_x, physical_y, text.c_str(), color);
|
||||||
|
}
|
||||||
|
|
||||||
int TextRenderer::getTextWidth(const char* text) {
|
int TextRenderer::getTextWidth(const char* text) {
|
||||||
if (!isInitialized() || text == nullptr) {
|
if (!isInitialized() || text == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -165,6 +227,23 @@ int TextRenderer::getTextWidth(const char* text) {
|
|||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TextRenderer::getTextWidthPhysical(const char* text) {
|
||||||
|
// Retorna el ancho REAL en píxeles físicos (sin escalado lógico)
|
||||||
|
// Idéntico a getTextWidth() pero semánticamente diferente:
|
||||||
|
// - Este método se usa cuando se necesita el ancho REAL de la fuente
|
||||||
|
// - Útil para calcular dimensiones de UI en coordenadas físicas absolutas
|
||||||
|
if (!isInitialized() || text == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
if (!TTF_GetStringSize(font_, text, strlen(text), &width, &height)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return width; // Ancho real de la textura generada por TTF
|
||||||
|
}
|
||||||
|
|
||||||
int TextRenderer::getTextHeight() {
|
int TextRenderer::getTextHeight() {
|
||||||
if (!isInitialized()) {
|
if (!isInitialized()) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -23,9 +23,18 @@ public:
|
|||||||
void printPhysical(int logical_x, int logical_y, const char* text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y);
|
void printPhysical(int logical_x, int logical_y, const char* text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y);
|
||||||
void printPhysical(int logical_x, int logical_y, const std::string& text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y);
|
void printPhysical(int logical_x, int logical_y, const std::string& text, uint8_t r, uint8_t g, uint8_t b, float scale_x, float scale_y);
|
||||||
|
|
||||||
|
// Renderiza texto en coordenadas físicas absolutas (tamaño fijo independiente de resolución)
|
||||||
|
// NOTA: Este método usa el tamaño de fuente tal cual fue cargado, sin escalado
|
||||||
|
void printAbsolute(int physical_x, int physical_y, const char* text, SDL_Color color);
|
||||||
|
void printAbsolute(int physical_x, int physical_y, const std::string& text, SDL_Color color);
|
||||||
|
|
||||||
// Obtiene el ancho de un texto renderizado (en píxeles lógicos para compatibilidad)
|
// Obtiene el ancho de un texto renderizado (en píxeles lógicos para compatibilidad)
|
||||||
int getTextWidth(const char* text);
|
int getTextWidth(const char* text);
|
||||||
|
|
||||||
|
// Obtiene el ancho de un texto en píxeles FÍSICOS reales (sin escalado)
|
||||||
|
// Ú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
|
||||||
int getTextHeight();
|
int getTextHeight();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ void ThemeManager::initialize() {
|
|||||||
"Sunset",
|
"Sunset",
|
||||||
"Atardecer",
|
"Atardecer",
|
||||||
255, 140, 60, // Color texto: naranja cálido
|
255, 140, 60, // Color texto: naranja cálido
|
||||||
|
120, 40, 80, // Color fondo notificación: púrpura oscuro (contrasta con naranja)
|
||||||
180.0f / 255.0f, 140.0f / 255.0f, 100.0f / 255.0f, // Fondo superior: naranja suave
|
180.0f / 255.0f, 140.0f / 255.0f, 100.0f / 255.0f, // Fondo superior: naranja suave
|
||||||
40.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior: púrpura oscuro
|
40.0f / 255.0f, 20.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior: púrpura oscuro
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -33,6 +34,7 @@ void ThemeManager::initialize() {
|
|||||||
"Ocean",
|
"Ocean",
|
||||||
"Océano",
|
"Océano",
|
||||||
80, 200, 255, // Color texto: azul océano
|
80, 200, 255, // Color texto: azul océano
|
||||||
|
20, 50, 90, // Color fondo notificación: azul marino oscuro (contrasta con cian)
|
||||||
100.0f / 255.0f, 150.0f / 255.0f, 200.0f / 255.0f, // Fondo superior: azul cielo
|
100.0f / 255.0f, 150.0f / 255.0f, 200.0f / 255.0f, // Fondo superior: azul cielo
|
||||||
20.0f / 255.0f, 40.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior: azul marino
|
20.0f / 255.0f, 40.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior: azul marino
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -46,6 +48,7 @@ void ThemeManager::initialize() {
|
|||||||
"Neon",
|
"Neon",
|
||||||
"Neón",
|
"Neón",
|
||||||
255, 60, 255, // Color texto: magenta brillante
|
255, 60, 255, // Color texto: magenta brillante
|
||||||
|
60, 0, 80, // Color fondo notificación: púrpura muy oscuro (contrasta con neón)
|
||||||
20.0f / 255.0f, 20.0f / 255.0f, 40.0f / 255.0f, // Fondo superior: negro azulado
|
20.0f / 255.0f, 20.0f / 255.0f, 40.0f / 255.0f, // Fondo superior: negro azulado
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -59,6 +62,7 @@ void ThemeManager::initialize() {
|
|||||||
"Forest",
|
"Forest",
|
||||||
"Bosque",
|
"Bosque",
|
||||||
100, 255, 100, // Color texto: verde natural
|
100, 255, 100, // Color texto: verde natural
|
||||||
|
70, 50, 30, // Color fondo notificación: marrón oscuro tierra (contrasta con verde)
|
||||||
144.0f / 255.0f, 238.0f / 255.0f, 144.0f / 255.0f, // Fondo superior: verde claro
|
144.0f / 255.0f, 238.0f / 255.0f, 144.0f / 255.0f, // Fondo superior: verde claro
|
||||||
101.0f / 255.0f, 67.0f / 255.0f, 33.0f / 255.0f, // Fondo inferior: marrón tierra
|
101.0f / 255.0f, 67.0f / 255.0f, 33.0f / 255.0f, // Fondo inferior: marrón tierra
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -72,6 +76,7 @@ void ThemeManager::initialize() {
|
|||||||
"RGB",
|
"RGB",
|
||||||
"RGB",
|
"RGB",
|
||||||
100, 100, 100, // Color texto: gris oscuro
|
100, 100, 100, // Color texto: gris oscuro
|
||||||
|
220, 220, 220, // Color fondo notificación: gris muy claro (contrasta sobre blanco)
|
||||||
1.0f, 1.0f, 1.0f, // Fondo superior: blanco puro
|
1.0f, 1.0f, 1.0f, // Fondo superior: blanco puro
|
||||||
1.0f, 1.0f, 1.0f, // Fondo inferior: blanco puro (sin degradado)
|
1.0f, 1.0f, 1.0f, // Fondo inferior: blanco puro (sin degradado)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -107,6 +112,7 @@ void ThemeManager::initialize() {
|
|||||||
"Monochrome",
|
"Monochrome",
|
||||||
"Monocromo",
|
"Monocromo",
|
||||||
200, 200, 200, // Color texto: gris claro
|
200, 200, 200, // Color texto: gris claro
|
||||||
|
50, 50, 50, // Color fondo notificación: gris medio oscuro (contrasta con texto claro)
|
||||||
20.0f / 255.0f, 20.0f / 255.0f, 20.0f / 255.0f, // Fondo superior: gris muy oscuro
|
20.0f / 255.0f, 20.0f / 255.0f, 20.0f / 255.0f, // Fondo superior: gris muy oscuro
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -120,6 +126,7 @@ void ThemeManager::initialize() {
|
|||||||
"Lavender",
|
"Lavender",
|
||||||
"Lavanda",
|
"Lavanda",
|
||||||
255, 200, 100, // Color texto: amarillo cálido
|
255, 200, 100, // Color texto: amarillo cálido
|
||||||
|
80, 50, 100, // Color fondo notificación: violeta muy oscuro (contrasta con amarillo)
|
||||||
120.0f / 255.0f, 80.0f / 255.0f, 140.0f / 255.0f, // Fondo superior: violeta oscuro
|
120.0f / 255.0f, 80.0f / 255.0f, 140.0f / 255.0f, // Fondo superior: violeta oscuro
|
||||||
25.0f / 255.0f, 30.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior: azul medianoche
|
25.0f / 255.0f, 30.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior: azul medianoche
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -133,6 +140,7 @@ void ThemeManager::initialize() {
|
|||||||
"Crimson",
|
"Crimson",
|
||||||
"Carmesí",
|
"Carmesí",
|
||||||
255, 100, 100, // Color texto: rojo claro
|
255, 100, 100, // Color texto: rojo claro
|
||||||
|
80, 10, 10, // Color fondo notificación: rojo muy oscuro (contrasta con texto claro)
|
||||||
40.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo superior: rojo muy oscuro
|
40.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo superior: rojo muy oscuro
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro puro
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro puro
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -146,6 +154,7 @@ void ThemeManager::initialize() {
|
|||||||
"Emerald",
|
"Emerald",
|
||||||
"Esmeralda",
|
"Esmeralda",
|
||||||
100, 255, 100, // Color texto: verde claro
|
100, 255, 100, // Color texto: verde claro
|
||||||
|
10, 80, 10, // Color fondo notificación: verde muy oscuro (contrasta con texto claro)
|
||||||
0.0f / 255.0f, 40.0f / 255.0f, 0.0f / 255.0f, // Fondo superior: verde muy oscuro
|
0.0f / 255.0f, 40.0f / 255.0f, 0.0f / 255.0f, // Fondo superior: verde muy oscuro
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro puro
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro puro
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
@@ -168,6 +177,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
20.0f / 255.0f, 25.0f / 255.0f, 60.0f / 255.0f, // Fondo superior: azul medianoche
|
20.0f / 255.0f, 25.0f / 255.0f, 60.0f / 255.0f, // Fondo superior: azul medianoche
|
||||||
10.0f / 255.0f, 10.0f / 255.0f, 30.0f / 255.0f, // Fondo inferior: azul muy oscuro
|
10.0f / 255.0f, 10.0f / 255.0f, 30.0f / 255.0f, // Fondo inferior: azul muy oscuro
|
||||||
|
20, 30, 80, // Color fondo notificación: azul oscuro (noche)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{100, 100, 150}, {120, 120, 170}, {90, 90, 140}, {110, 110, 160},
|
{100, 100, 150}, {120, 120, 170}, {90, 90, 140}, {110, 110, 160},
|
||||||
{95, 95, 145}, {105, 105, 155}, {100, 100, 150}, {115, 115, 165}
|
{95, 95, 145}, {105, 105, 155}, {100, 100, 150}, {115, 115, 165}
|
||||||
@@ -178,6 +188,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
180.0f / 255.0f, 100.0f / 255.0f, 120.0f / 255.0f, // Fondo superior: naranja-rosa
|
180.0f / 255.0f, 100.0f / 255.0f, 120.0f / 255.0f, // Fondo superior: naranja-rosa
|
||||||
255.0f / 255.0f, 140.0f / 255.0f, 100.0f / 255.0f, // Fondo inferior: naranja cálido
|
255.0f / 255.0f, 140.0f / 255.0f, 100.0f / 255.0f, // Fondo inferior: naranja cálido
|
||||||
|
140, 60, 80, // Color fondo notificación: naranja-rojo oscuro (alba)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{255, 180, 100}, {255, 160, 80}, {255, 200, 120}, {255, 150, 90},
|
{255, 180, 100}, {255, 160, 80}, {255, 200, 120}, {255, 150, 90},
|
||||||
{255, 190, 110}, {255, 170, 95}, {255, 185, 105}, {255, 165, 88}
|
{255, 190, 110}, {255, 170, 95}, {255, 185, 105}, {255, 165, 88}
|
||||||
@@ -188,6 +199,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
255.0f / 255.0f, 240.0f / 255.0f, 180.0f / 255.0f, // Fondo superior: amarillo claro
|
255.0f / 255.0f, 240.0f / 255.0f, 180.0f / 255.0f, // Fondo superior: amarillo claro
|
||||||
255.0f / 255.0f, 255.0f / 255.0f, 220.0f / 255.0f, // Fondo inferior: amarillo muy claro
|
255.0f / 255.0f, 255.0f / 255.0f, 220.0f / 255.0f, // Fondo inferior: amarillo muy claro
|
||||||
|
200, 180, 140, // Color fondo notificación: amarillo oscuro (día)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{255, 255, 200}, {255, 255, 180}, {255, 255, 220}, {255, 255, 190},
|
{255, 255, 200}, {255, 255, 180}, {255, 255, 220}, {255, 255, 190},
|
||||||
{255, 255, 210}, {255, 255, 185}, {255, 255, 205}, {255, 255, 195}
|
{255, 255, 210}, {255, 255, 185}, {255, 255, 205}, {255, 255, 195}
|
||||||
@@ -209,6 +221,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
20.0f / 255.0f, 50.0f / 255.0f, 100.0f / 255.0f, // Fondo superior: azul marino
|
20.0f / 255.0f, 50.0f / 255.0f, 100.0f / 255.0f, // Fondo superior: azul marino
|
||||||
10.0f / 255.0f, 30.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior: azul muy oscuro
|
10.0f / 255.0f, 30.0f / 255.0f, 60.0f / 255.0f, // Fondo inferior: azul muy oscuro
|
||||||
|
10, 30, 70, // Color fondo notificación: azul muy oscuro (profundidad)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{60, 100, 180}, {50, 90, 170}, {70, 110, 190}, {55, 95, 175},
|
{60, 100, 180}, {50, 90, 170}, {70, 110, 190}, {55, 95, 175},
|
||||||
{65, 105, 185}, {58, 98, 172}, {62, 102, 182}, {52, 92, 168}
|
{65, 105, 185}, {58, 98, 172}, {62, 102, 182}, {52, 92, 168}
|
||||||
@@ -219,6 +232,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
100.0f / 255.0f, 200.0f / 255.0f, 230.0f / 255.0f, // Fondo superior: turquesa claro
|
100.0f / 255.0f, 200.0f / 255.0f, 230.0f / 255.0f, // Fondo superior: turquesa claro
|
||||||
50.0f / 255.0f, 150.0f / 255.0f, 200.0f / 255.0f, // Fondo inferior: turquesa medio
|
50.0f / 255.0f, 150.0f / 255.0f, 200.0f / 255.0f, // Fondo inferior: turquesa medio
|
||||||
|
30, 100, 140, // Color fondo notificación: turquesa oscuro (aguas poco profundas)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{100, 220, 255}, {90, 210, 245}, {110, 230, 255}, {95, 215, 250},
|
{100, 220, 255}, {90, 210, 245}, {110, 230, 255}, {95, 215, 250},
|
||||||
{105, 225, 255}, {98, 218, 248}, {102, 222, 252}, {92, 212, 242}
|
{105, 225, 255}, {98, 218, 248}, {102, 222, 252}, {92, 212, 242}
|
||||||
@@ -240,6 +254,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo superior: negro
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo superior: negro
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro
|
||||||
|
30, 30, 30, // Color fondo notificación: gris muy oscuro (apagado)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{40, 40, 40}, {50, 50, 50}, {45, 45, 45}, {48, 48, 48},
|
{40, 40, 40}, {50, 50, 50}, {45, 45, 45}, {48, 48, 48},
|
||||||
{42, 42, 42}, {47, 47, 47}, {44, 44, 44}, {46, 46, 46}
|
{42, 42, 42}, {47, 47, 47}, {44, 44, 44}, {46, 46, 46}
|
||||||
@@ -250,6 +265,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
20.0f / 255.0f, 20.0f / 255.0f, 40.0f / 255.0f, // Fondo superior: azul oscuro
|
20.0f / 255.0f, 20.0f / 255.0f, 40.0f / 255.0f, // Fondo superior: azul oscuro
|
||||||
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro
|
0.0f / 255.0f, 0.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: negro
|
||||||
|
60, 0, 80, // Color fondo notificación: púrpura oscuro (neón encendido)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{0, 255, 255}, {255, 0, 255}, {0, 255, 200}, {255, 50, 255},
|
{0, 255, 255}, {255, 0, 255}, {0, 255, 200}, {255, 50, 255},
|
||||||
{50, 255, 255}, {255, 0, 200}, {0, 255, 230}, {255, 80, 255}
|
{50, 255, 255}, {255, 0, 200}, {0, 255, 230}, {255, 80, 255}
|
||||||
@@ -271,6 +287,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
60.0f / 255.0f, 20.0f / 255.0f, 10.0f / 255.0f, // Fondo superior: rojo muy oscuro
|
60.0f / 255.0f, 20.0f / 255.0f, 10.0f / 255.0f, // Fondo superior: rojo muy oscuro
|
||||||
20.0f / 255.0f, 10.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: casi negro
|
20.0f / 255.0f, 10.0f / 255.0f, 0.0f / 255.0f, // Fondo inferior: casi negro
|
||||||
|
70, 20, 10, // Color fondo notificación: rojo muy oscuro (brasas)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{120, 40, 20}, {140, 35, 15}, {130, 38, 18}, {125, 42, 22},
|
{120, 40, 20}, {140, 35, 15}, {130, 38, 18}, {125, 42, 22},
|
||||||
{135, 37, 16}, {128, 40, 20}, {132, 39, 19}, {138, 36, 17}
|
{135, 37, 16}, {128, 40, 20}, {132, 39, 19}, {138, 36, 17}
|
||||||
@@ -281,6 +298,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
180.0f / 255.0f, 80.0f / 255.0f, 20.0f / 255.0f, // Fondo superior: naranja fuerte
|
180.0f / 255.0f, 80.0f / 255.0f, 20.0f / 255.0f, // Fondo superior: naranja fuerte
|
||||||
100.0f / 255.0f, 30.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro
|
100.0f / 255.0f, 30.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro
|
||||||
|
110, 40, 10, // Color fondo notificación: naranja-rojo oscuro (llamas)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{255, 140, 0}, {255, 120, 10}, {255, 160, 20}, {255, 130, 5},
|
{255, 140, 0}, {255, 120, 10}, {255, 160, 20}, {255, 130, 5},
|
||||||
{255, 150, 15}, {255, 125, 8}, {255, 145, 12}, {255, 135, 18}
|
{255, 150, 15}, {255, 125, 8}, {255, 145, 12}, {255, 135, 18}
|
||||||
@@ -291,6 +309,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
255.0f / 255.0f, 180.0f / 255.0f, 80.0f / 255.0f, // Fondo superior: amarillo-naranja brillante
|
255.0f / 255.0f, 180.0f / 255.0f, 80.0f / 255.0f, // Fondo superior: amarillo-naranja brillante
|
||||||
220.0f / 255.0f, 100.0f / 255.0f, 30.0f / 255.0f, // Fondo inferior: naranja intenso
|
220.0f / 255.0f, 100.0f / 255.0f, 30.0f / 255.0f, // Fondo inferior: naranja intenso
|
||||||
|
160, 80, 30, // Color fondo notificación: naranja oscuro (inferno)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{255, 220, 100}, {255, 200, 80}, {255, 240, 120}, {255, 210, 90},
|
{255, 220, 100}, {255, 200, 80}, {255, 240, 120}, {255, 210, 90},
|
||||||
{255, 230, 110}, {255, 205, 85}, {255, 225, 105}, {255, 215, 95}
|
{255, 230, 110}, {255, 205, 85}, {255, 225, 105}, {255, 215, 95}
|
||||||
@@ -301,6 +320,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
180.0f / 255.0f, 80.0f / 255.0f, 20.0f / 255.0f, // Fondo superior: naranja fuerte
|
180.0f / 255.0f, 80.0f / 255.0f, 20.0f / 255.0f, // Fondo superior: naranja fuerte
|
||||||
100.0f / 255.0f, 30.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro
|
100.0f / 255.0f, 30.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro
|
||||||
|
110, 40, 10, // Color fondo notificación: naranja-rojo oscuro (llamas)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{255, 140, 0}, {255, 120, 10}, {255, 160, 20}, {255, 130, 5},
|
{255, 140, 0}, {255, 120, 10}, {255, 160, 20}, {255, 130, 5},
|
||||||
{255, 150, 15}, {255, 125, 8}, {255, 145, 12}, {255, 135, 18}
|
{255, 150, 15}, {255, 125, 8}, {255, 145, 12}, {255, 135, 18}
|
||||||
@@ -322,6 +342,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
30.0f / 255.0f, 80.0f / 255.0f, 60.0f / 255.0f, // Fondo superior: verde oscuro
|
30.0f / 255.0f, 80.0f / 255.0f, 60.0f / 255.0f, // Fondo superior: verde oscuro
|
||||||
10.0f / 255.0f, 20.0f / 255.0f, 30.0f / 255.0f, // Fondo inferior: azul muy oscuro
|
10.0f / 255.0f, 20.0f / 255.0f, 30.0f / 255.0f, // Fondo inferior: azul muy oscuro
|
||||||
|
15, 50, 40, // Color fondo notificación: verde muy oscuro (aurora verde)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{100, 255, 180}, {80, 240, 160}, {120, 255, 200}, {90, 245, 170},
|
{100, 255, 180}, {80, 240, 160}, {120, 255, 200}, {90, 245, 170},
|
||||||
{110, 255, 190}, {85, 242, 165}, {105, 252, 185}, {95, 248, 175}
|
{110, 255, 190}, {85, 242, 165}, {105, 252, 185}, {95, 248, 175}
|
||||||
@@ -332,6 +353,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
120.0f / 255.0f, 60.0f / 255.0f, 180.0f / 255.0f, // Fondo superior: violeta
|
120.0f / 255.0f, 60.0f / 255.0f, 180.0f / 255.0f, // Fondo superior: violeta
|
||||||
40.0f / 255.0f, 20.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior: violeta oscuro
|
40.0f / 255.0f, 20.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior: violeta oscuro
|
||||||
|
70, 30, 100, // Color fondo notificación: violeta oscuro (aurora violeta)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{200, 100, 255}, {180, 80, 240}, {220, 120, 255}, {190, 90, 245},
|
{200, 100, 255}, {180, 80, 240}, {220, 120, 255}, {190, 90, 245},
|
||||||
{210, 110, 255}, {185, 85, 242}, {205, 105, 252}, {195, 95, 248}
|
{210, 110, 255}, {185, 85, 242}, {205, 105, 252}, {195, 95, 248}
|
||||||
@@ -342,6 +364,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
60.0f / 255.0f, 180.0f / 255.0f, 220.0f / 255.0f, // Fondo superior: cian brillante
|
60.0f / 255.0f, 180.0f / 255.0f, 220.0f / 255.0f, // Fondo superior: cian brillante
|
||||||
20.0f / 255.0f, 80.0f / 255.0f, 120.0f / 255.0f, // Fondo inferior: azul oscuro
|
20.0f / 255.0f, 80.0f / 255.0f, 120.0f / 255.0f, // Fondo inferior: azul oscuro
|
||||||
|
20, 90, 120, // Color fondo notificación: cian oscuro (aurora cian)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{100, 220, 255}, {80, 200, 240}, {120, 240, 255}, {90, 210, 245},
|
{100, 220, 255}, {80, 200, 240}, {120, 240, 255}, {90, 210, 245},
|
||||||
{110, 230, 255}, {85, 205, 242}, {105, 225, 252}, {95, 215, 248}
|
{110, 230, 255}, {85, 205, 242}, {105, 225, 252}, {95, 215, 248}
|
||||||
@@ -352,6 +375,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
120.0f / 255.0f, 60.0f / 255.0f, 180.0f / 255.0f, // Fondo superior: violeta
|
120.0f / 255.0f, 60.0f / 255.0f, 180.0f / 255.0f, // Fondo superior: violeta
|
||||||
40.0f / 255.0f, 20.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior: violeta oscuro
|
40.0f / 255.0f, 20.0f / 255.0f, 80.0f / 255.0f, // Fondo inferior: violeta oscuro
|
||||||
|
70, 30, 100, // Color fondo notificación: violeta oscuro (aurora violeta)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{200, 100, 255}, {180, 80, 240}, {220, 120, 255}, {190, 90, 245},
|
{200, 100, 255}, {180, 80, 240}, {220, 120, 255}, {190, 90, 245},
|
||||||
{210, 110, 255}, {185, 85, 242}, {205, 105, 252}, {195, 95, 248}
|
{210, 110, 255}, {185, 85, 242}, {205, 105, 252}, {195, 95, 248}
|
||||||
@@ -373,6 +397,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
40.0f / 255.0f, 40.0f / 255.0f, 45.0f / 255.0f, // Fondo superior: gris oscuro
|
40.0f / 255.0f, 40.0f / 255.0f, 45.0f / 255.0f, // Fondo superior: gris oscuro
|
||||||
20.0f / 255.0f, 15.0f / 255.0f, 15.0f / 255.0f, // Fondo inferior: casi negro
|
20.0f / 255.0f, 15.0f / 255.0f, 15.0f / 255.0f, // Fondo inferior: casi negro
|
||||||
|
50, 50, 55, // Color fondo notificación: gris oscuro (ceniza)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{80, 80, 90}, {75, 75, 85}, {85, 85, 95}, {78, 78, 88},
|
{80, 80, 90}, {75, 75, 85}, {85, 85, 95}, {78, 78, 88},
|
||||||
{82, 82, 92}, {76, 76, 86}, {84, 84, 94}, {79, 79, 89}
|
{82, 82, 92}, {76, 76, 86}, {84, 84, 94}, {79, 79, 89}
|
||||||
@@ -383,6 +408,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
180.0f / 255.0f, 60.0f / 255.0f, 30.0f / 255.0f, // Fondo superior: naranja-rojo
|
180.0f / 255.0f, 60.0f / 255.0f, 30.0f / 255.0f, // Fondo superior: naranja-rojo
|
||||||
80.0f / 255.0f, 20.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro
|
80.0f / 255.0f, 20.0f / 255.0f, 10.0f / 255.0f, // Fondo inferior: rojo oscuro
|
||||||
|
120, 30, 15, // Color fondo notificación: naranja-rojo oscuro (erupción)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{255, 80, 40}, {255, 100, 50}, {255, 70, 35}, {255, 90, 45},
|
{255, 80, 40}, {255, 100, 50}, {255, 70, 35}, {255, 90, 45},
|
||||||
{255, 75, 38}, {255, 95, 48}, {255, 85, 42}, {255, 78, 40}
|
{255, 75, 38}, {255, 95, 48}, {255, 85, 42}, {255, 78, 40}
|
||||||
@@ -393,6 +419,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
220.0f / 255.0f, 120.0f / 255.0f, 40.0f / 255.0f, // Fondo superior: naranja brillante
|
220.0f / 255.0f, 120.0f / 255.0f, 40.0f / 255.0f, // Fondo superior: naranja brillante
|
||||||
180.0f / 255.0f, 60.0f / 255.0f, 20.0f / 255.0f, // Fondo inferior: naranja-rojo
|
180.0f / 255.0f, 60.0f / 255.0f, 20.0f / 255.0f, // Fondo inferior: naranja-rojo
|
||||||
|
150, 70, 25, // Color fondo notificación: naranja oscuro (lava)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{255, 180, 80}, {255, 200, 100}, {255, 170, 70}, {255, 190, 90},
|
{255, 180, 80}, {255, 200, 100}, {255, 170, 70}, {255, 190, 90},
|
||||||
{255, 175, 75}, {255, 195, 95}, {255, 185, 85}, {255, 178, 78}
|
{255, 175, 75}, {255, 195, 95}, {255, 185, 85}, {255, 178, 78}
|
||||||
@@ -403,6 +430,7 @@ void ThemeManager::initialize() {
|
|||||||
{
|
{
|
||||||
100.0f / 255.0f, 80.0f / 255.0f, 70.0f / 255.0f, // Fondo superior: gris-naranja
|
100.0f / 255.0f, 80.0f / 255.0f, 70.0f / 255.0f, // Fondo superior: gris-naranja
|
||||||
50.0f / 255.0f, 40.0f / 255.0f, 35.0f / 255.0f, // Fondo inferior: gris oscuro
|
50.0f / 255.0f, 40.0f / 255.0f, 35.0f / 255.0f, // Fondo inferior: gris oscuro
|
||||||
|
80, 60, 50, // Color fondo notificación: gris-naranja oscuro (enfriamiento)
|
||||||
std::vector<Color>{
|
std::vector<Color>{
|
||||||
{150, 120, 100}, {140, 110, 90}, {160, 130, 110}, {145, 115, 95},
|
{150, 120, 100}, {140, 110, 90}, {160, 130, 110}, {145, 115, 95},
|
||||||
{155, 125, 105}, {142, 112, 92}, {158, 128, 108}, {148, 118, 98}
|
{155, 125, 105}, {142, 112, 92}, {158, 128, 108}, {148, 118, 98}
|
||||||
@@ -567,6 +595,22 @@ void ThemeManager::getCurrentThemeTextColor(int& r, int& g, int& b) const {
|
|||||||
b = static_cast<int>(lerp(static_cast<float>(source_snapshot_->text_color_b), static_cast<float>(target_b), transition_progress_));
|
b = static_cast<int>(lerp(static_cast<float>(source_snapshot_->text_color_b), static_cast<float>(target_b), transition_progress_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ThemeManager::getCurrentNotificationBackgroundColor(int& r, int& g, int& b) const {
|
||||||
|
if (!transitioning_ || !source_snapshot_) {
|
||||||
|
// Sin transición: color directo del tema activo
|
||||||
|
themes_[current_theme_index_]->getNotificationBackgroundColor(r, g, b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHASE 3: Con transición: LERP entre snapshot origen y tema destino
|
||||||
|
int target_r, target_g, target_b;
|
||||||
|
themes_[current_theme_index_]->getNotificationBackgroundColor(target_r, target_g, target_b);
|
||||||
|
|
||||||
|
r = static_cast<int>(lerp(static_cast<float>(source_snapshot_->notif_bg_r), static_cast<float>(target_r), transition_progress_));
|
||||||
|
g = static_cast<int>(lerp(static_cast<float>(source_snapshot_->notif_bg_g), static_cast<float>(target_g), transition_progress_));
|
||||||
|
b = static_cast<int>(lerp(static_cast<float>(source_snapshot_->notif_bg_b), static_cast<float>(target_b), transition_progress_));
|
||||||
|
}
|
||||||
|
|
||||||
Color ThemeManager::getInitialBallColor(int random_index) const {
|
Color ThemeManager::getInitialBallColor(int random_index) const {
|
||||||
// Obtener color inicial del tema activo (progress = 0.0f)
|
// Obtener color inicial del tema activo (progress = 0.0f)
|
||||||
return themes_[current_theme_index_]->getBallColor(random_index, 0.0f);
|
return themes_[current_theme_index_]->getBallColor(random_index, 0.0f);
|
||||||
@@ -588,6 +632,10 @@ std::unique_ptr<ThemeSnapshot> ThemeManager::captureCurrentSnapshot() const {
|
|||||||
themes_[current_theme_index_]->getTextColor(
|
themes_[current_theme_index_]->getTextColor(
|
||||||
snapshot->text_color_r, snapshot->text_color_g, snapshot->text_color_b);
|
snapshot->text_color_r, snapshot->text_color_g, snapshot->text_color_b);
|
||||||
|
|
||||||
|
// Capturar color de fondo de notificaciones
|
||||||
|
themes_[current_theme_index_]->getNotificationBackgroundColor(
|
||||||
|
snapshot->notif_bg_r, snapshot->notif_bg_g, snapshot->notif_bg_b);
|
||||||
|
|
||||||
// Capturar nombres
|
// Capturar nombres
|
||||||
snapshot->name_en = themes_[current_theme_index_]->getNameEN();
|
snapshot->name_en = themes_[current_theme_index_]->getNameEN();
|
||||||
snapshot->name_es = themes_[current_theme_index_]->getNameES();
|
snapshot->name_es = themes_[current_theme_index_]->getNameES();
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class ThemeManager {
|
|||||||
const char* getCurrentThemeNameEN() const;
|
const char* getCurrentThemeNameEN() const;
|
||||||
const char* getCurrentThemeNameES() const;
|
const char* getCurrentThemeNameES() const;
|
||||||
void getCurrentThemeTextColor(int& r, int& g, int& b) const;
|
void getCurrentThemeTextColor(int& r, int& g, int& b) const;
|
||||||
|
void getCurrentNotificationBackgroundColor(int& r, int& g, int& b) const;
|
||||||
|
|
||||||
// Obtener color inicial para nuevas pelotas (usado en initBalls)
|
// Obtener color inicial para nuevas pelotas (usado en initBalls)
|
||||||
Color getInitialBallColor(int random_index) const;
|
Color getInitialBallColor(int random_index) const;
|
||||||
|
|||||||
@@ -120,3 +120,16 @@ void DynamicTheme::getBackgroundColors(float progress,
|
|||||||
bg = lerp(current_kf.bg_bottom_g, target_kf.bg_bottom_g, t);
|
bg = lerp(current_kf.bg_bottom_g, target_kf.bg_bottom_g, t);
|
||||||
bb = lerp(current_kf.bg_bottom_b, target_kf.bg_bottom_b, t);
|
bb = lerp(current_kf.bg_bottom_b, target_kf.bg_bottom_b, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DynamicTheme::getNotificationBackgroundColor(int& r, int& g, int& b) const {
|
||||||
|
// Obtener keyframes actual y objetivo
|
||||||
|
const auto& current_kf = keyframes_[current_keyframe_index_];
|
||||||
|
const auto& target_kf = keyframes_[target_keyframe_index_];
|
||||||
|
|
||||||
|
// Interpolar color de fondo de notificación usando progreso interno
|
||||||
|
float t = transition_progress_;
|
||||||
|
|
||||||
|
r = static_cast<int>(lerp(static_cast<float>(current_kf.notif_bg_r), static_cast<float>(target_kf.notif_bg_r), t));
|
||||||
|
g = static_cast<int>(lerp(static_cast<float>(current_kf.notif_bg_g), static_cast<float>(target_kf.notif_bg_g), t));
|
||||||
|
b = static_cast<int>(lerp(static_cast<float>(current_kf.notif_bg_b), static_cast<float>(target_kf.notif_bg_b), t));
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class DynamicTheme : public Theme {
|
|||||||
g = text_g_;
|
g = text_g_;
|
||||||
b = text_b_;
|
b = text_b_;
|
||||||
}
|
}
|
||||||
|
void getNotificationBackgroundColor(int& r, int& g, int& b) const override;
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// CORE: OBTENER COLORES (interpolados)
|
// CORE: OBTENER COLORES (interpolados)
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
StaticTheme::StaticTheme(const char* name_en, const char* name_es,
|
StaticTheme::StaticTheme(const char* name_en, const char* name_es,
|
||||||
int text_r, int text_g, int text_b,
|
int text_r, int text_g, int text_b,
|
||||||
|
int notif_bg_r, int notif_bg_g, int notif_bg_b,
|
||||||
float bg_top_r, float bg_top_g, float bg_top_b,
|
float bg_top_r, float bg_top_g, float bg_top_b,
|
||||||
float bg_bottom_r, float bg_bottom_g, float bg_bottom_b,
|
float bg_bottom_r, float bg_bottom_g, float bg_bottom_b,
|
||||||
std::vector<Color> ball_colors)
|
std::vector<Color> ball_colors)
|
||||||
: name_en_(name_en),
|
: name_en_(name_en),
|
||||||
name_es_(name_es),
|
name_es_(name_es),
|
||||||
text_r_(text_r), text_g_(text_g), text_b_(text_b),
|
text_r_(text_r), text_g_(text_g), text_b_(text_b),
|
||||||
|
notif_bg_r_(notif_bg_r), notif_bg_g_(notif_bg_g), notif_bg_b_(notif_bg_b),
|
||||||
bg_top_r_(bg_top_r), bg_top_g_(bg_top_g), bg_top_b_(bg_top_b),
|
bg_top_r_(bg_top_r), bg_top_g_(bg_top_g), bg_top_b_(bg_top_b),
|
||||||
bg_bottom_r_(bg_bottom_r), bg_bottom_g_(bg_bottom_g), bg_bottom_b_(bg_bottom_b),
|
bg_bottom_r_(bg_bottom_r), bg_bottom_g_(bg_bottom_g), bg_bottom_b_(bg_bottom_b),
|
||||||
ball_colors_(std::move(ball_colors)) {
|
ball_colors_(std::move(ball_colors)) {
|
||||||
|
|||||||
@@ -23,12 +23,14 @@ class StaticTheme : public Theme {
|
|||||||
* @param name_en: Nombre en inglés
|
* @param name_en: Nombre en inglés
|
||||||
* @param name_es: Nombre en español
|
* @param name_es: Nombre en español
|
||||||
* @param text_r, text_g, text_b: Color de texto UI
|
* @param text_r, text_g, text_b: Color de texto UI
|
||||||
|
* @param notif_bg_r, notif_bg_g, notif_bg_b: Color de fondo de notificaciones
|
||||||
* @param bg_top_r, bg_top_g, bg_top_b: Color superior de fondo
|
* @param bg_top_r, bg_top_g, bg_top_b: Color superior de fondo
|
||||||
* @param bg_bottom_r, bg_bottom_g, bg_bottom_b: Color inferior de fondo
|
* @param bg_bottom_r, bg_bottom_g, bg_bottom_b: Color inferior de fondo
|
||||||
* @param ball_colors: Paleta de colores para pelotas
|
* @param ball_colors: Paleta de colores para pelotas
|
||||||
*/
|
*/
|
||||||
StaticTheme(const char* name_en, const char* name_es,
|
StaticTheme(const char* name_en, const char* name_es,
|
||||||
int text_r, int text_g, int text_b,
|
int text_r, int text_g, int text_b,
|
||||||
|
int notif_bg_r, int notif_bg_g, int notif_bg_b,
|
||||||
float bg_top_r, float bg_top_g, float bg_top_b,
|
float bg_top_r, float bg_top_g, float bg_top_b,
|
||||||
float bg_bottom_r, float bg_bottom_g, float bg_bottom_b,
|
float bg_bottom_r, float bg_bottom_g, float bg_bottom_b,
|
||||||
std::vector<Color> ball_colors);
|
std::vector<Color> ball_colors);
|
||||||
@@ -46,6 +48,11 @@ class StaticTheme : public Theme {
|
|||||||
g = text_g_;
|
g = text_g_;
|
||||||
b = text_b_;
|
b = text_b_;
|
||||||
}
|
}
|
||||||
|
void getNotificationBackgroundColor(int& r, int& g, int& b) const override {
|
||||||
|
r = notif_bg_r_;
|
||||||
|
g = notif_bg_g_;
|
||||||
|
b = notif_bg_b_;
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// CORE: OBTENER COLORES
|
// CORE: OBTENER COLORES
|
||||||
@@ -66,6 +73,7 @@ class StaticTheme : public Theme {
|
|||||||
std::string name_en_;
|
std::string name_en_;
|
||||||
std::string name_es_;
|
std::string name_es_;
|
||||||
int text_r_, text_g_, text_b_;
|
int text_r_, text_g_, text_b_;
|
||||||
|
int notif_bg_r_, notif_bg_g_, notif_bg_b_;
|
||||||
float bg_top_r_, bg_top_g_, bg_top_b_;
|
float bg_top_r_, bg_top_g_, bg_top_b_;
|
||||||
float bg_bottom_r_, bg_bottom_g_, bg_bottom_b_;
|
float bg_bottom_r_, bg_bottom_g_, bg_bottom_b_;
|
||||||
std::vector<Color> ball_colors_;
|
std::vector<Color> ball_colors_;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class Theme {
|
|||||||
virtual const char* getNameEN() const = 0;
|
virtual const char* getNameEN() const = 0;
|
||||||
virtual const char* getNameES() const = 0;
|
virtual const char* getNameES() const = 0;
|
||||||
virtual void getTextColor(int& r, int& g, int& b) const = 0;
|
virtual void getTextColor(int& r, int& g, int& b) const = 0;
|
||||||
|
virtual void getNotificationBackgroundColor(int& r, int& g, int& b) const = 0;
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// CORE: OBTENER COLORES (polimórfico)
|
// CORE: OBTENER COLORES (polimórfico)
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ struct ThemeSnapshot {
|
|||||||
// Color de texto UI
|
// Color de texto UI
|
||||||
int text_color_r, text_color_g, text_color_b;
|
int text_color_r, text_color_g, text_color_b;
|
||||||
|
|
||||||
|
// Color de fondo de notificaciones
|
||||||
|
int notif_bg_r, notif_bg_g, notif_bg_b;
|
||||||
|
|
||||||
// Nombres del tema (para mostrar "SOURCE → TARGET" durante transición)
|
// Nombres del tema (para mostrar "SOURCE → TARGET" durante transición)
|
||||||
std::string name_en;
|
std::string name_en;
|
||||||
std::string name_es;
|
std::string name_es;
|
||||||
|
|||||||
214
source/ui/notifier.cpp
Normal file
214
source/ui/notifier.cpp
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
#include "notifier.h"
|
||||||
|
#include "../text/textrenderer.h"
|
||||||
|
#include "../defines.h"
|
||||||
|
#include "../utils/easing_functions.h"
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
Notifier::Notifier()
|
||||||
|
: renderer_(nullptr)
|
||||||
|
, text_renderer_(nullptr)
|
||||||
|
, window_width_(0)
|
||||||
|
, window_height_(0)
|
||||||
|
, current_notification_(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Notifier::~Notifier() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Notifier::init(SDL_Renderer* renderer, TextRenderer* text_renderer, int window_width, int window_height) {
|
||||||
|
renderer_ = renderer;
|
||||||
|
text_renderer_ = text_renderer;
|
||||||
|
window_width_ = window_width;
|
||||||
|
window_height_ = window_height;
|
||||||
|
return (renderer_ != nullptr && text_renderer_ != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notifier::updateWindowSize(int window_width, int window_height) {
|
||||||
|
window_width_ = window_width;
|
||||||
|
window_height_ = window_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notifier::show(const std::string& text, Uint64 duration, SDL_Color color, SDL_Color bg_color) {
|
||||||
|
if (text.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usar duración default si no se especifica
|
||||||
|
if (duration == 0) {
|
||||||
|
duration = NOTIFICATION_DURATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear nueva notificación
|
||||||
|
Notification notif;
|
||||||
|
notif.text = text;
|
||||||
|
notif.created_time = SDL_GetTicks();
|
||||||
|
notif.duration = duration;
|
||||||
|
notif.state = NotificationState::SLIDING_IN;
|
||||||
|
notif.alpha = 1.0f;
|
||||||
|
notif.y_offset = -50.0f; // Comienza 50px arriba (fuera de pantalla)
|
||||||
|
notif.color = color;
|
||||||
|
notif.bg_color = bg_color;
|
||||||
|
|
||||||
|
// Añadir a cola
|
||||||
|
notification_queue_.push(notif);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notifier::update(Uint64 current_time) {
|
||||||
|
// Activar siguiente notificación si no hay ninguna activa
|
||||||
|
if (!current_notification_ && !notification_queue_.empty()) {
|
||||||
|
processQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualizar notificación actual
|
||||||
|
if (current_notification_) {
|
||||||
|
Uint64 elapsed = current_time - current_notification_->created_time;
|
||||||
|
|
||||||
|
switch (current_notification_->state) {
|
||||||
|
case NotificationState::SLIDING_IN: {
|
||||||
|
// Animación de entrada (NOTIFICATION_SLIDE_TIME ms)
|
||||||
|
if (elapsed < NOTIFICATION_SLIDE_TIME) {
|
||||||
|
float progress = static_cast<float>(elapsed) / static_cast<float>(NOTIFICATION_SLIDE_TIME);
|
||||||
|
float eased = Easing::easeOutBack(progress); // Efecto con ligero overshoot
|
||||||
|
current_notification_->y_offset = -50.0f + (50.0f * eased); // De -50 a 0
|
||||||
|
} else {
|
||||||
|
// Transición a VISIBLE
|
||||||
|
current_notification_->y_offset = 0.0f;
|
||||||
|
current_notification_->state = NotificationState::VISIBLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationState::VISIBLE: {
|
||||||
|
// Esperar hasta que se cumpla la duración
|
||||||
|
Uint64 visible_time = current_notification_->duration - NOTIFICATION_FADE_TIME;
|
||||||
|
if (elapsed >= visible_time) {
|
||||||
|
current_notification_->state = NotificationState::FADING_OUT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationState::FADING_OUT: {
|
||||||
|
// Animación de salida (NOTIFICATION_FADE_TIME ms)
|
||||||
|
Uint64 fade_start = current_notification_->duration - NOTIFICATION_FADE_TIME;
|
||||||
|
Uint64 fade_elapsed = elapsed - fade_start;
|
||||||
|
|
||||||
|
if (fade_elapsed < NOTIFICATION_FADE_TIME) {
|
||||||
|
float progress = static_cast<float>(fade_elapsed) / static_cast<float>(NOTIFICATION_FADE_TIME);
|
||||||
|
float eased = Easing::easeInQuad(progress); // Fade suave
|
||||||
|
current_notification_->alpha = 1.0f - eased;
|
||||||
|
} else {
|
||||||
|
// Transición a DONE
|
||||||
|
current_notification_->alpha = 0.0f;
|
||||||
|
current_notification_->state = NotificationState::DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotificationState::DONE: {
|
||||||
|
// Eliminar notificación actual
|
||||||
|
current_notification_.reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notifier::render() {
|
||||||
|
if (!current_notification_ || !text_renderer_ || !renderer_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcular dimensiones del texto en píxeles FÍSICOS
|
||||||
|
// IMPORTANTE: Usar getTextWidthPhysical() en lugar de getTextWidth()
|
||||||
|
// para obtener el ancho REAL de la fuente (sin escalado lógico)
|
||||||
|
int text_width = text_renderer_->getTextWidthPhysical(current_notification_->text.c_str());
|
||||||
|
int text_height = text_renderer_->getTextHeight();
|
||||||
|
|
||||||
|
// Calcular dimensiones del fondo con padding (en píxeles físicos)
|
||||||
|
int bg_width = text_width + (NOTIFICATION_PADDING * 2);
|
||||||
|
int bg_height = text_height + (NOTIFICATION_PADDING * 2);
|
||||||
|
|
||||||
|
// Centrar en la ventana FÍSICA (no usar viewport lógico)
|
||||||
|
// CRÍTICO: Como renderizamos en píxeles físicos absolutos (bypass de presentación lógica),
|
||||||
|
// debemos centrar usando dimensiones físicas, no el viewport lógico de SDL
|
||||||
|
int x = (window_width_ / 2) - (bg_width / 2);
|
||||||
|
int y = NOTIFICATION_TOP_MARGIN + static_cast<int>(current_notification_->y_offset);
|
||||||
|
|
||||||
|
// Renderizar fondo semitransparente (con bypass de presentación lógica)
|
||||||
|
float bg_alpha = current_notification_->alpha * NOTIFICATION_BG_ALPHA;
|
||||||
|
renderBackground(x, y, bg_width, bg_height, bg_alpha, current_notification_->bg_color);
|
||||||
|
|
||||||
|
// Renderizar texto con alpha usando printAbsolute (tamaño físico fijo)
|
||||||
|
Uint8 text_alpha = static_cast<Uint8>(current_notification_->alpha * 255.0f);
|
||||||
|
SDL_Color text_color = current_notification_->color;
|
||||||
|
text_color.a = text_alpha;
|
||||||
|
|
||||||
|
int text_x = x + NOTIFICATION_PADDING;
|
||||||
|
int text_y = y + NOTIFICATION_PADDING;
|
||||||
|
|
||||||
|
// printAbsolute() ya maneja el bypass de presentación lógica internamente
|
||||||
|
text_renderer_->printAbsolute(text_x, text_y, current_notification_->text.c_str(), text_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notifier::renderBackground(int x, int y, int width, int height, float alpha, SDL_Color bg_color) {
|
||||||
|
if (!renderer_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear rectángulo para el fondo (en coordenadas físicas)
|
||||||
|
SDL_FRect bg_rect;
|
||||||
|
bg_rect.x = static_cast<float>(x);
|
||||||
|
bg_rect.y = static_cast<float>(y);
|
||||||
|
bg_rect.w = static_cast<float>(width);
|
||||||
|
bg_rect.h = static_cast<float>(height);
|
||||||
|
|
||||||
|
// Color del tema con alpha
|
||||||
|
Uint8 bg_alpha = static_cast<Uint8>(alpha * 255.0f);
|
||||||
|
SDL_SetRenderDrawColor(renderer_, bg_color.r, bg_color.g, bg_color.b, bg_alpha);
|
||||||
|
|
||||||
|
// Habilitar blending para transparencia
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
|
// CRÍTICO: Deshabilitar presentación lógica para renderizar en píxeles físicos absolutos
|
||||||
|
// (igual que printAbsolute() en TextRenderer)
|
||||||
|
int logical_w = 0, logical_h = 0;
|
||||||
|
SDL_RendererLogicalPresentation presentation_mode;
|
||||||
|
SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &presentation_mode);
|
||||||
|
|
||||||
|
// Renderizar sin presentación lógica (coordenadas físicas absolutas)
|
||||||
|
SDL_SetRenderLogicalPresentation(renderer_, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
|
||||||
|
SDL_RenderFillRect(renderer_, &bg_rect);
|
||||||
|
|
||||||
|
// Restaurar presentación lógica
|
||||||
|
SDL_SetRenderLogicalPresentation(renderer_, logical_w, logical_h, presentation_mode);
|
||||||
|
|
||||||
|
// Restaurar blend mode
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Notifier::isActive() const {
|
||||||
|
return (current_notification_ != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notifier::clear() {
|
||||||
|
// Vaciar cola
|
||||||
|
while (!notification_queue_.empty()) {
|
||||||
|
notification_queue_.pop();
|
||||||
|
}
|
||||||
|
// Eliminar notificación actual
|
||||||
|
current_notification_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notifier::processQueue() {
|
||||||
|
if (notification_queue_.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sacar siguiente notificación de la cola
|
||||||
|
Notification next_notif = notification_queue_.front();
|
||||||
|
notification_queue_.pop();
|
||||||
|
|
||||||
|
// Activarla como notificación actual
|
||||||
|
current_notification_ = std::make_unique<Notification>(next_notif);
|
||||||
|
}
|
||||||
108
source/ui/notifier.h
Normal file
108
source/ui/notifier.h
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <string>
|
||||||
|
#include <queue>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class TextRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sistema de notificaciones estilo iOS/Android
|
||||||
|
*
|
||||||
|
* Maneja notificaciones temporales con animaciones suaves:
|
||||||
|
* - Slide-in desde arriba
|
||||||
|
* - Fade-out al desaparecer
|
||||||
|
* - Cola FIFO de mensajes
|
||||||
|
* - Fondo semitransparente
|
||||||
|
* - Texto de tamaño fijo independiente de resolución
|
||||||
|
*/
|
||||||
|
class Notifier {
|
||||||
|
public:
|
||||||
|
enum class NotificationState {
|
||||||
|
SLIDING_IN, // Animación de entrada desde arriba
|
||||||
|
VISIBLE, // Visible estático
|
||||||
|
FADING_OUT, // Animación de salida (fade)
|
||||||
|
DONE // Completado, listo para eliminar
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Notification {
|
||||||
|
std::string text;
|
||||||
|
Uint64 created_time;
|
||||||
|
Uint64 duration;
|
||||||
|
NotificationState state;
|
||||||
|
float alpha; // Opacidad 0.0-1.0
|
||||||
|
float y_offset; // Offset Y para animación slide (píxeles)
|
||||||
|
SDL_Color color;
|
||||||
|
SDL_Color bg_color; // Color de fondo de la notificación (RGB)
|
||||||
|
};
|
||||||
|
|
||||||
|
Notifier();
|
||||||
|
~Notifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inicializa el notifier con un TextRenderer
|
||||||
|
* @param renderer SDL renderer para dibujar
|
||||||
|
* @param text_renderer TextRenderer configurado con tamaño absoluto
|
||||||
|
* @return true si inicialización exitosa
|
||||||
|
*/
|
||||||
|
bool init(SDL_Renderer* renderer, TextRenderer* text_renderer, int window_width, int window_height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Actualiza las dimensiones de la ventana (llamar en resize)
|
||||||
|
* @param window_width Nuevo ancho de ventana física
|
||||||
|
* @param window_height Nuevo alto de ventana física
|
||||||
|
*/
|
||||||
|
void updateWindowSize(int window_width, int window_height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Muestra una nueva notificación
|
||||||
|
* @param text Texto a mostrar
|
||||||
|
* @param duration Duración en milisegundos (0 = usar default)
|
||||||
|
* @param color Color del texto
|
||||||
|
* @param bg_color Color de fondo de la notificación
|
||||||
|
*/
|
||||||
|
void show(const std::string& text, Uint64 duration = 0, SDL_Color color = {255, 255, 255, 255}, SDL_Color bg_color = {0, 0, 0, 255});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Actualiza las animaciones de notificaciones
|
||||||
|
* @param current_time Tiempo actual en ms (SDL_GetTicks())
|
||||||
|
*/
|
||||||
|
void update(Uint64 current_time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Renderiza la notificación activa
|
||||||
|
*/
|
||||||
|
void render();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verifica si hay una notificación activa (visible)
|
||||||
|
* @return true si hay notificación mostrándose
|
||||||
|
*/
|
||||||
|
bool isActive() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Limpia todas las notificaciones pendientes
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_Renderer* renderer_;
|
||||||
|
TextRenderer* text_renderer_;
|
||||||
|
int window_width_;
|
||||||
|
int window_height_;
|
||||||
|
|
||||||
|
std::queue<Notification> notification_queue_;
|
||||||
|
std::unique_ptr<Notification> current_notification_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Procesa la cola y activa la siguiente notificación si es posible
|
||||||
|
*/
|
||||||
|
void processQueue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dibuja el fondo semitransparente de la notificación
|
||||||
|
*/
|
||||||
|
void renderBackground(int x, int y, int width, int height, float alpha, SDL_Color bg_color);
|
||||||
|
};
|
||||||
213
source/utils/easing_functions.h
Normal file
213
source/utils/easing_functions.h
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file easing_functions.h
|
||||||
|
* @brief Funciones de suavizado (easing) para animaciones
|
||||||
|
*
|
||||||
|
* Colección de funciones matemáticas para interpolar valores de forma suave.
|
||||||
|
* Todas las funciones toman un parámetro t (0.0 - 1.0) y devuelven un valor interpolado.
|
||||||
|
*
|
||||||
|
* Uso típico:
|
||||||
|
* float progress = elapsed_time / total_duration; // 0.0 a 1.0
|
||||||
|
* float eased = easeOutCubic(progress);
|
||||||
|
* float current_value = start + (end - start) * eased;
|
||||||
|
*
|
||||||
|
* Referencias:
|
||||||
|
* - https://easings.net/
|
||||||
|
* - Robert Penner's Easing Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace Easing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interpolación lineal (sin suavizado)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado linealmente
|
||||||
|
*
|
||||||
|
* Uso: Movimiento constante, sin aceleración
|
||||||
|
*/
|
||||||
|
inline float linear(float t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Aceleración cuadrática (slow start)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con aceleración cuadrática
|
||||||
|
*
|
||||||
|
* Uso: Inicio lento que acelera
|
||||||
|
*/
|
||||||
|
inline float easeInQuad(float t) {
|
||||||
|
return t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Desaceleración cuadrática (slow end)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con desaceleración cuadrática
|
||||||
|
*
|
||||||
|
* Uso: Llegada suave, objetos que frenan
|
||||||
|
*/
|
||||||
|
inline float easeOutQuad(float t) {
|
||||||
|
return t * (2.0f - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Aceleración y desaceleración cuadrática (slow start & end)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con aceleración y desaceleración
|
||||||
|
*
|
||||||
|
* Uso: Movimiento suave en ambos extremos
|
||||||
|
*/
|
||||||
|
inline float easeInOutQuad(float t) {
|
||||||
|
return t < 0.5f ? 2.0f * t * t : -1.0f + (4.0f - 2.0f * t) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Aceleración cúbica (slower start)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con aceleración cúbica
|
||||||
|
*
|
||||||
|
* Uso: Inicio muy lento, aceleración pronunciada
|
||||||
|
*/
|
||||||
|
inline float easeInCubic(float t) {
|
||||||
|
return t * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Desaceleración cúbica (slower end)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con desaceleración cúbica
|
||||||
|
*
|
||||||
|
* Uso: Frenado suave y natural
|
||||||
|
*/
|
||||||
|
inline float easeOutCubic(float t) {
|
||||||
|
float f = t - 1.0f;
|
||||||
|
return f * f * f + 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Aceleración y desaceleración cúbica
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con curva cúbica
|
||||||
|
*
|
||||||
|
* Uso: Animaciones muy suaves en ambos extremos
|
||||||
|
*/
|
||||||
|
inline float easeInOutCubic(float t) {
|
||||||
|
if (t < 0.5f) {
|
||||||
|
return 4.0f * t * t * t;
|
||||||
|
} else {
|
||||||
|
float f = (2.0f * t - 2.0f);
|
||||||
|
return 0.5f * f * f * f + 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Efecto elástico con rebote al final
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con rebote elástico
|
||||||
|
*
|
||||||
|
* Uso: Elementos UI que "rebotan" al llegar, notificaciones
|
||||||
|
* ⚠️ Puede sobrepasar el valor 1.0 temporalmente (overshoot)
|
||||||
|
*/
|
||||||
|
inline float easeOutElastic(float t) {
|
||||||
|
if (t == 0.0f) return 0.0f;
|
||||||
|
if (t == 1.0f) return 1.0f;
|
||||||
|
|
||||||
|
constexpr float p = 0.3f;
|
||||||
|
constexpr float s = p / 4.0f;
|
||||||
|
|
||||||
|
return powf(2.0f, -10.0f * t) * sinf((t - s) * (2.0f * 3.14159265358979323846f) / p) + 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sobrepaso suave al final (back easing)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con sobrepaso
|
||||||
|
*
|
||||||
|
* Uso: Elementos que "se pasan" levemente y vuelven, efecto cartoon
|
||||||
|
* ⚠️ Puede sobrepasar el valor 1.0 temporalmente (overshoot ~10%)
|
||||||
|
*/
|
||||||
|
inline float easeOutBack(float t) {
|
||||||
|
constexpr float c1 = 1.70158f;
|
||||||
|
constexpr float c3 = c1 + 1.0f;
|
||||||
|
|
||||||
|
return 1.0f + c3 * powf(t - 1.0f, 3.0f) + c1 * powf(t - 1.0f, 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sobrepaso suave al inicio (back easing)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con retroceso inicial
|
||||||
|
*
|
||||||
|
* Uso: Elementos que "retroceden" antes de avanzar
|
||||||
|
* ⚠️ Puede generar valores negativos al inicio (undershoot ~10%)
|
||||||
|
*/
|
||||||
|
inline float easeInBack(float t) {
|
||||||
|
constexpr float c1 = 1.70158f;
|
||||||
|
constexpr float c3 = c1 + 1.0f;
|
||||||
|
|
||||||
|
return c3 * t * t * t - c1 * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rebote físico al final (bounce effect)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con rebotes
|
||||||
|
*
|
||||||
|
* Uso: Objetos que caen y rebotan, físicas cartoon
|
||||||
|
*/
|
||||||
|
inline float easeOutBounce(float t) {
|
||||||
|
constexpr float n1 = 7.5625f;
|
||||||
|
constexpr float d1 = 2.75f;
|
||||||
|
|
||||||
|
if (t < 1.0f / d1) {
|
||||||
|
return n1 * t * t;
|
||||||
|
} else if (t < 2.0f / d1) {
|
||||||
|
t -= 1.5f / d1;
|
||||||
|
return n1 * t * t + 0.75f;
|
||||||
|
} else if (t < 2.5f / d1) {
|
||||||
|
t -= 2.25f / d1;
|
||||||
|
return n1 * t * t + 0.9375f;
|
||||||
|
} else {
|
||||||
|
t -= 2.625f / d1;
|
||||||
|
return n1 * t * t + 0.984375f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Aceleración exponencial (muy dramática)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado exponencialmente
|
||||||
|
*
|
||||||
|
* Uso: Efectos dramáticos, zoom in rápido
|
||||||
|
*/
|
||||||
|
inline float easeInExpo(float t) {
|
||||||
|
return t == 0.0f ? 0.0f : powf(2.0f, 10.0f * (t - 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Desaceleración exponencial (muy dramática)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado exponencialmente
|
||||||
|
*
|
||||||
|
* Uso: Fade outs dramáticos, zoom out rápido
|
||||||
|
*/
|
||||||
|
inline float easeOutExpo(float t) {
|
||||||
|
return t == 1.0f ? 1.0f : 1.0f - powf(2.0f, -10.0f * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Curva circular (cuarto de círculo)
|
||||||
|
* @param t Progreso normalizado (0.0 - 1.0)
|
||||||
|
* @return Valor interpolado con curva circular
|
||||||
|
*
|
||||||
|
* Uso: Movimientos muy suaves y naturales
|
||||||
|
*/
|
||||||
|
inline float easeOutCirc(float t) {
|
||||||
|
return sqrtf(1.0f - powf(t - 1.0f, 2.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Easing
|
||||||
Reference in New Issue
Block a user