|
|
|
@ -309,3 +309,74 @@ fun main() = application {
|
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
<img alt="reverse-order" src="focus-switcher.gif" height="480" /> |
|
|
|
|
|
|
|
|
|
## Known problems |
|
|
|
|
|
|
|
|
|
### Tab key navigation doesn't work in a multiline TextField |
|
|
|
|
``` Kotlin |
|
|
|
|
Column { |
|
|
|
|
repeat(5) { |
|
|
|
|
var text by remember { mutableStateOf("Hello, World!") } |
|
|
|
|
|
|
|
|
|
OutlinedTextField( |
|
|
|
|
value = text, |
|
|
|
|
singleLine = false, // Pay attention here! Also, by default, singleLine is false. |
|
|
|
|
onValueChange = { text = it }, |
|
|
|
|
modifier = Modifier.padding(8.dp) |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
When the user presses the 'Tab' key, the focus doesn't switch to the next focusable component. Instead the Tab character is added. |
|
|
|
|
|
|
|
|
|
#### A possible workaround |
|
|
|
|
|
|
|
|
|
This workaround is mentioned in [Issues/109](https://github.com/JetBrains/compose-jb/issues/109#issuecomment-1161705265). |
|
|
|
|
Write a custom Modifier.moveFocusOnTab: |
|
|
|
|
```Kotlin |
|
|
|
|
import androidx.compose.foundation.layout.Column |
|
|
|
|
import androidx.compose.foundation.layout.padding |
|
|
|
|
import androidx.compose.material.OutlinedTextField |
|
|
|
|
import androidx.compose.runtime.getValue |
|
|
|
|
import androidx.compose.runtime.mutableStateOf |
|
|
|
|
import androidx.compose.runtime.remember |
|
|
|
|
import androidx.compose.runtime.setValue |
|
|
|
|
import androidx.compose.ui.ExperimentalComposeUiApi |
|
|
|
|
import androidx.compose.ui.Modifier |
|
|
|
|
import androidx.compose.ui.composed |
|
|
|
|
import androidx.compose.ui.focus.FocusDirection |
|
|
|
|
import androidx.compose.ui.input.key.* |
|
|
|
|
import androidx.compose.ui.platform.LocalFocusManager |
|
|
|
|
import androidx.compose.ui.unit.dp |
|
|
|
|
import androidx.compose.ui.window.singleWindowApplication |
|
|
|
|
|
|
|
|
|
fun main() = singleWindowApplication { |
|
|
|
|
Column { |
|
|
|
|
repeat(5) { |
|
|
|
|
var text by remember { mutableStateOf("Hello, World!") } |
|
|
|
|
|
|
|
|
|
OutlinedTextField( |
|
|
|
|
value = text, |
|
|
|
|
singleLine = false, // Pay attention here! Also, by default, singleLine is false. |
|
|
|
|
onValueChange = { text = it }, |
|
|
|
|
modifier = Modifier.padding(8.dp).moveFocusOnTab() |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalComposeUiApi::class) |
|
|
|
|
fun Modifier.moveFocusOnTab() = composed { |
|
|
|
|
val focusManager = LocalFocusManager.current |
|
|
|
|
onPreviewKeyEvent { |
|
|
|
|
if (it.type == KeyEventType.KeyDown && it.key == Key.Tab) { |
|
|
|
|
focusManager.moveFocus( |
|
|
|
|
if (it.isShiftPressed) FocusDirection.Previous else FocusDirection.Next |
|
|
|
|
) |
|
|
|
|
true |
|
|
|
|
} else { |
|
|
|
|
false |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|