Fix KeyPad focus handling on devices without touch screen

This commit is contained in:
Thomas Farstrike
2025-10-16 12:29:44 +02:00
parent 6ae1778bfc
commit 49dc270e2b
5 changed files with 49 additions and 15 deletions
+1 -1
View File
@@ -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
+1 -4
View File
@@ -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")
+30 -10
View File
@@ -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()
+12
View File
@@ -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)
+5
View File
@@ -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: