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)) } .apply { addAll(context.resources.getStringArray(R.array.predefined_layouts)) }
if (locale.script() == ScriptUtils.SCRIPT_LATIN) if (locale.script() == ScriptUtils.SCRIPT_LATIN)
return context.resources.getStringArray(R.array.predefined_layouts).toList() 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> = fun getLMainLayoutsForLocales(locales: List<Locale>, context: Context): Collection<String> =

View file

@ -224,8 +224,21 @@ public final class SubtypeLocaleUtils {
} }
@NonNull @NonNull
public static String getSubtypeDisplayNameInSystemLocale( public static String getDisplayNameInSystemLocale(@NonNull final String mainLayoutName, @NonNull final Locale locale) {
@NonNull final InputMethodSubtype subtype) { 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()); final Locale displayLocale = ConfigurationCompatKt.locale(sResources.getConfiguration());
return getSubtypeDisplayNameInternal(subtype, displayLocale); return getSubtypeDisplayNameInternal(subtype, displayLocale);
} }

View file

@ -137,7 +137,7 @@ object SubtypeSettings {
return !resourceSubtypesByLocale[locale].isNullOrEmpty() 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> { fun getAvailableSubtypeLocales(): Collection<Locale> {
require(initialized) require(initialized)

View file

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