From acd43005dff055564dd6526eaa3f32347408be5c Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sun, 13 Nov 2022 22:59:23 +0700 Subject: [PATCH] SearchToolbar: Better physical keyboard support (#8529) Make enter keys behave like search key of on-screen keyboard --- .../kanade/presentation/components/AppBar.kt | 18 ++++++++++-------- .../presentation/manga/TrackServiceSearch.kt | 10 ++++++++-- .../settings/screen/SettingsSearchScreen.kt | 4 +++- .../eu/kanade/presentation/util/Modifier.kt | 19 +++++++++++++++++++ 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt index 63a26cbb24..440f597fa5 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt @@ -44,6 +44,7 @@ import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import eu.kanade.presentation.util.runOnEnterKeyPressed import eu.kanade.presentation.util.secondaryItemAlpha import eu.kanade.tachiyomi.R @@ -251,25 +252,26 @@ fun SearchToolbar( val keyboardController = LocalSoftwareKeyboardController.current val focusManager = LocalFocusManager.current + val searchAndClearFocus: () -> Unit = { + onSearch(searchQuery) + focusManager.clearFocus() + keyboardController?.hide() + } + BasicTextField( value = searchQuery, onValueChange = onChangeSearchQuery, modifier = Modifier .fillMaxWidth() - .focusRequester(focusRequester), + .focusRequester(focusRequester) + .runOnEnterKeyPressed(action = searchAndClearFocus), textStyle = MaterialTheme.typography.titleMedium.copy( color = MaterialTheme.colorScheme.onBackground, fontWeight = FontWeight.Normal, fontSize = 18.sp, ), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), - keyboardActions = KeyboardActions( - onSearch = { - onSearch(searchQuery) - focusManager.clearFocus() - keyboardController?.hide() - }, - ), + keyboardActions = KeyboardActions(onSearch = { searchAndClearFocus() }), singleLine = true, cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground), visualTransformation = visualTransformation, diff --git a/app/src/main/java/eu/kanade/presentation/manga/TrackServiceSearch.kt b/app/src/main/java/eu/kanade/presentation/manga/TrackServiceSearch.kt index 5c60785cd2..eeccdee6da 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/TrackServiceSearch.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/TrackServiceSearch.kt @@ -62,6 +62,7 @@ import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.MangaCover import eu.kanade.presentation.components.ScrollbarLazyColumn import eu.kanade.presentation.util.plus +import eu.kanade.presentation.util.runOnEnterKeyPressed import eu.kanade.presentation.util.secondaryItemAlpha import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.track.model.TrackSearch @@ -80,6 +81,10 @@ fun TrackServiceSearch( ) { val focusManager = LocalFocusManager.current val focusRequester = remember { FocusRequester() } + val dispatchQueryAndClearFocus: () -> Unit = { + onDispatchQuery() + focusManager.clearFocus() + } Scaffold( contentWindowInsets = WindowInsets( @@ -106,12 +111,13 @@ fun TrackServiceSearch( onValueChange = onQueryChange, modifier = Modifier .fillMaxWidth() - .focusRequester(focusRequester), + .focusRequester(focusRequester) + .runOnEnterKeyPressed(action = dispatchQueryAndClearFocus), textStyle = MaterialTheme.typography.bodyLarge .copy(color = MaterialTheme.colorScheme.onSurface), singleLine = true, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), - keyboardActions = KeyboardActions(onSearch = { focusManager.clearFocus(); onDispatchQuery() }), + keyboardActions = KeyboardActions(onSearch = { dispatchQueryAndClearFocus() }), cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), decorationBox = { if (query.text.isEmpty()) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt index 9911b368b6..2bd64fad7b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt @@ -54,6 +54,7 @@ import eu.kanade.presentation.components.Divider import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.more.settings.Preference +import eu.kanade.presentation.util.runOnEnterKeyPressed import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.isLTR @@ -108,7 +109,8 @@ class SettingsSearchScreen : Screen { onValueChange = { textFieldValue = it }, modifier = Modifier .fillMaxWidth() - .focusRequester(focusRequester), + .focusRequester(focusRequester) + .runOnEnterKeyPressed(action = focusManager::clearFocus), textStyle = MaterialTheme.typography.bodyLarge .copy(color = MaterialTheme.colorScheme.onSurface), singleLine = true, diff --git a/app/src/main/java/eu/kanade/presentation/util/Modifier.kt b/app/src/main/java/eu/kanade/presentation/util/Modifier.kt index 52b4eae570..6abf822dd8 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Modifier.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Modifier.kt @@ -10,6 +10,9 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.draw.alpha +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.layout.LayoutModifier import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult @@ -43,6 +46,22 @@ fun Modifier.clickableNoIndication( ) } +/** + * For TextField, the provided [action] will be invoked when + * physical enter key is pressed. + * + * Naturally, the TextField should be set to single line only. + */ +fun Modifier.runOnEnterKeyPressed(action: () -> Unit): Modifier = this.onPreviewKeyEvent { + when (it.key) { + Key.Enter, Key.NumPadEnter -> { + action() + true + } + else -> false + } +} + @Suppress("ModifierInspectorInfo") fun Modifier.minimumTouchTargetSize(): Modifier = composed( inspectorInfo = debugInspectorInfo {