rename AppTextField to AppOutlinedTextField and implement InspiringSearchField

This commit is contained in:
jonasgaudian
2026-02-13 15:15:16 +01:00
parent e5c58f58f6
commit b3e73db956
26 changed files with 344 additions and 203 deletions

View File

@@ -0,0 +1,231 @@
package eu.gaudian.translator.view.composable
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import eu.gaudian.translator.R
import kotlinx.coroutines.delay
/**
* Helper composable for consistent OutlinedTextField colors.
*/
@Composable
private fun appTextFieldColors() = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.onSurface.copy(alpha = ComponentDefaults.ALPHA_LOW),
focusedLabelColor = MaterialTheme.colorScheme.primary,
cursorColor = MaterialTheme.colorScheme.primary
)
/**
* A styled text input field.
*
* @param value The input text to be shown in the text field.
* @param onValueChange The callback that is triggered when the input service updates the text.
* @param modifier The modifier to be applied to the text field.
* @param label A composable lambda for the label to be displayed inside the text field.
* @param trailingIcon A composable lambda for the trailing icon.
* @param paste Controls the visibility of the paste icon.
* @param clear Controls the visibility of the clear icon.
*/
@Composable
fun AppOutlinedTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
label: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
singleLine: Boolean = false,
minLines: Int = 1,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
placeholder: @Composable (() -> Unit)? = null,
enabled: Boolean = true,
readOnly: Boolean = false,
leadingIcon: @Composable (() -> Unit)? = null,
paste: Boolean = false,
clear: Boolean = false,
supportingText: @Composable (() -> Unit)? = null,
) {
val clipboardManager = LocalClipboardManager.current
val finalTrailingIcon: @Composable (() -> Unit)? = if (paste || clear || trailingIcon != null) {
{
Row(verticalAlignment = Alignment.CenterVertically) {
if (paste && value.isEmpty()) {
IconButton(onClick = { onValueChange(clipboardManager.getText()?.text ?: "") }) {
Icon(
imageVector = AppIcons.Paste,
contentDescription = stringResource(R.string.cd_paste),
tint = MaterialTheme.colorScheme.primary
)
}
}
if (clear && value.isNotEmpty()) {
IconButton(onClick = { onValueChange("") }) {
Icon(
imageVector = AppIcons.Clear,
contentDescription = stringResource(R.string.label_clear),
tint = MaterialTheme.colorScheme.primary
)
}
}
if (trailingIcon != null) {
trailingIcon()
}
}
}
} else {
null
}
OutlinedTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier.fillMaxWidth(),
label = label,
trailingIcon = finalTrailingIcon,
shape = ComponentDefaults.DefaultShape,
colors = appTextFieldColors(),
placeholder = placeholder,
enabled = enabled,
readOnly = readOnly,
leadingIcon = leadingIcon,
minLines = minLines,
maxLines = maxLines,
supportingText = supportingText,
)
}
@Composable
fun InspiringSearchField(
value: String,
hints : Array<String>,
onValueChange: (String) -> Unit
) {
var currentHintIndex by remember { mutableIntStateOf(0) }
// Start rotating immediately when the dialog appears
LaunchedEffect(hints) {
if (hints.isNotEmpty()) {
while (true) {
delay(2800)
currentHintIndex = (currentHintIndex + 1) % hints.size
}
}
}
AppOutlinedTextField(
value = value,
onValueChange = onValueChange,
//label = { Text(stringResource(R.string.text_search_term)) },
modifier = Modifier.fillMaxWidth(),
placeholder = {
AnimatedContent(
targetState = if (hints.isNotEmpty()) hints[currentHintIndex] else "",
transitionSpec = {
(fadeIn(tween(400)) + slideInVertically { it / 2 })
.togetherWith(fadeOut(tween(400)) + slideOutVertically { -it / 2 })
},
label = "HintAnimation"
) { hint ->
Text(
text = hint,
color = Color.Gray.copy(alpha = 0.6f),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
},
singleLine = true,
minLines = 1,
maxLines = 1
)
}
@Suppress("HardCodedStringLiteral")
@Preview
@Composable
fun AppOutlinedTextFieldPreview() {
var text by remember { mutableStateOf("Test") }
AppOutlinedTextField(
value = text,
onValueChange = { },
label = { Text(stringResource(R.string.email_address)) }
)
}
@Suppress("HardCodedStringLiteral")
@Preview(showBackground = true, name = "Search Field Preview")
@Composable
fun PreviewInspiringSearchField() {
MaterialTheme {
Surface(
modifier = Modifier.fillMaxWidth(),
color = Color(0xFFF8F9FA) // Light gray background like in your screenshot
) {
Column(
modifier = Modifier
.padding(24.dp)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = "Deixe a IA encontrar\nvocabulário para você!",
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.Bold
)
// Our animated component
InspiringSearchField(
value = "Test",
hints = listOf("test1", "test2", "test3").toTypedArray(),
onValueChange = {}
)
// Placeholder for the rest of your UI
Button(
onClick = { },
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp)
) {
Text("Continuar")
}
}
}
}
}

View File

