fix(ui): stable LazyColumn keys, semantic roles, and content descriptions (#5168)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: jamesarich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-04-17 11:24:18 -05:00 committed by GitHub
parent 5eba7e4dce
commit 2a6e27de09
10 changed files with 99 additions and 20 deletions

View file

@ -1270,4 +1270,10 @@
<string name="desktop_tray_show">Show Meshtastic</string>
<string name="desktop_tray_quit">Quit</string>
<string name="desktop_notification_title">Meshtastic</string>
<string name="export_tak_data_package">Export TAK Data Package</string>
<string name="mpwrd_os" translatable="false">mPWRD-OS</string>
<string name="clear_time_zone">Clear time zone</string>
<string name="filter_icon">Filter</string>
<string name="remove_filter">Remove filter</string>
<string name="show_iaq_legend">Show air quality legend</string>
</resources>

View file

@ -38,6 +38,7 @@ fun ClickableTextField(
onClick: () -> Unit,
modifier: Modifier = Modifier,
isError: Boolean = false,
trailingIconContentDescription: String? = null,
) {
val source = remember { MutableInteractionSource() }
val isPressed by source.collectIsPressedAsState()
@ -49,7 +50,7 @@ fun ClickableTextField(
enabled = enabled,
readOnly = true,
label = { Text(stringResource(label)) },
trailingIcon = { Icon(trailingIcon, null) },
trailingIcon = { Icon(trailingIcon, trailingIconContentDescription) },
isError = isError,
interactionSource = source,
modifier = modifier,

View file

@ -44,6 +44,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -58,6 +59,7 @@ import org.meshtastic.core.resources.preview_gauge
import org.meshtastic.core.resources.preview_gradient
import org.meshtastic.core.resources.preview_pill
import org.meshtastic.core.resources.preview_text
import org.meshtastic.core.resources.show_iaq_legend
import org.meshtastic.core.ui.icon.MeshtasticIcons
import org.meshtastic.core.ui.icon.ThumbUp
import org.meshtastic.core.ui.icon.Warning
@ -120,13 +122,18 @@ fun IndoorAirQuality(iaq: Int?, displayMode: IaqDisplayMode = IaqDisplayMode.Pil
Column {
when (displayMode) {
IaqDisplayMode.Pill -> {
val legendLabel = stringResource(Res.string.show_iaq_legend)
Box(
modifier =
Modifier.clip(RoundedCornerShape(10.dp))
.background(iaqEnum.color)
.width(125.dp)
.height(30.dp)
.clickable { isLegendOpen = true },
.clickable(
onClickLabel = legendLabel,
role = Role.Button,
onClick = { isLegendOpen = true },
),
) {
Row(
modifier = Modifier.padding(4.dp).align(Alignment.CenterStart),
@ -144,7 +151,15 @@ fun IndoorAirQuality(iaq: Int?, displayMode: IaqDisplayMode = IaqDisplayMode.Pil
}
IaqDisplayMode.Dot -> {
Column(modifier = Modifier.clickable { isLegendOpen = true }) {
val legendLabel = stringResource(Res.string.show_iaq_legend)
Column(
modifier =
Modifier.clickable(
onClickLabel = legendLabel,
role = Role.Button,
onClick = { isLegendOpen = true },
),
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "$iaq")
Spacer(modifier = Modifier.width(4.dp))
@ -154,17 +169,30 @@ fun IndoorAirQuality(iaq: Int?, displayMode: IaqDisplayMode = IaqDisplayMode.Pil
}
IaqDisplayMode.Text -> {
val legendLabel = stringResource(Res.string.show_iaq_legend)
Text(
text = getIaqDescriptionWithRange(iaqEnum),
fontSize = 12.sp,
modifier = Modifier.clickable { isLegendOpen = true },
modifier =
Modifier.clickable(
onClickLabel = legendLabel,
role = Role.Button,
onClick = { isLegendOpen = true },
),
)
}
IaqDisplayMode.Gauge -> {
val legendLabel = stringResource(Res.string.show_iaq_legend)
CircularProgressIndicator(
progress = { iaq / 500f },
modifier = Modifier.size(60.dp).clickable { isLegendOpen = true },
modifier =
Modifier.size(60.dp)
.clickable(
onClickLabel = legendLabel,
role = Role.Button,
onClick = { isLegendOpen = true },
),
strokeWidth = 8.dp,
color = iaqEnum.color,
)
@ -172,9 +200,15 @@ fun IndoorAirQuality(iaq: Int?, displayMode: IaqDisplayMode = IaqDisplayMode.Pil
}
IaqDisplayMode.Gradient -> {
val legendLabel = stringResource(Res.string.show_iaq_legend)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.clickable { isLegendOpen = true },
modifier =
Modifier.clickable(
onClickLabel = legendLabel,
role = Role.Button,
onClick = { isLegendOpen = true },
),
) {
LinearProgressIndicator(
progress = { iaq / 500f },

View file

@ -34,6 +34,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -80,7 +81,13 @@ fun RegularPreference(
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
}
Column(modifier = modifier.fillMaxWidth().clickable(enabled = enabled, onClick = onClick).padding(all = 16.dp)) {
Column(
modifier =
modifier
.fillMaxWidth()
.clickable(enabled = enabled, onClick = onClick, role = Role.Button)
.padding(all = 16.dp),
) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween) {
FlowRow(modifier = Modifier.weight(1f), horizontalArrangement = Arrangement.SpaceBetween) {
Text(