feat: display selected route more prominently
This commit is contained in:
parent
81cdfcc9c5
commit
7281f6e1ba
2 changed files with 94 additions and 6 deletions
|
|
@ -15,6 +15,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.SearchBarDefaults
|
||||||
import androidx.compose.material3.SheetValue
|
import androidx.compose.material3.SheetValue
|
||||||
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
||||||
import androidx.compose.material3.rememberStandardBottomSheetState
|
import androidx.compose.material3.rememberStandardBottomSheetState
|
||||||
|
|
@ -126,13 +127,13 @@ fun App() {
|
||||||
|
|
||||||
LaunchedEffect(route) {
|
LaunchedEffect(route) {
|
||||||
val route = route
|
val route = route
|
||||||
|
polylines.clear()
|
||||||
if (route == null)
|
if (route == null)
|
||||||
return@LaunchedEffect
|
return@LaunchedEffect
|
||||||
val geoRoute = ptvService.route(route.routeId, true)
|
val geoRoute = ptvService.route(route.routeId, true)
|
||||||
val colour = route.routeType.getProperties().colour
|
val colour = route.routeType.getProperties().colour
|
||||||
|
|
||||||
val allPoints = mutableListOf<Point>()
|
val allPoints = mutableListOf<Point>()
|
||||||
polylines.clear()
|
|
||||||
geoRoute.geopath.forEach { pp ->
|
geoRoute.geopath.forEach { pp ->
|
||||||
// TODO: use gtfs colours
|
// TODO: use gtfs colours
|
||||||
pp.paths.forEach { sp ->
|
pp.paths.forEach { sp ->
|
||||||
|
|
@ -159,6 +160,7 @@ fun App() {
|
||||||
var stop by remember { mutableStateOf<PtvStop?>(null) }
|
var stop by remember { mutableStateOf<PtvStop?>(null) }
|
||||||
var markers by remember { mutableStateOf(listOf<Marker>()) }
|
var markers by remember { mutableStateOf(listOf<Marker>()) }
|
||||||
LaunchedEffect(route) {
|
LaunchedEffect(route) {
|
||||||
|
markers = listOf()
|
||||||
route?.let { route ->
|
route?.let { route ->
|
||||||
markers = buildStops(ptvService, route) {
|
markers = buildStops(ptvService, route) {
|
||||||
stop = it
|
stop = it
|
||||||
|
|
@ -196,7 +198,9 @@ fun App() {
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
newCameraPosition = newCameraPosition,
|
newCameraPosition = newCameraPosition,
|
||||||
cameraPositionUpdated = { newCameraPosition = null },
|
cameraPositionUpdated = { newCameraPosition = null },
|
||||||
extInsets = WindowInsets(top = with(LocalDensity.current) { 56.dp.roundToPx() }, bottom = extInsets),
|
extInsets = WindowInsets(top = with(LocalDensity.current) {
|
||||||
|
SearchBarDefaults.InputFieldHeight.roundToPx()
|
||||||
|
}, bottom = extInsets),
|
||||||
markers = markers,
|
markers = markers,
|
||||||
polylines = polylines,
|
polylines = polylines,
|
||||||
)
|
)
|
||||||
|
|
@ -208,6 +212,7 @@ fun App() {
|
||||||
if (it)
|
if (it)
|
||||||
scope.launch { scaffoldState.bottomSheetState.hide() }
|
scope.launch { scaffoldState.bottomSheetState.hide() }
|
||||||
},
|
},
|
||||||
|
route = route,
|
||||||
text = searchTextState,
|
text = searchTextState,
|
||||||
onTextChange = { searchTextState = it },
|
onTextChange = { searchTextState = it },
|
||||||
onRouteChange = { route = it }
|
onRouteChange = { route = it }
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
package moe.lava.banksia.ui
|
package moe.lava.banksia.ui
|
||||||
|
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Clear
|
import androidx.compose.material.icons.filled.Clear
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.ListItemDefaults
|
import androidx.compose.material3.ListItemDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -21,26 +29,34 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.backhandler.PredictiveBackHandler
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import moe.lava.banksia.api.ptv.PtvService
|
import moe.lava.banksia.api.ptv.PtvService
|
||||||
import moe.lava.banksia.api.ptv.structures.ComposableIcon
|
import moe.lava.banksia.api.ptv.structures.ComposableIcon
|
||||||
import moe.lava.banksia.api.ptv.structures.PtvRoute
|
import moe.lava.banksia.api.ptv.structures.PtvRoute
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Searcher(
|
fun Searcher(
|
||||||
ptvService: PtvService,
|
ptvService: PtvService,
|
||||||
expanded: Boolean,
|
expanded: Boolean,
|
||||||
onExpandedChange: (Boolean) -> Unit,
|
onExpandedChange: (Boolean) -> Unit,
|
||||||
|
route: PtvRoute?,
|
||||||
text: String,
|
text: String,
|
||||||
onTextChange: (String) -> Unit,
|
onTextChange: (String) -> Unit,
|
||||||
onRouteChange: (PtvRoute) -> Unit,
|
onRouteChange: (PtvRoute?) -> Unit,
|
||||||
) {
|
) {
|
||||||
val animatedPadding by animateDpAsState(
|
val animatedPadding by animateDpAsState(
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
|
|
@ -63,13 +79,41 @@ fun Searcher(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SearchBar(
|
SearchBar(
|
||||||
colors = SearchBarDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceContainer),
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopCenter)
|
.align(Alignment.TopCenter)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = animatedPadding),
|
.padding(horizontal = animatedPadding),
|
||||||
|
shadowElevation = 6.dp, // Elevation level 3
|
||||||
inputField = {
|
inputField = {
|
||||||
|
var backProgress by remember { mutableFloatStateOf(1f) }
|
||||||
|
var backEdgeIsLeft by remember { mutableStateOf<Boolean?>(null) }
|
||||||
|
val boxOpacityState by animateFloatAsState((1f - backProgress).pow(3))
|
||||||
|
val slideState by animateDpAsState((50 * backProgress).dp)
|
||||||
|
val slidePadding = if (backEdgeIsLeft == true)
|
||||||
|
PaddingValues(start = slideState)
|
||||||
|
else if (backEdgeIsLeft == false)
|
||||||
|
PaddingValues(end = slideState)
|
||||||
|
else
|
||||||
|
PaddingValues()
|
||||||
|
|
||||||
|
PredictiveBackHandler(enabled = route != null) { progress ->
|
||||||
|
try {
|
||||||
|
progress.collect { backEvent ->
|
||||||
|
backProgress = backEvent.progress
|
||||||
|
backEdgeIsLeft = backEvent.swipeEdge == 0
|
||||||
|
}
|
||||||
|
backProgress = 1f
|
||||||
|
onRouteChange(null)
|
||||||
|
} catch (_: CancellationException) {
|
||||||
|
backProgress = 0f
|
||||||
|
}
|
||||||
|
backEdgeIsLeft = null
|
||||||
|
}
|
||||||
SearchBarDefaults.InputField(
|
SearchBarDefaults.InputField(
|
||||||
|
enabled = route == null,
|
||||||
|
modifier = Modifier
|
||||||
|
.alpha(1f - boxOpacityState)
|
||||||
|
.padding(horizontal = 20.dp - animatedPadding),
|
||||||
query = text,
|
query = text,
|
||||||
onQueryChange = onTextChange,
|
onQueryChange = onTextChange,
|
||||||
onSearch = {},
|
onSearch = {},
|
||||||
|
|
@ -85,6 +129,45 @@ fun Searcher(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
LaunchedEffect(route) {
|
||||||
|
backProgress = if (route != null) 0f else 1f;
|
||||||
|
}
|
||||||
|
if (route != null) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.alpha(boxOpacityState)
|
||||||
|
.sizeIn(
|
||||||
|
minHeight = SearchBarDefaults.InputFieldHeight,
|
||||||
|
maxHeight = SearchBarDefaults.InputFieldHeight,
|
||||||
|
)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(slidePadding)
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
modifier = Modifier.align(Alignment.CenterStart),
|
||||||
|
onClick = {
|
||||||
|
onRouteChange(null)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(Icons.AutoMirrored.Filled.ArrowBack, "Clear route")
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 50.dp)
|
||||||
|
.align(Alignment.Center),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
route.routeType.ComposableIcon()
|
||||||
|
Spacer(Modifier.width(15.dp))
|
||||||
|
Text(
|
||||||
|
route.routeNumber.ifEmpty { route.routeName },
|
||||||
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onExpandedChange = onExpandedChange,
|
onExpandedChange = onExpandedChange,
|
||||||
|
|
@ -108,7 +191,7 @@ fun Searcher(
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 4.dp)
|
.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
onTextChange(route.getShortFullName())
|
onTextChange("")
|
||||||
onExpandedChange(false)
|
onExpandedChange(false)
|
||||||
onRouteChange(route)
|
onRouteChange(route)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue