Add overlapRatio parameter

This commit is contained in:
Benoit Marty 2025-01-02 06:43:46 +01:00 committed by Benoit Marty
parent 9f6f812377
commit df108adced
2 changed files with 64 additions and 15 deletions

View file

@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.components.avatar.Avatar 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.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight 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 @Composable
fun AvatarRow( fun AvatarRow(
avatarDataList: List<AvatarData>, avatarDataList: List<AvatarData>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
overlapRatio: Float = 0.5f,
) { ) {
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
Box( Box(
@ -39,31 +50,39 @@ fun AvatarRow(
) { ) {
val lastItemIndex = avatarDataList.size - 1 val lastItemIndex = avatarDataList.size - 1
val avatarSize = avatarDataList.firstOrNull()?.size?.dp ?: return val avatarSize = avatarDataList.firstOrNull()?.size?.dp ?: return
val avatarSizePx = avatarSize.toPx()
avatarDataList avatarDataList
.reversed() .reversed()
.forEachIndexed { index, avatarData -> .forEachIndexed { index, avatarData ->
Avatar( Avatar(
modifier = Modifier modifier = Modifier
.padding(start = avatarSize / 2 * (lastItemIndex - index)) .padding(start = avatarSize * (1 - overlapRatio) * (lastItemIndex - index))
.graphicsLayer { .graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen compositingStrategy = CompositingStrategy.Offscreen
} }
.drawWithContent { .drawWithContent {
// Draw content and clear the pixels for the avatar on the left (right in RTL). // Draw content and clear the pixels for the avatar on the left (right in RTL).
drawContent() drawContent()
val xOffset = if (isRtl) {
size.width - avatarSizePx * (overlapRatio - 0.5f)
} else {
0f + avatarSizePx * (overlapRatio - 0.5f)
}
if (index < lastItemIndex) { if (index < lastItemIndex) {
drawCircle( drawCircle(
color = Color.Black, color = Color.Black,
center = Offset( center = Offset(
x = if (isRtl) size.width else 0f, x = xOffset,
y = size.height / 2, y = size.height / 2,
), ),
radius = avatarSize.toPx() / 2, radius = avatarSizePx / 2,
blendMode = BlendMode.Clear, blendMode = BlendMode.Clear,
) )
} }
} }
.size(size = avatarSize) .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), .padding(2.dp),
avatarData = avatarData, avatarData = avatarData,
) )
@ -73,7 +92,26 @@ fun AvatarRow(
@Composable @Composable
@PreviewsDayNight @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( AvatarRow(
avatarDataList = listOf( avatarDataList = listOf(
"A", "B", "C" "A", "B", "C"
@ -83,16 +121,7 @@ internal fun AvatarRowPreview() = ElementPreview {
name = it, name = it,
size = AvatarSize.RoomListItem, size = AvatarSize.RoomListItem,
) )
} },
overlapRatio = overlapRatio,
) )
} }
@Composable
@PreviewsDayNight
internal fun AvatarRowRtlPreview() {
CompositionLocalProvider(
LocalLayoutDirection provides LayoutDirection.Rtl,
) {
AvatarRowPreview()
}
}

View file

@ -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<Float> {
override val values: Sequence<Float> = sequenceOf(
0f,
0.25f,
0.5f,
0.75f,
1f
)
}