Add overlapRatio parameter
This commit is contained in:
parent
9f6f812377
commit
df108adced
2 changed files with 64 additions and 15 deletions
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue