feat(box): routine bravo screen + cleanup
This commit is contained in:
+114
-6
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user