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 Plugin for Menus { fn build(&self, app: &mut App) { app.insert_state(self.start) .insert_resource(self.get_store()) .add_observer(build_ui::) // .add_observer(destroy_ui::) .add_systems( Update, ( update_ui_trigger::, handle_press_actions::.run_if(in_state(self.trigger.clone())), ), ); } } #[derive(Event)] struct DestroyUi(S); #[derive(Event)] struct BuildUi(S); #[derive(Component)] struct UiParent(PhantomData); #[derive(Component)] struct UiComponent; #[derive(Component)] struct Action(OnPressAction); impl Menus { fn get_store(&self) -> MenusStore { MenusStore(self.menus.clone()) } } #[derive(Resource)] struct MenusStore(HashMap>); fn build_ui( trigger: Trigger>, ui_parent: Query>>, mut c: Commands, menus_data: Res>, ) { 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::), 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( mut interaction_query: Query<(&Interaction, &Action), Changed>, mut nav_state: ResMut>, ) { 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( mut c: Commands, mut trans_reader: EventReader>, menus_data: Res>, ) { 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(mut c: Commands) { // }