@@ -30,12 +30,9 @@ import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.FabPosition import androidx.compose.material3.FabPosition
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedCard import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@@ -57,7 +54,6 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -449,106 +445,9 @@ fun SecondaryButtonInversePreview() {
SecondaryButton(onClick = { }, text = stringResource(R.string.secondary_inverse), icon = AppIcons.Add, inverse = true) SecondaryButton(onClick = { }, text = stringResource(R.string.secondary_inverse), icon = AppIcons.Add, inverse = true)
} }
/**
* Helper composable for consistent OutlinedTextField colors.
*/
@Composable
private fun appTextFieldColors() = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.onSurface.copy(alpha = ComponentDefaults.ALPHA_LOW),
focusedLabelColor = MaterialTheme.colorScheme.primary,
cursorColor = MaterialTheme.colorScheme.primary
)
/**
* A styled text input field.
*
* @param value The input text to be shown in the text field.
* @param onValueChange The callback that is triggered when the input service updates the text.
* @param modifier The modifier to be applied to the text field.
* @param label A composable lambda for the label to be displayed inside the text field.
* @param trailingIcon A composable lambda for the trailing icon.
* @param paste Controls the visibility of the paste icon.
* @param clear Controls the visibility of the clear icon.
*/
@Composable
fun AppTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
label: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
singleLine: Boolean = false,
minLines: Int = 1,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
placeholder: @Composable (() -> Unit)? = null,
enabled: Boolean = true,
readOnly: Boolean = false,
leadingIcon: @Composable (() -> Unit)? = null,
paste: Boolean = false,
clear: Boolean = false,
supportingText: @Composable (() -> Unit)? = null,
) {
val clipboardManager = LocalClipboardManager.current
val finalTrailingIcon: @Composable (() -> Unit)? = if (paste || clear || trailingIcon != null) {
{
Row(verticalAlignment = Alignment.CenterVertically) {
if (paste && value.isEmpty()) {
IconButton(onClick = { onValueChange(clipboardManager.getText()?.text ?: "") }) {
Icon(
imageVector = AppIcons.Paste,
contentDescription = stringResource(R.string.cd_paste),
tint = MaterialTheme.colorScheme.primary
)
}
}
if (clear && value.isNotEmpty()) {
IconButton(onClick = { onValueChange("") }) {
Icon(
imageVector = AppIcons.Clear,
contentDescription = stringResource(R.string.label_clear),
tint = MaterialTheme.colorScheme.primary
)
}
}
if (trailingIcon != null) {
trailingIcon()
}
}
}
} else {
null
}
OutlinedTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier.fillMaxWidth(),
label = label,
trailingIcon = finalTrailingIcon,
shape = ComponentDefaults.DefaultShape,
colors = appTextFieldColors(),
placeholder = placeholder,
enabled = enabled,
readOnly = readOnly,
leadingIcon = leadingIcon,
minLines = minLines,
maxLines = maxLines,
supportingText = supportingText,
)
}
@Preview
@Composable
fun AppTextFieldPreview() {
var text by remember { mutableStateOf("") }
AppTextField(
value = text,
onValueChange = { text = it },
label = { Text(stringResource(R.string.email_address)) }
)
}
@Composable @Composable

View File

