[WIP] Quotes

This commit is contained in:
Ali
2023-10-07 00:33:12 +04:00
parent 3cada5996b
commit eae866c77e
77 changed files with 2103 additions and 552 deletions

View File

@@ -118,17 +118,23 @@ public func textAttributedStringForStateText(_ stateText: NSAttributedString, fo
result.addAttribute(key, value: value, range: range)
result.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.clear, range: range)
} else if key == ChatTextInputAttributes.quote {
fontAttributes.insert(.blockQuote)
result.addAttribute(key, value: value, range: range)
result.addAttribute(NSAttributedString.Key.backgroundColor, value: accentTextColor.withAlphaComponent(0.15), range: range)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 8.0
paragraphStyle.headIndent = 8.0
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: paragraphStyle.headIndent, options: [:])]
result.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: range)
//paragraphStyle.paragraphSpacing = 8.0
//paragraphStyle.paragraphSpacingBefore = 8.0
//result.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: range)
}
}
if !fontAttributes.isEmpty {
var font: UIFont?
var fontSize = fontSize
if fontAttributes.contains(.blockQuote) {
fontSize = round(fontSize * 0.8235294117647058)
}
if fontAttributes == [.bold, .italic, .monospace] {
font = Font.semiboldItalicMonospace(fontSize)
} else if fontAttributes == [.bold, .monospace] {
@@ -143,6 +149,8 @@ public func textAttributedStringForStateText(_ stateText: NSAttributedString, fo
font = Font.italic(fontSize)
} else if fontAttributes == [.monospace] {
font = Font.monospace(fontSize)
} else {
font = Font.regular(fontSize)
}
if let font = font {
@@ -660,8 +668,8 @@ private func refreshBlockQuotes(text: NSString, initialAttributedText: NSAttribu
}
}
public func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme: PresentationTheme, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?) {
refreshChatTextInputAttributes(textView: textNode.textView, primaryTextColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, baseFontSize: baseFontSize, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
public func refreshChatTextInputAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?) {
refreshChatTextInputAttributes(textView: textView, primaryTextColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, baseFontSize: baseFontSize, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
}
public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColor: UIColor, accentTextColor: UIColor, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?) {
@@ -669,6 +677,8 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
return
}
textView.textStorage.beginEditing()
var writingDirection: NSWritingDirection?
if let style = initialAttributedText.attribute(NSAttributedString.Key.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
writingDirection = style.baseWritingDirection
@@ -752,12 +762,130 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
textView.textStorage.addAttribute(key, value: value, range: range)
textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.clear, range: range)
} else if key == ChatTextInputAttributes.quote {
fontAttributes.insert(.blockQuote)
textView.textStorage.addAttribute(key, value: value, range: range)
textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: accentTextColor.withAlphaComponent(0.15), range: range)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 8.0
paragraphStyle.headIndent = 8.0
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: paragraphStyle.headIndent, options: [:])]
textView.textStorage.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: range)
//paragraphStyle.paragraphSpacing = 8.0
//paragraphStyle.paragraphSpacingBefore = 8.0
//textView.textStorage.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: range)
}
}
if !fontAttributes.isEmpty {
var font: UIFont?
var baseFontSize = baseFontSize
if fontAttributes.contains(.blockQuote) {
baseFontSize = round(baseFontSize * 0.8235294117647058)
}
if fontAttributes == [.bold, .italic, .monospace] {
font = Font.semiboldItalicMonospace(baseFontSize)
} else if fontAttributes == [.bold, .italic] {
font = Font.semiboldItalic(baseFontSize)
} else if fontAttributes == [.bold, .monospace] {
font = Font.semiboldMonospace(baseFontSize)
} else if fontAttributes == [.italic, .monospace] {
font = Font.italicMonospace(baseFontSize)
} else if fontAttributes == [.bold] {
font = Font.semibold(baseFontSize)
} else if fontAttributes == [.italic] {
font = Font.italic(baseFontSize)
} else if fontAttributes == [.monospace] {
font = Font.monospace(baseFontSize)
} else {
font = Font.regular(baseFontSize)
}
if let font = font {
textView.textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: range)
}
}
})
for (range, attachment) in replaceRanges.sorted(by: { $0.0.location > $1.0.location }) {
textView.textStorage.replaceCharacters(in: range, with: NSAttributedString(attachment: attachment))
}
}
textView.textStorage.endEditing()
}
public func refreshGenericTextInputAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, spoilersRevealed: Bool = false) {
guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
return
}
var writingDirection: NSWritingDirection?
if let style = initialAttributedText.attribute(NSAttributedString.Key.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
writingDirection = style.baseWritingDirection
}
var text: NSString = initialAttributedText.string as NSString
var fullRange = NSRange(location: 0, length: initialAttributedText.length)
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText))
var resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
text = resultAttributedText.string as NSString
fullRange = NSRange(location: 0, length: initialAttributedText.length)
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
if !resultAttributedText.isEqual(to: initialAttributedText) {
textView.textStorage.removeAttribute(NSAttributedString.Key.font, range: fullRange)
textView.textStorage.removeAttribute(NSAttributedString.Key.foregroundColor, range: fullRange)
textView.textStorage.removeAttribute(NSAttributedString.Key.backgroundColor, range: fullRange)
textView.textStorage.removeAttribute(NSAttributedString.Key.underlineStyle, range: fullRange)
textView.textStorage.removeAttribute(NSAttributedString.Key.strikethroughStyle, range: fullRange)
textView.textStorage.removeAttribute(ChatTextInputAttributes.textMention, range: fullRange)
textView.textStorage.removeAttribute(ChatTextInputAttributes.textUrl, range: fullRange)
textView.textStorage.removeAttribute(ChatTextInputAttributes.spoiler, range: fullRange)
textView.textStorage.addAttribute(NSAttributedString.Key.font, value: Font.regular(baseFontSize), range: fullRange)
textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.chat.inputPanel.primaryTextColor, range: fullRange)
attributedText.enumerateAttributes(in: fullRange, options: [], using: { attributes, range, _ in
var fontAttributes: ChatTextFontAttributes = []
for (key, value) in attributes {
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
textView.textStorage.addAttribute(key, value: value, range: range)
textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.chat.inputPanel.panelControlAccentColor, range: range)
if theme.chat.inputPanel.panelControlAccentColor.isEqual(theme.chat.inputPanel.primaryTextColor) {
textView.textStorage.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range)
}
} else if key == ChatTextInputAttributes.bold {
textView.textStorage.addAttribute(key, value: value, range: range)
fontAttributes.insert(.bold)
} else if key == ChatTextInputAttributes.italic {
textView.textStorage.addAttribute(key, value: value, range: range)
fontAttributes.insert(.italic)
} else if key == ChatTextInputAttributes.monospace {
textView.textStorage.addAttribute(key, value: value, range: range)
fontAttributes.insert(.monospace)
} else if key == ChatTextInputAttributes.strikethrough {
textView.textStorage.addAttribute(key, value: value, range: range)
textView.textStorage.addAttribute(NSAttributedString.Key.strikethroughStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range)
} else if key == ChatTextInputAttributes.underline {
textView.textStorage.addAttribute(key, value: value, range: range)
textView.textStorage.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range)
} else if key == ChatTextInputAttributes.spoiler {
textView.textStorage.addAttribute(key, value: value, range: range)
if spoilersRevealed {
textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: theme.chat.inputPanel.primaryTextColor.withAlphaComponent(0.15), range: range)
} else {
textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.clear, range: range)
}
} else if key == ChatTextInputAttributes.quote {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 8.0
paragraphStyle.headIndent = 8.0
//paragraphStyle.paragraphSpacing = 8.0
//paragraphStyle.paragraphSpacingBefore = 8.0
//textView.textStorage.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: range)
}
}
@@ -784,113 +912,6 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
}
}
})
for (range, attachment) in replaceRanges.sorted(by: { $0.0.location > $1.0.location }) {
textView.textStorage.replaceCharacters(in: range, with: NSAttributedString(attachment: attachment))
}
}
}
public func refreshGenericTextInputAttributes(_ textNode: ASEditableTextNode, theme: PresentationTheme, baseFontSize: CGFloat, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, spoilersRevealed: Bool = false) {
guard let initialAttributedText = textNode.attributedText, initialAttributedText.length != 0 else {
return
}
var writingDirection: NSWritingDirection?
if let style = initialAttributedText.attribute(NSAttributedString.Key.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
writingDirection = style.baseWritingDirection
}
var text: NSString = initialAttributedText.string as NSString
var fullRange = NSRange(location: 0, length: initialAttributedText.length)
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText))
var resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
text = resultAttributedText.string as NSString
fullRange = NSRange(location: 0, length: initialAttributedText.length)
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
if !resultAttributedText.isEqual(to: initialAttributedText) {
textNode.textView.textStorage.removeAttribute(NSAttributedString.Key.font, range: fullRange)
textNode.textView.textStorage.removeAttribute(NSAttributedString.Key.foregroundColor, range: fullRange)
textNode.textView.textStorage.removeAttribute(NSAttributedString.Key.backgroundColor, range: fullRange)
textNode.textView.textStorage.removeAttribute(NSAttributedString.Key.underlineStyle, range: fullRange)
textNode.textView.textStorage.removeAttribute(NSAttributedString.Key.strikethroughStyle, range: fullRange)
textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textMention, range: fullRange)
textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.textUrl, range: fullRange)
textNode.textView.textStorage.removeAttribute(ChatTextInputAttributes.spoiler, range: fullRange)
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.font, value: Font.regular(baseFontSize), range: fullRange)
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.chat.inputPanel.primaryTextColor, range: fullRange)
attributedText.enumerateAttributes(in: fullRange, options: [], using: { attributes, range, _ in
var fontAttributes: ChatTextFontAttributes = []
for (key, value) in attributes {
if key == ChatTextInputAttributes.textMention || key == ChatTextInputAttributes.textUrl {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.chat.inputPanel.panelControlAccentColor, range: range)
if theme.chat.inputPanel.panelControlAccentColor.isEqual(theme.chat.inputPanel.primaryTextColor) {
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range)
}
} else if key == ChatTextInputAttributes.bold {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
fontAttributes.insert(.bold)
} else if key == ChatTextInputAttributes.italic {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
fontAttributes.insert(.italic)
} else if key == ChatTextInputAttributes.monospace {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
fontAttributes.insert(.monospace)
} else if key == ChatTextInputAttributes.strikethrough {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.strikethroughStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range)
} else if key == ChatTextInputAttributes.underline {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range)
} else if key == ChatTextInputAttributes.spoiler {
textNode.textView.textStorage.addAttribute(key, value: value, range: range)
if spoilersRevealed {
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: theme.chat.inputPanel.primaryTextColor.withAlphaComponent(0.15), range: range)
} else {
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.clear, range: range)
}
} else if key == ChatTextInputAttributes.quote {
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.15), range: range)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.headIndent = 8.0
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: paragraphStyle.headIndent, options: [:])]
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: range)
}
}
if !fontAttributes.isEmpty {
var font: UIFont?
if fontAttributes == [.bold, .italic, .monospace] {
font = Font.semiboldItalicMonospace(baseFontSize)
} else if fontAttributes == [.bold, .italic] {
font = Font.semiboldItalic(baseFontSize)
} else if fontAttributes == [.bold, .monospace] {
font = Font.semiboldMonospace(baseFontSize)
} else if fontAttributes == [.italic, .monospace] {
font = Font.italicMonospace(baseFontSize)
} else if fontAttributes == [.bold] {
font = Font.semibold(baseFontSize)
} else if fontAttributes == [.italic] {
font = Font.italic(baseFontSize)
} else if fontAttributes == [.monospace] {
font = Font.monospace(baseFontSize)
}
if let font = font {
textNode.textView.textStorage.addAttribute(NSAttributedString.Key.font, value: font, range: range)
}
}
})
}
}
@@ -919,7 +940,7 @@ public func refreshChatTextInputTypingAttributes(_ textView: UITextView, textCol
textView.typingAttributes = filteredAttributes
}
public func refreshChatTextInputTypingAttributes(_ textNode: ASEditableTextNode, theme: PresentationTheme, baseFontSize: CGFloat) {
public func refreshChatTextInputTypingAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat) {
var filteredAttributes: [NSAttributedString.Key: Any] = [
NSAttributedString.Key.font: Font.regular(baseFontSize),
NSAttributedString.Key.foregroundColor: theme.chat.inputPanel.primaryTextColor
@@ -927,8 +948,8 @@ public func refreshChatTextInputTypingAttributes(_ textNode: ASEditableTextNode,
let style = NSMutableParagraphStyle()
style.baseWritingDirection = .natural
filteredAttributes[NSAttributedString.Key.paragraphStyle] = style
if let attributedText = textNode.attributedText, attributedText.length != 0 {
let attributes = attributedText.attributes(at: max(0, min(textNode.selectedRange.location - 1, attributedText.length - 1)), effectiveRange: nil)
if let attributedText = textView.attributedText, attributedText.length != 0 {
let attributes = attributedText.attributes(at: max(0, min(textView.selectedRange.location - 1, attributedText.length - 1)), effectiveRange: nil)
for (key, value) in attributes {
if key == ChatTextInputAttributes.bold {
filteredAttributes[key] = value
@@ -941,7 +962,7 @@ public func refreshChatTextInputTypingAttributes(_ textNode: ASEditableTextNode,
}
}
}
textNode.textView.typingAttributes = filteredAttributes
textView.typingAttributes = filteredAttributes
}
private func trimRangesForChatInputText(_ text: NSAttributedString) -> (Int, Int) {

View File

@@ -55,7 +55,7 @@ public func chatInputStateStringWithAppliedEntities(_ text: String, entities: [M
return string
}
public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseQuoteTintColor: UIColor? = nil, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false, message: Message?, entityFiles: [MediaId: TelegramMediaFile] = [:]) -> NSAttributedString {
public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseQuoteTintColor: UIColor? = nil, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false, message: Message?, entityFiles: [MediaId: TelegramMediaFile] = [:], adjustQuoteFontSize: Bool = false) -> NSAttributedString {
let baseQuoteTintColor = baseQuoteTintColor ?? baseColor
var nsString: NSString?
@@ -289,9 +289,8 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
func addFont(ranges: [NSRange], fontAttributes: ChatTextFontAttributes) {
for range in ranges {
var font: UIFont?
if fontAttributes.contains(.blockQuote) {
font = baseFont.withSize(round(baseFont.pointSize * 0.8235294117647058))
} else if fontAttributes == [.bold, .italic] {
if fontAttributes == [.bold, .italic] {
font = boldItalicFont
} else if fontAttributes == [.bold] {
font = boldFont
@@ -299,7 +298,14 @@ public func stringWithAppliedEntities(_ text: String, entities: [MessageTextEnti
} else if fontAttributes == [.italic] {
font = italicFont
addedAttributes.append((range, fontAttributes))
} else {
font = baseFont
}
if adjustQuoteFontSize, let fontValue = font, fontAttributes.contains(.blockQuote) {
font = fontValue.withSize(round(fontValue.pointSize * 0.8235294117647058))
}
if let font = font {
string.addAttribute(NSAttributedString.Key.font, value: font, range: range)
}