claude: treballant en el nou define_buttons
This commit is contained in:
@@ -1,69 +1,55 @@
|
||||
#include "define_buttons.h"
|
||||
|
||||
#include <algorithm> // Para __all_of_fn, all_of
|
||||
#include <functional> // Para identity
|
||||
#include <memory> // Para allocator, shared_ptr, __shared_ptr_access, operator==
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "input.h" // Para Input
|
||||
#include "input_types.h" // Para InputAction
|
||||
#include "lang.h" // Para getText
|
||||
#include "options.h" // Para Gamepad
|
||||
#include "param.h" // Para Param, param, ParamGame, ParamTitle
|
||||
#include "resource.h" // Para Resource
|
||||
#include "text.h" // Para Text
|
||||
#include "input.h"
|
||||
#include "input_types.h"
|
||||
#include "lang.h"
|
||||
#include "options.h"
|
||||
#include "param.h"
|
||||
#include "resource.h"
|
||||
#include "text.h"
|
||||
#include "ui/window_message.h"
|
||||
|
||||
// Constructor
|
||||
DefineButtons::DefineButtons()
|
||||
: input_(Input::get()),
|
||||
: input_(Input::get()) {
|
||||
|
||||
x_(param.game.width / 2),
|
||||
y_(param.title.press_start_position) {
|
||||
clearButtons();
|
||||
|
||||
auto gamepads = input_->getGamepads();
|
||||
for (auto gamepad : gamepads) {
|
||||
controller_names_.emplace_back(Input::getControllerName(gamepad));
|
||||
}
|
||||
|
||||
// Crear la ventana de mensaje
|
||||
auto text_renderer = Resource::get()->getText("04b_25_flat");
|
||||
window_message_ = std::make_unique<WindowMessage>(
|
||||
text_renderer,
|
||||
Lang::getText("[DEFINE_BUTTONS] TITLE"),
|
||||
Color{20, 30, 50, 200}, // Fondo azul oscuro semi-transparente
|
||||
Color{100, 150, 200, 255}, // Borde azul claro
|
||||
Color{255, 255, 255, 255}, // Título blanco
|
||||
Color{220, 220, 220, 255} // Texto gris claro
|
||||
);
|
||||
|
||||
window_message_->setPadding(20.0f);
|
||||
window_message_->setLineSpacing(8.0f);
|
||||
}
|
||||
|
||||
// Dibuja el objeto en pantalla
|
||||
void DefineButtons::render() {
|
||||
static auto text_ = Resource::get()->getText("8bithud");
|
||||
if (enabled_) {
|
||||
text_->writeCentered(x_, y_ - 10, Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(static_cast<int>(options_gamepad_->player_id)));
|
||||
text_->writeCentered(x_, y_, options_gamepad_->name);
|
||||
text_->writeCentered(x_, y_ + 10, buttons_.at(index_button_).label);
|
||||
if (enabled_ && window_message_) {
|
||||
window_message_->render();
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba el botón que se ha pulsado
|
||||
void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event) {
|
||||
auto gamepad = input_->getGamepad(event.which);
|
||||
|
||||
// Asegúrate de que el gamepad sea válido y sea el que corresponde
|
||||
if (!gamepad || gamepad != options_gamepad_->instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto BUTTON = static_cast<SDL_GamepadButton>(event.button);
|
||||
if (checkButtonNotInUse(BUTTON)) {
|
||||
buttons_.at(index_button_).button = BUTTON;
|
||||
incIndexButton();
|
||||
void DefineButtons::update() {
|
||||
if (enabled_ && window_message_) {
|
||||
window_message_->update();
|
||||
}
|
||||
}
|
||||
|
||||
// Asigna los botones definidos al input_
|
||||
void DefineButtons::bindButtons(Options::Gamepad *options_gamepad) {
|
||||
for (const auto &button : buttons_) {
|
||||
Input::bindGameControllerButton(options_gamepad->instance, button.action, button.button);
|
||||
}
|
||||
|
||||
// Remapea los inputs a inputs
|
||||
Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_SELECT, Input::Action::FIRE_LEFT);
|
||||
Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_BACK, Input::Action::FIRE_CENTER);
|
||||
}
|
||||
|
||||
// Comprueba los eventos
|
||||
void DefineButtons::checkEvents(const SDL_Event &event) {
|
||||
if (enabled_) {
|
||||
switch (event.type) {
|
||||
@@ -79,7 +65,6 @@ void DefineButtons::checkEvents(const SDL_Event &event) {
|
||||
}
|
||||
}
|
||||
|
||||
// Habilita el objeto
|
||||
auto DefineButtons::enable(Options::Gamepad *options_gamepad) -> bool {
|
||||
if (options_gamepad != nullptr) {
|
||||
options_gamepad_ = options_gamepad;
|
||||
@@ -87,12 +72,52 @@ auto DefineButtons::enable(Options::Gamepad *options_gamepad) -> bool {
|
||||
finished_ = false;
|
||||
index_button_ = 0;
|
||||
clearButtons();
|
||||
updateWindowMessage();
|
||||
|
||||
if (window_message_) {
|
||||
window_message_->autoSize();
|
||||
window_message_->centerOnScreen();
|
||||
window_message_->show();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Incrementa el indice de los botones
|
||||
void DefineButtons::disable() {
|
||||
enabled_ = false;
|
||||
finished_ = false;
|
||||
|
||||
if (window_message_) {
|
||||
window_message_->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event) {
|
||||
auto gamepad = input_->getGamepad(event.which);
|
||||
|
||||
if (!gamepad || gamepad != options_gamepad_->instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto BUTTON = static_cast<SDL_GamepadButton>(event.button);
|
||||
if (checkButtonNotInUse(BUTTON)) {
|
||||
buttons_.at(index_button_).button = BUTTON;
|
||||
incIndexButton();
|
||||
updateWindowMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void DefineButtons::bindButtons(Options::Gamepad *options_gamepad) {
|
||||
for (const auto &button : buttons_) {
|
||||
Input::bindGameControllerButton(options_gamepad->instance, button.action, button.button);
|
||||
}
|
||||
|
||||
Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_SELECT, Input::Action::FIRE_LEFT);
|
||||
Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_BACK, Input::Action::FIRE_CENTER);
|
||||
}
|
||||
|
||||
void DefineButtons::incIndexButton() {
|
||||
if (index_button_ < buttons_.size() - 1) {
|
||||
++index_button_;
|
||||
@@ -101,14 +126,12 @@ void DefineButtons::incIndexButton() {
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba que un botón no esté ya asignado
|
||||
auto DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) -> bool {
|
||||
return std::ranges::all_of(buttons_, [button](const auto &b) {
|
||||
return b.button != button;
|
||||
});
|
||||
}
|
||||
|
||||
// Limpia la asignación de botones
|
||||
void DefineButtons::clearButtons() {
|
||||
buttons_.clear();
|
||||
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), Input::Action::FIRE_LEFT, SDL_GAMEPAD_BUTTON_INVALID);
|
||||
@@ -118,12 +141,57 @@ void DefineButtons::clearButtons() {
|
||||
buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] SERVICE_MENU"), Input::Action::SERVICE, SDL_GAMEPAD_BUTTON_INVALID);
|
||||
}
|
||||
|
||||
// Comprueba si ha finalizado
|
||||
void DefineButtons::checkEnd() {
|
||||
if (finished_) {
|
||||
bindButtons(options_gamepad_); // Asigna los botones definidos al input_
|
||||
input_->saveGamepadConfigFromGamepad(options_gamepad_->instance); // Guarda los cambios
|
||||
input_->resetInputStates(); // Reinicia los estados de las pulsaciones de los botones
|
||||
enabled_ = false; // Deshabilita
|
||||
bindButtons(options_gamepad_);
|
||||
input_->saveGamepadConfigFromGamepad(options_gamepad_->instance);
|
||||
input_->resetInputStates();
|
||||
|
||||
// Mostrar mensaje de finalización brevemente
|
||||
if (window_message_) {
|
||||
window_message_->clearTexts();
|
||||
window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURATION_COMPLETE"));
|
||||
window_message_->autoSize();
|
||||
window_message_->centerOnScreen();
|
||||
}
|
||||
|
||||
// Se deshabilitará desde el ServiceMenu después de un breve delay
|
||||
}
|
||||
}
|
||||
|
||||
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::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_GamepadButton, SDL_Event, SDL_GamepadButtonEvent
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cstddef> // Para size_t
|
||||
#include <string> // Para string
|
||||
#include <utility> // Para move
|
||||
#include <vector> // Para vector
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "input.h" // Para Input
|
||||
#include "input.h"
|
||||
|
||||
class WindowMessage;
|
||||
|
||||
namespace Options {
|
||||
struct Gamepad;
|
||||
} // namespace Options
|
||||
}
|
||||
|
||||
// Clase DefineButtons
|
||||
class DefineButtons {
|
||||
public:
|
||||
// Estructura para definir botones
|
||||
struct Button {
|
||||
std::string label; // Texto en pantalla
|
||||
Input::Action action; // Acción asociada
|
||||
SDL_GamepadButton button; // Botón del mando
|
||||
std::string label;
|
||||
Input::Action action;
|
||||
SDL_GamepadButton button;
|
||||
|
||||
Button(std::string label, Input::Action action, SDL_GamepadButton button)
|
||||
: label(std::move(label)), action(action), button(button) {}
|
||||
@@ -29,30 +30,30 @@ class DefineButtons {
|
||||
DefineButtons();
|
||||
~DefineButtons() = default;
|
||||
|
||||
void render(); // Dibuja el objeto en pantalla
|
||||
void checkEvents(const SDL_Event &event); // Procesa los eventos
|
||||
auto enable(Options::Gamepad *options_gamepad) -> bool; // Habilita la redefinición de botones
|
||||
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }; // Comprueba si está habilitado
|
||||
void render();
|
||||
void update();
|
||||
void checkEvents(const SDL_Event &event);
|
||||
auto enable(Options::Gamepad *options_gamepad) -> bool;
|
||||
void disable();
|
||||
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
|
||||
[[nodiscard]] auto isFinished() const -> bool { return finished_; }
|
||||
|
||||
private:
|
||||
// Objetos
|
||||
Input *input_ = nullptr; // Gestión de entrada
|
||||
Input *input_ = nullptr;
|
||||
std::unique_ptr<WindowMessage> window_message_;
|
||||
|
||||
// Variables
|
||||
bool enabled_ = false; // Indica si está activo
|
||||
bool finished_ = false; // Indica si ha terminado
|
||||
int x_ = 0; // Coordenadas de texto
|
||||
int y_ = 0; // Coordenadas de texto
|
||||
std::vector<Button> buttons_; // Definiciones de botones
|
||||
size_t index_button_ = 0; // Índice del botón en proceso
|
||||
std::vector<std::string> controller_names_; // Nombres de los mandos
|
||||
Options::Gamepad *options_gamepad_;
|
||||
bool enabled_ = false;
|
||||
bool finished_ = false;
|
||||
std::vector<Button> buttons_;
|
||||
size_t index_button_ = 0;
|
||||
std::vector<std::string> controller_names_;
|
||||
Options::Gamepad *options_gamepad_ = nullptr;
|
||||
|
||||
// Métodos internos
|
||||
void incIndexButton(); // Incrementa el índice de botones
|
||||
void doControllerButtonDown(const SDL_GamepadButtonEvent &event); // Procesa pulsaciones
|
||||
void bindButtons(Options::Gamepad *options_gamepad); // Asigna botones al sistema de entrada
|
||||
auto checkButtonNotInUse(SDL_GamepadButton button) -> bool; // Verifica uso de botones
|
||||
void clearButtons(); // Limpia asignaciones actuales
|
||||
void checkEnd(); // Comprueba si ha finalizado
|
||||
void incIndexButton();
|
||||
void doControllerButtonDown(const SDL_GamepadButtonEvent &event);
|
||||
void bindButtons(Options::Gamepad *options_gamepad);
|
||||
auto checkButtonNotInUse(SDL_GamepadButton button) -> bool;
|
||||
void clearButtons();
|
||||
void checkEnd();
|
||||
void updateWindowMessage();
|
||||
};
|
||||
@@ -43,7 +43,6 @@ Title::Title()
|
||||
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)),
|
||||
game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)),
|
||||
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
|
||||
define_buttons_(std::make_unique<DefineButtons>()),
|
||||
num_controllers_(Input::get()->getNumGamepads()),
|
||||
state_(TitleState::LOGO_ANIMATING) {
|
||||
// Configura objetos
|
||||
@@ -102,7 +101,6 @@ void Title::render() {
|
||||
renderPlayers();
|
||||
renderStartPrompt();
|
||||
renderCopyright();
|
||||
define_buttons_->render();
|
||||
fade_->render();
|
||||
|
||||
SCREEN->render();
|
||||
@@ -117,15 +115,10 @@ void Title::checkEvents() {
|
||||
}
|
||||
|
||||
GlobalEvents::check(event);
|
||||
define_buttons_->checkEvents(event);
|
||||
}
|
||||
}
|
||||
|
||||
void Title::handleKeyDownEvent(const SDL_Event& event) {
|
||||
bool is_first_press = static_cast<int>(event.key.repeat) == 0;
|
||||
if (is_first_press) {
|
||||
handleControlKeys(event.key.key);
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
bool is_repeat = static_cast<int>(event.key.repeat) == 1;
|
||||
if (is_repeat) {
|
||||
@@ -209,44 +202,8 @@ void Title::printColorValue(const Color& color) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void Title::handleControlKeys(SDL_Keycode key) {
|
||||
switch (key) {
|
||||
case SDLK_1:
|
||||
define_buttons_->enable(&Options::gamepad_manager.getGamepad(Player::Id::PLAYER1));
|
||||
resetCounter();
|
||||
break;
|
||||
|
||||
case SDLK_2:
|
||||
define_buttons_->enable(&Options::gamepad_manager.getGamepad(Player::Id::PLAYER2));
|
||||
resetCounter();
|
||||
break;
|
||||
|
||||
case SDLK_3:
|
||||
swapControllers();
|
||||
resetCounter();
|
||||
break;
|
||||
|
||||
case SDLK_4:
|
||||
swapKeyboard();
|
||||
resetCounter();
|
||||
break;
|
||||
|
||||
case SDLK_5:
|
||||
showControllers();
|
||||
resetCounter();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las entradas
|
||||
void Title::checkInput() {
|
||||
if (shouldSkipInputCheck()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Input::get()->update();
|
||||
|
||||
if (!ServiceMenu::get()->isEnabled()) {
|
||||
@@ -256,10 +213,6 @@ void Title::checkInput() {
|
||||
GlobalInputs::check();
|
||||
}
|
||||
|
||||
auto Title::shouldSkipInputCheck() const -> bool {
|
||||
return define_buttons_->isEnabled();
|
||||
}
|
||||
|
||||
void Title::processControllerInputs() {
|
||||
for (const auto& controller : Options::gamepad_manager) {
|
||||
if (isStartButtonPressed(&controller)) {
|
||||
@@ -396,14 +349,9 @@ void Title::updateState() {
|
||||
break;
|
||||
}
|
||||
case TitleState::LOGO_FINISHED: {
|
||||
// El contador solo sube si no estamos definiendo botones
|
||||
counter_ = define_buttons_->isEnabled() ? 0 : counter_ + 1;
|
||||
|
||||
// Actualiza el logo con el título del juego
|
||||
game_logo_->update();
|
||||
|
||||
// Actualiza el mosaico de fondo
|
||||
tiled_bg_->update();
|
||||
++counter_; // Incrementa el contador
|
||||
game_logo_->update(); // Actualiza el logo con el título del juego
|
||||
tiled_bg_->update(); // Actualiza el mosaico de fondo
|
||||
|
||||
if (counter_ == param.title.title_duration) {
|
||||
// El menu ha hecho time out
|
||||
@@ -411,20 +359,16 @@ void Title::updateState() {
|
||||
fade_->activate();
|
||||
selection_ = Section::Options::TITLE_TIME_OUT;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case TitleState::START_HAS_BEEN_PRESSED: {
|
||||
// Actualiza el logo con el título del juego
|
||||
game_logo_->update();
|
||||
|
||||
// Actualiza el mosaico de fondo
|
||||
tiled_bg_->update();
|
||||
++counter_; // Incrementa el contador
|
||||
game_logo_->update(); // Actualiza el logo con el título del juego
|
||||
tiled_bg_->update(); // Actualiza el mosaico de fondo
|
||||
|
||||
if (counter_ == 100) {
|
||||
fade_->activate();
|
||||
}
|
||||
++counter_;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -443,7 +387,6 @@ void Title::updateStartPrompt() {
|
||||
Uint32 time_ms = SDL_GetTicks();
|
||||
bool condition_met = false;
|
||||
|
||||
if (!define_buttons_->isEnabled()) {
|
||||
switch (state_) {
|
||||
case TitleState::LOGO_FINISHED:
|
||||
condition_met = (time_ms % LOGO_BLINK_PERIOD) >= (LOGO_BLINK_PERIOD - LOGO_BLINK_ON_TIME);
|
||||
@@ -456,7 +399,6 @@ void Title::updateStartPrompt() {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
should_render_start_prompt_ = condition_met;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "player.h" // Para Player
|
||||
#include "section.hpp" // Para Options, Name (ptr only)
|
||||
|
||||
class DefineButtons;
|
||||
class Fade;
|
||||
class GameLogo;
|
||||
class Sprite;
|
||||
@@ -63,7 +62,6 @@ class Title {
|
||||
std::unique_ptr<TiledBG> tiled_bg_; // Fondo animado de tiles
|
||||
std::unique_ptr<GameLogo> game_logo_; // Logo del juego
|
||||
std::unique_ptr<Sprite> mini_logo_sprite_; // Logo JailGames mini
|
||||
std::unique_ptr<DefineButtons> define_buttons_; // Definición de botones del joystick
|
||||
std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores
|
||||
|
||||
// --- Variables de estado ---
|
||||
@@ -90,8 +88,6 @@ class Title {
|
||||
void checkEvents(); // Comprueba los eventos
|
||||
void checkInput(); // Comprueba las entradas
|
||||
void handleKeyDownEvent(const SDL_Event& event); // Maneja el evento de tecla presionada
|
||||
void handleControlKeys(SDL_Keycode key); // Maneja las teclas de control específicas
|
||||
[[nodiscard]] auto shouldSkipInputCheck() const -> bool; // Determina si se debe omitir la comprobación de entrada
|
||||
void processControllerInputs(); // Procesa las entradas de los mandos
|
||||
[[nodiscard]] static auto isStartButtonPressed(const Options::Gamepad* controller) -> bool; // Comprueba si se ha pulsado el botón Start
|
||||
void handleStartButtonPress(const Options::Gamepad* controller); // Maneja la pulsación del botón Start
|
||||
|
||||
95
source/ui/action_list_option.cpp
Normal file
95
source/ui/action_list_option.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "action_list_option.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "text.h"
|
||||
|
||||
ActionListOption::ActionListOption(
|
||||
const std::string& caption,
|
||||
ServiceMenu::SettingsGroup group,
|
||||
std::vector<std::string> options,
|
||||
ValueGetter getter,
|
||||
ValueSetter setter,
|
||||
ActionExecutor action_executor,
|
||||
bool hidden) : MenuOption(caption, group, hidden),
|
||||
options_(std::move(options)),
|
||||
value_getter_(std::move(getter)),
|
||||
value_setter_(std::move(setter)),
|
||||
action_executor_(std::move(action_executor)),
|
||||
current_index_(0) {
|
||||
updateCurrentIndex();
|
||||
}
|
||||
|
||||
auto ActionListOption::getBehavior() const -> Behavior {
|
||||
// Puede tanto ajustar valor (lista) como ejecutar acción (botón)
|
||||
return Behavior::BOTH;
|
||||
}
|
||||
|
||||
auto ActionListOption::getValueAsString() const -> std::string {
|
||||
if (value_getter_) {
|
||||
return value_getter_();
|
||||
}
|
||||
|
||||
if (current_index_ < options_.size()) {
|
||||
return options_[current_index_];
|
||||
}
|
||||
|
||||
return options_.empty() ? "" : options_[0];
|
||||
}
|
||||
|
||||
auto ActionListOption::getMaxValueWidth(Text* text) const -> int {
|
||||
int max_width = 0;
|
||||
for (const auto& option : options_) {
|
||||
int width = text->lenght(option, -2);
|
||||
if (width > max_width) {
|
||||
max_width = width;
|
||||
}
|
||||
}
|
||||
return max_width;
|
||||
}
|
||||
|
||||
void ActionListOption::adjustValue(bool up) {
|
||||
if (options_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (up) {
|
||||
current_index_ = (current_index_ + 1) % options_.size();
|
||||
} else {
|
||||
current_index_ = (current_index_ == 0) ? options_.size() - 1 : current_index_ - 1;
|
||||
}
|
||||
|
||||
// Aplicar el cambio usando el setter
|
||||
if (value_setter_ && current_index_ < options_.size()) {
|
||||
value_setter_(options_[current_index_]);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionListOption::executeAction() {
|
||||
if (action_executor_) {
|
||||
action_executor_();
|
||||
}
|
||||
}
|
||||
|
||||
void ActionListOption::sync() {
|
||||
updateCurrentIndex();
|
||||
}
|
||||
|
||||
void ActionListOption::updateCurrentIndex() {
|
||||
current_index_ = findCurrentIndex();
|
||||
}
|
||||
|
||||
auto ActionListOption::findCurrentIndex() const -> size_t {
|
||||
if (!value_getter_ || options_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string current_value = value_getter_();
|
||||
auto it = std::find(options_.begin(), options_.end(), current_value);
|
||||
|
||||
if (it != options_.end()) {
|
||||
return static_cast<size_t>(std::distance(options_.begin(), it));
|
||||
}
|
||||
|
||||
return 0; // Valor por defecto si no se encuentra
|
||||
}
|
||||
42
source/ui/action_list_option.h
Normal file
42
source/ui/action_list_option.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "menu_option.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ActionListOption : public MenuOption {
|
||||
public:
|
||||
using ValueGetter = std::function<std::string()>;
|
||||
using ValueSetter = std::function<void(const std::string&)>;
|
||||
using ActionExecutor = std::function<void()>;
|
||||
|
||||
ActionListOption(
|
||||
const std::string& caption,
|
||||
ServiceMenu::SettingsGroup group,
|
||||
std::vector<std::string> options,
|
||||
ValueGetter getter,
|
||||
ValueSetter setter,
|
||||
ActionExecutor action_executor,
|
||||
bool hidden = false
|
||||
);
|
||||
|
||||
// Implementaciones de MenuOption
|
||||
[[nodiscard]] auto getBehavior() const -> Behavior override;
|
||||
[[nodiscard]] auto getValueAsString() const -> std::string override;
|
||||
[[nodiscard]] auto getMaxValueWidth(Text* text) const -> int override;
|
||||
void adjustValue(bool up) override;
|
||||
void executeAction() override;
|
||||
void sync(); //override;
|
||||
|
||||
private:
|
||||
std::vector<std::string> options_;
|
||||
ValueGetter value_getter_;
|
||||
ValueSetter value_setter_;
|
||||
ActionExecutor action_executor_;
|
||||
size_t current_index_;
|
||||
|
||||
void updateCurrentIndex();
|
||||
[[nodiscard]] auto findCurrentIndex() const -> size_t;
|
||||
};
|
||||
@@ -17,8 +17,9 @@
|
||||
class MenuOption {
|
||||
public:
|
||||
enum class Behavior {
|
||||
ADJUST,
|
||||
SELECT
|
||||
ADJUST, // Solo puede ajustar valor (como IntOption, BoolOption, ListOption)
|
||||
SELECT, // Solo puede ejecutar acción (como ActionOption, FolderOption)
|
||||
BOTH // Puede tanto ajustar como ejecutar acción (como ActionListOption)
|
||||
};
|
||||
|
||||
MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false)
|
||||
|
||||
@@ -45,27 +45,39 @@ void MenuRenderer::render(const ServiceMenu *menu_state) {
|
||||
// Dibuja las opciones
|
||||
y = options_y_;
|
||||
const auto &option_pairs = menu_state->getOptionPairs();
|
||||
const auto &display_options = menu_state->getDisplayOptions();
|
||||
|
||||
for (size_t i = 0; i < option_pairs.size(); ++i) {
|
||||
const bool IS_SELECTED = (i == menu_state->getSelectedIndex());
|
||||
const Color ¤t_color = IS_SELECTED ? param.service_menu.selected_color : param.service_menu.text_color;
|
||||
|
||||
if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) {
|
||||
// Para opciones alineadas a la izquierda, truncamos el valor si es necesario
|
||||
const int AVAILABLE_WIDTH = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2) -
|
||||
const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2) -
|
||||
element_text_->lenght(option_pairs.at(i).first, -2) -
|
||||
ServiceMenu::MIN_GAP_OPTION_VALUE;
|
||||
|
||||
const std::string TRUNCATED_VALUE = getTruncatedValue(option_pairs.at(i).second, AVAILABLE_WIDTH);
|
||||
std::string truncated_value = getTruncatedValue(option_pairs.at(i).second, available_width);
|
||||
|
||||
// Si la opción tiene Behavior::BOTH, añadir indicador visual
|
||||
if (i < display_options.size() && display_options[i]->getBehavior() == MenuOption::Behavior::BOTH) {
|
||||
truncated_value = "► " + truncated_value + " ◄";
|
||||
}
|
||||
|
||||
element_text_->writeColored(rect_.x + ServiceMenu::OPTIONS_HORIZONTAL_PADDING, y, option_pairs.at(i).first, current_color, -2);
|
||||
const int X = rect_.x + rect_.w - ServiceMenu::OPTIONS_HORIZONTAL_PADDING - element_text_->lenght(TRUNCATED_VALUE, -2);
|
||||
element_text_->writeColored(X, y, TRUNCATED_VALUE, current_color, -2);
|
||||
const int X = rect_.x + rect_.w - ServiceMenu::OPTIONS_HORIZONTAL_PADDING - element_text_->lenght(truncated_value, -2);
|
||||
element_text_->writeColored(X, y, truncated_value, current_color, -2);
|
||||
} else {
|
||||
// Para opciones centradas, también truncamos si es necesario
|
||||
const int AVAILABLE_WIDTH = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2);
|
||||
const std::string TRUNCATED_CAPTION = getTruncatedValue(option_pairs.at(i).first, AVAILABLE_WIDTH);
|
||||
const int available_width = rect_.w - (ServiceMenu::OPTIONS_HORIZONTAL_PADDING * 2);
|
||||
std::string truncated_caption = getTruncatedValue(option_pairs.at(i).first, available_width);
|
||||
|
||||
element_text_->writeDX(TEXT_CENTER | TEXT_COLOR, rect_.x + rect_.w / 2, y, TRUNCATED_CAPTION, -2, current_color);
|
||||
// Si la opción tiene Behavior::BOTH, añadir indicador visual
|
||||
if (i < display_options.size() && display_options[i]->getBehavior() == MenuOption::Behavior::BOTH) {
|
||||
truncated_caption = "► " + truncated_caption + " ◄";
|
||||
}
|
||||
|
||||
element_text_->writeDX(TEXT_CENTER | TEXT_COLOR, rect_.x + rect_.w / 2, y, truncated_caption, -2, current_color);
|
||||
}
|
||||
y += options_height_ + options_padding_;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "section.hpp" // Para Name, name, Options, options
|
||||
#include "ui/ui_message.h" // Para UIMessage
|
||||
#include "utils.h" // Para Zone
|
||||
#include "action_list_option.h" // Para ActionListOption
|
||||
#include "define_buttons.h" // Para DefineButtons
|
||||
|
||||
// Singleton
|
||||
ServiceMenu *ServiceMenu::instance = nullptr;
|
||||
@@ -32,6 +34,7 @@ ServiceMenu::ServiceMenu()
|
||||
|
||||
renderer_ = std::make_unique<MenuRenderer>(this, element_text, title_text);
|
||||
restart_message_ui_ = std::make_unique<UIMessage>(element_text, Lang::getText("[SERVICE_MENU] NEED_RESTART_MESSAGE"), param.service_menu.title_color);
|
||||
define_buttons_ = std::make_unique<DefineButtons>();
|
||||
|
||||
reset();
|
||||
}
|
||||
@@ -55,6 +58,11 @@ void ServiceMenu::render() {
|
||||
const float MSG_Y = renderer_->getRect().y + 39.0F;
|
||||
restart_message_ui_->setPosition(MSG_X, MSG_Y);
|
||||
restart_message_ui_->render();
|
||||
|
||||
// Renderizar DefineButtons si está activo (se dibuja por encima de todo)
|
||||
if (define_buttons_ && define_buttons_->isEnabled()) {
|
||||
define_buttons_->render();
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceMenu::update() {
|
||||
@@ -70,6 +78,22 @@ void ServiceMenu::update() {
|
||||
last_pending_changes_ = now_pending;
|
||||
}
|
||||
restart_message_ui_->update();
|
||||
|
||||
// Actualizar DefineButtons
|
||||
if (define_buttons_) {
|
||||
define_buttons_->update();
|
||||
|
||||
// Si DefineButtons ha terminado, deshabilitarlo
|
||||
if (define_buttons_->isEnabled() && define_buttons_->isFinished()) {
|
||||
// Pequeño delay antes de cerrar
|
||||
static int finish_delay = 0;
|
||||
finish_delay++;
|
||||
if (finish_delay > 60) { // 1 segundo a 60 FPS
|
||||
define_buttons_->disable();
|
||||
finish_delay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceMenu::reset() {
|
||||
@@ -247,8 +271,8 @@ auto ServiceMenu::countOptionsInGroup(SettingsGroup group) const -> size_t {
|
||||
void ServiceMenu::initializeOptions() {
|
||||
options_.clear();
|
||||
|
||||
// CONTROLS
|
||||
options_.push_back(std::make_unique<ListOption>(
|
||||
// CONTROLS - Usando ActionListOption para mandos
|
||||
options_.push_back(std::make_unique<ActionListOption>(
|
||||
Lang::getText("[SERVICE_MENU] CONTROLLER1"),
|
||||
SettingsGroup::CONTROLS,
|
||||
Input::get()->getControllerNames(),
|
||||
@@ -257,9 +281,16 @@ void ServiceMenu::initializeOptions() {
|
||||
},
|
||||
[](const std::string &val) {
|
||||
Options::gamepad_manager.assignGamepadToPlayer(Player::Id::PLAYER1, Input::get()->getGamepadByName(val), val);
|
||||
},
|
||||
[this]() {
|
||||
// Acción: configurar botones del mando del jugador 1
|
||||
auto* gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER1);
|
||||
if (gamepad && gamepad->instance) {
|
||||
define_buttons_->enable(gamepad);
|
||||
}
|
||||
}));
|
||||
|
||||
options_.push_back(std::make_unique<ListOption>(
|
||||
options_.push_back(std::make_unique<ActionListOption>(
|
||||
Lang::getText("[SERVICE_MENU] CONTROLLER2"),
|
||||
SettingsGroup::CONTROLS,
|
||||
Input::get()->getControllerNames(),
|
||||
@@ -268,8 +299,16 @@ void ServiceMenu::initializeOptions() {
|
||||
},
|
||||
[](const std::string &val) {
|
||||
Options::gamepad_manager.assignGamepadToPlayer(Player::Id::PLAYER2, Input::get()->getGamepadByName(val), val);
|
||||
},
|
||||
[this]() {
|
||||
// Acción: configurar botones del mando del jugador 2
|
||||
auto* gamepad = &Options::gamepad_manager.getGamepad(Player::Id::PLAYER2);
|
||||
if (gamepad && gamepad->instance) {
|
||||
define_buttons_->enable(gamepad);
|
||||
}
|
||||
}));
|
||||
|
||||
// CONTROLS - Opción para teclado (solo lista, sin acción)
|
||||
options_.push_back(std::make_unique<ListOption>(
|
||||
Lang::getText("[SERVICE_MENU] KEYBOARD"),
|
||||
SettingsGroup::CONTROLS,
|
||||
@@ -277,13 +316,16 @@ void ServiceMenu::initializeOptions() {
|
||||
Lang::getText("[SERVICE_MENU] PLAYER1"),
|
||||
Lang::getText("[SERVICE_MENU] PLAYER2")},
|
||||
[]() {
|
||||
return Lang::getNameFromCode(Options::pending_changes.new_language);
|
||||
// Aquí deberías devolver el jugador actual asignado al teclado
|
||||
// Por ahora devuelvo "Jugador 1" como ejemplo
|
||||
return Lang::getText("[SERVICE_MENU] PLAYER1");
|
||||
},
|
||||
[](const std::string &val) {
|
||||
Options::pending_changes.new_language = Lang::getCodeFromName(val);
|
||||
Options::checkPendingChanges();
|
||||
// Aquí asignarías el teclado al jugador seleccionado
|
||||
// Implementación pendiente según tu sistema de input
|
||||
}));
|
||||
|
||||
// CONTROLS - Acción para intercambiar mandos
|
||||
options_.push_back(std::make_unique<ActionOption>(
|
||||
Lang::getText("[SERVICE_MENU] SWAP_CONTROLLERS"),
|
||||
SettingsGroup::CONTROLS,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
class MenuOption;
|
||||
class MenuRenderer;
|
||||
class DefineButtons; // Forward declaration
|
||||
|
||||
class ServiceMenu {
|
||||
public:
|
||||
@@ -52,6 +53,7 @@ class ServiceMenu {
|
||||
void adjustOption(bool adjust_up);
|
||||
void selectOption();
|
||||
void moveBack();
|
||||
void checkEvents(const SDL_Event &event); // Nuevo método para eventos
|
||||
|
||||
// --- Getters para que el Renderer pueda leer el estado ---
|
||||
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
|
||||
@@ -81,6 +83,9 @@ class ServiceMenu {
|
||||
std::unique_ptr<UIMessage> restart_message_ui_;
|
||||
bool last_pending_changes_ = false;
|
||||
|
||||
// --- Configuración de botones ---
|
||||
std::unique_ptr<DefineButtons> define_buttons_;
|
||||
|
||||
// --- La Vista ---
|
||||
std::unique_ptr<MenuRenderer> renderer_;
|
||||
|
||||
|
||||
172
source/ui/window_message.cpp
Normal file
172
source/ui/window_message.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#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 Color& bg_color,
|
||||
const Color& border_color,
|
||||
const Color& title_color,
|
||||
const Color& text_color
|
||||
) : text_renderer_(std::move(text_renderer)),
|
||||
title_(title),
|
||||
bg_color_(bg_color),
|
||||
border_color_(border_color),
|
||||
title_color_(title_color),
|
||||
text_color_(text_color) {
|
||||
}
|
||||
|
||||
void WindowMessage::render() {
|
||||
if (!visible_) {
|
||||
return;
|
||||
}
|
||||
|
||||
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_RenderFillRect(renderer, &rect_);
|
||||
|
||||
// Dibujar borde
|
||||
SDL_SetRenderDrawColor(renderer, border_color_.r, border_color_.g, border_color_.b, border_color_.a);
|
||||
SDL_RenderRect(renderer, &rect_);
|
||||
|
||||
float current_y = rect_.y + padding_;
|
||||
|
||||
// Dibujar título si existe
|
||||
if (!title_.empty()) {
|
||||
text_renderer_->writeCentered(
|
||||
rect_.x + rect_.w / 2.0f,
|
||||
current_y,
|
||||
title_
|
||||
//title_color_
|
||||
);
|
||||
current_y += text_renderer_->getCharacterSize() + line_spacing_ * 2;
|
||||
|
||||
// Línea separadora debajo del título
|
||||
SDL_SetRenderDrawColor(renderer, border_color_.r, border_color_.g, border_color_.b, border_color_.a);
|
||||
SDL_RenderLine(renderer,
|
||||
rect_.x + padding_, current_y - line_spacing_,
|
||||
rect_.x + rect_.w - padding_, current_y - line_spacing_);
|
||||
}
|
||||
|
||||
// Dibujar textos
|
||||
for (const auto& text : texts_) {
|
||||
text_renderer_->writeCentered(
|
||||
rect_.x + rect_.w / 2.0f,
|
||||
current_y,
|
||||
text
|
||||
//text_color_
|
||||
);
|
||||
current_y += text_renderer_->getCharacterSize() + line_spacing_;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowMessage::update() {
|
||||
// Por ahora no hay animaciones, pero se puede extender
|
||||
}
|
||||
|
||||
void WindowMessage::show() {
|
||||
visible_ = true;
|
||||
}
|
||||
|
||||
void WindowMessage::hide() {
|
||||
visible_ = false;
|
||||
}
|
||||
|
||||
void WindowMessage::setTitle(const std::string& title) {
|
||||
title_ = title;
|
||||
}
|
||||
|
||||
void WindowMessage::setText(const std::string& text) {
|
||||
texts_.clear();
|
||||
texts_.push_back(text);
|
||||
}
|
||||
|
||||
void WindowMessage::setTexts(const std::vector<std::string>& texts) {
|
||||
texts_ = texts;
|
||||
}
|
||||
|
||||
void WindowMessage::addText(const std::string& text) {
|
||||
texts_.push_back(text);
|
||||
}
|
||||
|
||||
void WindowMessage::clearTexts() {
|
||||
texts_.clear();
|
||||
}
|
||||
|
||||
void WindowMessage::setPosition(float x, float y) {
|
||||
rect_.x = x;
|
||||
rect_.y = y;
|
||||
}
|
||||
|
||||
void WindowMessage::setSize(float width, float height) {
|
||||
rect_.w = width;
|
||||
rect_.h = height;
|
||||
}
|
||||
|
||||
void WindowMessage::centerOnScreen() {
|
||||
rect_.x = (param.game.width - rect_.w) / 2.0f;
|
||||
rect_.y = (param.game.height - rect_.h) / 2.0f;
|
||||
}
|
||||
|
||||
void WindowMessage::autoSize() {
|
||||
calculateAutoSize();
|
||||
}
|
||||
|
||||
void WindowMessage::calculateAutoSize() {
|
||||
float content_width = calculateContentWidth();
|
||||
float content_height = calculateContentHeight();
|
||||
|
||||
rect_.w = content_width + (padding_ * 2);
|
||||
rect_.h = content_height + (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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
auto WindowMessage::calculateContentHeight() const -> float {
|
||||
float height = 0;
|
||||
|
||||
// Altura del título
|
||||
if (!title_.empty()) {
|
||||
height += text_renderer_->getCharacterSize() + line_spacing_ * 2; // Espacio extra para separador
|
||||
}
|
||||
|
||||
// Altura de los textos
|
||||
if (!texts_.empty()) {
|
||||
height += (texts_.size() * text_renderer_->getCharacterSize());
|
||||
height += ((texts_.size() - 1) * line_spacing_); // Espaciado entre líneas
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
auto WindowMessage::calculateContentWidth() const -> float {
|
||||
float max_width = 200.0f; // Ancho mínimo
|
||||
|
||||
// Ancho del título
|
||||
if (!title_.empty()) {
|
||||
float title_width = text_renderer_->lenght(title_, -2);
|
||||
max_width = std::max(max_width, title_width);
|
||||
}
|
||||
|
||||
// Ancho de los textos
|
||||
for (const auto& text : texts_) {
|
||||
float text_width = text_renderer_->lenght(text, -2);
|
||||
max_width = std::max(max_width, text_width);
|
||||
}
|
||||
|
||||
return max_width;
|
||||
}
|
||||
84
source/ui/window_message.h
Normal file
84
source/ui/window_message.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "color.h"
|
||||
|
||||
class Text;
|
||||
|
||||
class WindowMessage {
|
||||
public:
|
||||
WindowMessage(
|
||||
std::shared_ptr<Text> 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}
|
||||
);
|
||||
|
||||
// Métodos principales
|
||||
void render();
|
||||
void update();
|
||||
|
||||
// Control de visibilidad
|
||||
void show();
|
||||
void hide();
|
||||
[[nodiscard]] auto isVisible() const -> bool { return visible_; }
|
||||
|
||||
// Configuración de contenido
|
||||
void setTitle(const std::string& title);
|
||||
void setText(const std::string& text);
|
||||
void setTexts(const std::vector<std::string>& texts);
|
||||
void addText(const std::string& text);
|
||||
void clearTexts();
|
||||
|
||||
// Configuración de posición y tamaño
|
||||
void setPosition(float x, float y);
|
||||
void setSize(float width, float height);
|
||||
void centerOnScreen();
|
||||
void autoSize(); // Ajusta automáticamente al contenido
|
||||
|
||||
// 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; }
|
||||
|
||||
// Configuración de espaciado
|
||||
void setPadding(float padding) { padding_ = padding; }
|
||||
void setLineSpacing(float spacing) { line_spacing_ = spacing; }
|
||||
|
||||
// Getters
|
||||
[[nodiscard]] auto getRect() const -> const SDL_FRect& { return rect_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Text> text_renderer_;
|
||||
|
||||
// Estado de visibilidad
|
||||
bool visible_ = false;
|
||||
|
||||
// Contenido
|
||||
std::string title_;
|
||||
std::vector<std::string> texts_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// Métodos privados
|
||||
void calculateAutoSize();
|
||||
[[nodiscard]] auto calculateContentHeight() const -> float;
|
||||
[[nodiscard]] auto calculateContentWidth() const -> float;
|
||||
};
|
||||
Reference in New Issue
Block a user