refactor(ui-query): extract shared find_elements() to common.py
Consolidates duplicate find_elements() from find-element.py and query-state.py. Shared function returns raw accessibles; callers map through their own info extractors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5a80055ed8
commit
fedec9c46a
|
|
@ -67,3 +67,56 @@ def get_all_windows():
|
|||
windows.append({"accessible": window})
|
||||
|
||||
return windows
|
||||
|
||||
|
||||
def find_elements(accessible, role=None, name=None, results=None, max_depth=15, depth=0, limit=20):
|
||||
"""Recursively search for elements matching role/name criteria.
|
||||
|
||||
Args:
|
||||
accessible: Root accessible to search from
|
||||
role: Role substring to match (case-insensitive)
|
||||
name: Name substring to match (case-insensitive)
|
||||
results: Accumulator list (internal use)
|
||||
max_depth: Maximum tree depth to traverse
|
||||
depth: Current depth (internal use)
|
||||
limit: Maximum results to return
|
||||
|
||||
Returns:
|
||||
List of matching accessible objects
|
||||
"""
|
||||
if results is None:
|
||||
results = []
|
||||
|
||||
if len(results) >= limit or depth > max_depth:
|
||||
return results
|
||||
|
||||
try:
|
||||
matches = True
|
||||
|
||||
if role:
|
||||
elem_role = accessible.getRoleName().lower().replace(" ", "-")
|
||||
if role.lower() not in elem_role:
|
||||
matches = False
|
||||
|
||||
if name and matches:
|
||||
elem_name = (accessible.name or "").lower()
|
||||
if name.lower() not in elem_name:
|
||||
matches = False
|
||||
|
||||
if matches and (role or name):
|
||||
results.append(accessible)
|
||||
|
||||
for i in range(accessible.childCount):
|
||||
if len(results) >= limit:
|
||||
break
|
||||
try:
|
||||
child = accessible.getChildAtIndex(i)
|
||||
if child:
|
||||
find_elements(child, role, name, results, max_depth, depth + 1, limit)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return results
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import sys
|
|||
|
||||
import pyatspi
|
||||
|
||||
from common import find_windows, get_all_windows
|
||||
from common import find_windows, get_all_windows, find_elements
|
||||
|
||||
|
||||
def get_element_info(element):
|
||||
|
|
@ -86,48 +86,6 @@ def get_element_info(element):
|
|||
return info
|
||||
|
||||
|
||||
def find_elements(accessible, role=None, name=None, results=None, max_depth=15, depth=0, limit=20):
|
||||
"""Recursively search for matching elements."""
|
||||
if results is None:
|
||||
results = []
|
||||
|
||||
if len(results) >= limit or depth > max_depth:
|
||||
return results
|
||||
|
||||
try:
|
||||
# Check if this element matches
|
||||
matches = True
|
||||
|
||||
if role:
|
||||
elem_role = accessible.getRoleName().lower().replace(" ", "-")
|
||||
if role.lower() not in elem_role:
|
||||
matches = False
|
||||
|
||||
if name and matches:
|
||||
elem_name = (accessible.name or "").lower()
|
||||
if name.lower() not in elem_name:
|
||||
matches = False
|
||||
|
||||
if matches and (role or name): # Only add if we're filtering
|
||||
results.append(get_element_info(accessible))
|
||||
|
||||
# Search children
|
||||
for i in range(accessible.childCount):
|
||||
if len(results) >= limit:
|
||||
break
|
||||
try:
|
||||
child = accessible.getChildAtIndex(i)
|
||||
if child:
|
||||
find_elements(child, role, name, results, max_depth, depth + 1, limit)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def print_results(results, as_json=False):
|
||||
"""Print search results."""
|
||||
if as_json:
|
||||
|
|
@ -191,13 +149,14 @@ def main():
|
|||
|
||||
all_results = []
|
||||
for win in windows:
|
||||
results = find_elements(
|
||||
elements = find_elements(
|
||||
win["accessible"],
|
||||
role=args.role,
|
||||
name=args.name,
|
||||
limit=args.limit - len(all_results)
|
||||
)
|
||||
all_results.extend(results)
|
||||
# Convert raw accessibles to info dicts
|
||||
all_results.extend(get_element_info(elem) for elem in elements)
|
||||
if len(all_results) >= args.limit:
|
||||
break
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import sys
|
|||
|
||||
import pyatspi
|
||||
|
||||
from common import find_elements
|
||||
|
||||
# Human-readable state descriptions
|
||||
STATE_DESCRIPTIONS = {
|
||||
"active": "Currently active/foreground",
|
||||
|
|
@ -170,43 +172,6 @@ def find_focused_element(accessible, depth=0, max_depth=20):
|
|||
return None
|
||||
|
||||
|
||||
def find_elements(accessible, role=None, name=None, results=None, max_depth=15, depth=0, limit=10):
|
||||
"""Find matching elements."""
|
||||
if results is None:
|
||||
results = []
|
||||
|
||||
if len(results) >= limit or depth > max_depth:
|
||||
return results
|
||||
|
||||
try:
|
||||
matches = True
|
||||
|
||||
if role:
|
||||
elem_role = accessible.getRoleName().lower().replace(" ", "-")
|
||||
if role.lower() not in elem_role:
|
||||
matches = False
|
||||
|
||||
if name and matches:
|
||||
elem_name = (accessible.name or "").lower()
|
||||
if name.lower() not in elem_name:
|
||||
matches = False
|
||||
|
||||
if matches and (role or name):
|
||||
results.append(accessible)
|
||||
|
||||
for i in range(accessible.childCount):
|
||||
if len(results) >= limit:
|
||||
break
|
||||
child = accessible.getChildAtIndex(i)
|
||||
if child:
|
||||
find_elements(child, role, name, results, max_depth, depth + 1, limit)
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def print_element(info, all_states=False):
|
||||
"""Print element info in readable format."""
|
||||
print(f"\n[{info['role']}] {info['name'] or '(unnamed)'}")
|
||||
|
|
|
|||
Loading…
Reference in a new issue