mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
PreparedLayoutTextViewManager (#50923)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50923 This implements the view manager for `PreparedLayoutTextView`, originating by taking the view managers composing `ReactTextView`, converting to Kotlin, and removing everything no longer needed. In Facsimile, anything influencing text appearance is applied earlier, when creating the Fabric layout, so there are many less setters here. Most visual attributes are instead present in the state we are presenting. We have tasks for some of these, that need to be reimplemented, as they do not currently influence the Spannable being measured. That includes e.g. `ReactTextViewManagerCallback`, used for injection, and `dataDetectorType` for linkifying Spannable. Changelog: [Internal] Reviewed By: rshest Differential Revision: D73287706 fbshipit-source-id: 938b57d4e443f6b8bb127e17b47cc371f31a416d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
6465cb1b0b
commit
fd126b36b7
+219
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.text
|
||||
|
||||
import android.text.Layout
|
||||
import android.text.Spannable
|
||||
import android.view.View
|
||||
import com.facebook.react.R
|
||||
import com.facebook.react.internal.SystraceSection
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.uimanager.BackgroundStyleApplicator
|
||||
import com.facebook.react.uimanager.BaseViewManager
|
||||
import com.facebook.react.uimanager.IViewGroupManager
|
||||
import com.facebook.react.uimanager.LayoutShadowNode
|
||||
import com.facebook.react.uimanager.LengthPercentage
|
||||
import com.facebook.react.uimanager.LengthPercentageType
|
||||
import com.facebook.react.uimanager.ReactStylesDiffMap
|
||||
import com.facebook.react.uimanager.ReferenceStateWrapper
|
||||
import com.facebook.react.uimanager.StateWrapper
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.ViewProps
|
||||
import com.facebook.react.uimanager.annotations.ReactProp
|
||||
import com.facebook.react.uimanager.annotations.ReactPropGroup
|
||||
import com.facebook.react.uimanager.style.BorderRadiusProp
|
||||
import com.facebook.react.uimanager.style.BorderStyle
|
||||
import com.facebook.react.uimanager.style.LogicalEdge
|
||||
import com.facebook.react.uimanager.style.Overflow
|
||||
import com.facebook.react.views.text.ReactTextViewAccessibilityDelegate.AccessibilityLinks
|
||||
import com.facebook.react.views.text.internal.span.ReactClickableSpan
|
||||
import java.util.HashMap
|
||||
|
||||
@ReactModule(name = PreparedLayoutTextViewManager.REACT_CLASS)
|
||||
internal class PreparedLayoutTextViewManager :
|
||||
BaseViewManager<PreparedLayoutTextView, LayoutShadowNode>(),
|
||||
IViewGroupManager<PreparedLayoutTextView> {
|
||||
|
||||
init {
|
||||
setupViewRecycling()
|
||||
}
|
||||
|
||||
override fun prepareToRecycleView(
|
||||
reactContext: ThemedReactContext,
|
||||
view: PreparedLayoutTextView
|
||||
): PreparedLayoutTextView? {
|
||||
val preparedView = super.prepareToRecycleView(reactContext, view)
|
||||
preparedView?.recycleView()
|
||||
return preparedView
|
||||
}
|
||||
|
||||
override fun getName(): String = REACT_CLASS
|
||||
|
||||
override fun updateViewAccessibility(view: PreparedLayoutTextView) {
|
||||
ReactTextViewAccessibilityDelegate.setDelegate(
|
||||
view, view.isFocusable, view.importantForAccessibility)
|
||||
}
|
||||
|
||||
public override fun createViewInstance(context: ThemedReactContext): PreparedLayoutTextView =
|
||||
PreparedLayoutTextView(context)
|
||||
|
||||
override fun updateExtraData(view: PreparedLayoutTextView, extraData: Any) {
|
||||
SystraceSection("PreparedLayoutTextViewManager.updateExtraData").use { _ ->
|
||||
val layout = extraData as Layout
|
||||
view.layout = layout
|
||||
|
||||
// If this text view contains any clickable spans, set a view tag and reset the accessibility
|
||||
// delegate so that these can be picked up by the accessibility system.
|
||||
if (layout.text is Spannable) {
|
||||
val spannableText = layout.text as Spannable
|
||||
|
||||
val clickableSpans =
|
||||
spannableText.getSpans(0, layout.text.length, ReactClickableSpan::class.java)
|
||||
view.setTag(
|
||||
R.id.accessibility_links,
|
||||
if (clickableSpans.size > 0) AccessibilityLinks(clickableSpans, spannableText)
|
||||
else null)
|
||||
ReactTextViewAccessibilityDelegate.resetDelegate(
|
||||
view, view.isFocusable, view.importantForAccessibility)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateState(
|
||||
view: PreparedLayoutTextView,
|
||||
props: ReactStylesDiffMap,
|
||||
stateWrapper: StateWrapper
|
||||
): Any? = (stateWrapper as? ReferenceStateWrapper)?.stateDataReference
|
||||
|
||||
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any>? {
|
||||
val baseEventTypeConstants = super.getExportedCustomDirectEventTypeConstants()
|
||||
val eventTypeConstants = baseEventTypeConstants ?: HashMap()
|
||||
eventTypeConstants.put("topTextLayout", mapOf("registrationName" to "onTextLayout"))
|
||||
return eventTypeConstants
|
||||
}
|
||||
|
||||
@ReactProp(name = "overflow")
|
||||
public fun setOverflow(view: PreparedLayoutTextView, overflow: String?): Unit {
|
||||
view.overflow = overflow?.let { Overflow.fromString(it) } ?: Overflow.HIDDEN
|
||||
}
|
||||
|
||||
@ReactProp(name = "accessible")
|
||||
public fun setAccessible(view: PreparedLayoutTextView, accessible: Boolean): Unit {
|
||||
view.isFocusable = accessible
|
||||
}
|
||||
|
||||
@ReactProp(name = "selectable", defaultBoolean = false)
|
||||
public fun setSelectable(view: PreparedLayoutTextView, isSelectable: Boolean): Unit {
|
||||
// T222052152: Implement fine-grained text selection for PreparedLayoutTextView
|
||||
// view.setTextIsSelectable(isSelectable);
|
||||
}
|
||||
|
||||
@ReactProp(name = "selectionColor", customType = "Color")
|
||||
public fun setSelectionColor(view: PreparedLayoutTextView, color: Int?): Unit {
|
||||
if (color == null) {
|
||||
view.selectionColor = DefaultStyleValuesUtil.getDefaultTextColorHighlight(view.context)
|
||||
} else {
|
||||
view.selectionColor = color
|
||||
}
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names =
|
||||
[
|
||||
ViewProps.BORDER_RADIUS,
|
||||
ViewProps.BORDER_TOP_LEFT_RADIUS,
|
||||
ViewProps.BORDER_TOP_RIGHT_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_RIGHT_RADIUS,
|
||||
ViewProps.BORDER_BOTTOM_LEFT_RADIUS],
|
||||
defaultFloat = Float.NaN)
|
||||
public fun setBorderRadius(view: PreparedLayoutTextView, index: Int, borderRadius: Float): Unit {
|
||||
val radius =
|
||||
if (borderRadius.isNaN()) null
|
||||
else LengthPercentage(borderRadius, LengthPercentageType.POINT)
|
||||
BackgroundStyleApplicator.setBorderRadius(view, BorderRadiusProp.values()[index], radius)
|
||||
}
|
||||
|
||||
@ReactProp(name = "borderStyle")
|
||||
public fun setBorderStyle(view: PreparedLayoutTextView, borderStyle: String?): Unit {
|
||||
val parsedBorderStyle = if (borderStyle == null) null else BorderStyle.fromString(borderStyle)
|
||||
BackgroundStyleApplicator.setBorderStyle(view, parsedBorderStyle)
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names =
|
||||
[
|
||||
ViewProps.BORDER_WIDTH,
|
||||
ViewProps.BORDER_LEFT_WIDTH,
|
||||
ViewProps.BORDER_RIGHT_WIDTH,
|
||||
ViewProps.BORDER_TOP_WIDTH,
|
||||
ViewProps.BORDER_BOTTOM_WIDTH,
|
||||
ViewProps.BORDER_START_WIDTH,
|
||||
ViewProps.BORDER_END_WIDTH],
|
||||
defaultFloat = Float.NaN)
|
||||
public fun setBorderWidth(view: PreparedLayoutTextView, index: Int, width: Float): Unit {
|
||||
BackgroundStyleApplicator.setBorderWidth(view, LogicalEdge.values()[index], width)
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names =
|
||||
[
|
||||
ViewProps.BORDER_COLOR,
|
||||
ViewProps.BORDER_LEFT_COLOR,
|
||||
ViewProps.BORDER_RIGHT_COLOR,
|
||||
ViewProps.BORDER_TOP_COLOR,
|
||||
ViewProps.BORDER_BOTTOM_COLOR,
|
||||
ViewProps.BORDER_START_COLOR,
|
||||
ViewProps.BORDER_END_COLOR,
|
||||
ViewProps.BORDER_BLOCK_COLOR,
|
||||
ViewProps.BORDER_BLOCK_END_COLOR,
|
||||
ViewProps.BORDER_BLOCK_START_COLOR,
|
||||
],
|
||||
customType = "Color")
|
||||
public fun setBorderColor(view: PreparedLayoutTextView, index: Int, color: Int?): Unit {
|
||||
BackgroundStyleApplicator.setBorderColor(view, LogicalEdge.values()[index], color)
|
||||
}
|
||||
|
||||
@ReactProp(name = "disabled", defaultBoolean = false)
|
||||
public fun setDisabled(view: PreparedLayoutTextView, disabled: Boolean): Unit {
|
||||
view.setEnabled(!disabled)
|
||||
}
|
||||
|
||||
override fun setPadding(
|
||||
view: PreparedLayoutTextView,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int
|
||||
): Unit {
|
||||
view.setPadding(left, top, right, bottom)
|
||||
}
|
||||
|
||||
override fun getShadowNodeClass(): Class<out LayoutShadowNode> {
|
||||
throw UnsupportedOperationException(
|
||||
"PreparedLayoutTextViewManager does not use legacy arch shadow nodes")
|
||||
}
|
||||
|
||||
override fun addView(parent: PreparedLayoutTextView, child: View, index: Int) {
|
||||
parent.addView(child, index)
|
||||
}
|
||||
|
||||
override fun getChildAt(parent: PreparedLayoutTextView, index: Int): View? =
|
||||
parent.getChildAt(index)
|
||||
|
||||
override fun removeViewAt(parent: PreparedLayoutTextView, index: Int) {
|
||||
parent.removeViewAt(index)
|
||||
}
|
||||
|
||||
override fun getChildCount(parent: PreparedLayoutTextView): Int = parent.childCount
|
||||
|
||||
override fun needsCustomLayoutForChildren(): Boolean = false
|
||||
|
||||
public companion object {
|
||||
public const val REACT_CLASS: String = "RCTText"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -270,7 +270,7 @@ internal class ReactTextViewAccessibilityDelegate : ReactAccessibilityDelegate {
|
||||
return null
|
||||
}
|
||||
|
||||
public class AccessibilityLinks(spans: Array<ClickableSpan?>, text: Spannable) {
|
||||
public class AccessibilityLinks(spans: Array<out ClickableSpan>, text: Spannable) {
|
||||
private val links: List<AccessibleLink>
|
||||
|
||||
init {
|
||||
|
||||
Reference in New Issue
Block a user