diff --git a/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt b/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt index 52ea57223..db3cf7976 100644 --- a/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt +++ b/android/src/main/java/com/swmansion/reactnativerichtexteditor/ReactNativeRichTextEditorView.kt @@ -11,6 +11,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 @@ -51,6 +52,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 @@ -82,7 +84,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() @@ -93,6 +98,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) @@ -128,6 +159,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 } @@ -212,6 +246,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 2d6701c7f..b259838a3 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