diff --git a/android/src/main/kotlin/ch/dissem/android/MainActivity.kt b/android/src/main/kotlin/ch/dissem/android/MainActivity.kt index 14c24bb..5418057 100644 --- a/android/src/main/kotlin/ch/dissem/android/MainActivity.kt +++ b/android/src/main/kotlin/ch/dissem/android/MainActivity.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import ch.dissem.yaep.domain.Game import ch.dissem.yaep.domain.generateGame import ch.dissem.yaep.ui.common.App @@ -63,6 +64,8 @@ class MainActivity : ComponentActivity() { ) { insets -> App( modifier = Modifier.padding(insets), + spacing = 4.dp, + selectDirectly = false, game = game, onNewGame = { game = generateGame() }, resetCluesBeacon = resetCluesBeacon @@ -75,11 +78,13 @@ class MainActivity : ComponentActivity() { @Preview @Composable fun AppAndroidPreview() { - var game by remember { mutableStateOf(generateGame()) } + var game by remember { mutableStateOf(generateGame()) } var resetCluesBeacon by remember { mutableStateOf(Any()) } App( game = game, + spacing = 4.dp, + selectDirectly = false, onNewGame = { game = generateGame() }, resetCluesBeacon = resetCluesBeacon ) diff --git a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/App.kt b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/App.kt index cd97f92..8a741f8 100644 --- a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/App.kt +++ b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/App.kt @@ -9,6 +9,7 @@ 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.shape.RoundedCornerShape import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard @@ -24,6 +25,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.shadow import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.dp @@ -59,6 +61,8 @@ class DisplayClue(val clue: C) { @OptIn(ExperimentalTime::class) fun App( modifier: Modifier = Modifier, + selectDirectly: Boolean, + spacing: Dp, game: Game, onNewGame: () -> Unit, resetCluesBeacon: Any = Any() @@ -89,6 +93,8 @@ fun App( grid = { PuzzleGrid( grid = game.grid, + spacing = spacing, + selectDirectly = selectDirectly, onUpdate = { horizontalClues.forEach { it.update(game.grid) } verticalClues.forEach { it.update(game.grid) } @@ -98,7 +104,8 @@ fun App( horizontalClues = { for (clue in horizontalClues) { HorizontalClue( - modifier = Modifier.forClue(clue), + modifier = Modifier.forClue(clue, spacing), + spacing = spacing, clue = clue.clue, isClueViolated = clue.isViolated ) @@ -107,7 +114,8 @@ fun App( verticalClues = { for (clue in verticalClues) { VerticalClue( - modifier = Modifier.forClue(clue), + modifier = Modifier.forClue(clue, spacing), + spacing = spacing, clue = clue.clue, isClueViolated = clue.isViolated ) @@ -119,7 +127,8 @@ fun App( fontSize = TextUnit(4f, TextUnitType.Companion.Em), textAlign = TextAlign.End ) - } + }, + spacing = spacing ) EndOfGame(isSolved = isSolved, time = time, onRestart = onNewGame) } @@ -128,6 +137,8 @@ fun App( @Composable fun PuzzleGrid( modifier: Modifier = Modifier, + selectDirectly: Boolean, + spacing: Dp = 8.dp, grid: Grid, onUpdate: () -> Unit ) { @@ -155,8 +166,10 @@ fun PuzzleGrid( } Selector( modifier = Modifier - .padding(8.dp) + .padding(spacing) .weight(1f), + spacing, + selectDirectly = selectDirectly, options = options, onOptionRemoved = { grid.snapshot() @@ -188,43 +201,49 @@ fun PuzzleGrid( } } -private fun Modifier.forClue(clue: DisplayClue): Modifier = this +private fun Modifier.forClue(clue: DisplayClue, padding: Dp = 8.dp): Modifier = this .alpha(if (clue.isActive) 1f else 0.2f) - .padding(8.dp) + .padding(padding) .onEitherPointerAction { clue.isActive = !clue.isActive } @Composable -fun HorizontalClue(modifier: Modifier = Modifier, clue: HorizontalClue, isClueViolated: Boolean) { +fun HorizontalClue( + modifier: Modifier = Modifier, + spacing: Dp, + clue: HorizontalClue, + isClueViolated: Boolean +) { ClueCard( modifier = modifier, + spacing = spacing, isClueViolated = isClueViolated ) { Row { when (clue) { is NeighbourClue<*, *> -> { - DrawItem(modifier = Modifier.weight(1f), clue.a) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.a) Image( modifier = Modifier.aspectRatio(1f).weight(1f), painter = painterResource(Res.drawable.neighbour), contentDescription = null ) - DrawItem(modifier = Modifier.weight(1f), clue.b) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.b) } is OrderClue<*, *> -> { - DrawItem(modifier = Modifier.weight(1f), clue.left) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.left) Image( modifier = Modifier.aspectRatio(1f).weight(1f), painter = painterResource(Res.drawable.order), contentDescription = null ) - DrawItem(modifier = Modifier.weight(1f), clue.right) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.right) } is TripletClue<*, *, *> -> { - DrawItem(modifier = Modifier.weight(1f), clue.a) - DrawItem(modifier = Modifier.weight(1f), clue.b) - DrawItem(modifier = Modifier.weight(1f), clue.c) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.a) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.b) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.c) } } } @@ -234,16 +253,18 @@ fun HorizontalClue(modifier: Modifier = Modifier, clue: HorizontalClue, isClueVi @Composable fun VerticalClue( modifier: Modifier = Modifier, + spacing: Dp, clue: SameColumnClue<*, *>, isClueViolated: Boolean = false ) { ClueCard( modifier = modifier.aspectRatio(0.5f), + spacing = spacing, isClueViolated = isClueViolated ) { Column { - DrawItem(modifier = Modifier.weight(1f), clue.a) - DrawItem(modifier = Modifier.weight(1f), clue.b) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.a) + DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.b) } } } @@ -253,6 +274,7 @@ expect fun CoroutineScope.logGame(game: Game) @Composable fun ClueCard( modifier: Modifier = Modifier, + spacing: Dp, isClueViolated: Boolean, content: @Composable () -> Unit ) { @@ -268,6 +290,7 @@ fun ClueCard( } else { modifier }, + shape = RoundedCornerShape(spacing), border = if (isClueViolated) { remember { BorderStroke(1.0.dp, colors.error) } } else { diff --git a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/adaptive game layout.kt b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/adaptive game layout.kt index 456c678..f3b219f 100644 --- a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/adaptive game layout.kt +++ b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/adaptive game layout.kt @@ -9,6 +9,7 @@ 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 androidx.compose.ui.unit.dp import ch.dissem.yaep.ui.common.AspectRatio.LANDSCAPE import ch.dissem.yaep.ui.common.AspectRatio.PORTRAIT @@ -17,8 +18,6 @@ import kotlin.math.max import kotlin.math.min -internal val spacing = 8.dp - private enum class AspectRatio { PORTRAIT, LANDSCAPE, SQUARISH; @@ -44,7 +43,8 @@ fun AdaptiveGameLayout( horizontalClues: @Composable () -> Unit, verticalClues: @Composable () -> Unit, time: @Composable () -> Unit, - divider: @Composable () -> Unit = { HorizontalDivider() } + divider: @Composable () -> Unit = { HorizontalDivider() }, + spacing: Dp = 8.dp ) { Layout( contents = listOf(grid, horizontalClues, verticalClues, time, divider, divider), diff --git a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/selector.kt b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/selector.kt index b23967b..01a5c9e 100644 --- a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/selector.kt +++ b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/selector.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.OutlinedCard import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -18,6 +19,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.drawText import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.Dp import ch.dissem.yaep.domain.Item import ch.dissem.yaep.domain.ItemClass import ch.dissem.yaep.ui.common.theme.emojiFontFamily @@ -26,16 +28,26 @@ import kotlin.math.min @Composable fun > Selector( modifier: Modifier = Modifier, + spacing: Dp, + selectDirectly: Boolean, options: List>>, onOptionRemoved: (Item) -> Unit, onOptionAdded: (Item) -> Unit, selectedItem: Item?, onSelectItem: (Item?) -> Unit, ) { + val radius = spacing * 1.5f if (selectedItem != null) { - DrawItem(item = selectedItem, modifier = modifier.clickable { onSelectItem(null) }) + DrawItem( + item = selectedItem, + modifier = modifier.clickable { onSelectItem(null) }, + spacing = radius + ) } else { - OutlinedCard(modifier = modifier.aspectRatio(1f)) { + OutlinedCard( + modifier = modifier.aspectRatio(1f), + shape = RoundedCornerShape(radius) + ) { LazyVerticalGrid( columns = GridCells.Fixed(3), verticalArrangement = Arrangement.Center, @@ -58,7 +70,8 @@ fun > Selector( onOptionAdded(option.item) } } - ) + ), + spacing = radius ) } } @@ -70,9 +83,10 @@ fun > Selector( @Composable fun > DrawItem( modifier: Modifier = Modifier, + spacing: Dp, item: Item ) { - OutlinedCard(modifier = modifier.aspectRatio(1f)) { + OutlinedCard(modifier = modifier.aspectRatio(1f), shape = RoundedCornerShape(spacing)) { val emoji = item.symbol val textMeasurer = rememberTextMeasurer() diff --git a/commonUI/src/jvmMain/kotlin/ch/dissem/yaep/ui/common/App.jvm.kt b/commonUI/src/jvmMain/kotlin/ch/dissem/yaep/ui/common/App.jvm.kt index d93e973..791eb6b 100644 --- a/commonUI/src/jvmMain/kotlin/ch/dissem/yaep/ui/common/App.jvm.kt +++ b/commonUI/src/jvmMain/kotlin/ch/dissem/yaep/ui/common/App.jvm.kt @@ -6,10 +6,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlin.io.path.Path -import kotlin.io.path.createDirectories import kotlin.io.path.createFile import kotlin.io.path.isDirectory -import kotlin.io.path.notExists import kotlin.io.path.writeText import kotlin.time.Clock import kotlin.time.ExperimentalTime @@ -21,17 +19,15 @@ actual fun CoroutineScope.logGame(game: Game) { launch(Dispatchers.IO) { val dirName = """${System.getProperty("user.home")}/.yaep""" val dir = Path(dirName) - if (dir.notExists()) { - dir.createDirectories() - } else if (!dir.isDirectory()) { - log.error { "Yaep data directory already exists and is not a directory: $dir" } - log.debug { "Game: $game" } - } else { + if (dir.isDirectory()) { val fileName = "$dirName/${Clock.System.now()}.yaep" Path(fileName) .createFile() .writeText(game.toString()) log.info { "Saved game to $fileName" } + } else { + log.debug { "Yaep data directory does not exist or is not a directory: $dir" } + log.debug { "Game: $game" } } } } \ No newline at end of file diff --git a/desktop/src/main/kotlin/ch/dissem/yaep/ui/desktop/main.kt b/desktop/src/main/kotlin/ch/dissem/yaep/ui/desktop/main.kt index 2110904..3ea59ae 100644 --- a/desktop/src/main/kotlin/ch/dissem/yaep/ui/desktop/main.kt +++ b/desktop/src/main/kotlin/ch/dissem/yaep/ui/desktop/main.kt @@ -68,6 +68,8 @@ fun main(): Unit = application { ) { App( modifier = Modifier.padding(it), + spacing = 8.dp, + selectDirectly = true, game = game, onNewGame = { game = generateGame() }, resetCluesBeacon = resetCluesBeacon