Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report the role of ComposeSceneAccessible on macOS as PANEL #1362

Merged
merged 1 commit into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading