Пользовательский ротор macOS VoiceOver не будет выбирать последний диапазон текста в NSTextView

#objective-c #macos #voiceover

#objective-c #macos #voiceover

Вопрос:

Я пытаюсь создать пользовательский ротор для выбора одного из нескольких диапазонов в текстовом представлении. API для этого не документирован (хотя и общедоступен), поэтому я адаптировал его из примера кода Apple здесь.

Цель кода — предоставить способ навигации по истории игрового текста, выбрав предыдущую команду или «переместить». Они хранятся как NSArray объектов значений NSRange.

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

Пример кода ниже. Исходный код в контексте здесь. Видео, иллюстрирующее проблему здесь.

 - (NSAccessibilityCustomRotorItemResult *)rotor:(NSAccessibilityCustomRotor *)rotor
                      resultForSearchParameters:(NSAccessibilityCustomRotorSearchParameters *)searchParameters {

    NSAccessibilityCustomRotorItemResult *searchResult = nil;
    NSAccessibilityCustomRotorItemResult *currentItemResult = searchParameters.currentItem;
    NSAccessibilityCustomRotorSearchDirection direction = searchParameters.searchDirection;
    NSRange currentRange = currentItemResult.targetRange;

    NSUInteger currentItemIndex;

    GlkTextBufferWindow *largest = [self largestWithMoves];

    if (!largest)
        return nil;

    NSArray *children = largest.moveRanges;

    currentItemIndex = [children indexOfObject:[NSValue valueWithRange:currentRange]];

    if (currentItemIndex == NSNotFound) {
        // Find the start or end element.
        if (direction == NSAccessibilityCustomRotorSearchDirectionNext) {
            currentItemIndex = 0;
        } else if (direction == NSAccessibilityCustomRotorSearchDirectionPrevious) {
            currentItemIndex = children.count - 1;
        }
    } else {
        if (direction == NSAccessibilityCustomRotorSearchDirectionPrevious) {
            if ((NSInteger)currentItemIndex == 0) {
                currentItemIndex = NSNotFound;
            } else {
                currentItemIndex--;
            }
        } else if (direction == NSAccessibilityCustomRotorSearchDirectionNext) {
            if (currentItemIndex == children.count - 1) {
                currentItemIndex = NSNotFound;
            } else {
                currentItemIndex  ;
            }
        }
    }

    if (currentItemIndex == NSNotFound) {
        return nil;
    }

    NSValue *targetRangeValue = children[(NSUInteger)currentItemIndex];

    if (targetRangeValue) {
        NSRange textRange = targetRangeValue.rangeValue;
        searchResult = [[NSAccessibilityCustomRotorItemResult alloc] initWithTargetElement: largest.textview];
        searchResult.targetRange = textRange;
        // By adding a custom label, all ranges are reliably listed in the rotor
        NSString *label = [largest.textview.string substringWithRange:textRange];
        searchResult.customLabel = label;
    }

    return searchResu<
}
 

Комментарии:

1. Совет: Если документация отсутствует, проверьте заголовки.

2. Спасибо! Это хороший совет, но теперь я понимаю, что упоминание о том, что API недокументирован, неуместно, во-первых, потому что Apple, похоже, в значительной степени отказалась от документирования своих новых API, и все это уже знают, а во-вторых, поработав над этим кодом пару месяцев, ячувствую, что у меня есть разумное представление о том, как работает этот API. Однако это не помогает мне с этой ошибкой.

Ответ №1:

Я предполагаю, что вы передаете текстовый диапазон, который выходит за рамки. Например, вот что я вижу, когда использую меню истории команд. Обратите внимание на «x Closed.», что неверно. Это должно быть «закрыть почтовый ящик закрыт».

введите описание изображения здесь

Это тоже происходит не всегда. В некоторых (большинстве?) Случаях я могу выбрать последнюю команду из ротора. Поэтому я бы внимательно посмотрел на диапазоны и сравнил те, которые работают, с теми, которые не работают.

Обратите внимание также на дублированную начальную команду на изображении ниже вместо команды «подойти к входной двери». Итак, в текущей реализации есть несколько различных ошибок, которые могут повлиять на поведение, которое вы видите.

Спасибо, что нашли время для поддержки VoiceOver! Это идеальный проект для этого.

Комментарии:

1. Спасибо, что взглянули на это. Похоже, что это проблема типа NSTextView, которая обычно решается путем введения задержки где-нибудь, чтобы дать ему время закончить выкладку текста в каком-то фоновом системном потоке, хотя я не уверен, насколько хорошо это работает с API rotor.

2. Если вы знаете шаги по воспроизведению этой ошибки, пожалуйста, дайте мне знать. Здесь или в репозитории Github проблемы .