richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/TableLayout.kt (64 lines of code) (raw):

package com.halilibo.richtext.ui import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.SubcomposeLayout import androidx.compose.ui.unit.Constraints import kotlin.math.roundToInt /** * The offsets of rows and columns of a [TableLayout], centered inside their spacing. * * E.g. If a table is given a cell spacing of 2px, then the first column and row offset will each * be 1px. */ @Immutable internal data class TableLayoutResult( val rowOffsets: List<Float>, val columnOffsets: List<Float> ) @Composable internal fun TableLayout( columns: Int, rows: List<List<@Composable () -> Unit>>, drawDecorations: (TableLayoutResult) -> Modifier, cellSpacing: Float, tableMeasurer: TableMeasurer, modifier: Modifier = Modifier ) { SubcomposeLayout(modifier = modifier) { constraints -> // Subcompose all cells in one pass. val measurables = subcompose("cells") { rows.forEach { row -> check(row.size == columns) row.forEach { cell -> cell() } } } val rowMeasurables = measurables.chunked(columns) val measurements = tableMeasurer.measure(rowMeasurables, constraints) val tableWidth = measurements.columnWidths.sum() + (cellSpacing * (columns + 1)).roundToInt() val tableHeight = measurements.rowHeights.sum() + (cellSpacing * (measurements.rowHeights.size + 1)).roundToInt() layout(tableWidth, tableHeight) { var y = cellSpacing val rowOffsets = mutableListOf<Float>() val columnOffsets = mutableListOf<Float>() measurements.rowPlaceables.forEachIndexed { rowIndex, cellPlaceables -> rowOffsets += y - cellSpacing / 2f var x = cellSpacing cellPlaceables.forEachIndexed { columnIndex, cell -> if (rowIndex == 0) { columnOffsets.add(x - cellSpacing / 2f) } cell.place(x.roundToInt(), y.roundToInt()) x += measurements.columnWidths[columnIndex] + cellSpacing } if (rowIndex == 0) { // Add the right-most edge. columnOffsets.add(x - cellSpacing / 2f) } y += measurements.rowHeights[rowIndex] + cellSpacing } rowOffsets.add(y - cellSpacing / 2f) // Compose and draw the borders. val layoutResult = TableLayoutResult(rowOffsets, columnOffsets) subcompose(true) { Box(modifier = drawDecorations(layoutResult)) }.single() .measure(Constraints.fixed(tableWidth, tableHeight)) .placeRelative(0, 0) } } }