Skip to content

Commit df21689

Browse files
authored
Call IAccessible::accFocus to move a11y focus (flutter#30256)
On receipt of a FOCUS_CHANGED event from the AX tree, call IAccessible::accFocus to tell screen readers to move the accessibility focus to that node. This is in addition to setting the keyboard focus via the EVENT_OBJECT_FOCUS event. Issue: flutter#77838
1 parent 8fd8912 commit df21689

File tree

5 files changed

+55
-4
lines changed

5 files changed

+55
-4
lines changed

shell/platform/windows/accessibility_bridge_delegate_win32.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ void AccessibilityBridgeDelegateWin32::OnAccessibilityEvent(
3939
break;
4040
case ui::AXEventGenerator::Event::FOCUS_CHANGED:
4141
DispatchWinAccessibilityEvent(win_delegate, EVENT_OBJECT_FOCUS);
42+
SetFocus(win_delegate);
4243
break;
4344
case ui::AXEventGenerator::Event::IGNORED_CHANGED:
4445
if (ax_node->IsIgnored()) {
@@ -152,4 +153,9 @@ void AccessibilityBridgeDelegateWin32::DispatchWinAccessibilityEvent(
152153
node_delegate->DispatchWinAccessibilityEvent(event_type);
153154
}
154155

156+
void AccessibilityBridgeDelegateWin32::SetFocus(
157+
std::shared_ptr<FlutterPlatformNodeDelegateWin32> node_delegate) {
158+
node_delegate->SetFocus();
159+
}
160+
155161
} // namespace flutter

shell/platform/windows/accessibility_bridge_delegate_win32.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ class AccessibilityBridgeDelegateWin32
4646
std::shared_ptr<FlutterPlatformNodeDelegateWin32> node_delegate,
4747
DWORD event_type);
4848

49+
// Sets the accessibility focus to the accessibility node associated with the
50+
// specified semantics node.
51+
virtual void SetFocus(
52+
std::shared_ptr<FlutterPlatformNodeDelegateWin32> node_delegate);
53+
4954
private:
5055
FlutterWindowsEngine* engine_;
5156
};

shell/platform/windows/accessibility_bridge_delegate_win32_unittests.cc

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,25 @@ class AccessibilityBridgeDelegateWin32Spy
4545
dispatched_events_.push_back({node_delegate, event_type});
4646
}
4747

48-
void Reset() { dispatched_events_.clear(); }
48+
void SetFocus(std::shared_ptr<FlutterPlatformNodeDelegateWin32> node_delegate)
49+
override {
50+
focused_nodes_.push_back(node_delegate->GetAXNode()->id());
51+
}
52+
53+
void Reset() {
54+
dispatched_events_.clear();
55+
focused_nodes_.clear();
56+
}
57+
4958
const std::vector<MsaaEvent>& dispatched_events() const {
5059
return dispatched_events_;
51-
};
60+
}
61+
62+
const std::vector<int32_t> focused_nodes() const { return focused_nodes_; }
5263

5364
private:
5465
std::vector<MsaaEvent> dispatched_events_;
66+
std::vector<int32_t> focused_nodes_;
5567
};
5668

5769
// Returns an engine instance configured with dummy project path values, and
@@ -203,8 +215,25 @@ TEST(AccessibilityBridgeDelegateWin32, OnAccessibilityEventChildrenChanged) {
203215
}
204216

205217
TEST(AccessibilityBridgeDelegateWin32, OnAccessibilityEventFocusChanged) {
206-
ExpectWinEventFromAXEvent(1, ui::AXEventGenerator::Event::FOCUS_CHANGED,
207-
EVENT_OBJECT_FOCUS);
218+
auto window_binding_handler =
219+
std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
220+
FlutterWindowsView view(std::move(window_binding_handler));
221+
view.SetEngine(GetTestEngine());
222+
view.OnUpdateSemanticsEnabled(true);
223+
224+
auto bridge = view.GetEngine()->accessibility_bridge().lock();
225+
PopulateAXTree(bridge);
226+
227+
AccessibilityBridgeDelegateWin32Spy spy(view.GetEngine());
228+
spy.OnAccessibilityEvent({AXNodeFromID(bridge, 1),
229+
{ui::AXEventGenerator::Event::FOCUS_CHANGED,
230+
ax::mojom::EventFrom::kNone,
231+
{}}});
232+
ASSERT_EQ(spy.dispatched_events().size(), 1);
233+
EXPECT_EQ(spy.dispatched_events()[0].event_type, EVENT_OBJECT_FOCUS);
234+
235+
ASSERT_EQ(spy.focused_nodes().size(), 1);
236+
EXPECT_EQ(spy.focused_nodes()[0], 1);
208237
}
209238

210239
TEST(AccessibilityBridgeDelegateWin32, OnAccessibilityEventIgnoredChanged) {

shell/platform/windows/flutter_platform_node_delegate_win32.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,11 @@ void FlutterPlatformNodeDelegateWin32::DispatchWinAccessibilityEvent(
9292
-ax_platform_node_->GetUniqueId());
9393
}
9494

95+
void FlutterPlatformNodeDelegateWin32::SetFocus() {
96+
VARIANT varchild{};
97+
varchild.vt = VT_I4;
98+
varchild.lVal = CHILDID_SELF;
99+
GetNativeViewAccessible()->accSelect(SELFLAG_TAKEFOCUS, varchild);
100+
}
101+
95102
} // namespace flutter

shell/platform/windows/flutter_platform_node_delegate_win32.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class FlutterPlatformNodeDelegateWin32 : public FlutterPlatformNodeDelegate {
4545
// convenience wrapper around |NotifyWinEvent|.
4646
virtual void DispatchWinAccessibilityEvent(DWORD event_type);
4747

48+
// Sets the accessibility focus to the accessibility node associated with
49+
// this object.
50+
void SetFocus();
51+
4852
private:
4953
ui::AXPlatformNode* ax_platform_node_;
5054
FlutterWindowsEngine* engine_;

0 commit comments

Comments
 (0)