Skip to content

Commit 4fa71ab

Browse files
Fix tests/test_graphical_keyboard_q_button_bug.py
1 parent 6096f4c commit 4fa71ab

File tree

2 files changed

+113
-54
lines changed

2 files changed

+113
-54
lines changed

internal_filesystem/lib/mpos/ui/testing.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,104 @@ def find_button_with_text(obj, search_text):
383383
return None
384384

385385

386+
def get_keyboard_button_coords(keyboard, button_text):
387+
"""
388+
Get the coordinates of a specific button on an LVGL keyboard/buttonmatrix.
389+
390+
This function calculates the exact center position of a keyboard button
391+
by finding its index and computing its position based on the keyboard's
392+
layout, control widths, and actual screen coordinates.
393+
394+
Args:
395+
keyboard: LVGL keyboard widget (or MposKeyboard wrapper)
396+
button_text: Text of the button to find (e.g., "q", "a", "1")
397+
398+
Returns:
399+
dict with 'center_x' and 'center_y', or None if button not found
400+
401+
Example:
402+
from mpos.ui.keyboard import MposKeyboard
403+
keyboard = MposKeyboard(screen)
404+
coords = get_keyboard_button_coords(keyboard, "q")
405+
if coords:
406+
simulate_click(coords['center_x'], coords['center_y'])
407+
"""
408+
# Get the underlying LVGL keyboard if this is a wrapper
409+
if hasattr(keyboard, '_keyboard'):
410+
lvgl_keyboard = keyboard._keyboard
411+
else:
412+
lvgl_keyboard = keyboard
413+
414+
# Find the button index
415+
button_idx = None
416+
for i in range(100): # Check up to 100 buttons
417+
try:
418+
text = lvgl_keyboard.get_button_text(i)
419+
if text == button_text:
420+
button_idx = i
421+
break
422+
except:
423+
break # No more buttons
424+
425+
if button_idx is None:
426+
return None
427+
428+
# Get keyboard widget coordinates
429+
area = lv.area_t()
430+
lvgl_keyboard.get_coords(area)
431+
kb_x = area.x1
432+
kb_y = area.y1
433+
kb_width = area.x2 - area.x1
434+
kb_height = area.y2 - area.y1
435+
436+
# Parse the keyboard layout to find button position
437+
# Note: LVGL get_button_text() skips '\n' markers, so they're not in the indices
438+
# Standard keyboard layout (from MposKeyboard):
439+
# Row 0: 10 buttons (q w e r t y u i o p)
440+
# Row 1: 9 buttons (a s d f g h j k l)
441+
# Row 2: 9 buttons (shift z x c v b n m backspace)
442+
# Row 3: 5 buttons (?123, comma, space, dot, enter)
443+
444+
# Define row lengths for standard keyboard
445+
row_lengths = [10, 9, 9, 5]
446+
447+
# Find which row our button is in
448+
row = 0
449+
buttons_before = 0
450+
for row_len in row_lengths:
451+
if button_idx < buttons_before + row_len:
452+
# Button is in this row
453+
col = button_idx - buttons_before
454+
buttons_this_row = row_len
455+
break
456+
buttons_before += row_len
457+
row += 1
458+
else:
459+
# Button not found in standard layout, use row 0
460+
row = 0
461+
col = button_idx
462+
buttons_this_row = 10
463+
464+
# Calculate position
465+
# Approximate: divide keyboard into equal rows and columns
466+
# (This is simplified - actual LVGL uses control widths, but this is good enough)
467+
num_rows = 4 # Typical keyboard has 4 rows
468+
button_height = kb_height / num_rows
469+
button_width = kb_width / max(buttons_this_row, 1)
470+
471+
# Calculate center position
472+
center_x = int(kb_x + (col * button_width) + (button_width / 2))
473+
center_y = int(kb_y + (row * button_height) + (button_height / 2))
474+
475+
return {
476+
'center_x': center_x,
477+
'center_y': center_y,
478+
'button_idx': button_idx,
479+
'row': row,
480+
'col': col
481+
}
482+
483+
386484
def _touch_read_cb(indev_drv, data):
387485
"""
388486
Internal callback for simulated touch input device.

tests/test_graphical_keyboard_q_button_bug.py

Lines changed: 15 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
wait_for_render,
2121
find_button_with_text,
2222
get_widget_coords,
23+
get_keyboard_button_coords,
2324
simulate_click,
2425
print_screen_labels
2526
)
@@ -79,43 +80,16 @@ def test_q_button_works(self):
7980
# --- Test 'q' button ---
8081
print("\n--- Testing 'q' button ---")
8182

82-
# Find button index for 'q' in the keyboard
83-
q_button_id = None
84-
for i in range(100): # Check first 100 button indices
85-
try:
86-
text = keyboard.get_button_text(i)
87-
if text == "q":
88-
q_button_id = i
89-
print(f"Found 'q' button at index {i}")
90-
break
91-
except:
92-
break # No more buttons
93-
94-
self.assertIsNotNone(q_button_id, "Should find 'q' button on keyboard")
95-
96-
# Get the keyboard widget coordinates to calculate button position
97-
keyboard_area = lv.area_t()
98-
keyboard.get_coords(keyboard_area)
99-
print(f"Keyboard area: x1={keyboard_area.x1}, y1={keyboard_area.y1}, x2={keyboard_area.x2}, y2={keyboard_area.y2}")
100-
101-
# LVGL keyboards organize buttons in a grid
102-
# From the map: "q" is at index 0, in top row (10 buttons per row)
103-
# Let's estimate position based on keyboard layout
104-
# Top row starts at y1 + some padding, each button is ~width/10
105-
keyboard_width = keyboard_area.x2 - keyboard_area.x1
106-
keyboard_height = keyboard_area.y2 - keyboard_area.y1
107-
button_width = keyboard_width // 10 # ~10 buttons per row
108-
button_height = keyboard_height // 4 # ~4 rows
109-
110-
# 'q' is first button (index 0), top row
111-
q_x = keyboard_area.x1 + button_width // 2
112-
q_y = keyboard_area.y1 + button_height // 2
83+
# Get exact button coordinates using helper function
84+
q_coords = get_keyboard_button_coords(keyboard, "q")
85+
self.assertIsNotNone(q_coords, "Should find 'q' button on keyboard")
11386

114-
print(f"Estimated 'q' button position: ({q_x}, {q_y})")
87+
print(f"Found 'q' button at index {q_coords['button_idx']}, row {q_coords['row']}, col {q_coords['col']}")
88+
print(f"Exact 'q' button position: ({q_coords['center_x']}, {q_coords['center_y']})")
11589

11690
# Click the 'q' button
117-
print(f"Clicking 'q' button at ({q_x}, {q_y})")
118-
simulate_click(q_x, q_y)
91+
print(f"Clicking 'q' button at ({q_coords['center_x']}, {q_coords['center_y']})")
92+
simulate_click(q_coords['center_x'], q_coords['center_y'])
11993
wait_for_render(10)
12094

12195
# Check textarea content
@@ -134,29 +108,16 @@ def test_q_button_works(self):
134108
wait_for_render(5)
135109
print("Cleared textarea")
136110

137-
# Find button index for 'a'
138-
a_button_id = None
139-
for i in range(100):
140-
try:
141-
text = keyboard.get_button_text(i)
142-
if text == "a":
143-
a_button_id = i
144-
print(f"Found 'a' button at index {i}")
145-
break
146-
except:
147-
break
148-
149-
self.assertIsNotNone(a_button_id, "Should find 'a' button on keyboard")
150-
151-
# 'a' is at index 11 (second row, first position)
152-
a_x = keyboard_area.x1 + button_width // 2
153-
a_y = keyboard_area.y1 + button_height + button_height // 2
111+
# Get exact button coordinates using helper function
112+
a_coords = get_keyboard_button_coords(keyboard, "a")
113+
self.assertIsNotNone(a_coords, "Should find 'a' button on keyboard")
154114

155-
print(f"Estimated 'a' button position: ({a_x}, {a_y})")
115+
print(f"Found 'a' button at index {a_coords['button_idx']}, row {a_coords['row']}, col {a_coords['col']}")
116+
print(f"Exact 'a' button position: ({a_coords['center_x']}, {a_coords['center_y']})")
156117

157118
# Click the 'a' button
158-
print(f"Clicking 'a' button at ({a_x}, {a_y})")
159-
simulate_click(a_x, a_y)
119+
print(f"Clicking 'a' button at ({a_coords['center_x']}, {a_coords['center_y']})")
120+
simulate_click(a_coords['center_x'], a_coords['center_y'])
160121
wait_for_render(10)
161122

162123
# Check textarea content

0 commit comments

Comments
 (0)