feat(box): routine bravo screen + cleanup

This commit is contained in:
L'électron rare
2026-06-21 11:11:57 +02:00
parent 797d9caf98
commit d809e8add4
+114 -6
View File
@@ -230,10 +230,15 @@ static void step_home_cb(lv_event_t *e)
{
(void)e;
lisael_aac_stop();
lisael_ui_goto(LISAEL_MODE_HOME);
// The step screen is rebuilt fresh on next open_routine(); drop the ref so
// the next build replaces it. (It is auto-deleted on the next load_anim.)
lv_obj_t *old = s_step_screen;
s_step_screen = NULL;
lisael_ui_goto(LISAEL_MODE_HOME);
// lisael_ui_goto uses auto_del=false (cached mode screens), so the transient
// step screen would leak without an explicit delete. Schedule it after the
// 250 ms slide animation finishes so it is off-screen before deletion.
if (old) {
lv_obj_delete_delayed(old, 350);
}
}
static void done_cb(lv_event_t *e)
@@ -331,10 +336,113 @@ static void open_routine(int ridx)
build_step_screen();
}
// Provisional: real body lands in Task 4c (the Bravo screen).
static void show_bravo(void)
// --- Bravo screen (Task 4c) --------------------------------------------------
// One-shot timer pointer shared between the auto-return timer and the tap
// handler so each can cancel the other without a dangling pointer.
static lv_timer_t *s_bravo_timer;
// Return HOME from the bravo screen. Used by both the tap handler and the
// auto-return timer. Idempotent (second call is a no-op).
static void bravo_go_home(void)
{
// Cancel the auto-return timer if the tap fired first, and vice versa.
if (s_bravo_timer) {
lv_timer_delete(s_bravo_timer);
s_bravo_timer = NULL;
}
// Grab and clear s_step_screen BEFORE the goto to prevent re-entry.
lv_obj_t *old = s_step_screen;
s_step_screen = NULL;
lisael_aac_stop();
lisael_ui_goto(LISAEL_MODE_HOME);
s_step_screen = NULL;
// lisael_ui_goto uses auto_del=false for cached mode screens, so the
// transient bravo screen must be freed explicitly. Schedule after 350 ms
// (> 250 ms slide anim) so it is already off-screen before deletion.
if (old) {
lv_obj_delete_delayed(old, 350);
}
}
static void bravo_home_cb(lv_event_t *e)
{
(void)e;
bravo_go_home();
}
static void bravo_auto_return_cb(lv_timer_t *t)
{
(void)t;
// Timer self-deletes when it is one-shot (repeat_count=1). Clear the
// pointer first so bravo_go_home() does not try to delete it again.
s_bravo_timer = NULL;
bravo_go_home();
}
// "Pop-in" scale animation for the star. Uses the style transform approach
// (pattern from balloons.c pop_scale_cb) — NOT lv_image_set_scale, which
// leaves the layout box at full size and overflows its cell (see histoires.c).
static void bravo_scale_cb(void *var, int32_t scale)
{
lv_obj_set_style_transform_scale_x((lv_obj_t *)var, scale, 0);
lv_obj_set_style_transform_scale_y((lv_obj_t *)var, scale, 0);
}
static void show_bravo(void)
{
lv_obj_t *scr = lv_obj_create(NULL);
lv_obj_set_style_bg_color(scr, lv_color_hex(0xFFF4C2), 0);
lv_obj_set_style_bg_grad_color(scr, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_bg_grad_dir(scr, LV_GRAD_DIR_VER, 0);
// Star image: pop-in from 0.33x to 1x with overshoot (bouncy feel).
lv_obj_t *star = lv_image_create(scr);
lv_image_set_src(star, &lisael_ic_star);
lv_obj_set_size(star, 140, 140);
lv_image_set_inner_align(star, LV_IMAGE_ALIGN_CONTAIN);
lv_obj_align(star, LV_ALIGN_CENTER, 0, -40);
// Pivot at centre so the scale grows in-place.
lv_obj_set_style_transform_pivot_x(star, 70, 0);
lv_obj_set_style_transform_pivot_y(star, 70, 0);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, star);
lv_anim_set_exec_cb(&a, bravo_scale_cb);
lv_anim_set_values(&a, LV_SCALE_NONE / 3, LV_SCALE_NONE); // 0.33x → 1x
lv_anim_set_duration(&a, 400);
lv_anim_set_path_cb(&a, lv_anim_path_overshoot);
lv_anim_start(&a);
// "Bravo !" label (Fredoka large, centred below the star).
lv_obj_t *txt = lv_label_create(scr);
lv_label_set_text(txt, "Bravo !");
lv_obj_set_style_text_font(txt, &lisael_font_30, 0);
lv_obj_align(txt, LV_ALIGN_CENTER, 0, 50);
// "Accueil" button: tap returns HOME immediately (also cancels the timer).
lv_obj_t *home = lv_button_create(scr);
lv_obj_add_style(home, lisael_ui_style_button(), 0);
lv_obj_set_style_bg_color(home, lv_color_hex(0x1971C2), 0);
lv_obj_set_size(home, 200, 60);
lv_obj_align(home, LV_ALIGN_BOTTOM_MID, 0, -16);
lv_obj_add_event_cb(home, bravo_home_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *hl = lv_label_create(home);
lv_label_set_text(hl, LV_SYMBOL_HOME " Accueil");
lv_obj_set_style_text_color(hl, lv_color_white(), 0);
lv_obj_set_style_text_font(hl, &lisael_font_24, 0);
lv_obj_center(hl);
// Celebratory sound (routine asset; silent fallback if absent on SD).
// provided by Tower routine seed
lisael_aac_play_file("/sdcard/routines/bravo.mp3");
// Load with fade; auto_del=true here deletes the outgoing STEP screen,
// which LVGL was already tracking as the active screen.
lv_screen_load_anim(scr, LV_SCR_LOAD_ANIM_FADE_IN, 250, 0, true);
s_step_screen = scr; // track the bravo screen for explicit cleanup on return
// Auto-return after 2.2 s (one-shot timer). The tap handler deletes it.
s_bravo_timer = lv_timer_create(bravo_auto_return_cb, 2200, NULL);
lv_timer_set_repeat_count(s_bravo_timer, 1);
}