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 8cb2d8b..db0f1e9 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 @@ -11,13 +11,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.input.key.Key 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 ch.dissem.yaep.domain.Game import focus -import ch.dissem.yaep.domain.HorizontalClue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlin.coroutines.CoroutineContext @@ -58,9 +58,11 @@ fun App( AdaptiveGameLayout( modifier = Modifier.blurOnFinished(isSolved), grid = { + val focusable = remember { selectionManager.add() } PuzzleGrid( modifier = Modifier - .focus(remember { selectionManager.add() }), + .focus(focusable), + remember { focusable.createChild(Key.DirectionDown, Key.DirectionUp) }, grid = game.grid, spacing = spacing, selectDirectly = selectDirectly, @@ -71,11 +73,11 @@ fun App( ) }, horizontalClues = { - val horizontalClueSelection = remember { selectionManager.add() } + val focusable = remember { selectionManager.add() } for (clue in horizontalClues) { HorizontalClue( modifier = Modifier - .focus(horizontalClueSelection) + .focus(focusable) .forClue(clue, spacing), spacing = spacing, clue = clue.clue, @@ -84,11 +86,11 @@ fun App( } }, verticalClues = { - val verticalClueSelection = remember { selectionManager.add() } + val focusable = remember { selectionManager.add() } for (clue in verticalClues) { VerticalClue( modifier = Modifier - .focus(verticalClueSelection) + .focus(focusable) .forClue(clue, spacing), spacing = spacing, clue = clue.clue, diff --git a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/puzzle clues.kt b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/puzzle clues.kt index e9abc55..b83b0ac 100644 --- a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/puzzle clues.kt +++ b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/puzzle clues.kt @@ -5,13 +5,8 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.runtime.Composable @@ -26,7 +21,6 @@ import androidx.compose.ui.unit.dp import ch.dissem.yaep.domain.Clue 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 @@ -49,56 +43,14 @@ class DisplayClue(val clue: C) { } } -@Composable -fun PuzzleClues( - modifier: Modifier = Modifier.Companion, - horizontalClues: List>, - verticalClues: List, ItemClass<*>>>> -) { - Column(modifier = modifier) { - LazyVerticalGrid( - modifier = Modifier.Companion.fillMaxWidth().wrapContentHeight(), - columns = GridCells.Fixed(4) - ) { - for (clue in horizontalClues) { - item { - HorizontalClue( - modifier = Modifier.Companion - .forClue(clue), - clue = clue.clue, - isClueViolated = clue.isViolated - ) - } - } - } - HorizontalDivider() - LazyVerticalGrid( - modifier = Modifier.Companion.fillMaxWidth().wrapContentHeight(), - columns = GridCells.Fixed(8) - ) { - for (clue in verticalClues) { - item { - VerticalClue( - modifier = Modifier.Companion - .forClue(clue) - .aspectRatio(0.33333334f), - clue = clue.clue, - isClueViolated = clue.isViolated - ) - } - } - } - } -} - -private fun Modifier.forClue(clue: DisplayClue): Modifier = this +internal fun Modifier.forClue(clue: DisplayClue): Modifier = this .alpha(if (clue.isActive) 1f else 0.2f) .padding(8.dp) .onEitherPointerAction { clue.isActive = !clue.isActive } @Composable fun HorizontalClue( - modifier: Modifier = Modifier.Companion, + modifier: Modifier = Modifier, clue: HorizontalClue, isClueViolated: Boolean ) { @@ -109,29 +61,29 @@ fun HorizontalClue( Row { when (clue) { is NeighbourClue<*, *> -> { - DrawItem(modifier = Modifier.Companion.weight(1f), clue.a) + DrawItem(modifier = Modifier.weight(1f), clue.a) Image( - modifier = Modifier.Companion.aspectRatio(1f).weight(1f), + modifier = Modifier.aspectRatio(1f).weight(1f), painter = painterResource(Res.drawable.neighbour), contentDescription = null ) - DrawItem(modifier = Modifier.Companion.weight(1f), clue.b) + DrawItem(modifier = Modifier.weight(1f), clue.b) } is OrderClue<*, *> -> { - DrawItem(modifier = Modifier.Companion.weight(1f), clue.left) + DrawItem(modifier = Modifier.weight(1f), clue.left) Image( - modifier = Modifier.Companion.aspectRatio(1f).weight(1f), + modifier = Modifier.aspectRatio(1f).weight(1f), painter = painterResource(Res.drawable.order), contentDescription = null ) - DrawItem(modifier = Modifier.Companion.weight(1f), clue.right) + DrawItem(modifier = Modifier.weight(1f), clue.right) } is TripletClue<*, *, *> -> { - DrawItem(modifier = Modifier.Companion.weight(1f), clue.a) - DrawItem(modifier = Modifier.Companion.weight(1f), clue.b) - DrawItem(modifier = Modifier.Companion.weight(1f), clue.c) + DrawItem(modifier = Modifier.weight(1f), clue.a) + DrawItem(modifier = Modifier.weight(1f), clue.b) + DrawItem(modifier = Modifier.weight(1f), clue.c) } } } @@ -140,7 +92,7 @@ fun HorizontalClue( @Composable fun VerticalClue( - modifier: Modifier = Modifier.Companion, + modifier: Modifier = Modifier, clue: SameColumnClue<*, *>, isClueViolated: Boolean = false ) { @@ -149,15 +101,15 @@ fun VerticalClue( isClueViolated = isClueViolated ) { Column { - DrawItem(modifier = Modifier.Companion.weight(1f), clue.a) - DrawItem(modifier = Modifier.Companion.weight(1f), clue.b) + DrawItem(modifier = Modifier.weight(1f), clue.a) + DrawItem(modifier = Modifier.weight(1f), clue.b) } } } @Composable fun ClueCard( - modifier: Modifier = Modifier.Companion, + modifier: Modifier = Modifier, isClueViolated: Boolean, content: @Composable () -> Unit ) { diff --git a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/puzzle grid.kt b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/puzzle grid.kt index f93e6b4..6bade7f 100644 --- a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/puzzle grid.kt +++ b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/puzzle grid.kt @@ -1,5 +1,6 @@ package ch.dissem.yaep.ui.common +import SelectionManager import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -12,24 +13,31 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.input.key.Key import androidx.compose.ui.unit.dp import ch.dissem.yaep.domain.Grid +import focus @Composable fun PuzzleGrid( - modifier: Modifier = Modifier.Companion, + modifier: Modifier = Modifier, + selectionManager: SelectionManager, grid: Grid, onUpdate: () -> Unit ) { Column(modifier = modifier) { for (row in grid) { + val focusableRow = remember { selectionManager.add() } Row( - modifier = Modifier.Companion + modifier = Modifier .fillMaxWidth() .wrapContentHeight() ) { val allOptions = row.options + val columnSelectionManager = + remember { focusableRow.createChild(Key.DirectionRight, Key.DirectionLeft) } for (item in row) { + val focusableItem = remember { columnSelectionManager.add() } var selection by remember(item) { mutableStateOf(item.selection) } val options = remember(item) { allOptions.map { Toggleable(it, item.options.contains(it)) } @@ -44,7 +52,8 @@ fun PuzzleGrid( } } Selector( - modifier = Modifier.Companion + modifier = Modifier + .focus(focusableItem) .padding(8.dp) .weight(1f), options = options, diff --git a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/widget status.kt b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/widget status.kt index dd95e11..d448521 100644 --- a/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/widget status.kt +++ b/commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/widget status.kt @@ -32,6 +32,11 @@ class SelectionManager( var focused: Focusable? get() = focusedFlow.value set(value) { + val previous = focusedFlow.value + if (previous != value) { + previous?.child?.isActive = false + value?.child?.isActive = true + } focusedFlow.value = value } @@ -78,7 +83,7 @@ class SelectionManager( } class Focusable( - manager: SelectionManager, + private val manager: SelectionManager, ) { val hasFocus: Flow = combine(manager.isActiveFlow, manager.focusedFlow) { isActive, focused -> @@ -106,7 +111,7 @@ class Focusable( keyPrevious: Key? = null ): SelectionManager { child = SelectionManager(keyNext, keyPrevious) - child!!.isActive = false + child!!.isActive = manager.isActive && manager.focused == this return child!! } } 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 ab34830..d5e355a 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 @@ -1,5 +1,6 @@ package ch.dissem.yaep.ui.desktop +import SelectionManager import androidx.compose.foundation.layout.padding import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf