[go: nahoru, domu]

blob: 63d0da731d8862c9c9c640b36815310f00912607 [file] [log] [blame]
/*
* Copyright 2020 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.compose.foundation.layout
import android.os.Build
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.platform.InspectableValue
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.ValueElement
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assert
import org.junit.Assume
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.math.roundToInt
@MediumTest
@RunWith(AndroidJUnit4::class)
class OffsetTest : LayoutTest() {
@get:Rule
val rule = createComposeRule()
@Before
fun before() {
// b/151728444
Assume.assumeFalse(
Build.MODEL.contains("Nexus 5") && Build.VERSION.SDK_INT == Build.VERSION_CODES.M
)
isDebugInspectorInfoEnabled = true
}
@After
fun after() {
isDebugInspectorInfoEnabled = false
}
@Test
fun offset_positionIsModified() = with(density) {
val offsetX = 10.dp
val offsetY = 20.dp
var positionX = 0
var positionY = 0
rule.setContent {
Box(
Modifier.testTag("box")
.wrapContentSize(Alignment.TopStart)
.offset(offsetX, offsetY)
.onGloballyPositioned { coordinates: LayoutCoordinates ->
positionX = coordinates.positionInRoot().x.roundToInt()
positionY = coordinates.positionInRoot().y.roundToInt()
}
) {
}
}
rule.onNodeWithTag("box").assertExists()
rule.runOnIdle {
assertEquals(offsetX.roundToPx(), positionX)
assertEquals(offsetY.roundToPx(), positionY)
}
}
@Test
fun offset_positionIsModified_rtl() = with(density) {
val containerWidth = 30.dp
val boxSize = 1
val offsetX = 10.dp
val offsetY = 20.dp
var positionX = 0
var positionY = 0
rule.setContent {
CompositionLocalProvider((LocalLayoutDirection provides LayoutDirection.Rtl)) {
Box(
Modifier.testTag("box")
.wrapContentSize(Alignment.TopEnd)
.preferredWidth(containerWidth)
.wrapContentSize(Alignment.TopStart)
.offset(offsetX, offsetY)
.onGloballyPositioned { coordinates: LayoutCoordinates ->
positionX = coordinates.positionInRoot().x.roundToInt()
positionY = coordinates.positionInRoot().y.roundToInt()
}
) {
// TODO(soboleva): this box should not be needed after b/154758475 is fixed.
Box(Modifier.size(boxSize.toDp()))
}
}
}
rule.onNodeWithTag("box").assertExists()
rule.runOnIdle {
assertEquals(containerWidth.roundToPx() - offsetX.roundToPx() - boxSize, positionX)
assertEquals(offsetY.roundToPx(), positionY)
}
}
@Test
fun absoluteOffset_positionModified() = with(density) {
val offsetX = 10.dp
val offsetY = 20.dp
var positionX = 0
var positionY = 0
rule.setContent {
Box(
Modifier.testTag("box")
.wrapContentSize(Alignment.TopStart)
.absoluteOffset(offsetX, offsetY)
.onGloballyPositioned { coordinates: LayoutCoordinates ->
positionX = coordinates.positionInRoot().x.roundToInt()
positionY = coordinates.positionInRoot().y.roundToInt()
}
) {
}
}
rule.onNodeWithTag("box").assertExists()
rule.runOnIdle {
assertEquals(offsetX.roundToPx(), positionX)
assertEquals(offsetY.roundToPx(), positionY)
}
}
@Test
fun absoluteOffset_positionModified_rtl() = with(density) {
val containerWidth = 30.dp
val boxSize = 1
val offsetX = 10.dp
val offsetY = 20.dp
var positionX = 0
var positionY = 0
rule.setContent {
CompositionLocalProvider((LocalLayoutDirection provides LayoutDirection.Rtl)) {
Box(
Modifier.testTag("box")
.wrapContentSize(Alignment.TopEnd)
.preferredWidth(containerWidth)
.wrapContentSize(Alignment.TopStart)
.absoluteOffset(offsetX, offsetY)
.onGloballyPositioned { coordinates: LayoutCoordinates ->
positionX = coordinates.positionInRoot().x.roundToInt()
positionY = coordinates.positionInRoot().y.roundToInt()
}
) {
// TODO(soboleva): this box should not be needed after b/154758475 is fixed.
Box(Modifier.size(boxSize.toDp()))
}
}
}
rule.onNodeWithTag("box").assertExists()
rule.runOnIdle {
assertEquals(containerWidth.roundToPx() - boxSize + offsetX.roundToPx(), positionX)
assertEquals(offsetY.roundToPx(), positionY)
}
}
@Test
fun offsetPx_positionIsModified() = with(density) {
val offsetX = 10f
val offsetY = 20f
var positionX = 0f
var positionY = 0f
rule.setContent {
Box(
Modifier.testTag("box")
.wrapContentSize(Alignment.TopStart)
.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
.onGloballyPositioned { coordinates: LayoutCoordinates ->
positionX = coordinates.positionInRoot().x
positionY = coordinates.positionInRoot().y
}
) {
}
}
rule.onNodeWithTag("box").assertExists()
rule.runOnIdle {
Assert.assertEquals(offsetX, positionX)
Assert.assertEquals(offsetY, positionY)
}
}
@Test
fun offsetPx_positionIsModified_rtl() = with(density) {
val containerWidth = 30.dp
val boxSize = 1
val offsetX = 10
val offsetY = 20
var positionX = 0
var positionY = 0
rule.setContent {
CompositionLocalProvider((LocalLayoutDirection provides LayoutDirection.Rtl)) {
Box(
Modifier.testTag("box")
.wrapContentSize(Alignment.TopEnd)
.preferredWidth(containerWidth)
.wrapContentSize(Alignment.TopStart)
.offset { IntOffset(offsetX, offsetY) }
.onGloballyPositioned { coordinates: LayoutCoordinates ->
positionX = coordinates.positionInRoot().x.roundToInt()
positionY = coordinates.positionInRoot().y.roundToInt()
}
) {
// TODO(soboleva): this box should not be needed after b/154758475 is fixed.
Box(Modifier.size(boxSize.toDp()))
}
}
}
rule.onNodeWithTag("box").assertExists()
rule.runOnIdle {
Assert.assertEquals(
containerWidth.roundToPx() - offsetX - boxSize,
positionX
)
Assert.assertEquals(offsetY, positionY)
}
}
@Test
fun absoluteOffsetPx_positionIsModified() = with(density) {
val offsetX = 10
val offsetY = 20
var positionX = 0
var positionY = 0
rule.setContent {
Box(
Modifier.testTag("box")
.wrapContentSize(Alignment.TopStart)
.absoluteOffset { IntOffset(offsetX, offsetY) }
.onGloballyPositioned { coordinates: LayoutCoordinates ->
positionX = coordinates.positionInRoot().x.roundToInt()
positionY = coordinates.positionInRoot().y.roundToInt()
}
) {
}
}
rule.onNodeWithTag("box").assertExists()
rule.runOnIdle {
Assert.assertEquals(offsetX, positionX)
Assert.assertEquals(offsetY, positionY)
}
}
@Test
fun absoluteOffsetPx_positionIsModified_rtl() = with(density) {
val containerWidth = 30.dp
val boxSize = 1
val offsetX = 10
val offsetY = 20
var positionX = 0
var positionY = 0
rule.setContent {
CompositionLocalProvider((LocalLayoutDirection provides LayoutDirection.Rtl)) {
Box(
Modifier.testTag("box")
.wrapContentSize(Alignment.TopEnd)
.preferredWidth(containerWidth)
.wrapContentSize(Alignment.TopStart)
.absoluteOffset { IntOffset(offsetX, offsetY) }
.onGloballyPositioned { coordinates: LayoutCoordinates ->
positionX = coordinates.positionInRoot().x.roundToInt()
positionY = coordinates.positionInRoot().y.roundToInt()
}
) {
// TODO(soboleva): this box should not be needed after b/154758475 is fixed.
Box(Modifier.size(boxSize.toDp()))
}
}
}
rule.onNodeWithTag("box").assertExists()
rule.runOnIdle {
Assert.assertEquals(
containerWidth.roundToPx() - boxSize + offsetX,
positionX
)
Assert.assertEquals(offsetY, positionY)
}
}
@Test
fun testOffsetInspectableValue() {
val modifier = Modifier.offset(3.0.dp, 4.5.dp) as InspectableValue
assertThat(modifier.nameFallback).isEqualTo("offset")
assertThat(modifier.valueOverride).isNull()
assertThat(modifier.inspectableElements.asIterable()).containsExactly(
ValueElement("x", 3.0.dp),
ValueElement("y", 4.5.dp)
)
}
@Test
fun testAbsoluteOffsetInspectableValue() {
val modifier = Modifier.absoluteOffset(3.0.dp, 1.5.dp) as InspectableValue
assertThat(modifier.nameFallback).isEqualTo("absoluteOffset")
assertThat(modifier.valueOverride).isNull()
assertThat(modifier.inspectableElements.asIterable()).containsExactly(
ValueElement("x", 3.0.dp),
ValueElement("y", 1.5.dp)
)
}
@Test
fun testOffsetPxInspectableValue() {
val modifier = Modifier.offset { IntOffset(10, 20) } as InspectableValue
assertThat(modifier.nameFallback).isEqualTo("offset")
assertThat(modifier.valueOverride).isNull()
assertThat(modifier.inspectableElements.map { it.name }.asIterable())
.containsExactly("offset")
}
@Test
fun testAbsoluteOffsetPxInspectableValue() {
val modifier = Modifier.absoluteOffset { IntOffset(10, 20) } as InspectableValue
assertThat(modifier.nameFallback).isEqualTo("absoluteOffset")
assertThat(modifier.valueOverride).isNull()
assertThat(modifier.inspectableElements.map { it.name }.asIterable())
.containsExactly("offset")
}
@Test
fun contentNotRedrawnWhenOffsetPxChanges() {
var contentRedrawsCount = 0
var offset by mutableStateOf(0f)
rule.setContent {
Box(
Modifier
.size(10.dp)
.offset { IntOffset(offset.roundToInt(), 0) }
.drawBehind {
contentRedrawsCount ++
}
)
}
rule.runOnIdle {
offset = 5f
}
rule.runOnIdle {
assertEquals(1, contentRedrawsCount)
}
}
}