bevy_a11y/
lib.rs

1#![forbid(unsafe_code)]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#![doc(
4    html_logo_url = "https://bevyengine.org/assets/icon.png",
5    html_favicon_url = "https://bevyengine.org/assets/icon.png"
6)]
7#![no_std]
8
9//! Accessibility for Bevy
10//!
11//! As of Bevy version 0.15 `accesskit` is no longer re-exported from this crate.
12//!
13//! If you need to use `accesskit`, you will need to add it as a separate dependency in your `Cargo.toml`.
14//!
15//! Make sure to use the same version of `accesskit` as Bevy.
16
17#[cfg(feature = "std")]
18extern crate std;
19
20extern crate alloc;
21
22use alloc::sync::Arc;
23use core::sync::atomic::{AtomicBool, Ordering};
24
25use accesskit::Node;
26use bevy_app::Plugin;
27use bevy_derive::{Deref, DerefMut};
28use bevy_ecs::{
29    prelude::{Component, Event},
30    resource::Resource,
31    schedule::SystemSet,
32};
33
34#[cfg(feature = "bevy_reflect")]
35use {
36    bevy_ecs::reflect::ReflectResource, bevy_reflect::std_traits::ReflectDefault,
37    bevy_reflect::Reflect,
38};
39
40#[cfg(feature = "serialize")]
41use serde::{Deserialize, Serialize};
42
43#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
44use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
45
46/// Wrapper struct for [`accesskit::ActionRequest`]. Required to allow it to be used as an `Event`.
47#[derive(Event, Deref, DerefMut)]
48#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
49pub struct ActionRequest(pub accesskit::ActionRequest);
50
51/// Resource that tracks whether an assistive technology has requested
52/// accessibility information.
53///
54/// Useful if a third-party plugin needs to conditionally integrate with
55/// `AccessKit`
56#[derive(Resource, Default, Clone, Debug, Deref, DerefMut)]
57#[cfg_attr(
58    feature = "bevy_reflect",
59    derive(Reflect),
60    reflect(Default, Clone, Resource)
61)]
62pub struct AccessibilityRequested(Arc<AtomicBool>);
63
64impl AccessibilityRequested {
65    /// Returns `true` if an access technology is active and accessibility tree
66    /// updates should be sent.
67    pub fn get(&self) -> bool {
68        self.load(Ordering::SeqCst)
69    }
70
71    /// Sets whether accessibility updates were requested by an access technology.
72    pub fn set(&self, value: bool) {
73        self.store(value, Ordering::SeqCst);
74    }
75}
76
77/// Resource whose value determines whether the accessibility tree is updated
78/// via the ECS.
79///
80/// Set to `false` in cases where an external GUI library is sending
81/// accessibility updates instead. Without this, the external library and ECS
82/// will generate conflicting updates.
83#[derive(Resource, Clone, Debug, Deref, DerefMut)]
84#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
85#[cfg_attr(
86    feature = "bevy_reflect",
87    derive(Reflect),
88    reflect(Resource, Clone, Default)
89)]
90#[cfg_attr(
91    all(feature = "bevy_reflect", feature = "serialize"),
92    reflect(Serialize, Deserialize)
93)]
94pub struct ManageAccessibilityUpdates(bool);
95
96impl Default for ManageAccessibilityUpdates {
97    fn default() -> Self {
98        Self(true)
99    }
100}
101
102impl ManageAccessibilityUpdates {
103    /// Returns `true` if the ECS should update the accessibility tree.
104    pub fn get(&self) -> bool {
105        self.0
106    }
107
108    /// Sets whether the ECS should update the accessibility tree.
109    pub fn set(&mut self, value: bool) {
110        self.0 = value;
111    }
112}
113
114/// Component to wrap a [`accesskit::Node`], representing this entity to the platform's
115/// accessibility API.
116///
117/// If an entity has a parent, and that parent also has an `AccessibilityNode`,
118/// the entity's node will be a child of the parent's node.
119///
120/// If the entity doesn't have a parent, or if the immediate parent doesn't have
121/// an `AccessibilityNode`, its node will be an immediate child of the primary window.
122#[derive(Component, Clone, Deref, DerefMut)]
123#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
124pub struct AccessibilityNode(pub Node);
125
126impl From<Node> for AccessibilityNode {
127    fn from(node: Node) -> Self {
128        Self(node)
129    }
130}
131
132/// Set enum for the systems relating to accessibility
133#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
134#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
135#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
136#[cfg_attr(
137    all(feature = "bevy_reflect", feature = "serialize"),
138    reflect(Serialize, Deserialize, Clone)
139)]
140pub enum AccessibilitySystem {
141    /// Update the accessibility tree
142    Update,
143}
144
145/// Plugin managing non-GUI aspects of integrating with accessibility APIs.
146#[derive(Default)]
147pub struct AccessibilityPlugin;
148
149impl Plugin for AccessibilityPlugin {
150    fn build(&self, app: &mut bevy_app::App) {
151        app.init_resource::<AccessibilityRequested>()
152            .init_resource::<ManageAccessibilityUpdates>()
153            .allow_ambiguous_component::<AccessibilityNode>();
154    }
155}