Skip to content

Commit

Permalink
Add support for assymetrical border radii when using %
Browse files Browse the repository at this point in the history
Summary:
as title

Changelog: [Internal]

Differential Revision: D61148739
  • Loading branch information
jorge-cab authored and facebook-github-bot committed Aug 13, 2024
1 parent 66ff4d3 commit cf65593
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -604,10 +604,14 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
static RCTCornerRadii RCTCornerRadiiFromBorderRadii(BorderRadii borderRadii)
{
return RCTCornerRadii{
.topLeft = (CGFloat)borderRadii.topLeft,
.topRight = (CGFloat)borderRadii.topRight,
.bottomLeft = (CGFloat)borderRadii.bottomLeft,
.bottomRight = (CGFloat)borderRadii.bottomRight};
.topLeftHorizontal = (CGFloat)borderRadii.topLeft.horizontal,
.topLeftVertical = (CGFloat)borderRadii.topLeft.vertical,
.topRightHorizontal = (CGFloat)borderRadii.topRight.horizontal,
.topRightVertical = (CGFloat)borderRadii.topRight.vertical,
.bottomLeftHorizontal = (CGFloat)borderRadii.bottomLeft.horizontal,
.bottomLeftVertical = (CGFloat)borderRadii.bottomLeft.vertical,
.bottomRightHorizontal = (CGFloat)borderRadii.bottomRight.horizontal,
.bottomRightVertical = (CGFloat)borderRadii.bottomRight.vertical};
}

static RCTBorderColors RCTCreateRCTBorderColorsFromBorderColors(BorderColors borderColors)
Expand Down Expand Up @@ -728,7 +732,7 @@ - (void)invalidateLayer
CGColorRef borderColor = RCTCreateCGColorRefFromSharedColor(borderMetrics.borderColors.left);
layer.borderColor = borderColor;
CGColorRelease(borderColor);
layer.cornerRadius = (CGFloat)borderMetrics.borderRadii.topLeft;
layer.cornerRadius = (CGFloat)borderMetrics.borderRadii.topLeft.horizontal;

layer.cornerCurve = CornerCurveFromBorderCurve(borderMetrics.borderCurves.topLeft);

Expand Down Expand Up @@ -791,7 +795,7 @@ - (void)invalidateLayer
if (self.clipsToBounds) {
if (borderMetrics.borderRadii.isUniform()) {
// In this case we can simply use `cornerRadius` exclusively.
cornerRadius = borderMetrics.borderRadii.topLeft;
cornerRadius = borderMetrics.borderRadii.topLeft.horizontal;
} else {
// In this case we have to generate masking layer manually.
CGPathRef path = RCTPathCreateWithRoundedRect(
Expand Down
12 changes: 8 additions & 4 deletions packages/react-native/React/Fabric/Utils/RCTBoxShadow.mm
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ static CGFloat adjustedCornerRadius(CGFloat cornerRadius, CGFloat spreadDistance
static RCTCornerRadii cornerRadiiForBoxShadow(RCTCornerRadii cornerRadii, CGFloat spreadDistance)
{
return {
adjustedCornerRadius(cornerRadii.topLeft, spreadDistance),
adjustedCornerRadius(cornerRadii.topRight, spreadDistance),
adjustedCornerRadius(cornerRadii.bottomLeft, spreadDistance),
adjustedCornerRadius(cornerRadii.bottomRight, spreadDistance)};
adjustedCornerRadius(cornerRadii.topLeftHorizontal, spreadDistance),
adjustedCornerRadius(cornerRadii.topLeftVertical, spreadDistance),
adjustedCornerRadius(cornerRadii.topRightHorizontal, spreadDistance),
adjustedCornerRadius(cornerRadii.topRightVertical, spreadDistance),
adjustedCornerRadius(cornerRadii.bottomLeftHorizontal, spreadDistance),
adjustedCornerRadius(cornerRadii.bottomLeftVertical, spreadDistance),
adjustedCornerRadius(cornerRadii.bottomRightHorizontal, spreadDistance),
adjustedCornerRadius(cornerRadii.bottomRightVertical, spreadDistance)};
}

// Returns the smallest CGRect that will contain all shadows and the layer itself.
Expand Down
12 changes: 8 additions & 4 deletions packages/react-native/React/Views/RCTBorderDrawing.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
#import <React/RCTDefines.h>

typedef struct {
CGFloat topLeft;
CGFloat topRight;
CGFloat bottomLeft;
CGFloat bottomRight;
CGFloat topLeftHorizontal;
CGFloat topLeftVertical;
CGFloat topRightHorizontal;
CGFloat topRightVertical;
CGFloat bottomLeftHorizontal;
CGFloat bottomLeftVertical;
CGFloat bottomRightHorizontal;
CGFloat bottomRightVertical;
} RCTCornerRadii;

typedef struct {
Expand Down
34 changes: 21 additions & 13 deletions packages/react-native/React/Views/RCTBorderDrawing.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ BOOL RCTBorderInsetsAreEqual(UIEdgeInsets borderInsets)

BOOL RCTCornerRadiiAreEqual(RCTCornerRadii cornerRadii)
{
return ABS(cornerRadii.topLeft - cornerRadii.topRight) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeft - cornerRadii.bottomLeft) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeft - cornerRadii.bottomRight) < RCTViewBorderThreshold;
return ABS(cornerRadii.topLeftVertical - cornerRadii.topRightVertical) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeftHorizontal - cornerRadii.topRightHorizontal) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeftVertical - cornerRadii.bottomLeftVertical) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeftHorizontal - cornerRadii.bottomLeftHorizontal) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeftVertical - cornerRadii.bottomRightVertical) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeftHorizontal - cornerRadii.bottomRightHorizontal) < RCTViewBorderThreshold;
}

