mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 00:35:26 +02:00
Keyboard in AddSubtract
This commit is contained in:
parent
532931914e
commit
7e6036f4ce
@ -21,8 +21,11 @@ package com.sadellie.unitto.core.ui
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import com.sadellie.unitto.core.base.R
|
||||
|
||||
/**
|
||||
@ -35,3 +38,6 @@ fun openLink(mContext: Context, url: String) {
|
||||
Toast.makeText(mContext, R.string.error_label, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun isPortrait() = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
package com.sadellie.unitto.core.ui.common
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@ -29,20 +28,20 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import com.sadellie.unitto.core.ui.isPortrait
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun BasicKeyboardButton(
|
||||
modifier: Modifier,
|
||||
contentHeight: Float,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: (() -> Unit)?,
|
||||
containerColor: Color,
|
||||
icon: ImageVector,
|
||||
iconColor: Color,
|
||||
allowVibration: Boolean,
|
||||
contentHeight: Float,
|
||||
) {
|
||||
val view = LocalView.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
@ -53,7 +52,6 @@ fun BasicKeyboardButton(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnittoButton(
|
||||
modifier = modifier,
|
||||
onClick = { onClick(); vibrate() },
|
||||
@ -75,18 +73,19 @@ fun KeyboardButtonLight(
|
||||
modifier: Modifier,
|
||||
icon: ImageVector,
|
||||
allowVibration: Boolean,
|
||||
contentHeight: Float = if (isPortrait()) 0.51f else 0.7f,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
BasicKeyboardButton(
|
||||
modifier = modifier,
|
||||
contentHeight = contentHeight,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
containerColor = MaterialTheme.colorScheme.inverseOnSurface,
|
||||
icon = icon,
|
||||
iconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
allowVibration = allowVibration,
|
||||
contentHeight = if (isPortrait()) 0.51f else 0.7f
|
||||
)
|
||||
}
|
||||
|
||||
@ -95,18 +94,19 @@ fun KeyboardButtonFilled(
|
||||
modifier: Modifier,
|
||||
icon: ImageVector,
|
||||
allowVibration: Boolean,
|
||||
contentHeight: Float = if (isPortrait()) 0.51f else 0.7f,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
BasicKeyboardButton(
|
||||
modifier = modifier,
|
||||
contentHeight = contentHeight,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
icon = icon,
|
||||
iconColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
allowVibration = allowVibration,
|
||||
contentHeight = if (isPortrait()) 0.51f else 0.7f
|
||||
)
|
||||
}
|
||||
|
||||
@ -115,21 +115,18 @@ fun KeyboardButtonAdditional(
|
||||
modifier: Modifier,
|
||||
icon: ImageVector,
|
||||
allowVibration: Boolean,
|
||||
contentHeight: Float = 0.8f,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
BasicKeyboardButton(
|
||||
modifier = modifier,
|
||||
contentHeight = contentHeight,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
containerColor = Color.Transparent,
|
||||
icon = icon,
|
||||
iconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
allowVibration = allowVibration,
|
||||
contentHeight = if (isPortrait()) 0.8f else 0.8f
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun isPortrait() =
|
||||
LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
@ -18,6 +18,11 @@
|
||||
|
||||
package com.sadellie.unitto.core.ui.common
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
@ -51,18 +56,25 @@ fun UnittoScreenWithTopBar(
|
||||
floatingActionButton: @Composable () -> Unit = {},
|
||||
floatingActionButtonPosition: FabPosition = FabPosition.End,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
showTopBar: Boolean = true,
|
||||
content: @Composable (PaddingValues) -> Unit
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
CenterAlignedTopAppBar(
|
||||
title = title,
|
||||
navigationIcon = navigationIcon,
|
||||
actions = actions,
|
||||
colors = colors,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = showTopBar,
|
||||
enter = slideInVertically() + fadeIn(),
|
||||
exit = slideOutVertically() + fadeOut()
|
||||
) {
|
||||
CenterAlignedTopAppBar(
|
||||
title = title,
|
||||
navigationIcon = navigationIcon,
|
||||
actions = actions,
|
||||
colors = colors,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
}
|
||||
},
|
||||
floatingActionButton = floatingActionButton,
|
||||
floatingActionButtonPosition = floatingActionButtonPosition,
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.core.ui.common.key.unittoicons
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
|
||||
import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.ImageVector.Builder
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.sadellie.unitto.core.ui.common.key.UnittoIcons
|
||||
|
||||
val @Suppress("UnusedReceiverParameter") UnittoIcons.Check: ImageVector
|
||||
get() {
|
||||
if (_check != null) {
|
||||
return _check!!
|
||||
}
|
||||
_check = Builder(name = "Check", defaultWidth = 150.0.dp, defaultHeight = 150.0.dp,
|
||||
viewportWidth = 150.0f, viewportHeight = 150.0f).apply {
|
||||
path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
|
||||
strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
|
||||
pathFillType = NonZero) {
|
||||
moveTo(59.625f, 112.156f)
|
||||
lineTo(24.0f, 76.531f)
|
||||
lineTo(32.906f, 67.625f)
|
||||
lineTo(59.625f, 94.344f)
|
||||
lineTo(116.969f, 37.0f)
|
||||
lineTo(125.875f, 45.906f)
|
||||
lineTo(59.625f, 112.156f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
return _check!!
|
||||
}
|
||||
|
||||
private var _check: ImageVector? = null
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.core.ui.common.key.unittoicons
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
|
||||
import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.ImageVector.Builder
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.sadellie.unitto.core.ui.common.key.UnittoIcons
|
||||
|
||||
val @Suppress("UnusedReceiverParameter") UnittoIcons.Tab: ImageVector
|
||||
get() {
|
||||
if (_tab != null) {
|
||||
return _tab!!
|
||||
}
|
||||
_tab = Builder(name = "Tab", defaultWidth = 150.0.dp, defaultHeight = 150.0.dp,
|
||||
viewportWidth = 150.0f, viewportHeight = 150.0f).apply {
|
||||
path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
|
||||
strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
|
||||
pathFillType = NonZero) {
|
||||
moveTo(125.0f, 112.5f)
|
||||
verticalLineTo(37.5f)
|
||||
horizontalLineTo(137.5f)
|
||||
verticalLineTo(112.5f)
|
||||
horizontalLineTo(125.0f)
|
||||
close()
|
||||
moveTo(75.0f, 112.5f)
|
||||
lineTo(66.094f, 103.75f)
|
||||
lineTo(88.594f, 81.25f)
|
||||
horizontalLineTo(12.5f)
|
||||
verticalLineTo(68.75f)
|
||||
horizontalLineTo(88.594f)
|
||||
lineTo(66.25f, 46.25f)
|
||||
lineTo(75.0f, 37.5f)
|
||||
lineTo(112.5f, 75.0f)
|
||||
lineTo(75.0f, 112.5f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
return _tab!!
|
||||
}
|
||||
|
||||
private var _tab: ImageVector? = null
|
@ -126,6 +126,7 @@ data class UnitGroupsPreferences(
|
||||
|
||||
data class AddSubtractPreferences(
|
||||
val separator: Int = Separator.SPACE,
|
||||
val enableVibrations: Boolean = true,
|
||||
)
|
||||
|
||||
data class AboutPreferences(
|
||||
@ -232,7 +233,8 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
||||
val addSubtractPrefs: Flow<AddSubtractPreferences> = data
|
||||
.map { preferences ->
|
||||
AddSubtractPreferences(
|
||||
separator = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACE
|
||||
separator = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACE,
|
||||
enableVibrations = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -386,7 +386,7 @@ private fun PortraitKeyboard(
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key0, allowVibration) { addSymbol(Token.Digit._0) }
|
||||
KeyboardButtonLight(mainButtonModifier, fractionalIcon, allowVibration) { addSymbol(Token.Digit.dot) }
|
||||
}
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Backspace, allowVibration, clearSymbols) { deleteSymbol() }
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Backspace, allowVibration, onLongClick = clearSymbols) { deleteSymbol() }
|
||||
KeyboardButtonFilled(mainButtonModifier, UnittoIcons.Equal, allowVibration) { evaluate() }
|
||||
}
|
||||
|
||||
@ -538,7 +538,7 @@ private fun LandscapeKeyboard(
|
||||
KeyboardButtonLight(buttonModifier, UnittoIcons.Key9, allowVibration) { addSymbol(Token.Digit._9) }
|
||||
KeyboardButtonLight(buttonModifier, UnittoIcons.Key6, allowVibration) { addSymbol(Token.Digit._6) }
|
||||
KeyboardButtonLight(buttonModifier, UnittoIcons.Key3, allowVibration) { addSymbol(Token.Digit._3) }
|
||||
KeyboardButtonLight(buttonModifier, UnittoIcons.Backspace, allowVibration, clearSymbols) { deleteSymbol() }
|
||||
KeyboardButtonLight(buttonModifier, UnittoIcons.Backspace, allowVibration, onLongClick = clearSymbols) { deleteSymbol() }
|
||||
}
|
||||
|
||||
Column(Modifier.weight(1f)) {
|
||||
|
@ -109,7 +109,7 @@ internal fun DefaultKeyboard(
|
||||
KeyboardButtonLight(bModifier, UnittoIcons.Key0, allowVibration) { addDigit(Token.Digit._0) }
|
||||
KeyboardButtonLight(bModifier, fractionalIcon, allowVibration) { addDigit(Token.Digit.dot) }
|
||||
}
|
||||
KeyboardButtonLight(bModifier, UnittoIcons.Backspace, allowVibration, clearInput) { deleteDigit() }
|
||||
KeyboardButtonLight(bModifier, UnittoIcons.Backspace, allowVibration, onLongClick = clearInput) { deleteDigit() }
|
||||
KeyboardButtonFilled(bModifier, UnittoIcons.Plus, allowVibration) { addDigit(Token.Operator.plus) }
|
||||
}
|
||||
}
|
||||
@ -159,7 +159,7 @@ internal fun NumberBaseKeyboard(
|
||||
Row(cModifier, horizontalArrangement) {
|
||||
KeyboardButtonLight(bModifier, UnittoIcons.Key0, allowVibration) { addDigit(Token.Digit._0) }
|
||||
KeyboardButtonLight(
|
||||
Modifier.fillMaxSize().weight(2f).padding(it.maxWidth * 0.015f, it.maxHeight * 0.008f), UnittoIcons.Backspace, allowVibration, clearInput) { deleteDigit() }
|
||||
Modifier.fillMaxSize().weight(2f).padding(it.maxWidth * 0.015f, it.maxHeight * 0.008f), UnittoIcons.Backspace, allowVibration, onLongClick = clearInput) { deleteDigit() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,11 @@ import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
@ -60,6 +63,7 @@ internal fun DateCalculatorScreen(
|
||||
val addSubtractLabel = "${stringResource(R.string.add)}/${stringResource(R.string.subtract)}"
|
||||
val differenceLabel = stringResource(R.string.difference)
|
||||
val focusManager = LocalFocusManager.current
|
||||
var topBarShown by remember { mutableStateOf(true) }
|
||||
|
||||
val allTabs = remember { mutableListOf(addSubtractLabel, differenceLabel) }
|
||||
val pagerState = rememberPagerState { allTabs.size }
|
||||
@ -69,9 +73,8 @@ internal fun DateCalculatorScreen(
|
||||
modifier = Modifier,
|
||||
title = { Text(stringResource(R.string.date_calculator)) },
|
||||
navigationIcon = { MenuButton(navigateToMenu) },
|
||||
actions = {
|
||||
SettingsButton(navigateToSettings)
|
||||
},
|
||||
actions = { SettingsButton(navigateToSettings) },
|
||||
showTopBar = topBarShown,
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
@ -97,9 +100,14 @@ internal fun DateCalculatorScreen(
|
||||
verticalAlignment = Alignment.Top
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> AddSubtractPage()
|
||||
1 -> DateDifferencePage().also {
|
||||
0 -> AddSubtractPage(
|
||||
toggleTopBar = { topBarShown = it }
|
||||
)
|
||||
1 -> {
|
||||
focusManager.clearFocus(true)
|
||||
topBarShown = true
|
||||
|
||||
DateDifferencePage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,19 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.provider.CalendarContract
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
@ -37,25 +45,34 @@ import androidx.compose.material.icons.filled.Event
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material.icons.outlined.Remove
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.focus.onFocusEvent
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.sadellie.unitto.core.base.R
|
||||
import com.sadellie.unitto.core.ui.common.textfield.addTokens
|
||||
import com.sadellie.unitto.core.ui.common.textfield.deleteTokens
|
||||
import com.sadellie.unitto.core.ui.isPortrait
|
||||
import com.sadellie.unitto.feature.datecalculator.components.AddSubtractKeyboard
|
||||
import com.sadellie.unitto.feature.datecalculator.components.DateTimeDialogs
|
||||
import com.sadellie.unitto.feature.datecalculator.components.DateTimeSelectorBlock
|
||||
import com.sadellie.unitto.feature.datecalculator.components.DialogState
|
||||
@ -65,18 +82,20 @@ import java.time.ZonedDateTime
|
||||
@Composable
|
||||
internal fun AddSubtractPage(
|
||||
viewModel: AddSubtractViewModel = hiltViewModel(),
|
||||
toggleTopBar: (Boolean) -> Unit,
|
||||
) {
|
||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||
|
||||
AddSubtractView(
|
||||
uiState = uiState,
|
||||
toggleTopBar = toggleTopBar,
|
||||
updateStart = viewModel::updateStart,
|
||||
updateYears = viewModel::updateYears,
|
||||
updateMonths = viewModel::updateMonths,
|
||||
updateDays = viewModel::updateDays,
|
||||
updateHours = viewModel::updateHours,
|
||||
updateMinutes = viewModel::updateMinutes,
|
||||
updateAddition = viewModel::updateAddition
|
||||
updateAddition = viewModel::updateAddition,
|
||||
)
|
||||
}
|
||||
|
||||
@ -85,120 +104,207 @@ internal fun AddSubtractPage(
|
||||
@Composable
|
||||
private fun AddSubtractView(
|
||||
uiState: AddSubtractState,
|
||||
toggleTopBar: (Boolean) -> Unit,
|
||||
updateStart: (ZonedDateTime) -> Unit,
|
||||
updateYears: (String) -> Unit,
|
||||
updateMonths: (String) -> Unit,
|
||||
updateDays: (String) -> Unit,
|
||||
updateHours: (String) -> Unit,
|
||||
updateMinutes: (String) -> Unit,
|
||||
updateYears: (TextFieldValue) -> Unit,
|
||||
updateMonths: (TextFieldValue) -> Unit,
|
||||
updateDays: (TextFieldValue) -> Unit,
|
||||
updateHours: (TextFieldValue) -> Unit,
|
||||
updateMinutes: (TextFieldValue) -> Unit,
|
||||
updateAddition: (Boolean) -> Unit,
|
||||
) {
|
||||
var dialogState by remember { mutableStateOf(DialogState.NONE) }
|
||||
val mContext = LocalContext.current
|
||||
var addSymbol: ((TextFieldValue) -> Unit)? by remember { mutableStateOf(null) }
|
||||
var focusedTextFieldValue: TextFieldValue? by remember { mutableStateOf(null) }
|
||||
val showKeyboard = (addSymbol != null) and (focusedTextFieldValue != null)
|
||||
val landscape = !isPortrait()
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = { mContext.addEvent(uiState.start, uiState.result) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Event,
|
||||
contentDescription = null,
|
||||
)
|
||||
LaunchedEffect(showKeyboard, landscape) {
|
||||
toggleTopBar(showKeyboard and landscape)
|
||||
}
|
||||
|
||||
BackHandler(showKeyboard) {
|
||||
focusManager.clearFocus()
|
||||
addSymbol = null
|
||||
focusedTextFieldValue = null
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f),
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
mContext.addEvent(uiState.start, uiState.result)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Event,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = PaddingValues(top = 16.dp)
|
||||
) {
|
||||
item("dates") {
|
||||
FlowRow(
|
||||
modifier = Modifier,
|
||||
maxItemsInEachRow = 2,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
DateTimeSelectorBlock(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
title = stringResource(R.string.date_difference_start),
|
||||
dateTime = uiState.start,
|
||||
onLongClick = { updateStart(ZonedDateTime.now()) },
|
||||
onClick = { dialogState = DialogState.FROM },
|
||||
onTimeClick = { dialogState = DialogState.FROM_TIME },
|
||||
onDateClick = { dialogState = DialogState.FROM_DATE },
|
||||
)
|
||||
|
||||
DateTimeSelectorBlock(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
title = stringResource(R.string.date_difference_end),
|
||||
dateTime = uiState.result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item("modes") {
|
||||
SingleChoiceSegmentedButtonRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
SegmentedButton(
|
||||
selected = uiState.addition,
|
||||
onClick = { updateAddition(true) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2),
|
||||
icon = {}
|
||||
) {
|
||||
Icon(Icons.Outlined.Add, null)
|
||||
}
|
||||
SegmentedButton(
|
||||
selected = !uiState.addition,
|
||||
onClick = { updateAddition(false) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2),
|
||||
icon = {}
|
||||
) {
|
||||
Icon(Icons.Outlined.Remove, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item("textFields") {
|
||||
Column {
|
||||
TimeUnitTextField(
|
||||
modifier = Modifier.onFocusEvent {
|
||||
if (it.hasFocus) {
|
||||
addSymbol = updateYears
|
||||
focusedTextFieldValue = uiState.years
|
||||
}
|
||||
},
|
||||
value = uiState.years,
|
||||
onValueChange = updateYears,
|
||||
label = stringResource(R.string.date_difference_years),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
TimeUnitTextField(
|
||||
modifier = Modifier.onFocusEvent {
|
||||
if (it.hasFocus) {
|
||||
addSymbol = updateMonths
|
||||
focusedTextFieldValue = uiState.months
|
||||
}
|
||||
},
|
||||
value = uiState.months,
|
||||
onValueChange = updateMonths,
|
||||
label = stringResource(R.string.date_difference_months),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
TimeUnitTextField(
|
||||
modifier = Modifier.onFocusEvent {
|
||||
if (it.hasFocus) {
|
||||
addSymbol = updateDays
|
||||
focusedTextFieldValue = uiState.days
|
||||
}
|
||||
},
|
||||
value = uiState.days,
|
||||
onValueChange = updateDays,
|
||||
label = stringResource(R.string.date_difference_days),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
TimeUnitTextField(
|
||||
modifier = Modifier.onFocusEvent {
|
||||
if (it.hasFocus) {
|
||||
addSymbol = updateHours
|
||||
focusedTextFieldValue = uiState.hours
|
||||
}
|
||||
},
|
||||
value = uiState.hours,
|
||||
onValueChange = updateHours,
|
||||
label = stringResource(R.string.date_difference_hours),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
TimeUnitTextField(
|
||||
modifier = Modifier.onFocusEvent {
|
||||
if (it.hasFocus) {
|
||||
addSymbol = updateMinutes
|
||||
focusedTextFieldValue = uiState.minutes
|
||||
}
|
||||
},
|
||||
value = uiState.minutes,
|
||||
onValueChange = updateMinutes,
|
||||
label = stringResource(R.string.date_difference_minutes),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = PaddingValues(bottom = 88.dp)
|
||||
AnimatedVisibility(
|
||||
visible = showKeyboard,
|
||||
enter = slideInVertically { it / 2 } + fadeIn(),
|
||||
exit = slideOutVertically { it / 2 } + fadeOut()
|
||||
) {
|
||||
item("dates") {
|
||||
FlowRow(
|
||||
modifier = Modifier,
|
||||
maxItemsInEachRow = 2,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
DateTimeSelectorBlock(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
title = stringResource(R.string.date_difference_start),
|
||||
dateTime = uiState.start,
|
||||
onLongClick = { updateStart(ZonedDateTime.now()) },
|
||||
onClick = { dialogState = DialogState.FROM },
|
||||
onTimeClick = { dialogState = DialogState.FROM_TIME },
|
||||
onDateClick = { dialogState = DialogState.FROM_DATE },
|
||||
)
|
||||
|
||||
DateTimeSelectorBlock(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
title = stringResource(R.string.date_difference_end),
|
||||
dateTime = uiState.result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item("modes") {
|
||||
SingleChoiceSegmentedButtonRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
SegmentedButton(
|
||||
selected = uiState.addition,
|
||||
onClick = { updateAddition(true) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2),
|
||||
icon = {}
|
||||
) {
|
||||
Icon(Icons.Outlined.Add, null)
|
||||
HorizontalDivider()
|
||||
AddSubtractKeyboard(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(if (isPortrait()) 0.4f else 0.6f)
|
||||
.padding(2.dp, 4.dp),
|
||||
addSymbol = {
|
||||
val newValue = focusedTextFieldValue?.addTokens(it)
|
||||
if (newValue != null) {
|
||||
addSymbol?.invoke(newValue)
|
||||
}
|
||||
SegmentedButton(
|
||||
selected = !uiState.addition,
|
||||
onClick = { updateAddition(false) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2),
|
||||
icon = {}
|
||||
) {
|
||||
Icon(Icons.Outlined.Remove, null)
|
||||
},
|
||||
deleteSymbol = {
|
||||
val newValue = focusedTextFieldValue?.deleteTokens()
|
||||
if (newValue != null) {
|
||||
addSymbol?.invoke(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item("textFields") {
|
||||
Column {
|
||||
TimeUnitTextField(
|
||||
value = uiState.years,
|
||||
onValueChange = updateYears,
|
||||
label = stringResource(R.string.date_difference_years),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
TimeUnitTextField(
|
||||
value = uiState.months,
|
||||
onValueChange = updateMonths,
|
||||
label = stringResource(R.string.date_difference_months),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
TimeUnitTextField(
|
||||
value = uiState.days,
|
||||
onValueChange = updateDays,
|
||||
label = stringResource(R.string.date_difference_days),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
TimeUnitTextField(
|
||||
value = uiState.hours,
|
||||
onValueChange = updateHours,
|
||||
label = stringResource(R.string.date_difference_hours),
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
TimeUnitTextField(
|
||||
value = uiState.minutes,
|
||||
onValueChange = updateMinutes,
|
||||
label = stringResource(R.string.date_difference_minutes),
|
||||
imeAction = ImeAction.Done,
|
||||
formatterSymbols = uiState.formatterSymbols
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onConfirm = {
|
||||
focusManager.clearFocus()
|
||||
addSymbol = null
|
||||
focusedTextFieldValue = null
|
||||
},
|
||||
allowVibration = uiState.allowVibration,
|
||||
imeAction = if (addSymbol == updateMinutes) ImeAction.Done else ImeAction.Next
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,14 +340,15 @@ private fun Context.addEvent(start: ZonedDateTime, end: ZonedDateTime) {
|
||||
fun AddSubtractViewPreview() {
|
||||
AddSubtractView(
|
||||
uiState = AddSubtractState(
|
||||
years = "12"
|
||||
years = TextFieldValue("12")
|
||||
),
|
||||
toggleTopBar = {},
|
||||
updateStart = {},
|
||||
updateYears = {},
|
||||
updateMonths = {},
|
||||
updateDays = {},
|
||||
updateHours = {},
|
||||
updateMinutes = {},
|
||||
updateAddition = {}
|
||||
updateAddition = {},
|
||||
)
|
||||
}
|
||||
|
@ -18,17 +18,19 @@
|
||||
|
||||
package com.sadellie.unitto.feature.datecalculator.addsubtract
|
||||
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
internal data class AddSubtractState(
|
||||
val start: ZonedDateTime = ZonedDateTime.now(),
|
||||
val result: ZonedDateTime = ZonedDateTime.now(),
|
||||
val years: String = "",
|
||||
val months: String = "",
|
||||
val days: String = "",
|
||||
val hours: String = "",
|
||||
val minutes: String = "",
|
||||
val years: TextFieldValue = TextFieldValue(),
|
||||
val months: TextFieldValue = TextFieldValue(),
|
||||
val days: TextFieldValue = TextFieldValue(),
|
||||
val hours: TextFieldValue = TextFieldValue(),
|
||||
val minutes: TextFieldValue = TextFieldValue(),
|
||||
val addition: Boolean = true,
|
||||
val formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces,
|
||||
val allowVibration: Boolean = false,
|
||||
)
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
package com.sadellie.unitto.feature.datecalculator.addsubtract
|
||||
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
|
||||
@ -44,7 +46,8 @@ internal class AddSubtractViewModel @Inject constructor(
|
||||
val uiState: StateFlow<AddSubtractState> = _uiState
|
||||
.combine(userPreferencesRepository.addSubtractPrefs) { uiState, userPrefs ->
|
||||
return@combine uiState.copy(
|
||||
formatterSymbols = AllFormatterSymbols.getById(userPrefs.separator)
|
||||
formatterSymbols = AllFormatterSymbols.getById(userPrefs.separator),
|
||||
allowVibration = userPrefs.enableVibrations,
|
||||
)
|
||||
}
|
||||
.onEach { updateResult() }
|
||||
@ -52,78 +55,57 @@ internal class AddSubtractViewModel @Inject constructor(
|
||||
|
||||
fun updateStart(newValue: ZonedDateTime) = _uiState.update { it.copy(start = newValue) }
|
||||
|
||||
fun updateYears(newValue: String) = _uiState.update {
|
||||
val years = when {
|
||||
newValue.isEmpty() -> newValue
|
||||
newValue.toLong() > 9_999L -> "9999"
|
||||
else -> newValue
|
||||
}
|
||||
|
||||
it.copy(years = years)
|
||||
fun updateYears(value: TextFieldValue) = _uiState.update {
|
||||
it.copy(years = checkWithMax(value, 9_999L))
|
||||
}
|
||||
|
||||
fun updateMonths(newValue: String) = _uiState.update {
|
||||
val months = when {
|
||||
newValue.isEmpty() -> newValue
|
||||
newValue.toLong() > 9_999L -> "9999"
|
||||
else -> newValue
|
||||
}
|
||||
|
||||
it.copy(months = months)
|
||||
fun updateMonths(value: TextFieldValue) = _uiState.update {
|
||||
it.copy(months = checkWithMax(value, 9_999L))
|
||||
}
|
||||
|
||||
fun updateDays(newValue: String) = _uiState.update {
|
||||
val days = when {
|
||||
newValue.isEmpty() -> newValue
|
||||
newValue.toLong() > 99_999L -> "99999"
|
||||
else -> newValue
|
||||
}
|
||||
|
||||
it.copy(days = days)
|
||||
fun updateDays(value: TextFieldValue) = _uiState.update {
|
||||
it.copy(days = checkWithMax(value, 99_999L))
|
||||
}
|
||||
|
||||
fun updateHours(newValue: String) = _uiState.update {
|
||||
val hours = when {
|
||||
newValue.isEmpty() -> newValue
|
||||
newValue.toLong() > 9_999_999L -> "9999999"
|
||||
else -> newValue
|
||||
}
|
||||
|
||||
it.copy(hours = hours)
|
||||
fun updateHours(value: TextFieldValue) = _uiState.update {
|
||||
it.copy(hours = checkWithMax(value, 9_999_999L))
|
||||
}
|
||||
|
||||
fun updateMinutes(newValue: String) = _uiState.update {
|
||||
val minutes = when {
|
||||
newValue.isEmpty() -> newValue
|
||||
newValue.toLong() > 99_999_999L -> "99999999"
|
||||
else -> newValue
|
||||
}
|
||||
|
||||
it.copy(minutes = minutes)
|
||||
fun updateMinutes(value: TextFieldValue) = _uiState.update {
|
||||
it.copy(minutes = checkWithMax(value, 99_999_999L))
|
||||
}
|
||||
|
||||
// BCE is not handled properly because who gives a shit...
|
||||
fun updateAddition(newValue: Boolean) = _uiState.update { it.copy(addition = newValue) }
|
||||
fun updateAddition(newValue: Boolean) = _uiState.update {
|
||||
it.copy(addition = newValue)
|
||||
}
|
||||
|
||||
private fun updateResult() = viewModelScope.launch(Dispatchers.Default) {
|
||||
// Gets canceled, works with latest _uiState only
|
||||
_uiState.update { ui ->
|
||||
val newResult = if (ui.addition) {
|
||||
ui.start
|
||||
.plusYears(ui.years.ifEmpty { "0" }.toLong())
|
||||
.plusMonths(ui.months.ifEmpty { "0" }.toLong())
|
||||
.plusDays(ui.days.ifEmpty { "0" }.toLong())
|
||||
.plusHours(ui.hours.ifEmpty { "0" }.toLong())
|
||||
.plusMinutes(ui.minutes.ifEmpty { "0" }.toLong())
|
||||
.plusYears(ui.years.text.ifEmpty { "0" }.toLong())
|
||||
.plusMonths(ui.months.text.ifEmpty { "0" }.toLong())
|
||||
.plusDays(ui.days.text.ifEmpty { "0" }.toLong())
|
||||
.plusHours(ui.hours.text.ifEmpty { "0" }.toLong())
|
||||
.plusMinutes(ui.minutes.text.ifEmpty { "0" }.toLong())
|
||||
} else {
|
||||
ui.start
|
||||
.minusYears(ui.years.ifEmpty { "0" }.toLong())
|
||||
.minusMonths(ui.months.ifEmpty { "0" }.toLong())
|
||||
.minusDays(ui.days.ifEmpty { "0" }.toLong())
|
||||
.minusHours(ui.hours.ifEmpty { "0" }.toLong())
|
||||
.minusMinutes(ui.minutes.ifEmpty { "0" }.toLong())
|
||||
.minusYears(ui.years.text.ifEmpty { "0" }.toLong())
|
||||
.minusMonths(ui.months.text.ifEmpty { "0" }.toLong())
|
||||
.minusDays(ui.days.text.ifEmpty { "0" }.toLong())
|
||||
.minusHours(ui.hours.text.ifEmpty { "0" }.toLong())
|
||||
.minusMinutes(ui.minutes.text.ifEmpty { "0" }.toLong())
|
||||
}
|
||||
ui.copy(result = newResult)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkWithMax(value: TextFieldValue, maxValue: Long): TextFieldValue {
|
||||
if (value.text.isEmpty()) return value
|
||||
if (value.text.toLong() <= maxValue) return value
|
||||
val maxValueText = maxValue.toString()
|
||||
return TextFieldValue(maxValueText, TextRange(maxValueText.length))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.unit.dp
|
||||
import com.sadellie.unitto.core.base.Token
|
||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonFilled
|
||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonLight
|
||||
import com.sadellie.unitto.core.ui.common.key.UnittoIcons
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Backspace
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Check
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key0
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key1
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key2
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key3
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key4
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key5
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key6
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key7
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key8
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Key9
|
||||
import com.sadellie.unitto.core.ui.common.key.unittoicons.Tab
|
||||
import com.sadellie.unitto.core.ui.isPortrait
|
||||
|
||||
@Composable
|
||||
internal fun AddSubtractKeyboard(
|
||||
modifier: Modifier,
|
||||
addSymbol: (String) -> Unit,
|
||||
deleteSymbol: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
allowVibration: Boolean,
|
||||
imeAction: ImeAction,
|
||||
focusManager: FocusManager = LocalFocusManager.current
|
||||
) {
|
||||
Row(modifier) {
|
||||
val weightModifier = Modifier.weight(1f)
|
||||
val mainButtonModifier = Modifier
|
||||
.fillMaxSize()
|
||||
.weight(1f)
|
||||
.padding(4.dp)
|
||||
val actionIconHeight = if (isPortrait()) 0.35f else 0.6f
|
||||
|
||||
fun keyboardAction() {
|
||||
when (imeAction) {
|
||||
ImeAction.Next -> focusManager.moveFocus(FocusDirection.Next)
|
||||
else -> onConfirm()
|
||||
}
|
||||
}
|
||||
|
||||
Column(weightModifier) {
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key7, allowVibration) {
|
||||
addSymbol(Token.Digit._7)
|
||||
}
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key4, allowVibration) {
|
||||
addSymbol(Token.Digit._4
|
||||
)
|
||||
}
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key1, allowVibration) {
|
||||
addSymbol(Token.Digit._1)
|
||||
}
|
||||
Spacer(mainButtonModifier)
|
||||
}
|
||||
|
||||
Column(weightModifier) {
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key8, allowVibration) {
|
||||
addSymbol(Token.Digit._8)
|
||||
}
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key5, allowVibration) {
|
||||
addSymbol(Token.Digit._5)
|
||||
}
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key2, allowVibration) {
|
||||
addSymbol(Token.Digit._2)
|
||||
}
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key0, allowVibration) {
|
||||
addSymbol(Token.Digit._0)
|
||||
}
|
||||
}
|
||||
|
||||
Column(weightModifier) {
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key9, allowVibration) {
|
||||
addSymbol(Token.Digit._9)
|
||||
}
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key6, allowVibration) {
|
||||
addSymbol(Token.Digit._6)
|
||||
}
|
||||
KeyboardButtonLight(mainButtonModifier, UnittoIcons.Key3, allowVibration) {
|
||||
addSymbol(Token.Digit._3)
|
||||
}
|
||||
Spacer(mainButtonModifier)
|
||||
}
|
||||
|
||||
Column(weightModifier) {
|
||||
Crossfade(targetState = imeAction, modifier = mainButtonModifier) {
|
||||
when (it) {
|
||||
ImeAction.Next -> KeyboardButtonFilled(
|
||||
Modifier.fillMaxSize(),
|
||||
UnittoIcons.Tab,
|
||||
allowVibration,
|
||||
actionIconHeight
|
||||
) { keyboardAction() }
|
||||
else -> KeyboardButtonFilled(
|
||||
Modifier.fillMaxSize(),
|
||||
UnittoIcons.Check,
|
||||
allowVibration,
|
||||
actionIconHeight
|
||||
) { keyboardAction() }
|
||||
}
|
||||
}
|
||||
KeyboardButtonLight(
|
||||
mainButtonModifier,
|
||||
UnittoIcons.Backspace,
|
||||
allowVibration,
|
||||
actionIconHeight
|
||||
) { deleteSymbol() }
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,6 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Clear
|
||||
import androidx.compose.material3.Icon
|
||||
@ -31,43 +30,39 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.platform.LocalTextInputService
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer
|
||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
||||
|
||||
@Composable
|
||||
internal fun TimeUnitTextField(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier,
|
||||
value: TextFieldValue,
|
||||
onValueChange: (TextFieldValue) -> Unit,
|
||||
label: String,
|
||||
imeAction: ImeAction = ImeAction.Next,
|
||||
formatterSymbols: FormatterSymbols
|
||||
) {
|
||||
) = CompositionLocalProvider(LocalTextInputService provides null) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
value = value,
|
||||
onValueChange = { newValue ->
|
||||
onValueChange(newValue.filter { it.isDigit() })
|
||||
onValueChange(newValue.copy(newValue.text.filter { it.isDigit() }))
|
||||
},
|
||||
label = { Text(label, color = MaterialTheme.colorScheme.onSurfaceVariant) },
|
||||
trailingIcon = {
|
||||
AnimatedVisibility(
|
||||
visible = value.isNotBlank(),
|
||||
visible = value.text.isNotBlank(),
|
||||
enter = scaleIn(),
|
||||
exit = scaleOut()
|
||||
) {
|
||||
IconButton(onClick = { onValueChange("") }) {
|
||||
IconButton(onClick = { onValueChange(TextFieldValue()) }) {
|
||||
Icon(Icons.Outlined.Clear, null)
|
||||
}
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
autoCorrect = false,
|
||||
keyboardType = KeyboardType.Decimal,
|
||||
imeAction = imeAction
|
||||
),
|
||||
visualTransformation = ExpressionTransformer(formatterSymbols)
|
||||
)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user