1use alloc::{collections::VecDeque, sync::Arc};
4use bevy_input_focus::InputFocus;
5use core::cell::RefCell;
6use std::sync::Mutex;
7use winit::event_loop::ActiveEventLoop;
8
9use accesskit::{
10 ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, Node, NodeId, Role, Tree,
11 TreeUpdate,
12};
13use accesskit_winit::Adapter;
14use bevy_a11y::{
15 AccessibilityNode, AccessibilityRequested, AccessibilitySystems,
16 ActionRequest as ActionRequestWrapper, ManageAccessibilityUpdates,
17};
18use bevy_app::{App, Plugin, PostUpdate};
19use bevy_derive::{Deref, DerefMut};
20use bevy_ecs::{entity::EntityHashMap, prelude::*, system::NonSendMarker};
21use bevy_window::{PrimaryWindow, Window, WindowClosed};
22
23thread_local! {
24 pub static ACCESS_KIT_ADAPTERS: RefCell<AccessKitAdapters> = const { RefCell::new(AccessKitAdapters::new()) };
27}
28
29#[derive(Default, Deref, DerefMut)]
31pub struct AccessKitAdapters(pub EntityHashMap<Adapter>);
32
33impl AccessKitAdapters {
34 pub const fn new() -> Self {
36 Self(EntityHashMap::new())
37 }
38}
39
40#[derive(Resource, Default, Deref, DerefMut)]
42pub struct WinitActionRequestHandlers(pub EntityHashMap<Arc<Mutex<WinitActionRequestHandler>>>);
43
44#[derive(Clone, Default, Deref, DerefMut)]
46pub struct WinitActionRequestHandler(pub VecDeque<ActionRequest>);
47
48impl WinitActionRequestHandler {
49 fn new() -> Arc<Mutex<Self>> {
50 Arc::new(Mutex::new(Self(VecDeque::new())))
51 }
52}
53
54struct AccessKitState {
55 name: String,
56 entity: Entity,
57 requested: AccessibilityRequested,
58}
59
60impl AccessKitState {
61 fn new(
62 name: impl Into<String>,
63 entity: Entity,
64 requested: AccessibilityRequested,
65 ) -> Arc<Mutex<Self>> {
66 let name = name.into();
67
68 Arc::new(Mutex::new(Self {
69 name,
70 entity,
71 requested,
72 }))
73 }
74
75 fn build_root(&mut self) -> Node {
76 let mut node = Node::new(Role::Window);
77 node.set_label(self.name.clone());
78 node
79 }
80
81 fn build_initial_tree(&mut self) -> TreeUpdate {
82 let root = self.build_root();
83 let accesskit_window_id = NodeId(self.entity.to_bits());
84 let tree = Tree::new(accesskit_window_id);
85 self.requested.set(true);
86
87 TreeUpdate {
88 nodes: vec![(accesskit_window_id, root)],
89 tree: Some(tree),
90 focus: accesskit_window_id,
91 }
92 }
93}
94
95struct WinitActivationHandler(Arc<Mutex<AccessKitState>>);
96
97impl ActivationHandler for WinitActivationHandler {
98 fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
99 Some(self.0.lock().unwrap().build_initial_tree())
100 }
101}
102
103impl WinitActivationHandler {
104 pub fn new(state: Arc<Mutex<AccessKitState>>) -> Self {
105 Self(state)
106 }
107}
108
109#[derive(Clone, Default)]
110struct WinitActionHandler(Arc<Mutex<WinitActionRequestHandler>>);
111
112impl ActionHandler for WinitActionHandler {
113 fn do_action(&mut self, request: ActionRequest) {
114 let mut requests = self.0.lock().unwrap();
115 requests.push_back(request);
116 }
117}
118
119impl WinitActionHandler {
120 pub fn new(handler: Arc<Mutex<WinitActionRequestHandler>>) -> Self {
121 Self(handler)
122 }
123}
124
125struct WinitDeactivationHandler;
126
127impl DeactivationHandler for WinitDeactivationHandler {
128 fn deactivate_accessibility(&mut self) {}
129}
130
131pub(crate) fn prepare_accessibility_for_window(
133 event_loop: &ActiveEventLoop,
134 winit_window: &winit::window::Window,
135 entity: Entity,
136 name: String,
137 accessibility_requested: AccessibilityRequested,
138 adapters: &mut AccessKitAdapters,
139 handlers: &mut WinitActionRequestHandlers,
140) {
141 let state = AccessKitState::new(name, entity, accessibility_requested);
142 let activation_handler = WinitActivationHandler::new(Arc::clone(&state));
143
144 let action_request_handler = WinitActionRequestHandler::new();
145 let action_handler = WinitActionHandler::new(Arc::clone(&action_request_handler));
146 let deactivation_handler = WinitDeactivationHandler;
147
148 let adapter = Adapter::with_direct_handlers(
149 event_loop,
150 winit_window,
151 activation_handler,
152 action_handler,
153 deactivation_handler,
154 );
155
156 adapters.insert(entity, adapter);
157 handlers.insert(entity, action_request_handler);
158}
159
160fn window_closed(
161 mut handlers: ResMut<WinitActionRequestHandlers>,
162 mut window_closed_reader: MessageReader<WindowClosed>,
163 _non_send_marker: NonSendMarker,
164) {
165 ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
166 for WindowClosed { window, .. } in window_closed_reader.read() {
167 adapters.remove(window);
168 handlers.remove(window);
169 }
170 });
171}
172
173fn poll_receivers(
174 handlers: Res<WinitActionRequestHandlers>,
175 mut actions: MessageWriter<ActionRequestWrapper>,
176) {
177 for (_id, handler) in handlers.iter() {
178 let mut handler = handler.lock().unwrap();
179 while let Some(event) = handler.pop_front() {
180 actions.write(ActionRequestWrapper(event));
181 }
182 }
183}
184
185fn should_update_accessibility_nodes(
186 accessibility_requested: Res<AccessibilityRequested>,
187 manage_accessibility_updates: Res<ManageAccessibilityUpdates>,
188) -> bool {
189 accessibility_requested.get() && manage_accessibility_updates.get()
190}
191
192fn update_accessibility_nodes(
193 focus: Option<Res<InputFocus>>,
194 primary_window: Query<(Entity, &Window), With<PrimaryWindow>>,
195 nodes: Query<(
196 Entity,
197 &AccessibilityNode,
198 Option<&Children>,
199 Option<&ChildOf>,
200 )>,
201 node_entities: Query<Entity, With<AccessibilityNode>>,
202 _non_send_marker: NonSendMarker,
203) {
204 ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
205 let Ok((primary_window_id, primary_window)) = primary_window.single() else {
206 return;
207 };
208 let Some(adapter) = adapters.get_mut(&primary_window_id) else {
209 return;
210 };
211 let Some(focus) = focus else {
212 return;
213 };
214 if focus.is_changed() || !nodes.is_empty() {
215 if let Some(focused_entity) = focus.0
218 && !node_entities.contains(focused_entity)
219 {
220 return;
221 }
222
223 adapter.update_if_active(|| {
224 update_adapter(
225 nodes,
226 node_entities,
227 primary_window,
228 primary_window_id,
229 focus,
230 )
231 });
232 }
233 });
234}
235
236fn update_adapter(
237 nodes: Query<(
238 Entity,
239 &AccessibilityNode,
240 Option<&Children>,
241 Option<&ChildOf>,
242 )>,
243 node_entities: Query<Entity, With<AccessibilityNode>>,
244 primary_window: &Window,
245 primary_window_id: Entity,
246 focus: Res<InputFocus>,
247) -> TreeUpdate {
248 let mut to_update = vec![];
249 let mut window_children = vec![];
250 for (entity, node, children, child_of) in &nodes {
251 let mut node = (**node).clone();
252 queue_node_for_update(entity, child_of, &node_entities, &mut window_children);
253 add_children_nodes(children, &node_entities, &mut node);
254 let node_id = NodeId(entity.to_bits());
255 to_update.push((node_id, node));
256 }
257 let mut window_node = Node::new(Role::Window);
258 if primary_window.focused {
259 let title = primary_window.title.clone();
260 window_node.set_label(title.into_boxed_str());
261 }
262 window_node.set_children(window_children);
263 let node_id = NodeId(primary_window_id.to_bits());
264 let window_update = (node_id, window_node);
265 to_update.insert(0, window_update);
266 TreeUpdate {
267 nodes: to_update,
268 tree: None,
269 focus: NodeId(focus.0.unwrap_or(primary_window_id).to_bits()),
270 }
271}
272
273#[inline]
274fn queue_node_for_update(
275 node_entity: Entity,
276 child_of: Option<&ChildOf>,
277 node_entities: &Query<Entity, With<AccessibilityNode>>,
278 window_children: &mut Vec<NodeId>,
279) {
280 let should_push = if let Some(child_of) = child_of {
281 !node_entities.contains(child_of.parent())
282 } else {
283 true
284 };
285 if should_push {
286 window_children.push(NodeId(node_entity.to_bits()));
287 }
288}
289
290#[inline]
291fn add_children_nodes(
292 children: Option<&Children>,
293 node_entities: &Query<Entity, With<AccessibilityNode>>,
294 node: &mut Node,
295) {
296 let Some(children) = children else {
297 return;
298 };
299 for child in children {
300 if node_entities.contains(*child) {
301 node.push_child(NodeId(child.to_bits()));
302 }
303 }
304}
305
306pub struct AccessKitPlugin;
308
309impl Plugin for AccessKitPlugin {
310 fn build(&self, app: &mut App) {
311 app.init_resource::<WinitActionRequestHandlers>()
312 .add_message::<ActionRequestWrapper>()
313 .add_systems(
314 PostUpdate,
315 (
316 poll_receivers,
317 update_accessibility_nodes.run_if(should_update_accessibility_nodes),
318 window_closed
319 .before(poll_receivers)
320 .before(update_accessibility_nodes),
321 )
322 .in_set(AccessibilitySystems::Update),
323 );
324 }
325}