diff --git a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/BackingDomInput.kt b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/BackingDomInput.kt
index ec7397570eff6..372aa81495a70 100644
--- a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/BackingDomInput.kt
+++ b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/BackingDomInput.kt
@@ -19,7 +19,9 @@ package androidx.compose.ui.platform
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.input.EditCommand
+import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.ImeOptions
+import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import kotlin.js.js
import kotlinx.browser.document
@@ -31,7 +33,9 @@ internal interface ComposeCommandCommunicator {
fun sendEditCommand(command: EditCommand) = sendEditCommand(listOf(command))
fun sendKeyboardEvent(keyboardEvent: KeyEvent): Boolean
+}
+internal interface TextLayoutProvider {
fun currentTextLayoutResult(): TextLayoutResult?
}
@@ -47,17 +51,19 @@ private fun setBackingInputBox(container: HTMLElement, left: Float, top: Float,
* and the DOM HTMLTextAreaElement we are actually listening events on in order to show
* the virtual keyboard.
*/
-internal class BackingDomInput(
+internal abstract class BackingDomInput(
val inputContainer: HTMLElement,
imeOptions: ImeOptions,
composeCommunicator : ComposeCommandCommunicator,
-) {
- private val inputStrategy = DomInputStrategy(
- imeOptions,
- composeCommunicator
- )
+): TextLayoutProvider {
+ internal val backingElement = imeOptions.createDomElement()
- internal val backingElement = inputStrategy.htmlInput
+ private val inputStrategy = object : DomInputStrategy(
+ backingElement,
+ composeCommunicator
+ ) {
+ override fun currentTextLayoutResult(): TextLayoutResult? = this@BackingDomInput.currentTextLayoutResult()
+ }
fun register() {
setBackingInputBox(container = inputContainer, 0f, 0f, 0f, 0f)
@@ -99,3 +105,84 @@ internal class BackingDomInput(
backingElement.remove()
}
}
+
+
+private fun ImeOptions.createDomElement(): HTMLElement {
+ val htmlElement = document.createElement(
+ if (singleLine) "input" else "textarea"
+ ) as HTMLElement
+
+ // without autocorrect set "on" iOS virtual keyboard won't suggest
+ // see https://youtrack.jetbrains.com/issue/CMP-8807
+ htmlElement.setAttribute("autocorrect", "on")
+ htmlElement.setAttribute("autocomplete", "off")
+ htmlElement.setAttribute("autocapitalize", "off")
+ htmlElement.setAttribute("spellcheck", "false")
+
+ val inputMode = when (keyboardType) {
+ KeyboardType.Text -> "text"
+ KeyboardType.Ascii -> "text"
+ KeyboardType.Number -> "number"
+ KeyboardType.Phone -> "tel"
+ KeyboardType.Uri -> "url"
+ KeyboardType.Email -> "email"
+ KeyboardType.Password -> "password"
+ KeyboardType.NumberPassword -> "number"
+ KeyboardType.Decimal -> "decimal"
+ else -> "text"
+ }
+
+ val enterKeyHint = when (imeAction) {
+ ImeAction.Default -> "enter"
+ ImeAction.None -> "enter"
+ ImeAction.Done -> "done"
+ ImeAction.Go -> "go"
+ ImeAction.Next -> "next"
+ ImeAction.Previous -> "previous"
+ ImeAction.Search -> "search"
+ ImeAction.Send -> "send"
+ else -> "enter"
+ }
+
+ htmlElement.setAttribute("inputmode", inputMode)
+ htmlElement.setAttribute("enterkeyhint", enterKeyHint)
+
+
+ htmlElement.style.apply {
+ setProperty("position", "absolute")
+ setProperty("user-select", "none")
+ setProperty("forced-color-adjust", "none")
+ setProperty("white-space", "pre")
+ setProperty("align-content", "center")
+ setProperty(
+ "top",
+ "calc(min(var(--compose-internal-web-backing-input-top) * 1px, 100vh - var(--compose-internal-web-backing-input-height) * 1px))"
+ )
+ setProperty(
+ "left",
+ "calc(min(var(--compose-internal-web-backing-input-left) * 1px, 100vw - var(--compose-internal-web-backing-input-width) * 1px))"
+ )
+ setProperty("width", "calc(var(--compose-internal-web-backing-input-width) * 1px")
+ setProperty("height", "calc(var(--compose-internal-web-backing-input-height) * 1px")
+ setProperty("padding", "0")
+ setProperty("color", "transparent")
+ setProperty("background", "transparent")
+ setProperty("caret-color", "transparent")
+ setProperty("outline", "none")
+ setProperty("border", "none")
+ setProperty("resize", "none")
+ setProperty("text-shadow", "none")
+ setProperty("z-index", "-1")
+ // TODO: do we need pointer-events: none
+ //setProperty("pointer-events", "none")
+
+ // I keep "opacity" commented to make it explicit that we can't use this property.
+ // Reason: Safari iOS keyboard overlaps the text input. See CMP-8611
+ // setProperty("opacity", "0")
+
+ // To prevent auto-zoom in some mobile browsers, we set a larger font-size
+ setProperty("font-size", "20px")
+ }
+
+ return htmlElement
+}
diff --git a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/DomInputStrategy.kt b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/DomInputStrategy.kt
index 4ad20d48a6449..8c4e47b99c2a1 100644
--- a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/DomInputStrategy.kt
+++ b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/DomInputStrategy.kt
@@ -17,9 +17,7 @@
package androidx.compose.ui.platform
import androidx.compose.ui.input.key.Key
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.ImeOptions
-import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.input.SetSelectionCommand
import androidx.compose.ui.text.input.TextFieldValue
import kotlin.js.ExperimentalWasmJsInterop
@@ -38,12 +36,10 @@ import org.w3c.dom.events.UIEvent
import org.w3c.dom.events.InputEvent
import org.w3c.dom.events.KeyboardEvent
-internal class DomInputStrategy(
- imeOptions: ImeOptions,
+internal abstract class DomInputStrategy(
+ private val htmlInput: HTMLElement,
private val composeSender: ComposeCommandCommunicator,
-) {
- val htmlInput = imeOptions.createDomElement()
-
+): TextLayoutProvider {
private var lastMeaningfulUpdate = TextFieldValue("")
// To avoid the re-triggering of the selection change
@@ -54,14 +50,6 @@ internal class DomInputStrategy(
initEvents()
}
- private val nativeInputEventsProcessor = object : NativeInputEventsProcessor(composeSender) {
- override fun scheduleCheckpoint() {
- window.requestAnimationFrame {
- runCheckpoint(currentTextFieldValue = lastMeaningfulUpdate)
- }
- }
- }
-
fun updateState(textFieldValue: TextFieldValue) {
htmlInput as HTMLElementWithValue
@@ -82,9 +70,19 @@ internal class DomInputStrategy(
private val tabKeyCode = Key.Tab.keyCode.toInt()
private fun initEvents() {
- htmlInput.addEventListener("blur", { evt ->
- // TODO: any actions here?
- })
+ val nativeInputEventsProcessor = object : NativeInputEventsProcessor() {
+ override fun scheduleCheckpoint() {
+ window.requestAnimationFrame {
+ runCheckpoint(currentTextFieldValue = lastMeaningfulUpdate)
+ }
+ }
+
+ override fun withCommandSenderContext(block: ComposeCommandCommunicator.() -> Unit) {
+ block.invoke(composeSender)
+ }
+
+ override fun currentTextLayoutResult(): TextLayoutResult? = this@DomInputStrategy.currentTextLayoutResult()
+ }
htmlInput.addEventListener("keydown", { evt ->
nativeInputEventsProcessor.registerEvent(evt as KeyboardEvent)
@@ -175,87 +173,6 @@ internal inline fun UIEvent.asInputEventExt(): InputEventExt = unsafeCast "text"
- KeyboardType.Ascii -> "text"
- KeyboardType.Number -> "number"
- KeyboardType.Phone -> "tel"
- KeyboardType.Uri -> "url"
- KeyboardType.Email -> "email"
- KeyboardType.Password -> "password"
- KeyboardType.NumberPassword -> "number"
- KeyboardType.Decimal -> "decimal"
- else -> "text"
- }
-
- val enterKeyHint = when (imeAction) {
- ImeAction.Default -> "enter"
- ImeAction.None -> "enter"
- ImeAction.Done -> "done"
- ImeAction.Go -> "go"
- ImeAction.Next -> "next"
- ImeAction.Previous -> "previous"
- ImeAction.Search -> "search"
- ImeAction.Send -> "send"
- else -> "enter"
- }
-
- htmlElement.setAttribute("inputmode", inputMode)
- htmlElement.setAttribute("enterkeyhint", enterKeyHint)
-
-
- htmlElement.style.apply {
- setProperty("position", "absolute")
- setProperty("user-select", "none")
- setProperty("forced-color-adjust", "none")
- setProperty("white-space", "pre")
- setProperty("align-content", "center")
- setProperty(
- "top",
- "calc(min(var(--compose-internal-web-backing-input-top) * 1px, 100vh - var(--compose-internal-web-backing-input-height) * 1px))"
- )
- setProperty(
- "left",
- "calc(min(var(--compose-internal-web-backing-input-left) * 1px, 100vw - var(--compose-internal-web-backing-input-width) * 1px))"
- )
- setProperty("width", "calc(var(--compose-internal-web-backing-input-width) * 1px")
- setProperty("height", "calc(var(--compose-internal-web-backing-input-height) * 1px")
- setProperty("padding", "0")
- setProperty("color", "transparent")
- setProperty("background", "transparent")
- setProperty("caret-color", "transparent")
- setProperty("outline", "none")
- setProperty("border", "none")
- setProperty("resize", "none")
- setProperty("text-shadow", "none")
- setProperty("z-index", "-1")
- // TODO: do we need pointer-events: none
- //setProperty("pointer-events", "none")
-
- // I keep "opacity" commented to make it explicit that we can't use this property.
- // Reason: Safari iOS keyboard overlaps the text input. See CMP-8611
- // setProperty("opacity", "0")
-
- // To prevent auto-zoom in some mobile browsers, we set a larger font-size
- setProperty("font-size", "20px")
- }
-
- return htmlElement
-}
-
private external interface HTMLElementWithValue {
var value: String
val selectionStart: Int
diff --git a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/NativeInputEventsProcessor.kt b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/NativeInputEventsProcessor.kt
index 4e8b8bdc44b19..848693a7033ae 100644
--- a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/NativeInputEventsProcessor.kt
+++ b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/NativeInputEventsProcessor.kt
@@ -44,12 +44,12 @@ import org.w3c.dom.events.UIEvent
* @param composeSender The communicator responsible for transmitting edit
* commands and keyboard events to the Compose system.
*/
-internal abstract class NativeInputEventsProcessor(
- private val composeSender: ComposeCommandCommunicator
-) {
+internal abstract class NativeInputEventsProcessor : TextLayoutProvider {
private val collectedEvents = mutableListOf()
+ internal abstract fun withCommandSenderContext(block: ComposeCommandCommunicator.() -> Unit)
+
@get:TestOnly
@set:TestOnly
internal var isCheckpointScheduled = false
@@ -57,16 +57,19 @@ internal abstract class NativeInputEventsProcessor(
internal var lastCompositionEndTimestamp = 0.0 // Double because of k/wasm where Number.toLong() leads to a compilation error
private var lastProcessedKeydown: KeyboardEvent? = null
- private tailrec fun TextLayoutResult.getPrevWordOffset(currentOffset: Int): Int {
+ private tailrec fun TextLayoutResult.getPrevWordOffset(
+ currentOffset: Int,
+ offsetMapping: OffsetMapping = OffsetMapping.Identity
+ ): Int {
if (currentOffset <= 0) {
return 0
}
val text = layoutInput.text
- val currentWord = getWordBoundary(currentOffset.coerceIn(0, text.length - 1))
+ val currentWord = getWordBoundary(currentOffset.coerceAtMost(text.length - 1))
return if (currentWord.start >= currentOffset) {
getPrevWordOffset(currentOffset - 1)
} else {
- OffsetMapping.Identity.transformedToOriginal(currentWord.start)
+ offsetMapping.transformedToOriginal(currentWord.start)
}
}
@@ -87,7 +90,7 @@ internal abstract class NativeInputEventsProcessor(
}
}
- fun runCheckpoint(currentTextFieldValue: TextFieldValue) {
+ fun runCheckpoint(currentTextFieldValue: TextFieldValue) = withCommandSenderContext {
isCheckpointScheduled = false
collectedEvents.sortBy { it.timeStamp.toInt() }
@@ -126,7 +129,7 @@ internal abstract class NativeInputEventsProcessor(
val shouldBeProcessed = timestamp == 0.0 || !isFromLastComposition
if (shouldBeProcessed) {
- val isProcessed = composeSender.sendKeyboardEvent(evt.toComposeEvent())
+ val isProcessed = sendKeyboardEvent(evt.toComposeEvent())
if (isProcessed) {
lastProcessedKeydown = evt
}
@@ -135,7 +138,7 @@ internal abstract class NativeInputEventsProcessor(
"compositionend" -> {
lastCompositionEndTimestamp = timestamp
- composeSender.sendEditCommand(CommitTextCommand((evt as CompositionEvent).data, 1))
+ sendEditCommand(CommitTextCommand((evt as CompositionEvent).data, 1))
}
"beforeinput" -> {
@@ -149,83 +152,89 @@ internal abstract class NativeInputEventsProcessor(
collectedEvents.clear()
}
- private fun InputEventExt.process(currentTextFieldValue: TextFieldValue) {
- val editCommands = when (inputType) {
- "deleteContentBackward" -> buildList {
- // this means "deleteContentBackward" happened because of an earlier "keydown" event, so skipping it here
- if (lastProcessedKeydown?.isBackspace() == true) return@buildList
-
- if (!currentTextFieldValue.selection.collapsed) {
- // Likely it's on mobile, where the Backspace has Unidentified key value.
- // When Compose TextField shows text selection,
- // a good UX for deleteContentBackward would be to emulate Backspace
- add(BackspaceCommand())
- } else {
- // This happens when an autocorrection is applied on mobile:
- // The system first tells us to delete the old text,
- // and then it would send the "insertText" event.
- if (textRangeSize > 0) {
- // deleteContentBackward can happen under very non-trivial circumstances,
- // for instance; when an input suggestion on Android Chrome is accepted,
- // the browser then deletes space after the word just to add space again
- add(SetSelectionCommand(textRangeStart, textRangeEnd))
- add(BackspaceCommand())
- } else if (textRangeSize == 0) {
- // under specific circumstance previous symbol can be deleted while inputing new one
- // see https://youtrack.jetbrains.com/issue/CMP-8773
+ private fun InputEventExt.process(currentTextFieldValue: TextFieldValue) = withCommandSenderContext {
+ val editCommands = buildList {
+ when (inputType) {
+ "deleteContentBackward" -> {
+ // this means "deleteContentBackward" happened because of an earlier "keydown" event, so skipping it here
+ if (lastProcessedKeydown?.isBackspace() == true) return@buildList
+
+ if (!currentTextFieldValue.selection.collapsed) {
+ // Likely it's on mobile, where the Backspace has Unidentified key value.
+ // When Compose TextField shows text selection,
+ // a good UX for deleteContentBackward would be to emulate Backspace
add(BackspaceCommand())
+ } else {
+ // This happens when an autocorrection is applied on mobile:
+ // The system first tells us to delete the old text,
+ // and then it would send the "insertText" event.
+ if (textRangeSize > 0) {
+ // deleteContentBackward can happen under very non-trivial circumstances,
+ // for instance; when an input suggestion on Android Chrome is accepted,
+ // the browser then deletes space after the word just to add space again
+ add(SetSelectionCommand(textRangeStart, textRangeEnd))
+ add(BackspaceCommand())
+ } else if (textRangeSize == 0) {
+ // under specific circumstance previous symbol can be deleted while inputing new one
+ // see https://youtrack.jetbrains.com/issue/CMP-8773
+ add(BackspaceCommand())
+ }
}
}
- }
- "deleteWordBackward" -> buildList {
- if (lastProcessedKeydown?.isBackspace() != true) return@buildList
+ "deleteWordBackward" -> {
+ if (lastProcessedKeydown?.isBackspace() != true) return@buildList
+
+ // This would mean event was triggered by long press on mobile device (iOS)
+ if (lastProcessedKeydown?.repeat == true) {
+ val layoutResult =
+ currentTextLayoutResult() ?: return@buildList
+
+ val offset = layoutResult.getPrevWordOffset(
+ OffsetMapping.Identity.originalToTransformed(textRangeEnd)
+ )
+ val deleteCommand = DeleteSurroundingTextCommand(
+ (textRangeEnd - offset).coerceAtLeast(0),
+ 0
+ )
+ add(deleteCommand)
+ }
+ }
- // This would mean event was triggered by long press on mobile device (iOS)
- if (lastProcessedKeydown?.repeat == true) {
- val layoutResult = composeSender.currentTextLayoutResult() ?: return@buildList
+ "insertReplacementText" -> {
+ if (data == null) return@buildList
+ if (textRangeSize > 0) {
+ add(SetSelectionCommand(textRangeStart, textRangeEnd))
+ }
- val offset = layoutResult.getPrevWordOffset(textRangeStart)
- val deleteCommand = DeleteSurroundingTextCommand((textRangeEnd - offset).coerceAtLeast(0), 0)
- add(deleteCommand)
+ add(CommitTextCommand(data, 1))
}
- }
+ "insertText" -> {
+ if (data == null) return@buildList
+ if (textRangeSize > 0 && currentTextFieldValue.selection.collapsed) {
+ add(SetSelectionCommand(textRangeStart, textRangeEnd))
+ }
- "insertReplacementText" -> buildList {
- if (data == null) return@buildList
- if (textRangeSize > 0) {
- add(SetSelectionCommand(textRangeStart, textRangeEnd))
+ add(CommitTextCommand(data, 1))
}
- add(CommitTextCommand(data, 1))
- }
-
- "insertText" -> buildList {
- if (data == null) return@buildList
- if (textRangeSize > 0 && currentTextFieldValue.selection.collapsed) {
- add(SetSelectionCommand(textRangeStart, textRangeEnd))
+ "insertCompositionText" -> {
+ if (data == null) return@buildList
+ if (textRangeSize > 0) {
+ add(SetSelectionCommand(textRangeStart, textRangeEnd))
+ }
+ add(SetComposingTextCommand(data, 1))
}
- add(CommitTextCommand(data, 1))
+ // "insertFromComposition", "deleteCompositionText" are triggered in Safari just before the 'compositionEnd' event.
+ // They're ignored because Safari also sends 'insertCompositionText' which we handle (alongside 'compositionEnd')
}
-
- "insertCompositionText" -> buildList {
- if (data == null) return@buildList
- if (textRangeSize > 0) {
- add(SetSelectionCommand(textRangeStart, textRangeEnd))
- }
- add(SetComposingTextCommand(data, 1))
- }
-
- // "insertFromComposition", "deleteCompositionText" are triggered in Safari just before the 'compositionEnd' event.
- // They're ignored because Safari also sends 'insertCompositionText' which we handle (alongside 'compositionEnd')
- else -> emptyList()
}
if (editCommands.isNotEmpty()) {
- composeSender.sendEditCommand(editCommands)
+ sendEditCommand(editCommands)
}
}
diff --git a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/WebTextInputService.kt b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/WebTextInputService.kt
index 8047801e10c30..34da010542925 100644
--- a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/WebTextInputService.kt
+++ b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/platform/WebTextInputService.kt
@@ -51,6 +51,8 @@ internal abstract class WebTextInputService :
field = value
}
+ internal var currentTextLayoutResult: (() -> TextLayoutResult?)? = null
+
/**
* It's used for the initial positioning of the backing HTML input.
* It's rather a workaround for the problem that startInput doesn't know the correct position yet.
@@ -65,12 +67,14 @@ internal abstract class WebTextInputService :
*/
abstract val backingDomInputContainer: HTMLElement
- fun startInput(
- request: PlatformTextInputMethodRequest,
+ override fun startInput(
+ value: TextFieldValue,
+ imeOptions: ImeOptions,
onEditCommand: (List) -> Unit,
+ onImeActionPerformed: (ImeAction) -> Unit
) {
- backingDomInput = BackingDomInput(
- imeOptions = request.imeOptions,
+ backingDomInput = object : BackingDomInput(
+ imeOptions = imeOptions,
composeCommunicator = object : ComposeCommandCommunicator {
override fun sendKeyboardEvent(keyboardEvent: KeyEvent): Boolean {
return this@WebTextInputService.processKeyboardEvent(keyboardEvent)
@@ -79,11 +83,13 @@ internal abstract class WebTextInputService :
override fun sendEditCommand(commands: List) {
onEditCommand(commands)
}
-
- override fun currentTextLayoutResult() = request.textLayoutResult()
},
inputContainer = backingDomInputContainer,
- )
+ ) {
+ override fun currentTextLayoutResult(): TextLayoutResult? =
+ this@WebTextInputService.currentTextLayoutResult?.invoke()
+ }
+
backingDomInput?.register()
if (currentTouchOffset != null) {
@@ -96,21 +102,12 @@ internal abstract class WebTextInputService :
showSoftwareKeyboard()
}
- override fun startInput(
- value: TextFieldValue,
- imeOptions: ImeOptions,
- onEditCommand: (List) -> Unit,
- onImeActionPerformed: (ImeAction) -> Unit
- ) {
- // This method is called from the common code.
- // It's not used in the new API, but we keep it for backward compatibility.
- }
-
fun getBackingInput(): HTMLElement? {
return backingDomInput?.backingElement?.takeIf { it.isConnected }
}
override fun stopInput() {
+ currentTextLayoutResult = null
backingDomInput?.dispose()
backingDomInput = null
}
diff --git a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/WebTextInputSession.kt b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/WebTextInputSession.kt
index c9850bc17f9cd..064470b419a4e 100644
--- a/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/WebTextInputSession.kt
+++ b/compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/WebTextInputSession.kt
@@ -51,10 +51,14 @@ internal class WebTextInputSession(
}
suspendCancellableCoroutine { continuation ->
webTextInputService.startInput(
- request,
- onEditCommand = request.onEditCommand
+ value = request.value(),
+ imeOptions = request.imeOptions,
+ onEditCommand = request.onEditCommand,
+ onImeActionPerformed = request.onImeAction ?: {}
)
+ webTextInputService.currentTextLayoutResult = request.textLayoutResult
+
continuation.invokeOnCancellation {
webTextInputService.stopInput()
}
diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/NativeInputEventsProcessorTest.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/NativeInputEventsProcessorTest.kt
index 78139383036f0..c7dd8c0de2542 100644
--- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/NativeInputEventsProcessorTest.kt
+++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/NativeInputEventsProcessorTest.kt
@@ -83,37 +83,6 @@ class NativeInputEventsProcessorTest {
return true
}
- override fun currentTextLayoutResult(): TextLayoutResult? {
- val text = editingBuffer.toString()
- val annotatedString = AnnotatedString(text)
- val density = Density(1f)
- val constraints = Constraints()
- val style = TextStyle.Default
-
- return TextLayoutResult(
- layoutInput = TextLayoutInput(
- text = annotatedString,
- style = style,
- placeholders = emptyList(),
- maxLines = Int.MAX_VALUE,
- softWrap = true,
- overflow = TextOverflow.Clip,
- density = density,
- layoutDirection = LayoutDirection.Ltr,
- fontFamilyResolver = fontFamilyResolver,
- constraints = constraints
- ),
- multiParagraph = MultiParagraph(
- annotatedString = annotatedString,
- style = style,
- constraints = constraints,
- density = density,
- fontFamilyResolver = fontFamilyResolver
- ),
- size = IntSize(0, 0)
- )
- }
-
@Suppress("INVISIBLE_REFERENCE")
fun currentTextFieldValue(): TextFieldValue {
return TextFieldValue(
@@ -127,8 +96,8 @@ class NativeInputEventsProcessorTest {
* A test implementation of NativeInputEventsProcessor that allows controlling when checkpoints are run
*/
private class TestNativeInputEventsProcessor(
- composeSender: ComposeCommandCommunicator
- ) : NativeInputEventsProcessor(composeSender) {
+ private val composeSender: ComposeCommandCommunicator
+ ) : NativeInputEventsProcessor() {
override fun scheduleCheckpoint() {
isCheckpointScheduled = true
@@ -138,6 +107,14 @@ class NativeInputEventsProcessorTest {
isCheckpointScheduled = false
runCheckpoint(currentTextFieldValue)
}
+
+ override fun withCommandSenderContext(block: ComposeCommandCommunicator.() -> Unit) {
+ block.invoke(composeSender)
+ }
+
+ override fun currentTextLayoutResult(): TextLayoutResult? {
+ TODO("currentTextLayoutResult is tested in integration tests")
+ }
}
@Test