diff --git a/app/src/main/java/eu/gaudian/translator/view/composable/LanguageDropDown.kt b/app/src/main/java/eu/gaudian/translator/view/composable/LanguageDropDown.kt index ff405e6..8027720 100644 --- a/app/src/main/java/eu/gaudian/translator/view/composable/LanguageDropDown.kt +++ b/app/src/main/java/eu/gaudian/translator/view/composable/LanguageDropDown.kt @@ -56,6 +56,8 @@ fun BaseLanguageDropDown( enableMultipleSelection: Boolean = false, onLanguagesSelected: (List) -> Unit = {}, alternateLanguages: List = emptyList(), + restrictToAlternateLanguages: Boolean = false, + enabled: Boolean = true, iconEnabled: Boolean = true, noBorder: Boolean = false, ) { @@ -68,8 +70,12 @@ fun BaseLanguageDropDown( var tempSelection by remember { mutableStateOf>(emptyList()) } var selectedLanguagesCount by remember { mutableIntStateOf(if (enableMultipleSelection) tempSelection.size else 0) } - val languages = remember(alternateLanguages, defaultLanguages) { - alternateLanguages.ifEmpty { defaultLanguages } + val languages = remember(alternateLanguages, defaultLanguages, restrictToAlternateLanguages) { + if (restrictToAlternateLanguages) { + alternateLanguages + } else { + alternateLanguages.ifEmpty { defaultLanguages } + } } val buttonText = when { @@ -90,6 +96,7 @@ fun BaseLanguageDropDown( AppOutlinedButton( shape = RoundedCornerShape(8.dp), onClick = { expanded = true }, + enabled = enabled, contentPadding = if (!iconEnabled) PaddingValues(0.dp) else PaddingValues(horizontal = 16.dp, vertical = 8.dp), borderColor = if (noBorder) Color.Unspecified else null ) { @@ -221,8 +228,13 @@ fun BaseLanguageDropDown( ) { val isSearching = searchText.isNotBlank() - if (isSearching) { - val searchResults = (favoriteLanguages + languageHistory + languages) + if (isSearching) { + val searchBase = if (restrictToAlternateLanguages) { + alternateLanguages + } else { + favoriteLanguages + languageHistory + languages + } + val searchResults = searchBase .distinctBy { it.nameResId } .filter { language -> val matchesName = language.name.contains(searchText, ignoreCase = true) @@ -237,8 +249,18 @@ fun BaseLanguageDropDown( searchResults.forEach { language -> SingleSelectItem(language) } } - } else if (alternateLanguages.isNotEmpty()) { - val sortedAlternate = alternateLanguages.sortedBy { it.name } + } else if (restrictToAlternateLanguages) { + val sortedAlternate = alternateLanguages.sortedBy { it.name } + if (enableMultipleSelection) { + DropdownHeader(text = stringResource(R.string.text_all_languages)) + sortedAlternate.forEach { language -> MultiSelectItem(language) } + } else { + DropdownHeader(text = stringResource(R.string.text_all_languages)) + sortedAlternate.forEach { language -> SingleSelectItem(language) } + } + + } else if (alternateLanguages.isNotEmpty()) { + val sortedAlternate = alternateLanguages.sortedBy { it.name } if (enableMultipleSelection) { DropdownHeader(text = stringResource(R.string.text_all_languages)) sortedAlternate.forEach { language -> MultiSelectItem(language) } @@ -458,7 +480,9 @@ fun SingleLanguageDropDown( onAutoSelected: () -> Unit = {}, showNoneOption: Boolean = false, onNoneSelected: () -> Unit = {}, - alternateLanguages: List = emptyList() + alternateLanguages: List = emptyList(), + restrictToAlternateLanguages: Boolean = false, + enabled: Boolean = true ) { val languageHistory by languageViewModel.languageHistory.collectAsState() @@ -477,6 +501,10 @@ fun SingleLanguageDropDown( showNoneOption = showNoneOption, onNoneSelected = onNoneSelected, enableMultipleSelection = false, - alternateLanguages = alternateLanguages + alternateLanguages = alternateLanguages, + restrictToAlternateLanguages = restrictToAlternateLanguages, + enabled = enabled, + iconEnabled = enabled, + noBorder = !enabled ) } \ No newline at end of file diff --git a/app/src/main/java/eu/gaudian/translator/view/exercises/StartExerciseScreen.kt b/app/src/main/java/eu/gaudian/translator/view/exercises/StartExerciseScreen.kt index 5f00857..d7d56fc 100644 --- a/app/src/main/java/eu/gaudian/translator/view/exercises/StartExerciseScreen.kt +++ b/app/src/main/java/eu/gaudian/translator/view/exercises/StartExerciseScreen.kt @@ -89,6 +89,7 @@ fun StartExerciseScreen( var selectedOriginLanguage by remember { mutableStateOf(null) } var selectedTargetLanguage by remember { mutableStateOf(null) } + val isDirectionPreferenceSet = selectedOriginLanguage != null || selectedTargetLanguage != null val selectedPairsIds = remember(selectedLanguagePairs) { selectedLanguagePairs.map { it.first.nameResId to it.second.nameResId } @@ -115,6 +116,15 @@ fun StartExerciseScreen( val itemsToShow by filteredItemsFlow.collectAsState(initial = emptyList()) val totalItemCount = itemsToShow.size + val availableLanguagesFromItems = remember(itemsToShow, selectedPairsIds) { + val ids = if (selectedPairsIds.isNotEmpty()) { + selectedPairsIds.flatMap { pair -> listOf(pair.first, pair.second) }.toSet() + } else { + itemsToShow.flatMap { listOfNotNull(it.languageFirstId, it.languageSecondId) }.toSet() + } + ids + } + var amount by remember { mutableStateOf(0) } androidx.compose.runtime.LaunchedEffect(totalItemCount) { amount = totalItemCount @@ -140,6 +150,7 @@ fun StartExerciseScreen( onShuffleCardsChanged = { updateConfig(exerciseConfig.copy(shuffleCards = it)) }, shuffleLanguages = exerciseConfig.shuffleLanguages, onShuffleLanguagesChanged = { updateConfig(exerciseConfig.copy(shuffleLanguages = it)) }, + shuffleLanguagesEnabled = !isDirectionPreferenceSet, trainingMode = exerciseConfig.trainingMode, onTrainingModeChanged = { updateConfig(exerciseConfig.copy(trainingMode = it)) }, ) @@ -154,21 +165,46 @@ fun StartExerciseScreen( item { LanguagePairSection( selectedPairs = selectedLanguagePairs, - onPairsChanged = { selectedLanguagePairs = it }, - onOriginLanguageSelected = { language -> - selectedOriginLanguage = language - if (selectedTargetLanguage?.nameResId == language?.nameResId) { + availableLanguageIds = availableLanguagesFromItems, + onPairsChanged = { updatedPairs -> + val hadPairs = selectedLanguagePairs.isNotEmpty() + selectedLanguagePairs = updatedPairs + if (updatedPairs.isNotEmpty()) { + selectedOriginLanguage = null selectedTargetLanguage = null + updateConfig(exerciseConfig.copy(originLanguageId = null, targetLanguageId = null)) + } else if (hadPairs) { + updateConfig(exerciseConfig.copy(originLanguageId = null, targetLanguageId = null)) + } + }, + onOriginLanguageSelected = { language -> + if (language?.nameResId == selectedOriginLanguage?.nameResId) { + selectedOriginLanguage = null + updateConfig(exerciseConfig.copy(originLanguageId = null)) + } else { + selectedOriginLanguage = language + if (selectedTargetLanguage?.nameResId == language?.nameResId) { + selectedTargetLanguage = null + updateConfig(exerciseConfig.copy(targetLanguageId = null)) + } + updateConfig(exerciseConfig.copy(originLanguageId = language?.nameResId)) } - updateConfig(exerciseConfig.copy(originLanguageId = language?.nameResId)) }, onTargetLanguageSelected = { language -> - selectedTargetLanguage = language - if (selectedOriginLanguage?.nameResId == language?.nameResId) { - selectedOriginLanguage = null + if (language?.nameResId == selectedTargetLanguage?.nameResId) { + selectedTargetLanguage = null + updateConfig(exerciseConfig.copy(targetLanguageId = null)) + } else { + selectedTargetLanguage = language + if (selectedOriginLanguage?.nameResId == language?.nameResId) { + selectedOriginLanguage = null + updateConfig(exerciseConfig.copy(originLanguageId = null)) + } + updateConfig(exerciseConfig.copy(targetLanguageId = language?.nameResId)) } - updateConfig(exerciseConfig.copy(targetLanguageId = language?.nameResId)) }, + languageSelectionEnabled = true, + selectedPairsCount = selectedLanguagePairs.size, selectedOriginLanguage = selectedOriginLanguage, selectedTargetLanguage = selectedTargetLanguage ) @@ -249,6 +285,7 @@ fun TopBarSection( onShuffleCardsChanged: (Boolean) -> Unit, shuffleLanguages: Boolean, onShuffleLanguagesChanged: (Boolean) -> Unit, + shuffleLanguagesEnabled: Boolean, trainingMode: Boolean, onTrainingModeChanged: (Boolean) -> Unit ) { @@ -306,6 +343,7 @@ fun TopBarSection( onShuffleCardsChanged = onShuffleCardsChanged, shuffleLanguages = shuffleLanguages, onShuffleLanguagesChanged = onShuffleLanguagesChanged, + shuffleLanguagesEnabled = shuffleLanguagesEnabled, trainingMode = trainingMode, onTrainingModeChanged = onTrainingModeChanged, onDismiss = { @@ -348,9 +386,12 @@ fun SectionHeader(title: String, actionText: String? = null, onActionClick: () - @Composable fun LanguagePairSection( selectedPairs: List>, + availableLanguageIds: Set, onPairsChanged: (List>) -> Unit, onOriginLanguageSelected: (Language?) -> Unit, onTargetLanguageSelected: (Language?) -> Unit, + languageSelectionEnabled: Boolean, + selectedPairsCount: Int, selectedOriginLanguage: Language?, selectedTargetLanguage: Language? ) { @@ -360,9 +401,8 @@ fun LanguagePairSection( val languagesPresent by vocabularyViewModel.languagesPresent.collectAsState(initial = emptySet()) val allLanguages by languageViewModel.allLanguages.collectAsState(initial = emptyList()) - val availableLanguages = remember(languagesPresent, allLanguages) { - val presentIds = languagesPresent.filterNotNull().toSet() - allLanguages.filter { it.nameResId in presentIds } + val availableLanguages = remember(availableLanguageIds, allLanguages) { + allLanguages.filter { it.nameResId in availableLanguageIds } } val allItems by vocabularyViewModel.vocabularyItems.collectAsState(initial = emptyList()) @@ -433,6 +473,14 @@ fun LanguagePairSection( style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) + if (!languageSelectionEnabled && selectedPairsCount > 0) { + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(R.string.text_language_direction_disabled_with_pairs), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } Spacer(modifier = Modifier.height(12.dp)) Row( horizontalArrangement = Arrangement.spacedBy(16.dp), @@ -448,9 +496,15 @@ fun LanguagePairSection( modifier = Modifier.fillMaxWidth(), languageViewModel = languageViewModel, selectedLanguage = selectedOriginLanguage, - onLanguageSelected = onOriginLanguageSelected, + onLanguageSelected = { language -> + if (selectedTargetLanguage?.nameResId == language.nameResId) return@SingleLanguageDropDown + onOriginLanguageSelected(language) + }, showNoneOption = true, - alternateLanguages = availableLanguages + onNoneSelected = { onOriginLanguageSelected(null) }, + alternateLanguages = availableLanguages, + restrictToAlternateLanguages = true, + enabled = languageSelectionEnabled ) } Column(modifier = Modifier.weight(1f)) { @@ -463,9 +517,15 @@ fun LanguagePairSection( modifier = Modifier.fillMaxWidth(), languageViewModel = languageViewModel, selectedLanguage = selectedTargetLanguage, - onLanguageSelected = onTargetLanguageSelected, + onLanguageSelected = { language -> + if (selectedOriginLanguage?.nameResId == language.nameResId) return@SingleLanguageDropDown + onTargetLanguageSelected(language) + }, showNoneOption = true, - alternateLanguages = availableLanguages + onNoneSelected = { onTargetLanguageSelected(null) }, + alternateLanguages = availableLanguages, + restrictToAlternateLanguages = true, + enabled = languageSelectionEnabled ) } } @@ -807,6 +867,7 @@ private fun StartExerciseSettingsBottomSheet( onShuffleCardsChanged: (Boolean) -> Unit, shuffleLanguages: Boolean, onShuffleLanguagesChanged: (Boolean) -> Unit, + shuffleLanguagesEnabled: Boolean, trainingMode: Boolean, onTrainingModeChanged: (Boolean) -> Unit, onDismiss: () -> Unit @@ -835,9 +896,22 @@ private fun StartExerciseSettingsBottomSheet( OptionItemSwitch( title = stringResource(R.string.text_shuffle_languages), description = stringResource(R.string.text_shuffle_languages_description), - checked = shuffleLanguages, - onCheckedChange = onShuffleLanguagesChanged + checked = shuffleLanguages && shuffleLanguagesEnabled, + onCheckedChange = { enabled -> + if (shuffleLanguagesEnabled) { + onShuffleLanguagesChanged(enabled) + } else { + onShuffleLanguagesChanged(false) + } + } ) + if (!shuffleLanguagesEnabled) { + Text( + text = stringResource(R.string.text_shuffle_languages_disabled_by_direction), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } OptionItemSwitch( title = stringResource(R.string.label_training_mode), description = stringResource(R.string.text_training_mode_description), diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 292c845..533cd78 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -680,6 +680,7 @@ Sprachenrichtung Du kannst eine optionale Einstellung vornehmen, welche Sprache zuerst oder zweit kommen soll. + Entferne die Sprachpaar-Auswahl, um eine Richtung zu wählen. Raten Rechtschreibung Multiple Choice @@ -687,6 +688,7 @@ Nur Karten anzeigen, die heute fällig sind. Kartenmischung Mische die Reihenfolge der Sprachen. Beeinflusst nicht deine Sprachrichtungs-Einstellungen. + Deaktiviere die Sprachrichtungs-Einstellung, um das Mischen zu aktivieren. Konjugation: %1$s Einklappen Ausklappen diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 3f60a06..1117d39 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -685,6 +685,7 @@ Apenas perguntar cartas que estão a vencer hoje. Embaralhar Ordem das Cartas Embaralhar qual idioma vem primeiro. Não afeta as preferências de direção do idioma. + Desative a preferência de direção do idioma para habilitar o embaralhamento. Conjugação: %1$s Recolher Expandir @@ -844,6 +845,7 @@ Falha ao buscar informações de download sobre dicionários disponíveis: %1$s Defina o modelo para tradução e dê instruções opcionais sobre como traduzir. Você pode definir uma preferência opcional sobre qual idioma deve vir primeiro ou segundo. + Limpe a seleção de pares de idiomas para escolher uma direção. Todas as Categorias Defina um modelo para gerar conteúdo do dicionário e dê instruções opcionais. Acompanhamento de Progresso de Vocabulário diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9724e61..79bc19f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -824,6 +824,7 @@ Enter a word\n Language Code You can set an optional preference which language should come first or second. + Clear language pair selection to choose a direction. Language Options Last 7 Days Let AI find vocabulary for you! @@ -900,6 +901,7 @@ Shuffle Card Order Shuffle Languages Shuffle what language comes first. Does not affect language direction preferences. + Disable language direction preference to enable shuffling. Shuffle questions Some items are in the wrong category. Stage %1$s