diff --git a/integration/compose/src/androidTest/assets/glideImage_withDrawBehind_drawsImageOnTopOfBackground.png b/integration/compose/src/androidTest/assets/glideImage_withDrawBehind_drawsImageOnTopOfBackground.png new file mode 100644 index 0000000000..e6da9a36a8 Binary files /dev/null and b/integration/compose/src/androidTest/assets/glideImage_withDrawBehind_drawsImageOnTopOfBackground.png differ diff --git a/integration/compose/src/androidTest/assets/glideImage_withPadding_appliesPaddingOnce.png b/integration/compose/src/androidTest/assets/glideImage_withPadding_appliesPaddingOnce.png new file mode 100644 index 0000000000..2fbf3bc474 Binary files /dev/null and b/integration/compose/src/androidTest/assets/glideImage_withPadding_appliesPaddingOnce.png differ diff --git a/integration/compose/src/androidTest/assets/glideSubcomposition_withPadding_appliesPaddingOnce.png b/integration/compose/src/androidTest/assets/glideSubcomposition_withPadding_appliesPaddingOnce.png new file mode 100644 index 0000000000..be29ef15e3 Binary files /dev/null and b/integration/compose/src/androidTest/assets/glideSubcomposition_withPadding_appliesPaddingOnce.png differ diff --git a/integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideImageTest.kt b/integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideImageTest.kt index cf448afe53..8d5dc40e43 100644 --- a/integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideImageTest.kt +++ b/integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideImageTest.kt @@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentSize @@ -16,6 +18,8 @@ import androidx.compose.material.TextButton import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag @@ -42,6 +46,7 @@ import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target import com.bumptech.glide.test.compareToGolden +import com.bumptech.glide.test.pxToDp import com.google.common.truth.Truth.assertThat import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicReference @@ -53,11 +58,12 @@ import org.junit.rules.TestName class GlideImageTest { private val context: Context = ApplicationProvider.getApplicationContext() - @get:Rule - val glideComposeRule = GlideComposeRule() - @get:Rule + @get:Rule(order = 1) val testName = TestName() + @get:Rule(order = 2) + val glideComposeRule = GlideComposeRule() + @Test fun glideImage_noModifierSize_resourceDrawable_displaysDrawable() { val description = "test" @@ -387,7 +393,7 @@ class GlideImageTest { @Test fun glideImage_withZeroSize_doesNotCrash() { glideComposeRule.setContent { - GlideImage( + GlideImage( model = android.R.drawable.star_big_on, contentDescription = null, modifier = Modifier.width(IntrinsicSize.Min), @@ -419,4 +425,47 @@ class GlideImageTest { .captureToImage() .compareToGolden(testName.methodName) } + + @Test + fun glideImage_withDrawBehind_drawsImageOnTopOfBackground() { + glideComposeRule.setContent { + GlideImage( + android.R.drawable.star_big_on, + "test", + Modifier + .size(100.pxToDp()) + .drawBehind { drawRect(Color.Red) }) { + it.override(100) + } + } + + glideComposeRule + .onNodeWithContentDescription("test") + .captureToImage() + .compareToGolden(testName.methodName) + } + + // See #5272 + @Test + fun glideImage_withPadding_appliesPaddingOnce() { + glideComposeRule.setContent { + GlideImage( + model = android.R.drawable.star_big_on, + contentDescription = "test", + modifier = Modifier + .size(400.pxToDp()) + .aspectRatio(1f) + .drawBehind { + drawRect(Color.Blue) + } + .padding(80.pxToDp()), + ) + } + glideComposeRule.waitForIdle() + + glideComposeRule + .onNodeWithContentDescription("test") + .captureToImage() + .compareToGolden(testName.methodName) + } } diff --git a/integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideSubcompositionTest.kt b/integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideSubcompositionTest.kt index 9084194c6c..f4f51821c8 100644 --- a/integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideSubcompositionTest.kt +++ b/integration/compose/src/androidTest/java/com/bumptech/glide/integration/compose/GlideSubcompositionTest.kt @@ -3,22 +3,43 @@ package com.bumptech.glide.integration.compose import android.content.Context import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Email +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.geometry.isUnspecified +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.test.captureToImage +import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.unit.dp import androidx.test.core.app.ApplicationProvider import com.bumptech.glide.Glide import com.bumptech.glide.integration.compose.test.GlideComposeRule import com.bumptech.glide.load.DataSource +import com.bumptech.glide.test.compareToGolden +import com.bumptech.glide.test.pxToDp import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test +import org.junit.rules.TestName @OptIn(ExperimentalGlideComposeApi::class) class GlideSubcompositionTest { - val context: Context = ApplicationProvider.getApplicationContext() + private val context: Context = ApplicationProvider.getApplicationContext() - @get:Rule + @get:Rule(order = 1) + val testName = TestName() + @get:Rule(order = 2) val glideComposeRule = GlideComposeRule() @Test @@ -162,5 +183,49 @@ class GlideSubcompositionTest { glideComposeRule.waitForIdle() assertThat(dataSource).isEqualTo(DataSource.MEMORY_CACHE) } + + // See #5272 + @Test + fun glideSubcomposition_withPadding_appliesPaddingOnce() { + glideComposeRule.setContent { + val lastSize = remember { mutableStateOf(Size.Unspecified) } + + GlideSubcomposition( + model = null, + modifier = Modifier + .semantics { + contentDescription = "test" + } + .width(400.pxToDp()) + .aspectRatio(1f) + .drawBehind { + if (lastSize.value.isUnspecified) { + lastSize.value = size + drawRect(Color.Blue) + } else if (lastSize.value != this.size) { + drawRect(Color.Red) + } else { + drawRect(Color.Blue) + } + } + .padding(80.pxToDp()), + ) { + when (state) { + RequestState.Failure -> Image( + imageVector = Icons.Default.Email, + contentDescription = "placeholder", + modifier = Modifier.width(400.pxToDp()) + ) + RequestState.Loading -> Spacer(modifier = Modifier.size(100.pxToDp())) + is RequestState.Success -> Image(painter = painter, contentDescription = null) + } + } + } + glideComposeRule.waitForIdle() + glideComposeRule.onNodeWithContentDescription("test") + .captureToImage() + .compareToGolden(testName.methodName) + } } + diff --git a/integration/compose/src/androidTest/java/com/bumptech/glide/test/goldens.kt b/integration/compose/src/androidTest/java/com/bumptech/glide/test/goldens.kt index 346345c359..8f59e09aec 100644 --- a/integration/compose/src/androidTest/java/com/bumptech/glide/test/goldens.kt +++ b/integration/compose/src/androidTest/java/com/bumptech/glide/test/goldens.kt @@ -4,8 +4,10 @@ import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.os.Environment +import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.toPixelMap +import androidx.compose.ui.platform.LocalDensity import androidx.test.core.app.ApplicationProvider import java.io.BufferedOutputStream import java.io.File @@ -18,6 +20,9 @@ import java.lang.IllegalStateException const val GENERATED_FILES_DIR = "compose_goldens" const val EXTENSION = "png" +@Composable +fun Int.pxToDp() = with(LocalDensity.current) { toDp() } + fun ImageBitmap.compareToGolden(testName: String) { val bitmap = toBitmap() val existingGolden = readExistingGolden(testName) diff --git a/integration/compose/src/main/java/com/bumptech/glide/integration/compose/GlideModifier.kt b/integration/compose/src/main/java/com/bumptech/glide/integration/compose/GlideModifier.kt index 2e175b17ed..12c5232e17 100644 --- a/integration/compose/src/main/java/com/bumptech/glide/integration/compose/GlideModifier.kt +++ b/integration/compose/src/main/java/com/bumptech/glide/integration/compose/GlideModifier.kt @@ -89,16 +89,14 @@ internal fun Modifier.glideNode( requestListener, draw, transitionFactory, - ) then - clipToBounds() then + ) + .clipToBounds() + .semantics { if (contentDescription != null) { - semantics { - this@semantics.contentDescription = contentDescription - role = Role.Image - } - } else { - Modifier + this@semantics.contentDescription = contentDescription } + role = Role.Image + } } @ExperimentalGlideComposeApi