build: S08 settings and S09 units
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package app.wetter.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.heading
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import app.wetter.R
|
||||
import app.wetter.data.DistanceUnit
|
||||
import app.wetter.data.Graph
|
||||
import app.wetter.data.PrecipUnit
|
||||
import app.wetter.data.PressureUnit
|
||||
import app.wetter.data.Settings
|
||||
import app.wetter.data.TempUnit
|
||||
import app.wetter.data.ThemeMode
|
||||
import app.wetter.data.WindUnit
|
||||
import app.wetter.ui.theme.WSpace
|
||||
import app.wetter.ui.theme.WType
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** S08 + S09: units, look, data sources. */
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(settings: Settings, onBack: () -> Unit) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val set: ((Settings) -> Settings) -> Unit = { t -> scope.launch { Graph.settings.update(t) } }
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings)) },
|
||||
navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Rounded.ArrowBack, stringResource(R.string.back)) } },
|
||||
)
|
||||
},
|
||||
) { pad ->
|
||||
Column(Modifier.padding(pad).verticalScroll(rememberScrollState()).padding(horizontal = WSpace.s16)) {
|
||||
Header(stringResource(R.string.set_units))
|
||||
Choice(stringResource(R.string.set_temp), TempUnit.entries, settings.temp, { if (it == TempUnit.C) "°C" else "°F" }) { v -> set { it.copy(temp = v) } }
|
||||
Choice(stringResource(R.string.set_wind), WindUnit.entries, settings.wind, {
|
||||
when (it) { WindUnit.KMH -> "km/h"; WindUnit.MS -> "m/s"; WindUnit.MPH -> "mph"; WindUnit.KN -> "kn" }
|
||||
}) { v -> set { it.copy(wind = v) } }
|
||||
Choice(stringResource(R.string.set_precip), PrecipUnit.entries, settings.precip, { if (it == PrecipUnit.MM) "mm" else "in" }) { v -> set { it.copy(precip = v) } }
|
||||
Choice(stringResource(R.string.set_pressure), PressureUnit.entries, settings.pressure, { if (it == PressureUnit.HPA) "hPa" else "inHg" }) { v -> set { it.copy(pressure = v) } }
|
||||
Choice(stringResource(R.string.set_distance), DistanceUnit.entries, settings.distance, { if (it == DistanceUnit.KM) "km" else "mi" }) { v -> set { it.copy(distance = v) } }
|
||||
|
||||
Header(stringResource(R.string.set_look))
|
||||
val themeLabels = mapOf(
|
||||
ThemeMode.SYSTEM to stringResource(R.string.theme_system),
|
||||
ThemeMode.LIGHT to stringResource(R.string.theme_light),
|
||||
ThemeMode.DARK to stringResource(R.string.theme_dark),
|
||||
)
|
||||
Choice(stringResource(R.string.set_theme), ThemeMode.entries, settings.theme, { themeLabels.getValue(it) }) { v -> set { it.copy(theme = v) } }
|
||||
Toggle(stringResource(R.string.set_animations), stringResource(R.string.set_animations_sub), settings.animations) { v -> set { it.copy(animations = v) } }
|
||||
Toggle(stringResource(R.string.set_brief), null, settings.brief) { v -> set { it.copy(brief = v) } }
|
||||
|
||||
Header(stringResource(R.string.set_about))
|
||||
Text(stringResource(R.string.attribution), style = WType.bodySm, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(bottom = WSpace.s32))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(text: String) = Text(
|
||||
text, style = WType.title, color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(top = WSpace.s24, bottom = WSpace.s8).semantics { heading() },
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun <T> Choice(label: String, options: List<T>, selected: T, name: (T) -> String, onPick: (T) -> Unit) {
|
||||
Column(Modifier.padding(vertical = WSpace.s8)) {
|
||||
Text(label, style = WType.body)
|
||||
SingleChoiceSegmentedButtonRow(Modifier.fillMaxWidth().padding(top = WSpace.s8).selectableGroup()) {
|
||||
options.forEachIndexed { i, o ->
|
||||
SegmentedButton(
|
||||
selected = o == selected,
|
||||
onClick = { onPick(o) },
|
||||
shape = SegmentedButtonDefaults.itemShape(i, options.size),
|
||||
) { Text(name(o)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Toggle(label: String, sub: String?, checked: Boolean, onChange: (Boolean) -> Unit) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth().toggleable(checked, role = Role.Switch, onValueChange = onChange).padding(vertical = WSpace.s12),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(label, style = WType.body)
|
||||
if (sub != null) Text(sub, style = WType.bodySm, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
Switch(checked, onCheckedChange = null)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user