mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
Prepare for text2
Enabled physical keyboard support (#148), but not tested
This commit is contained in:
parent
bf9bf37812
commit
5c81a1c675
@ -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())
|
||||
|
@ -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,
|
||||
|
@ -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 = {},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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 = {},
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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 = {}
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user