Added marked text range support (#345)

* 👍 Added markedTextRangeSupport.

* 👍 ignore when markedTextRange is non-nil.

* Update TextView revision

Co-authored-by: Hidemune Takahashi <h1d3mun3.74k4h45h1@gmail.com>
Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
This commit is contained in:
Hidemune Takahashi 2023-01-25 01:26:56 +09:00 committed by GitHub
parent 6ebee8f5b5
commit ae3d190799
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 12 additions and 3 deletions

View file

@ -69,7 +69,7 @@
"location" : "https://github.com/Dimillian/TextView", "location" : "https://github.com/Dimillian/TextView",
"state" : { "state" : {
"branch" : "main", "branch" : "main",
"revision" : "a4ba065a69e3376aeb45b32846d50fd71a7e20e3" "revision" : "22eec87ccac1270557b2b446fddc13c311bae5e7"
} }
} }
], ],

View file

@ -37,7 +37,7 @@ public struct StatusEditorView: View {
VStack(spacing: 12) { VStack(spacing: 12) {
accountHeaderView accountHeaderView
.padding(.horizontal, .layoutPadding) .padding(.horizontal, .layoutPadding)
TextView($viewModel.statusText, $viewModel.selectedRange) TextView($viewModel.statusText, $viewModel.selectedRange, $viewModel.markedTextRange)
.placeholder(String(localized: "status.editor.text.placeholder")) .placeholder(String(localized: "status.editor.text.placeholder"))
.font(Font.scaledBodyUIFont) .font(Font.scaledBodyUIFont)
.keyboardType(.twitter) .keyboardType(.twitter)

View file

@ -43,6 +43,7 @@ public class StatusEditorViewModel: ObservableObject {
@Published var spoilerText: String = "" @Published var spoilerText: String = ""
@Published var selectedRange: NSRange = .init(location: 0, length: 0) @Published var selectedRange: NSRange = .init(location: 0, length: 0)
@Published var markedTextRange: UITextRange? = nil
@Published var isPosting: Bool = false @Published var isPosting: Bool = false
@Published var selectedMedias: [PhotosPickerItem] = [] { @Published var selectedMedias: [PhotosPickerItem] = [] {
@ -155,6 +156,7 @@ public class StatusEditorViewModel: ObservableObject {
string.mutableString.insert(text, at: selectedRange.location) string.mutableString.insert(text, at: selectedRange.location)
statusText = string statusText = string
selectedRange = NSRange(location: selectedRange.location + text.utf16.count, length: 0) selectedRange = NSRange(location: selectedRange.location + text.utf16.count, length: 0)
markedTextRange = nil
} }
func replaceTextWith(text: String, inRange: NSRange) { func replaceTextWith(text: String, inRange: NSRange) {
@ -163,11 +165,13 @@ public class StatusEditorViewModel: ObservableObject {
string.mutableString.insert(text, at: inRange.location) string.mutableString.insert(text, at: inRange.location)
statusText = string statusText = string
selectedRange = NSRange(location: inRange.location + text.utf16.count, length: 0) selectedRange = NSRange(location: inRange.location + text.utf16.count, length: 0)
markedTextRange = nil
} }
func replaceTextWith(text: String) { func replaceTextWith(text: String) {
statusText = .init(string: text) statusText = .init(string: text)
selectedRange = .init(location: text.utf16.count, length: 0) selectedRange = .init(location: text.utf16.count, length: 0)
markedTextRange = nil
} }
func prepareStatusText() { func prepareStatusText() {
@ -193,7 +197,7 @@ public class StatusEditorViewModel: ObservableObject {
visibility = status.visibility visibility = status.visibility
statusText = .init(string: mentionString) statusText = .init(string: mentionString)
selectedRange = .init(location: mentionString.utf16.count, length: 0) selectedRange = .init(location: mentionString.utf16.count, length: 0)
markedTextRange = nil
if !mentionString.isEmpty { if !mentionString.isEmpty {
self.mentionString = mentionString.trimmingCharacters(in: .whitespaces) self.mentionString = mentionString.trimmingCharacters(in: .whitespaces)
} }
@ -201,6 +205,7 @@ public class StatusEditorViewModel: ObservableObject {
statusText = .init(string: "@\(account.acct) ") statusText = .init(string: "@\(account.acct) ")
self.visibility = visibility self.visibility = visibility
selectedRange = .init(location: statusText.string.utf16.count, length: 0) selectedRange = .init(location: statusText.string.utf16.count, length: 0)
markedTextRange = nil
case let .edit(status): case let .edit(status):
var rawText = NSAttributedString(status.content.asSafeMarkdownAttributedString).string var rawText = NSAttributedString(status.content.asSafeMarkdownAttributedString).string
for mention in status.mentions { for mention in status.mentions {
@ -208,6 +213,7 @@ public class StatusEditorViewModel: ObservableObject {
} }
statusText = .init(string: rawText) statusText = .init(string: rawText)
selectedRange = .init(location: statusText.string.utf16.count, length: 0) selectedRange = .init(location: statusText.string.utf16.count, length: 0)
markedTextRange = nil
spoilerOn = !status.spoilerText.asRawText.isEmpty spoilerOn = !status.spoilerText.asRawText.isEmpty
spoilerText = status.spoilerText.asRawText spoilerText = status.spoilerText.asRawText
visibility = status.visibility visibility = status.visibility
@ -220,11 +226,13 @@ public class StatusEditorViewModel: ObservableObject {
if let url = embeddedStatusURL { if let url = embeddedStatusURL {
statusText = .init(string: "\n\nFrom: @\(status.reblog?.account.acct ?? status.account.acct)\n\(url)") statusText = .init(string: "\n\nFrom: @\(status.reblog?.account.acct ?? status.account.acct)\n\(url)")
selectedRange = .init(location: 0, length: 0) selectedRange = .init(location: 0, length: 0)
markedTextRange = nil
} }
} }
} }
private func processText() { private func processText() {
guard markedTextRange == nil else { return }
statusText.addAttributes([.foregroundColor: UIColor(Color.label), statusText.addAttributes([.foregroundColor: UIColor(Color.label),
.underlineColor: .clear], .underlineColor: .clear],
range: NSMakeRange(0, statusText.string.utf16.count)) range: NSMakeRange(0, statusText.string.utf16.count))
@ -332,6 +340,7 @@ public class StatusEditorViewModel: ObservableObject {
if !initialText.isEmpty { if !initialText.isEmpty {
statusText = .init(string: initialText) statusText = .init(string: initialText)
selectedRange = .init(location: statusText.string.utf16.count, length: 0) selectedRange = .init(location: statusText.string.utf16.count, length: 0)
markedTextRange = nil
} }
if !mediasImages.isEmpty { if !mediasImages.isEmpty {
processMediasToUpload() processMediasToUpload()