NSTextView не показывает красные орфографические ошибки, когда на слое - PullRequest
6 голосов
/ 06 декабря 2010

Когда NSTextView является подпредставлением NSView с подложкой (-wantsLayer == YES), оно не отображает волнистые красные подчеркивания для слов с ошибками. Чтобы воспроизвести это, достаточно создать пустой проект Cocoa, открыть перо, перетащить NSTextView в окно и переключить вид содержимого окна, чтобы получить слой. Бум - больше нет красных подчеркиваний.

Я провел некоторый поиск, и это, кажется, известная ситуация, которая была верна с 10.5. Однако я не могу найти обходной путь для этого. Нет ли способа получить подчеркивания для рендеринга, когда NSTextView находится в представлении со слоем?

Я могу представить себе переопределение NSTextView drawRect: и использование диспетчера раскладки, чтобы найти правильные риты с соответствующим набором временных атрибутов, которые указывают на орфографические ошибки, а затем рисовать красные загогулины самостоятельно, но это, конечно, полный взлом. Я также могу представить, что Apple исправит это в 10.7 (возможно), и вдруг мое приложение будет иметь двойное подчеркивание или что-то в этом роде.


[обновление] Мой обходной путь

Мой текущий обходной путь был вдохновлен упомянутым методом делегирования проверки орфографии nptacek, который побудил меня копать глубже по пути, который я раньше не замечал, поэтому я приму этот ответ, но опубликую то, что я сделал для потомков и / или дальнейшего обсуждения.

Я бегу 10.6.5. У меня есть подкласс NSTextView, который является представлением документа пользовательского подкласса NSClipView, который, в свою очередь, является подвидом contentView моего окна, в котором включены слои. Играя с этим, я в конце концов закомментировал все настройки, и все же проверка орфографии работала неправильно.

Я выделил, что, как мне кажется, две разные проблемы:

# 1 заключается в том, что NSTextView, если он размещен в представлении со слоем, даже не удосуживается нарисовать подчеркивание с ошибками. (Я понял, основываясь на поиске в Google, что в 10,5 днях, когда он рисовал подчеркивания, могло быть время, но не в том месте, поэтому Apple, возможно, просто отключила их полностью, чтобы избежать этой проблемы в 10.6. Я не уверен, Может также быть некоторый побочный эффект от того, как я позиционирую вещи и т. Д., Которые заставили их вообще не появляться в моем случае. В настоящее время неизвестно.)

# 2 заключается в том, что когда NSTextView находится в этой ситуации, связанной со слоем, он, кажется, неправильно помечает текст как орфографическую ошибку, когда вы печатаете его, даже когда -isContinuousSpellCheckingEnabled установлен в YES. Я проверил это, реализовав некоторые методы делегирования для проверки орфографии и наблюдая, как NSTextView отправлял сообщения об изменениях, но никогда не уведомлял о том, чтобы установить текстовые диапазоны с ошибками - даже с явно ошибочными словами, которые будут показывать красное подчеркивание в TextEdit (и других текстовых представлениях в других приложениях). Я также переопределил NSTextView -handleTextCheckingResults: forRange: types: options: orthography: wordCount: чтобы увидеть то, что он видел, и он увидел то же самое там. Как будто NSTextView активно устанавливал слово под курсором как не написанное с ошибкой, а затем, когда пользователь вводит пробел или удаляется от него или чего-либо еще, он не перепроверял ошибки. Хотя я не совсем уверен.

Хорошо, чтобы обойти # 1 , я переопределил -drawRect: в моем собственном подклассе NSTextView, чтобы он выглядел так:

- (void)drawRect:(NSRect)rect
{
    [super drawRect:rect];
    [self drawFakeSpellingUnderlinesInRect:rect];
}

Затем я реализовал -drawFakeSpellingUnderlinesInRect: чтобы использовать layoutManager для получения текстовых диапазонов, которые содержат NSSpellingStateAttributeName, в качестве временного атрибута, и отображать точечный шаблон, достаточно близкий к стандартному образцу точных ошибок ввода OSX.

- (void)drawFakeSpellingUnderlinesInRect:(NSRect)rect
{
    CGFloat lineDash[2] = {0.75, 3.25};

    NSBezierPath *underlinePath = [NSBezierPath bezierPath];
    [underlinePath setLineDash:lineDash count:2 phase:0];
    [underlinePath setLineWidth:2];
    [underlinePath setLineCapStyle:NSRoundLineCapStyle];

    NSLayoutManager *layout = [self layoutManager];
    NSRange checkRange = NSMakeRange(0,[[self string] length]);

    while (checkRange.length > 0) {
        NSRange effectiveRange = NSMakeRange(checkRange.location,0);
        id spellingValue = [layout temporaryAttribute:NSSpellingStateAttributeName atCharacterIndex:checkRange.location longestEffectiveRange:&effectiveRange inRange:checkRange];

        if (spellingValue) {
            const NSInteger spellingFlag = [spellingValue intValue];

            if ((spellingFlag & NSSpellingStateSpellingFlag) == NSSpellingStateSpellingFlag) {
                NSUInteger count = 0;
                const NSRectArray rects = [layout rectArrayForCharacterRange:effectiveRange withinSelectedCharacterRange:NSMakeRange(NSNotFound,0) inTextContainer:[self textContainer] rectCount:&count];

                for (NSUInteger i=0; i<count; i++) {
                    if (NSIntersectsRect(rects[i], rect)) {
                        [underlinePath moveToPoint:NSMakePoint(rects[i].origin.x, rects[i].origin.y+rects[i].size.height-1.5)];
                        [underlinePath relativeLineToPoint:NSMakePoint(rects[i].size.width,0)];
                    }
                }
            }
        }

        checkRange.location = NSMaxRange(effectiveRange);
        checkRange.length = [[self string] length] - checkRange.location;
    }

    [[NSColor redColor] setStroke];
    [underlinePath stroke];
}