BOOL RCTBorderColorsAreEqual(RCTBorderColors borderColors)
Expand All @@ -35,20 +38,20 @@ RCTCornerInsets RCTGetCornerInsets(RCTCornerRadii cornerRadii, UIEdgeInsets edge
{
return (RCTCornerInsets){
{
MAX(0, cornerRadii.topLeft - edgeInsets.left),
MAX(0, cornerRadii.topLeft - edgeInsets.top),
MAX(0, cornerRadii.topLeftHorizontal - edgeInsets.left),
MAX(0, cornerRadii.topLeftVertical - edgeInsets.top),
},
{
MAX(0, cornerRadii.topRight - edgeInsets.right),
MAX(0, cornerRadii.topRight - edgeInsets.top),
MAX(0, cornerRadii.topRightHorizontal - edgeInsets.right),
MAX(0, cornerRadii.topRightVertical - edgeInsets.top),
},
{
MAX(0, cornerRadii.bottomLeft - edgeInsets.left),
MAX(0, cornerRadii.bottomLeft - edgeInsets.bottom),
MAX(0, cornerRadii.bottomLeftHorizontal - edgeInsets.left),
MAX(0, cornerRadii.bottomLeftVertical - edgeInsets.bottom),
},
{
MAX(0, cornerRadii.bottomRight - edgeInsets.right),
MAX(0, cornerRadii.bottomRight - edgeInsets.bottom),
MAX(0, cornerRadii.bottomRightHorizontal - edgeInsets.right),
MAX(0, cornerRadii.bottomRightVertical - edgeInsets.bottom),
}};
}

Expand Down Expand Up @@ -159,8 +162,13 @@ CGPathRef RCTPathCreateWithRoundedRect(CGRect bounds, RCTCornerInsets cornerInse
NS_INLINE BOOL RCTCornerRadiiAreAboveThreshold(RCTCornerRadii cornerRadii)
{
return (
cornerRadii.topLeft > RCTViewBorderThreshold || cornerRadii.topRight > RCTViewBorderThreshold ||
cornerRadii.bottomLeft > RCTViewBorderThreshold || cornerRadii.bottomRight > RCTViewBorderThreshold);
cornerRadii.topLeftHorizontal > RCTViewBorderThreshold || cornerRadii.topLeftVertical > RCTViewBorderThreshold ||
cornerRadii.topRightHorizontal > RCTViewBorderThreshold ||
cornerRadii.topRightVertical > RCTViewBorderThreshold ||
cornerRadii.bottomLeftHorizontal > RCTViewBorderThreshold ||
cornerRadii.bottomLeftVertical > RCTViewBorderThreshold ||
cornerRadii.bottomRightHorizontal > RCTViewBorderThreshold ||
cornerRadii.bottomRightVertical > RCTViewBorderThreshold);
}

static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCornerRadii cornerRadii)
Expand Down
4 changes: 2 additions & 2 deletions packages/react-native/React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@ - (void)displayLayer:(CALayer *)layer
backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:self.traitCollection].CGColor;

