Skip to content

Commit 6db021e

Browse files
fabriziocuccifacebook-github-bot
authored andcommitted
Override getClipBounds to expose overflow clipping to the framework
Summary: Override `getClipBounds()` on ReactViewGroup to return the padding box rect when `overflow` is hidden or scroll, gated behind the `syncAndroidClipBoundsWithOverflow` feature flag. This allows systems that query `View.getClipBounds()` to determine that a view clips its children, without the heavyweight side effects of `setClipBounds()`. Unlike `setClipBounds()`, this approach: - Does not clip the view's own rendering at the RenderNode level (no border clipping) - Does not require maintaining a Rect across size/border/overflow changes - Computes the padding box rect lazily, only when queried Adds `BackgroundStyleApplicator.getPaddingBoxRect()` (internal) which computes the padding box (view bounds minus border insets) reusing the same border inset resolution logic as `clipToPaddingBox()`. Changelog: [Internal] Differential Revision: D102353840
1 parent 0de5a01 commit 6db021e

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,37 @@ public object BackgroundStyleApplicator {
465465
clipToPaddingBoxWithAntiAliasing(view, canvas, null)
466466
}
467467

468+
/**
469+
* Populates [outRect] with the padding box rect of the view.
470+
*
471+
* The padding box is the area within the borders of the view. For views without a
472+
* [CompositeBackgroundDrawable] or without borders, this returns the full view bounds.
473+
*
474+
* This is useful for overriding [View.getClipBounds] to communicate the view's clipping region to
475+
* the Android framework (e.g. for [View.getGlobalVisibleRect] calculations).
476+
*
477+
* @param view The view whose padding box to compute
478+
* @param outRect The rect to populate with the padding box bounds
479+
*/
480+
@JvmStatic
481+
internal fun getPaddingBoxRect(view: View, outRect: Rect) {
482+
val composite = getCompositeBackgroundDrawable(view)
483+
if (composite == null) {
484+
outRect.set(0, 0, view.width, view.height)
485+
return
486+
}
487+
488+
val computedBorderInsets =
489+
composite.borderInsets?.resolve(composite.layoutDirection, view.context)
490+
491+
val left = (computedBorderInsets?.left?.dpToPx() ?: 0f).toInt()
492+
val top = (computedBorderInsets?.top?.dpToPx() ?: 0f).toInt()
493+
val right = (view.width.toFloat() - (computedBorderInsets?.right?.dpToPx() ?: 0f)).toInt()
494+
val bottom = (view.height.toFloat() - (computedBorderInsets?.bottom?.dpToPx() ?: 0f)).toInt()
495+
496+
outRect.set(left, top, right, bottom)
497+
}
498+
468499
/**
469500
* Clips the canvas to the padding box of the view.
470501
*

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import com.facebook.react.touch.OnInterceptTouchEventListener
3737
import com.facebook.react.touch.ReactHitSlopView
3838
import com.facebook.react.touch.ReactInterceptingViewGroup
3939
import com.facebook.react.uimanager.BackgroundStyleApplicator.clipToPaddingBox
40+
import com.facebook.react.uimanager.BackgroundStyleApplicator.getPaddingBoxRect
4041
import com.facebook.react.uimanager.BackgroundStyleApplicator.setBackgroundColor
4142
import com.facebook.react.uimanager.BackgroundStyleApplicator.setBorderColor
4243
import com.facebook.react.uimanager.BackgroundStyleApplicator.setBorderRadius
@@ -820,6 +821,38 @@ public open class ReactViewGroup public constructor(context: Context?) :
820821
invalidate()
821822
}
822823

824+
/**
825+
* Returns the clip bounds for this view based on the overflow property.
826+
*
827+
* When overflow is hidden or scroll, returns the padding box rect (the area inside the borders)
828+
* so that systems querying [View.getClipBounds] can determine the view's clipping region. Returns
829+
* null when overflow is visible (no clipping).
830+
*/
831+
override fun getClipBounds(): Rect? {
832+
if (
833+
ReactNativeFeatureFlags.syncAndroidClipBoundsWithOverflow() &&
834+
_overflow != null &&
835+
_overflow != Overflow.VISIBLE
836+
) {
837+
val rect = Rect()
838+
getPaddingBoxRect(this, rect)
839+
return rect
840+
}
841+
return super.getClipBounds()
842+
}
843+
844+
override fun getClipBounds(outRect: Rect): Boolean {
845+
if (
846+
ReactNativeFeatureFlags.syncAndroidClipBoundsWithOverflow() &&
847+
_overflow != null &&
848+
_overflow != Overflow.VISIBLE
849+
) {
850+
getPaddingBoxRect(this, outRect)
851+
return true
852+
}
853+
return super.getClipBounds(outRect)
854+
}
855+
823856
override fun setOverflowInset(left: Int, top: Int, right: Int, bottom: Int) {
824857
if (
825858
needsIsolatedLayer(this) &&

0 commit comments

Comments
 (0)