[go: nahoru, domu]

blob: b12e9c5d654ffce629d8942a337a3c083a0e75b0 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.ui.layout.test
import androidx.compose.Composable
import androidx.compose.emptyContent
import androidx.test.filters.SmallTest
import androidx.ui.core.Alignment
import androidx.ui.core.Constraints
import androidx.ui.core.FirstBaseline
import androidx.ui.core.HorizontalAlignmentLine
import androidx.ui.core.Layout
import androidx.ui.core.LayoutCoordinates
import androidx.ui.core.Modifier
import androidx.ui.core.OnChildPositioned
import androidx.ui.core.OnPositioned
import androidx.ui.core.Ref
import androidx.ui.core.VerticalAlignmentLine
import androidx.ui.core.WithConstraints
import androidx.ui.core.globalPosition
import androidx.ui.layout.Align
import androidx.ui.layout.Center
import androidx.ui.layout.Column
import androidx.ui.layout.Container
import androidx.ui.layout.DpConstraints
import androidx.ui.layout.Arrangement
import androidx.ui.layout.LayoutAlign
import androidx.ui.layout.LayoutAspectRatio
import androidx.ui.layout.LayoutDirectionModifier
import androidx.ui.layout.LayoutGravity
import androidx.ui.layout.LayoutHeight
import androidx.ui.layout.LayoutPadding
import androidx.ui.layout.LayoutSize
import androidx.ui.layout.LayoutWidth
import androidx.ui.layout.Row
import androidx.ui.layout.Spacer
import androidx.ui.layout.Wrap
import androidx.ui.unit.Dp
import androidx.ui.unit.IntPx
import androidx.ui.unit.IntPxSize
import androidx.ui.unit.PxPosition
import androidx.ui.unit.dp
import androidx.ui.unit.ipx
import androidx.ui.unit.max
import androidx.ui.unit.min
import androidx.ui.unit.px
import androidx.ui.unit.round
import androidx.ui.unit.toPx
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@SmallTest
@RunWith(JUnit4::class)
class FlexTest : LayoutTest() {
// region Size and position tests for Row and Column
@Test
fun testRow() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(2)
val childSize = arrayOf(IntPxSize(-1.ipx, -1.ipx), IntPxSize(-1.ipx, -1.ipx))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
Container(alignment = Alignment.TopStart) {
Row {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(width = (sizeDp * 2), height = (sizeDp * 2)) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(IntPxSize(size, size), childSize[0])
assertEquals(
IntPxSize((sizeDp.toPx() * 2).round(), (sizeDp.toPx() * 2).round()),
childSize[1]
)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(size.toPx(), 0.px), childPosition[1])
}
@Test
fun testRow_withFlexibleChildren() = with(density) {
val width = 50.dp
val height = 80.dp
val childrenHeight = height.toIntPx()
val drawLatch = CountDownLatch(2)
val childSize = arrayOfNulls<IntPxSize>(2)
val childPosition = arrayOfNulls<PxPosition>(2)
show {
Container(alignment = Alignment.TopStart) {
Row {
Container(LayoutFlexible(1f), width = width, height = height) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(LayoutFlexible(2f), width = width, height = height) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val rootWidth = root.width.px
assertEquals(
IntPxSize((rootWidth / 3).round(), childrenHeight),
childSize[0]
)
assertEquals(
IntPxSize((rootWidth * 2 / 3).round(), childrenHeight),
childSize[1]
)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition((rootWidth / 3).round().toPx(), 0.px), childPosition[1])
}
@Test
fun testRow_withLooselyFlexibleChildren() = with(density) {
val width = 50.dp
val childrenWidth = width.toIntPx()
val height = 80.dp
val childrenHeight = height.toIntPx()
val drawLatch = CountDownLatch(2)
val childSize = arrayOfNulls<IntPxSize>(2)
val childPosition = arrayOfNulls<PxPosition>(2)
show {
Container(alignment = Alignment.TopStart) {
Row {
Container(
LayoutFlexible(1f, tight = false),
width = width,
height = height
) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(
LayoutFlexible(2f, tight = false),
width = width,
height = height * 2
) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(IntPxSize(childrenWidth, childrenHeight), childSize[0])
assertEquals(IntPxSize(childrenWidth, childrenHeight * 2), childSize[1])
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(childrenWidth.toPx(), 0.px), childPosition[1])
}
@Test
fun testColumn() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(2)
val childSize = arrayOf(IntPxSize(-1.ipx, -1.ipx), IntPxSize(-1.ipx, -1.ipx))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
Container(alignment = Alignment.TopStart) {
Column {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(width = (sizeDp * 2), height = (sizeDp * 2)) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(IntPxSize(size, size), childSize[0])
assertEquals(
IntPxSize((sizeDp.toPx() * 2).round(), (sizeDp.toPx() * 2).round()),
childSize[1]
)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(0.px, size.toPx()), childPosition[1])
}
@Test
fun testColumn_withFlexibleChildren() = with(density) {
val width = 80.dp
val childrenWidth = width.toIntPx()
val height = 50.dp
val drawLatch = CountDownLatch(2)
val childSize = arrayOfNulls<IntPxSize>(2)
val childPosition = arrayOfNulls<PxPosition>(2)
show {
Container(alignment = Alignment.TopStart) {
Column {
Container(LayoutFlexible(1f), width = width, height = height) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(LayoutFlexible(2f), width = width, height = height) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val rootHeight = root.height.px
assertEquals(
IntPxSize(childrenWidth, (rootHeight / 3).round()), childSize[0]
)
assertEquals(
IntPxSize(childrenWidth, (rootHeight * 2 / 3).round()), childSize[1]
)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(0.px, (rootHeight / 3).round().toPx()), childPosition[1])
}
@Test
fun testColumn_withLooselyFlexibleChildren() = with(density) {
val width = 80.dp
val childrenWidth = width.toIntPx()
val height = 50.dp
val childrenHeight = height.toIntPx()
val drawLatch = CountDownLatch(2)
val childSize = arrayOfNulls<IntPxSize>(2)
val childPosition = arrayOfNulls<PxPosition>(2)
show {
Container(alignment = Alignment.TopStart) {
Column {
Container(LayoutFlexible(1f, tight = false), width = width, height = height) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(LayoutFlexible(2f, tight = false), width = width, height = height) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(IntPxSize(childrenWidth, childrenHeight), childSize[0])
assertEquals(
IntPxSize(childrenWidth, childrenHeight), childSize[1]
)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(0.px, childrenHeight.toPx()), childPosition[1])
}
@Test
fun testRow_doesNotPlaceChildrenOutOfBounds_becauseOfRoundings() = with(density) {
val expectedRowWidth = 11.ipx
val leftPadding = 1.px
var rowWidth = 0.ipx
val width = Array(2) { 0.ipx }
val x = Array(2) { 0.px }
val latch = CountDownLatch(2)
show {
Row(
LayoutAlign.TopStart + LayoutPadding(start = leftPadding.toDp()) +
LayoutWidth.Max(expectedRowWidth.toDp())
) {
OnPositioned { coordinates -> rowWidth = coordinates.size.width }
Container(LayoutFlexible(1f)) {
OnPositioned { coordinates ->
width[0] = coordinates.size.width
x[0] = coordinates.globalPosition.x
latch.countDown()
}
}
Container(LayoutFlexible(1f)) {
OnPositioned { coordinates ->
width[1] = coordinates.size.width
x[1] = coordinates.globalPosition.x
latch.countDown()
}
}
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
assertEquals(expectedRowWidth, rowWidth)
assertEquals(leftPadding, x[0])
assertEquals(leftPadding + width[0], x[1])
assertEquals(rowWidth, width[0] + width[1])
}
@Test
fun testRow_isNotLargerThanItsChildren_becauseOfRoundings() = with(density) {
val expectedRowWidth = 8.ipx
val leftPadding = 1.px
var rowWidth = 0.ipx
val width = Array(3) { 0.ipx }
val x = Array(3) { 0.px }
val latch = CountDownLatch(3)
show {
Row(
LayoutAlign.TopStart + LayoutPadding(start = leftPadding.toDp()) +
LayoutWidth.Max(expectedRowWidth.toDp())
) {
OnPositioned { coordinates -> rowWidth = coordinates.size.width }
Container(LayoutFlexible(2f)) {
OnPositioned { coordinates ->
width[0] = coordinates.size.width
x[0] = coordinates.globalPosition.x
latch.countDown()
}
}
Container(LayoutFlexible(2f)) {
OnPositioned { coordinates ->
width[1] = coordinates.size.width
x[1] = coordinates.globalPosition.x
latch.countDown()
}
}
Container(LayoutFlexible(3f)) {
OnPositioned { coordinates ->
width[2] = coordinates.size.width
x[2] = coordinates.globalPosition.x
latch.countDown()
}
}
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
assertEquals(expectedRowWidth, rowWidth)
assertEquals(leftPadding, x[0])
assertEquals(leftPadding + width[0], x[1])
assertEquals(leftPadding + width[0] + width[1], x[2])
assertEquals(rowWidth, width[0] + width[1] + width[2])
}
@Test
fun testColumn_isNotLargetThanItsChildren_becauseOfRoundings() = with(density) {
val expectedColumnHeight = 8.ipx
val topPadding = 1.px
var columnHeight = 0.ipx
val height = Array(3) { 0.ipx }
val y = Array(3) { 0.px }
val latch = CountDownLatch(3)
show {
Column(
LayoutAlign.TopStart + LayoutPadding(top = topPadding.toDp()) +
LayoutHeight.Max(expectedColumnHeight.toDp())
) {
OnPositioned { coordinates -> columnHeight = coordinates.size.height }
Container(LayoutFlexible(1f)) {
OnPositioned { coordinates ->
height[0] = coordinates.size.height
y[0] = coordinates.globalPosition.y
latch.countDown()
}
}
Container(LayoutFlexible(1f)) {
OnPositioned { coordinates ->
height[1] = coordinates.size.height
y[1] = coordinates.globalPosition.y
latch.countDown()
}
}
Container(LayoutFlexible(1f)) {
OnPositioned { coordinates ->
height[2] = coordinates.size.height
y[2] = coordinates.globalPosition.y
latch.countDown()
}
}
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
assertEquals(expectedColumnHeight, columnHeight)
assertEquals(topPadding, y[0])
assertEquals(topPadding + height[0], y[1])
assertEquals(topPadding + height[0] + height[1], y[2])
assertEquals(columnHeight, height[0] + height[1] + height[2])
}
@Test
fun testColumn_doesNotPlaceChildrenOutOfBounds_becauseOfRoundings() = with(density) {
val expectedColumnHeight = 11.ipx
val topPadding = 1.px
var columnHeight = 0.ipx
val height = Array(2) { 0.ipx }
val y = Array(2) { 0.px }
val latch = CountDownLatch(2)
show {
Column(
LayoutAlign.TopStart + LayoutPadding(top = topPadding.toDp()) +
LayoutHeight.Max(expectedColumnHeight.toDp())
) {
OnPositioned { coordinates -> columnHeight = coordinates.size.height }
Container(LayoutFlexible(1f)) {
OnPositioned { coordinates ->
height[0] = coordinates.size.height
y[0] = coordinates.globalPosition.y
latch.countDown()
}
}
Container(LayoutFlexible(1f)) {
OnPositioned { coordinates ->
height[1] = coordinates.size.height
y[1] = coordinates.globalPosition.y
latch.countDown()
}
}
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
assertEquals(expectedColumnHeight, columnHeight)
assertEquals(topPadding, y[0])
assertEquals(topPadding + height[0], y[1])
assertEquals(columnHeight, height[0] + height[1])
}
// endregion
// region Cross axis alignment tests in Row
@Test
fun testRow_withStretchCrossAxisAlignment() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(2)
val childSize = arrayOf(IntPxSize(-1.ipx, -1.ipx), IntPxSize(-1.ipx, -1.ipx))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
Align(Alignment.CenterStart) {
Row {
Container(width = sizeDp, height = sizeDp, modifier = LayoutHeight.Fill) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(
width = (sizeDp * 2),
height = (sizeDp * 2),
modifier = LayoutHeight.Fill
) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(IntPxSize(size, root.height.ipx), childSize[0])
assertEquals(
IntPxSize((sizeDp.toPx() * 2).round(), root.height.ipx),
childSize[1]
)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(size.toPx(), 0.px), childPosition[1])
}
@Test
fun testRow_withGravityModifier() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(3)
val childSize = arrayOfNulls<IntPxSize>(3)
val childPosition = arrayOfNulls<PxPosition>(3)
show {
Align(Alignment.TopStart) {
Row(LayoutHeight.Fill) {
Container(width = sizeDp, height = sizeDp, modifier = LayoutGravity.Top) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.globalPosition
drawLatch.countDown()
})
}
Container(width = sizeDp, height = sizeDp, modifier = LayoutGravity.Center) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.globalPosition
drawLatch.countDown()
})
}
Container(width = sizeDp, height = sizeDp, modifier = LayoutGravity.Bottom) {
OnPositioned(onPositioned = { coordinates ->
childSize[2] = coordinates.size
childPosition[2] = coordinates.globalPosition
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val rootHeight = root.height.px
assertEquals(IntPxSize(size, size), childSize[0])
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(IntPxSize(size, size), childSize[1])
assertEquals(
PxPosition(size.toPx(), ((rootHeight - size.toPx()) / 2).round().toPx()),
childPosition[1]
)
assertEquals(IntPxSize(size, size), childSize[2])
assertEquals(PxPosition(size.toPx() * 2, rootHeight - size.toPx()), childPosition[2])
}
@Test
fun testRow_withRelativeToSiblingsModifier() = with(density) {
val baseline1Dp = 30.dp
val baseline1 = baseline1Dp.toIntPx()
val baseline2Dp = 25.dp
val baseline2 = baseline2Dp.toIntPx()
val sizeDp = 40.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childSize = arrayOfNulls<IntPxSize>(4)
val childPosition = arrayOfNulls<PxPosition>(4)
show {
Align(Alignment.TopStart) {
Row(LayoutHeight.Fill) {
BaselineTestLayout(
baseline = baseline1Dp,
width = sizeDp,
height = sizeDp,
modifier = LayoutGravity.RelativeToSiblings(TestHorizontalLine)
) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.globalPosition
drawLatch.countDown()
})
}
Container(
width = sizeDp,
height = sizeDp,
modifier = LayoutGravity.RelativeToSiblings { it.height * 0.5 }
) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.globalPosition
drawLatch.countDown()
})
}
BaselineTestLayout(
baseline = baseline2Dp,
width = sizeDp,
height = sizeDp,
modifier = LayoutGravity.RelativeToSiblings(TestHorizontalLine)
) {
OnPositioned(onPositioned = { coordinates ->
childSize[2] = coordinates.size
childPosition[2] = coordinates.globalPosition
drawLatch.countDown()
})
}
Container(
width = sizeDp,
height = sizeDp,
modifier = LayoutGravity.RelativeToSiblings { it.height * 0.75 }
) {
OnPositioned(onPositioned = { coordinates ->
childSize[3] = coordinates.size
childPosition[3] = coordinates.globalPosition
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
assertEquals(IntPxSize(size, size), childSize[0])
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(IntPxSize(size, size), childSize[1])
assertEquals(
PxPosition(size.toPx(), (baseline1 - (size.toPx() / 2).round()).toPx()),
childPosition[1]
)
assertEquals(IntPxSize(size, size), childSize[2])
assertEquals(
PxPosition(size.toPx() * 2, (baseline1 - baseline2).toPx()),
childPosition[2]
)
assertEquals(IntPxSize(size, size), childSize[3])
assertEquals(
PxPosition(size.toPx() * 3, 0.px),
childPosition[3]
)
}
// endregion
// region Cross axis alignment tests in Column
@Test
fun testColumn_withStretchCrossAxisAlignment() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(2)
val childSize = arrayOf(IntPxSize(-1.ipx, -1.ipx), IntPxSize(-1.ipx, -1.ipx))
val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
show {
Align(Alignment.TopCenter) {
Column {
Container(width = sizeDp, height = sizeDp, modifier = LayoutWidth.Fill) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(
width = (sizeDp * 2), height = (sizeDp * 2), modifier = LayoutWidth.Fill
) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(IntPxSize(root.width.ipx, size), childSize[0])
assertEquals(
IntPxSize(root.width.ipx, (sizeDp * 2).toIntPx()),
childSize[1]
)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(0.px, size.toPx()), childPosition[1])
}
@Test
fun testColumn_withGravityModifier() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(3)
val childSize = arrayOfNulls<IntPxSize>(3)
val childPosition = arrayOfNulls<PxPosition>(3)
show {
Align(Alignment.TopCenter) {
Column(LayoutWidth.Fill) {
Container(width = sizeDp, height = sizeDp, modifier = LayoutGravity.Start) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.globalPosition
drawLatch.countDown()
})
}
Container(width = sizeDp, height = sizeDp, modifier = LayoutGravity.Center) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.globalPosition
drawLatch.countDown()
})
}
Container(width = sizeDp, height = sizeDp, modifier = LayoutGravity.End) {
OnPositioned(onPositioned = { coordinates ->
childSize[2] = coordinates.size
childPosition[2] = coordinates.globalPosition
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val rootWidth = root.width.px
assertEquals(IntPxSize(size, size), childSize[0])
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(IntPxSize(size, size), childSize[1])
assertEquals(
PxPosition(((rootWidth - size.toPx()) / 2).round().toPx(), size.toPx()),
childPosition[1]
)
assertEquals(IntPxSize(size, size), childSize[2])
assertEquals(PxPosition(rootWidth - size.toPx(), size.toPx() * 2), childPosition[2])
}
@Test
fun testColumn_withRelativeToSiblingsModifier() = with(density) {
val sizeDp = 40.dp
val size = sizeDp.toIntPx()
val firstBaseline1Dp = 20.dp
val firstBaseline2Dp = 30.dp
val drawLatch = CountDownLatch(4)
val childSize = arrayOfNulls<IntPxSize>(4)
val childPosition = arrayOfNulls<PxPosition>(4)
show {
Align(Alignment.TopStart) {
Column(LayoutWidth.Fill) {
Container(
width = sizeDp,
height = sizeDp,
modifier = LayoutGravity.RelativeToSiblings { it.width }
) {
OnPositioned(onPositioned = { coordinates ->
childSize[0] = coordinates.size
childPosition[0] = coordinates.globalPosition
drawLatch.countDown()
})
}
Container(
width = sizeDp,
height = sizeDp,
modifier = LayoutGravity.RelativeToSiblings { 0.ipx }
) {
OnPositioned(onPositioned = { coordinates ->
childSize[1] = coordinates.size
childPosition[1] = coordinates.globalPosition
drawLatch.countDown()
})
}
BaselineTestLayout(
width = sizeDp,
height = sizeDp,
baseline = firstBaseline1Dp,
modifier = LayoutGravity.RelativeToSiblings(TestVerticalLine)
) {
OnPositioned(onPositioned = { coordinates ->
childSize[2] = coordinates.size
childPosition[2] = coordinates.globalPosition
drawLatch.countDown()
})
}
BaselineTestLayout(
width = sizeDp,
height = sizeDp,
baseline = firstBaseline2Dp,
modifier = LayoutGravity.RelativeToSiblings(TestVerticalLine)
) {
OnPositioned(onPositioned = { coordinates ->
childSize[3] = coordinates.size
childPosition[3] = coordinates.globalPosition
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
assertEquals(IntPxSize(size, size), childSize[0])
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(IntPxSize(size, size), childSize[1])
assertEquals(PxPosition(size.toPx(), size.toPx()), childPosition[1])
assertEquals(IntPxSize(size, size), childSize[2])
assertEquals(
PxPosition((size - firstBaseline1Dp.toIntPx()).toPx(), size.toPx() * 2),
childPosition[2]
)
assertEquals(IntPxSize(size, size), childSize[3])
assertEquals(
PxPosition((size - firstBaseline2Dp.toIntPx()).toPx(), size.toPx() * 3),
childPosition[3]
)
}
// endregion
// region Size tests in Row
@Test
fun testRow_expandedWidth_withExpandedModifier() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
Row(LayoutWidth.Fill) {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = (sizeDp * 2), height = (sizeDp * 2)))
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
root.width.ipx,
rowSize.width
)
}
@Test
fun testRow_wrappedWidth_withNoFlexibleChildren() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
Row {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = (sizeDp * 2), height = (sizeDp * 2)))
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
(sizeDp * 3).toIntPx(),
rowSize.width
)
}
@Test
fun testRow_expandedWidth_withFlexibleChildren() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
Row {
Container(
LayoutFlexible(1f),
width = sizeDp,
height = sizeDp,
children = emptyContent()
)
Container(
width = (sizeDp * 2),
height = (sizeDp * 2),
children = emptyContent()
)
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
root.width.ipx,
rowSize.width
)
}
@Test
fun testRow_withMaxCrossAxisSize() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
Row(LayoutHeight.Fill) {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = (sizeDp * 2), height = (sizeDp * 2)))
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
root.height.ipx,
rowSize.height
)
}
@Test
fun testRow_withMinCrossAxisSize() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
Row {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = (sizeDp * 2), height = (sizeDp * 2)))
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
(sizeDp * 2).toIntPx(),
rowSize.height
)
}
@Test
fun testRow_withExpandedModifier_respectsMaxWidthConstraint() = with(density) {
val sizeDp = 50.dp
val rowWidthDp = 250.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(maxWidth = rowWidthDp)) {
Row(LayoutWidth.Fill) {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = sizeDp * 2, height = sizeDp * 2))
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
min(root.width.ipx, rowWidthDp.toIntPx()),
rowSize.width
)
}
@Test
fun testRow_withFlexibleChildren_respectsMaxWidthConstraint() = with(density) {
val sizeDp = 50.dp
val rowWidthDp = 250.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(maxWidth = rowWidthDp)) {
Row {
Container(
LayoutFlexible(1f),
width = sizeDp,
height = sizeDp,
children = emptyContent()
)
Container(
width = sizeDp * 2,
height = sizeDp * 2,
children = emptyContent()
)
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
min(root.width.ipx, rowWidthDp.toIntPx()),
rowSize.width
)
}
@Test
fun testRow_withInflexibleChildren_respectsMinWidthConstraint() = with(density) {
val sizeDp = 50.dp
val rowWidthDp = 250.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(minWidth = rowWidthDp)) {
Row {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = sizeDp * 2, height = sizeDp * 2))
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
rowWidthDp.toIntPx(),
rowSize.width
)
}
@Test
fun testRow_withMaxCrossAxisSize_respectsMaxHeightConstraint() = with(density) {
val sizeDp = 50.dp
val rowHeightDp = 250.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(maxHeight = rowHeightDp)) {
Row(LayoutHeight.Fill) {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = sizeDp * 2, height = sizeDp * 2))
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
min(root.height.ipx, rowHeightDp.toIntPx()),
rowSize.height
)
}
@Test
fun testRow_withMinCrossAxisSize_respectsMinHeightConstraint() = with(density) {
val sizeDp = 50.dp
val rowHeightDp = 150.dp
val drawLatch = CountDownLatch(1)
lateinit var rowSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(minHeight = rowHeightDp)) {
Row {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = sizeDp * 2, height = sizeDp * 2))
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
rowHeightDp.toIntPx(),
rowSize.height
)
}
@Test
@Ignore(
"Wrap is not supported when there are flexible children. " +
"Should use maxWidth(.Infinity) modifier when it is available"
)
fun testRow_withMinMainAxisSize() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val rowWidthDp = 250.dp
val rowWidth = rowWidthDp.toIntPx()
val drawLatch = CountDownLatch(2)
lateinit var rowSize: IntPxSize
lateinit var expandedChildSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(minWidth = rowWidthDp)) {
// TODO: add maxWidth(IntPx.Infinity) modifier
Row {
Container(modifier = LayoutFlexible(1f), width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
expandedChildSize = coordinates.size
drawLatch.countDown()
})
}
OnPositioned(onPositioned = { coordinates ->
rowSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
IntPxSize(rowWidth, size),
rowSize
)
assertEquals(
IntPxSize(rowWidth, size),
expandedChildSize
)
}
@Test
fun testRow_measuresChildrenCorrectly_whenMeasuredWithInfiniteWidth() = with(density) {
val rowMinWidth = 100.dp
val inflexibleChildWidth = 30.dp
val latch = CountDownLatch(1)
show {
WithInfiniteConstraints {
ConstrainedBox(DpConstraints(minWidth = rowMinWidth)) {
Row {
WithConstraints { constraints, _ ->
assertEquals(Constraints(), constraints)
FixedSizeLayout(inflexibleChildWidth.toIntPx(), 0.ipx, mapOf())
}
WithConstraints { constraints, _ ->
assertEquals(Constraints(), constraints)
FixedSizeLayout(inflexibleChildWidth.toIntPx(), 0.ipx, mapOf())
}
Layout({}, LayoutFlexible(1f)) { _, constraints, _ ->
assertEquals(
rowMinWidth.toIntPx() - inflexibleChildWidth.toIntPx() * 2,
constraints.minWidth
)
assertEquals(
rowMinWidth.toIntPx() - inflexibleChildWidth.toIntPx() * 2,
constraints.maxWidth
)
latch.countDown()
layout(0.ipx, 0.ipx) { }
}
}
}
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
}
@Test
fun testRow_measuresInflexibleChildrenCorrectly() = with(density) {
val availableWidth = 100.dp
val childWidth = 50.dp
val availableHeight = 200.dp
val childHeight = 100.dp
val latch = CountDownLatch(1)
show {
Wrap {
ConstrainedBox(
DpConstraints(
minWidth = availableWidth,
maxWidth = availableWidth,
minHeight = availableHeight,
maxHeight = availableHeight
)
) {
Row {
WithConstraints { constraints, _ ->
assertEquals(
Constraints(
maxWidth = availableWidth.toIntPx(),
maxHeight = availableHeight.toIntPx()
),
constraints
)
FixedSizeLayout(childWidth.toIntPx(), childHeight.toIntPx(), mapOf())
}
WithConstraints { constraints, _ ->
assertEquals(
Constraints(
maxWidth = availableWidth.toIntPx() - childWidth.toIntPx(),
maxHeight = availableHeight.toIntPx()
),
constraints
)
FixedSizeLayout(childWidth.toIntPx(), childHeight.toIntPx(), mapOf())
latch.countDown()
}
}
}
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
}
// endregion
// region Size tests in Column
@Test
fun testColumn_expandedHeight_withExpandedModifier() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
Column(LayoutHeight.Fill) {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = (sizeDp * 2), height = (sizeDp * 2)))
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
root.height.ipx,
columnSize.height
)
}
@Test
fun testColumn_wrappedHeight_withNoFlexibleChildren() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
Column {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = (sizeDp * 2), height = (sizeDp * 2)))
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
(sizeDp * 3).toIntPx(),
columnSize.height
)
}
@Test
fun testColumn_expandedHeight_withFlexibleChildren() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
Column {
Container(
LayoutFlexible(1f),
width = sizeDp,
height = sizeDp,
children = emptyContent()
)
Container(
width = (sizeDp * 2),
height = (sizeDp * 2),
children = emptyContent()
)
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
root.height.ipx,
columnSize.height
)
}
@Test
fun testColumn_withMaxCrossAxisSize() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
Column(LayoutWidth.Fill) {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = (sizeDp * 2), height = (sizeDp * 2)))
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
root.width.ipx,
columnSize.width
)
}
@Test
fun testColumn_withMinCrossAxisSize() = with(density) {
val sizeDp = 50.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
Column {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = (sizeDp * 2), height = (sizeDp * 2)))
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
(sizeDp * 2).toIntPx(),
columnSize.width
)
}
@Test
fun testColumn_withExpandedModifier_respectsMaxHeightConstraint() = with(density) {
val sizeDp = 50.dp
val columnHeightDp = 250.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(maxHeight = columnHeightDp)) {
Column(LayoutHeight.Fill) {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = sizeDp * 2, height = sizeDp * 2))
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
min(root.height.ipx, columnHeightDp.toIntPx()),
columnSize.height
)
}
@Test
fun testColumn_withFlexibleChildren_respectsMaxHeightConstraint() = with(density) {
val sizeDp = 50.dp
val columnHeightDp = 250.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(maxHeight = columnHeightDp)) {
Column {
Container(
LayoutFlexible(1f),
width = sizeDp,
height = sizeDp,
children = emptyContent()
)
Container(
width = sizeDp * 2,
height = sizeDp * 2,
children = emptyContent()
)
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
min(root.height.ipx, columnHeightDp.toIntPx()),
columnSize.height
)
}
@Test
fun testColumn_withInflexibleChildren_respectsMinHeightConstraint() = with(density) {
val sizeDp = 50.dp
val columnHeightDp = 250.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(minHeight = columnHeightDp)) {
Column {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = sizeDp * 2, height = sizeDp * 2))
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
columnHeightDp.toIntPx(),
columnSize.height
)
}
@Test
fun testColumn_withMaxCrossAxisSize_respectsMaxWidthConstraint() = with(density) {
val sizeDp = 50.dp
val columnWidthDp = 250.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(maxWidth = columnWidthDp)) {
Column(LayoutWidth.Fill) {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = sizeDp * 2, height = sizeDp * 2))
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
min(root.width.ipx, columnWidthDp.toIntPx()),
columnSize.width
)
}
@Test
fun testColumn_withMinCrossAxisSize_respectsMinWidthConstraint() = with(density) {
val sizeDp = 50.dp
val columnWidthDp = 150.dp
val drawLatch = CountDownLatch(1)
lateinit var columnSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(minWidth = columnWidthDp)) {
Column {
Spacer(LayoutSize(width = sizeDp, height = sizeDp))
Spacer(LayoutSize(width = sizeDp * 2, height = sizeDp * 2))
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
columnWidthDp.toIntPx(),
columnSize.width
)
}
@Test
@Ignore(
"Wrap is not supported when there are flexible children. " +
"Should use maxHeight(IntPx.Infinity) modifier when it is available"
)
fun testColumn_withMinMainAxisSize() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val columnHeightDp = 250.dp
val columnHeight = columnHeightDp.toIntPx()
val drawLatch = CountDownLatch(2)
lateinit var columnSize: IntPxSize
lateinit var expandedChildSize: IntPxSize
show {
Center {
ConstrainedBox(constraints = DpConstraints(minHeight = columnHeightDp)) {
// TODO: add maxHeight(IntPx.Infinity) modifier
Column(LayoutHeight.Max(Dp.Infinity)) {
Container(LayoutFlexible(1f), width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
expandedChildSize = coordinates.size
drawLatch.countDown()
})
}
OnPositioned(onPositioned = { coordinates ->
columnSize = coordinates.size
drawLatch.countDown()
})
}
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(
IntPxSize(size, columnHeight),
columnSize
)
assertEquals(
IntPxSize(size, columnHeight),
expandedChildSize
)
}
@Test
fun testColumn_measuresChildrenCorrectly_whenMeasuredWithInfiniteHeight() =
with(density) {
val columnMinHeight = 100.dp
val inflexibleChildHeight = 30.dp
val latch = CountDownLatch(1)
show {
WithInfiniteConstraints {
ConstrainedBox(DpConstraints(minHeight = columnMinHeight)) {
Column {
WithConstraints { constraints, _ ->
assertEquals(Constraints(), constraints)
FixedSizeLayout(0.ipx, inflexibleChildHeight.toIntPx(), mapOf())
}
WithConstraints { constraints, _ ->
assertEquals(Constraints(), constraints)
FixedSizeLayout(0.ipx, inflexibleChildHeight.toIntPx(), mapOf())
}
Layout(emptyContent(), LayoutFlexible(1f)) { _, constraints, _ ->
assertEquals(
columnMinHeight.toIntPx() - inflexibleChildHeight.toIntPx() * 2,
constraints.minHeight
)
assertEquals(
columnMinHeight.toIntPx() - inflexibleChildHeight.toIntPx() * 2,
constraints.maxHeight
)
latch.countDown()
layout(0.ipx, 0.ipx) { }
}
}
}
}
}
}
@Test
fun testColumn_measuresInflexibleChildrenCorrectly() = with(density) {
val availableWidth = 100.dp
val childWidth = 50.dp
val availableHeight = 200.dp
val childHeight = 100.dp
val latch = CountDownLatch(1)
show {
Wrap {
ConstrainedBox(
DpConstraints(
minWidth = availableWidth,
maxWidth = availableWidth,
minHeight = availableHeight,
maxHeight = availableHeight
)
) {
Column {
WithConstraints { constraints, _ ->
assertEquals(
Constraints(
maxWidth = availableWidth.toIntPx(),
maxHeight = availableHeight.toIntPx()
),
constraints
)
FixedSizeLayout(childWidth.toIntPx(), childHeight.toIntPx(), mapOf())
}
WithConstraints { constraints, _ ->
assertEquals(
Constraints(
maxWidth = availableWidth.toIntPx(),
maxHeight = availableHeight.toIntPx() - childHeight.toIntPx()
),
constraints
)
FixedSizeLayout(childWidth.toIntPx(), childHeight.toIntPx(), mapOf())
latch.countDown()
}
}
}
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
}
// endregion
// region Main axis alignment tests in Row
@Test
fun testRow_withBeginArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Row(LayoutWidth.Fill) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(size.toPx(), 0.px), childPosition[1])
assertEquals(PxPosition(size.toPx() * 2, 0.px), childPosition[2])
}
@Test
fun testRow_withEndArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Row(LayoutWidth.Fill, arrangement = Arrangement.End) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(PxPosition(root.width.px - size.toPx() * 3, 0.px), childPosition[0])
assertEquals(PxPosition(root.width.px - size.toPx() * 2, 0.px), childPosition[1])
assertEquals(PxPosition(root.width.px - size.toPx(), 0.px), childPosition[2])
}
@Test
fun testRow_withCenterArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Row(LayoutWidth.Fill, arrangement = Arrangement.Center) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val extraSpace = root.width.px.round() - size * 3
assertEquals(PxPosition((extraSpace / 2).toPx(), 0.px), childPosition[0])
assertEquals(PxPosition((extraSpace / 2).toPx() + size.toPx(), 0.px), childPosition[1])
assertEquals(PxPosition((extraSpace / 2).toPx() + size.toPx() * 2, 0.px), childPosition[2])
}
@Test
fun testRow_withSpaceEvenlyArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Row(LayoutWidth.Fill, arrangement = Arrangement.SpaceEvenly) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val gap = (root.width.px - size.toPx() * 3) / 4
assertEquals(PxPosition(gap.round().toPx(), 0.px), childPosition[0])
assertEquals(PxPosition((size.toPx() + gap * 2).round().toPx(), 0.px), childPosition[1])
assertEquals(PxPosition((size.toPx() * 2 + gap * 3).round().toPx(), 0.px), childPosition[2])
}
@Test
fun testRow_withSpaceBetweenArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Row(LayoutWidth.Fill, arrangement = Arrangement.SpaceBetween) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val gap = (root.width.px - size.toPx() * 3) / 2
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition((gap + size.toPx()).round().toPx(), 0.px), childPosition[1])
assertEquals(PxPosition((gap * 2 + size.toPx() * 2).round().toPx(), 0.px), childPosition[2])
}
@Test
fun testRow_withSpaceAroundArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Row(LayoutWidth.Fill, arrangement = Arrangement.SpaceAround) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val gap = (root.width.px.round() - size * 3) / 3
assertEquals(PxPosition((gap / 2).toPx(), 0.px), childPosition[0])
assertEquals(PxPosition((gap * 3 / 2).toPx() + size.toPx(), 0.px), childPosition[1])
assertEquals(PxPosition((gap * 5 / 2).toPx() + size.toPx() * 2, 0.px), childPosition[2])
}
@Test
fun testRow_withCustomArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(3)
val childPosition = arrayOf(
PxPosition.Origin, PxPosition.Origin, PxPosition.Origin
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Row(LayoutWidth.Fill, arrangement = customHorizontalArrangement) {
for (i in childPosition.indices) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in childPosition.indices) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val step = (root.width.px - size.toPx() * 3) / 3
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition((step + size.toPx()).round().toPx(), 0.px), childPosition[1])
assertEquals(
PxPosition((step * 3 + size.toPx() * 2).round().toPx(), 0.px),
childPosition[2]
)
}
// endregion
// region Main axis alignment tests in Column
@Test
fun testColumn_withStartArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Column(LayoutHeight.Fill) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(0.px, size.toPx()), childPosition[1])
assertEquals(PxPosition(0.px, size.toPx() * 2), childPosition[2])
}
@Test
fun testColumn_withEndArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Column(LayoutHeight.Fill, arrangement = Arrangement.Bottom) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(PxPosition(0.px, root.height.px - size.toPx() * 3), childPosition[0])
assertEquals(PxPosition(0.px, root.height.px - size.toPx() * 2), childPosition[1])
assertEquals(PxPosition(0.px, root.height.px - size.toPx()), childPosition[2])
}
@Test
fun testColumn_withCenterArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Column(LayoutHeight.Fill, arrangement = Arrangement.Center) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val extraSpace = root.height.px.round() - size * 3
assertEquals(PxPosition(0.px, (extraSpace / 2).toPx()), childPosition[0])
assertEquals(PxPosition(0.px, (extraSpace / 2).toPx() + size.toPx()), childPosition[1])
assertEquals(PxPosition(0.px, (extraSpace / 2).toPx() + size.toPx() * 2), childPosition[2])
}
@Test
fun testColumn_withSpaceEvenlyArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Column(LayoutHeight.Fill, arrangement = Arrangement.SpaceEvenly) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val gap = (root.height.px - size.toPx() * 3) / 4
assertEquals(PxPosition(0.px, gap.round().toPx()), childPosition[0])
assertEquals(PxPosition(0.px, (size.toPx() + gap * 2).round().toPx()), childPosition[1])
assertEquals(PxPosition(0.px, (size.toPx() * 2 + gap * 3).round().toPx()), childPosition[2])
}
@Test
fun testColumn_withSpaceBetweenArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Column(LayoutHeight.Fill, arrangement = Arrangement.SpaceBetween) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val gap = (root.height.px - size.toPx() * 3) / 2
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(0.px, (gap + size.toPx()).round().toPx()), childPosition[1])
assertEquals(PxPosition(0.px, (gap * 2 + size.toPx() * 2).round().toPx()), childPosition[2])
}
@Test
fun testColumn_withSpaceAroundArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(4)
val childPosition = arrayOf(
PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px)
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Column(LayoutHeight.Fill, arrangement = Arrangement.SpaceAround) {
for (i in 0 until childPosition.size) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in 0 until childPosition.size) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val gap = (root.height.px - size.toPx() * 3) / 3
assertEquals(PxPosition(0.px, (gap / 2).round().toPx()), childPosition[0])
assertEquals(
PxPosition(0.px, ((gap * 3 / 2) + size.toPx()).round().toPx()),
childPosition[1]
)
assertEquals(
PxPosition(0.px, ((gap * 5 / 2) + size.toPx() * 2).round().toPx()),
childPosition[2]
)
}
@Test
fun testColumn_withCustomArrangement() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(3)
val childPosition = arrayOf(
PxPosition.Origin, PxPosition.Origin, PxPosition.Origin
)
val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
show {
Center {
Column(LayoutHeight.Fill, arrangement = customVerticalArrangement) {
for (i in childPosition.indices) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childLayoutCoordinates[i] = coordinates
drawLatch.countDown()
})
}
}
OnPositioned(onPositioned = { coordinates ->
for (i in childPosition.indices) {
childPosition[i] = coordinates
.childToLocal(childLayoutCoordinates[i]!!, PxPosition(0.px, 0.px))
}
drawLatch.countDown()
})
}
}
}
drawLatch.await(1, TimeUnit.SECONDS)
val root = findAndroidComposeView()
waitForDraw(root)
val step = (root.height.px - size.toPx() * 3) / 3
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(0.px, (step + size.toPx()).round().toPx()), childPosition[1])
assertEquals(
PxPosition(0.px, (step * 3 + size.toPx() * 2).round().toPx()),
childPosition[2]
)
}
@Test
fun testRow_doesNotUseMinConstraintsOnChildren() = with(density) {
val sizeDp = 50.dp
val childSizeDp = 30.dp
val childSize = childSizeDp.toIntPx()
val layoutLatch = CountDownLatch(1)
val containerSize = Ref<IntPxSize>()
show {
Center {
ConstrainedBox(
constraints = DpConstraints.fixed(sizeDp, sizeDp)
) {
Row {
OnChildPositioned(onPositioned = { coordinates ->
containerSize.value = coordinates.size
layoutLatch.countDown()
}) {
Spacer(LayoutSize(width = childSizeDp, height = childSizeDp))
}
}
}
}
}
assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
assertEquals(IntPxSize(childSize, childSize), containerSize.value)
}
@Test
fun testColumn_doesNotUseMinConstraintsOnChildren() = with(density) {
val sizeDp = 50.dp
val childSizeDp = 30.dp
val childSize = childSizeDp.toIntPx()
val layoutLatch = CountDownLatch(1)
val containerSize = Ref<IntPxSize>()
show {
Center {
ConstrainedBox(
constraints = DpConstraints.fixed(sizeDp, sizeDp)
) {
Column {
OnChildPositioned(onPositioned = { coordinates ->
containerSize.value = coordinates.size
layoutLatch.countDown()
}) {
Spacer(LayoutSize(width = childSizeDp, height = childSizeDp))
}
}
}
}
}
assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
assertEquals(IntPxSize(childSize, childSize), containerSize.value)
}
// endregion
// region Intrinsic measurement tests
@Test
fun testRow_withInflexibleChildren_hasCorrectIntrinsicMeasurements() = with(density) {
testIntrinsics(@Composable {
Row {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Row(LayoutWidth.Fill) {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Row {
Container(LayoutAspectRatio(2f) + LayoutGravity.Top, children = emptyContent())
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutGravity.Center,
children = emptyContent()
)
}
}, @Composable {
Row {
Container(
LayoutAspectRatio(2f) + LayoutGravity.RelativeToSiblings(FirstBaseline),
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutGravity.RelativeToSiblings { it.width },
children = emptyContent()
)
}
}, @Composable {
Row(LayoutWidth.Fill, arrangement = Arrangement.Start) {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Row(LayoutWidth.Fill, arrangement = Arrangement.Center) {
Container(LayoutGravity.Center + LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutGravity.Center,
children = emptyContent()
)
}
}, @Composable {
Row(LayoutWidth.Fill, arrangement = Arrangement.End) {
Container(LayoutGravity.Bottom + LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutGravity.Bottom,
children = emptyContent()
)
}
}, @Composable {
Row(LayoutWidth.Fill, arrangement = Arrangement.SpaceAround) {
Container(LayoutHeight.Fill + LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutHeight.Fill,
children = emptyContent()
)
}
}, @Composable {
Row(LayoutWidth.Fill, arrangement = Arrangement.SpaceBetween) {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Row(LayoutWidth.Fill, arrangement = Arrangement.SpaceEvenly) {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
// Min width.
assertEquals(50.dp.toIntPx(), minIntrinsicWidth(0.dp.toIntPx()))
assertEquals(25.dp.toIntPx() * 2 + 50.dp.toIntPx(), minIntrinsicWidth(25.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), minIntrinsicWidth(IntPx.Infinity))
// Min height.
assertEquals(40.dp.toIntPx(), minIntrinsicHeight(0.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), minIntrinsicHeight(70.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), minIntrinsicHeight(IntPx.Infinity))
// Max width.
assertEquals(50.dp.toIntPx(), maxIntrinsicWidth(0.dp.toIntPx()))
assertEquals(25.dp.toIntPx() * 2 + 50.dp.toIntPx(), maxIntrinsicWidth(25.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), maxIntrinsicWidth(IntPx.Infinity))
// Max height.
assertEquals(40.dp.toIntPx(), maxIntrinsicHeight(0.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), maxIntrinsicHeight(70.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), maxIntrinsicHeight(IntPx.Infinity))
}
}
@Test
fun testRow_withFlexibleChildren_hasCorrectIntrinsicMeasurements() = with(density) {
testIntrinsics(@Composable {
Row {
ConstrainedBox(
DpConstraints.fixed(20.dp, 30.dp),
LayoutFlexible(3f),
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(30.dp, 40.dp),
LayoutFlexible(2f),
children = emptyContent()
)
Container(LayoutAspectRatio(2f) + LayoutFlexible(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(20.dp, 30.dp), children = emptyContent())
}
}, @Composable {
Row {
ConstrainedBox(
DpConstraints.fixed(20.dp, 30.dp),
LayoutFlexible(3f) + LayoutGravity.Top,
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(30.dp, 40.dp),
LayoutFlexible(2f) + LayoutGravity.Center,
children = emptyContent()
)
Container(LayoutAspectRatio(2f) + LayoutFlexible(2f), children = emptyContent())
ConstrainedBox(
DpConstraints.fixed(20.dp, 30.dp),
LayoutGravity.Bottom,
children = emptyContent()
)
}
}, @Composable {
Row(arrangement = Arrangement.Start) {
ConstrainedBox(
DpConstraints.fixed(20.dp, 30.dp),
LayoutFlexible(3f),
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(30.dp, 40.dp),
LayoutFlexible(2f),
children = emptyContent()
)
Container(LayoutAspectRatio(2f) + LayoutFlexible(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(20.dp, 30.dp), children = emptyContent())
}
}, @Composable {
Row(arrangement = Arrangement.Center) {
ConstrainedBox(
constraints = DpConstraints.fixed(20.dp, 30.dp),
modifier = LayoutFlexible(3f) + LayoutGravity.Center,
children = emptyContent()
)
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 40.dp),
modifier = LayoutFlexible(2f) + LayoutGravity.Center,
children = emptyContent()
)
Container(
LayoutAspectRatio(2f) + LayoutFlexible(2f) + LayoutGravity.Center,
children =
emptyContent()
)
ConstrainedBox(
constraints = DpConstraints.fixed(20.dp, 30.dp),
modifier = LayoutGravity.Center,
children = emptyContent()
)
}
}, @Composable {
Row(arrangement = Arrangement.End) {
ConstrainedBox(
constraints = DpConstraints.fixed(20.dp, 30.dp),
modifier = LayoutFlexible(3f) + LayoutGravity.Bottom,
children = emptyContent()
)
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 40.dp),
modifier = LayoutFlexible(2f) + LayoutGravity.Bottom,
children = emptyContent()
)
Container(
LayoutAspectRatio(2f) + LayoutFlexible(2f) + LayoutGravity.Bottom,
children = emptyContent()
)
ConstrainedBox(
constraints = DpConstraints.fixed(20.dp, 30.dp),
modifier = LayoutGravity.Bottom,
children = emptyContent()
)
}
}, @Composable {
Row(arrangement = Arrangement.SpaceAround) {
ConstrainedBox(
constraints = DpConstraints.fixed(20.dp, 30.dp),
modifier = LayoutFlexible(3f) + LayoutHeight.Fill,
children = emptyContent()
)
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 40.dp),
modifier = LayoutFlexible(2f) + LayoutHeight.Fill,
children = emptyContent()
)
Container(
LayoutAspectRatio(2f) + LayoutFlexible(2f) + LayoutHeight.Fill,
children =
emptyContent()
)
ConstrainedBox(
constraints = DpConstraints.fixed(20.dp, 30.dp),
modifier = LayoutHeight.Fill,
children = emptyContent()
)
}
}, @Composable {
Row(arrangement = Arrangement.SpaceBetween) {
ConstrainedBox(
DpConstraints.fixed(20.dp, 30.dp),
LayoutFlexible(3f),
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(30.dp, 40.dp),
LayoutFlexible(2f),
children = emptyContent()
)
Container(LayoutAspectRatio(2f) + LayoutFlexible(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(20.dp, 30.dp), children = emptyContent())
}
}, @Composable {
Row(arrangement = Arrangement.SpaceEvenly) {
ConstrainedBox(
DpConstraints.fixed(20.dp, 30.dp),
LayoutFlexible(3f),
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(30.dp, 40.dp),
LayoutFlexible(2f),
children = emptyContent()
)
Container(LayoutAspectRatio(2f) + LayoutFlexible(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(20.dp, 30.dp), children = emptyContent())
}
}) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
// Min width.
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
minIntrinsicWidth(0.ipx)
)
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
minIntrinsicWidth(10.dp.toIntPx())
)
assertEquals(
25.dp.toIntPx() * 2 / 2 * 7 + 20.dp.toIntPx(),
minIntrinsicWidth(25.dp.toIntPx())
)
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
minIntrinsicWidth(IntPx.Infinity)
)
// Min height.
assertEquals(40.dp.toIntPx(), minIntrinsicHeight(0.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), minIntrinsicHeight(125.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), minIntrinsicHeight(370.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), minIntrinsicHeight(IntPx.Infinity))
// Max width.
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
maxIntrinsicWidth(0.ipx)
)
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
maxIntrinsicWidth(10.dp.toIntPx())
)
assertEquals(
25.dp.toIntPx() * 2 / 2 * 7 + 20.dp.toIntPx(),
maxIntrinsicWidth(25.dp.toIntPx())
)
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
maxIntrinsicWidth(IntPx.Infinity)
)
// Max height.
assertEquals(40.dp.toIntPx(), maxIntrinsicHeight(0.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), maxIntrinsicHeight(125.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), maxIntrinsicHeight(370.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), maxIntrinsicHeight(IntPx.Infinity))
}
}
@Test
fun testColumn_withInflexibleChildren_hasCorrectIntrinsicMeasurements() = with(density) {
testIntrinsics(@Composable {
Column {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Column {
Container(LayoutAspectRatio(2f) + LayoutGravity.Start, children = emptyContent())
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutGravity.End,
children = emptyContent()
)
}
}, @Composable {
Column {
Container(
LayoutAspectRatio(2f) + LayoutGravity.RelativeToSiblings { 0.ipx },
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutGravity.RelativeToSiblings(TestVerticalLine),
children = emptyContent()
)
}
}, @Composable {
Column(LayoutHeight.Fill) {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Column(LayoutHeight.Fill, arrangement = Arrangement.Top) {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Column(LayoutHeight.Fill, arrangement = Arrangement.Center) {
Container(LayoutGravity.Center + LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Column(LayoutHeight.Fill, arrangement = Arrangement.Bottom) {
Container(LayoutGravity.End + LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutGravity.End,
children = emptyContent()
)
}
}, @Composable {
Column(LayoutHeight.Fill, arrangement = Arrangement.SpaceAround) {
Container(LayoutWidth.Fill + LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(
DpConstraints.fixed(50.dp, 40.dp),
LayoutWidth.Fill,
children = emptyContent()
)
}
}, @Composable {
Column(LayoutHeight.Fill, arrangement = Arrangement.SpaceBetween) {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}, @Composable {
Column(LayoutHeight.Fill, arrangement = Arrangement.SpaceEvenly) {
Container(LayoutAspectRatio(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(50.dp, 40.dp), children = emptyContent())
}
}) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
// Min width.
assertEquals(50.dp.toIntPx(), minIntrinsicWidth(0.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), minIntrinsicWidth(25.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), minIntrinsicWidth(IntPx.Infinity))
// Min height.
assertEquals(40.dp.toIntPx(), minIntrinsicHeight(0.dp.toIntPx()))
assertEquals(50.dp.toIntPx() / 2 + 40.dp.toIntPx(), minIntrinsicHeight(50.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), minIntrinsicHeight(IntPx.Infinity))
// Max width.
assertEquals(50.dp.toIntPx(), maxIntrinsicWidth(0.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), maxIntrinsicWidth(25.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), maxIntrinsicWidth(IntPx.Infinity))
// Max height.
assertEquals(40.dp.toIntPx(), maxIntrinsicHeight(0.dp.toIntPx()))
assertEquals(50.dp.toIntPx() / 2 + 40.dp.toIntPx(), maxIntrinsicHeight(50.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), maxIntrinsicHeight(IntPx.Infinity))
}
}
@Test
fun testColumn_withFlexibleChildren_hasCorrectIntrinsicMeasurements() = with(density) {
testIntrinsics(@Composable {
Column {
ConstrainedBox(
DpConstraints.fixed(30.dp, 20.dp),
LayoutFlexible(3f),
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(40.dp, 30.dp),
LayoutFlexible(2f),
children = emptyContent()
)
Container(LayoutAspectRatio(0.5f) + LayoutFlexible(2f), children = emptyContent())
ConstrainedBox(DpConstraints.fixed(30.dp, 20.dp), children = emptyContent())
}
}, @Composable {
Column {
ConstrainedBox(
DpConstraints.fixed(30.dp, 20.dp),
LayoutFlexible(3f) + LayoutGravity.Start,
children = emptyContent()
)
ConstrainedBox(
DpConstraints.fixed(40.dp, 30.dp),
LayoutFlexible(2f) + LayoutGravity.Center,
children = emptyContent()
)
Container(LayoutAspectRatio(0.5f) + LayoutFlexible(2f)) { }
ConstrainedBox(DpConstraints.fixed(30.dp, 20.dp), LayoutGravity.End) { }
}
}, @Composable {
Column(arrangement = Arrangement.Top) {
ConstrainedBox(DpConstraints.fixed(30.dp, 20.dp), LayoutFlexible(3f)) { }
ConstrainedBox(DpConstraints.fixed(40.dp, 30.dp), LayoutFlexible(2f)) { }
Container(LayoutAspectRatio(0.5f) + LayoutFlexible(2f)) { }
ConstrainedBox(DpConstraints.fixed(30.dp, 20.dp)) { }
}
}, @Composable {
Column(arrangement = Arrangement.Center) {
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 20.dp),
modifier = LayoutFlexible(3f) + LayoutGravity.Center
) { }
ConstrainedBox(
constraints = DpConstraints.fixed(40.dp, 30.dp),
modifier = LayoutFlexible(2f) + LayoutGravity.Center
) { }
Container(
LayoutAspectRatio(0.5f) + LayoutFlexible(2f) + LayoutGravity.Center
) { }
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 20.dp),
modifier = LayoutGravity.Center
) { }
}
}, @Composable {
Column(arrangement = Arrangement.Bottom) {
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 20.dp),
modifier = LayoutFlexible(3f) + LayoutGravity.End
) { }
ConstrainedBox(
constraints = DpConstraints.fixed(40.dp, 30.dp),
modifier = LayoutFlexible(2f) + LayoutGravity.End
) { }
Container(
LayoutAspectRatio(0.5f) + LayoutFlexible(2f) + LayoutGravity.End
) { }
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 20.dp),
modifier = LayoutGravity.End
) { }
}
}, @Composable {
Column(arrangement = Arrangement.SpaceAround) {
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 20.dp),
modifier = LayoutFlexible(3f) + LayoutWidth.Fill
) { }
ConstrainedBox(
constraints = DpConstraints.fixed(40.dp, 30.dp),
modifier = LayoutFlexible(2f) + LayoutWidth.Fill
) { }
Container(
LayoutAspectRatio(0.5f) + LayoutFlexible(2f) + LayoutWidth.Fill
) { }
ConstrainedBox(
constraints = DpConstraints.fixed(30.dp, 20.dp),
modifier = LayoutWidth.Fill
) { }
}
}, @Composable {
Column(arrangement = Arrangement.SpaceBetween) {
ConstrainedBox(DpConstraints.fixed(30.dp, 20.dp), LayoutFlexible(3f)) { }
ConstrainedBox(DpConstraints.fixed(40.dp, 30.dp), LayoutFlexible(2f)) { }
Container(LayoutAspectRatio(0.5f) + LayoutFlexible(2f)) { }
ConstrainedBox(DpConstraints.fixed(30.dp, 20.dp)) { }
}
}, @Composable {
Column(arrangement = Arrangement.SpaceEvenly) {
ConstrainedBox(DpConstraints.fixed(30.dp, 20.dp), LayoutFlexible(3f)) { }
ConstrainedBox(DpConstraints.fixed(40.dp, 30.dp), LayoutFlexible(2f)) { }
Container(LayoutAspectRatio(0.5f) + LayoutFlexible(2f)) { }
ConstrainedBox(DpConstraints.fixed(30.dp, 20.dp)) { }
}
}) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
// Min width.
assertEquals(40.dp.toIntPx(), minIntrinsicWidth(0.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), minIntrinsicWidth(125.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), minIntrinsicWidth(370.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), minIntrinsicWidth(IntPx.Infinity))
// Min height.
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
minIntrinsicHeight(0.ipx)
)
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
minIntrinsicHeight(10.dp.toIntPx())
)
assertEquals(
25.dp.toIntPx() * 2 / 2 * 7 + 20.dp.toIntPx(),
minIntrinsicHeight(25.dp.toIntPx())
)
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
minIntrinsicHeight(IntPx.Infinity)
)
// Max width.
assertEquals(40.dp.toIntPx(), maxIntrinsicWidth(0.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), maxIntrinsicWidth(125.dp.toIntPx()))
assertEquals(50.dp.toIntPx(), maxIntrinsicWidth(370.dp.toIntPx()))
assertEquals(40.dp.toIntPx(), maxIntrinsicWidth(IntPx.Infinity))
// Max height.
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
maxIntrinsicHeight(0.ipx)
)
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
maxIntrinsicHeight(10.dp.toIntPx())
)
assertEquals(
25.dp.toIntPx() * 2 / 2 * 7 + 20.dp.toIntPx(),
maxIntrinsicHeight(25.dp.toIntPx())
)
assertEquals(
30.dp.toIntPx() / 2 * 7 + 20.dp.toIntPx(),
maxIntrinsicHeight(IntPx.Infinity)
)
}
}
// endregion
// region Modifiers specific tests
@Test
fun testFlexModifiersChain_leftMostWins() = with(density) {
val positionedLatch = CountDownLatch(1)
val containerHeight = Ref<IntPx>()
val columnHeight = 24.ipx
show {
Align(Alignment.TopStart) {
Column(LayoutHeight(columnHeight.toDp())) {
OnChildPositioned(onPositioned = { coordinates ->
containerHeight.value = coordinates.size.height
positionedLatch.countDown()
}) {
Container(
LayoutFlexible(2f) + LayoutFlexible(1f),
children = emptyContent()
)
}
Container(LayoutFlexible(1f), children = emptyContent())
}
}
}
positionedLatch.await(1, TimeUnit.SECONDS)
assertNotNull(containerHeight.value)
assertEquals(columnHeight * 2 / 3, containerHeight.value)
}
@Test
fun testRelativeToSiblingsModifiersChain_leftMostWins() = with(density) {
val positionedLatch = CountDownLatch(1)
val containerSize = Ref<IntPxSize>()
val containerPosition = Ref<PxPosition>()
val size = 40.dp
show {
Row {
Container(
modifier = LayoutGravity.RelativeToSiblings { it.height },
width = size,
height = size,
children = emptyContent()
)
OnChildPositioned(onPositioned = { coordinates ->
containerSize.value = coordinates.size
containerPosition.value = coordinates.globalPosition
positionedLatch.countDown()
}) {
Container(
modifier = LayoutGravity.RelativeToSiblings { 0.ipx } +
LayoutGravity.RelativeToSiblings { it.height * 0.5 },
width = size,
height = size,
children = emptyContent()
)
}
}
}
positionedLatch.await(1, TimeUnit.SECONDS)
assertNotNull(containerSize)
assertEquals(PxPosition(size.toPx(), size.toPx()), containerPosition.value)
}
// endregion
// region Rtl tests
@Test
fun testRow_Rtl_arrangementStart() = with(density) {
val sizeDp = 35.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(2)
val childPosition = arrayOf(PxPosition.Origin, PxPosition.Origin)
show {
Row(LayoutWidth.Fill + LayoutDirectionModifier.Rtl) {
Container(LayoutSize(sizeDp, sizeDp)) {
OnPositioned(onPositioned = { coordinates ->
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(LayoutSize(sizeDp * 2, sizeDp * 2)) {
OnPositioned(onPositioned = { coordinates ->
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
val root = findAndroidComposeView()
waitForDraw(root)
val rootWidth = root.width.px
assertEquals(PxPosition(rootWidth - size.toPx(), 0.px), childPosition[0])
assertEquals(
PxPosition(rootWidth - (sizeDp.toPx() * 3).round().toPx(), 0.px),
childPosition[1]
)
}
@Test
fun testRow_Rtl_arrangementEnd() = with(density) {
val sizeDp = 35.dp
val drawLatch = CountDownLatch(2)
val childPosition = arrayOf(PxPosition.Origin, PxPosition.Origin)
show {
Row(
LayoutWidth.Fill + LayoutDirectionModifier.Rtl,
arrangement = Arrangement.End
) {
Container(LayoutSize(sizeDp, sizeDp)) {
OnPositioned(onPositioned = { coordinates ->
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(LayoutSize(sizeDp * 2, sizeDp * 2)) {
OnPositioned(onPositioned = { coordinates ->
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
assertEquals(PxPosition((sizeDp.toPx() * 2).round().toPx(), 0.px), childPosition[0])
assertEquals(PxPosition(0.px, 0.px), childPosition[1])
}
@Test
fun testRow_Rtl_customArrangement() = with(density) {
val sizeDp = 35.dp
val drawLatch = CountDownLatch(2)
val childPosition = arrayOf(PxPosition.Origin, PxPosition.Origin)
show {
Row(
LayoutWidth.Fill + LayoutDirectionModifier.Rtl,
arrangement = customHorizontalArrangement
) {
Container(width = sizeDp, height = sizeDp) {
OnPositioned(onPositioned = { coordinates ->
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(width = (sizeDp * 2), height = (sizeDp * 2)) {
OnPositioned(onPositioned = { coordinates ->
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
val root = findAndroidComposeView()
waitForDraw(root)
assertEquals(PxPosition((sizeDp.toPx() * 2).round().toPx(), 0.px), childPosition[0])
assertEquals(PxPosition(0.px, 0.px), childPosition[1])
}
@Test
fun testColumn_Rtl_gravityStart() = with(density) {
val sizeDp = 35.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(2)
val childPosition = arrayOf(PxPosition.Origin, PxPosition.Origin)
show {
Column(LayoutWidth.Fill + LayoutDirectionModifier.Rtl) {
Container(LayoutSize(sizeDp, sizeDp)) {
OnPositioned(onPositioned = { coordinates ->
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(LayoutSize(sizeDp * 2, sizeDp * 2)) {
OnPositioned(onPositioned = { coordinates ->
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
val root = findAndroidComposeView()
waitForDraw(root)
val rootWidth = root.width.px
assertEquals(PxPosition(rootWidth - size.toPx(), 0.px), childPosition[0])
assertEquals(
PxPosition((rootWidth - (sizeDp * 2).toPx()).round().toPx(), size.toPx()),
childPosition[1]
)
}
@Test
fun testColumn_Rtl_gravityEnd() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(2)
val childPosition = arrayOf(PxPosition.Origin, PxPosition.Origin)
show {
Column(LayoutWidth.Fill + LayoutDirectionModifier.Rtl) {
Container(LayoutSize(sizeDp, sizeDp) + LayoutGravity.End) {
OnPositioned(onPositioned = { coordinates ->
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(LayoutSize(sizeDp * 2, sizeDp * 2) + LayoutGravity.End) {
OnPositioned(onPositioned = { coordinates ->
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
assertEquals(PxPosition(0.px, 0.px), childPosition[0])
assertEquals(PxPosition(0.px, size.toPx()), childPosition[1])
}
@Test
fun testColumn_Rtl_gravityRelativeToSiblings() = with(density) {
val sizeDp = 50.dp
val size = sizeDp.toIntPx()
val drawLatch = CountDownLatch(2)
val childPosition = arrayOf(PxPosition.Origin, PxPosition.Origin)
show {
Column(LayoutWidth.Fill + LayoutDirectionModifier.Rtl) {
Container(LayoutSize(sizeDp, sizeDp) +
LayoutGravity.RelativeToSiblings { it.width }
) {
OnPositioned(onPositioned = { coordinates ->
childPosition[0] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
Container(LayoutSize(sizeDp, sizeDp) +
LayoutGravity.RelativeToSiblings { it.width / 2 }
) {
OnPositioned(onPositioned = { coordinates ->
childPosition[1] = coordinates.localToGlobal(PxPosition(0.px, 0.px))
drawLatch.countDown()
})
}
}
}
assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
val root = findAndroidComposeView()
waitForDraw(root)
val rootWidth = root.width.px
assertEquals(PxPosition(rootWidth - size.toPx(), 0.px), childPosition[0])
assertEquals(
PxPosition((rootWidth - size.toPx() * 1.5f).round().toPx(), size.toPx()),
childPosition[1]
)
}
// endregion
}
private val TestHorizontalLine = HorizontalAlignmentLine(::min)
private val TestVerticalLine = VerticalAlignmentLine(::min)
@Composable
private fun BaselineTestLayout(
width: Dp,
height: Dp,
baseline: Dp,
modifier: Modifier,
children: @Composable() () -> Unit
) {
Layout(children = children, modifier = modifier, measureBlock = { _, constraints, _ ->
val widthPx = max(width.toIntPx(), constraints.minWidth)
val heightPx = max(height.toIntPx(), constraints.minHeight)
layout(
widthPx, heightPx,
mapOf(TestHorizontalLine to baseline.toIntPx(), TestVerticalLine to baseline.toIntPx())
) {}
})
}