#![warn(missing_docs)]
#![allow(
clippy::return_self_not_must_use,
clippy::unit_arg,
clippy::needless_doctest_main,
clippy::too_many_arguments,
clippy::collapsible_if
)]
#[macro_use]
mod utils;
pub mod widgets;
#[allow(missing_docs)]
mod impls;
pub mod plugin;
pub mod world_inspector;
pub mod prelude {
pub use crate::{Inspectable, InspectorPlugin, RegisterInspectable, WorldInspectorPlugin};
}
use std::hash::Hasher;
use std::marker::PhantomData;
use bevy::ecs::system::Resource;
use bevy::prelude::{App, Mut, World};
use egui::CtxRef;
use utils::error_label_needs_world;
#[doc(inline)]
pub use world_inspector::{InspectableRegistry, WorldInspectorParams, WorldInspectorPlugin};
pub mod reflect;
pub use bevy_egui;
pub use bevy_egui::egui;
pub use bevy_inspector_egui_derive::Inspectable;
#[doc(inline)]
pub use plugin::InspectorPlugin;
pub mod options {
pub use crate::impls::*;
pub use crate::widgets::button::ButtonAttributes;
pub use crate::widgets::new_window::WindowAttributes;
pub use crate::world_inspector::impls::EntityAttributes;
}
pub struct Context<'a> {
pub ui_ctx: Option<&'a CtxRef>,
world: Option<*mut World>,
_world_marker: PhantomData<&'a mut ()>,
pub id: Option<u64>,
}
impl<'a> Context<'a> {
pub fn world(&self) -> Option<&'a World> {
self.world.map(|world| unsafe { &*world })
}
pub unsafe fn world_mut(&mut self) -> Option<&'a mut World> {
match self.world {
Some(world) => Some(&mut *world),
None => None,
}
}
pub fn world_scope(
&mut self,
ui: &mut egui::Ui,
ty: &str,
f: impl FnOnce(&mut World, &mut egui::Ui, &mut Context) -> bool,
) -> bool {
let world = match self.world.take() {
Some(world) => world,
None => return error_label_needs_world(ui, ty),
};
let mut cx = Context {
world: None,
..*self
};
let world = unsafe { &mut *world };
let changed = f(world, ui, &mut cx);
self.world = Some(world);
changed
}
pub unsafe fn world_scope_unchecked(
&mut self,
ui: &mut egui::Ui,
ty: &str,
f: impl FnOnce(&mut World, &mut egui::Ui, &mut Context) -> bool,
) -> bool {
let world = match self.world {
Some(world) => &mut *world,
None => return error_label_needs_world(ui, ty),
};
let changed = f(world, ui, self);
self.world = Some(world);
changed
}
pub fn resource_scope<T: Resource, F: FnOnce(&mut egui::Ui, &mut Context, Mut<T>) -> bool>(
&mut self,
ui: &mut egui::Ui,
ty: &str,
f: F,
) -> bool {
unsafe {
self.world_scope_unchecked(ui, ty, |world, ui, context| {
world.resource_scope(|world, res: Mut<T>| {
let mut context = context.with_world(world);
f(ui, &mut context, res)
})
})
}
}
fn take_world(&mut self) -> Option<(Context, &'a mut World)> {
let world = self.world.take()?;
let world = unsafe { &mut *world };
let context = Context {
world: None,
..*self
};
Some((context, world))
}
}
impl<'a> Context<'a> {
pub fn new_world_access(ui_ctx: Option<&'a CtxRef>, world: &'a mut World) -> Self {
Context {
ui_ctx,
world: Some(world),
_world_marker: PhantomData,
id: None,
}
}
pub fn new_shared(ui_ctx: Option<&'a CtxRef>) -> Self {
Context {
ui_ctx,
world: None,
_world_marker: PhantomData,
id: None,
}
}
pub fn with_world(&self, world: &'a mut World) -> Self {
Context {
world: Some(world),
..*self
}
}
pub fn with_id(&mut self, id: u64) -> Context<'_> {
let mut hasher = std::collections::hash_map::DefaultHasher::default();
if let Some(id) = self.id {
hasher.write_u64(id);
}
hasher.write_u64(id);
let id = hasher.finish();
Context {
id: Some(id),
world: self.world.as_mut().map(|world| *world),
_world_marker: PhantomData,
ui_ctx: self.ui_ctx,
}
}
pub fn id(&self) -> egui::Id {
let dummy_id = egui::Id::new(42);
match self.id {
Some(id) => egui::Id::new(id),
None => dummy_id,
}
}
}
pub trait Inspectable {
type Attributes: Default + Clone;
fn ui(&mut self, ui: &mut egui::Ui, options: Self::Attributes, context: &mut Context) -> bool;
fn ui_raw(&mut self, ui: &mut egui::Ui, options: Self::Attributes) {
let mut empty_context = Context::new_shared(None);
self.ui(ui, options, &mut empty_context);
}
#[allow(unused_variables)]
fn setup(app: &mut App) {}
}
pub trait RegisterInspectable {
fn register_inspectable<T: Inspectable + 'static>(&mut self) -> &mut Self;
}
impl RegisterInspectable for App {
fn register_inspectable<T: Inspectable + 'static>(&mut self) -> &mut Self {
self.world
.get_resource_mut::<InspectableRegistry>()
.unwrap()
.register::<T>();
self
}
}