Search code examples
objective-cmacoscocoaaccessibilitymacos-carbon

Get NSAttributedString from other app's textfield via accessibility API?


My app (perfectly user-benevolent) uses the Accessibility APIs to grab the contents of textfields owned by other apps.

I'm using the excellent Apple-provided Accessibility Inspector for help and inspiration

https://developer.apple.com/library/mac/#documentation/Accessibility/Conceptual/AccessibilityMacOSX/OSXAXTesting/OSXAXTestingApps.html

I've included that project's UIElementUtilities class, which I can use to do the below:

//Get focused App
AXUIElementRef frontMostApp;
frontMostApp = getFrontMostApp();

//Get focused UIElement (Such as a textfield)
AXUIElementRef focusedUIElement;
AXUIElementCopyAttributeValue(frontMostApp, kAXFocusedUIElementAttribute, (CFTypeRef *)&focusedUIElement);

NSString *textfieldContents = [UIElementUtilities descriptionForUIElement:focusedUIElement attribute:(NSString *)kAXValueAttribute beingVerbose:NO];
//descriptionForUIElement:attribute:beingVerbose uses `AXUIElementCopyAttributeValue`


NSLog(@"TEXT FIELD CONTENTS: %@", textfieldContents);

So I can get the kAXValueAttribute (aka the text contents of an app's textfield) as a plaintext NSString.

Is there a way to do the same, but get an NSAttributedString? I need to pull out the font name and font size currently being used in the textfield.

The end goal is to use the font name and font size to calculate the caret's position in the textfield. (I can already do this, given the font name/size). But if there is a better way to calculate the caret position, that would be a preferable answer. In that case I would not need to get an NSAttributedString.


Solution

  • How to get global screen coordinates of currently selected text via Accessibility APIs.

    This method uses the currently selected text to get the bounds of the selected text rectangle. Much better than calculating the position via the font name/size.

    The tricky part is that you cannot get the bounds for an empty selection (aka a bare text insertion point caret). Thus you need to facke a selection, then pass that to selection to the method that calculates the bounding rectangle.