diff --git a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift index 2868bce605..4a521443bf 100644 --- a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift +++ b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift @@ -112,13 +112,14 @@ private final class InteractiveTextNodeLine { let isTruncated: Bool let isRTL: Bool var strikethroughs: [InteractiveTextNodeStrikethrough] + var underlines: [InteractiveTextNodeStrikethrough] var spoilers: [InteractiveTextNodeSpoiler] var spoilerWords: [InteractiveTextNodeSpoiler] var embeddedItems: [InteractiveTextNodeEmbeddedItem] var attachments: [InteractiveTextNodeAttachment] let additionalTrailingLine: (CTLine, Double)? - init(line: CTLine, constrainedWidth: CGFloat, frame: CGRect, intrinsicWidth: CGFloat, ascent: CGFloat, descent: CGFloat, range: NSRange?, isTruncated: Bool, isRTL: Bool, strikethroughs: [InteractiveTextNodeStrikethrough], spoilers: [InteractiveTextNodeSpoiler], spoilerWords: [InteractiveTextNodeSpoiler], embeddedItems: [InteractiveTextNodeEmbeddedItem], attachments: [InteractiveTextNodeAttachment], additionalTrailingLine: (CTLine, Double)?) { + init(line: CTLine, constrainedWidth: CGFloat, frame: CGRect, intrinsicWidth: CGFloat, ascent: CGFloat, descent: CGFloat, range: NSRange?, isTruncated: Bool, isRTL: Bool, strikethroughs: [InteractiveTextNodeStrikethrough], underlines: [InteractiveTextNodeStrikethrough], spoilers: [InteractiveTextNodeSpoiler], spoilerWords: [InteractiveTextNodeSpoiler], embeddedItems: [InteractiveTextNodeEmbeddedItem], attachments: [InteractiveTextNodeAttachment], additionalTrailingLine: (CTLine, Double)?) { self.line = line self.constrainedWidth = constrainedWidth self.frame = frame @@ -129,6 +130,7 @@ private final class InteractiveTextNodeLine { self.isTruncated = isTruncated self.isRTL = isRTL self.strikethroughs = strikethroughs + self.underlines = underlines self.spoilers = spoilers self.spoilerWords = spoilerWords self.embeddedItems = embeddedItems @@ -1452,6 +1454,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn isTruncated: false, isRTL: false, strikethroughs: [], + underlines: [], spoilers: [], spoilerWords: [], embeddedItems: [], @@ -1493,6 +1496,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn isTruncated: false, isRTL: isRTL && segment.blockQuote == nil, strikethroughs: [], + underlines: [], spoilers: [], spoilerWords: [], embeddedItems: [], @@ -1551,6 +1555,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn isTruncated: true, isRTL: lastLine.isRTL, strikethroughs: [], + underlines: [], spoilers: [], spoilerWords: [], embeddedItems: [], @@ -1605,6 +1610,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn isTruncated: true, isRTL: lastLine.isRTL, strikethroughs: [], + underlines: [], spoilers: [], spoilerWords: [], embeddedItems: [], @@ -1736,6 +1742,11 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn let upperX = ceil(CTLineGetOffsetForStringIndex(line.line, range.location + range.length, nil)) let x = lowerX < upperX ? lowerX : upperX line.strikethroughs.append(InteractiveTextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: line.frame.height))) + } else if let _ = attributes[NSAttributedString.Key.underlineStyle] { + let lowerX = floor(CTLineGetOffsetForStringIndex(line.line, range.location, nil)) + let upperX = ceil(CTLineGetOffsetForStringIndex(line.line, range.location + range.length, nil)) + let x = lowerX < upperX ? lowerX : upperX + line.underlines.append(InteractiveTextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: line.frame.height))) } if let embeddedItem = (attributes[NSAttributedString.Key(rawValue: "TelegramEmbeddedItem")] as? AnyHashable ?? attributes[NSAttributedString.Key(rawValue: "Attribute__EmbeddedItem")] as? AnyHashable) { @@ -2090,6 +2101,14 @@ final class TextContentItem { } } +private let drawUnderlinesManually: Bool = { + if #available(iOS 18.0, *) { + return true + } else { + return false + } +}() + final class TextContentItemLayer: SimpleLayer { final class Params { let item: TextContentItem @@ -2322,6 +2341,46 @@ final class TextContentItemLayer: SimpleLayer { } } + if drawUnderlinesManually { + if !line.strikethroughs.isEmpty { + for strikethrough in line.strikethroughs { + guard let lineRange = line.range else { + continue + } + var textColor: UIColor? + params.item.attributedString?.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in + if range == strikethrough.range, let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor { + textColor = color + } + } + if let textColor = textColor { + context.setFillColor(textColor.cgColor) + } + let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) + context.fill(CGRect(x: frame.minX, y: frame.midY, width: frame.width, height: 1.0)) + } + } + + if !line.underlines.isEmpty { + for strikethrough in line.underlines { + guard let lineRange = line.range else { + continue + } + var textColor: UIColor? + params.item.attributedString?.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in + if range == strikethrough.range, let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor { + textColor = color + } + } + if let textColor = textColor { + context.setFillColor(textColor.cgColor) + } + let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) + context.fill(CGRect(x: frame.minX, y: frame.maxY - 2.0, width: frame.width, height: 1.0)) + } + } + } + if let (additionalTrailingLine, _) = line.additionalTrailingLine { context.textPosition = CGPoint(x: lineFrame.minX + line.intrinsicWidth, y: lineFrame.maxY - line.descent)