Improve search keyboard dismissal (#317)

I need to test this more

Fixes the imeAction for search

Initiating the search will move focus down if results are available

Pressing back while focused on the search text box will move focus to
either the results or the nav drawer. I think will will help devices
that do not dismiss on the soft keyboard on back.

Might fix #311
This commit is contained in:
damontecres 2025-12-01 10:44:39 -05:00 committed by GitHub
parent d0bfdac3bd
commit fc330a3400
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 10 deletions

View file

@ -53,8 +53,8 @@ fun EditTextBox(
isInputValid: (String) -> Boolean = { true }, isInputValid: (String) -> Boolean = { true },
supportingText: @Composable (() -> Unit)? = null, supportingText: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null, placeholder: @Composable (() -> Unit)? = null,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) { ) {
val interactionSource = remember { MutableInteractionSource() }
// From ButtonDefaults // From ButtonDefaults
val colors = val colors =
TextFieldDefaults.colors( TextFieldDefaults.colors(
@ -157,6 +157,7 @@ fun SearchEditTextBox(
enabled: Boolean = true, enabled: Boolean = true,
readOnly: Boolean = false, readOnly: Boolean = false,
height: Dp = 40.dp, height: Dp = 40.dp,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) { ) {
EditTextBox( EditTextBox(
value, value,
@ -171,7 +172,7 @@ fun SearchEditTextBox(
KeyboardActions( KeyboardActions(
onSearch = { onSearch = {
onSearchClick.invoke() onSearchClick.invoke()
this.defaultKeyboardAction(ImeAction.Done) this.defaultKeyboardAction(ImeAction.Search)
}, },
), ),
leadingIcon = { leadingIcon = {
@ -181,9 +182,10 @@ fun SearchEditTextBox(
tint = MaterialTheme.colorScheme.onPrimaryContainer, tint = MaterialTheme.colorScheme.onPrimaryContainer,
) )
}, },
enabled, enabled = enabled,
readOnly, readOnly = readOnly,
height, height = height,
interactionSource = interactionSource,
) )
} }

View file

@ -1,5 +1,9 @@
package com.github.damontecres.wholphin.ui.main package com.github.damontecres.wholphin.ui.main
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -18,10 +22,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
@ -157,6 +164,8 @@ fun SearchPage(
viewModel: SearchViewModel = hiltViewModel(), viewModel: SearchViewModel = hiltViewModel(),
) { ) {
val context = LocalContext.current val context = LocalContext.current
val focusManager = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
val movies by viewModel.movies.observeAsState(SearchResult.NoQuery) val movies by viewModel.movies.observeAsState(SearchResult.NoQuery)
val collections by viewModel.collections.observeAsState(SearchResult.NoQuery) val collections by viewModel.collections.observeAsState(SearchResult.NoQuery)
val series by viewModel.series.observeAsState(SearchResult.NoQuery) val series by viewModel.series.observeAsState(SearchResult.NoQuery)
@ -166,6 +175,7 @@ fun SearchPage(
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
var position by rememberPosition() var position by rememberPosition()
var searchClicked by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(query) { LaunchedEffect(query) {
delay(750L) delay(750L)
@ -177,17 +187,31 @@ fun SearchPage(
val onClickItem = { index: Int, item: BaseItem -> val onClickItem = { index: Int, item: BaseItem ->
viewModel.navigationManager.navigateTo(item.destination()) viewModel.navigationManager.navigateTo(item.destination())
} }
LaunchedEffect(searchClicked, movies, collections, series, episodes) {
if (searchClicked) {
if (listOf(movies, collections, series, episodes).any { it is SearchResult.Success }) {
focusManager.moveFocus(FocusDirection.Next)
searchClicked = false
}
}
}
LazyColumn( LazyColumn(
contentPadding = PaddingValues(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 44.dp), contentPadding = PaddingValues(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 44.dp),
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier, modifier = modifier.focusGroup(),
) { ) {
item { item {
Box( Box(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
val interactionSource = remember { MutableInteractionSource() }
val focused by interactionSource.collectIsFocusedAsState()
BackHandler(focused) {
keyboardController?.hide()
focusManager.moveFocus(FocusDirection.Next)
}
SearchEditTextBox( SearchEditTextBox(
value = query, value = query,
onValueChange = { onValueChange = {
@ -195,12 +219,15 @@ fun SearchPage(
}, },
onSearchClick = { onSearchClick = {
viewModel.search(query) viewModel.search(query)
searchClicked = true
}, },
modifier = modifier =
Modifier.ifElse( Modifier
.ifElse(
position.row < MOVIE_ROW, position.row < MOVIE_ROW,
Modifier.focusRequester(focusRequester), Modifier.focusRequester(focusRequester),
), ),
interactionSource = interactionSource,
) )
} }
} }