mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 08:45:27 +02:00
Refactor CalculatorScreen
This commit is contained in:
parent
e14cd51070
commit
c8b11a4b02
@ -19,12 +19,16 @@
|
|||||||
package com.sadellie.unitto.core.ui.common
|
package com.sadellie.unitto.core.ui.common
|
||||||
|
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
@ -52,12 +56,18 @@ fun BasicKeyboardButton(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UnittoButton(
|
|
||||||
modifier = modifier,
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.squashable(
|
||||||
onClick = { onClick(); vibrate() },
|
onClick = { onClick(); vibrate() },
|
||||||
onLongClick = if (onLongClick != null) { { onLongClick(); vibrate() } } else null,
|
onLongClick = if (onLongClick != null) { { onLongClick(); vibrate() } } else null,
|
||||||
containerColor = containerColor,
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
contentPadding = PaddingValues()
|
cornerRadiusRange = 30..50,
|
||||||
|
)
|
||||||
|
.background(containerColor)
|
||||||
|
,
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
|
@ -20,10 +20,12 @@ package com.sadellie.unitto.feature.calculator
|
|||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
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.Orientation
|
||||||
import androidx.compose.foundation.gestures.anchoredDraggable
|
import androidx.compose.foundation.gestures.anchoredDraggable
|
||||||
import androidx.compose.foundation.gestures.snapTo
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
@ -49,7 +51,6 @@ import androidx.compose.runtime.derivedStateOf
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@ -70,13 +71,13 @@ import com.sadellie.unitto.core.base.R
|
|||||||
import com.sadellie.unitto.core.ui.common.MenuButton
|
import com.sadellie.unitto.core.ui.common.MenuButton
|
||||||
import com.sadellie.unitto.core.ui.common.SettingsButton
|
import com.sadellie.unitto.core.ui.common.SettingsButton
|
||||||
import com.sadellie.unitto.core.ui.common.UnittoScreenWithTopBar
|
import com.sadellie.unitto.core.ui.common.UnittoScreenWithTopBar
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField
|
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField
|
import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField
|
||||||
import com.sadellie.unitto.data.model.HistoryItem
|
import com.sadellie.unitto.data.model.HistoryItem
|
||||||
import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboard
|
import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboard
|
||||||
import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboardLoading
|
import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboardLoading
|
||||||
|
import com.sadellie.unitto.feature.calculator.components.HistoryItemHeight
|
||||||
import com.sadellie.unitto.feature.calculator.components.HistoryList
|
import com.sadellie.unitto.feature.calculator.components.HistoryList
|
||||||
import kotlinx.coroutines.launch
|
import com.sadellie.unitto.feature.calculator.components.TextBox
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@ -179,146 +180,89 @@ private fun Ready(
|
|||||||
modifier = Modifier.padding(paddingValues),
|
modifier = Modifier.padding(paddingValues),
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
var historyItemHeight by remember { mutableStateOf(0.dp) }
|
|
||||||
val textBoxHeight = maxHeight * 0.25f
|
val textBoxHeight = maxHeight * 0.25f
|
||||||
var dragStateCurrentValue by rememberSaveable { mutableStateOf(DragState.CLOSED) }
|
|
||||||
val corScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
val dragState = rememberDragState(
|
val dragState = remember {
|
||||||
historyItem = historyItemHeight,
|
AnchoredDraggableState(
|
||||||
max = maxHeight - textBoxHeight,
|
initialValue = DragState.CLOSED,
|
||||||
initialValue = dragStateCurrentValue,
|
positionalThreshold = { distance -> distance * 0.5f },
|
||||||
enablePartialView = uiState.partialHistoryView
|
velocityThreshold = { with(density) { HistoryItemHeight.toPx() } },
|
||||||
|
animationSpec = tween()
|
||||||
)
|
)
|
||||||
val dragDp by remember(dragState) {
|
}
|
||||||
|
|
||||||
|
var historyListHeight by remember { mutableStateOf(0.dp) }
|
||||||
|
val keyboardHeight by remember(historyListHeight) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
focusManager.clearFocus(true)
|
if (historyListHeight > HistoryItemHeight) {
|
||||||
with(density) {
|
maxHeight - textBoxHeight - HistoryItemHeight
|
||||||
try {
|
|
||||||
dragState.requireOffset().toDp()
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
corScope.launch { dragState.snapTo(DragState.CLOSED) }
|
|
||||||
0.dp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val keyboardHeight by remember(dragState) {
|
|
||||||
derivedStateOf {
|
|
||||||
if (dragDp > historyItemHeight) {
|
|
||||||
maxHeight - textBoxHeight - historyItemHeight
|
|
||||||
} else {
|
} else {
|
||||||
maxHeight - textBoxHeight - dragDp
|
maxHeight - textBoxHeight - historyListHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(dragState.currentValue) {
|
LaunchedEffect(uiState.partialHistoryView) {
|
||||||
dragStateCurrentValue = dragState.currentValue
|
val anchors: DraggableAnchors<DragState> = with(density) {
|
||||||
|
if (uiState.partialHistoryView) {
|
||||||
|
DraggableAnchors {
|
||||||
|
DragState.CLOSED at 0f
|
||||||
|
DragState.SMALL at HistoryItemHeight.toPx()
|
||||||
|
DragState.OPEN at (maxHeight * 0.75f).toPx()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DraggableAnchors {
|
||||||
|
DragState.CLOSED at 0f
|
||||||
|
DragState.OPEN at (maxHeight * 0.75f).toPx()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dragState.updateAnchors(anchors)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(dragState.offset) {
|
||||||
|
with(density) {
|
||||||
|
if (!dragState.offset.isNaN()) {
|
||||||
|
historyListHeight = dragState.requireOffset().toDp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
focusManager.clearFocus()
|
||||||
showClearHistoryButton = dragState.currentValue == DragState.OPEN
|
showClearHistoryButton = dragState.currentValue == DragState.OPEN
|
||||||
}
|
}
|
||||||
|
|
||||||
// History
|
|
||||||
HistoryList(
|
HistoryList(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f))
|
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(dragDp),
|
.height(historyListHeight),
|
||||||
historyItems = uiState.history,
|
historyItems = uiState.history,
|
||||||
heightCallback = { historyItemHeight = it },
|
|
||||||
formatterSymbols = uiState.formatterSymbols,
|
formatterSymbols = uiState.formatterSymbols,
|
||||||
addTokens = addSymbol,
|
addTokens = addSymbol
|
||||||
)
|
)
|
||||||
|
|
||||||
// Input
|
TextBox(
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.semantics { testTag = "inputBox" }
|
.offset(y = historyListHeight)
|
||||||
.offset(y = dragDp)
|
|
||||||
.height(textBoxHeight)
|
.height(textBoxHeight)
|
||||||
.background(
|
.fillMaxWidth()
|
||||||
MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
RoundedCornerShape(
|
|
||||||
topStartPercent = 0, topEndPercent = 0,
|
|
||||||
bottomStartPercent = 20, bottomEndPercent = 20
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.anchoredDraggable(
|
.anchoredDraggable(
|
||||||
state = dragState,
|
state = dragState,
|
||||||
orientation = Orientation.Vertical
|
orientation = Orientation.Vertical
|
||||||
)
|
)
|
||||||
.padding(top = 12.dp),
|
,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
ExpressionTextField(
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(2f)
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 8.dp),
|
|
||||||
value = uiState.input,
|
|
||||||
minRatio = 0.5f,
|
|
||||||
cutCallback = deleteSymbol,
|
|
||||||
pasteCallback = addSymbol,
|
|
||||||
onCursorChange = onCursorChange,
|
|
||||||
formatterSymbols = uiState.formatterSymbols
|
|
||||||
)
|
|
||||||
if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
|
||||||
when (uiState.output) {
|
|
||||||
is CalculationResult.Default -> {
|
|
||||||
var output by remember(uiState.output) {
|
|
||||||
mutableStateOf(TextFieldValue(uiState.output.text))
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionTextField(
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 8.dp),
|
|
||||||
value = output,
|
|
||||||
minRatio = 1f,
|
|
||||||
onCursorChange = { output = output.copy(selection = it) },
|
|
||||||
formatterSymbols = uiState.formatterSymbols,
|
formatterSymbols = uiState.formatterSymbols,
|
||||||
textColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(0.6f),
|
input = uiState.input,
|
||||||
readOnly = true,
|
deleteSymbol = deleteSymbol,
|
||||||
|
addSymbol = addSymbol,
|
||||||
|
onCursorChange = onCursorChange,
|
||||||
|
output = uiState.output
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
val label = uiState.output.label?.let { stringResource(it) } ?: ""
|
|
||||||
|
|
||||||
UnformattedTextField(
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 8.dp),
|
|
||||||
value = TextFieldValue(label),
|
|
||||||
minRatio = 1f,
|
|
||||||
onCursorChange = {},
|
|
||||||
textColor = MaterialTheme.colorScheme.error,
|
|
||||||
readOnly = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// Handle
|
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.background(
|
|
||||||
MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
RoundedCornerShape(100)
|
|
||||||
)
|
|
||||||
.sizeIn(24.dp, 4.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keyboard
|
|
||||||
CalculatorKeyboard(
|
CalculatorKeyboard(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.semantics { testTag = "ready" }
|
.semantics { testTag = "ready" }
|
||||||
.offset(y = dragDp + textBoxHeight)
|
.offset(y = historyListHeight + textBoxHeight)
|
||||||
.height(keyboardHeight)
|
.height(keyboardHeight)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||||
|
@ -18,45 +18,4 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.feature.calculator
|
package com.sadellie.unitto.feature.calculator
|
||||||
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.foundation.gestures.AnchoredDraggableState
|
|
||||||
import androidx.compose.foundation.gestures.DraggableAnchors
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
|
|
||||||
internal enum class DragState { CLOSED, SMALL, OPEN }
|
internal enum class DragState { CLOSED, SMALL, OPEN }
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun rememberDragState(
|
|
||||||
initialValue: DragState = DragState.CLOSED,
|
|
||||||
historyItem: Dp,
|
|
||||||
max: Dp,
|
|
||||||
enablePartialView: Boolean,
|
|
||||||
): AnchoredDraggableState<DragState> {
|
|
||||||
val historyItemHeight = with(LocalDensity.current) { historyItem.toPx() }
|
|
||||||
val maxHeight = with(LocalDensity.current) { max.toPx() }
|
|
||||||
val anchors: DraggableAnchors<DragState> = if (enablePartialView) {
|
|
||||||
DraggableAnchors {
|
|
||||||
DragState.CLOSED at 0f
|
|
||||||
DragState.SMALL at historyItemHeight
|
|
||||||
DragState.OPEN at maxHeight
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DraggableAnchors {
|
|
||||||
DragState.CLOSED at 0f
|
|
||||||
DragState.OPEN at maxHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return remember(historyItem, enablePartialView) {
|
|
||||||
AnchoredDraggableState(
|
|
||||||
initialValue = initialValue,
|
|
||||||
anchors = anchors,
|
|
||||||
positionalThreshold = { 0f },
|
|
||||||
velocityThreshold = { 0f },
|
|
||||||
animationSpec = tween()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.Arrangement
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
@ -47,9 +48,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.onPlaced
|
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.platform.LocalTextInputService
|
import androidx.compose.ui.platform.LocalTextInputService
|
||||||
import androidx.compose.ui.platform.LocalTextToolbar
|
import androidx.compose.ui.platform.LocalTextToolbar
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
@ -58,7 +57,6 @@ import androidx.compose.ui.text.TextRange
|
|||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.sadellie.unitto.core.base.R
|
import com.sadellie.unitto.core.base.R
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer
|
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer
|
||||||
@ -75,14 +73,12 @@ import java.util.Locale
|
|||||||
internal fun HistoryList(
|
internal fun HistoryList(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
historyItems: List<HistoryItem>,
|
historyItems: List<HistoryItem>,
|
||||||
heightCallback: (Dp) -> Unit,
|
|
||||||
formatterSymbols: FormatterSymbols,
|
formatterSymbols: FormatterSymbols,
|
||||||
addTokens: (String) -> Unit,
|
addTokens: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
if (historyItems.isEmpty()) {
|
if (historyItems.isEmpty()) {
|
||||||
HistoryListPlaceholder(
|
HistoryListPlaceholder(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
heightCallback = heightCallback
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
HistoryListContent(
|
HistoryListContent(
|
||||||
@ -90,7 +86,6 @@ internal fun HistoryList(
|
|||||||
historyItems = historyItems,
|
historyItems = historyItems,
|
||||||
addTokens = addTokens,
|
addTokens = addTokens,
|
||||||
formatterSymbols = formatterSymbols,
|
formatterSymbols = formatterSymbols,
|
||||||
heightCallback = heightCallback
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,19 +93,14 @@ internal fun HistoryList(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun HistoryListPlaceholder(
|
private fun HistoryListPlaceholder(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
heightCallback: (Dp) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.wrapContentHeight(unbounded = true),
|
modifier = modifier.wrapContentHeight(unbounded = true),
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier.height(HistoryItemHeight),
|
||||||
.onPlaced { heightCallback(with(density) { it.size.height.toDp() }) }
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(vertical = 32.dp),
|
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
@ -126,12 +116,8 @@ private fun HistoryListContent(
|
|||||||
historyItems: List<HistoryItem>,
|
historyItems: List<HistoryItem>,
|
||||||
addTokens: (String) -> Unit,
|
addTokens: (String) -> Unit,
|
||||||
formatterSymbols: FormatterSymbols,
|
formatterSymbols: FormatterSymbols,
|
||||||
heightCallback: (Dp) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
|
||||||
val state = rememberLazyListState()
|
val state = rememberLazyListState()
|
||||||
val firstItem by remember(historyItems) { mutableStateOf(historyItems.first()) }
|
|
||||||
val restOfTheItems by remember(firstItem) { mutableStateOf(historyItems.drop(1)) }
|
|
||||||
|
|
||||||
LaunchedEffect(historyItems) { state.scrollToItem(0) }
|
LaunchedEffect(historyItems) { state.scrollToItem(0) }
|
||||||
|
|
||||||
@ -141,19 +127,8 @@ private fun HistoryListContent(
|
|||||||
reverseLayout = true,
|
reverseLayout = true,
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Bottom)
|
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Bottom)
|
||||||
) {
|
) {
|
||||||
// We do this so that callback for items height is called only once
|
items(historyItems, { it.id }) { historyItem ->
|
||||||
item(firstItem.id) {
|
|
||||||
HistoryListItem(
|
HistoryListItem(
|
||||||
modifier = Modifier.onPlaced { heightCallback(with(density) { it.size.height.toDp() }) },
|
|
||||||
historyItem = historyItems.first(),
|
|
||||||
formatterSymbols = formatterSymbols,
|
|
||||||
addTokens = addTokens,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
items(restOfTheItems, { it.id }) { historyItem ->
|
|
||||||
HistoryListItem(
|
|
||||||
modifier = Modifier,
|
|
||||||
historyItem = historyItem,
|
historyItem = historyItem,
|
||||||
formatterSymbols = formatterSymbols,
|
formatterSymbols = formatterSymbols,
|
||||||
addTokens = addTokens,
|
addTokens = addTokens,
|
||||||
@ -193,7 +168,10 @@ private fun HistoryListItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(modifier = modifier) {
|
Column(
|
||||||
|
modifier = modifier.height(HistoryItemHeight),
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalTextInputService provides null,
|
LocalTextInputService provides null,
|
||||||
LocalTextToolbar provides UnittoTextToolbar(
|
LocalTextToolbar provides UnittoTextToolbar(
|
||||||
@ -246,6 +224,8 @@ private fun HistoryListItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val HistoryItemHeight = 92.dp
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun PreviewHistoryList() {
|
private fun PreviewHistoryList() {
|
||||||
@ -275,7 +255,6 @@ private fun PreviewHistoryList() {
|
|||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
historyItems = historyItems,
|
historyItems = historyItems,
|
||||||
formatterSymbols = FormatterSymbols.Spaces,
|
formatterSymbols = FormatterSymbols.Spaces,
|
||||||
heightCallback = {},
|
|
||||||
addTokens = {}
|
addTokens = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.feature.calculator.components
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.semantics.testTag
|
||||||
|
import androidx.compose.ui.text.TextRange
|
||||||
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField
|
||||||
|
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
||||||
|
import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField
|
||||||
|
import com.sadellie.unitto.feature.calculator.CalculationResult
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TextBox(
|
||||||
|
modifier: Modifier,
|
||||||
|
formatterSymbols: FormatterSymbols,
|
||||||
|
input: TextFieldValue,
|
||||||
|
deleteSymbol: () -> Unit,
|
||||||
|
addSymbol: (String) -> Unit,
|
||||||
|
onCursorChange: (TextRange) -> Unit,
|
||||||
|
output: CalculationResult,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.semantics { testTag = "inputBox" }
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
RoundedCornerShape(
|
||||||
|
topStartPercent = 0, topEndPercent = 0,
|
||||||
|
bottomStartPercent = 20, bottomEndPercent = 20
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.padding(top = 12.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
ExpressionTextField(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(2f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
value = input,
|
||||||
|
minRatio = 0.5f,
|
||||||
|
cutCallback = deleteSymbol,
|
||||||
|
pasteCallback = addSymbol,
|
||||||
|
onCursorChange = onCursorChange,
|
||||||
|
formatterSymbols = formatterSymbols
|
||||||
|
)
|
||||||
|
if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||||
|
when (output) {
|
||||||
|
is CalculationResult.Default -> {
|
||||||
|
var outputTF by remember(output) {
|
||||||
|
mutableStateOf(TextFieldValue(output.text))
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionTextField(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
value = outputTF,
|
||||||
|
minRatio = 1f,
|
||||||
|
onCursorChange = { outputTF = outputTF.copy(selection = it) },
|
||||||
|
formatterSymbols = formatterSymbols,
|
||||||
|
textColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(0.6f),
|
||||||
|
readOnly = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
val label = output.label?.let { stringResource(it) } ?: ""
|
||||||
|
|
||||||
|
UnformattedTextField(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp),
|
||||||
|
value = TextFieldValue(label),
|
||||||
|
minRatio = 1f,
|
||||||
|
onCursorChange = {},
|
||||||
|
textColor = MaterialTheme.colorScheme.error,
|
||||||
|
readOnly = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// Handle
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
RoundedCornerShape(100)
|
||||||
|
)
|
||||||
|
.sizeIn(24.dp, 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user