@@ -29,8 +29,6 @@ import androidx.compose.foundation.layout.Spacer
2929import androidx.compose.foundation.layout.WindowInsets
3030import androidx.compose.foundation.layout.WindowInsetsSides
3131import androidx.compose.foundation.layout.asPaddingValues
32- import androidx.compose.foundation.layout.calculateEndPadding
33- import androidx.compose.foundation.layout.calculateStartPadding
3432import androidx.compose.foundation.layout.only
3533import androidx.compose.foundation.layout.padding
3634import androidx.compose.foundation.layout.safeDrawing
@@ -50,23 +48,25 @@ import androidx.compose.material3.minimumInteractiveComponentSize
5048import androidx.compose.runtime.Composable
5149import androidx.compose.runtime.getValue
5250import androidx.compose.ui.Modifier
51+ import androidx.compose.ui.platform.LocalDensity
5352import androidx.compose.ui.platform.LocalFocusManager
5453import androidx.compose.ui.platform.LocalLayoutDirection
5554import androidx.compose.ui.platform.LocalSoftwareKeyboardController
5655import androidx.compose.ui.res.stringResource
5756import androidx.compose.ui.text.style.TextOverflow
57+ import androidx.compose.ui.util.lerp
5858import ru.spbu.depnav.R
5959import ru.spbu.depnav.ui.theme.DEFAULT_PADDING
6060import ru.spbu.depnav.ui.theme.ON_MAP_SURFACE_ALPHA
6161import ru.spbu.depnav.ui.viewmodel.SearchResults
6262
6363// These are basically copied from SearchBar implementation
64- private val ACTIVATION_ENTER_SPEC = tween<Float >(
64+ private val EXPANSION_ENTER_SPEC = tween<Float >(
6565 durationMillis = 600 ,
6666 delayMillis = 100 ,
6767 easing = CubicBezierEasing (0.05f , 0.7f , 0.1f , 1.0f )
6868)
69- private val ACTIVATION_EXIT_SPEC = tween<Float >(
69+ private val EXPANSION_EXIT_SPEC = tween<Float >(
7070 durationMillis = 350 ,
7171 delayMillis = 100 ,
7272 easing = CubicBezierEasing (0.0f , 1.0f , 0.0f , 1.0f )
@@ -76,81 +76,59 @@ private val ACTIVATION_EXIT_SPEC = tween<Float>(
7676 * Search bar for querying map markers on [ru.spbu.depnav.ui.screen.MapScreen].
7777 */
7878@Composable
79- @Suppress(
80- " LongMethod" , // No point in further shrinking
81- " LongParameterList" // Considered OK for a composable
82- )
79+ @Suppress(" LongParameterList" ) // Considered OK for a composable
8380@OptIn(ExperimentalMaterial3Api ::class )
8481fun MapSearchBar (
8582 query : String ,
8683 onQueryChange : (String ) -> Unit ,
8784 mapTitle : String ,
88- active : Boolean ,
89- onActiveChange : (Boolean ) -> Unit ,
85+ expanded : Boolean ,
86+ onExpandedChange : (Boolean ) -> Unit ,
9087 results : SearchResults ,
9188 onResultClick : (Int ) -> Unit ,
9289 onMenuClick : () -> Unit ,
9390 modifier : Modifier = Modifier
9491) {
95- val activationAnimationProgress by animateFloatAsState(
96- targetValue = if (active ) 1f else 0f ,
97- animationSpec = if (active) ACTIVATION_ENTER_SPEC else ACTIVATION_EXIT_SPEC ,
98- label = " Map search bar activation animation progress"
92+ val expansionAnimationProgress by animateFloatAsState(
93+ targetValue = if (expanded ) 1f else 0f ,
94+ animationSpec = if (expanded) EXPANSION_ENTER_SPEC else EXPANSION_EXIT_SPEC ,
95+ label = " Map search bar expansion animation progress"
9996 )
10097
101- val (insetsStartPadding, insetsEndPadding) = with (WindowInsets .safeDrawing.asPaddingValues()) {
102- val layoutDirection = LocalLayoutDirection .current
103- calculateStartPadding(layoutDirection) to calculateEndPadding(layoutDirection)
104- }
105-
106- val outerStartPadding = insetsStartPadding * (1 - activationAnimationProgress)
107- val outerEndPadding = insetsEndPadding * (1 - activationAnimationProgress)
108- val innerStartPadding = insetsStartPadding * activationAnimationProgress
109- val innerEndPadding = insetsEndPadding * activationAnimationProgress
110-
111- val focusManager = LocalFocusManager .current
112-
113- val containerColorAlpha =
114- ON_MAP_SURFACE_ALPHA + (1 - ON_MAP_SURFACE_ALPHA ) * activationAnimationProgress
115-
11698 SearchBar (
117- query = query,
118- onQueryChange = onQueryChange,
119- onSearch = { focusManager.clearFocus() },
120- active = active,
121- onActiveChange = onActiveChange,
122- modifier = Modifier
123- .run {
124- if (active) padding(start = outerStartPadding, end = outerEndPadding)
125- else windowInsetsPadding(WindowInsets .safeDrawing.only(WindowInsetsSides .Horizontal ))
126- }
127- .then(modifier),
128- placeholder = {
129- Text (
130- stringResource(R .string.search_on_map, mapTitle),
131- overflow = TextOverflow .Ellipsis ,
132- maxLines = 1
133- )
134- },
135- leadingIcon = {
136- AnimatedLeadingIcon (
137- active,
138- onMenuClick = onMenuClick,
139- modifier = Modifier .padding(start = innerStartPadding),
140- onNavigateBackClick = { onActiveChange(false ) }
141- )
142- },
143- trailingIcon = {
144- AnimatedTrailingIcon (
145- active,
146- query.isEmpty(),
147- onClearClick = { onQueryChange(" " ) },
148- modifier = Modifier .padding(end = innerEndPadding)
149- )
99+ inputField = {
100+ SearchBarDefaults .InputField (
101+ query = query,
102+ onQueryChange = onQueryChange,
103+ onSearch = with (LocalFocusManager .current) { { clearFocus() } },
104+ expanded = expanded,
105+ onExpandedChange = onExpandedChange,
106+ modifier = Modifier .windowInsetsPadding(
107+ WindowInsets .safeDrawing.only(WindowInsetsSides .Horizontal ) *
108+ expansionAnimationProgress
109+ ),
110+ placeholder = { Placeholder (mapTitle) },
111+ leadingIcon = {
112+ AnimatedLeadingIcon (
113+ expanded,
114+ onMenuClick = onMenuClick,
115+ onNavigateBackClick = { onExpandedChange(false ) }
116+ )
117+ },
118+ trailingIcon = {
119+ AnimatedTrailingIcon (
120+ expanded,
121+ query.isEmpty(),
122+ onClearClick = { onQueryChange(" " ) }
123+ )
124+ })
150125 },
126+ expanded = expanded,
127+ onExpandedChange = onExpandedChange,
128+ modifier = modifier,
151129 colors = SearchBarDefaults .colors(
152130 containerColor = MaterialTheme .colorScheme.surfaceVariant.copy(
153- alpha = containerColorAlpha
131+ alpha = lerp( ON_MAP_SURFACE_ALPHA , 1f , expansionAnimationProgress)
154132 )
155133 )
156134 ) {
@@ -160,22 +138,37 @@ fun MapSearchBar(
160138 results,
161139 onScroll = { onTop -> keyboard?.apply { if (onTop) show() else hide() } },
162140 onResultClick = {
163- onActiveChange (false )
141+ onExpandedChange (false )
164142 onResultClick(it)
165143 },
166144 modifier = Modifier
145+ .windowInsetsPadding(WindowInsets .safeDrawing)
167146 .padding(horizontal = DEFAULT_PADDING * 1.5f )
168- .padding(
169- start = innerStartPadding,
170- end = innerEndPadding,
171- bottom = WindowInsets .safeDrawing
172- .asPaddingValues()
173- .calculateBottomPadding()
174- )
175147 )
176148 }
177149}
178150
151+ @Composable
152+ operator fun WindowInsets.times (num : Float ): WindowInsets {
153+ val paddings = asPaddingValues(LocalDensity .current)
154+ val layoutDirection = LocalLayoutDirection .current
155+ return WindowInsets (
156+ paddings.calculateLeftPadding(layoutDirection) * num,
157+ paddings.calculateTopPadding() * num,
158+ paddings.calculateRightPadding(layoutDirection) * num,
159+ paddings.calculateBottomPadding() * num
160+ )
161+ }
162+
163+ @Composable
164+ private fun Placeholder (mapTitle : String ) {
165+ Text (
166+ stringResource(R .string.search_on_map, mapTitle),
167+ overflow = TextOverflow .Ellipsis ,
168+ maxLines = 1
169+ )
170+ }
171+
179172@Composable
180173private fun AnimatedLeadingIcon (
181174 searchBarActive : Boolean ,
0 commit comments