Skip to content

Commit 9846369

Browse files
NickGerlemanmeta-codesync[bot]
authored andcommitted
Add SelectableParagraph component and SelectableTextViewManager
Summary: Add the native components needed to route selectable text through ReactTextView instead of PreparedLayoutTextView when enablePreparedTextLayout is on. C++ side: Add SelectableParagraphShadowNode (inherits ParagraphShadowNode) and SelectableParagraphComponentDescriptor (inherits BaseParagraphComponentDescriptor). Register in CoreComponentsRegistry and componentNameByReactViewName. Android side: Make ReactTextViewManager open so it can be subclassed. Add getReactTextUpdateFromPreparedLayout to handle ReferenceStateWrapper holding PreparedLayout. Create SelectableTextViewManager (extends ReactTextViewManager, registered as RCTSelectableText). Add FabricNameComponentMapping entry for SelectableParagraph -> RCTSelectableText. No JS code references RCTSelectableText yet, so the new components are inert. Changelog: [Internal] Differential Revision: D93829400
1 parent fc80afe commit 9846369

File tree

8 files changed

+138
-2
lines changed

8 files changed

+138
-2
lines changed

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6110,7 +6110,7 @@ public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/wi
61106110
public fun updateView ()V
61116111
}
61126112

6113-
public final class com/facebook/react/views/text/ReactTextViewManager : com/facebook/react/uimanager/BaseViewManager, com/facebook/react/uimanager/IViewManagerWithChildren, com/facebook/react/views/text/ReactTextViewManagerCallback {
6113+
public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/react/uimanager/BaseViewManager, com/facebook/react/uimanager/IViewManagerWithChildren, com/facebook/react/views/text/ReactTextViewManagerCallback {
61146114
public static final field Companion Lcom/facebook/react/views/text/ReactTextViewManager$Companion;
61156115
public static final field REACT_CLASS Ljava/lang/String;
61166116
public fun <init> ()V
@@ -6123,11 +6123,14 @@ public final class com/facebook/react/views/text/ReactTextViewManager : com/face
61236123
public fun createViewInstance (Lcom/facebook/react/uimanager/ThemedReactContext;)Lcom/facebook/react/views/text/ReactTextView;
61246124
public fun getExportedCustomDirectEventTypeConstants ()Ljava/util/Map;
61256125
public fun getName ()Ljava/lang/String;
6126+
protected final fun getReactTextViewManagerCallback ()Lcom/facebook/react/views/text/ReactTextViewManagerCallback;
61266127
public fun getShadowNodeClass ()Ljava/lang/Class;
61276128
public fun needsCustomLayoutForChildren ()Z
61286129
public synthetic fun onAfterUpdateTransaction (Landroid/view/View;)V
6130+
protected fun onAfterUpdateTransaction (Lcom/facebook/react/views/text/ReactTextView;)V
61296131
public fun onPostProcessSpannable (Landroid/text/Spannable;)V
61306132
public synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View;
6133+
protected fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/views/text/ReactTextView;)Lcom/facebook/react/views/text/ReactTextView;
61316134
public final fun setAccessible (Lcom/facebook/react/views/text/ReactTextView;Z)V
61326135
public final fun setAdjustFontSizeToFit (Lcom/facebook/react/views/text/ReactTextView;Z)V
61336136
public final fun setAndroidHyphenationFrequency (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
@@ -6145,6 +6148,7 @@ public final class com/facebook/react/views/text/ReactTextViewManager : com/face
61456148
public final fun setOverflow (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
61466149
public synthetic fun setPadding (Landroid/view/View;IIII)V
61476150
public fun setPadding (Lcom/facebook/react/views/text/ReactTextView;IIII)V
6151+
protected final fun setReactTextViewManagerCallback (Lcom/facebook/react/views/text/ReactTextViewManagerCallback;)V
61486152
public final fun setSelectable (Lcom/facebook/react/views/text/ReactTextView;Z)V
61496153
public final fun setSelectionColor (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/Integer;)V
61506154
public final fun setTextAlignVertical (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
@@ -6153,6 +6157,7 @@ public final class com/facebook/react/views/text/ReactTextViewManager : com/face
61536157
public synthetic fun updateState (Landroid/view/View;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;)Ljava/lang/Object;
61546158
public fun updateState (Lcom/facebook/react/views/text/ReactTextView;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;)Ljava/lang/Object;
61556159
public synthetic fun updateViewAccessibility (Landroid/view/View;)V
6160+
protected fun updateViewAccessibility (Lcom/facebook/react/views/text/ReactTextView;)V
61566161
}
61576162

61586163
public final class com/facebook/react/views/text/ReactTextViewManager$Companion {
@@ -6170,6 +6175,9 @@ public final class com/facebook/react/views/text/ReactTypefaceUtils {
61706175
public static final fun parseFontWeight (Ljava/lang/String;)I
61716176
}
61726177

6178+
public final class com/facebook/react/views/text/SelectableTextViewManager$Companion {
6179+
}
6180+
61736181
public final class com/facebook/react/views/text/TextAttributeProps {
61746182
public static final field Companion Lcom/facebook/react/views/text/TextAttributeProps$Companion;
61756183
public static final field TA_KEY_ACCESSIBILITY_ROLE I

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/FabricNameComponentMapping.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal object FabricNameComponentMapping {
1818
"Slider" to "RCTSlider",
1919
"ModalHostView" to "RCTModalHostView",
2020
"Paragraph" to "RCTText",
21+
"SelectableParagraph" to "RCTSelectableText",
2122
"Text" to "RCTText",
2223
"RawText" to "RCTRawText",
2324
"ActivityIndicatorView" to "AndroidProgressBar",

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package com.facebook.react.views.text
1212
import android.os.Build
1313
import android.text.Layout
1414
import android.text.Spannable
15+
import android.text.SpannableString
1516
import android.text.Spanned
1617
import android.text.TextUtils
1718
import android.text.util.Linkify
@@ -31,6 +32,7 @@ import com.facebook.react.uimanager.LayoutShadowNode
3132
import com.facebook.react.uimanager.LengthPercentage
3233
import com.facebook.react.uimanager.LengthPercentageType
3334
import com.facebook.react.uimanager.ReactStylesDiffMap
35+
import com.facebook.react.uimanager.ReferenceStateWrapper
3436
import com.facebook.react.uimanager.StateWrapper
3537
import com.facebook.react.uimanager.ThemedReactContext
3638
import com.facebook.react.uimanager.ViewDefaults
@@ -46,7 +48,7 @@ import java.util.HashMap
4648
/** View manager for `<Text>` nodes. */
4749
@ReactModule(name = ReactTextViewManager.REACT_CLASS)
4850
@OptIn(UnstableReactNativeAPI::class)
49-
public class ReactTextViewManager
51+
public open class ReactTextViewManager
5052
@JvmOverloads
5153
public constructor(
5254
protected var reactTextViewManagerCallback: ReactTextViewManagerCallback? = null
@@ -131,6 +133,11 @@ public constructor(
131133
stateWrapper: StateWrapper,
132134
): Any? {
133135
SystraceSection("ReactTextViewManager.updateState").use { s ->
136+
val refState = (stateWrapper as? ReferenceStateWrapper)?.stateDataReference
137+
if (refState is PreparedLayout) {
138+
return getReactTextUpdateFromPreparedLayout(view, refState)
139+
}
140+
134141
val stateMapBuffer = stateWrapper.stateDataMapBuffer
135142
return if (stateMapBuffer != null) {
136143
getReactTextUpdate(view, props, stateMapBuffer)
@@ -176,6 +183,34 @@ public constructor(
176183
)
177184
}
178185

186+
/**
187+
* Constructs a [ReactTextUpdate] from a [PreparedLayout] received via [ReferenceStateWrapper].
188+
*/
189+
private fun getReactTextUpdateFromPreparedLayout(
190+
view: ReactTextView,
191+
preparedLayout: PreparedLayout,
192+
): ReactTextUpdate {
193+
val layout = preparedLayout.layout
194+
val text = layout.text
195+
val spanned = if (text is Spannable) text else SpannableString(text)
196+
view.setSpanned(spanned)
197+
198+
val textAlign =
199+
when (layout.alignment) {
200+
Layout.Alignment.ALIGN_CENTER -> Gravity.CENTER_HORIZONTAL
201+
Layout.Alignment.ALIGN_OPPOSITE -> Gravity.END
202+
else -> Gravity.START
203+
}
204+
205+
return ReactTextUpdate(
206+
spanned,
207+
-1,
208+
textAlign,
209+
Layout.BREAK_STRATEGY_HIGH_QUALITY,
210+
0,
211+
)
212+
}
213+
179214
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any>? {
180215
val baseEventTypeConstants = super.getExportedCustomDirectEventTypeConstants()
181216
val eventTypeConstants = baseEventTypeConstants ?: HashMap()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.views.text
9+
10+
import com.facebook.react.common.annotations.UnstableReactNativeAPI
11+
12+
/**
13+
* A [ReactTextViewManager] registered under the name "RCTSelectableText". Used to route selectable
14+
* text through [ReactTextView] (a real [android.widget.TextView]) instead of
15+
* [PreparedLayoutTextView] when enablePreparedTextLayout is on, since [PreparedLayoutTextView] does
16+
* not support native text selection.
17+
*/
18+
@UnstableReactNativeAPI
19+
public class SelectableTextViewManager
20+
@JvmOverloads
21+
public constructor(reactTextViewManagerCallback: ReactTextViewManagerCallback? = null) :
22+
ReactTextViewManager(reactTextViewManagerCallback) {
23+
24+
override fun getName(): String = REACT_CLASS
25+
26+
public companion object {
27+
public const val REACT_CLASS: String = "RCTSelectableText"
28+
}
29+
}

packages/react-native/ReactAndroid/src/main/jni/react/fabric/CoreComponentsRegistry.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <react/renderer/components/scrollview/ScrollViewComponentDescriptor.h>
2222
#include <react/renderer/components/text/ParagraphComponentDescriptor.h>
2323
#include <react/renderer/components/text/RawTextComponentDescriptor.h>
24+
#include <react/renderer/components/text/SelectableParagraphComponentDescriptor.h>
2425
#include <react/renderer/components/text/TextComponentDescriptor.h>
2526
#include <react/renderer/components/view/LayoutConformanceComponentDescriptor.h>
2627
#include <react/renderer/components/view/ViewComponentDescriptor.h>
@@ -71,6 +72,9 @@ void addCoreComponents(
7172
AndroidHorizontalScrollContentViewComponentDescriptor>());
7273
providerRegistry->add(
7374
concreteComponentDescriptorProvider<ParagraphComponentDescriptor>());
75+
providerRegistry->add(
76+
concreteComponentDescriptorProvider<
77+
SelectableParagraphComponentDescriptor>());
7478
providerRegistry->add(
7579
concreteComponentDescriptorProvider<
7680
AndroidDrawerLayoutComponentDescriptor>());

packages/react-native/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ std::string componentNameByReactViewName(std::string viewName) {
2727
if (viewName == "Text") {
2828
return "Paragraph";
2929
}
30+
if (viewName == "SelectableText") {
31+
return "SelectableParagraph";
32+
}
3033

3134
if (viewName == "VirtualText") {
3235
return "Text";
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <react/renderer/components/text/BaseParagraphComponentDescriptor.h>
11+
#include <react/renderer/components/text/SelectableParagraphShadowNode.h>
12+
13+
namespace facebook::react {
14+
/*
15+
* Descriptor for <SelectableParagraph> component, which may render to a
16+
* different native view than <Paragraph>.
17+
*/
18+
class SelectableParagraphComponentDescriptor final
19+
: public BaseParagraphComponentDescriptor<SelectableParagraphShadowNode> {
20+
public:
21+
using BaseParagraphComponentDescriptor::BaseParagraphComponentDescriptor;
22+
};
23+
24+
} // namespace facebook::react
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <react/renderer/components/text/ParagraphShadowNode.h>
11+
12+
namespace facebook::react {
13+
14+
/*
15+
* ShadowNode for selectable Paragraph components, which may map to different native component than Paragraph.
16+
*/
17+
class SelectableParagraphShadowNode : public ParagraphShadowNode {
18+
public:
19+
using ParagraphShadowNode::ParagraphShadowNode;
20+
21+
static constexpr ComponentName Name()
22+
{
23+
return "SelectableParagraph";
24+
}
25+
26+
static ComponentHandle Handle()
27+
{
28+
return ComponentHandle(Name());
29+
}
30+
};
31+
32+
} // namespace facebook::react

0 commit comments

Comments
 (0)