Skip to content

Commit

Permalink
Report the role of ComposeSceneAccessible on macOS as PANEL (#1362)
Browse files Browse the repository at this point in the history
  • Loading branch information
m-sasha committed May 22, 2024
1 parent f5c82d6 commit 7ad119c
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ internal class ComposeWindowPanel(
window = window,
useSwingGraphics = false,
layerType = ComposeFeatureFlags.layerType.let {
// LayerType.OnComponent might be used only with rendering to Swing graphics,
// LayerType.OnComponent may can only be used with rendering via Swing graphics,
// but it's always disabled here. Using fallback instead of [check] to support
// opening separate windows from [ComposePanel] with such layer type.
if (it == LayerType.OnComponent) LayerType.OnSameCanvas else it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import java.awt.*
import java.awt.event.FocusListener
import java.util.*
import javax.accessibility.*
import org.jetbrains.skiko.OS
import org.jetbrains.skiko.hostOs

/**
* This is a root [Accessible] for a [androidx.compose.ui.ComposeScene]
Expand Down Expand Up @@ -153,7 +155,16 @@ internal class ComposeSceneAccessible(
}

override fun getAccessibleRole(): AccessibleRole {
return AccessibleRole.UNKNOWN
// We want to return a role that makes the ComposeScene container "transparent" to
// accessibility, as if its contents are inside the parent directly.
// On macOS, PANEL is ignored by Java's a11y (see CAccessibility.ignoredRoles), but on
// Windows, it makes NVDA read it as "panel".
// On Windows, NVDA ignores UNKNOWN, but on macOS UNKNOWN causes VoiceOver to highlight
// the entire component when traversing via VoiceOver shortcuts.
return when (hostOs) {
OS.MacOS -> AccessibleRole.PANEL
else -> AccessibleRole.UNKNOWN
}
}

override fun getAccessibleStateSet(): AccessibleStateSet {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ internal class ComposeContainer(
this.windowContainer = windowContainer

if (layerType == LayerType.OnComponent && !useSwingGraphics) {
error("Unsupported LayerType.OnComponent might be used only with rendering to Swing graphics")
error("LayerType.OnComponent can only be used with rendering via Swing graphics")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ import androidx.compose.ui.scene.ComposeSceneInputHandler
import androidx.compose.ui.scene.ComposeScenePointer
import androidx.compose.ui.semantics.EmptySemanticsElement
import androidx.compose.ui.semantics.SemanticsOwner
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.createFontFamilyResolver
import androidx.compose.ui.text.input.TextInputService
import androidx.compose.ui.unit.Constraints
Expand Down Expand Up @@ -118,6 +120,11 @@ internal class RootNodeOwner(
// Consume the key event if we moved focus.
focusOwner.moveFocus(focusDirection)
}
.semantics {
// This makes the reported role of the root node "PANEL", which is ignored by VoiceOver
// (which is what we want).
isTraversalGroup = true
}
val owner: Owner = OwnerImpl(layoutDirection, coroutineContext)
val semanticsOwner = SemanticsOwner(owner.root)
var size by mutableStateOf(size)
Expand Down

0 comments on commit 7ad119c

Please sign in to comment.