diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/SubtypeDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/SubtypeDialog.kt index 4a32d2536..f52a50557 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/SubtypeDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/SubtypeDialog.kt @@ -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? 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 = "" + ctx.getString(R.string.dictionary_link_text) + "" - 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) }, - onDefault = { currentSubtype = currentSubtype.withoutLayout(type) }, - isDefault = explicitLayout == null + 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, + 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? 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 = "" + ctx.getString(R.string.dictionary_link_text) + "" + 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 DropDownField( items: List, 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 DropDownField( Modifier.rotate(-90f) ) } - if (isDefault != null) - DefaultButton(onDefault, isDefault) + if (extraButton != null) + extraButton() } } DropdownMenu( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2d28ab00d..f2e698806 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -196,6 +196,8 @@ Restore Multilingual typing + + Languages with dictionaries Load gesture typing library