diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt index b23e2d5b190..9dc06d3111d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt @@ -24,8 +24,11 @@ import androidx.annotation.DoNotInline import androidx.annotation.RequiresApi import androidx.core.view.ViewCompat import com.facebook.react.uimanager.BackgroundStyleApplicator +import com.facebook.react.uimanager.ReactCompoundView import com.facebook.react.uimanager.style.Overflow +import com.facebook.react.views.text.internal.span.ReactTagSpan import kotlin.collections.ArrayList +import kotlin.math.roundToInt /** * A custom version of Android's TextView, providing React Native with lower-level hooks for text @@ -33,7 +36,7 @@ import kotlin.collections.ArrayList * existing layout, previously generated for measurement by Fabric, to ensure consistency of * measurements, and avoid duplicate work. */ -internal class PreparedLayoutTextView(context: Context) : ViewGroup(context) { +internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), ReactCompoundView { private var clickableSpans: List = emptyList() private var selection: TextSelection? = null @@ -143,7 +146,6 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context) { invalidate() } - // T222163602: We should reconcile this hit testing with ReactCompoundView hit testing override fun onTouchEvent(event: MotionEvent): Boolean { if (!isEnabled || clickableSpans.isEmpty()) { return super.onTouchEvent(event) @@ -322,4 +324,21 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context) { return spans } } + + override fun reactTagForTouch(touchX: Float, touchY: Float): Int { + val offset = getTextOffsetAt(touchX.roundToInt(), touchY.roundToInt()) + if (offset < 0) { + return id + } + + val spanned = text as? Spanned ?: return id + val reactSpans = spanned.getSpans(offset, offset, ReactTagSpan::class.java) + check(reactSpans.size <= 1) + + return if (reactSpans.isNotEmpty()) { + reactSpans[0].reactTag + } else { + id + } + } } diff --git a/packages/rn-tester/js/examples/Text/TextExample.android.js b/packages/rn-tester/js/examples/Text/TextExample.android.js index 070e3fd7596..271156139b7 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.android.js +++ b/packages/rn-tester/js/examples/Text/TextExample.android.js @@ -62,7 +62,7 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> { fontSize: this.state.fontSize, }; return ( - + Tap the controls below to change attributes. @@ -75,11 +75,12 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> { - Toggle Weight - {' (with highlight onPress)'} + + Toggle Weight + - - Increase Size (suppressHighlighting true) + + Increase Size ); @@ -1478,9 +1479,7 @@ const examples = [ { title: 'Toggling Attributes', name: 'togglingAttributes', - render(): React.Node { - return ; - }, + render: AttributeToggler, }, { title: 'backgroundColor attribute', diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index 1515727f37e..869b111297e 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -117,7 +117,7 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> { fontSize: this.state.fontSize, }; return ( - + {/* $FlowFixMe[incompatible-type] */} Tap the controls below to change attributes. @@ -130,12 +130,14 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> { + onPress={this.toggleWeight} + testID="toggle-weight"> Toggle Weight + onPress={this.increaseSize} + testID="increase-size"> Increase Size @@ -1064,9 +1066,8 @@ const examples = [ }, { title: 'Toggling Attributes', - render: function (): React.MixedElement { - return ; - }, + name: 'togglingAttributes', + render: AttributeToggler, }, { title: 'backgroundColor attribute',