From 941873fb7883f630cac36aa02766852df198bb05 Mon Sep 17 00:00:00 2001 From: IvanIhnatsiuk Date: Tue, 9 Jun 2026 15:50:04 +0200 Subject: [PATCH 1/2] fix: correct scroll position when insert text on Android --- .../enriched/EnrichedStyleManipulator.kt | 51 ++++++++++++++----- .../enriched/EnrichedTextInputView.kt | 15 ++++-- .../enriched/watchers/EnrichedTextWatcher.kt | 1 + 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/android/src/main/java/com/swmansion/enriched/EnrichedStyleManipulator.kt b/android/src/main/java/com/swmansion/enriched/EnrichedStyleManipulator.kt index 5233c48f..eaee258b 100644 --- a/android/src/main/java/com/swmansion/enriched/EnrichedStyleManipulator.kt +++ b/android/src/main/java/com/swmansion/enriched/EnrichedStyleManipulator.kt @@ -10,7 +10,7 @@ import com.swmansion.enriched.styles.ParagraphStyles import com.swmansion.enriched.styles.ParametrizedStyles class EnrichedStyleManipulator( - val view: EnrichedTextInputView, + private val view: EnrichedTextInputView, ) { val inlineStyles: InlineStyles = InlineStyles(view) val paragraphStyles: ParagraphStyles = ParagraphStyles(view) @@ -29,26 +29,53 @@ class EnrichedStyleManipulator( name: TextStyle, start: Int, end: Int, - ): Boolean = - when (EnrichedSpans.getStyleGroup(name)) { - TextStyleGroup.INLINE -> inlineStyles.removeStyle(name, start, end) - TextStyleGroup.PARAGRAPH -> paragraphStyles.removeStyle(name, start, end) - TextStyleGroup.LIST -> listStyles.removeStyle(name, start, end) - TextStyleGroup.PARAMETRIZED -> parametrizedStyles.removeStyle(name, start, end) - null -> false + ): Boolean { + return when (EnrichedSpans.getStyleGroup(name)) { + TextStyleGroup.INLINE -> { + inlineStyles.removeStyle(name, start, end) + } + + TextStyleGroup.PARAGRAPH -> { + val removed = paragraphStyles.removeStyle(name, start, end) + view.correctScrollPositionIfNeeded() + + return removed + } + + TextStyleGroup.LIST -> { + listStyles.removeStyle(name, start, end) + } + + TextStyleGroup.PARAMETRIZED -> { + parametrizedStyles.removeStyle(name, start, end) + } + + null -> { + false + } } + } internal fun toggleStyle(name: TextStyle) { when (EnrichedSpans.getStyleGroup(name)) { - TextStyleGroup.INLINE -> inlineStyles.toggleStyle(name) + TextStyleGroup.INLINE -> { + inlineStyles.toggleStyle(name) + } - TextStyleGroup.PARAGRAPH -> paragraphStyles.toggleStyle(name) + TextStyleGroup.PARAGRAPH -> { + paragraphStyles.toggleStyle(name) + view.correctScrollPositionIfNeeded() + } - TextStyleGroup.LIST -> listStyles.toggleStyle(name) + TextStyleGroup.LIST -> { + listStyles.toggleStyle(name) + } TextStyleGroup.PARAMETRIZED, null, - -> Log.w("EnrichedTextInputView", "Unknown style: $name") + -> { + Log.w("EnrichedTextInputView", "Unknown style: $name") + } } } diff --git a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt index 1453f181..3ac1dace 100644 --- a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +++ b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt @@ -280,9 +280,9 @@ class EnrichedTextInputView : AppCompatEditText { return super.onTouchEvent(event) } - override fun canScrollVertically(direction: Int): Boolean = scrollEnabled + override fun canScrollVertically(direction: Int): Boolean = scrollEnabled && super.canScrollVertically(direction) - override fun canScrollHorizontally(direction: Int): Boolean = scrollEnabled + override fun canScrollHorizontally(direction: Int): Boolean = scrollEnabled && super.canScrollHorizontally(direction) override fun onScrollChanged( horiz: Int, @@ -652,8 +652,7 @@ class EnrichedTextInputView : AppCompatEditText { // next layout() to be called. However, we do not perform a layout() after a requestLayout(), so // we need to override isLayoutRequested to force EditText to scroll to the end of the new text // immediately. - // Ivan Ihnatsiuk: let android calculate layout to avoid jumping behavior when we insert a new line. -// override fun isLayoutRequested(): Boolean = false + override fun isLayoutRequested(): Boolean = false fun afterUpdateTransaction() { updateTypeface() @@ -740,6 +739,14 @@ class EnrichedTextInputView : AppCompatEditText { dispatcher?.dispatchEvent(event) } + fun correctScrollPositionIfNeeded() { + if (!scrollEnabled) { + return + } + + forceScrollToSelection() + } + private fun forceScrollToSelection() { val textLayout = layout ?: return val cursorOffset = selectionStart diff --git a/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt b/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt index 78e85f6e..5a6ba7a8 100644 --- a/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +++ b/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt @@ -70,6 +70,7 @@ class EnrichedTextWatcher( } } } + view.correctScrollPositionIfNeeded() } private fun applyStyles(s: Editable) { From bc3dc69c1ad332e7ab3f52524dddabbc18da0f53 Mon Sep 17 00:00:00 2001 From: IvanIhnatsiuk Date: Tue, 9 Jun 2026 20:47:03 +0200 Subject: [PATCH 2/2] fix: comment isLayoutRequested override --- .../main/java/com/swmansion/enriched/EnrichedTextInputView.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt index 3ac1dace..145616a6 100644 --- a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +++ b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt @@ -652,7 +652,8 @@ class EnrichedTextInputView : AppCompatEditText { // next layout() to be called. However, we do not perform a layout() after a requestLayout(), so // we need to override isLayoutRequested to force EditText to scroll to the end of the new text // immediately. - override fun isLayoutRequested(): Boolean = false + // Ivan Ihnatsiuk: let android calculate layout to avoid jumping behavior when we insert a new line. + // override fun isLayoutRequested(): Boolean = false fun afterUpdateTransaction() { updateTypeface()