diff --git a/Apps/Breakout/main/Source/Breakout.cpp b/Apps/Breakout/main/Source/Breakout.cpp index bb004a1..d4b78ce 100644 --- a/Apps/Breakout/main/Source/Breakout.cpp +++ b/Apps/Breakout/main/Source/Breakout.cpp @@ -167,6 +167,7 @@ static uint32_t getActionIconPadding(UiDensity uiDensity) { /* ── Event Callbacks (declared here so createWidgets can wire them up) ── */ static void onTick(lv_timer_t* timer); +static void onGameAreaDeleted(lv_event_t* e); static void onPressed(lv_event_t* e); static void onClicked(lv_event_t* e); static void onKey(lv_event_t* e); @@ -404,6 +405,7 @@ void breakoutCreateWidgets(lv_obj_t* parent, void* userData) { lv_obj_add_event_cb(ctx->gameArea, onClicked, LV_EVENT_SHORT_CLICKED, ctx); lv_obj_add_event_cb(ctx->gameArea, onKey, LV_EVENT_KEY, ctx); lv_obj_add_event_cb(ctx->gameArea, onReenterKeyMode, LV_EVENT_CLICKED, ctx); + lv_obj_add_event_cb(ctx->gameArea, onGameAreaDeleted, LV_EVENT_DELETE, ctx); // Keyboard focus - explicit enter/exit, no focus/defocus handlers lv_group_t* group = lv_group_get_default(); @@ -417,16 +419,16 @@ void breakoutCreateWidgets(lv_obj_t* parent, void* userData) { ctx->gameTimer = lv_timer_create(onTick, TICK_MS, ctx); } -void breakoutTeardown(Context* ctx) { +// The window manager deletes the widgets when the app closes, and when another window covers it. +// The game timer moves the widgets, so it must stop with them. +static void onGameAreaDeleted(lv_event_t* e) { + auto* ctx = static_cast(lv_event_get_user_data(e)); if (ctx->gameTimer) { lv_timer_delete(ctx->gameTimer); ctx->gameTimer = nullptr; } - if (ctx->gameArea) { - lv_group_t* group = lv_group_get_default(); - if (group) lv_group_set_editing(group, false); - lv_group_remove_obj(ctx->gameArea); - } + lv_group_t* group = lv_group_get_default(); + if (group) lv_group_set_editing(group, false); ctx->gameArea = nullptr; ctx->paddle = nullptr; for (int i = 0; i < MAX_BRICKS; i++) ctx->bricks[i] = nullptr; @@ -441,7 +443,9 @@ void breakoutTeardown(Context* ctx) { ctx->livesLabel = nullptr; ctx->messageLabel = nullptr; ctx->soundBtnIcon = nullptr; +} +void breakoutTeardown(Context* ctx) { // Clean up sfx engine if (ctx->sfxEngine) { ctx->sfxEngine->stop(); diff --git a/Apps/Breakout/main/Source/Breakout.h b/Apps/Breakout/main/Source/Breakout.h index ea2c22e..99d6717 100644 --- a/Apps/Breakout/main/Source/Breakout.h +++ b/Apps/Breakout/main/Source/Breakout.h @@ -131,5 +131,5 @@ struct Context { /** window_manager_create()'s WindowCreateWidgetsFn - @a userData is the Context* for this instance. */ void breakoutCreateWidgets(lv_obj_t* parent, void* userData); -/** Releases resources acquired while the window was shown (sfx engine, LVGL group/timer). Call once the window is torn down. */ +/** Releases the sfx engine. Call once the window is torn down. */ void breakoutTeardown(Context* ctx); diff --git a/Apps/Breakout/manifest.properties b/Apps/Breakout/manifest.properties index 6a102e5..cba12e5 100644 --- a/Apps/Breakout/manifest.properties +++ b/Apps/Breakout/manifest.properties @@ -2,8 +2,8 @@ manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 id=tactility.breakout -version.name=0.18.0 -version.code=18 +version.name=0.19.0 +version.code=19 name=Breakout description=Classic brick-breaking arcade game app.0.id=tactility.breakout