Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.foundation.text.handwriting.stylusHandwriting
import androidx.compose.foundation.text.input.internal.CoreTextFieldSemanticsModifier
import androidx.compose.foundation.text.input.internal.legacyTextInputAdapter
import androidx.compose.foundation.text.input.internal.createLegacyPlatformTextInputServiceAdapter
import androidx.compose.foundation.text.input.internal.legacyTextInputAdapter
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.OffsetProvider
import androidx.compose.foundation.text.selection.SelectedTextType
Expand Down Expand Up @@ -72,6 +72,7 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.pointer.pointerInput
Expand Down Expand Up @@ -1134,27 +1135,32 @@ internal expect fun Modifier.textFieldDraw(
offsetMapping: OffsetMapping,
): Modifier

internal fun Modifier.defaultTextFieldDraw(
internal fun DefaultTextFieldOnDrawBehind(
state: LegacyTextFieldState,
value: TextFieldValue,
offsetMapping: OffsetMapping,
): Modifier =
this.drawBehind {
state.layoutResult?.let { layoutResult ->
drawIntoCanvas { canvas ->
TextFieldDelegate.draw(
canvas,
value,
state.selectionPreviewHighlightRange,
state.deletionPreviewHighlightRange,
offsetMapping,
layoutResult.value,
state.highlightPaint,
state.selectionBackgroundColor,
)
}
): DrawScope.() -> Unit = {
state.layoutResult?.let { layoutResult ->
drawIntoCanvas { canvas ->
TextFieldDelegate.draw(
canvas,
value,
state.selectionPreviewHighlightRange,
state.deletionPreviewHighlightRange,
offsetMapping,
layoutResult.value,
state.highlightPaint,
state.selectionBackgroundColor,
)
}
}
}

internal fun Modifier.defaultTextFieldDraw(
state: LegacyTextFieldState,
value: TextFieldValue,
offsetMapping: OffsetMapping,
): Modifier = this.drawBehind(DefaultTextFieldOnDrawBehind(state, value, offsetMapping))

@Composable
internal expect fun CursorHandle(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,18 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.ObserverModifierNode
import androidx.compose.ui.node.currentValueOf
import androidx.compose.ui.node.invalidateDraw
import androidx.compose.ui.node.observeReads
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.text.TextPainter
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.TextFieldValue
Expand Down Expand Up @@ -61,21 +70,99 @@ internal actual fun Modifier.textFieldDraw(
state: LegacyTextFieldState,
value: TextFieldValue,
offsetMapping: OffsetMapping,
): Modifier = composed {
val nativeInputContext = LocalNativeTextInputContext.current
val usingNativeTextInput = nativeInputContext.usingNativeTextInput()
): Modifier = this then TextFieldDrawElement(state, value, offsetMapping)

// iOS handles selection drawing itself in native text input mode
if (usingNativeTextInput) {
this.drawBehind {
state.layoutResult?.let { layoutResult ->
drawIntoCanvas { canvas ->
// Still need this for text rendering
TextPainter.paint(canvas, layoutResult.value)
}
private data class TextFieldDrawElement(
private val state: LegacyTextFieldState,
private val value: TextFieldValue,
private val offsetMapping: OffsetMapping,
) : ModifierNodeElement<TextFieldDrawNode>() {

override fun create() = TextFieldDrawNode(
state = state,
value = value,
offsetMapping = offsetMapping,
)

override fun update(node: TextFieldDrawNode) {
node.update(
state = state,
value = value,
offsetMapping = offsetMapping
)
}

override fun InspectorInfo.inspectableProperties() {
name = "textFieldDraw"
properties["state"] = state
properties["value"] = value
properties["offsetMapping"] = offsetMapping
}
}

@OptIn(InternalComposeUiApi::class)
private class TextFieldDrawNode(
private var state: LegacyTextFieldState,
private var value: TextFieldValue,
private var offsetMapping: OffsetMapping,
) : Modifier.Node(),
ObserverModifierNode,
CompositionLocalConsumerModifierNode,
DrawModifierNode {

private var onDraw: DrawScope.() -> Unit = {}
private var usingNativeTextInput: Boolean = false

override fun onAttach() {
super.onAttach()
onObservedReadsChanged()
updateOnDraw()
}

override fun onObservedReadsChanged() {
observeReads {
val usingNativeTextInput = currentValueOf(LocalNativeTextInputContext).usingNativeTextInput()
if (usingNativeTextInput != this.usingNativeTextInput) {
this.usingNativeTextInput = usingNativeTextInput
updateOnDraw()
}
}
} else {
defaultTextFieldDraw(state, value, offsetMapping)
}

fun update(
state: LegacyTextFieldState,
value: TextFieldValue,
offsetMapping: OffsetMapping,
) {
this.state = state
this.value = value
this.offsetMapping = offsetMapping
updateOnDraw()
}

private fun updateOnDraw() {
onDraw =
if (usingNativeTextInput) {
{
// iOS handles selection drawing itself in native text input mode
// still needs this for text rendering
state.layoutResult?.let { layoutResult ->
drawIntoCanvas { canvas ->
TextPainter.paint(canvas, layoutResult.value)
}
}
}
} else {
DefaultTextFieldOnDrawBehind(state, value, offsetMapping)
}

invalidateDraw()
}

override fun ContentDrawScope.draw() = drawBehind()

private fun ContentDrawScope.drawBehind() {
onDraw()
drawContent()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need drawContent() call here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is consistent with the drawBehind functionality we need here

}
}
Loading