#![doc = include_str!("../README.md")]
mod navigation_menu;
pub mod style;
mod systems;
mod types;
mod widgets;
use bevy::{ecs::schedule::ShouldRun, prelude::*};
use style::Stylesheet;
use types::{CleanUpUI, MenuAssets};
use std::fmt::Debug;
use std::hash::Hash;
pub use navigation_menu::NavigationMenu;
pub use types::{
ButtonComponent, Menu, MenuIcon, MenuItem, MenuOptions, MenuSelection, NavigationEvent,
PrimaryMenu, RedrawEvent, RichTextEntry, Selections, VerticalMenuComponent,
};
pub struct QuickMenuPlugin<S>
where
S: ScreenTrait + 'static,
{
s: std::marker::PhantomData<S>,
options: Option<MenuOptions>,
}
impl<S> QuickMenuPlugin<S>
where
S: ScreenTrait + 'static,
{
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
s: Default::default(),
options: None,
}
}
pub fn with_options(options: MenuOptions) -> Self {
Self {
s: Default::default(),
options: Some(options),
}
}
}
impl<State, A ,S> Plugin for QuickMenuPlugin<S>
where
State: 'static + Send + Sync,
A: ActionTrait<State = State> + 'static,
S: ScreenTrait<Action = A, State = State> + 'static,
{
fn build(&self, app: &mut bevy::prelude::App) {
app.insert_resource(self.options.unwrap_or_default())
.init_resource::<MenuAssets>()
.insert_resource(Selections::default())
.add_event::<NavigationEvent>()
.add_event::<RedrawEvent>()
.add_system_set(
SystemSet::new()
.with_run_criteria(resource_exists::<CleanUpUI>)
.with_system(systems::cleanup_system::<S>),
)
.add_system_set(
SystemSet::new()
.with_run_criteria(resource_exists::<MenuState<S>>)
.with_system(crate::systems::keyboard_input_system)
.with_system(crate::systems::input_system::<S>)
.with_system(crate::systems::mouse_system::<S>)
.with_system(crate::systems::redraw_system::<S>),
);
}
}
pub fn cleanup(commands: &mut Commands) {
commands.init_resource::<CleanUpUI>();
}
pub trait ActionTrait: Debug + PartialEq + Eq + Clone + Copy + Hash + Send + Sync {
type State;
type Event: Send + Sync + 'static;
fn handle(&self, state: &mut Self::State, event_writer: &mut EventWriter<Self::Event>);
}
pub trait ScreenTrait: Debug + PartialEq + Eq + Clone + Copy + Hash + Send + Sync {
type Action: ActionTrait<State = Self::State>;
type State: Send + Sync + 'static;
fn resolve(
&self,
state: &<<Self as ScreenTrait>::Action as ActionTrait>::State,
) -> Menu<Self>;
}
#[derive(Resource)]
pub struct MenuState<S>
where
S: ScreenTrait + 'static,
{
menu: NavigationMenu<S>,
pub initial_render_done: bool,
}
impl<S> MenuState<S>
where
S: ScreenTrait + 'static,
{
pub fn new(state: S::State, screen: S, sheet: Option<Stylesheet>) -> Self {
Self {
menu: NavigationMenu::new(state, screen, sheet),
initial_render_done: false,
}
}
pub fn state_mut(&mut self) -> &mut S::State {
&mut self.menu.state
}
pub fn state(&self) -> &S::State {
&self.menu.state
}
}
pub fn resource_exists<T: Resource>(resource: Option<Res<T>>) -> ShouldRun {
resource.is_some().into()
}