Add keyboard navigation logic for password fields to handle the tab button (#1365)

This commit is contained in:
David Perez 2024-05-13 13:28:27 -05:00 committed by Álison Fernandes
parent 1ede84d22c
commit 829a05a598
3 changed files with 39 additions and 1 deletions

View file

@ -10,8 +10,16 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.draw.scale import androidx.compose.ui.draw.scale
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.isShiftPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
@ -87,3 +95,28 @@ fun Modifier.mirrorIfRtl(): Modifier =
} else { } else {
this this
} }
/**
* This is a [Modifier] extension for ensuring that the tab button navigates properly when using
* a physical keyboard.
*/
@OmitFromCoverage
@Stable
@Composable
fun Modifier.tabNavigation(): Modifier {
val focusManager = LocalFocusManager.current
return onPreviewKeyEvent { keyEvent ->
if (keyEvent.key == Key.Tab && keyEvent.type == KeyEventType.KeyDown) {
focusManager.moveFocus(
if (keyEvent.isShiftPressed) {
FocusDirection.Previous
} else {
FocusDirection.Next
},
)
true
} else {
false
}
}
}

View file

@ -24,6 +24,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import com.x8bit.bitwarden.R import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.mirrorIfRtl import com.x8bit.bitwarden.ui.platform.base.util.mirrorIfRtl
import com.x8bit.bitwarden.ui.platform.base.util.tabNavigation
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
/** /**
@ -91,6 +92,7 @@ fun BitwardenSearchTopAppBar(
}, },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
modifier = Modifier modifier = Modifier
.tabNavigation()
.focusRequester(focusRequester) .focusRequester(focusRequester)
.fillMaxWidth(), .fillMaxWidth(),
) )

View file

@ -27,6 +27,7 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import com.x8bit.bitwarden.R import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.tabNavigation
import com.x8bit.bitwarden.ui.platform.components.util.nonLetterColorVisualTransformation import com.x8bit.bitwarden.ui.platform.components.util.nonLetterColorVisualTransformation
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
@ -70,7 +71,9 @@ fun BitwardenPasswordField(
) { ) {
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
OutlinedTextField( OutlinedTextField(
modifier = modifier.focusRequester(focusRequester), modifier = modifier
.tabNavigation()
.focusRequester(focusRequester),
textStyle = MaterialTheme.typography.bodyLarge, textStyle = MaterialTheme.typography.bodyLarge,
label = { Text(text = label) }, label = { Text(text = label) },
value = value, value = value,