Skip to content

Instantly share code, notes, and snippets.

@pawelsa
Created November 18, 2020 15:52
Show Gist options
  • Save pawelsa/f1c5cbaa7ce2303091624b6d6f2ff6ee to your computer and use it in GitHub Desktop.
Save pawelsa/f1c5cbaa7ce2303091624b6d6f2ff6ee to your computer and use it in GitHub Desktop.
This is used when items in a row cannot fit and we want to indicate it by using some overflow indicator. In text we often have '…', so this does the same for row @composables
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
fun <T> List<T>.skipLast(skip: Int) = this.take(this.size - skip)
enum class OverflowRowAlignment { Start, End, Center, SpaceBetween, SpaceEvenly }
@Composable
fun OverflowRow(
modifier: Modifier = Modifier,
alignment: OverflowRowAlignment = OverflowRowAlignment.Start,
spacing: Dp = 0.dp,
children: @Composable () -> Unit,
overflow: @Composable () -> Unit,
) {
val childrenWithOverflow: @Composable () -> Unit = {
children()
overflow()
}
Layout(
children = childrenWithOverflow,
modifier = modifier
) { measurables, constraints ->
val maxWidth = constraints.maxWidth
val spacingPx = spacing.toIntPx()
val toDisplay = mutableListOf<Placeable>()
var currentMainAxisSize: Int
val childConstraints = Constraints(maxWidth = maxWidth)
fun List<Placeable>.overflow(): Placeable = this.last()
val placeables = measurables.map { it.measure(childConstraints) }
val widthOfAllPlaceablesButOverflow = placeables
.skipLast(1)
.sumBy { it.width + spacingPx } - spacingPx
if (widthOfAllPlaceablesButOverflow > maxWidth) {
currentMainAxisSize = placeables.overflow().width
for (placeable in placeables.skipLast(1)) {
if (currentMainAxisSize + placeable.width + spacingPx < maxWidth) {
toDisplay.add(placeable)
currentMainAxisSize += placeable.width + spacingPx
}
}
toDisplay.add(placeables.overflow())
} else {
currentMainAxisSize = widthOfAllPlaceablesButOverflow
toDisplay.addAll(placeables.skipLast(1))
}
val mainAxisLayoutSize = currentMainAxisSize
val crossAxisLayoutSize =
toDisplay.maxByOrNull { it.height }?.height ?: constraints.maxHeight
val leftOverSpace = maxWidth - mainAxisLayoutSize
layout(mainAxisLayoutSize, crossAxisLayoutSize) {
var width = when (alignment) {
OverflowRowAlignment.Center -> leftOverSpace / 2
OverflowRowAlignment.End -> leftOverSpace
OverflowRowAlignment.SpaceEvenly -> leftOverSpace / (toDisplay.size + 1)
else -> 0
}
val spacer = when (alignment) {
OverflowRowAlignment.SpaceEvenly -> leftOverSpace / (toDisplay.size + 1)
OverflowRowAlignment.SpaceBetween -> leftOverSpace / (toDisplay.size - 1)
else -> spacingPx
}
toDisplay.forEachIndexed { index, placeable ->
placeable.placeRelative(width, 0)
width += placeable.width + if (index != toDisplay.lastIndex) spacer else 0
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment