Skip to content

Commit cc3863f

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Cleanup border radius resolution
Differential Revision: D55635743
1 parent 455a6c5 commit cc3863f

File tree

5 files changed

+218
-250
lines changed

5 files changed

+218
-250
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/FloatUtil.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,15 @@ public object FloatUtil {
1818
java.lang.Float.isNaN(f1) && java.lang.Float.isNaN(f2)
1919
} else abs(f2 - f1) < EPSILON
2020
}
21+
22+
@JvmStatic
23+
public fun floatsEqual(f1: Float?, f2: Float?): Boolean {
24+
if (f1 == null) {
25+
return f2 == null
26+
} else if (f2 == null) {
27+
return false
28+
}
29+
30+
return floatsEqual(f1, f2)
31+
}
2132
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CSSBackgroundDrawable.java

Lines changed: 29 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
import com.facebook.react.modules.i18nmanager.I18nUtil;
2929
import com.facebook.react.uimanager.FloatUtil;
3030
import com.facebook.react.uimanager.Spacing;
31-
import java.util.Arrays;
31+
import com.facebook.react.uimanager.style.BorderRadiusProp;
32+
import com.facebook.react.uimanager.style.BorderRadiusStyle;
33+
import com.facebook.react.uimanager.style.ComputedBorderRadius;
3234
import java.util.Locale;
3335

