use bevy::color::palettes::css::DARK_GRAY;
use bevy::color::palettes::css::RED;
use bevy::color::palettes::css::YELLOW;
use bevy::prelude::*;
use core::f32::consts::FRAC_PI_8;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, button_system)
.add_systems(Update, translation_system)
.run();
}
const NORMAL_BUTTON: Color = Color::WHITE;
const HOVERED_BUTTON: Color = Color::Srgba(YELLOW);
const PRESSED_BUTTON: Color = Color::Srgba(RED);
#[derive(Component)]
pub struct RotateButton(pub Rot2);
#[derive(Component)]
pub struct ScaleButton(pub f32);
#[derive(Component)]
pub struct TargetNode;
fn button_system(
mut interaction_query: Query<
(
&Interaction,
&mut BackgroundColor,
Option<&RotateButton>,
Option<&ScaleButton>,
),
(Changed<Interaction>, With<Button>),
>,
mut rotator_query: Query<&mut UiTransform, With<TargetNode>>,
) {
for (interaction, mut color, maybe_rotate, maybe_scale) in &mut interaction_query {
match *interaction {
Interaction::Pressed => {
*color = PRESSED_BUTTON.into();
if let Some(step) = maybe_rotate {
for mut transform in rotator_query.iter_mut() {
transform.rotation *= step.0;
}
}
if let Some(step) = maybe_scale {
for mut transform in rotator_query.iter_mut() {
transform.scale += step.0;
transform.scale =
transform.scale.clamp(Vec2::splat(0.25), Vec2::splat(3.0));
}
}
}
Interaction::Hovered => {
*color = HOVERED_BUTTON.into();
}
Interaction::None => {
*color = NORMAL_BUTTON.into();
}
}
}
}
fn translation_system(
time: Res<Time>,
input: Res<ButtonInput<KeyCode>>,
mut translation_query: Query<&mut UiTransform, With<TargetNode>>,
) {
let controls = [
(KeyCode::ArrowLeft, -Vec2::X),
(KeyCode::ArrowRight, Vec2::X),
(KeyCode::ArrowUp, -Vec2::Y),
(KeyCode::ArrowDown, Vec2::Y),
];
for &(key_code, direction) in &controls {
if input.pressed(key_code) {
for mut transform in translation_query.iter_mut() {
let d = direction * 50.0 * time.delta_secs();
let (Val::Px(x), Val::Px(y)) = (transform.translation.x, transform.translation.y)
else {
continue;
};
let x = (x + d.x).clamp(-150., 150.);
let y = (y + d.y).clamp(-150., 150.);
transform.translation = Val2::px(x, y);
}
}
}
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
commands.spawn((
Node {
width: percent(100),
height: percent(100),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(Color::BLACK),
children![(
Node {
align_items: AlignItems::Center,
justify_content: JustifyContent::SpaceEvenly,
column_gap: px(25),
row_gap: px(25),
..default()
},
BackgroundColor(Color::BLACK),
children![
(
Node {
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::Center,
row_gap: px(10),
column_gap: px(10),
padding: UiRect::all(px(10)),
..default()
},
BackgroundColor(Color::BLACK),
GlobalZIndex(1),
children![
(
Button,
Node {
height: px(50),
width: px(50),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(Color::WHITE),
RotateButton(Rot2::radians(-FRAC_PI_8)),
children![(Text::new("<--"), TextColor(Color::BLACK),)]
),
(
Button,
Node {
height: px(50),
width: px(50),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(Color::WHITE),
ScaleButton(-0.25),
children![(Text::new("-"), TextColor(Color::BLACK),)]
),
]
),
(
Node {
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::SpaceBetween,
align_items: AlignItems::Center,
width: px(300),
height: px(300),
..default()
},
BackgroundColor(DARK_GRAY.into()),
TargetNode,
children![
(
Button,
Node {
width: px(80),
height: px(80),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(Color::WHITE),
children![(Text::new("Top"), TextColor(Color::BLACK))]
),
(
Node {
align_self: AlignSelf::Stretch,
justify_content: JustifyContent::SpaceBetween,
align_items: AlignItems::Center,
..default()
},
children![
(
Button,
Node {
width: px(80),
height: px(80),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(Color::WHITE),
UiTransform::from_rotation(Rot2::radians(
-std::f32::consts::FRAC_PI_2
)),
children![(Text::new("Left"), TextColor(Color::BLACK),)]
),
(
Node {
width: px(100),
height: px(100),
..Default::default()
},
ImageNode {
image: asset_server.load("branding/icon.png"),
image_mode: NodeImageMode::Stretch,
..default()
}
),
(
Button,
Node {
width: px(80),
height: px(80),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
UiTransform::from_rotation(Rot2::radians(
core::f32::consts::FRAC_PI_2
)),
BackgroundColor(Color::WHITE),
children![(Text::new("Right"), TextColor(Color::BLACK))]
),
]
),
(
Button,
Node {
width: px(80),
height: px(80),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(Color::WHITE),
UiTransform::from_rotation(Rot2::radians(std::f32::consts::PI)),
children![(Text::new("Bottom"), TextColor(Color::BLACK),)]
),
]
),
(
Node {
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::Center,
row_gap: px(10),
column_gap: px(10),
padding: UiRect::all(px(10)),
..default()
},
BackgroundColor(Color::BLACK),
GlobalZIndex(1),
children![
(
Button,
Node {
height: px(50),
width: px(50),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(Color::WHITE),
RotateButton(Rot2::radians(FRAC_PI_8)),
children![(Text::new("-->"), TextColor(Color::BLACK),)]
),
(
Button,
Node {
height: px(50),
width: px(50),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(Color::WHITE),
ScaleButton(0.25),
children![(Text::new("+"), TextColor(Color::BLACK),)]
),
]
)
]
)],
));
}