mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
Remove RPN experiment
This commit is contained in:
parent
4a499b8fd3
commit
b2030ad9dc
@ -112,7 +112,6 @@ internal fun ComponentActivity.App(prefs: AppPreferences?) {
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
themmoController = it,
|
themmoController = it,
|
||||||
startDestination = prefs.startingScreen,
|
startDestination = prefs.startingScreen,
|
||||||
rpnMode = prefs.rpnMode,
|
|
||||||
openDrawer = { drawerScope.launch { drawerState.open() } }
|
openDrawer = { drawerScope.launch { drawerState.open() } }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,6 @@ internal fun UnittoNavigation(
|
|||||||
themmoController: ThemmoController,
|
themmoController: ThemmoController,
|
||||||
startDestination: String,
|
startDestination: String,
|
||||||
openDrawer: () -> Unit,
|
openDrawer: () -> Unit,
|
||||||
rpnMode: Boolean,
|
|
||||||
) {
|
) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
@ -65,7 +64,6 @@ internal fun UnittoNavigation(
|
|||||||
|
|
||||||
calculatorGraph(
|
calculatorGraph(
|
||||||
openDrawer = openDrawer,
|
openDrawer = openDrawer,
|
||||||
rpnMode = rpnMode,
|
|
||||||
navigateToSettings = navController::navigateToSettings
|
navigateToSettings = navController::navigateToSettings
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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 io.github.sadellie.evaluatto
|
|
||||||
|
|
||||||
sealed class RPNCalculation {
|
|
||||||
data object Negate : RPNCalculation()
|
|
||||||
|
|
||||||
data object Clear : RPNCalculation()
|
|
||||||
data object Enter : RPNCalculation()
|
|
||||||
data object RotateUp : RPNCalculation()
|
|
||||||
data object RotateDown : RPNCalculation()
|
|
||||||
data object Swap : RPNCalculation()
|
|
||||||
data object Pop : RPNCalculation()
|
|
||||||
|
|
||||||
data object Plus : RPNCalculation()
|
|
||||||
data object Minus : RPNCalculation()
|
|
||||||
data object Multiply : RPNCalculation()
|
|
||||||
data object Divide : RPNCalculation()
|
|
||||||
data object Percent : RPNCalculation()
|
|
||||||
data object Power : RPNCalculation() // unused
|
|
||||||
}
|
|
@ -1,191 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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 io.github.sadellie.evaluatto
|
|
||||||
|
|
||||||
import com.sadellie.unitto.core.base.MAX_PRECISION
|
|
||||||
import java.math.BigDecimal
|
|
||||||
import java.math.RoundingMode
|
|
||||||
|
|
||||||
sealed class RPNResult {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Both input and stack were changed.
|
|
||||||
*
|
|
||||||
* @property input New input. Empty when `null`.
|
|
||||||
* @property stack New stack.
|
|
||||||
*/
|
|
||||||
data class Result(
|
|
||||||
val input: BigDecimal?,
|
|
||||||
val stack: List<BigDecimal>,
|
|
||||||
) : RPNResult()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only input has been changed.
|
|
||||||
*
|
|
||||||
* @property input New input.
|
|
||||||
*/
|
|
||||||
data class NewInput(
|
|
||||||
val input: BigDecimal,
|
|
||||||
) : RPNResult()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only stack has been changed.
|
|
||||||
*
|
|
||||||
* @property stack New stack.
|
|
||||||
*/
|
|
||||||
data class NewStack(
|
|
||||||
val stack: List<BigDecimal>,
|
|
||||||
) : RPNResult()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Something is wrong. Input/stack is empty or there ane not enough stack objects.
|
|
||||||
*/
|
|
||||||
data object BadInput : RPNResult()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dividing by zero, duh
|
|
||||||
*/
|
|
||||||
data object DivideByZero : RPNResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
// vroom vroom mfs
|
|
||||||
// overdose on early returns
|
|
||||||
fun RPNCalculation.perform(
|
|
||||||
input: String,
|
|
||||||
stack: List<BigDecimal>,
|
|
||||||
): RPNResult {
|
|
||||||
when (this) {
|
|
||||||
RPNCalculation.Clear -> {
|
|
||||||
return RPNResult.Result(null, emptyList())
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Enter -> {
|
|
||||||
val inputBD = input.toBigDecimalOrNull() ?: return RPNResult.BadInput
|
|
||||||
return RPNResult.Result(null, stack + inputBD)
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Negate -> {
|
|
||||||
val inputBD = input.toBigDecimalOrNull() ?: return RPNResult.BadInput
|
|
||||||
val result = inputBD.negate()
|
|
||||||
return RPNResult.NewInput(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.RotateUp -> {
|
|
||||||
if (stack.size < 2) return RPNResult.BadInput
|
|
||||||
return RPNResult.NewStack(stack.rotateUp())
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.RotateDown -> {
|
|
||||||
if (stack.size < 2) return RPNResult.BadInput
|
|
||||||
return RPNResult.NewStack(stack.rotateDown())
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Swap -> {
|
|
||||||
if (stack.isEmpty()) return RPNResult.BadInput
|
|
||||||
if (input.isEmpty()) {
|
|
||||||
// Swap last 2 in stack
|
|
||||||
if (stack.size < 2) return RPNResult.BadInput
|
|
||||||
return RPNResult.NewStack(stack.swapLastTwo())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap last and input
|
|
||||||
val (lastFromStack, inputBD) = operands(stack, input) ?: return RPNResult.BadInput
|
|
||||||
return RPNResult.Result(lastFromStack, stack.dropLast(1) + inputBD)
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Pop -> {
|
|
||||||
val lastStacked = stack.lastOrNull() ?: return RPNResult.BadInput
|
|
||||||
return RPNResult.Result(lastStacked, stack.dropLast(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Plus -> {
|
|
||||||
val (lastFromStack, inputBD) = operands(stack, input) ?: return RPNResult.BadInput
|
|
||||||
val result = lastFromStack.plus(inputBD)
|
|
||||||
return RPNResult.Result(result, stack.dropLast(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Minus -> {
|
|
||||||
val (lastFromStack, inputBD) = operands(stack, input) ?: return RPNResult.BadInput
|
|
||||||
val result = lastFromStack.minus(inputBD)
|
|
||||||
return RPNResult.Result(result, stack.dropLast(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Multiply -> {
|
|
||||||
val (lastFromStack, inputBD) = operands(stack, input) ?: return RPNResult.BadInput
|
|
||||||
val result = lastFromStack.multiply(inputBD)
|
|
||||||
return RPNResult.Result(result, stack.dropLast(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Divide -> {
|
|
||||||
val (lastFromStack, inputBD) = operands(stack, input) ?: return RPNResult.BadInput
|
|
||||||
if (inputBD.compareTo(BigDecimal.ZERO) == 0) return RPNResult.DivideByZero
|
|
||||||
|
|
||||||
val result = lastFromStack.divide(inputBD, MAX_PRECISION, RoundingMode.HALF_EVEN)
|
|
||||||
return RPNResult.Result(result, stack.dropLast(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Percent -> {
|
|
||||||
val (lastFromStack, inputBD) = operands(stack, input) ?: return RPNResult.BadInput
|
|
||||||
// 100 * 24 / 100 =
|
|
||||||
val result = lastFromStack
|
|
||||||
.multiply(inputBD)
|
|
||||||
.divide(bigDecimalHundred, MAX_PRECISION, RoundingMode.HALF_EVEN)
|
|
||||||
return RPNResult.Result(result, stack.dropLast(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
RPNCalculation.Power -> {
|
|
||||||
val (lastFromStack, inputBD) = operands(stack, input) ?: return RPNResult.BadInput
|
|
||||||
val result = lastFromStack.pow(inputBD)
|
|
||||||
return RPNResult.Result(result, stack.dropLast(1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val bigDecimalHundred by lazy { BigDecimal("100") }
|
|
||||||
|
|
||||||
private fun operands(
|
|
||||||
stack: List<BigDecimal>,
|
|
||||||
input: String,
|
|
||||||
): Pair<BigDecimal, BigDecimal>? {
|
|
||||||
val first = stack.lastOrNull() ?: return null
|
|
||||||
val second = input.toBigDecimalOrNull() ?: return null
|
|
||||||
|
|
||||||
return first to second
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> List<T>.swapLastTwo(): List<T> {
|
|
||||||
if (size < 2) return this
|
|
||||||
return this
|
|
||||||
.dropLast(2)
|
|
||||||
.plus(get(lastIndex))
|
|
||||||
.plus(get(lastIndex - 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> List<T>.rotateUp(): List<T> {
|
|
||||||
if (size < 2) return this
|
|
||||||
return this
|
|
||||||
.drop(1)
|
|
||||||
.plus(first())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> List<T>.rotateDown(): List<T> {
|
|
||||||
if (size < 2) return this
|
|
||||||
return listOf(last())
|
|
||||||
.plus(this.dropLast(1))
|
|
||||||
}
|
|
@ -1,256 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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 io.github.sadellie.evaluatto
|
|
||||||
|
|
||||||
import com.sadellie.unitto.core.base.MAX_PRECISION
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
class RPNEngineKtTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testBadOperands() {
|
|
||||||
// no funny business if input and/or stack is empty
|
|
||||||
val actual = RPNCalculation.Divide.perform(
|
|
||||||
input = "",
|
|
||||||
stack = emptyList()
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(RPNResult.BadInput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDivide() {
|
|
||||||
val actual = RPNCalculation.Divide.perform(
|
|
||||||
input = "2",
|
|
||||||
stack = listOf(BigDecimal("5"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("2.5").setScale(MAX_PRECISION), actual.input)
|
|
||||||
assertEquals(emptyList<BigDecimal>(), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDivideByZero() {
|
|
||||||
val actual = RPNCalculation.Divide.perform(
|
|
||||||
input = "0",
|
|
||||||
stack = listOf(BigDecimal("5"))
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(RPNResult.DivideByZero, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMinus() {
|
|
||||||
val actual = RPNCalculation.Minus.perform(
|
|
||||||
input = "2",
|
|
||||||
stack = listOf(BigDecimal("5"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("3"), actual.input)
|
|
||||||
assertEquals(emptyList<BigDecimal>(), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMultiply() {
|
|
||||||
val actual = RPNCalculation.Multiply.perform(
|
|
||||||
input = "2",
|
|
||||||
stack = listOf(BigDecimal("5"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("10"), actual.input)
|
|
||||||
assertEquals(emptyList<BigDecimal>(), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testNegate() {
|
|
||||||
val actual = RPNCalculation.Negate.perform(
|
|
||||||
input = "2",
|
|
||||||
stack = listOf(BigDecimal("5"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.NewInput) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("-2"), actual.input)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testPercent() {
|
|
||||||
val actual = RPNCalculation.Percent.perform(
|
|
||||||
input = "150",
|
|
||||||
stack = listOf(BigDecimal("69"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("103.5").setScale(MAX_PRECISION), actual.input)
|
|
||||||
assertEquals(emptyList<BigDecimal>(), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testPlus() {
|
|
||||||
val actual = RPNCalculation.Plus.perform(
|
|
||||||
input = "150",
|
|
||||||
stack = listOf(BigDecimal("69"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("219"), actual.input)
|
|
||||||
assertEquals(emptyList<BigDecimal>(), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testPower() {
|
|
||||||
val actual = RPNCalculation.Power.perform(
|
|
||||||
input = "3",
|
|
||||||
stack = listOf(BigDecimal("2"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("8"), actual.input)
|
|
||||||
assertEquals(emptyList<BigDecimal>(), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRotateUp() {
|
|
||||||
val actual = RPNCalculation.RotateUp.perform(
|
|
||||||
input = "",
|
|
||||||
stack = listOf(BigDecimal("1"), BigDecimal("2"), BigDecimal("3"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.NewStack) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(listOf(BigDecimal("2"), BigDecimal("3"), BigDecimal("1")), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRotateDown() {
|
|
||||||
val actual = RPNCalculation.RotateDown.perform(
|
|
||||||
input = "",
|
|
||||||
stack = listOf(BigDecimal("1"), BigDecimal("2"), BigDecimal("3"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.NewStack) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(listOf(BigDecimal("3"), BigDecimal("1"), BigDecimal("2")), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testPop() {
|
|
||||||
val actual = RPNCalculation.Pop.perform(
|
|
||||||
input = "",
|
|
||||||
stack = listOf(BigDecimal("1"), BigDecimal("2"), BigDecimal("3"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("3"), actual.input)
|
|
||||||
assertEquals(listOf(BigDecimal("1"), BigDecimal("2")), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testClear() {
|
|
||||||
val actual = RPNCalculation.Clear.perform(
|
|
||||||
input = "3",
|
|
||||||
stack = listOf(BigDecimal("2"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(null, actual.input)
|
|
||||||
assertEquals(emptyList<BigDecimal>(), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testEnter() {
|
|
||||||
val actual = RPNCalculation.Enter.perform(
|
|
||||||
input = "3",
|
|
||||||
stack = listOf(BigDecimal("2"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(null, actual.input)
|
|
||||||
assertEquals(listOf(BigDecimal("2"), BigDecimal("3")), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSwap() {
|
|
||||||
val actual = RPNCalculation.Swap.perform(
|
|
||||||
input = "3",
|
|
||||||
stack = listOf(BigDecimal("2"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.Result) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(BigDecimal("2"), actual.input)
|
|
||||||
assertEquals(listOf(BigDecimal("3")), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSwapEmptyInput() {
|
|
||||||
val actual = RPNCalculation.Swap.perform(
|
|
||||||
input = "",
|
|
||||||
stack = listOf(BigDecimal("1"), BigDecimal("2"))
|
|
||||||
)
|
|
||||||
|
|
||||||
if (actual !is RPNResult.NewStack) throw Exception("Wrong return")
|
|
||||||
|
|
||||||
assertEquals(listOf(BigDecimal("2"), BigDecimal("1")), actual.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSwapEmptyInputNotEnoughInStack() {
|
|
||||||
val actual = RPNCalculation.Swap.perform(
|
|
||||||
input = "",
|
|
||||||
stack = listOf(BigDecimal("1"))
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(RPNResult.BadInput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSwapEmptyStack() {
|
|
||||||
val actual = RPNCalculation.Swap.perform(
|
|
||||||
input = "123",
|
|
||||||
stack = emptyList()
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(RPNResult.BadInput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSwapEmptyBoth() {
|
|
||||||
val actual = RPNCalculation.Swap.perform(
|
|
||||||
input = "",
|
|
||||||
stack = emptyList()
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(RPNResult.BadInput, actual)
|
|
||||||
}
|
|
||||||
}
|
|
@ -92,6 +92,4 @@ interface UserPreferencesRepository {
|
|||||||
suspend fun updatePartialHistoryView(enabled: Boolean)
|
suspend fun updatePartialHistoryView(enabled: Boolean)
|
||||||
|
|
||||||
suspend fun updateAcButton(enabled: Boolean)
|
suspend fun updateAcButton(enabled: Boolean)
|
||||||
|
|
||||||
suspend fun updateRpnMode(enabled: Boolean)
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,5 @@ interface AppPreferences {
|
|||||||
val startingScreen: String
|
val startingScreen: String
|
||||||
val enableToolsExperiment: Boolean
|
val enableToolsExperiment: Boolean
|
||||||
val systemFont: Boolean
|
val systemFont: Boolean
|
||||||
val rpnMode: Boolean
|
|
||||||
val enableVibrations: Boolean
|
val enableVibrations: Boolean
|
||||||
}
|
}
|
||||||
|
@ -134,10 +134,6 @@ fun Preferences.getAcButton(): Boolean {
|
|||||||
return this[PrefsKeys.AC_BUTTON] ?: true
|
return this[PrefsKeys.AC_BUTTON] ?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Preferences.getRpnMode(): Boolean {
|
|
||||||
return this[PrefsKeys.RPN_MODE] ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <T, R> T.letTryOrNull(block: (T) -> R): R? = try {
|
private inline fun <T, R> T.letTryOrNull(block: (T) -> R): R? = try {
|
||||||
this?.let(block)
|
this?.let(block)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -44,7 +44,6 @@ data class AppPreferencesImpl(
|
|||||||
override val startingScreen: String,
|
override val startingScreen: String,
|
||||||
override val enableToolsExperiment: Boolean,
|
override val enableToolsExperiment: Boolean,
|
||||||
override val systemFont: Boolean,
|
override val systemFont: Boolean,
|
||||||
override val rpnMode: Boolean,
|
|
||||||
override val enableVibrations: Boolean,
|
override val enableVibrations: Boolean,
|
||||||
) : AppPreferences
|
) : AppPreferences
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ object PrefsKeys {
|
|||||||
val ENABLE_VIBRATIONS = booleanPreferencesKey("ENABLE_VIBRATIONS_PREF_KEY")
|
val ENABLE_VIBRATIONS = booleanPreferencesKey("ENABLE_VIBRATIONS_PREF_KEY")
|
||||||
val MIDDLE_ZERO = booleanPreferencesKey("MIDDLE_ZERO_PREF_KEY")
|
val MIDDLE_ZERO = booleanPreferencesKey("MIDDLE_ZERO_PREF_KEY")
|
||||||
val AC_BUTTON = booleanPreferencesKey("AC_BUTTON_PREF_KEY")
|
val AC_BUTTON = booleanPreferencesKey("AC_BUTTON_PREF_KEY")
|
||||||
val RPN_MODE = booleanPreferencesKey("RPN_MODE_PREF_KEY")
|
// val RPN_MODE = booleanPreferencesKey("RPN_MODE_PREF_KEY")
|
||||||
|
|
||||||
// FORMATTER
|
// FORMATTER
|
||||||
val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY")
|
val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY")
|
||||||
|
@ -62,7 +62,6 @@ class UserPreferencesRepositoryImpl @Inject constructor(
|
|||||||
startingScreen = preferences.getStartingScreen(),
|
startingScreen = preferences.getStartingScreen(),
|
||||||
enableToolsExperiment = preferences.getEnableToolsExperiment(),
|
enableToolsExperiment = preferences.getEnableToolsExperiment(),
|
||||||
systemFont = preferences.getSystemFont(),
|
systemFont = preferences.getSystemFont(),
|
||||||
rpnMode = preferences.getRpnMode(),
|
|
||||||
enableVibrations = preferences.getEnableVibrations(),
|
enableVibrations = preferences.getEnableVibrations(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -291,10 +290,4 @@ class UserPreferencesRepositoryImpl @Inject constructor(
|
|||||||
preferences[PrefsKeys.AC_BUTTON] = enabled
|
preferences[PrefsKeys.AC_BUTTON] = enabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updateRpnMode(enabled: Boolean) {
|
|
||||||
dataStore.edit { preferences ->
|
|
||||||
preferences[PrefsKeys.RPN_MODE] = enabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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.calculator
|
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
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
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.text.TextRange
|
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.sadellie.unitto.core.base.OutputFormat
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.FixedInputTextField
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
|
||||||
import com.sadellie.unitto.data.common.format
|
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun InputBox(
|
|
||||||
modifier: Modifier,
|
|
||||||
input: TextFieldValue,
|
|
||||||
onCursorChange: (TextRange) -> Unit,
|
|
||||||
stack: List<BigDecimal>,
|
|
||||||
formatterSymbols: FormatterSymbols,
|
|
||||||
precision: Int,
|
|
||||||
outputFormat: Int,
|
|
||||||
) {
|
|
||||||
val listState = rememberLazyListState()
|
|
||||||
|
|
||||||
LaunchedEffect(stack) {
|
|
||||||
listState.animateScrollToItem(stack.lastIndex.coerceAtLeast(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = modifier
|
|
||||||
.clip(RoundedCornerShape(24.dp))
|
|
||||||
.fillMaxWidth()
|
|
||||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
|
||||||
.padding(start = 12.dp, end = 12.dp, bottom = 12.dp),
|
|
||||||
verticalArrangement = Arrangement.Bottom
|
|
||||||
) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
state = listState,
|
|
||||||
verticalArrangement = Arrangement.Bottom,
|
|
||||||
) {
|
|
||||||
items(stack) {
|
|
||||||
FixedInputTextField(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
value = it.format(precision, outputFormat),
|
|
||||||
formatterSymbols = formatterSymbols,
|
|
||||||
textColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
onClick = {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionTextField(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.fillMaxHeight(0.25f),
|
|
||||||
value = input,
|
|
||||||
minRatio = 0.6f,
|
|
||||||
onCursorChange = onCursorChange,
|
|
||||||
formatterSymbols = formatterSymbols,
|
|
||||||
textColor = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(device = "spec:width=1080px,height=2160px,dpi=440")
|
|
||||||
@Composable
|
|
||||||
fun PreviewInputBox() {
|
|
||||||
InputBox(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
input = TextFieldValue("123456.789"),
|
|
||||||
onCursorChange = {},
|
|
||||||
stack = listOf(
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
),
|
|
||||||
formatterSymbols = FormatterSymbols.Spaces,
|
|
||||||
precision = 3,
|
|
||||||
outputFormat = OutputFormat.PLAIN
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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.calculator
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
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.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import com.sadellie.unitto.core.base.Token
|
|
||||||
import com.sadellie.unitto.core.ui.LocalWindowSize
|
|
||||||
import com.sadellie.unitto.core.ui.WindowHeightSizeClass
|
|
||||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonAdditional
|
|
||||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonContentHeightShort
|
|
||||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonContentHeightTall
|
|
||||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonContentHeightTallAdditional
|
|
||||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonFilled
|
|
||||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonLight
|
|
||||||
import com.sadellie.unitto.core.ui.common.KeyboardButtonTertiary
|
|
||||||
import com.sadellie.unitto.core.ui.common.KeypadFlow
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.IconPack
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Backspace
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Clear
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Comma
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Divide
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Dot
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Down
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Enter
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key0
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key1
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key2
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key3
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key4
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key5
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key6
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key7
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key8
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Key9
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Minus
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Multiply
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Percent
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Plus
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Pop
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Swap
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Unary
|
|
||||||
import com.sadellie.unitto.core.ui.common.icons.iconpack.Up
|
|
||||||
import io.github.sadellie.evaluatto.RPNCalculation
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun RPNCalculatorKeyboard(
|
|
||||||
modifier: Modifier,
|
|
||||||
fractional: String,
|
|
||||||
middleZero: Boolean,
|
|
||||||
onCalculationClick: (RPNCalculation) -> Unit,
|
|
||||||
onInputEditClick: (RPNInputEdit) -> Unit,
|
|
||||||
) {
|
|
||||||
val fractionalIcon = remember(fractional) { if (fractional == Token.Digit.dot) IconPack.Dot else IconPack.Comma }
|
|
||||||
|
|
||||||
if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) {
|
|
||||||
RPNCalculatorKeyboardLandscape(
|
|
||||||
modifier = modifier,
|
|
||||||
fractionalIcon = fractionalIcon,
|
|
||||||
middleZero = middleZero,
|
|
||||||
onCalculationClick = onCalculationClick,
|
|
||||||
onInputEditClick = onInputEditClick
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
RPNCalculatorKeyboardPortrait(
|
|
||||||
modifier = modifier,
|
|
||||||
fractionalIcon = fractionalIcon,
|
|
||||||
middleZero = middleZero,
|
|
||||||
onCalculationClick = onCalculationClick,
|
|
||||||
onInputEditClick = onInputEditClick
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RPNCalculatorKeyboardPortrait(
|
|
||||||
modifier: Modifier,
|
|
||||||
fractionalIcon: ImageVector,
|
|
||||||
middleZero: Boolean,
|
|
||||||
onCalculationClick: (RPNCalculation) -> Unit,
|
|
||||||
onInputEditClick: (RPNInputEdit) -> Unit,
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = modifier
|
|
||||||
) {
|
|
||||||
KeypadFlow(
|
|
||||||
modifier = Modifier.fillMaxHeight(0.1f).fillMaxWidth(),
|
|
||||||
rows = 1,
|
|
||||||
columns = 4
|
|
||||||
) { width, height ->
|
|
||||||
val aModifier = Modifier
|
|
||||||
.fillMaxWidth(width)
|
|
||||||
.fillMaxHeight(height)
|
|
||||||
|
|
||||||
KeyboardButtonAdditional(aModifier, IconPack.Swap, null, KeyboardButtonContentHeightTallAdditional) { onCalculationClick(RPNCalculation.Swap) }
|
|
||||||
KeyboardButtonAdditional(aModifier, IconPack.Up, null, KeyboardButtonContentHeightTallAdditional) { onCalculationClick(RPNCalculation.RotateUp) }
|
|
||||||
KeyboardButtonAdditional(aModifier, IconPack.Down, null, KeyboardButtonContentHeightTallAdditional) { onCalculationClick(RPNCalculation.RotateDown) }
|
|
||||||
KeyboardButtonAdditional(aModifier, IconPack.Pop, null, KeyboardButtonContentHeightTallAdditional) { onCalculationClick(RPNCalculation.Pop) }
|
|
||||||
}
|
|
||||||
|
|
||||||
KeypadFlow(
|
|
||||||
modifier = Modifier.weight(1f).fillMaxSize(),
|
|
||||||
rows = 5,
|
|
||||||
columns = 4
|
|
||||||
) { width, height ->
|
|
||||||
val bModifier = Modifier
|
|
||||||
.fillMaxWidth(width)
|
|
||||||
.fillMaxHeight(height)
|
|
||||||
|
|
||||||
KeyboardButtonTertiary(bModifier, IconPack.Clear, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Clear) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Unary, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Negate) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Percent, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Percent) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Divide, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Divide) }
|
|
||||||
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key7, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._7)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key8, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._8)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key9, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._9)) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Multiply, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Multiply) }
|
|
||||||
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key4, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._4)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key5, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._5)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key6, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._6)) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Minus, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Minus) }
|
|
||||||
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key1, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._1)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key2, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._2)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key3, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._3)) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Plus, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Plus) }
|
|
||||||
|
|
||||||
if (middleZero) {
|
|
||||||
KeyboardButtonLight(bModifier, fractionalIcon, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Dot) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key0, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._0)) }
|
|
||||||
} else {
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key0, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._0)) }
|
|
||||||
KeyboardButtonLight(bModifier, fractionalIcon, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Dot) }
|
|
||||||
}
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Backspace, null, KeyboardButtonContentHeightTall) { onInputEditClick(RPNInputEdit.Delete) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Enter, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Enter) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RPNCalculatorKeyboardLandscape(
|
|
||||||
modifier: Modifier,
|
|
||||||
fractionalIcon: ImageVector,
|
|
||||||
middleZero: Boolean,
|
|
||||||
onCalculationClick: (RPNCalculation) -> Unit,
|
|
||||||
onInputEditClick: (RPNInputEdit) -> Unit,
|
|
||||||
) {
|
|
||||||
KeypadFlow(
|
|
||||||
modifier = modifier,
|
|
||||||
rows = 4,
|
|
||||||
columns = 6
|
|
||||||
) { width, height ->
|
|
||||||
val bModifier = Modifier
|
|
||||||
.fillMaxHeight(height)
|
|
||||||
.fillMaxWidth(width)
|
|
||||||
|
|
||||||
KeyboardButtonAdditional(bModifier, IconPack.Swap, null, KeyboardButtonContentHeightTallAdditional) { onCalculationClick(RPNCalculation.Swap) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key7, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._7)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key8, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._8)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key9, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._9)) }
|
|
||||||
KeyboardButtonTertiary(bModifier, IconPack.Clear, null, KeyboardButtonContentHeightTall) { onCalculationClick(RPNCalculation.Clear) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Unary, null, KeyboardButtonContentHeightShort) { onCalculationClick(RPNCalculation.Negate) }
|
|
||||||
|
|
||||||
KeyboardButtonAdditional(bModifier, IconPack.Up, null, KeyboardButtonContentHeightTallAdditional) { onCalculationClick(RPNCalculation.RotateUp) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key4, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._4)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key5, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._5)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key6, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._6)) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Multiply, null, KeyboardButtonContentHeightShort) { onCalculationClick(RPNCalculation.Multiply) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Divide, null, KeyboardButtonContentHeightShort) { onCalculationClick(RPNCalculation.Divide) }
|
|
||||||
|
|
||||||
KeyboardButtonAdditional(bModifier, IconPack.Down, null, KeyboardButtonContentHeightTallAdditional) { onCalculationClick(RPNCalculation.RotateDown) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key1, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._1)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key2, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._2)) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key3, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._3)) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Plus, null, KeyboardButtonContentHeightShort) { onCalculationClick(RPNCalculation.Plus) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Minus, null, KeyboardButtonContentHeightShort) { onCalculationClick(RPNCalculation.Minus) }
|
|
||||||
|
|
||||||
KeyboardButtonAdditional(bModifier, IconPack.Pop, null, KeyboardButtonContentHeightTallAdditional) { onCalculationClick(RPNCalculation.Pop) }
|
|
||||||
if (middleZero) {
|
|
||||||
KeyboardButtonLight(bModifier, fractionalIcon, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Dot) }
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key0, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._0)) }
|
|
||||||
} else {
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Key0, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Digit(Token.Digit._0)) }
|
|
||||||
KeyboardButtonLight(bModifier, fractionalIcon, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Dot) }
|
|
||||||
}
|
|
||||||
KeyboardButtonLight(bModifier, IconPack.Backspace, null, KeyboardButtonContentHeightShort) { onInputEditClick(RPNInputEdit.Delete) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Percent, null, KeyboardButtonContentHeightShort) { onCalculationClick(RPNCalculation.Percent) }
|
|
||||||
KeyboardButtonFilled(bModifier, IconPack.Enter, null, KeyboardButtonContentHeightShort) { onCalculationClick(RPNCalculation.Enter) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(device = "spec:parent=pixel_5,orientation=portrait")
|
|
||||||
@Preview(device = "spec:parent=pixel_5,orientation=landscape")
|
|
||||||
@Composable
|
|
||||||
private fun PreviewKeyboard() {
|
|
||||||
RPNCalculatorKeyboard(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
fractional = Token.Digit.dot,
|
|
||||||
middleZero = false,
|
|
||||||
onCalculationClick = {},
|
|
||||||
onInputEditClick = {}
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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.calculator
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import com.sadellie.unitto.core.ui.WindowHeightSizeClass
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.TextRange
|
|
||||||
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.OutputFormat
|
|
||||||
import com.sadellie.unitto.core.base.R
|
|
||||||
import com.sadellie.unitto.core.ui.LocalWindowSize
|
|
||||||
import com.sadellie.unitto.core.ui.common.MenuButton
|
|
||||||
import com.sadellie.unitto.core.ui.common.SettingsButton
|
|
||||||
import com.sadellie.unitto.core.ui.common.EmptyScreen
|
|
||||||
import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
|
||||||
import io.github.sadellie.evaluatto.RPNCalculation
|
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun RPNCalculatorRoute(
|
|
||||||
openDrawer: () -> Unit,
|
|
||||||
navigateToSettings: () -> Unit,
|
|
||||||
viewModel: RPNCalculatorViewModel = hiltViewModel(),
|
|
||||||
) {
|
|
||||||
when (val uiState = viewModel.uiState.collectAsStateWithLifecycle().value) {
|
|
||||||
RPNCalculatorUIState.Loading -> EmptyScreen()
|
|
||||||
is RPNCalculatorUIState.Ready -> RPNCalculatorScreen(
|
|
||||||
uiState = uiState,
|
|
||||||
openDrawer = openDrawer,
|
|
||||||
navigateToSettings = navigateToSettings,
|
|
||||||
onCursorChange = viewModel::onCursorChange,
|
|
||||||
onCalculationClick = viewModel::onCalculationClick,
|
|
||||||
onInputEditClick = viewModel::onInputEdit
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun RPNCalculatorScreen(
|
|
||||||
uiState: RPNCalculatorUIState.Ready,
|
|
||||||
openDrawer: () -> Unit,
|
|
||||||
navigateToSettings: () -> Unit,
|
|
||||||
onCursorChange: (TextRange) -> Unit,
|
|
||||||
onCalculationClick: (RPNCalculation) -> Unit,
|
|
||||||
onInputEditClick: (RPNInputEdit) -> Unit,
|
|
||||||
) {
|
|
||||||
ScaffoldWithTopBar(
|
|
||||||
title = { Text(stringResource(id = R.string.calculator_title)) },
|
|
||||||
navigationIcon = { MenuButton(openDrawer) },
|
|
||||||
actions = { SettingsButton(navigateToSettings) }
|
|
||||||
) { paddingValues ->
|
|
||||||
Column(
|
|
||||||
Modifier.padding(paddingValues)
|
|
||||||
) {
|
|
||||||
InputBox(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.fillMaxHeight(if (LocalWindowSize.current.heightSizeClass > WindowHeightSizeClass.Compact) 0.3f else 0.5f),
|
|
||||||
input = uiState.input,
|
|
||||||
stack = uiState.stack,
|
|
||||||
formatterSymbols = uiState.formatterSymbols,
|
|
||||||
precision = uiState.precision,
|
|
||||||
outputFormat = uiState.outputFormat,
|
|
||||||
onCursorChange = onCursorChange
|
|
||||||
)
|
|
||||||
RPNCalculatorKeyboard(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 4.dp)
|
|
||||||
.fillMaxSize(),
|
|
||||||
fractional = uiState.formatterSymbols.fractional,
|
|
||||||
middleZero = uiState.middleZero,
|
|
||||||
onCalculationClick = onCalculationClick,
|
|
||||||
onInputEditClick = onInputEditClick
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(widthDp = 432, heightDp = 1008, device = "spec:parent=pixel_5,orientation=portrait")
|
|
||||||
@Preview(widthDp = 432, heightDp = 864, device = "spec:parent=pixel_5,orientation=portrait")
|
|
||||||
@Preview(widthDp = 597, heightDp = 1393, device = "spec:parent=pixel_5,orientation=portrait")
|
|
||||||
@Composable
|
|
||||||
private fun RPNCalculatorScreenPreview() {
|
|
||||||
RPNCalculatorScreen(
|
|
||||||
uiState = RPNCalculatorUIState.Ready(
|
|
||||||
input = TextFieldValue("test"),
|
|
||||||
stack = listOf(
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
BigDecimal("123456.7890"),
|
|
||||||
),
|
|
||||||
precision = 3,
|
|
||||||
outputFormat = OutputFormat.PLAIN,
|
|
||||||
formatterSymbols = FormatterSymbols.Spaces,
|
|
||||||
middleZero = true,
|
|
||||||
),
|
|
||||||
openDrawer = {},
|
|
||||||
navigateToSettings = {},
|
|
||||||
onCalculationClick = {},
|
|
||||||
onInputEditClick = {},
|
|
||||||
onCursorChange = {}
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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.calculator
|
|
||||||
|
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
internal sealed class RPNCalculatorUIState {
|
|
||||||
data object Loading : RPNCalculatorUIState()
|
|
||||||
|
|
||||||
data class Ready(
|
|
||||||
val input: TextFieldValue,
|
|
||||||
val stack: List<BigDecimal>,
|
|
||||||
val precision: Int,
|
|
||||||
val outputFormat: Int,
|
|
||||||
val formatterSymbols: FormatterSymbols,
|
|
||||||
val middleZero: Boolean,
|
|
||||||
) : RPNCalculatorUIState()
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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.calculator
|
|
||||||
|
|
||||||
import androidx.compose.ui.text.TextRange
|
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.sadellie.unitto.core.base.Token
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.addTokens
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.deleteTokens
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.getTextField
|
|
||||||
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 io.github.sadellie.evaluatto.RPNCalculation
|
|
||||||
import io.github.sadellie.evaluatto.RPNResult
|
|
||||||
import io.github.sadellie.evaluatto.perform
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.math.BigDecimal
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
internal class RPNCalculatorViewModel @Inject constructor(
|
|
||||||
userPrefsRepository: UserPreferencesRepository,
|
|
||||||
private val savedStateHandle: SavedStateHandle,
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val _inputKey = "RPN_CALCULATOR_INPUT"
|
|
||||||
private val _input = MutableStateFlow(savedStateHandle.getTextField(_inputKey))
|
|
||||||
private val _stack = MutableStateFlow(emptyList<BigDecimal>())
|
|
||||||
private val _prefs = userPrefsRepository.calculatorPrefs.stateIn(viewModelScope, null)
|
|
||||||
|
|
||||||
val uiState = combine(
|
|
||||||
_prefs,
|
|
||||||
_input,
|
|
||||||
_stack
|
|
||||||
) { prefs, input, stack ->
|
|
||||||
prefs ?: return@combine RPNCalculatorUIState.Loading
|
|
||||||
|
|
||||||
return@combine RPNCalculatorUIState.Ready(
|
|
||||||
input = input,
|
|
||||||
stack = stack,
|
|
||||||
precision = prefs.precision,
|
|
||||||
outputFormat = prefs.outputFormat,
|
|
||||||
formatterSymbols = AllFormatterSymbols.getById(prefs.separator),
|
|
||||||
middleZero = prefs.middleZero
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.stateIn(viewModelScope, RPNCalculatorUIState.Loading)
|
|
||||||
|
|
||||||
fun onInputEdit(action: RPNInputEdit) {
|
|
||||||
val input = _input.value
|
|
||||||
val newInput = when (action) {
|
|
||||||
is RPNInputEdit.Digit -> input.addTokens(action.value)
|
|
||||||
RPNInputEdit.Dot -> {
|
|
||||||
if (_input.value.text.contains(Token.Digit.dot)) return
|
|
||||||
input.addTokens(Token.Digit.dot)
|
|
||||||
}
|
|
||||||
RPNInputEdit.Delete -> input.deleteTokens()
|
|
||||||
}
|
|
||||||
|
|
||||||
_input.update { newInput }
|
|
||||||
savedStateHandle[_inputKey] = newInput.text
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onCalculationClick(action: RPNCalculation) = viewModelScope.launch {
|
|
||||||
val prefs = _prefs.value ?: return@launch
|
|
||||||
|
|
||||||
val newResult = withContext(Dispatchers.Default) {
|
|
||||||
action.perform(_input.value.text, _stack.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
when (newResult) {
|
|
||||||
is RPNResult.Result -> {
|
|
||||||
val newInput = newResult.input?.format(prefs.precision, prefs.outputFormat) ?: ""
|
|
||||||
_input.update { TextFieldValue(newInput, TextRange(newInput.length)) }
|
|
||||||
_stack.update { newResult.stack }
|
|
||||||
}
|
|
||||||
|
|
||||||
is RPNResult.NewStack -> {
|
|
||||||
_stack.update { newResult.stack }
|
|
||||||
}
|
|
||||||
|
|
||||||
is RPNResult.NewInput -> {
|
|
||||||
val newInput = newResult.input.format(prefs.precision, prefs.outputFormat)
|
|
||||||
_input.update { TextFieldValue(newInput, TextRange(newInput.length)) }
|
|
||||||
}
|
|
||||||
RPNResult.BadInput, RPNResult.DivideByZero -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onCursorChange(selection: TextRange) = _input.update { it.copy(selection = selection) }
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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.calculator
|
|
||||||
|
|
||||||
sealed class RPNInputEdit {
|
|
||||||
data class Digit(val value: String) : RPNInputEdit()
|
|
||||||
data object Delete : RPNInputEdit()
|
|
||||||
data object Dot : RPNInputEdit()
|
|
||||||
}
|
|
@ -24,13 +24,11 @@ import com.sadellie.unitto.core.ui.model.DrawerItem
|
|||||||
import com.sadellie.unitto.core.ui.unittoComposable
|
import com.sadellie.unitto.core.ui.unittoComposable
|
||||||
import com.sadellie.unitto.core.ui.unittoNavigation
|
import com.sadellie.unitto.core.ui.unittoNavigation
|
||||||
import com.sadellie.unitto.feature.calculator.CalculatorRoute
|
import com.sadellie.unitto.feature.calculator.CalculatorRoute
|
||||||
import com.sadellie.unitto.feature.calculator.RPNCalculatorRoute
|
|
||||||
|
|
||||||
private val graph = DrawerItem.Calculator.graph
|
private val graph = DrawerItem.Calculator.graph
|
||||||
private val start = DrawerItem.Calculator.start
|
private val start = DrawerItem.Calculator.start
|
||||||
|
|
||||||
fun NavGraphBuilder.calculatorGraph(
|
fun NavGraphBuilder.calculatorGraph(
|
||||||
rpnMode: Boolean,
|
|
||||||
openDrawer: () -> Unit,
|
openDrawer: () -> Unit,
|
||||||
navigateToSettings: () -> Unit
|
navigateToSettings: () -> Unit
|
||||||
) {
|
) {
|
||||||
@ -42,17 +40,10 @@ fun NavGraphBuilder.calculatorGraph(
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
unittoComposable(start) {
|
unittoComposable(start) {
|
||||||
if (rpnMode) {
|
CalculatorRoute(
|
||||||
RPNCalculatorRoute(
|
navigateToMenu = openDrawer,
|
||||||
openDrawer = openDrawer,
|
navigateToSettings = navigateToSettings
|
||||||
navigateToSettings = navigateToSettings
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
CalculatorRoute(
|
|
||||||
navigateToMenu = openDrawer,
|
|
||||||
navigateToSettings = navigateToSettings
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.feature.settings.calculator
|
package com.sadellie.unitto.feature.settings.calculator
|
||||||
|
|
||||||
import androidx.compose.animation.Crossfade
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -29,84 +28,51 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
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.ui.common.NavigateUpButton
|
import com.sadellie.unitto.core.base.Separator
|
||||||
import com.sadellie.unitto.core.ui.common.EmptyScreen
|
import com.sadellie.unitto.core.ui.common.EmptyScreen
|
||||||
import com.sadellie.unitto.core.ui.common.ListItem
|
import com.sadellie.unitto.core.ui.common.ListItem
|
||||||
|
import com.sadellie.unitto.core.ui.common.NavigateUpButton
|
||||||
import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar
|
import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar
|
||||||
|
import com.sadellie.unitto.data.model.userprefs.CalculatorPreferences
|
||||||
|
import com.sadellie.unitto.data.userprefs.CalculatorPreferencesImpl
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun CalculatorSettingsRoute(
|
internal fun CalculatorSettingsRoute(
|
||||||
viewModel: CalculatorSettingsViewModel = hiltViewModel(),
|
viewModel: CalculatorSettingsViewModel = hiltViewModel(),
|
||||||
navigateUpAction: () -> Unit,
|
navigateUpAction: () -> Unit,
|
||||||
) {
|
) {
|
||||||
when (val prefs = viewModel.uiState.collectAsStateWithLifecycle().value) {
|
when (val prefs = viewModel.prefs.collectAsStateWithLifecycle().value) {
|
||||||
CalculatorSettingsUIState.Loading -> EmptyScreen()
|
null -> EmptyScreen()
|
||||||
else -> {
|
else -> {
|
||||||
CalculatorSettingsScreen(
|
CalculatorSettingsScreen(
|
||||||
uiState = prefs,
|
prefs = prefs,
|
||||||
navigateUpAction = navigateUpAction,
|
navigateUpAction = navigateUpAction,
|
||||||
updatePartialHistoryView = viewModel::updatePartialHistoryView,
|
updatePartialHistoryView = viewModel::updatePartialHistoryView,
|
||||||
updateRpnMode = viewModel::updateRpnMode,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Translate
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CalculatorSettingsScreen(
|
private fun CalculatorSettingsScreen(
|
||||||
uiState: CalculatorSettingsUIState,
|
prefs: CalculatorPreferences,
|
||||||
navigateUpAction: () -> Unit,
|
navigateUpAction: () -> Unit,
|
||||||
updatePartialHistoryView: (Boolean) -> Unit,
|
updatePartialHistoryView: (Boolean) -> Unit,
|
||||||
updateRpnMode: (Boolean) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
ScaffoldWithLargeTopBar(
|
ScaffoldWithLargeTopBar(
|
||||||
title = stringResource(R.string.calculator_title),
|
title = stringResource(R.string.calculator_title),
|
||||||
navigationIcon = { NavigateUpButton(navigateUpAction) }
|
navigationIcon = { NavigateUpButton(navigateUpAction) }
|
||||||
) { padding ->
|
) { padding ->
|
||||||
Column(Modifier.padding(padding)) {
|
Column(Modifier.padding(padding)) {
|
||||||
// SingleChoiceSegmentedButtonRow(
|
ListItem(
|
||||||
// modifier = Modifier
|
headlineText = stringResource(R.string.settings_partial_history_view),
|
||||||
// .fillMaxWidth()
|
icon = Icons.Default.Timer,
|
||||||
// .padding(16.dp)
|
supportingText = stringResource(R.string.settings_partial_history_view_support),
|
||||||
// ) {
|
switchState = prefs.partialHistoryView,
|
||||||
// SegmentedButton(
|
onSwitchChange = updatePartialHistoryView
|
||||||
// selected = uiState is CalculatorSettingsUIState.Standard,
|
)
|
||||||
// onClick = { updateRpnMode(false) },
|
|
||||||
// shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2),
|
|
||||||
// ) {
|
|
||||||
// Text("Standard")
|
|
||||||
// }
|
|
||||||
// SegmentedButton(
|
|
||||||
// selected = uiState == CalculatorSettingsUIState.RPN,
|
|
||||||
// onClick = { updateRpnMode(true) },
|
|
||||||
// shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2),
|
|
||||||
// ) {
|
|
||||||
// Text("RPN")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
Crossfade(
|
|
||||||
targetState = uiState,
|
|
||||||
label = "Mode switch"
|
|
||||||
) { state ->
|
|
||||||
when (state) {
|
|
||||||
is CalculatorSettingsUIState.Standard -> {
|
|
||||||
Column {
|
|
||||||
ListItem(
|
|
||||||
headlineText = stringResource(R.string.settings_partial_history_view),
|
|
||||||
icon = Icons.Default.Timer,
|
|
||||||
supportingText = stringResource(R.string.settings_partial_history_view_support),
|
|
||||||
switchState = state.partialHistoryView,
|
|
||||||
onSwitchChange = updatePartialHistoryView
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,22 +81,16 @@ private fun CalculatorSettingsScreen(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun PreviewCalculatorSettingsScreenStandard() {
|
private fun PreviewCalculatorSettingsScreenStandard() {
|
||||||
CalculatorSettingsScreen(
|
CalculatorSettingsScreen(
|
||||||
uiState = CalculatorSettingsUIState.Standard(
|
prefs = CalculatorPreferencesImpl(
|
||||||
partialHistoryView = true,
|
radianMode = true,
|
||||||
|
separator = Separator.SPACE,
|
||||||
|
middleZero = false,
|
||||||
|
acButton = false,
|
||||||
|
partialHistoryView = false,
|
||||||
|
precision = 3,
|
||||||
|
outputFormat = OutputFormat.PLAIN
|
||||||
),
|
),
|
||||||
navigateUpAction = {},
|
navigateUpAction = {},
|
||||||
updatePartialHistoryView = {},
|
updatePartialHistoryView = {},
|
||||||
updateRpnMode = {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
private fun PreviewCalculatorSettingsScreenRPN() {
|
|
||||||
CalculatorSettingsScreen(
|
|
||||||
uiState = CalculatorSettingsUIState.RPN,
|
|
||||||
navigateUpAction = {},
|
|
||||||
updatePartialHistoryView = {},
|
|
||||||
updateRpnMode = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Unitto is a calculator for Android
|
|
||||||
* Copyright (c) 2023-2024 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.settings.calculator
|
|
||||||
|
|
||||||
internal sealed class CalculatorSettingsUIState {
|
|
||||||
data object Loading : CalculatorSettingsUIState()
|
|
||||||
|
|
||||||
data object RPN : CalculatorSettingsUIState()
|
|
||||||
|
|
||||||
data class Standard(
|
|
||||||
val partialHistoryView: Boolean,
|
|
||||||
) : CalculatorSettingsUIState()
|
|
||||||
}
|
|
@ -23,7 +23,6 @@ import androidx.lifecycle.viewModelScope
|
|||||||
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.combine
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -31,25 +30,10 @@ import javax.inject.Inject
|
|||||||
internal class CalculatorSettingsViewModel @Inject constructor(
|
internal class CalculatorSettingsViewModel @Inject constructor(
|
||||||
private val userPrefsRepository: UserPreferencesRepository,
|
private val userPrefsRepository: UserPreferencesRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val uiState = combine(
|
val prefs = userPrefsRepository.calculatorPrefs
|
||||||
userPrefsRepository.appPrefs,
|
.stateIn(viewModelScope, null)
|
||||||
userPrefsRepository.calculatorPrefs,
|
|
||||||
) { app, calc ->
|
|
||||||
if (app.rpnMode) {
|
|
||||||
CalculatorSettingsUIState.RPN
|
|
||||||
} else {
|
|
||||||
CalculatorSettingsUIState.Standard(
|
|
||||||
partialHistoryView = calc.partialHistoryView,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(viewModelScope, CalculatorSettingsUIState.Loading)
|
|
||||||
|
|
||||||
fun updatePartialHistoryView(enabled: Boolean) = viewModelScope.launch {
|
fun updatePartialHistoryView(enabled: Boolean) = viewModelScope.launch {
|
||||||
userPrefsRepository.updatePartialHistoryView(enabled)
|
userPrefsRepository.updatePartialHistoryView(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateRpnMode(enabled: Boolean) = viewModelScope.launch {
|
|
||||||
userPrefsRepository.updateRpnMode(enabled)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user