From c0e091ab864572da160778169846cd2935656c04 Mon Sep 17 00:00:00 2001 From: Igor Furgala Date: Wed, 21 May 2025 09:28:43 +0200 Subject: [PATCH 1/3] fix: edit text layout after setting up default value --- .../reactnativerichtexteditor/ReactNativeRichTextEditorView.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt b/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt index 0e18c790b..947ee95c4 100644 --- a/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt +++ b/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt @@ -125,6 +125,9 @@ class ReactNativeRichTextEditorView : AppCompatEditText { // Assign SpanWatcher one more time as our previous spannable has been replaced addSpanWatcher(EditorSpanWatcher(this)) + // Scroll to the last line of text + setSelection(text?.length ?: 0) + isSettingDefaultValue = false } From 8e2c071c28183093378e8ce25a6fad3ba2beaca2 Mon Sep 17 00:00:00 2001 From: Igor Furgala Date: Wed, 21 May 2025 15:12:56 +0200 Subject: [PATCH 2/3] fix: scrolling inside scroll view --- .../ReactNativeRichTextEditorView.kt | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt b/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt index 947ee95c4..cd8193bcf 100644 --- a/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt +++ b/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt @@ -8,6 +8,7 @@ import android.text.method.LinkMovementMethod import android.util.AttributeSet import android.util.Log import android.util.TypedValue +import android.view.MotionEvent import android.view.inputmethod.InputMethodManager import androidx.appcompat.widget.AppCompatEditText import com.facebook.react.bridge.Arguments @@ -48,6 +49,7 @@ class ReactNativeRichTextEditorView : AppCompatEditText { private var autoFocus = false private var typefaceDirty = false private var didAttachToWindow = false + private var detectScrollMovement = false private var fontSize: Float? = null private var fontFamily: String? = null private var fontStyle: Int = ReactConstants.UNSET @@ -79,7 +81,10 @@ class ReactNativeRichTextEditorView : AppCompatEditText { private fun prepareComponent() { isSingleLine = false isHorizontalScrollBarEnabled = false - gravity = android.view.Gravity.CENTER or android.view.Gravity.START + isVerticalScrollBarEnabled = true + gravity = android.view.Gravity.TOP or android.view.Gravity.START + inputType = android.text.InputType.TYPE_CLASS_TEXT or android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE + // required to make ClickableSpans really clickable movementMethod = LinkMovementMethod.getInstance() @@ -90,6 +95,32 @@ class ReactNativeRichTextEditorView : AppCompatEditText { addTextChangedListener(EditorTextWatcher(this)) } + // https://github.com/facebook/react-native/blob/36df97f500aa0aa8031098caf7526db358b6ddc1/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt#L295C1-L296C1 + override fun onTouchEvent(ev: MotionEvent): Boolean { + when (ev.action) { + MotionEvent.ACTION_DOWN -> { + detectScrollMovement = true + // Disallow parent views to intercept touch events, until we can detect if we should be + // capturing these touches or not. + this.parent.requestDisallowInterceptTouchEvent(true) + } + + MotionEvent.ACTION_MOVE -> + if (detectScrollMovement) { + if (!canScrollVertically(-1) && + !canScrollVertically(1) && + !canScrollHorizontally(-1) && + !canScrollHorizontally(1)) { + // We cannot scroll, let parent views take care of these touches. + this.parent.requestDisallowInterceptTouchEvent(false) + } + detectScrollMovement = false + } + } + + return super.onTouchEvent(ev) + } + override fun onSelectionChanged(selStart: Int, selEnd: Int) { super.onSelectionChanged(selStart, selEnd) selection?.onSelection(selStart, selEnd) From 22d17e1cdeb1b62517a4159d993e6c5935a8779d Mon Sep 17 00:00:00 2001 From: Igor Furgala Date: Wed, 21 May 2025 15:45:14 +0200 Subject: [PATCH 3/3] fix: auto scroll while editing text --- .../ReactNativeRichTextEditorView.kt | 10 ++++++++ example/src/App.tsx | 24 +++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt b/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt index cd8193bcf..ab4a62016 100644 --- a/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt +++ b/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt @@ -222,6 +222,16 @@ class ReactNativeRichTextEditorView : AppCompatEditText { } } + // https://github.com/facebook/react-native/blob/36df97f500aa0aa8031098caf7526db358b6ddc1/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt#L283C2-L284C1 + // After the text changes inside an EditText, TextView checks if a layout() has been requested. + // If it has, it will not scroll the text to the end of the new text inserted, but wait for the + // 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 { + return false + } + fun measureSize(maxWidth: Float): Pair { val paint = this.paint val spannable = text as Spannable diff --git a/example/src/App.tsx b/example/src/App.tsx index 1fda873a2..68427124a 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -6,6 +6,7 @@ import { Linking, Alert, TextInput, + ScrollView, } from 'react-native'; import { RichTextInput, @@ -53,6 +54,8 @@ const DEFAULT_LINK_STATE = { url: '', }; +const DEBUG_SCROLLABLE = false; + export default function App() { const [isMentionPopupOpen, setIsMentionPopupOpen] = useState(false); const [isLinkModalOpen, setIsLinkModalOpen] = useState(false); @@ -153,7 +156,10 @@ export default function App() { return ( <> - + SWM Rich Text Editor