From 7850926c1e68ee573ab2824920e1457057683c24 Mon Sep 17 00:00:00 2001 From: Jorge Cabiedes Acosta Date: Mon, 12 Aug 2024 14:52:17 -0700 Subject: [PATCH] Add support for assymetrical bonder radii when using % (#45985) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45985 As title. We can now create ellipses when using percentages. The algorithm for this is still flawed and to get it to be a 1:1 to web it will probably require a re-write of some of the logic but this should get us closer for now. Some examples: 1. Border thinning on large single corner radii (100%) {F1798145800} 2. Thinning gets worse when having irregular border colors (100%) {F1798148002} Changelog: [Internal] Differential Revision: D61025927 --- .../ReactAndroid/api/ReactAndroid.api | 41 ++++--- .../react/uimanager/LengthPercentage.kt | 7 +- .../drawable/CSSBackgroundDrawable.java | 109 ++++++++++-------- .../drawable/InsetBoxShadowDrawable.kt | 11 +- .../drawable/OutsetBoxShadowDrawable.kt | 54 +++++---- .../uimanager/style/BorderRadiusStyle.kt | 39 +++++-- .../uimanager/style/ComputedBorderRadius.kt | 21 ++-- .../react/uimanager/style/CornerRadii.kt | 13 +++ .../react/views/view/ReactViewGroup.java | 16 +-- .../rn-tester/js/examples/View/ViewExample.js | 19 +++ 10 files changed, 217 insertions(+), 113 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/CornerRadii.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 1ddf3d0d0101be..61d888abde12c6 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -4360,7 +4360,7 @@ public final class com/facebook/react/uimanager/LengthPercentage { public fun ()V public fun (FLcom/facebook/react/uimanager/LengthPercentageType;)V public final fun getUnit ()Lcom/facebook/react/uimanager/LengthPercentageType; - public final fun resolve (FF)F + public final fun resolve (FF)Lcom/facebook/react/uimanager/style/CornerRadii; public static final fun setFromDynamic (Lcom/facebook/react/bridge/Dynamic;)Lcom/facebook/react/uimanager/LengthPercentage; } @@ -6113,19 +6113,19 @@ public final class com/facebook/react/uimanager/style/BoxShadow$Companion { public final class com/facebook/react/uimanager/style/ComputedBorderRadius { public fun ()V - public fun (FFFF)V - public final fun component1 ()F - public final fun component2 ()F - public final fun component3 ()F - public final fun component4 ()F - public final fun copy (FFFF)Lcom/facebook/react/uimanager/style/ComputedBorderRadius; - public static synthetic fun copy$default (Lcom/facebook/react/uimanager/style/ComputedBorderRadius;FFFFILjava/lang/Object;)Lcom/facebook/react/uimanager/style/ComputedBorderRadius; + public fun (Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;)V + public final fun component1 ()Lcom/facebook/react/uimanager/style/CornerRadii; + public final fun component2 ()Lcom/facebook/react/uimanager/style/CornerRadii; + public final fun component3 ()Lcom/facebook/react/uimanager/style/CornerRadii; + public final fun component4 ()Lcom/facebook/react/uimanager/style/CornerRadii; + public final fun copy (Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;)Lcom/facebook/react/uimanager/style/ComputedBorderRadius; + public static synthetic fun copy$default (Lcom/facebook/react/uimanager/style/ComputedBorderRadius;Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;Lcom/facebook/react/uimanager/style/CornerRadii;ILjava/lang/Object;)Lcom/facebook/react/uimanager/style/ComputedBorderRadius; public fun equals (Ljava/lang/Object;)Z - public final fun get (Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp;)F - public final fun getBottomLeft ()F - public final fun getBottomRight ()F - public final fun getTopLeft ()F - public final fun getTopRight ()F + public final fun get (Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp;)Lcom/facebook/react/uimanager/style/CornerRadii; + public final fun getBottomLeft ()Lcom/facebook/react/uimanager/style/CornerRadii; + public final fun getBottomRight ()Lcom/facebook/react/uimanager/style/CornerRadii; + public final fun getTopLeft ()Lcom/facebook/react/uimanager/style/CornerRadii; + public final fun getTopRight ()Lcom/facebook/react/uimanager/style/CornerRadii; public final fun hasRoundedBorders ()Z public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -6141,6 +6141,21 @@ public final class com/facebook/react/uimanager/style/ComputedBorderRadiusProp : public static fun values ()[Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; } +public final class com/facebook/react/uimanager/style/CornerRadii { + public fun ()V + public fun (FF)V + public synthetic fun (FFILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()F + public final fun component2 ()F + public final fun copy (FF)Lcom/facebook/react/uimanager/style/CornerRadii; + public static synthetic fun copy$default (Lcom/facebook/react/uimanager/style/CornerRadii;FFILjava/lang/Object;)Lcom/facebook/react/uimanager/style/CornerRadii; + public fun equals (Ljava/lang/Object;)Z + public final fun getHorizontal ()F + public final fun getVertical ()F + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public final class com/facebook/react/uimanager/style/Gradient { public fun (Lcom/facebook/react/bridge/ReadableMap;)V public final fun getShader (Landroid/graphics/Rect;)Landroid/graphics/Shader; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/LengthPercentage.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/LengthPercentage.kt index c4b18fc50a1ed6..d1005c0906d27c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/LengthPercentage.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/LengthPercentage.kt @@ -11,6 +11,7 @@ import com.facebook.common.logging.FLog import com.facebook.react.bridge.Dynamic import com.facebook.react.bridge.ReadableType import com.facebook.react.common.ReactConstants +import com.facebook.react.uimanager.style.CornerRadii import java.lang.NumberFormatException public enum class LengthPercentageType { @@ -61,12 +62,12 @@ public class LengthPercentage( } } - public fun resolve(width: Float, height: Float): Float { + public fun resolve(width: Float, height: Float): CornerRadii { if (unit == LengthPercentageType.PERCENT) { - return (value / 100) * Math.min(width, height) + return CornerRadii((value / 100) * width, (value / 100) * height) } - return value + return CornerRadii(value, value) } public constructor() : this(0f, LengthPercentageType.POINT) 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 05dbfb0ff70bcc..131fb1ebd78ce4 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 @@ -41,6 +41,7 @@ import com.facebook.react.uimanager.style.BorderRadiusStyle; import com.facebook.react.uimanager.style.BorderStyle; import com.facebook.react.uimanager.style.ComputedBorderRadius; +import com.facebook.react.uimanager.style.CornerRadii; import com.facebook.react.uimanager.style.Gradient; import java.util.Locale; import java.util.Objects; @@ -662,21 +663,27 @@ private void updatePath() { mContext, mOuterClipTempRectForBorderRadius.width(), mOuterClipTempRectForBorderRadius.height()); - float topLeftRadius = mComputedBorderRadius.getTopLeft(); - float topRightRadius = mComputedBorderRadius.getTopRight(); - float bottomLeftRadius = mComputedBorderRadius.getBottomLeft(); - float bottomRightRadius = mComputedBorderRadius.getBottomRight(); - - final float innerTopLeftRadiusX = getInnerBorderRadius(topLeftRadius, borderWidth.left); - final float innerTopLeftRadiusY = getInnerBorderRadius(topLeftRadius, borderWidth.top); - final float innerTopRightRadiusX = getInnerBorderRadius(topRightRadius, borderWidth.right); - final float innerTopRightRadiusY = getInnerBorderRadius(topRightRadius, borderWidth.top); + CornerRadii topLeftRadius = mComputedBorderRadius.getTopLeft(); + CornerRadii topRightRadius = mComputedBorderRadius.getTopRight(); + CornerRadii bottomLeftRadius = mComputedBorderRadius.getBottomLeft(); + CornerRadii bottomRightRadius = mComputedBorderRadius.getBottomRight(); + + final float innerTopLeftRadiusX = + getInnerBorderRadius(topLeftRadius.getHorizontal(), borderWidth.left); + final float innerTopLeftRadiusY = + getInnerBorderRadius(topLeftRadius.getVertical(), borderWidth.top); + final float innerTopRightRadiusX = + getInnerBorderRadius(topRightRadius.getHorizontal(), borderWidth.right); + final float innerTopRightRadiusY = + getInnerBorderRadius(topRightRadius.getVertical(), borderWidth.top); final float innerBottomRightRadiusX = - getInnerBorderRadius(bottomRightRadius, borderWidth.right); + getInnerBorderRadius(bottomRightRadius.getHorizontal(), borderWidth.right); final float innerBottomRightRadiusY = - getInnerBorderRadius(bottomRightRadius, borderWidth.bottom); - final float innerBottomLeftRadiusX = getInnerBorderRadius(bottomLeftRadius, borderWidth.left); - final float innerBottomLeftRadiusY = getInnerBorderRadius(bottomLeftRadius, borderWidth.bottom); + getInnerBorderRadius(bottomRightRadius.getVertical(), borderWidth.bottom); + final float innerBottomLeftRadiusX = + getInnerBorderRadius(bottomLeftRadius.getHorizontal(), borderWidth.left); + final float innerBottomLeftRadiusY = + getInnerBorderRadius(bottomLeftRadius.getVertical(), borderWidth.bottom); mInnerClipPathForBorderRadius.addRoundRect( mInnerClipTempRectForBorderRadius, @@ -716,14 +723,14 @@ private void updatePath() { mOuterClipPathForBorderRadius.addRoundRect( mOuterClipTempRectForBorderRadius, new float[] { - topLeftRadius, - topLeftRadius, - topRightRadius, - topRightRadius, - bottomRightRadius, - bottomRightRadius, - bottomLeftRadius, - bottomLeftRadius + topLeftRadius.getHorizontal(), + topLeftRadius.getVertical(), + topRightRadius.getHorizontal(), + topRightRadius.getVertical(), + bottomRightRadius.getHorizontal(), + bottomRightRadius.getVertical(), + bottomLeftRadius.getHorizontal(), + bottomLeftRadius.getVertical() }, Path.Direction.CW); @@ -736,14 +743,14 @@ private void updatePath() { mPathForBorderRadiusOutline.addRoundRect( mTempRectForBorderRadiusOutline, new float[] { - topLeftRadius + extraRadiusForOutline, - topLeftRadius + extraRadiusForOutline, - topRightRadius + extraRadiusForOutline, - topRightRadius + extraRadiusForOutline, - bottomRightRadius + extraRadiusForOutline, - bottomRightRadius + extraRadiusForOutline, - bottomLeftRadius + extraRadiusForOutline, - bottomLeftRadius + extraRadiusForOutline + topLeftRadius.getHorizontal() + extraRadiusForOutline, + topLeftRadius.getVertical() + extraRadiusForOutline, + topRightRadius.getHorizontal() + extraRadiusForOutline, + topRightRadius.getVertical() + extraRadiusForOutline, + bottomRightRadius.getHorizontal() + extraRadiusForOutline, + bottomRightRadius.getVertical() + extraRadiusForOutline, + bottomLeftRadius.getHorizontal() + extraRadiusForOutline, + bottomLeftRadius.getVertical() + extraRadiusForOutline }, Path.Direction.CW); @@ -751,29 +758,41 @@ private void updatePath() { mTempRectForCenterDrawPath, new float[] { Math.max( - topLeftRadius - borderWidth.left * 0.5f, - (borderWidth.left > 0.0f) ? (topLeftRadius / borderWidth.left) : 0.0f), + topLeftRadius.getHorizontal() - borderWidth.left * 0.5f, + (borderWidth.left > 0.0f) + ? (topLeftRadius.getHorizontal() / borderWidth.left) + : 0.0f), Math.max( - topLeftRadius - borderWidth.top * 0.5f, - (borderWidth.top > 0.0f) ? (topLeftRadius / borderWidth.top) : 0.0f), + topLeftRadius.getVertical() - borderWidth.top * 0.5f, + (borderWidth.top > 0.0f) ? (topLeftRadius.getVertical() / borderWidth.top) : 0.0f), Math.max( - topRightRadius - borderWidth.right * 0.5f, - (borderWidth.right > 0.0f) ? (topRightRadius / borderWidth.right) : 0.0f), + topRightRadius.getHorizontal() - borderWidth.right * 0.5f, + (borderWidth.right > 0.0f) + ? (topRightRadius.getHorizontal() / borderWidth.right) + : 0.0f), Math.max( - topRightRadius - borderWidth.top * 0.5f, - (borderWidth.top > 0.0f) ? (topRightRadius / borderWidth.top) : 0.0f), + topRightRadius.getVertical() - borderWidth.top * 0.5f, + (borderWidth.top > 0.0f) ? (topRightRadius.getVertical() / borderWidth.top) : 0.0f), Math.max( - bottomRightRadius - borderWidth.right * 0.5f, - (borderWidth.right > 0.0f) ? (bottomRightRadius / borderWidth.right) : 0.0f), + bottomRightRadius.getHorizontal() - borderWidth.right * 0.5f, + (borderWidth.right > 0.0f) + ? (bottomRightRadius.getHorizontal() / borderWidth.right) + : 0.0f), Math.max( - bottomRightRadius - borderWidth.bottom * 0.5f, - (borderWidth.bottom > 0.0f) ? (bottomRightRadius / borderWidth.bottom) : 0.0f), + bottomRightRadius.getVertical() - borderWidth.bottom * 0.5f, + (borderWidth.bottom > 0.0f) + ? (bottomRightRadius.getVertical() / borderWidth.bottom) + : 0.0f), Math.max( - bottomLeftRadius - borderWidth.left * 0.5f, - (borderWidth.left > 0.0f) ? (bottomLeftRadius / borderWidth.left) : 0.0f), + bottomLeftRadius.getHorizontal() - borderWidth.left * 0.5f, + (borderWidth.left > 0.0f) + ? (bottomLeftRadius.getHorizontal() / borderWidth.left) + : 0.0f), Math.max( - bottomLeftRadius - borderWidth.bottom * 0.5f, - (borderWidth.bottom > 0.0f) ? (bottomLeftRadius / borderWidth.bottom) : 0.0f) + bottomLeftRadius.getVertical() - borderWidth.bottom * 0.5f, + (borderWidth.bottom > 0.0f) + ? (bottomLeftRadius.getVertical() / borderWidth.bottom) + : 0.0f) }, Path.Direction.CW); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/InsetBoxShadowDrawable.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/InsetBoxShadowDrawable.kt index 5e358094c86120..3ffe21f11f4eb8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/InsetBoxShadowDrawable.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/InsetBoxShadowDrawable.kt @@ -142,11 +142,14 @@ internal class InsetBoxShadowDrawable( val bottomLeftRadius = computedBorderRadii.bottomLeft val bottomRightRadius = computedBorderRadii.bottomRight - val innerTopLeftRadius = background.getInnerBorderRadius(topLeftRadius, borderWidth.left) - val innerTopRightRadius = background.getInnerBorderRadius(topRightRadius, borderWidth.right) + val innerTopLeftRadius = + background.getInnerBorderRadius(topLeftRadius.horizontal, borderWidth.left) + val innerTopRightRadius = + background.getInnerBorderRadius(topRightRadius.horizontal, borderWidth.right) val innerBottomRightRadius = - background.getInnerBorderRadius(bottomRightRadius, borderWidth.right) - val innerBottomLeftRadius = background.getInnerBorderRadius(bottomLeftRadius, borderWidth.left) + background.getInnerBorderRadius(bottomRightRadius.horizontal, borderWidth.right) + val innerBottomLeftRadius = + background.getInnerBorderRadius(bottomLeftRadius.horizontal, borderWidth.left) val spreadWithDirection = -spread.toFloat() return BorderRadiusStyle( diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutsetBoxShadowDrawable.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutsetBoxShadowDrawable.kt index 4efb9450513bee..dec7f5b5adf3d4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutsetBoxShadowDrawable.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/OutsetBoxShadowDrawable.kt @@ -23,6 +23,7 @@ import com.facebook.react.uimanager.FilterHelper import com.facebook.react.uimanager.PixelUtil import com.facebook.react.uimanager.style.BorderRadiusStyle import com.facebook.react.uimanager.style.ComputedBorderRadius +import com.facebook.react.uimanager.style.CornerRadii import kotlin.math.roundToInt private const val TAG = "OutsetBoxShadowDrawable" @@ -91,10 +92,22 @@ internal class OutsetBoxShadowDrawable( val shadowBorderRadii = computedBorderRadii?.let { radii -> ComputedBorderRadius( - topLeft = adjustRadiusForSpread(radii.topLeft, spreadExtent.toFloat()), - topRight = adjustRadiusForSpread(radii.topRight, spreadExtent.toFloat()), - bottomRight = adjustRadiusForSpread(radii.bottomRight, spreadExtent.toFloat()), - bottomLeft = adjustRadiusForSpread(radii.bottomLeft, spreadExtent.toFloat()), + topLeft = + CornerRadii( + adjustRadiusForSpread(radii.topLeft.horizontal, spreadExtent.toFloat()), + adjustRadiusForSpread(radii.topLeft.vertical, spreadExtent.toFloat())), + topRight = + CornerRadii( + adjustRadiusForSpread(radii.topRight.horizontal, spreadExtent.toFloat()), + adjustRadiusForSpread(radii.topRight.vertical, spreadExtent.toFloat())), + bottomRight = + CornerRadii( + adjustRadiusForSpread(radii.bottomRight.horizontal, spreadExtent.toFloat()), + adjustRadiusForSpread(radii.bottomRight.vertical, spreadExtent.toFloat())), + bottomLeft = + CornerRadii( + adjustRadiusForSpread(radii.bottomLeft.horizontal, spreadExtent.toFloat()), + adjustRadiusForSpread(radii.bottomLeft.vertical, spreadExtent.toFloat())), ) } @@ -105,7 +118,6 @@ internal class OutsetBoxShadowDrawable( lastBorderRadius = shadowBorderRadii shadowOuterRect.set( RectF(bounds).apply { inset(-spreadExtent.toFloat(), -spreadExtent.toFloat()) }) - // We remove the portion of the shadow which overlaps the background border box, to avoid // showing the shadow shape e.g. behind a transparent background. There may be a subpixel gap // between the border box path, and the edge of border rendering, so we slightly inflate the @@ -118,28 +130,28 @@ internal class OutsetBoxShadowDrawable( shadowClipOutPath.addRoundRect( subpixelInsetBounds, floatArrayOf( - computedBorderRadii.topLeft, - computedBorderRadii.topLeft, - computedBorderRadii.topRight, - computedBorderRadii.topRight, - computedBorderRadii.bottomRight, - computedBorderRadii.bottomRight, - computedBorderRadii.bottomLeft, - computedBorderRadii.bottomLeft), + computedBorderRadii.topLeft.horizontal, + computedBorderRadii.topLeft.vertical, + computedBorderRadii.topRight.horizontal, + computedBorderRadii.topRight.vertical, + computedBorderRadii.bottomRight.horizontal, + computedBorderRadii.bottomRight.vertical, + computedBorderRadii.bottomLeft.horizontal, + computedBorderRadii.bottomLeft.vertical), Path.Direction.CW) shadowOuterPath.rewind() shadowOuterPath.addRoundRect( shadowOuterRect, floatArrayOf( - shadowBorderRadii.topLeft, - shadowBorderRadii.topLeft, - shadowBorderRadii.topRight, - shadowBorderRadii.topRight, - shadowBorderRadii.bottomRight, - shadowBorderRadii.bottomRight, - shadowBorderRadii.bottomLeft, - shadowBorderRadii.bottomLeft), + shadowBorderRadii.topLeft.horizontal, + shadowBorderRadii.topLeft.vertical, + shadowBorderRadii.topRight.horizontal, + shadowBorderRadii.topRight.vertical, + shadowBorderRadii.bottomRight.horizontal, + shadowBorderRadii.bottomRight.vertical, + shadowBorderRadii.bottomLeft.horizontal, + shadowBorderRadii.bottomLeft.vertical), Path.Direction.CW) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderRadiusStyle.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderRadiusStyle.kt index 996f4bba472da1..18df34fe02200c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderRadiusStyle.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/BorderRadiusStyle.kt @@ -107,37 +107,52 @@ public data class BorderRadiusStyle( width: Float, height: Float, ): ComputedBorderRadius { + val zeroRadii: CornerRadii = CornerRadii(0f, 0f) + return when (layoutDirection) { LayoutDirection.LTR -> ComputedBorderRadius( topLeft = - (startStart ?: topStart ?: topLeft ?: uniform)?.resolve(width, height) ?: 0f, - topRight = (endStart ?: topEnd ?: topRight ?: uniform)?.resolve(width, height) ?: 0f, + (startStart ?: topStart ?: topLeft ?: uniform)?.resolve(width, height) + ?: zeroRadii, + topRight = + (endStart ?: topEnd ?: topRight ?: uniform)?.resolve(width, height) ?: zeroRadii, bottomLeft = - (startEnd ?: bottomStart ?: bottomLeft ?: uniform)?.resolve(width, height) ?: 0f, + (startEnd ?: bottomStart ?: bottomLeft ?: uniform)?.resolve(width, height) + ?: zeroRadii, bottomRight = - (endEnd ?: bottomEnd ?: bottomRight ?: uniform)?.resolve(width, height) ?: 0f, + (endEnd ?: bottomEnd ?: bottomRight ?: uniform)?.resolve(width, height) + ?: zeroRadii, ) LayoutDirection.RTL -> if (I18nUtil.instance.doLeftAndRightSwapInRTL(context)) { ComputedBorderRadius( - topLeft = (endStart ?: topEnd ?: topRight ?: uniform)?.resolve(width, height) ?: 0f, + topLeft = + (endStart ?: topEnd ?: topRight ?: uniform)?.resolve(width, height) + ?: zeroRadii, topRight = - (startStart ?: topStart ?: topLeft ?: uniform)?.resolve(width, height) ?: 0f, + (startStart ?: topStart ?: topLeft ?: uniform)?.resolve(width, height) + ?: zeroRadii, bottomLeft = - (endEnd ?: bottomStart ?: bottomRight ?: uniform)?.resolve(width, height) ?: 0f, + (endEnd ?: bottomStart ?: bottomRight ?: uniform)?.resolve(width, height) + ?: zeroRadii, bottomRight = - (startEnd ?: bottomEnd ?: bottomLeft ?: uniform)?.resolve(width, height) ?: 0f, + (startEnd ?: bottomEnd ?: bottomLeft ?: uniform)?.resolve(width, height) + ?: zeroRadii, ) } else { ComputedBorderRadius( - topLeft = (endStart ?: topEnd ?: topLeft ?: uniform)?.resolve(width, height) ?: 0f, + topLeft = + (endStart ?: topEnd ?: topLeft ?: uniform)?.resolve(width, height) ?: zeroRadii, topRight = - (startStart ?: topStart ?: topRight ?: uniform)?.resolve(width, height) ?: 0f, + (startStart ?: topStart ?: topRight ?: uniform)?.resolve(width, height) + ?: zeroRadii, bottomLeft = - (endEnd ?: bottomStart ?: bottomLeft ?: uniform)?.resolve(width, height) ?: 0f, + (endEnd ?: bottomStart ?: bottomLeft ?: uniform)?.resolve(width, height) + ?: zeroRadii, bottomRight = - (startEnd ?: bottomEnd ?: bottomRight ?: uniform)?.resolve(width, height) ?: 0f, + (startEnd ?: bottomEnd ?: bottomRight ?: uniform)?.resolve(width, height) + ?: zeroRadii, ) } else -> throw IllegalArgumentException("Expected?.resolved layout direction") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ComputedBorderRadius.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ComputedBorderRadius.kt index 292b66c5b4dfbc..a16c951510e267 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ComputedBorderRadius.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ComputedBorderRadius.kt @@ -17,16 +17,22 @@ public enum class ComputedBorderRadiusProp { /** Phsysical edge lengths (in DIPs) for a border-radius. */ public data class ComputedBorderRadius( - val topLeft: Float, - val topRight: Float, - val bottomLeft: Float, - val bottomRight: Float, + val topLeft: CornerRadii, + val topRight: CornerRadii, + val bottomLeft: CornerRadii, + val bottomRight: CornerRadii, ) { public fun hasRoundedBorders(): Boolean { - return topLeft > 0f || topRight > 0f || bottomLeft > 0f || bottomRight > 0f + return topLeft.horizontal > 0f || + topLeft.vertical > 0f || + topRight.horizontal > 0f || + topRight.vertical > 0f || + bottomLeft.horizontal > 0f || + bottomLeft.vertical > 0f || + bottomRight.horizontal > 0f } - public fun get(property: ComputedBorderRadiusProp): Float { + public fun get(property: ComputedBorderRadiusProp): CornerRadii { return when (property) { ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_LEFT_RADIUS -> topLeft ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_RIGHT_RADIUS -> topRight @@ -35,5 +41,6 @@ public data class ComputedBorderRadius( } } - public constructor() : this(0f, 0f, 0f, 0f) + public constructor() : + this(CornerRadii(0f, 0f), CornerRadii(0f, 0f), CornerRadii(0f, 0f), CornerRadii(0f, 0f)) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/CornerRadii.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/CornerRadii.kt new file mode 100644 index 00000000000000..4e9109cecfec19 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/CornerRadii.kt @@ -0,0 +1,13 @@ +/* + * 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 data class CornerRadii( + val horizontal: Float = 0f, + val vertical: Float = 0f, +) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index 66f666a261ec37..f3dcdfd75de24c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -998,14 +998,14 @@ private void dispatchOverflowDraw(Canvas canvas) { mPath.addRoundRect( new RectF(left, top, right, bottom), new float[] { - Math.max(borderRadius.getTopLeft() - borderWidth.left, 0), - Math.max(borderRadius.getTopLeft() - borderWidth.top, 0), - Math.max(borderRadius.getTopRight() - borderWidth.right, 0), - Math.max(borderRadius.getTopRight() - borderWidth.top, 0), - Math.max(borderRadius.getBottomRight() - borderWidth.right, 0), - Math.max(borderRadius.getBottomRight() - borderWidth.bottom, 0), - Math.max(borderRadius.getBottomLeft() - borderWidth.left, 0), - Math.max(borderRadius.getBottomLeft() - borderWidth.bottom, 0), + Math.max(borderRadius.getTopLeft().getHorizontal() - borderWidth.left, 0), + Math.max(borderRadius.getTopLeft().getVertical() - borderWidth.top, 0), + Math.max(borderRadius.getTopRight().getHorizontal() - borderWidth.right, 0), + Math.max(borderRadius.getTopRight().getVertical() - borderWidth.top, 0), + Math.max(borderRadius.getBottomRight().getHorizontal() - borderWidth.right, 0), + Math.max(borderRadius.getBottomRight().getVertical() - borderWidth.bottom, 0), + Math.max(borderRadius.getBottomLeft().getHorizontal() - borderWidth.left, 0), + Math.max(borderRadius.getBottomLeft().getVertical() - borderWidth.bottom, 0), }, Path.Direction.CW); canvas.clipPath(mPath); diff --git a/packages/rn-tester/js/examples/View/ViewExample.js b/packages/rn-tester/js/examples/View/ViewExample.js index 949e25b44a7868..5cda36f53f36a6 100644 --- a/packages/rn-tester/js/examples/View/ViewExample.js +++ b/packages/rn-tester/js/examples/View/ViewExample.js @@ -733,6 +733,25 @@ export default ({ borderBottomRightRadius: '40%', }} /> + + ); },