Fix keyboard filter bar placement issues
This commit is contained in:
parent
9b29696757
commit
eda57aea2b
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user