Auto commit Thu Mar 19 08:01:01 PM CDT 2026
This commit is contained in:
@@ -8,9 +8,8 @@ import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.background
|
||||
import com.mindmachine.mvp.session.TimelineEditorScreen
|
||||
import com.mindmachine.mvp.session.TimelineModel
|
||||
import com.mindmachine.mvp.session.TimelineParameterControl
|
||||
import com.mindmachine.mvp.session.DurationSliderCard
|
||||
import com.mindmachine.mvp.session.SetupTimelineEditor
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -194,13 +193,6 @@ fun App(vm: MainViewModel = viewModel()) {
|
||||
nav.navigate("active")
|
||||
},
|
||||
onHolder = { nav.navigate("holder") },
|
||||
onOpenTimelineEditor = { nav.navigate("timeline") },
|
||||
)
|
||||
}
|
||||
composable("timeline") {
|
||||
TimelineEditorScreen(
|
||||
vm = vm,
|
||||
onBack = { nav.popBackStack() }
|
||||
)
|
||||
}
|
||||
composable("active") {
|
||||
@@ -267,13 +259,9 @@ fun SetupScreen(
|
||||
vm: MainViewModel,
|
||||
onStart: () -> Unit,
|
||||
onHolder: () -> Unit,
|
||||
onOpenTimelineEditor: () -> Unit,
|
||||
) {
|
||||
val ui by vm.ui.collectAsStateWithLifecycle()
|
||||
val cfg = ui.config
|
||||
var model by remember(ui.selectedPreset.id, cfg.durationSec) {
|
||||
mutableStateOf(TimelineModel.createForPreset(ui.selectedPreset, cfg.durationSec))
|
||||
}
|
||||
val scrollState = rememberScrollState()
|
||||
val containerModifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -282,32 +270,27 @@ fun SetupScreen(
|
||||
.safeDrawingPadding()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
|
||||
TimelineParameterControl(
|
||||
model = model,
|
||||
onParameterChanged = { id: String, newValue: Float ->
|
||||
model = model.updateParameterValue(id, newValue)
|
||||
when (id) {
|
||||
"flash_interval" -> vm.setFlashIntervalMs((newValue * 1000f).roundToInt())
|
||||
"carrier_frequency" -> vm.setCarrier(newValue)
|
||||
"binaural_difference" -> vm.setDifference(newValue)
|
||||
}
|
||||
},
|
||||
modifier = containerModifier,
|
||||
showTimeline = true,
|
||||
onTimelineChanged = { newDuration ->
|
||||
vm.setDurationMin((newDuration / 60f).roundToInt())
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
// Additional controls
|
||||
Column(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
modifier = containerModifier,
|
||||
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||
) {
|
||||
OutlinedButton(onClick = onOpenTimelineEditor, modifier = Modifier.fillMaxWidth()) {
|
||||
Text("Open Timeline Editor")
|
||||
}
|
||||
Text(
|
||||
ui.selectedPreset.name,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
|
||||
DurationSliderCard(
|
||||
durationSec = ui.timeline.durationSec,
|
||||
onDurationSecChanged = { vm.setDurationSec(it) },
|
||||
)
|
||||
|
||||
// Integrated timeline editor (no separate screen).
|
||||
SetupTimelineEditor(
|
||||
state = ui.timeline,
|
||||
onStateChanged = vm::updateTimeline,
|
||||
)
|
||||
|
||||
TextButton(onClick = onHolder) { Text("Holder Guidance") }
|
||||
if (ui.error != null) Text(ui.error!!, color = MaterialTheme.colorScheme.error)
|
||||
Button(onClick = onStart, modifier = Modifier.fillMaxWidth().height(52.dp)) { Text("Start") }
|
||||
@@ -359,7 +342,8 @@ fun ActiveSessionScreen(vm: MainViewModel, onFinish: () -> Unit, onBackToSetup:
|
||||
val flashFrame by rememberSplitFlashFrame(
|
||||
isRunning = ui.runtimeState == RuntimeState.RUNNING,
|
||||
mode = ui.config.mode,
|
||||
flashIntervalMs = ui.config.flashIntervalMs,
|
||||
flashOnMs = ui.runtimeParams.flashOnMs,
|
||||
flashOffMs = ui.runtimeParams.flashOffMs,
|
||||
)
|
||||
|
||||
Box(
|
||||
@@ -471,15 +455,16 @@ private fun FlashColor.toComposeColor(): Color = when (this) {
|
||||
private fun rememberSplitFlashFrame(
|
||||
isRunning: Boolean,
|
||||
mode: SessionMode,
|
||||
flashIntervalMs: Int,
|
||||
) = produceState(initialValue = SplitFlashFrame(FlashColor.BLACK, FlashColor.BLACK), isRunning, mode, flashIntervalMs) {
|
||||
flashOnMs: Int,
|
||||
flashOffMs: Int,
|
||||
) = produceState(initialValue = SplitFlashFrame(FlashColor.BLACK, FlashColor.BLACK), isRunning, mode, flashOnMs, flashOffMs) {
|
||||
if (!isRunning || mode == SessionMode.AUDIO_ONLY) {
|
||||
value = SplitFlashFrame(FlashColor.BLACK, FlashColor.BLACK)
|
||||
return@produceState
|
||||
}
|
||||
while (true) {
|
||||
withFrameNanos { frameTimeNanos ->
|
||||
value = computeSplitFlashFrame(frameTimeNanos, flashIntervalMs)
|
||||
value = computeSplitFlashFrame(frameTimeNanos, flashOnMs, flashOffMs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user