From 49cbcabf5e33572becb97be80b48224275a33c16 Mon Sep 17 00:00:00 2001 From: Aleksandras Kostarevas Date: Wed, 8 May 2024 13:02:36 -0500 Subject: [PATCH] Add slider for long press duration --- .../latin/uix/settings/Components.kt | 83 ++++++++++++++++--- .../inputmethod/latin/uix/settings/Hooks.kt | 63 ++++++++++++-- .../latin/uix/settings/pages/Typing.kt | 12 +++ 3 files changed, 138 insertions(+), 20 deletions(-) diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/Components.kt b/java/src/org/futo/inputmethod/latin/uix/settings/Components.kt index 796666018..3e9dab9cd 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/Components.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/Components.kt @@ -23,7 +23,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowForward import androidx.compose.material.icons.filled.Send -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme 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.graphics.Color import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.painter.Painter 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.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.getSettingBlocking import org.futo.inputmethod.latin.uix.theme.Typography import kotlin.math.pow @@ -268,20 +266,22 @@ fun SettingRadio( } @Composable -fun SettingSlider( +private fun SettingSliderForDataStoreItem( title: String, - setting: SettingsKey, + item: DataStoreItem, + default: T, range: ClosedFloatingPointRange, transform: (Float) -> T, indicator: (T) -> String = { it.toString() }, hardRange: ClosedFloatingPointRange = range, power: Float = 1.0f, - subtitle: String? = null + subtitle: String? = null, + steps: Int = 0, ) { 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 { + val (value, setValue) = item + var virtualValue by remember { mutableFloatStateOf(value.toFloat().let { if(it == Float.POSITIVE_INFINITY || it == Float.NEGATIVE_INFINITY) { it } else { @@ -317,7 +317,7 @@ fun SettingSlider( val newValue = if (number != null) { transform(number.coerceIn(hardRange)) } else { - setting.default + default } setValue(newValue) @@ -327,12 +327,13 @@ fun SettingSlider( textFieldValue = TextFieldValue() } } + BasicTextField( value = textFieldValue, onValueChange = { textFieldValue = it }, modifier = Modifier .weight(0.33f) - .align(Alignment.CenterVertically) + .align(CenterVertically) .focusRequester(focusRequester) .onFocusChanged { if (it.isFocused) hasTextFieldFocusedYet = true @@ -345,9 +346,9 @@ fun SettingSlider( } ), singleLine = true, - textStyle = Typography.labelMedium + textStyle = Typography.labelMedium.copy(color = MaterialTheme.colorScheme.onBackground), + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary) ) - } else { Text( text = indicator(value), @@ -368,12 +369,68 @@ fun SettingSlider( 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) + modifier = Modifier.weight(1.0f), + steps = steps ) } } } + + +@Composable +fun SettingSlider( + title: String, + setting: SettingsKey, + range: ClosedFloatingPointRange, + transform: (Float) -> T, + indicator: (T) -> String = { it.toString() }, + hardRange: ClosedFloatingPointRange = 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, + transform: (Float) -> Int, + indicator: (Int) -> String = { it.toString() }, + hardRange: ClosedFloatingPointRange = 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 fun ScrollableList(content: @Composable () -> Unit) { val scrollState = rememberScrollState() diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/Hooks.kt b/java/src/org/futo/inputmethod/latin/uix/settings/Hooks.kt index ad0f2bb55..3240c83f9 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/Hooks.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/Hooks.kt @@ -4,6 +4,7 @@ import android.content.SharedPreferences import android.preference.PreferenceManager import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -80,19 +81,19 @@ fun useDataStore(key: SettingsKey): DataStoreItem { } @Composable -fun useSharedPrefsBool(key: String, default: Boolean): DataStoreItem { +fun useSharedPrefsGeneric(key: String, default: T, get: (SharedPreferences, String, T) -> T, put: (SharedPreferences, String, T) -> Unit): DataStoreItem { val coroutineScope = rememberCoroutineScope() val context = LocalContext.current 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 DisposableEffect(Unit) { val listener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, 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 { } } - val setValue = { newValue: Boolean -> + val setValue = { newValue: T -> coroutineScope.launch { withContext(Dispatchers.Main) { - sharedPrefs.edit { - putBoolean(key, newValue) - } + put(sharedPrefs, key, newValue) } } } return DataStoreItem(value.value, setValue) +} + + +@Composable +fun useSharedPrefsBool(key: String, default: Boolean): DataStoreItem { + 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 { + return useSharedPrefsGeneric(key, default, + get = { sharedPreferences, k, d -> + sharedPreferences.getInt(k, d) + }, + put = { sharedPreferences, k, v -> + sharedPreferences.edit { + putInt(k, v) + } + } + ) +} + + +@Composable +private fun SyncDataStoreToPreferences(key: SettingsKey, 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, sharedPreference: String) { + SyncDataStoreToPreferences(key) { value, editor -> + editor.putInt(sharedPreference, value) + } } \ No newline at end of file diff --git a/java/src/org/futo/inputmethod/latin/uix/settings/pages/Typing.kt b/java/src/org/futo/inputmethod/latin/uix/settings/pages/Typing.kt index 866d40c43..b17528a4c 100644 --- a/java/src/org/futo/inputmethod/latin/uix/settings/pages/Typing.kt +++ b/java/src/org/futo/inputmethod/latin/uix/settings/pages/Typing.kt @@ -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.ScrollableList 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.SettingToggleSharedPrefs 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 + ) } } \ No newline at end of file