NSTextView选择突出显示所有字符,甚至段落缩进 [英] NSTextView selection highlights all characters even paragraph indents

查看:92
本文介绍了NSTextView选择突出显示所有字符,甚至段落缩进的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

找不到任何线索来解决这个问题.

Can't find any clue how to manage this.

默认情况下,NSTextView选择突出显示其文本容器的整个大小.它忽略行距,头或尾缩进等.但是在Pages应用程序选择中不会突出显示那些辅助部分,它只会突出显示字符.即使文本容器的高度较小(前后的段落间距),它也会突出显示行的所有高度.

By default, NSTextView selection highlights the whole size of its text container. It ignores line spacing, head or tail indents etc. But in Pages app selection doesn't highlight those ancillary parts, it highlight characters ONLY. And it highlights all the height of the line even if text container's height is smaller (paragraph spacing before and after).

我想实现该行为,但不知道从哪里开始.我在这里搜索过,我在搜索过Apple文档,还尝试过示例项目.没事.

I want to implement that behavior but can't understand where to begin. I've searched here, I've searched Apple docs, I've tried sample projects. Nothing.

也许有人可以指引我正确的方向?谢谢!

Maybe someone can guide me in the right direction? Thanks!

推荐答案

我发现 hamstergene 的答案不正确.实际上,NSTextView会突出显示其文本容器行的整个范围.

I found that hamstergene's answer isn't correct. In fact, NSTextView highlights its text container lines all over their bounds.

因此,如果您使用段落的头部缩进,则前导文本空白将突出显示.如果选择EOL字符,则文本容器的尾部将突出显示.

So, if you use paragraph's head indents then a leading the text empty space will be highlighted. And if you select EOL character then the tail of the text container will be highlighted.

我的解决方案是使段落样式的首尾缩进为零(我将其缓存在私有变量中,并在访问我的文本存储进行打印时将它们放回去),并通过覆盖的 lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect 方法.

My solution was to nullify head and tail indents of the paragraph style (I cache them in the private variable and put them back when my text storage is accessed for printing) and simply adjust frame of the text container line via overrided lineFragmentRectForProposedRect: atIndex: writingDirection: remainingRect method of my NSTextContainer subclass.

但是后来我找到了很多正确的方法.只需覆盖NSLayoutManager的func fillBackgroundRectArray(_ rectArray: UnsafePointer<NSRect>, count rectCount: Int, forCharacterRange charRange: NSRange, color: NSColor),计算您的rect并使用这些rect调用super.而且,如果您正确计算了选择矩形,您将获得与Apple Pages或MS Word中相同的选择行为.

But then I found much proper way. Just override func fillBackgroundRectArray(_ rectArray: UnsafePointer<NSRect>, count rectCount: Int, forCharacterRange charRange: NSRange, color: NSColor) of the NSLayoutManager, calculate your rects and call super with those rects. And if you calculated selection rectangles properly, you'll get the exact selection behavior like in Apple Pages or MS Word.

简单容易!

更新 这是我的用于计算选择区域的代码:

UPDATE Here's my code for calculating selection rects:

