diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 94e3879b76a608..2a6214f996b8ec 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -6196,7 +6196,7 @@ public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/wi protected fun verifyDrawable (Landroid/graphics/drawable/Drawable;)Z } -public final class com/facebook/react/views/text/ReactTextViewManager : com/facebook/react/views/text/ReactTextAnchorViewManager, com/facebook/react/uimanager/IViewManagerWithChildren, com/facebook/react/views/text/ReactTextViewManagerCallback { +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 { public static final field Companion Lcom/facebook/react/views/text/ReactTextViewManager$Companion; public static final field REACT_CLASS Ljava/lang/String; public fun ()V @@ -6214,9 +6214,26 @@ public final class com/facebook/react/views/text/ReactTextViewManager : com/face public synthetic fun onAfterUpdateTransaction (Landroid/view/View;)V public fun onPostProcessSpannable (Landroid/text/Spannable;)V public synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View; + public final fun setAccessible (Lcom/facebook/react/views/text/ReactTextView;Z)V + public final fun setAdjustFontSizeToFit (Lcom/facebook/react/views/text/ReactTextView;Z)V + public final fun setAndroidHyphenationFrequency (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V + public final fun setBorderColor (Lcom/facebook/react/views/text/ReactTextView;ILjava/lang/Integer;)V + public final fun setBorderRadius (Lcom/facebook/react/views/text/ReactTextView;IF)V + public final fun setBorderStyle (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V + public final fun setBorderWidth (Lcom/facebook/react/views/text/ReactTextView;IF)V + public final fun setDataDetectorType (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V + public final fun setDisabled (Lcom/facebook/react/views/text/ReactTextView;Z)V + public final fun setEllipsizeMode (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V + public final fun setFontSize (Lcom/facebook/react/views/text/ReactTextView;F)V + public final fun setIncludeFontPadding (Lcom/facebook/react/views/text/ReactTextView;Z)V + public final fun setLetterSpacing (Lcom/facebook/react/views/text/ReactTextView;F)V + public final fun setNumberOfLines (Lcom/facebook/react/views/text/ReactTextView;I)V public final fun setOverflow (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V public synthetic fun setPadding (Landroid/view/View;IIII)V public fun setPadding (Lcom/facebook/react/views/text/ReactTextView;IIII)V + public final fun setSelectable (Lcom/facebook/react/views/text/ReactTextView;Z)V + public final fun setSelectionColor (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/Integer;)V + public final fun setTextAlignVertical (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V public synthetic fun updateExtraData (Landroid/view/View;Ljava/lang/Object;)V public fun updateExtraData (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/Object;)V public synthetic fun updateState (Landroid/view/View;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;)Ljava/lang/Object; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.kt deleted file mode 100644 index 12f48e5bc976e7..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.kt +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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.TextUtils -import android.text.util.Linkify -import android.view.Gravity -import com.facebook.common.logging.FLog -import com.facebook.react.common.ReactConstants -import com.facebook.react.common.annotations.UnstableReactNativeAPI -import com.facebook.react.uimanager.BackgroundStyleApplicator -import com.facebook.react.uimanager.BaseViewManager -import com.facebook.react.uimanager.LengthPercentage -import com.facebook.react.uimanager.LengthPercentageType -import com.facebook.react.uimanager.ViewDefaults -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.Companion.fromString -import com.facebook.react.uimanager.style.LogicalEdge -import com.facebook.react.views.text.DefaultStyleValuesUtil.getDefaultTextColorHighlight - -/** - * Previously a superclass of multiple text view managers. Now only used by [ReactTextViewManager]. - * - * This is a "shadowing" view manager, which means that the - * [com.facebook.react.uimanager.NativeViewHierarchyManager] will NOT manage children of native - * [android.widget.TextView] instances instantiated by this manager. Instead we use - * [ReactBaseTextShadowNode] hierarchy to calculate a [android.text.Spannable] text represented the - * whole text subtree. - */ -@UnstableReactNativeAPI -public abstract class ReactTextAnchorViewManager< - @Suppress("DEPRECATION") - C : ReactBaseTextShadowNode? -> : BaseViewManager() { - - @ReactProp(name = "accessible") - internal fun setAccessible(view: ReactTextView, accessible: Boolean) { - view.isFocusable = accessible - } - - // maxLines can only be set in master view (block), doesn't really make sense to set in a span - @ReactProp(name = ViewProps.NUMBER_OF_LINES, defaultInt = ViewDefaults.NUMBER_OF_LINES) - internal fun setNumberOfLines(view: ReactTextView, numberOfLines: Int) { - view.setNumberOfLines(numberOfLines) - } - - @ReactProp(name = ViewProps.ELLIPSIZE_MODE) - internal fun setEllipsizeMode(view: ReactTextView, ellipsizeMode: String?) { - when (ellipsizeMode) { - null, - "tail" -> view.setEllipsizeLocation(TextUtils.TruncateAt.END) - "head" -> view.setEllipsizeLocation(TextUtils.TruncateAt.START) - "middle" -> view.setEllipsizeLocation(TextUtils.TruncateAt.MIDDLE) - "clip" -> view.setEllipsizeLocation(null) - else -> { - FLog.w(ReactConstants.TAG, "Invalid ellipsizeMode: $ellipsizeMode") - view.setEllipsizeLocation(TextUtils.TruncateAt.END) - } - } - } - - @ReactProp(name = ViewProps.ADJUSTS_FONT_SIZE_TO_FIT) - internal fun setAdjustFontSizeToFit(view: ReactTextView, adjustsFontSizeToFit: Boolean) { - view.setAdjustFontSizeToFit(adjustsFontSizeToFit) - } - - @ReactProp(name = ViewProps.FONT_SIZE) - internal fun setFontSize(view: ReactTextView, fontSize: Float) { - view.setFontSize(fontSize) - } - - @ReactProp(name = ViewProps.LETTER_SPACING, defaultFloat = 0f) - internal fun setLetterSpacing(view: ReactTextView, letterSpacing: Float) { - view.letterSpacing = letterSpacing - } - - @ReactProp(name = ViewProps.TEXT_ALIGN_VERTICAL) - internal fun setTextAlignVertical(view: ReactTextView, textAlignVertical: String?) { - when (textAlignVertical) { - null, - "auto" -> view.setGravityVertical(Gravity.NO_GRAVITY) - "top" -> view.setGravityVertical(Gravity.TOP) - "bottom" -> view.setGravityVertical(Gravity.BOTTOM) - "center" -> view.setGravityVertical(Gravity.CENTER_VERTICAL) - else -> { - FLog.w(ReactConstants.TAG, "Invalid textAlignVertical: $textAlignVertical") - view.setGravityVertical(Gravity.NO_GRAVITY) - } - } - } - - @ReactProp(name = "selectable") - internal fun setSelectable(view: ReactTextView, isSelectable: Boolean) { - view.setTextIsSelectable(isSelectable) - } - - @ReactProp(name = "selectionColor", customType = "Color") - internal fun setSelectionColor(view: ReactTextView, color: Int?) { - view.highlightColor = color ?: getDefaultTextColorHighlight(view.context) - } - - @ReactProp(name = "android_hyphenationFrequency") - internal fun setAndroidHyphenationFrequency(view: ReactTextView, frequency: String?) { - when (frequency) { - null, - "none" -> view.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE - "full" -> view.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_FULL - "normal" -> view.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NORMAL - else -> { - FLog.w(ReactConstants.TAG, "Invalid android_hyphenationFrequency: $frequency") - view.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE - } - } - } - - @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, - ) - internal fun setBorderRadius(view: ReactTextView, index: Int, borderRadius: Float) { - val radius = - if (borderRadius.isNaN()) { - null - } else { - LengthPercentage(borderRadius, LengthPercentageType.POINT) - } - BackgroundStyleApplicator.setBorderRadius(view, BorderRadiusProp.values()[index], radius) - } - - @ReactProp(name = "borderStyle") - internal fun setBorderStyle(view: ReactTextView, borderStyle: String?) { - val parsedBorderStyle = if (borderStyle == null) null else 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, - ) - internal fun setBorderWidth(view: ReactTextView, index: Int, width: Float) { - BackgroundStyleApplicator.setBorderWidth(view, LogicalEdge.values()[index], width) - } - - @ReactPropGroup( - names = - [ - "borderColor", - "borderLeftColor", - "borderRightColor", - "borderTopColor", - "borderBottomColor", - ], - customType = "Color", - ) - internal fun setBorderColor(view: ReactTextView, index: Int, color: Int?) { - BackgroundStyleApplicator.setBorderColor(view, LogicalEdge.values()[index], color) - } - - @ReactProp(name = ViewProps.INCLUDE_FONT_PADDING, defaultBoolean = true) - internal fun setIncludeFontPadding(view: ReactTextView, includepad: Boolean) { - view.includeFontPadding = includepad - } - - @ReactProp(name = "disabled", defaultBoolean = false) - internal fun setDisabled(view: ReactTextView, disabled: Boolean) { - view.isEnabled = !disabled - } - - @ReactProp(name = "dataDetectorType") - internal fun setDataDetectorType(view: ReactTextView, type: String?) { - when (type) { - "phoneNumber" -> { - view.setLinkifyMask(Linkify.PHONE_NUMBERS) - return - } - "link" -> { - view.setLinkifyMask(Linkify.WEB_URLS) - return - } - "email" -> { - view.setLinkifyMask(Linkify.EMAIL_ADDRESSES) - return - } - "all" -> { - @Suppress("DEPRECATION") view.setLinkifyMask(Linkify.ALL) - return - } - } - - // "none" case, default, and null type are equivalent. - view.setLinkifyMask(0) - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt index 180f1059463114..a80fcd93566622 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.kt @@ -8,25 +8,39 @@ package com.facebook.react.views.text import android.os.Build +import android.text.Layout import android.text.Spannable +import android.text.TextUtils +import android.text.util.Linkify +import android.view.Gravity +import com.facebook.common.logging.FLog import com.facebook.react.R +import com.facebook.react.common.ReactConstants import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.common.mapbuffer.MapBuffer import com.facebook.react.internal.SystraceSection import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.BackgroundStyleApplicator +import com.facebook.react.uimanager.BaseViewManager import com.facebook.react.uimanager.IViewManagerWithChildren +import com.facebook.react.uimanager.LengthPercentage +import com.facebook.react.uimanager.LengthPercentageType import com.facebook.react.uimanager.ReactStylesDiffMap import com.facebook.react.uimanager.StateWrapper import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewDefaults +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.Companion.fromString +import com.facebook.react.uimanager.style.LogicalEdge +import com.facebook.react.views.text.DefaultStyleValuesUtil.getDefaultTextColorHighlight import com.facebook.react.views.text.internal.span.TextInlineImageSpan import java.util.HashMap -/** - * Concrete class for [ReactTextAnchorViewManager] which represents view managers of anchor `` - * nodes. - */ +/** View manager for `` nodes. */ @Suppress("DEPRECATION") @ReactModule(name = ReactTextViewManager.REACT_CLASS) @OptIn(UnstableReactNativeAPI::class) @@ -35,7 +49,7 @@ public class ReactTextViewManager public constructor( protected var reactTextViewManagerCallback: ReactTextViewManagerCallback? = null ) : - ReactTextAnchorViewManager(), + BaseViewManager(), IViewManagerWithChildren, ReactTextViewManagerCallback { init { @@ -53,7 +67,7 @@ public constructor( if (preparedView != null) { // Resets background and borders preparedView.recycleView() - // Defaults from ReactTextAnchorViewManager + // Reset selection color to default setSelectionColor(preparedView, null) } return preparedView @@ -186,6 +200,180 @@ public constructor( view.setOverflow(overflow) } + @ReactProp(name = "accessible") + public fun setAccessible(view: ReactTextView, accessible: Boolean) { + view.isFocusable = accessible + } + + // maxLines can only be set in master view (block), doesn't really make sense to set in a span + @ReactProp(name = ViewProps.NUMBER_OF_LINES, defaultInt = ViewDefaults.NUMBER_OF_LINES) + public fun setNumberOfLines(view: ReactTextView, numberOfLines: Int) { + view.setNumberOfLines(numberOfLines) + } + + @ReactProp(name = ViewProps.ELLIPSIZE_MODE) + public fun setEllipsizeMode(view: ReactTextView, ellipsizeMode: String?) { + when (ellipsizeMode) { + null, + "tail" -> view.setEllipsizeLocation(TextUtils.TruncateAt.END) + "head" -> view.setEllipsizeLocation(TextUtils.TruncateAt.START) + "middle" -> view.setEllipsizeLocation(TextUtils.TruncateAt.MIDDLE) + "clip" -> view.setEllipsizeLocation(null) + else -> { + FLog.w(ReactConstants.TAG, "Invalid ellipsizeMode: $ellipsizeMode") + view.setEllipsizeLocation(TextUtils.TruncateAt.END) + } + } + } + + @ReactProp(name = ViewProps.ADJUSTS_FONT_SIZE_TO_FIT) + public fun setAdjustFontSizeToFit(view: ReactTextView, adjustsFontSizeToFit: Boolean) { + view.setAdjustFontSizeToFit(adjustsFontSizeToFit) + } + + @ReactProp(name = ViewProps.FONT_SIZE) + public fun setFontSize(view: ReactTextView, fontSize: Float) { + view.setFontSize(fontSize) + } + + @ReactProp(name = ViewProps.LETTER_SPACING, defaultFloat = 0f) + public fun setLetterSpacing(view: ReactTextView, letterSpacing: Float) { + view.letterSpacing = letterSpacing + } + + @ReactProp(name = ViewProps.TEXT_ALIGN_VERTICAL) + public fun setTextAlignVertical(view: ReactTextView, textAlignVertical: String?) { + when (textAlignVertical) { + null, + "auto" -> view.setGravityVertical(Gravity.NO_GRAVITY) + "top" -> view.setGravityVertical(Gravity.TOP) + "bottom" -> view.setGravityVertical(Gravity.BOTTOM) + "center" -> view.setGravityVertical(Gravity.CENTER_VERTICAL) + else -> { + FLog.w(ReactConstants.TAG, "Invalid textAlignVertical: $textAlignVertical") + view.setGravityVertical(Gravity.NO_GRAVITY) + } + } + } + + @ReactProp(name = "selectable") + public fun setSelectable(view: ReactTextView, isSelectable: Boolean) { + view.setTextIsSelectable(isSelectable) + } + + @ReactProp(name = "selectionColor", customType = "Color") + public fun setSelectionColor(view: ReactTextView, color: Int?) { + view.highlightColor = color ?: getDefaultTextColorHighlight(view.context) + } + + @ReactProp(name = "android_hyphenationFrequency") + public fun setAndroidHyphenationFrequency(view: ReactTextView, frequency: String?) { + when (frequency) { + null, + "none" -> view.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE + "full" -> view.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_FULL + "normal" -> view.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NORMAL + else -> { + FLog.w(ReactConstants.TAG, "Invalid android_hyphenationFrequency: $frequency") + view.hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE + } + } + } + + @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: ReactTextView, index: Int, borderRadius: Float) { + 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: ReactTextView, borderStyle: String?) { + val parsedBorderStyle = if (borderStyle == null) null else 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: ReactTextView, index: Int, width: Float) { + BackgroundStyleApplicator.setBorderWidth(view, LogicalEdge.values()[index], width) + } + + @ReactPropGroup( + names = + [ + "borderColor", + "borderLeftColor", + "borderRightColor", + "borderTopColor", + "borderBottomColor", + ], + customType = "Color", + ) + public fun setBorderColor(view: ReactTextView, index: Int, color: Int?) { + BackgroundStyleApplicator.setBorderColor(view, LogicalEdge.values()[index], color) + } + + @ReactProp(name = ViewProps.INCLUDE_FONT_PADDING, defaultBoolean = true) + public fun setIncludeFontPadding(view: ReactTextView, includepad: Boolean) { + view.includeFontPadding = includepad + } + + @ReactProp(name = "disabled", defaultBoolean = false) + public fun setDisabled(view: ReactTextView, disabled: Boolean) { + view.isEnabled = !disabled + } + + @ReactProp(name = "dataDetectorType") + public fun setDataDetectorType(view: ReactTextView, type: String?) { + when (type) { + "phoneNumber" -> { + view.setLinkifyMask(Linkify.PHONE_NUMBERS) + return + } + "link" -> { + view.setLinkifyMask(Linkify.WEB_URLS) + return + } + "email" -> { + view.setLinkifyMask(Linkify.EMAIL_ADDRESSES) + return + } + "all" -> { + @Suppress("DEPRECATION") view.setLinkifyMask(Linkify.ALL) + return + } + } + + // "none" case, default, and null type are equivalent. + view.setLinkifyMask(0) + } + public companion object { private const val TX_STATE_KEY_ATTRIBUTED_STRING: Short = 0 private const val TX_STATE_KEY_PARAGRAPH_ATTRIBUTES: Short = 1