Эх сурвалжийг харах

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
Ivan Iskandar 2 жил өмнө
parent
commit
7a1b599462

+ 1 - 0
app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt

@@ -45,6 +45,7 @@ fun BrowseSourceToolbar(
     var selectingDisplayMode by remember { mutableStateOf(false) }
 
     SearchToolbar(
+        initialShowKeyboard = searchQuery.isNullOrEmpty(),
         navigateUp = navigateUp,
         titleContent = { AppBarTitle(title) },
         searchQuery = searchQuery,

+ 13 - 17
app/src/main/java/eu/kanade/presentation/components/AppBar.kt

@@ -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 {

+ 61 - 0
presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt

@@ -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
+        }
+    }
+}