TextFieldContentSemantics.kt
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.foundation.text2
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.SemanticsModifierNode
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.editableText
import androidx.compose.ui.semantics.getTextLayoutResult
import androidx.compose.ui.semantics.textSelectionRange
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextRange
@OptIn(ExperimentalFoundationApi::class)
internal class TextFieldContentSemanticsElement(
private val textFieldState: TextFieldState,
private val textLayoutState: TextLayoutState
) : ModifierNodeElement<TextFieldContentSemanticsNode>() {
override fun create(): TextFieldContentSemanticsNode = TextFieldContentSemanticsNode(
textFieldState,
textLayoutState
)
override fun update(node: TextFieldContentSemanticsNode): TextFieldContentSemanticsNode {
node.textFieldState = textFieldState
node.textLayoutState = textLayoutState
return node
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is TextFieldContentSemanticsElement) return false
if (textFieldState != other.textFieldState) return false
return true
}
override fun hashCode(): Int {
return textFieldState.hashCode()
}
override fun InspectorInfo.inspectableProperties() {
// Show nothing in the inspector.
}
}
@OptIn(ExperimentalFoundationApi::class)
internal data class TextFieldContentSemanticsNode(
var textFieldState: TextFieldState,
var textLayoutState: TextLayoutState
) : Modifier.Node(), SemanticsModifierNode {
private var lastText: AnnotatedString? = null
private var lastSelection: TextRange? = null
private var _semanticsConfiguration: SemanticsConfiguration? = null
private fun generateSemantics(
text: AnnotatedString,
selection: TextRange
): SemanticsConfiguration {
lastText = text
lastSelection = selection
return SemanticsConfiguration().also { configuration ->
configuration.getTextLayoutResult {
textLayoutState.layoutResult?.let { result -> it.add(result) } ?: false
}
configuration.editableText = text
configuration.textSelectionRange = selection
_semanticsConfiguration = configuration
}
}
override val semanticsConfiguration: SemanticsConfiguration
get() {
var localSemantics = _semanticsConfiguration
val value = textFieldState.value
if (localSemantics == null ||
lastText != value.annotatedString ||
lastSelection != value.selection
) {
localSemantics = generateSemantics(value.annotatedString, value.selection)
}
return localSemantics
}
}