From 613130e5b46f96a4f072a6886e67a56c85f06aa2 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sun, 16 Aug 2026 21:38:12 +0200 Subject: [PATCH] Restore scrolling and clean up comments --- Tactility/Source/app/apphub/AppHubApp.cpp | 23 +++++++++++++++---- .../app/apphubdetails/AppHubDetailsApp.cpp | 4 ---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Tactility/Source/app/apphub/AppHubApp.cpp b/Tactility/Source/app/apphub/AppHubApp.cpp index 1891aa29c..12257c888 100644 --- a/Tactility/Source/app/apphub/AppHubApp.cpp +++ b/Tactility/Source/app/apphub/AppHubApp.cpp @@ -38,6 +38,9 @@ struct Context { std::string cachedAppsJsonFile = std::format("{}/app_hub.json", getTempPath()); AppHubEntryList entries; Mutex mutex; + + // Survives across a bury/resurface cycle (e.g. opening AppHubDetailsApp and returning), + int32_t scrollY = 0; }; @@ -46,10 +49,6 @@ void refresh(Context* ctx); void onBackPressed(lv_event_t* event) { auto* ctx = static_cast(lv_event_get_user_data(event)); - // Async, non-blocking - must NOT call app_manager_stop() directly here: that bound-waits - // (thread_join) for this app's own thread to finish, which needs the LVGL lock - // (window_manager_remove()) - but this callback runs ON the LVGL task, which would - // deadlock against itself. AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; app_event_emit(ctx->appInstanceId, &closeEvent); } @@ -86,6 +85,9 @@ void showNoInternet(Context* ctx) { } void showApps(Context* ctx) { + // Refresh rebuilds the list from scratch (cached copy, then again once the network fetch + // lands), which would otherwise reset the user's scroll position each time. + auto scrollY = lv_obj_get_scroll_y(ctx->contentWrapper); lv_obj_clean(ctx->contentWrapper); ctx->mutex.lock(); if (parseJson(ctx->cachedAppsJsonFile, ctx->entries)) { @@ -119,6 +121,8 @@ void showApps(Context* ctx) { lv_obj_set_user_data(entry_button, int_as_voidptr); lv_obj_add_event_cb(entry_button, onAppPressed, LV_EVENT_SHORT_CLICKED, ctx); } + + lv_obj_scroll_to_y(ctx->contentWrapper, scrollY, LV_ANIM_OFF); } else { showRefreshFailedError(ctx, "Failed to load content"); } @@ -182,6 +186,15 @@ void createWidgets(lv_obj_t* parent, void* userData) { lv_obj_set_style_pad_ver(ctx->contentWrapper, 0, LV_STATE_DEFAULT); refresh(ctx); + + lv_obj_scroll_to_y(ctx->contentWrapper, ctx->scrollY, LV_ANIM_OFF); +} + +void destroyWidgets(void* userData) { + auto* ctx = static_cast(userData); + ctx->scrollY = lv_obj_get_scroll_y(ctx->contentWrapper); + ctx->contentWrapper = nullptr; + ctx->refreshButton = nullptr; } int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { @@ -192,7 +205,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) { sub.app_instance_id = appInstanceId; app_event_subscribe(&sub); - WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx); + WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx); bool shouldClose = false; while (!shouldClose) { diff --git a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp index 5009c2901..4fd9117e1 100644 --- a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp +++ b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp @@ -61,10 +61,6 @@ uint32_t showConfirmDialog(Context* ctx, const char* action) { void onBackPressed(lv_event_t* e) { auto* ctx = static_cast(lv_event_get_user_data(e)); - // Async, non-blocking - must NOT call app_manager_stop() directly here: that bound-waits - // (thread_join) for this app's own thread to finish, which needs the LVGL lock - // (window_manager_remove()) - but this callback runs ON the LVGL task, which would - // deadlock against itself. AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} }; app_event_emit(ctx->appInstanceId, &closeEvent); }