Adaptive Layout (WIP)

This commit is contained in:
Christian Basler
2025-07-09 21:19:41 +02:00
parent 3dbc994dc5
commit 7f88095a4b
6 changed files with 75 additions and 80 deletions

View File

@@ -9,8 +9,6 @@ import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
@@ -33,7 +31,6 @@ import ch.dissem.yaep.domain.Clue
import ch.dissem.yaep.domain.Game
import ch.dissem.yaep.domain.Grid
import ch.dissem.yaep.domain.HorizontalClue
import ch.dissem.yaep.domain.ItemClass
import ch.dissem.yaep.domain.NeighbourClue
import ch.dissem.yaep.domain.OrderClue
import ch.dissem.yaep.domain.SameColumnClue
@@ -216,48 +213,6 @@ fun PuzzleGrid(
}
}
@Composable
fun HorizontalClues(
modifier: Modifier = Modifier,
clues: List<DisplayClue<HorizontalClue>>
) {
LazyVerticalGrid(
modifier = modifier,
columns = GridCells.Fixed(4)
) {
for (clue in clues) {
item {
HorizontalClue(
modifier = Modifier.forClue(clue),
clue = clue.clue,
isClueViolated = clue.isViolated
)
}
}
}
}
@Composable
fun VerticalClues(
modifier: Modifier = Modifier,
clues: List<DisplayClue<SameColumnClue<ItemClass<*>, ItemClass<*>>>>
) {
LazyVerticalGrid(
modifier = modifier,
columns = GridCells.Fixed(8)
) {
for (clue in clues) {
item {
VerticalClue(
modifier = Modifier.forClue(clue),
clue = clue.clue,
isClueViolated = clue.isViolated
)
}
}
}
}
private fun Modifier.forClue(clue: DisplayClue<out Clue>): Modifier = this
.alpha(if (clue.isActive) 1f else 0.2f)
.padding(8.dp)

View File

