Skip to main content

button/
button.rs

1//! This example illustrates how to create a button that changes color and text based on its
2//! interaction state.
3
4use bevy::{
5    color::palettes::basic::*,
6    input_focus::{FocusCause, InputFocus},
7    prelude::*,
8};
9
10fn main() {
11    App::new()
12        .add_plugins(DefaultPlugins)
13        // `InputFocus` must be set for accessibility to recognize the button.
14        .init_resource::<InputFocus>()
15        .add_systems(Startup, setup)
16        .add_systems(Update, button_system)
17        .run();
18}
19
20const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
21const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
22const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);
23
24fn button_system(
25    mut input_focus: ResMut<InputFocus>,
26    mut interaction_query: Query<
27        (
28            Entity,
29            &Interaction,
30            &mut BackgroundColor,
31            &mut BorderColor,
32            &mut Button,
33            &Children,
34        ),
35        Changed<Interaction>,
36    >,
37    mut text_query: Query<&mut Text>,
38) {
39    for (entity, interaction, mut color, mut border_color, mut button, children) in
40        &mut interaction_query
41    {
42        let mut text = text_query.get_mut(children[0]).unwrap();
43
44        match *interaction {
45            Interaction::Pressed => {
46                input_focus.set(entity, FocusCause::Pressed);
47                **text = "Press".to_string();
48                *color = PRESSED_BUTTON.into();
49                *border_color = BorderColor::all(RED);
50
51                // The accessibility system's only update the button's state when the `Button` component is marked as changed.
52                button.set_changed();
53            }
54            Interaction::Hovered => {
55                input_focus.set(entity, FocusCause::Pressed);
56                **text = "Hover".to_string();
57                *color = HOVERED_BUTTON.into();
58                *border_color = BorderColor::all(Color::WHITE);
59                button.set_changed();
60            }
61            Interaction::None => {
62                input_focus.clear();
63                **text = "Button".to_string();
64                *color = NORMAL_BUTTON.into();
65                *border_color = BorderColor::all(Color::BLACK);
66            }
67        }
68    }
69}
70
71fn setup(mut commands: Commands, assets: Res<AssetServer>) {
72    // ui camera
73    commands.spawn(Camera2d);
74    commands.spawn(button(&assets));
75}
76
77fn button(asset_server: &AssetServer) -> impl Bundle {
78    (
79        Node {
80            width: percent(100),
81            height: percent(100),
82            align_items: AlignItems::Center,
83            justify_content: JustifyContent::Center,
84            ..default()
85        },
86        children![(
87            Button,
88            Node {
89                width: px(150),
90                height: px(65),
91                border: UiRect::all(px(5)),
92                // horizontally center child text
93                justify_content: JustifyContent::Center,
94                // vertically center child text
95                align_items: AlignItems::Center,
96                border_radius: BorderRadius::MAX,
97                ..default()
98            },
99            BorderColor::all(Color::WHITE),
100            BackgroundColor(Color::BLACK),
101            children![(
102                Text::new("Button"),
103                TextFont {
104                    font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
105                    font_size: FontSize::Px(33.0),
106                    ..default()
107                },
108                TextColor(Color::srgb(0.9, 0.9, 0.9)),
109                TextShadow::default(),
110            )]
111        )],
112    )
113}