@@ -496,31 +496,112 @@ int getChildrenCount(void* element) {
496496 return result;
497497}
498498
499+ static CFStringRef kAXLinkRole = CFSTR (" AXLink" );
500+ static CFStringRef kAXCheckboxRole = CFSTR (" AXCheckBox" );
501+ static CFStringRef kAXFocusableAttribute = CFSTR (" AXFocusable" );
502+ static CFStringRef kAXVisibleAttribute = CFSTR (" AXVisible" );
503+
499504// Check if element has click action
500505int hasClickAction (void * element) {
501506 if (!element) return 0 ;
502-
503507 AXUIElementRef axElement = (AXUIElementRef)element;
508+
509+ // Check explicit actions
504510 CFArrayRef actions = NULL ;
511+ if (AXUIElementCopyActionNames (axElement, &actions) == kAXErrorSuccess && actions) {
512+ CFIndex count = CFArrayGetCount (actions);
513+ for (CFIndex i = 0 ; i < count; i++) {
514+ CFStringRef action = (CFStringRef)CFArrayGetValueAtIndex (actions, i);
515+ if (CFStringCompare (action, kAXPressAction , 0 ) == kCFCompareEqualTo ||
516+ CFStringCompare (action, CFSTR (" AXConfirm" ), 0 ) == kCFCompareEqualTo ||
517+ CFStringCompare (action, CFSTR (" AXPick" ), 0 ) == kCFCompareEqualTo ||
518+ CFStringCompare (action, CFSTR (" AXShowMenu" ), 0 ) == kCFCompareEqualTo ) {
519+ CFRelease (actions);
520+ return 1 ;
521+ }
522+ }
523+ CFRelease (actions);
524+ }
525+
526+ // Focusable + enabled
527+ CFBooleanRef enabled = NULL ;
528+ if (AXUIElementCopyAttributeValue (axElement, kAXEnabledAttribute , (CFTypeRef *)&enabled) == kAXErrorSuccess && enabled) {
529+ if (CFBooleanGetValue (enabled)) {
530+ CFRelease (enabled);
531+ CFBooleanRef focusable = NULL ;
532+ if (AXUIElementCopyAttributeValue (axElement, kAXFocusableAttribute , (CFTypeRef *)&focusable) == kAXErrorSuccess && focusable) {
533+ int isFocus = CFBooleanGetValue (focusable);
534+ CFRelease (focusable);
535+ if (isFocus) return 1 ;
536+ }
537+ } else CFRelease (enabled);
538+ }
505539
506- AXError error = AXUIElementCopyActionNames (axElement, &actions);
507- if (error != kAXErrorSuccess || !actions) {
508- return 0 ;
540+ // Has title or label
541+ CFTypeRef title = NULL ;
542+ if (AXUIElementCopyAttributeValue (axElement, kAXTitleAttribute , &title) == kAXErrorSuccess && title) {
543+ CFRelease (title);
544+ return 1 ;
545+ }
546+
547+ // Has description or help
548+ CFTypeRef desc = NULL ;
549+ if (AXUIElementCopyAttributeValue (axElement, kAXDescriptionAttribute , &desc) == kAXErrorSuccess && desc) {
550+ CFRelease (desc);
551+ return 1 ;
552+ }
553+
554+ // Has value or selected state
555+ CFTypeRef value = NULL ;
556+ if (AXUIElementCopyAttributeValue (axElement, kAXValueAttribute , &value) == kAXErrorSuccess && value) {
557+ CFRelease (value);
558+ return 1 ;
559+ }
560+
561+ CFTypeRef selected = NULL ;
562+ if (AXUIElementCopyAttributeValue (axElement, kAXSelectedAttribute , &selected) == kAXErrorSuccess && selected) {
563+ CFRelease (selected);
564+ return 1 ;
565+ }
566+
567+ // Has visual bounds
568+ CFTypeRef position = NULL ;
569+ CFTypeRef size = NULL ;
570+ if (AXUIElementCopyAttributeValue (axElement, kAXPositionAttribute , &position) == kAXErrorSuccess &&
571+ AXUIElementCopyAttributeValue (axElement, kAXSizeAttribute , &size) == kAXErrorSuccess &&
572+ position && size) {
573+ CFRelease (position);
574+ CFRelease (size);
575+ return 1 ;
509576 }
577+ if (position) CFRelease (position);
578+ if (size) CFRelease (size);
510579
511- CFIndex count = CFArrayGetCount (actions);
512- int hasPress = 0 ;
580+ // Expanded/collapsible state
581+ CFTypeRef expanded = NULL ;
582+ if (AXUIElementCopyAttributeValue (axElement, kAXExpandedAttribute , &expanded) == kAXErrorSuccess && expanded) {
583+ CFRelease (expanded);
584+ return 1 ;
585+ }
513586
514- for (CFIndex i = 0 ; i < count; i++) {
515- CFStringRef action = (CFStringRef)CFArrayGetValueAtIndex (actions, i);
516- if (CFStringCompare (action, kAXPressAction , 0 ) == kCFCompareEqualTo ) {
517- hasPress = 1 ;
518- break ;
587+ // Visible + leaf node
588+ CFBooleanRef visible = NULL ;
589+ if (AXUIElementCopyAttributeValue (axElement, kAXVisibleAttribute , (CFTypeRef *)&visible) == kAXErrorSuccess && visible) {
590+ if (CFBooleanGetValue (visible)) {
591+ CFArrayRef children = NULL ;
592+ if (AXUIElementCopyAttributeValue (axElement, kAXChildrenAttribute , (CFTypeRef *)&children) == kAXErrorSuccess ) {
593+ if (children && CFArrayGetCount (children) == 0 ) {
594+ CFRelease (children);
595+ CFRelease (visible);
596+ return 1 ;
597+ }
598+ if (children) CFRelease (children);
599+ }
519600 }
601+ CFRelease (visible);
520602 }
521603
522- CFRelease (actions);
523- return hasPress;
604+ return 0 ;
524605}
525606
526607// Perform click
0 commit comments