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,17 +183,11 @@ 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,
@ -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,19 +39,16 @@ 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(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceContainerLow)
.imePadding()
.height(50.dp)
) {
HorizontalDivider()
Row(
@ -234,6 +232,4 @@ fun KeyboardFilterBar(filters: SearchFilters, onFiltersChange: (SearchFilters) -
}
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,21 +75,18 @@ fun LauncherSearchBar(
else focusManager.clearFocus()
}
if (isSearchOpen && !searchVM.showFilters.value && WindowInsets.isImeVisible) {
KeyboardFilterBar(
filters = searchVM.filters.value,
onFiltersChange = {
searchVM.setFilters(it)
}
)
}
val _value = value()
Box(modifier = modifier) {
SearchBar(
modifier = modifier,
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 = reverse,
reverse = bottomSearchBar,
darkColors = darkColors,
menu = {
AnimatedVisibility(
@ -112,7 +123,7 @@ fun LauncherSearchBar(
actions = {
SearchBarActions(
actions = actions,
reverse = reverse,
reverse = bottomSearchBar,
highlightedAction = highlightedAction
)
},
@ -121,4 +132,20 @@ fun LauncherSearchBar(
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)
}
)
}
}
}