Quote improvements

This commit is contained in:
Isaac 2024-05-24 18:10:30 +04:00
parent 16b6083b6e
commit 17d9d6caf3
7 changed files with 82 additions and 30 deletions

View File

@ -550,18 +550,24 @@ public struct ChatTextInputStateText: Codable, Equatable {
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .underline, range: range.location ..< (range.location + range.length)))
} else if key == ChatTextInputAttributes.spoiler {
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .spoiler, range: range.location ..< (range.location + range.length)))
} else if key == ChatTextInputAttributes.block, let value = value as? ChatTextInputTextQuoteAttribute {
switch value.kind {
case .quote:
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .quote(isCollapsed: value.isCollapsed), range: range.location ..< (range.location + range.length)))
case let .code(language):
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .codeBlock(language: language), range: range.location ..< (range.location + range.length)))
}
} else if key == ChatTextInputAttributes.collapsedBlock, let value = value as? NSAttributedString {
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .collapsedQuote(text: ChatTextInputStateText(attributedText: value)), range: range.location ..< (range.location + range.length)))
}
}
})
attributedText.enumerateAttribute(ChatTextInputAttributes.block, in: NSRange(location: 0, length: attributedText.length), options: [], using: { value, range, _ in
if let value = value as? ChatTextInputTextQuoteAttribute {
switch value.kind {
case .quote:
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .quote(isCollapsed: value.isCollapsed), range: range.location ..< (range.location + range.length)))
case let .code(language):
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .codeBlock(language: language), range: range.location ..< (range.location + range.length)))
}
}
})
attributedText.enumerateAttribute(ChatTextInputAttributes.collapsedBlock, in: NSRange(location: 0, length: attributedText.length), options: [], using: { value, range, _ in
if let value = value as? NSAttributedString {
parsedAttributes.append(ChatTextInputStateTextAttribute(type: .collapsedQuote(text: ChatTextInputStateText(attributedText: value)), range: range.location ..< (range.location + range.length)))
}
})
self.attributes = parsedAttributes
}

View File

@ -470,7 +470,8 @@ public final class ChatInterfaceState: Codable, Equatable {
if self.composeInputState.inputText.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && self.replyMessageSubject == nil {
return nil
} else {
return SynchronizeableChatInputState(replySubject: self.replyMessageSubject?.subjectModel, text: self.composeInputState.inputText.string, entities: generateChatInputTextEntities(self.composeInputState.inputText), timestamp: self.timestamp, textSelection: self.composeInputState.selectionRange)
let sourceText = expandedInputStateAttributedString(self.composeInputState.inputText)
return SynchronizeableChatInputState(replySubject: self.replyMessageSubject?.subjectModel, text: sourceText.string, entities: generateChatInputTextEntities(sourceText), timestamp: self.timestamp, textSelection: self.composeInputState.selectionRange)
}
}

View File