@@ -1,5 +1,6 @@
package ch.dissem.yaep.ui.common
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
@@ -7,6 +8,7 @@ import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Constraints.Companion.fixed
import androidx.compose.ui.unit.Constraints.Companion.fixedWidth
import androidx.compose.ui.unit.dp
import ch.dissem.yaep.ui.common.AspectRatio.LANDSCAPE
import ch.dissem.yaep.ui.common.AspectRatio.PORTRAIT
@@ -41,10 +43,11 @@ fun AdaptiveGameLayout(
grid: @Composable () -> Unit,
horizontalClues: @Composable () -> Unit,
verticalClues: @Composable () -> Unit,
time: @Composable () -> Unit
time: @Composable () -> Unit,
divider: @Composable () -> Unit = { HorizontalDivider() }
) {
Layout(
contents = listOf(grid, horizontalClues, verticalClues, time),
contents = listOf(grid, horizontalClues, verticalClues, time, divider),
modifier = modifier
) { measurables, constraints ->
layout(width = constraints.maxWidth, height = constraints.maxHeight) {
@@ -54,6 +57,7 @@ fun AdaptiveGameLayout(
val horizontalCluesMeasurables = measurables[1]
val verticalCluesMeasurables = measurables[2]
val timeMeasurable = measurables[3][0]
val dividerMeasurable = measurables[4][0]
val spacingPx = spacing.roundToPx()
@@ -65,7 +69,8 @@ fun AdaptiveGameLayout(
gridMeasurable,
horizontalCluesMeasurables,
verticalCluesMeasurables,
timeMeasurable
timeMeasurable,
dividerMeasurable
)
}
@@ -87,7 +92,8 @@ fun AdaptiveGameLayout(
gridMeasurable,
horizontalCluesMeasurables,
verticalCluesMeasurables,
timeMeasurable
timeMeasurable,
dividerMeasurable
)
}
}
@@ -101,7 +107,8 @@ private fun Placeable.PlacementScope.portrait(
gridMeasurable: Measurable,
horizontalCluesMeasurables: List<Measurable>,
verticalCluesMeasurables: List<Measurable>,
timeMeasurable: Measurable
timeMeasurable: Measurable,
dividerMeasurable: Measurable
) {
val gridSize = constraints.maxWidth
val bottomBarHeight = constraints.maxHeight - gridSize - spacingPx
@@ -112,6 +119,7 @@ private fun Placeable.PlacementScope.portrait(
val verticalCluesConstraints =
fixed(gridSize - timeWidth - spacingPx, bottomBarHeight / 2)
val timeConstraints = fixed(timeWidth, bottomBarHeight / 2)
val dividerConstraints = fixedWidth(gridSize)
val gridPlaceable = gridMeasurable.measure(gridConstraints)
val horizontalCluesPlaceables = horizontalCluesMeasurables.map {
@@ -121,15 +129,29 @@ private fun Placeable.PlacementScope.portrait(
it.measure(verticalCluesConstraints)
}
val timePlaceable = timeMeasurable.measure(timeConstraints)
val dividerPlaceable = dividerMeasurable.measure(dividerConstraints)
// Position the grid
gridPlaceable.place(0, 0)
// Position the horizontal clues
// TODO horizontalCluesPlaceable.place(0, gridPlaceable.height + spacingPx)
val offsetY = placeClues(
placeables = horizontalCluesPlaceables,
offsetX = 0,
offsetY = gridSize + 2 * spacingPx,
maxWidth = gridSize
)
// Add divider in between
dividerPlaceable.place(gridSize + 3 * spacingPx, offsetY + spacingPx)
// Position the vertical clues
// TODO verticalCluesPlaceable.place(0, gridPlaceable.height + horizontalCluesPlaceable.height + 2 * spacingPx)
placeClues(
placeables = verticalCluesPlaceables,
offsetX = 0,
offsetY = offsetY + 2 * spacingPx + dividerPlaceable.height,
maxWidth = gridSize
)
// Position the time
timePlaceable.place(
@@ -146,14 +168,20 @@ private fun Placeable.PlacementScope.squarish(
verticalCluesMeasurables: List<Measurable>,
timeMeasurable: Measurable
) {
val gridSize = (8 * min(constraints.maxWidth, constraints.maxHeight)) / 10
val gridSize = (7 * min(constraints.maxWidth, constraints.maxHeight)) / 10
val gridItemSize = (gridSize - 12 * spacingPx) / 18
val rightBarWidth = constraints.maxWidth - gridSize - spacingPx
val bottomBarHeight = constraints.maxHeight - gridSize - spacingPx
val gridConstraints = fixed(gridSize, gridSize)
val horizontalCluesConstraints = fixed(rightBarWidth, gridSize)
val verticalCluesConstraints = fixed(gridSize, bottomBarHeight)
val timeConstraints = fixed(rightBarWidth, bottomBarHeight)
val horizontalCluesConstraints = fixed(
width = 3 * gridItemSize + 2 * spacingPx,
height = gridItemSize + 2 * spacingPx
)
val verticalCluesConstraints = fixed(
width = gridItemSize + 2 * spacingPx,
height = 3 * gridItemSize + 2 * spacingPx
)
val timeConstraints = Constraints()
val gridPlaceable = gridMeasurable.measure(gridConstraints)
val horizontalCluesPlaceables = horizontalCluesMeasurables.map {
@@ -168,10 +196,20 @@ private fun Placeable.PlacementScope.squarish(
gridPlaceable.place(0, 0)
// Position the horizontal clues
// TODO horizontalCluesPlaceable.place(gridPlaceable.width + spacingPx, 0)
placeClues(
placeables = horizontalCluesPlaceables,
offsetX = gridSize + 2 * spacingPx,
offsetY = 0,
maxWidth = rightBarWidth
)
// Position the vertical clues
// TODO verticalCluesPlaceable.place(0, gridPlaceable.height + spacingPx)
placeClues(
placeables = verticalCluesPlaceables,
offsetX = 0,
offsetY = gridSize + 2 * spacingPx,
maxWidth = gridSize
)
// Position the time
timePlaceable.place(
@@ -186,11 +224,12 @@ private fun Placeable.PlacementScope.landscape(
gridMeasurable: Measurable,
horizontalCluesMeasurables: List<Measurable>,
verticalCluesMeasurables: List<Measurable>,
timeMeasurable: Measurable
timeMeasurable: Measurable,
dividerMeasurable: Measurable
) {
val gridSize = constraints.maxHeight
val gridItemSize = (gridSize - 12 * spacingPx) / 18
val rightBarWidth = constraints.maxWidth - gridSize - spacingPx
val rightBarWidth = constraints.maxWidth - gridSize - 2 * spacingPx
val gridConstraints = fixed(gridSize, gridSize)
val baseSpace = gridSize - 2 * spacingPx
@@ -203,6 +242,7 @@ private fun Placeable.PlacementScope.landscape(
height = 3 * gridItemSize + 2 * spacingPx
)
val timeConstraints = Constraints.fixedHeight(baseSpace / 10)
val dividerConstraints = fixedWidth(rightBarWidth - 2 * spacingPx)
val gridPlaceable = gridMeasurable.measure(gridConstraints)
val horizontalCluesPlaceables = horizontalCluesMeasurables.map {
@@ -212,6 +252,7 @@ private fun Placeable.PlacementScope.landscape(
it.measure(verticalCluesConstraints)
}
val timePlaceable = timeMeasurable.measure(timeConstraints)
val dividerPlaceable = dividerMeasurable.measure(dividerConstraints)
// Position the grid
gridPlaceable.place(0, 0)
@@ -219,18 +260,19 @@ private fun Placeable.PlacementScope.landscape(
// Position the horizontal clues
val offsetY = placeClues(
placeables = horizontalCluesPlaceables,
offsetX = gridPlaceable.width + spacingPx,
offsetX = gridSize + 2 * spacingPx,
offsetY = 0,
maxWidth = rightBarWidth
)
// TODO: add spacer in between
// Add divider in between
dividerPlaceable.place(gridSize + 3 * spacingPx, offsetY + spacingPx)
// Position the vertical clues
placeClues(
placeables = verticalCluesPlaceables,
offsetX = gridPlaceable.width + spacingPx,
offsetY = offsetY + spacingPx,
offsetX = gridSize + 2 * spacingPx,
offsetY = offsetY + 2 * spacingPx + dividerPlaceable.height,
maxWidth = rightBarWidth
)
@@ -253,7 +295,7 @@ private fun Placeable.PlacementScope.placeClues(
val itemWidth = placeables.first().width
val itemHeight = placeables.first().height
val columns = max(1, maxWidth / itemWidth)
val spacing = max(minSpacing, (maxWidth - columns * itemWidth) / (columns - 1))
val spacing = if (columns == 1) 0 else (maxWidth - columns * itemWidth) / (columns - 1)
var currentX = offsetX
var currentY = offsetY
var i = 0
@@ -261,7 +303,7 @@ private fun Placeable.PlacementScope.placeClues(
placeable.place(currentX, currentY)
currentX += itemWidth + spacing
i++
if (i % columns == 0) {
if (i % columns == 0 && i < placeables.size) {
currentX = offsetX
currentY += itemHeight
}