mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Quote improvements
This commit is contained in:
parent
2cf5236569
commit
b7fe230590
@ -338,7 +338,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
|||||||
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
||||||
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||||
}
|
}
|
||||||
textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count)
|
textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count)
|
||||||
self.updatingInputState = false
|
self.updatingInputState = false
|
||||||
self.updateTextNodeText(animated: animated)
|
self.updateTextNodeText(animated: animated)
|
||||||
@ -1020,7 +1022,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
|||||||
public func chatInputTextNodeDidUpdateText() {
|
public func chatInputTextNodeDidUpdateText() {
|
||||||
if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState {
|
if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState {
|
||||||
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||||
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||||
|
|
||||||
self.updateSpoiler()
|
self.updateSpoiler()
|
||||||
@ -1192,9 +1196,13 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
|||||||
|
|
||||||
textInputNode.textView.isScrollEnabled = false
|
textInputNode.textView.isScrollEnabled = false
|
||||||
|
|
||||||
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
|
|
||||||
textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
|
|
||||||
if textInputNode.textView.subviews.count > 1, animated {
|
if textInputNode.textView.subviews.count > 1, animated {
|
||||||
let containerView = textInputNode.textView.subviews[1]
|
let containerView = textInputNode.textView.subviews[1]
|
||||||
@ -1349,7 +1357,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
|||||||
let textFont = Font.regular(baseFontSize)
|
let textFont = Font.regular(baseFontSize)
|
||||||
let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
||||||
|
|
||||||
let attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: false, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
let attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: false, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
|
|
||||||
let range = (attributedText.string as NSString).range(of: "\n")
|
let range = (attributedText.string as NSString).range(of: "\n")
|
||||||
if range.location != NSNotFound {
|
if range.location != NSNotFound {
|
||||||
@ -1754,7 +1764,9 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
|||||||
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
||||||
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||||
}
|
}
|
||||||
let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
string.replaceCharacters(in: range, with: cleanReplacementString)
|
string.replaceCharacters(in: range, with: cleanReplacementString)
|
||||||
self.textInputNode?.attributedText = string
|
self.textInputNode?.attributedText = string
|
||||||
self.textInputNode?.selectedRange = NSMakeRange(range.lowerBound + cleanReplacementString.length, 0)
|
self.textInputNode?.selectedRange = NSMakeRange(range.lowerBound + cleanReplacementString.length, 0)
|
||||||
|
@ -228,7 +228,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
|
|||||||
rightInset += inlineAction.icon.size.width + 8.0
|
rightInset += inlineAction.icon.size.width + 8.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemText = textAttributedStringForStateText(item.text, fontSize: 17.0, textColor: item.presentationData.theme.chat.inputPanel.primaryTextColor, accentTextColor: item.presentationData.theme.chat.inputPanel.panelControlAccentColor, writingDirection: nil, spoilersRevealed: false, availableEmojis: Set(), emojiViewProvider: nil)
|
let itemText = textAttributedStringForStateText(item.text, fontSize: 17.0, textColor: item.presentationData.theme.chat.inputPanel.primaryTextColor, accentTextColor: item.presentationData.theme.chat.inputPanel.panelControlAccentColor, writingDirection: nil, spoilersRevealed: false, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
|
||||||
let measureText = NSMutableAttributedString(attributedString: itemText)
|
let measureText = NSMutableAttributedString(attributedString: itemText)
|
||||||
let measureRawString = measureText.string
|
let measureRawString = measureText.string
|
||||||
if measureRawString.hasSuffix("\n") || measureRawString.isEmpty {
|
if measureRawString.hasSuffix("\n") || measureRawString.isEmpty {
|
||||||
@ -294,11 +294,11 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
|
|||||||
if let currentText = strongSelf.textNode.attributedText {
|
if let currentText = strongSelf.textNode.attributedText {
|
||||||
if currentText.string != attributedText.string || updatedTheme != nil {
|
if currentText.string != attributedText.string || updatedTheme != nil {
|
||||||
strongSelf.textNode.attributedText = attributedText
|
strongSelf.textNode.attributedText = attributedText
|
||||||
refreshGenericTextInputAttributes(strongSelf.textNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil)
|
refreshGenericTextInputAttributes(strongSelf.textNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
strongSelf.textNode.attributedText = attributedText
|
strongSelf.textNode.attributedText = attributedText
|
||||||
refreshGenericTextInputAttributes(strongSelf.textNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil)
|
refreshGenericTextInputAttributes(strongSelf.textNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strongSelf.backgroundNode.supernode == nil {
|
if strongSelf.backgroundNode.supernode == nil {
|
||||||
@ -591,7 +591,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
|
|||||||
public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
||||||
if let item = self.item {
|
if let item = self.item {
|
||||||
if let _ = self.textNode.attributedText {
|
if let _ = self.textNode.attributedText {
|
||||||
refreshGenericTextInputAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil)
|
refreshGenericTextInputAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
|
||||||
let updatedText = stateAttributedStringForText(self.textNode.attributedText!)
|
let updatedText = stateAttributedStringForText(self.textNode.attributedText!)
|
||||||
item.textUpdated(updatedText)
|
item.textUpdated(updatedText)
|
||||||
} else {
|
} else {
|
||||||
@ -621,7 +621,7 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshChatTextInputTypingAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0)
|
refreshChatTextInputTypingAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0)
|
||||||
refreshGenericTextInputAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil)
|
refreshGenericTextInputAttributes(editableTextNode.textView, theme: item.presentationData.theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,7 +672,7 @@ private func chatTextInputAddFormattingAttribute(item: CreatePollTextInputItem,
|
|||||||
textNode.selectedRange = nsRange
|
textNode.selectedRange = nsRange
|
||||||
|
|
||||||
refreshChatTextInputTypingAttributes(textNode.textView, theme: theme, baseFontSize: 17.0)
|
refreshChatTextInputTypingAttributes(textNode.textView, theme: theme, baseFontSize: 17.0)
|
||||||
refreshGenericTextInputAttributes(textNode.textView, theme: theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil)
|
refreshGenericTextInputAttributes(textNode.textView, theme: theme, baseFontSize: 17.0, availableEmojis: Set(), emojiViewProvider: nil, makeCollapsedQuoteAttachment: nil)
|
||||||
|
|
||||||
let updatedText = stateAttributedStringForText(textNode.attributedText!)
|
let updatedText = stateAttributedStringForText(textNode.attributedText!)
|
||||||
item.textUpdated(updatedText)
|
item.textUpdated(updatedText)
|
||||||
|
@ -400,13 +400,15 @@ private final class ChatInputLegacyLayoutManager: NSLayoutManager {
|
|||||||
private struct DisplayBlockQuote {
|
private struct DisplayBlockQuote {
|
||||||
var id: Int
|
var id: Int
|
||||||
var boundingRect: CGRect
|
var boundingRect: CGRect
|
||||||
var attribute: ChatTextInputTextQuoteAttribute
|
var kind: ChatTextInputTextQuoteAttribute.Kind
|
||||||
|
var isCollapsed: Bool
|
||||||
var range: NSRange
|
var range: NSRange
|
||||||
|
|
||||||
init(id: Int, boundingRect: CGRect, attribute: ChatTextInputTextQuoteAttribute, range: NSRange) {
|
init(id: Int, boundingRect: CGRect, kind: ChatTextInputTextQuoteAttribute.Kind, isCollapsed: Bool, range: NSRange) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.boundingRect = boundingRect
|
self.boundingRect = boundingRect
|
||||||
self.attribute = attribute
|
self.kind = kind
|
||||||
|
self.isCollapsed = isCollapsed
|
||||||
self.range = range
|
self.range = range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -612,7 +614,50 @@ private final class ChatInputTextLegacyInternal: NSObject, ChatInputTextInternal
|
|||||||
boundingRect.origin.y -= 4.0
|
boundingRect.origin.y -= 4.0
|
||||||
boundingRect.size.height += 8.0
|
boundingRect.size.height += 8.0
|
||||||
|
|
||||||
result.append(DisplayBlockQuote(id: id, boundingRect: boundingRect, attribute: value, range: range))
|
result.append(DisplayBlockQuote(id: id, boundingRect: boundingRect, kind: value.kind, isCollapsed: value.isCollapsed, range: range))
|
||||||
|
|
||||||
|
blockQuoteIndex += 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.customTextStorage.enumerateAttribute(NSAttributedString.Key.attachment, in: NSRange(location: 0, length: self.customTextStorage.length), using: { value, range, _ in
|
||||||
|
if let _ = value as? ChatInputTextCollapsedQuoteAttachment {
|
||||||
|
let glyphRange = self.customLayoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
|
||||||
|
if self.customLayoutManager.isValidGlyphIndex(glyphRange.location) && self.customLayoutManager.isValidGlyphIndex(glyphRange.location + glyphRange.length - 1) {
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = blockQuoteIndex
|
||||||
|
|
||||||
|
var boundingRect = CGRect()
|
||||||
|
var startIndex = glyphRange.lowerBound
|
||||||
|
while startIndex < glyphRange.upperBound {
|
||||||
|
var effectiveRange = NSRange(location: NSNotFound, length: 0)
|
||||||
|
let rect = self.customLayoutManager.lineFragmentUsedRect(forGlyphAt: startIndex, effectiveRange: &effectiveRange)
|
||||||
|
if boundingRect.isEmpty {
|
||||||
|
boundingRect = rect
|
||||||
|
} else {
|
||||||
|
boundingRect = boundingRect.union(rect)
|
||||||
|
}
|
||||||
|
if effectiveRange.location != NSNotFound {
|
||||||
|
startIndex = max(startIndex + 1, effectiveRange.upperBound)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boundingRect.origin.y += self.defaultTextContainerInset.top
|
||||||
|
|
||||||
|
boundingRect.origin.x += 5.0
|
||||||
|
boundingRect.size.width += 4.0
|
||||||
|
boundingRect.size.width += 18.0
|
||||||
|
boundingRect.size.width = min(boundingRect.size.width, self.textContainer.size.width - 18.0)
|
||||||
|
boundingRect.size.width = min(boundingRect.size.width, self.textContainer.size.width)
|
||||||
|
|
||||||
|
boundingRect.origin.y += 4.0
|
||||||
|
boundingRect.size.height -= 8.0
|
||||||
|
|
||||||
|
result.append(DisplayBlockQuote(id: id, boundingRect: boundingRect, kind: .quote, isCollapsed: true, range: range))
|
||||||
|
|
||||||
blockQuoteIndex += 1
|
blockQuoteIndex += 1
|
||||||
}
|
}
|
||||||
@ -735,7 +780,7 @@ private final class ChatInputTextNewInternal: NSObject, ChatInputTextInternal, N
|
|||||||
} else {
|
} else {
|
||||||
let id = nextId
|
let id = nextId
|
||||||
nextId += 1
|
nextId += 1
|
||||||
result[quoteId] = DisplayBlockQuote(id: id, boundingRect: fragmentFrame, attribute: attribute, range: fragmentRange)
|
result[quoteId] = DisplayBlockQuote(id: id, boundingRect: fragmentFrame, kind: attribute.kind, isCollapsed: attribute.isCollapsed, range: fragmentRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -749,6 +794,116 @@ private final class ChatInputTextNewInternal: NSObject, ChatInputTextInternal, N
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let registeredViewProvider: Void = {
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
NSTextAttachment.registerViewProviderClass(ChatInputTextCollapsedQuoteAttachmentImpl.ViewProvider.self, forFileType: "public.data")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment, ChatInputTextCollapsedQuoteAttachment {
|
||||||
|
final class View: UIView {
|
||||||
|
let attachment: ChatInputTextCollapsedQuoteAttachmentImpl
|
||||||
|
let textNode: ImmediateTextNode
|
||||||
|
|
||||||
|
init(attachment: ChatInputTextCollapsedQuoteAttachmentImpl) {
|
||||||
|
self.attachment = attachment
|
||||||
|
self.textNode = ImmediateTextNode()
|
||||||
|
self.textNode.maximumNumberOfLines = 3
|
||||||
|
|
||||||
|
super.init(frame: CGRect())
|
||||||
|
|
||||||
|
self.addSubview(self.textNode.view)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init(coder: NSCoder) {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
static func calculateSize(attachment: ChatInputTextCollapsedQuoteAttachmentImpl, constrainedSize: CGSize) -> CGSize {
|
||||||
|
let renderingText = NSMutableAttributedString(attributedString: attachment.text)
|
||||||
|
renderingText.addAttribute(.font, value: attachment.attributes.font, range: NSRange(location: 0, length: renderingText.length))
|
||||||
|
renderingText.addAttribute(.foregroundColor, value: attachment.attributes.textColor, range: NSRange(location: 0, length: renderingText.length))
|
||||||
|
|
||||||
|
let textNode = ImmediateTextNode()
|
||||||
|
textNode.maximumNumberOfLines = 3
|
||||||
|
|
||||||
|
textNode.attributedText = renderingText
|
||||||
|
textNode.cutout = TextNodeCutout(topRight: CGSize(width: 40.0, height: 10.0))
|
||||||
|
|
||||||
|
let layoutInfo = textNode.updateLayoutFullInfo(CGSize(width: constrainedSize.width - 9.0, height: constrainedSize.height))
|
||||||
|
|
||||||
|
return CGSize(width: constrainedSize.width, height: 8.0 + layoutInfo.size.height + 8.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
let renderingText = NSMutableAttributedString(attributedString: attachment.text)
|
||||||
|
renderingText.addAttribute(.font, value: attachment.attributes.font, range: NSRange(location: 0, length: renderingText.length))
|
||||||
|
renderingText.addAttribute(.foregroundColor, value: attachment.attributes.textColor, range: NSRange(location: 0, length: renderingText.length))
|
||||||
|
|
||||||
|
self.textNode.attributedText = renderingText
|
||||||
|
self.textNode.cutout = TextNodeCutout(topRight: CGSize(width: 10.0, height: 8.0))
|
||||||
|
|
||||||
|
let maxTextSize = CGSize(width: self.bounds.size.width - 9.0, height: self.bounds.size.height)
|
||||||
|
let layoutInfo = self.textNode.updateLayoutFullInfo(maxTextSize)
|
||||||
|
|
||||||
|
self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: 8.0), size: layoutInfo.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 15.0, *)
|
||||||
|
final class ViewProvider: NSTextAttachmentViewProvider {
|
||||||
|
override init(
|
||||||
|
textAttachment: NSTextAttachment,
|
||||||
|
parentView: UIView?,
|
||||||
|
textLayoutManager: NSTextLayoutManager?,
|
||||||
|
location: NSTextLocation
|
||||||
|
) {
|
||||||
|
super.init(textAttachment: textAttachment, parentView: parentView, textLayoutManager: textLayoutManager, location: location)
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func loadView() {
|
||||||
|
if let textAttachment = self.textAttachment as? ChatInputTextCollapsedQuoteAttachmentImpl {
|
||||||
|
self.view = View(attachment: textAttachment)
|
||||||
|
} else {
|
||||||
|
self.view = UIView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let text: NSAttributedString
|
||||||
|
public let attributes: ChatInputTextCollapsedQuoteAttributes
|
||||||
|
|
||||||
|
public init(text: NSAttributedString, attributes: ChatInputTextCollapsedQuoteAttributes) {
|
||||||
|
let _ = registeredViewProvider
|
||||||
|
|
||||||
|
self.text = text
|
||||||
|
self.attributes = attributes
|
||||||
|
|
||||||
|
super.init(data: nil, ofType: "public.data")
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func renderingText() -> NSAttributedString {
|
||||||
|
let result = NSMutableAttributedString(attributedString: self.text)
|
||||||
|
result.addAttribute(.font, value: self.attributes.font, range: NSRange(location: 0, length: result.length))
|
||||||
|
result.addAttribute(.foregroundColor, value: self.attributes.textColor, range: NSRange(location: 0, length: result.length))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
|
||||||
|
return CGRect(origin: CGPoint(), size: View.calculateSize(attachment: self, constrainedSize: CGSize(width: lineFrag.width, height: 10000.0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func image(forBounds imageBounds: CGRect, textContainer: NSTextContainer?, characterIndex charIndex: Int) -> UIImage? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class ChatInputTextView: ChatInputTextViewImpl, UITextViewDelegate, NSLayoutManagerDelegate, NSTextStorageDelegate {
|
public final class ChatInputTextView: ChatInputTextViewImpl, UITextViewDelegate, NSLayoutManagerDelegate, NSTextStorageDelegate {
|
||||||
public final class Theme: Equatable {
|
public final class Theme: Equatable {
|
||||||
public final class Quote: Equatable {
|
public final class Quote: Equatable {
|
||||||
@ -1161,7 +1316,7 @@ public final class ChatInputTextView: ChatInputTextViewImpl, UITextViewDelegate,
|
|||||||
|
|
||||||
blockQuote.frame = displayBlockQuote.boundingRect
|
blockQuote.frame = displayBlockQuote.boundingRect
|
||||||
if let theme = self.theme {
|
if let theme = self.theme {
|
||||||
blockQuote.update(value: displayBlockQuote.attribute, range: displayBlockQuote.range, size: displayBlockQuote.boundingRect.size, theme: theme.quote)
|
blockQuote.update(kind: displayBlockQuote.kind, isCollapsed: displayBlockQuote.isCollapsed, range: displayBlockQuote.range, size: displayBlockQuote.boundingRect.size, theme: theme.quote)
|
||||||
}
|
}
|
||||||
|
|
||||||
validBlockQuotes.append(displayBlockQuote.id)
|
validBlockQuotes.append(displayBlockQuote.id)
|
||||||
@ -1255,7 +1410,7 @@ private final class QuoteBackgroundView: UIView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(value: ChatTextInputTextQuoteAttribute, range: NSRange, size: CGSize, theme: ChatInputTextView.Theme.Quote) {
|
func update(kind: ChatTextInputTextQuoteAttribute.Kind, isCollapsed: Bool, range: NSRange, size: CGSize, theme: ChatInputTextView.Theme.Quote) {
|
||||||
self.range = range
|
self.range = range
|
||||||
|
|
||||||
if self.theme != theme {
|
if self.theme != theme {
|
||||||
@ -1270,7 +1425,7 @@ private final class QuoteBackgroundView: UIView {
|
|||||||
let collapseButtonSize = CGSize(width: 18.0, height: 18.0)
|
let collapseButtonSize = CGSize(width: 18.0, height: 18.0)
|
||||||
self.collapseButton.frame = CGRect(origin: CGPoint(x: size.width - 2.0 - collapseButtonSize.width, y: 2.0), size: collapseButtonSize)
|
self.collapseButton.frame = CGRect(origin: CGPoint(x: size.width - 2.0 - collapseButtonSize.width, y: 2.0), size: collapseButtonSize)
|
||||||
|
|
||||||
if value.isCollapsed {
|
if isCollapsed {
|
||||||
self.collapseButtonIconView.image = quoteExpandImage
|
self.collapseButtonIconView.image = quoteExpandImage
|
||||||
} else {
|
} else {
|
||||||
self.collapseButtonIconView.image = quoteCollapseImage
|
self.collapseButtonIconView.image = quoteCollapseImage
|
||||||
@ -1285,9 +1440,9 @@ private final class QuoteBackgroundView: UIView {
|
|||||||
var tertiaryColor: UIColor?
|
var tertiaryColor: UIColor?
|
||||||
let backgroundColor: UIColor?
|
let backgroundColor: UIColor?
|
||||||
|
|
||||||
switch value.kind {
|
switch kind {
|
||||||
case .quote:
|
case .quote:
|
||||||
if size.height >= 100.0 {
|
if size.height >= 100.0 || isCollapsed {
|
||||||
self.iconView.isHidden = true
|
self.iconView.isHidden = true
|
||||||
self.collapseButton.isHidden = false
|
self.collapseButton.isHidden = false
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,13 +7,12 @@ import ChatMessageItemCommon
|
|||||||
public func chatMessageBubbleImageContentCorners(relativeContentPosition position: ChatMessageBubbleContentPosition, normalRadius: CGFloat, mergedRadius: CGFloat, mergedWithAnotherContentRadius: CGFloat, layoutConstants: ChatMessageItemLayoutConstants, chatPresentationData: ChatPresentationData) -> ImageCorners {
|
public func chatMessageBubbleImageContentCorners(relativeContentPosition position: ChatMessageBubbleContentPosition, normalRadius: CGFloat, mergedRadius: CGFloat, mergedWithAnotherContentRadius: CGFloat, layoutConstants: ChatMessageItemLayoutConstants, chatPresentationData: ChatPresentationData) -> ImageCorners {
|
||||||
let topLeftCorner: ImageCorner
|
let topLeftCorner: ImageCorner
|
||||||
let topRightCorner: ImageCorner
|
let topRightCorner: ImageCorner
|
||||||
|
|
||||||
switch position {
|
switch position {
|
||||||
case let .linear(top, _):
|
case let .linear(top, _):
|
||||||
switch top {
|
switch top {
|
||||||
case .Neighbour:
|
case .Neighbour:
|
||||||
topLeftCorner = .Corner(normalRadius)
|
topLeftCorner = .Corner(mergedWithAnotherContentRadius)
|
||||||
topRightCorner = .Corner(normalRadius)
|
topRightCorner = .Corner(mergedWithAnotherContentRadius)
|
||||||
case .BubbleNeighbour:
|
case .BubbleNeighbour:
|
||||||
topLeftCorner = .Corner(mergedRadius)
|
topLeftCorner = .Corner(mergedRadius)
|
||||||
topRightCorner = .Corner(mergedRadius)
|
topRightCorner = .Corner(mergedRadius)
|
||||||
|
@ -111,7 +111,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
|
|
||||||
private var codeHighlightState: (id: EngineMessage.Id, specs: [CachedMessageSyntaxHighlight.Spec], disposable: Disposable)?
|
private var codeHighlightState: (id: EngineMessage.Id, specs: [CachedMessageSyntaxHighlight.Spec], disposable: Disposable)?
|
||||||
|
|
||||||
private var collapsedBlockIds: Set<Int> = Set()
|
private var expandedBlockIds: Set<Int> = Set()
|
||||||
|
|
||||||
override public var visibility: ListViewItemNodeVisibility {
|
override public var visibility: ListViewItemNodeVisibility {
|
||||||
didSet {
|
didSet {
|
||||||
@ -155,13 +155,25 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
guard let self, let item = self.item else {
|
guard let self, let item = self.item else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.collapsedBlockIds.contains(blockId) {
|
if self.expandedBlockIds.contains(blockId) {
|
||||||
self.collapsedBlockIds.remove(blockId)
|
self.expandedBlockIds.remove(blockId)
|
||||||
} else {
|
} else {
|
||||||
self.collapsedBlockIds.insert(blockId)
|
self.expandedBlockIds.insert(blockId)
|
||||||
}
|
}
|
||||||
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
|
item.controllerInteraction.requestMessageUpdate(item.message.id, false)
|
||||||
}
|
}
|
||||||
|
self.textNode.textNode.canHandleTapAtPoint = { [weak self] point in
|
||||||
|
guard let self else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let localPoint = self.textNode.textNode.view.convert(point, to: self.view)
|
||||||
|
let action = self.tapActionAtPoint(localPoint, gesture: .tap, isEstimating: true)
|
||||||
|
if case .none = action.content {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init?(coder aDecoder: NSCoder) {
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
@ -179,7 +191,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
let statusLayout = ChatMessageDateAndStatusNode.asyncLayout(self.statusNode)
|
let statusLayout = ChatMessageDateAndStatusNode.asyncLayout(self.statusNode)
|
||||||
|
|
||||||
let currentCachedChatMessageText = self.cachedChatMessageText
|
let currentCachedChatMessageText = self.cachedChatMessageText
|
||||||
let collapsedBlockIds = self.collapsedBlockIds
|
let expandedBlockIds = self.expandedBlockIds
|
||||||
|
|
||||||
return { item, layoutConstants, _, _, _, _ in
|
return { item, layoutConstants, _, _, _, _ in
|
||||||
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
|
||||||
@ -434,7 +446,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
var codeHighlightSpecs: [CachedMessageSyntaxHighlight.Spec] = []
|
var codeHighlightSpecs: [CachedMessageSyntaxHighlight.Spec] = []
|
||||||
var cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight?
|
var cachedMessageSyntaxHighlight: CachedMessageSyntaxHighlight?
|
||||||
|
|
||||||
if let entities = entities {
|
if let entities {
|
||||||
var underlineLinks = true
|
var underlineLinks = true
|
||||||
if !messageTheme.primaryTextColor.isEqual(messageTheme.linkTextColor) {
|
if !messageTheme.primaryTextColor.isEqual(messageTheme.linkTextColor) {
|
||||||
underlineLinks = false
|
underlineLinks = false
|
||||||
@ -569,7 +581,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
lineColor: messageTheme.accentControlColor,
|
lineColor: messageTheme.accentControlColor,
|
||||||
displayContentsUnderSpoilers: false,
|
displayContentsUnderSpoilers: false,
|
||||||
customTruncationToken: customTruncationToken,
|
customTruncationToken: customTruncationToken,
|
||||||
collapsedBlocks: collapsedBlockIds
|
expandedBlocks: expandedBlockIds
|
||||||
))
|
))
|
||||||
|
|
||||||
var statusSuggestedWidthAndContinue: (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageDateAndStatusNode))?
|
var statusSuggestedWidthAndContinue: (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageDateAndStatusNode))?
|
||||||
|
@ -271,7 +271,7 @@ public final class InteractiveTextNodeLayoutArguments {
|
|||||||
public let textStroke: (UIColor, CGFloat)?
|
public let textStroke: (UIColor, CGFloat)?
|
||||||
public let displayContentsUnderSpoilers: Bool
|
public let displayContentsUnderSpoilers: Bool
|
||||||
public let customTruncationToken: NSAttributedString?
|
public let customTruncationToken: NSAttributedString?
|
||||||
public let collapsedBlocks: Set<Int>
|
public let expandedBlocks: Set<Int>
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
attributedString: NSAttributedString?,
|
attributedString: NSAttributedString?,
|
||||||
@ -291,7 +291,7 @@ public final class InteractiveTextNodeLayoutArguments {
|
|||||||
textStroke: (UIColor, CGFloat)? = nil,
|
textStroke: (UIColor, CGFloat)? = nil,
|
||||||
displayContentsUnderSpoilers: Bool = false,
|
displayContentsUnderSpoilers: Bool = false,
|
||||||
customTruncationToken: NSAttributedString? = nil,
|
customTruncationToken: NSAttributedString? = nil,
|
||||||
collapsedBlocks: Set<Int> = Set()
|
expandedBlocks: Set<Int> = Set()
|
||||||
) {
|
) {
|
||||||
self.attributedString = attributedString
|
self.attributedString = attributedString
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
@ -310,7 +310,7 @@ public final class InteractiveTextNodeLayoutArguments {
|
|||||||
self.textStroke = textStroke
|
self.textStroke = textStroke
|
||||||
self.displayContentsUnderSpoilers = displayContentsUnderSpoilers
|
self.displayContentsUnderSpoilers = displayContentsUnderSpoilers
|
||||||
self.customTruncationToken = customTruncationToken
|
self.customTruncationToken = customTruncationToken
|
||||||
self.collapsedBlocks = collapsedBlocks
|
self.expandedBlocks = expandedBlocks
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withAttributedString(_ attributedString: NSAttributedString?) -> InteractiveTextNodeLayoutArguments {
|
public func withAttributedString(_ attributedString: NSAttributedString?) -> InteractiveTextNodeLayoutArguments {
|
||||||
@ -332,7 +332,7 @@ public final class InteractiveTextNodeLayoutArguments {
|
|||||||
textStroke: self.textStroke,
|
textStroke: self.textStroke,
|
||||||
displayContentsUnderSpoilers: self.displayContentsUnderSpoilers,
|
displayContentsUnderSpoilers: self.displayContentsUnderSpoilers,
|
||||||
customTruncationToken: self.customTruncationToken,
|
customTruncationToken: self.customTruncationToken,
|
||||||
collapsedBlocks: self.collapsedBlocks
|
expandedBlocks: self.expandedBlocks
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,7 +394,7 @@ public final class InteractiveTextNodeLayout: NSObject {
|
|||||||
fileprivate let textShadowBlur: CGFloat?
|
fileprivate let textShadowBlur: CGFloat?
|
||||||
fileprivate let textStroke: (UIColor, CGFloat)?
|
fileprivate let textStroke: (UIColor, CGFloat)?
|
||||||
fileprivate let displayContentsUnderSpoilers: Bool
|
fileprivate let displayContentsUnderSpoilers: Bool
|
||||||
fileprivate let collapsedBlocks: Set<Int>
|
fileprivate let expandedBlocks: Set<Int>
|
||||||
|
|
||||||
fileprivate init(
|
fileprivate init(
|
||||||
attributedString: NSAttributedString?,
|
attributedString: NSAttributedString?,
|
||||||
@ -418,7 +418,7 @@ public final class InteractiveTextNodeLayout: NSObject {
|
|||||||
textShadowBlur: CGFloat?,
|
textShadowBlur: CGFloat?,
|
||||||
textStroke: (UIColor, CGFloat)?,
|
textStroke: (UIColor, CGFloat)?,
|
||||||
displayContentsUnderSpoilers: Bool,
|
displayContentsUnderSpoilers: Bool,
|
||||||
collapsedBlocks: Set<Int>
|
expandedBlocks: Set<Int>
|
||||||
) {
|
) {
|
||||||
self.attributedString = attributedString
|
self.attributedString = attributedString
|
||||||
self.maximumNumberOfLines = maximumNumberOfLines
|
self.maximumNumberOfLines = maximumNumberOfLines
|
||||||
@ -441,7 +441,7 @@ public final class InteractiveTextNodeLayout: NSObject {
|
|||||||
self.textShadowBlur = textShadowBlur
|
self.textShadowBlur = textShadowBlur
|
||||||
self.textStroke = textStroke
|
self.textStroke = textStroke
|
||||||
self.displayContentsUnderSpoilers = displayContentsUnderSpoilers
|
self.displayContentsUnderSpoilers = displayContentsUnderSpoilers
|
||||||
self.collapsedBlocks = collapsedBlocks
|
self.expandedBlocks = expandedBlocks
|
||||||
}
|
}
|
||||||
|
|
||||||
public var numberOfLines: Int {
|
public var numberOfLines: Int {
|
||||||
@ -1048,7 +1048,7 @@ private func addAttachment(attachment: UIImage, line: InteractiveTextNodeLine, a
|
|||||||
line.attachments.append(InteractiveTextNodeAttachment(range: NSMakeRange(startIndex, endIndex - startIndex), frame: CGRect(x: min(leftOffset, rightOffset), y: descent - (ascent + descent), width: abs(rightOffset - leftOffset) + rightInset, height: ascent + descent), attachment: attachment))
|
line.attachments.append(InteractiveTextNodeAttachment(range: NSMakeRange(startIndex, endIndex - startIndex), frame: CGRect(x: min(leftOffset, rightOffset), y: descent - (ascent + descent), width: abs(rightOffset - leftOffset) + rightInset, height: ascent + descent), attachment: attachment))
|
||||||
}
|
}
|
||||||
|
|
||||||
open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecognizerDelegate {
|
||||||
public struct RenderContentTypes: OptionSet {
|
public struct RenderContentTypes: OptionSet {
|
||||||
public var rawValue: Int
|
public var rawValue: Int
|
||||||
|
|
||||||
@ -1078,6 +1078,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
public var renderContentTypes: RenderContentTypes = .all
|
public var renderContentTypes: RenderContentTypes = .all
|
||||||
private var contentItemLayers: [Int: TextContentItemLayer] = [:]
|
private var contentItemLayers: [Int: TextContentItemLayer] = [:]
|
||||||
|
|
||||||
|
public var canHandleTapAtPoint: ((CGPoint) -> Bool)?
|
||||||
public var requestToggleBlockCollapsed: ((Int) -> Void)?
|
public var requestToggleBlockCollapsed: ((Int) -> Void)?
|
||||||
private var tapRecognizer: UITapGestureRecognizer?
|
private var tapRecognizer: UITapGestureRecognizer?
|
||||||
|
|
||||||
@ -1138,6 +1139,13 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
guard let canHandleTapAtPoint = self.canHandleTapAtPoint else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !canHandleTapAtPoint(point) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
guard let result = super.hitTest(point, with: event) else {
|
guard let result = super.hitTest(point, with: event) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1198,7 +1206,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
textStroke: (UIColor, CGFloat)?,
|
textStroke: (UIColor, CGFloat)?,
|
||||||
displayContentsUnderSpoilers: Bool,
|
displayContentsUnderSpoilers: Bool,
|
||||||
customTruncationToken: NSAttributedString?,
|
customTruncationToken: NSAttributedString?,
|
||||||
collapsedBlocks: Set<Int>
|
expandedBlocks: Set<Int>
|
||||||
) -> InteractiveTextNodeLayout {
|
) -> InteractiveTextNodeLayout {
|
||||||
let blockQuoteLeftInset: CGFloat = 9.0
|
let blockQuoteLeftInset: CGFloat = 9.0
|
||||||
let blockQuoteRightInset: CGFloat = 0.0
|
let blockQuoteRightInset: CGFloat = 0.0
|
||||||
@ -1459,7 +1467,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
blockIndex = blockIndexValue
|
blockIndex = blockIndexValue
|
||||||
nextBlockIndex += 1
|
nextBlockIndex += 1
|
||||||
if blockQuote.isCollapsible {
|
if blockQuote.isCollapsible {
|
||||||
isCollapsed = collapsedBlocks.contains(blockIndexValue)
|
isCollapsed = !expandedBlocks.contains(blockIndexValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1550,6 +1558,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
if firstLineOffset == nil, let firstLine = segmentLines.first {
|
if firstLineOffset == nil, let firstLine = segmentLines.first {
|
||||||
firstLineOffset = firstLine.descent
|
firstLineOffset = firstLine.descent
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !isCollapsed, let blockQuote = segment.blockQuote, blockQuote.isCollapsible, !segment.lines.isEmpty {
|
if !isCollapsed, let blockQuote = segment.blockQuote, blockQuote.isCollapsible, !segment.lines.isEmpty {
|
||||||
let lastLine = segment.lines[segment.lines.count - 1]
|
let lastLine = segment.lines[segment.lines.count - 1]
|
||||||
@ -1561,7 +1570,6 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
effectiveSegmentHeight += 10.0
|
effectiveSegmentHeight += 10.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
segmentHeight = ceil(segmentHeight)
|
segmentHeight = ceil(segmentHeight)
|
||||||
effectiveSegmentHeight = ceil(effectiveSegmentHeight)
|
effectiveSegmentHeight = ceil(effectiveSegmentHeight)
|
||||||
@ -1626,16 +1634,16 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
textShadowBlur: textShadowBlur,
|
textShadowBlur: textShadowBlur,
|
||||||
textStroke: textStroke,
|
textStroke: textStroke,
|
||||||
displayContentsUnderSpoilers: displayContentsUnderSpoilers,
|
displayContentsUnderSpoilers: displayContentsUnderSpoilers,
|
||||||
collapsedBlocks: collapsedBlocks
|
expandedBlocks: expandedBlocks
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textShadowBlur: CGFloat?, textStroke: (UIColor, CGFloat)?, displayContentsUnderSpoilers: Bool, customTruncationToken: NSAttributedString?, collapsedBlocks: Set<Int>) -> InteractiveTextNodeLayout {
|
static func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textShadowBlur: CGFloat?, textStroke: (UIColor, CGFloat)?, displayContentsUnderSpoilers: Bool, customTruncationToken: NSAttributedString?, expandedBlocks: Set<Int>) -> InteractiveTextNodeLayout {
|
||||||
guard let attributedString else {
|
guard let attributedString else {
|
||||||
return InteractiveTextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, segments: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textShadowBlur: textShadowBlur, textStroke: textStroke, displayContentsUnderSpoilers: displayContentsUnderSpoilers, collapsedBlocks: collapsedBlocks)
|
return InteractiveTextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, segments: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textShadowBlur: textShadowBlur, textStroke: textStroke, displayContentsUnderSpoilers: displayContentsUnderSpoilers, expandedBlocks: expandedBlocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
return calculateLayoutV2(attributedString: attributedString, minimumNumberOfLines: minimumNumberOfLines, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, backgroundColor: backgroundColor, constrainedSize: constrainedSize, alignment: alignment, verticalAlignment: verticalAlignment, lineSpacingFactor: lineSpacingFactor, cutout: cutout, insets: insets, lineColor: lineColor, textShadowColor: textShadowColor, textShadowBlur: textShadowBlur, textStroke: textStroke, displayContentsUnderSpoilers: displayContentsUnderSpoilers, customTruncationToken: customTruncationToken, collapsedBlocks: collapsedBlocks)
|
return calculateLayoutV2(attributedString: attributedString, minimumNumberOfLines: minimumNumberOfLines, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, backgroundColor: backgroundColor, constrainedSize: constrainedSize, alignment: alignment, verticalAlignment: verticalAlignment, lineSpacingFactor: lineSpacingFactor, cutout: cutout, insets: insets, lineColor: lineColor, textShadowColor: textShadowColor, textShadowBlur: textShadowBlur, textStroke: textStroke, displayContentsUnderSpoilers: displayContentsUnderSpoilers, customTruncationToken: customTruncationToken, expandedBlocks: expandedBlocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateContentItems(animation: ListViewItemUpdateAnimation) {
|
private func updateContentItems(animation: ListViewItemUpdateAnimation) {
|
||||||
@ -1716,6 +1724,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapGesture(_:)))
|
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapGesture(_:)))
|
||||||
self.tapRecognizer = tapRecognizer
|
self.tapRecognizer = tapRecognizer
|
||||||
self.view.addGestureRecognizer(tapRecognizer)
|
self.view.addGestureRecognizer(tapRecognizer)
|
||||||
|
tapRecognizer.delegate = self
|
||||||
}
|
}
|
||||||
} else if let tapRecognizer = self.tapRecognizer {
|
} else if let tapRecognizer = self.tapRecognizer {
|
||||||
self.tapRecognizer = nil
|
self.tapRecognizer = nil
|
||||||
@ -1723,6 +1732,10 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||||
if case .ended = recognizer.state {
|
if case .ended = recognizer.state {
|
||||||
let point = recognizer.location(in: self.view)
|
let point = recognizer.location(in: self.view)
|
||||||
@ -1738,7 +1751,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
return { arguments in
|
return { arguments in
|
||||||
let layout: InteractiveTextNodeLayout
|
let layout: InteractiveTextNodeLayout
|
||||||
|
|
||||||
if let existingLayout = existingLayout, existingLayout.constrainedSize == arguments.constrainedSize && existingLayout.maximumNumberOfLines == arguments.maximumNumberOfLines && existingLayout.truncationType == arguments.truncationType && existingLayout.cutout == arguments.cutout && existingLayout.explicitAlignment == arguments.alignment && existingLayout.lineSpacing.isEqual(to: arguments.lineSpacing) && existingLayout.collapsedBlocks == arguments.collapsedBlocks {
|
if let existingLayout = existingLayout, existingLayout.constrainedSize == arguments.constrainedSize && existingLayout.maximumNumberOfLines == arguments.maximumNumberOfLines && existingLayout.truncationType == arguments.truncationType && existingLayout.cutout == arguments.cutout && existingLayout.explicitAlignment == arguments.alignment && existingLayout.lineSpacing.isEqual(to: arguments.lineSpacing) && existingLayout.expandedBlocks == arguments.expandedBlocks {
|
||||||
let stringMatch: Bool
|
let stringMatch: Bool
|
||||||
|
|
||||||
var colorMatch: Bool = true
|
var colorMatch: Bool = true
|
||||||
@ -1763,10 +1776,10 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol {
|
|||||||
if stringMatch {
|
if stringMatch {
|
||||||
layout = existingLayout
|
layout = existingLayout
|
||||||
} else {
|
} else {
|
||||||
layout = InteractiveTextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displayContentsUnderSpoilers: arguments.displayContentsUnderSpoilers, customTruncationToken: arguments.customTruncationToken, collapsedBlocks: arguments.collapsedBlocks)
|
layout = InteractiveTextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displayContentsUnderSpoilers: arguments.displayContentsUnderSpoilers, customTruncationToken: arguments.customTruncationToken, expandedBlocks: arguments.expandedBlocks)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
layout = InteractiveTextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displayContentsUnderSpoilers: arguments.displayContentsUnderSpoilers, customTruncationToken: arguments.customTruncationToken, collapsedBlocks: arguments.collapsedBlocks)
|
layout = InteractiveTextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displayContentsUnderSpoilers: arguments.displayContentsUnderSpoilers, customTruncationToken: arguments.customTruncationToken, expandedBlocks: arguments.expandedBlocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = maybeNode ?? InteractiveTextNode()
|
let node = maybeNode ?? InteractiveTextNode()
|
||||||
|
@ -314,13 +314,17 @@ public final class TextFieldComponent: Component {
|
|||||||
let inputState = f(self.inputState)
|
let inputState = f(self.inputState)
|
||||||
|
|
||||||
let currentAttributedText = self.textView.attributedText
|
let currentAttributedText = self.textView.attributedText
|
||||||
let updatedAttributedText = textAttributedStringForStateText(inputState.inputText, fontSize: component.fontSize, textColor: component.textColor, accentTextColor: component.accentColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
let updatedAttributedText = textAttributedStringForStateText(inputState.inputText, fontSize: component.fontSize, textColor: component.textColor, accentTextColor: component.accentColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
if currentAttributedText != updatedAttributedText {
|
if currentAttributedText != updatedAttributedText {
|
||||||
self.textView.attributedText = updatedAttributedText
|
self.textView.attributedText = updatedAttributedText
|
||||||
}
|
}
|
||||||
self.textView.selectedRange = NSMakeRange(inputState.selectionRange.lowerBound, inputState.selectionRange.count)
|
self.textView.selectedRange = NSMakeRange(inputState.selectionRange.lowerBound, inputState.selectionRange.count)
|
||||||
|
|
||||||
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
|
|
||||||
self.updateEntities()
|
self.updateEntities()
|
||||||
|
|
||||||
@ -452,7 +456,9 @@ public final class TextFieldComponent: Component {
|
|||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize)
|
refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize)
|
||||||
self.textView.updateTextContainerInset()
|
self.textView.updateTextContainerInset()
|
||||||
|
|
||||||
@ -957,7 +963,9 @@ public final class TextFieldComponent: Component {
|
|||||||
|
|
||||||
self.textView.isScrollEnabled = false
|
self.textView.isScrollEnabled = false
|
||||||
|
|
||||||
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider)
|
refreshChatTextInputAttributes(textView: self.textView, primaryTextColor: component.textColor, accentTextColor: component.accentColor, baseFontSize: component.fontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize)
|
refreshChatTextInputTypingAttributes(self.textView, textColor: component.textColor, baseFontSize: component.fontSize)
|
||||||
|
|
||||||
if self.textView.subviews.count > 1, animated {
|
if self.textView.subviews.count > 1, animated {
|
||||||
|
@ -731,11 +731,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
||||||
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||||
}
|
}
|
||||||
textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider)
|
textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count)
|
textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count)
|
||||||
|
|
||||||
if let presentationInterfaceState = self.presentationInterfaceState {
|
if let presentationInterfaceState = self.presentationInterfaceState {
|
||||||
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider)
|
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updatingInputState = false
|
self.updatingInputState = false
|
||||||
@ -1205,15 +1209,45 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
guard let presentationInterfaceState = self.presentationInterfaceState else {
|
||||||
let result = NSMutableAttributedString(attributedString: current.inputText)
|
return
|
||||||
|
|
||||||
if let current = result.attribute(ChatTextInputAttributes.block, at: range.lowerBound, effectiveRange: nil) as? ChatTextInputTextQuoteAttribute {
|
|
||||||
result.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: current.kind, isCollapsed: !current.isCollapsed), range: range)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor
|
||||||
|
let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
||||||
|
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||||
|
|
||||||
|
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||||
|
let textAttributedString = textAttributedStringForStateText(
|
||||||
|
current.inputText,
|
||||||
|
fontSize: baseFontSize,
|
||||||
|
textColor: textColor,
|
||||||
|
accentTextColor: accentTextColor,
|
||||||
|
writingDirection: nil,
|
||||||
|
spoilersRevealed: self.spoilersRevealed,
|
||||||
|
availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(),
|
||||||
|
emojiViewProvider: self.emojiViewProvider,
|
||||||
|
makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
let result = NSMutableAttributedString(attributedString: textAttributedString)
|
||||||
|
|
||||||
|
var effectiveRange: NSRange = NSRange(location: NSNotFound, length: 0)
|
||||||
|
if let current = result.attribute(ChatTextInputAttributes.block, at: range.lowerBound, effectiveRange: &effectiveRange) as? ChatTextInputTextQuoteAttribute {
|
||||||
|
result.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: current.kind, isCollapsed: !current.isCollapsed), range: effectiveRange)
|
||||||
|
} else if let current = result.attribute(.attachment, at: range.lowerBound, effectiveRange: &effectiveRange) as? ChatInputTextCollapsedQuoteAttachment {
|
||||||
|
result.replaceCharacters(in: effectiveRange, with: "")
|
||||||
|
let updatedQuote = NSMutableAttributedString(attributedString: current.text)
|
||||||
|
updatedQuote.removeAttribute(ChatTextInputAttributes.block, range: NSRange(location: 0, length: updatedQuote.length))
|
||||||
|
updatedQuote.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote, isCollapsed: false), range: NSRange(location: 0, length: updatedQuote.length))
|
||||||
|
result.insert(updatedQuote, at: effectiveRange.lowerBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
let stateResult = stateAttributedStringForText(result)
|
||||||
|
|
||||||
return (ChatTextInputState(
|
return (ChatTextInputState(
|
||||||
inputText: result,
|
inputText: stateResult,
|
||||||
selectionRange: current.selectionRange
|
selectionRange: current.selectionRange
|
||||||
), inputMode)
|
), inputMode)
|
||||||
}
|
}
|
||||||
@ -2912,7 +2946,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
func chatInputTextNodeDidUpdateText() {
|
func chatInputTextNodeDidUpdateText() {
|
||||||
if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState {
|
if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState {
|
||||||
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||||
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider)
|
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||||
|
|
||||||
self.updateSpoiler()
|
self.updateSpoiler()
|
||||||
@ -3082,9 +3118,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
|
|
||||||
textInputNode.textView.isScrollEnabled = false
|
textInputNode.textView.isScrollEnabled = false
|
||||||
|
|
||||||
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider)
|
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
|
|
||||||
textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider)
|
textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
|
|
||||||
if textInputNode.textView.subviews.count > 1, animated {
|
if textInputNode.textView.subviews.count > 1, animated {
|
||||||
let containerView = textInputNode.textView.subviews[1]
|
let containerView = textInputNode.textView.subviews[1]
|
||||||
@ -4340,7 +4380,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor
|
||||||
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||||
}
|
}
|
||||||
let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider)
|
let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: (self.context?.animatedEmojiStickersValue.keys).flatMap(Set.init) ?? Set(), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in
|
||||||
|
return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes)
|
||||||
|
})
|
||||||
string.replaceCharacters(in: range, with: cleanReplacementString)
|
string.replaceCharacters(in: range, with: cleanReplacementString)
|
||||||
self.textInputNode?.attributedText = string
|
self.textInputNode?.attributedText = string
|
||||||
self.textInputNode?.selectedRange = NSMakeRange(range.lowerBound + cleanReplacementString.length, 0)
|
self.textInputNode?.selectedRange = NSMakeRange(range.lowerBound + cleanReplacementString.length, 0)
|
||||||
|
@ -35,6 +35,37 @@ public final class OriginalTextAttribute: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class ChatInputTextCollapsedQuoteAttributes: Equatable {
|
||||||
|
public let font: UIFont
|
||||||
|
public let textColor: UIColor
|
||||||
|
|
||||||
|
public init(
|
||||||
|
font: UIFont,
|
||||||
|
textColor: UIColor
|
||||||
|
) {
|
||||||
|
self.font = font
|
||||||
|
self.textColor = textColor
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: ChatInputTextCollapsedQuoteAttributes, rhs: ChatInputTextCollapsedQuoteAttributes) -> Bool {
|
||||||
|
if lhs === rhs {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !lhs.font.isEqual(rhs.font) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !lhs.textColor.isEqual(rhs.textColor) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol ChatInputTextCollapsedQuoteAttachment: NSTextAttachment {
|
||||||
|
var text: NSAttributedString { get }
|
||||||
|
}
|
||||||
|
|
||||||
public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedString {
|
public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttributedString {
|
||||||
let sourceString = NSMutableAttributedString(attributedString: text)
|
let sourceString = NSMutableAttributedString(attributedString: text)
|
||||||
while true {
|
while true {
|
||||||
@ -45,6 +76,10 @@ public func stateAttributedStringForText(_ text: NSAttributedString) -> NSAttrib
|
|||||||
sourceString.replaceCharacters(in: range, with: NSAttributedString(string: value.text, attributes: [ChatTextInputAttributes.customEmoji: value.emoji]))
|
sourceString.replaceCharacters(in: range, with: NSAttributedString(string: value.text, attributes: [ChatTextInputAttributes.customEmoji: value.emoji]))
|
||||||
stop.pointee = true
|
stop.pointee = true
|
||||||
found = true
|
found = true
|
||||||
|
} else if let value = value as? ChatInputTextCollapsedQuoteAttachment {
|
||||||
|
sourceString.replaceCharacters(in: range, with: value.text)
|
||||||
|
stop.pointee = true
|
||||||
|
found = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if !found {
|
if !found {
|
||||||
@ -102,7 +137,33 @@ public struct ChatTextFontAttributes: OptionSet, Hashable, Sequence {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor, writingDirection: NSWritingDirection?, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?) -> NSAttributedString {
|
public func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor, writingDirection: NSWritingDirection?, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) -> NSAttributedString {
|
||||||
|
let quoteAttributes = ChatInputTextCollapsedQuoteAttributes(
|
||||||
|
font: Font.regular(round(fontSize * 0.8235294117647058)),
|
||||||
|
textColor: textColor
|
||||||
|
)
|
||||||
|
|
||||||
|
let stateText = NSMutableAttributedString(attributedString: stateText)
|
||||||
|
|
||||||
|
while true {
|
||||||
|
var found = false
|
||||||
|
stateText.enumerateAttribute(ChatTextInputAttributes.block, in: NSRange(location: 0, length: stateText.length), options: [.longestEffectiveRangeNotRequired], using: { value, range, stop in
|
||||||
|
if let value = value as? ChatTextInputTextQuoteAttribute {
|
||||||
|
if value.isCollapsed, let makeCollapsedQuoteAttachment {
|
||||||
|
found = true
|
||||||
|
stop.pointee = true
|
||||||
|
|
||||||
|
let quoteText = stateText.attributedSubstring(from: range)
|
||||||
|
stateText.replaceCharacters(in: range, with: "")
|
||||||
|
stateText.insert(NSAttributedString(attachment: makeCollapsedQuoteAttachment(quoteText, quoteAttributes)), at: range.lowerBound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if !found {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let result = NSMutableAttributedString(string: stateText.string)
|
let result = NSMutableAttributedString(string: stateText.string)
|
||||||
let fullRange = NSRange(location: 0, length: result.length)
|
let fullRange = NSRange(location: 0, length: result.length)
|
||||||
|
|
||||||
@ -157,6 +218,8 @@ public func textAttributedStringForStateText(_ stateText: NSAttributedString, fo
|
|||||||
fontAttributes.insert(.monospace)
|
fontAttributes.insert(.monospace)
|
||||||
}
|
}
|
||||||
result.addAttribute(key, value: value, range: range)
|
result.addAttribute(key, value: value, range: range)
|
||||||
|
} else if key == .attachment, value is ChatInputTextCollapsedQuoteAttachment {
|
||||||
|
result.addAttribute(key, value: value, range: range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,11 +719,11 @@ private func refreshBlockQuotes(text: NSString, initialAttributedText: NSAttribu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func refreshChatTextInputAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?) {
|
public func refreshChatTextInputAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) {
|
||||||
refreshChatTextInputAttributes(textView: textView, primaryTextColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, baseFontSize: baseFontSize, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
|
refreshChatTextInputAttributes(textView: textView, primaryTextColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, baseFontSize: baseFontSize, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColor: UIColor, accentTextColor: UIColor, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?) {
|
public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColor: UIColor, accentTextColor: UIColor, baseFontSize: CGFloat, spoilersRevealed: Bool, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?) {
|
||||||
guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
|
guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -677,21 +740,21 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
|
|||||||
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText))
|
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText))
|
||||||
refreshTextMentions(text: text, initialAttributedText: initialAttributedText, attributedText: attributedText, fullRange: fullRange)
|
refreshTextMentions(text: text, initialAttributedText: initialAttributedText, attributedText: attributedText, fullRange: fullRange)
|
||||||
|
|
||||||
var resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
|
var resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
|
||||||
|
|
||||||
text = resultAttributedText.string as NSString
|
text = resultAttributedText.string as NSString
|
||||||
fullRange = NSRange(location: 0, length: text.length)
|
fullRange = NSRange(location: 0, length: text.length)
|
||||||
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
|
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
|
||||||
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
|
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
|
||||||
|
|
||||||
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
|
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
|
||||||
|
|
||||||
text = resultAttributedText.string as NSString
|
text = resultAttributedText.string as NSString
|
||||||
fullRange = NSRange(location: 0, length: text.length)
|
fullRange = NSRange(location: 0, length: text.length)
|
||||||
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
|
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
|
||||||
refreshBlockQuotes(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
|
refreshBlockQuotes(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
|
||||||
|
|
||||||
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider)
|
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: primaryTextColor, accentTextColor: accentTextColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
|
||||||
|
|
||||||
if !resultAttributedText.isEqual(to: initialAttributedText) {
|
if !resultAttributedText.isEqual(to: initialAttributedText) {
|
||||||
fullRange = NSRange(location: 0, length: textView.textStorage.length)
|
fullRange = NSRange(location: 0, length: textView.textStorage.length)
|
||||||
@ -750,6 +813,7 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
|
|||||||
textView.textStorage.addAttribute(key, value: value, range: range)
|
textView.textStorage.addAttribute(key, value: value, range: range)
|
||||||
textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.clear, range: range)
|
textView.textStorage.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.clear, range: range)
|
||||||
} else if key == ChatTextInputAttributes.block, let value = value as? ChatTextInputTextQuoteAttribute {
|
} else if key == ChatTextInputAttributes.block, let value = value as? ChatTextInputTextQuoteAttribute {
|
||||||
|
if !value.isCollapsed {
|
||||||
switch value.kind {
|
switch value.kind {
|
||||||
case .quote:
|
case .quote:
|
||||||
fontAttributes.insert(.blockQuote)
|
fontAttributes.insert(.blockQuote)
|
||||||
@ -759,6 +823,7 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
|
|||||||
textView.textStorage.addAttribute(key, value: value, range: range)
|
textView.textStorage.addAttribute(key, value: value, range: range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !fontAttributes.isEmpty {
|
if !fontAttributes.isEmpty {
|
||||||
var font: UIFont?
|
var font: UIFont?
|
||||||
@ -799,7 +864,7 @@ public func refreshChatTextInputAttributes(textView: UITextView, primaryTextColo
|
|||||||
textView.textStorage.endEditing()
|
textView.textStorage.endEditing()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func refreshGenericTextInputAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, spoilersRevealed: Bool = false) {
|
public func refreshGenericTextInputAttributes(_ textView: UITextView, theme: PresentationTheme, baseFontSize: CGFloat, availableEmojis: Set<String>, emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?, makeCollapsedQuoteAttachment: ((NSAttributedString, ChatInputTextCollapsedQuoteAttributes) -> ChatInputTextCollapsedQuoteAttachment)?, spoilersRevealed: Bool = false) {
|
||||||
guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
|
guard let initialAttributedText = textView.attributedText, initialAttributedText.length != 0 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -812,14 +877,14 @@ public func refreshGenericTextInputAttributes(_ textView: UITextView, theme: Pre
|
|||||||
var text: NSString = initialAttributedText.string as NSString
|
var text: NSString = initialAttributedText.string as NSString
|
||||||
var fullRange = NSRange(location: 0, length: initialAttributedText.length)
|
var fullRange = NSRange(location: 0, length: initialAttributedText.length)
|
||||||
var attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(initialAttributedText))
|
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)
|
var resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
|
||||||
|
|
||||||
text = resultAttributedText.string as NSString
|
text = resultAttributedText.string as NSString
|
||||||
fullRange = NSRange(location: 0, length: initialAttributedText.length)
|
fullRange = NSRange(location: 0, length: initialAttributedText.length)
|
||||||
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
|
attributedText = NSMutableAttributedString(attributedString: stateAttributedStringForText(resultAttributedText))
|
||||||
refreshTextUrls(text: text, initialAttributedText: resultAttributedText, attributedText: attributedText, fullRange: fullRange)
|
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)
|
resultAttributedText = textAttributedStringForStateText(attributedText, fontSize: baseFontSize, textColor: theme.chat.inputPanel.primaryTextColor, accentTextColor: theme.chat.inputPanel.panelControlAccentColor, writingDirection: writingDirection, spoilersRevealed: spoilersRevealed, availableEmojis: availableEmojis, emojiViewProvider: emojiViewProvider, makeCollapsedQuoteAttachment: makeCollapsedQuoteAttachment)
|
||||||
|
|
||||||
if !resultAttributedText.isEqual(to: initialAttributedText) {
|
if !resultAttributedText.isEqual(to: initialAttributedText) {
|
||||||
textView.textStorage.removeAttribute(NSAttributedString.Key.font, range: fullRange)
|
textView.textStorage.removeAttribute(NSAttributedString.Key.font, range: fullRange)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user