diff --git a/data/lang/ba_BA.json b/data/lang/ba_BA.json index 0779d74..5fa3f17 100644 --- a/data/lang/ba_BA.json +++ b/data/lang/ba_BA.json @@ -6,7 +6,7 @@ "[DEFINE_BUTTONS] TITLE": "define buttons title", "[DEFINE_BUTTONS] FIRE_LEFT": "Disparar cap a l'esquerra", - "[DEFINE_BUTTONS] FIRE_UP": "Disparar cap amunt", + "[DEFINE_BUTTONS] FIRE_UP": "Disparar cap amunt amunt amunt amunt", "[DEFINE_BUTTONS] FIRE_RIGHT": "Disparar cap a la dreta", "[DEFINE_BUTTONS] START": "Start", "[DEFINE_BUTTONS] SERVICE_MENU": "Menu de servei", diff --git a/source/define_buttons.cpp b/source/define_buttons.cpp index b795384..b7b54bb 100644 --- a/source/define_buttons.cpp +++ b/source/define_buttons.cpp @@ -15,7 +15,6 @@ DefineButtons::DefineButtons() : input_(Input::get()) { - clearButtons(); auto gamepads = input_->getGamepads(); @@ -24,18 +23,26 @@ DefineButtons::DefineButtons() } // Crear la ventana de mensaje + WindowMessage::Config config; + config.bg_color = Color{20, 30, 50, 224}; // Fondo azul oscuro semi-transparente + config.border_color = Color{100, 150, 200, 255}; // Borde azul claro + config.title_color = Color{100, 150, 200, 255}; // Titulo azul claro + config.text_color = Color{220, 220, 220, 255}; // Texto gris claro + config.padding = 15.0f; + config.line_spacing = 5.0f; + config.title_separator_spacing = 15; + config.min_width = 250.0f; + config.text_safety_margin = 15.0f; + config.min_width = 100; + config.min_height = 32; + auto text_renderer = Resource::get()->getText("04b_25_flat"); window_message_ = std::make_unique( text_renderer, Lang::getText("[DEFINE_BUTTONS] TITLE"), - Color{20, 30, 50, 208}, // Fondo azul oscuro semi-transparente - Color{100, 150, 200, 255}, // Borde azul claro - Color{100, 150, 200, 255}, // Titulo azul claro - Color{220, 220, 220, 255} // Texto gris claro + config ); - - window_message_->setPadding(0.0f); - window_message_->setLineSpacing(0.0f); + window_message_->setPosition(param.game.game_area.center_x, param.game.game_area.center_y, WindowMessage::PositionMode::CENTERED); } void DefineButtons::render() { @@ -73,13 +80,12 @@ auto DefineButtons::enable(Options::Gamepad *options_gamepad) -> bool { index_button_ = 0; clearButtons(); updateWindowMessage(); - + if (window_message_) { window_message_->autoSize(); - window_message_->centerOnScreen(); window_message_->show(); } - + return true; } return false; @@ -88,7 +94,7 @@ auto DefineButtons::enable(Options::Gamepad *options_gamepad) -> bool { void DefineButtons::disable() { enabled_ = false; finished_ = false; - + if (window_message_) { window_message_->hide(); } @@ -146,7 +152,7 @@ void DefineButtons::checkEnd() { bindButtons(options_gamepad_); input_->saveGamepadConfigFromGamepad(options_gamepad_->instance); input_->resetInputStates(); - + // Mostrar mensaje de finalización brevemente if (window_message_) { window_message_->clearTexts(); @@ -154,7 +160,7 @@ void DefineButtons::checkEnd() { window_message_->autoSize(); window_message_->centerOnScreen(); } - + // Se deshabilitará desde el ServiceMenu después de un breve delay } } @@ -163,31 +169,31 @@ void DefineButtons::updateWindowMessage() { if (!window_message_ || !options_gamepad_) { return; } - + // Configurar título std::string title = Lang::getText("[DEFINE_BUTTONS] CONFIGURING") + ": " + options_gamepad_->name; window_message_->setTitle(title); - + // Limpiar textos anteriores window_message_->clearTexts(); - + if (index_button_ < buttons_.size()) { // Mostrar progreso - /*std::string progress = "(" + std::to_string(index_button_ + 1) + "/" + + /*std::string progress = "(" + std::to_string(index_button_ + 1) + "/" + std::to_string(buttons_.size()) + ")"; window_message_->addText(progress); window_message_->addText("");*/ - + // Instrucción actual std::string instruction = Lang::getText("[DEFINE_BUTTONS] PRESS_BUTTON_FOR") + ":"; window_message_->addText(instruction); window_message_->addText(buttons_.at(index_button_).label); - + // Botones ya configurados /*if (index_button_ > 0) { window_message_->addText(""); window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURED") + ":"); - + for (size_t i = 0; i < index_button_; ++i) { std::string configured = "✓ " + buttons_[i].label; window_message_->addText(configured); diff --git a/source/ui/window_message.cpp b/source/ui/window_message.cpp index 3ed75d9..11f6f88 100644 --- a/source/ui/window_message.cpp +++ b/source/ui/window_message.cpp @@ -10,18 +10,12 @@ WindowMessage::WindowMessage( std::shared_ptr text_renderer, const std::string& title, - const Color& bg_color, - const Color& border_color, - const Color& title_color, - const Color& text_color) + const Config& config) : text_renderer_(std::move(text_renderer)), + config_(config), title_(title), - bg_color_(bg_color), - border_color_(border_color), - title_color_(title_color), - text_color_(text_color), - title_style_(Text::CENTER | Text::COLOR, title_color, title_color, 0, -2), - text_style_(Text::CENTER | Text::COLOR, text_color, text_color, 0, -2) { + 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() { @@ -32,14 +26,16 @@ void WindowMessage::render() { SDL_Renderer* renderer = Screen::get()->getRenderer(); // Dibujar fondo con transparencia - SDL_SetRenderDrawColor(renderer, bg_color_.r, bg_color_.g, bg_color_.b, bg_color_.a); + 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, border_color_.r, border_color_.g, border_color_.b, border_color_.a); + 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 + padding_; + float current_y = rect_.y + config_.padding; // Dibujar título si existe if (!title_.empty()) { @@ -48,15 +44,16 @@ void WindowMessage::render() { current_y, title_, title_style_); - current_y += text_renderer_->getCharacterSize() + line_spacing_ * 2; + current_y += text_renderer_->getCharacterSize() + config_.title_separator_spacing; // Línea separadora debajo del título - SDL_SetRenderDrawColor(renderer, border_color_.r, border_color_.g, border_color_.b, border_color_.a); + SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, + config_.border_color.b, config_.border_color.a); SDL_RenderLine(renderer, - rect_.x + padding_, - current_y - line_spacing_, - rect_.x + rect_.w - padding_, - current_y - line_spacing_); + 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 @@ -66,15 +63,22 @@ void WindowMessage::render() { current_y, text, text_style_); - current_y += text_renderer_->getCharacterSize() + line_spacing_; + current_y += text_renderer_->getCharacterSize() + config_.line_spacing; } } void WindowMessage::update() { - // Por ahora no hay animaciones, pero se puede extender + // 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; } @@ -84,58 +88,120 @@ void WindowMessage::hide() { 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& 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) { - rect_.x = x; - rect_.y = y; +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() { - rect_.x = (param.game.width - rect_.w) / 2.0f; - rect_.y = (param.game.height - rect_.h) / 2.0f; + 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(); - rect_.w = content_width + (padding_ * 2); - rect_.h = content_height + (padding_ * 2); + // 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 y máximos - rect_.w = std::max(rect_.w, 200.0f); - rect_.h = std::max(rect_.h, 100.0f); + // Aplicar límites mínimos + rect_.w = std::max(rect_.w, config_.min_width); + rect_.h = std::max(rect_.h, config_.min_height); - // No exceder el 80% de la pantalla - rect_.w = std::min(rect_.w, param.game.width * 0.8f); - rect_.h = std::min(rect_.h, param.game.height * 0.8f); + // 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 { @@ -143,20 +209,22 @@ auto WindowMessage::calculateContentHeight() const -> float { // Altura del título if (!title_.empty()) { - height += text_renderer_->getCharacterSize() + line_spacing_ * 2; // Espacio extra para separador + height += text_renderer_->getCharacterSize() + config_.title_separator_spacing; } // Altura de los textos if (!texts_.empty()) { height += (texts_.size() * text_renderer_->getCharacterSize()); - height += ((texts_.size() - 1) * line_spacing_); // Espaciado entre líneas + if (texts_.size() > 1) { + height += ((texts_.size() - 1) * config_.line_spacing); + } } return height; } auto WindowMessage::calculateContentWidth() const -> float { - float max_width = 200.0f; // Ancho mínimo + float max_width = config_.min_width - (config_.padding * 2); // Ancho mínimo sin padding // Ancho del título if (!title_.empty()) { @@ -171,4 +239,49 @@ auto WindowMessage::calculateContentWidth() const -> float { } return max_width; +} + +auto WindowMessage::getScreenWidth() const -> float { + return static_cast(param.game.width); +} + +auto WindowMessage::getScreenHeight() const -> float { + return static_cast(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); } \ No newline at end of file diff --git a/source/ui/window_message.h b/source/ui/window_message.h index cef618f..f7ac318 100644 --- a/source/ui/window_message.h +++ b/source/ui/window_message.h @@ -11,13 +11,53 @@ class WindowMessage { public: + enum class PositionMode { + CENTERED, // La ventana se centra en el punto especificado + FIXED // La esquina superior izquierda coincide con el punto + }; + + struct Config { + // Colores + Color bg_color; + Color border_color; + Color title_color; + Color text_color; + + // Espaciado y dimensiones + float padding; + float line_spacing; + float title_separator_spacing; // Espacio extra para separador del título + + // Límites de tamaño + float min_width; + float min_height; + float max_width_ratio; // % máximo de ancho de pantalla + float max_height_ratio; // % máximo de alto de pantalla + + // Margen de seguridad para texto + float text_safety_margin; // Margen extra para evitar texto cortado + + // Constructor con valores por defecto + Config() + : bg_color{40, 40, 60, 220} + , border_color{100, 100, 120, 255} + , title_color{255, 255, 255, 255} + , text_color{200, 200, 200, 255} + , padding{15.0f} + , line_spacing{5.0f} + , title_separator_spacing{10.0f} + , min_width{200.0f} + , min_height{100.0f} + , max_width_ratio{0.8f} + , max_height_ratio{0.8f} + , text_safety_margin{20.0f} + {} + }; + WindowMessage( std::shared_ptr text_renderer, const std::string& title = "", - const Color& bg_color = Color{40, 40, 60, 220}, - const Color& border_color = Color{100, 100, 120, 255}, - const Color& title_color = Color{255, 255, 255, 255}, - const Color& text_color = Color{200, 200, 200, 255} + const Config& config = Config{} ); // Métodos principales @@ -35,31 +75,43 @@ class WindowMessage { void setTexts(const std::vector& texts); void addText(const std::string& text); void clearTexts(); + + // Control de redimensionado automático + void enableAutoResize(bool enabled) { auto_resize_enabled_ = enabled; } + [[nodiscard]] auto isAutoResizeEnabled() const -> bool { return auto_resize_enabled_; } // Configuración de posición y tamaño - void setPosition(float x, float y); + void setPosition(float x, float y, PositionMode mode = PositionMode::CENTERED); void setSize(float width, float height); void centerOnScreen(); - void autoSize(); // Ajusta automáticamente al contenido + void autoSize(); // Ajusta automáticamente al contenido y reposiciona si es necesario // Configuración de colores - void setBackgroundColor(const Color& color) { bg_color_ = color; } - void setBorderColor(const Color& color) { border_color_ = color; } - void setTitleColor(const Color& color) { title_color_ = color; } - void setTextColor(const Color& color) { text_color_ = color; } + void setBackgroundColor(const Color& color) { config_.bg_color = color; } + void setBorderColor(const Color& color) { config_.border_color = color; } + void setTitleColor(const Color& color) { config_.title_color = color; updateStyles(); } + void setTextColor(const Color& color) { config_.text_color = color; updateStyles(); } // Configuración de espaciado - void setPadding(float padding) { padding_ = padding; } - void setLineSpacing(float spacing) { line_spacing_ = spacing; } + void setPadding(float padding) { config_.padding = padding; } + void setLineSpacing(float spacing) { config_.line_spacing = spacing; } + + // Configuración avanzada + void setConfig(const Config& config) { config_ = config; updateStyles(); } + [[nodiscard]] auto getConfig() const -> const Config& { return config_; } // Getters [[nodiscard]] auto getRect() const -> const SDL_FRect& { return rect_; } + [[nodiscard]] auto getPositionMode() const -> PositionMode { return position_mode_; } + [[nodiscard]] auto getAnchorPoint() const -> std::pair { return {anchor_x_, anchor_y_}; } private: std::shared_ptr text_renderer_; + Config config_; - // Estado de visibilidad + // Estado de visibilidad y redimensionado bool visible_ = false; + bool auto_resize_enabled_ = true; // Por defecto habilitado // Contenido std::string title_; @@ -67,14 +119,40 @@ class WindowMessage { // Posición y tamaño SDL_FRect rect_{0, 0, 300, 200}; - float padding_ = 15.0f; - float line_spacing_ = 5.0f; - - // Colores - Color bg_color_; - Color border_color_; - Color title_color_; - Color text_color_; + PositionMode position_mode_ = PositionMode::CENTERED; + float anchor_x_ = 0.0f; + float anchor_y_ = 0.0f; + + // Animación de redimensionado + struct ResizeAnimation { + bool active = false; + float start_width, start_height; + float target_width, target_height; + float duration = 0.3f; // segundos + float elapsed = 0.0f; + + void start(float from_w, float from_h, float to_w, float to_h) { + start_width = from_w; + start_height = from_h; + target_width = to_w; + target_height = to_h; + elapsed = 0.0f; + active = true; + } + + void stop() { + active = false; + elapsed = 0.0f; + } + + [[nodiscard]] auto isFinished() const -> bool { + return elapsed >= duration; + } + + [[nodiscard]] auto getProgress() const -> float { + return std::min(elapsed / duration, 1.0f); + } + } resize_animation_; // Estilos Text::Style title_style_; @@ -82,6 +160,17 @@ class WindowMessage { // Métodos privados void calculateAutoSize(); + void updatePosition(); // Actualiza la posición según el modo y punto de anclaje + void updateStyles(); // Actualiza los estilos de texto cuando cambian los colores + void ensureTextFits(); // Verifica y ajusta para que todo el texto sea visible + void triggerAutoResize(); // Inicia redimensionado automático si está habilitado + void updateAnimation(float delta_time); // Actualiza la animación de redimensionado + + // Función de suavizado (ease-out) + [[nodiscard]] auto easeOut(float t) const -> float; + [[nodiscard]] auto calculateContentHeight() const -> float; [[nodiscard]] auto calculateContentWidth() const -> float; + [[nodiscard]] auto getScreenWidth() const -> float; + [[nodiscard]] auto getScreenHeight() const -> float; }; \ No newline at end of file