bevy/crates/bevy_ui/src/interaction_states.rs
Talin 9fdddf7089
Core Checkbox (#19665)
# Objective

This is part of the "core widgets" effort:
https://github.com/bevyengine/bevy/issues/19236.

## Solution

This adds the "core checkbox" widget type.

## Testing

Tested using examples core_widgets and core_widgets_observers.

Note to reviewers: I reorganized the code in the examples, so the diffs
are large because of code moves.
2025-06-20 16:37:18 +00:00

83 lines
3.2 KiB
Rust

/// This module contains components that are used to track the interaction state of UI widgets.
use bevy_a11y::AccessibilityNode;
use bevy_ecs::{
component::Component,
lifecycle::{Add, Remove},
observer::On,
world::DeferredWorld,
};
/// A component indicating that a widget is disabled and should be "grayed out".
/// This is used to prevent user interaction with the widget. It should not, however, prevent
/// the widget from being updated or rendered, or from acquiring keyboard focus.
///
/// For apps which support a11y: if a widget (such as a slider) contains multiple entities,
/// the `InteractionDisabled` component should be added to the root entity of the widget - the
/// same entity that contains the `AccessibilityNode` component. This will ensure that
/// the a11y tree is updated correctly.
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct InteractionDisabled;
pub(crate) fn on_add_disabled(trigger: On<Add, InteractionDisabled>, mut world: DeferredWorld) {
let mut entity = world.entity_mut(trigger.target());
if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
accessibility.set_disabled();
}
}
pub(crate) fn on_remove_disabled(
trigger: On<Remove, InteractionDisabled>,
mut world: DeferredWorld,
) {
let mut entity = world.entity_mut(trigger.target());
if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
accessibility.clear_disabled();
}
}
/// Component that indicates whether a button or widget is currently in a pressed or "held down"
/// state.
#[derive(Component, Default, Debug)]
pub struct Pressed;
/// Component that indicates that a widget can be checked.
#[derive(Component, Default, Debug)]
pub struct Checkable;
/// Component that indicates whether a checkbox or radio button is in a checked state.
#[derive(Component, Default, Debug)]
pub struct Checked;
pub(crate) fn on_add_checkable(trigger: On<Add, Checked>, mut world: DeferredWorld) {
let mut entity = world.entity_mut(trigger.target());
let checked = entity.get::<Checked>().is_some();
if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
accessibility.set_toggled(match checked {
true => accesskit::Toggled::True,
false => accesskit::Toggled::False,
});
}
}
pub(crate) fn on_remove_checkable(trigger: On<Add, Checked>, mut world: DeferredWorld) {
// Remove the 'toggled' attribute entirely.
let mut entity = world.entity_mut(trigger.target());
if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
accessibility.clear_toggled();
}
}
pub(crate) fn on_add_checked(trigger: On<Add, Checked>, mut world: DeferredWorld) {
let mut entity = world.entity_mut(trigger.target());
if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
accessibility.set_toggled(accesskit::Toggled::True);
}
}
pub(crate) fn on_remove_checked(trigger: On<Remove, Checked>, mut world: DeferredWorld) {
let mut entity = world.entity_mut(trigger.target());
if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() {
accessibility.set_toggled(accesskit::Toggled::False);
}
}