如何手动告诉NSTextContainer重新计算lineFragmentRects?

时间:2014-07-16 15:08:26

标签: macos cocoa swift nstextcontainer

在我的应用程序中,我一直在添加对NSTextView的支持,允许子视图自然地与文本编辑器一起工作。 (例如,按“enter”会删除低于插入点的每个子视图,并且文本会围绕子视图。)

我已成功通过覆盖lineFragmentRectForProposedRect(proposedRect: NSRect, sweepDirection: NSLineSweepDirection, movementDirection: NSLineMovementDirection, remainingRect: NSRectPointer) -> NSRect来绕过子视图进行自动换行。

但是,当拖动或调整子视图时,需要重新计算文本中的中断以获得所需的效果。文本需要更新自身以在拖动或调整大小时包围子视图。但是,我注意到在一行上输入会更新该行的lineFragmentRect。有没有办法在拖动子视图后立即将文本标记为“脏”?以下是文本容器子类的代码:

import Cocoa

class CASTextContainer: NSTextContainer {
    var mainView: CASTextView = CASTextView()
    var textSubviews: Array<AnyObject>  {
        get {
            return mainView.subviews
        }
    }

    override var simpleRectangularTextContainer: Bool {
        get {
            return false
        }
    }

    func rectForActiveRange() -> NSRect {
        let range = layoutManager.glyphRangeForCharacterRange(textView.selectedRange, actualCharacterRange:nil)
        var rect = layoutManager.boundingRectForGlyphRange(range, inTextContainer: self)
        rect = NSOffsetRect(rect, textView.textContainerOrigin.x, textView.textContainerOrigin.y)
        return rect;
    }

    override func lineFragmentRectForProposedRect(proposedRect: NSRect,
        sweepDirection: NSLineSweepDirection,
        movementDirection: NSLineMovementDirection,
        remainingRect: NSRectPointer) -> NSRect
    {
        remainingRect.initialize(NSZeroRect)
        var frames: Array<NSRect> = []
        if textSubviews.isEmpty {
            let fullRect = NSMakeRect(0, 0, containerSize.width, containerSize.height)
            return NSIntersectionRect(fullRect, proposedRect)
        }

        for view in textSubviews {
            frames.append(view.frame)
        }

        // Sort the array so that the frames are ordered by increasing origin x coordinate.
        let fullRect = NSMakeRect(0, 0, containerSize.width, containerSize.height)
        let region = NSIntersectionRect(fullRect, proposedRect)
        let sortedFrames: Array<NSRect> = sorted(frames, { (first: NSRect, second: NSRect) -> Bool in
            return first.origin.x < second.origin.x
        })

        // Get the first rectangle height that overlaps with the text region's height.
        var textDrawingRegion = NSZeroRect
        var subviewFrame = NSZeroRect // Will be needed later to set remainingRect pointer.
        var precedingRegion: NSRect = NSZeroRect // Hold the view frame preceding our insertion point.
        for frame in sortedFrames {
            if region.origin.y + NSHeight(region) >= frame.origin.y &&
                region.origin.y <= frame.origin.y + NSHeight(frame)
            {
                if region.origin.x <= frame.origin.x {
                    // Calculate the distance between the preceding subview and the approaching one.
                    var width: CGFloat = frame.origin.x - precedingRegion.origin.x - NSWidth(precedingRegion)
                    textDrawingRegion.origin = region.origin
                    textDrawingRegion.size = NSMakeSize(width, NSHeight(region))
                    subviewFrame = frame
                    break
                }

                else {
                    precedingRegion = frame
                }
            }
        }

        if textDrawingRegion == NSZeroRect {
            return region
        }

        else {
            // Set the "break" in the text to the subview's location:
            let nextRect = NSMakeRect(subviewFrame.origin.x + NSWidth(subviewFrame),
                region.origin.y,
                NSWidth(proposedRect),
                NSHeight(proposedRect))

            remainingRect.initialize(nextRect)
            return textDrawingRegion
        }
    }
}

0 个答案:

没有答案