mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 08:45:27 +02:00
Use dots to indicate pages
This commit is contained in:
parent
4305354931
commit
4e77eaf01c
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,11 +18,8 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.feature.settings.formatting
|
package com.sadellie.unitto.feature.settings.formatting
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.horizontalScroll
|
import androidx.compose.foundation.horizontalScroll
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
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.R
|
||||||
import com.sadellie.unitto.core.base.Separator
|
import com.sadellie.unitto.core.base.Separator
|
||||||
import com.sadellie.unitto.core.ui.common.NavigateUpButton
|
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.SegmentedButton
|
||||||
import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow
|
import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow
|
||||||
import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen
|
import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen
|
||||||
import com.sadellie.unitto.core.ui.common.UnittoListItem
|
import com.sadellie.unitto.core.ui.common.UnittoListItem
|
||||||
import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar
|
import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar
|
||||||
import com.sadellie.unitto.core.ui.common.UnittoSlider
|
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.common.textfield.formatExpression
|
||||||
import com.sadellie.unitto.core.ui.theme.NumberTypographyUnitto
|
import com.sadellie.unitto.core.ui.theme.NumberTypographyUnitto
|
||||||
|
import com.sadellie.unitto.data.common.format
|
||||||
|
import kotlin.math.ceil
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -80,7 +80,6 @@ fun FormattingRoute(
|
|||||||
onPrecisionChange = viewModel::updatePrecision,
|
onPrecisionChange = viewModel::updatePrecision,
|
||||||
onSeparatorChange = viewModel::updateSeparator,
|
onSeparatorChange = viewModel::updateSeparator,
|
||||||
onOutputFormatChange = viewModel::updateOutputFormat,
|
onOutputFormatChange = viewModel::updateOutputFormat,
|
||||||
togglePreview = viewModel::togglePreview
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,7 +92,6 @@ fun FormattingScreen(
|
|||||||
onPrecisionChange: (Int) -> Unit,
|
onPrecisionChange: (Int) -> Unit,
|
||||||
onSeparatorChange: (Int) -> Unit,
|
onSeparatorChange: (Int) -> Unit,
|
||||||
onOutputFormatChange: (Int) -> Unit,
|
onOutputFormatChange: (Int) -> Unit,
|
||||||
togglePreview: () -> Unit,
|
|
||||||
precisions: ClosedFloatingPointRange<Float> = 0f..16f, // 16th is a MAX_PRECISION (1000)
|
precisions: ClosedFloatingPointRange<Float> = 0f..16f, // 16th is a MAX_PRECISION (1000)
|
||||||
) {
|
) {
|
||||||
val resources = LocalContext.current.resources
|
val resources = LocalContext.current.resources
|
||||||
@ -120,25 +118,24 @@ fun FormattingScreen(
|
|||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
) {
|
) {
|
||||||
item("preview") {
|
item("preview") {
|
||||||
Column(
|
PagedIsland(
|
||||||
Modifier
|
modifier = Modifier
|
||||||
.padding(16.dp)
|
|
||||||
.squashable(
|
|
||||||
onClick = togglePreview,
|
|
||||||
cornerRadiusRange = 8.dp..32.dp,
|
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
|
||||||
)
|
|
||||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
|
||||||
.fillMaxWidth()
|
.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(
|
||||||
text = stringResource(R.string.settings_formatting_preview),
|
text = preview,
|
||||||
style = MaterialTheme.typography.labelMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onSecondaryContainer
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = uiState.preview,
|
|
||||||
style = NumberTypographyUnitto.displayMedium,
|
style = NumberTypographyUnitto.displayMedium,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -153,7 +150,10 @@ fun FormattingScreen(
|
|||||||
item("precision_label") {
|
item("precision_label") {
|
||||||
UnittoListItem(
|
UnittoListItem(
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(Icons.Default.Architecture, stringResource(R.string.settings_precision))
|
Icon(
|
||||||
|
Icons.Default.Architecture,
|
||||||
|
stringResource(R.string.settings_precision)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Row(
|
Row(
|
||||||
@ -265,15 +265,14 @@ private fun PreviewFormattingScreen() {
|
|||||||
|
|
||||||
FormattingScreen(
|
FormattingScreen(
|
||||||
uiState = FormattingUIState(
|
uiState = FormattingUIState(
|
||||||
preview = "123456.789",
|
|
||||||
precision = 16,
|
precision = 16,
|
||||||
separator = Separator.SPACE,
|
separator = Separator.SPACE,
|
||||||
outputFormat = OutputFormat.PLAIN
|
outputFormat = OutputFormat.PLAIN,
|
||||||
|
formatterSymbols = FormatterSymbols.Spaces
|
||||||
),
|
),
|
||||||
onPrecisionChange = { currentPrecision = it },
|
onPrecisionChange = { currentPrecision = it },
|
||||||
onSeparatorChange = { currentSeparator = it },
|
onSeparatorChange = { currentSeparator = it },
|
||||||
onOutputFormatChange = { currentOutputFormat = it },
|
onOutputFormatChange = { currentOutputFormat = it },
|
||||||
navigateUpAction = {},
|
navigateUpAction = {},
|
||||||
togglePreview = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,8 @@ package com.sadellie.unitto.feature.settings.formatting
|
|||||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
||||||
|
|
||||||
data class FormattingUIState(
|
data class FormattingUIState(
|
||||||
val preview: String = "",
|
val precision: Int,
|
||||||
val precision: Int = 0,
|
val separator: Int,
|
||||||
val separator: Int? = null,
|
val outputFormat: Int,
|
||||||
val outputFormat: Int? = null,
|
val formatterSymbols: FormatterSymbols,
|
||||||
val formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces
|
|
||||||
)
|
)
|
||||||
|
@ -22,64 +22,29 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.sadellie.unitto.core.base.MAX_PRECISION
|
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.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.common.stateIn
|
||||||
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
|
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.math.BigDecimal
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.ceil
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class FormattingViewModel @Inject constructor(
|
class FormattingViewModel @Inject constructor(
|
||||||
private val userPreferencesRepository: UserPreferencesRepository
|
private val userPreferencesRepository: UserPreferencesRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _prefs = userPreferencesRepository.formattingPrefs
|
private val _prefs = userPreferencesRepository.formattingPrefs
|
||||||
private val _fractional = MutableStateFlow(false)
|
|
||||||
|
|
||||||
val uiState = combine(_prefs, _fractional) { mainPrefs, fractional ->
|
val uiState = _prefs.map { mainPrefs ->
|
||||||
val formatterSymbols = AllFormatterSymbols.getById(mainPrefs.separator)
|
FormattingUIState(
|
||||||
|
|
||||||
return@combine FormattingUIState(
|
|
||||||
preview = updatePreview(
|
|
||||||
fractional = fractional,
|
|
||||||
precision = mainPrefs.digitsPrecision,
|
|
||||||
outputFormat = mainPrefs.outputFormat,
|
|
||||||
formatterSymbols = formatterSymbols
|
|
||||||
),
|
|
||||||
precision = mainPrefs.digitsPrecision,
|
precision = mainPrefs.digitsPrecision,
|
||||||
separator = mainPrefs.separator,
|
separator = mainPrefs.separator,
|
||||||
outputFormat = mainPrefs.outputFormat,
|
outputFormat = mainPrefs.outputFormat,
|
||||||
formatterSymbols = formatterSymbols
|
formatterSymbols = AllFormatterSymbols.getById(mainPrefs.separator)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.stateIn(viewModelScope, null)
|
.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
|
* @see UserPreferencesRepository.updateDigitsPrecision
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user