mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
[WIP] Quotes
This commit is contained in:
@@ -23,6 +23,7 @@ import LottieAnimationComponent
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import TextNodeWithEntities
|
||||
import ChatInputTextNode
|
||||
|
||||
private let counterFont = Font.with(size: 14.0, design: .regular, traits: [.monospacedNumbers])
|
||||
private let minInputFontSize: CGFloat = 5.0
|
||||
@@ -112,7 +113,7 @@ private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackground
|
||||
}
|
||||
}
|
||||
|
||||
private class CaptionEditableTextNode: EditableTextNode {
|
||||
private class CaptionEditableTextNode: ChatInputTextNode {
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let previousAlpha = self.alpha
|
||||
self.alpha = 1.0
|
||||
@@ -190,7 +191,7 @@ final class CustomEmojiContainerView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, ASEditableTextNodeDelegate {
|
||||
public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, ASEditableTextNodeDelegate, ChatInputTextNodeDelegate {
|
||||
private let context: AccountContext
|
||||
|
||||
private let isCaption: Bool
|
||||
@@ -202,7 +203,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
private var textPlaceholderNode: ImmediateTextNode
|
||||
private let textInputContainerBackgroundNode: ASImageNode
|
||||
private let textInputContainer: ASDisplayNode
|
||||
public var textInputNode: EditableTextNode?
|
||||
public var textInputNode: ChatInputTextNode?
|
||||
private var dustNode: InvisibleInkDustNode?
|
||||
private var customEmojiContainerView: CustomEmojiContainerView?
|
||||
private var oneLineNode: TextNodeWithEntities
|
||||
@@ -249,7 +250,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
var storedInputLanguage: String?
|
||||
var effectiveInputLanguage: String? {
|
||||
if let textInputNode = textInputNode, textInputNode.isFirstResponder() {
|
||||
return textInputNode.textInputMode.primaryLanguage
|
||||
return textInputNode.textInputMode?.primaryLanguage
|
||||
} else {
|
||||
return self.storedInputLanguage
|
||||
}
|
||||
@@ -308,7 +309,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||
}
|
||||
textInputNode.attributedText = NSAttributedString(string: value, font: Font.regular(baseFontSize), textColor: textColor)
|
||||
self.editableTextNodeDidUpdateText(textInputNode)
|
||||
self.chatInputTextNodeDidUpdateText()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,7 +517,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
paragraphStyle.maximumLineHeight = 20.0
|
||||
paragraphStyle.minimumLineHeight = 20.0
|
||||
|
||||
textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(max(minInputFontSize, baseFontSize)), NSAttributedString.Key.foregroundColor.rawValue: textColor, NSAttributedString.Key.paragraphStyle.rawValue: paragraphStyle]
|
||||
textInputNode.textView.typingAttributes = [NSAttributedString.Key.font: Font.regular(max(minInputFontSize, baseFontSize)), NSAttributedString.Key.foregroundColor: textColor, NSAttributedString.Key.paragraphStyle: paragraphStyle]
|
||||
textInputNode.clipsToBounds = false
|
||||
textInputNode.textView.clipsToBounds = false
|
||||
textInputNode.delegate = self
|
||||
@@ -532,7 +533,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
textInputNode.textView.inputAssistantItem.trailingBarButtonGroups = []
|
||||
|
||||
if let presentationInterfaceState = self.presentationInterfaceState {
|
||||
refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||
textInputNode.textContainerInset = calculateTextFieldRealInsets(presentationInterfaceState)
|
||||
}
|
||||
|
||||
@@ -541,6 +542,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
|
||||
textInputNode.frame = CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: self.textInputViewInternalInsets.top), size: CGSize(width: textInputFrame.size.width - (self.textInputViewInternalInsets.left + self.textInputViewInternalInsets.right), height: textInputFrame.size.height - self.textInputViewInternalInsets.top - self.textInputViewInternalInsets.bottom))
|
||||
textInputNode.view.layoutIfNeeded()
|
||||
textInputNode.textView.updateLayout(size: textInputNode.bounds.size)
|
||||
self.updateSpoiler()
|
||||
}
|
||||
|
||||
@@ -587,8 +589,8 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
let textFieldHeight: CGFloat
|
||||
if let textInputNode = self.textInputNode {
|
||||
let maxTextWidth = width - textFieldInsets.left - textFieldInsets.right - self.textInputViewInternalInsets.left - self.textInputViewInternalInsets.right
|
||||
let measuredHeight = textInputNode.measure(CGSize(width: maxTextWidth, height: CGFloat.greatestFiniteMagnitude))
|
||||
let unboundTextFieldHeight = max(textFieldMinHeight, ceil(measuredHeight.height))
|
||||
let measuredHeight = textInputNode.textHeightForWidth(maxTextWidth)
|
||||
let unboundTextFieldHeight = max(textFieldMinHeight, ceil(measuredHeight))
|
||||
|
||||
let maxNumberOfLines = min(12, (Int(fieldMaxHeight - 11.0) - 33) / 22)
|
||||
|
||||
@@ -674,7 +676,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
textInputNode.attributedText = updatedText
|
||||
textInputNode.selectedRange = selectedRange
|
||||
}
|
||||
textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(baseFontSize), NSAttributedString.Key.foregroundColor.rawValue: textColor]
|
||||
textInputNode.textView.typingAttributes = [NSAttributedString.Key.font: Font.regular(baseFontSize), NSAttributedString.Key.foregroundColor: textColor]
|
||||
|
||||
self.updateSpoiler()
|
||||
}
|
||||
@@ -960,11 +962,11 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
}
|
||||
|
||||
private var skipUpdate = false
|
||||
@objc public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
||||
public func chatInputTextNodeDidUpdateText() {
|
||||
if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState {
|
||||
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||
refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickers.keys), emojiViewProvider: self.emojiViewProvider)
|
||||
refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickers.keys), emojiViewProvider: self.emojiViewProvider)
|
||||
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||
|
||||
self.updateSpoiler()
|
||||
|
||||
@@ -973,7 +975,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
self.skipUpdate = true
|
||||
|
||||
self.interfaceInteraction?.updateTextInputStateAndMode({ _, inputMode in return (inputTextState, inputMode) })
|
||||
self.interfaceInteraction?.updateInputLanguage({ _ in return textInputNode.textInputMode.primaryLanguage })
|
||||
self.interfaceInteraction?.updateInputLanguage({ _ in return textInputNode.textInputMode?.primaryLanguage })
|
||||
if self.isCaption, let presentationInterfaceState = self.presentationInterfaceState {
|
||||
self.presentationInterfaceState = presentationInterfaceState.updatedInterfaceState({
|
||||
return $0.withUpdatedComposeInputState(inputTextState)
|
||||
@@ -988,6 +990,10 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) {
|
||||
self.chatInputTextNodeDidUpdateText()
|
||||
}
|
||||
|
||||
private func updateSpoiler() {
|
||||
guard let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState else {
|
||||
return
|
||||
@@ -1131,7 +1137,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
|
||||
textInputNode.textView.isScrollEnabled = false
|
||||
|
||||
refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickers.keys), emojiViewProvider: self.emojiViewProvider)
|
||||
refreshChatTextInputAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(self.context.animatedEmojiStickers.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.animatedEmojiStickers.keys), emojiViewProvider: self.emojiViewProvider)
|
||||
|
||||
@@ -1345,13 +1351,17 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeShouldReturn(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
public func chatInputTextNodeShouldReturn() -> Bool {
|
||||
if self.actionButtons.sendButton.supernode != nil && !self.actionButtons.sendButton.isHidden && !self.actionButtons.sendButton.alpha.isZero {
|
||||
self.sendButtonPressed()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeShouldReturn(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
return self.chatInputTextNodeShouldReturn()
|
||||
}
|
||||
|
||||
private func applyUpdateSendButtonIcon() {
|
||||
if let interfaceState = self.presentationInterfaceState {
|
||||
let sendButtonHasApplyIcon = interfaceState.interfaceState.editMessage != nil
|
||||
@@ -1371,7 +1381,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeDidChangeSelection(_ editableTextNode: ASEditableTextNode, fromSelectedRange: NSRange, toSelectedRange: NSRange, dueToEditing: Bool) {
|
||||
public func chatInputTextNodeDidChangeSelection(dueToEditing: Bool) {
|
||||
if !dueToEditing && !self.updatingInputState {
|
||||
let inputTextState = self.inputTextState
|
||||
self.skipUpdate = true
|
||||
@@ -1385,13 +1395,17 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
}
|
||||
|
||||
let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)
|
||||
refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||
refreshChatTextInputTypingAttributes(textInputNode.textView, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize)
|
||||
|
||||
self.updateSpoilersRevealed()
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {
|
||||
@objc public func editableTextNodeDidChangeSelection(_ editableTextNode: ASEditableTextNode, fromSelectedRange: NSRange, toSelectedRange: NSRange, dueToEditing: Bool) {
|
||||
self.chatInputTextNodeDidChangeSelection(dueToEditing: dueToEditing)
|
||||
}
|
||||
|
||||
public func chatInputTextNodeDidBeginEditing() {
|
||||
self.interfaceInteraction?.updateInputModeAndDismissedButtonKeyboardMessageId({ state in
|
||||
return (.text, state.keyboardButtonsMessage?.id)
|
||||
})
|
||||
@@ -1404,8 +1418,15 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
}
|
||||
}
|
||||
|
||||
public func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) {
|
||||
self.storedInputLanguage = editableTextNode.textInputMode.primaryLanguage
|
||||
@objc public func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) {
|
||||
self.chatInputTextNodeDidBeginEditing()
|
||||
}
|
||||
|
||||
public func chatInputTextNodeDidFinishEditing() {
|
||||
guard let editableTextNode = self.textInputNode else {
|
||||
return
|
||||
}
|
||||
self.storedInputLanguage = editableTextNode.textInputMode?.primaryLanguage
|
||||
self.inputMenu.deactivate()
|
||||
|
||||
self.focusUpdated?(false)
|
||||
@@ -1415,6 +1436,10 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
}
|
||||
}
|
||||
|
||||
public func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) {
|
||||
self.chatInputTextNodeDidFinishEditing()
|
||||
}
|
||||
|
||||
public func editableTextNodeTarget(forAction action: Selector) -> ASEditableTextNodeTargetForAction? {
|
||||
if action == makeSelectorFromString("_accessibilitySpeak:") {
|
||||
if case .format = self.inputMenu.state {
|
||||
@@ -1461,8 +1486,12 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
return nil
|
||||
}
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
public func editableTextNodeMenu(_ editableTextNode: ASEditableTextNode, forTextRange textRange: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu {
|
||||
@available(iOS 13.0, *)
|
||||
public func chatInputTextNodeMenu(forTextRange textRange: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu {
|
||||
guard let editableTextNode = self.textInputNode else {
|
||||
return UIMenu(children: suggestedActions)
|
||||
}
|
||||
|
||||
var actions = suggestedActions
|
||||
|
||||
if editableTextNode.attributedText == nil || editableTextNode.attributedText!.length == 0 || editableTextNode.selectedRange.length == 0 {
|
||||
@@ -1520,6 +1549,11 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
return UIMenu(children: actions)
|
||||
}
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
public func editableTextNodeMenu(_ editableTextNode: ASEditableTextNode, forTextRange textRange: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu {
|
||||
return self.chatInputTextNodeMenu(forTextRange: textRange, suggestedActions: suggestedActions)
|
||||
}
|
||||
|
||||
private var currentSpeechHolder: SpeechSynthesizerHolder?
|
||||
@objc func _accessibilitySpeak(_ sender: Any) {
|
||||
var text = ""
|
||||
@@ -1612,7 +1646,11 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
self.updateSpoilersRevealed(animated: animated)
|
||||
}
|
||||
|
||||
@objc public func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
public func chatInputTextNode(shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
guard let editableTextNode = self.textInputNode else {
|
||||
return true
|
||||
}
|
||||
|
||||
var cleanText = text
|
||||
let removeSequences: [String] = ["\u{202d}", "\u{202c}"]
|
||||
for sequence in removeSequences {
|
||||
@@ -1645,7 +1683,11 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeShouldCopy(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
@objc public func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
||||
return self.chatInputTextNode(shouldChangeTextIn: range, replacementText: text)
|
||||
}
|
||||
|
||||
public func chatInputTextNodeShouldCopy() -> Bool {
|
||||
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||
storeInputTextInPasteboard(current.inputText.attributedSubstring(from: NSMakeRange(current.selectionRange.lowerBound, current.selectionRange.count)))
|
||||
return (current, inputMode)
|
||||
@@ -1653,7 +1695,11 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
return false
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
@objc public func editableTextNodeShouldCopy(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
return self.chatInputTextNodeShouldCopy()
|
||||
}
|
||||
|
||||
public func chatInputTextNodeShouldPaste() -> Bool {
|
||||
let pasteboard = UIPasteboard.general
|
||||
|
||||
var attributedString: NSAttributedString?
|
||||
@@ -1678,6 +1724,13 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
return self.chatInputTextNodeShouldPaste()
|
||||
}
|
||||
|
||||
public func chatInputTextNodeBackspaceWhileEmpty() {
|
||||
}
|
||||
|
||||
@objc func sendButtonPressed() {
|
||||
let inputTextMaxLength: Int32?
|
||||
if let maxCaptionLength = self.maxCaptionLength {
|
||||
|
||||
Reference in New Issue
Block a user