mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
Merge pull request #13 from Myzel394/improve-keyboard-layout
Improve converter screen
This commit is contained in:
commit
f92becc333
@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.FlowRowScope
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
/**
|
||||
@ -51,8 +50,8 @@ fun KeypadFlow(
|
||||
@IntRange(0, 100) verticalPadding: Int = 10,
|
||||
content: @Composable FlowRowScope.(width: Float, height: Float) -> Unit,
|
||||
) {
|
||||
val height: Float = remember { (1f - verticalPadding / 100f) / rows }
|
||||
val width: Float = remember { (1f - horizontalPadding / 100f) / columns }
|
||||
val height: Float = (1f - verticalPadding / 100f) / rows
|
||||
val width: Float = (1f - horizontalPadding / 100f) / columns
|
||||
|
||||
FlowRow(
|
||||
modifier = modifier,
|
||||
|
@ -102,17 +102,232 @@ class UnitsRepositoryImpl @Inject constructor(
|
||||
|
||||
suspend fun getPairId(id: String): String = withContext(Dispatchers.IO) {
|
||||
val basedUnitPair = getUnitStats(id).pairedUnitId
|
||||
if (basedUnitPair != null) return@withContext basedUnitPair
|
||||
if (basedUnitPair != null) {
|
||||
return@withContext basedUnitPair
|
||||
}
|
||||
|
||||
val inMemoryUnit = inMemory.first { it.id == id }
|
||||
val collection = inMemory.filter { it.group == inMemoryUnit.group }
|
||||
|
||||
val pair = collection
|
||||
.map { getById(it.id) to getUnitStats(it.id) }
|
||||
.sortedByDescending { it.second.frequency }
|
||||
.firstOrNull { it.second.isFavorite }?.first ?: collection.first()
|
||||
return@withContext when (inMemoryUnit.id) {
|
||||
// === === === Length === === ===
|
||||
UnitID.nanometer -> UnitID.micrometer
|
||||
UnitID.micrometer -> UnitID.millimeter
|
||||
UnitID.millimeter -> UnitID.centimeter
|
||||
|
||||
return@withContext pair.id
|
||||
UnitID.centimeter -> UnitID.inch
|
||||
UnitID.inch -> UnitID.centimeter
|
||||
UnitID.decimeter -> UnitID.centimeter
|
||||
UnitID.foot -> UnitID.meter
|
||||
UnitID.yard -> UnitID.meter
|
||||
UnitID.meter -> UnitID.foot
|
||||
|
||||
UnitID.kilometer -> UnitID.mile
|
||||
UnitID.mile -> UnitID.kilometer
|
||||
UnitID.nautical_mile -> UnitID.kilometer
|
||||
|
||||
UnitID.mercury_equatorial_radius -> UnitID.kilometer
|
||||
UnitID.mars_equatorial_radius -> UnitID.kilometer
|
||||
UnitID.venus_equatorial_radius -> UnitID.kilometer
|
||||
UnitID.earth_equatorial_radius -> UnitID.kilometer
|
||||
UnitID.neptune_equatorial_radius -> UnitID.kilometer
|
||||
UnitID.uranus_equatorial_radius -> UnitID.kilometer
|
||||
UnitID.saturn_equatorial_radius -> UnitID.kilometer
|
||||
UnitID.jupiter_equatorial_radius -> UnitID.kilometer
|
||||
UnitID.sun_equatorial_radius -> UnitID.kilometer
|
||||
|
||||
UnitID.light_year -> UnitID.kilometer
|
||||
|
||||
UnitID.parsec -> UnitID.light_year
|
||||
UnitID.kiloparsec -> UnitID.parsec
|
||||
UnitID.megaparsec -> UnitID.kiloparsec
|
||||
|
||||
|
||||
// === === === Mass === === ===
|
||||
UnitID.electron_mass_rest -> UnitID.atomic_mass_unit
|
||||
UnitID.atomic_mass_unit -> UnitID.electron_mass_rest
|
||||
|
||||
UnitID.microgram -> UnitID.milligram
|
||||
UnitID.milligram -> UnitID.gram
|
||||
UnitID.grain -> UnitID.gram
|
||||
UnitID.carat -> UnitID.gram
|
||||
|
||||
UnitID.gram -> UnitID.carat
|
||||
UnitID.ounce -> UnitID.gram
|
||||
UnitID.pound -> UnitID.kilogram
|
||||
UnitID.kilogram -> UnitID.pound
|
||||
|
||||
UnitID.metric_ton -> UnitID.kilogram
|
||||
UnitID.imperial_ton -> UnitID.pound
|
||||
|
||||
UnitID.mercury_mass -> UnitID.kilogram
|
||||
UnitID.mars_mass -> UnitID.kilogram
|
||||
UnitID.venus_mass -> UnitID.kilogram
|
||||
UnitID.earth_mass -> UnitID.kilogram
|
||||
UnitID.uranus_mass -> UnitID.kilogram
|
||||
UnitID.neptune_mass -> UnitID.kilogram
|
||||
UnitID.saturn_mass -> UnitID.kilogram
|
||||
UnitID.jupiter_mass -> UnitID.kilogram
|
||||
UnitID.sun_mass -> UnitID.kilogram
|
||||
|
||||
|
||||
// === === === Speed === === ===
|
||||
UnitID.millimeter_per_hour -> UnitID.millimeter_per_second
|
||||
UnitID.millimeter_per_second -> UnitID.millimeter_per_hour
|
||||
UnitID.millimeter_per_minute -> UnitID.millimeter_per_second
|
||||
UnitID.centimeter_per_hour -> UnitID.centimeter_per_second
|
||||
UnitID.centimeter_per_second -> UnitID.centimeter_per_hour
|
||||
UnitID.centimeter_per_minute -> UnitID.centimeter_per_second
|
||||
UnitID.meter_per_hour -> UnitID.meter_per_second
|
||||
UnitID.meter_per_second -> UnitID.meter_per_hour
|
||||
UnitID.meter_per_minute -> UnitID.meter_per_second
|
||||
|
||||
UnitID.kilometer_per_hour -> UnitID.mile_per_hour
|
||||
UnitID.kilometer_per_second -> UnitID.mile_per_second
|
||||
UnitID.kilometer_per_minute -> UnitID.kilometer_per_second
|
||||
UnitID.mile_per_hour -> UnitID.kilometer_per_hour
|
||||
UnitID.mile_per_second -> UnitID.kilometer_per_second
|
||||
UnitID.mile_per_minute -> UnitID.mile_per_second
|
||||
|
||||
UnitID.foot_per_hour -> UnitID.foot_per_second
|
||||
UnitID.foot_per_second -> UnitID.foot_per_hour
|
||||
UnitID.foot_per_minute -> UnitID.foot_per_second
|
||||
UnitID.yard_per_hour -> UnitID.yard_per_second
|
||||
UnitID.yard_per_second -> UnitID.yard_per_hour
|
||||
UnitID.yard_per_minute -> UnitID.yard_per_second
|
||||
|
||||
UnitID.knot -> UnitID.kilometer_per_hour
|
||||
UnitID.mach -> UnitID.kilometer_per_hour
|
||||
UnitID.velocity_of_light_in_vacuum -> UnitID.kilometer_per_hour
|
||||
UnitID.earths_orbital_speed -> UnitID.kilometer_per_hour
|
||||
|
||||
UnitID.cosmic_velocity_first -> UnitID.kilometer_per_hour
|
||||
UnitID.cosmic_velocity_second -> UnitID.kilometer_per_hour
|
||||
UnitID.cosmic_velocity_third -> UnitID.kilometer_per_hour
|
||||
|
||||
|
||||
// === === === Temperature === === ===
|
||||
UnitID.celsius -> UnitID.fahrenheit
|
||||
UnitID.fahrenheit -> UnitID.celsius
|
||||
UnitID.kelvin -> UnitID.celsius
|
||||
|
||||
|
||||
// === === === Area === === ===
|
||||
UnitID.square_micrometer -> UnitID.square_millimeter
|
||||
UnitID.square_millimeter -> UnitID.square_centimeter
|
||||
UnitID.square_centimeter -> UnitID.square_meter
|
||||
UnitID.square_decimeter -> UnitID.square_meter
|
||||
UnitID.square_meter -> UnitID.square_kilometer
|
||||
|
||||
UnitID.square_kilometer -> UnitID.square_meter
|
||||
|
||||
UnitID.square_inch -> UnitID.square_foot
|
||||
UnitID.square_foot -> UnitID.square_inch
|
||||
UnitID.square_yard -> UnitID.square_meter
|
||||
UnitID.square_mile -> UnitID.square_kilometer
|
||||
|
||||
UnitID.acre -> UnitID.square_meter
|
||||
UnitID.hectare -> UnitID.square_meter
|
||||
UnitID.cent -> UnitID.square_meter
|
||||
|
||||
|
||||
// === === === Time === === ===
|
||||
UnitID.attosecond -> UnitID.nanosecond
|
||||
UnitID.nanosecond -> UnitID.microsecond
|
||||
UnitID.microsecond -> UnitID.millisecond
|
||||
UnitID.millisecond -> UnitID.second
|
||||
|
||||
UnitID.jiffy -> UnitID.millisecond
|
||||
|
||||
UnitID.second -> UnitID.millisecond
|
||||
UnitID.minute -> UnitID.second
|
||||
UnitID.hour -> UnitID.minute
|
||||
UnitID.day -> UnitID.hour
|
||||
UnitID.week -> UnitID.day
|
||||
|
||||
|
||||
// === === === Data === === ===
|
||||
// TODO: Add tibibyte, exibyte
|
||||
UnitID.bit -> UnitID.byte
|
||||
UnitID.byte -> UnitID.kilobyte
|
||||
UnitID.kilobyte -> UnitID.megabyte
|
||||
UnitID.megabyte -> UnitID.gigabyte
|
||||
UnitID.gigabyte -> UnitID.terabyte
|
||||
UnitID.terabyte -> UnitID.petabyte
|
||||
UnitID.petabyte -> UnitID.exabyte
|
||||
|
||||
UnitID.kilobit -> UnitID.kilobyte
|
||||
UnitID.megabit -> UnitID.megabyte
|
||||
UnitID.gigabit -> UnitID.gigabyte
|
||||
UnitID.terabit -> UnitID.terabyte
|
||||
UnitID.petabit -> UnitID.petabyte
|
||||
UnitID.exabit -> UnitID.exabyte
|
||||
|
||||
UnitID.kibibit -> UnitID.kilobyte
|
||||
UnitID.mebibit -> UnitID.megabyte
|
||||
UnitID.gibibit -> UnitID.gigabyte
|
||||
|
||||
UnitID.kibibyte -> UnitID.kilobyte
|
||||
UnitID.mebibyte -> UnitID.megabyte
|
||||
UnitID.gibibyte -> UnitID.gigabyte
|
||||
|
||||
|
||||
// === === === Acceleration === === ===
|
||||
UnitID.millimeter_per_square_second -> UnitID.centimeter_per_square_second
|
||||
UnitID.centimeter_per_square_second -> UnitID.meter_per_square_second
|
||||
UnitID.decimeter_per_square_second -> UnitID.meter_per_square_second
|
||||
UnitID.meter_per_square_second -> UnitID.kilometer_per_square_second
|
||||
|
||||
UnitID.mercury_surface_gravity -> UnitID.meter_per_square_second
|
||||
UnitID.mars_surface_gravity -> UnitID.meter_per_square_second
|
||||
UnitID.venus_surface_gravity -> UnitID.meter_per_square_second
|
||||
UnitID.uranus_surface_gravity -> UnitID.meter_per_square_second
|
||||
UnitID.earth_surface_gravity -> UnitID.meter_per_square_second
|
||||
UnitID.saturn_surface_gravity -> UnitID.meter_per_square_second
|
||||
UnitID.neptune_surface_gravity -> UnitID.meter_per_square_second
|
||||
UnitID.jupiter_surface_gravity -> UnitID.meter_per_square_second
|
||||
UnitID.sun_surface_gravity -> UnitID.meter_per_square_second
|
||||
|
||||
|
||||
// === === === Power === === ===
|
||||
UnitID.attowatt -> UnitID.watt
|
||||
UnitID.watt -> UnitID.kilowatt
|
||||
UnitID.kilowatt -> UnitID.watt
|
||||
UnitID.megawatt -> UnitID.kilowatt
|
||||
|
||||
|
||||
// === === === Angle === === ===
|
||||
UnitID.degree -> UnitID.radian
|
||||
UnitID.radian -> UnitID.degree
|
||||
|
||||
|
||||
// === === === Data Transfer === === ===
|
||||
UnitID.bit_per_second -> UnitID.byte_per_second
|
||||
UnitID.kilobit_per_second -> UnitID.kilobyte_per_second
|
||||
UnitID.megabit_per_second -> UnitID.megabyte_per_second
|
||||
UnitID.gigabit_per_second -> UnitID.gigabyte_per_second
|
||||
UnitID.terabit_per_second -> UnitID.terabyte_per_second
|
||||
UnitID.petabit_per_second -> UnitID.petabyte_per_second
|
||||
UnitID.exabit_per_second -> UnitID.exabyte_per_second
|
||||
|
||||
UnitID.byte_per_second -> UnitID.kilobyte_per_second
|
||||
UnitID.kilobyte_per_second -> UnitID.megabyte_per_second
|
||||
UnitID.megabyte_per_second -> UnitID.gigabyte_per_second
|
||||
UnitID.gigabyte_per_second -> UnitID.megabyte_per_second
|
||||
UnitID.terabyte_per_second -> UnitID.gigabyte_per_second
|
||||
UnitID.petabyte_per_second -> UnitID.terabyte_per_second
|
||||
|
||||
|
||||
// === === === Fuel === === ===
|
||||
UnitID.kilometer_per_liter -> UnitID.mile_us_per_liter
|
||||
UnitID.mile_us_per_liter -> UnitID.kilometer_per_liter
|
||||
|
||||
else ->
|
||||
(collection
|
||||
.map { getById(it.id) to getUnitStats(it.id) }
|
||||
.sortedByDescending { it.second.frequency }
|
||||
.firstOrNull { it.second.isFavorite }?.first ?: collection.first()).id
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun incrementCounter(id: String) = withContext(Dispatchers.IO) {
|
||||
|
@ -37,6 +37,16 @@ sealed interface BasicUnit {
|
||||
|
||||
interface NumberBase : BasicUnit {
|
||||
fun convert(unitTo: NumberBase, value: String): String
|
||||
|
||||
companion object {
|
||||
val Hexadecimal = NumberBaseUnit(
|
||||
"hexadecimal",
|
||||
BigDecimal(16),
|
||||
UnitGroup.NUMBER_BASE,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interface Default : BasicUnit {
|
||||
|
@ -120,7 +120,7 @@ internal fun Preferences.getUnitConverterFormatTime(): Boolean {
|
||||
|
||||
internal fun Preferences.getUnitConverterSorting(): UnitsListSorting {
|
||||
return this[PrefsKeys.UNIT_CONVERTER_SORTING]
|
||||
?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE
|
||||
?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.SCALE_ASC
|
||||
}
|
||||
|
||||
internal fun Preferences.getShownUnitGroups(): List<UnitGroup> {
|
||||
|
@ -22,6 +22,7 @@ import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.SizeTransform
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandHorizontally
|
||||
@ -31,6 +32,11 @@ import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.AnchoredDraggableState
|
||||
import androidx.compose.foundation.gestures.DraggableAnchors
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.anchoredDraggable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@ -40,6 +46,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.SwapHoriz
|
||||
@ -64,6 +71,7 @@ import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.focus.onFocusEvent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@ -94,11 +102,14 @@ import app.myzel394.numberhub.data.common.isExpression
|
||||
import app.myzel394.numberhub.data.converter.ConverterResult
|
||||
import app.myzel394.numberhub.data.converter.UnitID
|
||||
import app.myzel394.numberhub.data.model.converter.UnitGroup
|
||||
import app.myzel394.numberhub.feature.converter.components.BaseCalculationSummary
|
||||
import app.myzel394.numberhub.feature.converter.components.DefaultKeyboard
|
||||
import app.myzel394.numberhub.feature.converter.components.NumberBaseKeyboard
|
||||
import app.myzel394.numberhub.feature.converter.components.UnitSelectionButton
|
||||
import app.myzel394.numberhub.feature.converter.components.ValueOneSummary
|
||||
import java.math.BigDecimal
|
||||
import java.util.Locale
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@Composable
|
||||
internal fun ConverterRoute(
|
||||
@ -203,10 +214,30 @@ private fun NumberBase(
|
||||
convert()
|
||||
}
|
||||
|
||||
val density = LocalDensity.current
|
||||
val dragState = remember {
|
||||
AnchoredDraggableState(
|
||||
initialValue = DragState.CLOSED,
|
||||
anchors = DraggableAnchors {
|
||||
DragState.CLOSED at 0f
|
||||
DragState.OPEN at with(density) { -60.dp.toPx() }
|
||||
},
|
||||
positionalThreshold = { 0f },
|
||||
velocityThreshold = { 0f },
|
||||
animationSpec = tween(easing = LinearEasing, durationMillis = 50),
|
||||
)
|
||||
}
|
||||
|
||||
PortraitLandscape(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
content1 = { contentModifier ->
|
||||
ColumnWithConstraints(modifier = contentModifier) {
|
||||
ColumnWithConstraints(
|
||||
modifier = contentModifier
|
||||
.anchoredDraggable(
|
||||
state = dragState,
|
||||
orientation = Orientation.Vertical,
|
||||
),
|
||||
) {
|
||||
val textFieldModifier = Modifier.weight(2f)
|
||||
|
||||
NumberBaseTextField(
|
||||
@ -224,6 +255,22 @@ private fun NumberBase(
|
||||
)
|
||||
AnimatedUnitShortName(stringResource(uiState.unitTo.shortName))
|
||||
|
||||
if (uiState.result is ConverterResult.NumberBase) {
|
||||
BaseCalculationSummary(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.then(with(density) { Modifier.height(dragState.offset.absoluteValue.toDp()) }),
|
||||
basis = uiState.unitTo,
|
||||
result = uiState.result,
|
||||
onResultChange = { newValue ->
|
||||
val valueConverted = uiState.unitTo.convert(uiState.unitFrom, newValue)
|
||||
|
||||
updateInput1(TextFieldValue(valueConverted))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(it.maxHeight * 0.03f))
|
||||
|
||||
UnitSelectionButtons(
|
||||
@ -250,6 +297,7 @@ private fun NumberBase(
|
||||
addDigit = { updateInput1(uiState.input.addTokens(it)) },
|
||||
deleteDigit = { updateInput1(uiState.input.deleteTokens()) },
|
||||
clearInput = { updateInput1(TextFieldValue()) },
|
||||
basis = uiState.unitFrom,
|
||||
)
|
||||
},
|
||||
)
|
||||
@ -284,6 +332,20 @@ private fun Default(
|
||||
}
|
||||
var focusedOnInput1 by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
val density = LocalDensity.current
|
||||
val dragState = remember {
|
||||
AnchoredDraggableState(
|
||||
initialValue = DragState.CLOSED,
|
||||
anchors = DraggableAnchors {
|
||||
DragState.CLOSED at 0f
|
||||
DragState.OPEN at with(density) { -60.dp.toPx() }
|
||||
},
|
||||
positionalThreshold = { 0f },
|
||||
velocityThreshold = { 0f },
|
||||
animationSpec = tween(easing = LinearEasing, durationMillis = 50),
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(connection) {
|
||||
if ((connection == ConnectionState.Available) and (uiState.result is ConverterResult.Error)) {
|
||||
val unitFrom = uiState.unitFrom
|
||||
@ -304,7 +366,13 @@ private fun Default(
|
||||
PortraitLandscape(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
content1 = { contentModifier ->
|
||||
ColumnWithConstraints(modifier = contentModifier) { boxWithConstraintsScope ->
|
||||
ColumnWithConstraints(
|
||||
modifier = contentModifier
|
||||
.anchoredDraggable(
|
||||
state = dragState,
|
||||
orientation = Orientation.Vertical,
|
||||
),
|
||||
) { boxWithConstraintsScope ->
|
||||
val textFieldModifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(2f)
|
||||
@ -425,6 +493,18 @@ private fun Default(
|
||||
),
|
||||
)
|
||||
|
||||
if (uiState.result is ConverterResult.Default && uiState.unitTo.factor >= BigDecimal.ZERO) {
|
||||
ValueOneSummary(
|
||||
modifier = with(density) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(dragState.offset.absoluteValue.toDp())
|
||||
.horizontalScroll(rememberScrollState())
|
||||
},
|
||||
uiState = uiState,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(boxWithConstraintsScope.maxHeight * 0.03f))
|
||||
|
||||
UnitSelectionButtons(
|
||||
|
@ -0,0 +1,3 @@
|
||||
package app.myzel394.numberhub.feature.converter
|
||||
|
||||
internal enum class DragState { CLOSED, OPEN }
|
@ -156,7 +156,7 @@ private fun UnitFromSelectorScreenPreview() {
|
||||
selectedUnitGroup = UnitGroup.SPEED,
|
||||
shownUnitGroups = UnitGroup.entries,
|
||||
showFavoritesOnly = false,
|
||||
sorting = UnitsListSorting.USAGE,
|
||||
sorting = UnitsListSorting.SCALE_ASC,
|
||||
),
|
||||
onQueryChange = {},
|
||||
toggleFavoritesOnly = {},
|
||||
|
@ -155,7 +155,7 @@ private fun UnitToSelectorPreview() {
|
||||
query = TextFieldValue("test"),
|
||||
units = units,
|
||||
showFavoritesOnly = false,
|
||||
sorting = UnitsListSorting.USAGE,
|
||||
sorting = UnitsListSorting.SCALE_ASC,
|
||||
input = "100",
|
||||
scale = 3,
|
||||
outputFormat = OutputFormat.PLAIN,
|
||||
|
@ -0,0 +1,86 @@
|
||||
package app.myzel394.numberhub.feature.converter.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.myzel394.numberhub.data.converter.ConverterResult
|
||||
import app.myzel394.numberhub.data.model.converter.unit.BasicUnit
|
||||
|
||||
@Composable
|
||||
internal fun BaseCalculationSummary(
|
||||
modifier: Modifier = Modifier,
|
||||
basis: BasicUnit.NumberBase,
|
||||
result: ConverterResult.NumberBase,
|
||||
onResultChange: (String) -> Unit,
|
||||
) {
|
||||
val fontStyle = MaterialTheme.typography.headlineSmall
|
||||
|
||||
Row(
|
||||
modifier,
|
||||
horizontalArrangement = Arrangement.End,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
for (index in 0..<result.value.length) {
|
||||
val character = result.value[index]
|
||||
val digit = character.digitToInt(16);
|
||||
val base = basis.factor.toInt()
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.small)
|
||||
.clickable {
|
||||
val newCurrentValue = (digit + 1) % base
|
||||
val newResultText = result.value.substring(
|
||||
0,
|
||||
index,
|
||||
) + newCurrentValue.toString(16) + result.value.substring(index + 1)
|
||||
|
||||
onResultChange(newResultText)
|
||||
}
|
||||
.padding(vertical = 2.dp, horizontal = 6.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "$digit",
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Text(
|
||||
text = " · $base",
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Text(
|
||||
text = "${result.value.length - index - 1}",
|
||||
modifier = Modifier.offset(
|
||||
y = -(MaterialTheme.typography.bodySmall.fontSize.div(
|
||||
2,
|
||||
)).value.dp,
|
||||
),
|
||||
style = fontStyle.copy(
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
),
|
||||
color = MaterialTheme.colorScheme.tertiary,
|
||||
)
|
||||
}
|
||||
|
||||
if (index < result.value.length - 1) {
|
||||
Text(
|
||||
text = " + ",
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,18 +18,33 @@
|
||||
|
||||
package app.myzel394.numberhub.feature.converter.components
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
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.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.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.myzel394.numberhub.core.base.R
|
||||
import app.myzel394.numberhub.core.base.Token
|
||||
import app.myzel394.numberhub.core.ui.LocalWindowSize
|
||||
import app.myzel394.numberhub.core.ui.WindowHeightSizeClass
|
||||
import app.myzel394.numberhub.core.ui.WindowWidthSizeClass
|
||||
import app.myzel394.numberhub.core.ui.common.ColumnWithConstraints
|
||||
import app.myzel394.numberhub.core.ui.common.KeyboardButtonFilled
|
||||
import app.myzel394.numberhub.core.ui.common.KeyboardButtonLight
|
||||
import app.myzel394.numberhub.core.ui.common.KeyboardButtonTertiary
|
||||
@ -65,6 +80,9 @@ import app.myzel394.numberhub.core.ui.common.icons.iconpack.Plus
|
||||
import app.myzel394.numberhub.core.ui.common.icons.iconpack.Power
|
||||
import app.myzel394.numberhub.core.ui.common.icons.iconpack.RightBracket
|
||||
import app.myzel394.numberhub.core.ui.common.icons.iconpack.Root
|
||||
import app.myzel394.numberhub.data.model.converter.unit.BasicUnit
|
||||
import app.myzel394.numberhub.feature.converter.createSortedArray
|
||||
import kotlin.math.ceil
|
||||
|
||||
@Composable
|
||||
internal fun DefaultKeyboard(
|
||||
@ -79,92 +97,401 @@ internal fun DefaultKeyboard(
|
||||
) {
|
||||
val fractionalIcon = remember(fractional) { if (fractional == Token.PERIOD) IconPack.Dot else IconPack.Comma }
|
||||
val fractionalIconDescription = remember(fractional) { if (fractional == Token.PERIOD) R.string.keyboard_dot else R.string.comma }
|
||||
val contentHeight: Float = if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) KeyboardButtonToken.CONTENT_HEIGHT_SHORT else KeyboardButtonToken.CONTENT_HEIGHT_TALL
|
||||
val contentHeight: Float =
|
||||
if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) KeyboardButtonToken.CONTENT_HEIGHT_SHORT else KeyboardButtonToken.CONTENT_HEIGHT_TALL
|
||||
|
||||
KeypadFlow(
|
||||
modifier = modifier,
|
||||
rows = 5,
|
||||
columns = 4,
|
||||
) { width, height ->
|
||||
val bModifier = Modifier.fillMaxWidth(width).fillMaxHeight(height)
|
||||
val bModifier = Modifier
|
||||
.fillMaxWidth(width)
|
||||
.fillMaxHeight(height)
|
||||
|
||||
if (acButton) {
|
||||
KeyboardButtonTertiary(bModifier, IconPack.Clear, stringResource(R.string.delete_label), contentHeight) { clearInput() }
|
||||
KeyboardButtonFilled(bModifier, IconPack.Brackets, stringResource(R.string.keyboard_brackets), contentHeight) { addBracket() }
|
||||
KeyboardButtonTertiary(
|
||||
bModifier,
|
||||
IconPack.Clear,
|
||||
stringResource(R.string.delete_label),
|
||||
contentHeight,
|
||||
) { clearInput() }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.Brackets,
|
||||
stringResource(R.string.keyboard_brackets),
|
||||
contentHeight,
|
||||
) { addBracket() }
|
||||
} else {
|
||||
KeyboardButtonFilled(bModifier, IconPack.LeftBracket, stringResource(R.string.keyboard_left_bracket), contentHeight) { addDigit(Token.Operator.leftBracket) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.RightBracket, stringResource(R.string.keyboard_right_bracket), contentHeight) { addDigit(Token.Operator.rightBracket) }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.LeftBracket,
|
||||
stringResource(R.string.keyboard_left_bracket),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Operator.leftBracket) }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.RightBracket,
|
||||
stringResource(R.string.keyboard_right_bracket),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Operator.rightBracket) }
|
||||
}
|
||||
KeyboardButtonFilled(bModifier, IconPack.Power, stringResource(R.string.keyboard_power), contentHeight) { addDigit(Token.Operator.power) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.Root, stringResource(R.string.keyboard_root), contentHeight) { addDigit(Token.Operator.sqrt) }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.Power,
|
||||
stringResource(R.string.keyboard_power),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Operator.power) }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.Root,
|
||||
stringResource(R.string.keyboard_root),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Operator.sqrt) }
|
||||
|
||||
KeyboardButtonLight(bModifier, IconPack.Key7, Token.Digit._7, contentHeight) { addDigit(Token.Digit._7) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key8, Token.Digit._8, contentHeight) { addDigit(Token.Digit._8) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key9, Token.Digit._9, contentHeight) { addDigit(Token.Digit._9) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.Divide, stringResource(R.string.keyboard_divide), contentHeight) { addDigit(Token.Operator.divide) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key7,
|
||||
Token.Digit._7,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._7) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key8,
|
||||
Token.Digit._8,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._8) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key9,
|
||||
Token.Digit._9,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._9) }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.Divide,
|
||||
stringResource(R.string.keyboard_divide),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Operator.divide) }
|
||||
|
||||
KeyboardButtonLight(bModifier, IconPack.Key4, Token.Digit._4, contentHeight) { addDigit(Token.Digit._4) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key5, Token.Digit._5, contentHeight) { addDigit(Token.Digit._5) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key6, Token.Digit._6, contentHeight) { addDigit(Token.Digit._6) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.Multiply, stringResource(R.string.keyboard_multiply), contentHeight) { addDigit(Token.Operator.multiply) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key4,
|
||||
Token.Digit._4,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._4) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key5,
|
||||
Token.Digit._5,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._5) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key6,
|
||||
Token.Digit._6,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._6) }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.Multiply,
|
||||
stringResource(R.string.keyboard_multiply),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Operator.multiply) }
|
||||
|
||||
KeyboardButtonLight(bModifier, IconPack.Key1, Token.Digit._1, contentHeight) { addDigit(Token.Digit._1) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key2, Token.Digit._2, contentHeight) { addDigit(Token.Digit._2) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key3, Token.Digit._3, contentHeight) { addDigit(Token.Digit._3) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.Minus, stringResource(R.string.keyboard_minus), contentHeight) { addDigit(Token.Operator.minus) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key1,
|
||||
Token.Digit._1,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._1) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key2,
|
||||
Token.Digit._2,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._2) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key3,
|
||||
Token.Digit._3,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._3) }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.Minus,
|
||||
stringResource(R.string.keyboard_minus),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Operator.minus) }
|
||||
|
||||
if (middleZero) {
|
||||
KeyboardButtonLight(bModifier, fractionalIcon, stringResource(fractionalIconDescription), contentHeight) { addDigit(Token.Digit.dot) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key0, Token.Digit._0, contentHeight) { addDigit(Token.Digit._0) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
fractionalIcon,
|
||||
stringResource(fractionalIconDescription),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit.dot) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key0,
|
||||
Token.Digit._0,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._0) }
|
||||
} else {
|
||||
KeyboardButtonLight(bModifier, IconPack.Key0, Token.Digit._0, contentHeight) { addDigit(Token.Digit._0) }
|
||||
KeyboardButtonLight(bModifier, fractionalIcon, stringResource(fractionalIconDescription), contentHeight) { addDigit(Token.Digit.dot) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Key0,
|
||||
Token.Digit._0,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._0) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
fractionalIcon,
|
||||
stringResource(fractionalIconDescription),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit.dot) }
|
||||
}
|
||||
KeyboardButtonLight(bModifier, IconPack.Backspace, stringResource(R.string.delete_label), contentHeight, onLongClick = clearInput) { deleteDigit() }
|
||||
KeyboardButtonFilled(bModifier, IconPack.Plus, stringResource(R.string.keyboard_plus), contentHeight) { addDigit(Token.Operator.plus) }
|
||||
KeyboardButtonLight(
|
||||
bModifier,
|
||||
IconPack.Backspace,
|
||||
stringResource(R.string.delete_label),
|
||||
contentHeight,
|
||||
onLongClick = clearInput,
|
||||
) { deleteDigit() }
|
||||
KeyboardButtonFilled(
|
||||
bModifier,
|
||||
IconPack.Plus,
|
||||
stringResource(R.string.keyboard_plus),
|
||||
contentHeight,
|
||||
) { addDigit(Token.Operator.plus) }
|
||||
}
|
||||
}
|
||||
|
||||
val AVAILABLE_NUMBERS = mapOf<String, ImageVector>(
|
||||
Token.Digit._0 to IconPack.Key0,
|
||||
Token.Digit._1 to IconPack.Key1,
|
||||
Token.Digit._2 to IconPack.Key2,
|
||||
Token.Digit._3 to IconPack.Key3,
|
||||
Token.Digit._4 to IconPack.Key4,
|
||||
Token.Digit._5 to IconPack.Key5,
|
||||
Token.Digit._6 to IconPack.Key6,
|
||||
Token.Digit._7 to IconPack.Key7,
|
||||
Token.Digit._8 to IconPack.Key8,
|
||||
Token.Digit._9 to IconPack.Key9,
|
||||
Token.Letter._A to IconPack.KeyA,
|
||||
Token.Letter._B to IconPack.KeyB,
|
||||
Token.Letter._C to IconPack.KeyC,
|
||||
Token.Letter._D to IconPack.KeyD,
|
||||
Token.Letter._E to IconPack.KeyE,
|
||||
Token.Letter._F to IconPack.KeyF,
|
||||
)
|
||||
|
||||
@Composable
|
||||
internal fun NumberBaseKeyboard(
|
||||
modifier: Modifier,
|
||||
addDigit: (String) -> Unit,
|
||||
clearInput: () -> Unit,
|
||||
deleteDigit: () -> Unit,
|
||||
basis: BasicUnit.NumberBase,
|
||||
) {
|
||||
val contentHeight: Float = if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) KeyboardButtonToken.CONTENT_HEIGHT_SHORT else KeyboardButtonToken.CONTENT_HEIGHT_TALL
|
||||
val isUsingColumn =
|
||||
(LocalWindowSize.current.widthSizeClass > WindowWidthSizeClass.Expanded) or (LocalWindowSize.current.heightSizeClass > WindowHeightSizeClass.Compact)
|
||||
|
||||
KeypadFlow(
|
||||
modifier = modifier,
|
||||
rows = 6,
|
||||
columns = 3,
|
||||
) { width, height ->
|
||||
val bModifier = Modifier.fillMaxWidth(width).fillMaxHeight(height)
|
||||
val wideButtonModifier = Modifier.fillMaxHeight(height).fillMaxWidth(width * 2)
|
||||
var direction by remember {
|
||||
mutableStateOf(AnimatedContentTransitionScope.SlideDirection.Right)
|
||||
}
|
||||
|
||||
KeyboardButtonFilled(bModifier, IconPack.KeyA, Token.Letter._A, contentHeight) { addDigit(Token.Letter._A) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.KeyB, Token.Letter._B, contentHeight) { addDigit(Token.Letter._B) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.KeyC, Token.Letter._C, contentHeight) { addDigit(Token.Letter._C) }
|
||||
LaunchedEffect(isUsingColumn) {
|
||||
direction = if (isUsingColumn) {
|
||||
AnimatedContentTransitionScope.SlideDirection.Right
|
||||
} else {
|
||||
AnimatedContentTransitionScope.SlideDirection.Up
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardButtonFilled(bModifier, IconPack.KeyD, Token.Letter._D, contentHeight) { addDigit(Token.Letter._D) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.KeyE, Token.Letter._E, contentHeight) { addDigit(Token.Letter._E) }
|
||||
KeyboardButtonFilled(bModifier, IconPack.KeyF, Token.Letter._F, contentHeight) { addDigit(Token.Letter._F) }
|
||||
LaunchedEffect(basis) {
|
||||
direction = when (direction) {
|
||||
AnimatedContentTransitionScope.SlideDirection.Up -> AnimatedContentTransitionScope.SlideDirection.Down
|
||||
AnimatedContentTransitionScope.SlideDirection.Down -> AnimatedContentTransitionScope.SlideDirection.Up
|
||||
AnimatedContentTransitionScope.SlideDirection.Left -> AnimatedContentTransitionScope.SlideDirection.Right
|
||||
AnimatedContentTransitionScope.SlideDirection.Right -> AnimatedContentTransitionScope.SlideDirection.Left
|
||||
else -> direction
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardButtonLight(bModifier, IconPack.Key7, Token.Digit._7, contentHeight) { addDigit(Token.Digit._7) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key8, Token.Digit._8, contentHeight) { addDigit(Token.Digit._8) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key9, Token.Digit._9, contentHeight) { addDigit(Token.Digit._9) }
|
||||
ColumnWithConstraints(modifier) { constraints ->
|
||||
AnimatedContent(
|
||||
transitionSpec = {
|
||||
slideIntoContainer(animationSpec = tween(600), towards = direction).togetherWith(
|
||||
slideOutOfContainer(animationSpec = tween(600), towards = direction),
|
||||
)
|
||||
},
|
||||
targetState = basis.factor.toInt(),
|
||||
label = "ConverterKeyboard-FlowRow",
|
||||
) { amount ->
|
||||
val columns: Int = when {
|
||||
amount == 5 -> 2
|
||||
amount == 3 -> 2
|
||||
amount == 7 -> 2
|
||||
amount == 10 -> 3
|
||||
amount < 10 -> if (amount.and(1) == 0) 2 else if (amount % 3 == 0) 3 else amount % 3
|
||||
else -> 3
|
||||
}
|
||||
val rows = when {
|
||||
amount == 5 -> 3
|
||||
amount == 3 -> 2
|
||||
amount == 7 -> 4
|
||||
amount == 10 -> 4
|
||||
amount < 10 -> ceil(amount.toDouble() / columns.toDouble()).toInt() + 1
|
||||
amount == 11 -> 5
|
||||
amount == 12 -> 5
|
||||
amount == 13 -> 5
|
||||
else -> 6
|
||||
}
|
||||
val horizontalSpacing = 8.dp
|
||||
val verticalSpacing = 12.dp
|
||||
val height: Float = (1f - (verticalSpacing * (rows - 1) / constraints.maxHeight)) / rows
|
||||
|
||||
KeyboardButtonLight(bModifier, IconPack.Key4, Token.Digit._4, contentHeight) { addDigit(Token.Digit._4) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key5, Token.Digit._5, contentHeight) { addDigit(Token.Digit._5) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key6, Token.Digit._6, contentHeight) { addDigit(Token.Digit._6) }
|
||||
FlowRow(
|
||||
modifier = modifier,
|
||||
maxItemsInEachRow = columns,
|
||||
horizontalArrangement = Arrangement.spacedBy(horizontalSpacing),
|
||||
verticalArrangement = Arrangement.spacedBy(verticalSpacing),
|
||||
) {
|
||||
val bModifier = Modifier
|
||||
.fillMaxHeight(height)
|
||||
.fillMaxWidth()
|
||||
|
||||
KeyboardButtonLight(bModifier, IconPack.Key1, Token.Digit._1, contentHeight) { addDigit(Token.Digit._1) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key2, Token.Digit._2, contentHeight) { addDigit(Token.Digit._2) }
|
||||
KeyboardButtonLight(bModifier, IconPack.Key3, Token.Digit._3, contentHeight) { addDigit(Token.Digit._3) }
|
||||
when {
|
||||
amount in arrayOf(3, 5, 7) -> {
|
||||
for (int in createSortedArray(1..<amount, columns)) {
|
||||
val key = AVAILABLE_NUMBERS.keys.elementAt(int)
|
||||
val icon = AVAILABLE_NUMBERS[key]!!
|
||||
KeyboardButtonLight(
|
||||
bModifier.weight(1f),
|
||||
icon,
|
||||
key,
|
||||
contentHeight,
|
||||
) { addDigit(key) }
|
||||
}
|
||||
|
||||
// TODO Should be a separate o use custom widthFillFactors and heightFillFactors
|
||||
KeyboardButtonLight(bModifier, IconPack.Key0, Token.Digit._0, contentHeight) { addDigit(Token.Digit._0) }
|
||||
KeyboardButtonLight(wideButtonModifier, IconPack.Backspace, stringResource(R.string.delete_label), contentHeight, clearInput) { deleteDigit() }
|
||||
KeyboardButtonLight(
|
||||
bModifier.weight(1f),
|
||||
IconPack.Key0,
|
||||
Token.Digit._0,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._0) }
|
||||
KeyboardButtonTertiary(
|
||||
bModifier.weight(1f),
|
||||
IconPack.Backspace,
|
||||
stringResource(R.string.delete_label),
|
||||
contentHeight,
|
||||
clearInput,
|
||||
) { deleteDigit() }
|
||||
}
|
||||
|
||||
amount < 10 -> {
|
||||
for (int in createSortedArray(0..<amount.coerceAtMost(10), columns)) {
|
||||
val key = AVAILABLE_NUMBERS.keys.elementAt(int)
|
||||
val icon = AVAILABLE_NUMBERS[key]!!
|
||||
KeyboardButtonLight(
|
||||
bModifier.weight(1f),
|
||||
icon,
|
||||
key,
|
||||
contentHeight,
|
||||
) { addDigit(key) }
|
||||
}
|
||||
|
||||
KeyboardButtonTertiary(
|
||||
bModifier,
|
||||
IconPack.Backspace,
|
||||
stringResource(R.string.delete_label),
|
||||
contentHeight,
|
||||
clearInput,
|
||||
) { deleteDigit() }
|
||||
}
|
||||
|
||||
amount == 10 -> {
|
||||
for (int in createSortedArray(1..9, columns)) {
|
||||
val key = AVAILABLE_NUMBERS.keys.elementAt(int)
|
||||
val icon = AVAILABLE_NUMBERS[key]!!
|
||||
KeyboardButtonLight(
|
||||
bModifier.weight(1f),
|
||||
icon,
|
||||
key,
|
||||
contentHeight,
|
||||
) { addDigit(key) }
|
||||
}
|
||||
|
||||
KeyboardButtonLight(
|
||||
bModifier.weight(1f),
|
||||
IconPack.Key0,
|
||||
Token.Digit._0,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._0) }
|
||||
KeyboardButtonTertiary(
|
||||
bModifier.weight(2f),
|
||||
IconPack.Backspace,
|
||||
stringResource(R.string.delete_label),
|
||||
contentHeight,
|
||||
clearInput,
|
||||
) { deleteDigit() }
|
||||
}
|
||||
|
||||
else -> {
|
||||
val rowsAmount = ceil((amount - 10) / 3f).toInt()
|
||||
|
||||
for (row in (rowsAmount - 1) downTo 0) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(horizontalSpacing),
|
||||
) {
|
||||
val rowStart = 10 + (row) * columns
|
||||
val rowEnd = (rowStart + 3).coerceAtMost(amount.coerceAtMost(16))
|
||||
|
||||
for (index in createSortedArray(rowStart..<rowEnd, columns)) {
|
||||
val key = AVAILABLE_NUMBERS.keys.elementAt(index)
|
||||
val icon = AVAILABLE_NUMBERS[key]!!
|
||||
KeyboardButtonFilled(
|
||||
bModifier.weight(1f),
|
||||
icon,
|
||||
key,
|
||||
contentHeight,
|
||||
) { addDigit(key) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int in createSortedArray(1..9, columns)) {
|
||||
val key = AVAILABLE_NUMBERS.keys.elementAt(int)
|
||||
val icon = AVAILABLE_NUMBERS[key]!!
|
||||
KeyboardButtonLight(
|
||||
bModifier.weight(1f),
|
||||
icon,
|
||||
key,
|
||||
contentHeight,
|
||||
) { addDigit(key) }
|
||||
}
|
||||
|
||||
KeyboardButtonLight(
|
||||
bModifier.weight(1f),
|
||||
IconPack.Key0,
|
||||
Token.Digit._0,
|
||||
contentHeight,
|
||||
) { addDigit(Token.Digit._0) }
|
||||
|
||||
KeyboardButtonTertiary(
|
||||
bModifier.weight(2f),
|
||||
IconPack.Backspace,
|
||||
stringResource(R.string.delete_label),
|
||||
contentHeight,
|
||||
clearInput,
|
||||
) { deleteDigit() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,5 +518,6 @@ private fun PreviewConverterKeyboardNumberBase() {
|
||||
addDigit = {},
|
||||
clearInput = {},
|
||||
deleteDigit = {},
|
||||
basis = BasicUnit.NumberBase.Hexadecimal,
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
package app.myzel394.numberhub.feature.converter.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import app.myzel394.numberhub.data.common.format
|
||||
import app.myzel394.numberhub.feature.converter.UnitConverterUIState
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Composable
|
||||
internal fun ValueOneSummary(
|
||||
modifier: Modifier = Modifier,
|
||||
uiState: UnitConverterUIState.Default,
|
||||
) {
|
||||
val unitFromLabel = LocalContext.current.getString(uiState.unitFrom.displayName)
|
||||
val unitToLabel = LocalContext.current.getString(uiState.unitTo.displayName)
|
||||
val value = uiState.unitFrom.convert(uiState.unitTo, BigDecimal(1))
|
||||
.format(uiState.scale, uiState.outputFormat)
|
||||
|
||||
val fontStyle = MaterialTheme.typography.headlineSmall
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
Text(
|
||||
1.toString(),
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
|
||||
Text(
|
||||
" ",
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
|
||||
Text(
|
||||
unitFromLabel,
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.tertiary,
|
||||
)
|
||||
|
||||
Text(
|
||||
" = ",
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
|
||||
Text(
|
||||
value,
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
|
||||
Text(
|
||||
" ",
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
|
||||
Text(
|
||||
unitToLabel,
|
||||
style = fontStyle,
|
||||
color = MaterialTheme.colorScheme.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package app.myzel394.numberhub.feature.converter
|
||||
|
||||
import kotlin.math.ceil
|
||||
|
||||
// So here's the problem. If we just go down from 3 downto 0 with columns 2, this results in:
|
||||
// [3, 2]
|
||||
// [1, 0]
|
||||
// While this is correct, the ordering is incorrect. It should be
|
||||
// [2, 3]
|
||||
// [0, 1]
|
||||
// TODO: Add support for rtl languages
|
||||
internal fun createSortedArray(range: IntRange, columns: Int): List<Int> {
|
||||
val result = mutableListOf<Int>()
|
||||
val rows = ceil(range.count().toDouble() / columns.toDouble()).toInt()
|
||||
for (row in rows downTo 0) {
|
||||
for (column in 0 until columns) {
|
||||
val index = row * columns + column + range.first
|
||||
if (index <= range.last) {
|
||||
result.add(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
@ -139,7 +139,7 @@ private fun PreviewConverterSettingsScreen() {
|
||||
precision = 3,
|
||||
outputFormat = OutputFormat.PLAIN,
|
||||
unitConverterFormatTime = false,
|
||||
unitConverterSorting = UnitsListSorting.USAGE,
|
||||
unitConverterSorting = UnitsListSorting.SCALE_ASC,
|
||||
shownUnitGroups = UnitGroup.entries,
|
||||
unitConverterFavoritesOnly = false,
|
||||
enableToolsExperiment = false,
|
||||
|
@ -5,25 +5,25 @@ versionName = "NumberHub"
|
||||
androidxBrowserBrowser = "1.8.0"
|
||||
androidGradlePlugin = "8.3.2"
|
||||
androidxActivityActivityCompose = "1.9.0"
|
||||
androidxAppCompatAppCompat = "1.6.1"
|
||||
androidxCompose = "1.6.7"
|
||||
androidxAppCompatAppCompat = "1.7.0"
|
||||
androidxCompose = "1.6.8"
|
||||
androidxComposeCompiler = "1.5.9"
|
||||
androidxComposeMaterial3 = "1.2.1"
|
||||
androidxCoreCoreKts = "1.13.1"
|
||||
androidxGlanceGlance = "1.0.0"
|
||||
androidxGlanceGlance = "1.1.0"
|
||||
androidxDatastoreDatastorePreferences = "1.1.1"
|
||||
androidxEspresso = "3.5.1"
|
||||
androidxEspresso = "3.6.1"
|
||||
androidxHiltHiltNavigationCompose = "1.2.0"
|
||||
androidxMacroBenchmark = "1.2.4"
|
||||
androidxLifecycleLifecycleRuntimeCompose = "2.7.0"
|
||||
androidxLifecycleLifecycleRuntimeCompose = "2.8.3"
|
||||
androidxNavigationNavigationCompose = "2.7.7"
|
||||
androidxProfileinstallerProfileinstaller = "1.3.1"
|
||||
androidxRoom = "2.6.1"
|
||||
androidxTest = "1.5.0"
|
||||
androidxTestExtJunitKtx = "1.1.5"
|
||||
androidxTestRunner = "1.5.2"
|
||||
androidxTest = "1.6.1"
|
||||
androidxTestExtJunitKtx = "1.2.1"
|
||||
androidxTestRunner = "1.6.1"
|
||||
androidxUiAutomator = "2.3.0"
|
||||
androidxWindowWindow = "1.2.0"
|
||||
androidxWindowWindow = "1.3.0"
|
||||
androidxWorkWorkRuntimeKtx = "2.9.0"
|
||||
comAndroidToolsDesugarJdkLibs = "2.0.4"
|
||||
comGithubSadellieThemmo = "1.3.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user