use std::marker::PhantomData; use bevy::{ ecs::spawn::SpawnIter, platform::collections::HashMap, prelude::*, state::state::FreelyMutableState, }; use super::{ ItemPosition, MenuItemInternal, MenuItemType, OnPressAction, menus::{Menu, Menus}, }; mod components; impl Plugin for Menus where TriggerState: States, NavState: States + FreelyMutableState + Copy, { fn build(&self, app: &mut App) { app.insert_state(self.start) .insert_resource(self.get_store()) .insert_resource(MenusClosedWhen(self.closed_when)) .add_observer(build_ui::) .add_observer(destroy_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(PhantomData); #[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>); #[derive(Resource)] struct MenusClosedWhen(S); fn destroy_ui( trigger: Trigger>, ui_parent: Query>>, mut c: Commands, ) { trace!("destroy_ui called"); if let Ok(parent) = ui_parent.single_inner() { c.entity(parent).despawn(); } } 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| ItemPosition::MainView == it.pos); 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 c: Commands, mut interaction_query: Query<(&Interaction, &Action), Changed>, mut nav_state: ResMut>, closed_when: Res>, ) { for (interaction, Action(action)) in interaction_query { if *interaction != Interaction::Pressed { continue; } action.run(&mut c, &mut nav_state, &closed_when.0); } } fn update_ui_trigger( mut c: Commands, mut trans_reader: EventReader>, menus_data: Res>, closed_when: Res>, ) { for trans in trans_reader.read() { info!("{trans:?}"); c.trigger(DestroyUi(PhantomData::)); if let Some(to_build) = trans.entered.clone() { if to_build != closed_when.0 { c.trigger(BuildUi(to_build)); } } } }