Browse Source

Selected users: if scrollable, make last one peek

If there are enough selected users that they can't all be displayed,
add extra padding in between the users to ensure that the last
visible one is half visible to provide some scroll affordance.
feature/fga/small_timeline_improvements
Chris Smith 1 year ago
parent
commit
a5849ca0bc
  1. 89
      libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt

89
libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt

@ -16,18 +16,26 @@ @@ -16,18 +16,26 @@
package io.element.android.libraries.matrix.ui.components
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@ -35,6 +43,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight @@ -35,6 +43,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlin.math.floor
@Composable
fun SelectedUsersList(
@ -56,16 +65,60 @@ fun SelectedUsersList( @@ -56,16 +65,60 @@ fun SelectedUsersList(
}
}
val rowWidth by remember {
derivedStateOf {
lazyListState.layoutInfo.viewportSize.width - lazyListState.layoutInfo.beforeContentPadding
}
}
LazyRow(
state = lazyListState,
modifier = modifier,
modifier = modifier
.fillMaxWidth(),
contentPadding = contentPadding,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
items(selectedUsers.toList()) { matrixUser ->
SelectedUser(
matrixUser = matrixUser,
onUserRemoved = onUserRemoved,
itemsIndexed(selectedUsers.toList()) { index, matrixUser ->
Layout(
content = {
SelectedUser(
matrixUser = matrixUser,
onUserRemoved = onUserRemoved,
)
},
measurePolicy = { measurables, constraints ->
// Measures out extra space between each user to ensure that the last visible user is exactly half visible, giving an affordance that
// the user can scroll to see more.
val placeable = measurables.first().measure(constraints)
val isLastItem = index == selectedUsers.lastIndex
val width = if (isLastItem) {
// Don't add any padding on the final item
placeable.width
} else {
val minimumSpacing = 24.dp.toPx()
val userWidth = placeable.width + minimumSpacing
val maxVisibleUsers = rowWidth / userWidth
if (maxVisibleUsers >= selectedUsers.size) {
// If we can fit all the users in, don't do anything fancy
(placeable.width + minimumSpacing).toInt()
} else {
// Round down the number of visible users to end with a state where one is half visible
val targetFraction = (placeable.width / 2) / userWidth
val targetUsers = floor(maxVisibleUsers - targetFraction) + targetFraction
// Work out how much extra spacing we need to reduce the number of users that much, then split it evenly amongst the visible users
val extraSpacing = (maxVisibleUsers - targetUsers) * userWidth
val extraSpacingPerUser = (extraSpacing) / floor(targetUsers)
(placeable.width + minimumSpacing + extraSpacingPerUser).toInt()
}
}
layout(width, placeable.height) {
placeable.place(0, 0)
}
}
)
}
}
@ -81,7 +134,23 @@ internal fun SelectedUsersListDarkPreview() = ElementPreviewDark { ContentToPrev @@ -81,7 +134,23 @@ internal fun SelectedUsersListDarkPreview() = ElementPreviewDark { ContentToPrev
@Composable
private fun ContentToPreview() {
SelectedUsersList(
selectedUsers = aMatrixUserList().take(6).toImmutableList(),
)
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
// Two users that will be visible with no scrolling
SelectedUsersList(
selectedUsers = aMatrixUserList().take(2).toImmutableList(),
modifier = Modifier
.width(200.dp)
.border(1.dp, Color.Red)
)
// Multiple users that don't fit, so will be spaced out per the measure policy
for (i in 0..5) {
SelectedUsersList(
selectedUsers = aMatrixUserList().take(6).toImmutableList(),
modifier = Modifier
.width((200 + (i * 20)).dp)
.border(1.dp, Color.Red)
)
}
}
}

Loading…
Cancel
Save