UI: filtering improvements

This commit is contained in:
2026-08-26 11:05:34 +02:00
parent 8dc67adad3
commit 7343300536
@@ -212,7 +212,7 @@ fun AddVersionDialog(onCancel: () -> Unit, onAdd: (name: String, color: Color, m
CancelConfirmXRow(onCancel, {
onAdd(name.trim(), color, meta.trim())
}, confirmText = "Create", canConfirm = name.trim().isNotBlank() && meta.trim().isNotBlank())
}, confirmText = "Create", canConfirm = name.trim().isNotBlank())
}
}
}
@@ -303,7 +303,7 @@ fun AddEntryDialog(
DropdownMenuItem({
Text(
"Create new label...",
"Create new version...",
color = LocalContentColor.current.copy(alpha = 0.75f),
fontStyle = FontStyle.Italic
)
@@ -379,6 +379,34 @@ fun AddEntryDialog(
}
}
@Composable
fun EditVersionDialog(current: ProjectVM.Version, onCancel: () -> Unit, onUpdate: (id: Uuid, name: String?, color: Color?, meta: String?) -> Unit) {
Dialog(onDismissRequest = onCancel) {
var name by remember { mutableStateOf(current.name) }
var color by remember { mutableStateOf(current.uiColor) }
var meta by remember { mutableStateOf(current.meta) }
Surface(Modifier.padding(15.dp).widthIn(400.dp), shape = MaterialTheme.shapes.medium) {
Column(Modifier.padding(8.dp).width(IntrinsicSize.Min)) {
Text("Create version", Modifier.align(Alignment.CenterHorizontally), style = MaterialTheme.typography.headlineMedium)
OutlinedTextField(name, { name = it }, Modifier.fillMaxWidth(), label = { Text("Name") })
Spacer(Modifier.height(10.dp))
KolorPicker(color, { color = it }, alphaTrackVisible = false, modifier = Modifier.aspectRatio(1f))
Spacer(Modifier.height(10.dp))
OutlinedTextField(meta, { meta = it }, Modifier.fillMaxWidth(), label = { Text("Additional information") })
Spacer(Modifier.height(10.dp))
CancelConfirmXRow(onCancel, {
val useName = name.trim() nonEq current.name
val useColor = color nonEq current.uiColor
val useMeta = meta.trim() nonEq current.meta
onUpdate(current.id, useName, useColor, useMeta)
}, confirmText = "Update", canConfirm = name.trim().isNotBlank())
}
}
}
}
@Composable
fun ProjectPlotArea(vm: ProjectVM){
val entries by vm.entries
@@ -393,25 +421,27 @@ fun ProjectPlotArea(vm: ProjectVM){
var timeFilter by remember { mutableStateOf(0f..1f) }
var timeFilterString by remember { mutableStateOf("") }
val versionFilter = remember(versions) { mutableStateSetFrom(versions.values) }
val labelVersionFilter by versionFilter.revision
val enabledInputs = remember { mutableStateSetOf<String>() }
val enabledHardware = remember { mutableStateSetOf<String>() }
var filterInput by remember { mutableStateOf<String?>(null) }
val inputFilter = remember(allInputs) { mutableStateSetFrom(allInputs) }
var filterHardware by remember { mutableStateOf<String?>(null) }
val versionFilterRev by versionFilter.revision
val inputFilterRev by inputFilter.revision
LaunchedEffect(entries, allInputs, allHardware, timeFilter, timeMin, timeMax, labelVersionFilter, filterInput, filterHardware) {
val enabledHardware = remember(allHardware) { mutableStateSetOf<String>() }
val enabledInputs = remember(allInputs) { mutableStateSetOf<String>() }
LaunchedEffect(entries, allInputs, allHardware, timeFilter, timeMin, timeMax, versionFilterRev, inputFilterRev, filterHardware) {
val timeRange = dualLerp(timeMin, timeMax, timeFilter.start, timeFilter.endInclusive)
enabledInputs.clear()
enabledHardware.clear()
enabledInputs.clear()
filteredEntries = entries.filter { entry ->
if(entry.label !in versionFilter) return@filter false
if(!(entry.timeStamp inRange timeRange)) return@filter false
if(filterInput != null && entry.input != filterInput) return@filter false
if(entry.input !in inputFilter) return@filter false
if(filterHardware != null && entry.hardware != filterHardware) return@filter false
enabledInputs.add(entry.input)
enabledHardware.add(entry.hardware)
enabledInputs.add(entry.input)
true
}
}
@@ -446,6 +476,7 @@ fun ProjectPlotArea(vm: ProjectVM){
var deletingVersion by remember { mutableStateOf<ProjectVM.Version?>(null) }
var editingVersion by remember { mutableStateOf<ProjectVM.Version?>(null) }
var baseline by remember { mutableStateOf<ProjectVM.Version?>(null) }
var baselineComparison by remember { mutableStateOf<List<Tuple4<String, Float, Color, Float>>?>(null) }
var baselineMax by remember { mutableStateOf(2.0f) }
@@ -614,6 +645,12 @@ fun ProjectPlotArea(vm: ProjectVM){
}
}
}
if(editable) {
IconButton({ editingVersion = lbl }, Modifier.align(Alignment.CenterVertically)) {
Icon(Lucide.Pencil, "Edit version")
}
}
IconButton({ baseline = if(baseline == lbl) null else lbl },
Modifier.align(Alignment.CenterVertically), enabled = lbl in versionFilter) {
if(baseline == lbl) {
@@ -644,14 +681,39 @@ fun ProjectPlotArea(vm: ProjectVM){
item { Spacer(Modifier.height(10.dp)) }
items((allInputs intersect enabledInputs.toSet()).toList()) {
Row(Modifier.fillMaxWidth().clickable { filterInput = if(filterInput == null) it else null }) {
Text(it, Modifier.padding(start = 5.dp))
items((allInputs intersect inputFilter.toSet()).toList()) {
Row(Modifier.fillMaxWidth().clickable {
if(inputFilter.size == allInputs.size) {
inputFilter.clear()
inputFilter.add(it)
}
else if(inputFilter.size == 1) {
inputFilter.addAll(allInputs.toSet()); inputFilter.remove(it)
}
else {
inputFilter.toggle(it)
if(inputFilter.isEmpty()) inputFilter.addAll(allInputs.toSet())
}
}) {
Text(it, Modifier.padding(start = 5.dp), color = LocalContentColor.current.copy(alpha = if(it in enabledInputs) 1.0f else 0.9f))
if(it !in enabledInputs) Text(" (no data)", Modifier.align(Alignment.Bottom),
style = MaterialTheme.typography.bodySmall, color = LocalContentColor.current.copy(0.9f))
}
}
items((allInputs - enabledInputs.toSet()).toList()) {
Text(it, Modifier.padding(start = 5.dp, top = 2.dp, bottom = 2.dp), color = LocalContentColor.current.copy(0.66f))
items((allInputs - inputFilter.toSet()).toList()) {
Row(Modifier.fillMaxWidth().clickable {
if(inputFilter.size == allInputs.size) {
inputFilter.clear()
inputFilter.add(it)
}
else {
inputFilter.toggle(it)
if(inputFilter.isEmpty()) inputFilter.addAll(allInputs.toSet())
}
}) {
Text(it, Modifier.padding(start = 5.dp), color = LocalContentColor.current.copy(0.66f))
}
}
item { Spacer(Modifier.height(10.dp)) }
@@ -726,6 +788,13 @@ fun ProjectPlotArea(vm: ProjectVM){
vm.deleteVersion(rmV.id)
}
}
editingVersion?.let { edV ->
EditVersionDialog(edV, { editingVersion = null }) { id, name, color, meta ->
vm.updateLabel(id, name, color, meta)
editingVersion = null
}
}
}
@Composable