diff --git a/src/debugging.rs b/src/debugging.rs index 5833dbc..3147859 100644 --- a/src/debugging.rs +++ b/src/debugging.rs @@ -5,7 +5,7 @@ use bevy_inspector_egui::{ quick::{StateInspectorPlugin, WorldInspectorPlugin}, }; -use crate::AppState; +use crate::{AppState, menus}; pub fn plugin(app: &mut App) { app.add_plugins(EguiPlugin { @@ -14,5 +14,6 @@ pub fn plugin(app: &mut App) { .add_plugins(( WorldInspectorPlugin::new(), StateInspectorPlugin::::new(), + StateInspectorPlugin::::new(), )); } diff --git a/src/main.rs b/src/main.rs index 86746fb..4a02e9a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,8 +10,8 @@ mod menus; #[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash, Reflect)] #[allow(unused)] enum AppState { - Menus, #[default] + Menus, Ingame, PostGame, } @@ -21,8 +21,10 @@ fn main() { .add_plugins(DefaultPlugins) .add_systems(Startup, camera::setup) .add_plugins(game::plugin) + .add_plugins(menus::plugin) .init_state::() - .register_type::() .add_plugins(debugging::plugin) + .register_type::() + // .insert_state(AppState::Ingame) .run(); } diff --git a/src/menus.rs b/src/menus.rs index d7dd07a..79c0185 100644 --- a/src/menus.rs +++ b/src/menus.rs @@ -1,18 +1,21 @@ use bevy::{input::ButtonState, prelude::*}; use lib::{ItemPosition, Menu, MenuItemType, Menus, OnPressAction}; +use crate::AppState; + mod lib; -#[derive(States, Debug, Clone, PartialEq, Eq, Hash)] -enum CurrentMenu { +#[derive(States, Debug, Copy, Clone, PartialEq, Eq, Hash, Default, Reflect)] +pub enum CurrentMenu { NotInMenus, + #[default] MainMenu, Settings, HighScores, StartGame, } -pub fn todo_meow() { - let plugin = Menus::::new() +pub fn plugin(app: &mut App) { + let plugin = Menus::::new(AppState::Menus) .add_menu( CurrentMenu::MainMenu, Menu::new().add_items(&[ @@ -31,7 +34,7 @@ pub fn todo_meow() { CurrentMenu::Settings, Menu::new() .add_items(&[ - &MenuItemType::Text(String::from("Hello, World!")), + &MenuItemType::Text(String::from("Settings")), &( MenuItemType::Button(String::from("Sample setting")), OnPressAction::SampleAction, @@ -46,5 +49,6 @@ pub fn todo_meow() { ), ); - dbg!(plugin); + dbg!(&plugin); + app.add_plugins(plugin); } diff --git a/src/menus/lib.rs b/src/menus/lib.rs index 5319c31..61da438 100644 --- a/src/menus/lib.rs +++ b/src/menus/lib.rs @@ -6,30 +6,39 @@ use std::marker::PhantomData; use bevy::{platform::collections::HashMap, prelude::*}; -type ToDo = (); - -#[derive(Debug)] -pub struct Menus { - menus: HashMap>, -} - -#[derive(Debug)] -pub struct Menu { - menu_items: Vec>, -} +mod item; pub use item::types::*; pub use item::*; -mod item; +type ToDo = (); -impl Menus { - pub fn new() -> Self { +#[derive(Debug, Clone)] +pub struct Menus { + start: NavState, + /// The state needed to trigger this set of menus + trigger: TriggerState, + menus: HashMap>, +} + +#[derive(Debug, Clone)] +pub struct Menu { + menu_items: Vec>, +} + +pub mod plugin; + +impl Menus { + pub fn new(trigger: TriggerState) -> Self { Self { + trigger, + start: NavState::default(), menus: HashMap::new(), } } +} +impl Menus { pub fn add_menu(mut self, state: NavState, menu: Menu) -> Self { self.menus.insert(state, menu); self diff --git a/src/menus/lib/item.rs b/src/menus/lib/item.rs index f7f1547..af374b5 100644 --- a/src/menus/lib/item.rs +++ b/src/menus/lib/item.rs @@ -3,11 +3,11 @@ use types::{ItemPosition, MenuItemType, OnPressAction}; pub mod types; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(super) struct MenuItemInternal { - r#type: MenuItemType, - pos: ItemPosition, - action: Option>, + pub(super) r#type: MenuItemType, + pub(super) pos: ItemPosition, + pub(super) action: Option>, } pub(super) trait IntoMenuItemInternal { diff --git a/src/menus/lib/item/types.rs b/src/menus/lib/item/types.rs index c72db23..87e4995 100644 --- a/src/menus/lib/item/types.rs +++ b/src/menus/lib/item/types.rs @@ -3,7 +3,7 @@ use bevy::state::state::States; use crate::menus::lib::ToDo; /// positions the item in the menu -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub enum ItemPosition { #[default] MainView, @@ -22,12 +22,12 @@ pub enum ItemPosition { pub enum MenuItemType { Text(String), Button(String), - Subsection(Vec), + // Subsection(Vec), } #[derive(Clone, Debug)] pub enum OnPressAction { NavigateTo(NavState), - RunSystem(ToDo), + DispatchEvent(ToDo), SampleAction, } diff --git a/src/menus/lib/plugin.rs b/src/menus/lib/plugin.rs new file mode 100644 index 0000000..087efb8 --- /dev/null +++ b/src/menus/lib/plugin.rs @@ -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 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) { + +// }