Use dots to indicate pages

This commit is contained in:
Sad Ellie 2023-11-24 13:25:22 +03:00
parent 4305354931
commit 4e77eaf01c
4 changed files with 142 additions and 71 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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")
}
}
}

View File

@ -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<Float> = 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 = {}
)
}

View File

@ -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,
)

View File

@ -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
*/