-
Notifications
You must be signed in to change notification settings - Fork 98
Support focus change with VoiceOver #2217
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
Changes from 1 commit
72716c3
2bb8497
3ecb854
92684f0
6e28d49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -721,6 +721,10 @@ private sealed interface AccessibilityElementFocusMode { | |
data class KeepFocus(val key: AccessibilityElementKey) : AccessibilityElementFocusMode { | ||
override val targetElementKey: AccessibilityElementKey = key | ||
} | ||
|
||
data class Focus(val key: AccessibilityElementKey) : AccessibilityElementFocusMode { | ||
override val targetElementKey: AccessibilityElementKey = key | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -746,6 +750,7 @@ internal class AccessibilityMediator( | |
private set | ||
|
||
var keyboardFocusedElementKey: AccessibilityElementKey? = null | ||
private var forceFocusedElementKey: AccessibilityElementKey? = null | ||
|
||
/** | ||
* A set of node ids that had their bounds invalidated after the last sync. | ||
|
@@ -987,7 +992,9 @@ internal class AccessibilityMediator( | |
* Inserts new elements to [accessibilityElementsMap], updates the old ones, and removes the elements | ||
* that are not present in the tree anymore. | ||
*/ | ||
private fun traverseSemanticsTree(rootNode: SemanticsNode): AccessibilityElement { | ||
private fun traverseSemanticsTree( | ||
rootNode: SemanticsNode | ||
): Pair<AccessibilityElement, AccessibilityElementKey?> { | ||
val presentIds = mutableSetOf<AccessibilityElementKey>() | ||
|
||
val nodes = owner.getAllUncoveredSemanticsNodesToIntObjectMap(rootNode.id) | ||
|
@@ -998,6 +1005,7 @@ internal class AccessibilityMediator( | |
refocusKeyboardElementIfNeeded() | ||
} | ||
} | ||
var focusedKey: AccessibilityElementKey? = null | ||
|
||
// 1. Flatten all children except nodes inside traversal groups to: | ||
// - have the same traversal order as on Android | ||
|
@@ -1038,6 +1046,9 @@ internal class AccessibilityMediator( | |
presentIds.add(node.semanticsKey) | ||
|
||
val frame = nodes[node.id]?.adjustedBounds?.toRect() ?: node.unclippedBoundsInWindow | ||
if (node.unmergedConfig.getOrNull(SemanticsProperties.Focused) == true) { | ||
svastven marked this conversation as resolved.
Show resolved
Hide resolved
|
||
focusedKey = node.semanticsKey | ||
} | ||
|
||
fun makeSemanticsNode() = createOrUpdateAccessibilityElement( | ||
node = AccessibilityNode.Semantics( | ||
|
@@ -1097,7 +1108,7 @@ internal class AccessibilityMediator( | |
isPresent | ||
} | ||
|
||
return rootAccessibilityElement | ||
return rootAccessibilityElement to focusedKey | ||
} | ||
|
||
/** | ||
|
@@ -1110,12 +1121,20 @@ internal class AccessibilityMediator( | |
"Root view must not be an accessibility element" | ||
} | ||
|
||
root.element = traverseSemanticsTree(rootSemanticsNode) | ||
val (element, focusedElementKey) = traverseSemanticsTree(rootSemanticsNode) | ||
root.element = element | ||
|
||
accessibilityDebugLogger?.let { | ||
debugTraverse(it, view) | ||
} | ||
|
||
if (forceFocusedElementKey != focusedElementKey) { | ||
forceFocusedElementKey = focusedElementKey | ||
focusedElementKey?.let { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if there is no focused elemet ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, we should keep the old value. The reason is that Compose Ficus and Accessibility focus are work in parallel. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, thanks for the explanation! |
||
focusMode = AccessibilityElementFocusMode.Focus(it) | ||
} | ||
} | ||
|
||
return updateFocusedElement() | ||
} | ||
|
||
|
@@ -1144,6 +1163,17 @@ internal class AccessibilityMediator( | |
NodesSyncResult(null, isScreenChange = false) | ||
} | ||
} | ||
|
||
is AccessibilityElementFocusMode.Focus -> { | ||
val element = accessibilityElementsMap[mode.key] | ||
if (element != null && !CGRectIsEmpty(element.accessibilityFrame())) { | ||
focusMode = AccessibilityElementFocusMode.KeepFocus(mode.key) | ||
NodesSyncResult(element, isScreenChange = false) | ||
} else { | ||
focusMode = AccessibilityElementFocusMode.None | ||
NodesSyncResult(null, isScreenChange = false) | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.