Skip to content

Commit

Permalink
Prepare for text2
Browse files Browse the repository at this point in the history
Enabled physical keyboard support (#148), but not tested
  • Loading branch information
sadellie committed Feb 5, 2024
1 parent bf9bf37 commit 5c81a1c
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 189 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,46 @@

package com.sadellie.unitto.core.ui.common.textfield

import android.content.ClipData
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.base.Token

/**
* Copy value to clipboard with fractional symbols.
* Unformatted values are expected. Basically what BigDecimal and Expression parser use.
*
* Example:
* "123456.789" will be copied as "123456,789"
*
* @param value Internal [TextFieldValue] without formatting with [Token.Digit.dot] as fractional.
* @property formatterSymbols Current [FormatterSymbols].
* @property clipboardManager [android.content.ClipboardManager] provided by system.
*/
internal fun ClipboardManager.copyWithFractional(
value: TextFieldValue,
formatterSymbols: FormatterSymbols
) = this.setText(
AnnotatedString(
value.annotatedString
.subSequence(value.selection)
.text
.replace(Token.Digit.dot, formatterSymbols.fractional)
internal class ExpressionClipboardManager(
private val formatterSymbols: FormatterSymbols,
private val clipboardManager: android.content.ClipboardManager
): ClipboardManager {
override fun setText(annotatedString: AnnotatedString) = clipboardManager.setPrimaryClip(
ClipData.newPlainText(
PLAIN_TEXT_LABEL,
annotatedString
.text
.replace(Token.Digit.dot, formatterSymbols.fractional)
)
)
)

internal fun ClipboardManager.copy(value: TextFieldValue) = this.setText(
AnnotatedString(
value.annotatedString
.subSequence(value.selection)
.text
)
)
override fun getText(): AnnotatedString? = clipboardManager.primaryClip?.let { primaryClip ->
if (primaryClip.itemCount > 0) {
val clipText = primaryClip.getItemAt(0)?.text ?:return@let null

clipText
.toString()
.toAnnotatedString()
} else {
null
}
}

override fun hasText() =
clipboardManager.primaryClipDescription?.hasMimeType("text/*") ?: false
}

internal const val PLAIN_TEXT_LABEL = "plain text"

private fun CharSequence.toAnnotatedString(): AnnotatedString = AnnotatedString(this.toString())
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

package com.sadellie.unitto.core.ui.common.textfield

import androidx.compose.foundation.clickable
import android.content.Context
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.BasicTextField
Expand All @@ -32,141 +32,113 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalTextInputService
import androidx.compose.ui.platform.LocalTextToolbar
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit
import com.sadellie.unitto.core.ui.common.autosize.AutoSizeTextStyleBox
import com.sadellie.unitto.core.ui.common.textfield.texttoolbar.UnittoTextToolbar
import com.sadellie.unitto.core.ui.theme.LocalNumberTypography

@Composable
fun ExpressionTextField(
modifier: Modifier,
value: TextFieldValue,
minRatio: Float = 1f,
cutCallback: () -> Unit = {},
pasteCallback: (String) -> Unit = {},
onCursorChange: (TextRange) -> Unit,
onValueChange: (TextFieldValue) -> Unit,
textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
formatterSymbols: FormatterSymbols,
readOnly: Boolean = false,
placeholder: String = "",
) {
val localView = LocalView.current
val clipboardManager = LocalClipboardManager.current
val expressionTransformer = remember(formatterSymbols) { ExpressionTransformer(formatterSymbols) }

fun copyCallback() {
clipboardManager.copyWithFractional(value, formatterSymbols)
onCursorChange(TextRange(value.selection.end))
val context = LocalContext.current
val clipboardManager = remember(formatterSymbols) {
ExpressionClipboardManager(
formatterSymbols = formatterSymbols,
clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE)
as android.content.ClipboardManager
)
}
val expressionTransformer = remember(formatterSymbols) {
ExpressionTransformer(formatterSymbols)
}

val textToolbar: UnittoTextToolbar = if (readOnly) {
UnittoTextToolbar(
view = localView,
copyCallback = ::copyCallback,
)
} else {
UnittoTextToolbar(
view = localView,
copyCallback = ::copyCallback,
pasteCallback = {
pasteCallback(clipboardManager.getText()?.text?.clearAndFilterExpression(formatterSymbols) ?: "")
CompositionLocalProvider(
LocalClipboardManager provides clipboardManager
) {
AutoSizeTextField(
modifier = modifier,
value = value,
onValueChange = {
onValueChange(it.copy(text = it.text.clearAndFilterExpression(formatterSymbols)))
},
cutCallback = {
clipboardManager.copyWithFractional(value, formatterSymbols)
cutCallback()
}
placeholder = placeholder,
readOnly = readOnly,
textStyle = LocalNumberTypography.current.displayLarge.copy(textColor),
visualTransformation = expressionTransformer,
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant),
minRatio = minRatio
)
}
}

@Composable
fun NumberBaseTextField(
modifier: Modifier,
value: TextFieldValue,
minRatio: Float = 1f,
onValueChange: (TextFieldValue) -> Unit,
textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
readOnly: Boolean = false,
placeholder: String = "",
) {
AutoSizeTextField(
modifier = modifier,
value = value,
onValueChange = { onCursorChange(it.selection) },
onValueChange = {
onValueChange(it.copy(text = it.text.clearAndFilterNumberBase()))
},
placeholder = placeholder,
textToolbar = textToolbar,
readOnly = readOnly,
textStyle = LocalNumberTypography.current.displayLarge.copy(textColor),
visualTransformation = expressionTransformer,
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant),
minRatio = minRatio
)
}

@Composable
fun UnformattedTextField(
fun SimpleTextField(
modifier: Modifier,
value: TextFieldValue,
minRatio: Float = 1f,
cutCallback: () -> Unit = {},
pasteCallback: (String) -> Unit = {},
onCursorChange: (TextRange) -> Unit,
onValueChange: (TextFieldValue) -> Unit,
textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
readOnly: Boolean = false,
placeholder: String = "",
) {
val localView = LocalView.current
val clipboardManager = LocalClipboardManager.current
fun copyCallback() {
clipboardManager.copy(value)
onCursorChange(TextRange(value.selection.end))
}

val textToolbar: UnittoTextToolbar = remember(readOnly) {
if (readOnly) {
UnittoTextToolbar(
view = localView,
copyCallback = ::copyCallback,
)
} else {
UnittoTextToolbar(
view = localView,
copyCallback = ::copyCallback,
pasteCallback = {
pasteCallback(clipboardManager.getText()?.text?.clearAndFilterNumberBase() ?: "")
},
cutCallback = {
clipboardManager.copy(value)
cutCallback()
}
)
}
}

AutoSizeTextField(
modifier = modifier,
value = value,
onValueChange = { onCursorChange(it.selection) },
onValueChange = onValueChange,
placeholder = placeholder,
textToolbar = textToolbar,
readOnly = readOnly,
textStyle = LocalNumberTypography.current.displayLarge.copy(color = textColor),
minRatio = minRatio,
textStyle = LocalNumberTypography.current.displayLarge.copy(textColor),
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant),
minRatio = minRatio
)
}

/**
* Based on: https://gist.github.com/inidamleader/b594d35362ebcf3cedf81055df519300
*
* @param placeholder Placeholder text, shown when [value] is empty.
* @param textToolbar [TextToolbar] with modified actions in menu.
* @param alignment The alignment of the text within its container.
* @see [BasicTextField]
* @see [AutoSizeTextStyleBox]
Expand All @@ -177,7 +149,6 @@ private fun AutoSizeTextField(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
placeholder: String? = null,
textToolbar: TextToolbar = LocalTextToolbar.current,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = TextStyle.Default,
Expand Down Expand Up @@ -206,28 +177,14 @@ private fun AutoSizeTextField(
) {
CompositionLocalProvider(
LocalTextInputService provides null,
LocalTextToolbar provides textToolbar
) {
val currentTextToolbar = LocalTextToolbar.current
val style = LocalTextStyle.current
val focusRequester = remember { FocusRequester() }

BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = {
currentTextToolbar.hide()
focusRequester.requestFocus()
onValueChange(value.copy(selection = TextRange.Zero))
currentTextToolbar.showMenu(Rect(Offset.Zero, 0f))
}
),
.fillMaxWidth(),
enabled = enabled,
readOnly = readOnly,
textStyle = style,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ class CalculatorScreenTest {
addTokens = {},
clearInput = {},
deleteTokens = {},
onCursorChange = {},
onValueChange = {},
toggleCalculatorMode = {},
equal = {},
clearHistory = {},
addBracket = {}
addBracket = {},
onDelete = {},
)
}

Expand All @@ -69,7 +70,6 @@ class CalculatorScreenTest {
outputFormat = OutputFormat.PLAIN,
formatterSymbols = FormatterSymbols.Spaces,
history = emptyList(),
allowVibration = false,
middleZero = false,
acButton = true,
partialHistoryView = true
Expand All @@ -79,11 +79,12 @@ class CalculatorScreenTest {
addTokens = {},
clearInput = {},
deleteTokens = {},
onCursorChange = {},
onValueChange = {},
toggleCalculatorMode = {},
equal = {},
clearHistory = {},
addBracket = {}
addBracket = {},
onDelete = {},
)
}

Expand All @@ -103,7 +104,6 @@ class CalculatorScreenTest {
outputFormat = OutputFormat.PLAIN,
formatterSymbols = FormatterSymbols.Spaces,
history = emptyList(),
allowVibration = false,
middleZero = false,
acButton = true,
partialHistoryView = true
Expand All @@ -113,11 +113,12 @@ class CalculatorScreenTest {
addTokens = {},
clearInput = {},
deleteTokens = {},
onCursorChange = {},
onValueChange = {},
toggleCalculatorMode = {},
equal = {},
clearHistory = {},
addBracket = {}
addBracket = {},
onDelete = {},
)
}

Expand Down
Loading

0 comments on commit 5c81a1c

Please sign in to comment.