3436
/**
@@ -99,7 +101,6 @@ private enum BorderStyle {
99101
private @Nullable PointF mInnerBottomRightCorner;
100102
private @Nullable PointF mInnerBottomLeftCorner;
101103
private boolean mNeedUpdatePathForBorderRadius = false;
102-
private float mBorderRadius = Float.NaN;
103104

104105
/* Used by all types of background and for drawing borders */
105106
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -112,25 +113,10 @@ private enum BorderStyle {
112113
// the paths, overlapping them and closing the visible gap.
113114
private final float mGapBetweenPaths = 0.8f;
114115

115-
private @Nullable float[] mBorderCornerRadii;
116+
private BorderRadiusStyle mBorderRadius = new BorderRadiusStyle();
116117
private final Context mContext;
117118
private int mLayoutDirection;
118119

119-
public enum BorderRadiusLocation {
120-
TOP_LEFT,
121-
TOP_RIGHT,
122-
BOTTOM_RIGHT,
123-
BOTTOM_LEFT,
124-
TOP_START,
125-
TOP_END,
126-
BOTTOM_START,
127-
BOTTOM_END,
128-
END_END,
129-
END_START,
130-
START_END,
131-
START_START
132-
}
133-
134120
public CSSBackgroundDrawable(Context context) {
135121
mContext = context;
136122
}
@@ -146,19 +132,7 @@ public void draw(Canvas canvas) {
146132
}
147133

148134
public boolean hasRoundedBorders() {
149-
if (!Float.isNaN(mBorderRadius) && mBorderRadius > 0) {
150-
return true;
151-
}
152-
153-
if (mBorderCornerRadii != null) {
154-
for (final float borderRadii : mBorderCornerRadii) {
155-
if (!Float.isNaN(borderRadii) && borderRadii > 0) {
156-
return true;
157-
}
158-
}
159-
}
160-
161-
return false;
135+
return mBorderRadius.hasRoundedBorders();
162136
}
163137

164138
@Override
@@ -193,7 +167,7 @@ public int getOpacity() {
193167
/* Android's elevation implementation requires this to be implemented to know where to draw the shadow. */
194168
@Override
195169
public void getOutline(Outline outline) {
196-
if ((!Float.isNaN(mBorderRadius) && mBorderRadius > 0) || mBorderCornerRadii != null) {
170+
if (hasRoundedBorders()) {
197171
updatePath();
198172

199173
outline.setConvexPath(mPathForBorderRadiusOutline);
@@ -260,48 +234,36 @@ public void setBorderStyle(@Nullable String style) {
260234
}
261235
}
262236

237+
/**
238+
* @deprecated Use {@link #setBorderRadius(BorderRadiusProp, Float)} instead.
239+
*/
263240
public void setRadius(float radius) {
264-
if (!FloatUtil.floatsEqual(mBorderRadius, radius)) {
265-
mBorderRadius = radius;
266-
mNeedUpdatePathForBorderRadius = true;
267-
invalidateSelf();
268-
}
241+
@Nullable Float boxedRadius = Float.isNaN(radius) ? null : Float.valueOf(radius);
242+
setBorderRadius(BorderRadiusProp.BORDER_RADIUS, boxedRadius);
269243
}
270244

245+
/**
246+
* @deprecated Use {@link #setBorderRadius(BorderRadiusProp, Float)} instead.
247+
*/
271248
public void setRadius(float radius, int position) {
272-
if (mBorderCornerRadii == null) {
273-
mBorderCornerRadii = new float[12];
274-
Arrays.fill(mBorderCornerRadii, Float.NaN);
275-
}
249+
@Nullable Float boxedRadius = Float.isNaN(radius) ? null : Float.valueOf(radius);
250+
setBorderRadius(BorderRadiusProp.values()[position], boxedRadius);
251+
}
276252

277-
if (!FloatUtil.floatsEqual(mBorderCornerRadii[position], radius)) {
278-
mBorderCornerRadii[position] = radius;
253+
public void setBorderRadius(BorderRadiusProp property, @Nullable Float radius) {
254+
if (!FloatUtil.floatsEqual(mBorderRadius.getUniform(), radius)) {
255+
mBorderRadius.set(property, radius);
279256
mNeedUpdatePathForBorderRadius = true;
280257
invalidateSelf();
281258
}
282259
}
283260

284-
public float getFullBorderRadius() {
285-
return Float.isNaN(mBorderRadius) ? 0 : mBorderRadius;
286-
}
287-
288-
public float getBorderRadius(final BorderRadiusLocation location) {
289-
return getBorderRadiusOrDefaultTo(Float.NaN, location);
261+
public void setBorderRadius(BorderRadiusStyle radius) {
262+
mBorderRadius = radius;
290263
}
291264

292-
public float getBorderRadiusOrDefaultTo(
293-
final float defaultValue, final BorderRadiusLocation location) {
294-
if (mBorderCornerRadii == null) {
295-
return defaultValue;
296-
}
297-
298-
final float radius = mBorderCornerRadii[location.ordinal()];
299-
300-
if (Float.isNaN(radius)) {
301-
return defaultValue;
302-
}
303-
304-
return radius;
265+
public BorderRadiusStyle getBorderRadius() {
266+
return mBorderRadius;
305267
}
306268

307269
public void setColor(int color) {
@@ -606,95 +568,11 @@ private void updatePath() {
606568
mTempRectForCenterDrawPath.left += borderWidth.left * 0.5f;
607569
mTempRectForCenterDrawPath.right -= borderWidth.right * 0.5f;
608570

609-
final float borderRadius = getFullBorderRadius();
610-
float topLeftRadius = getBorderRadiusOrDefaultTo(borderRadius, BorderRadiusLocation.TOP_LEFT);
611-
float topRightRadius = getBorderRadiusOrDefaultTo(borderRadius, BorderRadiusLocation.TOP_RIGHT);
612-
float bottomLeftRadius =
613-
getBorderRadiusOrDefaultTo(borderRadius, BorderRadiusLocation.BOTTOM_LEFT);
614-
float bottomRightRadius =
615-
getBorderRadiusOrDefaultTo(borderRadius, BorderRadiusLocation.BOTTOM_RIGHT);
616-
617-
final boolean isRTL = getResolvedLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
618-
float topStartRadius = getBorderRadius(BorderRadiusLocation.TOP_START);
619-
float topEndRadius = getBorderRadius(BorderRadiusLocation.TOP_END);
620-
float bottomStartRadius = getBorderRadius(BorderRadiusLocation.BOTTOM_START);
621-
float bottomEndRadius = getBorderRadius(BorderRadiusLocation.BOTTOM_END);
622-
623-
float endEndRadius = getBorderRadius(BorderRadiusLocation.END_END);
624-
float endStartRadius = getBorderRadius(BorderRadiusLocation.END_START);
625-
float startEndRadius = getBorderRadius(BorderRadiusLocation.START_END);
626-
float startStartRadius = getBorderRadius(BorderRadiusLocation.START_START);
627-
628-
if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(mContext)) {
629-
if (Float.isNaN(topStartRadius)) {
630-
topStartRadius = topLeftRadius;
631-
}
632-
633-
if (Float.isNaN(topEndRadius)) {
634-
topEndRadius = topRightRadius;
635-
}
636-
637-
if (Float.isNaN(bottomStartRadius)) {
638-
bottomStartRadius = bottomLeftRadius;
639-
}
640-
641-
if (Float.isNaN(bottomEndRadius)) {
642-
bottomEndRadius = bottomRightRadius;
643-
}
644-
645-
final float logicalTopStartRadius =
646-
Float.isNaN(topStartRadius) ? startStartRadius : topStartRadius;
647-
final float logicalTopEndRadius = Float.isNaN(topEndRadius) ? startEndRadius : topEndRadius;
648-
final float logicalBottomStartRadius =
649-
Float.isNaN(bottomStartRadius) ? endStartRadius : bottomStartRadius;
650-
final float logicalBottomEndRadius =
651-
Float.isNaN(bottomEndRadius) ? endEndRadius : bottomEndRadius;
652-
653-
final float directionAwareTopLeftRadius = isRTL ? logicalTopEndRadius : logicalTopStartRadius;
654-
final float directionAwareTopRightRadius =
655-
isRTL ? logicalTopStartRadius : logicalTopEndRadius;
656-
final float directionAwareBottomLeftRadius =
657-
isRTL ? logicalBottomEndRadius : logicalBottomStartRadius;
658-
final float directionAwareBottomRightRadius =
659-
isRTL ? logicalBottomStartRadius : logicalBottomEndRadius;
660-
661-
topLeftRadius = directionAwareTopLeftRadius;
662-
topRightRadius = directionAwareTopRightRadius;
663-
bottomLeftRadius = directionAwareBottomLeftRadius;
664-
bottomRightRadius = directionAwareBottomRightRadius;
665-
} else {
666-
final float logicalTopStartRadius =
667-
Float.isNaN(topStartRadius) ? startStartRadius : topStartRadius;
668-
final float logicalTopEndRadius = Float.isNaN(topEndRadius) ? startEndRadius : topEndRadius;
669-
final float logicalBottomStartRadius =
670-
Float.isNaN(bottomStartRadius) ? endStartRadius : bottomStartRadius;
671-
final float logicalBottomEndRadius =
672-
Float.isNaN(bottomEndRadius) ? endEndRadius : bottomEndRadius;
673-
674-
final float directionAwareTopLeftRadius = isRTL ? logicalTopEndRadius : logicalTopStartRadius;
675-
final float directionAwareTopRightRadius =
676-
isRTL ? logicalTopStartRadius : logicalTopEndRadius;
677-
final float directionAwareBottomLeftRadius =
678-
isRTL ? logicalBottomEndRadius : logicalBottomStartRadius;
679-
final float directionAwareBottomRightRadius =
680-
isRTL ? logicalBottomStartRadius : logicalBottomEndRadius;
681-
682-
if (!Float.isNaN(directionAwareTopLeftRadius)) {
683-
topLeftRadius = directionAwareTopLeftRadius;
684-
}
685-
686-
if (!Float.isNaN(directionAwareTopRightRadius)) {
687-
topRightRadius = directionAwareTopRightRadius;
688-
}
689-
690-
if (!Float.isNaN(directionAwareBottomLeftRadius)) {
691-
bottomLeftRadius = directionAwareBottomLeftRadius;
692-
}
693-
694-
if (!Float.isNaN(directionAwareBottomRightRadius)) {
695-
bottomRightRadius = directionAwareBottomRightRadius;
696-
}
697-
}
571+
ComputedBorderRadius radius = mBorderRadius.resolve(mLayoutDirection, mContext);
572+
float topLeftRadius = radius.getTopLeft();
573+
float topRightRadius = radius.getTopRight();
574+
float bottomLeftRadius = radius.getBottomLeft();
575+
float bottomRightRadius = radius.getBottomRight();
698576

699577
final float innerTopLeftRadiusX = Math.max(topLeftRadius - borderWidth.left, 0);
700578
final float innerTopLeftRadiusY = Math.max(topLeftRadius - borderWidth.top, 0);
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.uimanager.style
9+
10+
import android.content.Context
11+
import android.util.LayoutDirection
12+
import com.facebook.react.modules.i18nmanager.I18nUtil
13+
14+
/** Represents the collection of possible border radius style properties. */
15+
public enum class BorderRadiusProp {
16+
BORDER_TOP_LEFT_RADIUS,
17+
BORDER_TOP_RIGHT_RADIUS,
18+
BORDER_BOTTOM_RIGHT_RADIUS,
19+
BORDER_BOTTOM_LEFT_RADIUS,
20+
BORDER_TOP_START_RADIUS,
21+
BORDER_TOP_END_RADIUS,
22+
BORDER_BOTTOM_START_RADIUS,
23+
BORDER_BOTTOM_END_RADIUS,
24+
BORDER_END_END_RADIUS,
25+
BORDER_END_START_RADIUS,
26+
BORDER_START_END_RADIUS,
27+
BORDER_START_START_RADIUS,
28+
BORDER_RADIUS,
29+
}
30+
31+
/** Represents all logical properties and shorthands for border radius. */
32+
public data class BorderRadiusStyle(
33+
var uniform: Float? = null,
34+
var topLeft: Float? = null,
35+
var topRight: Float? = null,
36+
var bottomLeft: Float? = null,
37+
var bottomRight: Float? = null,
38+
var topStart: Float? = null,
39+
var topEnd: Float? = null,
40+
var bottomStart: Float? = null,
41+
var bottomEnd: Float? = null,
42+
var startStart: Float? = null,
43+
var startEnd: Float? = null,
44+
var endStart: Float? = null,
45+
var endEnd: Float? = null
46+
) {
47+
public constructor(properties: List<Pair<BorderRadiusProp, Float>>) : this() {
48+
properties.forEach { (k, v) -> set(k, v) }
49+
}
50+
51+
public fun set(property: BorderRadiusProp, value: Float?) {
52+
when (property) {
53+
BorderRadiusProp.BORDER_RADIUS -> uniform = value
54+
BorderRadiusProp.BORDER_TOP_LEFT_RADIUS -> topLeft = value
55+
BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS -> topRight = value
56+
BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS -> bottomLeft = value
57+
BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS -> bottomRight = value
58+
BorderRadiusProp.BORDER_TOP_START_RADIUS -> topStart = value
59+
BorderRadiusProp.BORDER_TOP_END_RADIUS -> topEnd = value
60+
BorderRadiusProp.BORDER_BOTTOM_START_RADIUS -> bottomStart = value
61+
BorderRadiusProp.BORDER_BOTTOM_END_RADIUS -> bottomEnd = value
62+
BorderRadiusProp.BORDER_START_START_RADIUS -> startStart = value
63+
BorderRadiusProp.BORDER_START_END_RADIUS -> startEnd = value
64+
BorderRadiusProp.BORDER_END_START_RADIUS -> endStart = value
65+
BorderRadiusProp.BORDER_END_END_RADIUS -> endEnd = value
66+
}
67+
}
68+
69+
public fun get(property: BorderRadiusProp): Float? {
70+
return when (property) {
71+
BorderRadiusProp.BORDER_RADIUS -> uniform
72+
BorderRadiusProp.BORDER_TOP_LEFT_RADIUS -> topLeft
73+
BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS -> topRight
74+
BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS -> bottomLeft
75+
BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS -> bottomRight
76+
BorderRadiusProp.BORDER_TOP_START_RADIUS -> topStart
77+
BorderRadiusProp.BORDER_TOP_END_RADIUS -> topEnd
78+
BorderRadiusProp.BORDER_BOTTOM_START_RADIUS -> bottomStart
79+
BorderRadiusProp.BORDER_BOTTOM_END_RADIUS -> bottomEnd
80+
BorderRadiusProp.BORDER_START_START_RADIUS -> startStart
81+
BorderRadiusProp.BORDER_START_END_RADIUS -> startEnd
82+
BorderRadiusProp.BORDER_END_START_RADIUS -> endStart
83+
BorderRadiusProp.BORDER_END_END_RADIUS -> endEnd
84+
}
85+
}
86+
87+
public fun hasRoundedBorders(): Boolean {
88+
return ((uniform ?: 0f) > 0f) ||
89+
((topLeft ?: 0f) > 0f) ||
90+
((topRight ?: 0f) > 0f) ||
91+
((bottomLeft ?: 0f) > 0f) ||
92+
((bottomRight ?: 0f) > 0f) ||
93+
((topStart ?: 0f) > 0f) ||
94+
((topEnd ?: 0f) > 0f) ||
95+
((bottomStart ?: 0f) > 0f) ||
96+
((bottomEnd ?: 0f) > 0f) ||
97+
((startStart ?: 0f) > 0f) ||
98+
((startEnd ?: 0f) > 0f) ||
99+
((endStart ?: 0f) > 0f) ||
100+
((endEnd ?: 0f) > 0f)
101+
}
102+
103+
public fun resolve(
104+
layoutDirection: Int,
105+
context: Context,
106+
): ComputedBorderRadius {
107+
return when (layoutDirection) {
108+
LayoutDirection.LTR ->
109+
ComputedBorderRadius(
110+
topLeft = startStart ?: topStart ?: topLeft ?: uniform ?: 0f,
111+
topRight = endStart ?: topEnd ?: topRight ?: uniform ?: 0f,
112+
bottomLeft = startEnd ?: bottomStart ?: bottomLeft ?: uniform ?: 0f,
113+
bottomRight = endEnd ?: bottomEnd ?: bottomRight ?: uniform ?: 0f)
114+
LayoutDirection.RTL ->
115+
if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(context)) {
116+
ComputedBorderRadius(
117+
topLeft = endStart ?: topEnd ?: topRight ?: uniform ?: 0f,
118+
topRight = startStart ?: topStart ?: topLeft ?: uniform ?: 0f,
119+
bottomLeft = endEnd ?: bottomStart ?: bottomRight ?: uniform ?: 0f,
120+
bottomRight = startEnd ?: bottomEnd ?: bottomLeft ?: uniform ?: 0f)
121+
} else {
122+
ComputedBorderRadius(
123+
topLeft = endStart ?: topEnd ?: topLeft ?: uniform ?: 0f,
124+
topRight = startStart ?: topStart ?: topRight ?: uniform ?: 0f,
125+
bottomLeft = endEnd ?: bottomStart ?: bottomLeft ?: uniform ?: 0f,
126+
bottomRight = startEnd ?: bottomEnd ?: bottomRight ?: uniform ?: 0f)
127+
}
128+
else -> throw IllegalArgumentException("Expected resolved layout direction")
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)