work on appearance screen

This commit is contained in:
Helium314 2025-02-02 19:57:09 +01:00
parent d31bca1208
commit 22878578a7
7 changed files with 92 additions and 177 deletions

View file

@ -40,7 +40,7 @@ import helium314.keyboard.latin.R
import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.dialogs.SimpleListPickerDialog
import helium314.keyboard.settings.dialogs.ListPickerDialog
import helium314.keyboard.settings.dialogs.SliderDialog
// taken from StreetComplete (and a bit SCEE)
@ -230,6 +230,7 @@ fun <T: Any> ListPreference(
def: PrefDef,
items: List<Pair<String, T>>,
default: T,
onChanged: (T) -> Unit = { }
) {
var showDialog by remember { mutableStateOf(false) }
val prefs = LocalContext.current.prefs()
@ -240,12 +241,13 @@ fun <T: Any> ListPreference(
onClick = { showDialog = true }
)
if (showDialog) {
SimpleListPickerDialog(
ListPickerDialog(
onDismissRequest = { showDialog = false },
items = items,
onItemSelected = {
if (it != selected)
if (it == selected) return@ListPickerDialog
putPrefOfType(prefs, def.key, it.second)
onChanged(it.second)
},
selectedItem = selected,
title = { Text(def.title) },

View file

@ -16,12 +16,22 @@ import kotlinx.coroutines.flow.MutableStateFlow
// todo (roughly in order)
// make all prefs actually work
// appearance
// holo white selectable for non-holo style if we had selected holo theme before
// the list just stays the old one after changing the setting
// also, even if selected it should not be used...
// click on bg image does nothing when already set (but works after reload)
// split spacer scale setting does not reload?
// narrow key gaps setting is not changing properly?
// custom font loading not implemented
// have large bg image, and first-time load the keyboard on new search field -> bg image expands full size
// advanced
// preferences
// 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)
// try making text size similar to old state (also in dialogs)
// check whether dialogs have the same colors, i think currently it's a bit inconsistent
// see all the properties for each alertDialog -> any way to set it in a single place?
// title too huge for bg image and text on spacebar dialogs, also maybe somewhere else -> where to set in one place?
// check dark and light theme (don't have dynamic)
// rename both settingsActivities
// work on todos in other files

View file

@ -4,6 +4,7 @@ import android.graphics.drawable.VectorDrawable
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
@ -18,6 +19,7 @@ import androidx.compose.material3.LocalContentColor
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.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
@ -32,6 +34,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.util.TypedValueCompat
@ -50,26 +53,40 @@ fun CustomizeIconsDialog(
) {
val state = rememberLazyListState()
val ctx = LocalContext.current
val iconsAndNames = KeyboardIconsSet.getAllIcons(ctx).keys.map { iconName ->
var iconsAndNames by remember { mutableStateOf(
KeyboardIconsSet.getAllIcons(ctx).keys.map { iconName ->
val name = iconName.getStringResourceOrName("", ctx)
if (name == iconName) iconName to iconName.getStringResourceOrName("label_", ctx)
else iconName to name
}.sortedBy { it.second }
) }
fun reloadItem(iconName: String) {
iconsAndNames = iconsAndNames.map { item ->
if (item.first == iconName) {
item.first to if (item.second.endsWith(" ")) item.second.trimEnd() else item.second + " "
}
else item
}
}
var showIconDialog: Pair<String, String>? by remember { mutableStateOf(null) }
var showDeletePrefConfirmDialog by remember { mutableStateOf(false) }
val prefs = ctx.prefs()
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = { TextButton(onClick = onDismissRequest) { Text(stringResource(R.string.dialog_close)) } },
confirmButton = { },
dismissButton = {
Row {
if (prefs.contains(prefKey))
TextButton(onClick = { showDeletePrefConfirmDialog = true })
{ Text(stringResource(R.string.button_default)) }
Spacer(Modifier.weight(1f))
TextButton(onClick = onDismissRequest) { Text(stringResource(R.string.dialog_close)) }
}
},
title = { Text(stringResource(R.string.customize_icons)) },
text = {
LazyColumn(state = state) {
items(iconsAndNames, key = { it.first }) { (iconName, displayName) ->
items(iconsAndNames, key = { it.second }) { (iconName, displayName) ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { showIconDialog = iconName to displayName }
@ -80,6 +97,11 @@ fun CustomizeIconsDialog(
}
}
},
shape = MaterialTheme.shapes.medium,
containerColor = MaterialTheme.colorScheme.surface,
textContentColor = contentColorFor(MaterialTheme.colorScheme.surface),
properties = DialogProperties(),
)
if (showIconDialog != null) {
val iconName = showIconDialog!!.first
@ -91,7 +113,7 @@ fun CustomizeIconsDialog(
val icons = iconsSet.toList()
var selectedIcon by remember { mutableStateOf(KeyboardIconsSet.instance.iconIds[iconName]) }
ThreeButtonAlertDialog(
onDismissRequest = onDismissRequest,
onDismissRequest = { showIconDialog = null },
onConfirmed = {
runCatching {
val newIcons = customIconNames(prefs).toMutableMap()
@ -99,7 +121,7 @@ fun CustomizeIconsDialog(
prefs.edit().putString(prefKey, Json.encodeToString(newIcons)).apply()
KeyboardIconsSet.instance.loadIcons(ctx)
}
// todo: show outer dialog again and reload icons?
reloadItem(iconName)
},
neutralButtonText = if (customIconNames(prefs).contains(iconName)) stringResource(R.string.button_default) else null,
onNeutral = {
@ -110,7 +132,7 @@ fun CustomizeIconsDialog(
else prefs.edit().putString(prefKey, Json.encodeToString(icons2)).apply()
KeyboardIconsSet.instance.loadIcons(ctx)
}
// todo: show outer dialog again and reload icons?
reloadItem(iconName)
},
title = { Text(showIconDialog!!.second) },
text = {
@ -141,16 +163,19 @@ fun CustomizeIconsDialog(
},
)
}
if (showDeletePrefConfirmDialog)
if (showDeletePrefConfirmDialog) {
val ctx = LocalContext.current
ConfirmationDialog(
onDismissRequest = { showDeletePrefConfirmDialog = false },
onConfirmed = {
showDeletePrefConfirmDialog = false
onDismissRequest()
prefs.edit().remove(prefKey).apply()
KeyboardIconsSet.instance.loadIcons(ctx)
},
text = { Text(stringResource(R.string.customize_icons_reset_message)) }
)
}
}
@Preview

View file

@ -41,6 +41,7 @@ fun <T: Any> ListPickerDialog(
title: (@Composable () -> Unit)? = null,
selectedItem: T? = null,
getItemName: (@Composable (T) -> String) = { it.toString() },
confirmImmediately: Boolean = true,
width: Dp? = null,
height: Dp? = null,
shape: Shape = MaterialTheme.shapes.medium,
@ -58,6 +59,7 @@ fun <T: Any> ListPickerDialog(
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
if (!confirmImmediately)
TextButton(
onClick = { onDismissRequest(); selected?.let { onItemSelected(it) } },
enabled = selected != null,
@ -76,7 +78,13 @@ fun <T: Any> ListPickerDialog(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable { selected = item }
.clickable {
if (confirmImmediately) {
onDismissRequest()
onItemSelected(item)
}
selected = item
}
.padding(horizontal = 24.dp)
) {
Text(
@ -86,7 +94,13 @@ fun <T: Any> ListPickerDialog(
)
RadioButton(
selected = selected == item,
onClick = { selected = item }
onClick = {
if (confirmImmediately) {
onDismissRequest()
onItemSelected(item)
}
selected = item
}
)
}
}

View file

@ -1,138 +0,0 @@
package helium314.keyboard.settings.dialogs
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
// taken from StreetComplete
/** Similar to ListPickerDialog, but tapping on one item immediately closes the dialog
* (no OK button, no cancel button)
*
* This dialog doesn't have the caveat of the ListPickerDialog in that it takes as much width
* as possible */
@Composable
fun <T> SimpleListPickerDialog(
onDismissRequest: () -> Unit,
items: List<T>,
onItemSelected: (T) -> Unit,
title: (@Composable () -> Unit)? = null,
selectedItem: T? = null,
getItemName: (@Composable (T) -> String) = { it.toString() },
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(backgroundColor),
properties: DialogProperties = DialogProperties()
) {
val selected by remember { mutableStateOf(selectedItem) }
val state = rememberLazyListState()
fun select(item: T) {
onDismissRequest()
onItemSelected(item)
}
LaunchedEffect(selectedItem) {
val index = items.indexOf(selectedItem)
if (index != -1) state.scrollToItem(index, -state.layoutInfo.viewportSize.height / 3)
}
Dialog(
onDismissRequest = onDismissRequest,
properties = properties
) {
Surface(
shape = shape,
color = backgroundColor,
contentColor = contentColor
) {
Column(Modifier.padding(vertical = 24.dp)) {
if (title != null) {
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.titleLarge
) {
Column(Modifier.padding(start = 24.dp, bottom = 16.dp, end = 24.dp)) {
title()
}
}
}
if (state.canScrollBackward) HorizontalDivider()
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.bodyLarge
) {
LazyColumn(state = state) {
items(items) { item ->
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable { select(item) }
.padding(horizontal = 24.dp)
) {
Text(
text = getItemName(item),
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f),
)
RadioButton(
selected = selected == item,
onClick = { select(item) }
)
}
}
}
}
if (state.canScrollForward) HorizontalDivider()
// todo: button not visible when there are many entries
Row(Modifier.padding(end = 24.dp)) {
Spacer(Modifier.weight(1f))
TextButton(
onClick = onDismissRequest,
) { Text(stringResource(android.R.string.cancel)) }
}
}
}
}
}
@Preview
@Composable
private fun PreviewSimpleListPickerDialog() {
val items = remember { (0..<5).toList() }
SimpleListPickerDialog(
onDismissRequest = {},
items = items,
onItemSelected = {},
title = { Text("Select something") },
selectedItem = 2,
getItemName = { "Item $it" },
)
}

View file

@ -34,7 +34,6 @@ import helium314.keyboard.latin.settings.SettingsValues
import helium314.keyboard.latin.utils.Log
import helium314.keyboard.latin.utils.getActivity
import helium314.keyboard.latin.utils.getStringResourceOrName
import helium314.keyboard.latin.utils.infoDialog
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.latin.utils.switchTo
import helium314.keyboard.settings.AllPrefs
@ -92,7 +91,6 @@ fun AppearanceScreen(
SettingsActivity2.allPrefs.map[Settings.PREF_ENABLE_SPLIT_KEYBOARD]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_SPLIT_SPACER_SCALE]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_NARROW_KEY_GAPS]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_NARROW_KEY_GAPS]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_KEYBOARD_HEIGHT_SCALE]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_BOTTOM_PADDING_SCALE]!!.Preference()
SettingsActivity2.allPrefs.map[Settings.PREF_SPACE_BAR_TEXT]!!.Preference()
@ -122,7 +120,7 @@ fun createAppearancePrefs(context: Context) = listOf(
def,
items,
KeyboardTheme.STYLE_MATERIAL
)
) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_CUSTOM_ICON_NAMES, R.string.customize_icons) { def ->
var showDialog by remember { mutableStateOf(false) }
@ -142,7 +140,7 @@ fun createAppearancePrefs(context: Context) = listOf(
val ctx = LocalContext.current
val currentStyle = ctx.prefs().getString(Settings.PREF_THEME_STYLE, KeyboardTheme.STYLE_MATERIAL)
val items = KeyboardTheme.COLORS.mapNotNull {
if (it == KeyboardTheme.THEME_HOLO_WHITE && currentStyle == KeyboardTheme.STYLE_HOLO)
if (it == KeyboardTheme.THEME_HOLO_WHITE && currentStyle != KeyboardTheme.STYLE_HOLO)
return@mapNotNull null
it.getStringResourceOrName("theme_name_", ctx) to it
}
@ -150,7 +148,7 @@ fun createAppearancePrefs(context: Context) = listOf(
def,
items,
KeyboardTheme.THEME_LIGHT
)
) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_THEME_COLORS_NIGHT, R.string.theme_colors_night) { def ->
val ctx = LocalContext.current
@ -164,7 +162,7 @@ fun createAppearancePrefs(context: Context) = listOf(
def,
items,
KeyboardTheme.THEME_DARK
)
) { keyboardNeedsReload = true }
},
PrefDef(context, NonSettingsPrefs.ADJUST_COLORS, R.string.select_user_colors, R.string.select_user_colors_summary) { def ->
val ctx = LocalContext.current
@ -192,7 +190,7 @@ fun createAppearancePrefs(context: Context) = listOf(
SwitchPreference(def, false)
},
PrefDef(context, Settings.PREF_THEME_DAY_NIGHT, R.string.day_night_mode, R.string.day_night_mode_summary) { def ->
SwitchPreference(def, Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
SwitchPreference(def, Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_NAVBAR_COLOR, R.string.theme_navbar, R.string.day_night_mode_summary) { def ->
SwitchPreference(def, Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
@ -280,10 +278,10 @@ fun createAppearancePrefs(context: Context) = listOf(
default = SettingsValues.DEFAULT_SIZE_SCALE,
range = 0.5f..2f,
description = { "${(100 * it).toInt()}%" }
)
) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_NARROW_KEY_GAPS, R.string.prefs_narrow_key_gaps) {
SwitchPreference(it, false)
SwitchPreference(it, false) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_KEYBOARD_HEIGHT_SCALE, R.string.prefs_keyboard_height_scale) {
SliderPreference(
@ -292,7 +290,7 @@ fun createAppearancePrefs(context: Context) = listOf(
default = SettingsValues.DEFAULT_SIZE_SCALE,
range = 0.5f..1.5f,
description = { "${(100 * it).toInt()}%" }
)
) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_BOTTOM_PADDING_SCALE, R.string.prefs_bottom_padding_scale) {
SliderPreference(
@ -301,7 +299,7 @@ fun createAppearancePrefs(context: Context) = listOf(
default = SettingsValues.DEFAULT_SIZE_SCALE,
range = 0f..5f,
description = { "${(100 * it).toInt()}%" }
)
) { keyboardNeedsReload = true }
},
PrefDef(context, Settings.PREF_SPACE_BAR_TEXT, R.string.prefs_space_bar_text) { def ->
var showDialog by remember { mutableStateOf(false) }
@ -314,9 +312,13 @@ fun createAppearancePrefs(context: Context) = listOf(
if (showDialog) {
TextInputDialog(
onDismissRequest = { showDialog = false },
onConfirmed = { prefs.edit().putString(def.key, it).apply() },
onConfirmed = {
prefs.edit().putString(def.key, it).apply()
keyboardNeedsReload = true
},
initialText = prefs.getString(def.key, "") ?: "",
title = { Text(def.title) }
title = { Text(def.title) },
checkTextValid = { true }
)
}
},

View file

@ -73,7 +73,7 @@ fun createDebugPrefs(context: Context) = listOf(
if (!it) prefs.edit().putBoolean(DebugSettings.PREF_SHOW_SUGGESTION_INFOS, false).apply()
showConfirmDialog = true
}
if (showConfirmDialog) {
if (showConfirmDialog) { // todo: maybe do it differently?
ConfirmationDialog(
onDismissRequest = { showConfirmDialog = false },
onConfirmed = { Runtime.getRuntime().exit(0) },
@ -87,7 +87,7 @@ fun createDebugPrefs(context: Context) = listOf(
PrefDef(context, DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, R.string.prefs_force_non_distinct_multitouch) { def ->
var showConfirmDialog by remember { mutableStateOf(false) }
SwitchPreference(def, false) { showConfirmDialog = true }
if (showConfirmDialog) {
if (showConfirmDialog) { // todo: maybe do it differently?
ConfirmationDialog(
onDismissRequest = { showConfirmDialog = false },
onConfirmed = { Runtime.getRuntime().exit(0) },