From 49dc270e2b725d1462a45466235d0c2d00d07421 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Thu, 16 Oct 2025 12:29:44 +0200 Subject: [PATCH] Fix KeyPad focus handling on devices without touch screen --- internal_filesystem/boot_fri3d-2024.py | 2 +- internal_filesystem/lib/mpos/apps.py | 5 +-- internal_filesystem/lib/mpos/ui/__init__.py | 40 +++++++++++++++------ internal_filesystem/lib/mpos/util.py | 12 +++++++ internal_filesystem/main.py | 5 +++ 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/internal_filesystem/boot_fri3d-2024.py b/internal_filesystem/boot_fri3d-2024.py index 368ed38a..cceb0570 100644 --- a/internal_filesystem/boot_fri3d-2024.py +++ b/internal_filesystem/boot_fri3d-2024.py @@ -210,7 +210,7 @@ group.set_default() indev = lv.indev_create() indev.set_type(lv.INDEV_TYPE.KEYPAD) indev.set_read_cb(keypad_read_cb) -indev.set_group(group) +indev.set_group(group) # is this needed? maybe better to move the default group creation to main.py so it's available everywhere... disp = lv.display_get_default() # NOQA indev.set_display(disp) # different from display indev.enable(True) # NOQA diff --git a/internal_filesystem/lib/mpos/apps.py b/internal_filesystem/lib/mpos/apps.py index bd342c3b..554ab77c 100644 --- a/internal_filesystem/lib/mpos/apps.py +++ b/internal_filesystem/lib/mpos/apps.py @@ -334,10 +334,7 @@ class ActivityNavigator: activity.intent = intent activity._result_callback = result_callback # Pass callback to activity start_time = utime.ticks_ms() - # Remove objects from previous screens from the focus group: - focusgroup = lv.group_get_default() - if focusgroup: # on esp32 this may not be set - focusgroup.remove_all_objs() # might be better to save and restore the group for "back" actions + mpos.ui.save_and_clear_current_focusgroup() activity.onCreate() end_time = utime.ticks_diff(utime.ticks_ms(), start_time) print(f"apps.py _launch_activity: activity.onCreate took {end_time}ms") diff --git a/internal_filesystem/lib/mpos/ui/__init__.py b/internal_filesystem/lib/mpos/ui/__init__.py index 58b3b5de..21c26459 100644 --- a/internal_filesystem/lib/mpos/ui/__init__.py +++ b/internal_filesystem/lib/mpos/ui/__init__.py @@ -4,6 +4,7 @@ import mpos.time import mpos.wifi from mpos.ui.anim import WidgetAnimator import mpos.ui.topmenu +import mpos.util th = None @@ -172,30 +173,44 @@ def close_top_layer_msgboxes(): print(f"Top layer still has {child_count} children") -screen_stack = [] # Stack of (activity, screen) tuples +screen_stack = [] # Stack of (activity, screen, focusgroup) tuples def empty_screen_stack(): global screen_stack screen_stack.clear() +def move_focusgroup_objects(fromgroup, togroup): + print(f"Moving {fromgroup.get_obj_count()} focused objects") + for objnr in range(fromgroup.get_obj_count()): + #print(f"saving object {objnr} from default focusgroup to current_focusgroup") + next = fromgroup.get_obj_by_index(0) + mpos.util.print_lvgl_widget(next) + if next: + togroup.add_obj(next) + print("Done moving focused objects") + + +# Saves all objects from the default focus group in the activity's focus group +def save_and_clear_current_focusgroup(): + global screen_stack + if len(screen_stack) > 0: + current_activity, current_screen, current_focusgroup = screen_stack[-1] + move_focusgroup_objects(lv.group_get_default(), current_focusgroup) + # new_activity might be None for compatibility, can be removed if compatibility is no longer needed def setContentView(new_activity, new_screen): global screen_stack # Get current activity and screen - current_activity, current_screen = None, None if len(screen_stack) > 0: - current_activity, current_screen = screen_stack[-1] - - if current_activity and current_screen: + current_activity, current_screen, current_focusgroup = screen_stack[-1] # Notify current activity that it's being backgrounded: current_activity.onPause(current_screen) current_activity.onStop(current_screen) # don't destroy because the user might go back to it - # Start the new one: - print("Appending screen to screen_stack") - screen_stack.append((new_activity, new_screen)) + print("Appending new activity, new screen and new focusgroup to screen_stack") + screen_stack.append((new_activity, new_screen, lv.group_create())) close_top_layer_msgboxes() # otherwise they remain if new_activity: #start_time = utime.ticks_ms() @@ -215,11 +230,13 @@ def setContentView(new_activity, new_screen): #print(f"ui.py setContentView: new_activity.onResume took {end_time}ms") def remove_and_stop_current_activity(): - current_activity, current_screen = screen_stack.pop() # Get current activity and its screen + current_activity, current_screen, current_focusgroup = screen_stack.pop() # Get current activity and its screen if current_activity: current_activity.onPause(current_screen) current_activity.onStop(current_screen) current_activity.onDestroy(current_screen) + if current_screen: + current_screen.clean() # should free up memory def back_screen(): print("back_screen() running") @@ -229,11 +246,14 @@ def back_screen(): return False # No previous screen #close_top_layer_msgboxes() # would be nicer to "cancel" all input events remove_and_stop_current_activity() - prev_activity, prev_screen = screen_stack[-1] # load previous screen + prev_activity, prev_screen, prev_focusgroup = screen_stack[-1] # load previous screen print("loading prev_screen with animation") lv.screen_load_anim(prev_screen, lv.SCR_LOAD_ANIM.OVER_RIGHT, 500, 0, True) # True means delete the old screen, which is fine as we're going back and current_activity.onDestroy() was called + # Restore the focused objects + move_focusgroup_objects(prev_focusgroup, lv.group_get_default()) if prev_activity: prev_activity.onResume(prev_screen) + print(f"5 default focus group has {lv.group_get_default().get_obj_count()} items") if len(screen_stack) == 1: mpos.ui.topmenu.open_bar() diff --git a/internal_filesystem/lib/mpos/util.py b/internal_filesystem/lib/mpos/util.py index 8fb8c03f..f4d3e9e3 100644 --- a/internal_filesystem/lib/mpos/util.py +++ b/internal_filesystem/lib/mpos/util.py @@ -1,3 +1,5 @@ +import lvgl as lv + def urldecode(s): result = "" i = 0 @@ -9,3 +11,13 @@ def urldecode(s): result += s[i] i += 1 return result + +def print_lvgl_widget(obj, depth=0): + if obj: + label = "" + if isinstance(obj,lv.label): + label = f"has label {obj.get_text()}" + padding = " " * depth + print(f"{padding}{obj} with size {obj.get_width()}x{obj.get_height()} {label}") + for childnr in range(obj.get_child_count()): + print_lvgl_widget(obj.get_child(childnr), depth+1) diff --git a/internal_filesystem/main.py b/internal_filesystem/main.py index 530fbad6..0fd399cd 100644 --- a/internal_filesystem/main.py +++ b/internal_filesystem/main.py @@ -36,6 +36,11 @@ mpos.ui.topmenu.create_notification_bar() mpos.ui.topmenu.create_drawer(display) mpos.ui.handle_back_swipe() mpos.ui.handle_top_swipe() +# Clear top menu, notification bar, swipe back and swipe down buttons +# Ideally, these would be stored in a different focusgroup that is used when the user opens the drawer +focusgroup = lv.group_get_default() +if focusgroup: # on esp32 this may not be set + focusgroup.remove_all_objs() # might be better to save and restore the group for "back" actions mpos.ui.th = task_handler.TaskHandler(duration=5) # 5ms is recommended for MicroPython+LVGL on desktop try: