diff --git a/CHANGELOG.md b/CHANGELOG.md index 2109e2b..1a058bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added `overflow` prop to `FlexWidget` and `OverlapWidget` that can be used to clip the view if it has border radius + ## [0.9.0] - 2023-11-29 ### Added diff --git a/android/src/main/java/com/reactnativeandroidwidget/builder/widget/FrameLayoutWidget.java b/android/src/main/java/com/reactnativeandroidwidget/builder/widget/FrameLayoutWidget.java index 76c376d..cfdf152 100644 --- a/android/src/main/java/com/reactnativeandroidwidget/builder/widget/FrameLayoutWidget.java +++ b/android/src/main/java/com/reactnativeandroidwidget/builder/widget/FrameLayoutWidget.java @@ -1,5 +1,9 @@ package com.reactnativeandroidwidget.builder.widget; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.RectF; import android.view.View; import android.widget.FrameLayout; @@ -9,13 +13,51 @@ import java.util.List; public class FrameLayoutWidget extends BaseLayoutWidget { + private class ClippedFrameLayout extends FrameLayout { + private Path path; + + public ClippedFrameLayout(Context appContext) { + super(appContext); + } + + @Override + protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { + super.onSizeChanged(width, height, oldWidth, oldHeight); + + if ("hidden".equals(props.getString("overflow")) && props.hasKey("borderRadius")) { + ReadableMap borderRadius = props.getMap("borderRadius"); + float[] radii = new float[]{ + (float) dpToPx(borderRadius.getDouble("topLeft")), + (float) dpToPx(borderRadius.getDouble("topLeft")), + (float) dpToPx(borderRadius.getDouble("topRight")), + (float) dpToPx(borderRadius.getDouble("topRight")), + (float) dpToPx(borderRadius.getDouble("bottomRight")), + (float) dpToPx(borderRadius.getDouble("bottomRight")), + (float) dpToPx(borderRadius.getDouble("bottomLeft")), + (float) dpToPx(borderRadius.getDouble("bottomLeft")) + }; + + this.path = new Path(); + this.path.addRoundRect(new RectF(0, 0, width, height), radii, Path.Direction.CW); + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (this.path != null) { + canvas.clipPath(this.path); + } + super.dispatchDraw(canvas); + } + } + public FrameLayoutWidget(ReactApplicationContext context, ReadableMap props, List children) { super(context, props, children); } @Override protected FrameLayout createView() { - return new FrameLayout(appContext); + return new ClippedFrameLayout(appContext); } @Override diff --git a/android/src/main/java/com/reactnativeandroidwidget/builder/widget/LinearLayoutWidget.java b/android/src/main/java/com/reactnativeandroidwidget/builder/widget/LinearLayoutWidget.java index 71b0804..92e3029 100644 --- a/android/src/main/java/com/reactnativeandroidwidget/builder/widget/LinearLayoutWidget.java +++ b/android/src/main/java/com/reactnativeandroidwidget/builder/widget/LinearLayoutWidget.java @@ -1,6 +1,10 @@ package com.reactnativeandroidwidget.builder.widget; +import android.content.Context; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Path; +import android.graphics.RectF; import android.graphics.drawable.ShapeDrawable; import android.view.View; import android.widget.LinearLayout; @@ -11,13 +15,51 @@ import java.util.List; public class LinearLayoutWidget extends BaseLayoutWidget { + private class ClippedLinearLayout extends LinearLayout { + private Path path; + + public ClippedLinearLayout(Context appContext) { + super(appContext); + } + + @Override + protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { + super.onSizeChanged(width, height, oldWidth, oldHeight); + + if ("hidden".equals(props.getString("overflow")) && props.hasKey("borderRadius")) { + ReadableMap borderRadius = props.getMap("borderRadius"); + float[] radii = new float[]{ + (float) dpToPx(borderRadius.getDouble("topLeft")), + (float) dpToPx(borderRadius.getDouble("topLeft")), + (float) dpToPx(borderRadius.getDouble("topRight")), + (float) dpToPx(borderRadius.getDouble("topRight")), + (float) dpToPx(borderRadius.getDouble("bottomRight")), + (float) dpToPx(borderRadius.getDouble("bottomRight")), + (float) dpToPx(borderRadius.getDouble("bottomLeft")), + (float) dpToPx(borderRadius.getDouble("bottomLeft")) + }; + + this.path = new Path(); + this.path.addRoundRect(new RectF(0, 0, width, height), radii, Path.Direction.CW); + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (this.path != null) { + canvas.clipPath(this.path); + } + super.dispatchDraw(canvas); + } + } + public LinearLayoutWidget(ReactApplicationContext context, ReadableMap props, List children) { super(context, props, children); } @Override protected LinearLayout createView() { - return new LinearLayout(appContext); + return new ClippedLinearLayout(appContext); } @Override diff --git a/src/widgets/FlexWidget.tsx b/src/widgets/FlexWidget.tsx index b85cd61..72aaba6 100644 --- a/src/widgets/FlexWidget.tsx +++ b/src/widgets/FlexWidget.tsx @@ -22,6 +22,7 @@ interface FlexWidgetInternalProps extends CommonInternalProps { size: number; color: string; }; + overflow?: 'hidden'; } interface FlexStyleProps { @@ -37,6 +38,7 @@ interface FlexStyleProps { | 'space-evenly'; flexGap?: number; flexGapColor?: ColorProp; + overflow?: 'hidden'; } export interface FlexWidgetStyle extends FlexStyleProps, CommonStyleProps {} @@ -63,6 +65,7 @@ FlexWidget.convertProps = (props: FlexWidgetProps): FlexWidgetInternalProps => { }, } : {}), + ...(props?.style?.overflow ? { overflow: props.style.overflow } : {}), }; }; FlexWidget.processChildren = ( diff --git a/src/widgets/OverlapWidget.tsx b/src/widgets/OverlapWidget.tsx index 66e8233..3669127 100644 --- a/src/widgets/OverlapWidget.tsx +++ b/src/widgets/OverlapWidget.tsx @@ -6,9 +6,13 @@ import type { CommonInternalProps } from './utils/common-internal.props'; import type { CommonStyleProps } from './utils/style.props'; import { convertCommonStyle } from './utils/style.utils'; -type OverlapWidgetInternalProps = CommonInternalProps; +interface OverlapWidgetInternalProps extends CommonInternalProps { + overflow?: 'hidden'; +} -export type OverlapWidgetStyle = CommonStyleProps; +export interface OverlapWidgetStyle extends CommonStyleProps { + overflow?: 'hidden'; +} export interface OverlapWidgetProps extends ClickActionProps { children?: any; @@ -25,5 +29,6 @@ OverlapWidget.convertProps = ( return { ...convertCommonStyle(props.style ?? {}), ...convertClickAction(props), + ...(props?.style?.overflow ? { overflow: props.style.overflow } : {}), }; };