mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement ReactCompoundView for PreparedLayoutTextView (#51551)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/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
This commit is contained in:
committed by
Facebook GitHub Bot
parent
83226431c2
commit
6e8ce60bd4
+21
-2
@@ -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<ClickableSpan> = 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
|
||||
fontSize: this.state.fontSize,
|
||||
};
|
||||
return (
|
||||
<View>
|
||||
<View testID="text-with-toggle-attributes">
|
||||
<RNTesterText style={curStyle}>
|
||||
Tap the controls below to change attributes.
|
||||
</RNTesterText>
|
||||
@@ -75,11 +75,12 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
|
||||
</RNTesterText>
|
||||
</RNTesterText>
|
||||
<RNTesterText>
|
||||
<RNTesterText onPress={this.toggleWeight}>Toggle Weight</RNTesterText>
|
||||
{' (with highlight onPress)'}
|
||||
<RNTesterText onPress={this.toggleWeight} testID="toggle-weight">
|
||||
Toggle Weight
|
||||
</RNTesterText>
|
||||
</RNTesterText>
|
||||
<RNTesterText onPress={this.increaseSize} suppressHighlighting={true}>
|
||||
Increase Size (suppressHighlighting true)
|
||||
<RNTesterText onPress={this.increaseSize} testID="increase-size">
|
||||
Increase Size
|
||||
</RNTesterText>
|
||||
</View>
|
||||
);
|
||||
@@ -1478,9 +1479,7 @@ const examples = [
|
||||
{
|
||||
title: 'Toggling Attributes',
|
||||
name: 'togglingAttributes',
|
||||
render(): React.Node {
|
||||
return <AttributeToggler />;
|
||||
},
|
||||
render: AttributeToggler,
|
||||
},
|
||||
{
|
||||
title: 'backgroundColor attribute',
|
||||
|
||||
@@ -117,7 +117,7 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
|
||||
fontSize: this.state.fontSize,
|
||||
};
|
||||
return (
|
||||
<View>
|
||||
<View testID="text-with-toggle-attributes">
|
||||
{/* $FlowFixMe[incompatible-type] */}
|
||||
<Text style={curStyle}>
|
||||
Tap the controls below to change attributes.
|
||||
@@ -130,12 +130,14 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
|
||||
</Text>
|
||||
<Text
|
||||
style={{backgroundColor: '#ffaaaa', marginTop: 5}}
|
||||
onPress={this.toggleWeight}>
|
||||
onPress={this.toggleWeight}
|
||||
testID="toggle-weight">
|
||||
Toggle Weight
|
||||
</Text>
|
||||
<Text
|
||||
style={{backgroundColor: '#aaaaff', marginTop: 5}}
|
||||
onPress={this.increaseSize}>
|
||||
onPress={this.increaseSize}
|
||||
testID="increase-size">
|
||||
Increase Size
|
||||
</Text>
|
||||
</View>
|
||||
@@ -1064,9 +1066,8 @@ const examples = [
|
||||
},
|
||||
{
|
||||
title: 'Toggling Attributes',
|
||||
render: function (): React.MixedElement {
|
||||
return <AttributeToggler />;
|
||||
},
|
||||
name: 'togglingAttributes',
|
||||
render: AttributeToggler,
|
||||
},
|
||||
{
|
||||
title: 'backgroundColor attribute',
|
||||
|
||||
Reference in New Issue
Block a user