diff --git a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/PagedIsland.kt b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/PagedIsland.kt new file mode 100644 index 00000000..4ffedd94 --- /dev/null +++ b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/PagedIsland.kt @@ -0,0 +1,108 @@ +/* + * 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 . + */ + +package com.sadellie.unitto.core.ui.common + +import androidx.annotation.IntRange +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +@Composable +fun PagedIsland( + modifier: Modifier = Modifier, + @IntRange(from = 1) pagesCount: Int, + onPageChange: (currentPage: Int) -> Unit = {}, + backgroundColor: Color = MaterialTheme.colorScheme.secondaryContainer, + pageContent: @Composable ColumnScope.(currentPage: Int) -> Unit, +) { + var currentPage: Int by remember { mutableIntStateOf(0) } + val contentColor = MaterialTheme.colorScheme.contentColorFor(backgroundColor) + val disabledContentColor = contentColor.copy(alpha = 0.5f) + + AnimatedContent( + modifier = modifier + .squashable( + onClick = { + if (currentPage == pagesCount - 1) currentPage = 0 else currentPage++ + onPageChange(currentPage) + }, + cornerRadiusRange = 8.dp..32.dp, + interactionSource = remember { MutableInteractionSource() } + ) + .background(backgroundColor), + targetState = currentPage + ) { state -> + ProvideColor(color = contentColor) { + Column( + modifier = Modifier.padding(16.dp) + ) { + Row( + modifier = Modifier + .padding(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + repeat(pagesCount) { + ADot(color = if (it == state) contentColor else disabledContentColor) + } + } + pageContent(state) + } + } + } +} + +@Composable +private fun ADot( + color: Color, +) { + Canvas(modifier = Modifier.size(4.dp)) { + drawCircle(color) + } +} + +@Preview +@Composable +private fun PreviewPagedIsland() { + PagedIsland(pagesCount = 5) { currentPage -> + Text("Current page: $currentPage") + + if (currentPage == 3) { + Text("Middle in: $currentPage") + } + } +} diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingScreen.kt index ee28e56c..c048e4ea 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingScreen.kt @@ -18,11 +18,8 @@ package com.sadellie.unitto.feature.settings.formatting -import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -55,15 +52,18 @@ import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.ui.common.NavigateUpButton +import com.sadellie.unitto.core.ui.common.PagedIsland import com.sadellie.unitto.core.ui.common.SegmentedButton import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.core.ui.common.UnittoSlider -import com.sadellie.unitto.core.ui.common.squashable +import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.theme.NumberTypographyUnitto +import com.sadellie.unitto.data.common.format +import kotlin.math.ceil import kotlin.math.roundToInt @Composable @@ -80,7 +80,6 @@ fun FormattingRoute( onPrecisionChange = viewModel::updatePrecision, onSeparatorChange = viewModel::updateSeparator, onOutputFormatChange = viewModel::updateOutputFormat, - togglePreview = viewModel::togglePreview ) } } @@ -93,7 +92,6 @@ fun FormattingScreen( onPrecisionChange: (Int) -> Unit, onSeparatorChange: (Int) -> Unit, onOutputFormatChange: (Int) -> Unit, - togglePreview: () -> Unit, precisions: ClosedFloatingPointRange = 0f..16f, // 16th is a MAX_PRECISION (1000) ) { val resources = LocalContext.current.resources @@ -120,25 +118,24 @@ fun FormattingScreen( .padding(paddingValues) ) { item("preview") { - Column( - Modifier - .padding(16.dp) - .squashable( - onClick = togglePreview, - cornerRadiusRange = 8.dp..32.dp, - interactionSource = remember { MutableInteractionSource() } - ) - .background(MaterialTheme.colorScheme.secondaryContainer) + PagedIsland( + modifier = Modifier .fillMaxWidth() - .padding(16.dp) - ) { + .padding(16.dp), + pagesCount = 2, + ) { currentPage -> + val preview = when (currentPage) { + 0 -> "123456.${"789123456".repeat(ceil(uiState.precision.toDouble() / 9.0).toInt())}" + 1 -> "0.${"1".padStart(uiState.precision, '0')}" + else -> "" + } + .toBigDecimalOrNull() + ?.format(uiState.precision, uiState.outputFormat) + ?.formatExpression(uiState.formatterSymbols) + ?: "" + Text( - text = stringResource(R.string.settings_formatting_preview), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSecondaryContainer - ) - Text( - text = uiState.preview, + text = preview, style = NumberTypographyUnitto.displayMedium, maxLines = 1, modifier = Modifier @@ -153,7 +150,10 @@ fun FormattingScreen( item("precision_label") { UnittoListItem( leadingContent = { - Icon(Icons.Default.Architecture, stringResource(R.string.settings_precision)) + Icon( + Icons.Default.Architecture, + stringResource(R.string.settings_precision) + ) }, headlineContent = { Row( @@ -265,15 +265,14 @@ private fun PreviewFormattingScreen() { FormattingScreen( uiState = FormattingUIState( - preview = "123456.789", precision = 16, separator = Separator.SPACE, - outputFormat = OutputFormat.PLAIN + outputFormat = OutputFormat.PLAIN, + formatterSymbols = FormatterSymbols.Spaces ), onPrecisionChange = { currentPrecision = it }, onSeparatorChange = { currentSeparator = it }, onOutputFormatChange = { currentOutputFormat = it }, navigateUpAction = {}, - togglePreview = {} ) } diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingUIState.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingUIState.kt index fb23227d..3a5b6ce0 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingUIState.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingUIState.kt @@ -21,9 +21,8 @@ package com.sadellie.unitto.feature.settings.formatting import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols data class FormattingUIState( - val preview: String = "", - val precision: Int = 0, - val separator: Int? = null, - val outputFormat: Int? = null, - val formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces + val precision: Int, + val separator: Int, + val outputFormat: Int, + val formatterSymbols: FormatterSymbols, ) diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingViewModel.kt index 7a66d743..94acb92a 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingViewModel.kt @@ -22,64 +22,29 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sadellie.unitto.core.base.MAX_PRECISION import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols -import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols -import com.sadellie.unitto.core.ui.common.textfield.formatExpression -import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import java.math.BigDecimal import javax.inject.Inject -import kotlin.math.ceil @HiltViewModel class FormattingViewModel @Inject constructor( private val userPreferencesRepository: UserPreferencesRepository ) : ViewModel() { private val _prefs = userPreferencesRepository.formattingPrefs - private val _fractional = MutableStateFlow(false) - val uiState = combine(_prefs, _fractional) { mainPrefs, fractional -> - val formatterSymbols = AllFormatterSymbols.getById(mainPrefs.separator) - - return@combine FormattingUIState( - preview = updatePreview( - fractional = fractional, - precision = mainPrefs.digitsPrecision, - outputFormat = mainPrefs.outputFormat, - formatterSymbols = formatterSymbols - ), + val uiState = _prefs.map { mainPrefs -> + FormattingUIState( precision = mainPrefs.digitsPrecision, separator = mainPrefs.separator, outputFormat = mainPrefs.outputFormat, - formatterSymbols = formatterSymbols + formatterSymbols = AllFormatterSymbols.getById(mainPrefs.separator) ) } .stateIn(viewModelScope, null) - fun togglePreview() = _fractional.update { !it } - - private fun updatePreview( - fractional: Boolean, - precision: Int, - outputFormat: Int, - formatterSymbols: FormatterSymbols - ): String { - val bigD = when { - fractional -> "0.${"1".padStart(precision, '0')}" - precision > 0 -> "123456.${"789123456".repeat(ceil(precision.toDouble() / 9.0).toInt())}" - else -> "123456" - } - - return BigDecimal(bigD) - .format(precision, outputFormat) - .formatExpression(formatterSymbols) - } - /** * @see UserPreferencesRepository.updateDigitsPrecision */