if (useIOSBorderRendering) {
layer.cornerRadius = cornerRadii.topLeft;
layer.cornerRadius = cornerRadii.topLeftHorizontal;
layer.borderColor = borderColors.left;
layer.borderWidth = borderInsets.left;
layer.backgroundColor = backgroundColor;
Expand Down Expand Up @@ -929,7 +929,7 @@ - (void)updateClippingForLayer:(CALayer *)layer
if (self.clipsToBounds) {
const RCTCornerRadii cornerRadii = [self cornerRadii];
if (RCTCornerRadiiAreEqual(cornerRadii)) {
cornerRadius = cornerRadii.topLeft;
cornerRadius = cornerRadii.topLeftHorizontal;

} else {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,10 @@ static BorderRadii ensureNoOverlap(const BorderRadii& radii, const Size& size) {
// Source: https://www.w3.org/TR/css-backgrounds-3/#corner-overlap

auto insets = EdgeInsets{
/* .left = */ radii.topLeft + radii.bottomLeft,
/* .top = */ radii.topLeft + radii.topRight,
/* .right = */ radii.topRight + radii.bottomRight,
/* .bottom = */ radii.bottomLeft + radii.bottomRight,
/* .left = */ radii.topLeft.horizontal + radii.bottomLeft.horizontal,
/* .top = */ radii.topLeft.vertical + radii.topRight.vertical,
/* .right = */ radii.topRight.horizontal + radii.bottomRight.horizontal,
/* .bottom = */ radii.bottomLeft.vertical + radii.bottomRight.vertical,
};

auto insetsScale = EdgeInsets{
Expand All @@ -398,17 +398,33 @@ static BorderRadii ensureNoOverlap(const BorderRadii& radii, const Size& size) {

return BorderRadii{
/* topLeft = */
static_cast<float>(
radii.topLeft * std::min(insetsScale.top, insetsScale.left)),
{static_cast<float>(
radii.topLeft.horizontal *
std::min(insetsScale.top, insetsScale.left)),
static_cast<float>(
radii.topLeft.vertical *
std::min(insetsScale.top, insetsScale.left))},
/* topRight = */
static_cast<float>(
radii.topRight * std::min(insetsScale.top, insetsScale.right)),
{static_cast<float>(
radii.topRight.horizontal *
std::min(insetsScale.top, insetsScale.right)),
static_cast<float>(
radii.topRight.vertical *
std::min(insetsScale.top, insetsScale.right))},
/* bottomLeft = */
static_cast<float>(
radii.bottomLeft * std::min(insetsScale.bottom, insetsScale.left)),
{static_cast<float>(
radii.bottomLeft.horizontal *
std::min(insetsScale.bottom, insetsScale.left)),
static_cast<float>(
radii.bottomLeft.vertical *
std::min(insetsScale.bottom, insetsScale.left))},
/* bottomRight = */
static_cast<float>(
radii.bottomRight * std::min(insetsScale.bottom, insetsScale.right)),
{static_cast<float>(
radii.bottomRight.horizontal *
std::min(insetsScale.bottom, insetsScale.right)),
static_cast<float>(
radii.bottomRight.vertical *
std::min(insetsScale.bottom, insetsScale.right))},
};
}

Expand All @@ -417,28 +433,33 @@ static BorderRadii radiiPercentToPoint(
const Size& size) {
return BorderRadii{
/* topLeft = */
(radii.topLeft.unit == UnitType::Percent)
? static_cast<float>(
(radii.topLeft.value / 100) * std::max(size.width, size.height))
: static_cast<float>(radii.topLeft.value),
{(radii.topLeft.unit == UnitType::Percent)
? static_cast<float>((radii.topLeft.value / 100) * size.width)
: static_cast<float>(radii.topLeft.value),
(radii.topLeft.unit == UnitType::Percent)
? static_cast<float>((radii.topLeft.value / 100) * size.height)
: static_cast<float>(radii.topLeft.value)},
/* topRight = */
(radii.topRight.unit == UnitType::Percent)
? static_cast<float>(
(radii.topRight.value / 100) *
std::max(size.width, size.height))
: static_cast<float>(radii.topRight.value),
{(radii.topRight.unit == UnitType::Percent)
? static_cast<float>((radii.topRight.value / 100) * size.width)
: static_cast<float>(radii.topRight.value),
(radii.topRight.unit == UnitType::Percent)
? static_cast<float>((radii.topRight.value / 100) * size.height)
: static_cast<float>(radii.topRight.value)},
/* bottomLeft = */
(radii.bottomLeft.unit == UnitType::Percent)
? static_cast<float>(
(radii.bottomLeft.value / 100) *
std::max(size.width, size.height))
: static_cast<float>(radii.bottomLeft.value),
{(radii.bottomLeft.unit == UnitType::Percent)
? static_cast<float>((radii.bottomLeft.value / 100) * size.width)
: static_cast<float>(radii.bottomLeft.value),
(radii.bottomLeft.unit == UnitType::Percent)
? static_cast<float>((radii.bottomLeft.value / 100) * size.height)
: static_cast<float>(radii.bottomLeft.value)},
/* bottomRight = */
(radii.bottomRight.unit == UnitType::Percent)
? static_cast<float>(
(radii.bottomRight.value / 100) *
std::max(size.width, size.height))
: static_cast<float>(radii.bottomRight.value),
{(radii.bottomRight.unit == UnitType::Percent)
? static_cast<float>((radii.bottomRight.value / 100) * size.width)
: static_cast<float>(radii.bottomRight.value),
(radii.bottomRight.unit == UnitType::Percent)
? static_cast<float>((radii.bottomRight.value / 100) * size.height)
: static_cast<float>(radii.bottomRight.value)},
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ enum class BorderCurve : uint8_t { Circular, Continuous };

enum class BorderStyle : uint8_t { Solid, Dotted, Dashed };

struct CornerRadii {
float vertical;
float horizontal;

bool operator==(const CornerRadii& other) const {
return vertical == other.vertical && horizontal == other.horizontal;
}
};

enum class Cursor : uint8_t {
Auto,
Alias,
Expand Down Expand Up @@ -289,7 +298,7 @@ using BorderWidths = RectangleEdges<Float>;
using BorderCurves = RectangleCorners<BorderCurve>;
using BorderStyles = RectangleEdges<BorderStyle>;
using BorderColors = RectangleEdges<SharedColor>;
using BorderRadii = RectangleCorners<Float>;
using BorderRadii = RectangleCorners<CornerRadii>;

using CascadedBorderWidths = CascadedRectangleEdges<Float>;
using CascadedBorderCurves = CascadedRectangleCorners<BorderCurve>;
Expand Down
19 changes: 19 additions & 0 deletions packages/rn-tester/js/examples/View/ViewExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,25 @@ export default ({
borderBottomRightRadius: '40%',
}}
/>
<View
style={{
width: 100,
height: 50,
borderWidth: 6,
borderRadius: '100%',
}}
/>
<View
style={{
width: 100,
height: 50,
borderWidth: 6,
borderTopLeftRadius: '10%',
borderTopRightRadius: '20%',
borderBottomRightRadius: '50%',
borderBottomLeftRadius: '100%',
}}
/>
</View>
);
},
Expand Down

0 comments on commit cf65593

Please sign in to comment.