diff --git a/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt b/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt
index 4e3b86601..af9df9327 100644
--- a/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt
+++ b/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt
@@ -103,8 +103,16 @@ class InlineStyles(
fun afterTextChanged(
s: Editable,
+ startCursorPosition: Int,
endCursorPosition: Int,
) {
+ if (endCursorPosition > startCursorPosition) {
+ for ((style, config) in EnrichedSpans.inlineSpans) {
+ if (view.spanState?.getStart(style) != null) continue
+ splitSpanOnInsertion(s, config.clazz, startCursorPosition, endCursorPosition)
+ }
+ }
+
for ((style, config) in EnrichedSpans.inlineSpans) {
val start = view.spanState?.getStart(style) ?: continue
var end = endCursorPosition
@@ -117,6 +125,22 @@ class InlineStyles(
setSpan(s, config.clazz, start, end)
}
+
+ val isBackspace = endCursorPosition == startCursorPosition
+ if (!isBackspace) {
+ return
+ }
+
+ // Collapse same-type inline spans that ended up adjacent to each other after deletion.
+ // Without this the HTML output would emit separate tags like .......
+ for ((_, config) in EnrichedSpans.inlineSpans) {
+ for (span in s.getSpans(startCursorPosition, startCursorPosition, config.clazz)) {
+ val spanStart = s.getSpanStart(span)
+ val spanEnd = s.getSpanEnd(span)
+ if (spanStart < 0 || spanEnd < 0) continue
+ setSpan(s, config.clazz, spanStart, spanEnd)
+ }
+ }
}
fun toggleStyle(name: String) {
@@ -143,6 +167,33 @@ class InlineStyles(
view.selection.validateStyles()
}
+ private fun splitSpanOnInsertion(
+ spannable: Spannable,
+ type: Class,
+ insertStart: Int,
+ insertEnd: Int,
+ ) {
+ val spans = spannable.getSpans(insertStart, insertEnd, type)
+ for (span in spans) {
+ val spanStart = spannable.getSpanStart(span)
+ val spanEnd = spannable.getSpanEnd(span)
+ if (spanStart < 0 || spanEnd < 0) continue
+
+ spannable.removeSpan(span)
+
+ if (spanStart < insertStart) {
+ val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(spanStart, insertStart)
+ val left = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
+ spannable.setSpan(left, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
+ }
+ if (spanEnd > insertEnd) {
+ val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(insertEnd, spanEnd)
+ val right = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
+ spannable.setSpan(right, safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
+ }
+ }
+ }
+
fun removeStyle(
name: String,
start: Int,
diff --git a/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt b/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt
index 028b41c15..cbc11fc36 100644
--- a/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt
+++ b/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt
@@ -44,7 +44,7 @@ class EnrichedTextWatcher(
}
private fun applyStyles(s: Editable) {
- view.inlineStyles?.afterTextChanged(s, endCursorPosition)
+ view.inlineStyles?.afterTextChanged(s, startCursorPosition, endCursorPosition)
view.paragraphStyles?.afterTextChanged(s, endCursorPosition, previousTextLength)
view.listStyles?.afterTextChanged(s, endCursorPosition, previousTextLength)
view.parametrizedStyles?.afterTextChanged(s, startCursorPosition, endCursorPosition)