Fix keyboard filter bar placement issues

This commit is contained in:
MM20 2024-04-21 19:03:35 +02:00
parent 9b29696757
commit eda57aea2b
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
5 changed files with 307 additions and 299 deletions

View File

@ -41,6 +41,7 @@ fun AssistantScaffold(
fixedSearchBar: Boolean = false,
) {
val viewModel: LauncherScaffoldVM = viewModel()
val searchVM: SearchVM = viewModel()
val context = LocalContext.current
@ -60,6 +61,10 @@ fun AssistantScaffold(
val searchState = rememberLazyListState()
val keyboardFilterBarPadding by animateDpAsState(
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value) 50.dp else 0.dp
)
val isSearchAtStart by remember {
derivedStateOf {
searchState.firstVisibleItemIndex == 0 && searchState.firstVisibleItemScrollOffset == 0
@ -94,6 +99,7 @@ fun AssistantScaffold(
}
}
}
val showNavBarScrim by remember {
derivedStateOf {
if (reverseSearchResults) {
@ -148,8 +154,6 @@ fun AssistantScaffold(
}
}
}
val searchVM: SearchVM = viewModel()
val actions by searchVM.searchActionResults
val webSearchPadding by animateDpAsState(
if (actions.isEmpty()) 0.dp else 48.dp
@ -164,7 +168,7 @@ fun AssistantScaffold(
modifier = Modifier.fillMaxSize(),
paddingValues = PaddingValues(
top = (if (bottomSearchBar) 0.dp else 56.dp + webSearchPadding) + 4.dp + windowInsets.calculateTopPadding(),
bottom = (if (bottomSearchBar) 56.dp + webSearchPadding else 0.dp) + 4.dp + windowInsets.calculateBottomPadding()
bottom = (if (bottomSearchBar) 56.dp + webSearchPadding else 0.dp) + 4.dp + windowInsets.calculateBottomPadding() + keyboardFilterBarPadding
),
reverse = reverseSearchResults,
state = searchState
@ -179,18 +183,12 @@ fun AssistantScaffold(
LauncherSearchBar(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.align(if (bottomSearchBar) Alignment.BottomCenter else Alignment.TopCenter)
.windowInsetsPadding(WindowInsets.safeDrawing)
.padding(8.dp)
.offset {
if (searchBarFocused || fixedSearchBar) IntOffset.Zero
else IntOffset(
0,
searchBarOffset.toInt() * if (bottomSearchBar) -1 else 1
)
},
.fillMaxSize(),
searchBarOffset = {
(if (searchBarFocused || fixedSearchBar) 0
else searchBarOffset.toInt() * if (bottomSearchBar) -1 else 1)
- (if (bottomSearchBar) with(density) { keyboardFilterBarPadding.toPx() }.toInt() else 0)
},
level = { searchBarLevel },
focused = searchBarFocused,
onFocusChange = {
@ -204,7 +202,7 @@ fun AssistantScaffold(
onValueChange = { searchVM.search(it) },
darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark,
style = searchBarStyle,
reverse = bottomSearchBar,
bottomSearchBar = bottomSearchBar,
onKeyboardActionGo = if (launchOnEnter) {
{ searchVM.launchBestMatchOrAction(context) }
} else null

View File

@ -24,7 +24,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.safeDrawing
@ -131,6 +131,10 @@ fun PagerScaffold(
val pagerState = rememberPagerState { 2 }
val keyboardFilterBarPadding by animateDpAsState(
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value) 50.dp else 0.dp
)
val isSearchAtBottom by remember {
derivedStateOf {
if (reverseSearchResults) {
@ -511,11 +515,11 @@ fun PagerScaffold(
val paddingValues = if (bottomSearchBar) {
PaddingValues(
top = 4.dp + windowInsets.calculateTopPadding(),
bottom = 60.dp + webSearchPadding + windowInsets.calculateBottomPadding()
bottom = 60.dp + webSearchPadding + windowInsets.calculateBottomPadding() + keyboardFilterBarPadding
)
} else {
PaddingValues(
bottom = 4.dp + windowInsets.calculateBottomPadding(),
bottom = 4.dp + windowInsets.calculateBottomPadding() + keyboardFilterBarPadding,
top = 60.dp + webSearchPadding + windowInsets.calculateTopPadding()
)
}
@ -597,27 +601,17 @@ fun PagerScaffold(
LauncherSearchBar(
modifier = Modifier
.align(if (bottomSearchBar) Alignment.BottomCenter else Alignment.TopCenter)
.fillMaxWidth()
.wrapContentHeight()
.windowInsetsPadding(WindowInsets.safeDrawing)
.padding(8.dp)
.offset {
IntOffset(
0,
if (focusSearchBar || fixedSearchBar) 0 else searchBarOffset.value.toInt() * if (bottomSearchBar) 1 else -1
)
}
.offset {
IntOffset(
0,
.fillMaxSize(),
level = { searchBarLevel },
searchBarOffset = {
(if (focusSearchBar || fixedSearchBar) 0 else searchBarOffset.value.toInt() * if (bottomSearchBar) 1 else -1) +
with(density) {
widgetEditModeOffset
(widgetEditModeOffset - if (bottomSearchBar) keyboardFilterBarPadding else 0.dp)
.toPx()
.roundToInt()
})
},
level = { searchBarLevel },
}
},
bottomSearchBar = bottomSearchBar,
focused = focusSearchBar,
onFocusChange = {
if (it) viewModel.openSearch()
@ -630,7 +624,6 @@ fun PagerScaffold(
onValueChange = { searchVM.search(it) },
darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark,
style = searchBarStyle,
reverse = bottomSearchBar,
onKeyboardActionGo = if (launchOnEnter) {
{ searchVM.launchBestMatchOrAction(context) }
} else null
@ -702,7 +695,8 @@ fun Modifier.pagerScaffoldScrollHandler(
val available = dragAmount - preConsumed
val consumedY =
scrollableState.scrollBy(available.y * scrollMultiplier) * scrollMultiplier
val consumedX = pagerState.scrollBy(available.x * pagerMultiplier) * pagerMultiplier
val consumedX =
pagerState.scrollBy(available.x * pagerMultiplier) * pagerMultiplier
val totalConsumed =
Offset(preConsumed.x + consumedX, preConsumed.y + consumedY)
nestedScrollDispatcher.dispatchPostScroll(

View File

@ -21,11 +21,11 @@ import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.VerticalPager
import androidx.compose.foundation.pager.rememberPagerState
@ -116,6 +116,10 @@ fun PullDownScaffold(
val maxOffset = with(density) { 64.dp.toPx() }
val toggleSearchThreshold = with(density) { 48.dp.toPx() }
val keyboardFilterBarPadding by animateDpAsState(
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value) 50.dp else 0.dp
)
val isSearchAtTop by remember {
derivedStateOf {
if (reverseSearchResults) {
@ -300,9 +304,7 @@ fun PullDownScaffold(
handleBackOrHomeEvent()
}
val keyboardController = LocalSoftwareKeyboardController.current
val gestureManager = LocalGestureDetector.current
val hapticFeedback = LocalHapticFeedback.current
val view = LocalView.current
LaunchedEffect(isOverThreshold) {
@ -517,7 +519,9 @@ fun PullDownScaffold(
),
paddingValues = PaddingValues(
top = windowInsets.calculateTopPadding() + if (!bottomSearchBar) 60.dp + webSearchPadding else 4.dp,
bottom = windowInsets.calculateBottomPadding() + if (bottomSearchBar) 60.dp + webSearchPadding else 4.dp
bottom = windowInsets.calculateBottomPadding() +
keyboardFilterBarPadding +
if (bottomSearchBar) 60.dp + webSearchPadding else 4.dp
),
state = searchState,
reverse = reverseSearchResults,
@ -575,32 +579,21 @@ fun PullDownScaffold(
LauncherSearchBar(
modifier = Modifier
.align(if (bottomSearchBar) Alignment.BottomCenter else Alignment.TopCenter)
.fillMaxWidth()
.wrapContentHeight()
.windowInsetsPadding(WindowInsets.safeDrawing)
.padding(8.dp)
.offset {
IntOffset(
0,
if (searchBarFocused || fixedSearchBar) 0 else searchBarOffset.value.toInt() * (if (bottomSearchBar) 1 else -1)
)
}
.offset {
IntOffset(
0,
with(density) {
editModeSearchBarOffset
.toPx()
.roundToInt()
})
},
.fillMaxSize(),
level = { searchBarLevel },
focused = searchBarFocused,
onFocusChange = {
if (it) viewModel.openSearch()
viewModel.setSearchbarFocus(it)
},
searchBarOffset = {
(if (searchBarFocused || fixedSearchBar) 0 else searchBarOffset.value.toInt() * (if (bottomSearchBar) 1 else -1)) +
with(density) {
(editModeSearchBarOffset - if(bottomSearchBar) keyboardFilterBarPadding else 0.dp)
.toPx()
.roundToInt()
}
},
actions = actions,
highlightedAction = searchVM.bestMatch.value as? SearchAction,
isSearchOpen = isSearchOpen,
@ -608,7 +601,7 @@ fun PullDownScaffold(
onValueChange = { searchVM.search(it) },
darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark,
style = searchBarStyle,
reverse = bottomSearchBar,
bottomSearchBar = bottomSearchBar,
onKeyboardActionGo = if (launchOnEnter) {
{ searchVM.launchBestMatchOrAction(context) }
} else null

View File

@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.launcher.search.filters
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
@ -38,202 +39,197 @@ import androidx.compose.ui.unit.dp
import de.mm20.launcher2.search.SearchFilters
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.icons.Wikipedia
import de.mm20.launcher2.ui.overlays.Overlay
@Composable
fun KeyboardFilterBar(filters: SearchFilters, onFiltersChange: (SearchFilters) -> Unit) {
Overlay {
val allCategoriesEnabled = filters.allCategoriesEnabled
Box(modifier = Modifier
.fillMaxSize()
.imePadding(), contentAlignment = Alignment.BottomCenter) {
Column(
val allCategoriesEnabled = filters.allCategoriesEnabled
Column(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceContainerLow)
.imePadding()
.height(50.dp)
) {
HorizontalDivider()
Row(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
FilterChip(
selected = filters.allowNetwork,
onClick = {
onFiltersChange(filters.copy(allowNetwork = !filters.allowNetwork))
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Language,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.search_filter_online)) }
)
VerticalDivider(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceContainerLow)
) {
HorizontalDivider()
Row(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
FilterChip(
selected = filters.allowNetwork,
onClick = {
onFiltersChange(filters.copy(allowNetwork = !filters.allowNetwork))
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Language,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.search_filter_online)) }
.height(36.dp)
.padding(horizontal = 8.dp)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.apps && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleApps())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Apps,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
VerticalDivider(
modifier = Modifier
.height(36.dp)
.padding(horizontal = 8.dp)
},
label = { Text(stringResource(R.string.search_filter_apps)) }
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.files && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleFiles())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Description,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.apps && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleApps())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Apps,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.search_filter_apps)) }
},
label = { Text(stringResource(R.string.preference_search_files)) }
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.contacts && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleContacts())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Person,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.files && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleFiles())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Description,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.preference_search_files)) }
},
label = { Text(stringResource(R.string.preference_search_contacts)) }
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.events && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleEvents())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Today,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.contacts && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleContacts())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Person,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.preference_search_contacts)) }
},
label = { Text(stringResource(R.string.preference_search_calendar)) }
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.shortcuts && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleShortcuts())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.AppShortcut,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.events && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleEvents())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Today,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.preference_search_calendar)) }
},
label = { Text(stringResource(R.string.preference_search_appshortcuts)) }
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.articles && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleArticles())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Wikipedia,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.shortcuts && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleShortcuts())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.AppShortcut,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.preference_search_appshortcuts)) }
},
label = { Text(stringResource(R.string.preference_search_wikipedia)) }
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.websites && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleWebsites())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Public,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.articles && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleArticles())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Wikipedia,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.preference_search_wikipedia)) }
},
label = { Text(stringResource(R.string.preference_search_websites)) }
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.places && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.togglePlaces())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Place,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.websites && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleWebsites())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Public,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.preference_search_websites)) }
},
label = { Text(stringResource(R.string.preference_search_locations)) }
)
FilterChip(
selected = filters.tools && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleTools())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Handyman,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
modifier = Modifier.padding(end = 8.dp),
selected = filters.places && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.togglePlaces())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Place,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.preference_search_locations)) }
},
label = { Text(stringResource(R.string.search_filter_tools)) }
)
VerticalDivider(
modifier = Modifier
.height(36.dp)
.padding(horizontal = 8.dp)
)
FilterChip(
selected = filters.hiddenItems,
onClick = {
onFiltersChange(filters.copy(hiddenItems = !filters.hiddenItems))
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.VisibilityOff,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
FilterChip(
selected = filters.tools && !allCategoriesEnabled,
onClick = {
onFiltersChange(filters.toggleTools())
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.Handyman,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.search_filter_tools)) }
)
VerticalDivider(
modifier = Modifier
.height(36.dp)
.padding(horizontal = 8.dp)
)
FilterChip(
selected = filters.hiddenItems,
onClick = {
onFiltersChange(filters.copy(hiddenItems = !filters.hiddenItems))
},
leadingIcon = {
Icon(
imageVector = Icons.Rounded.VisibilityOff,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
},
label = { Text(stringResource(R.string.preference_hidden_items)) }
)
}
HorizontalDivider()
}
},
label = { Text(stringResource(R.string.preference_hidden_items)) }
)
}
HorizontalDivider()
}
}

