UI: filtering improvements
This commit is contained in:
+84
-15
@@ -212,7 +212,7 @@ fun AddVersionDialog(onCancel: () -> Unit, onAdd: (name: String, color: Color, m
|
|||||||
|
|
||||||
CancelConfirmXRow(onCancel, {
|
CancelConfirmXRow(onCancel, {
|
||||||
onAdd(name.trim(), color, meta.trim())
|
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({
|
DropdownMenuItem({
|
||||||
Text(
|
Text(
|
||||||
"Create new label...",
|
"Create new version...",
|
||||||
color = LocalContentColor.current.copy(alpha = 0.75f),
|
color = LocalContentColor.current.copy(alpha = 0.75f),
|
||||||
fontStyle = FontStyle.Italic
|
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
|
@Composable
|
||||||
fun ProjectPlotArea(vm: ProjectVM){
|
fun ProjectPlotArea(vm: ProjectVM){
|
||||||
val entries by vm.entries
|
val entries by vm.entries
|
||||||
@@ -393,25 +421,27 @@ fun ProjectPlotArea(vm: ProjectVM){
|
|||||||
var timeFilter by remember { mutableStateOf(0f..1f) }
|
var timeFilter by remember { mutableStateOf(0f..1f) }
|
||||||
var timeFilterString by remember { mutableStateOf("") }
|
var timeFilterString by remember { mutableStateOf("") }
|
||||||
val versionFilter = remember(versions) { mutableStateSetFrom(versions.values) }
|
val versionFilter = remember(versions) { mutableStateSetFrom(versions.values) }
|
||||||
val labelVersionFilter by versionFilter.revision
|
val inputFilter = remember(allInputs) { mutableStateSetFrom(allInputs) }
|
||||||
val enabledInputs = remember { mutableStateSetOf<String>() }
|
|
||||||
val enabledHardware = remember { mutableStateSetOf<String>() }
|
|
||||||
var filterInput by remember { mutableStateOf<String?>(null) }
|
|
||||||
var filterHardware by remember { mutableStateOf<String?>(null) }
|
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)
|
val timeRange = dualLerp(timeMin, timeMax, timeFilter.start, timeFilter.endInclusive)
|
||||||
enabledInputs.clear()
|
|
||||||
enabledHardware.clear()
|
enabledHardware.clear()
|
||||||
|
enabledInputs.clear()
|
||||||
|
|
||||||
filteredEntries = entries.filter { entry ->
|
filteredEntries = entries.filter { entry ->
|
||||||
if(entry.label !in versionFilter) return@filter false
|
if(entry.label !in versionFilter) return@filter false
|
||||||
if(!(entry.timeStamp inRange timeRange)) 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
|
if(filterHardware != null && entry.hardware != filterHardware) return@filter false
|
||||||
|
|
||||||
enabledInputs.add(entry.input)
|
|
||||||
enabledHardware.add(entry.hardware)
|
enabledHardware.add(entry.hardware)
|
||||||
|
enabledInputs.add(entry.input)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,6 +476,7 @@ fun ProjectPlotArea(vm: ProjectVM){
|
|||||||
|
|
||||||
|
|
||||||
var deletingVersion by remember { mutableStateOf<ProjectVM.Version?>(null) }
|
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 baseline by remember { mutableStateOf<ProjectVM.Version?>(null) }
|
||||||
var baselineComparison by remember { mutableStateOf<List<Tuple4<String, Float, Color, Float>>?>(null) }
|
var baselineComparison by remember { mutableStateOf<List<Tuple4<String, Float, Color, Float>>?>(null) }
|
||||||
var baselineMax by remember { mutableStateOf(2.0f) }
|
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 },
|
IconButton({ baseline = if(baseline == lbl) null else lbl },
|
||||||
Modifier.align(Alignment.CenterVertically), enabled = lbl in versionFilter) {
|
Modifier.align(Alignment.CenterVertically), enabled = lbl in versionFilter) {
|
||||||
if(baseline == lbl) {
|
if(baseline == lbl) {
|
||||||
@@ -644,14 +681,39 @@ fun ProjectPlotArea(vm: ProjectVM){
|
|||||||
|
|
||||||
item { Spacer(Modifier.height(10.dp)) }
|
item { Spacer(Modifier.height(10.dp)) }
|
||||||
|
|
||||||
items((allInputs intersect enabledInputs.toSet()).toList()) {
|
items((allInputs intersect inputFilter.toSet()).toList()) {
|
||||||
Row(Modifier.fillMaxWidth().clickable { filterInput = if(filterInput == null) it else null }) {
|
Row(Modifier.fillMaxWidth().clickable {
|
||||||
Text(it, Modifier.padding(start = 5.dp))
|
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()) {
|
items((allInputs - inputFilter.toSet()).toList()) {
|
||||||
Text(it, Modifier.padding(start = 5.dp, top = 2.dp, bottom = 2.dp), color = LocalContentColor.current.copy(0.66f))
|
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)) }
|
item { Spacer(Modifier.height(10.dp)) }
|
||||||
@@ -726,6 +788,13 @@ fun ProjectPlotArea(vm: ProjectVM){
|
|||||||
vm.deleteVersion(rmV.id)
|
vm.deleteVersion(rmV.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editingVersion?.let { edV ->
|
||||||
|
EditVersionDialog(edV, { editingVersion = null }) { id, name, color, meta ->
|
||||||
|
vm.updateLabel(id, name, color, meta)
|
||||||
|
editingVersion = null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
Reference in New Issue
Block a user