mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Adjust SearchToolbar soft keyboard behavior (#9282)
* Show soft keyboard when the text field is composed (a redo) * Clear focus on text field when soft keyboard is hidden * Request focus on text field and show soft keyboard when clear button is clicked
This commit is contained in:
		| @@ -45,6 +45,7 @@ fun BrowseSourceToolbar( | ||||
|     var selectingDisplayMode by remember { mutableStateOf(false) } | ||||
|  | ||||
|     SearchToolbar( | ||||
|         initialShowKeyboard = searchQuery.isNullOrEmpty(), | ||||
|         navigateUp = navigateUp, | ||||
|         titleContent = { AppBarTitle(title) }, | ||||
|         searchQuery = searchQuery, | ||||
|   | ||||
| @@ -23,7 +23,6 @@ import androidx.compose.material3.TopAppBarDefaults | ||||
| import androidx.compose.material3.TopAppBarScrollBehavior | ||||
| import androidx.compose.material3.surfaceColorAtElevation | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.LaunchedEffect | ||||
| import androidx.compose.runtime.derivedStateOf | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.key | ||||
| @@ -45,8 +44,10 @@ import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import eu.kanade.tachiyomi.R | ||||
| import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide | ||||
| import tachiyomi.presentation.core.util.runOnEnterKeyPressed | ||||
| import tachiyomi.presentation.core.util.secondaryItemAlpha | ||||
| import tachiyomi.presentation.core.util.showSoftKeyboard | ||||
|  | ||||
| const val SEARCH_DEBOUNCE_MILLIS = 250L | ||||
|  | ||||
| @@ -231,9 +232,9 @@ fun SearchToolbar( | ||||
|     scrollBehavior: TopAppBarScrollBehavior? = null, | ||||
|     visualTransformation: VisualTransformation = VisualTransformation.None, | ||||
|     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, | ||||
|     initialShowKeyboard: Boolean = true, | ||||
| ) { | ||||
|     val focusRequester = remember { FocusRequester() } | ||||
|     var searchClickCount by remember { mutableStateOf(0) } | ||||
|  | ||||
|     AppBar( | ||||
|         titleContent = { | ||||
| @@ -255,7 +256,9 @@ fun SearchToolbar( | ||||
|                 modifier = Modifier | ||||
|                     .fillMaxWidth() | ||||
|                     .focusRequester(focusRequester) | ||||
|                     .runOnEnterKeyPressed(action = searchAndClearFocus), | ||||
|                     .runOnEnterKeyPressed(action = searchAndClearFocus) | ||||
|                     .showSoftKeyboard(initialShowKeyboard) | ||||
|                     .clearFocusOnSoftKeyboardHide(), | ||||
|                 textStyle = MaterialTheme.typography.titleMedium.copy( | ||||
|                     color = MaterialTheme.colorScheme.onBackground, | ||||
|                     fontWeight = FontWeight.Normal, | ||||
| @@ -294,10 +297,7 @@ fun SearchToolbar( | ||||
|         navigateUp = if (searchQuery == null) navigateUp else onClickCloseSearch, | ||||
|         actions = { | ||||
|             key("search") { | ||||
|                 val onClick = { | ||||
|                     searchClickCount++ | ||||
|                     onChangeSearchQuery("") | ||||
|                 } | ||||
|                 val onClick = { onChangeSearchQuery("") } | ||||
|  | ||||
|                 if (!searchEnabled) { | ||||
|                     // Don't show search action | ||||
| @@ -306,7 +306,12 @@ fun SearchToolbar( | ||||
|                         Icon(Icons.Outlined.Search, contentDescription = stringResource(R.string.action_search)) | ||||
|                     } | ||||
|                 } else if (searchQuery.isNotEmpty()) { | ||||
|                     IconButton(onClick) { | ||||
|                     IconButton( | ||||
|                         onClick = { | ||||
|                             onClick() | ||||
|                             focusRequester.requestFocus() | ||||
|                         }, | ||||
|                     ) { | ||||
|                         Icon(Icons.Outlined.Close, contentDescription = stringResource(R.string.action_reset)) | ||||
|                     } | ||||
|                 } | ||||
| @@ -317,15 +322,6 @@ fun SearchToolbar( | ||||
|         isActionMode = false, | ||||
|         scrollBehavior = scrollBehavior, | ||||
|     ) | ||||
|     LaunchedEffect(searchClickCount) { | ||||
|         if (searchQuery == null) return@LaunchedEffect | ||||
|         if (searchClickCount == 0 && searchQuery.isNotEmpty()) return@LaunchedEffect | ||||
|         try { | ||||
|             focusRequester.requestFocus() | ||||
|         } catch (_: Throwable) { | ||||
|             // TextField is gone | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| sealed interface AppBar { | ||||
|   | ||||
| @@ -4,14 +4,25 @@ import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.combinedClickable | ||||
| import androidx.compose.foundation.interaction.MutableInteractionSource | ||||
| import androidx.compose.foundation.isSystemInDarkTheme | ||||
| import androidx.compose.foundation.layout.WindowInsets | ||||
| import androidx.compose.foundation.layout.isImeVisible | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.runtime.LaunchedEffect | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.saveable.rememberSaveable | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.composed | ||||
| import androidx.compose.ui.draw.alpha | ||||
| import androidx.compose.ui.focus.FocusRequester | ||||
| import androidx.compose.ui.focus.focusRequester | ||||
| import androidx.compose.ui.focus.onFocusChanged | ||||
| 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.platform.LocalFocusManager | ||||
| import tachiyomi.presentation.core.components.material.SecondaryItemAlpha | ||||
|  | ||||
| fun Modifier.selectedBackground(isSelected: Boolean): Modifier = composed { | ||||
| @@ -52,3 +63,53 @@ fun Modifier.runOnEnterKeyPressed(action: () -> Unit): Modifier = this.onPreview | ||||
|         else -> false | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * For TextField on AppBar, this modifier will request focus | ||||
|  * to the element the first time it's composed. | ||||
|  */ | ||||
| fun Modifier.showSoftKeyboard(show: Boolean): Modifier = if (show) { | ||||
|     composed { | ||||
|         val focusRequester = remember { FocusRequester() } | ||||
|         var openKeyboard by rememberSaveable { mutableStateOf(show) } | ||||
|         LaunchedEffect(focusRequester) { | ||||
|             if (openKeyboard) { | ||||
|                 focusRequester.requestFocus() | ||||
|                 openKeyboard = false | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Modifier.focusRequester(focusRequester) | ||||
|     } | ||||
| } else { | ||||
|     this | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * For TextField, this modifier will clear focus when soft | ||||
|  * keyboard is hidden. | ||||
|  */ | ||||
| fun Modifier.clearFocusOnSoftKeyboardHide(): Modifier = composed { | ||||
|     var isFocused by remember { mutableStateOf(false) } | ||||
|     var keyboardShowedSinceFocused by remember { mutableStateOf(false) } | ||||
|     if (isFocused) { | ||||
|         val imeVisible = WindowInsets.isImeVisible | ||||
|         val focusManager = LocalFocusManager.current | ||||
|         LaunchedEffect(imeVisible) { | ||||
|             if (imeVisible) { | ||||
|                 keyboardShowedSinceFocused = true | ||||
|             } else if (keyboardShowedSinceFocused) { | ||||
|                 focusManager.clearFocus() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Modifier.onFocusChanged { | ||||
|         if (isFocused != it.isFocused) { | ||||
|             if (isFocused) { | ||||
|                 keyboardShowedSinceFocused = false | ||||
|             } | ||||
|             isFocused = it.isFocused | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user