diff --git a/app/src/main/java/com/heller/calculator/CalculatorScreen.kt b/app/src/main/java/com/heller/calculator/CalculatorScreen.kt index 97f0d34..7c34eb0 100644 --- a/app/src/main/java/com/heller/calculator/CalculatorScreen.kt +++ b/app/src/main/java/com/heller/calculator/CalculatorScreen.kt @@ -54,21 +54,41 @@ fun CalculatorScreen(viewModel: CalculatorViewModel) { .padding(16.dp), verticalArrangement = Arrangement.SpaceBetween, ) { - DisplaySection(display = uiState.display) - ButtonGrid(viewModel = viewModel) + DisplaySection( + display = uiState.display, + pendingExpression = uiState.pendingExpression, + ) + ButtonGrid( + viewModel = viewModel, + pendingOperation = uiState.pendingOperation, + ) } } } } @Composable -private fun DisplaySection(display: String) { +private fun DisplaySection( + display: String, + pendingExpression: String?, +) { Column( modifier = Modifier .fillMaxWidth() .padding(vertical = 24.dp), 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 = display, style = MaterialTheme.typography.headlineLarge, @@ -82,7 +102,10 @@ private fun DisplaySection(display: String) { } @Composable -private fun ButtonGrid(viewModel: CalculatorViewModel) { +private fun ButtonGrid( + viewModel: CalculatorViewModel, + pendingOperation: CalculatorOperation?, +) { val rows = listOf( listOf("C" to CalculatorButtonType.Clear, "÷" 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 = "÷", type = CalculatorButtonType.Operator, onClick = { viewModel.onOperation(CalculatorOperation.Divide) }, + isSelected = pendingOperation == CalculatorOperation.Divide, modifier = Modifier .weight(1f) .aspectRatio(1f), @@ -128,6 +152,7 @@ private fun ButtonGrid(viewModel: CalculatorViewModel) { label = label, type = type, onClick = { handleButtonClick(label, viewModel) }, + isSelected = labelToOperation(label) == pendingOperation, modifier = Modifier .weight(weight) .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) { when (label) { "C" -> viewModel.onClear() diff --git a/app/src/main/java/com/heller/calculator/CalculatorState.kt b/app/src/main/java/com/heller/calculator/CalculatorState.kt index 17de912..d7fb14c 100644 --- a/app/src/main/java/com/heller/calculator/CalculatorState.kt +++ b/app/src/main/java/com/heller/calculator/CalculatorState.kt @@ -1,10 +1,10 @@ package com.heller.calculator -enum class CalculatorOperation(val symbol: String) { - Add("+"), - Subtract("-"), - Multiply("*"), - Divide("/"); +enum class CalculatorOperation(val symbol: String, val displaySymbol: String) { + Add("+", "+"), + Subtract("-", "−"), + Multiply("*", "×"), + Divide("/", "÷"); fun apply(a: Double, b: Double): Double = when (this) { Add -> a + b @@ -17,4 +17,6 @@ enum class CalculatorOperation(val symbol: String) { data class CalculatorUiState( val display: String = "0", val error: String? = null, + val pendingOperation: CalculatorOperation? = null, + val pendingExpression: String? = null, ) diff --git a/app/src/main/java/com/heller/calculator/CalculatorViewModel.kt b/app/src/main/java/com/heller/calculator/CalculatorViewModel.kt index 8048d8b..3d65acb 100644 --- a/app/src/main/java/com/heller/calculator/CalculatorViewModel.kt +++ b/app/src/main/java/com/heller/calculator/CalculatorViewModel.kt @@ -57,6 +57,7 @@ class CalculatorViewModel : ViewModel() { pendingOperation = operation waitingForOperand = true + syncUiState() } fun onEquals() { @@ -69,7 +70,7 @@ class CalculatorViewModel : ViewModel() { storedValue = null pendingOperation = null waitingForOperand = true - updateDisplay(formatResult(result)) + syncUiState(display = formatResult(result)) } fun onClear() { @@ -106,6 +107,33 @@ class CalculatorViewModel : ViewModel() { } 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, + ) + } } } diff --git a/app/src/main/java/com/heller/calculator/ui/components/CalculatorButton.kt b/app/src/main/java/com/heller/calculator/ui/components/CalculatorButton.kt index af046ed..b5560a7 100644 --- a/app/src/main/java/com/heller/calculator/ui/components/CalculatorButton.kt +++ b/app/src/main/java/com/heller/calculator/ui/components/CalculatorButton.kt @@ -1,5 +1,6 @@ package com.heller.calculator.ui.components +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.shape.RoundedCornerShape @@ -26,19 +27,22 @@ fun CalculatorButton( type: CalculatorButtonType, onClick: () -> Unit, modifier: Modifier = Modifier, + isSelected: Boolean = false, ) { - val containerColor = when (type) { - CalculatorButtonType.Digit -> MaterialTheme.colorScheme.surfaceVariant - CalculatorButtonType.Operator -> MaterialTheme.colorScheme.primaryContainer - CalculatorButtonType.Equals -> MaterialTheme.colorScheme.primary - CalculatorButtonType.Clear -> MaterialTheme.colorScheme.errorContainer + val containerColor = when { + isSelected -> MaterialTheme.colorScheme.primary + type == CalculatorButtonType.Digit -> MaterialTheme.colorScheme.surfaceVariant + type == CalculatorButtonType.Operator -> MaterialTheme.colorScheme.primaryContainer + type == CalculatorButtonType.Equals -> MaterialTheme.colorScheme.primary + else -> MaterialTheme.colorScheme.errorContainer } - val contentColor = when (type) { - CalculatorButtonType.Digit -> MaterialTheme.colorScheme.onSurfaceVariant - CalculatorButtonType.Operator -> MaterialTheme.colorScheme.onPrimaryContainer - CalculatorButtonType.Equals -> MaterialTheme.colorScheme.onPrimary - CalculatorButtonType.Clear -> MaterialTheme.colorScheme.onErrorContainer + val contentColor = when { + isSelected -> MaterialTheme.colorScheme.onPrimary + type == CalculatorButtonType.Digit -> MaterialTheme.colorScheme.onSurfaceVariant + type == CalculatorButtonType.Operator -> MaterialTheme.colorScheme.onPrimaryContainer + type == CalculatorButtonType.Equals -> MaterialTheme.colorScheme.onPrimary + else -> MaterialTheme.colorScheme.onErrorContainer } Button( @@ -49,7 +53,14 @@ fun CalculatorButton( containerColor = containerColor, 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( modifier = Modifier.fillMaxSize(),