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.rememberScrollState
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.material.icons.Icons
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.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
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.Companion.CenterVertically
import androidx.compose.ui.Modifier
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.ColorFilter
import androidx.compose.ui.graphics.drawscope.translate
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.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.runBlocking
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.getSetting
import org.futo.inputmethod.latin.uix.theme.Typography
import kotlin.math.pow
@Composable
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
fun ScrollableList(content: @Composable () -> Unit) {
val scrollState = rememberScrollState()

View File

@ -1,20 +1,13 @@
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.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavHostController
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.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.useDataStore
import org.futo.inputmethod.latin.uix.theme.selector.ThemePicker
import org.futo.inputmethod.latin.xlm.AutocorrectThresholdSetting
import org.futo.inputmethod.latin.xlm.BinaryDictTransformerWeightSetting
@ -24,50 +17,55 @@ fun AdvancedParametersScreen(navController: NavHostController = rememberNavContr
ScrollableList {
ScreenTitle("Advanced Parameters", showBack = true, navController)
val optionsWeight = mapOf(
Float.NEGATIVE_INFINITY to "always BinaryDictionary, except if blank",
0.0001f to "significantly favor BinaryDictionary, except if BinaryDictionary score < 0",
0.01f to "favor BinaryDictionary",
0.1f to "favor BinaryDictionary",
0.2f to "favor BinaryDictionary",
0.3f to "favor BinaryDictionary",
0.4f to "favor BinaryDictionary",
0.5f to "favor BinaryDictionary",
1.0f to "normal",
2.0f to "favor TransformerLM",
4.0f to "significantly favor TransformerLM",
Float.POSITIVE_INFINITY to "always TransformerLM"
)
val namesWeight = optionsWeight.map { "a = ${it.key} (${it.value})" }
SettingRadio(
title = "Weight of Transformer LM suggestions with respect to BinaryDictionary",
options = optionsWeight.keys.toList(),
optionNames = namesWeight,
setting = BinaryDictTransformerWeightSetting
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.")
SettingSlider(
title = "Transformer LM strength",
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}",
setting = BinaryDictTransformerWeightSetting,
range = 0.0f .. 100.0f,
transform = {
if(it > 99.9f) {
Float.POSITIVE_INFINITY
} else if(it < 0.0001f) {
Float.NEGATIVE_INFINITY
} else {
it
}
},
indicator = {
when {
it == Float.POSITIVE_INFINITY -> {
"always Transformer LM"
}
it == Float.NEGATIVE_INFINITY -> {
"always Binary Dictionary"
}
(it > 0.1f) -> {
"a = ${String.format("%.1f", it)}"
}
else -> {
"a = $it"
}
}
},
power = 2.5f
)
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(
SettingSlider(
title = "Autocorrect Threshold",
options = options.keys.toList(),
optionNames = names,
setting = AutocorrectThresholdSetting
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}",
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 androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.booleanResource
import androidx.compose.ui.res.stringResource
@ -13,7 +12,6 @@ import androidx.datastore.preferences.core.intPreferencesKey
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.futo.inputmethod.latin.R
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.settings.ScreenTitle
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.SettingToggleSharedPrefs
import org.futo.inputmethod.latin.uix.settings.useDataStore
import org.futo.inputmethod.latin.uix.settings.useSharedPrefsBool
import kotlin.math.roundToInt
val vibrationDurationSetting = SettingsKey(
intPreferencesKey("vibration_duration"),
@ -90,6 +88,19 @@ fun TypingScreen(navController: NavHostController = rememberNavController()) {
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"
}
}
)
}
}