Таким образом, после этого я вижу красные подчеркивания, но, похоже, это не обновляет состояние правописания при вводе. Чтобы обойти эту проблему, я реализовал в своем подклассе NSTextView следующие злые хаки:

- (void)setNeedsFakeSpellCheck
{
    if ([self isContinuousSpellCheckingEnabled]) {
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(forcedSpellCheck) object:nil];
        [self performSelector:@selector(forcedSpellCheck) withObject:nil afterDelay:0.5];
    }
}

- (void)didChangeText
{
    [super didChangeText];
    [self setNeedsFakeSpellCheck];
}

- (void)updateInsertionPointStateAndRestartTimer:(BOOL)flag
{
    [super updateInsertionPointStateAndRestartTimer:flag];
    [self setNeedsFakeSpellCheck];
}

- (void)forcedSpellCheck
{
    [self checkTextInRange:NSMakeRange(0,[[self string] length]) types:[self enabledTextCheckingTypes] options:nil];
}

Он не работает так же, как реальное, ожидаемое поведение OSX, но он довольно близок, и на данный момент он выполняет свою работу. Надеюсь, это будет полезно для кого-то еще, или, что еще лучше, кто-то приходит сюда и говорит мне, что я упустил что-то невероятно простое, и объясняет, как это исправить. :)

Ответы [ 2 ]

3 голосов
/ 07 декабря 2010

Базовая анимация потрясающая, если не считать текста.Я испытал это на собственном опыте, когда обнаружил, что субпиксельное сглаживание не было дано при работе с представлениями на основе слоев (что технически можно обойти, установив непрозрачный backgroundColor и убедившись, что нарисовал фон).Субпиксельное сглаживание - это лишь одно из многих замечаний, возникающих при работе с текстовыми представлениями и представлениями со слоем.

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

Не заходя так далеко, как переопределение drawRect, вы можете достичь чего-то, что близко к стандартному поведению, с помощью следующего кода:


- (NSArray *)textView:(NSTextView *)view didCheckTextInRange:(NSRange)range types:(NSTextCheckingTypes)checkingTypes options:(NSDictionary *)options results:(NSArray *)results orthography:(NSOrthography *)orthography wordCount:(NSInteger)wordCount
{
     for(NSTextCheckingResult *myResult in results){
        if(myResult.resultType == NSTextCheckingTypeSpelling){
            NSMutableDictionary *attr = [[NSMutableDictionary alloc] init];
            [attr setObject:[NSColor redColor] forKey:NSUnderlineColorAttributeName];
            [attr setObject:[NSNumber numberWithInt:(NSUnderlinePatternDot | NSUnderlineStyleThick | NSUnderlineByWordMask)] forKey:NSUnderlineStyleAttributeName];
            [[inTextView layoutManager] setTemporaryAttributes:attr forCharacterRange:myResult.range];
            [attr release];
        }
    }
    return results;
}
В основном мы выполняем быстрый и грязный метод делегата для NSTextView (обязательно установите делегат в IB!), Который проверяет, помечено ли слово как неправильное, и если да, то, устанавливает цветное подчеркивание.

Обратите внимание, что с этим кодом есть некоторые проблемы - а именно, символы с потомками (например, g, j, p, q, y) не будут правильно отображать подчеркивание,и это было проверено только на орфографические ошибки (здесь нет проверки грамматики!).Шаблон подчеркивания точек (NSUnderlinePatternDot) не соответствует стилю Apple для проверки орфографии, и код по-прежнему включен, даже если для вида отключена поддержка слоев.Кроме того, я уверен, что есть и другие проблемы, так как этот код быстрый и грязный, и его не проверяли на предмет управления памятью или чего-либо еще.

Удачи в ваших усилиях, отчеты об ошибках в файлах в Apple,и, надеюсь, когда-нибудь это уйдет в прошлое!

1 голос
/ 07 декабря 2010

Это тоже немного хак, но единственное, что я мог получить, - это поместить промежуточного делегата на слой NSTextView, чтобы все селекторы проходили, но drawLayer:inContext: затем вызывает NSTextView's drawRect:. Это работает, и, вероятно, еще более перспективно на будущее, хотя я не уверен, сломает ли это какую-либо анимацию CALayer. Также кажется, что вы должны исправить CTM CGContextRef (на основе кадра заднего слоя?).

Edit: Вы можете получить прямоугольник рисования, как в документации drawInContext:, с CGContextGetClipBoundingBox(ctx), но может быть проблема с перевернутыми координатами в NSTextView.

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

Возможно, стоит поискать NSCell, поддерживающий NSTextView, так как это, вероятно, более уместно использовать вместо этого.

...