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.
110 lines
4.3 KiB
110 lines
4.3 KiB
import Cocoa
|
|
import ApplicationServices
|
|
|
|
func extractTextAndCursor(from elem: AXUIElement) -> (String, Int, Int)? {
|
|
var roleRef: CFTypeRef?
|
|
AXUIElementCopyAttributeValue(elem, kAXRoleAttribute as CFString, &roleRef)
|
|
let role = (roleRef as? String) ?? ""
|
|
|
|
var currentText = ""
|
|
var valueRef: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(elem, kAXValueAttribute as CFString, &valueRef) == .success,
|
|
let val = valueRef {
|
|
if let str = val as? String {
|
|
currentText = str
|
|
} else if let attrStr = val as? NSAttributedString {
|
|
currentText = attrStr.string
|
|
}
|
|
}
|
|
|
|
if currentText.isEmpty {
|
|
var countRef: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(elem, kAXNumberOfCharactersAttribute as CFString, &countRef) == .success,
|
|
let count = countRef as? Int, count > 0 {
|
|
var range = CFRange(location: 0, length: count)
|
|
if let axRange = AXValueCreate(.cfRange, &range) {
|
|
var stringRef: CFTypeRef?
|
|
if AXUIElementCopyParameterizedAttributeValue(elem, kAXStringForRangeParameterizedAttribute as CFString, axRange, &stringRef) == .success,
|
|
let str = stringRef as? String {
|
|
currentText = str
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var cursor = currentText.count
|
|
var selLen = 0
|
|
var rangeRef: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(elem, kAXSelectedTextRangeAttribute as CFString, &rangeRef) == .success,
|
|
let val = rangeRef {
|
|
var cfRange = CFRange()
|
|
if AXValueGetValue(val as! AXValue, .cfRange, &cfRange) {
|
|
cursor = cfRange.location
|
|
selLen = cfRange.length
|
|
}
|
|
}
|
|
|
|
if !currentText.isEmpty || role == "AXTextField" || role == "AXTextArea" || role == "AXSearchField" || rangeRef != nil {
|
|
return (currentText, cursor, selLen)
|
|
}
|
|
|
|
// Check focused child
|
|
var focusedChildRef: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(elem, kAXFocusedUIElementAttribute as CFString, &focusedChildRef) == .success,
|
|
let child = focusedChildRef {
|
|
if let res = extractTextAndCursor(from: child as! AXUIElement) {
|
|
return res
|
|
}
|
|
}
|
|
|
|
// Check children
|
|
var childrenRef: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(elem, kAXChildrenAttribute as CFString, &childrenRef) == .success,
|
|
let children = childrenRef as? [AXUIElement] {
|
|
for child in children {
|
|
var isFocusedRef: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(child, kAXFocusedAttribute as CFString, &isFocusedRef) == .success,
|
|
let isFoc = isFocusedRef as? Bool, isFoc {
|
|
if let res = extractTextAndCursor(from: child) {
|
|
return res
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func testFullAX() {
|
|
let sysWide = AXUIElementCreateSystemWide()
|
|
AXUIElementSetAttributeValue(sysWide, "AXEnhancedUserInterface" as CFString, kCFBooleanTrue)
|
|
AXUIElementSetAttributeValue(sysWide, "AXManualAccessibility" as CFString, kCFBooleanTrue)
|
|
|
|
guard let frontApp = NSWorkspace.shared.frontmostApplication else { return }
|
|
print("Front App:", frontApp.localizedName ?? "")
|
|
|
|
let appElem = AXUIElementCreateApplication(frontApp.processIdentifier)
|
|
AXUIElementSetAttributeValue(appElem, "AXEnhancedUserInterface" as CFString, kCFBooleanTrue)
|
|
AXUIElementSetAttributeValue(appElem, "AXManualAccessibility" as CFString, kCFBooleanTrue)
|
|
|
|
var focusedElem: CFTypeRef?
|
|
if AXUIElementCopyAttributeValue(sysWide, kAXFocusedUIElementAttribute as CFString, &focusedElem) == .success,
|
|
let elem = focusedElem {
|
|
if let (text, cursor, sel) = extractTextAndCursor(from: elem as! AXUIElement) {
|
|
print("Extracted from sysWide -> Text: '\(text)', Cursor: \(cursor), Sel: \(sel)")
|
|
return
|
|
}
|
|
}
|
|
|
|
if AXUIElementCopyAttributeValue(appElem, kAXFocusedUIElementAttribute as CFString, &focusedElem) == .success,
|
|
let elem = focusedElem {
|
|
if let (text, cursor, sel) = extractTextAndCursor(from: elem as! AXUIElement) {
|
|
print("Extracted from appElem -> Text: '\(text)', Cursor: \(cursor), Sel: \(sel)")
|
|
return
|
|
}
|
|
}
|
|
|
|
print("Could not extract text")
|
|
}
|
|
|
|
testFullAX()
|