use bevy_app::{App, Plugin, PluginGroup, PluginGroupBuilder};
use bevy_ecs::prelude::{On, Res, ResMut, Resource};
use bevy_log::warn;
use bevy_minibuffer::prelude::*;
use std::borrow::Cow;
use std::marker::PhantomData;
use trie_rs::map::Trie;
#[derive(Resource)]
pub(crate) struct Inspectors<M: Send + Sync + 'static> {
pub(crate) names: Trie<u8, usize>,
pub(crate) visible: Vec<bool>,
marker: PhantomData<M>,
}
pub(crate) struct InspectorPlugins<M> {
plugins: Option<PluginGroupBuilder>,
names: Vec<String>,
marker: PhantomData<M>,
}
impl<M: Send + Sync + 'static> InspectorPlugins<M> {
pub(crate) fn add_inspector<F: Fn(usize, &mut Self)>(
&mut self,
name: String,
add_plugin_fn: F,
) {
let index = self.names.len();
self.names.push(name);
add_plugin_fn(index, self)
}
pub(crate) fn add_plugin<T: Plugin>(&mut self, plugin: T) {
let builder = self.plugins.take().expect("plugin builder");
self.plugins = Some(builder.add(plugin));
}
pub(crate) fn visible(index: usize) -> impl Fn(Res<Inspectors<M>>) -> bool {
move |inspectors: Res<Inspectors<M>>| {
inspectors
.into_inner()
.visible
.get(index)
.copied()
.unwrap_or(false)
}
}
#[allow(dead_code)]
pub(crate) fn inspector(
prompt: impl Into<Cow<'static, str>>,
none_msg: impl Into<Cow<'static, str>>,
) -> impl Fn(Res<Inspectors<M>>, Minibuffer) {
let prompt = prompt.into();
let none_msg = none_msg.into();
move |inspectors: Res<Inspectors<M>>, mut minibuffer: Minibuffer| {
if !inspectors.visible.is_empty() {
minibuffer
.prompt_map(prompt.clone(), inspectors.names.clone())
.observe(
|mut trigger: On<Completed<usize>>,
mut inspectors: ResMut<Inspectors<M>>| {
if let Ok(index) = trigger.event_mut().state.take_result().unwrap() {
inspectors.visible[index] = !inspectors.visible[index];
}
},
);
} else {
minibuffer.message(none_msg.clone());
}
}
}
pub(crate) fn warn_on_empty(&self, msg: impl Into<Cow<'static, str>>) {
if self.names.is_empty() {
let msg = msg.into();
warn!("{}", msg);
}
}
}
impl<M: Send + Sync + 'static> Default for InspectorPlugins<M> {
fn default() -> Self {
Self {
plugins: Some(PluginGroupBuilder::start::<Self>()),
names: vec![],
marker: PhantomData,
}
}
}
impl<M: Send + Sync + 'static> PluginGroup for InspectorPlugins<M> {
fn build(mut self) -> PluginGroupBuilder {
let builder = self.plugins.take().expect("plugin builder");
builder.add(move |app: &mut App| {
let count = self.names.len();
let trie = Trie::from_iter(
self.names
.clone()
.into_iter()
.enumerate()
.map(|(index, name)| (name, index)),
);
app.insert_resource(Inspectors {
names: trie,
visible: vec![false; count],
marker: PhantomData::<M>,
});
})
}
}