Skip to content

Commit 6e8ce60

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Implement ReactCompoundView for PreparedLayoutTextView (#51551)
Summary: Pull Request resolved: #51551 This allows hit RN's hit testing to find nested spans, and click them. This mechanism is fully separate from the one used by a11y virtual views, and ClickableSpan, such as those added for links via dataDetectorType (and also the `link` role). When we do have a link accessibilityRole, that ClickableSpan hit test seems to prevent the React one, and we only activate the onPress once (but then add keyboard interaction, press visual, and add to the a11y tree). Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D75257326 fbshipit-source-id: 0c693f581ec121cf4b4e3e2040d141985118224f
1 parent 8322643 commit 6e8ce60

3 files changed

Lines changed: 35 additions & 16 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,19 @@ import androidx.annotation.DoNotInline
2424
import androidx.annotation.RequiresApi
2525
import androidx.core.view.ViewCompat
2626
import com.facebook.react.uimanager.BackgroundStyleApplicator
27+
import com.facebook.react.uimanager.ReactCompoundView
2728
import com.facebook.react.uimanager.style.Overflow
29+
import com.facebook.react.views.text.internal.span.ReactTagSpan
2830
import kotlin.collections.ArrayList
31+
import kotlin.math.roundToInt
2932

3033
/**
3134
* A custom version of Android's TextView, providing React Native with lower-level hooks for text
3235
* drawing, such as fine-grained control over clipping. PreparedLayoutTextView directly draws an
3336
* existing layout, previously generated for measurement by Fabric, to ensure consistency of
3437
* measurements, and avoid duplicate work.
3538
*/
36-
internal class PreparedLayoutTextView(context: Context) : ViewGroup(context) {
39+
internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), ReactCompoundView {
3740

3841
private var clickableSpans: List<ClickableSpan> = emptyList()
3942
private var selection: TextSelection? = null
@@ -143,7 +146,6 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context) {
143146
invalidate()
144147
}
145148

146-
// T222163602: We should reconcile this hit testing with ReactCompoundView hit testing
147149
override fun onTouchEvent(event: MotionEvent): Boolean {
148150
if (!isEnabled || clickableSpans.isEmpty()) {
149151
return super.onTouchEvent(event)
@@ -322,4 +324,21 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context) {
322324
return spans
323325
}
324326
}
327+
328+
override fun reactTagForTouch(touchX: Float, touchY: Float): Int {
329+
val offset = getTextOffsetAt(touchX.roundToInt(), touchY.roundToInt())
330+
if (offset < 0) {
331+
return id
332+
}
333+
334+
val spanned = text as? Spanned ?: return id
335+
val reactSpans = spanned.getSpans(offset, offset, ReactTagSpan::class.java)
336+
check(reactSpans.size <= 1)
337+
338+
return if (reactSpans.isNotEmpty()) {
339+
reactSpans[0].reactTag
340+
} else {
341+
id
342+
}
343+
}
325344
}

packages/rn-tester/js/examples/Text/TextExample.android.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
6262
fontSize: this.state.fontSize,
6363
};
6464
return (
65-
<View>
65+
<View testID="text-with-toggle-attributes">
6666
<RNTesterText style={curStyle}>
6767
Tap the controls below to change attributes.
6868
</RNTesterText>
@@ -75,11 +75,12 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
7575
</RNTesterText>
7676
</RNTesterText>
7777
<RNTesterText>
78-
<RNTesterText onPress={this.toggleWeight}>Toggle Weight</RNTesterText>
79-
{' (with highlight onPress)'}
78+
<RNTesterText onPress={this.toggleWeight} testID="toggle-weight">
79+
Toggle Weight
80+
</RNTesterText>
8081
</RNTesterText>
81-
<RNTesterText onPress={this.increaseSize} suppressHighlighting={true}>
82-
Increase Size (suppressHighlighting true)
82+
<RNTesterText onPress={this.increaseSize} testID="increase-size">
83+
Increase Size
8384
</RNTesterText>
8485
</View>
8586
);
@@ -1478,9 +1479,7 @@ const examples = [
14781479
{
14791480
title: 'Toggling Attributes',
14801481
name: 'togglingAttributes',
1481-
render(): React.Node {
1482-
return <AttributeToggler />;
1483-
},
1482+
render: AttributeToggler,
14841483
},
14851484
{
14861485
title: 'backgroundColor attribute',

packages/rn-tester/js/examples/Text/TextExample.ios.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
117117
fontSize: this.state.fontSize,
118118
};
119119
return (
120-
<View>
120+
<View testID="text-with-toggle-attributes">
121121
{/* $FlowFixMe[incompatible-type] */}
122122
<Text style={curStyle}>
123123
Tap the controls below to change attributes.
@@ -130,12 +130,14 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
130130
</Text>
131131
<Text
132132
style={{backgroundColor: '#ffaaaa', marginTop: 5}}
133-
onPress={this.toggleWeight}>
133+
onPress={this.toggleWeight}
134+
testID="toggle-weight">
134135
Toggle Weight
135136
</Text>
136137
<Text
137138
style={{backgroundColor: '#aaaaff', marginTop: 5}}
138-
onPress={this.increaseSize}>
139+
onPress={this.increaseSize}
140+
testID="increase-size">
139141
Increase Size
140142
</Text>
141143
</View>
@@ -1064,9 +1066,8 @@ const examples = [
10641066
},
10651067
{
10661068
title: 'Toggling Attributes',
1067-
render: function (): React.MixedElement {
1068-
return <AttributeToggler />;
1069-
},
1069+
name: 'togglingAttributes',
1070+
render: AttributeToggler,
10701071
},
10711072
{
10721073
title: 'backgroundColor attribute',

0 commit comments

Comments
 (0)