some todos in SubtypeDialog

This commit is contained in:
Helium314 2025-02-23 17:58:04 +01:00
parent e3fa210031
commit 4edaa485b6
2 changed files with 114 additions and 96 deletions

View file

@ -21,6 +21,7 @@ import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -51,6 +52,7 @@ import helium314.keyboard.latin.utils.LayoutType
import helium314.keyboard.latin.utils.LayoutType.Companion.displayNameId
import helium314.keyboard.latin.utils.LayoutUtils
import helium314.keyboard.latin.utils.LayoutUtilsCustom
import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_LATIN
import helium314.keyboard.latin.utils.ScriptUtils.script
import helium314.keyboard.latin.utils.SettingsSubtype
@ -58,22 +60,21 @@ import helium314.keyboard.latin.utils.SettingsSubtype.Companion.toSettingsSubtyp
import helium314.keyboard.latin.utils.SubtypeLocaleUtils
import helium314.keyboard.latin.utils.SubtypeSettings
import helium314.keyboard.latin.utils.SubtypeUtilsAdditional
import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.getDictionaryLocales
import helium314.keyboard.latin.utils.getSecondaryLocales
import helium314.keyboard.latin.utils.getStringResourceOrName
import helium314.keyboard.latin.utils.mainLayoutName
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.SettingsActivity
import helium314.keyboard.settings.layoutFilePicker
import helium314.keyboard.settings.layoutIntent
import helium314.keyboard.settings.screens.GetIcon
import java.util.Locale
// todo:
// fix the display name (why is the layout always added now e.g. after adding a secondary locale, when it's not there initially?)
// dropdown content is not refreshed on deleting a layout, maybe need to do the pref listener trick
// layout edit dialog does not care about ime padding when called from here
// layout edit dialog does not care about ime padding when called from here, why?
// rotating closes the dialog
// split this up a little, it's too long... and maybe we could re-use parts other dialogs?
@Composable
fun SubtypeDialog(
onDismissRequest: () -> Unit,
@ -83,6 +84,9 @@ fun SubtypeDialog(
// todo: make sure the values are always correct (e.g. if using rememberSaveable and rotating)
val ctx = LocalContext.current
val prefs = ctx.prefs()
val b = (LocalContext.current.getActivity() as? SettingsActivity)?.prefChanged?.collectAsState()
if ((b?.value ?: 0) < 0)
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
var currentSubtype by remember { mutableStateOf(subtype.toSettingsSubtype()) }
val availableLocalesForScript = getAvailableSecondaryLocales(ctx, currentSubtype.locale).sortedBy { it.toLanguageTag() }
var showSecondaryLocaleDialog by remember { mutableStateOf(false) }
@ -90,6 +94,7 @@ fun SubtypeDialog(
var showHintOrderDialog by remember { mutableStateOf(false) }
var showMorePopupsDialog by remember { mutableStateOf(false) }
val scrollState = rememberScrollState()
val customMainLayouts = LayoutUtilsCustom.getLayoutFiles(LayoutType.MAIN, ctx, currentSubtype.locale).map { it.name }
ThreeButtonAlertDialog(
onDismissRequest = onDismissRequest,
onConfirmed = { onConfirmed(currentSubtype) },
@ -105,91 +110,7 @@ fun SubtypeDialog(
modifier = Modifier.verticalScroll(scrollState),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
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 }
var showAddLayoutDialog by remember { mutableStateOf(false) }
var showLayoutEditDialog: Pair<String, String?>? by remember { mutableStateOf(null) }
val layoutPicker = layoutFilePicker { content, name ->
showLayoutEditDialog = (name ?: "new layout") to content
}
DropDownField(
items = appLayouts + customLayouts,
selectedItem = currentSubtype.mainLayoutName() ?: "qwerty",
onSelected = {
currentSubtype = currentSubtype.withLayout(LayoutType.MAIN, it)
}
) {
var showLayoutDeleteDialog by remember { mutableStateOf(false) }
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(SubtypeLocaleUtils.getDisplayNameInSystemLocale(it, currentSubtype.locale))
Row (verticalAlignment = Alignment.CenterVertically) {
Icon(painterResource(R.drawable.ic_edit), stringResource(R.string.edit_layout), Modifier.clickable { showLayoutEditDialog = it to null })
if (it in customLayouts && subtype.mainLayoutName() != it) // don't allow current main layout
Icon(painterResource(R.drawable.ic_bin), stringResource(R.string.delete), Modifier.clickable { showLayoutDeleteDialog = true })
}
}
if (showLayoutDeleteDialog) {
val others = SubtypeSettings.getAdditionalSubtypes().filter { st -> st.mainLayoutName() == it }.any { it != subtype }
ConfirmationDialog(
onDismissRequest = { showLayoutDeleteDialog = false },
confirmButtonText = stringResource(R.string.delete),
title = { Text(stringResource(R.string.delete_layout, LayoutUtilsCustom.getDisplayName(it))) },
text = { if (others) Text(stringResource(R.string.layout_in_use)) },
onConfirmed = {
if (it == currentSubtype.mainLayoutName())
currentSubtype = currentSubtype.withoutLayout(LayoutType.MAIN)
LayoutUtilsCustom.deleteLayout(it, LayoutType.MAIN, ctx)
}
)
}
}
// todo: should be in same row as DropDownField
// maybe make the default button more customizable
IconButton(
{ showAddLayoutDialog = true }
) { Icon(painterResource(R.drawable.ic_plus), stringResource(R.string.button_title_add_custom_layout)) }
if (showLayoutEditDialog != null) {
val layoutName = showLayoutEditDialog!!.first
val startContent = showLayoutEditDialog?.second
?: if (layoutName in appLayouts) LayoutUtils.getContent(LayoutType.MAIN, layoutName, ctx)
else null
LayoutEditDialog(
onDismissRequest = { showLayoutEditDialog = null },
layoutType = LayoutType.MAIN,
initialLayoutName = layoutName,
startContent = startContent,
locale = currentSubtype.locale,
// only can edit name for new custom layout
isNameValid = if (layoutName in customLayouts) null else ({ it !in customLayouts }),
onEdited = {
if (layoutName !in customLayouts)
currentSubtype = currentSubtype.withLayout(LayoutType.MAIN, it)
}
)
}
if (showAddLayoutDialog) {
// todo: maybe supply link to discussion section for layouts
// todo: no html for compose, so message is broken
// try annotatedString
val link = "<a href='$LAYOUT_FORMAT_URL'>" + ctx.getString(R.string.dictionary_link_text) + "</a>"
ConfirmationDialog(
onDismissRequest = { showAddLayoutDialog = false },
title = { Text(stringResource(R.string.button_title_add_custom_layout)) },
text = { Text(stringResource(R.string.message_add_custom_layout, link)) },
onConfirmed = { showLayoutEditDialog = "new layout" to "" },
neutralButtonText = stringResource(R.string.button_load_custom),
onNeutral = {
showAddLayoutDialog = false
layoutPicker.launch(layoutIntent)
}
)
}
}
MainLayoutRow(subtype, currentSubtype, customMainLayouts) { currentSubtype = it }
if (availableLocalesForScript.size > 1) {
WithSmallTitle(stringResource(R.string.secondary_locale)) {
TextButton(onClick = { showSecondaryLocaleDialog = true }) {
@ -261,8 +182,10 @@ fun SubtypeDialog(
onSelected = {
currentSubtype = currentSubtype.withLayout(type, it)
},
extraButton = { DefaultButton(
onDefault = { currentSubtype = currentSubtype.withoutLayout(type) },
isDefault = explicitLayout == null
) },
) {
val displayName = if (LayoutUtilsCustom.isCustomLayout(it)) LayoutUtilsCustom.getDisplayName(it)
else it.getStringResourceOrName("layout_", ctx)
@ -296,7 +219,7 @@ fun SubtypeDialog(
currentSubtype = if (newValue.isEmpty()) currentSubtype.without(ExtraValue.SECONDARY_LOCALES)
else currentSubtype.with(ExtraValue.SECONDARY_LOCALES, newValue)
},
title = { Text("languages with dictionaries") }, // todo: string resource
title = { Text(stringResource(R.string.locales_with_dict)) },
items = availableLocalesForScript,
initialSelection = currentSubtype.getExtraValueOf(ExtraValue.SECONDARY_LOCALES)
?.split(Separators.KV)?.map { it.constructLocale() }.orEmpty(),
@ -382,6 +305,100 @@ private fun PopupOrderDialog(
)
}
@Composable
private fun MainLayoutRow(
subtype: InputMethodSubtype,
currentSubtype: SettingsSubtype,
customLayouts: List<String>,
setCurrentSubtype: (SettingsSubtype) -> Unit,
) {
val ctx = LocalContext.current
WithSmallTitle(stringResource(R.string.keyboard_layout_set)) {
val appLayouts = LayoutUtils.getAvailableLayouts(LayoutType.MAIN, ctx, currentSubtype.locale)
var showAddLayoutDialog by remember { mutableStateOf(false) }
var showLayoutEditDialog: Pair<String, String?>? by remember { mutableStateOf(null) }
val layoutPicker = layoutFilePicker { content, name ->
showLayoutEditDialog = (name ?: "new layout") to content
}
DropDownField(
items = appLayouts + customLayouts,
selectedItem = currentSubtype.mainLayoutName() ?: SubtypeLocaleUtils.QWERTY,
onSelected = {
setCurrentSubtype(currentSubtype.withLayout(LayoutType.MAIN, it))
},
extraButton = {
IconButton({ showAddLayoutDialog = true })
{ Icon(painterResource(R.drawable.ic_plus), stringResource(R.string.button_title_add_custom_layout)) }
}
) {
var showLayoutDeleteDialog by remember { mutableStateOf(false) }
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(SubtypeLocaleUtils.getDisplayNameInSystemLocale(it, currentSubtype.locale))
Row (verticalAlignment = Alignment.CenterVertically) {
Icon(painterResource(R.drawable.ic_edit), stringResource(R.string.edit_layout), Modifier.clickable { showLayoutEditDialog = it to null })
if (it in customLayouts && subtype.mainLayoutName() != it) // don't allow current main layout
Icon(painterResource(R.drawable.ic_bin), stringResource(R.string.delete), Modifier.clickable { showLayoutDeleteDialog = true })
}
}
if (showLayoutDeleteDialog) {
val others = SubtypeSettings.getAdditionalSubtypes().filter { st -> st.mainLayoutName() == it }.any { it != subtype }
ConfirmationDialog(
onDismissRequest = { showLayoutDeleteDialog = false },
confirmButtonText = stringResource(R.string.delete),
title = { Text(stringResource(R.string.delete_layout, LayoutUtilsCustom.getDisplayName(it))) },
text = { if (others) Text(stringResource(R.string.layout_in_use)) },
onConfirmed = {
if (it == currentSubtype.mainLayoutName())
setCurrentSubtype(currentSubtype.withoutLayout(LayoutType.MAIN))
LayoutUtilsCustom.deleteLayout(it, LayoutType.MAIN, ctx)
(ctx.getActivity() as? SettingsActivity)?.prefChanged?.value = 1234
}
)
}
}
if (showLayoutEditDialog != null) {
val layoutName = showLayoutEditDialog!!.first
val startContent = showLayoutEditDialog?.second
?: if (layoutName in appLayouts) LayoutUtils.getContent(LayoutType.MAIN, layoutName, ctx)
else null
LayoutEditDialog(
onDismissRequest = { showLayoutEditDialog = null },
layoutType = LayoutType.MAIN,
initialLayoutName = layoutName,
startContent = startContent,
locale = currentSubtype.locale,
// only can edit name for new custom layout
isNameValid = if (layoutName in customLayouts) null else ({ it !in customLayouts }),
onEdited = {
if (layoutName !in customLayouts)
setCurrentSubtype(currentSubtype.withLayout(LayoutType.MAIN, it))
}
)
}
if (showAddLayoutDialog) {
// todo: maybe supply link to discussion section for layouts
// todo: no html for compose, so message is broken
// try annotatedString
val link = "<a href='$LAYOUT_FORMAT_URL'>" + ctx.getString(R.string.dictionary_link_text) + "</a>"
ConfirmationDialog(
onDismissRequest = { showAddLayoutDialog = false },
title = { Text(stringResource(R.string.button_title_add_custom_layout)) },
text = { Text(stringResource(R.string.message_add_custom_layout, link)) },
onConfirmed = { showLayoutEditDialog = "new layout" to "" },
neutralButtonText = stringResource(R.string.button_load_custom),
onNeutral = {
showAddLayoutDialog = false
layoutPicker.launch(layoutIntent)
}
)
}
}
}
@Composable
private fun WithSmallTitle(
description: String,
@ -398,8 +415,7 @@ fun <T>DropDownField(
items: List<T>,
selectedItem: T,
onSelected: (T) -> Unit,
isDefault: Boolean? = null,
onDefault: () -> Unit = {},
extraButton: @Composable (() -> Unit)? = null,
itemContent: @Composable (T) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
@ -423,8 +439,8 @@ fun <T>DropDownField(
Modifier.rotate(-90f)
)
}
if (isDefault != null)
DefaultButton(onDefault, isDefault)
if (extraButton != null)
extraButton()
}
}
DropdownMenu(

View file

@ -196,6 +196,8 @@
<string name="button_restore">Restore</string>
<!-- Preferences item for choosing secondary language -->
<string name="secondary_locale">Multilingual typing</string>
<!-- Clarification which locales are available for multilingual typing -->
<string name="locales_with_dict">Languages with dictionaries</string>
<!-- Preferences item for loading an external gesture typing library -->
<string name="load_gesture_library">Load gesture typing library</string>
<!-- Description for "load_gesture_library" option -->