@@ -14,16 +14,19 @@ use bevy_a11y::{
1414use bevy_app:: { App , Plugin , PostUpdate } ;
1515use bevy_derive:: { Deref , DerefMut } ;
1616use bevy_ecs:: {
17- prelude:: { DetectChanges , Entity , EventReader , EventWriter } ,
17+ prelude:: { DetectChanges , Entity , EventReader , EventWriter , ThreadLocal , ThreadLocalResource } ,
1818 query:: With ,
19- system:: { NonSend , NonSendMut , Query , Res , ResMut , Resource } ,
19+ system:: { Query , Res , ResMut , Resource } ,
2020} ;
2121use bevy_hierarchy:: { Children , Parent } ;
2222use bevy_utils:: { default, HashMap } ;
2323use bevy_window:: { PrimaryWindow , Window , WindowClosed , WindowFocused } ;
2424
25- /// Maps window entities to their `AccessKit` [`Adapter`]s.
26- #[ derive( Default , Deref , DerefMut ) ]
25+ /// Maps each window entity to its `AccessKit` [`Adapter`].
26+ ///
27+ /// **Note:** This is a [`ThreadLocalResource`] because the macOS implementation of [`Adapter`]
28+ /// is not [`Send`](Send).
29+ #[ derive( ThreadLocalResource , Default , Deref , DerefMut ) ]
2730pub struct AccessKitAdapters ( pub HashMap < Entity , Adapter > ) ;
2831
2932/// Maps window entities to their respective [`WinitActionHandler`]s.
@@ -43,35 +46,41 @@ impl ActionHandler for WinitActionHandler {
4346
4447fn handle_window_focus (
4548 focus : Res < Focus > ,
46- adapters : NonSend < AccessKitAdapters > ,
4749 mut focused : EventReader < WindowFocused > ,
50+ mut main_thread : ThreadLocal ,
4851) {
49- for event in focused. read ( ) {
50- if let Some ( adapter) = adapters. get ( & event. window ) {
51- adapter. update_if_active ( || {
52- let focus_id = ( * focus) . unwrap_or_else ( || event. window ) ;
53- TreeUpdate {
54- focus : if event. focused {
55- Some ( focus_id. to_node_id ( ) )
56- } else {
57- None
58- } ,
59- ..default ( )
60- }
61- } ) ;
52+ main_thread. run ( |tls| {
53+ let adapters = tls. resource :: < AccessKitAdapters > ( ) ;
54+ for event in focused. read ( ) {
55+ if let Some ( adapter) = adapters. get ( & event. window ) {
56+ adapter. update_if_active ( || {
57+ let focus_id = ( * focus) . unwrap_or_else ( || event. window ) ;
58+ TreeUpdate {
59+ focus : if event. focused {
60+ Some ( focus_id. to_node_id ( ) )
61+ } else {
62+ None
63+ } ,
64+ ..default ( )
65+ }
66+ } ) ;
67+ }
6268 }
63- }
69+ } ) ;
6470}
6571
6672fn window_closed (
67- mut adapters : NonSendMut < AccessKitAdapters > ,
6873 mut receivers : ResMut < WinitActionHandlers > ,
6974 mut events : EventReader < WindowClosed > ,
75+ mut main_thread : ThreadLocal ,
7076) {
71- for WindowClosed { window, .. } in events. read ( ) {
72- adapters. remove ( window) ;
73- receivers. remove ( window) ;
74- }
77+ main_thread. run ( |tls| {
78+ let mut adapters = tls. resource_mut :: < AccessKitAdapters > ( ) ;
79+ for WindowClosed { window, .. } in events. read ( ) {
80+ adapters. remove ( window) ;
81+ receivers. remove ( window) ;
82+ }
83+ } ) ;
7584}
7685
7786fn poll_receivers (
@@ -87,7 +96,6 @@ fn poll_receivers(
8796}
8897
8998fn update_accessibility_nodes (
90- adapters : NonSend < AccessKitAdapters > ,
9199 focus : Res < Focus > ,
92100 accessibility_requested : Res < AccessibilityRequested > ,
93101 primary_window : Query < ( Entity , & Window ) , With < PrimaryWindow > > ,
@@ -98,70 +106,75 @@ fn update_accessibility_nodes(
98106 Option < & Parent > ,
99107 ) > ,
100108 node_entities : Query < Entity , With < AccessibilityNode > > ,
109+ mut main_thread : ThreadLocal ,
101110) {
102111 if !accessibility_requested. load ( Ordering :: SeqCst ) {
103112 return ;
104113 }
114+
105115 if let Ok ( ( primary_window_id, primary_window) ) = primary_window. get_single ( ) {
106- if let Some ( adapter) = adapters. get ( & primary_window_id) {
107- let should_run = focus. is_changed ( ) || !nodes. is_empty ( ) ;
108- if should_run {
109- adapter. update_if_active ( || {
110- let mut to_update = vec ! [ ] ;
111- let mut has_focus = false ;
112- let mut name = None ;
113- if primary_window. focused {
114- has_focus = true ;
115- let title = primary_window. title . clone ( ) ;
116- name = Some ( title. into_boxed_str ( ) ) ;
117- }
118- let focus_id = if has_focus {
119- ( * focus) . or_else ( || Some ( primary_window_id) )
120- } else {
121- None
122- } ;
123- let mut root_children = vec ! [ ] ;
124- for ( entity, node, children, parent) in & nodes {
125- let mut node = ( * * node) . clone ( ) ;
126- if let Some ( parent) = parent {
127- if node_entities. get ( * * parent) . is_err ( ) {
116+ main_thread. run ( |tls| {
117+ let adapters = tls. resource :: < AccessKitAdapters > ( ) ;
118+ if let Some ( adapter) = adapters. get ( & primary_window_id) {
119+ let should_run = focus. is_changed ( ) || !nodes. is_empty ( ) ;
120+ if should_run {
121+ adapter. update_if_active ( || {
122+ let mut to_update = vec ! [ ] ;
123+ let mut has_focus = false ;
124+ let mut name = None ;
125+ if primary_window. focused {
126+ has_focus = true ;
127+ let title = primary_window. title . clone ( ) ;
128+ name = Some ( title. into_boxed_str ( ) ) ;
129+ }
130+ let focus_id = if has_focus {
131+ ( * focus) . or_else ( || Some ( primary_window_id) )
132+ } else {
133+ None
134+ } ;
135+ let mut root_children = vec ! [ ] ;
136+ for ( entity, node, children, parent) in & nodes {
137+ let mut node = ( * * node) . clone ( ) ;
138+ if let Some ( parent) = parent {
139+ if node_entities. get ( * * parent) . is_err ( ) {
140+ root_children. push ( entity. to_node_id ( ) ) ;
141+ }
142+ } else {
128143 root_children. push ( entity. to_node_id ( ) ) ;
129144 }
130- } else {
131- root_children. push ( entity. to_node_id ( ) ) ;
132- }
133- if let Some ( children) = children {
134- for child in children {
135- if node_entities. get ( * child) . is_ok ( ) {
136- node. push_child ( child. to_node_id ( ) ) ;
145+ if let Some ( children) = children {
146+ for child in children {
147+ if node_entities. get ( * child) . is_ok ( ) {
148+ node. push_child ( child. to_node_id ( ) ) ;
149+ }
137150 }
138151 }
152+ to_update. push ( (
153+ entity. to_node_id ( ) ,
154+ node. build ( & mut NodeClassSet :: lock_global ( ) ) ,
155+ ) ) ;
139156 }
140- to_update. push ( (
141- entity. to_node_id ( ) ,
142- node. build ( & mut NodeClassSet :: lock_global ( ) ) ,
143- ) ) ;
144- }
145- let mut root = NodeBuilder :: new ( Role :: Window ) ;
146- if let Some ( name) = name {
147- root. set_name ( name) ;
148- }
149- root. set_children ( root_children) ;
150- let root = root. build ( & mut NodeClassSet :: lock_global ( ) ) ;
151- let window_update = ( primary_window_id. to_node_id ( ) , root) ;
152- to_update. insert ( 0 , window_update) ;
153- TreeUpdate {
154- nodes : to_update,
155- focus : focus_id. map ( |v| v. to_node_id ( ) ) ,
156- ..default ( )
157- }
158- } ) ;
157+ let mut root = NodeBuilder :: new ( Role :: Window ) ;
158+ if let Some ( name) = name {
159+ root. set_name ( name) ;
160+ }
161+ root. set_children ( root_children) ;
162+ let root = root. build ( & mut NodeClassSet :: lock_global ( ) ) ;
163+ let window_update = ( primary_window_id. to_node_id ( ) , root) ;
164+ to_update. insert ( 0 , window_update) ;
165+ TreeUpdate {
166+ nodes : to_update,
167+ focus : focus_id. map ( |v| v. to_node_id ( ) ) ,
168+ ..default ( )
169+ }
170+ } ) ;
171+ }
159172 }
160- }
173+ } ) ;
161174 }
162175}
163176
164- /// Implements winit-specific `AccessKit` functionality.
177+ /// Implements [` winit`] -specific `AccessKit` functionality.
165178pub struct AccessibilityPlugin ;
166179
167180impl Plugin for AccessibilityPlugin {
0 commit comments