From 3fba63f15cafc635e60ff27034f4692e5271cf38 Mon Sep 17 00:00:00 2001 From: Jost Herkenhoff <22686781+jherkenhoff@users.noreply.github.com> Date: Thu, 26 Sep 2024 07:42:27 +0200 Subject: [PATCH] Autocomplete (#65) * Stop soft keyboard being forced open * First implementation of autocomplete list card * Made viewmodel scope more private * Restructure package structure * Moved autocompletion logic back into usecase * Larger font on quick keys. Autocomplete list shows most relevant item on the buttom (easier to reach) --- .../qalculate/domain/AutocompleteUseCase.kt | 50 ++++++-- .../jherkenhoff/qalculate/ui/QalculateApp.kt | 4 +- .../qalculate/ui/QalculateNavGraph.kt | 4 + .../ui/{ => calculator}/AltKeyboard.kt | 2 +- .../ui/{ => calculator}/AutoSizeText.kt | 8 +- .../ui/{ => calculator}/AutocompleteChips.kt | 2 +- .../ui/calculator/AutocompleteList.kt | 102 ++++++++++++++++ .../ui/{ => calculator}/CalculationDivider.kt | 3 +- .../ui/{ => calculator}/CalculationList.kt | 2 +- .../{ => calculator}/CalculationListItem.kt | 17 +-- .../ui/{ => calculator}/CalculatorScreen.kt | 89 ++++++++++---- .../{ => calculator}/CalculatorViewModel.kt | 115 ++++++------------ .../qalculate/ui/{ => calculator}/InputBar.kt | 27 ++-- .../ui/{ => calculator}/NavigationDrawer.kt | 2 +- .../ui/{ => calculator}/QuickKeys.kt | 3 +- .../ui/{ => calculator}/QuickKeysPanel.kt | 14 ++- .../ui/{ => calculator}/SupplementaryBar.kt | 2 +- .../qalculate/ui/{ => calculator}/density.kt | 2 +- .../mathExpressionFormatted.kt} | 4 +- .../qalculate/ui/{ => units}/UnitsScreen.kt | 2 +- .../ui/{ => units}/UnitsViewModel.kt | 2 +- 21 files changed, 286 insertions(+), 170 deletions(-) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/AltKeyboard.kt (99%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/AutoSizeText.kt (98%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/AutocompleteChips.kt (98%) create mode 100644 app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutocompleteList.kt rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/CalculationDivider.kt (94%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/CalculationList.kt (99%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/CalculationListItem.kt (74%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/CalculatorScreen.kt (69%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/CalculatorViewModel.kt (52%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/InputBar.kt (90%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/NavigationDrawer.kt (99%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/QuickKeys.kt (97%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/QuickKeysPanel.kt (78%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/SupplementaryBar.kt (97%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => calculator}/density.kt (99%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{messageFormatter.kt => calculator/mathExpressionFormatted.kt} (95%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => units}/UnitsScreen.kt (99%) rename app/src/main/java/com/jherkenhoff/qalculate/ui/{ => units}/UnitsViewModel.kt (96%) diff --git a/app/src/main/java/com/jherkenhoff/qalculate/domain/AutocompleteUseCase.kt b/app/src/main/java/com/jherkenhoff/qalculate/domain/AutocompleteUseCase.kt index b050817..492f0e2 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/domain/AutocompleteUseCase.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/domain/AutocompleteUseCase.kt @@ -1,8 +1,10 @@ package com.jherkenhoff.qalculate.domain import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.input.getTextAfterSelection import androidx.compose.ui.text.input.getTextBeforeSelection import com.jherkenhoff.libqalculate.Calculator +import com.jherkenhoff.libqalculate.Unit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import javax.inject.Inject @@ -13,32 +15,60 @@ data class AutocompleteItem( val abbreviation: String ) +enum class AutocompleteContext{ + INPUT, CONVERSION +} + +data class AutocompleteResult( + val success: Boolean, + val items: List = emptyList(), + val relevantText: String = "", + val textBefore: String = "", + val textAfter: String = "", + val context: AutocompleteContext = AutocompleteContext.INPUT +) + class AutocompleteUseCase @Inject constructor( private val calc: Calculator ) { - suspend operator fun invoke(input: TextFieldValue): List { + + private suspend fun formatUnit(unit: Unit): String { + + return unit.title() + } + suspend operator fun invoke(input: TextFieldValue): AutocompleteResult { if (input.selection.length > 0) { - return listOf() + return AutocompleteResult(success = false) } return withContext(Dispatchers.Default) { - var currentString = input.getTextBeforeSelection(input.text.length).toString() + val textBefore = input.getTextBeforeSelection(input.text.length).toString() + val textAfter = input.getTextAfterSelection(input.text.length).toString() val pattern = Regex("([a-zA-Z_]+$)") - val match = pattern.find(currentString) ?: return@withContext listOf() + val match = pattern.find(textBefore) ?: return@withContext AutocompleteResult(success = false) + + val textBeforeWithoutRelevant = pattern.split(textBefore).first() - currentString = match.value + val relevantText = match.value - val unitList = calc.units.filter { - it.title().lowercase().startsWith(currentString.lowercase()) - || it.name().lowercase().startsWith(currentString.lowercase()) + val autocompleteItems = calc.units.filter { + it.title().lowercase().startsWith(relevantText.lowercase()) + || it.name().lowercase().startsWith(relevantText.lowercase()) }.map { - AutocompleteItem(it.title(), it.name(), it.abbreviation()) + AutocompleteItem(formatUnit(it), it.name(), it.abbreviation()) } - return@withContext unitList + return@withContext AutocompleteResult( + success = true, + items = autocompleteItems, + relevantText = relevantText, + textBefore = textBeforeWithoutRelevant, + textAfter = textAfter, + context = AutocompleteContext.INPUT + ) } } diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/QalculateApp.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/QalculateApp.kt index 85616bd..b79e7d6 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/QalculateApp.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/QalculateApp.kt @@ -13,6 +13,7 @@ import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController +import com.jherkenhoff.qalculate.ui.calculator.NavigationDrawer import com.jherkenhoff.qalculate.ui.theme.QalculateTheme import kotlinx.coroutines.launch @@ -51,7 +52,8 @@ fun QalculateApp() { onCalculatorClick = { navController.navigate(NavDestinations.Calculator); coroutineScope.launch { drawerState.close() } }, onUnitsClick = { navController.navigate(NavDestinations.Units); coroutineScope.launch { drawerState.close() } }, onAboutClick = { navController.navigate(NavDestinations.About); coroutineScope.launch { drawerState.close() } }, - )} + ) + } ) { QalculateNavGraph( navController = navController, diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/QalculateNavGraph.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/QalculateNavGraph.kt index 409cc4e..d3b9c27 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/QalculateNavGraph.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/QalculateNavGraph.kt @@ -8,6 +8,10 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.dialog import androidx.navigation.compose.rememberNavController +import com.jherkenhoff.qalculate.ui.calculator.CalculatorScreen +import com.jherkenhoff.qalculate.ui.calculator.CalculatorViewModel +import com.jherkenhoff.qalculate.ui.units.UnitsScreen +import com.jherkenhoff.qalculate.ui.units.UnitsViewModel import kotlinx.serialization.Serializable @Serializable diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/AltKeyboard.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AltKeyboard.kt similarity index 99% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/AltKeyboard.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AltKeyboard.kt index d783e9c..e258fa3 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/AltKeyboard.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AltKeyboard.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/AutoSizeText.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutoSizeText.kt similarity index 98% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/AutoSizeText.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutoSizeText.kt index c2eded9..437cd3a 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/AutoSizeText.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutoSizeText.kt @@ -1,5 +1,5 @@ // V6.3 - 01 Aug 2024 -package com.inidamleader.ovtracker.util.compose +package com.jherkenhoff.qalculate.ui.calculator import android.util.Log import androidx.compose.foundation.layout.BoxWithConstraints @@ -43,11 +43,7 @@ import androidx.compose.ui.unit.isSpecified import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastAll import androidx.compose.ui.util.fastFilter -import com.inidamleader.ovtracker.util.compose.SuggestedFontSizesStatus.Companion.validSuggestedFontSizes -import com.inidamleader.ovtracker.util.compose.geometry.dpSizeRoundToIntSize -import com.inidamleader.ovtracker.util.compose.geometry.intPxToSp -import com.inidamleader.ovtracker.util.compose.geometry.spRoundToPx -import com.inidamleader.ovtracker.util.compose.geometry.spToIntPx +import com.jherkenhoff.qalculate.ui.calculator.SuggestedFontSizesStatus.Companion.validSuggestedFontSizes import kotlin.math.min private const val TAG = "AutoSizeText" diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/AutocompleteChips.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutocompleteChips.kt similarity index 98% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/AutocompleteChips.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutocompleteChips.kt index cadd816..99cf936 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/AutocompleteChips.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutocompleteChips.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.ContextualFlowRow diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutocompleteList.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutocompleteList.kt new file mode 100644 index 0000000..df92b86 --- /dev/null +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/AutocompleteList.kt @@ -0,0 +1,102 @@ +package com.jherkenhoff.qalculate.ui.calculator + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SwipeToDismissBox +import androidx.compose.material3.SwipeToDismissBoxValue +import androidx.compose.material3.Text +import androidx.compose.material3.rememberSwipeToDismissBoxState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.jherkenhoff.qalculate.domain.AutocompleteItem + + +@Composable +fun AutocompleteList( + autocompleteText: () -> String, + entries: () -> List, + modifier: Modifier = Modifier, + onEntryClick: (String) -> Unit = {}, + onDismiss: () -> Unit = {} +) { + val dismissState = rememberSwipeToDismissBoxState() + + when (dismissState.currentValue) { + SwipeToDismissBoxValue.EndToStart -> { + + } + + else -> { } + } + + SwipeToDismissBox( + state = dismissState, + backgroundContent = {}, + ) { + Card( + modifier = modifier, + elevation = CardDefaults.elevatedCardElevation(defaultElevation = 6.dp) + ) { + Text( + buildAnnotatedString { + append("Suggestions for ") + + withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { + append(autocompleteText()) + } + }, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.8f), + textAlign = TextAlign.Center, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp).fillMaxWidth() + ) + HorizontalDivider() + LazyColumn( + reverseLayout = true + ) { + for (entry in entries()) { + item { + ListItem( + headlineContent = { Text(entry.title) }, + trailingContent = { Text(entry.name) }, + modifier = Modifier.clickable { onEntryClick(entry.abbreviation) }, + colors = ListItemDefaults.colors(containerColor = Color.Transparent) + ) + } + } + } + } + } +} + + +@Preview +@Composable +private fun DefaultPreview() { + val list = listOf( + AutocompleteItem("Tesla", "M", "T"), + AutocompleteItem("Thomson cross section", "M", "T"), + AutocompleteItem("Terabyte", "M", "T"), + AutocompleteItem("Planck temperature", "M", "T"), + ) + AutocompleteList( + { "t" }, + { list } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationDivider.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationDivider.kt similarity index 94% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationDivider.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationDivider.kt index 39ce7b9..04d383a 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationDivider.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationDivider.kt @@ -1,11 +1,10 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.foundation.background import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationList.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationList.kt similarity index 99% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationList.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationList.kt index 3f186ee..82ef933 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationList.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationList.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationListItem.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationListItem.kt similarity index 74% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationListItem.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationListItem.kt index 0d515b6..df2e70f 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculationListItem.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculationListItem.kt @@ -1,12 +1,9 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.BoxWithConstraintsScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme @@ -14,18 +11,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.text.ParagraphIntrinsics -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.createFontFamilyResolver -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.inidamleader.ovtracker.util.compose.AutoSizeText @Composable fun ColumnScope.CalculationListItem( @@ -35,7 +24,7 @@ fun ColumnScope.CalculationListItem( ) { Text( - messageFormatter(parsed), + mathExpressionFormatted(parsed), style = MaterialTheme.typography.bodyMedium ) Box( @@ -45,7 +34,7 @@ fun ColumnScope.CalculationListItem( .defaultMinSize(minHeight = 80.dp) ) { AutoSizeText( - text = messageFormatter(result), + text = mathExpressionFormatted(result), modifier = Modifier.fillMaxWidth(), alignment = Alignment.CenterEnd, style = MaterialTheme.typography.displayMedium, diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculatorScreen.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculatorScreen.kt similarity index 69% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/CalculatorScreen.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculatorScreen.kt index 8b686b9..db24688 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculatorScreen.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculatorScreen.kt @@ -1,7 +1,9 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -30,6 +32,7 @@ import com.jherkenhoff.qalculate.domain.AutocompleteItem import kotlinx.coroutines.runBlocking import java.time.LocalDateTime + @Composable fun CalculatorScreen( viewModel: CalculatorViewModel = viewModel(), @@ -38,18 +41,19 @@ fun CalculatorScreen( val calculationHistory = viewModel.calculationHistory.collectAsState() CalculatorScreenContent( - input = { viewModel.inputTextFieldValue.value }, + input = { viewModel.inputTextFieldValue }, onInputChanged = viewModel::updateInput, onQuickKeyPressed = viewModel::insertText, onDelKeyPressed = viewModel::removeLastChar, onACKeyPressed = viewModel::clearAll, calculationHistory = calculationHistory.value, - parsedString = { viewModel.parsedString.value }, - resultString = { viewModel.resultString.value }, + parsedString = { viewModel.parsedString }, + resultString = { viewModel.resultString }, onCalculationSubmit = viewModel::submitCalculation, onAutocompleteClick = viewModel::acceptAutocomplete, openDrawer = openDrawer, - autocompleteList = { viewModel.autocompleteList.value } + autocompleteList = { viewModel.autocompleteResult.items }, + autocompleteText = { viewModel.autocompleteResult.relevantText } ) } @@ -65,6 +69,7 @@ fun CalculatorScreenContent( parsedString: () -> String, resultString: () -> String, onCalculationSubmit: () -> Unit = {}, + autocompleteText: () -> String = {""}, autocompleteList: () -> List = { emptyList() }, onAutocompleteClick: (String) -> Unit = {}, openDrawer: () -> Unit = { } @@ -118,23 +123,34 @@ fun CalculatorScreenContent( resultString, bottomSpacing = 64.dp ) - InputBar( - textFieldValue = input, - onValueChange = onInputChanged, - onFocused = {}, - focusState = false, - onSubmit = { onCalculationSubmit() }, - altKeyboardEnabled = isAltKeyboardOpen, - onKeyboardToggleClick = { - isAltKeyboardOpen = !isAltKeyboardOpen - runBlocking { - screenSettingsRepository.saveAltKeyboardOpen(isAltKeyboardOpen) + Column(modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 16.dp) + ) { + + AnimatedVisibility(autocompleteList().isNotEmpty()) { + AutocompleteList( + autocompleteText, + entries = autocompleteList, + onEntryClick = onAutocompleteClick, + modifier = Modifier + .padding(vertical = 16.dp) + .heightIn(max = 300.dp) + ) + } + InputBar( + textFieldValue = input, + onValueChange = onInputChanged, + onSubmit = { onCalculationSubmit() }, + altKeyboardEnabled = isAltKeyboardOpen, + onKeyboardToggleClick = { + isAltKeyboardOpen = !isAltKeyboardOpen + runBlocking { + screenSettingsRepository.saveAltKeyboardOpen(isAltKeyboardOpen) + } } - }, - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 16.dp) - ) + ) + } } if (isAltKeyboardOpen) { @@ -147,7 +163,7 @@ fun CalculatorScreenContent( } else { SupplementaryBar( onKey = onQuickKeyPressed, - autocompleteItems = autocompleteList, + autocompleteItems = { emptyList() }, // autocompleteList onAutocompleteClick = onAutocompleteClick ) } @@ -182,7 +198,7 @@ private val testCalculationHistory = listOf( ) ) -@Preview(showBackground = true) +@Preview @Composable private fun DefaultPreview() { CalculatorScreenContent( @@ -197,3 +213,30 @@ private fun DefaultPreview() { onCalculationSubmit = {} ) } + + +@Preview +@Composable +private fun AutocompletePreview() { + + val list = listOf( + AutocompleteItem("Tesla", "M", "T"), + AutocompleteItem("Thomson cross section", "M", "T"), + AutocompleteItem("Terabyte", "M", "T"), + AutocompleteItem("Planck temperature", "M", "T"), + ) + + CalculatorScreenContent( + input = { TextFieldValue("1*t") }, + onInputChanged = {}, + onQuickKeyPressed = {}, + onDelKeyPressed = {}, + onACKeyPressed = {}, + calculationHistory = testCalculationHistory, + parsedString = { "" }, + resultString = { "" }, + onCalculationSubmit = {}, + autocompleteText = { "t" }, + autocompleteList = { list } + ) +} diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculatorViewModel.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculatorViewModel.kt similarity index 52% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/CalculatorViewModel.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculatorViewModel.kt index 727baa3..f1d2a6f 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/CalculatorViewModel.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/CalculatorViewModel.kt @@ -1,7 +1,8 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator -import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.getSelectedText @@ -15,14 +16,11 @@ import com.jherkenhoff.libqalculate.PrintOptions import com.jherkenhoff.libqalculate.libqalculateConstants.TAG_TYPE_HTML import com.jherkenhoff.qalculate.data.CalculationHistoryRepository import com.jherkenhoff.qalculate.data.model.CalculationHistoryItem -import com.jherkenhoff.qalculate.domain.AutocompleteItem +import com.jherkenhoff.qalculate.domain.AutocompleteResult import com.jherkenhoff.qalculate.domain.AutocompleteUseCase import com.jherkenhoff.qalculate.domain.CalculateUseCase import com.jherkenhoff.qalculate.domain.ParseUseCase import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -46,21 +44,25 @@ class CalculatorViewModel @Inject constructor( emptyList() ) - val parsedString : MutableState = mutableStateOf("0") - val resultString : MutableState = mutableStateOf("0") + var parsedString by mutableStateOf("0") + private set - val inputTextFieldValue : MutableState = mutableStateOf(TextFieldValue("")) + var resultString by mutableStateOf("0") + private set - private val _autocompleteList = mutableStateOf>(emptyList()) - val autocompleteList = _autocompleteList + var autocompleteResult by mutableStateOf(AutocompleteResult(success = false)) + private set + + var inputTextFieldValue by mutableStateOf(TextFieldValue("")) + private set fun submitCalculation() { calculationHistoryRepository.appendCalculation( CalculationHistoryItem( LocalDateTime.now(), - inputTextFieldValue.value.text, - parsedString.value, - resultString.value + inputTextFieldValue.text, + parsedString, + resultString ) ) @@ -68,67 +70,32 @@ class CalculatorViewModel @Inject constructor( } fun updateInput(input: TextFieldValue) { - inputTextFieldValue.value = input - updateAutocompleteList() + inputTextFieldValue = input + handleAutocomplete() recalculate() } - private fun updateAutocompleteList() { - - // https://gist.github.com/Terenfear/a84863be501d3399889455f391eeefe5 - fun throttleLatest( - intervalMs: Long = 300L, - coroutineScope: CoroutineScope, - destinationFunction: (T) -> Unit - ): (T) -> Unit { - var throttleJob: Job? = null - var latestParam: T - return { param: T -> - latestParam = param - if (throttleJob?.isCompleted != false) { - throttleJob = coroutineScope.launch { - delay(intervalMs) - destinationFunction(latestParam) - } - } - } - } - - //val throttledUpdate = throttleLatest(200L, viewModelScope) { _autocompleteList.value = autocompleteUseCase(it) } - - //throttledUpdate(inputTextFieldValue.value) + private fun handleAutocomplete() { viewModelScope.launch { - _autocompleteList.value = autocompleteUseCase(inputTextFieldValue.value) + autocompleteResult = autocompleteUseCase(inputTextFieldValue) } } fun acceptAutocomplete(autocompleteString: String) { - var textBefore = inputTextFieldValue.value.getTextBeforeSelection(inputTextFieldValue.value.text.length).toString() - var textAfter = inputTextFieldValue.value.getTextAfterSelection(inputTextFieldValue.value.text.length).toString() - - - val pattern = Regex("([a-zA-Z_]+$)") - - val match = pattern.split(textBefore).first() - - val newCursorPosition = match.length + autocompleteString.length - val newText = "$match$autocompleteString$textAfter" - updateInput(TextFieldValue( - text = newText, - selection = TextRange(newCursorPosition) + text = autocompleteResult.textBefore + autocompleteString + autocompleteResult.textAfter, + selection = TextRange(autocompleteResult.textBefore.length + autocompleteString.length) )) } fun insertText(quickKeyText: String) { - val maxChars = inputTextFieldValue.value.text.length - val textBeforeSelection = inputTextFieldValue.value.getTextBeforeSelection(maxChars) - val textAfterSelection = inputTextFieldValue.value.getTextAfterSelection(maxChars) + val maxChars = inputTextFieldValue.text.length + val textBeforeSelection = inputTextFieldValue.getTextBeforeSelection(maxChars) + val textAfterSelection = inputTextFieldValue.getTextAfterSelection(maxChars) val newText = "$textBeforeSelection$quickKeyText$textAfterSelection" val newCursorPosition = textBeforeSelection.length + quickKeyText.length - updateInput(TextFieldValue( text = newText, selection = TextRange(newCursorPosition) @@ -136,10 +103,10 @@ class CalculatorViewModel @Inject constructor( } fun removeLastChar() { - val maxChars = inputTextFieldValue.value.text.length - val textBeforeSelection = inputTextFieldValue.value.getTextBeforeSelection(maxChars) - val textAfterSelection = inputTextFieldValue.value.getTextAfterSelection(maxChars) - val selectedText = inputTextFieldValue.value.getSelectedText() + val maxChars = inputTextFieldValue.text.length + val textBeforeSelection = inputTextFieldValue.getTextBeforeSelection(maxChars) + val textAfterSelection = inputTextFieldValue.getTextAfterSelection(maxChars) + val selectedText = inputTextFieldValue.getSelectedText() var newText: String var newCursorPosition: Int @@ -173,34 +140,22 @@ class CalculatorViewModel @Inject constructor( viewModelScope.launch { - val parsedMathStructure = parseUseCase(inputTextFieldValue.value.text) + val parsedMathStructure = parseUseCase(inputTextFieldValue.text) - val calculatedMathStructure = calculateUseCase(inputTextFieldValue.value.text) + val calculatedMathStructure = calculateUseCase(inputTextFieldValue.text) val parsedPrintOptions = PrintOptions() parsedPrintOptions.place_units_separately = false parsedPrintOptions.preserve_format = true parsedPrintOptions.use_unicode_signs = 1 - parsedString.value = calculator.print(parsedMathStructure, 2000, parsedPrintOptions, true, 1, TAG_TYPE_HTML) + parsedPrintOptions.abbreviate_names = false + parsedPrintOptions.short_multiplication = false + parsedString = calculator.print(parsedMathStructure, 2000, parsedPrintOptions, true, 1, TAG_TYPE_HTML) val resultPo = PrintOptions() resultPo.interval_display = IntervalDisplay.INTERVAL_DISPLAY_SIGNIFICANT_DIGITS resultPo.use_unicode_signs = 1 - resultString.value = calculator.print(calculatedMathStructure, 2000, resultPo, true, 1, TAG_TYPE_HTML) + resultString = calculator.print(calculatedMathStructure, 2000, resultPo, true, 1, TAG_TYPE_HTML) } } -} - -private fun TextFieldValue.addText(newString: String): TextFieldValue { - val newText = this.text.replaceRange( - this.selection.start, - this.selection.end, - newString - ) - val newSelection = TextRange( - start = this.selection.end+newText.length, - end = this.selection.end+newText.length - ) - - return this.copy(text = newText, selection = newSelection) } \ No newline at end of file diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/InputBar.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/InputBar.kt similarity index 90% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/InputBar.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/InputBar.kt index 12aad55..b6212f2 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/InputBar.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/InputBar.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField @@ -51,8 +52,6 @@ import kotlinx.coroutines.awaitCancellation fun InputBar( textFieldValue: () -> TextFieldValue, onValueChange: (TextFieldValue) -> Unit, - onFocused: (Boolean) -> Unit, - focusState: Boolean, onSubmit: (String) -> Unit, altKeyboardEnabled: Boolean, modifier: Modifier = Modifier, @@ -71,6 +70,7 @@ fun InputBar( modifier = modifier .fillMaxWidth() .height(48.dp) + .heightIn(48.dp, 48.dp) ) { val placeholdeVisible by remember { derivedStateOf { textFieldValue().text.isEmpty() } } @@ -109,12 +109,7 @@ fun InputBar( modifier = Modifier .fillMaxWidth() .focusRequester(focusRequester) - .onFocusChanged { state -> - if (lastFocusState != state.isFocused) { - onFocused(state.isFocused) - } - lastFocusState = state.isFocused - }, + .onFocusChanged { state -> lastFocusState = state.isFocused }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Uri, imeAction = ImeAction.Go, @@ -129,7 +124,7 @@ fun InputBar( ) } - if (placeholdeVisible && !focusState) { + if (placeholdeVisible && !lastFocusState) { Text( text = stringResource(R.string.textfield_hint), style = MaterialTheme.typography.bodyLarge.copy(color = MaterialTheme.colorScheme.onSecondaryContainer), @@ -152,9 +147,7 @@ private fun PlaceholderPreview() { {TextFieldValue("")}, {}, {}, - false, - {}, - false) + false,) } @Preview(showBackground = true) @@ -164,9 +157,7 @@ private fun WithInputPreview() { {TextFieldValue("1km + 1m")}, {}, {}, - false, - {}, - false) + false,) } @Preview(showBackground = true) @@ -176,7 +167,5 @@ private fun WithInputAltKeyboardPreview() { {TextFieldValue("1km + 1m")}, {}, {}, - false, - {}, - true) + false,) } \ No newline at end of file diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/NavigationDrawer.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/NavigationDrawer.kt similarity index 99% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/NavigationDrawer.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/NavigationDrawer.kt index ba3b7b8..5eff206 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/NavigationDrawer.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/NavigationDrawer.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/QuickKeys.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/QuickKeys.kt similarity index 97% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/QuickKeys.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/QuickKeys.kt index f81c422..bfbcd23 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/QuickKeys.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/QuickKeys.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.ExperimentalFoundationApi @@ -21,7 +21,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/QuickKeysPanel.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/QuickKeysPanel.kt similarity index 78% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/QuickKeysPanel.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/QuickKeysPanel.kt index bb7e9d5..4d116c3 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/QuickKeysPanel.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/QuickKeysPanel.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -39,7 +39,11 @@ fun QuickKeysPanel( modifier = Modifier .weight(1f) ) { - Text(text = i, color = MaterialTheme.colorScheme.onSurfaceVariant) + Text( + text = i, + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyLarge + ) } } } @@ -55,7 +59,11 @@ fun QuickKeysPanel( modifier = Modifier .weight(1f) ) { - Text(text = i.toString(), color = MaterialTheme.colorScheme.onSurfaceVariant) + Text( + text = i.toString(), + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyLarge + ) } } } diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/SupplementaryBar.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/SupplementaryBar.kt similarity index 97% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/SupplementaryBar.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/SupplementaryBar.kt index af76cea..8bf8f72 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/SupplementaryBar.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/SupplementaryBar.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.ExperimentalFoundationApi diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/density.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/density.kt similarity index 99% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/density.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/density.kt index cc607da..72e31f2 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/density.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/density.kt @@ -1,4 +1,4 @@ -package com.inidamleader.ovtracker.util.compose.geometry +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.runtime.Composable import androidx.compose.ui.geometry.Offset diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/messageFormatter.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/mathExpressionFormatted.kt similarity index 95% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/messageFormatter.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/mathExpressionFormatted.kt index 43fcedb..ea7b8d5 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/messageFormatter.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/calculator/mathExpressionFormatted.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.calculator import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -9,7 +9,7 @@ import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.BaselineShift @Composable -fun messageFormatter( +fun mathExpressionFormatted( text: String ): AnnotatedString { val tokens = Regex("""<.*?>|([^<]+)?|( )""").findAll(text) diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/UnitsScreen.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/units/UnitsScreen.kt similarity index 99% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/UnitsScreen.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/units/UnitsScreen.kt index 5aa2e00..a59d177 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/UnitsScreen.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/units/UnitsScreen.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.units import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement diff --git a/app/src/main/java/com/jherkenhoff/qalculate/ui/UnitsViewModel.kt b/app/src/main/java/com/jherkenhoff/qalculate/ui/units/UnitsViewModel.kt similarity index 96% rename from app/src/main/java/com/jherkenhoff/qalculate/ui/UnitsViewModel.kt rename to app/src/main/java/com/jherkenhoff/qalculate/ui/units/UnitsViewModel.kt index c4af0bf..2f52126 100644 --- a/app/src/main/java/com/jherkenhoff/qalculate/ui/UnitsViewModel.kt +++ b/app/src/main/java/com/jherkenhoff/qalculate/ui/units/UnitsViewModel.kt @@ -1,4 +1,4 @@ -package com.jherkenhoff.qalculate.ui +package com.jherkenhoff.qalculate.ui.units import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel