Flash sequence UX polish + dirty tracking

This commit is contained in:
Tretzi
2026-04-15 10:26:46 -05:00
parent 50272cd6a6
commit 180fd0124e
5 changed files with 45 additions and 13 deletions

View File

@@ -535,8 +535,9 @@ fun SetupScreen(
) {
Text("Save")
}
} else {
Button(onClick = onStart, modifier = Modifier.weight(1f).height(52.dp)) { Text("Start") }
}
Button(onClick = onStart, modifier = Modifier.weight(1f).height(52.dp)) { Text("Start") }
}
if (ui.error != null) Text(ui.error!!, color = if (ui.error!!.startsWith("Saved")) Color(0xFF72E39A) else MaterialTheme.colorScheme.error)
Spacer(Modifier.height(8.dp))

View File

@@ -6,7 +6,9 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
@@ -16,11 +18,13 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@Composable
@@ -30,6 +34,8 @@ fun FlashSequenceEditor(
modifier: Modifier = Modifier,
) {
val rows = sequence.ifEmpty { defaultFlashSequence() }
val splitColWidth = 56.dp
val deleteColWidth = 40.dp
Column(
modifier = modifier
@@ -45,24 +51,40 @@ fun FlashSequenceEditor(
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.72f),
)
// Table header row.
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Box(modifier = Modifier.width(splitColWidth), contentAlignment = Alignment.CenterStart) {
Text("Split", style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurface)
}
Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.CenterStart) {
Text("Left", style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurface)
}
Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.CenterStart) {
Text("Right", style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurface)
}
Box(modifier = Modifier.width(deleteColWidth))
}
rows.forEachIndexed { index, row ->
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) {
Box(modifier = Modifier.width(splitColWidth), contentAlignment = Alignment.CenterStart) {
Checkbox(
checked = row.splitScreen,
onCheckedChange = { checked ->
onSequenceChanged(rows.toMutableList().also { it[index] = row.copy(splitScreen = checked) })
}
)
Text("Split screen", color = MaterialTheme.colorScheme.onSurface)
}
ColorCycleButton(
label = if (row.splitScreen) "Left" else "Color",
color = row.leftColor,
onClick = {
onSequenceChanged(rows.toMutableList().also {
@@ -74,7 +96,6 @@ fun FlashSequenceEditor(
if (row.splitScreen) {
ColorCycleButton(
label = "Right",
color = row.rightColor,
onClick = {
onSequenceChanged(rows.toMutableList().also {
@@ -90,7 +111,8 @@ fun FlashSequenceEditor(
IconButton(
onClick = {
onSequenceChanged(rows.filterIndexed { i, _ -> i != index }.ifEmpty { defaultFlashSequence() })
}
},
modifier = Modifier.width(deleteColWidth),
) {
Icon(Icons.Default.Delete, contentDescription = "Remove row", tint = MaterialTheme.colorScheme.onSurface)
}
@@ -109,19 +131,26 @@ fun FlashSequenceEditor(
@Composable
private fun ColorCycleButton(
label: String,
color: FlashPaletteColor,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
OutlinedButton(onClick = onClick, modifier = modifier) {
OutlinedButton(
onClick = onClick,
modifier = modifier.heightIn(min = 40.dp),
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 8.dp),
) {
Box(
modifier = Modifier
.padding(end = 8.dp)
.background(color.previewColor(), RoundedCornerShape(6.dp))
.padding(horizontal = 10.dp, vertical = 10.dp)
.padding(horizontal = 10.dp, vertical = 8.dp)
)
Text(
text = color.displayName,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text("$label: ${color.displayName}")
}
}

View File

@@ -57,6 +57,7 @@ data class UiState(
data class TimelineProgramSignature(
val durationSec: Int,
val curveGranularitySec: Int,
val flashSequence: List<FlashSequenceRow>,
val visualLeft: List<TimelinePoint>,
val visualRight: List<TimelinePoint>,
val audioLeft: List<TimelinePoint>,
@@ -66,6 +67,7 @@ data class TimelineProgramSignature(
private fun timelineProgramSignature(timeline: TimelineEditorState): TimelineProgramSignature = TimelineProgramSignature(
durationSec = timeline.durationSec,
curveGranularitySec = timeline.curveGranularitySec,
flashSequence = timeline.flashSequence,
visualLeft = timeline.visualLeft.points,
visualRight = timeline.visualRight.points,
audioLeft = timeline.audioLeft.points,