-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Adding interaction states for headless widgets. #19238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5e153ed
743ca0d
cdc2b04
508fffd
a98547b
11aaf8a
6e4fc7f
58fa91a
50f0da6
48c98d9
c38af19
090307a
5547e8e
25d9d44
fc3943f
7d83ebc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/// This module contains components that are used to track the interaction state of UI widgets. | ||
/// | ||
// Note to implementers: This uses a combination of both marker components and newtype components | ||
// containing a bool. Markers are used for one-way binding, "write-only" components | ||
// (like `InteractionDisabled`) that relay instructions from the user to the framework, whereas | ||
// newtype components are used to request state updates from the framework, which mutates the | ||
// content of those components on update. | ||
use bevy_a11y::AccessibilityNode; | ||
use bevy_ecs::{ | ||
component::{Component, HookContext}, | ||
world::DeferredWorld, | ||
}; | ||
|
||
/// A marker component to indicate 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)] | ||
#[component(on_add = on_add_disabled, on_remove = on_remove_disabled)] | ||
pub struct InteractionDisabled; | ||
|
||
// Hook to set the a11y "disabled" state when the widget is disabled. | ||
fn on_add_disabled(mut world: DeferredWorld, context: HookContext) { | ||
let mut entity = world.entity_mut(context.entity); | ||
if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() { | ||
accessibility.set_disabled(); | ||
} | ||
} | ||
|
||
// Hook to remove the a11y "disabled" state when the widget is enabled. | ||
fn on_remove_disabled(mut world: DeferredWorld, context: HookContext) { | ||
let mut entity = world.entity_mut(context.entity); | ||
if let Some(mut accessibility) = entity.get_mut::<AccessibilityNode>() { | ||
accessibility.clear_disabled(); | ||
} | ||
} | ||
|
||
/// Component that indicates whether a button is currently pressed. | ||
#[derive(Component, Default, Debug)] | ||
#[component(immutable)] | ||
pub struct ButtonPressed(pub bool); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that the other "bool state" components are immutable, maybe we should make this immutable too for consistency / to enable reliable observer-driven management? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
|
||
/// Component that indicates whether a checkbox or radio button is in a checked state. | ||
#[derive(Component, Default, Debug)] | ||
#[component(immutable, on_add = on_add_checked, on_replace = on_add_checked)] | ||
pub struct Checked(pub bool); | ||
|
||
// Hook to set the a11y "checked" state when the checkbox is added. | ||
fn on_add_checked(mut world: DeferredWorld, context: HookContext) { | ||
let mut entity = world.entity_mut(context.entity); | ||
let checked = entity.get::<Checked>().unwrap().0; | ||
let mut accessibility = entity.get_mut::<AccessibilityNode>().unwrap(); | ||
accessibility.set_toggled(match checked { | ||
true => accesskit::Toggled::True, | ||
false => accesskit::Toggled::False, | ||
}); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly this naming could be confusing because
Is-
components in bevy tend to be unit structs used as markers? It seems natural to use aWith<IsHovered>
query filter assuming that it would return only hovered UI nodes.