Use a slider for several settings

This commit is contained in:
Aleksandras Kostarevas 2024-04-25 01:02:40 -04:00
parent 645c7d6e49
commit 5c9bada7ae
3 changed files with 188 additions and 54 deletions

View File

@ -15,6 +15,9 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
@ -22,24 +25,41 @@ import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextRange
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.runBlocking
import org.futo.inputmethod.latin.uix.SettingsKey import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.getSetting
import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.latin.uix.theme.Typography
import kotlin.math.pow
@Composable @Composable
fun ScreenTitle(title: String, showBack: Boolean = false, navController: NavHostController = rememberNavController()) { fun ScreenTitle(title: String, showBack: Boolean = false, navController: NavHostController = rememberNavController()) {
@ -243,6 +263,111 @@ fun<T> SettingRadio(
} }
} }
@Composable
fun<T: Number> SettingSlider(
title: String,
setting: SettingsKey<T>,
range: ClosedFloatingPointRange<Float>,
transform: (Float) -> T,
indicator: (T) -> String = { it.toString() },
hardRange: ClosedFloatingPointRange<Float> = range,
power: Float = 1.0f,
subtitle: String? = null
) {
val context = LocalContext.current
val (value, setValue) = useDataStore(key = setting.key, default = setting.default)
var virtualValue by remember { mutableFloatStateOf((runBlocking { context.getSetting(setting) }).toFloat().let {
if(it == Float.POSITIVE_INFINITY || it == Float.NEGATIVE_INFINITY) {
it
} else {
it.pow(1.0f / power)
}
}) }
var isTextFieldVisible by remember { mutableStateOf(false) }
var hasTextFieldFocusedYet by remember { mutableStateOf(false) }
var textFieldValue by remember(value) {
val s = value.toString()
mutableStateOf(TextFieldValue(
s,
selection = TextRange(0, s.length)
))
}
val focusRequester = remember { FocusRequester() }
LaunchedEffect(isTextFieldVisible) {
if(isTextFieldVisible) focusRequester.requestFocus()
}
ScreenTitle(title, showBack = false)
if(subtitle != null) {
Text(subtitle, style = Typography.bodyMedium, modifier = Modifier.padding(12.dp, 0.dp))
}
Row(modifier = Modifier.padding(16.dp, 0.dp)) {
if (isTextFieldVisible) {
val apply = {
if(isTextFieldVisible) {
val number = textFieldValue.text.trim().toFloatOrNull()
val newValue = if (number != null) {
transform(number.coerceIn(hardRange))
} else {
setting.default
}
setValue(newValue)
virtualValue = newValue.toFloat().pow(1.0f / power)
isTextFieldVisible = false
textFieldValue = TextFieldValue()
}
}
BasicTextField(
value = textFieldValue,
onValueChange = { textFieldValue = it },
modifier = Modifier
.weight(0.33f)
.align(Alignment.CenterVertically)
.focusRequester(focusRequester)
.onFocusChanged {
if(it.isFocused) hasTextFieldFocusedYet = true
else if(!it.isFocused && hasTextFieldFocusedYet) apply()
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
keyboardActions = KeyboardActions(
onDone = {
apply()
}
),
singleLine = true,
textStyle = Typography.labelMedium
)
} else {
Text(
text = indicator(value),
modifier = Modifier
.weight(0.33f)
.align(Alignment.CenterVertically)
.clickable {
hasTextFieldFocusedYet = false
isTextFieldVisible = true
},
style = Typography.labelMedium
)
}
Slider(
value = virtualValue,
onValueChange = {
virtualValue = it
setValue(transform(it.pow(power))) },
valueRange = range.start.pow(1.0f / power) .. range.endInclusive.pow(1.0f / power),
enabled = !isTextFieldVisible,
modifier = Modifier.weight(1.0f)
)
}
}
@Composable @Composable
fun ScrollableList(content: @Composable () -> Unit) { fun ScrollableList(content: @Composable () -> Unit) {
val scrollState = rememberScrollState() val scrollState = rememberScrollState()

View File

@ -1,20 +1,13 @@
package org.futo.inputmethod.latin.uix.settings.pages package org.futo.inputmethod.latin.uix.settings.pages
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import org.futo.inputmethod.latin.uix.THEME_KEY
import org.futo.inputmethod.latin.uix.settings.ScreenTitle import org.futo.inputmethod.latin.uix.settings.ScreenTitle
import org.futo.inputmethod.latin.uix.settings.ScrollableList import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.settings.SettingRadio import org.futo.inputmethod.latin.uix.settings.SettingSlider
import org.futo.inputmethod.latin.uix.settings.Tip import org.futo.inputmethod.latin.uix.settings.Tip
import org.futo.inputmethod.latin.uix.settings.useDataStore
import org.futo.inputmethod.latin.uix.theme.selector.ThemePicker
import org.futo.inputmethod.latin.xlm.AutocorrectThresholdSetting import org.futo.inputmethod.latin.xlm.AutocorrectThresholdSetting
import org.futo.inputmethod.latin.xlm.BinaryDictTransformerWeightSetting import org.futo.inputmethod.latin.xlm.BinaryDictTransformerWeightSetting
@ -24,50 +17,55 @@ fun AdvancedParametersScreen(navController: NavHostController = rememberNavContr
ScrollableList { ScrollableList {
ScreenTitle("Advanced Parameters", showBack = true, navController) ScreenTitle("Advanced Parameters", showBack = true, navController)
val optionsWeight = mapOf( Tip("Options below are experimental and may be removed or changed in the future as internal workings of the app change. Changing these values may have an adverse impact on your experience.")
Float.NEGATIVE_INFINITY to "always BinaryDictionary, except if blank",
0.0001f to "significantly favor BinaryDictionary, except if BinaryDictionary score < 0", SettingSlider(
0.01f to "favor BinaryDictionary", title = "Transformer LM strength",
0.1f to "favor BinaryDictionary", subtitle = "Lower value will make autocorrect behave more similarly to standard AOSP keyboard, while higher value will make it more dependent on the neural network\nDefault is ${BinaryDictTransformerWeightSetting.default}",
0.2f to "favor BinaryDictionary", setting = BinaryDictTransformerWeightSetting,
0.3f to "favor BinaryDictionary", range = 0.0f .. 100.0f,
0.4f to "favor BinaryDictionary", transform = {
0.5f to "favor BinaryDictionary", if(it > 99.9f) {
1.0f to "normal", Float.POSITIVE_INFINITY
2.0f to "favor TransformerLM", } else if(it < 0.0001f) {
4.0f to "significantly favor TransformerLM", Float.NEGATIVE_INFINITY
Float.POSITIVE_INFINITY to "always TransformerLM" } else {
) it
val namesWeight = optionsWeight.map { "a = ${it.key} (${it.value})" } }
SettingRadio( },
title = "Weight of Transformer LM suggestions with respect to BinaryDictionary", indicator = {
options = optionsWeight.keys.toList(), when {
optionNames = namesWeight, it == Float.POSITIVE_INFINITY -> {
setting = BinaryDictTransformerWeightSetting "always Transformer LM"
}
it == Float.NEGATIVE_INFINITY -> {
"always Binary Dictionary"
}
(it > 0.1f) -> {
"a = ${String.format("%.1f", it)}"
}
else -> {
"a = $it"
}
}
},
power = 2.5f
) )
SettingSlider(
Tip("Adjust the autocorrect threshold below. A lower threshold will autocorrect more often (and miscorrect more often), while a higher threshold will autocorrect less often (and miscorrect less often)" )
val options = mapOf(
0.0f to "none (94.6% : 5.4%)",
1.0f to "very low (93.4% : 4.3%)",
2.0f to "very low (91.2% : 2.4%)",
4.0f to "low (87.3% : 1.4%)",
6.0f to "low (no data)",
8.0f to "medium (82.3% : 0.9%)",
10.0f to "medium (80.1% : 0.8%)",
14.0f to "medium (no data)",
18.0f to "high (74.8% : 0.5%)",
25.0f to "high (71.6% : 0.4%)",
50.0f to "very high (63.5% : 0.3%)",
100.0f to "very high (54.7% : 0.2%)"
)
val names = options.map { "T = ${it.key}" }
SettingRadio(
title = "Autocorrect Threshold", title = "Autocorrect Threshold",
options = options.keys.toList(), subtitle = "A lower threshold will autocorrect more often (and miscorrect more often), while a higher threshold will autocorrect less often (and miscorrect less often).\nDefault is ${AutocorrectThresholdSetting.default}",
optionNames = names, setting = AutocorrectThresholdSetting,
setting = AutocorrectThresholdSetting range = 0.0f .. 25.0f,
hardRange = 0.0f .. 25.0f,
transform = {
it
},
indicator = {
"T = ${String.format("%.1f", it)}"
},
power = 2.5f
) )
} }
} }

View File

@ -3,7 +3,6 @@ package org.futo.inputmethod.latin.uix.settings.pages
import android.preference.PreferenceManager import android.preference.PreferenceManager
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.booleanResource import androidx.compose.ui.res.booleanResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -13,7 +12,6 @@ import androidx.datastore.preferences.core.intPreferencesKey
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.futo.inputmethod.latin.R import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.settings.Settings import org.futo.inputmethod.latin.settings.Settings
@ -22,11 +20,11 @@ import org.futo.inputmethod.latin.uix.SHOW_EMOJI_SUGGESTIONS
import org.futo.inputmethod.latin.uix.SettingsKey import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.settings.ScreenTitle import org.futo.inputmethod.latin.uix.settings.ScreenTitle
import org.futo.inputmethod.latin.uix.settings.ScrollableList import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.settings.SettingRadio import org.futo.inputmethod.latin.uix.settings.SettingSlider
import org.futo.inputmethod.latin.uix.settings.SettingToggleDataStore import org.futo.inputmethod.latin.uix.settings.SettingToggleDataStore
import org.futo.inputmethod.latin.uix.settings.SettingToggleSharedPrefs import org.futo.inputmethod.latin.uix.settings.SettingToggleSharedPrefs
import org.futo.inputmethod.latin.uix.settings.useDataStore import org.futo.inputmethod.latin.uix.settings.useDataStore
import org.futo.inputmethod.latin.uix.settings.useSharedPrefsBool import kotlin.math.roundToInt
val vibrationDurationSetting = SettingsKey( val vibrationDurationSetting = SettingsKey(
intPreferencesKey("vibration_duration"), intPreferencesKey("vibration_duration"),
@ -90,6 +88,19 @@ fun TypingScreen(navController: NavHostController = rememberNavController()) {
default = booleanResource(R.bool.config_default_key_preview_popup) default = booleanResource(R.bool.config_default_key_preview_popup)
) )
SettingRadio(title = "Vibration Duration", options = listOf(-1, 5, 10, 20, 40), optionNames = listOf("Default", "Low", "Medium", "High", "Higher"), setting = vibrationDurationSetting) SettingSlider(
title = "Vibration",
setting = vibrationDurationSetting,
range = -1.0f .. 100.0f,
hardRange = -1.0f .. 2000.0f,
transform = { it.roundToInt() },
indicator = {
if(it == -1) {
"Default"
} else {
"$it ms"
}
}
)
} }
} }