All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.outsidesource.oskitcompose.layout.WrappableRow.kt Maven / Gradle / Ivy

The newest version!
package com.outsidesource.oskitcompose.layout

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.*
import kotlin.math.max

/**
 * WrappableRow allows for content to wrap to multiple lines with the specified [verticalSpacing]
 */
@Composable
fun WrappableRow(
    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
    verticalAlignment: Alignment.Vertical = Alignment.Top,
    verticalSpacing: Dp = 0.dp,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    val density = LocalDensity.current
    val hArrangement = remember<(Int, IntArray, LayoutDirection, Density, IntArray) -> Unit>(density, horizontalArrangement) {
        { totalSize, size, layoutDirection, density, outPosition ->
            with(horizontalArrangement) { density.arrange(totalSize, size, layoutDirection, outPosition) }
        }
    }

    Layout(
        modifier = modifier,
        content = content
    ) {measurables, constraints ->
        var ongoingWidth = 0
        var ongoingHeight = 0
        val rowWidths = mutableListOf()
        val rowHeights = mutableListOf()
        val placeables: MutableList> = mutableListOf(mutableListOf())

        measurables.forEach { measurable ->
            val placeable = measurable.measure(constraints.copy(minWidth = 0, minHeight = 0))
            val spacing =  horizontalArrangement.spacing.roundToPx()

            if (ongoingWidth + spacing + placeable.width > constraints.maxWidth) {
                rowHeights.add(ongoingHeight)
                rowWidths.add(ongoingWidth)
                placeables.add(mutableListOf())
                ongoingWidth = placeable.width
                ongoingHeight = placeable.height
            } else {
                ongoingWidth += spacing + placeable.width
                ongoingHeight = max(ongoingHeight, placeable.height)
            }

            placeables.last().add(placeable)
        }

        rowHeights.add(ongoingHeight)
        rowWidths.add(ongoingWidth)

        val totalWidth = rowWidths.maxOf { it }
        val totalHeight = rowHeights.sum() + ((rowHeights.size - 1) * verticalSpacing.roundToPx())
        val layoutWidth = max(constraints.minWidth, totalWidth)
        val layoutHeight = max(constraints.minHeight, totalHeight)

        layout(layoutWidth, layoutHeight) {
            var baseY = verticalAlignment.align(totalHeight, layoutHeight)

            placeables.forEachIndexed { rowI, row ->
                val childrenWidths = IntArray(row.size) { i -> row[i].width }
                val mainAxisPositions = IntArray(row.size) { 0 }
                hArrangement(layoutWidth, childrenWidths, LayoutDirection.Ltr, density, mainAxisPositions)

                row.forEachIndexed { i, placeable ->
                    placeable.placeRelative(IntOffset(
                        x = mainAxisPositions[i],
                        y = baseY,
                    ))
                }

                baseY += rowHeights[rowI] + verticalSpacing.roundToPx()
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy