Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_a11y/src/lib.rs
6598 views
1
#![forbid(unsafe_code)]
2
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3
#![doc(
4
html_logo_url = "https://bevy.org/assets/icon.png",
5
html_favicon_url = "https://bevy.org/assets/icon.png"
6
)]
7
#![no_std]
8
9
//! Reusable accessibility primitives
10
//!
11
//! This crate provides accessibility integration for the engine. It exposes the
12
//! [`AccessibilityPlugin`]. This plugin integrates `AccessKit`, a Rust crate
13
//! providing OS-agnostic accessibility primitives, with Bevy's ECS.
14
//!
15
//! ## Some notes on utility
16
//!
17
//! While this crate defines useful types for accessibility, it does not
18
//! actually power accessibility features in Bevy.
19
//!
20
//! Instead, it helps other interfaces coordinate their approach to
21
//! accessibility. Binary authors should add the [`AccessibilityPlugin`], while
22
//! library maintainers may use the [`AccessibilityRequested`] and
23
//! [`ManageAccessibilityUpdates`] resources.
24
//!
25
//! The [`AccessibilityNode`] component is useful in both cases. It helps
26
//! describe an entity in terms of its accessibility factors through an
27
//! `AccessKit` "node".
28
//!
29
//! Typical UI concepts, like buttons, checkboxes, and textboxes, are easily
30
//! described by this component, though, technically, it can represent any kind
31
//! of Bevy [`Entity`].
32
//!
33
//! ## This crate no longer re-exports `AccessKit`
34
//!
35
//! As of Bevy version 0.15, [the `accesskit` crate][accesskit_crate] is no
36
//! longer re-exported from this crate.[^accesskit_node_confusion] If you need
37
//! to use `AccessKit` yourself, you'll have to add it as a separate dependency
38
//! in your project's `Cargo.toml`.
39
//!
40
//! Make sure to use the same version of the `accesskit` crate as Bevy.
41
//! Otherwise, you may experience errors similar to: "Perhaps two different
42
//! versions of crate `accesskit` are being used?"
43
//!
44
//! [accesskit_crate]: https://crates.io/crates/accesskit
45
//! [`Entity`]: bevy_ecs::entity::Entity
46
//!
47
//! <!--
48
//! note: multi-line footnotes need to be indented like this!
49
//!
50
//! please do not remove the indentation, or the second paragraph will display
51
//! at the end of the module docs, **before** the footnotes...
52
//! -->
53
//!
54
//! [^accesskit_node_confusion]: Some users were confused about `AccessKit`'s
55
//! `Node` type, sometimes thinking it was Bevy UI's primary way to define
56
//! nodes!
57
//!
58
//! For this reason, its re-export was removed by default. Users who need
59
//! its types can instead manually depend on the `accesskit` crate.
60
61
#[cfg(feature = "std")]
62
extern crate std;
63
64
extern crate alloc;
65
66
use alloc::sync::Arc;
67
use core::sync::atomic::{AtomicBool, Ordering};
68
69
use accesskit::Node;
70
use bevy_app::Plugin;
71
use bevy_derive::{Deref, DerefMut};
72
use bevy_ecs::{
73
component::Component, event::BufferedEvent, resource::Resource, schedule::SystemSet,
74
};
75
76
#[cfg(feature = "bevy_reflect")]
77
use {
78
bevy_ecs::reflect::ReflectResource, bevy_reflect::std_traits::ReflectDefault,
79
bevy_reflect::Reflect,
80
};
81
82
#[cfg(feature = "serialize")]
83
use serde::{Deserialize, Serialize};
84
85
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
86
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
87
88
/// Wrapper struct for [`accesskit::ActionRequest`].
89
///
90
/// This newtype is required to use `ActionRequest` as a Bevy `Event`.
91
#[derive(BufferedEvent, Deref, DerefMut)]
92
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
93
pub struct ActionRequest(pub accesskit::ActionRequest);
94
95
/// Tracks whether an assistive technology has requested accessibility
96
/// information.
97
///
98
/// This type is a [`Resource`] initialized by the
99
/// [`AccessibilityPlugin`]. It may be useful if a third-party plugin needs to
100
/// conditionally integrate with `AccessKit`.
101
///
102
/// In other words, this resource represents whether accessibility providers
103
/// are "turned on" or "turned off" across an entire Bevy `App`.
104
///
105
/// By default, it is set to `false`, indicating that nothing has requested
106
/// accessibility information yet.
107
///
108
/// [`Resource`]: bevy_ecs::resource::Resource
109
#[derive(Resource, Default, Clone, Debug, Deref, DerefMut)]
110
#[cfg_attr(
111
feature = "bevy_reflect",
112
derive(Reflect),
113
reflect(Default, Clone, Resource)
114
)]
115
pub struct AccessibilityRequested(Arc<AtomicBool>);
116
117
impl AccessibilityRequested {
118
/// Checks if any assistive technology has requested accessibility
119
/// information.
120
///
121
/// If so, this method returns `true`, indicating that accessibility tree
122
/// updates should be sent.
123
pub fn get(&self) -> bool {
124
self.load(Ordering::SeqCst)
125
}
126
127
/// Sets the app's preference for sending accessibility updates.
128
///
129
/// If the `value` argument is `true`, this method requests that the app,
130
/// including both Bevy and third-party interfaces, provides updates to
131
/// accessibility information.
132
///
133
/// Setting with `false` requests that the entire app stops providing these
134
/// updates.
135
pub fn set(&self, value: bool) {
136
self.store(value, Ordering::SeqCst);
137
}
138
}
139
140
/// Determines whether Bevy's ECS updates the accessibility tree.
141
///
142
/// This [`Resource`] tells Bevy internals whether it should be handling
143
/// `AccessKit` updates (`true`), or if something else is doing that (`false`).
144
///
145
/// It defaults to `true`. So, by default, Bevy is configured to maintain the
146
/// `AccessKit` tree.
147
///
148
/// Set to `false` in cases where an external GUI library is sending
149
/// accessibility updates instead. When this option is set inconsistently with
150
/// that requirement, the external library and ECS will generate conflicting
151
/// updates.
152
///
153
/// [`Resource`]: bevy_ecs::resource::Resource
154
#[derive(Resource, Clone, Debug, Deref, DerefMut)]
155
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
156
#[cfg_attr(
157
feature = "bevy_reflect",
158
derive(Reflect),
159
reflect(Resource, Clone, Default)
160
)]
161
#[cfg_attr(
162
all(feature = "bevy_reflect", feature = "serialize"),
163
reflect(Serialize, Deserialize)
164
)]
165
pub struct ManageAccessibilityUpdates(bool);
166
167
impl Default for ManageAccessibilityUpdates {
168
fn default() -> Self {
169
Self(true)
170
}
171
}
172
173
impl ManageAccessibilityUpdates {
174
/// Returns `true` if Bevy's ECS should update the accessibility tree.
175
pub fn get(&self) -> bool {
176
self.0
177
}
178
179
/// Sets whether Bevy's ECS should update the accessibility tree.
180
pub fn set(&mut self, value: bool) {
181
self.0 = value;
182
}
183
}
184
185
/// Represents an entity to `AccessKit` through an [`accesskit::Node`].
186
///
187
/// Platform-specific accessibility APIs utilize `AccessKit` nodes in their
188
/// accessibility frameworks. So, this component acts as a translation between
189
/// "Bevy entity" and "platform-agnostic accessibility element".
190
///
191
/// ## Organization in the `AccessKit` Accessibility Tree
192
///
193
/// `AccessKit` allows users to form a "tree of nodes" providing accessibility
194
/// information. That tree is **not** Bevy's ECS!
195
///
196
/// To explain, let's say this component is added to an entity, `E`.
197
///
198
/// ### Parent and Child
199
///
200
/// If `E` has a parent, `P`, and `P` also has this `AccessibilityNode`
201
/// component, then `E`'s `AccessKit` node will be a child of `P`'s `AccessKit`
202
/// node.
203
///
204
/// Resulting `AccessKit` tree:
205
/// - P
206
/// - E
207
///
208
/// In other words, parent-child relationships are maintained, but only if both
209
/// have this component.
210
///
211
/// ### On the Window
212
///
213
/// If `E` doesn't have a parent, or if the immediate parent doesn't have an
214
/// `AccessibilityNode`, its `AccessKit` node will be an immediate child of the
215
/// primary window.
216
///
217
/// Resulting `AccessKit` tree:
218
/// - Primary window
219
/// - E
220
///
221
/// When there's no `AccessKit`-compatible parent, the child lacks hierarchical
222
/// information in `AccessKit`. As such, it is placed directly under the
223
/// primary window on the `AccessKit` tree.
224
///
225
/// This behavior may or may not be intended, so please utilize
226
/// `AccessibilityNode`s with care.
227
#[derive(Component, Clone, Deref, DerefMut)]
228
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
229
pub struct AccessibilityNode(
230
/// A representation of this component's entity to `AccessKit`.
231
///
232
/// Note that, with its parent struct acting as just a newtype, users are
233
/// intended to directly update this field.
234
pub Node,
235
);
236
237
impl From<Node> for AccessibilityNode {
238
/// Converts an [`accesskit::Node`] into the Bevy Engine
239
/// [`AccessibilityNode`] newtype.
240
///
241
/// Doing so allows it to be inserted onto Bevy entities, representing Bevy
242
/// entities in the `AccessKit` tree.
243
fn from(node: Node) -> Self {
244
Self(node)
245
}
246
}
247
248
/// A system set relating to accessibility.
249
///
250
/// Helps run accessibility updates all at once.
251
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
252
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
253
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
254
#[cfg_attr(
255
all(feature = "bevy_reflect", feature = "serialize"),
256
reflect(Serialize, Deserialize, Clone)
257
)]
258
pub enum AccessibilitySystems {
259
/// Update the accessibility tree.
260
Update,
261
}
262
263
/// Deprecated alias for [`AccessibilitySystems`].
264
#[deprecated(since = "0.17.0", note = "Renamed to `AccessibilitySystems`.")]
265
pub type AccessibilitySystem = AccessibilitySystems;
266
267
/// Plugin managing integration with accessibility APIs.
268
///
269
/// Note that it doesn't handle GUI aspects of this integration, instead
270
/// providing helpful resources for other interfaces to utilize.
271
///
272
/// ## Behavior
273
///
274
/// This plugin's main role is to initialize the [`AccessibilityRequested`] and
275
/// [`ManageAccessibilityUpdates`] resources to their default values, meaning:
276
///
277
/// - no assistive technologies have requested accessibility information yet,
278
/// and
279
/// - Bevy's ECS will manage updates to the accessibility tree.
280
#[derive(Default)]
281
pub struct AccessibilityPlugin;
282
283
impl Plugin for AccessibilityPlugin {
284
fn build(&self, app: &mut bevy_app::App) {
285
app.init_resource::<AccessibilityRequested>()
286
.init_resource::<ManageAccessibilityUpdates>()
287
.allow_ambiguous_component::<AccessibilityNode>();
288
}
289
}
290
291