mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Added block quote entity support
This commit is contained in:
parent
185a3c4dad
commit
fec4f50849
@ -5,6 +5,14 @@ import CoreText
|
||||
|
||||
private let defaultFont = UIFont.systemFont(ofSize: 15.0)
|
||||
|
||||
private final class TextNodeStrikethrough {
|
||||
let frame: CGRect
|
||||
|
||||
init(frame: CGRect) {
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
private final class TextNodeLine {
|
||||
let line: CTLine
|
||||
let frame: CGRect
|
||||
@ -21,7 +29,7 @@ private final class TextNodeLine {
|
||||
}
|
||||
}
|
||||
|
||||
private final class TextNodeStrikethrough {
|
||||
private final class TextNodeBlockQuote {
|
||||
let frame: CGRect
|
||||
|
||||
init(frame: CGRect) {
|
||||
@ -83,8 +91,9 @@ public final class TextNodeLayoutArguments {
|
||||
public let lineSpacing: CGFloat
|
||||
public let cutout: TextNodeCutout?
|
||||
public let insets: UIEdgeInsets
|
||||
public let lineColor: UIColor?
|
||||
|
||||
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()) {
|
||||
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) {
|
||||
self.attributedString = attributedString
|
||||
self.backgroundColor = backgroundColor
|
||||
self.maximumNumberOfLines = maximumNumberOfLines
|
||||
@ -94,6 +103,7 @@ public final class TextNodeLayoutArguments {
|
||||
self.lineSpacing = lineSpacing
|
||||
self.cutout = cutout
|
||||
self.insets = insets
|
||||
self.lineColor = lineColor
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,9 +121,11 @@ public final class TextNodeLayout: NSObject {
|
||||
public let truncated: Bool
|
||||
fileprivate let firstLineOffset: CGFloat
|
||||
fileprivate let lines: [TextNodeLine]
|
||||
fileprivate let blockQuotes: [TextNodeBlockQuote]
|
||||
fileprivate let lineColor: UIColor?
|
||||
public let hasRTL: Bool
|
||||
|
||||
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], backgroundColor: UIColor?) {
|
||||
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?) {
|
||||
self.attributedString = attributedString
|
||||
self.maximumNumberOfLines = maximumNumberOfLines
|
||||
self.truncationType = truncationType
|
||||
@ -126,7 +138,9 @@ public final class TextNodeLayout: NSObject {
|
||||
self.truncated = truncated
|
||||
self.firstLineOffset = firstLineOffset
|
||||
self.lines = lines
|
||||
self.blockQuotes = blockQuotes
|
||||
self.backgroundColor = backgroundColor
|
||||
self.lineColor = lineColor
|
||||
var hasRTL = false
|
||||
for line in lines {
|
||||
if line.isRTL {
|
||||
@ -561,7 +575,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) -> TextNodeLayout {
|
||||
private class func calculateLayout(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?) -> TextNodeLayout {
|
||||
if let attributedString = attributedString {
|
||||
let stringLength = attributedString.length
|
||||
|
||||
@ -582,11 +596,12 @@ public class TextNode: ASDisplayNode {
|
||||
let fontLineSpacing = floor(fontLineHeight * lineSpacingFactor)
|
||||
|
||||
var lines: [TextNodeLine] = []
|
||||
var blockQuotes: [TextNodeBlockQuote] = []
|
||||
|
||||
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(), truncated: false, firstLineOffset: 0.0, lines: [], backgroundColor: backgroundColor)
|
||||
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor)
|
||||
}
|
||||
|
||||
let typesetter = maybeTypesetter!
|
||||
@ -682,11 +697,28 @@ public class TextNode: ASDisplayNode {
|
||||
truncated = true
|
||||
}
|
||||
|
||||
var headIndent: CGFloat = 0.0
|
||||
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
|
||||
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
|
||||
let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil))
|
||||
let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil))
|
||||
let x = lowerX < upperX ? lowerX : upperX
|
||||
strikethroughs.append(TextNodeStrikethrough(frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight)))
|
||||
} else if let paragraphStyle = attributes[NSAttributedStringKey.paragraphStyle] as? NSParagraphStyle {
|
||||
headIndent = paragraphStyle.headIndent
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let lineWidth = min(constrainedSize.width, ceil(CGFloat(CTLineGetTypographicBounds(coreTextLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(coreTextLine))))
|
||||
let lineFrame = CGRect(x: lineCutoutOffset, y: lineOriginY, width: lineWidth, height: fontLineHeight)
|
||||
let lineFrame = CGRect(x: lineCutoutOffset + headIndent, y: lineOriginY, width: lineWidth, height: fontLineHeight)
|
||||
layoutSize.height += fontLineHeight + fontLineSpacing
|
||||
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
|
||||
|
||||
if headIndent > 0.0 {
|
||||
blockQuotes.append(TextNodeBlockQuote(frame: lineFrame))
|
||||
}
|
||||
|
||||
var isRTL = false
|
||||
let glyphRuns = CTLineGetGlyphRuns(coreTextLine) as NSArray
|
||||
if glyphRuns.count != 0 {
|
||||
@ -696,16 +728,7 @@ public class TextNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
|
||||
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
|
||||
let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil))
|
||||
let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil))
|
||||
let x = lowerX < upperX ? lowerX : upperX
|
||||
strikethroughs.append(TextNodeStrikethrough(frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight)))
|
||||
}
|
||||
}
|
||||
lines.append(TextNodeLine(line: coreTextLine, frame: lineFrame, range: NSMakeRange(lineRange.location, lineRange.length), isRTL: isRTL, strikethroughs: strikethroughs))
|
||||
|
||||
break
|
||||
} else {
|
||||
if lineCharacterCount > 0 {
|
||||
@ -719,11 +742,27 @@ public class TextNode: ASDisplayNode {
|
||||
let coreTextLine = CTTypesetterCreateLineWithOffset(typesetter, lineRange, 100.0)
|
||||
lastLineCharacterIndex += lineCharacterCount
|
||||
|
||||
var headIndent: CGFloat = 0.0
|
||||
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
|
||||
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
|
||||
let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil))
|
||||
let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil))
|
||||
let x = lowerX < upperX ? lowerX : upperX
|
||||
strikethroughs.append(TextNodeStrikethrough(frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight)))
|
||||
} else if let paragraphStyle = attributes[NSAttributedStringKey.paragraphStyle] as? NSParagraphStyle {
|
||||
headIndent = paragraphStyle.headIndent
|
||||
}
|
||||
}
|
||||
|
||||
let lineWidth = ceil(CGFloat(CTLineGetTypographicBounds(coreTextLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(coreTextLine)))
|
||||
let lineFrame = CGRect(x: lineCutoutOffset, y: lineOriginY, width: lineWidth, height: fontLineHeight)
|
||||
let lineFrame = CGRect(x: lineCutoutOffset + headIndent, y: lineOriginY, width: lineWidth, height: fontLineHeight)
|
||||
layoutSize.height += fontLineHeight
|
||||
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
|
||||
|
||||
if headIndent > 0.0 {
|
||||
blockQuotes.append(TextNodeBlockQuote(frame: lineFrame))
|
||||
}
|
||||
|
||||
var isRTL = false
|
||||
let glyphRuns = CTLineGetGlyphRuns(coreTextLine) as NSArray
|
||||
if glyphRuns.count != 0 {
|
||||
@ -733,14 +772,6 @@ public class TextNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
|
||||
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
|
||||
let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil))
|
||||
let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil))
|
||||
let x = lowerX < upperX ? lowerX : upperX
|
||||
strikethroughs.append(TextNodeStrikethrough(frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight)))
|
||||
}
|
||||
}
|
||||
lines.append(TextNodeLine(line: coreTextLine, frame: lineFrame, range: NSMakeRange(lineRange.location, lineRange.length), isRTL: isRTL, strikethroughs: strikethroughs))
|
||||
} else {
|
||||
if !lines.isEmpty {
|
||||
@ -762,9 +793,9 @@ 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), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, backgroundColor: backgroundColor)
|
||||
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), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor)
|
||||
} else {
|
||||
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], backgroundColor: backgroundColor)
|
||||
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor)
|
||||
}
|
||||
}
|
||||
|
||||
@ -799,11 +830,8 @@ public class TextNode: ASDisplayNode {
|
||||
|
||||
let textMatrix = context.textMatrix
|
||||
let textPosition = context.textPosition
|
||||
//CGContextSaveGState(context)
|
||||
|
||||
context.textMatrix = CGAffineTransform(scaleX: 1.0, y: -1.0)
|
||||
|
||||
//let clipRect = CGContextGetClipBoundingBox(context)
|
||||
|
||||
let alignment = layout.alignment
|
||||
let offset = CGPoint(x: layout.insets.left, y: layout.insets.top)
|
||||
@ -832,7 +860,34 @@ public class TextNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
//CGContextRestoreGState(context)
|
||||
var blockQuoteFrames: [CGRect] = []
|
||||
var currentBlockQuoteFrame: CGRect?
|
||||
for blockQuote in layout.blockQuotes {
|
||||
if let frame = currentBlockQuoteFrame {
|
||||
if blockQuote.frame.minY - frame.maxY < 20.0 {
|
||||
currentBlockQuoteFrame = frame.union(blockQuote.frame)
|
||||
} else {
|
||||
blockQuoteFrames.append(frame)
|
||||
currentBlockQuoteFrame = frame
|
||||
}
|
||||
} else {
|
||||
currentBlockQuoteFrame = blockQuote.frame
|
||||
}
|
||||
}
|
||||
|
||||
if let frame = currentBlockQuoteFrame {
|
||||
blockQuoteFrames.append(frame)
|
||||
}
|
||||
|
||||
for frame in blockQuoteFrames {
|
||||
if let lineColor = layout.lineColor {
|
||||
context.setFillColor(lineColor.cgColor)
|
||||
}
|
||||
let rect = UIBezierPath(roundedRect: CGRect(x: frame.minX - 9.0, y: frame.minY - 14.0, width: 2.0, height: frame.height), cornerRadius: 1.0)
|
||||
context.addPath(rect.cgPath)
|
||||
context.fillPath()
|
||||
}
|
||||
|
||||
context.textMatrix = textMatrix
|
||||
context.textPosition = CGPoint(x: textPosition.x, y: textPosition.y)
|
||||
}
|
||||
@ -872,11 +927,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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
updated = true
|
||||
}
|
||||
|
||||
|
@ -277,7 +277,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
}
|
||||
var dismissImpl: (() -> Void)?
|
||||
let alertTheme = AlertControllerTheme(presentationTheme: strongSelf.theme)
|
||||
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.regular(13.0))
|
||||
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.regular(13.0), blockQuoteFont: Font.regular(13.0))
|
||||
let contentNode = TextAlertContentNode(theme: alertTheme, title: NSAttributedString(string: strongSelf.strings.Login_TermsOfServiceHeader, font: Font.medium(17.0), textColor: alertTheme.primaryColor, paragraphAlignment: .center), text: attributedText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.strings.Login_TermsOfServiceAgree, action: {
|
||||
dismissImpl?()
|
||||
|
@ -152,7 +152,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
|
||||
updatedTextAndEntities = (item.text, generateTextEntities(item.text, enabledTypes: .all))
|
||||
}
|
||||
|
||||
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.message.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.message.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, boldItalicFont: messageBoldItalicFont, fixedFont: messageFixedFont)
|
||||
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.message.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.message.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, boldItalicFont: messageBoldItalicFont, fixedFont: messageFixedFont, blockQuoteFont: messageFont)
|
||||
|
||||
let horizontalEdgeInset: CGFloat = 10.0 + params.leftInset
|
||||
let horizontalContentInset: CGFloat = 12.0
|
||||
|
@ -385,7 +385,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st
|
||||
}
|
||||
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||
case let .customText(text, entities):
|
||||
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, underlineLinks: false)
|
||||
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false)
|
||||
case let .botDomainAccessGranted(domain):
|
||||
attributedString = NSAttributedString(string: strings.AuthSessions_Message(domain).0, font: titleFont, textColor: primaryTextColor)
|
||||
case let .botSentSecureValues(types):
|
||||
|
@ -15,6 +15,7 @@ private let textBoldFont = Font.semibold(15.0)
|
||||
private let textItalicFont = Font.italic(15.0)
|
||||
private let textBoldItalicFont = Font.semiboldItalic(15.0)
|
||||
private let textFixedFont = Font.regular(15.0)
|
||||
private let textBlockQuoteFont = Font.regular(15.0)
|
||||
private let buttonFont = Font.semibold(13.0)
|
||||
|
||||
enum ChatMessageAttachedContentActionIcon {
|
||||
@ -371,7 +372,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
string.append(NSAttributedString(string: "\n", font: textFont, textColor: messageTheme.primaryTextColor))
|
||||
}
|
||||
if let entities = entities {
|
||||
string.append(stringWithAppliedEntities(text, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont))
|
||||
string.append(stringWithAppliedEntities(text, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textBlockQuoteFont))
|
||||
} else {
|
||||
string.append(NSAttributedString(string: text + "\n", font: textFont, textColor: messageTheme.primaryTextColor))
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let forceStatusNewline = false
|
||||
|
||||
if let entities = entities {
|
||||
attributedText = stringWithAppliedEntities(rawText, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: item.presentationData.messageBoldFont, italicFont: item.presentationData.messageItalicFont, boldItalicFont: item.presentationData.messageBoldItalicFont, fixedFont: item.presentationData.messageFixedFont)
|
||||
attributedText = stringWithAppliedEntities(rawText, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: item.presentationData.messageBoldFont, italicFont: item.presentationData.messageItalicFont, boldItalicFont: item.presentationData.messageBoldItalicFont, fixedFont: item.presentationData.messageFixedFont, blockQuoteFont: item.presentationData.messageBlockQuoteFont)
|
||||
} else {
|
||||
attributedText = NSAttributedString(string: rawText, font: textFont, textColor: messageTheme.primaryTextColor)
|
||||
}
|
||||
@ -242,7 +242,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
let textInsets = UIEdgeInsets(top: 2.0, left: 0.0, bottom: 5.0, right: 0.0)
|
||||
|
||||
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: cutout, insets: textInsets))
|
||||
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: cutout, insets: textInsets, lineColor: messageTheme.accentControlColor))
|
||||
|
||||
var textFrame = CGRect(origin: CGPoint(x: -textInsets.left, y: -textInsets.top), size: textLayout.size)
|
||||
var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom))
|
||||
|
@ -84,6 +84,7 @@ public final class ChatPresentationData {
|
||||
let messageItalicFont: UIFont
|
||||
let messageBoldItalicFont: UIFont
|
||||
let messageFixedFont: UIFont
|
||||
let messageBlockQuoteFont: UIFont
|
||||
|
||||
init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) {
|
||||
self.theme = theme
|
||||
@ -103,5 +104,6 @@ public final class ChatPresentationData {
|
||||
self.messageItalicFont = UIFont.italicSystemFont(ofSize: baseFontSize)
|
||||
self.messageBoldItalicFont = Font.semiboldItalic(baseFontSize)
|
||||
self.messageFixedFont = UIFont(name: "Menlo-Regular", size: baseFontSize - 1.0) ?? UIFont.systemFont(ofSize: baseFontSize)
|
||||
self.messageBlockQuoteFont = UIFont.systemFont(ofSize: baseFontSize - 1.0)
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ struct ChatTextFontAttributes: OptionSet {
|
||||
static let bold = ChatTextFontAttributes(rawValue: 1 << 0)
|
||||
static let italic = ChatTextFontAttributes(rawValue: 1 << 1)
|
||||
static let monospace = ChatTextFontAttributes(rawValue: 1 << 2)
|
||||
static let blockQuote = ChatTextFontAttributes(rawValue: 1 << 3)
|
||||
}
|
||||
|
||||
func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor) -> NSAttributedString {
|
||||
|
@ -122,7 +122,7 @@ private let boldItalicFont = Font.semiboldItalic(16.0)
|
||||
private let fixedFont = UIFont(name: "Menlo-Regular", size: 15.0) ?? textFont
|
||||
|
||||
func galleryCaptionStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> NSAttributedString {
|
||||
return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, underlineLinks: false)
|
||||
return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: textFont, underlineLinks: false)
|
||||
}
|
||||
|
||||
private func galleryMessageCaptionText(_ message: Message) -> String {
|
||||
|
@ -182,7 +182,7 @@ class ItemListAddressItemNode: ListViewItemNode {
|
||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label, font: labelFont, textColor: labelColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let baseColor = item.theme.list.itemPrimaryTextColor
|
||||
let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont)
|
||||
let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont)
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset - 98.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let padding: CGFloat = !item.label.isEmpty ? 39.0 : 20.0
|
||||
|
@ -185,7 +185,7 @@ class ItemListMultilineTextItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
let entities = generateTextEntities(item.text, enabledTypes: item.enabledEntityTypes)
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleBoldFont, italicFont: titleItalicFont, boldItalicFont: titleBoldItalicFont, fixedFont: titleFixedFont)
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleBoldFont, italicFont: titleItalicFont, boldItalicFont: titleBoldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: titleFont)
|
||||
|
||||
let (titleLayout, titleApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
|
@ -209,7 +209,7 @@ class ItemListTextWithLabelItemNode: ListViewItemNode {
|
||||
case .highlighted:
|
||||
baseColor = item.theme.list.itemHighlightedColor
|
||||
}
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont)
|
||||
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont)
|
||||
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: item.multiline ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let contentSize = CGSize(width: params.width, height: textLayout.size.height + 39.0)
|
||||
|
@ -5,7 +5,7 @@ import TelegramCore
|
||||
import MobileCoreServices
|
||||
|
||||
private func rtfStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> String {
|
||||
let test = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), underlineLinks: false, external: true)
|
||||
let test = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), blockQuoteFont: Font.regular(14.0), underlineLinks: false, external: true)
|
||||
|
||||
if let data = try? test.data(from: NSRange(location: 0, length: test.length), documentAttributes: [NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.rtf]) {
|
||||
if var rtf = String(data: data, encoding: .windowsCP1252) {
|
||||
|
@ -371,7 +371,8 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
||||
|
||||
if self.currentItems.isEmpty && !updatedItems.isEmpty {
|
||||
let entities = generateTextEntities(info.title, enabledTypes: [.mention])
|
||||
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: Font.medium(20.0), linkFont: Font.medium(20.0), boldFont: Font.medium(20.0), italicFont: Font.medium(20.0), boldItalicFont: Font.medium(20.0), fixedFont: Font.medium(20.0))
|
||||
let font = Font.medium(20.0)
|
||||
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: font, linkFont: font, boldFont: font, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font)
|
||||
animateIn = true
|
||||
}
|
||||
transaction = StickerPackPreviewGridTransaction(previousList: self.currentItems, list: updatedItems, account: self.context.account, interaction: self.interaction)
|
||||
|
@ -45,24 +45,25 @@ func chatInputStateStringWithAppliedEntities(_ text: String, entities: [MessageT
|
||||
return string
|
||||
}
|
||||
|
||||
func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, underlineLinks: Bool = true, external: Bool = false) -> NSAttributedString {
|
||||
func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false) -> NSAttributedString {
|
||||
var nsString: NSString?
|
||||
let string = NSMutableAttributedString(string: text, attributes: [NSAttributedStringKey.font: baseFont, NSAttributedStringKey.foregroundColor: baseColor])
|
||||
var skipEntity = false
|
||||
let stringLength = string.length
|
||||
var underlineAllLinks = false
|
||||
if linkColor.isEqual(baseColor) {
|
||||
underlineAllLinks = true
|
||||
}
|
||||
var fontAttributes: [NSRange: ChatTextFontAttributes] = [:]
|
||||
|
||||
var rangeOffset: Int = 0
|
||||
for i in 0 ..< entities.count {
|
||||
if skipEntity {
|
||||
skipEntity = false
|
||||
continue
|
||||
}
|
||||
let stringLength = string.length
|
||||
let entity = entities[i]
|
||||
var range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound)
|
||||
var range = NSRange(location: entity.range.lowerBound + rangeOffset, length: entity.range.upperBound - entity.range.lowerBound)
|
||||
if nsString == nil {
|
||||
nsString = text as NSString
|
||||
}
|
||||
@ -188,6 +189,25 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
|
||||
string.addAttribute(NSAttributedStringKey(rawValue: TelegramTextAttributes.BotCommand), value: nsString!.substring(with: range), range: range)
|
||||
case .Code, .Pre:
|
||||
string.addAttribute(NSAttributedStringKey.font, value: fixedFont, range: range)
|
||||
case .BlockQuote:
|
||||
if let fontAttribute = fontAttributes[range] {
|
||||
fontAttributes[range] = fontAttribute.union(.blockQuote)
|
||||
} else {
|
||||
fontAttributes[range] = .blockQuote
|
||||
}
|
||||
|
||||
let paragraphBreak = "\n"
|
||||
string.insert(NSAttributedString(string: paragraphBreak), at: range.lowerBound)
|
||||
|
||||
let paragraphRange = NSRange(location: range.lowerBound + paragraphBreak.count, length: range.upperBound - range.lowerBound)
|
||||
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
paragraphStyle.headIndent = 10.0
|
||||
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: paragraphStyle.headIndent, options: [:])]
|
||||
string.addAttribute(NSAttributedStringKey.paragraphStyle, value: paragraphStyle, range: paragraphRange)
|
||||
|
||||
string.insert(NSAttributedString(string: paragraphBreak), at: paragraphRange.upperBound)
|
||||
rangeOffset += paragraphBreak.count
|
||||
case let .Custom(type):
|
||||
if type == ApplicationSpecificEntityType.Timecode {
|
||||
string.addAttribute(NSAttributedStringKey.foregroundColor, value: linkColor, range: range)
|
||||
@ -208,7 +228,9 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
|
||||
|
||||
for (range, fontAttributes) in fontAttributes {
|
||||
var font: UIFont?
|
||||
if fontAttributes == [.bold, .italic] {
|
||||
if fontAttributes.contains(.blockQuote) {
|
||||
font = blockQuoteFont
|
||||
} else if fontAttributes == [.bold, .italic] {
|
||||
font = boldItalicFont
|
||||
} else if fontAttributes == [.bold] {
|
||||
font = boldFont
|
||||
|
@ -57,7 +57,7 @@ final class TermsOfServiceControllerNode: ViewControllerTracingNode {
|
||||
self.contentTextNode = ImmediateTextNode()
|
||||
self.contentTextNode.displaysAsynchronously = false
|
||||
self.contentTextNode.maximumNumberOfLines = 0
|
||||
self.contentTextNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primary, linkColor: theme.accent, baseFont: Font.regular(15.0), linkFont: Font.regular(15.0), boldFont: Font.semibold(15.0), italicFont: Font.italic(15.0), boldItalicFont: Font.semiboldItalic(15.0), fixedFont: Font.monospace(15.0))
|
||||
self.contentTextNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primary, linkColor: theme.accent, baseFont: Font.regular(15.0), linkFont: Font.regular(15.0), boldFont: Font.semibold(15.0), italicFont: Font.italic(15.0), boldItalicFont: Font.semiboldItalic(15.0), fixedFont: Font.monospace(15.0), blockQuoteFont: Font.regular(15.0))
|
||||
|
||||
self.toolbarNode = ASDisplayNode()
|
||||
self.toolbarSeparatorNode = ASDisplayNode()
|
||||
|
@ -41,4 +41,5 @@ struct TelegramTextAttributes {
|
||||
static let BotCommand = "TelegramBotCommand"
|
||||
static let Hashtag = "TelegramHashtag"
|
||||
static let Timecode = "TelegramTimecode"
|
||||
static let BlockQuote = "TelegramBlockQuote"
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ class UpdateInfoItemNode: ListViewItemNode {
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 88.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let string = stringWithAppliedEntities(item.text, entities: item.entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont)
|
||||
let string = stringWithAppliedEntities(item.text, entities: item.entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont)
|
||||
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 28.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let contentSize: CGSize
|
||||
|
Loading…
x
Reference in New Issue
Block a user