@ -16,6 +16,7 @@ swift_library(
"//submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl",
"//submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView",
"//submodules/AccountContext",
"//submodules/TelegramUI/Components/TextNodeWithEntities",
],
visibility = [
"//visibility:public",

View File

@ -7,6 +7,7 @@ import ChatInputTextViewImpl
import MessageInlineBlockBackgroundView
import TextFormat
import AccountContext
import TextNodeWithEntities
public protocol ChatInputTextNodeDelegate: AnyObject {
func chatInputTextNodeDidUpdateText()
@ -540,7 +541,19 @@ private final class ChatInputTextLegacyInternal: NSObject, ChatInputTextInternal
var startIndex = glyphRange.lowerBound
while startIndex < glyphRange.upperBound {
var effectiveRange = NSRange(location: NSNotFound, length: 0)
let rect = self.customLayoutManager.lineFragmentUsedRect(forGlyphAt: startIndex, effectiveRange: &effectiveRange)
var rect = self.customLayoutManager.lineFragmentUsedRect(forGlyphAt: startIndex, effectiveRange: &effectiveRange)
let characterRange = self.customLayoutManager.characterRange(forGlyphRange: NSRange(location: startIndex, length: 1), actualGlyphRange: nil)
if characterRange.location != NSNotFound {
if let attribute = self.customTextStorage.attribute(NSAttributedString.Key("Attribute__Blockquote"), at: characterRange.location, effectiveRange: nil) {
let _ = attribute
rect.size.width += 13.0
} else if let attribute = self.customTextStorage.attribute(.attachment, at: characterRange.location, effectiveRange: nil) as? ChatInputTextCollapsedQuoteAttachment {
let _ = attribute
rect.size.width += 8.0
}
}
if boundingRect.isEmpty {
boundingRect = rect
} else {
@ -552,6 +565,7 @@ private final class ChatInputTextLegacyInternal: NSObject, ChatInputTextInternal
break
}
}
return boundingRect
}
@ -804,11 +818,12 @@ private let registeredViewProvider: Void = {
public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment, ChatInputTextCollapsedQuoteAttachment {
final class View: UIView {
let attachment: ChatInputTextCollapsedQuoteAttachmentImpl
let textNode: ImmediateTextNode
let textNode: ImmediateTextNodeWithEntities
init(attachment: ChatInputTextCollapsedQuoteAttachmentImpl) {
self.attachment = attachment
self.textNode = ImmediateTextNode()
self.textNode = ImmediateTextNodeWithEntities()
self.textNode.displaysAsynchronously = false
self.textNode.maximumNumberOfLines = 3
super.init(frame: CGRect())
@ -825,10 +840,6 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment,
return CGSize(width: 10.0, height: 10.0)
}
/*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 renderingText = textAttributedStringForStateText(
context: context,
stateText: attachment.text,
@ -846,11 +857,11 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment,
textNode.maximumNumberOfLines = 3
textNode.attributedText = renderingText
textNode.cutout = TextNodeCutout(topRight: CGSize(width: 40.0, height: 10.0))
textNode.cutout = TextNodeCutout(topRight: CGSize(width: 30.0, height: 10.0))
let layoutInfo = textNode.updateLayoutFullInfo(CGSize(width: constrainedSize.width - 9.0, height: constrainedSize.height))
let layoutSize = textNode.updateLayout(CGSize(width: constrainedSize.width - 9.0, height: constrainedSize.height))
return CGSize(width: constrainedSize.width, height: 8.0 + layoutInfo.size.height + 8.0)
return CGSize(width: constrainedSize.width, height: 8.0 + layoutSize.height + 8.0)
}
override func layoutSubviews() {
@ -876,13 +887,24 @@ public final class ChatInputTextCollapsedQuoteAttachmentImpl: NSTextAttachment,
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.arguments = TextNodeWithEntities.Arguments(
context: context,
cache: context.animationCache,
renderer: context.animationRenderer,
placeholderColor: .gray,
attemptSynchronous: true
)
self.textNode.attributedText = renderingText
self.textNode.cutout = TextNodeCutout(topRight: CGSize(width: 10.0, height: 8.0))
self.textNode.cutout = TextNodeCutout(topRight: CGSize(width: 30.0, height: 10.0))
self.textNode.displaySpoilerEffect = true
self.textNode.visibility = true
let maxTextSize = CGSize(width: self.bounds.size.width - 9.0, height: self.bounds.size.height)
let layoutInfo = self.textNode.updateLayoutFullInfo(maxTextSize)
let layoutSize = self.textNode.updateLayout(maxTextSize)
self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: 8.0), size: layoutInfo.size)
self.textNode.frame = CGRect(origin: CGPoint(x: 9.0, y: 8.0), size: layoutSize)
}
}
@ -1450,16 +1472,17 @@ private final class QuoteBackgroundView: UIView {
self.iconView.frame = CGRect(origin: CGPoint(x: size.width - 4.0 - quoteIcon.size.width, y: 4.0), size: quoteIcon.size)
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)
if isCollapsed {
self.collapseButtonIconView.image = quoteExpandImage
self.collapseButton.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)
} else {
self.collapseButtonIconView.image = quoteCollapseImage
self.collapseButton.frame = CGRect(origin: CGPoint(x: size.width - 2.0 - collapseButtonSize.width, y: 2.0), size: collapseButtonSize)
}
if let image = self.collapseButtonIconView.image {
let iconSize = image.size.aspectFitted(collapseButtonSize)
self.collapseButtonIconView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((collapseButtonSize.width - iconSize.width) * 0.5), y: floorToScreenPixels((collapseButtonSize.height - iconSize.height) * 0.5)), size: iconSize)
self.collapseButtonIconView.frame = CGRect(origin: CGPoint(x: self.collapseButton.bounds.width - 2.0 - collapseButtonSize.width + floorToScreenPixels((collapseButtonSize.width - iconSize.width) * 0.5), y: 2.0 + floorToScreenPixels((collapseButtonSize.height - iconSize.height) * 0.5)), size: iconSize)
}
var primaryColor: UIColor
@ -1469,7 +1492,7 @@ private final class QuoteBackgroundView: UIView {
switch kind {
case .quote:
if size.height >= 100.0 || isCollapsed {
if size.height >= 60.0 || isCollapsed {
self.iconView.isHidden = true
self.collapseButton.isHidden = false
} else {

View File

@ -45,7 +45,7 @@ private func generateBlockMaskImage() -> UIImage {
gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.setBlendMode(.destinationIn)
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: size.height), end: CGPoint(x: 0.0, y: size.height - 7.0), options: CGGradientDrawingOptions())
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: size.height), end: CGPoint(x: 0.0, y: size.height - 18.0), options: CGGradientDrawingOptions())
})!.resizableImage(withCapInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: size.height - 1.0, right: size.width - 1.0), resizingMode: .stretch)
}

View File

@ -1212,13 +1212,23 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
let result = NSMutableAttributedString(attributedString: current.inputText)
var selectionRange = current.selectionRange
if let _ = result.attribute(ChatTextInputAttributes.block, at: range.lowerBound, effectiveRange: nil) as? ChatTextInputTextQuoteAttribute {
let blockString = result.attributedSubstring(from: range)
let blockString = NSMutableAttributedString(attributedString: result.attributedSubstring(from: range))
blockString.removeAttribute(ChatTextInputAttributes.block, range: NSRange(location: 0, length: blockString.length))
result.replaceCharacters(in: range, with: "")
result.insert(NSAttributedString(string: " ", attributes: [
ChatTextInputAttributes.collapsedBlock: blockString
]), at: range.lowerBound)
if selectionRange.lowerBound >= range.lowerBound && selectionRange.upperBound < range.upperBound {
selectionRange = range.lowerBound ..< range.lowerBound
} else if selectionRange.lowerBound >= range.upperBound {
let deltaLength = 1 - range.length
selectionRange = (selectionRange.lowerBound + deltaLength) ..< (selectionRange.lowerBound + deltaLength)
}
} else if let current = result.attribute(ChatTextInputAttributes.collapsedBlock, at: range.lowerBound, effectiveRange: nil) as? NSAttributedString {
result.replaceCharacters(in: range, with: "")
@ -1226,13 +1236,24 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
updatedBlockString.addAttribute(ChatTextInputAttributes.block, value: ChatTextInputTextQuoteAttribute(kind: .quote, isCollapsed: false), range: NSRange(location: 0, length: updatedBlockString.length))
result.insert(updatedBlockString, at: range.lowerBound)
if selectionRange.lowerBound >= range.upperBound {
let deltaLength = updatedBlockString.length - 1
selectionRange = (selectionRange.lowerBound + deltaLength) ..< (selectionRange.lowerBound + deltaLength)
}
}
let stateResult = stateAttributedStringForText(result)
if selectionRange.lowerBound < 0 {
selectionRange = 0 ..< selectionRange.upperBound
}
if selectionRange.upperBound > stateResult.length {
selectionRange = selectionRange.lowerBound ..< stateResult.length
}
return (ChatTextInputState(
inputText: stateResult,
selectionRange: current.selectionRange
selectionRange: selectionRange
), inputMode)
}
}

View File

@ -179,7 +179,7 @@ public struct ChatTextFontAttributes: OptionSet, Hashable, Sequence {
public func textAttributedStringForStateText(context: AnyObject, 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(
context: context,
fontSize: fontSize,
fontSize: round(fontSize * 0.8235294117647058),
textColor: textColor,
accentTextColor: accentTextColor
)