fix(map): chart fill padding and dropdown selected border

- AirMQChart: add bottom padding so fill extends below line and thick line stays in bounds
- DeviceSensorDropdown: show border on selected item when dropdown is opened

Made-with: Cursor
This commit is contained in:
2026-03-04 22:41:26 +01:00
parent e29e6ef498
commit 863961405d
2 changed files with 25 additions and 15 deletions

View File

@@ -179,15 +179,17 @@ fun AirMQChart(
val innerLeft = with(density) { ChartPaddingLeftDp.toPx() }
val innerRight = w - with(density) { ChartPaddingRightDp.toPx() }
val innerTop = with(density) { ChartPaddingTopDp.toPx() }
val bottomPaddingPx = with(density) { 12.dp.toPx() }
val bottomMarginPx = with(density) { 16.dp.toPx() }
val fakePieceTop = h - bottomMarginPx - 1f
val innerBottomLegacy = h - with(density) { ChartPaddingBottomDp.toPx() }
val fillBottom = if (config.roundCorners) fakePieceTop else innerBottomLegacy + bottomMarginPx
// fillBottom = where the fill extends to (space below line is filled with same color)
// chartBottom = where the line's lowest point plots (keeps thick line inside bounds, padding above fillBottom)
val fillBottom = if (config.roundCorners) h - bottomMarginPx - 1f else h
val chartBottom = fillBottom - bottomPaddingPx
val chartWidth = innerRight - innerLeft
val chartHeight = fillBottom - innerTop
val chartHeight = chartBottom - innerTop
fun toX(ts: Long) = innerLeft + ((ts - xMin) / rangeX.toFloat()) * chartWidth
fun toY(v: Float) = fillBottom - ((v - yMin) / rangeY) * chartHeight
fun toY(v: Float) = chartBottom - ((v - yMin) / rangeY) * chartHeight
drawChartBackground(
left = innerLeft,
@@ -244,7 +246,7 @@ fun AirMQChart(
innerLeft = innerLeft,
innerRight = innerRight,
innerTop = innerTop,
fillBottom = fillBottom,
chartBottom = chartBottom,
config = config,
unit = unit,
textMeasurer = textMeasurer
@@ -264,7 +266,7 @@ fun AirMQChart(
innerLeft = innerLeft,
innerRight = innerRight,
innerTop = innerTop,
fillBottom = fillBottom,
chartBottom = chartBottom,
textMeasurer = textMeasurer
)
}
@@ -520,7 +522,7 @@ private fun DrawScope.drawLabels(
innerLeft: Float,
innerRight: Float,
innerTop: Float,
fillBottom: Float,
chartBottom: Float,
config: ChartConfig,
unit: String,
textMeasurer: androidx.compose.ui.text.TextMeasurer
@@ -533,15 +535,15 @@ private fun DrawScope.drawLabels(
val pad = with(density) { 8.dp.toPx() }
val textLayout = textMeasurer.measure("0", TextStyle(fontSize = LabelTextSizeSp.sp))
val baselineOffset = textLayout.size.height * 0.75f
val chartHeight = fillBottom - innerTop
val chartHeight = chartBottom - innerTop
val midY = (maxY + minY) / 2f
val midText = formatRounded(midY, rangeY)
val midYpos = innerTop + chartHeight / 2f + baselineOffset
val bottomBaseline = fillBottom + baselineOffset
val bottomBaseline = chartBottom + baselineOffset
drawTextLabelRightAligned(maxText, innerLeft - pad, innerTop + baselineOffset, labelColor, textMeasurer)
drawTextLabelRightAligned(midText, innerLeft - pad, midYpos, labelColor, textMeasurer)
drawTextLabelRightAligned(minText, innerLeft - pad, bottomBaseline, labelColor, textMeasurer)
val lastYpos = fillBottom - ((lastY - minY) / rangeY) * chartHeight
val lastYpos = chartBottom - ((lastY - minY) / rangeY) * chartHeight
val lastTextLayout = textMeasurer.measure(lastText, TextStyle(fontSize = LabelTextSizeSp.sp))
val lastBaseline = lastYpos - lastTextLayout.size.height / 3f
drawTextLabel(lastText, innerRight + pad, lastBaseline, labelColor, textMeasurer)
@@ -592,11 +594,11 @@ private fun DrawScope.drawMarker(
innerLeft: Float,
innerRight: Float,
innerTop: Float,
fillBottom: Float,
chartBottom: Float,
textMeasurer: androidx.compose.ui.text.TextMeasurer
) {
drawLine(config.lineColor, Offset(innerLeft, y), Offset(innerRight, y), strokeWidth = 1f)
drawLine(config.lineColor, Offset(x, innerTop - 16), Offset(x, fillBottom + 16), strokeWidth = 1f)
drawLine(config.lineColor, Offset(x, innerTop - 16), Offset(x, chartBottom + 16), strokeWidth = 1f)
drawCircle(config.lineColor, radius = MarkerRadiusPx, center = Offset(x, y))
val valueStr = "${formatRounded(point.value, rangeY)} $unit".trim()

View File

@@ -1,6 +1,7 @@
package org.db3.airmq.features.map
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -27,6 +28,7 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.runtime.getValue
@@ -576,8 +578,8 @@ private fun DeviceSensorDropdown(
) {
AnimatedVisibility(
visible = isExpanded,
enter = expandVertically(),
exit = shrinkVertically()
enter = expandVertically(animationSpec = tween(durationMillis = 150)),
exit = shrinkVertically(animationSpec = tween(durationMillis = 150))
) {
Surface(
modifier = Modifier.fillMaxWidth(),
@@ -593,6 +595,7 @@ private fun DeviceSensorDropdown(
DeviceSensorDropdownOption(
sensorType = sensorType,
selected = selectedSensor == sensorType,
showSelectedBorder = isExpanded && selectedSensor == sensorType,
onClick = {
onSelected(sensorType)
isExpanded = false
@@ -664,12 +667,17 @@ private fun sensorLabelRes(sensorType: DeviceSensorType): Int = when (sensorType
private fun DeviceSensorDropdownOption(
sensorType: DeviceSensorType,
selected: Boolean,
showSelectedBorder: Boolean,
onClick: () -> Unit
) {
val textColor = if (selected) Color(0xFF1C1C1C) else Color(0xFF616161)
val borderModifier = if (showSelectedBorder) {
Modifier.border(1.dp, Color(0x61000000), RoundedCornerShape(8.dp))
} else Modifier
Row(
modifier = Modifier
.fillMaxWidth()
.then(borderModifier)
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically