From a413fbe783ac5e68bc5db976aa774ffdb1a8a6d6 Mon Sep 17 00:00:00 2001 From: Rajdeep Kwatra Date: Fri, 4 Oct 2024 16:31:26 +1000 Subject: [PATCH] Fixed issue where selectedEditorView may be unintentionally updated on setting attributed text --- Proton/Sources/Swift/Attachment/Attachment.swift | 2 +- Proton/Sources/Swift/Core/RichTextView.swift | 8 ++++++++ .../Sources/Swift/Core/RichTextViewContext.swift | 15 ++++++++++----- Proton/Sources/Swift/Editor/EditorView.swift | 3 ++- Proton/Tests/Core/RichTextViewContextTests.swift | 1 + 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Proton/Sources/Swift/Attachment/Attachment.swift b/Proton/Sources/Swift/Attachment/Attachment.swift index bcc775d2..640529e8 100644 --- a/Proton/Sources/Swift/Attachment/Attachment.swift +++ b/Proton/Sources/Swift/Attachment/Attachment.swift @@ -469,7 +469,7 @@ open class Attachment: NSTextAttachment, BoundsObserving { containerTextView.textStorage.replaceCharacters(in: range, with: "") // Set the selected range in container to show the cursor at deleted location // after attachment is removed. - containerTextView.selectedRange = NSRange(location: range.location, length: 0) + containerTextView.updateSelectedRangeIgnoringCallback(NSRange(location: range.location, length: 0)) } /// Range of this attachment in it's container diff --git a/Proton/Sources/Swift/Core/RichTextView.swift b/Proton/Sources/Swift/Core/RichTextView.swift index 15f5d389..233688c4 100644 --- a/Proton/Sources/Swift/Core/RichTextView.swift +++ b/Proton/Sources/Swift/Core/RichTextView.swift @@ -36,6 +36,8 @@ class RichTextView: AutogrowingTextView { private var delegateOverrides = [GestureRecognizerDelegateOverride]() + private(set) var ignoreSelectedRangeChangeCallback = false + private var _canBecomeFirstResponder = true override var canBecomeFirstResponder: Bool { return _canBecomeFirstResponder @@ -203,6 +205,12 @@ class RichTextView: AutogrowingTextView { draw(CGRect(origin: .zero, size: contentSize)) } + func updateSelectedRangeIgnoringCallback(_ selectedRange: NSRange) { + ignoreSelectedRangeChangeCallback = true + self.selectedRange = selectedRange + ignoreSelectedRangeChangeCallback = false + } + override var selectedTextRange: UITextRange? { didSet{ let old = oldValue?.toNSRange(in: self) diff --git a/Proton/Sources/Swift/Core/RichTextViewContext.swift b/Proton/Sources/Swift/Core/RichTextViewContext.swift index 0fef4dbb..d5dcc909 100644 --- a/Proton/Sources/Swift/Core/RichTextViewContext.swift +++ b/Proton/Sources/Swift/Core/RichTextViewContext.swift @@ -31,11 +31,16 @@ class RichTextViewContext: NSObject, UITextViewDelegate { } func textViewDidChangeSelection(_ textView: UITextView) { - guard textView.delegate === self else { return } - if textView.selectedTextRange != nil { - selectedTextView = textView.asRichTextView - } else { - selectedTextView = nil + guard textView.delegate === self, + textView.asRichTextView?.ignoreSelectedRangeChangeCallback == false, + textView.asRichTextView?.editorView?.isSettingAttributedText != true else { return } + + if textView.isEditable && textView.isFirstResponder || textView.isEditable == false { + if textView.selectedTextRange != nil { + selectedTextView = textView.asRichTextView + } else { + selectedTextView = nil + } } guard let richTextView = textView as? RichTextView else { return } diff --git a/Proton/Sources/Swift/Editor/EditorView.swift b/Proton/Sources/Swift/Editor/EditorView.swift index 8db06e14..d72c61e2 100644 --- a/Proton/Sources/Swift/Editor/EditorView.swift +++ b/Proton/Sources/Swift/Editor/EditorView.swift @@ -559,6 +559,7 @@ open class EditorView: UIView { pendingAttributedText = newValue return } + isSettingAttributedText = true attachmentRenderingScheduler.cancel() renderedViewport = nil // Clear text before setting new value to avoid issues with formatting/layout when @@ -569,7 +570,7 @@ open class EditorView: UIView { pendingAttributedText = nil AggregateEditorViewDelegate.editor(self, willSetAttributedText: newValue, isDeferred: isDeferred) - isSettingAttributedText = true + richTextView.attributedText = newValue isSettingAttributedText = false AggregateEditorViewDelegate.editor(self, didSetAttributedText: newValue, isDeferred: isDeferred) diff --git a/Proton/Tests/Core/RichTextViewContextTests.swift b/Proton/Tests/Core/RichTextViewContextTests.swift index 1343b655..72a20a43 100644 --- a/Proton/Tests/Core/RichTextViewContextTests.swift +++ b/Proton/Tests/Core/RichTextViewContextTests.swift @@ -305,6 +305,7 @@ class RichTextViewContextTests: XCTestCase { let textView = RichTextView(context: context) textView.richTextViewDelegate = mockTextViewDelegate + textView.isEditable = false textView.text = "Sample text" textView.selectedRange = .zero