如何通过辅助功能 API 获取当前选定文本的全局屏幕坐标。

Posted

技术标签:

【中文标题】如何通过辅助功能 API 获取当前选定文本的全局屏幕坐标。【英文标题】:How to get global screen coordinates of currently selected text via Accessibility APIs. 【发布时间】:2011-09-26 12:33:14 【问题描述】:

我需要帮助来了解,在任何应用程序上按 CMD+CTRL+D 时,词典应用程序如何显示所选文本的以下弹出对话框。我要实施 我的 cocoa 应用程序具有相同的功能,我的应用程序将在后台运行,并在某些热键按下时显示所选文本的建议。

我已经实现了热键捕获,我只需要代码来获取屏幕上选定文本的矩形区域,这样我就可以像字典应用程序一样显示对话框。

谢谢

【问题讨论】:

您是如何在自己的应用程序限制之外显示弹出框的? @AmitSri ,你是如何在其他应用程序上显示建议窗口的? 【参考方案1】:

您可以为此使用辅助功能 API。确保选中“启用辅助设备访问”设置(在系统偏好设置/通用访问中)。

以下代码 sn-p 将确定大多数应用程序中所选文本的边界(在屏幕坐标中)。不幸的是,它在 Mail 和 Safari 中不起作用,因为它们使用私有的可访问性属性。也有可能让它在那里工作,但它需要更多的工作和可能的私有 API 调用。

AXUIElementRef systemWideElement = AXUIElementCreateSystemWide();
AXUIElementRef focussedElement = NULL;
AXError error = AXUIElementCopyAttributeValue(systemWideElement, kAXFocusedUIElementAttribute, (CFTypeRef *)&focussedElement);
if (error != kAXErrorSuccess) 
    NSLog(@"Could not get focussed element");
 else 
    AXValueRef selectedRangeValue = NULL;
    AXError getSelectedRangeError = AXUIElementCopyAttributeValue(focussedElement, kAXSelectedTextRangeAttribute, (CFTypeRef *)&selectedRangeValue);
    if (getSelectedRangeError == kAXErrorSuccess) 
        CFRange selectedRange;
        AXValueGetValue(selectedRangeValue, kAXValueCFRangeType, &selectedRange);
        AXValueRef selectionBoundsValue = NULL;
        AXError getSelectionBoundsError = AXUIElementCopyParameterizedAttributeValue(focussedElement, kAXBoundsForRangeParameterizedAttribute, selectedRangeValue, (CFTypeRef *)&selectionBoundsValue);
        CFRelease(selectedRangeValue);
        if (getSelectionBoundsError == kAXErrorSuccess) 
            CGRect selectionBounds;
            AXValueGetValue(selectionBoundsValue, kAXValueCGRectType, &selectionBounds);
            NSLog(@"Selection bounds: %@", NSStringFromRect(NSRectFromCGRect(selectionBounds)));
         else 
            NSLog(@"Could not get bounds for selected range");
        
        if (selectionBoundsValue != NULL) CFRelease(selectionBoundsValue);
     else 
        NSLog(@"Could not get selected range");
    

if (focussedElement != NULL) CFRelease(focussedElement);
CFRelease(systemWideElement);

【讨论】:

您好,感谢您的代码。我检查了你的代码,似乎工作正常,我可以在 TextEdit 中获得选择绑定,但问题是当我尝试在该特定矩形上显示弹出对话框时,弹出窗口显示错误。基本上,它的 x 位置工作正常,但不是 y 位置。请告诉我,我是否需要进行一些转换才能获得相对于屏幕的正确 x 和 y 位置。 坐标垂直翻转。要为您的面板获取框架,您必须像这样转换 y 坐标:selectionBounds.origin.y = [[NSScreen mainScreen] frame].size.height - selectionBounds.origin.y - selectionBounds.size.height(稍微简化一下,假设您只有一个屏幕)。 完美。再次感谢您帮助我。我想知道是否可以使用相同的代码来获取插入点位置,就像选择矩形一样。 一般情况下是可以的,但是有些情况比较难处理。如果没有选择,选择范围的长度为 0。您无法获得空范围的边界,因此您可以构造前一个字符的范围(长度 1)并获得该范围的边界(右边缘它是插入点)。但是,如果文本为空或插入点位于新行的开头,这将不起作用。您可以合成一个按键(如空格),获取刚刚输入的字符的边界并再次删除它......虽然不是一个真正漂亮的解决方案...... 再次感谢,您说得对,按照您的建议,我可以得到这个职位。但是,我的要求是在用户键入时显示弹出窗口,我们从集合中找到了一个单词来显示与该单词相关的所有建议。我不确定,但我读过 Carbon 中有文本服务管理器,它提供事件来跟踪插入点的位置。谢谢【参考方案2】:

你可以在 Swift 中找到@omz 的答案

    let systemWideElement = AXUIElementCreateSystemWide()
    var focusedElement : AnyObject?

    let error = AXUIElementCopyAttributeValue(systemWideElement, kAXFocusedUIElementAttribute as CFString, &focusedElement)
    if (error != .success)
        print("Couldn't get the focused element. Probably a webkit application")
     else 
        var selectedRangeValue : AnyObject?
        let selectedRangeError = AXUIElementCopyAttributeValue(focusedElement as! AXUIElement, kAXSelectedTextRangeAttribute as CFString, &selectedRangeValue)
        if (selectedRangeError == .success)
            var selectedRange : CFRange?
            AXValueGetValue(selectedRangeValue as! AXValue, AXValueType(rawValue: kAXValueCFRangeType)!, &selectedRange)
            var selectRect = CGRect()
            var selectBounds : AnyObject?
            let selectedBoundsError = AXUIElementCopyParameterizedAttributeValue(focusedElement as! AXUIElement, kAXBoundsForRangeParameterizedAttribute as CFString, selectedRangeValue!, &selectBounds)
            if (selectedBoundsError == .success)
                AXValueGetValue(selectBounds as! AXValue, .cgRect, &selectRect)
                //do whatever you want with your selectRect
                print(selectRect)
            
        
    

【讨论】:

【参考方案3】:

您正在寻找的是服务。 借助服务,您的应用甚至不必运行或捕获全局热键。

例如,您描述的词典应用程序的功能实际上是一项服务,可在 Services 菜单中观察到。

Apple 的 Service Implementation Guide 可能是有关服务的最佳信息。

【讨论】:

感谢您的回答。我知道这些服务,并且已经在我的应用程序中用于抓取文本选择。但要求是在屏幕上选定/突出显示的字典应用程序上显示弹出对话框,就像我的帖子中显示的字典应用程序一样。请让我知道我是否在服务代表中获得了相对于屏幕的选择区域?谢谢

以上是关于如何通过辅助功能 API 获取当前选定文本的全局屏幕坐标。的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Eclipse 的代码辅助弹出窗口中更改当前选定行的颜色?

怎么用百度地图api自动获取当前位置(谷歌的也行)

secureCRT脚本如何实现查找当前屏显文本中最后一个出现的关键字如sd*并获取该关键字呢?

如何通过javascript获取textarea元素内的选定文本?

使用 C++\Qt 从当前窗口中获取选定的文本

MFC 获取Combo Box控件 当前选定项的序号和文本内容