Skip to content

Commit fcad981

Browse files
authored
Concatenate child text and content description with Modifier.semantics(mergeDescendants = true) (#2202)
1 parent 0f79bd1 commit fcad981

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/a11y/ComposeAccessible.kt

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,6 @@ import kotlinx.atomicfu.atomic
6767
import org.jetbrains.skia.BreakIterator
6868
import org.jetbrains.skiko.nativeInitializeAccessible
6969

70-
private fun <T> SemanticsConfiguration.getFirstOrNull(key: SemanticsPropertyKey<List<T>>): T? {
71-
return getOrNull(key)?.firstOrNull()
72-
}
73-
7470
private typealias ActionKey = SemanticsPropertyKey<AccessibilityAction<() -> Boolean>>
7571

7672
/**
@@ -139,11 +135,8 @@ internal class ComposeAccessible(
139135
val setSelection
140136
get() = semanticsConfig.getOrNull(SemanticsActions.SetSelection)
141137
val text
142-
// TODO should we concatenate the texts instead of getting only the first one
143-
// Concatenation seems to be reasonable eg, for button with two text nodes inside
144-
// but conflicts with setText action
145138
get() = semanticsConfig.getOrNull(SemanticsProperties.EditableText)
146-
?: semanticsConfig.getFirstOrNull(SemanticsProperties.Text)
139+
?: semanticsConfig.getOrNull(SemanticsProperties.Text)?.mergeText()
147140

148141
val textLayoutResult: TextLayoutResult?
149142
get() {
@@ -255,9 +248,9 @@ internal class ComposeAccessible(
255248
}
256249

257250
override fun getAccessibleDescription(): String? {
258-
// TODO concatenate values?
259251
return semanticsConfig
260-
.getFirstOrNull(SemanticsProperties.ContentDescription)
252+
.getOrNull(SemanticsProperties.ContentDescription)
253+
?.mergeText()
261254
}
262255

263256
override fun getAccessibleParent(): Accessible? {
@@ -870,6 +863,8 @@ internal class ComposeAccessible(
870863
override fun doAccessibleAction(i: Int): Boolean {
871864
return accessibleAction?.doAccessibleAction(i) ?: false
872865
}
866+
867+
private fun List<CharSequence>.mergeText() = joinToString(", ")
873868
}
874869
}
875870

compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/AccessibilityTest.kt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package androidx.compose.ui.platform
1818

1919
import androidx.compose.foundation.layout.Box
2020
import androidx.compose.foundation.layout.Column
21+
import androidx.compose.foundation.layout.Row
2122
import androidx.compose.foundation.layout.offset
2223
import androidx.compose.foundation.layout.size
2324
import androidx.compose.material.Button
@@ -61,6 +62,7 @@ import javax.accessibility.AccessibleText
6162
import javax.accessibility.AccessibleValue
6263
import kotlin.test.assertEquals
6364
import kotlin.test.assertFalse
65+
import kotlin.test.assertNotNull
6466
import kotlin.test.assertTrue
6567
import kotlin.test.fail
6668
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -300,7 +302,26 @@ class AccessibilityTest {
300302
test.onNodeWithTag("box").fetchAccessibleComponent().let {
301303
assertEquals(size, it.size.toDpSize())
302304
}
303-
}
305+
}
306+
307+
@Test
308+
fun mergeDescendantsMergesText() = runDesktopA11yTest {
309+
test.setContent {
310+
Row(
311+
Modifier
312+
.testTag("text")
313+
.semantics(mergeDescendants = true) {}
314+
) {
315+
Text("Hello")
316+
Text("World")
317+
}
318+
}
319+
320+
test.onNodeWithTag("text").apply {
321+
assertTextContains("Hello")
322+
assertTextContains("World")
323+
}
324+
}
304325

305326
}
306327

@@ -443,4 +464,13 @@ internal class ComposeA11yTestScope(
443464
message = "Current accessible value expected to, but does not equal: $number",
444465
)
445466
}
467+
468+
/**
469+
* Asserts that the text of the accessible
470+
*/
471+
fun SemanticsNodeInteraction.assertTextContains(value: String) {
472+
val text = fetchAccessible().composeAccessibleContext.text
473+
assertNotNull(text, "Text is null")
474+
assertTrue(value in text, "Text does not contain $value")
475+
}
446476
}

0 commit comments

Comments
 (0)