Merge branch 'master' into bazel

This commit is contained in:
Ali
2020-02-18 03:43:19 +04:00
1232 changed files with 86593 additions and 21224 deletions

View File

@@ -13,6 +13,18 @@ private final class TextNodeStrikethrough {
}
}
public struct TextRangeRectEdge: Equatable {
public var x: CGFloat
public var y: CGFloat
public var height: CGFloat
public init(x: CGFloat, y: CGFloat, height: CGFloat) {
self.x = x
self.y = y
self.height = height
}
}
private final class TextNodeLine {
let line: CTLine
let frame: CGRect
@@ -85,6 +97,7 @@ private func displayLineFrame(frame: CGRect, isRTL: Bool, boundingRect: CGRect,
public final class TextNodeLayoutArguments {
public let attributedString: NSAttributedString?
public let backgroundColor: UIColor?
public let minimumNumberOfLines: Int
public let maximumNumberOfLines: Int
public let truncationType: CTLineTruncationType
public let constrainedSize: CGSize
@@ -94,10 +107,12 @@ public final class TextNodeLayoutArguments {
public let insets: UIEdgeInsets
public let lineColor: UIColor?
public let textShadowColor: UIColor?
public let textStroke: (UIColor, CGFloat)?
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil, textShadowColor: UIColor? = nil) {
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, minimumNumberOfLines: Int = 0, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil, textShadowColor: UIColor? = nil, textStroke: (UIColor, CGFloat)? = nil) {
self.attributedString = attributedString
self.backgroundColor = backgroundColor
self.minimumNumberOfLines = minimumNumberOfLines
self.maximumNumberOfLines = maximumNumberOfLines
self.truncationType = truncationType
self.constrainedSize = constrainedSize
@@ -107,6 +122,7 @@ public final class TextNodeLayoutArguments {
self.insets = insets
self.lineColor = lineColor
self.textShadowColor = textShadowColor
self.textStroke = textStroke
}
}
@@ -128,9 +144,10 @@ public final class TextNodeLayout: NSObject {
fileprivate let blockQuotes: [TextNodeBlockQuote]
fileprivate let lineColor: UIColor?
fileprivate let textShadowColor: UIColor?
fileprivate let textStroke: (UIColor, CGFloat)?
public let hasRTL: Bool
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, rawTextSize: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?) {
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, rawTextSize: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?) {
self.attributedString = attributedString
self.maximumNumberOfLines = maximumNumberOfLines
self.truncationType = truncationType
@@ -148,6 +165,7 @@ public final class TextNodeLayout: NSObject {
self.backgroundColor = backgroundColor
self.lineColor = lineColor
self.textShadowColor = textShadowColor
self.textStroke = textStroke
var hasRTL = false
for line in lines {
if line.isRTL {
@@ -583,11 +601,13 @@ public final class TextNodeLayout: NSObject {
return nil
}
public func rangeRects(in range: NSRange) -> [CGRect]? {
public func rangeRects(in range: NSRange) -> (rects: [CGRect], start: TextRangeRectEdge, end: TextRangeRectEdge)? {
guard let _ = self.attributedString, range.length != 0 else {
return nil
}
var rects: [(CGRect, CGRect)] = []
var startEdge: TextRangeRectEdge?
var endEdge: TextRangeRectEdge?
for line in self.lines {
let lineRange = NSIntersectionRange(range, line.range)
if lineRange.length != 0 {
@@ -610,11 +630,34 @@ public final class TextNodeLayout: NSObject {
let width = max(0.0, abs(rightOffset - leftOffset))
if line.range.contains(range.lowerBound) {
let offsetX = floor(CTLineGetOffsetForStringIndex(line.line, range.lowerBound, nil))
startEdge = TextRangeRectEdge(x: lineFrame.minX + offsetX, y: lineFrame.minY, height: lineFrame.height)
}
if line.range.contains(range.upperBound - 1) {
let offsetX: CGFloat
if line.range.upperBound == range.upperBound {
offsetX = lineFrame.maxX
} else {
var secondaryOffset: CGFloat = 0.0
let primaryOffset = floor(CTLineGetOffsetForStringIndex(line.line, range.upperBound - 1, &secondaryOffset))
secondaryOffset = floor(secondaryOffset)
let nextOffet = floor(CTLineGetOffsetForStringIndex(line.line, range.upperBound, &secondaryOffset))
if primaryOffset != secondaryOffset {
offsetX = secondaryOffset
} else {
offsetX = nextOffet
}
}
endEdge = TextRangeRectEdge(x: lineFrame.minX + offsetX, y: lineFrame.minY, height: lineFrame.height)
}
rects.append((lineFrame, CGRect(origin: CGPoint(x: lineFrame.minX + min(leftOffset, rightOffset) + self.insets.left, y: lineFrame.minY + self.insets.top), size: CGSize(width: width, height: lineFrame.size.height))))
}
}
if !rects.isEmpty {
return rects.map { $1 }
if !rects.isEmpty, let startEdge = startEdge, let endEdge = endEdge {
return (rects.map { $1 }, startEdge, endEdge)
}
return nil
}
@@ -728,7 +771,7 @@ public final class TextAccessibilityOverlayNode: ASDisplayNode {
}
public class TextNode: ASDisplayNode {
public private(set) var cachedLayout: TextNodeLayout?
public internal(set) var cachedLayout: TextNodeLayout?
override public init() {
super.init()
@@ -762,7 +805,7 @@ public class TextNode: ASDisplayNode {
}
}
public func rangeRects(in range: NSRange) -> [CGRect]? {
public func rangeRects(in range: NSRange) -> (rects: [CGRect], start: TextRangeRectEdge, end: TextRangeRectEdge)? {
if let cachedLayout = self.cachedLayout {
return cachedLayout.rangeRects(in: range)
} else {
@@ -778,7 +821,7 @@ public class TextNode: ASDisplayNode {
}
}
private class func calculateLayout(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?) -> TextNodeLayout {
private class func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?) -> TextNodeLayout {
if let attributedString = attributedString {
let stringLength = attributedString.length
@@ -804,7 +847,7 @@ public class TextNode: ASDisplayNode {
var maybeTypesetter: CTTypesetter?
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
if maybeTypesetter == nil {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke)
}
let typesetter = maybeTypesetter!
@@ -886,7 +929,12 @@ public class TextNode: ASDisplayNode {
let coreTextLine: CTLine
let originalLine = CTTypesetterCreateLineWithOffset(typesetter, lineRange, 0.0)
if CTLineGetTypographicBounds(originalLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(originalLine) < Double(constrainedSize.width) {
var lineConstrainedSize = constrainedSize
if bottomCutoutEnabled {
lineConstrainedSize.width -= bottomCutoutSize.width
}
if CTLineGetTypographicBounds(originalLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(originalLine) < Double(lineConstrainedSize.width) {
coreTextLine = originalLine
} else {
var truncationTokenAttributes: [NSAttributedString.Key : AnyObject] = [:]
@@ -896,7 +944,7 @@ public class TextNode: ASDisplayNode {
let truncatedTokenString = NSAttributedString(string: tokenString, attributes: truncationTokenAttributes)
let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString)
coreTextLine = CTLineCreateTruncatedLine(originalLine, Double(constrainedSize.width), truncationType, truncationToken) ?? truncationToken
coreTextLine = CTLineCreateTruncatedLine(originalLine, Double(lineConstrainedSize.width), truncationType, truncationToken) ?? truncationToken
truncated = true
}
@@ -913,7 +961,7 @@ public class TextNode: ASDisplayNode {
}
}
let lineWidth = min(constrainedSize.width, ceil(CGFloat(CTLineGetTypographicBounds(coreTextLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(coreTextLine))))
let lineWidth = min(lineConstrainedSize.width, ceil(CGFloat(CTLineGetTypographicBounds(coreTextLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(coreTextLine))))
let lineFrame = CGRect(x: lineCutoutOffset + headIndent, y: lineOriginY, width: lineWidth, height: fontLineHeight)
layoutSize.height += fontLineHeight + fontLineSpacing
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
@@ -989,7 +1037,7 @@ public class TextNode: ASDisplayNode {
if !lines.isEmpty && bottomCutoutEnabled {
let proposedWidth = lines[lines.count - 1].frame.width + bottomCutoutSize.width
if proposedWidth > layoutSize.width {
if proposedWidth < constrainedSize.width {
if proposedWidth <= constrainedSize.width + .ulpOfOne {
layoutSize.width = proposedWidth
} else {
layoutSize.height += bottomCutoutSize.height
@@ -997,9 +1045,20 @@ public class TextNode: ASDisplayNode {
}
}
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor)
if lines.count < minimumNumberOfLines {
var lineCount = lines.count
while lineCount < minimumNumberOfLines {
if lineCount != 0 {
layoutSize.height += fontLineSpacing
}
layoutSize.height += fontLineHeight
lineCount += 1
}
}
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke)
} else {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke)
}
}
@@ -1037,6 +1096,16 @@ public class TextNode: ASDisplayNode {
context.setShadow(offset: CGSize(width: 0.0, height: 1.0), blur: 0.0, color: textShadowColor.cgColor)
}
if let (textStrokeColor, textStrokeWidth) = layout.textStroke {
context.setBlendMode(.normal)
context.setLineCap(.round)
context.setLineJoin(.round)
context.setStrokeColor(textStrokeColor.cgColor)
context.setFillColor(textStrokeColor.cgColor)
context.setLineWidth(textStrokeWidth)
context.setTextDrawingMode(.fillStroke)
}
let textMatrix = context.textMatrix
let textPosition = context.textPosition
context.textMatrix = CGAffineTransform(scaleX: 1.0, y: -1.0)
@@ -1143,11 +1212,11 @@ public class TextNode: ASDisplayNode {
if stringMatch {
layout = existingLayout
} else {
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor)
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textStroke: arguments.textStroke)
updated = true
}
} else {
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor)
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textStroke: arguments.textStroke)
updated = true
}