View File

@ -2,12 +2,23 @@ package de.mm20.launcher2.ui.launcher.searchbar
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.layout.isImeVisible
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.text.KeyboardActionScope
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.FilterAlt
@ -24,7 +35,9 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.preferences.SearchBarStyle
@ -47,8 +60,9 @@ fun LauncherSearchBar(
actions: List<SearchAction>,
highlightedAction: SearchAction?,
isSearchOpen: Boolean = false,
reverse: Boolean = false,
darkColors: Boolean = false,
bottomSearchBar: Boolean = false,
searchBarOffset: () -> Int = { 0 },
onKeyboardActionGo: (KeyboardActionScope.() -> Unit)? = null
) {
val focusManager = LocalFocusManager.current
@ -61,64 +75,77 @@ fun LauncherSearchBar(
else focusManager.clearFocus()
}
if (isSearchOpen && !searchVM.showFilters.value && WindowInsets.isImeVisible) {
KeyboardFilterBar(
filters = searchVM.filters.value,
onFiltersChange = {
searchVM.setFilters(it)
}
)
}
val _value = value()
SearchBar(
modifier = modifier,
style = style, level = level(), value = _value, onValueChange = onValueChange,
reverse = reverse,
darkColors = darkColors,
menu = {
AnimatedVisibility(
isSearchOpen,
enter = scaleIn(tween(100)),
exit = scaleOut(tween(100))
) {
FilledIconButton(
onClick = {
searchVM.showFilters.value = !searchVM.showFilters.value
},
colors = if (searchVM.showFilters.value) IconButtonDefaults.filledTonalIconButtonColors()
else IconButtonDefaults.iconButtonColors()
Box(modifier = modifier) {
SearchBar(
modifier = Modifier
.align(if (bottomSearchBar) Alignment.BottomCenter else Alignment.TopCenter)
.windowInsetsPadding(WindowInsets.safeDrawing)
.padding(8.dp)
.offset { IntOffset(0, searchBarOffset()) },
style = style, level = level(), value = _value, onValueChange = onValueChange,
reverse = bottomSearchBar,
darkColors = darkColors,
menu = {
AnimatedVisibility(
isSearchOpen,
enter = scaleIn(tween(100)),
exit = scaleOut(tween(100))
) {
Box {
Icon(imageVector = Icons.Rounded.FilterAlt, contentDescription = null)
androidx.compose.animation.AnimatedVisibility(
!searchVM.filters.value.allCategoriesEnabled,
enter = scaleIn(tween(100)),
exit = scaleOut(tween(100)),
modifier = Modifier
.align(Alignment.BottomEnd)
.offset(-3.dp, -3.dp)
) {
Badge(
containerColor = MaterialTheme.colorScheme.tertiary,
)
FilledIconButton(
onClick = {
searchVM.showFilters.value = !searchVM.showFilters.value
},
colors = if (searchVM.showFilters.value) IconButtonDefaults.filledTonalIconButtonColors()
else IconButtonDefaults.iconButtonColors()
) {
Box {
Icon(imageVector = Icons.Rounded.FilterAlt, contentDescription = null)
androidx.compose.animation.AnimatedVisibility(
!searchVM.filters.value.allCategoriesEnabled,
enter = scaleIn(tween(100)),
exit = scaleOut(tween(100)),
modifier = Modifier
.align(Alignment.BottomEnd)
.offset(-3.dp, -3.dp)
) {
Badge(
containerColor = MaterialTheme.colorScheme.tertiary,
)
}
}
}
}
}
SearchBarMenu(searchBarValue = _value, onSearchBarValueChange = onValueChange)
},
actions = {
SearchBarActions(
actions = actions,
reverse = reverse,
highlightedAction = highlightedAction
SearchBarMenu(searchBarValue = _value, onSearchBarValueChange = onValueChange)
},
actions = {
SearchBarActions(
actions = actions,
reverse = bottomSearchBar,
highlightedAction = highlightedAction
)
},
focusRequester = focusRequester,
onFocus = { onFocusChange(true) },
onUnfocus = { onFocusChange(false) },
onKeyboardActionGo = onKeyboardActionGo
)
AnimatedVisibility (isSearchOpen && !searchVM.showFilters.value
// Use imeAnimationTarget instead of isImeVisible to animate the filter bar at the same time as the keyboard
&& WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0,
enter = fadeIn(),
exit = fadeOut(),
modifier = Modifier.align(Alignment.BottomCenter)
) {
KeyboardFilterBar(
filters = searchVM.filters.value,
onFiltersChange = {
searchVM.setFilters(it)
}
)
},
focusRequester = focusRequester,
onFocus = { onFocusChange(true) },
onUnfocus = { onFocusChange(false) },
onKeyboardActionGo = onKeyboardActionGo
)
}
}
}