diff --git a/engine/src/flutter/lib/ui/semantics.dart b/engine/src/flutter/lib/ui/semantics.dart index 1735dd1f39..f5104ed3c1 100644 --- a/engine/src/flutter/lib/ui/semantics.dart +++ b/engine/src/flutter/lib/ui/semantics.dart @@ -24,6 +24,8 @@ class SemanticsAction { static const int _kCopyIndex = 1 << 12; static const int _kCutIndex = 1 << 13; static const int _kPasteIndex = 1 << 14; + static const int _kDidGainAccessibilityFocusIndex = 1 << 15; + static const int _kDidLoseAccessibilityFocusIndex = 1 << 16; /// The numerical value for this action. /// @@ -118,6 +120,32 @@ class SemanticsAction { /// Paste the current content of the clipboard. static const SemanticsAction paste = const SemanticsAction._(_kPasteIndex); + /// Indicates that the nodes has gained accessibility focus. + /// + /// This handler is invoked when the node annotated with this handler gains + /// the accessibility focus. The accessibility focus is the + /// green (on Android with TalkBack) or black (on iOS with VoiceOver) + /// rectangle shown on screen to indicate what element an accessibility + /// user is currently interacting with. + /// + /// The accessibility focus is different from the input focus. The input focus + /// is usually held by the element that currently responds to keyboard inputs. + /// Accessibility focus and input focus can be held by two different nodes! + static const SemanticsAction didGainAccessibilityFocus = const SemanticsAction._(_kDidGainAccessibilityFocusIndex); + + /// Indicates that the nodes has lost accessibility focus. + /// + /// This handler is invoked when the node annotated with this handler + /// loses the accessibility focus. The accessibility focus is + /// the green (on Android with TalkBack) or black (on iOS with VoiceOver) + /// rectangle shown on screen to indicate what element an accessibility + /// user is currently interacting with. + /// + /// The accessibility focus is different from the input focus. The input focus + /// is usually held by the element that currently responds to keyboard inputs. + /// Accessibility focus and input focus can be held by two different nodes! + static const SemanticsAction didLoseAccessibilityFocus = const SemanticsAction._(_kDidLoseAccessibilityFocusIndex); + /// The possible semantics actions. /// /// The map's key is the [index] of the action and the value is the action @@ -138,6 +166,8 @@ class SemanticsAction { _kCopyIndex: copy, _kCutIndex: cut, _kPasteIndex: paste, + _kDidGainAccessibilityFocusIndex: didGainAccessibilityFocus, + _kDidLoseAccessibilityFocusIndex: didLoseAccessibilityFocus, }; @override @@ -173,6 +203,10 @@ class SemanticsAction { return 'SemanticsAction.cut'; case _kPasteIndex: return 'SemanticsAction.paste'; + case _kDidGainAccessibilityFocusIndex: + return 'SemanticsAction.didGainAccessibilityFocus'; + case _kDidLoseAccessibilityFocusIndex: + return 'SemanticsAction.didLoseAccessibilityFocus'; } return null; } diff --git a/engine/src/flutter/lib/ui/semantics/semantics_node.h b/engine/src/flutter/lib/ui/semantics/semantics_node.h index a159613857..c69fb20268 100644 --- a/engine/src/flutter/lib/ui/semantics/semantics_node.h +++ b/engine/src/flutter/lib/ui/semantics/semantics_node.h @@ -26,6 +26,15 @@ enum class SemanticsAction : int32_t { kScrollDown = 1 << 5, kIncrease = 1 << 6, kDecrease = 1 << 7, + kShowOnScreen = 1 << 8, + kMoveCursorForwardByCharacter = 1 << 9, + kMoveCursorBackwardByCharacter = 1 << 10, + kSetSelection = 1 << 11, + kCopy = 1 << 12, + kCut = 1 << 13, + kPaste = 1 << 14, + kDidGainAccessibilityFocus = 1 << 15, + kDidLoseAccessibilityFocus = 1 << 16, }; const int kScrollableSemanticsActions = diff --git a/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 66226af16d..079135a34b 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -63,7 +63,9 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess SET_SELECTION(1 << 11), COPY(1 << 12), CUT(1 << 13), - PASTE(1 << 14); + PASTE(1 << 14), + DID_GAIN_ACCESSIBILITY_FOCUS(1 << 15), + DID_LOSE_ACCESSIBILITY_FOCUS(1 << 16); Action(int value) { this.value = value; @@ -312,11 +314,13 @@ class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMess return performCursorMoveAction(object, virtualViewId, arguments, true); } case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: { + mOwner.dispatchSemanticsAction(virtualViewId, Action.DID_LOSE_ACCESSIBILITY_FOCUS); sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); mA11yFocusedObject = null; return true; } case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: { + mOwner.dispatchSemanticsAction(virtualViewId, Action.DID_GAIN_ACCESSIBILITY_FOCUS); sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); if (mA11yFocusedObject == null) { diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 5ae1ed9ec5..299a615c32 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -257,6 +257,20 @@ bool GeometryComparator(SemanticsObject* a, SemanticsObject* b) { return YES; } +#pragma mark UIAccessibilityFocus overrides + +- (void)accessibilityElementDidBecomeFocused { + if ([self node].HasAction(blink::SemanticsAction::kDidGainAccessibilityFocus)) { + [self bridge] -> DispatchSemanticsAction([self uid], blink::SemanticsAction::kDidGainAccessibilityFocus); + } +} + +- (void)accessibilityElementDidLoseFocus { + if ([self node].HasAction(blink::SemanticsAction::kDidLoseAccessibilityFocus)) { + [self bridge] -> DispatchSemanticsAction([self uid], blink::SemanticsAction::kDidLoseAccessibilityFocus); + } +} + @end @implementation FlutterSemanticsObject { diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm index 6d786ed59d..c15704c7c3 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm @@ -224,10 +224,12 @@ - (void)accessibilityElementDidBecomeFocused { [[self textInputSurrogate] accessibilityElementDidBecomeFocused]; + [super accessibilityElementDidBecomeFocused]; } - (void)accessibilityElementDidLoseFocus { [[self textInputSurrogate] accessibilityElementDidLoseFocus]; + [super accessibilityElementDidLoseFocus]; } - (BOOL)accessibilityElementIsFocused {