1use bevy::{
4 color::palettes::basic::*,
5 input_focus::{
6 tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin},
7 InputDispatchPlugin, InputFocus,
8 },
9 prelude::*,
10};
11
12fn main() {
13 App::new()
14 .add_plugins((DefaultPlugins, InputDispatchPlugin, TabNavigationPlugin))
15 .add_systems(Startup, setup)
16 .add_systems(Update, (button_system, focus_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 interaction_query: Query<
26 (&Interaction, &mut BackgroundColor, &mut BorderColor),
27 (Changed<Interaction>, With<Button>),
28 >,
29) {
30 for (interaction, mut color, mut border_color) in &mut interaction_query {
31 match *interaction {
32 Interaction::Pressed => {
33 *color = PRESSED_BUTTON.into();
34 *border_color = BorderColor::all(RED);
35 }
36 Interaction::Hovered => {
37 *color = HOVERED_BUTTON.into();
38 *border_color = BorderColor::all(Color::WHITE);
39 }
40 Interaction::None => {
41 *color = NORMAL_BUTTON.into();
42 *border_color = BorderColor::all(Color::BLACK);
43 }
44 }
45 }
46}
47
48fn focus_system(
49 mut commands: Commands,
50 focus: Res<InputFocus>,
51 mut query: Query<Entity, With<Button>>,
52) {
53 if focus.is_changed() {
54 for button in query.iter_mut() {
55 if focus.0 == Some(button) {
56 commands.entity(button).insert(Outline {
57 color: Color::WHITE,
58 width: px(2),
59 offset: px(2),
60 });
61 } else {
62 commands.entity(button).remove::<Outline>();
63 }
64 }
65 }
66}
67
68fn setup(mut commands: Commands) {
69 commands.spawn(Camera2d);
71 commands
72 .spawn(Node {
73 width: percent(100),
74 height: percent(100),
75 display: Display::Flex,
76 flex_direction: FlexDirection::Column,
77 align_items: AlignItems::Center,
78 justify_content: JustifyContent::Center,
79 row_gap: px(6),
80 ..default()
81 })
82 .observe(
83 |mut event: On<Pointer<Click>>, mut focus: ResMut<InputFocus>| {
84 focus.0 = None;
85 event.propagate(false);
86 },
87 )
88 .with_children(|parent| {
89 for (label, tab_group, indices) in [
90 ("TabGroup 0", TabGroup::new(0), [0, 0, 0, 0]),
92 ("TabGroup 2", TabGroup::new(2), [3, 2, 1, 0]),
94 ("TabGroup 1", TabGroup::new(1), [0, 1, 2, 3]),
96 ("Modal TabGroup", TabGroup::modal(), [0, 3, 1, 2]),
98 ] {
99 parent.spawn(Text::new(label));
100 parent
101 .spawn((
102 Node {
103 display: Display::Flex,
104 flex_direction: FlexDirection::Row,
105 column_gap: px(6),
106 margin: UiRect {
107 bottom: px(10),
108 ..default()
109 },
110 ..default()
111 },
112 tab_group,
113 ))
114 .with_children(|parent| {
115 for i in indices {
116 parent
117 .spawn((
118 Button,
119 Node {
120 width: px(200),
121 height: px(65),
122 border: UiRect::all(px(5)),
123 justify_content: JustifyContent::Center,
124 align_items: AlignItems::Center,
125 ..default()
126 },
127 BorderColor::all(Color::BLACK),
128 BackgroundColor(NORMAL_BUTTON),
129 TabIndex(i),
130 children![(
131 Text::new(format!("TabIndex {i}")),
132 TextFont {
133 font_size: 20.0,
134 ..default()
135 },
136 TextColor(Color::srgb(0.9, 0.9, 0.9)),
137 )],
138 ))
139 .observe(
140 |mut click: On<Pointer<Click>>,
141 mut focus: ResMut<InputFocus>| {
142 focus.0 = Some(click.entity);
143 click.propagate(false);
144 },
145 );
146 }
147 });
148 }
149 });
150}