animacio de tancar el menu

This commit is contained in:
2026-04-16 20:14:35 +02:00
parent e0f9b60f22
commit 5956d874c3
4 changed files with 52 additions and 13 deletions

View File

@@ -44,6 +44,7 @@ namespace Menu {
// --- Animació ---
static constexpr float OPEN_SPEED = 8.0F; // 1.0 / 0.125s
static constexpr float CLOSE_SPEED = 10.0F; // 1.0 / 0.1s (una mica més ràpida que l'obertura)
static constexpr float HEIGHT_RATE = 12.0F; // smoothing exponencial de l'alçada (~150 ms al 90%)
// --- Items ---
@@ -92,6 +93,7 @@ namespace Menu {
static float animated_h_{0.0F}; // alçada actual animada (smoothing cap al target visible)
static Uint32 last_ticks_{0};
static SDL_Scancode* capturing_{nullptr}; // != null → esperant tecla per assignar
static bool closing_{false}; // true mentre l'animació de tancament és en curs
// --- Transició entre pàgines ---
static constexpr float TRANSITION_SPEED = 5.5F; // ~180 ms
@@ -354,36 +356,56 @@ namespace Menu {
font_ = std::make_unique<Text>("fonts/8bithud.fnt", "fonts/8bithud.gif");
stack_.clear();
open_anim_ = 0.0F;
closing_ = false;
last_ticks_ = SDL_GetTicks();
}
void destroy() {
font_.reset();
stack_.clear();
closing_ = false;
}
// "Actiu": accepta input. Durant l'animació de tancament la pila encara
// té pàgines però ja no ha de processar tecles.
auto isOpen() -> bool {
return !stack_.empty() && !closing_;
}
// "Visible": encara hi ha caixa per pintar (incloent close animation).
auto isVisible() -> bool {
return !stack_.empty();
}
void toggle() {
if (closing_ && !stack_.empty()) {
// Cancel·la el tancament en curs — continua l'animació cap a "obert"
// des del valor actual d'open_anim_.
closing_ = false;
last_ticks_ = SDL_GetTicks();
return;
}
if (isOpen()) {
close();
} else {
stack_.push_back(buildRoot());
open_anim_ = 0.0F;
closing_ = false;
animated_h_ = static_cast<float>(boxHeight(stack_.back()));
last_ticks_ = SDL_GetTicks();
}
}
// close() no buida la pila immediatament: marca closing_ i deixa que
// render() faça decréixer open_anim_ fins a 0. En aquell moment es neteja
// l'estat. Si es crida estant ja tancat o tancant-se, no-op.
void close() {
stack_.clear();
open_anim_ = 0.0F;
animated_h_ = 0.0F;
if (stack_.empty() || closing_) return;
closing_ = true;
capturing_ = nullptr;
transition_active_ = false;
transition_progress_ = 1.0F;
last_ticks_ = SDL_GetTicks();
}
auto isCapturing() -> bool {
@@ -556,13 +578,23 @@ namespace Menu {
}
void render(Uint32* pixel_data) {
if (!isOpen() || !font_ || !pixel_data) return;
if (!isVisible() || !font_ || !pixel_data) return;
// Delta time
Uint32 now = SDL_GetTicks();
float dt = static_cast<float>(now - last_ticks_) / 1000.0F;
last_ticks_ = now;
if (open_anim_ < 1.0F) {
if (closing_) {
open_anim_ -= CLOSE_SPEED * dt;
if (open_anim_ <= 0.0F) {
// Animació de tancament completada — buida l'estat de veritat.
open_anim_ = 0.0F;
stack_.clear();
animated_h_ = 0.0F;
closing_ = false;
return;
}
} else if (open_anim_ < 1.0F) {
open_anim_ += OPEN_SPEED * dt;
if (open_anim_ > 1.0F) open_anim_ = 1.0F;
}

View File

@@ -6,11 +6,15 @@ namespace Menu {
void init();
void destroy();
// "Actiu": el menú accepta input. Fals durant l'animació de tancament.
[[nodiscard]] auto isOpen() -> bool;
// "Visible": hi ha una caixa pintada (incloent l'animació de tancament).
// Overlay la usa per a decidir si cridar render().
[[nodiscard]] auto isVisible() -> bool;
void toggle();
void close();
// Pinta el menú sobre el buffer ARGB — cridat des d'Overlay::render si està obert
// Pinta el menú sobre el buffer ARGB — cridat des d'Overlay::render si està visible
void render(Uint32* pixel_data);
// Gestió d'input — cridat des del Director en KEY_DOWN

View File

@@ -361,8 +361,8 @@ namespace Overlay {
std::remove_if(notifications_.begin(), notifications_.end(), [](const Notification& n) { return n.status == Status::FINISHED; }),
notifications_.end());
// Menú flotant per damunt de tot
if (Menu::isOpen()) {
// Menú flotant per damunt de tot (isVisible inclou l'animació de tancament)
if (Menu::isVisible()) {
Menu::render(pixel_data);
}
}

View File

@@ -286,11 +286,6 @@ void Director::handleEvent(const SDL_Event& event) {
Gamepad::handleEvent(event);
return;
}
// Salta els crèdits amb qualsevol tecla; no deixem que arribi al joc
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat && Overlay::creditsActive()) {
Overlay::cancelCredits();
return;
}
// Empassar-se el KEY_UP de qualsevol tecla que el menú va consumir en KEY_DOWN
if (event.type == SDL_EVENT_KEY_UP && event.key.scancode >= 0 &&
event.key.scancode < SDL_SCANCODE_COUNT && menu_keys_held_[event.key.scancode]) {
@@ -340,6 +335,14 @@ void Director::handleEvent(const SDL_Event& event) {
if (Menu::isOpen() && event.type == SDL_EVENT_KEY_UP) {
return; // no deixem passar KEY_UP al joc tampoc
}
// Salta els crèdits amb qualsevol tecla que arribe al joc. Es fa DESPRÉS
// del toggle del menú/pausa i del handling del menú obert — així F12 i
// SELECT (gamepad) obrin el menú sense cancel·lar els crèdits, i la
// navegació per dins del menú tampoc els anul·la.
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat && Overlay::creditsActive()) {
Overlay::cancelCredits();
return;
}
// Allibera el bloqueig d'ESC quan l'usuari la deixa anar
if (event.type == SDL_EVENT_KEY_UP && event.key.scancode == SDL_SCANCODE_ESCAPE && esc_swallow_until_release_) {
esc_swallow_until_release_ = false;