feat: добавил визуальную индикацию ожидающей операции

This commit is contained in:
Heller
2026-06-18 23:48:19 +00:00
parent 4ca53cf34e
commit babec55122
4 changed files with 96 additions and 22 deletions

View File

@@ -54,21 +54,41 @@ fun CalculatorScreen(viewModel: CalculatorViewModel) {
.padding(16.dp), .padding(16.dp),
verticalArrangement = Arrangement.SpaceBetween, verticalArrangement = Arrangement.SpaceBetween,
) { ) {
DisplaySection(display = uiState.display) DisplaySection(
ButtonGrid(viewModel = viewModel) display = uiState.display,
pendingExpression = uiState.pendingExpression,
)
ButtonGrid(
viewModel = viewModel,
pendingOperation = uiState.pendingOperation,
)
} }
} }
} }
} }
@Composable @Composable
private fun DisplaySection(display: String) { private fun DisplaySection(
display: String,
pendingExpression: String?,
) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 24.dp), .padding(vertical = 24.dp),
horizontalAlignment = Alignment.End, horizontalAlignment = Alignment.End,
) { ) {
if (pendingExpression != null) {
Text(
text = pendingExpression,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f),
textAlign = TextAlign.End,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(),
)
}
Text( Text(
text = display, text = display,
style = MaterialTheme.typography.headlineLarge, style = MaterialTheme.typography.headlineLarge,
@@ -82,7 +102,10 @@ private fun DisplaySection(display: String) {
} }
@Composable @Composable
private fun ButtonGrid(viewModel: CalculatorViewModel) { private fun ButtonGrid(
viewModel: CalculatorViewModel,
pendingOperation: CalculatorOperation?,
) {
val rows = listOf( val rows = listOf(
listOf("C" to CalculatorButtonType.Clear, "÷" to CalculatorButtonType.Operator), listOf("C" to CalculatorButtonType.Clear, "÷" to CalculatorButtonType.Operator),
listOf("7" to CalculatorButtonType.Digit, "8" to CalculatorButtonType.Digit, "9" to CalculatorButtonType.Digit, "×" to CalculatorButtonType.Operator), listOf("7" to CalculatorButtonType.Digit, "8" to CalculatorButtonType.Digit, "9" to CalculatorButtonType.Digit, "×" to CalculatorButtonType.Operator),
@@ -111,6 +134,7 @@ private fun ButtonGrid(viewModel: CalculatorViewModel) {
label = "÷", label = "÷",
type = CalculatorButtonType.Operator, type = CalculatorButtonType.Operator,
onClick = { viewModel.onOperation(CalculatorOperation.Divide) }, onClick = { viewModel.onOperation(CalculatorOperation.Divide) },
isSelected = pendingOperation == CalculatorOperation.Divide,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.aspectRatio(1f), .aspectRatio(1f),
@@ -128,6 +152,7 @@ private fun ButtonGrid(viewModel: CalculatorViewModel) {
label = label, label = label,
type = type, type = type,
onClick = { handleButtonClick(label, viewModel) }, onClick = { handleButtonClick(label, viewModel) },
isSelected = labelToOperation(label) == pendingOperation,
modifier = Modifier modifier = Modifier
.weight(weight) .weight(weight)
.aspectRatio(if (label == "0") 2.15f else 1f), .aspectRatio(if (label == "0") 2.15f else 1f),
@@ -139,6 +164,14 @@ private fun ButtonGrid(viewModel: CalculatorViewModel) {
} }
} }
private fun labelToOperation(label: String): CalculatorOperation? = when (label) {
"+" -> CalculatorOperation.Add
"" -> CalculatorOperation.Subtract
"×" -> CalculatorOperation.Multiply
"÷" -> CalculatorOperation.Divide
else -> null
}
private fun handleButtonClick(label: String, viewModel: CalculatorViewModel) { private fun handleButtonClick(label: String, viewModel: CalculatorViewModel) {
when (label) { when (label) {
"C" -> viewModel.onClear() "C" -> viewModel.onClear()

View File

@@ -1,10 +1,10 @@
package com.heller.calculator package com.heller.calculator
enum class CalculatorOperation(val symbol: String) { enum class CalculatorOperation(val symbol: String, val displaySymbol: String) {
Add("+"), Add("+", "+"),
Subtract("-"), Subtract("-", ""),
Multiply("*"), Multiply("*", "×"),
Divide("/"); Divide("/", "÷");
fun apply(a: Double, b: Double): Double = when (this) { fun apply(a: Double, b: Double): Double = when (this) {
Add -> a + b Add -> a + b
@@ -17,4 +17,6 @@ enum class CalculatorOperation(val symbol: String) {
data class CalculatorUiState( data class CalculatorUiState(
val display: String = "0", val display: String = "0",
val error: String? = null, val error: String? = null,
val pendingOperation: CalculatorOperation? = null,
val pendingExpression: String? = null,
) )

View File

@@ -57,6 +57,7 @@ class CalculatorViewModel : ViewModel() {
pendingOperation = operation pendingOperation = operation
waitingForOperand = true waitingForOperand = true
syncUiState()
} }
fun onEquals() { fun onEquals() {
@@ -69,7 +70,7 @@ class CalculatorViewModel : ViewModel() {
storedValue = null storedValue = null
pendingOperation = null pendingOperation = null
waitingForOperand = true waitingForOperand = true
updateDisplay(formatResult(result)) syncUiState(display = formatResult(result))
} }
fun onClear() { fun onClear() {
@@ -106,6 +107,33 @@ class CalculatorViewModel : ViewModel() {
} }
private fun updateDisplay(display: String) { private fun updateDisplay(display: String) {
_uiState.update { it.copy(display = display) } syncUiState(display = display)
}
private fun syncUiState(
display: String? = null,
clearError: Boolean = false,
setError: String? = null,
) {
_uiState.update { state ->
val newDisplay = display ?: state.display
val newError = when {
setError != null -> setError
clearError -> null
else -> state.error
}
val expression = if (pendingOperation != null && storedValue != null) {
"${formatResult(storedValue!!)} ${pendingOperation!!.displaySymbol}"
} else {
null
}
state.copy(
display = newDisplay,
error = newError,
pendingOperation = pendingOperation,
pendingExpression = expression,
)
}
} }
} }

View File

@@ -1,5 +1,6 @@
package com.heller.calculator.ui.components package com.heller.calculator.ui.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -26,19 +27,22 @@ fun CalculatorButton(
type: CalculatorButtonType, type: CalculatorButtonType,
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isSelected: Boolean = false,
) { ) {
val containerColor = when (type) { val containerColor = when {
CalculatorButtonType.Digit -> MaterialTheme.colorScheme.surfaceVariant isSelected -> MaterialTheme.colorScheme.primary
CalculatorButtonType.Operator -> MaterialTheme.colorScheme.primaryContainer type == CalculatorButtonType.Digit -> MaterialTheme.colorScheme.surfaceVariant
CalculatorButtonType.Equals -> MaterialTheme.colorScheme.primary type == CalculatorButtonType.Operator -> MaterialTheme.colorScheme.primaryContainer
CalculatorButtonType.Clear -> MaterialTheme.colorScheme.errorContainer type == CalculatorButtonType.Equals -> MaterialTheme.colorScheme.primary
else -> MaterialTheme.colorScheme.errorContainer
} }
val contentColor = when (type) { val contentColor = when {
CalculatorButtonType.Digit -> MaterialTheme.colorScheme.onSurfaceVariant isSelected -> MaterialTheme.colorScheme.onPrimary
CalculatorButtonType.Operator -> MaterialTheme.colorScheme.onPrimaryContainer type == CalculatorButtonType.Digit -> MaterialTheme.colorScheme.onSurfaceVariant
CalculatorButtonType.Equals -> MaterialTheme.colorScheme.onPrimary type == CalculatorButtonType.Operator -> MaterialTheme.colorScheme.onPrimaryContainer
CalculatorButtonType.Clear -> MaterialTheme.colorScheme.onErrorContainer type == CalculatorButtonType.Equals -> MaterialTheme.colorScheme.onPrimary
else -> MaterialTheme.colorScheme.onErrorContainer
} }
Button( Button(
@@ -49,7 +53,14 @@ fun CalculatorButton(
containerColor = containerColor, containerColor = containerColor,
contentColor = contentColor, contentColor = contentColor,
), ),
elevation = ButtonDefaults.buttonElevation(defaultElevation = 2.dp), border = if (isSelected) {
BorderStroke(2.dp, MaterialTheme.colorScheme.onPrimary)
} else {
null
},
elevation = ButtonDefaults.buttonElevation(
defaultElevation = if (isSelected) 6.dp else 2.dp,
),
) { ) {
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),