diff --git a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/ClipboardManagerExt.kt b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/ClipboardManagerExt.kt index a606b219..600d5efb 100644 --- a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/ClipboardManagerExt.kt +++ b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/ClipboardManagerExt.kt @@ -18,37 +18,46 @@ package com.sadellie.unitto.core.ui.common.textfield +import android.content.ClipData import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.input.TextFieldValue import com.sadellie.unitto.core.base.Token /** - * Copy value to clipboard with fractional symbols. + * Unformatted values are expected. Basically what BigDecimal and Expression parser use. * - * Example: - * "123456.789" will be copied as "123456,789" - * - * @param value Internal [TextFieldValue] without formatting with [Token.Digit.dot] as fractional. + * @property formatterSymbols Current [FormatterSymbols]. + * @property clipboardManager [android.content.ClipboardManager] provided by system. */ -internal fun ClipboardManager.copyWithFractional( - value: TextFieldValue, - formatterSymbols: FormatterSymbols -) = this.setText( - AnnotatedString( - value.annotatedString - .subSequence(value.selection) - .text - .replace(Token.Digit.dot, formatterSymbols.fractional) +internal class ExpressionClipboardManager( + private val formatterSymbols: FormatterSymbols, + private val clipboardManager: android.content.ClipboardManager +): ClipboardManager { + override fun setText(annotatedString: AnnotatedString) = clipboardManager.setPrimaryClip( + ClipData.newPlainText( + PLAIN_TEXT_LABEL, + annotatedString + .text + .replace(Token.Digit.dot, formatterSymbols.fractional) + ) ) -) -internal fun ClipboardManager.copy(value: TextFieldValue) = this.setText( - AnnotatedString( - value.annotatedString - .subSequence(value.selection) - .text - ) -) + override fun getText(): AnnotatedString? = clipboardManager.primaryClip?.let { primaryClip -> + if (primaryClip.itemCount > 0) { + val clipText = primaryClip.getItemAt(0)?.text ?:return@let null + + clipText + .toString() + .toAnnotatedString() + } else { + null + } + } + + override fun hasText() = + clipboardManager.primaryClipDescription?.hasMimeType("text/*") ?: false +} internal const val PLAIN_TEXT_LABEL = "plain text" + +private fun CharSequence.toAnnotatedString(): AnnotatedString = AnnotatedString(this.toString()) diff --git a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/InputTextField.kt b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/InputTextField.kt index bb7e02e9..4ebc36b5 100644 --- a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/InputTextField.kt +++ b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/InputTextField.kt @@ -18,7 +18,7 @@ package com.sadellie.unitto.core.ui.common.textfield -import androidx.compose.foundation.clickable +import android.content.Context import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.text.BasicTextField @@ -32,27 +32,19 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalTextInputService -import androidx.compose.ui.platform.LocalTextToolbar -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.platform.TextToolbar import androidx.compose.ui.text.TextLayoutResult -import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.TextUnit import com.sadellie.unitto.core.ui.common.autosize.AutoSizeTextStyleBox -import com.sadellie.unitto.core.ui.common.textfield.texttoolbar.UnittoTextToolbar import com.sadellie.unitto.core.ui.theme.LocalNumberTypography @Composable @@ -60,105 +52,86 @@ fun ExpressionTextField( modifier: Modifier, value: TextFieldValue, minRatio: Float = 1f, - cutCallback: () -> Unit = {}, - pasteCallback: (String) -> Unit = {}, - onCursorChange: (TextRange) -> Unit, + onValueChange: (TextFieldValue) -> Unit, textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, formatterSymbols: FormatterSymbols, readOnly: Boolean = false, placeholder: String = "", ) { - val localView = LocalView.current - val clipboardManager = LocalClipboardManager.current - val expressionTransformer = remember(formatterSymbols) { ExpressionTransformer(formatterSymbols) } - - fun copyCallback() { - clipboardManager.copyWithFractional(value, formatterSymbols) - onCursorChange(TextRange(value.selection.end)) + val context = LocalContext.current + val clipboardManager = remember(formatterSymbols) { + ExpressionClipboardManager( + formatterSymbols = formatterSymbols, + clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) + as android.content.ClipboardManager + ) + } + val expressionTransformer = remember(formatterSymbols) { + ExpressionTransformer(formatterSymbols) } - val textToolbar: UnittoTextToolbar = if (readOnly) { - UnittoTextToolbar( - view = localView, - copyCallback = ::copyCallback, - ) - } else { - UnittoTextToolbar( - view = localView, - copyCallback = ::copyCallback, - pasteCallback = { - pasteCallback(clipboardManager.getText()?.text?.clearAndFilterExpression(formatterSymbols) ?: "") + CompositionLocalProvider( + LocalClipboardManager provides clipboardManager + ) { + AutoSizeTextField( + modifier = modifier, + value = value, + onValueChange = { + onValueChange(it.copy(text = it.text.clearAndFilterExpression(formatterSymbols))) }, - cutCallback = { - clipboardManager.copyWithFractional(value, formatterSymbols) - cutCallback() - } + placeholder = placeholder, + readOnly = readOnly, + textStyle = LocalNumberTypography.current.displayLarge.copy(textColor), + visualTransformation = expressionTransformer, + cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant), + minRatio = minRatio ) } +} +@Composable +fun NumberBaseTextField( + modifier: Modifier, + value: TextFieldValue, + minRatio: Float = 1f, + onValueChange: (TextFieldValue) -> Unit, + textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, + readOnly: Boolean = false, + placeholder: String = "", +) { AutoSizeTextField( modifier = modifier, value = value, - onValueChange = { onCursorChange(it.selection) }, + onValueChange = { + onValueChange(it.copy(text = it.text.clearAndFilterNumberBase())) + }, placeholder = placeholder, - textToolbar = textToolbar, readOnly = readOnly, textStyle = LocalNumberTypography.current.displayLarge.copy(textColor), - visualTransformation = expressionTransformer, cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant), minRatio = minRatio ) } @Composable -fun UnformattedTextField( +fun SimpleTextField( modifier: Modifier, value: TextFieldValue, minRatio: Float = 1f, - cutCallback: () -> Unit = {}, - pasteCallback: (String) -> Unit = {}, - onCursorChange: (TextRange) -> Unit, + onValueChange: (TextFieldValue) -> Unit, textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, readOnly: Boolean = false, placeholder: String = "", ) { - val localView = LocalView.current - val clipboardManager = LocalClipboardManager.current - fun copyCallback() { - clipboardManager.copy(value) - onCursorChange(TextRange(value.selection.end)) - } - - val textToolbar: UnittoTextToolbar = remember(readOnly) { - if (readOnly) { - UnittoTextToolbar( - view = localView, - copyCallback = ::copyCallback, - ) - } else { - UnittoTextToolbar( - view = localView, - copyCallback = ::copyCallback, - pasteCallback = { - pasteCallback(clipboardManager.getText()?.text?.clearAndFilterNumberBase() ?: "") - }, - cutCallback = { - clipboardManager.copy(value) - cutCallback() - } - ) - } - } - AutoSizeTextField( modifier = modifier, value = value, - onValueChange = { onCursorChange(it.selection) }, + onValueChange = onValueChange, placeholder = placeholder, - textToolbar = textToolbar, readOnly = readOnly, - textStyle = LocalNumberTypography.current.displayLarge.copy(color = textColor), - minRatio = minRatio, + textStyle = LocalNumberTypography.current.displayLarge.copy(textColor), + cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant), + minRatio = minRatio ) } @@ -166,7 +139,6 @@ fun UnformattedTextField( * Based on: https://gist.github.com/inidamleader/b594d35362ebcf3cedf81055df519300 * * @param placeholder Placeholder text, shown when [value] is empty. - * @param textToolbar [TextToolbar] with modified actions in menu. * @param alignment The alignment of the text within its container. * @see [BasicTextField] * @see [AutoSizeTextStyleBox] @@ -177,7 +149,6 @@ private fun AutoSizeTextField( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, placeholder: String? = null, - textToolbar: TextToolbar = LocalTextToolbar.current, enabled: Boolean = true, readOnly: Boolean = false, textStyle: TextStyle = TextStyle.Default, @@ -206,28 +177,14 @@ private fun AutoSizeTextField( ) { CompositionLocalProvider( LocalTextInputService provides null, - LocalTextToolbar provides textToolbar ) { - val currentTextToolbar = LocalTextToolbar.current val style = LocalTextStyle.current - val focusRequester = remember { FocusRequester() } BasicTextField( value = value, onValueChange = onValueChange, modifier = Modifier - .fillMaxWidth() - .focusRequester(focusRequester) - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onClick = { - currentTextToolbar.hide() - focusRequester.requestFocus() - onValueChange(value.copy(selection = TextRange.Zero)) - currentTextToolbar.showMenu(Rect(Offset.Zero, 0f)) - } - ), + .fillMaxWidth(), enabled = enabled, readOnly = readOnly, textStyle = style, diff --git a/feature/calculator/src/androidTest/java/com/sadellie/unitto/feature/calculator/CalculatorScreenTest.kt b/feature/calculator/src/androidTest/java/com/sadellie/unitto/feature/calculator/CalculatorScreenTest.kt index 653c3253..f6e8547e 100644 --- a/feature/calculator/src/androidTest/java/com/sadellie/unitto/feature/calculator/CalculatorScreenTest.kt +++ b/feature/calculator/src/androidTest/java/com/sadellie/unitto/feature/calculator/CalculatorScreenTest.kt @@ -46,11 +46,12 @@ class CalculatorScreenTest { addTokens = {}, clearInput = {}, deleteTokens = {}, - onCursorChange = {}, + onValueChange = {}, toggleCalculatorMode = {}, equal = {}, clearHistory = {}, - addBracket = {} + addBracket = {}, + onDelete = {}, ) } @@ -69,7 +70,6 @@ class CalculatorScreenTest { outputFormat = OutputFormat.PLAIN, formatterSymbols = FormatterSymbols.Spaces, history = emptyList(), - allowVibration = false, middleZero = false, acButton = true, partialHistoryView = true @@ -79,11 +79,12 @@ class CalculatorScreenTest { addTokens = {}, clearInput = {}, deleteTokens = {}, - onCursorChange = {}, + onValueChange = {}, toggleCalculatorMode = {}, equal = {}, clearHistory = {}, - addBracket = {} + addBracket = {}, + onDelete = {}, ) } @@ -103,7 +104,6 @@ class CalculatorScreenTest { outputFormat = OutputFormat.PLAIN, formatterSymbols = FormatterSymbols.Spaces, history = emptyList(), - allowVibration = false, middleZero = false, acButton = true, partialHistoryView = true @@ -113,11 +113,12 @@ class CalculatorScreenTest { addTokens = {}, clearInput = {}, deleteTokens = {}, - onCursorChange = {}, + onValueChange = {}, toggleCalculatorMode = {}, equal = {}, clearHistory = {}, - addBracket = {} + addBracket = {}, + onDelete = {}, ) } diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt index d986482e..3018ea8a 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt @@ -56,7 +56,6 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTag -import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntOffset @@ -97,7 +96,7 @@ internal fun CalculatorRoute( addTokens = viewModel::addTokens, clearInput = viewModel::clearInput, deleteTokens = viewModel::deleteTokens, - onCursorChange = viewModel::onCursorChange, + onValueChange = viewModel::updateInput, toggleCalculatorMode = viewModel::updateRadianMode, equal = viewModel::equal, clearHistory = viewModel::clearHistory, @@ -115,7 +114,7 @@ internal fun CalculatorScreen( addBracket: () -> Unit, clearInput: () -> Unit, deleteTokens: () -> Unit, - onCursorChange: (TextRange) -> Unit, + onValueChange: (TextFieldValue) -> Unit, toggleCalculatorMode: (Boolean) -> Unit, equal: () -> Unit, clearHistory: () -> Unit, @@ -130,7 +129,7 @@ internal fun CalculatorScreen( addSymbol = addTokens, clearSymbols = clearInput, deleteSymbol = deleteTokens, - onCursorChange = onCursorChange, + onValueChange = onValueChange, toggleAngleMode = { toggleCalculatorMode(!uiState.radianMode) }, equal = equal, clearHistory = clearHistory, @@ -149,7 +148,7 @@ private fun Ready( addBracket: () -> Unit, clearSymbols: () -> Unit, deleteSymbol: () -> Unit, - onCursorChange: (TextRange) -> Unit, + onValueChange: (TextFieldValue) -> Unit, toggleAngleMode: () -> Unit, equal: () -> Unit, clearHistory: () -> Unit, @@ -266,9 +265,7 @@ private fun Ready( ), formatterSymbols = uiState.formatterSymbols, input = uiState.input, - deleteSymbol = deleteSymbol, - addSymbol = addSymbol, - onCursorChange = onCursorChange, + onValueChange = onValueChange, output = uiState.output ) @@ -379,7 +376,7 @@ private fun PreviewCalculatorScreen() { addTokens = {}, clearInput = {}, deleteTokens = {}, - onCursorChange = {}, + onValueChange = {}, toggleCalculatorMode = {}, equal = {}, clearHistory = {}, diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt index bce8601c..1a70fbcc 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt @@ -166,11 +166,11 @@ internal class CalculatorViewModel @Inject constructor( TextFieldValue() } - fun onCursorChange(selection: TextRange) = _input.update { + fun updateInput(value: TextFieldValue) = _input.update { // Without this line: will place token (even in the middle of the input) and place cursor at // the end. This line also removes fractional output once user touches input text field _equalClicked.update { false } - it.copy(selection = selection) + value } fun updateRadianMode(newValue: Boolean) = viewModelScope.launch { diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/TextBox.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/TextBox.kt index f94099ed..e1daaf1a 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/TextBox.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/TextBox.kt @@ -37,14 +37,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTag -import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import com.sadellie.unitto.core.ui.LocalWindowSize import com.sadellie.unitto.core.ui.WindowHeightSizeClass import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols -import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField +import com.sadellie.unitto.core.ui.common.textfield.SimpleTextField import com.sadellie.unitto.feature.calculator.CalculationResult @Composable @@ -52,9 +51,7 @@ fun TextBox( modifier: Modifier, formatterSymbols: FormatterSymbols, input: TextFieldValue, - deleteSymbol: () -> Unit, - addSymbol: (String) -> Unit, - onCursorChange: (TextRange) -> Unit, + onValueChange: (TextFieldValue) -> Unit, output: CalculationResult, ) { Column( @@ -77,9 +74,7 @@ fun TextBox( .padding(horizontal = 8.dp), value = input, minRatio = 0.5f, - cutCallback = deleteSymbol, - pasteCallback = addSymbol, - onCursorChange = onCursorChange, + onValueChange = onValueChange, formatterSymbols = formatterSymbols ) if (LocalWindowSize.current.heightSizeClass > WindowHeightSizeClass.Compact) { @@ -105,9 +100,9 @@ fun TextBox( .padding(horizontal = 8.dp), value = outputTF, minRatio = 0.8f, - onCursorChange = { outputTF = outputTF.copy(selection = it) }, - formatterSymbols = formatterSymbols, + onValueChange = { outputTF = it }, textColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(0.6f), + formatterSymbols = formatterSymbols, readOnly = true, ) } @@ -123,36 +118,36 @@ fun TextBox( .padding(horizontal = 8.dp), value = outputTF, minRatio = 0.8f, - onCursorChange = { outputTF = outputTF.copy(selection = it) }, - formatterSymbols = formatterSymbols, + onValueChange = { outputTF = it }, textColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(0.6f), + formatterSymbols = formatterSymbols, readOnly = true, ) } is CalculationResult.DivideByZeroError -> { - UnformattedTextField( + SimpleTextField( modifier = Modifier .weight(2f) .fillMaxWidth() .padding(horizontal = 8.dp), value = TextFieldValue(stringResource(output.label)), minRatio = 0.8f, - onCursorChange = {}, + onValueChange = {}, textColor = MaterialTheme.colorScheme.error, readOnly = true, ) } is CalculationResult.Error -> { - UnformattedTextField( + SimpleTextField( modifier = Modifier .weight(2f) .fillMaxWidth() .padding(horizontal = 8.dp), value = TextFieldValue(stringResource(output.label)), minRatio = 0.8f, - onCursorChange = {}, + onValueChange = {}, textColor = MaterialTheme.colorScheme.error, readOnly = true, ) diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt index eb9548f3..824aedd8 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt @@ -64,7 +64,6 @@ import androidx.compose.ui.focus.onFocusEvent import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -83,7 +82,8 @@ import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar import com.sadellie.unitto.core.ui.common.SettingsButton import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols -import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField +import com.sadellie.unitto.core.ui.common.textfield.NumberBaseTextField +import com.sadellie.unitto.core.ui.common.textfield.SimpleTextField import com.sadellie.unitto.core.ui.datetime.formatDateWeekDayMonthYear import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.converter.UnitID @@ -114,7 +114,7 @@ internal fun ConverterRoute( processInput = viewModel::addTokens, deleteDigit = viewModel::deleteTokens, clearInput = viewModel::clearInput, - onCursorChange = viewModel::onCursorChange, + onValueChange = viewModel::updateInput, onFocusOnInput2 = viewModel::updateFocused, onErrorClick = viewModel::updateCurrencyRates, addBracket = viewModel::addBracket @@ -132,7 +132,7 @@ private fun ConverterScreen( processInput: (String) -> Unit, deleteDigit: () -> Unit, clearInput: () -> Unit, - onCursorChange: (TextRange) -> Unit, + onValueChange: (TextFieldValue) -> Unit, onFocusOnInput2: (Boolean) -> Unit, onErrorClick: (AbstractUnit) -> Unit, addBracket: () -> Unit, @@ -148,7 +148,7 @@ private fun ConverterScreen( NumberBase( modifier = Modifier.padding(it), uiState = uiState, - onCursorChange = onCursorChange, + onValueChange = onValueChange, processInput = processInput, deleteDigit = deleteDigit, navigateToLeftScreen = navigateToLeftScreen, @@ -167,7 +167,7 @@ private fun ConverterScreen( Default( modifier = Modifier.padding(it), uiState = uiState, - onCursorChange = onCursorChange, + onValueChange = onValueChange, onFocusOnInput2 = onFocusOnInput2, processInput = processInput, deleteDigit = deleteDigit, @@ -204,7 +204,7 @@ private fun UnitConverterTopBar( private fun NumberBase( modifier: Modifier, uiState: UnitConverterUIState.NumberBase, - onCursorChange: (TextRange) -> Unit, + onValueChange: (TextFieldValue) -> Unit, processInput: (String) -> Unit, deleteDigit: () -> Unit, navigateToLeftScreen: () -> Unit, @@ -218,14 +218,12 @@ private fun NumberBase( ColumnWithConstraints(modifier = contentModifier) { val textFieldModifier = Modifier.weight(2f) - UnformattedTextField( + NumberBaseTextField( modifier = textFieldModifier, minRatio = 0.7f, placeholder = Token.Digit._0, value = uiState.input, - onCursorChange = onCursorChange, - pasteCallback = processInput, - cutCallback = deleteDigit, + onValueChange = onValueChange, ) AnimatedUnitShortName(stringResource(uiState.unitFrom.shortName)) @@ -261,7 +259,7 @@ private fun NumberBase( private fun Default( modifier: Modifier, uiState: UnitConverterUIState.Default, - onCursorChange: (TextRange) -> Unit, + onValueChange: (TextFieldValue) -> Unit, onFocusOnInput2: (Boolean) -> Unit, processInput: (String) -> Unit, deleteDigit: () -> Unit, @@ -330,13 +328,11 @@ private fun Default( ) { ExpressionTextField( modifier = Modifier.fillMaxWidth().weight(1f), - minRatio = 0.7f, - placeholder = Token.Digit._0, value = uiState.input1, - onCursorChange = onCursorChange, - pasteCallback = processInput, - cutCallback = deleteDigit, + minRatio = 0.7f, + onValueChange = onValueChange, formatterSymbols = uiState.formatterSymbols, + placeholder = Token.Digit._0, ) AnimatedUnitShortName(stringResource(uiState.unitFrom.shortName)) } @@ -351,13 +347,11 @@ private fun Default( ExpressionTextField( modifier = Modifier.fillMaxWidth().weight(1f) .onFocusEvent { state -> onFocusOnInput2(state.hasFocus) }, - minRatio = 0.7f, - placeholder = Token.Digit._0, value = uiState.input2, - onCursorChange = onCursorChange, - pasteCallback = processInput, - cutCallback = deleteDigit, + minRatio = 0.7f, + onValueChange = onValueChange, formatterSymbols = uiState.formatterSymbols, + placeholder = Token.Digit._0, ) AnimatedUnitShortName(stringResource(R.string.unit_inch_short)) } @@ -365,13 +359,11 @@ private fun Default( } else { ExpressionTextField( modifier = textFieldModifier, - minRatio = 0.7f, - placeholder = Token.Digit._0, value = uiState.input1, - onCursorChange = onCursorChange, - pasteCallback = processInput, - cutCallback = deleteDigit, + minRatio = 0.7f, + onValueChange = onValueChange, formatterSymbols = uiState.formatterSymbols, + placeholder = Token.Digit._0, ) AnimatedVisibility( visible = calculation.text.isNotEmpty(), @@ -382,10 +374,10 @@ private fun Default( ExpressionTextField( modifier = Modifier, value = calculation, - onCursorChange = { calculation = calculation.copy(selection = it) }, - formatterSymbols = uiState.formatterSymbols, minRatio = 0.7f, + onValueChange = { calculation = it }, textColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f), + formatterSymbols = uiState.formatterSymbols, readOnly = true ) } @@ -456,20 +448,20 @@ private fun ConverterResultTextField( when (result) { is ConverterResult.Loading -> { - UnformattedTextField( + SimpleTextField( modifier = modifier, value = TextFieldValue(stringResource(R.string.loading_label)), - onCursorChange = {}, + onValueChange = {}, minRatio = 0.7f, readOnly = true ) } is ConverterResult.Error -> { - UnformattedTextField( + SimpleTextField( modifier = modifier, value = TextFieldValue(stringResource(R.string.error_label)), - onCursorChange = { onErrorClick() }, + onValueChange = { onErrorClick() }, minRatio = 0.7f, readOnly = true, textColor = MaterialTheme.colorScheme.error, @@ -480,20 +472,29 @@ private fun ConverterResultTextField( ExpressionTextField( modifier = modifier, value = resultTextField, - onCursorChange = { resultTextField = resultTextField.copy(selection = it) }, + minRatio = 0.7f, + onValueChange = { resultTextField = it }, formatterSymbols = formatterSymbols, + readOnly = true + ) + } + + is ConverterResult.NumberBase -> { + NumberBaseTextField( + modifier = modifier, + value = resultTextField, + onValueChange = { resultTextField = it }, minRatio = 0.7f, readOnly = true ) } - is ConverterResult.NumberBase, is ConverterResult.Time, is ConverterResult.FootInch -> { - UnformattedTextField( + SimpleTextField( modifier = modifier, value = resultTextField, - onCursorChange = { resultTextField = resultTextField.copy(selection = it) }, + onValueChange = { resultTextField = it }, minRatio = 0.7f, readOnly = true ) @@ -585,7 +586,7 @@ private fun PreviewConverterScreen() { processInput = {}, deleteDigit = {}, clearInput = {}, - onCursorChange = {}, + onValueChange = {}, onFocusOnInput2 = {}, onErrorClick = {}, addBracket = {} diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt index 70a3e623..3bcc19b5 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt @@ -18,7 +18,6 @@ package com.sadellie.unitto.feature.converter -import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel @@ -321,7 +320,7 @@ internal class ConverterViewModel @Inject constructor( } } - fun onCursorChange(selection: TextRange) = _input1.update { it.copy(selection = selection) } + fun updateInput(value: TextFieldValue) = _input1.update { value } fun updateCurrencyRates(unit: AbstractUnit) { _loadCurrenciesJob = viewModelScope.launch(Dispatchers.IO) {