3

i am using Cocoa with Accessibility API system wide to obtain some info like text value, selected text value but is there a way to change any of that? As you can see on my picture, you can edit the location of the keyboard cursor (Loc) and the length (Len) of the selected text so there must be a way to do this programmatically. Also, I am looking how to get the keyboard cursor position (blinking caret) inside the screen even if nothing is selected. (I have seen applications doing it)

EDIT :

This is my code so far to get the attribute :

 AXValueRef textValue = NULL;
 //Get the location string inside the selectedtextrange
 AXError gettextvalueError = AXUIElementCopyAttributeValue(focussedElement, kAXSelectedTextRangeAttribute , (CFTypeRef *)&textValue);   //get the text value of focussedElement in object textValue

 if(gettextvalueError != kAXErrorSuccess){
     NSLog(@"error");
 }else{
     NSString* textStr = (__bridge NSString*)textValue;      // Convert textValue to NSString
 }

This is getting me a string containing bounds object of the selected text and location + length. Is it possible to only change the location.

<AXValue 0x7f8dda3c4340> {value = location:3 length:0 type = kAXValueCFRangeType}

value I want to change

hugo411
  • 320
  • 1
  • 12
  • 29
  • Possible duplicate of [Native caret position macos cocoa](https://stackoverflow.com/questions/50214342/native-caret-position-macos-cocoa) – Willeke May 18 '18 at 22:57
  • 1
    What is your code so far? What have you tried? – Ken Thomases May 19 '18 at 02:19
  • 2
    If you can get a value, you can set the value (if it's settable) by using `AXUIElementSetAttributeValue` instead of `AXUIElementCopyAttributeValue`. – Willeke May 21 '18 at 23:12
  • @Willeke Thanks but the attribute is my problem, I get this : {value = location:3 length:0 type = kAXValueCFRangeType}. How can I change only the location or length. – hugo411 May 22 '18 at 14:18

1 Answers1

4

First, the value you get is not a string object. Casting it to NSString* doesn't "convert" it, it just lies to the compiler. The reason that logging it works is that %@ asks the object for its description and the AXValue object describes itself as you're seeing in the output.

To decode the value, use code like this:

CFRange range;
if (AXValueGetValue(textValue, kAXValueTypeCFRange, &range))
{
    // use the range
}
else
    /* the value isn't of the type that the attribute is supposed to have; abort */;

If you want to change it:

range.location = /* some new value */;
AXValueRef newValue = AXValueCreate(kAXValueTypeCFRange, &range);
if (newValue)
{
    AXError setError = AXUIElementSetAttributeValue(focussedElement, kAXSelectedTextRangeAttribute, newValue);
    /* check setError and handle as appropriate */
    CFRelease(newValue);
}
else
    /* handle failure */;
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • This was very helpful thanks a lot. I also managed to get the bounds of a selected range without being selected at all. (Very useful if you want to get the caret position without destroying the user experience). Ill update my question with your answer. – hugo411 May 22 '18 at 17:31
  • @hugo411 how did you get the caret position without selecting a range? I'm only able to get the caret position except when the range length and location are 0. Any tips? – bruteforce Aug 19 '20 at 20:02
  • 1
    @hugo411 I'm curious how you did this as well? I'm trying to get the caret position, to show a popover. – Nick Hayward Dec 28 '20 at 16:17