diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/AvatarRow.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/AvatarRow.kt index 4fc31b192b..ee8d8000e2 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/AvatarRow.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/AvatarRow.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -27,11 +28,21 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.text.toPx +/** + * Draw a row of avatars (they must all have the same size), from start to end + * @param avatarDataList the avatars to render. Note: they will all be rendered, the caller may + * want to limit the list size + * @param modifier Jetpack Compose modifier + * @param overlapRatio the overlap ration. When 0f, avatars will render without overlap, when 1f + * only the first avatar will be visible + */ @Composable fun AvatarRow( avatarDataList: List, modifier: Modifier = Modifier, + overlapRatio: Float = 0.5f, ) { val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl Box( @@ -39,31 +50,39 @@ fun AvatarRow( ) { val lastItemIndex = avatarDataList.size - 1 val avatarSize = avatarDataList.firstOrNull()?.size?.dp ?: return + val avatarSizePx = avatarSize.toPx() avatarDataList .reversed() .forEachIndexed { index, avatarData -> Avatar( modifier = Modifier - .padding(start = avatarSize / 2 * (lastItemIndex - index)) + .padding(start = avatarSize * (1 - overlapRatio) * (lastItemIndex - index)) .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } .drawWithContent { // Draw content and clear the pixels for the avatar on the left (right in RTL). drawContent() + val xOffset = if (isRtl) { + size.width - avatarSizePx * (overlapRatio - 0.5f) + } else { + 0f + avatarSizePx * (overlapRatio - 0.5f) + } if (index < lastItemIndex) { drawCircle( color = Color.Black, center = Offset( - x = if (isRtl) size.width else 0f, + x = xOffset, y = size.height / 2, ), - radius = avatarSize.toPx() / 2, + radius = avatarSizePx / 2, blendMode = BlendMode.Clear, ) } } .size(size = avatarSize) + // Keep internal padding, it has the advantage to not reduce the size of the Avatar image, + // which is already small in our use case. .padding(2.dp), avatarData = avatarData, ) @@ -73,7 +92,26 @@ fun AvatarRow( @Composable @PreviewsDayNight -internal fun AvatarRowPreview() = ElementPreview { +internal fun AvatarRowPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) { + ElementPreview { + ContentToPreview(overlapRatio) + } +} + +@Composable +@PreviewsDayNight +internal fun AvatarRowRtlPreview(@PreviewParameter(OverlapRatioProvider::class) overlapRatio: Float) { + CompositionLocalProvider( + LocalLayoutDirection provides LayoutDirection.Rtl, + ) { + ElementPreview { + ContentToPreview(overlapRatio) + } + } +} + +@Composable +private fun ContentToPreview(overlapRatio: Float) { AvatarRow( avatarDataList = listOf( "A", "B", "C" @@ -83,16 +121,7 @@ internal fun AvatarRowPreview() = ElementPreview { name = it, size = AvatarSize.RoomListItem, ) - } + }, + overlapRatio = overlapRatio, ) } - -@Composable -@PreviewsDayNight -internal fun AvatarRowRtlPreview() { - CompositionLocalProvider( - LocalLayoutDirection provides LayoutDirection.Rtl, - ) { - AvatarRowPreview() - } -} diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/OverlapRatioProvider.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/OverlapRatioProvider.kt new file mode 100644 index 0000000000..3725f3f251 --- /dev/null +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/OverlapRatioProvider.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.knockrequests.impl.banner + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider + +class OverlapRatioProvider : PreviewParameterProvider { + override val values: Sequence = sequenceOf( + 0f, + 0.25f, + 0.5f, + 0.75f, + 1f + ) +}