Add slider for long press duration

This commit is contained in:
Aleksandras Kostarevas 2024-05-08 13:02:36 -05:00
parent abda1a3d08
commit 49cbcabf5e
3 changed files with 138 additions and 20 deletions

View File

@ -23,7 +23,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowForward import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.filled.Send import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.ExperimentalMaterial3Api
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
@ -48,6 +47,7 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged 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.SolidColor
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.platform.LocalContext
@ -58,9 +58,7 @@ 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.getSettingBlocking import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.latin.uix.theme.Typography
import kotlin.math.pow import kotlin.math.pow
@ -268,20 +266,22 @@ fun<T> SettingRadio(
} }
@Composable @Composable
fun<T: Number> SettingSlider( private fun<T: Number> SettingSliderForDataStoreItem(
title: String, title: String,
setting: SettingsKey<T>, item: DataStoreItem<T>,
default: T,
range: ClosedFloatingPointRange<Float>, range: ClosedFloatingPointRange<Float>,
transform: (Float) -> T, transform: (Float) -> T,
indicator: (T) -> String = { it.toString() }, indicator: (T) -> String = { it.toString() },
hardRange: ClosedFloatingPointRange<Float> = range, hardRange: ClosedFloatingPointRange<Float> = range,
power: Float = 1.0f, power: Float = 1.0f,
subtitle: String? = null subtitle: String? = null,
steps: Int = 0,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val (value, setValue) = useDataStore(key = setting.key, default = setting.default) val (value, setValue) = item
var virtualValue by remember { mutableFloatStateOf((runBlocking { context.getSetting(setting) }).toFloat().let { var virtualValue by remember { mutableFloatStateOf(value.toFloat().let {
if(it == Float.POSITIVE_INFINITY || it == Float.NEGATIVE_INFINITY) { if(it == Float.POSITIVE_INFINITY || it == Float.NEGATIVE_INFINITY) {
it it
} else { } else {
@ -317,7 +317,7 @@ fun<T: Number> SettingSlider(
val newValue = if (number != null) { val newValue = if (number != null) {
transform(number.coerceIn(hardRange)) transform(number.coerceIn(hardRange))
} else { } else {
setting.default default
} }
setValue(newValue) setValue(newValue)
@ -327,12 +327,13 @@ fun<T: Number> SettingSlider(
textFieldValue = TextFieldValue() textFieldValue = TextFieldValue()
} }
} }
BasicTextField( BasicTextField(
value = textFieldValue, value = textFieldValue,
onValueChange = { textFieldValue = it }, onValueChange = { textFieldValue = it },
modifier = Modifier modifier = Modifier
.weight(0.33f) .weight(0.33f)
.align(Alignment.CenterVertically) .align(CenterVertically)
.focusRequester(focusRequester) .focusRequester(focusRequester)
.onFocusChanged { .onFocusChanged {
if (it.isFocused) hasTextFieldFocusedYet = true if (it.isFocused) hasTextFieldFocusedYet = true
@ -345,9 +346,9 @@ fun<T: Number> SettingSlider(
} }
), ),
singleLine = true, singleLine = true,
textStyle = Typography.labelMedium textStyle = Typography.labelMedium.copy(color = MaterialTheme.colorScheme.onBackground),
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary)
) )
} else { } else {
Text( Text(
text = indicator(value), text = indicator(value),
@ -368,12 +369,68 @@ fun<T: Number> SettingSlider(
setValue(transform(it.pow(power))) }, setValue(transform(it.pow(power))) },
valueRange = range.start.pow(1.0f / power) .. range.endInclusive.pow(1.0f / power), valueRange = range.start.pow(1.0f / power) .. range.endInclusive.pow(1.0f / power),
enabled = !isTextFieldVisible, enabled = !isTextFieldVisible,
modifier = Modifier.weight(1.0f) modifier = Modifier.weight(1.0f),
steps = steps
) )
} }
} }
} }
@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,
steps: Int = 0
) {
SettingSliderForDataStoreItem(
title = title,
item = useDataStore(setting),
default = setting.default,
range = range,
transform = transform,
indicator = indicator,
hardRange = hardRange,
power = power,
subtitle = subtitle,
steps = steps
)
}
@Composable
fun SettingSliderSharedPrefsInt(
title: String,
key: String,
default: Int,
range: ClosedFloatingPointRange<Float>,
transform: (Float) -> Int,
indicator: (Int) -> String = { it.toString() },
hardRange: ClosedFloatingPointRange<Float> = range,
power: Float = 1.0f,
subtitle: String? = null,
steps: Int = 0
) {
SettingSliderForDataStoreItem(
title = title,
item = useSharedPrefsInt(key, default),
default = default,
range = range,
transform = transform,
indicator = indicator,
hardRange = hardRange,
power = power,
subtitle = subtitle,
steps = steps
)
}
@Composable @Composable
fun ScrollableList(content: @Composable () -> Unit) { fun ScrollableList(content: @Composable () -> Unit) {
val scrollState = rememberScrollState() val scrollState = rememberScrollState()

View File

@ -4,6 +4,7 @@ import android.content.SharedPreferences
import android.preference.PreferenceManager import android.preference.PreferenceManager
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -80,19 +81,19 @@ fun <T> useDataStore(key: SettingsKey<T>): DataStoreItem<T> {
} }
@Composable @Composable
fun useSharedPrefsBool(key: String, default: Boolean): DataStoreItem<Boolean> { fun<T> useSharedPrefsGeneric(key: String, default: T, get: (SharedPreferences, String, T) -> T, put: (SharedPreferences, String, T) -> Unit): DataStoreItem<T> {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current val context = LocalContext.current
val sharedPrefs = remember { PreferenceManager.getDefaultSharedPreferences(context) } val sharedPrefs = remember { PreferenceManager.getDefaultSharedPreferences(context) }
val value = remember { mutableStateOf(sharedPrefs.getBoolean(key, default)) } val value = remember { mutableStateOf(get(sharedPrefs, key, default)) }
// This is not the most efficient way to do this... but it works for a settings menu // This is not the most efficient way to do this... but it works for a settings menu
DisposableEffect(Unit) { DisposableEffect(Unit) {
val listener = val listener =
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, changedKey -> SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, changedKey ->
if (key == changedKey) { if (key == changedKey) {
value.value = sharedPreferences.getBoolean(key, value.value) value.value = get(sharedPreferences, key, value.value)
} }
} }
@ -103,15 +104,63 @@ fun useSharedPrefsBool(key: String, default: Boolean): DataStoreItem<Boolean> {
} }
} }
val setValue = { newValue: Boolean -> val setValue = { newValue: T ->
coroutineScope.launch { coroutineScope.launch {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
sharedPrefs.edit { put(sharedPrefs, key, newValue)
putBoolean(key, newValue)
}
} }
} }
} }
return DataStoreItem(value.value, setValue) return DataStoreItem(value.value, setValue)
}
@Composable
fun useSharedPrefsBool(key: String, default: Boolean): DataStoreItem<Boolean> {
return useSharedPrefsGeneric(key, default,
get = { sharedPreferences, k, d ->
sharedPreferences.getBoolean(k, d)
},
put = { sharedPreferences, k, v ->
sharedPreferences.edit {
putBoolean(k, v)
}
}
)
}
@Composable
fun useSharedPrefsInt(key: String, default: Int): DataStoreItem<Int> {
return useSharedPrefsGeneric(key, default,
get = { sharedPreferences, k, d ->
sharedPreferences.getInt(k, d)
},
put = { sharedPreferences, k, v ->
sharedPreferences.edit {
putInt(k, v)
}
}
)
}
@Composable
private fun<T> SyncDataStoreToPreferences(key: SettingsKey<T>, update: (newValue: T, editor: SharedPreferences.Editor) -> Unit) {
val context = LocalContext.current
val sharedPrefs = remember { PreferenceManager.getDefaultSharedPreferences(context) }
val value = useDataStoreValueBlocking(key)
LaunchedEffect(value) {
val edit = sharedPrefs.edit {
update(value, this)
}
}
}
@Composable
fun SyncDataStoreToPreferencesInt(key: SettingsKey<Int>, sharedPreference: String) {
SyncDataStoreToPreferences(key) { value, editor ->
editor.putInt(sharedPreference, value)
}
} }

View File

@ -21,6 +21,7 @@ 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.SettingSlider import org.futo.inputmethod.latin.uix.settings.SettingSlider
import org.futo.inputmethod.latin.uix.settings.SettingSliderSharedPrefsInt
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
@ -107,5 +108,16 @@ fun TypingScreen(navController: NavHostController = rememberNavController()) {
} }
} }
) )
SettingSliderSharedPrefsInt(
title = "Long Press Duration",
key = Settings.PREF_KEY_LONGPRESS_TIMEOUT,
default = 300,
range = 100.0f .. 700.0f,
hardRange = 25.0f .. 1200.0f,
transform = { it.roundToInt() },
indicator = { "$it ms" },
steps = 23
)
} }
} }