From 31b2a36bcc2dbd5b925497b28250488a15d7f407 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sun, 2 Feb 2025 15:04:40 +0100 Subject: [PATCH] add dialog with default button --- .../keyboard/settings/SettingsActivity.kt | 3 - .../settings/dialogs/CustomizeIconsDialog.kt | 54 ++++++++++++----- .../settings/dialogs/LayoutEditDialog.kt | 11 +++- .../settings/dialogs/ReorderDialog.kt | 17 +++--- .../keyboard/settings/dialogs/SliderDialog.kt | 24 +++----- .../settings/dialogs/TextInputDialog.kt | 33 +++++------ .../dialogs/ThreeButtonAlertDialog.kt | 58 ++++++++++++++++++ .../settings/dialogs/ToolbarKeysCustomizer.kt | 59 ++++++++++++++----- .../settings/screens/AppearanceScreen.kt | 4 ++ .../settings/screens/ToolbarScreen.kt | 2 + 10 files changed, 183 insertions(+), 82 deletions(-) create mode 100644 app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt b/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt index e6a20f0b0..f8b63a5d3 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt @@ -14,9 +14,6 @@ import helium314.keyboard.latin.utils.prefs import kotlinx.coroutines.flow.MutableStateFlow // todo (roughly in order) -// default buttons for toolbar key(s) customizer, icon customizer, and toolbar reorder dialog -// make a dialog wrapper that has a default button? -// yes, definitely need a 3 button dialog... // make all prefs actually work // try moving the recomposition of pref change somewhere else, so it's not duplicated everywhere // make the pref lists more compact (compare with old settings) diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/CustomizeIconsDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/CustomizeIconsDialog.kt index 3034e76e6..de1148722 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/CustomizeIconsDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/CustomizeIconsDialog.kt @@ -56,11 +56,16 @@ fun CustomizeIconsDialog( else iconName to name }.sortedBy { it.second } var showIconDialog: Pair? by remember { mutableStateOf(null) } + var showDeletePrefConfirmDialog by remember { mutableStateOf(false) } val prefs = ctx.prefs() AlertDialog( onDismissRequest = onDismissRequest, - confirmButton = { }, // no confirm button - dismissButton = { TextButton(onClick = onDismissRequest) { Text(stringResource(R.string.dialog_close)) } }, + confirmButton = { TextButton(onClick = onDismissRequest) { Text(stringResource(R.string.dialog_close)) } }, + dismissButton = { + if (prefs.contains(prefKey)) + TextButton(onClick = { showDeletePrefConfirmDialog = true }) + { Text(stringResource(R.string.button_default)) } + }, title = { Text(stringResource(R.string.customize_icons)) }, text = { LazyColumn(state = state) { @@ -82,24 +87,31 @@ fun CustomizeIconsDialog( val iconsForName = allIcons[iconName].orEmpty() val iconsSet = mutableSetOf() iconsSet.addAll(iconsForName) - KeyboardIconsSet.getAllIcons(ctx).forEach { iconsSet.addAll(it.value) } // is this called again on UI interaction? + KeyboardIconsSet.getAllIcons(ctx).forEach { iconsSet.addAll(it.value) } // todo: is this called again on UI interaction? val icons = iconsSet.toList() var selectedIcon by remember { mutableStateOf(KeyboardIconsSet.instance.iconIds[iconName]) } - AlertDialog( + ThreeButtonAlertDialog( onDismissRequest = onDismissRequest, - confirmButton = { TextButton( - onClick = { - onDismissRequest() - runCatching { - val newIcons = customIconNames(prefs).toMutableMap() - newIcons[iconName] = selectedIcon?.let { ctx.resources.getResourceEntryName(it) } ?: return@runCatching - prefs.edit().putString(prefKey, Json.encodeToString(newIcons)).apply() - KeyboardIconsSet.instance.loadIcons(ctx) - } - // todo: show outer dialog again and reload icons? + onConfirmed = { + runCatching { + val newIcons = customIconNames(prefs).toMutableMap() + newIcons[iconName] = selectedIcon?.let { ctx.resources.getResourceEntryName(it) } ?: return@runCatching + prefs.edit().putString(prefKey, Json.encodeToString(newIcons)).apply() + KeyboardIconsSet.instance.loadIcons(ctx) } - ) { Text(stringResource(android.R.string.ok)) } }, - dismissButton = { TextButton(onClick = onDismissRequest) { Text(stringResource(android.R.string.cancel)) } }, + // todo: show outer dialog again and reload icons? + }, + neutralButtonText = if (customIconNames(prefs).contains(iconName)) stringResource(R.string.button_default) else null, + onNeutral = { + runCatching { + val icons2 = customIconNames(prefs).toMutableMap() + icons2.remove(iconName) + if (icons2.isEmpty()) prefs.edit().remove(prefKey).apply() + else prefs.edit().putString(prefKey, Json.encodeToString(icons2)).apply() + KeyboardIconsSet.instance.loadIcons(ctx) + } + // todo: show outer dialog again and reload icons? + }, title = { Text(showIconDialog!!.second) }, text = { LazyVerticalGrid( @@ -129,6 +141,16 @@ fun CustomizeIconsDialog( }, ) } + if (showDeletePrefConfirmDialog) + ConfirmationDialog( + onDismissRequest = { showDeletePrefConfirmDialog = false }, + onConfirmed = { + showDeletePrefConfirmDialog = false + onDismissRequest() + prefs.edit().remove(prefKey).apply() + }, + text = { Text(stringResource(R.string.customize_icons_reset_message)) } + ) } @Preview diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutEditDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutEditDialog.kt index 95194ab64..8cbf51a53 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutEditDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/LayoutEditDialog.kt @@ -28,8 +28,6 @@ fun LayoutEditDialog( val initialText = startContent ?: file.readText() var showDeleteConfirmation by remember { mutableStateOf(false) } // todo: try make it really full width, at least if we have a json file - // todo: ok button should be "save" - // todo: if displayName not null, there is an existing file TextInputDialog( onDismissRequest = onDismissRequest, onConfirmed = { @@ -38,13 +36,20 @@ fun LayoutEditDialog( onCustomLayoutFileListChanged() keyboardNeedsReload = true }, + confirmButtonText = stringResource(R.string.save), + neutralButtonText = if (displayName != null && file.exists()) stringResource(R.string.delete) else null, + onNeutral = { + if (!file.exists()) return@TextInputDialog + file.delete() + onCustomLayoutFileListChanged() + keyboardNeedsReload = true + }, initialText = initialText, singleLine = false, title = { Text(displayName ?: getLayoutDisplayName(layoutName)) }, checkTextValid = { checkLayout(it, ctx) // todo: toast with reason why it doesn't work -> should re-do getting the reason }, - // todo: delete button if displayName not null and file exists ) if (showDeleteConfirmation) ConfirmationDialog( diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/ReorderDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/ReorderDialog.kt index 66531e26d..006f02b8d 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/ReorderDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/ReorderDialog.kt @@ -43,8 +43,8 @@ fun ReorderDialog( displayItem: @Composable (T) -> Unit, modifier: Modifier = Modifier, title: @Composable (() -> Unit)? = null, - confirmButtonText: String = stringResource(android.R.string.ok), - cancelButtonText: String = stringResource(android.R.string.cancel), + onNeutral: () -> Unit = { }, + neutralButtonText: String? = null, shape: Shape = MaterialTheme.shapes.medium, backgroundColor: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(backgroundColor), @@ -58,13 +58,12 @@ fun ReorderDialog( add(to.index, removeAt(from.index)) } } - AlertDialog( + ThreeButtonAlertDialog( onDismissRequest = onDismissRequest, - confirmButton = { - TextButton(onClick = { onConfirmed(reorderableItems); onDismissRequest() }) { Text(confirmButtonText) } - }, + onConfirmed = { onConfirmed(reorderableItems) }, + onNeutral = onNeutral, + neutralButtonText = neutralButtonText, modifier = modifier, - dismissButton = { TextButton(onClick = onDismissRequest) { Text(cancelButtonText) } }, title = title, text = { LazyColumn( @@ -93,8 +92,8 @@ fun ReorderDialog( } }, shape = shape, - containerColor = backgroundColor, - textContentColor = contentColor, + backgroundColor = backgroundColor, + contentColor = contentColor, properties = properties, ) } diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/SliderDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/SliderDialog.kt index ea583de30..e87bee78e 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/SliderDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/SliderDialog.kt @@ -32,7 +32,7 @@ fun SliderDialog( range: ClosedFloatingPointRange, modifier: Modifier = Modifier, showDefault: Boolean = false, - onDefault: () -> Unit? = { }, + onDefault: () -> Unit = { }, onValueChanged: (Float) -> Unit = { }, title: (@Composable () -> Unit)? = null, intermediateSteps: Int? = null, @@ -44,21 +44,11 @@ fun SliderDialog( ) { var sliderPosition by remember { mutableFloatStateOf(initialValue) } - AlertDialog( + ThreeButtonAlertDialog( onDismissRequest = onDismissRequest, - confirmButton = { // mis-use the confirm button and put everything in there - Row { - if (showDefault) - TextButton( - onClick = { onDismissRequest(); onDefault() } - ) { Text(stringResource(R.string.button_default)) } - Spacer(modifier.weight(1f)) - TextButton(onClick = onDismissRequest) { Text(stringResource(android.R.string.cancel)) } - TextButton( - onClick = { onDismissRequest(); onDone(sliderPosition) }, - ) { Text(stringResource(android.R.string.ok)) } - } - }, + neutralButtonText = if (showDefault) stringResource(R.string.button_default) else null, + onNeutral = onDefault, + onConfirmed = { onDone(sliderPosition) }, modifier = modifier, title = title, text = { @@ -86,8 +76,8 @@ fun SliderDialog( } }, shape = shape, - containerColor = backgroundColor, - textContentColor = contentColor, + backgroundColor = backgroundColor, + contentColor = contentColor, properties = properties, ) } diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/TextInputDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/TextInputDialog.kt index 3dce756e6..963e127a0 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/TextInputDialog.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/TextInputDialog.kt @@ -2,11 +2,9 @@ package helium314.keyboard.settings.dialogs import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.AlertDialog import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -26,7 +24,7 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.window.DialogProperties -// taken from StreetComplete / SCEE +// mostly taken from StreetComplete / SCEE /** Dialog with which to input text. OK button is only clickable if [checkTextValid] returns true. */ @Composable fun TextInputDialog( @@ -34,6 +32,9 @@ fun TextInputDialog( onConfirmed: (text: String) -> Unit, modifier: Modifier = Modifier, title: @Composable (() -> Unit)? = null, + onNeutral: () -> Unit = { }, + neutralButtonText: String? = null, + confirmButtonText: String = stringResource(android.R.string.ok), initialText: String = "", textInputLabel: @Composable (() -> Unit)? = null, shape: Shape = MaterialTheme.shapes.medium, @@ -52,34 +53,30 @@ fun TextInputDialog( LaunchedEffect(initialText) { focusRequester.requestFocus() } - AlertDialog( + ThreeButtonAlertDialog( onDismissRequest = onDismissRequest, - confirmButton = { - TextButton( - enabled = value.text.isNotBlank() && checkTextValid(value.text), - onClick = { onDismissRequest(); onConfirmed(value.text) } - ) { - Text(stringResource(android.R.string.ok)) - } - }, - dismissButton = { - TextButton(onClick = onDismissRequest) { Text(stringResource(android.R.string.cancel)) } - }, + onConfirmed = { onConfirmed(value.text) }, + confirmButtonText = confirmButtonText, + checkOk = { checkTextValid(value.text) }, + neutralButtonText = neutralButtonText, + onNeutral = onNeutral, modifier = modifier, title = title, text = { OutlinedTextField( value = value, onValueChange = { value = it }, - modifier = Modifier.fillMaxWidth().focusRequester(focusRequester), + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester), label = textInputLabel, keyboardOptions = KeyboardOptions(keyboardType = keyboardType), singleLine = singleLine ) }, shape = shape, - containerColor = backgroundColor, - textContentColor = contentColor, + backgroundColor = backgroundColor, + contentColor = contentColor, properties = properties, ) } diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt new file mode 100644 index 000000000..f263f2939 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/ThreeButtonAlertDialog.kt @@ -0,0 +1,58 @@ +package helium314.keyboard.settings.dialogs + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.window.DialogProperties + +@Composable +fun ThreeButtonAlertDialog( + onDismissRequest: () -> Unit, + onConfirmed: () -> Unit, + modifier: Modifier = Modifier, + title: @Composable (() -> Unit)? = null, + text: @Composable (() -> Unit)? = null, + onNeutral: () -> Unit = { }, + checkOk: () -> Boolean = { true }, + confirmButtonText: String = stringResource(android.R.string.ok), + cancelButtonText: String = stringResource(android.R.string.cancel), + neutralButtonText: String? = null, + shape: Shape = MaterialTheme.shapes.medium, + backgroundColor: Color = MaterialTheme.colorScheme.surface, + contentColor: Color = contentColorFor(backgroundColor), + properties: DialogProperties = DialogProperties(), +) { + AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = { // mis-use the confirm button and put everything in there + Row { + if (neutralButtonText != null) + TextButton( + onClick = { onDismissRequest(); onNeutral() } + ) { Text(neutralButtonText) } + Spacer(modifier.weight(1f)) + TextButton(onClick = onDismissRequest) { Text(cancelButtonText) } + TextButton( + enabled = checkOk(), + onClick = { onDismissRequest(); onConfirmed() }, + ) { Text(confirmButtonText) } + } + }, + modifier = modifier, + title = title, + text = text, + shape = shape, + containerColor = backgroundColor, + textContentColor = contentColor, + properties = properties, + ) +} diff --git a/app/src/main/java/helium314/keyboard/settings/dialogs/ToolbarKeysCustomizer.kt b/app/src/main/java/helium314/keyboard/settings/dialogs/ToolbarKeysCustomizer.kt index 2c131efb3..2c2994880 100644 --- a/app/src/main/java/helium314/keyboard/settings/dialogs/ToolbarKeysCustomizer.kt +++ b/app/src/main/java/helium314/keyboard/settings/dialogs/ToolbarKeysCustomizer.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.window.DialogProperties +import androidx.core.content.edit import helium314.keyboard.keyboard.internal.KeyboardIconsSet import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode.checkAndConvertCode import helium314.keyboard.latin.R @@ -39,21 +40,28 @@ import helium314.keyboard.latin.utils.readCustomLongpressCodes import helium314.keyboard.latin.utils.writeCustomKeyCodes import helium314.keyboard.latin.utils.writeCustomLongpressCodes import helium314.keyboard.settings.screens.GetIcon +import kotlinx.serialization.json.Json -// todo: -// reading and writing prefs should be done in the preference, or at least with the provided (single!) key +// todo (later): reading and writing prefs should be done in the preference, or at least with the provided (single!) pref key @Composable fun ToolbarKeysCustomizer( onDismissRequest: () -> Unit ) { val ctx = LocalContext.current + val prefs = ctx.prefs() var showKeyCustomizer: ToolbarKey? by remember { mutableStateOf(null) } + var showDeletePrefConfirmDialog by remember { mutableStateOf(false) } AlertDialog( onDismissRequest = onDismissRequest, confirmButton = { TextButton(onClick = onDismissRequest) { Text(stringResource(R.string.dialog_close)) } }, - dismissButton = { }, + dismissButton = { + if (readCustomKeyCodes(prefs).isNotEmpty() || readCustomLongpressCodes(prefs).isNotEmpty()) + TextButton( + onClick = { showDeletePrefConfirmDialog = true } + ) { Text(stringResource(R.string.button_default)) } + }, title = { Text(stringResource(R.string.customize_toolbar_key_codes)) }, text = { LazyColumn { @@ -78,8 +86,22 @@ fun ToolbarKeysCustomizer( if (shownKey != null) ToolbarKeyCustomizer(shownKey) { showKeyCustomizer = null } } + if (showDeletePrefConfirmDialog) + ConfirmationDialog( + onDismissRequest = { showDeletePrefConfirmDialog = false }, + onConfirmed = { + showDeletePrefConfirmDialog = false + onDismissRequest() + prefs.edit { + remove(Settings.PREF_TOOLBAR_CUSTOM_KEY_CODES) + remove(Settings.PREF_TOOLBAR_CUSTOM_LONGPRESS_CODES) + } + }, + text = { Text(stringResource(R.string.customize_toolbar_key_code_reset_message)) } + ) } +// todo: show updated ToolbarKeysCustomizer after ok / default? because default button @Composable private fun ToolbarKeyCustomizer( key: ToolbarKey, @@ -89,19 +111,24 @@ private fun ToolbarKeyCustomizer( val prefs = ctx.prefs() var code by remember { mutableStateOf(TextFieldValue(getCodeForToolbarKey(key).toString())) } var longPressCode by remember { mutableStateOf(TextFieldValue(getCodeForToolbarKeyLongClick(key).toString())) } - AlertDialog( + ThreeButtonAlertDialog( onDismissRequest = onDismissRequest, - confirmButton = { - TextButton( - onClick = { - writeCustomKeyCodes(prefs, readCustomKeyCodes(prefs) + (key.name to checkCode(code))) - writeCustomLongpressCodes(prefs, readCustomLongpressCodes(prefs) + (key.name to checkCode(longPressCode))) - onDismissRequest() - }, - enabled = checkCode(code) != null && checkCode(longPressCode) != null - ) { Text(stringResource(android.R.string.ok)) } + onConfirmed = { + writeCustomKeyCodes(prefs, readCustomKeyCodes(prefs) + (key.name to checkCode(code))) + writeCustomLongpressCodes(prefs, readCustomLongpressCodes(prefs) + (key.name to checkCode(longPressCode))) + }, + checkOk = { checkCode(code) != null && checkCode(longPressCode) != null }, + neutralButtonText = if (readCustomKeyCodes(prefs).containsKey(key.name) || readCustomLongpressCodes(prefs).containsKey(key.name)) + stringResource(R.string.button_default) + else null, + onNeutral = { + val keys = readCustomKeyCodes(prefs).toMutableMap() + keys.remove(key.name) + prefs.edit().putString(Settings.PREF_TOOLBAR_CUSTOM_KEY_CODES, Json.encodeToString(keys)).apply() + val longpressKeys = readCustomLongpressCodes(prefs).toMutableMap() + longpressKeys.remove(key.name) + prefs.edit().putString(Settings.PREF_TOOLBAR_CUSTOM_LONGPRESS_CODES, Json.encodeToString(longpressKeys)).apply() }, - dismissButton = { TextButton(onClick = onDismissRequest) { Text(stringResource(android.R.string.cancel)) } }, title = { Text(key.name.lowercase().getStringResourceOrName("", ctx)) }, text = { Column { @@ -126,8 +153,8 @@ private fun ToolbarKeyCustomizer( } }, shape = MaterialTheme.shapes.medium, - containerColor = MaterialTheme.colorScheme.surface, - textContentColor = contentColorFor(MaterialTheme.colorScheme.surface), + backgroundColor = MaterialTheme.colorScheme.surface, + contentColor = contentColorFor(MaterialTheme.colorScheme.surface), properties = DialogProperties(), ) } diff --git a/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt index 228234f50..0785cde9f 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/AppearanceScreen.kt @@ -131,6 +131,10 @@ fun createAppearancePrefs(context: Context) = listOf( onClick = { showDialog = true } ) if (showDialog) { + if (keyboardNeedsReload) { + KeyboardSwitcher.getInstance().forceUpdateKeyboardTheme(LocalContext.current) + keyboardNeedsReload = false + } CustomizeIconsDialog(def.key) { showDialog = false } } }, diff --git a/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt index 1d0edc77f..617d0e507 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/ToolbarScreen.kt @@ -165,6 +165,8 @@ fun ToolbarKeyReorderDialog( keyboardNeedsReload = true }, onDismissRequest = onDismiss, + onNeutral = { prefs.edit().remove(prefKey).apply() }, + neutralButtonText = if (prefs.contains(prefKey)) stringResource(R.string.button_default) else null, items = items, title = { Text(title) }, displayItem = { item ->