gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-android] 10/14: [wallet] Improved AmountInputField with a V


From: gnunet
Subject: [taler-taler-android] 10/14: [wallet] Improved AmountInputField with a VisualTransformation
Date: Tue, 26 Sep 2023 18:31:30 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a commit to branch master
in repository taler-android.

commit 66d96c5b18c878f545c2081ed5526271dd39125b
Author: Iván Ávalos <avalos@disroot.org>
AuthorDate: Fri Sep 15 00:01:49 2023 -0600

    [wallet] Improved AmountInputField with a VisualTransformation
---
 .../java/net/taler/wallet/ReceiveFundsFragment.kt  |  2 +-
 .../java/net/taler/wallet/SendFundsFragment.kt     |  2 +-
 .../net/taler/wallet/compose/AmountInputField.kt   | 82 +++++++++++++++++-----
 .../net/taler/wallet/deposit/PayToUriFragment.kt   |  2 +-
 4 files changed, 69 insertions(+), 19 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt 
b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
index 1511128..e560a71 100644
--- a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
@@ -124,7 +124,7 @@ private fun ReceiveFundsIntro(
             .fillMaxWidth()
             .verticalScroll(scrollState),
     ) {
-        var text by rememberSaveable { mutableStateOf("") }
+        var text by rememberSaveable { mutableStateOf("0") }
         var isError by rememberSaveable { mutableStateOf(false) }
         Row(
             verticalAlignment = Alignment.CenterVertically,
diff --git a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt 
b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
index 2e5eb52..b33e53b 100644
--- a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
@@ -105,7 +105,7 @@ private fun SendFundsIntro(
             .fillMaxWidth()
             .verticalScroll(scrollState),
     ) {
-        var text by rememberSaveable { mutableStateOf("") }
+        var text by rememberSaveable { mutableStateOf("0") }
         var isError by rememberSaveable { mutableStateOf(false) }
         var insufficientBalance by rememberSaveable { mutableStateOf(false) }
         Row(
diff --git a/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt 
b/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
index df82546..a9503d7 100644
--- a/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
+++ b/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
@@ -22,18 +22,24 @@ import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.LocalTextStyle
 import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Text
 import androidx.compose.material3.TextFieldColors
 import androidx.compose.material3.TextFieldDefaults
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.OffsetMapping
+import androidx.compose.ui.text.input.TransformedText
 import androidx.compose.ui.text.input.VisualTransformation
 import net.taler.common.Amount
+import java.text.DecimalFormat
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
@@ -49,28 +55,20 @@ fun AmountInputField(
     trailingIcon: @Composable (() -> Unit)? = null,
     supportingText: @Composable (() -> Unit)? = null,
     isError: Boolean = false,
-    visualTransformation: VisualTransformation = VisualTransformation.None,
     keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
     keyboardActions: KeyboardActions = KeyboardActions.Default,
     interactionSource: MutableInteractionSource = remember { 
MutableInteractionSource() },
     shape: Shape = TextFieldDefaults.outlinedShape,
     colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
 ) {
+    val decimalSeparator = 
DecimalFormat().decimalFormatSymbols.decimalSeparator
+    var intermediate by remember { mutableStateOf(value) }
     OutlinedTextField(
-        value = when {
-            value == "0" -> ""
-            value.startsWith("0.") -> value.trimStart('0')
-            value.endsWith(".0") -> value.trimEnd('0')
-            else -> value
-        },
+        value = intermediate,
         onValueChange = { input ->
-            val filtered = when {
-                input.isEmpty() -> "0"
-                input.startsWith(".") -> "0${input}"
-                input.endsWith(".") -> "${input}0"
-                else -> input
-            }
+            val filtered = transformOutput(input, decimalSeparator, '.')
             if (Amount.isValidAmountStr(filtered)) {
+                intermediate = transformInput(input, decimalSeparator, '.')
                 onValueChange(filtered)
             }
         },
@@ -79,12 +77,11 @@ fun AmountInputField(
         readOnly = readOnly,
         textStyle = textStyle.copy(fontFamily = FontFamily.Monospace),
         label = label,
-        placeholder = { Text("0") },
         leadingIcon = leadingIcon,
         trailingIcon = trailingIcon,
         supportingText = supportingText,
         isError = isError,
-        visualTransformation = visualTransformation,
+        visualTransformation = 
AmountInputVisualTransformation(decimalSeparator),
         keyboardOptions = keyboardOptions.copy(keyboardType = 
KeyboardType.Decimal),
         keyboardActions = keyboardActions,
         singleLine = true,
@@ -93,4 +90,57 @@ fun AmountInputField(
         shape = shape,
         colors = colors,
     )
+}
+
+private class AmountInputVisualTransformation(
+    private val decimalSeparator: Char,
+): VisualTransformation {
+
+    override fun filter(text: AnnotatedString): TransformedText {
+        val value = text.text
+        val output = transformOutput(value, '.', decimalSeparator)
+        val newText = AnnotatedString(output)
+        return TransformedText(newText, CursorOffsetMapping(
+            unmaskedText = text.toString(),
+            maskedText = newText.toString().replace(decimalSeparator, '.'),
+        ))
+    }
+
+    private class CursorOffsetMapping(
+        private val unmaskedText: String,
+        private val maskedText: String,
+    ): OffsetMapping {
+        override fun originalToTransformed(offset: Int) = when {
+            unmaskedText.startsWith('.') -> if (offset == 0) 0 else (offset + 
1) // ".x" -> "0.x"
+            else -> offset
+        }
+
+        override fun transformedToOriginal(offset: Int) = when {
+            unmaskedText == "" -> 0 // "0" -> ""
+            unmaskedText == "." -> if (offset < 1) 0 else 1 // "0.0" -> "."
+            unmaskedText.startsWith('.') -> if (offset < 1) 0 else (offset - 
1) // "0.x" -> ".x"
+            unmaskedText.endsWith('.') && offset == maskedText.length -> 
offset - 1 // "x.0" -> "x."
+            else -> offset // "x" -> "x"
+        }
+    }
+}
+
+private fun transformInput(
+    input: String,
+    inputDecimalSeparator: Char = '.',
+    outputDecimalSeparator: Char = '.',
+) = input.trim().replace(inputDecimalSeparator, outputDecimalSeparator)
+
+private fun transformOutput(
+    input: String,
+    inputDecimalSeparator: Char = '.',
+    outputDecimalSeparator: Char = '.',
+) = transformInput(input, inputDecimalSeparator, outputDecimalSeparator).let {
+    when {
+        it.isEmpty() -> "0"
+        it == "$outputDecimalSeparator" -> "0${outputDecimalSeparator}0"
+        it.startsWith(outputDecimalSeparator) -> "0$it"
+        it.endsWith(outputDecimalSeparator) -> "${it}0"
+        else -> it
+    }
 }
\ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt 
b/wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt
index d4c9f6c..4bc91e1 100644
--- a/wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt
@@ -131,7 +131,7 @@ private fun PayToComposable(
         horizontalAlignment = Alignment.CenterHorizontally,
         verticalArrangement = Arrangement.spacedBy(16.dp),
     ) {
-        var amountText by rememberSaveable { mutableStateOf("") }
+        var amountText by rememberSaveable { mutableStateOf("0") }
         var amountError by rememberSaveable { mutableStateOf("") }
         var currency by rememberSaveable { mutableStateOf(currencies[0]) }
         val focusRequester = remember { FocusRequester() }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]