Refactor text fields for number input

- removed custom keyboard to improve accessibility
This commit is contained in:
Sad Ellie 2024-01-17 18:12:55 +03:00
parent 84c29682f8
commit 5141652394
7 changed files with 102 additions and 320 deletions

View File

@ -0,0 +1,65 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2024 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.core.ui.common.textfield
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
@Composable
fun OutlinedDecimalTextField(
modifier: Modifier,
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
label: @Composable () -> Unit,
expressionFormatter: VisualTransformation,
imeAction: ImeAction = ImeAction.Next
) {
OutlinedTextField(
modifier = modifier,
value = value,
onValueChange = onValueChange,
trailingIcon = {
AnimatedVisibility(value.text.isNotBlank(), enter = scaleIn(), exit = scaleOut()) {
IconButton(onClick = { onValueChange(TextFieldValue()) }) {
Icon(Icons.Outlined.Clear, null)
}
}
},
label = label,
singleLine = true,
visualTransformation = expressionFormatter,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Decimal,
imeAction = imeAction
),
)
}

View File

@ -153,7 +153,6 @@ private fun BodyMassScreen(
onValueChange = updateHeight1, onValueChange = updateHeight1,
label = "${stringResource(R.string.body_mass_height)}, ${stringResource(R.string.unit_centimeter_short)}", label = "${stringResource(R.string.body_mass_height)}, ${stringResource(R.string.unit_centimeter_short)}",
expressionFormatter = expressionTransformer, expressionFormatter = expressionTransformer,
imeAction = ImeAction.Next
) )
} else { } else {
Row( Row(
@ -165,7 +164,6 @@ private fun BodyMassScreen(
onValueChange = updateHeight1, onValueChange = updateHeight1,
label = "${stringResource(R.string.body_mass_height)}, ${stringResource(R.string.unit_foot_short)}", label = "${stringResource(R.string.body_mass_height)}, ${stringResource(R.string.unit_foot_short)}",
expressionFormatter = expressionTransformer, expressionFormatter = expressionTransformer,
imeAction = ImeAction.Next
) )
BodyMassTextField( BodyMassTextField(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
@ -173,7 +171,6 @@ private fun BodyMassScreen(
onValueChange = updateHeight2, onValueChange = updateHeight2,
label = "${stringResource(R.string.body_mass_height)}, ${stringResource(R.string.unit_inch_short)}", label = "${stringResource(R.string.body_mass_height)}, ${stringResource(R.string.unit_inch_short)}",
expressionFormatter = expressionTransformer, expressionFormatter = expressionTransformer,
imeAction = ImeAction.Next
) )
} }
} }

View File

@ -19,32 +19,25 @@
package com.sadellie.unitto.feature.bodymass.components package com.sadellie.unitto.feature.bodymass.components
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.OutlinedDecimalTextField
@Composable @Composable
internal fun BodyMassTextField( internal fun BodyMassTextField(
modifier: Modifier, modifier: Modifier,
value: TextFieldValue, value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
label: String, label: String,
onValueChange: (TextFieldValue) -> Unit,
expressionFormatter: VisualTransformation, expressionFormatter: VisualTransformation,
imeAction: ImeAction imeAction: ImeAction = ImeAction.Next
) { ) {
val focusManager = LocalFocusManager.current OutlinedDecimalTextField(
OutlinedTextField(
modifier = modifier, modifier = modifier,
value = value, value = value,
onValueChange = { onValueChange = {
@ -56,13 +49,7 @@ internal fun BodyMassTextField(
onValueChange(it.copy(cleanText)) onValueChange(it.copy(cleanText))
}, },
label = { AnimatedContent(label) { Text(it) } }, label = { AnimatedContent(label) { Text(it) } },
singleLine = true, expressionFormatter = expressionFormatter,
visualTransformation = expressionFormatter, imeAction = imeAction
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Decimal,
imeAction = imeAction
),
textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.End),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
) )
} }