public override func fillBackgroundRectArray(_ rectArray: UnsafePointer<CGRect>, count rectCount: Int, forCharacterRange charRange: NSRange, color: OSColor) {

    // if characters are selected, make sure that we draw selection of those characters only, not the whole text container bounds
    guard let textView = textContainer(forCharacterIndex: charRange.location)?.textView,
        NSIntersectionRange(textView.selectedRange(), charRange).length > 0,
        let textStorage = self.textStorage as? ParagraphTextStorage else {
        super.fillBackgroundRectArray(rectArray, count: rectCount, forCharacterRange: charRange, color: color)
        return
    }

    let selectedGlyphRange = self.glyphRange(forCharacterRange: charRange, actualCharacterRange: nil)
    var selectionRectArray: [CGRect] = []

    enumerateLineFragments(forGlyphRange: selectedGlyphRange) { (rect, usedRect, textContainer, glyphRange, stop) in
        let lineCharRange = self.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
        let intersection = NSIntersectionRange(charRange, lineCharRange)

        // if selected all characters of the line, then we already have teir layout rects
        if intersection == lineCharRange {
            let paragraphIndex = textStorage.paragraphIndex(at: intersection.location)
            let paragraphRange = textStorage.paragraphRanges[paragraphIndex]

            let hasNewLineChar = lineCharRange.max == paragraphRange.max && paragraphRange.max < textStorage.length ||
                paragraphRange.max == lineCharRange.max && intersection.max == textStorage.length && paragraphIndex < textStorage.paragraphRanges.count - 1

            let newLineCharSize = hasNewLineChar ? self.newLineCharSize : .zero

            let lineRect = CGRect(x: usedRect.origin.x + textView.textContainerInset.width + textContainer.lineFragmentPadding,
                                  y: usedRect.origin.y + textView.textContainerInset.height - (rect.height - usedRect.height),
                                  width: usedRect.width + newLineCharSize.width - textContainer.lineFragmentPadding * 2,
                                  height: rect.height)
            selectionRectArray.append(lineRect)
        } else {
            // calculate rect for partially selected characters of the line
            let partialRect = self.usedLineRect(forCharacterRange: intersection, in: textContainer)
            selectionRectArray.append(partialRect)
        }
    }
    super.fillBackgroundRectArray(selectionRectArray, count: selectionRectArray.count, forCharacterRange: charRange, color: color)
}

public func usedLineRect(forCharacterRange charRange: NSRange, in textContainer: NSTextContainer) -> CGRect {
    guard let textView = textContainer.textView, let textStorage = textStorage as? ParagraphTextStorage else { return .zero }

    let glyphRange = self.glyphRange(forCharacterRange: charRange, actualCharacterRange: nil)
    let textContainer = self.textContainer(forGlyphAt: glyphRange.location, effectiveRange: nil) as! ModernTextContainer

    let paragraphIndex = textStorage.paragraphIndex(at: charRange.location)
    let paragraphRange = textStorage.paragraphRanges[paragraphIndex]
    let hasNewLine = paragraphRange.max == charRange.max && charRange.max < textStorage.length ||
        paragraphRange.max == charRange.max && charRange.max == textStorage.length && paragraphIndex < textStorage.paragraphRanges.count - 1
    let newLineCharSize = hasNewLine ? self.newLineCharSize : .zero

    // if new line is in range, boundingRect will return the whole width of the text container, fix that
    let noNewLineGlyphRange = hasNewLine ? NSRange(location: glyphRange.location, length: glyphRange.length - 1) : glyphRange

    let charRect = boundingRect(forGlyphRange: noNewLineGlyphRange, in: textContainer)
    let lineRect = lineFragmentRect(forGlyphAt: noNewLineGlyphRange.location, effectiveRange: nil, withoutAdditionalLayout: true)

    #if os(macOS)
    // respect the flipped coordinate system with abs function
    let rect = CGRect(x: charRect.origin.x + textView.textContainerInset.width,
                      y: abs(charRect.origin.y + textView.textContainerInset.height - (lineRect.height - charRect.height)),
                      width: charRect.width + newLineCharSize.width,
                      height: lineRect.height)
    #else
    let rect = CGRect(x: charRect.origin.x + textView.textContainerInset.left,
                      y: abs(charRect.origin.y + textView.textContainerInset.top - (lineRect.height - charRect.height)),
                      width: charRect.width + newLineCharSize.width,
                      height: lineRect.height)
    #endif

    return rect
}

这种非常快速执行的计算的重要部分是我正在使用自己的ParagraphTextStorage实现.其目的是在编辑文本存储时实时计算段落范围.知道正确的段落范围,我可以在计算选定的rect时使用整数.否则,我不得不做一堆子字符串来了解是否选择了换行符.而且操作真的很慢.

The important part of this extremely fast-performed calculation is that I'm using my own ParagraphTextStorage implementation. Its purpose is to calculate paragraph ranges in real-time, right when the text storage is edited. Knowing the correct paragraph ranges, I can work with integers while calculating the selected rect. Otherwise I'd had to do a bunch of substrings to get to know whether the new line characters are selected or not. And that operations are really slow.

我的ParagraphTextStorage的实现在这里: https://github.com/CineDev/ParagraphTextKit

The implementation of my ParagraphTextStorage is here: https://github.com/CineDev/ParagraphTextKit

这篇关于NSTextView选择突出显示所有字符,甚至段落缩进的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