From eda57aea2b81e9b0b303bf4f67a827fd5754979c Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Sun, 21 Apr 2024 19:03:35 +0200 Subject: [PATCH] Fix keyboard filter bar placement issues --- .../ui/assistant/AssistantScaffold.kt | 30 +- .../launcher2/ui/launcher/PagerScaffold.kt | 40 +- .../launcher2/ui/launcher/PullDownScaffold.kt | 43 +-- .../search/filters/KeyboardFilterBar.kt | 360 +++++++++--------- .../launcher/searchbar/LauncherSearchBar.kt | 133 ++++--- 5 files changed, 307 insertions(+), 299 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt index 4d6bd4af..5de8dd4c 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt @@ -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 diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt index 16c23c51..5c55cc13 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt @@ -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( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt index 09814745..3c34f8f6 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt @@ -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 diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/KeyboardFilterBar.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/KeyboardFilterBar.kt index 3180d036..a299dcfc 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/KeyboardFilterBar.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/KeyboardFilterBar.kt @@ -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() } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt index bbec3ae8..05d24f80 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt @@ -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, 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 - ) + } + } } \ No newline at end of file