You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
76 lines
2.9 KiB
76 lines
2.9 KiB
import Cocoa
|
|
import ApplicationServices
|
|
|
|
func findFocusedDescendant(_ elem: AXUIElement) -> AXUIElement? {
|
|
// Check if this element is a text field/text area or has value
|
|
var roleObj: CFTypeRef?
|
|
AXUIElementCopyAttributeValue(elem, kAXRoleAttribute as CFString, &roleObj)
|
|
let role = (roleObj as? String) ?? ""
|
|
|
|
if role == "AXTextField" || role == "AXTextArea" || role == "AXComboBox" || role == "AXSearchField" {
|
|
return elem
|
|
}
|
|
|
|
var focusedObj: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(elem, kAXFocusedAttribute as CFString, &focusedObj) == .success,
|
|
let isFocused = focusedObj as? Bool, isFocused {
|
|
// If it has value attribute, return it
|
|
var valObj: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(elem, kAXValueAttribute as CFString, &valObj) == .success {
|
|
return elem
|
|
}
|
|
}
|
|
|
|
// Check children
|
|
var childrenObj: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(elem, kAXChildrenAttribute as CFString, &childrenObj) == .success,
|
|
let children = childrenObj as? [AXUIElement] {
|
|
for child in children {
|
|
if let found = findFocusedDescendant(child) {
|
|
return found
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getDeepFocusedElement() -> (AXUIElement, String, String)? {
|
|
guard let frontApp = NSWorkspace.shared.frontmostApplication else { return nil }
|
|
let appElem = AXUIElementCreateApplication(frontApp.processIdentifier)
|
|
|
|
// First try standard focused element
|
|
var focusedObj: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(appElem, kAXFocusedUIElementAttribute as CFString, &focusedObj) == .success,
|
|
let elem = focusedObj as! AXUIElement? {
|
|
var roleObj: CFTypeRef?
|
|
AXUIElementCopyAttributeValue(elem, kAXRoleAttribute as CFString, &roleObj)
|
|
let role = (roleObj as? String) ?? ""
|
|
|
|
var valObj: CFTypeRef?
|
|
AXUIElementCopyAttributeValue(elem, kAXValueAttribute as CFString, &valObj)
|
|
let val = (valObj as? String) ?? ""
|
|
|
|
if !val.isEmpty || role == "AXTextField" || role == "AXTextArea" {
|
|
return (elem, role, val)
|
|
}
|
|
|
|
// If it's a window or web area, search descendants
|
|
if let deep = findFocusedDescendant(elem) {
|
|
var deepRoleObj: CFTypeRef?
|
|
AXUIElementCopyAttributeValue(deep, kAXRoleAttribute as CFString, &deepRoleObj)
|
|
let deepRole = (deepRoleObj as? String) ?? ""
|
|
|
|
var deepValObj: CFTypeRef?
|
|
AXUIElementCopyAttributeValue(deep, kAXValueAttribute as CFString, &deepValObj)
|
|
let deepVal = (deepValObj as? String) ?? ""
|
|
return (deep, deepRole, deepVal)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if let (elem, role, val) = getDeepFocusedElement() {
|
|
print("Found deep focused element! Role: \(role), Value: '\(val)'")
|
|
} else {
|
|
print("Deep focused element not found")
|
|
}
|