Files
coffee_crisis_arcade_edition/source/ui/window_message.cpp

287 lines
8.8 KiB
C++

#include "window_message.h"
#include <algorithm>
#include <utility>
#include "param.h"
#include "screen.h"
#include "text.h"
WindowMessage::WindowMessage(
std::shared_ptr<Text> text_renderer,
const std::string& title,
const Config& config)
: text_renderer_(std::move(text_renderer)),
config_(config),
title_(title),
title_style_(Text::CENTER | Text::COLOR, config_.title_color, config_.title_color, 0, -2),
text_style_(Text::CENTER | Text::COLOR, config_.text_color, config_.text_color, 0, -2) {
}
void WindowMessage::render() {
if (!visible_) {
return;
}
SDL_Renderer* renderer = Screen::get()->getRenderer();
// Dibujar fondo con transparencia
SDL_SetRenderDrawColor(renderer, config_.bg_color.r, config_.bg_color.g,
config_.bg_color.b, config_.bg_color.a);
SDL_RenderFillRect(renderer, &rect_);
// Dibujar borde
SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g,
config_.border_color.b, config_.border_color.a);
SDL_RenderRect(renderer, &rect_);
float current_y = rect_.y + config_.padding;
// Dibujar título si existe
if (!title_.empty()) {
text_renderer_->writeStyle(
rect_.x + rect_.w / 2.0f,
current_y,
title_,
title_style_);
current_y += text_renderer_->getCharacterSize() + config_.title_separator_spacing;
// Línea separadora debajo del título
SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g,
config_.border_color.b, config_.border_color.a);
SDL_RenderLine(renderer,
rect_.x + config_.padding,
current_y - config_.title_separator_spacing / 2.0f,
rect_.x + rect_.w - config_.padding,
current_y - config_.title_separator_spacing / 2.0f);
}
// Dibujar textos
for (const auto& text : texts_) {
text_renderer_->writeStyle(
rect_.x + rect_.w / 2.0f,
current_y,
text,
text_style_);
current_y += text_renderer_->getCharacterSize() + config_.line_spacing;
}
}
void WindowMessage::update() {
// Actualizar animación de redimensionado
if (resize_animation_.active) {
// Aquí necesitarías el delta_time del game loop
// Por ahora usamos un valor fijo, pero idealmente se pasaría como parámetro
float delta_time = 1.0f / 60.0f; // Asumiendo 60 FPS
updateAnimation(delta_time);
}
}
void WindowMessage::show() {
ensureTextFits();
visible_ = true;
}
void WindowMessage::hide() {
visible_ = false;
}
void WindowMessage::setTitle(const std::string& title) {
title_ = title;
triggerAutoResize();
}
void WindowMessage::setText(const std::string& text) {
texts_.clear();
texts_.push_back(text);
triggerAutoResize();
}
void WindowMessage::setTexts(const std::vector<std::string>& texts) {
texts_ = texts;
triggerAutoResize();
}
void WindowMessage::addText(const std::string& text) {
texts_.push_back(text);
triggerAutoResize();
}
void WindowMessage::clearTexts() {
texts_.clear();
triggerAutoResize();
}
void WindowMessage::setPosition(float x, float y, PositionMode mode) {
anchor_x_ = x;
anchor_y_ = y;
position_mode_ = mode;
updatePosition();
}
void WindowMessage::setSize(float width, float height) {
rect_.w = width;
rect_.h = height;
updatePosition(); // Reposicionar después de cambiar el tamaño
}
void WindowMessage::centerOnScreen() {
setPosition(getScreenWidth() / 2.0f, getScreenHeight() / 2.0f, PositionMode::CENTERED);
}
void WindowMessage::autoSize() {
if (resize_animation_.active) {
resize_animation_.stop(); // Detener animación anterior
}
float old_width = rect_.w;
float old_height = rect_.h;
calculateAutoSize();
// Solo animar si hay cambio en el tamaño y la ventana está visible
if (visible_ && (old_width != rect_.w || old_height != rect_.h)) {
resize_animation_.start(old_width, old_height, rect_.w, rect_.h);
// Restaurar el tamaño anterior para que la animación funcione
rect_.w = old_width;
rect_.h = old_height;
} else {
updatePosition(); // Reposicionar después de ajustar el tamaño
}
}
void WindowMessage::updateStyles() {
title_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.title_color,
config_.title_color, 0, -2);
text_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.text_color,
config_.text_color, 0, -2);
}
void WindowMessage::updatePosition() {
switch (position_mode_) {
case PositionMode::CENTERED:
rect_.x = anchor_x_ - rect_.w / 2.0f;
rect_.y = anchor_y_ - rect_.h / 2.0f;
break;
case PositionMode::FIXED:
rect_.x = anchor_x_;
rect_.y = anchor_y_;
break;
}
// Asegurar que la ventana esté dentro de los límites de la pantalla
rect_.x = std::max(0.0f, std::min(rect_.x, getScreenWidth() - rect_.w));
rect_.y = std::max(0.0f, std::min(rect_.y, getScreenHeight() - rect_.h));
}
void WindowMessage::ensureTextFits() {
float required_width = calculateContentWidth() + (config_.padding * 2) + config_.text_safety_margin;
float required_height = calculateContentHeight() + (config_.padding * 2) + config_.text_safety_margin;
// Verificar si el tamaño actual es suficiente
if (rect_.w < required_width || rect_.h < required_height) {
autoSize(); // Recalcular tamaño automáticamente
}
}
void WindowMessage::calculateAutoSize() {
float content_width = calculateContentWidth();
float content_height = calculateContentHeight();
// Calcular dimensiones con padding y margen de seguridad
rect_.w = content_width + (config_.padding * 2) + config_.text_safety_margin;
rect_.h = content_height + (config_.padding * 2);
// Aplicar límites mínimos
rect_.w = std::max(rect_.w, config_.min_width);
rect_.h = std::max(rect_.h, config_.min_height);
// Aplicar límites máximos basados en el tamaño de pantalla
float max_width = getScreenWidth() * config_.max_width_ratio;
float max_height = getScreenHeight() * config_.max_height_ratio;
rect_.w = std::min(rect_.w, max_width);
rect_.h = std::min(rect_.h, max_height);
}
auto WindowMessage::calculateContentHeight() const -> float {
float height = 0;
// Altura del título
if (!title_.empty()) {
height += text_renderer_->getCharacterSize() + config_.title_separator_spacing;
}
// Altura de los textos
if (!texts_.empty()) {
height += (texts_.size() * text_renderer_->getCharacterSize());
if (texts_.size() > 1) {
height += ((texts_.size() - 1) * config_.line_spacing);
}
}
return height;
}
auto WindowMessage::calculateContentWidth() const -> float {
float max_width = config_.min_width - (config_.padding * 2); // Ancho mínimo sin padding
// Ancho del título
if (!title_.empty()) {
float title_width = text_renderer_->length(title_, -2);
max_width = std::max(max_width, title_width);
}
// Ancho de los textos
for (const auto& text : texts_) {
float text_width = text_renderer_->length(text, -2);
max_width = std::max(max_width, text_width);
}
return max_width;
}
auto WindowMessage::getScreenWidth() const -> float {
return static_cast<float>(param.game.width);
}
auto WindowMessage::getScreenHeight() const -> float {
return static_cast<float>(param.game.height);
}
void WindowMessage::triggerAutoResize() {
if (auto_resize_enabled_) {
autoSize();
}
}
void WindowMessage::updateAnimation(float delta_time) {
if (!resize_animation_.active) {
return;
}
resize_animation_.elapsed += delta_time;
if (resize_animation_.isFinished()) {
// Animación terminada
rect_.w = resize_animation_.target_width;
rect_.h = resize_animation_.target_height;
resize_animation_.stop();
updatePosition();
} else {
// Interpolar el tamaño
float progress = easeOut(resize_animation_.getProgress());
rect_.w = resize_animation_.start_width +
(resize_animation_.target_width - resize_animation_.start_width) * progress;
rect_.h = resize_animation_.start_height +
(resize_animation_.target_height - resize_animation_.start_height) * progress;
updatePosition(); // Mantener la posición centrada durante la animación
}
}
auto WindowMessage::easeOut(float t) const -> float {
// Función de suavizado ease-out cuadrática
return 1.0f - (1.0f - t) * (1.0f - t);
}