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