@@ -46,7 +46,7 @@ import eu.gaudian.translator.model.VocabularyStage
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.MultipleLanguageDropdown import eu.gaudian.translator.view.composable.MultipleLanguageDropdown
import eu.gaudian.translator.view.hints.CategoryHint import eu.gaudian.translator.view.hints.CategoryHint
import eu.gaudian.translator.viewmodel.CategoryViewModel import eu.gaudian.translator.viewmodel.CategoryViewModel
@@ -96,7 +96,7 @@ fun AddCategoryDialog(
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
AppTextField( AppOutlinedTextField(
value = categoryName, value = categoryName,
onValueChange = { categoryName = it }, onValueChange = { categoryName = it },
label = { Text(stringResource(R.string.text_category_name)) }, label = { Text(stringResource(R.string.text_category_name)) },

View File

@@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp
import eu.gaudian.translator.R import eu.gaudian.translator.R
import eu.gaudian.translator.model.Language import eu.gaudian.translator.model.Language
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.DialogButton import eu.gaudian.translator.view.composable.DialogButton
@Composable @Composable
@@ -56,33 +56,37 @@ fun AddCustomLanguageDialog(
modifier = Modifier.padding(bottom = 8.dp) modifier = Modifier.padding(bottom = 8.dp)
) )
AppTextField( AppOutlinedTextField(
value = languageCode, value = languageCode,
onValueChange = { languageCode = it }, onValueChange = { languageCode = it },
label = { Text(text = stringResource(R.string.text_language_code)) }, label = { Text(text = stringResource(R.string.text_language_code)) },
placeholder = { Text(text = stringResource(R.string.text_e_g_en)) }, placeholder = { Text(text = stringResource(R.string.text_e_g_en)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth(),
singleLine = true
) )
AppTextField( AppOutlinedTextField(
value = languageRegion, value = languageRegion,
onValueChange = { languageRegion = it }, onValueChange = { languageRegion = it },
label = { Text(text = stringResource(R.string.text_region)) }, label = { Text(text = stringResource(R.string.text_region)) },
placeholder = { Text(text = stringResource(R.string.text_e_g_us)) }, placeholder = { Text(text = stringResource(R.string.text_e_g_us)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth(),
singleLine = true
) )
AppTextField( AppOutlinedTextField(
value = languageName, value = languageName,
onValueChange = { languageName = it }, onValueChange = { languageName = it },
label = { Text(text = stringResource(R.string.text_name_of_the_language)) }, label = { Text(text = stringResource(R.string.text_name_of_the_language)) },
placeholder = { Text(text = stringResource(R.string.text_e_g_english)) }, placeholder = { Text(text = stringResource(R.string.text_e_g_english)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth(),
singleLine = true
) )
AppTextField( AppOutlinedTextField(
value = englishName, value = englishName,
onValueChange = { englishName = it }, onValueChange = { englishName = it },
label = { Text(text = stringResource(R.string.name_in_english)) }, label = { Text(text = stringResource(R.string.name_in_english)) },
placeholder = { Text(text = stringResource(R.string.text_e_g_english)) }, placeholder = { Text(text = stringResource(R.string.text_e_g_english)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth(),
singleLine = true
) )
Row( Row(

View File

@@ -46,7 +46,7 @@ import eu.gaudian.translator.view.LocalConnectionConfigured
import eu.gaudian.translator.view.composable.AppButton import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppCheckbox import eu.gaudian.translator.view.composable.AppCheckbox
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.SourceLanguageDropdown import eu.gaudian.translator.view.composable.SourceLanguageDropdown
import eu.gaudian.translator.view.composable.TargetLanguageDropdown import eu.gaudian.translator.view.composable.TargetLanguageDropdown
import eu.gaudian.translator.viewmodel.LanguageViewModel import eu.gaudian.translator.viewmodel.LanguageViewModel
@@ -136,7 +136,7 @@ fun AddVocabularyDialog(
} }
if (selectedTab != VocabularyDialogTab.TEXT) { if (selectedTab != VocabularyDialogTab.TEXT) {
AppTextField( AppOutlinedTextField(
value = word, value = word,
onValueChange = { word = it }, onValueChange = { word = it },
label = { Text(stringResource(R.string.text_label_word)) }, label = { Text(stringResource(R.string.text_label_word)) },
@@ -165,7 +165,7 @@ fun AddVocabularyDialog(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { Text(stringResource(R.string.label_translate)) } ) { Text(stringResource(R.string.label_translate)) }
} }
AppTextField( AppOutlinedTextField(
value = singleTranslation, value = singleTranslation,
onValueChange = { singleTranslation = it }, onValueChange = { singleTranslation = it },
label = { Text(stringResource(R.string.text_translation)) }, label = { Text(stringResource(R.string.text_translation)) },
@@ -238,7 +238,7 @@ fun AddVocabularyDialog(
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(stringResource(R.string.text_enter_a_text_to_extract)) Text(stringResource(R.string.text_enter_a_text_to_extract))
Box(Modifier.fillMaxWidth().padding(0.dp).heightIn(max = 300.dp)){ Box(Modifier.fillMaxWidth().padding(0.dp).heightIn(max = 300.dp)){
AppTextField( AppOutlinedTextField(
value = word, value = word,
onValueChange = { word = it }, onValueChange = { word = it },
label = { Text(stringResource(R.string.label_enter_a_text)) }, label = { Text(stringResource(R.string.label_enter_a_text)) },

View File

@@ -35,7 +35,7 @@ import eu.gaudian.translator.view.composable.AppCheckbox
import eu.gaudian.translator.view.composable.AppDropdownMenuItem import eu.gaudian.translator.view.composable.AppDropdownMenuItem
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedButton import eu.gaudian.translator.view.composable.AppOutlinedButton
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.viewmodel.CategoryViewModel import eu.gaudian.translator.viewmodel.CategoryViewModel
@@ -180,7 +180,7 @@ fun CategoryDropdown(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
AppTextField( AppOutlinedTextField(
value = newCategoryName, value = newCategoryName,
onValueChange = { newCategoryName = it }, onValueChange = { newCategoryName = it },
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),

View File

@@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp
import eu.gaudian.translator.R import eu.gaudian.translator.R
import eu.gaudian.translator.view.composable.AppButton import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
@Composable @Composable
fun CreateCategoryListDialog( fun CreateCategoryListDialog(
@@ -57,7 +57,7 @@ fun CreateCategoryListDialog(
modifier = Modifier.padding(bottom = 8.dp) modifier = Modifier.padding(bottom = 8.dp)
) )
AppTextField( AppOutlinedTextField(
value = categoryName, value = categoryName,
onValueChange = { categoryName = it }, onValueChange = { categoryName = it },
label = { Text(stringResource(R.string.text_category_name)) }, label = { Text(stringResource(R.string.text_category_name)) },

View File

@@ -26,7 +26,7 @@ import eu.gaudian.translator.model.VocabularyCategory
import eu.gaudian.translator.model.VocabularyFilter import eu.gaudian.translator.model.VocabularyFilter
import eu.gaudian.translator.model.VocabularyStage import eu.gaudian.translator.model.VocabularyStage
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.MultipleLanguageDropdown import eu.gaudian.translator.view.composable.MultipleLanguageDropdown
import eu.gaudian.translator.viewmodel.CategoryViewModel import eu.gaudian.translator.viewmodel.CategoryViewModel
import eu.gaudian.translator.viewmodel.LanguageViewModel import eu.gaudian.translator.viewmodel.LanguageViewModel
@@ -84,7 +84,7 @@ fun EditCategoryDialog(
.fillMaxWidth() .fillMaxWidth()
.padding(bottom = 16.dp) .padding(bottom = 16.dp)
) { ) {
AppTextField( AppOutlinedTextField(
value = categoryName, value = categoryName,
onValueChange = { categoryName = it }, onValueChange = { categoryName = it },
label = { Text(stringResource(R.string.text_category_name)) }, label = { Text(stringResource(R.string.text_category_name)) },

View File

@@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp
import eu.gaudian.translator.R import eu.gaudian.translator.R
import eu.gaudian.translator.model.Language import eu.gaudian.translator.model.Language
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.DialogButton import eu.gaudian.translator.view.composable.DialogButton
@Composable @Composable
@@ -54,7 +54,7 @@ fun EditLanguageDialog(
) )
// Language name: editable only for custom languages // Language name: editable only for custom languages
AppTextField( AppOutlinedTextField(
value = name, value = name,
onValueChange = { if (language.isCustom == true) name = it }, onValueChange = { if (language.isCustom == true) name = it },
enabled = language.isCustom == true, enabled = language.isCustom == true,
@@ -62,14 +62,14 @@ fun EditLanguageDialog(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = code, value = code,
onValueChange = { code = it }, onValueChange = { code = it },
label = { Text(text = stringResource(R.string.text_language_code)) }, label = { Text(text = stringResource(R.string.text_language_code)) },
placeholder = { Text(text = stringResource(R.string.text_e_g_en)) }, placeholder = { Text(text = stringResource(R.string.text_e_g_en)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = region, value = region,
onValueChange = { region = it }, onValueChange = { region = it },
label = { Text(text = stringResource(R.string.text_region)) }, label = { Text(text = stringResource(R.string.text_region)) },

View File

@@ -9,7 +9,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.width
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@@ -25,6 +25,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -39,8 +40,8 @@ import eu.gaudian.translator.R
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppSlider import eu.gaudian.translator.view.composable.AppSlider
import eu.gaudian.translator.view.composable.AppTextField
import eu.gaudian.translator.view.composable.DialogButton import eu.gaudian.translator.view.composable.DialogButton
import eu.gaudian.translator.view.composable.InspiringSearchField
import eu.gaudian.translator.view.composable.SourceLanguageDropdown import eu.gaudian.translator.view.composable.SourceLanguageDropdown
import eu.gaudian.translator.view.composable.TargetLanguageDropdown import eu.gaudian.translator.view.composable.TargetLanguageDropdown
import eu.gaudian.translator.view.hints.getImportVocabularyHint import eu.gaudian.translator.view.hints.getImportVocabularyHint
@@ -117,27 +118,26 @@ fun ImportDialogContent(
) { ) {
if (isGenerating) { if (isGenerating) {
Box( Box(
modifier = Modifier modifier = Modifier.fillMaxWidth().padding(16.dp),
.fillMaxWidth()
.padding(16.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
CircularProgressIndicator() CircularProgressIndicator()
} }
} else { } else {
AppTextField(
value = category,
onValueChange = { category = it },
label = { Text(stringResource(R.string.text_search_term)) },
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
)
Text( Text(
text = stringResource(R.string.text_hint_you_can_search), text = stringResource(R.string.text_search_term),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.labelLarge,
modifier = Modifier.fillMaxWidth() modifier = Modifier.padding(bottom = 8.dp)
) )
// Modern rotating field using XML resource array
InspiringSearchField(
value = category,
hints = stringArrayResource(R.array.vocabulary_hints),
onValueChange = { category = it }
)
// The "Dica" string has been removed to keep the interface clean
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Text( Text(
text = stringResource(R.string.text_select_languages), text = stringResource(R.string.text_select_languages),
@@ -172,14 +172,11 @@ fun ImportDialogContent(
Text( Text(
text = stringResource(R.string.text_amount_2d, amount.toInt()), text = stringResource(R.string.text_amount_2d, amount.toInt()),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
modifier = Modifier modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
.fillMaxWidth()
.padding(top = 8.dp)
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
} }
// Action buttons
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -190,7 +187,7 @@ fun ImportDialogContent(
content = { Text(stringResource(R.string.label_cancel)) } content = { Text(stringResource(R.string.label_cancel)) }
) )
if (category.isNotBlank() && !isGenerating) { if (category.isNotBlank() && !isGenerating) {
Spacer(modifier = Modifier.widthIn(8.dp)) Spacer(modifier = Modifier.width(8.dp))
DialogButton(onClick = { DialogButton(onClick = {
coroutineScope.launch { coroutineScope.launch {
vocabularyViewModel.generateVocabularyItems(category, amount.toInt()) vocabularyViewModel.generateVocabularyItems(category, amount.toInt())

View File

@@ -29,7 +29,7 @@ import eu.gaudian.translator.R
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppButton import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppOutlinedCard import eu.gaudian.translator.view.composable.AppOutlinedCard
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.DictionaryLanguageDropDown import eu.gaudian.translator.view.composable.DictionaryLanguageDropDown
import eu.gaudian.translator.viewmodel.DictionaryViewModel import eu.gaudian.translator.viewmodel.DictionaryViewModel
import eu.gaudian.translator.viewmodel.LanguageViewModel import eu.gaudian.translator.viewmodel.LanguageViewModel
@@ -49,7 +49,7 @@ fun EtymologyScreen(
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
AppTextField( AppOutlinedTextField(
value = searchQuery, value = searchQuery,
onValueChange = { searchQuery = it }, onValueChange = { searchQuery = it },
label = { Text(stringResource(R.string.search_for_a_word_s_origin)) }, label = { Text(stringResource(R.string.search_for_a_word_s_origin)) },

View File

@@ -54,7 +54,7 @@ import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppCard import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedButton import eu.gaudian.translator.view.composable.AppOutlinedButton
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.DictionaryLanguageDropDown import eu.gaudian.translator.view.composable.DictionaryLanguageDropDown
import eu.gaudian.translator.view.composable.OptionItemSwitch import eu.gaudian.translator.view.composable.OptionItemSwitch
import eu.gaudian.translator.viewmodel.DictionaryViewModel import eu.gaudian.translator.viewmodel.DictionaryViewModel
@@ -243,11 +243,10 @@ private fun SearchFieldWithSuggestions(
expanded = expanded && suggestions.isNotEmpty(), expanded = expanded && suggestions.isNotEmpty(),
onExpandedChange = { expanded = !expanded } onExpandedChange = { expanded = !expanded }
) { ) {
AppTextField( AppOutlinedTextField(
value = searchQuery, value = searchQuery,
onValueChange = { onValueChange = {
onSearchQueryChange(it) onSearchQueryChange(it)
// Using ViewModel's fetchSuggestions with debounce
lastJob?.cancel() lastJob?.cancel()
lastJob = scope.launch { lastJob = scope.launch {
delay(100) delay(100)

View File

@@ -42,8 +42,8 @@ import eu.gaudian.translator.R
import eu.gaudian.translator.model.Question import eu.gaudian.translator.model.Question
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.AppSlider import eu.gaudian.translator.view.composable.AppSlider
import eu.gaudian.translator.view.composable.AppTextField
import eu.gaudian.translator.view.composable.DialogButton import eu.gaudian.translator.view.composable.DialogButton
import eu.gaudian.translator.view.composable.SourceLanguageDropdown import eu.gaudian.translator.view.composable.SourceLanguageDropdown
import eu.gaudian.translator.view.composable.TargetLanguageDropdown import eu.gaudian.translator.view.composable.TargetLanguageDropdown
@@ -94,7 +94,7 @@ fun GenerateExerciseDialog(
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(bottom = 8.dp) modifier = Modifier.padding(bottom = 8.dp)
) )
AppTextField( AppOutlinedTextField(
value = category, value = category,
onValueChange = { category = it }, onValueChange = { category = it },
label = { Text(stringResource(R.string.text_category_prompt)) }, label = { Text(stringResource(R.string.text_category_prompt)) },

View File

@@ -49,7 +49,7 @@ import eu.gaudian.translator.utils.TextToSpeechHelper
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppButton import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.viewmodel.SettingsViewModel import eu.gaudian.translator.viewmodel.SettingsViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Locale import java.util.Locale
@@ -102,7 +102,7 @@ fun FillInTheBlankQuestionUi(
} }
} }
} }
AppTextField( AppOutlinedTextField(
value = selectedAnswer, value = selectedAnswer,
onValueChange = onAnswerSelect, onValueChange = onAnswerSelect,
placeholder = { Text(placeholder) }, placeholder = { Text(placeholder) },
@@ -337,7 +337,7 @@ fun ListeningComprehensionQuestionUi(
if (maskedHint.isNotBlank()) { if (maskedHint.isNotBlank()) {
Text(maskedHint, style = MaterialTheme.typography.bodyMedium) Text(maskedHint, style = MaterialTheme.typography.bodyMedium)
} }
AppTextField( AppOutlinedTextField(
value = selectedAnswer, value = selectedAnswer,
onValueChange = onAnswerSelect, onValueChange = onAnswerSelect,
label = { Text(stringResource(R.string.type_what_you_hear)) }, label = { Text(stringResource(R.string.type_what_you_hear)) },
@@ -482,7 +482,7 @@ fun VocabularyTestQuestionUi(
text = stringResource(R.string.translate_the_following_d, question.languageDirection), text = stringResource(R.string.translate_the_following_d, question.languageDirection),
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium
) )
AppTextField( AppOutlinedTextField(
value = selectedAnswer, value = selectedAnswer,
onValueChange = onAnswerSelect, onValueChange = onAnswerSelect,
label = { Text(stringResource(R.string.label_your_translation)) }, label = { Text(stringResource(R.string.label_your_translation)) },

View File

@@ -29,7 +29,7 @@ import eu.gaudian.translator.R
import eu.gaudian.translator.model.Language import eu.gaudian.translator.model.Language
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.DialogButton import eu.gaudian.translator.view.composable.DialogButton
import eu.gaudian.translator.view.composable.SourceLanguageDropdown import eu.gaudian.translator.view.composable.SourceLanguageDropdown
import eu.gaudian.translator.view.composable.TargetLanguageDropdown import eu.gaudian.translator.view.composable.TargetLanguageDropdown
@@ -64,12 +64,14 @@ fun YouTubeExerciseDialog(
) )
if (!languageOnly) { if (!languageOnly) {
AppTextField( AppOutlinedTextField(
value = youtubeUrl, value = youtubeUrl,
onValueChange = { youtubeUrl = it }, onValueChange = { youtubeUrl = it },
label = { Text(stringResource(R.string.text_youtube_link)) }, label = { Text(stringResource(R.string.text_youtube_link)) },
placeholder = { Text("https://www.youtube.com/watch?v=...") }, placeholder = { Text("https://www.youtube.com/watch?v=...") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth(),
singleLine = true,
paste = true,
) )
} }

View File

@@ -16,8 +16,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.gaudian.translator.R import eu.gaudian.translator.R
import eu.gaudian.translator.view.composable.AppCheckbox import eu.gaudian.translator.view.composable.AppCheckbox
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.AppSlider import eu.gaudian.translator.view.composable.AppSlider
import eu.gaudian.translator.view.composable.AppTextField
/** /**
* Provides the migrated Hint for ImportVocabulary. * Provides the migrated Hint for ImportVocabulary.
@@ -38,7 +38,7 @@ fun getImportVocabularyHint(): Hint {
description = stringResource(R.string.text_hint_you_can_search), description = stringResource(R.string.text_hint_you_can_search),
trailing = { trailing = {
// The AppTextField is wrapped in the VisualStep's trailing composable // The AppTextField is wrapped in the VisualStep's trailing composable
AppTextField( AppOutlinedTextField(
value = stringResource(R.string.search_term_placeholder), value = stringResource(R.string.search_term_placeholder),
onValueChange = {}, onValueChange = {},
label = { Text(stringResource(R.string.text_search_term)) }, label = { Text(stringResource(R.string.text_search_term)) },

View File

@@ -36,7 +36,7 @@ import androidx.compose.ui.unit.sp
import eu.gaudian.translator.R import eu.gaudian.translator.R
import eu.gaudian.translator.view.composable.AppButton import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
object SortingScreenHint : LegacyHint() { object SortingScreenHint : LegacyHint() {
override val titleRes: Int = R.string.sorting_hint_title override val titleRes: Int = R.string.sorting_hint_title
@@ -56,7 +56,7 @@ object SortingScreenHint : LegacyHint() {
HintSection(title = stringResource(R.string.sorting_hint_intro_text)) { HintSection(title = stringResource(R.string.sorting_hint_intro_text)) {
@Suppress("HardCodedStringLiteral") @Suppress("HardCodedStringLiteral")
AppTextField( AppOutlinedTextField(
value = "der Hund", value = "der Hund",
onValueChange = {}, onValueChange = {},
label = { Text(stringResource(R.string.label_word)) }, label = { Text(stringResource(R.string.label_word)) },
@@ -64,7 +64,7 @@ object SortingScreenHint : LegacyHint() {
enabled = false enabled = false
) )
@Suppress("HardCodedStringLiteral") @Suppress("HardCodedStringLiteral")
AppTextField( AppOutlinedTextField(
value = "the dog", value = "the dog",
onValueChange = {}, onValueChange = {},
label = { Text(stringResource(R.string.label_translation)) }, label = { Text(stringResource(R.string.label_translation)) },

View File

@@ -53,9 +53,9 @@ import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppCard import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.AppScaffold import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppSwitch import eu.gaudian.translator.view.composable.AppSwitch
import eu.gaudian.translator.view.composable.AppTextField
import eu.gaudian.translator.view.composable.AppTopAppBar import eu.gaudian.translator.view.composable.AppTopAppBar
import eu.gaudian.translator.view.composable.ModelBadges import eu.gaudian.translator.view.composable.ModelBadges
import eu.gaudian.translator.view.hints.AddModelScanHint import eu.gaudian.translator.view.hints.AddModelScanHint
@@ -272,7 +272,7 @@ fun AddModelScreen(navController: NavController, providerKey: String) {
} }
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
AppTextField( AppOutlinedTextField(
value = displayName, value = displayName,
onValueChange = { displayName = it }, onValueChange = { displayName = it },
label = { Text(stringResource(R.string.label_display_name)) }, label = { Text(stringResource(R.string.label_display_name)) },
@@ -289,7 +289,7 @@ fun AddModelScreen(navController: NavController, providerKey: String) {
} }
} }
) )
AppTextField( AppOutlinedTextField(
value = modelId, value = modelId,
onValueChange = { modelId = it }, onValueChange = { modelId = it },
label = { Text(stringResource(R.string.label_model_id_star)) }, label = { Text(stringResource(R.string.label_model_id_star)) },
@@ -312,7 +312,7 @@ fun AddModelScreen(navController: NavController, providerKey: String) {
} }
} }
) )
AppTextField( AppOutlinedTextField(
value = description, value = description,
onValueChange = { description = it }, onValueChange = { description = it },
label = { Text(stringResource(R.string.label_description)) }, label = { Text(stringResource(R.string.label_description)) },
@@ -430,7 +430,7 @@ private fun EnhancedScannedModelsDialog(
if (showSearch || hasFreeModels) { if (showSearch || hasFreeModels) {
Column(Modifier.padding(horizontal = 8.dp)) { Column(Modifier.padding(horizontal = 8.dp)) {
if (showSearch) { if (showSearch) {
AppTextField( AppOutlinedTextField(
value = searchQuery, value = searchQuery,
onValueChange = { searchQuery = it }, onValueChange = { searchQuery = it },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -474,7 +474,7 @@ private fun EnhancedScannedModelsDialog(
) )
} }
} else { } else {
itemsIndexed(filteredModels, key = { index, model -> "${model.modelId}_${model.providerKey}_${model.displayName}_$index" }) { index, model -> itemsIndexed(filteredModels, key = { index, model -> "${model.modelId}_${model.providerKey}_${model.displayName}_$index" }) { _, model ->
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
var canExpand by remember { mutableStateOf(false) } var canExpand by remember { mutableStateOf(false) }
val secondary = buildString { val secondary = buildString {

View File

@@ -71,9 +71,9 @@ import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppCard import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.AppScaffold import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppTabLayout import eu.gaudian.translator.view.composable.AppTabLayout
import eu.gaudian.translator.view.composable.AppTextField
import eu.gaudian.translator.view.composable.AppTopAppBar import eu.gaudian.translator.view.composable.AppTopAppBar
import eu.gaudian.translator.view.composable.ClickableText import eu.gaudian.translator.view.composable.ClickableText
import eu.gaudian.translator.view.composable.PrimaryButton import eu.gaudian.translator.view.composable.PrimaryButton
@@ -1006,11 +1006,12 @@ private fun ApiKeyInput(
) { ) {
Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) { Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) {
val clipboard = LocalClipboardManager.current val clipboard = LocalClipboardManager.current
AppTextField( AppOutlinedTextField(
value = apiKey, value = apiKey,
onValueChange = onApiKeyChanged, onValueChange = onApiKeyChanged,
label = { Text(stringResource(R.string.text_enter_api_key)) }, label = { Text(stringResource(R.string.text_enter_api_key)) },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
maxLines = 3,
trailingIcon = { trailingIcon = {
IconButton( IconButton(
onClick = { onClick = {
@@ -1066,20 +1067,20 @@ fun AddProviderDialog(onDismiss: () -> Unit, onConfirm: (ApiProvider) -> Unit) {
modifier = Modifier.padding(8.dp), modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
AppTextField( AppOutlinedTextField(
value = displayName, value = displayName,
onValueChange = { newValue: String -> displayName = newValue }, onValueChange = { newValue: String -> displayName = newValue },
label = { Text(stringResource(R.string.display_name) + " *") }, label = { Text(stringResource(R.string.display_name) + " *") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = baseUrl, value = baseUrl,
onValueChange = { newValue: String -> baseUrl = newValue }, onValueChange = { newValue: String -> baseUrl = newValue },
label = { Text(stringResource(R.string.text_base_url_and_example) + " *") }, label = { Text(stringResource(R.string.text_base_url_and_example) + " *") },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
maxLines = 2 maxLines = 2
) )
AppTextField( AppOutlinedTextField(
value = endpoint, value = endpoint,
onValueChange = { newValue: String -> endpoint = newValue }, onValueChange = { newValue: String -> endpoint = newValue },
label = { Text(buildString { label = { Text(buildString {
@@ -1088,7 +1089,7 @@ fun AddProviderDialog(onDismiss: () -> Unit, onConfirm: (ApiProvider) -> Unit) {
}) }, }) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = websiteUrl, value = websiteUrl,
onValueChange = { newValue: String -> websiteUrl = newValue }, onValueChange = { newValue: String -> websiteUrl = newValue },
label = { Text(buildString { label = { Text(buildString {
@@ -1189,19 +1190,19 @@ fun EditProviderDialog(
modifier = Modifier.padding(24.dp), modifier = Modifier.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
AppTextField( AppOutlinedTextField(
value = displayName, value = displayName,
onValueChange = { displayName = it }, onValueChange = { displayName = it },
label = { Text(stringResource(R.string.display_name) + " *") }, label = { Text(stringResource(R.string.display_name) + " *") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = baseUrl, value = baseUrl,
onValueChange = { baseUrl = it }, onValueChange = { baseUrl = it },
label = { Text(stringResource(R.string.text_base_url_and_example) + " *") }, label = { Text(stringResource(R.string.text_base_url_and_example) + " *") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = endpoint, value = endpoint,
onValueChange = { endpoint = it }, onValueChange = { endpoint = it },
label = { Text(buildString { label = { Text(buildString {
@@ -1210,7 +1211,7 @@ fun EditProviderDialog(
}) }, }) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = websiteUrl, value = websiteUrl,
onValueChange = { websiteUrl = it }, onValueChange = { websiteUrl = it },
label = { Text(buildString { label = { Text(buildString {
@@ -1324,14 +1325,14 @@ fun AddModelDialog(
} }
// Manual entry // Manual entry
AppTextField( AppOutlinedTextField(
value = displayName, value = displayName,
onValueChange = { newValue: String -> displayName = newValue }, onValueChange = { newValue: String -> displayName = newValue },
label = { Text(stringResource(R.string.display_name)) }, label = { Text(stringResource(R.string.display_name)) },
enabled = !isLoading, enabled = !isLoading,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = modelId, value = modelId,
onValueChange = { newValue: String -> modelId = newValue }, onValueChange = { newValue: String -> modelId = newValue },
label = { Text(stringResource(R.string.model_id)) }, label = { Text(stringResource(R.string.model_id)) },
@@ -1339,7 +1340,7 @@ fun AddModelDialog(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
maxLines = 2 maxLines = 2
) )
AppTextField( AppOutlinedTextField(
value = description, value = description,
onValueChange = { newValue: String -> description = newValue }, onValueChange = { newValue: String -> description = newValue },
label = { Text(stringResource(R.string.description)) }, label = { Text(stringResource(R.string.description)) },
@@ -1429,19 +1430,19 @@ fun EditModelDialog(
modifier = Modifier.padding(24.dp), modifier = Modifier.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
AppTextField( AppOutlinedTextField(
value = displayName, value = displayName,
onValueChange = { displayName = it }, onValueChange = { displayName = it },
label = { Text(stringResource(R.string.display_name)) }, label = { Text(stringResource(R.string.display_name)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = modelId, value = modelId,
onValueChange = { modelId = it }, onValueChange = { modelId = it },
label = { Text(stringResource(R.string.model_id)) }, label = { Text(stringResource(R.string.model_id)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
AppTextField( AppOutlinedTextField(
value = description, value = description,
onValueChange = { description = it }, onValueChange = { description = it },
label = { Text(stringResource(R.string.description)) }, label = { Text(stringResource(R.string.description)) },

View File

@@ -42,7 +42,7 @@ import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppCard import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedButton import eu.gaudian.translator.view.composable.AppOutlinedButton
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.ModelBadges import eu.gaudian.translator.view.composable.ModelBadges
data class PromptSettingsState( data class PromptSettingsState(
@@ -77,7 +77,7 @@ fun BasePromptSettingsScreen(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
Column(Modifier.padding(16.dp)) { Column(Modifier.padding(16.dp)) {
AppTextField( AppOutlinedTextField(
value = state.customPrompt, value = state.customPrompt,
onValueChange = onPromptChanged, onValueChange = onPromptChanged,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),

View File

@@ -34,8 +34,8 @@ import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.LocalShowExperimentalFeatures import eu.gaudian.translator.view.LocalShowExperimentalFeatures
import eu.gaudian.translator.view.composable.AppCard import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.AppScaffold import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppTextField
import eu.gaudian.translator.view.composable.AppTopAppBar import eu.gaudian.translator.view.composable.AppTopAppBar
import eu.gaudian.translator.view.composable.OptionItemSwitch import eu.gaudian.translator.view.composable.OptionItemSwitch
import eu.gaudian.translator.view.composable.PrimaryButton import eu.gaudian.translator.view.composable.PrimaryButton
@@ -130,7 +130,7 @@ fun DictionaryOptionsScreen(
apiViewModel.setDictionaryModel(model) apiViewModel.setDictionaryModel(model)
}, },
) )
AppTextField( AppOutlinedTextField(
value = tempPrompt, value = tempPrompt,
onValueChange = { tempPrompt = it }, onValueChange = { tempPrompt = it },
label = { Text(stringResource(R.string.text_custom_dictionary_prompt)) }, label = { Text(stringResource(R.string.text_custom_dictionary_prompt)) },

View File

@@ -31,8 +31,8 @@ import eu.gaudian.translator.R
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppCard import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.AppScaffold import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppTextField
import eu.gaudian.translator.view.composable.AppTopAppBar import eu.gaudian.translator.view.composable.AppTopAppBar
import eu.gaudian.translator.view.composable.PrimaryButton import eu.gaudian.translator.view.composable.PrimaryButton
import eu.gaudian.translator.view.composable.SecondaryButton import eu.gaudian.translator.view.composable.SecondaryButton
@@ -114,7 +114,7 @@ fun ExerciseSettingsScreen(
apiViewModel.setExerciseModel(model) apiViewModel.setExerciseModel(model)
}, },
) )
AppTextField( AppOutlinedTextField(
value = tempPrompt, value = tempPrompt,
onValueChange = { tempPrompt = it }, onValueChange = { tempPrompt = it },
label = { Text(stringResource(R.string.custom_exercise_prompt)) }, label = { Text(stringResource(R.string.custom_exercise_prompt)) },

View File

@@ -20,7 +20,7 @@ import androidx.compose.ui.unit.dp
import eu.gaudian.translator.R import eu.gaudian.translator.R
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedButton import eu.gaudian.translator.view.composable.AppOutlinedButton
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.CorrectButton import eu.gaudian.translator.view.composable.CorrectButton
import eu.gaudian.translator.view.composable.WrongButton import eu.gaudian.translator.view.composable.WrongButton
@@ -43,7 +43,7 @@ fun ExerciseControls(
Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) { Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
// Input field for spelling, only shown before the answer is revealed. // Input field for spelling, only shown before the answer is revealed.
if (!isRevealed && state is VocabularyExerciseState.Spelling) { if (!isRevealed && state is VocabularyExerciseState.Spelling) {
AppTextField( AppOutlinedTextField(
value = spellingAnswer, value = spellingAnswer,
onValueChange = { spellingAnswer = it }, onValueChange = { spellingAnswer = it },
label = { Text(stringResource(R.string.type_the_translation)) }, label = { Text(stringResource(R.string.type_the_translation)) },

View File

@@ -68,8 +68,8 @@ import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppCard import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppIcons import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.AppScaffold import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppTextField
import eu.gaudian.translator.view.composable.AppTopAppBar import eu.gaudian.translator.view.composable.AppTopAppBar
import eu.gaudian.translator.view.composable.SingleLanguageDropDown import eu.gaudian.translator.view.composable.SingleLanguageDropDown
import eu.gaudian.translator.view.dialogs.CategoryDropdown import eu.gaudian.translator.view.dialogs.CategoryDropdown
@@ -455,7 +455,7 @@ fun VocabularySortingItem(
} }
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
AppTextField( AppOutlinedTextField(
value = wordFirst, value = wordFirst,
onValueChange = { wordFirst = it }, onValueChange = { wordFirst = it },
label = { Text(stringResource(R.string.label_word)) }, label = { Text(stringResource(R.string.label_word)) },
@@ -469,7 +469,7 @@ fun VocabularySortingItem(
} }
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
AppTextField( AppOutlinedTextField(
value = wordSecond, value = wordSecond,
onValueChange = { wordSecond = it }, onValueChange = { wordSecond = it },
label = { Text(stringResource(R.string.label_translation)) }, label = { Text(stringResource(R.string.label_translation)) },

View File

@@ -45,7 +45,7 @@ import eu.gaudian.translator.model.grammar.formatGrammarDetails
import eu.gaudian.translator.utils.Log import eu.gaudian.translator.utils.Log
import eu.gaudian.translator.utils.findActivity import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppDialog import eu.gaudian.translator.view.composable.AppDialog
import eu.gaudian.translator.view.composable.AppTextField import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.DialogButton import eu.gaudian.translator.view.composable.DialogButton
import eu.gaudian.translator.viewmodel.LanguageConfigViewModel import eu.gaudian.translator.viewmodel.LanguageConfigViewModel
@@ -185,7 +185,7 @@ private fun GuidedEditTabContent(
expanded = isCategoryDropdownExpanded, expanded = isCategoryDropdownExpanded,
onExpandedChange = { isCategoryDropdownExpanded = !isCategoryDropdownExpanded } onExpandedChange = { isCategoryDropdownExpanded = !isCategoryDropdownExpanded }
) { ) {
AppTextField( AppOutlinedTextField(
modifier = Modifier modifier = Modifier
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable, enabled = true) .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable, enabled = true)
.fillMaxWidth(), .fillMaxWidth(),
@@ -247,7 +247,7 @@ private fun DynamicField(
when (fieldConfig.type) { when (fieldConfig.type) {
"text" -> { "text" -> {
AppTextField( AppOutlinedTextField(
value = currentValue ?: "", value = currentValue ?: "",
onValueChange = { onValueChange(it.ifEmpty { null }) }, onValueChange = { onValueChange(it.ifEmpty { null }) },
label = { Text(label) }, label = { Text(label) },
@@ -262,7 +262,7 @@ private fun DynamicField(
isExpanded = !isExpanded isExpanded = !isExpanded
} }
) { ) {
AppTextField( AppOutlinedTextField(
modifier = Modifier modifier = Modifier
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable, enabled = true) .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable, enabled = true)
.fillMaxWidth(), .fillMaxWidth(),

View File

@@ -41,6 +41,14 @@
<item>Make it informal.</item> <item>Make it informal.</item>
</string-array> </string-array>
<string-array name="vocabulary_hints">
<item>Basic greetings</item>
<item>Irregular verbs</item>
<item>Vocabulary at the airport</item>
<item>How to order a coffee</item>
<item>Idiomatic expressions</item>
</string-array>
<string-array name="changelog_entries"> <string-array name="changelog_entries">
<item>Version 0.3.0 \n• Enabled CSV Import for Vocabulary\n• Option to use a translation server for translations instead of AI models for some supported langugaes\n• UI bug fixes \n• Show word frequency \n• Performance optimizations \n• Improved translations (German and Portuguese)</item> <item>Version 0.3.0 \n• Enabled CSV Import for Vocabulary\n• Option to use a translation server for translations instead of AI models for some supported langugaes\n• UI bug fixes \n• Show word frequency \n• Performance optimizations \n• Improved translations (German and Portuguese)</item>
<item>Version 0.4.0 \n• Added dictionary download (beta) \n• UI enhancements \n• Bugfixes \n• Re-designed vocabulary card with improved UI \n• More pre-configured providers \n• Improved performance</item> <item>Version 0.4.0 \n• Added dictionary download (beta) \n• UI enhancements \n• Bugfixes \n• Re-designed vocabulary card with improved UI \n• More pre-configured providers \n• Improved performance</item>