Skip to content

Commit

Permalink
Autocomplete (#65)
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
jherkenhoff authored Sep 26, 2024
1 parent 54db549 commit 3fba63f
Show file tree
Hide file tree
Showing 21 changed files with 286 additions and 170 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -13,32 +15,60 @@ data class AutocompleteItem(
val abbreviation: String
)

enum class AutocompleteContext{
INPUT, CONVERSION
}

data class AutocompleteResult(
val success: Boolean,
val items: List<AutocompleteItem> = 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<AutocompleteItem> {

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
)
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<AutocompleteItem>,
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 }
)
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
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
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(
Expand All @@ -35,7 +24,7 @@ fun ColumnScope.CalculationListItem(
) {

Text(
messageFormatter(parsed),
mathExpressionFormatted(parsed),
style = MaterialTheme.typography.bodyMedium
)
Box(
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 3fba63f

Please sign in to comment.