@@ -14,16 +14,19 @@ use bevy_a11y::{
14
14
use bevy_app:: { App , Plugin , PostUpdate } ;
15
15
use bevy_derive:: { Deref , DerefMut } ;
16
16
use bevy_ecs:: {
17
- prelude:: { DetectChanges , Entity , EventReader , EventWriter } ,
17
+ prelude:: { DetectChanges , Entity , EventReader , EventWriter , ThreadLocal , ThreadLocalResource } ,
18
18
query:: With ,
19
- system:: { NonSend , NonSendMut , Query , Res , ResMut , Resource } ,
19
+ system:: { Query , Res , ResMut , Resource } ,
20
20
} ;
21
21
use bevy_hierarchy:: { Children , Parent } ;
22
22
use bevy_utils:: { default, HashMap } ;
23
23
use bevy_window:: { PrimaryWindow , Window , WindowClosed , WindowFocused } ;
24
24
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 ) ]
27
30
pub struct AccessKitAdapters ( pub HashMap < Entity , Adapter > ) ;
28
31
29
32
/// Maps window entities to their respective [`WinitActionHandler`]s.
@@ -43,35 +46,41 @@ impl ActionHandler for WinitActionHandler {
43
46
44
47
fn handle_window_focus (
45
48
focus : Res < Focus > ,
46
- adapters : NonSend < AccessKitAdapters > ,
47
49
mut focused : EventReader < WindowFocused > ,
50
+ mut main_thread : ThreadLocal ,
48
51
) {
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
+ }
62
68
}
63
- }
69
+ } ) ;
64
70
}
65
71
66
72
fn window_closed (
67
- mut adapters : NonSendMut < AccessKitAdapters > ,
68
73
mut receivers : ResMut < WinitActionHandlers > ,
69
74
mut events : EventReader < WindowClosed > ,
75
+ mut main_thread : ThreadLocal ,
70
76
) {
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
+ } ) ;
75
84
}
76
85
77
86
fn poll_receivers (
@@ -87,7 +96,6 @@ fn poll_receivers(
87
96
}
88
97
89
98
fn update_accessibility_nodes (
90
- adapters : NonSend < AccessKitAdapters > ,
91
99
focus : Res < Focus > ,
92
100
accessibility_requested : Res < AccessibilityRequested > ,
93
101
primary_window : Query < ( Entity , & Window ) , With < PrimaryWindow > > ,
@@ -98,70 +106,75 @@ fn update_accessibility_nodes(
98
106
Option < & Parent > ,
99
107
) > ,
100
108
node_entities : Query < Entity , With < AccessibilityNode > > ,
109
+ mut main_thread : ThreadLocal ,
101
110
) {
102
111
if !accessibility_requested. load ( Ordering :: SeqCst ) {
103
112
return ;
104
113
}
114
+
105
115
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 {
128
143
root_children. push ( entity. to_node_id ( ) ) ;
129
144
}
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
+ }
137
150
}
138
151
}
152
+ to_update. push ( (
153
+ entity. to_node_id ( ) ,
154
+ node. build ( & mut NodeClassSet :: lock_global ( ) ) ,
155
+ ) ) ;
139
156
}
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
+ }
159
172
}
160
- }
173
+ } ) ;
161
174
}
162
175
}
163
176
164
- /// Implements winit-specific `AccessKit` functionality.
177
+ /// Implements [` winit`] -specific `AccessKit` functionality.
165
178
pub struct AccessibilityPlugin ;
166
179
167
180
impl Plugin for AccessibilityPlugin {
0 commit comments