You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
martian-chess/src/ui.rs

146 lines
4.8 KiB
Rust

use bevy::window::PrimaryWindow;
use crate::prelude::*;
pub(crate) struct UiPlugin;
impl Plugin for UiPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, initialize);
app.add_systems(OnEnter(GameState::Menu), sync_display_mode);
app.add_systems(
Update,
(
toggle_display_mode.run_if(any_component_changed::<Interaction>),
sync_display_mode
.run_if(state_changed::<DisplayState>())
.run_if(in_state(GameState::Menu)),
sync_display_mode
.run_if(state_changed::<DisplayState>())
.run_if(in_state(GameState::Play)),
manage_cursor.run_if(any_component_changed::<Interaction>),
interactive_button.run_if(any_component_changed::<Interaction>),
),
);
}
}
#[derive(Debug, Component)]
struct UiRoot;
fn initialize(mut commands: Commands) {
commands
.spawn((
NodeBundle {
style: Style {
position_type: PositionType::Absolute,
right: Val::Px(5.0),
bottom: Val::Px(5.0),
..default()
},
background_color: Color::WHITE.into(),
visibility: Visibility::Hidden,
..default()
},
UiRoot,
))
.with_children(|parent| {
parent.spawn((
ButtonBundle {
style: Style {
width: Val::Px(25.0),
height: Val::Px(25.0),
..default()
},
background_color: Color::WHITE.into(),
..default()
},
DisplayState::Display2d,
));
});
}
/// When button is clicked, switch display state to that state and hide the other option.
/// Display2d -> Click -> Display2d mode & Display3d butto
fn toggle_display_mode(
events: Query<(&Interaction, &DisplayState), (Changed<Interaction>, With<Button>)>,
mut next_state: ResMut<NextState<DisplayState>>,
) {
events
.iter()
.for_each(|(interaction, display_state)| match interaction {
Interaction::Pressed => match display_state {
DisplayState::Display2d => next_state.set(DisplayState::Display2d),
DisplayState::Display3d => next_state.set(DisplayState::Display3d),
},
_ => (),
});
}
/// Syncs the display button with the UI
/// Runs every time the DisplayState changes
fn sync_display_mode(
mut query: Query<(&mut UiImage, &mut DisplayState), With<Button>>,
mut visibilities: Query<&mut Visibility, With<UiRoot>>,
state: Res<State<DisplayState>>,
server: Res<AssetServer>,
) {
query.iter_mut().for_each(|(mut image, mut display_state)| {
let (texture, target_state) = match state.get() {
DisplayState::Display2d => (server.load("images/3DIcon.png"), DisplayState::Display3d),
DisplayState::Display3d => (server.load("images/2DIcon.png"), DisplayState::Display2d),
};
*image = UiImage {
texture,
..default()
};
*display_state = target_state;
});
visibilities
.iter_mut()
.for_each(|mut visibility| *visibility = Visibility::Visible);
}
fn manage_cursor(
mut window: Query<&mut Window, With<PrimaryWindow>>,
state: Res<State<GameState>>,
buttons: Query<&Interaction, With<Button>>,
) {
window.single_mut().cursor.icon = {
// In Loading state, icon is Progess/Wait
if *state == GameState::Loading {
CursorIcon::Wait
// If a button is hovered, icon is hand
} else if buttons.iter().any(|i| *i == Interaction::Pressed) {
CursorIcon::Grabbing
// If a button is clicked, icon is grab
} else if buttons.iter().any(|i| *i == Interaction::Hovered) {
CursorIcon::Hand
// Default icon is Crosshair
} else {
CursorIcon::Crosshair
}
};
}
fn interactive_button(
mut events: Query<(&mut BackgroundColor, &Interaction), (With<Button>, Changed<Interaction>)>,
mut writer: EventWriter<audio::AudioEvent>,
) {
events
.iter_mut()
.for_each(|(mut bg_color, interaction)| match interaction {
Interaction::None => {
bg_color.0.set_a(0.5);
}
Interaction::Hovered => {
bg_color.0.set_a(0.75);
writer.send(audio::AudioEvent::MenuHover);
}
Interaction::Pressed => {
bg_color.0.set_a(1.0);
writer.send(audio::AudioEvent::MenuSelect);
}
});
}