Enable accessibility features for the button example (#18749)

# Objective

Accessibility features don't work with the UI `button` example because
`InputFocus` must be set for the accessibility systems to recognise the
button.

Fixes #18760

## Solution

* Set the button entity as the `InputFocus` when it is hovered or
pressed.
* Call `set_changed` on the `Button` component when the button's state
changes to hovered or pressed (the accessibility system's only update
the button's state when the `Button` component is marked as changed).

## Testing

Install NVDA, it should say "hover" when the button is hovered and
"pressed" when the button is pressed.

The bounds of the accessibility node are reported incorrectly. I thought
we fixed this, I'll take another look at it. It's not a problem with
this PR.
This commit is contained in:
ickshonpe 2025-05-26 16:19:55 +01:00 committed by GitHub
parent ed0266b5a6
commit 3edacb5e46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,13 +1,15 @@
//! This example illustrates how to create a button that changes color and text based on its //! This example illustrates how to create a button that changes color and text based on its
//! interaction state. //! interaction state.
use bevy::{color::palettes::basic::*, prelude::*, winit::WinitSettings}; use bevy::{color::palettes::basic::*, input_focus::InputFocus, prelude::*, winit::WinitSettings};
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
// Only run the app when there is user input. This will significantly reduce CPU/GPU use. // Only run the app when there is user input. This will significantly reduce CPU/GPU use.
.insert_resource(WinitSettings::desktop_app()) .insert_resource(WinitSettings::desktop_app())
// `InputFocus` must be set for accessibility to recognize the button.
.init_resource::<InputFocus>()
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, button_system) .add_systems(Update, button_system)
.run(); .run();
@ -18,31 +20,44 @@ const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35); const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);
fn button_system( fn button_system(
mut input_focus: ResMut<InputFocus>,
mut interaction_query: Query< mut interaction_query: Query<
( (
Entity,
&Interaction, &Interaction,
&mut BackgroundColor, &mut BackgroundColor,
&mut BorderColor, &mut BorderColor,
&mut Button,
&Children, &Children,
), ),
(Changed<Interaction>, With<Button>), Changed<Interaction>,
>, >,
mut text_query: Query<&mut Text>, mut text_query: Query<&mut Text>,
) { ) {
for (interaction, mut color, mut border_color, children) in &mut interaction_query { for (entity, interaction, mut color, mut border_color, mut button, children) in
&mut interaction_query
{
let mut text = text_query.get_mut(children[0]).unwrap(); let mut text = text_query.get_mut(children[0]).unwrap();
match *interaction { match *interaction {
Interaction::Pressed => { Interaction::Pressed => {
input_focus.set(entity);
**text = "Press".to_string(); **text = "Press".to_string();
*color = PRESSED_BUTTON.into(); *color = PRESSED_BUTTON.into();
border_color.0 = RED.into(); border_color.0 = RED.into();
// The accessibility system's only update the button's state when the `Button` component is marked as changed.
button.set_changed();
} }
Interaction::Hovered => { Interaction::Hovered => {
input_focus.set(entity);
**text = "Hover".to_string(); **text = "Hover".to_string();
*color = HOVERED_BUTTON.into(); *color = HOVERED_BUTTON.into();
border_color.0 = Color::WHITE; border_color.0 = Color::WHITE;
button.set_changed();
} }
Interaction::None => { Interaction::None => {
input_focus.clear();
**text = "Button".to_string(); **text = "Button".to_string();
*color = NORMAL_BUTTON.into(); *color = NORMAL_BUTTON.into();
border_color.0 = Color::BLACK; border_color.0 = Color::BLACK;