Fix clue rendering on light mode
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after 5m35s
Some checks failed
SonarQube Scan / SonarQube Trigger (push) Failing after 5m35s
This commit is contained in:
@@ -1,18 +1,6 @@
|
|||||||
package ch.dissem.yaep.ui.common
|
package ch.dissem.yaep.ui.common
|
||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
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.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.OutlinedCard
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -22,46 +10,16 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
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.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import androidx.compose.ui.unit.TextUnitType
|
import androidx.compose.ui.unit.TextUnitType
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import ch.dissem.yaep.domain.Clue
|
|
||||||
import ch.dissem.yaep.domain.Game
|
import ch.dissem.yaep.domain.Game
|
||||||
import ch.dissem.yaep.domain.GameCell
|
|
||||||
import ch.dissem.yaep.domain.GameRow
|
|
||||||
import ch.dissem.yaep.domain.Grid
|
|
||||||
import ch.dissem.yaep.domain.HorizontalClue
|
|
||||||
import ch.dissem.yaep.domain.Item
|
|
||||||
import ch.dissem.yaep.domain.ItemClass
|
|
||||||
import ch.dissem.yaep.domain.NeighbourClue
|
|
||||||
import ch.dissem.yaep.domain.OrderClue
|
|
||||||
import ch.dissem.yaep.domain.SameColumnClue
|
|
||||||
import ch.dissem.yaep.domain.TripletClue
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.jetbrains.compose.resources.painterResource
|
|
||||||
import yaep.commonui.generated.resources.Res
|
|
||||||
import yaep.commonui.generated.resources.neighbour
|
|
||||||
import yaep.commonui.generated.resources.order
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
|
|
||||||
class DisplayClue<C : Clue>(val clue: C) {
|
|
||||||
var isActive: Boolean by mutableStateOf(true)
|
|
||||||
|
|
||||||
var isViolated: Boolean by mutableStateOf(false)
|
|
||||||
|
|
||||||
fun update(grid: Grid) {
|
|
||||||
isViolated = !clue.isValid(grid)
|
|
||||||
if (isViolated) {
|
|
||||||
isActive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalTime::class)
|
@OptIn(ExperimentalTime::class)
|
||||||
@@ -140,199 +98,4 @@ fun App(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun PuzzleGrid(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
selectDirectly: Boolean,
|
|
||||||
spacing: Dp = 8.dp,
|
|
||||||
grid: Grid,
|
|
||||||
onUpdate: () -> Unit
|
|
||||||
) {
|
|
||||||
Column(modifier = modifier) {
|
|
||||||
for (row in grid) {
|
|
||||||
PuzzleRow(
|
|
||||||
row = row,
|
|
||||||
onUpdate = onUpdate,
|
|
||||||
onSnapshot = { grid.snapshot() },
|
|
||||||
onUndo = { grid.undo() },
|
|
||||||
spacing = spacing,
|
|
||||||
selectDirectly = selectDirectly
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun PuzzleRow(
|
|
||||||
row: GameRow<ItemClass<*>>,
|
|
||||||
onUpdate: () -> Unit,
|
|
||||||
onSnapshot: () -> Unit,
|
|
||||||
onUndo: () -> Boolean,
|
|
||||||
spacing: Dp,
|
|
||||||
selectDirectly: Boolean
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.wrapContentHeight()
|
|
||||||
) {
|
|
||||||
val allOptions = row.options
|
|
||||||
for (cell in row) {
|
|
||||||
var selection by remember(cell) { mutableStateOf(cell.selection) }
|
|
||||||
val options = remember(cell) {
|
|
||||||
allOptions.map { Toggleable(it, cell.options.contains(it)) }
|
|
||||||
}
|
|
||||||
LaunchedEffect(cell) {
|
|
||||||
cell.optionsChangedListeners.add { enabled ->
|
|
||||||
options.forEach { it.enabled = enabled.contains(it.item) }
|
|
||||||
}
|
|
||||||
cell.selectionChangedListeners.add {
|
|
||||||
selection = it
|
|
||||||
onUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Selector(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(spacing)
|
|
||||||
.weight(1f),
|
|
||||||
spacing,
|
|
||||||
selectDirectly = selectDirectly,
|
|
||||||
options = options,
|
|
||||||
onOptionRemoved = {
|
|
||||||
onSnapshot()
|
|
||||||
cell.options.remove(it)
|
|
||||||
row.cleanupOptions()
|
|
||||||
},
|
|
||||||
onOptionAdded = {
|
|
||||||
cell.options.add(it)
|
|
||||||
},
|
|
||||||
selectedItem = selection,
|
|
||||||
onSelectItem = { selectedItem ->
|
|
||||||
onSelectItem(row, cell, options, selectedItem, onSnapshot, onUndo)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onSelectItem(
|
|
||||||
row: GameRow<ItemClass<*>>,
|
|
||||||
cell: GameCell<ItemClass<*>>,
|
|
||||||
options: List<Toggleable<Item<ItemClass<*>>>>,
|
|
||||||
selectedItem: Item<ItemClass<*>>?,
|
|
||||||
onSnapshot: () -> Unit,
|
|
||||||
onUndo: () -> Boolean
|
|
||||||
) {
|
|
||||||
if (selectedItem != null) {
|
|
||||||
onSnapshot()
|
|
||||||
cell.selection = selectedItem
|
|
||||||
row.cleanupOptions()
|
|
||||||
} else {
|
|
||||||
while (cell.selection != null) {
|
|
||||||
if (!onUndo()) break
|
|
||||||
}
|
|
||||||
options.forEach { option ->
|
|
||||||
option.enabled = cell.options.contains(option.item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Modifier.forClue(clue: DisplayClue<out Clue>, padding: Dp = 8.dp): Modifier = this
|
|
||||||
.alpha(if (clue.isActive) 1f else 0.2f)
|
|
||||||
.padding(padding)
|
|
||||||
.onEitherPointerAction { clue.isActive = !clue.isActive }
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
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), spacing = spacing, item = clue.a)
|
|
||||||
Image(
|
|
||||||
modifier = Modifier.aspectRatio(1f).weight(1f),
|
|
||||||
painter = painterResource(Res.drawable.neighbour),
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.b)
|
|
||||||
}
|
|
||||||
|
|
||||||
is OrderClue<*, *> -> {
|
|
||||||
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), spacing = spacing, item = clue.right)
|
|
||||||
}
|
|
||||||
|
|
||||||
is TripletClue<*, *, *> -> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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), spacing = spacing, item = clue.a)
|
|
||||||
DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect fun CoroutineScope.logGame(game: Game, dispatcher: CoroutineContext = Dispatchers.IO)
|
expect fun CoroutineScope.logGame(game: Game, dispatcher: CoroutineContext = Dispatchers.IO)
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ClueCard(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
spacing: Dp,
|
|
||||||
isClueViolated: Boolean,
|
|
||||||
content: @Composable () -> Unit
|
|
||||||
) {
|
|
||||||
val colors = MaterialTheme.colorScheme
|
|
||||||
OutlinedCard(
|
|
||||||
modifier = if (isClueViolated) {
|
|
||||||
modifier.shadow(
|
|
||||||
8.dp,
|
|
||||||
shape = CardDefaults.outlinedShape,
|
|
||||||
ambientColor = colors.error,
|
|
||||||
spotColor = colors.error
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
modifier
|
|
||||||
},
|
|
||||||
shape = RoundedCornerShape(spacing),
|
|
||||||
border = if (isClueViolated) {
|
|
||||||
remember { BorderStroke(1.0.dp, colors.error) }
|
|
||||||
} else {
|
|
||||||
CardDefaults.outlinedCardBorder()
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
144
commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/clues.kt
Normal file
144
commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/clues.kt
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package ch.dissem.yaep.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
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.NeighbourClue
|
||||||
|
import ch.dissem.yaep.domain.OrderClue
|
||||||
|
import ch.dissem.yaep.domain.SameColumnClue
|
||||||
|
import ch.dissem.yaep.domain.TripletClue
|
||||||
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
import yaep.commonui.generated.resources.Res
|
||||||
|
import yaep.commonui.generated.resources.neighbour
|
||||||
|
import yaep.commonui.generated.resources.order
|
||||||
|
|
||||||
|
fun Modifier.forClue(clue: DisplayClue<out Clue>, padding: Dp = 8.dp): Modifier = this
|
||||||
|
.alpha(if (clue.isActive) 1f else 0.2f)
|
||||||
|
.padding(padding)
|
||||||
|
.onEitherPointerAction { clue.isActive = !clue.isActive }
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
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), spacing = spacing, item = clue.a)
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.aspectRatio(1f).weight(1f),
|
||||||
|
painter = painterResource(Res.drawable.neighbour),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
is OrderClue<*, *> -> {
|
||||||
|
DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.left)
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.aspectRatio(1f).weight(1f),
|
||||||
|
painter = painterResource(Res.drawable.order),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
is TripletClue<*, *, *> -> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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), spacing = spacing, item = clue.a)
|
||||||
|
DrawItem(modifier = Modifier.weight(1f), spacing = spacing, item = clue.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ClueCard(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
spacing: Dp,
|
||||||
|
isClueViolated: Boolean,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val colors = MaterialTheme.colorScheme
|
||||||
|
OutlinedCard(
|
||||||
|
modifier = if (isClueViolated) {
|
||||||
|
modifier.shadow(
|
||||||
|
8.dp,
|
||||||
|
shape = CardDefaults.outlinedShape,
|
||||||
|
ambientColor = colors.error,
|
||||||
|
spotColor = colors.error
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
modifier
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(spacing),
|
||||||
|
border = if (isClueViolated) {
|
||||||
|
remember { BorderStroke(1.0.dp, colors.error) }
|
||||||
|
} else {
|
||||||
|
CardDefaults.outlinedCardBorder()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DisplayClue<C : Clue>(val clue: C) {
|
||||||
|
var isActive: Boolean by mutableStateOf(true)
|
||||||
|
|
||||||
|
var isViolated: Boolean by mutableStateOf(false)
|
||||||
|
|
||||||
|
fun update(grid: Grid) {
|
||||||
|
isViolated = !clue.isValid(grid)
|
||||||
|
if (isViolated) {
|
||||||
|
isActive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/grid.kt
Normal file
118
commonUI/src/commonMain/kotlin/ch/dissem/yaep/ui/common/grid.kt
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package ch.dissem.yaep.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import ch.dissem.yaep.domain.GameCell
|
||||||
|
import ch.dissem.yaep.domain.GameRow
|
||||||
|
import ch.dissem.yaep.domain.Grid
|
||||||
|
import ch.dissem.yaep.domain.Item
|
||||||
|
import ch.dissem.yaep.domain.ItemClass
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PuzzleGrid(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
selectDirectly: Boolean,
|
||||||
|
spacing: Dp = 8.dp,
|
||||||
|
grid: Grid,
|
||||||
|
onUpdate: () -> Unit
|
||||||
|
) {
|
||||||
|
Column(modifier = modifier) {
|
||||||
|
for (row in grid) {
|
||||||
|
PuzzleRow(
|
||||||
|
row = row,
|
||||||
|
onUpdate = onUpdate,
|
||||||
|
onSnapshot = { grid.snapshot() },
|
||||||
|
onUndo = { grid.undo() },
|
||||||
|
spacing = spacing,
|
||||||
|
selectDirectly = selectDirectly
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PuzzleRow(
|
||||||
|
row: GameRow<ItemClass<*>>,
|
||||||
|
onUpdate: () -> Unit,
|
||||||
|
onSnapshot: () -> Unit,
|
||||||
|
onUndo: () -> Boolean,
|
||||||
|
spacing: Dp,
|
||||||
|
selectDirectly: Boolean
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
) {
|
||||||
|
val allOptions = row.options
|
||||||
|
for (cell in row) {
|
||||||
|
var selection by remember(cell) { mutableStateOf(cell.selection) }
|
||||||
|
val options = remember(cell) {
|
||||||
|
allOptions.map { Toggleable(it, cell.options.contains(it)) }
|
||||||
|
}
|
||||||
|
LaunchedEffect(cell) {
|
||||||
|
cell.optionsChangedListeners.add { enabled ->
|
||||||
|
options.forEach { it.enabled = enabled.contains(it.item) }
|
||||||
|
}
|
||||||
|
cell.selectionChangedListeners.add {
|
||||||
|
selection = it
|
||||||
|
onUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Selector(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(spacing)
|
||||||
|
.weight(1f),
|
||||||
|
spacing,
|
||||||
|
selectDirectly = selectDirectly,
|
||||||
|
options = options,
|
||||||
|
onOptionRemoved = {
|
||||||
|
onSnapshot()
|
||||||
|
cell.options.remove(it)
|
||||||
|
row.cleanupOptions()
|
||||||
|
},
|
||||||
|
onOptionAdded = {
|
||||||
|
cell.options.add(it)
|
||||||
|
},
|
||||||
|
selectedItem = selection,
|
||||||
|
onSelectItem = { selectedItem ->
|
||||||
|
onSelectItem(row, cell, options, selectedItem, onSnapshot, onUndo)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onSelectItem(
|
||||||
|
row: GameRow<ItemClass<*>>,
|
||||||
|
cell: GameCell<ItemClass<*>>,
|
||||||
|
options: List<Toggleable<Item<ItemClass<*>>>>,
|
||||||
|
selectedItem: Item<ItemClass<*>>?,
|
||||||
|
onSnapshot: () -> Unit,
|
||||||
|
onUndo: () -> Boolean
|
||||||
|
) {
|
||||||
|
if (selectedItem != null) {
|
||||||
|
onSnapshot()
|
||||||
|
cell.selection = selectedItem
|
||||||
|
row.cleanupOptions()
|
||||||
|
} else {
|
||||||
|
while (cell.selection != null) {
|
||||||
|
if (!onUndo()) break
|
||||||
|
}
|
||||||
|
options.forEach { option ->
|
||||||
|
option.enabled = cell.options.contains(option.item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user