diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js index 0f00224adfff..2db5431fc564 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js @@ -778,8 +778,8 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ elevation?: number, pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only', cursor?: CursorValue, - experimental_boxShadow?: $ReadOnlyArray, - experimental_filter?: $ReadOnlyArray, + experimental_boxShadow?: $ReadOnlyArray | string, + experimental_filter?: $ReadOnlyArray | string, experimental_mixBlendMode?: ____BlendMode_Internal, }>; diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 910d0b3f0f4f..b5532794cb6d 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -8776,8 +8776,8 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ elevation?: number, pointerEvents?: \\"auto\\" | \\"none\\" | \\"box-none\\" | \\"box-only\\", cursor?: CursorValue, - experimental_boxShadow?: $ReadOnlyArray, - experimental_filter?: $ReadOnlyArray, + experimental_boxShadow?: $ReadOnlyArray | string, + experimental_filter?: $ReadOnlyArray | string, experimental_mixBlendMode?: ____BlendMode_Internal, }>; export type ____ViewStyle_Internal = $ReadOnly<{ diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 8afeee79e05f..13455f62aa46 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -3969,6 +3969,22 @@ public abstract interface class com/facebook/react/turbomodule/core/interfaces/T public abstract fun getBindingsInstaller ()Lcom/facebook/react/turbomodule/core/interfaces/BindingsInstallerHolder; } +public final class com/facebook/react/uimanager/BackgroundStyleApplicator { + public static final field INSTANCE Lcom/facebook/react/uimanager/BackgroundStyleApplicator; + public static final fun clipToPaddingBox (Landroid/view/View;Landroid/graphics/Canvas;)V + public static final fun getBackgroundColor (Landroid/view/View;)Ljava/lang/Integer; + public static final fun getBorderColor (Landroid/view/View;Lcom/facebook/react/uimanager/style/LogicalEdge;)Ljava/lang/Integer; + public static final fun getBorderRadius (Landroid/view/View;Lcom/facebook/react/uimanager/style/BorderRadiusProp;)Lcom/facebook/react/uimanager/LengthPercentage; + public static final fun getBorderStyle (Landroid/view/View;)Lcom/facebook/react/uimanager/style/BorderStyle; + public static final fun getBorderWidth (Landroid/view/View;Lcom/facebook/react/uimanager/style/LogicalEdge;)Ljava/lang/Float; + public static final fun setBackgroundColor (Landroid/view/View;Ljava/lang/Integer;)V + public static final fun setBorderColor (Landroid/view/View;Lcom/facebook/react/uimanager/style/LogicalEdge;Ljava/lang/Integer;)V + public static final fun setBorderRadius (Landroid/view/View;Lcom/facebook/react/uimanager/style/BorderRadiusProp;Lcom/facebook/react/uimanager/LengthPercentage;)V + public static final fun setBorderStyle (Landroid/view/View;Lcom/facebook/react/uimanager/style/BorderStyle;)V + public static final fun setBorderWidth (Landroid/view/View;Lcom/facebook/react/uimanager/style/LogicalEdge;Ljava/lang/Float;)V + public static final fun setShadows (Landroid/view/View;Ljava/util/List;)V +} + public abstract class com/facebook/react/uimanager/BaseViewManager : com/facebook/react/uimanager/ViewManager, android/view/View$OnLayoutChangeListener, com/facebook/react/uimanager/BaseViewManagerInterface { public fun ()V public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V @@ -5915,6 +5931,49 @@ public final class com/facebook/react/uimanager/style/BorderRadiusStyle { public fun toString ()Ljava/lang/String; } +public final class com/facebook/react/uimanager/style/BorderStyle : java/lang/Enum { + public static final field Companion Lcom/facebook/react/uimanager/style/BorderStyle$Companion; + public static final field DASHED Lcom/facebook/react/uimanager/style/BorderStyle; + public static final field DOTTED Lcom/facebook/react/uimanager/style/BorderStyle; + public static final field SOLID Lcom/facebook/react/uimanager/style/BorderStyle; + public static final fun fromString (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/BorderStyle; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/BorderStyle; + public static fun values ()[Lcom/facebook/react/uimanager/style/BorderStyle; +} + +public final class com/facebook/react/uimanager/style/BorderStyle$Companion { + public final fun fromString (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/BorderStyle; +} + +public final class com/facebook/react/uimanager/style/BoxShadow { + public static final field Companion Lcom/facebook/react/uimanager/style/BoxShadow$Companion; + public fun (FFLjava/lang/Integer;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Boolean;)V + public synthetic fun (FFLjava/lang/Integer;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Boolean;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()F + public final fun component2 ()F + public final fun component3 ()Ljava/lang/Integer; + public final fun component4 ()Ljava/lang/Float; + public final fun component5 ()Ljava/lang/Float; + public final fun component6 ()Ljava/lang/Boolean; + public final fun copy (FFLjava/lang/Integer;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Boolean;)Lcom/facebook/react/uimanager/style/BoxShadow; + public static synthetic fun copy$default (Lcom/facebook/react/uimanager/style/BoxShadow;FFLjava/lang/Integer;Ljava/lang/Float;Ljava/lang/Float;Ljava/lang/Boolean;ILjava/lang/Object;)Lcom/facebook/react/uimanager/style/BoxShadow; + public fun equals (Ljava/lang/Object;)Z + public final fun getBlurRadius ()Ljava/lang/Float; + public final fun getColor ()Ljava/lang/Integer; + public final fun getInset ()Ljava/lang/Boolean; + public final fun getOffsetX ()F + public final fun getOffsetY ()F + public final fun getSpreadRadius ()Ljava/lang/Float; + public fun hashCode ()I + public static final fun parse (Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/uimanager/style/BoxShadow; + public fun toString ()Ljava/lang/String; +} + +public final class com/facebook/react/uimanager/style/BoxShadow$Companion { + public final fun parse (Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/uimanager/style/BoxShadow; +} + public final class com/facebook/react/uimanager/style/ComputedBorderRadius { public fun ()V public fun (FFFF)V @@ -5945,6 +6004,47 @@ public final class com/facebook/react/uimanager/style/ComputedBorderRadiusProp : public static fun values ()[Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; } +public abstract class com/facebook/react/uimanager/style/LogicalEdge : java/lang/Enum { + public static final field ALL Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field BLOCK Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field BLOCK_END Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field BLOCK_START Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field BOTTOM Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field Companion Lcom/facebook/react/uimanager/style/LogicalEdge$Companion; + public static final field END Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field HORIZONTAL Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field LEFT Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field RIGHT Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field START Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field TOP Lcom/facebook/react/uimanager/style/LogicalEdge; + public static final field VERTICAL Lcom/facebook/react/uimanager/style/LogicalEdge; + public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public static final fun fromSpacingType (I)Lcom/facebook/react/uimanager/style/LogicalEdge; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public abstract fun toSpacingType ()I + public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/LogicalEdge; + public static fun values ()[Lcom/facebook/react/uimanager/style/LogicalEdge; +} + +public final class com/facebook/react/uimanager/style/LogicalEdge$Companion { + public final fun fromSpacingType (I)Lcom/facebook/react/uimanager/style/LogicalEdge; +} + +public final class com/facebook/react/uimanager/style/Overflow : java/lang/Enum { + public static final field Companion Lcom/facebook/react/uimanager/style/Overflow$Companion; + public static final field HIDDEN Lcom/facebook/react/uimanager/style/Overflow; + public static final field SCROLL Lcom/facebook/react/uimanager/style/Overflow; + public static final field VISIBLE Lcom/facebook/react/uimanager/style/Overflow; + public static final fun fromString (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/Overflow; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/Overflow; + public static fun values ()[Lcom/facebook/react/uimanager/style/Overflow; +} + +public final class com/facebook/react/uimanager/style/Overflow$Companion { + public final fun fromString (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/Overflow; +} + public class com/facebook/react/uimanager/util/ReactFindViewUtil { public fun ()V public static fun addViewListener (Lcom/facebook/react/uimanager/util/ReactFindViewUtil$OnViewFoundListener;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index ace6da52e8b8..9bd77823f495 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -76,6 +76,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableAlignItemsBaselineOnFabricIOS(): Boolean = accessor.enableAlignItemsBaselineOnFabricIOS() + /** + * Use BackgroundStyleApplicator in place of other background/border drawing code + */ + @JvmStatic + public fun enableBackgroundStyleApplicator(): Boolean = accessor.enableBackgroundStyleApplicator() + /** * Clean yoga node when does not change. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 0b3ceebbcf2d..e65477e52843 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2a8d5ca8a0ac46b4d3177cb8b12ad7ac>> + * @generated SignedSource<> */ /** @@ -28,6 +28,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var completeReactInstanceCreationOnBgThreadOnAndroidCache: Boolean? = null private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null private var enableAlignItemsBaselineOnFabricIOSCache: Boolean? = null + private var enableBackgroundStyleApplicatorCache: Boolean? = null private var enableCleanTextInputYogaNodeCache: Boolean? = null private var enableFabricRendererExclusivelyCache: Boolean? = null private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null @@ -130,6 +131,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun enableBackgroundStyleApplicator(): Boolean { + var cached = enableBackgroundStyleApplicatorCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableBackgroundStyleApplicator() + enableBackgroundStyleApplicatorCache = cached + } + return cached + } + override fun enableCleanTextInputYogaNode(): Boolean { var cached = enableCleanTextInputYogaNodeCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 48394927c533..757dcaa5dd11 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<5dbc425b8d99c30ea6d5a5896145e1c6>> */ /** @@ -44,6 +44,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableAlignItemsBaselineOnFabricIOS(): Boolean + @DoNotStrip @JvmStatic public external fun enableBackgroundStyleApplicator(): Boolean + @DoNotStrip @JvmStatic public external fun enableCleanTextInputYogaNode(): Boolean @DoNotStrip @JvmStatic public external fun enableFabricRendererExclusively(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index c229e8d69b91..67e890ceb2b0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -39,6 +39,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableAlignItemsBaselineOnFabricIOS(): Boolean = true + override fun enableBackgroundStyleApplicator(): Boolean = false + override fun enableCleanTextInputYogaNode(): Boolean = false override fun enableFabricRendererExclusively(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 6575e5f91460..2797ad95b827 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<82f6d4eda4011bee81810af366f5d677>> + * @generated SignedSource<<54b74f1ba5e46b981b9b4e9a0b6c8dc0>> */ /** @@ -32,6 +32,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var completeReactInstanceCreationOnBgThreadOnAndroidCache: Boolean? = null private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null private var enableAlignItemsBaselineOnFabricIOSCache: Boolean? = null + private var enableBackgroundStyleApplicatorCache: Boolean? = null private var enableCleanTextInputYogaNodeCache: Boolean? = null private var enableFabricRendererExclusivelyCache: Boolean? = null private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null @@ -142,6 +143,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableBackgroundStyleApplicator(): Boolean { + var cached = enableBackgroundStyleApplicatorCache + if (cached == null) { + cached = currentProvider.enableBackgroundStyleApplicator() + accessedFeatureFlags.add("enableBackgroundStyleApplicator") + enableBackgroundStyleApplicatorCache = cached + } + return cached + } + override fun enableCleanTextInputYogaNode(): Boolean { var cached = enableCleanTextInputYogaNodeCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 0184ee6e5772..49f315ac5823 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -39,6 +39,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableAlignItemsBaselineOnFabricIOS(): Boolean + @DoNotStrip public fun enableBackgroundStyleApplicator(): Boolean + @DoNotStrip public fun enableCleanTextInputYogaNode(): Boolean @DoNotStrip public fun enableFabricRendererExclusively(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BackgroundStyleApplicator.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BackgroundStyleApplicator.kt new file mode 100644 index 000000000000..86b872173d07 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BackgroundStyleApplicator.kt @@ -0,0 +1,185 @@ +/* + * 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.uimanager + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Rect +import android.view.View +import androidx.annotation.ColorInt +import androidx.annotation.RequiresApi +import com.facebook.react.common.annotations.UnstableReactNativeAPI +import com.facebook.react.uimanager.drawable.CSSBackgroundDrawable +import com.facebook.react.uimanager.drawable.CompositeBackgroundDrawable +import com.facebook.react.uimanager.drawable.InsetBoxShadowDrawable +import com.facebook.react.uimanager.drawable.OutsetBoxShadowDrawable +import com.facebook.react.uimanager.style.BorderRadiusProp +import com.facebook.react.uimanager.style.BorderStyle +import com.facebook.react.uimanager.style.BoxShadow +import com.facebook.react.uimanager.style.LogicalEdge + +/** + * BackgroundStyleApplicator is responsible for applying backgrounds, borders, and related effects, + * to an Android view + */ +@OptIn(UnstableReactNativeAPI::class) +public object BackgroundStyleApplicator { + + @JvmStatic + public fun setBackgroundColor(view: View, @ColorInt color: Int?): Unit { + // No color to set, and no color already set + if ((color == null || color == Color.TRANSPARENT) && + view.background !is CompositeBackgroundDrawable) { + return + } + + ensureCSSBackground(view).color = color ?: Color.TRANSPARENT + } + + @JvmStatic + @ColorInt + public fun getBackgroundColor(view: View): Int? = getCSSBackground(view)?.color + + @JvmStatic + public fun setBorderWidth(view: View, edge: LogicalEdge, width: Float?): Unit = + ensureCSSBackground(view) + .setBorderWidth(edge.toSpacingType(), PixelUtil.toPixelFromDIP(width ?: Float.NaN)) + + @JvmStatic + public fun getBorderWidth(view: View, edge: LogicalEdge): Float? { + val width = getCSSBackground(view)?.getBorderWidth(edge.toSpacingType()) + return if (width == null || width.isNaN()) null else PixelUtil.toDIPFromPixel((width)) + } + + @JvmStatic + public fun setBorderColor(view: View, edge: LogicalEdge, @ColorInt color: Int?): Unit = + ensureCSSBackground(view).setBorderColor(edge.toSpacingType(), color) + + @JvmStatic + @ColorInt + public fun getBorderColor(view: View, edge: LogicalEdge): Int? = + getCSSBackground(view)?.getBorderColor(edge.toSpacingType()) + + @JvmStatic + public fun setBorderRadius( + view: View, + corner: BorderRadiusProp, + // TODO: LengthPercentage silently converts from pixels to DIPs before here already + radius: LengthPercentage? + ): Unit = ensureCSSBackground(view).setBorderRadius(corner, radius) + + @JvmStatic + public fun getBorderRadius(view: View, corner: BorderRadiusProp): LengthPercentage? = + getCSSBackground(view)?.borderRadius?.get(corner) + + @JvmStatic + public fun setBorderStyle(view: View, borderStyle: BorderStyle?): Unit { + ensureCSSBackground(view).borderStyle = borderStyle + } + + @JvmStatic + public fun getBorderStyle(view: View): BorderStyle? = getCSSBackground(view)?.borderStyle + + @JvmStatic + @RequiresApi(31) + public fun setShadows(view: View, shadows: List): Unit { + val shadowDrawables = + shadows.map { boxShadow -> + val offsetX = boxShadow.offsetX + val offsetY = boxShadow.offsetY + val color = boxShadow.color ?: Color.TRANSPARENT + val blurRadius = boxShadow.blurRadius ?: 0f + val spreadDistance = boxShadow.spreadDistance ?: 0f + val inset = boxShadow.inset ?: false + + if (inset) { + InsetBoxShadowDrawable( + context = view.context, + background = ensureCSSBackground(view), + shadowColor = color, + offsetX = offsetX, + offsetY = offsetY, + blurRadius = blurRadius, + spread = spreadDistance) + } else { + OutsetBoxShadowDrawable( + context = view.context, + background = ensureCSSBackground(view), + shadowColor = color, + offsetX = offsetX, + offsetY = offsetY, + blurRadius = blurRadius, + spread = spreadDistance) + } + } + + updateCompositeDrawable( + view, + ensureCompositeBackgroundDrawable(view).withNewShadows(shadowDrawables.toTypedArray())) + } + + @JvmStatic + public fun clipToPaddingBox(view: View, canvas: Canvas): Unit { + // The canvas may be scrolled, so we need to offset + val drawingRect = Rect() + view.getDrawingRect(drawingRect) + + val cssBackground = getCSSBackground(view) + if (cssBackground == null) { + canvas.clipRect(drawingRect) + return + } + + val paddingBoxPath = cssBackground.paddingBoxPath + if (paddingBoxPath != null) { + paddingBoxPath.offset(drawingRect.left.toFloat(), drawingRect.top.toFloat()) + canvas.clipPath(paddingBoxPath) + } else { + val paddingBoxRect = cssBackground.paddingBoxRect + paddingBoxRect.offset(drawingRect.left.toFloat(), drawingRect.top.toFloat()) + canvas.clipRect(paddingBoxRect) + } + } + + private fun updateCompositeDrawable( + view: View, + compositeDrawable: CompositeBackgroundDrawable + ): Unit { + view.background = null + view.background = compositeDrawable + view.invalidate() + } + + private fun ensureCompositeBackgroundDrawable(view: View): CompositeBackgroundDrawable { + if (view.background is CompositeBackgroundDrawable) { + return view.background as CompositeBackgroundDrawable + } + + val compositeDrawable = CompositeBackgroundDrawable(view.background, null, emptyArray(), null) + updateCompositeDrawable(view, compositeDrawable) + return compositeDrawable + } + + private fun ensureCSSBackground(view: View): CSSBackgroundDrawable { + val compositeBackgroundDrawable = ensureCompositeBackgroundDrawable(view) + if (compositeBackgroundDrawable.cssBackground != null) { + return compositeBackgroundDrawable.cssBackground + } else { + val cssBackground = CSSBackgroundDrawable(view.context) + updateCompositeDrawable(view, compositeBackgroundDrawable.withNewCssBackground(cssBackground)) + return cssBackground + } + } + + private fun getCSSBackground(view: View): CSSBackgroundDrawable? { + if (view.background is CompositeBackgroundDrawable) { + return (view.background as CompositeBackgroundDrawable).cssBackground + } + return null + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/PixelUtil.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/PixelUtil.kt index 48f1f1e2f246..7009a586adc7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/PixelUtil.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/PixelUtil.kt @@ -15,6 +15,10 @@ public object PixelUtil { /** Convert from DIP to PX */ @JvmStatic public fun toPixelFromDIP(value: Float): Float { + if (value.isNaN()) { + return Float.NaN + } + return TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, value, DisplayMetricsHolder.getWindowDisplayMetrics()) } @@ -22,6 +26,10 @@ public object PixelUtil { /** Convert from DIP to PX */ @JvmStatic public fun toPixelFromDIP(value: Double): Float { + if (value.isNaN()) { + return Float.NaN + } + return toPixelFromDIP(value.toFloat()) } @@ -29,6 +37,10 @@ public object PixelUtil { @JvmOverloads @JvmStatic public fun toPixelFromSP(value: Float, maxFontScale: Float = Float.NaN): Float { + if (value.isNaN()) { + return Float.NaN + } + val displayMetrics = DisplayMetricsHolder.getWindowDisplayMetrics() val scaledValue = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, value, displayMetrics) @@ -42,12 +54,20 @@ public object PixelUtil { /** Convert from SP to PX */ @JvmStatic public fun toPixelFromSP(value: Double): Float { + if (value.isNaN()) { + return Float.NaN + } + return toPixelFromSP(value.toFloat()) } /** Convert from PX to DP */ @JvmStatic public fun toDIPFromPixel(value: Float): Float { + if (value.isNaN()) { + return Float.NaN + } + return value / DisplayMetricsHolder.getWindowDisplayMetrics().density } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java index 7369bc9de442..6b3bea46ab28 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java @@ -36,6 +36,7 @@ import com.facebook.react.uimanager.Spacing; import com.facebook.react.uimanager.style.BorderRadiusProp; import com.facebook.react.uimanager.style.BorderRadiusStyle; +import com.facebook.react.uimanager.style.BorderStyle; import com.facebook.react.uimanager.style.ComputedBorderRadius; import java.util.Locale; import java.util.Objects; @@ -64,29 +65,23 @@ public class CSSBackgroundDrawable extends Drawable { // 0 == 0x00000000, all bits set to 0. private static final int ALL_BITS_UNSET = 0; - private enum BorderStyle { - SOLID, - DASHED, - DOTTED; + private static @Nullable PathEffect getPathEffect(BorderStyle style, float borderWidth) { + switch (style) { + case SOLID: + return null; - public static @Nullable PathEffect getPathEffect(BorderStyle style, float borderWidth) { - switch (style) { - case SOLID: - return null; + case DASHED: + return new DashPathEffect( + new float[] {borderWidth * 3, borderWidth * 3, borderWidth * 3, borderWidth * 3}, 0); - case DASHED: - return new DashPathEffect( - new float[] {borderWidth * 3, borderWidth * 3, borderWidth * 3, borderWidth * 3}, 0); + case DOTTED: + return new DashPathEffect( + new float[] {borderWidth, borderWidth, borderWidth, borderWidth}, 0); - case DOTTED: - return new DashPathEffect( - new float[] {borderWidth, borderWidth, borderWidth, borderWidth}, 0); - - default: - return null; - } + default: + return null; } - }; + } /* Value at Spacing.ALL index used for rounded borders, whole array used by rectangular borders */ private @Nullable Spacing mBorderWidth; @@ -255,6 +250,10 @@ private void setBorderAlpha(int position, float alpha) { public void setBorderStyle(@Nullable String style) { BorderStyle borderStyle = style == null ? null : BorderStyle.valueOf(style.toUpperCase(Locale.US)); + setBorderStyle(borderStyle); + } + + public void setBorderStyle(@Nullable BorderStyle borderStyle) { if (mBorderStyle != borderStyle) { mBorderStyle = borderStyle; mNeedUpdatePathForBorderRadius = true; @@ -262,6 +261,10 @@ public void setBorderStyle(@Nullable String style) { } } + public @Nullable BorderStyle getBorderStyle() { + return mBorderStyle; + } + /** * @deprecated Use {@link #setBorderRadius(BorderRadiusProp, LengthPercentage)} instead. */ @@ -286,6 +289,7 @@ public void setRadius(float radius, int position) { if (boxedRadius == null) { mBorderRadius.set(BorderRadiusProp.values()[position], null); + invalidateSelf(); } else { setBorderRadius( BorderRadiusProp.values()[position], @@ -1012,14 +1016,23 @@ private static void getEllipseIntersectionWithLine( } public float getBorderWidthOrDefaultTo(final float defaultValue, final int spacingType) { - if (mBorderWidth == null) { + @Nullable Float width = getBorderWidth(spacingType); + if (width == null) { return defaultValue; } + return width; + } + + public @Nullable Float getBorderWidth(int spacingType) { + if (mBorderWidth == null) { + return null; + } + final float width = mBorderWidth.getRaw(spacingType); if (Float.isNaN(width)) { - return defaultValue; + return null; } return width; @@ -1029,7 +1042,7 @@ public float getBorderWidthOrDefaultTo(final float defaultValue, final int spaci private void updatePathEffect() { // Used for rounded border and rounded background PathEffect mPathEffectForBorderStyle = - mBorderStyle != null ? BorderStyle.getPathEffect(mBorderStyle, getFullBorderWidth()) : null; + mBorderStyle != null ? getPathEffect(mBorderStyle, getFullBorderWidth()) : null; mPaint.setPathEffect(mPathEffectForBorderStyle); } @@ -1037,7 +1050,7 @@ private void updatePathEffect() { private void updatePathEffect(int borderWidth) { PathEffect pathEffectForBorderStyle = null; if (mBorderStyle != null) { - pathEffectForBorderStyle = BorderStyle.getPathEffect(mBorderStyle, borderWidth); + pathEffectForBorderStyle = getPathEffect(mBorderStyle, borderWidth); } mPaint.setPathEffect(pathEffectForBorderStyle); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt new file mode 100644 index 000000000000..b7f20a8faaf0 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt @@ -0,0 +1,63 @@ +/* + * 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.uimanager.drawable + +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import com.facebook.react.common.annotations.UnstableReactNativeAPI + +/** + * CompositeBackgroundDrawable can overlay multiple different layers, shadows, and native effects + * such as ripple, into an Android View's background drawable. + */ +@OptIn(UnstableReactNativeAPI::class) +internal class CompositeBackgroundDrawable( + /** + * Any non-react-managed background already part of the view, like one set as Android style on a + * TextInput + */ + public val originalBackground: Drawable? = null, + + /** + * CSS background layer and border rendering + * + * TODO: we should extract path logic from here, and fast-path to using simpler drawables like + * ColorDrawable in the common cases + */ + public val cssBackground: CSSBackgroundDrawable? = null, + + /** Inner and outer box shadows */ + public val shadows: Array = emptyArray(), + + /** Native riplple effect (e.g. used by TouchableNativeFeedback) */ + public val nativeRipple: Drawable? = null +) : + LayerDrawable( + listOfNotNull(originalBackground, cssBackground, *shadows, nativeRipple).toTypedArray()) { + + init { + // We want to overlay drawables, instead of placing future drawables within the content area of + // previous ones. E.g. an EditText style may set padding on a TextInput, but we don't want to + // constrain background color to the area inside of the padding. + setPaddingMode(LayerDrawable.PADDING_MODE_STACK) + } + + public fun withNewCssBackground( + cssBackground: CSSBackgroundDrawable? + ): CompositeBackgroundDrawable { + return CompositeBackgroundDrawable(originalBackground, cssBackground, shadows, nativeRipple) + } + + public fun withNewShadows(newShadows: Array): CompositeBackgroundDrawable { + return CompositeBackgroundDrawable(originalBackground, cssBackground, newShadows, nativeRipple) + } + + public fun withNewNativeRipple(newRipple: Drawable?): CompositeBackgroundDrawable { + return CompositeBackgroundDrawable(originalBackground, cssBackground, shadows, newRipple) + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderStyle.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderStyle.kt new file mode 100644 index 000000000000..c23030b3fc90 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderStyle.kt @@ -0,0 +1,26 @@ +/* + * 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.uimanager.style + +public enum class BorderStyle { + SOLID, + DASHED, + DOTTED; + + public companion object { + @JvmStatic + public fun fromString(borderStyle: String): BorderStyle? { + return when (borderStyle.lowercase()) { + "solid" -> SOLID + "dashed" -> DASHED + "dotted" -> DOTTED + else -> null + } + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BoxShadow.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BoxShadow.kt new file mode 100644 index 000000000000..0282f4581a86 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BoxShadow.kt @@ -0,0 +1,50 @@ +/* + * 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.uimanager.style + +import androidx.annotation.ColorInt +import com.facebook.react.bridge.ReadableMap + +/** Represents all logical properties and shorthands for border radius. */ +public data class BoxShadow( + val offsetX: Float, + val offsetY: Float, + @ColorInt val color: Int? = null, + val blurRadius: Float? = null, + val spreadDistance: Float? = null, + val inset: Boolean? = null, +) { + public companion object { + @JvmStatic + public fun parse(boxShadow: ReadableMap): BoxShadow? { + if (!(boxShadow.hasKey("offsetX") && boxShadow.hasKey("offsetY"))) { + return null + } + + val offsetX = boxShadow.getDouble("offsetX").toFloat() + val offsetY = boxShadow.getDouble("offsetY").toFloat() + + val color = if (boxShadow.hasKey("color")) boxShadow.getInt("color") else null + val blurRadius = + if (boxShadow.hasKey("blurRadius")) boxShadow.getDouble("blurRadius").toFloat() else null + val spreadDistance = + if (boxShadow.hasKey("spreadDistance")) boxShadow.getDouble("spreadDistance").toFloat() + else null + val inset = if (boxShadow.hasKey("inset")) boxShadow.getBoolean("inset") else null + + return BoxShadow( + offsetX = offsetX, + offsetY = offsetY, + color = color, + blurRadius = blurRadius, + spreadDistance = spreadDistance, + inset = inset, + ) + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/LogicalEdge.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/LogicalEdge.kt new file mode 100644 index 000000000000..1f35909d6f00 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/LogicalEdge.kt @@ -0,0 +1,79 @@ +/* + * 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.uimanager.style + +import com.facebook.react.uimanager.Spacing +import java.lang.IllegalArgumentException + +/** Represents the collection of possible box edges and shorthands. */ +public enum class LogicalEdge { + ALL { + override fun toSpacingType(): Int = Spacing.ALL + }, + LEFT { + override fun toSpacingType(): Int = Spacing.LEFT + }, + RIGHT { + override fun toSpacingType(): Int = Spacing.RIGHT + }, + TOP { + override fun toSpacingType(): Int = Spacing.TOP + }, + BOTTOM { + override fun toSpacingType(): Int = Spacing.BOTTOM + }, + START { + override fun toSpacingType(): Int = Spacing.START + }, + END { + override fun toSpacingType(): Int = Spacing.END + }, + HORIZONTAL { + override fun toSpacingType(): Int = Spacing.HORIZONTAL + }, + VERTICAL { + override fun toSpacingType(): Int = Spacing.VERTICAL + }, + BLOCK_START { + override fun toSpacingType(): Int = Spacing.BLOCK_START + }, + BLOCK_END { + override fun toSpacingType(): Int = Spacing.BLOCK_END + }, + BLOCK { + override fun toSpacingType(): Int = Spacing.BLOCK + }; + + // TODO: not supported by Spacing users + // INLINE_START, + // INLINE_END, + // INLINE; + + abstract public fun toSpacingType(): Int + + public companion object { + @JvmStatic + public fun fromSpacingType(spacingType: Int): LogicalEdge { + return when (spacingType) { + Spacing.ALL -> ALL + Spacing.LEFT -> LEFT + Spacing.RIGHT -> RIGHT + Spacing.TOP -> TOP + Spacing.BOTTOM -> BOTTOM + Spacing.START -> START + Spacing.END -> END + Spacing.HORIZONTAL -> HORIZONTAL + Spacing.VERTICAL -> VERTICAL + Spacing.BLOCK_START -> BLOCK_START + Spacing.BLOCK_END -> BLOCK_END + Spacing.BLOCK -> BLOCK + else -> throw IllegalArgumentException("Unknown spacing type: $spacingType") + } + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/Overflow.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/Overflow.kt new file mode 100644 index 000000000000..a057a79b144d --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/Overflow.kt @@ -0,0 +1,26 @@ +/* + * 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.uimanager.style + +public enum class Overflow { + VISIBLE, + HIDDEN, + SCROLL; + + public companion object { + @JvmStatic + public fun fromString(overflow: String): Overflow { + return when (overflow.lowercase()) { + "visible" -> VISIBLE + "hidden" -> HIDDEN + "scroll" -> SCROLL + else -> throw IllegalArgumentException("Unknown overflow: $overflow") + } + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java index f9e66481c161..4d9627fd1983 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java @@ -9,6 +9,7 @@ import android.graphics.Color; import android.graphics.PorterDuff.Mode; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.drawee.backends.pipeline.Fresco; @@ -17,13 +18,19 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.ReactConstants; +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.LengthPercentage; +import com.facebook.react.uimanager.LengthPercentageType; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.SimpleViewManager; 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.LogicalEdge; import java.util.HashMap; import java.util.Map; @@ -160,10 +167,14 @@ public void setLoadingIndicatorSource(ReactImageView view, @Nullable String sour @ReactProp(name = "borderColor", customType = "Color") public void setBorderColor(ReactImageView view, @Nullable Integer borderColor) { - if (borderColor == null) { - view.setBorderColor(Color.TRANSPARENT); + if (ReactNativeFeatureFlags.enableBackgroundStyleApplicator()) { + BackgroundStyleApplicator.setBorderColor(view, LogicalEdge.ALL, borderColor); } else { - view.setBorderColor(borderColor); + if (borderColor == null) { + view.setBorderColor(Color.TRANSPARENT); + } else { + view.setBorderColor(borderColor); + } } } @@ -178,7 +189,11 @@ public void setOverlayColor(ReactImageView view, @Nullable Integer overlayColor) @ReactProp(name = "borderWidth") public void setBorderWidth(ReactImageView view, float borderWidth) { - view.setBorderWidth(borderWidth); + if (ReactNativeFeatureFlags.enableBackgroundStyleApplicator()) { + BackgroundStyleApplicator.setBorderWidth(view, LogicalEdge.ALL, borderWidth); + } else { + view.setBorderWidth(borderWidth); + } } @ReactPropGroup( @@ -191,14 +206,21 @@ public void setBorderWidth(ReactImageView view, float borderWidth) { }, defaultFloat = Float.NaN) public void setBorderRadius(ReactImageView view, int index, float borderRadius) { - if (!Float.isNaN(borderRadius)) { - borderRadius = PixelUtil.toPixelFromDIP(borderRadius); - } - - if (index == 0) { - view.setBorderRadius(borderRadius); + if (ReactNativeFeatureFlags.enableBackgroundStyleApplicator()) { + BackgroundStyleApplicator.setBorderRadius( + view, + BorderRadiusProp.values()[index], + new LengthPercentage(PixelUtil.toPixelFromDIP(borderRadius), LengthPercentageType.POINT)); } else { - view.setBorderRadius(borderRadius, index - 1); + if (!Float.isNaN(borderRadius)) { + borderRadius = PixelUtil.toPixelFromDIP(borderRadius); + } + + if (index == 0) { + view.setBorderRadius(borderRadius); + } else { + view.setBorderRadius(borderRadius, index - 1); + } } } @@ -259,6 +281,15 @@ public void setHeaders(ReactImageView view, ReadableMap headers) { view.setHeaders(headers); } + @Override + public void setBackgroundColor(ReactImageView view, int backgroundColor) { + if (ReactNativeFeatureFlags.enableBackgroundStyleApplicator()) { + BackgroundStyleApplicator.setBackgroundColor(view, backgroundColor); + } else { + super.setBackgroundColor(view, backgroundColor); + } + } + @Override public @Nullable Map getExportedCustomDirectEventTypeConstants() { @Nullable diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java index 66e668e12393..d0a934cc0407 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java @@ -48,6 +48,7 @@ import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.modules.fresco.ReactNetworkImageRequest; +import com.facebook.react.uimanager.BackgroundStyleApplicator; import com.facebook.react.uimanager.FloatUtil; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.Spacing; @@ -631,9 +632,12 @@ public boolean hasOverlappingRendering() { @Override public void onDraw(Canvas canvas) { - if (ReactNativeFeatureFlags.useNewReactImageViewBackgroundDrawing()) { + if (ReactNativeFeatureFlags.enableBackgroundStyleApplicator()) { + BackgroundStyleApplicator.clipToPaddingBox(this, canvas); + } else if (ReactNativeFeatureFlags.useNewReactImageViewBackgroundDrawing()) { mReactBackgroundManager.maybeClipToPaddingBox(canvas); } + super.onDraw(canvas); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 5719d853bcfc..bc692f4ed8af 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<7c14f956fd20226acfb532d806c6eb7a>> + * @generated SignedSource<<15d2f1dacb8c15642bc02959b32b083f>> */ /** @@ -87,6 +87,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool enableBackgroundStyleApplicator() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableBackgroundStyleApplicator"); + return method(javaProvider_); + } + bool enableCleanTextInputYogaNode() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableCleanTextInputYogaNode"); @@ -305,6 +311,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableAlignItemsBaselineOnFabricIOS( return ReactNativeFeatureFlags::enableAlignItemsBaselineOnFabricIOS(); } +bool JReactNativeFeatureFlagsCxxInterop::enableBackgroundStyleApplicator( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableBackgroundStyleApplicator(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableCleanTextInputYogaNode( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableCleanTextInputYogaNode(); @@ -491,6 +502,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableAlignItemsBaselineOnFabricIOS", JReactNativeFeatureFlagsCxxInterop::enableAlignItemsBaselineOnFabricIOS), + makeNativeMethod( + "enableBackgroundStyleApplicator", + JReactNativeFeatureFlagsCxxInterop::enableBackgroundStyleApplicator), makeNativeMethod( "enableCleanTextInputYogaNode", JReactNativeFeatureFlagsCxxInterop::enableCleanTextInputYogaNode), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 4847c7e89f5c..ccffbc049b76 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<0345dd07076374f5724fad579d1ea44a>> */ /** @@ -54,6 +54,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableAlignItemsBaselineOnFabricIOS( facebook::jni::alias_ref); + static bool enableBackgroundStyleApplicator( + facebook::jni::alias_ref); + static bool enableCleanTextInputYogaNode( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 4f06af9e6dab..19c2f9b8a9ba 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -53,6 +53,10 @@ bool ReactNativeFeatureFlags::enableAlignItemsBaselineOnFabricIOS() { return getAccessor().enableAlignItemsBaselineOnFabricIOS(); } +bool ReactNativeFeatureFlags::enableBackgroundStyleApplicator() { + return getAccessor().enableBackgroundStyleApplicator(); +} + bool ReactNativeFeatureFlags::enableCleanTextInputYogaNode() { return getAccessor().enableCleanTextInputYogaNode(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 4fe7c6ae6f92..3d4aee5412fb 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<55d6c0f7ab8abe8411194235ab362cd2>> + * @generated SignedSource<<9ea0c53ed751402e9a5002814e4e39f1>> */ /** @@ -77,6 +77,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableAlignItemsBaselineOnFabricIOS(); + /** + * Use BackgroundStyleApplicator in place of other background/border drawing code + */ + RN_EXPORT static bool enableBackgroundStyleApplicator(); + /** * Clean yoga node when does not change. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 875b1f3f50bc..22f2cd2d1f15 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -173,6 +173,24 @@ bool ReactNativeFeatureFlagsAccessor::enableAlignItemsBaselineOnFabricIOS() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableBackgroundStyleApplicator() { + auto flagValue = enableBackgroundStyleApplicator_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(8, "enableBackgroundStyleApplicator"); + + flagValue = currentProvider_->enableBackgroundStyleApplicator(); + enableBackgroundStyleApplicator_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableCleanTextInputYogaNode() { auto flagValue = enableCleanTextInputYogaNode_.load(); @@ -182,7 +200,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCleanTextInputYogaNode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(8, "enableCleanTextInputYogaNode"); + markFlagAsAccessed(9, "enableCleanTextInputYogaNode"); flagValue = currentProvider_->enableCleanTextInputYogaNode(); enableCleanTextInputYogaNode_ = flagValue; @@ -200,7 +218,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricRendererExclusively() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(9, "enableFabricRendererExclusively"); + markFlagAsAccessed(10, "enableFabricRendererExclusively"); flagValue = currentProvider_->enableFabricRendererExclusively(); enableFabricRendererExclusively_ = flagValue; @@ -218,7 +236,7 @@ bool ReactNativeFeatureFlagsAccessor::enableGranularShadowTreeStateReconciliatio // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(10, "enableGranularShadowTreeStateReconciliation"); + markFlagAsAccessed(11, "enableGranularShadowTreeStateReconciliation"); flagValue = currentProvider_->enableGranularShadowTreeStateReconciliation(); enableGranularShadowTreeStateReconciliation_ = flagValue; @@ -236,7 +254,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLongTaskAPI() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(11, "enableLongTaskAPI"); + markFlagAsAccessed(12, "enableLongTaskAPI"); flagValue = currentProvider_->enableLongTaskAPI(); enableLongTaskAPI_ = flagValue; @@ -254,7 +272,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(12, "enableMicrotasks"); + markFlagAsAccessed(13, "enableMicrotasks"); flagValue = currentProvider_->enableMicrotasks(); enableMicrotasks_ = flagValue; @@ -272,7 +290,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(13, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(14, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -290,7 +308,7 @@ bool ReactNativeFeatureFlagsAccessor::enableReportEventPaintTime() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(14, "enableReportEventPaintTime"); + markFlagAsAccessed(15, "enableReportEventPaintTime"); flagValue = currentProvider_->enableReportEventPaintTime(); enableReportEventPaintTime_ = flagValue; @@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(15, "enableSynchronousStateUpdates"); + markFlagAsAccessed(16, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::enableUIConsistency() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(16, "enableUIConsistency"); + markFlagAsAccessed(17, "enableUIConsistency"); flagValue = currentProvider_->enableUIConsistency(); enableUIConsistency_ = flagValue; @@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::excludeYogaFromRawProps() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(17, "excludeYogaFromRawProps"); + markFlagAsAccessed(18, "excludeYogaFromRawProps"); flagValue = currentProvider_->excludeYogaFromRawProps(); excludeYogaFromRawProps_ = flagValue; @@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::fetchImagesInViewPreallocation() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(18, "fetchImagesInViewPreallocation"); + markFlagAsAccessed(19, "fetchImagesInViewPreallocation"); flagValue = currentProvider_->fetchImagesInViewPreallocation(); fetchImagesInViewPreallocation_ = flagValue; @@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::fixIncorrectScrollViewStateUpdateOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(19, "fixIncorrectScrollViewStateUpdateOnAndroid"); + markFlagAsAccessed(20, "fixIncorrectScrollViewStateUpdateOnAndroid"); flagValue = currentProvider_->fixIncorrectScrollViewStateUpdateOnAndroid(); fixIncorrectScrollViewStateUpdateOnAndroid_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(21, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMissedFabricStateUpdatesOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "fixMissedFabricStateUpdatesOnAndroid"); + markFlagAsAccessed(22, "fixMissedFabricStateUpdatesOnAndroid"); flagValue = currentProvider_->fixMissedFabricStateUpdatesOnAndroid(); fixMissedFabricStateUpdatesOnAndroid_ = flagValue; @@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::forceBatchingMountItemsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "forceBatchingMountItemsOnAndroid"); + markFlagAsAccessed(23, "forceBatchingMountItemsOnAndroid"); flagValue = currentProvider_->forceBatchingMountItemsOnAndroid(); forceBatchingMountItemsOnAndroid_ = flagValue; @@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledDebug() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "fuseboxEnabledDebug"); + markFlagAsAccessed(24, "fuseboxEnabledDebug"); flagValue = currentProvider_->fuseboxEnabledDebug(); fuseboxEnabledDebug_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "fuseboxEnabledRelease"); + markFlagAsAccessed(25, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::initEagerTurboModulesOnNativeModulesQueueA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "initEagerTurboModulesOnNativeModulesQueueAndroid"); + markFlagAsAccessed(26, "initEagerTurboModulesOnNativeModulesQueueAndroid"); flagValue = currentProvider_->initEagerTurboModulesOnNativeModulesQueueAndroid(); initEagerTurboModulesOnNativeModulesQueueAndroid_ = flagValue; @@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(26, "lazyAnimationCallbacks"); + markFlagAsAccessed(27, "lazyAnimationCallbacks"); flagValue = currentProvider_->lazyAnimationCallbacks(); lazyAnimationCallbacks_ = flagValue; @@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::loadVectorDrawablesOnImages() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(27, "loadVectorDrawablesOnImages"); + markFlagAsAccessed(28, "loadVectorDrawablesOnImages"); flagValue = currentProvider_->loadVectorDrawablesOnImages(); loadVectorDrawablesOnImages_ = flagValue; @@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::setAndroidLayoutDirection() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(28, "setAndroidLayoutDirection"); + markFlagAsAccessed(29, "setAndroidLayoutDirection"); flagValue = currentProvider_->setAndroidLayoutDirection(); setAndroidLayoutDirection_ = flagValue; @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::useImmediateExecutorInAndroidBridgeless() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "useImmediateExecutorInAndroidBridgeless"); + markFlagAsAccessed(30, "useImmediateExecutorInAndroidBridgeless"); flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless(); useImmediateExecutorInAndroidBridgeless_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "useModernRuntimeScheduler"); + markFlagAsAccessed(31, "useModernRuntimeScheduler"); flagValue = currentProvider_->useModernRuntimeScheduler(); useModernRuntimeScheduler_ = flagValue; @@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(31, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(32, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::useNewReactImageViewBackgroundDrawing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "useNewReactImageViewBackgroundDrawing"); + markFlagAsAccessed(33, "useNewReactImageViewBackgroundDrawing"); flagValue = currentProvider_->useNewReactImageViewBackgroundDrawing(); useNewReactImageViewBackgroundDrawing_ = flagValue; @@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimisedViewPreallocationOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "useOptimisedViewPreallocationOnAndroid"); + markFlagAsAccessed(34, "useOptimisedViewPreallocationOnAndroid"); flagValue = currentProvider_->useOptimisedViewPreallocationOnAndroid(); useOptimisedViewPreallocationOnAndroid_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "useRuntimeShadowNodeReferenceUpdate"); + markFlagAsAccessed(35, "useRuntimeShadowNodeReferenceUpdate"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate(); useRuntimeShadowNodeReferenceUpdate_ = flagValue; @@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdateOnLayou // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(35, "useRuntimeShadowNodeReferenceUpdateOnLayout"); + markFlagAsAccessed(36, "useRuntimeShadowNodeReferenceUpdateOnLayout"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdateOnLayout(); useRuntimeShadowNodeReferenceUpdateOnLayout_ = flagValue; @@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "useStateAlignmentMechanism"); + markFlagAsAccessed(37, "useStateAlignmentMechanism"); flagValue = currentProvider_->useStateAlignmentMechanism(); useStateAlignmentMechanism_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index c3261cb03d1e..ee3ff6d5e5fa 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -39,6 +39,7 @@ class ReactNativeFeatureFlagsAccessor { bool completeReactInstanceCreationOnBgThreadOnAndroid(); bool destroyFabricSurfacesInReactInstanceManager(); bool enableAlignItemsBaselineOnFabricIOS(); + bool enableBackgroundStyleApplicator(); bool enableCleanTextInputYogaNode(); bool enableFabricRendererExclusively(); bool enableGranularShadowTreeStateReconciliation(); @@ -78,7 +79,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 37> accessedFeatureFlags_; + std::array, 38> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> allowCollapsableChildren_; @@ -88,6 +89,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> completeReactInstanceCreationOnBgThreadOnAndroid_; std::atomic> destroyFabricSurfacesInReactInstanceManager_; std::atomic> enableAlignItemsBaselineOnFabricIOS_; + std::atomic> enableBackgroundStyleApplicator_; std::atomic> enableCleanTextInputYogaNode_; std::atomic> enableFabricRendererExclusively_; std::atomic> enableGranularShadowTreeStateReconciliation_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index d8a87d083736..a16be8fa2e62 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<436dd66c962b5bd70cc574d573ddfc0a>> + * @generated SignedSource<<7fa2f84aa5aa29423be7f5fcde04207c>> */ /** @@ -59,6 +59,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return true; } + bool enableBackgroundStyleApplicator() override { + return false; + } + bool enableCleanTextInputYogaNode() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 8d044153ae65..971ccd09cb38 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<48be14c9be6da39c4acbf449d9ec8741>> + * @generated SignedSource<<94e268c46c82fec2d0db28439df7a9b1>> */ /** @@ -33,6 +33,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool completeReactInstanceCreationOnBgThreadOnAndroid() = 0; virtual bool destroyFabricSurfacesInReactInstanceManager() = 0; virtual bool enableAlignItemsBaselineOnFabricIOS() = 0; + virtual bool enableBackgroundStyleApplicator() = 0; virtual bool enableCleanTextInputYogaNode() = 0; virtual bool enableFabricRendererExclusively() = 0; virtual bool enableGranularShadowTreeStateReconciliation() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 4ddfea42f3f0..0a8d94cb1e90 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<2b25cf46c7e92088ee2e26432cb87c8f>> */ /** @@ -77,6 +77,11 @@ bool NativeReactNativeFeatureFlags::enableAlignItemsBaselineOnFabricIOS( return ReactNativeFeatureFlags::enableAlignItemsBaselineOnFabricIOS(); } +bool NativeReactNativeFeatureFlags::enableBackgroundStyleApplicator( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableBackgroundStyleApplicator(); +} + bool NativeReactNativeFeatureFlags::enableCleanTextInputYogaNode( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableCleanTextInputYogaNode(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index ffee73a8b3b7..9e6c156bb78e 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -51,6 +51,8 @@ class NativeReactNativeFeatureFlags bool enableAlignItemsBaselineOnFabricIOS(jsi::Runtime& runtime); + bool enableBackgroundStyleApplicator(jsi::Runtime& runtime); + bool enableCleanTextInputYogaNode(jsi::Runtime& runtime); bool enableFabricRendererExclusively(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index f092601a56ad..dc1d118b936f 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -74,6 +74,11 @@ const definitions: FeatureFlagDefinitions = { description: 'Kill-switch to turn off support for aling-items:baseline on Fabric iOS.', }, + enableBackgroundStyleApplicator: { + defaultValue: false, + description: + 'Use BackgroundStyleApplicator in place of other background/border drawing code', + }, enableCleanTextInputYogaNode: { defaultValue: false, description: 'Clean yoga node when does not change.', diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index f396bc2e38f8..2f7bb745055f 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<5d49b9dc97e6b57012a1a3284ef7b628>> * @flow strict-local */ @@ -52,6 +52,7 @@ export type ReactNativeFeatureFlags = { completeReactInstanceCreationOnBgThreadOnAndroid: Getter, destroyFabricSurfacesInReactInstanceManager: Getter, enableAlignItemsBaselineOnFabricIOS: Getter, + enableBackgroundStyleApplicator: Getter, enableCleanTextInputYogaNode: Getter, enableFabricRendererExclusively: Getter, enableGranularShadowTreeStateReconciliation: Getter, @@ -175,6 +176,10 @@ export const destroyFabricSurfacesInReactInstanceManager: Getter = crea * Kill-switch to turn off support for aling-items:baseline on Fabric iOS. */ export const enableAlignItemsBaselineOnFabricIOS: Getter = createNativeFlagGetter('enableAlignItemsBaselineOnFabricIOS', true); +/** + * Use BackgroundStyleApplicator in place of other background/border drawing code + */ +export const enableBackgroundStyleApplicator: Getter = createNativeFlagGetter('enableBackgroundStyleApplicator', false); /** * Clean yoga node when does not change. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 39090d741680..732a65352472 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<9397d7313ce789c990780c868cdf1629>> * @flow strict-local */ @@ -31,6 +31,7 @@ export interface Spec extends TurboModule { +completeReactInstanceCreationOnBgThreadOnAndroid?: () => boolean; +destroyFabricSurfacesInReactInstanceManager?: () => boolean; +enableAlignItemsBaselineOnFabricIOS?: () => boolean; + +enableBackgroundStyleApplicator?: () => boolean; +enableCleanTextInputYogaNode?: () => boolean; +enableFabricRendererExclusively?: () => boolean; +enableGranularShadowTreeStateReconciliation?: () => boolean; diff --git a/packages/react-native/types/experimental.d.ts b/packages/react-native/types/experimental.d.ts index deea3034b457..859252932a97 100644 --- a/packages/react-native/types/experimental.d.ts +++ b/packages/react-native/types/experimental.d.ts @@ -149,8 +149,11 @@ declare module '.' { } export interface ViewStyle { - experimental_boxShadow?: BoxShadowPrimitive | undefined; - experimental_filter?: ReadonlyArray | undefined; + experimental_boxShadow?: + | ReadonlyArray + | string + | undefined; + experimental_filter?: ReadonlyArray | string | undefined; experimental_mixBlendMode?: BlendMode | undefined; } }