commonMain.androidx.compose.ui.semantics.SemanticsConfiguration.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ui-desktop Show documentation
Show all versions of ui-desktop Show documentation
Compose UI primitives. This library contains the primitives that form the Compose UI Toolkit, such as drawing, measurement and layout.
/*
* Copyright 2019 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.ui.semantics
import androidx.compose.ui.platform.simpleIdentityToString
/**
* Describes the semantic information associated with the owning component
*
* The information provided in the configuration is used to to generate the
* semantics tree.
*/
class SemanticsConfiguration :
SemanticsPropertyReceiver,
Iterable, Any?>> {
private val props: MutableMap, Any?> = mutableMapOf()
/**
* Retrieves the value for the given property, if one has been set.
* If a value has not been set, throws [IllegalStateException]
*/
// Unavoidable, guaranteed by [set]
@Suppress("UNCHECKED_CAST")
operator fun get(key: SemanticsPropertyKey): T {
return props.getOrElse(key) {
throw IllegalStateException("Key not present: $key - consider getOrElse or getOrNull")
} as T
}
// Unavoidable, guaranteed by [set]
@Suppress("UNCHECKED_CAST")
fun getOrElse(key: SemanticsPropertyKey, defaultValue: () -> T): T {
return props.getOrElse(key, defaultValue) as T
}
// Unavoidable, guaranteed by [set]
@Suppress("UNCHECKED_CAST")
fun getOrElseNullable(key: SemanticsPropertyKey, defaultValue: () -> T?): T? {
return props.getOrElse(key, defaultValue) as T?
}
override fun iterator(): Iterator, Any?>> {
return props.iterator()
}
override fun set(key: SemanticsPropertyKey, value: T) {
if (value is AccessibilityAction<*> && contains(key)) {
val prev = props[key] as AccessibilityAction<*>
props[key] = AccessibilityAction(
value.label ?: prev.label,
value.action ?: prev.action
)
} else {
props[key] = value
}
}
operator fun contains(key: SemanticsPropertyKey): Boolean {
return props.containsKey(key)
}
internal fun containsImportantForAccessibility() =
props.keys.any { it.isImportantForAccessibility }
/**
* Whether the semantic information provided by the owning component and
* all of its descendants should be treated as one logical entity.
*
* If set to true, the descendants of the owning component's
* [SemanticsNode] will merge their semantic information into the
* [SemanticsNode] representing the owning component.
*/
var isMergingSemanticsOfDescendants: Boolean = false
var isClearingSemantics: Boolean = false
// CONFIGURATION COMBINATION LOGIC
/**
* Absorb the semantic information from a child SemanticsNode into this configuration.
*
* This merges the child's semantic configuration using the `merge()` method defined
* on the key. This is used when mergeDescendants is specified (for accessibility focusable
* nodes).
*/
@Suppress("UNCHECKED_CAST")
internal fun mergeChild(child: SemanticsConfiguration) {
for ((key, nextValue) in child.props) {
val existingValue = props[key]
val mergeResult = (key as SemanticsPropertyKey).merge(existingValue, nextValue)
if (mergeResult != null) {
props[key] = mergeResult
}
}
}
/**
* Absorb the semantic information from a peer modifier into this configuration.
*
* This is repeatedly called for each semantics {} modifier on one LayoutNode to collapse
* them into one SemanticsConfiguration. If a key is already seen and the value is
* AccessibilityAction, the resulting AccessibilityAction's label/action will be the
* label/action of the outermost modifier with this key and nonnull label/action, or null if no
* nonnull label/action is found. If the value is not AccessibilityAction, values with a key
* already seen are ignored (the semantics value of the outermost modifier with a given
* semantics key is the one used).
*/
internal fun collapsePeer(peer: SemanticsConfiguration) {
if (peer.isMergingSemanticsOfDescendants) {
isMergingSemanticsOfDescendants = true
}
if (peer.isClearingSemantics) {
isClearingSemantics = true
}
for ((key, nextValue) in peer.props) {
if (!props.contains(key)) {
props[key] = nextValue
} else if (nextValue is AccessibilityAction<*>) {
val value = props[key] as AccessibilityAction<*>
props[key] = AccessibilityAction(
value.label ?: nextValue.label,
value.action ?: nextValue.action
)
}
}
}
/** Returns an exact copy of this configuration. */
fun copy(): SemanticsConfiguration {
val copy = SemanticsConfiguration()
copy.isMergingSemanticsOfDescendants = isMergingSemanticsOfDescendants
copy.isClearingSemantics = isClearingSemantics
copy.props.putAll(props)
return copy
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SemanticsConfiguration) return false
if (props != other.props) return false
if (isMergingSemanticsOfDescendants != other.isMergingSemanticsOfDescendants) return false
if (isClearingSemantics != other.isClearingSemantics) return false
return true
}
override fun hashCode(): Int {
var result = props.hashCode()
result = 31 * result + isMergingSemanticsOfDescendants.hashCode()
result = 31 * result + isClearingSemantics.hashCode()
return result
}
override fun toString(): String {
val propsString = StringBuilder()
var nextSeparator = ""
if (isMergingSemanticsOfDescendants) {
propsString.append(nextSeparator)
propsString.append("mergeDescendants=true")
nextSeparator = ", "
}
if (isClearingSemantics) {
propsString.append(nextSeparator)
propsString.append("isClearingSemantics=true")
nextSeparator = ", "
}
for ((key, value) in props) {
propsString.append(nextSeparator)
propsString.append(key.name)
propsString.append(" : ")
propsString.append(value)
nextSeparator = ", "
}
return "${simpleIdentityToString(this@SemanticsConfiguration, null)}{ $propsString }"
}
}
fun SemanticsConfiguration.getOrNull(key: SemanticsPropertyKey): T? {
return getOrElseNullable(key) { null }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy