1use bevy::color::palettes::css::DARK_GRAY;
3use bevy::color::palettes::css::RED;
4use bevy::color::palettes::css::YELLOW;
5use bevy::prelude::*;
6use core::f32::consts::FRAC_PI_8;
7
8fn main() {
9 App::new()
10 .add_plugins(DefaultPlugins)
11 .add_systems(Startup, setup)
12 .add_systems(Update, button_system)
13 .add_systems(Update, translation_system)
14 .run();
15}
16
17const NORMAL_BUTTON: Color = Color::WHITE;
18const HOVERED_BUTTON: Color = Color::Srgba(YELLOW);
19const PRESSED_BUTTON: Color = Color::Srgba(RED);
20
21#[derive(Component)]
23pub struct RotateButton(pub Rot2);
24
25#[derive(Component)]
27pub struct ScaleButton(pub f32);
28
29#[derive(Component)]
31pub struct TargetNode;
32
33fn button_system(
35 mut interaction_query: Query<
36 (
37 &Interaction,
38 &mut BackgroundColor,
39 Option<&RotateButton>,
40 Option<&ScaleButton>,
41 ),
42 (Changed<Interaction>, With<Button>),
43 >,
44 mut rotator_query: Query<&mut UiTransform, With<TargetNode>>,
45) {
46 for (interaction, mut color, maybe_rotate, maybe_scale) in &mut interaction_query {
47 match *interaction {
48 Interaction::Pressed => {
49 *color = PRESSED_BUTTON.into();
50 if let Some(step) = maybe_rotate {
51 for mut transform in rotator_query.iter_mut() {
52 transform.rotation *= step.0;
53 }
54 }
55 if let Some(step) = maybe_scale {
56 for mut transform in rotator_query.iter_mut() {
57 transform.scale += step.0;
58 transform.scale =
59 transform.scale.clamp(Vec2::splat(0.25), Vec2::splat(3.0));
60 }
61 }
62 }
63 Interaction::Hovered => {
64 *color = HOVERED_BUTTON.into();
65 }
66 Interaction::None => {
67 *color = NORMAL_BUTTON.into();
68 }
69 }
70 }
71}
72
73fn translation_system(
75 time: Res<Time>,
76 input: Res<ButtonInput<KeyCode>>,
77 mut translation_query: Query<&mut UiTransform, With<TargetNode>>,
78) {
79 let controls = [
80 (KeyCode::ArrowLeft, -Vec2::X),
81 (KeyCode::ArrowRight, Vec2::X),
82 (KeyCode::ArrowUp, -Vec2::Y),
83 (KeyCode::ArrowDown, Vec2::Y),
84 ];
85 for &(key_code, direction) in &controls {
86 if input.pressed(key_code) {
87 for mut transform in translation_query.iter_mut() {
88 let d = direction * 50.0 * time.delta_secs();
89 let (Val::Px(x), Val::Px(y)) = (transform.translation.x, transform.translation.y)
90 else {
91 continue;
92 };
93 let x = (x + d.x).clamp(-150., 150.);
94 let y = (y + d.y).clamp(-150., 150.);
95
96 transform.translation = Val2::px(x, y);
97 }
98 }
99 }
100}
101
102fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
103 commands.spawn(Camera2d);
105
106 commands.spawn((
108 Node {
109 width: percent(100),
110 height: percent(100),
111 align_items: AlignItems::Center,
112 justify_content: JustifyContent::Center,
113 ..default()
114 },
115 BackgroundColor(Color::BLACK),
116 children![(
117 Node {
118 align_items: AlignItems::Center,
119 justify_content: JustifyContent::SpaceEvenly,
120 column_gap: px(25),
121 row_gap: px(25),
122 ..default()
123 },
124 BackgroundColor(Color::BLACK),
125 children![
126 (
127 Node {
128 flex_direction: FlexDirection::Column,
129 justify_content: JustifyContent::Center,
130 row_gap: px(10),
131 column_gap: px(10),
132 padding: UiRect::all(px(10)),
133 ..default()
134 },
135 BackgroundColor(Color::BLACK),
136 GlobalZIndex(1),
137 children![
138 (
139 Button,
140 Node {
141 height: px(50),
142 width: px(50),
143 align_items: AlignItems::Center,
144 justify_content: JustifyContent::Center,
145 ..default()
146 },
147 BackgroundColor(Color::WHITE),
148 RotateButton(Rot2::radians(-FRAC_PI_8)),
149 children![(Text::new("<--"), TextColor(Color::BLACK),)]
150 ),
151 (
152 Button,
153 Node {
154 height: px(50),
155 width: px(50),
156 align_items: AlignItems::Center,
157 justify_content: JustifyContent::Center,
158 ..default()
159 },
160 BackgroundColor(Color::WHITE),
161 ScaleButton(-0.25),
162 children![(Text::new("-"), TextColor(Color::BLACK),)]
163 ),
164 ]
165 ),
166 (
168 Node {
169 flex_direction: FlexDirection::Column,
170 justify_content: JustifyContent::SpaceBetween,
171 align_items: AlignItems::Center,
172 width: px(300),
173 height: px(300),
174 ..default()
175 },
176 BackgroundColor(DARK_GRAY.into()),
177 TargetNode,
178 children![
179 (
180 Button,
181 Node {
182 width: px(80),
183 height: px(80),
184 align_items: AlignItems::Center,
185 justify_content: JustifyContent::Center,
186 ..default()
187 },
188 BackgroundColor(Color::WHITE),
189 children![(Text::new("Top"), TextColor(Color::BLACK))]
190 ),
191 (
192 Node {
193 align_self: AlignSelf::Stretch,
194 justify_content: JustifyContent::SpaceBetween,
195 align_items: AlignItems::Center,
196 ..default()
197 },
198 children![
199 (
200 Button,
201 Node {
202 width: px(80),
203 height: px(80),
204 align_items: AlignItems::Center,
205 justify_content: JustifyContent::Center,
206 ..default()
207 },
208 BackgroundColor(Color::WHITE),
209 UiTransform::from_rotation(Rot2::radians(
210 -std::f32::consts::FRAC_PI_2
211 )),
212 children![(Text::new("Left"), TextColor(Color::BLACK),)]
213 ),
214 (
215 Node {
216 width: px(100),
217 height: px(100),
218 ..Default::default()
219 },
220 ImageNode {
221 image: asset_server.load("branding/icon.png"),
222 image_mode: NodeImageMode::Stretch,
223 ..default()
224 }
225 ),
226 (
227 Button,
228 Node {
229 width: px(80),
230 height: px(80),
231 align_items: AlignItems::Center,
232 justify_content: JustifyContent::Center,
233 ..default()
234 },
235 UiTransform::from_rotation(Rot2::radians(
236 core::f32::consts::FRAC_PI_2
237 )),
238 BackgroundColor(Color::WHITE),
239 children![(Text::new("Right"), TextColor(Color::BLACK))]
240 ),
241 ]
242 ),
243 (
244 Button,
245 Node {
246 width: px(80),
247 height: px(80),
248 align_items: AlignItems::Center,
249 justify_content: JustifyContent::Center,
250 ..default()
251 },
252 BackgroundColor(Color::WHITE),
253 UiTransform::from_rotation(Rot2::radians(std::f32::consts::PI)),
254 children![(Text::new("Bottom"), TextColor(Color::BLACK),)]
255 ),
256 ]
257 ),
258 (
260 Node {
261 flex_direction: FlexDirection::Column,
262 justify_content: JustifyContent::Center,
263 row_gap: px(10),
264 column_gap: px(10),
265 padding: UiRect::all(px(10)),
266 ..default()
267 },
268 BackgroundColor(Color::BLACK),
269 GlobalZIndex(1),
270 children![
271 (
272 Button,
273 Node {
274 height: px(50),
275 width: px(50),
276 align_items: AlignItems::Center,
277 justify_content: JustifyContent::Center,
278 ..default()
279 },
280 BackgroundColor(Color::WHITE),
281 RotateButton(Rot2::radians(FRAC_PI_8)),
282 children![(Text::new("-->"), TextColor(Color::BLACK),)]
283 ),
284 (
285 Button,
286 Node {
287 height: px(50),
288 width: px(50),
289 align_items: AlignItems::Center,
290 justify_content: JustifyContent::Center,
291 ..default()
292 },
293 BackgroundColor(Color::WHITE),
294 ScaleButton(0.25),
295 children![(Text::new("+"), TextColor(Color::BLACK),)]
296 ),
297 ]
298 )
299 ]
300 )],
301 ));
302}