Как получить глобальные экранные координаты текущего выделенного текста через API специальных возможностей.

Мне нужна помощь, чтобы узнать, как приложение «Словарь» показывает следующее всплывающее диалоговое окно для выделенного текста при нажатии CMD + CTRL + D в любом приложении. Я хочу реализовать такую ​​же функциональность для своего какао-приложения, где мое приложение будет работать в фоновом режиме и показывать предложения при нажатии горячих клавиш для выделенного текста.

Диалог с предложением горячих клавиш приложения-словаря

Я уже реализовал захват горячих клавиш, мне просто нужен код для вывода прямоугольной области выделенного текста на экран, чтобы я мог показать диалог, как приложение-словарь.

Спасибо


person AmitSri    schedule 01.07.2011    source источник
comment
как вы показали всплывающее окно за пределами вашего собственного приложения?   -  person Laureano Bonilla    schedule 09.11.2013
comment
@AmitSri, как вы отображали окно предложений в каком-либо другом приложении?   -  person Krishna Maru    schedule 19.12.2018


Ответы (3)


Для этого вы можете использовать API специальных возможностей. Убедитесь, что установлен флажок «Разрешить доступ для вспомогательных устройств» (в Системных настройках / Универсальный доступ).

Следующий фрагмент кода определит границы (в экранных координатах) выделенного текста в большинстве приложений. К сожалению, это не работает в 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);
person omz    schedule 10.07.2011
comment
Привет, спасибо за код. Я проверил ваш код и, похоже, работает, и я могу привязать выделение в TextEdit, но проблема в том, что когда я пытаюсь показать всплывающее диалоговое окно в этом конкретном прямоугольнике, всплывающее окно отображается неправильно. В принципе, его позиция x работает нормально, но не позиция y. Пожалуйста, дайте мне знать, нужно ли мне сделать какое-то преобразование, чтобы получить правильные координаты x и y относительно экрана. - person AmitSri; 11.07.2011
comment
Координаты перевернуты по вертикали. Чтобы получить рамку для вашей панели, вам нужно преобразовать координату y следующим образом: selectionBounds.origin.y = [[NSScreen mainScreen] frame].size.height - selectionBounds.origin.y - selectionBounds.size.height (немного упрощено, предполагается, что у вас только один экран). - person omz; 11.07.2011
comment
Идеально. Еще раз спасибо за помощь. Мне интересно, могу ли я использовать тот же код для получения позиции точки вставки, как прямоугольник выделения. - person AmitSri; 11.07.2011
comment
Как правило, это возможно, но в некоторых случаях довольно сложно справиться. Без выделения диапазон выбора имеет длину 0. Вы не можете получить границы пустого диапазона, поэтому вы можете построить диапазон предыдущего символа (длина 1) и получить границы этого (правый край это точка вставки). Однако это не сработает, если текст пуст или точка вставки находится в начале новой строки. Вы можете синтезировать нажатие клавиши (например, пробел), получить границы только что введенного символа и снова удалить его ... Хотя это не совсем красивое решение ... - person omz; 11.07.2011
comment
Еще раз спасибо, вы правы, я могу получить позицию, сделав то, что вы предложили. Но мое требование - показывать всплывающее окно, пока пользователь печатает, и мы нашли слово из коллекции, чтобы показать все предложения, связанные с этим словом. Я не уверен, но я читал, что в Carbon есть Text Services Manager, который предоставляет события для отслеживания местоположения точки вставки. Спасибо - person AmitSri; 11.07.2011
comment
Я никогда не использовал его, но в документации указано, что почти все функции Text Services Manager (TSM) недоступны для 64-битных приложений, так что это может быть тупиком. - person omz; 11.07.2011
comment
@omz Я использую тот же код и пытаюсь получить выбранный текстовый диапазон в изолированной среде, но он не работает. Я получаю getSelectionBoundsError == kAXErrorAttributeUnsupported - person kulss; 24.07.2017
comment
@omz, не могли бы вы помочь мне найти, как получить выбранный диапазон текста для Safari и почты? Любая справочная ссылка или документ, на который я могу сослаться - person Krishna Maru; 30.10.2018

Здесь вы идете для ответа @omz в Swift

    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)
            }
        }
    }
person Bezaleel    schedule 04.02.2020

Вам нужна служба. Благодаря службам вашему приложению даже не нужно запускать или захватывать глобальные горячие клавиши.

Например, функциональность описанного вами словарного приложения на самом деле является службой, которую можно увидеть в меню Services.

Меню службы словаря

Apple Руководство по внедрению службы, вероятно, является лучшей информацией об имеющихся службах.

person spudwaffle    schedule 04.07.2011
comment
Спасибо за Ваш ответ. Я знаю об этих сервисах и уже использую в моем приложении для захвата выделенного текста. Но требование состоит в том, чтобы отображать всплывающее диалоговое окно, такое как приложение-словарь, на выбранном / выделенном на экране, точно так же, как приложение-словарь отображается в моем сообщении. Пожалуйста, дайте мне знать, получил ли я область выбора относительно экрана в сервисном делегате? Спасибо - person AmitSri; 05.07.2011