solve some todos in SubtypeDialog

This commit is contained in:
Helium314 2025-02-17 19:19:08 +01:00
parent 47070c1086
commit 40de17c148
4 changed files with 91 additions and 48 deletions

View file

@ -18,7 +18,7 @@ object LayoutUtils {
.apply { addAll(context.resources.getStringArray(R.array.predefined_layouts)) }
if (locale.script() == ScriptUtils.SCRIPT_LATIN)
return context.resources.getStringArray(R.array.predefined_layouts).toList()
return SubtypeSettings.getSubtypesForLocale(locale).mapNotNullTo(HashSet()) { it.mainLayoutName() }
return SubtypeSettings.getResourceSubtypesForLocale(locale).mapNotNullTo(HashSet()) { it.mainLayoutName() }
}
fun getLMainLayoutsForLocales(locales: List<Locale>, context: Context): Collection<String> =

View file

@ -224,8 +224,21 @@ public final class SubtypeLocaleUtils {
}
@NonNull
public static String getSubtypeDisplayNameInSystemLocale(
@NonNull final InputMethodSubtype subtype) {
public static String getDisplayNameInSystemLocale(@NonNull final String mainLayoutName, @NonNull final Locale locale) {
final String displayName = getMainLayoutDisplayName(mainLayoutName);
if (displayName != null) // works for custom and latin layouts
return displayName;
// we have some locale-specific layout
for (InputMethodSubtype subtype : SubtypeSettings.INSTANCE.getResourceSubtypesForLocale(locale)) {
final String main = LayoutType.Companion.getMainLayoutFromExtraValue(subtype.getExtraValue());
if (mainLayoutName.equals(main))
return getSubtypeDisplayNameInSystemLocale(subtype);
}
return mainLayoutName; // should never happen...
}
@NonNull
public static String getSubtypeDisplayNameInSystemLocale(@NonNull final InputMethodSubtype subtype) {
final Locale displayLocale = ConfigurationCompatKt.locale(sResources.getConfiguration());
return getSubtypeDisplayNameInternal(subtype, displayLocale);
}

View file

@ -137,7 +137,7 @@ object SubtypeSettings {
return !resourceSubtypesByLocale[locale].isNullOrEmpty()
}
fun getSubtypesForLocale(locale: Locale): List<InputMethodSubtype> = resourceSubtypesByLocale[locale].orEmpty()
fun getResourceSubtypesForLocale(locale: Locale): List<InputMethodSubtype> = resourceSubtypesByLocale[locale].orEmpty()
fun getAvailableSubtypeLocales(): Collection<Locale> {
require(initialized)

View file

@ -3,6 +3,7 @@ package helium314.keyboard.settings.dialogs
import android.content.Context
import android.view.inputmethod.InputMethodSubtype
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -12,6 +13,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@ -58,20 +60,16 @@ import helium314.keyboard.latin.utils.SubtypeSettings
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
import helium314.keyboard.latin.utils.getDictionaryLocales
import helium314.keyboard.latin.utils.getStringResourceOrName
import helium314.keyboard.latin.utils.locale
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.screens.GetIcon
import java.util.Locale
// todo:
// save when "editing" a resource subtypes is not working
// default buttons missing
// string resources
// dialog doesn't look good...
// settings upgrade to move the override settings to extra values, and actually use them (via getSelectedSubtype, not RichIMM)
@Composable
fun SubtypeDialog(
// could also use InputMethodSubtype if there is any advantage
// but as soon as anything is changed we will need an additional subtype anyway...
onDismissRequest: () -> Unit,
subtype: InputMethodSubtype,
onConfirmed: (SettingsSubtype) -> Unit,
@ -99,8 +97,9 @@ fun SubtypeDialog(
text = {
Column(
modifier = Modifier.verticalScroll(scrollState),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
WithSmallTitle("main layout") {
WithSmallTitle(stringResource(R.string.keyboard_layout_set)) {
val appLayouts = LayoutUtils.getAvailableLayouts(LayoutType.MAIN, ctx, currentSubtype.locale)
val customLayouts = LayoutUtilsCustom.getLayoutFiles(LayoutType.MAIN, ctx, currentSubtype.locale).map { it.name }
DropDownField(
@ -110,10 +109,9 @@ fun SubtypeDialog(
currentSubtype = currentSubtype.withLayout(LayoutType.MAIN, it)
}
) {
// todo: displayName can be complicated and may require an inputmethodsubtype...
// maybe search for stuff in resource subtypes?
Text(it)
Text(SubtypeLocaleUtils.getDisplayNameInSystemLocale(it, currentSubtype.locale))
// todo: edit button? or only for selected layout? and delete button?
// yes, even just to make clear what is custom
}
}
WithSmallTitle(stringResource(R.string.secondary_locale)) {
@ -121,7 +119,7 @@ fun SubtypeDialog(
val text = currentSubtype.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)
?.split(Separators.KV)?.joinToString(", ") {
LocaleUtils.getLocaleDisplayNameInSystemLocale(it.constructLocale(), ctx)
} ?: "none"
} ?: stringResource(R.string.action_none)
Text(text, Modifier.fillMaxWidth(), style = MaterialTheme.typography.bodyLarge)
}
}
@ -131,32 +129,56 @@ fun SubtypeDialog(
Text("not yet implemented")
}
// todo: this looks strange without the title
TextButton(onClick = { showKeyOrderDialog = true })
{ Text(stringResource(R.string.popup_order), Modifier.fillMaxWidth(), style = MaterialTheme.typography.bodyLarge) }
TextButton(onClick = { showHintOrderDialog = true })
{ Text(stringResource(R.string.hint_source), Modifier.fillMaxWidth(), style = MaterialTheme.typography.bodyLarge) }
if (currentSubtype.locale.script() == SCRIPT_LATIN)
Row {
TextButton(onClick = { showKeyOrderDialog = true }, Modifier.weight(1f))
{ Text(stringResource(R.string.popup_order), style = MaterialTheme.typography.bodyLarge) }
DefaultButton(
{ currentSubtype = currentSubtype.without(ExtraValue.POPUP_ORDER) },
currentSubtype.getExtraValueOf(ExtraValue.POPUP_ORDER) == null
)
}
Row {
TextButton(onClick = { showHintOrderDialog = true }, Modifier.weight(1f))
{ Text(stringResource(R.string.hint_source), style = MaterialTheme.typography.bodyLarge) }
DefaultButton(
{ currentSubtype = currentSubtype.without(ExtraValue.HINT_ORDER) },
currentSubtype.getExtraValueOf(ExtraValue.HINT_ORDER) == null
)
}
if (currentSubtype.locale.script() == SCRIPT_LATIN) {
WithSmallTitle(stringResource(R.string.show_popup_keys_title)) {
val explicitValue = currentSubtype.getExtraValueOf(ExtraValue.MORE_POPUPS)
val value = explicitValue ?: prefs.getString(Settings.PREF_MORE_POPUP_KEYS, Defaults.PREF_MORE_POPUP_KEYS)!!
TextButton(onClick = { showMorePopupsDialog = true }, Modifier.fillMaxWidth())
Row {
TextButton(onClick = { showMorePopupsDialog = true }, Modifier.weight(1f))
{ Text(stringResource(morePopupKeysResId(value))) }
DefaultButton(
{ currentSubtype = currentSubtype.without(ExtraValue.MORE_POPUPS) },
explicitValue == null
)
}
}
if (hasLocalizedNumberRow(currentSubtype.locale, ctx))
}
if (hasLocalizedNumberRow(currentSubtype.locale, ctx)) {
Row {
val checked = currentSubtype.getExtraValueOf(ExtraValue.LOCALIZED_NUMBER_ROW)?.toBoolean()
Text(stringResource(R.string.localized_number_row), Modifier.weight(1f))
Switch(
checked = currentSubtype.getExtraValueOf(ExtraValue.LOCALIZED_NUMBER_ROW)?.toBoolean()
?: prefs.getBoolean(Settings.PREF_LOCALIZED_NUMBER_ROW, Defaults.PREF_LOCALIZED_NUMBER_ROW),
checked = checked ?: prefs.getBoolean(Settings.PREF_LOCALIZED_NUMBER_ROW, Defaults.PREF_LOCALIZED_NUMBER_ROW),
onCheckedChange = {
currentSubtype = currentSubtype.with(ExtraValue.LOCALIZED_NUMBER_ROW, it.toString())
}
)
// todo: default button?
DefaultButton(
{ currentSubtype = currentSubtype.without(ExtraValue.LOCALIZED_NUMBER_ROW) },
checked == null
)
}
}
HorizontalDivider()
Text(stringResource(R.string.settings_screen_secondary_layouts), style = MaterialTheme.typography.titleMedium)
LayoutType.entries.forEach { type ->
if (type == LayoutType.MAIN) return@forEach
// todo: also some default button, to be shown when necessary, uses currentSubtype.withoutLayout(type)
WithSmallTitle(stringResource(type.displayNameId)) {
val explicitLayout = currentSubtype.layoutName(type)
val layout = explicitLayout ?: Settings.readDefaultLayoutName(type, prefs)
@ -167,10 +189,12 @@ fun SubtypeDialog(
selectedItem = layout,
onSelected = {
currentSubtype = currentSubtype.withLayout(type, it)
}
},
onDefault = { currentSubtype = currentSubtype.withoutLayout(type) },
isDefault = explicitLayout == null
) {
val displayName = if (LayoutUtilsCustom.isCustomLayout(it)) LayoutUtilsCustom.getDisplayName(it)
else it.getStringResourceOrName("layout_", ctx)
else it.getStringResourceOrName("layout_", ctx)
Text(displayName)
// content is name, and if it's user layout there is an edit button
// also maybe there should be an "add" button similar to the old settings
@ -183,8 +207,8 @@ fun SubtypeDialog(
if (showSecondaryLocaleDialog)
MultiListPickerDialog(
onDismissRequest = { showSecondaryLocaleDialog = false },
onConfirmed = {
val newValue = it.joinToString(Separators.KV) { it.toLanguageTag() }
onConfirmed = { locales ->
val newValue = locales.joinToString(Separators.KV) { it.toLanguageTag() }
currentSubtype = if (newValue.isEmpty()) currentSubtype.without(ExtraValue.SECONDARY_LOCALES)
else currentSubtype.with(ExtraValue.SECONDARY_LOCALES, newValue)
},
@ -201,8 +225,8 @@ fun SubtypeDialog(
title = stringResource(R.string.popup_order),
showDefault = setting != null,
onConfirmed = {
if (it == null) currentSubtype = currentSubtype.without(ExtraValue.POPUP_ORDER)
else currentSubtype = currentSubtype.with(ExtraValue.POPUP_ORDER, it)
currentSubtype = if (it == null) currentSubtype.without(ExtraValue.POPUP_ORDER)
else currentSubtype.with(ExtraValue.POPUP_ORDER, it)
}
)
}
@ -214,13 +238,12 @@ fun SubtypeDialog(
title = stringResource(R.string.hint_source),
showDefault = setting != null,
onConfirmed = {
if (it == null) currentSubtype = currentSubtype.without(ExtraValue.HINT_ORDER)
else currentSubtype = currentSubtype.with(ExtraValue.HINT_ORDER, it)
currentSubtype = if (it == null) currentSubtype.without(ExtraValue.HINT_ORDER)
else currentSubtype.with(ExtraValue.HINT_ORDER, it)
}
)
}
if (showMorePopupsDialog) {
// todo: default button in here? or next to the pref?
val items = listOf(POPUP_KEYS_NORMAL, POPUP_KEYS_MAIN, POPUP_KEYS_MORE, POPUP_KEYS_ALL)
val explicitValue = currentSubtype.getExtraValueOf(ExtraValue.MORE_POPUPS)
val value = explicitValue ?: prefs.getString(Settings.PREF_MORE_POPUP_KEYS, Defaults.PREF_MORE_POPUP_KEYS)
@ -262,7 +285,7 @@ private fun PopupOrderDialog(
var checked by rememberSaveable { mutableStateOf(item.state) }
Row(verticalAlignment = Alignment.CenterVertically) {
KeyboardIconsSet.instance.GetIcon(item.name)
val text = item.name.lowercase().getStringResourceOrName("", ctx)
val text = item.name.lowercase().getStringResourceOrName("popup_keys_", ctx)
Text(text, Modifier.weight(1f))
Switch(
checked = checked,
@ -290,6 +313,8 @@ private fun <T>DropDownField(
items: List<T>,
selectedItem: T,
onSelected: (T) -> Unit,
isDefault: Boolean? = null,
onDefault: () -> Unit = {},
itemContent: @Composable (T) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
@ -299,7 +324,7 @@ private fun <T>DropDownField(
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 4.dp)
modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)
) {
Box(Modifier.weight(1f)) {
itemContent(selectedItem)
@ -310,10 +335,12 @@ private fun <T>DropDownField(
) {
Icon(
painterResource(R.drawable.ic_arrow_left),
null,
"show dropdown",
Modifier.rotate(-90f)
)
}
if (isDefault != null)
DefaultButton(onDefault, isDefault)
}
}
DropdownMenu(
@ -329,16 +356,19 @@ private fun <T>DropDownField(
}
}
// get locales with same script as main locale, but different language
// todo: do we need any sort of force-ascii like in old variant?
// now we use hi-Latn and sr-Latn for the relevant subtypes, so it should be fine
// only potential issue is the Latn-default if we don't have the script for a locale,
// but in that case we should rather add the script to ScriptUtils
private fun getAvailableSecondaryLocales(context: Context, mainLocale: Locale): List<Locale> {
val locales = getDictionaryLocales(context)
locales.removeAll {
// it.language == mainLocale.language || it.script() != mainLocale.script()
it == mainLocale || it.script() != mainLocale.script() // todo: check whether this is fine, otherwise go back to the variant above
@Composable
private fun DefaultButton(
onDefault: () -> Unit,
isDefault: Boolean
) {
IconButton(
onClick = onDefault,
enabled = !isDefault
) {
Icon(painterResource(R.drawable.sym_keyboard_settings_holo), "default") // todo: more understandable icon!
}
return locales.toList()
}
private fun getAvailableSecondaryLocales(context: Context, mainLocale: Locale): List<Locale> =
getDictionaryLocales(context).filter { it != mainLocale && it.script() == mainLocale.script() }