View File

@ -26,11 +26,9 @@ import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
@ -63,7 +61,6 @@ internal fun DateCalculatorScreen(
val addSubtractLabel = "${stringResource(R.string.date_calculator_add)}/${stringResource(R.string.date_calculator_subtract)}" val addSubtractLabel = "${stringResource(R.string.date_calculator_add)}/${stringResource(R.string.date_calculator_subtract)}"
val differenceLabel = stringResource(R.string.date_calculator_difference) val differenceLabel = stringResource(R.string.date_calculator_difference)
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
var showKeyboard by remember { mutableStateOf(false) }
val allTabs = remember { listOf(addSubtractLabel, differenceLabel) } val allTabs = remember { listOf(addSubtractLabel, differenceLabel) }
val pagerState = rememberPagerState { allTabs.size } val pagerState = rememberPagerState { allTabs.size }
@ -99,14 +96,9 @@ internal fun DateCalculatorScreen(
verticalAlignment = Alignment.Top verticalAlignment = Alignment.Top
) { page -> ) { page ->
when (page) { when (page) {
0 -> AddSubtractPage( 0 -> AddSubtractPage()
showKeyboard = showKeyboard,
toggleKeyboard = {showKeyboard = it }
)
1 -> { 1 -> {
focusManager.clearFocus(true) SideEffect { focusManager.clearFocus(true) }
showKeyboard = false
DateDifferencePage() DateDifferencePage()
} }
} }

View File

@ -23,14 +23,10 @@ import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.provider.CalendarContract import android.provider.CalendarContract
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SizeTransform import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -50,25 +46,20 @@ import androidx.compose.material.icons.filled.Event
import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.Remove import androidx.compose.material.icons.outlined.Remove
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import com.sadellie.unitto.core.ui.WindowHeightSizeClass
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
@ -77,12 +68,9 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.LocalWindowSize import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer
import com.sadellie.unitto.core.ui.common.textfield.addTokens
import com.sadellie.unitto.core.ui.common.textfield.deleteTokens
import com.sadellie.unitto.core.ui.showToast import com.sadellie.unitto.core.ui.showToast
import com.sadellie.unitto.feature.datecalculator.ZonedDateTimeUtils import com.sadellie.unitto.feature.datecalculator.ZonedDateTimeUtils
import com.sadellie.unitto.feature.datecalculator.components.AddSubtractKeyboard
import com.sadellie.unitto.feature.datecalculator.components.DateTimeDialogs import com.sadellie.unitto.feature.datecalculator.components.DateTimeDialogs
import com.sadellie.unitto.feature.datecalculator.components.DateTimeSelectorBlock import com.sadellie.unitto.feature.datecalculator.components.DateTimeSelectorBlock
import com.sadellie.unitto.feature.datecalculator.components.DialogState import com.sadellie.unitto.feature.datecalculator.components.DialogState
@ -92,15 +80,11 @@ import java.time.ZonedDateTime
@Composable @Composable
internal fun AddSubtractPage( internal fun AddSubtractPage(
viewModel: AddSubtractViewModel = hiltViewModel(), viewModel: AddSubtractViewModel = hiltViewModel(),
showKeyboard: Boolean,
toggleKeyboard: (Boolean) -> Unit,
) { ) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
AddSubtractView( AddSubtractView(
uiState = uiState, uiState = uiState,
showKeyboard = showKeyboard,
toggleKeyboard = toggleKeyboard,
updateStart = viewModel::updateStart, updateStart = viewModel::updateStart,
updateYears = viewModel::updateYears, updateYears = viewModel::updateYears,
updateMonths = viewModel::updateMonths, updateMonths = viewModel::updateMonths,
@ -115,8 +99,6 @@ internal fun AddSubtractPage(
@Composable @Composable
private fun AddSubtractView( private fun AddSubtractView(
uiState: AddSubtractState, uiState: AddSubtractState,
showKeyboard: Boolean,
toggleKeyboard: (Boolean) -> Unit,
updateStart: (ZonedDateTime) -> Unit, updateStart: (ZonedDateTime) -> Unit,
updateYears: (TextFieldValue) -> Unit, updateYears: (TextFieldValue) -> Unit,
updateMonths: (TextFieldValue) -> Unit, updateMonths: (TextFieldValue) -> Unit,
@ -126,20 +108,9 @@ private fun AddSubtractView(
updateAddition: (Boolean) -> Unit, updateAddition: (Boolean) -> Unit,
) { ) {
val mContext = LocalContext.current val mContext = LocalContext.current
val focusManager = LocalFocusManager.current
var dialogState by remember { mutableStateOf(DialogState.NONE) } var dialogState by remember { mutableStateOf(DialogState.NONE) }
var addSymbol: ((TextFieldValue) -> Unit)? by remember { mutableStateOf(null) } val expressionTransformer = remember(uiState.formatterSymbols) {
var focusedTextFieldValue: TextFieldValue? by remember { mutableStateOf(null) } ExpressionTransformer(uiState.formatterSymbols)
LaunchedEffect(addSymbol, focusedTextFieldValue) {
toggleKeyboard((addSymbol != null) and (focusedTextFieldValue != null))
}
BackHandler(showKeyboard) {
focusManager.clearFocus()
addSymbol = null
focusedTextFieldValue = null
} }
val showResult = remember(uiState.start, uiState.result) { uiState.start != uiState.result } val showResult = remember(uiState.start, uiState.result) { uiState.start != uiState.result }
@ -241,127 +212,58 @@ private fun AddSubtractView(
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
TimeUnitTextField( TimeUnitTextField(
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateYears
focusedTextFieldValue = uiState.years
}
}
.fillMaxWidth(),
value = uiState.years, value = uiState.years,
onValueChange = updateYears, onValueChange = updateYears,
label = stringResource(R.string.date_calculator_years), label = stringResource(R.string.date_calculator_years),
formatterSymbols = uiState.formatterSymbols expressionFormatter = expressionTransformer,
) )
Row( Row(
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
TimeUnitTextField( TimeUnitTextField(
modifier = Modifier modifier = Modifier.weight(1f),
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateMonths
focusedTextFieldValue = uiState.months
}
}
.weight(1f),
value = uiState.months, value = uiState.months,
onValueChange = updateMonths, onValueChange = updateMonths,
label = stringResource(R.string.date_calculator_months), label = stringResource(R.string.date_calculator_months),
formatterSymbols = uiState.formatterSymbols expressionFormatter = expressionTransformer,
) )
TimeUnitTextField( TimeUnitTextField(
modifier = Modifier modifier = Modifier.weight(1f),
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateDays
focusedTextFieldValue = uiState.days
}
}
.weight(1f),
value = uiState.days, value = uiState.days,
onValueChange = updateDays, onValueChange = updateDays,
label = stringResource(R.string.date_calculator_days), label = stringResource(R.string.date_calculator_days),
formatterSymbols = uiState.formatterSymbols expressionFormatter = expressionTransformer,
) )
} }
Row( Row(
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
TimeUnitTextField( TimeUnitTextField(
modifier = Modifier modifier = Modifier.weight(1f),
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateHours
focusedTextFieldValue = uiState.hours
}
}
.weight(1f),
value = uiState.hours, value = uiState.hours,
onValueChange = updateHours, onValueChange = updateHours,
label = stringResource(R.string.date_calculator_hours), label = stringResource(R.string.date_calculator_hours),
formatterSymbols = uiState.formatterSymbols expressionFormatter = expressionTransformer,
) )
TimeUnitTextField( TimeUnitTextField(
modifier = Modifier modifier = Modifier.weight(1f),
.onFocusEvent {
if (it.hasFocus) {
addSymbol = updateMinutes
focusedTextFieldValue = uiState.minutes
}
}
.weight(1f),
value = uiState.minutes, value = uiState.minutes,
onValueChange = updateMinutes, onValueChange = updateMinutes,
label = stringResource(R.string.date_calculator_minutes), label = stringResource(R.string.date_calculator_minutes),
formatterSymbols = uiState.formatterSymbols expressionFormatter = expressionTransformer,
imeAction = ImeAction.Done
) )
} }
} }
} }
} }
AnimatedVisibility(
visible = showKeyboard,
enter = slideInVertically { it / 2 } + fadeIn(),
exit = slideOutVertically { it / 2 } + fadeOut()
) {
HorizontalDivider()
AddSubtractKeyboard(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.fillMaxHeight(if (LocalWindowSize.current.heightSizeClass > WindowHeightSizeClass.Compact) 0.4f else 0.6f)
.padding(2.dp, 4.dp),
addSymbol = {
val newValue = focusedTextFieldValue?.addTokens(it)
if (newValue != null) {
addSymbol?.invoke(newValue)
}
},
deleteSymbol = {
val newValue = focusedTextFieldValue?.deleteTokens()
if (newValue != null) {
addSymbol?.invoke(newValue)
}
},
onConfirm = {
focusManager.clearFocus()
addSymbol = null
focusedTextFieldValue = null
},
allowVibration = uiState.allowVibration,
imeAction = if (addSymbol == updateMinutes) ImeAction.Done else ImeAction.Next
)
}
} }
DateTimeDialogs( DateTimeDialogs(
@ -399,8 +301,6 @@ fun AddSubtractViewPreview() {
start = ZonedDateTimeUtils.nowWithMinutes(), start = ZonedDateTimeUtils.nowWithMinutes(),
result = ZonedDateTimeUtils.nowWithMinutes().plusSeconds(1) result = ZonedDateTimeUtils.nowWithMinutes().plusSeconds(1)
), ),
showKeyboard = false,
toggleKeyboard = {},
updateStart = {}, updateStart = {},
updateYears = {}, updateYears = {},
updateMonths = {}, updateMonths = {},

View File

@ -1,141 +0,0 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.feature.datecalculator.components
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.LocalWindowSize
import com.sadellie.unitto.core.ui.WindowHeightSizeClass
import com.sadellie.unitto.core.ui.common.KeyboardButtonContentHeightTall
import com.sadellie.unitto.core.ui.common.KeyboardButtonFilled
import com.sadellie.unitto.core.ui.common.KeyboardButtonLight
import com.sadellie.unitto.core.ui.common.KeypadFlow
import com.sadellie.unitto.core.ui.common.icons.IconPack
import com.sadellie.unitto.core.ui.common.icons.iconpack.Backspace
import com.sadellie.unitto.core.ui.common.icons.iconpack.Check
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key0
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key1
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key2
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key3
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key4
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key5
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key6
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key7
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key8
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key9
import com.sadellie.unitto.core.ui.common.icons.iconpack.Tab
@Composable
internal fun AddSubtractKeyboard(
modifier: Modifier,
addSymbol: (String) -> Unit,
deleteSymbol: () -> Unit,
onConfirm: () -> Unit,
allowVibration: Boolean,
imeAction: ImeAction,
focusManager: FocusManager = LocalFocusManager.current
) {
Row(
modifier = modifier
) {
KeypadFlow(
modifier = Modifier
.fillMaxHeight()
.weight(3f),
rows = 4,
columns = 3
) { width, height ->
val buttonModifier = Modifier
.fillMaxWidth(width)
.fillMaxHeight(height)
KeyboardButtonLight(buttonModifier, IconPack.Key7, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._7) }
KeyboardButtonLight(buttonModifier, IconPack.Key8, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._8) }
KeyboardButtonLight(buttonModifier, IconPack.Key9, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._9) }
KeyboardButtonLight(buttonModifier, IconPack.Key4, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._4) }
KeyboardButtonLight(buttonModifier, IconPack.Key5, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._5) }
KeyboardButtonLight(buttonModifier, IconPack.Key6, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._6) }
KeyboardButtonLight(buttonModifier, IconPack.Key1, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._1) }
KeyboardButtonLight(buttonModifier, IconPack.Key2, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._2) }
KeyboardButtonLight(buttonModifier, IconPack.Key3, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._3) }
Spacer(buttonModifier)
KeyboardButtonLight(buttonModifier, IconPack.Key0, allowVibration, KeyboardButtonContentHeightTall) { addSymbol(Token.Digit._0) }
Spacer(buttonModifier)
}
KeypadFlow(
modifier = Modifier
.fillMaxHeight()
.weight(1f),
rows = 2,
columns = 1,
// In digits keypad there are 4 rows with verticalPadding set to 10
// In this keypad we have 2 times less rows, we use 2 times smaller verticalPadding -> 5
verticalPadding = 5
) { width, height ->
val mainButtonModifier = Modifier
.fillMaxWidth(width)
.fillMaxHeight(height)
val actionIconHeight = if (LocalWindowSize.current.heightSizeClass > WindowHeightSizeClass.Compact) 0.8f else 1.3f
Crossfade(
targetState = imeAction == ImeAction.Next,
modifier = mainButtonModifier,
label = "Primary button animation"
) { showNext ->
if (showNext) {
KeyboardButtonFilled(Modifier.fillMaxSize(), IconPack.Tab, allowVibration, actionIconHeight) { focusManager.moveFocus(FocusDirection.Next) }
} else {
KeyboardButtonFilled(Modifier.fillMaxSize(), IconPack.Check, allowVibration, actionIconHeight) { onConfirm() }
}
}
KeyboardButtonLight(mainButtonModifier, IconPack.Backspace, allowVibration, actionIconHeight) { deleteSymbol() }
}
}
}
@Preview
@Composable
fun PreviewAddSubtractKeyboardNew() {
AddSubtractKeyboard(
modifier = Modifier
.fillMaxSize(),
addSymbol = {},
deleteSymbol = {},
onConfirm = {},
allowVibration = true,
imeAction = ImeAction.Next
)
}

