diff --git a/redwood-layout-composeui/src/test/snapshots/images/app.cash.redwood.layout.composeui_ComposeUiBoxTest_RTL_testBoxMeasurementIncludesMargins.png b/redwood-layout-composeui/src/test/snapshots/images/app.cash.redwood.layout.composeui_ComposeUiBoxTest_RTL_testBoxMeasurementIncludesMargins.png new file mode 100644 index 0000000000..36572dfc12 --- /dev/null +++ b/redwood-layout-composeui/src/test/snapshots/images/app.cash.redwood.layout.composeui_ComposeUiBoxTest_RTL_testBoxMeasurementIncludesMargins.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3cd5ccd11f1c0674eea8877235763363168c7088205b30875a9ee3ed7e2953f +size 8422 diff --git a/redwood-layout-composeui/src/test/snapshots/images/app.cash.redwood.layout.composeui_ComposeUiBoxTest_testBoxMeasurementIncludesMargins.png b/redwood-layout-composeui/src/test/snapshots/images/app.cash.redwood.layout.composeui_ComposeUiBoxTest_testBoxMeasurementIncludesMargins.png new file mode 100644 index 0000000000..cb153c2c8a --- /dev/null +++ b/redwood-layout-composeui/src/test/snapshots/images/app.cash.redwood.layout.composeui_ComposeUiBoxTest_testBoxMeasurementIncludesMargins.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4e4c93fbb3daedb79fc97501c6de6922657ffdc4d032ec20a949ade65fdd441 +size 8332 diff --git a/redwood-layout-shared-test/src/commonMain/kotlin/app/cash/redwood/layout/AbstractBoxTest.kt b/redwood-layout-shared-test/src/commonMain/kotlin/app/cash/redwood/layout/AbstractBoxTest.kt index e2f100b5c3..a09fa185f1 100644 --- a/redwood-layout-shared-test/src/commonMain/kotlin/app/cash/redwood/layout/AbstractBoxTest.kt +++ b/redwood-layout-shared-test/src/commonMain/kotlin/app/cash/redwood/layout/AbstractBoxTest.kt @@ -361,6 +361,49 @@ abstract class AbstractBoxTest { snapshotter(widget.value).snapshot() } + @Test + fun testBoxMeasurementIncludesMargins() { + val container = widgetFactory.column() + container.add( + box().apply { + children.insert(0, widgetFactory.text("box 1")) + }.value, + ) + + container.add( + box().apply { + horizontalAlignment(CrossAxisAlignment.End) + margin(Margin(start = 10.dp, top = 20.dp, end = 30.dp, bottom = 40.dp)) + children.insert(0, widgetFactory.text("box 2")) + }.value, + ) + + container.add( + box().apply { + horizontalAlignment(CrossAxisAlignment.Center) + children.insert(0, widgetFactory.text("box 3")) + }.value, + ) + + container.add( + box().apply { + margin(Margin(start = 10.dp, top = 20.dp, end = 30.dp, bottom = 40.dp)) + children.insert(0, widgetFactory.text("box 4")) + }.value, + ) + + container.add( + box().apply { + horizontalAlignment(CrossAxisAlignment.End) + children.insert(0, widgetFactory.text("box 5")) + }.value, + ) + + val scrollWrapper = widgetFactory.scrollWrapper() + scrollWrapper.content = container.value + snapshotter(scrollWrapper.value).snapshot() + } + @Test fun testMarginsAndAlignment() { val widget = box().apply { diff --git a/redwood-layout-uiview/RedwoodLayoutUIViewTests/__Snapshots__/UIViewBoxTestHost/testBoxMeasurementIncludesMargins.1.png b/redwood-layout-uiview/RedwoodLayoutUIViewTests/__Snapshots__/UIViewBoxTestHost/testBoxMeasurementIncludesMargins.1.png new file mode 100644 index 0000000000..e52555d519 --- /dev/null +++ b/redwood-layout-uiview/RedwoodLayoutUIViewTests/__Snapshots__/UIViewBoxTestHost/testBoxMeasurementIncludesMargins.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7a9baf660d9a1d37e2f83d34d6456f21a37b579f2be401146e1529e38989d69 +size 77937 diff --git a/redwood-layout-uiview/src/commonMain/kotlin/app/cash/redwood/layout/uiview/UIViewBox.kt b/redwood-layout-uiview/src/commonMain/kotlin/app/cash/redwood/layout/uiview/UIViewBox.kt index 9c8eee7577..00625b0a80 100644 --- a/redwood-layout-uiview/src/commonMain/kotlin/app/cash/redwood/layout/uiview/UIViewBox.kt +++ b/redwood-layout-uiview/src/commonMain/kotlin/app/cash/redwood/layout/uiview/UIViewBox.kt @@ -36,12 +36,12 @@ import kotlinx.cinterop.convert import kotlinx.cinterop.readValue import kotlinx.cinterop.useContents import platform.CoreGraphics.CGFloat -import platform.CoreGraphics.CGRect import platform.CoreGraphics.CGRectMake import platform.CoreGraphics.CGRectZero import platform.CoreGraphics.CGSize import platform.CoreGraphics.CGSizeMake import platform.UIKit.UIView +import platform.UIKit.UIViewNoIntrinsicMetric import platform.darwin.NSInteger internal class UIViewBox : @@ -118,6 +118,9 @@ internal class UIViewBox : } } + override fun intrinsicContentSize() = + sizeThatFits(CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric)) + override fun layoutSubviews() { super.layoutSubviews() @@ -126,7 +129,8 @@ internal class UIViewBox : boxHorizontalAlignment = horizontalAlignment, boxVerticalAlignment = verticalAlignment, boxMargin = margin, - boxFrame = frame, + boxWidth = frame.useContents { size.width }, + boxHeight = frame.useContents { size.height }, ) for (widget in children.widgets) { @@ -141,7 +145,8 @@ internal class UIViewBox : boxHorizontalAlignment = horizontalAlignment, boxVerticalAlignment = verticalAlignment, boxMargin = margin, - boxFrame = frame, + boxWidth = size.useContents { width }, + boxHeight = size.useContents { height }, ) var maxWidth = 0.0 @@ -152,7 +157,10 @@ internal class UIViewBox : maxHeight = maxOf(maxHeight, measurer.height + measurer.marginHeight) } - return CGSizeMake(maxWidth, maxHeight) + val boxMarginWidth = measurer.boxMarginStart + measurer.boxMarginEnd + val boxMarginHeight = measurer.boxMarginTop + measurer.boxMarginBottom + + return CGSizeMake(maxWidth + boxMarginWidth, maxHeight + boxMarginHeight) } } } @@ -201,7 +209,8 @@ private class Measurer { boxHorizontalAlignment: CrossAxisAlignment, boxVerticalAlignment: CrossAxisAlignment, boxMargin: Margin, - boxFrame: CValue, + boxWidth: CGFloat, + boxHeight: CGFloat, ) { this.boxDensity = boxDensity this.boxHorizontalAlignment = boxHorizontalAlignment @@ -212,9 +221,13 @@ private class Measurer { boxMarginTop = boxMargin.top.toPx() boxMarginBottom = boxMargin.bottom.toPx() } - boxFrame.useContents { - frameWidth = (size.width - boxMarginStart - boxMarginEnd).coerceAtLeast(0.0) - frameHeight = (size.height - boxMarginTop - boxMarginBottom).coerceAtLeast(0.0) + this.frameWidth = when { + boxWidth == UIViewNoIntrinsicMetric -> UIViewNoIntrinsicMetric + else -> (boxWidth - boxMarginStart - boxMarginEnd).coerceAtLeast(0.0) + } + this.frameHeight = when { + boxHeight == UIViewNoIntrinsicMetric -> UIViewNoIntrinsicMetric + else -> (boxHeight - boxMarginTop - boxMarginBottom).coerceAtLeast(0.0) } } @@ -253,8 +266,14 @@ private class Measurer { } } - val availableWidth = (frameWidth - marginWidth).coerceAtLeast(0.0) - val availableHeight = (frameHeight - marginHeight).coerceAtLeast(0.0) + val availableWidth = when { + frameWidth == UIViewNoIntrinsicMetric -> UIViewNoIntrinsicMetric + else -> (frameWidth - marginWidth).coerceAtLeast(0.0) + } + val availableHeight = when { + frameHeight == UIViewNoIntrinsicMetric -> UIViewNoIntrinsicMetric + else -> (frameHeight - marginHeight).coerceAtLeast(0.0) + } val fitWidth = when { !requestedWidth.isNaN() -> requestedWidth diff --git a/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/ViewBox.kt b/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/ViewBox.kt index 9317ba5404..7225721d7e 100644 --- a/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/ViewBox.kt +++ b/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/ViewBox.kt @@ -122,15 +122,18 @@ private class BoxViewGroup( maxHeight = maxOf(maxHeight, measurer.height + measurer.marginHeight) } - val resultWidth = when { - widthMode == MeasureSpec.EXACTLY -> widthSize - widthMode == MeasureSpec.AT_MOST -> maxWidth.coerceAtMost(widthSize) - else -> maxWidth + val boxMarginWidth = measurer.boxMarginStart + measurer.boxMarginEnd + val boxMarginHeight = measurer.boxMarginTop + measurer.boxMarginBottom + + val resultWidth = when (widthMode) { + MeasureSpec.EXACTLY -> widthSize + MeasureSpec.AT_MOST -> (maxWidth + boxMarginWidth).coerceAtMost(widthSize) + else -> maxWidth + boxMarginWidth } - val resultHeight = when { - heightMode == MeasureSpec.EXACTLY -> heightSize - heightMode == MeasureSpec.AT_MOST -> maxHeight.coerceAtMost(heightSize) - else -> maxHeight + val resultHeight = when (heightMode) { + MeasureSpec.EXACTLY -> heightSize + MeasureSpec.AT_MOST -> (maxHeight + boxMarginHeight).coerceAtMost(heightSize) + else -> maxHeight + boxMarginHeight } setMeasuredDimension(resultWidth, resultHeight) diff --git a/redwood-layout-view/src/test/snapshots/images/app.cash.redwood.layout.view_ViewBoxTest_RTL_testBoxMeasurementIncludesMargins.png b/redwood-layout-view/src/test/snapshots/images/app.cash.redwood.layout.view_ViewBoxTest_RTL_testBoxMeasurementIncludesMargins.png new file mode 100644 index 0000000000..a44a910fdc --- /dev/null +++ b/redwood-layout-view/src/test/snapshots/images/app.cash.redwood.layout.view_ViewBoxTest_RTL_testBoxMeasurementIncludesMargins.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c553478329b73549f225c773d8bd27bdf8768721b6804f0b92e4708c36d4eb44 +size 8441 diff --git a/redwood-layout-view/src/test/snapshots/images/app.cash.redwood.layout.view_ViewBoxTest_testBoxMeasurementIncludesMargins.png b/redwood-layout-view/src/test/snapshots/images/app.cash.redwood.layout.view_ViewBoxTest_testBoxMeasurementIncludesMargins.png new file mode 100644 index 0000000000..738e4c5cbc --- /dev/null +++ b/redwood-layout-view/src/test/snapshots/images/app.cash.redwood.layout.view_ViewBoxTest_testBoxMeasurementIncludesMargins.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed598eabc6f6dd10df2d912c1a46e47b1bdb8cc79d0a1670f97e420d8456338c +size 8459