From 808ac6592863a9a08c4fc62bd483acae8353a91e Mon Sep 17 00:00:00 2001 From: Martin Felber <45291671+FelberMartin@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:37:24 +0100 Subject: [PATCH] Improve TextField IME actions (#88) --- .../CustomInstanceSelectionScreen.kt | 1 + .../feature/login/login/CommonLoginUi.kt | 7 +++-- .../login/login_options/PasswordBasedLogin.kt | 5 ++++ .../feature/login/register/RegisterUi.kt | 29 ++++++++++++------- .../ui/reply/MarkdownTextField.kt | 3 -- .../PotentiallyIllegalTextField.kt | 3 +- .../members/ConversationMembersBody.kt | 1 + .../short_answer/ShortAnswerQuizQuestionUi.kt | 9 ++++-- .../quiz/screens/WaitForQuizStartScreen.kt | 9 +++--- 9 files changed, 44 insertions(+), 23 deletions(-) diff --git a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/custom_instance_selection/CustomInstanceSelectionScreen.kt b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/custom_instance_selection/CustomInstanceSelectionScreen.kt index f33653a09..d3551fa2b 100644 --- a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/custom_instance_selection/CustomInstanceSelectionScreen.kt +++ b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/custom_instance_selection/CustomInstanceSelectionScreen.kt @@ -106,6 +106,7 @@ internal fun CustomInstanceSelectionScreen( modifier = Modifier.fillMaxWidth(), value = enteredUrl, onValueChange = viewModel::updateServerUrl, + singleLine = true, textStyle = MaterialTheme.typography.bodyLarge, label = { Text(text = stringResource(id = R.string.account_select_custom_instance_selection_text_field_label)) } ) diff --git a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/login/CommonLoginUi.kt b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/login/CommonLoginUi.kt index b9bd28efd..7efcfa68e 100644 --- a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/login/CommonLoginUi.kt +++ b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/login/CommonLoginUi.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation @@ -33,7 +34,8 @@ fun PasswordTextField( password: String, label: String, updatePassword: (String) -> Unit, - isError: Boolean = false + isError: Boolean = false, + imeAction: ImeAction = ImeAction.Done ) { var showPasswordPlaintext by rememberSaveable { mutableStateOf(false) } val visualTransformation = remember(showPasswordPlaintext) { @@ -59,7 +61,8 @@ fun PasswordTextField( }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Password, - autoCorrect = false + autoCorrect = false, + imeAction = imeAction ), isError = isError ) diff --git a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/login/login_options/PasswordBasedLogin.kt b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/login/login_options/PasswordBasedLogin.kt index 8947ca811..5a9866b97 100644 --- a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/login/login_options/PasswordBasedLogin.kt +++ b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/login/login_options/PasswordBasedLogin.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text @@ -22,6 +23,7 @@ import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalAutofill import androidx.compose.ui.platform.LocalAutofillTree import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import de.tum.informatics.www1.artemis.native_app.feature.login.R import de.tum.informatics.www1.artemis.native_app.feature.login.login.PasswordTextField @@ -72,6 +74,9 @@ internal fun PasswordBasedLogin( .then(createAutofillModifier(autofillNode)), value = username, onValueChange = updateUsername, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Next + ), label = { Text(text = stringResource(id = R.string.login_username_label)) } ) } diff --git a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/register/RegisterUi.kt b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/register/RegisterUi.kt index 6cd4716c3..93dcbb6b2 100644 --- a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/register/RegisterUi.kt +++ b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/register/RegisterUi.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Divider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -18,6 +19,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import de.tum.informatics.www1.artemis.native_app.core.data.DataState import de.tum.informatics.www1.artemis.native_app.core.model.server_config.ProfileInfo @@ -101,7 +104,6 @@ internal fun RegisterUi( onValueChange = viewModel::updateFirstName, label = stringResource(id = R.string.register_label_first_name), errorText = firstNameStatus.localizedErrorString, - isPassword = false ) ErrorTextField( @@ -110,7 +112,6 @@ internal fun RegisterUi( onValueChange = viewModel::updateLastName, label = stringResource(id = R.string.register_label_last_name), errorText = lastNameStatus.localizedErrorString, - isPassword = false ) ErrorTextField( @@ -119,7 +120,6 @@ internal fun RegisterUi( onValueChange = viewModel::updateUsername, label = stringResource(id = R.string.register_label_username), errorText = usernameStatus.localizedErrorString, - isPassword = false ) Divider(modifier = dividerModifier) @@ -138,7 +138,7 @@ internal fun RegisterUi( onValueChange = viewModel::updateEmail, label = stringResource(id = R.string.register_label_email), errorText = emailStatus.localizedErrorString, - isPassword = false + keyboardType = KeyboardType.Email ) Divider(modifier = dividerModifier) @@ -149,7 +149,7 @@ internal fun RegisterUi( onValueChange = viewModel::updatePassword, label = stringResource(id = R.string.register_label_password), errorText = passwordStatus.localizedErrorString, - isPassword = true + keyboardType = KeyboardType.Password ) ErrorTextField( @@ -158,7 +158,8 @@ internal fun RegisterUi( onValueChange = viewModel::updateConfirmPassword, label = stringResource(id = R.string.register_label_confirm_password), errorText = confirmPasswordStatus.localizedErrorString, - isPassword = true + keyboardType = KeyboardType.Password, + isLastTextField = true ) ButtonWithLoadingAnimation( @@ -201,15 +202,19 @@ private fun ErrorTextField( onValueChange: (String) -> Unit, label: String, errorText: String?, - isPassword: Boolean + keyboardType: KeyboardType = KeyboardOptions.Default.keyboardType, + isLastTextField: Boolean = false ) { + val imeAction = if (isLastTextField) ImeAction.Done else ImeAction.Next + Column(modifier = modifier) { - if (isPassword) { + if (keyboardType == KeyboardType.Password) { PasswordTextField( modifier = Modifier.fillMaxWidth(), password = value, label = label, - updatePassword = onValueChange + updatePassword = onValueChange, + imeAction = imeAction, ) } else { TextField( @@ -217,7 +222,11 @@ private fun ErrorTextField( value = value, onValueChange = onValueChange, label = { Text(text = label) }, - isError = errorText != null + isError = errorText != null, + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = keyboardType, + imeAction = imeAction + ), ) } diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/MarkdownTextField.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/MarkdownTextField.kt index 8a121d3a2..0f67f4ebb 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/MarkdownTextField.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/MarkdownTextField.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.InputChip import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text @@ -23,7 +22,6 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import de.tum.informatics.www1.artemis.native_app.core.ui.markdown.MarkdownText @@ -97,7 +95,6 @@ internal fun MarkdownTextField( }, value = textFieldValue, onValueChange = onTextChanged, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) ) } diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/PotentiallyIllegalTextField.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/PotentiallyIllegalTextField.kt index 64bb499a5..8010a9193 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/PotentiallyIllegalTextField.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/PotentiallyIllegalTextField.kt @@ -58,6 +58,7 @@ internal fun PotentiallyIllegalTextField( } }, isError = isIllegal, - readOnly = readOnly + readOnly = readOnly, + singleLine = true, ) } \ No newline at end of file diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/settings/members/ConversationMembersBody.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/settings/members/ConversationMembersBody.kt index 1c95d4a06..ec590800f 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/settings/members/ConversationMembersBody.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/settings/members/ConversationMembersBody.kt @@ -88,6 +88,7 @@ internal fun ConversationMembersBody( .padding(horizontal = Spacings.ScreenHorizontalSpacing), value = query, onValueChange = viewModel::updateQuery, + singleLine = true, placeholder = { Text(text = stringResource(id = R.string.conversation_members_query_placeholder)) } ) diff --git a/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/question/short_answer/ShortAnswerQuizQuestionUi.kt b/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/question/short_answer/ShortAnswerQuizQuestionUi.kt index c361abc3b..104ec5647 100644 --- a/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/question/short_answer/ShortAnswerQuizQuestionUi.kt +++ b/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/question/short_answer/ShortAnswerQuizQuestionUi.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.InlineTextContent +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.appendInlineContent import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme @@ -26,6 +27,7 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.Placeholder import androidx.compose.ui.text.PlaceholderVerticalAlign import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp @@ -69,6 +71,7 @@ internal fun ShortAnswerQuizQuestionUi( } else data.solutionTexts val inlineContentMap = remember(question.spots, solutionTexts) { + val maxSpotNr = question.spots.maxOfOrNull { it.spotNr ?: 0 } ?: 0 question.spots.associate { spot -> val spotNr = spot.spotNr ?: 0 val key = spotNr.toString() @@ -80,6 +83,7 @@ internal fun ShortAnswerQuizQuestionUi( ), children = { val textColor = if (isSystemInDarkTheme()) Color.White else Color.Black + val imeAction = if (spotNr == maxSpotNr) ImeAction.Done else ImeAction.Next BasicTextField( modifier = Modifier @@ -96,9 +100,10 @@ internal fun ShortAnswerQuizQuestionUi( data.onUpdateSolutionText(spotNr, newText) } }, - maxLines = 1, textStyle = LocalTextStyle.current.copy(color = textColor), - cursorBrush = SolidColor(textColor) + cursorBrush = SolidColor(textColor), + singleLine = true, + keyboardOptions = KeyboardOptions.Default.copy(imeAction = imeAction) ) } ) diff --git a/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/screens/WaitForQuizStartScreen.kt b/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/screens/WaitForQuizStartScreen.kt index 978e00374..a7a017828 100644 --- a/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/screens/WaitForQuizStartScreen.kt +++ b/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/screens/WaitForQuizStartScreen.kt @@ -1,6 +1,5 @@ package de.tum.informatics.www1.artemis.native_app.feature.quiz.screens -import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -11,7 +10,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.Button -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -226,12 +224,13 @@ private fun BodyJoinBatched( ) TextField( - value = passcode, - onValueChange = { passcode = it }, modifier = Modifier .fillMaxWidth(0.8f) .widthIn(max = 600.dp) - .testTag(TEST_TAG_TEXT_FIELD_BATCH_PASSWORD) + .testTag(TEST_TAG_TEXT_FIELD_BATCH_PASSWORD), + value = passcode, + onValueChange = { passcode = it }, + singleLine = true, ) StartButton(