View File

@ -18,50 +18,32 @@
package com.sadellie.unitto.feature.datecalculator.components package com.sadellie.unitto.feature.datecalculator.components
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalTextInputService import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer import androidx.compose.ui.text.input.VisualTransformation
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.ui.common.textfield.OutlinedDecimalTextField
@Composable @Composable
internal fun TimeUnitTextField( internal fun TimeUnitTextField(
modifier: Modifier, modifier: Modifier,
value: TextFieldValue, value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
label: String, label: String,
formatterSymbols: FormatterSymbols onValueChange: (TextFieldValue) -> Unit,
) = CompositionLocalProvider(LocalTextInputService provides null) { expressionFormatter: VisualTransformation,
OutlinedTextField( imeAction: ImeAction = ImeAction.Next,
) {
OutlinedDecimalTextField(
modifier = modifier, modifier = modifier,
value = value, value = value,
onValueChange = { newValue -> onValueChange = { newValue ->
onValueChange(newValue.copy(newValue.text.filter { it.isDigit() })) onValueChange(newValue.copy(newValue.text.filter { it.isDigit() }))
}, },
label = { Text(label, color = MaterialTheme.colorScheme.onSurfaceVariant) }, label = { AnimatedContent(label) { Text(it) } },
trailingIcon = { expressionFormatter = expressionFormatter,
AnimatedVisibility( imeAction = imeAction
visible = value.text.isNotBlank(),
enter = scaleIn(),
exit = scaleOut()
) {
IconButton(onClick = { onValueChange(TextFieldValue()) }) {
Icon(Icons.Outlined.Clear, null)
}
}
},
visualTransformation = ExpressionTransformer(formatterSymbols)
) )
} }