bevy_ui/
interaction_states.rs

1/// This module contains components that are used to track the interaction state of UI widgets.
2use bevy_a11y::AccessibilityNode;
3use bevy_ecs::{
4    component::Component,
5    lifecycle::{Add, Remove},
6    observer::On,
7    world::DeferredWorld,
8};
9
10/// A component indicating that a widget is disabled and should be "grayed out".
11/// This is used to prevent user interaction with the widget. It should not, however, prevent
12/// the widget from being updated or rendered, or from acquiring keyboard focus.
13///
14/// For apps which support a11y: if a widget (such as a slider) contains multiple entities,
15/// the `InteractionDisabled` component should be added to the root entity of the widget - the
16/// same entity that contains the `AccessibilityNode` component. This will ensure that
17/// the a11y tree is updated correctly.
18#[derive(Component, Debug, Clone, Copy, Default)]
19pub struct InteractionDisabled;
20
21pub(crate) fn on_add_disabled(add: On<Add, InteractionDisabled>, mut world: DeferredWorld) {
22    let mut entity = world.entity_mut(add.entity);
23    if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
24        accessibility.set_disabled();
25    }
26}
27
28pub(crate) fn on_remove_disabled(
29    remove: On<Remove, InteractionDisabled>,
30    mut world: DeferredWorld,
31) {
32    let mut entity = world.entity_mut(remove.entity);
33    if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
34        accessibility.clear_disabled();
35    }
36}
37
38/// Component that indicates whether a button or widget is currently in a pressed or "held down"
39/// state.
40#[derive(Component, Default, Debug)]
41pub struct Pressed;
42
43/// Component that indicates that a widget can be checked.
44#[derive(Component, Default, Debug)]
45pub struct Checkable;
46
47/// Component that indicates whether a checkbox or radio button is in a checked state.
48#[derive(Component, Default, Debug)]
49pub struct Checked;
50
51pub(crate) fn on_add_checkable(add: On<Add, Checked>, mut world: DeferredWorld) {
52    let mut entity = world.entity_mut(add.entity);
53    let checked = entity.get::<Checked>().is_some();
54    if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
55        accessibility.set_toggled(match checked {
56            true => accesskit::Toggled::True,
57            false => accesskit::Toggled::False,
58        });
59    }
60}
61
62pub(crate) fn on_remove_checkable(add: On<Add, Checked>, mut world: DeferredWorld) {
63    // Remove the 'toggled' attribute entirely.
64    let mut entity = world.entity_mut(add.entity);
65    if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
66        accessibility.clear_toggled();
67    }
68}
69
70pub(crate) fn on_add_checked(add: On<Add, Checked>, mut world: DeferredWorld) {
71    let mut entity = world.entity_mut(add.entity);
72    if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
73        accessibility.set_toggled(accesskit::Toggled::True);
74    }
75}
76
77pub(crate) fn on_remove_checked(remove: On<Remove, Checked>, mut world: DeferredWorld) {
78    let mut entity = world.entity_mut(remove.entity);
79    if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
80        accessibility.set_toggled(accesskit::Toggled::False);
81    }
82}