mirror of
https://codeberg.org/schrottkatze/mgd2-tram-championships.git
synced 2025-07-04 19:17:38 +00:00
menu system: ITS ALIVE
This commit is contained in:
parent
2b18cdea38
commit
acbcfe5956
7 changed files with 216 additions and 30 deletions
170
src/menus/lib/plugin.rs
Normal file
170
src/menus/lib/plugin.rs
Normal file
|
@ -0,0 +1,170 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use bevy::{
|
||||
ecs::spawn::SpawnIter, platform::collections::HashMap, prelude::*,
|
||||
state::state::FreelyMutableState,
|
||||
};
|
||||
|
||||
use super::{ItemPosition, Menu, MenuItemInternal, MenuItemType, Menus, OnPressAction};
|
||||
|
||||
impl<TriggerState: States, NavState: States + FreelyMutableState + Copy> Plugin
|
||||
for Menus<TriggerState, NavState>
|
||||
{
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_state(self.start)
|
||||
.insert_resource(self.get_store())
|
||||
.add_observer(build_ui::<NavState>)
|
||||
// .add_observer(destroy_ui::<NavStates>)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
update_ui_trigger::<NavState>,
|
||||
handle_press_actions::<NavState>.run_if(in_state(self.trigger.clone())),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
struct DestroyUi<S: States>(S);
|
||||
#[derive(Event)]
|
||||
struct BuildUi<S: States>(S);
|
||||
|
||||
#[derive(Component)]
|
||||
struct UiParent<S: States>(PhantomData<S>);
|
||||
#[derive(Component)]
|
||||
struct UiComponent;
|
||||
|
||||
#[derive(Component)]
|
||||
struct Action<S: States>(OnPressAction<S>);
|
||||
|
||||
impl<T: States, S: States> Menus<T, S> {
|
||||
fn get_store(&self) -> MenusStore<S> {
|
||||
MenusStore(self.menus.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct MenusStore<S: States>(HashMap<S, Menu<S>>);
|
||||
|
||||
fn build_ui<S: States>(
|
||||
trigger: Trigger<BuildUi<S>>,
|
||||
ui_parent: Query<Entity, With<UiParent<S>>>,
|
||||
mut c: Commands,
|
||||
menus_data: Res<MenusStore<S>>,
|
||||
) {
|
||||
let e = trigger.event();
|
||||
let Some(menu_structure) = menus_data.0.get(&e.0) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let items = menu_structure
|
||||
.menu_items
|
||||
.clone()
|
||||
.into_iter()
|
||||
// TODO: implement other item positions
|
||||
.filter(|it| it.pos == ItemPosition::MainView);
|
||||
|
||||
if let Ok(parent) = ui_parent.single_inner() {
|
||||
c.entity(parent).despawn();
|
||||
}
|
||||
|
||||
c.spawn((
|
||||
UiParent(PhantomData::<S>),
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
flex_direction: FlexDirection::Column,
|
||||
..default()
|
||||
},
|
||||
))
|
||||
.with_children(|parent| {
|
||||
for MenuItemInternal { r#type, action, .. } in items {
|
||||
let mut e = match r#type {
|
||||
MenuItemType::Text(s) => parent.spawn(components::text(s)),
|
||||
MenuItemType::Button(s) => parent.spawn(components::button(s)),
|
||||
};
|
||||
e.insert(UiComponent);
|
||||
|
||||
if let Some(action) = action {
|
||||
e.insert(Action(action));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_press_actions<S: States + FreelyMutableState>(
|
||||
mut interaction_query: Query<(&Interaction, &Action<S>), Changed<Interaction>>,
|
||||
mut nav_state: ResMut<NextState<S>>,
|
||||
) {
|
||||
for (interaction, Action(action)) in interaction_query {
|
||||
if *interaction != Interaction::Pressed {
|
||||
continue;
|
||||
}
|
||||
match action {
|
||||
OnPressAction::NavigateTo(to) => nav_state.set(to.clone()),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod components {
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::menus::lib::OnPressAction;
|
||||
|
||||
use super::Action;
|
||||
|
||||
pub fn text(t: String) -> impl Bundle {
|
||||
(
|
||||
Text::new(t),
|
||||
TextFont {
|
||||
// font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 33.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::srgb(0.9, 0.9, 0.9)),
|
||||
TextShadow::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn button(t: String) -> impl Bundle {
|
||||
(
|
||||
Button,
|
||||
Node {
|
||||
border: UiRect::all(Val::Px(5.0)),
|
||||
// horizontally center child text
|
||||
justify_content: JustifyContent::Center,
|
||||
// vertically center child text
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(10.0)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::BLACK),
|
||||
BorderRadius::MAX,
|
||||
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
|
||||
children![text(t)],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ui_trigger<S: States>(
|
||||
mut c: Commands,
|
||||
mut trans_reader: EventReader<StateTransitionEvent<S>>,
|
||||
menus_data: Res<MenusStore<S>>,
|
||||
) {
|
||||
for trans in trans_reader.read() {
|
||||
info!("{trans:?}");
|
||||
|
||||
// if let Some(to_delete) = trans.exited {}
|
||||
if let Some(to_build) = trans.entered.clone() {
|
||||
c.trigger(BuildUi(to_build));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn build_ui<S: States>(mut c: Commands) {
|
||||
|
||||
// }
|
Loading…
Add table
Add a link
Reference in a new issue