@@ -518,7 +518,7 @@ def _ensure_touch_indev():
518518 print ("Created simulated touch input device" )
519519
520520
521- def simulate_click (x , y , press_duration_ms = 50 ):
521+ def simulate_click (x , y , press_duration_ms = 100 ):
522522 """
523523 Simulate a touch/click at the specified coordinates.
524524
@@ -543,7 +543,7 @@ def simulate_click(x, y, press_duration_ms=50):
543543 Args:
544544 x: X coordinate to click (in pixels)
545545 y: Y coordinate to click (in pixels)
546- press_duration_ms: How long to hold the press (default: 50ms )
546+ press_duration_ms: How long to hold the press (default: 100ms )
547547
548548 Example:
549549 from mpos.ui.testing import simulate_click, wait_for_render
@@ -568,50 +568,205 @@ def simulate_click(x, y, press_duration_ms=50):
568568 _touch_y = y
569569 _touch_pressed = True
570570
571- # Process the press immediately
571+ # Process the press event
572+ lv .task_handler ()
573+ time .sleep (0.02 )
572574 lv .task_handler ()
573575
574- def release_timer_cb (timer ):
575- """Timer callback to release the touch press."""
576- global _touch_pressed
577- _touch_pressed = False
578- lv .task_handler () # Process the release immediately
576+ # Wait for press duration
577+ time .sleep (press_duration_ms / 1000.0 )
579578
580- # Schedule the release
581- timer = lv .timer_create (release_timer_cb , press_duration_ms , None )
582- timer .set_repeat_count (1 )
579+ # Release the touch
580+ _touch_pressed = False
583581
584- def click_button (button_text , timeout = 5 ):
585- """Find and click a button with given text."""
582+ # Process the release event - this triggers the CLICKED event
583+ lv .task_handler ()
584+ time .sleep (0.02 )
585+ lv .task_handler ()
586+ time .sleep (0.02 )
587+ lv .task_handler ()
588+
589+ def click_button (button_text , timeout = 5 , use_send_event = True ):
590+ """Find and click a button with given text.
591+
592+ Args:
593+ button_text: Text to search for in button labels
594+ timeout: Maximum time to wait for button to appear (default: 5s)
595+ use_send_event: If True, use send_event() which is more reliable for
596+ triggering button actions. If False, use simulate_click()
597+ which simulates actual touch input. (default: True)
598+
599+ Returns:
600+ True if button was found and clicked, False otherwise
601+ """
586602 start = time .time ()
587603 while time .time () - start < timeout :
588604 button = find_button_with_text (lv .screen_active (), button_text )
589605 if button :
590606 coords = get_widget_coords (button )
591607 if coords :
592608 print (f"Clicking button '{ button_text } ' at ({ coords ['center_x' ]} , { coords ['center_y' ]} )" )
593- simulate_click (coords ['center_x' ], coords ['center_y' ])
609+ if use_send_event :
610+ # Use send_event for more reliable button triggering
611+ button .send_event (lv .EVENT .CLICKED , None )
612+ else :
613+ # Use simulate_click for actual touch simulation
614+ simulate_click (coords ['center_x' ], coords ['center_y' ])
594615 wait_for_render (iterations = 20 )
595616 return True
596617 wait_for_render (iterations = 5 )
597618 print (f"ERROR: Button '{ button_text } ' not found after { timeout } s" )
598619 return False
599620
600- def click_label (label_text , timeout = 5 ):
601- """Find a label with given text and click on it (or its clickable parent)."""
621+ def click_label (label_text , timeout = 5 , use_send_event = True ):
622+ """Find a label with given text and click on it (or its clickable parent).
623+
624+ This function finds a label, scrolls it into view (with multiple attempts
625+ if needed), verifies it's within the visible viewport, and then clicks it.
626+ If the label itself is not clickable, it will try clicking the parent container.
627+
628+ Args:
629+ label_text: Text to search for in labels
630+ timeout: Maximum time to wait for label to appear (default: 5s)
631+ use_send_event: If True, use send_event() on clickable parent which is more
632+ reliable. If False, use simulate_click(). (default: True)
633+
634+ Returns:
635+ True if label was found and clicked, False otherwise
636+ """
602637 start = time .time ()
603638 while time .time () - start < timeout :
604639 label = find_label_with_text (lv .screen_active (), label_text )
605640 if label :
606- print ("Scrolling label to view..." )
607- label .scroll_to_view_recursive (True )
608- wait_for_render (iterations = 50 ) # needs quite a bit of time
641+ # Get screen dimensions for viewport check
642+ screen = lv .screen_active ()
643+ screen_coords = get_widget_coords (screen )
644+ if not screen_coords :
645+ screen_coords = {'x1' : 0 , 'y1' : 0 , 'x2' : 320 , 'y2' : 240 }
646+
647+ # Try scrolling multiple times to ensure label is fully visible
648+ max_scroll_attempts = 5
649+ for scroll_attempt in range (max_scroll_attempts ):
650+ print (f"Scrolling label to view (attempt { scroll_attempt + 1 } /{ max_scroll_attempts } )..." )
651+ label .scroll_to_view_recursive (True )
652+ wait_for_render (iterations = 50 ) # needs quite a bit of time for scroll animation
653+
654+ # Get updated coordinates after scroll
655+ coords = get_widget_coords (label )
656+ if not coords :
657+ break
658+
659+ # Check if label center is within visible viewport
660+ # Account for some margin (e.g., status bar at top, nav bar at bottom)
661+ # Use a larger bottom margin to ensure the element is fully clickable
662+ viewport_top = screen_coords ['y1' ] + 30 # Account for status bar
663+ viewport_bottom = screen_coords ['y2' ] - 30 # Larger margin at bottom for clickability
664+ viewport_left = screen_coords ['x1' ]
665+ viewport_right = screen_coords ['x2' ]
666+
667+ center_x = coords ['center_x' ]
668+ center_y = coords ['center_y' ]
669+
670+ is_visible = (viewport_left <= center_x <= viewport_right and
671+ viewport_top <= center_y <= viewport_bottom )
672+
673+ if is_visible :
674+ print (f"Label '{ label_text } ' is visible at ({ center_x } , { center_y } )" )
675+
676+ # Try to find a clickable parent (container) - many UIs have clickable containers
677+ # with non-clickable labels inside. We'll click on the label's position but
678+ # the event should bubble up to the clickable parent.
679+ click_target = label
680+ clickable_parent = None
681+ click_coords = coords
682+ try :
683+ parent = label .get_parent ()
684+ if parent and parent .has_flag (lv .obj .FLAG .CLICKABLE ):
685+ # The parent is clickable - we can use send_event on it
686+ clickable_parent = parent
687+ parent_coords = get_widget_coords (parent )
688+ if parent_coords :
689+ print (f"Found clickable parent container: ({ parent_coords ['x1' ]} , { parent_coords ['y1' ]} ) to ({ parent_coords ['x2' ]} , { parent_coords ['y2' ]} )" )
690+ # Use label's x but ensure y is within parent bounds
691+ click_x = center_x
692+ click_y = center_y
693+ # Clamp to parent bounds with some margin
694+ if click_y < parent_coords ['y1' ] + 5 :
695+ click_y = parent_coords ['y1' ] + 5
696+ if click_y > parent_coords ['y2' ] - 5 :
697+ click_y = parent_coords ['y2' ] - 5
698+ click_coords = {'center_x' : click_x , 'center_y' : click_y }
699+ except Exception as e :
700+ print (f"Could not check parent clickability: { e } " )
701+
702+ print (f"Clicking label '{ label_text } ' at ({ click_coords ['center_x' ]} , { click_coords ['center_y' ]} )" )
703+ if use_send_event and clickable_parent :
704+ # Use send_event on the clickable parent for more reliable triggering
705+ print (f"Using send_event on clickable parent" )
706+ clickable_parent .send_event (lv .EVENT .CLICKED , None )
707+ else :
708+ # Use simulate_click for actual touch simulation
709+ simulate_click (click_coords ['center_x' ], click_coords ['center_y' ])
710+ wait_for_render (iterations = 20 )
711+ return True
712+ else :
713+ print (f"Label '{ label_text } ' at ({ center_x } , { center_y } ) not fully visible "
714+ f"(viewport: y={ viewport_top } -{ viewport_bottom } ), scrolling more..." )
715+ # Additional scroll - try scrolling the parent container
716+ try :
717+ parent = label .get_parent ()
718+ if parent :
719+ # Try to find a scrollable ancestor
720+ scrollable = parent
721+ for _ in range (5 ): # Check up to 5 levels up
722+ try :
723+ grandparent = scrollable .get_parent ()
724+ if grandparent :
725+ scrollable = grandparent
726+ except :
727+ break
728+
729+ # Scroll by a fixed amount to bring label more into view
730+ current_scroll = scrollable .get_scroll_y ()
731+ if center_y > viewport_bottom :
732+ # Need to scroll down (increase scroll_y)
733+ scrollable .scroll_to_y (current_scroll + 60 , True )
734+ elif center_y < viewport_top :
735+ # Need to scroll up (decrease scroll_y)
736+ scrollable .scroll_to_y (max (0 , current_scroll - 60 ), True )
737+ wait_for_render (iterations = 30 )
738+ except Exception as e :
739+ print (f"Additional scroll failed: { e } " )
740+
741+ # If we exhausted scroll attempts, try clicking anyway
609742 coords = get_widget_coords (label )
610743 if coords :
611- print (f"Clicking label '{ label_text } ' at ({ coords ['center_x' ]} , { coords ['center_y' ]} )" )
612- simulate_click (coords ['center_x' ], coords ['center_y' ])
744+ # Try to find a clickable parent even for fallback click
745+ click_coords = coords
746+ try :
747+ parent = label .get_parent ()
748+ if parent and parent .has_flag (lv .obj .FLAG .CLICKABLE ):
749+ parent_coords = get_widget_coords (parent )
750+ if parent_coords :
751+ click_coords = parent_coords
752+ print (f"Using clickable parent for fallback click" )
753+ except :
754+ pass
755+
756+ print (f"Clicking at ({ click_coords ['center_x' ]} , { click_coords ['center_y' ]} ) after max scroll attempts" )
757+ # Try to use send_event if we have a clickable parent
758+ try :
759+ parent = label .get_parent ()
760+ if use_send_event and parent and parent .has_flag (lv .obj .FLAG .CLICKABLE ):
761+ print (f"Using send_event on clickable parent for fallback" )
762+ parent .send_event (lv .EVENT .CLICKED , None )
763+ else :
764+ simulate_click (click_coords ['center_x' ], click_coords ['center_y' ])
765+ except :
766+ simulate_click (click_coords ['center_x' ], click_coords ['center_y' ])
613767 wait_for_render (iterations = 20 )
614768 return True
769+
615770 wait_for_render (iterations = 5 )
616771 print (f"ERROR: Label '{ label_text } ' not found after { timeout } s" )
617772 return False
0 commit comments