Problema resuelto: 1. Color del tema saliente: Notificaciones mostraban color del tema ANTIGUO 2. Sin transiciones LERP: Notificaciones no participaban en transiciones suaves Cambios implementados: - Arquitectura cambiada de estática a dinámica - Notifier ahora consulta ThemeManager cada frame en render() - Eliminados colores estáticos de struct Notification - Notifier::init() recibe puntero a ThemeManager - Notifier::show() ya no recibe parámetros de color - Simplificado showNotificationForAction() (-23 líneas) Fix crítico de inicialización: - ThemeManager ahora se inicializa ANTES de updatePhysicalWindowSize() - Previene nullptr en notifier_.init() que causaba que no se mostraran Resultado: - ✅ Notificaciones usan color del tema DESTINO (no origen) - ✅ Transiciones LERP suaves automáticas durante cambios de tema - ✅ Código más limpio y centralizado en ThemeManager - ✅ -50 líneas de código duplicado eliminadas 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
232 lines
8.4 KiB
C++
232 lines
8.4 KiB
C++
#include "notifier.h"
|
|
#include "../text/textrenderer.h"
|
|
#include "../theme_manager.h"
|
|
#include "../defines.h"
|
|
#include "../utils/easing_functions.h"
|
|
#include <SDL3/SDL.h>
|
|
|
|
Notifier::Notifier()
|
|
: renderer_(nullptr)
|
|
, text_renderer_(nullptr)
|
|
, theme_manager_(nullptr)
|
|
, window_width_(0)
|
|
, window_height_(0)
|
|
, current_notification_(nullptr) {
|
|
}
|
|
|
|
Notifier::~Notifier() {
|
|
clear();
|
|
}
|
|
|
|
bool Notifier::init(SDL_Renderer* renderer, TextRenderer* text_renderer, ThemeManager* theme_manager, int window_width, int window_height) {
|
|
renderer_ = renderer;
|
|
text_renderer_ = text_renderer;
|
|
theme_manager_ = theme_manager;
|
|
window_width_ = window_width;
|
|
window_height_ = window_height;
|
|
return (renderer_ != nullptr && text_renderer_ != nullptr && theme_manager_ != 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) {
|
|
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)
|
|
// NOTA: Los colores se obtienen dinámicamente desde ThemeManager en render()
|
|
|
|
// 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_ || !theme_manager_) {
|
|
return;
|
|
}
|
|
|
|
// Obtener colores DINÁMICOS desde ThemeManager (incluye LERP automático)
|
|
int text_r, text_g, text_b;
|
|
theme_manager_->getCurrentThemeTextColor(text_r, text_g, text_b);
|
|
SDL_Color text_color = {
|
|
static_cast<Uint8>(text_r),
|
|
static_cast<Uint8>(text_g),
|
|
static_cast<Uint8>(text_b),
|
|
static_cast<Uint8>(current_notification_->alpha * 255.0f)
|
|
};
|
|
|
|
int bg_r, bg_g, bg_b;
|
|
theme_manager_->getCurrentNotificationBackgroundColor(bg_r, bg_g, bg_b);
|
|
SDL_Color bg_color = {
|
|
static_cast<Uint8>(bg_r),
|
|
static_cast<Uint8>(bg_g),
|
|
static_cast<Uint8>(bg_b),
|
|
255
|
|
};
|
|
|
|
// 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, bg_color);
|
|
|
|
// Renderizar texto con alpha usando printAbsolute (tamaño físico fijo)
|
|
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);
|
|
}
|