pub mod animation;
pub mod audio;
pub mod bounding_volume;
pub mod camera;
#[cfg(feature = "physics")]
pub mod character_controller;
pub mod decal;
pub mod grass;
pub mod helpers;
pub mod instanced_mesh;
pub mod light;
pub mod lines;
pub mod material;
pub mod mesh;
pub mod name;
#[cfg(feature = "navmesh")]
pub mod navmesh;
pub mod navmesh_agent;
pub mod particle_emitter;
#[cfg(feature = "physics")]
pub mod physics;
pub mod render_layer;
#[cfg(feature = "scripting")]
pub mod script;
pub mod text;
pub mod transform;
pub mod visibility;
pub mod water;
use super::selection::EntitySelection;
use super::undo::UndoHistory;
use crate::mosaic::ToastKind;
use crate::prelude::*;
use animation::*;
use audio::*;
use bounding_volume::*;
use camera::*;
#[cfg(feature = "physics")]
use character_controller::*;
use decal::*;
use grass::*;
use instanced_mesh::*;
use light::*;
use lines::*;
use material::*;
use mesh::*;
use name::*;
use navmesh_agent::*;
use particle_emitter::*;
#[cfg(feature = "physics")]
use physics::*;
use render_layer::*;
#[cfg(feature = "scripting")]
use script::*;
use text::*;
use transform::*;
use visibility::*;
use water::*;
#[derive(Clone)]
pub enum InspectorAction {
LookupMaterial(String),
}
pub struct InspectorContext<'a> {
pub transform_edit_pending: &'a mut Option<(Entity, LocalTransform)>,
pub undo_history: &'a mut UndoHistory,
pub pending_notifications: &'a mut Vec<(ToastKind, String)>,
pub actions: &'a mut Vec<InspectorAction>,
pub selection: &'a EntitySelection,
}
pub trait ComponentInspector {
fn name(&self) -> &str;
fn has_component(&self, world: &World, entity: Entity) -> bool;
fn add_component(&self, world: &mut World, entity: Entity);
fn remove_component(&self, world: &mut World, entity: Entity);
fn ui(
&mut self,
world: &mut World,
entity: Entity,
ui: &mut egui::Ui,
context: &mut InspectorContext,
);
}
macro_rules! impl_simple_inspector {
($struct_name:ident, $display_name:expr, $has:ident, $set:ident, $remove:ident, $default_val:expr, $ui_fn:path) => {
pub struct $struct_name;
impl ComponentInspector for $struct_name {
fn name(&self) -> &str {
$display_name
}
fn has_component(&self, world: &World, entity: Entity) -> bool {
world.$has(entity)
}
fn add_component(&self, world: &mut World, entity: Entity) {
world.$set(entity, $default_val);
}
fn remove_component(&self, world: &mut World, entity: Entity) {
world.$remove(entity);
}
fn ui(
&mut self,
world: &mut World,
entity: Entity,
ui: &mut egui::Ui,
context: &mut InspectorContext,
) {
$ui_fn(world, entity, ui, context);
}
}
};
($struct_name:ident, $display_name:expr, $has:ident, $set:ident, $remove:ident, $default_val:expr) => {
pub struct $struct_name;
impl ComponentInspector for $struct_name {
fn name(&self) -> &str {
$display_name
}
fn has_component(&self, world: &World, entity: Entity) -> bool {
world.$has(entity)
}
fn add_component(&self, world: &mut World, entity: Entity) {
world.$set(entity, $default_val);
}
fn remove_component(&self, world: &mut World, entity: Entity) {
world.$remove(entity);
}
fn ui(
&mut self,
_world: &mut World,
_entity: Entity,
ui: &mut egui::Ui,
_context: &mut InspectorContext,
) {
ui.label(concat!($display_name, " (no inspector)"));
}
}
};
($struct_name:ident, $display_name:expr, $has:ident, $remove:ident, $ui_fn:path, custom_add => $add_body:expr) => {
pub struct $struct_name;
impl ComponentInspector for $struct_name {
fn name(&self) -> &str {
$display_name
}
fn has_component(&self, world: &World, entity: Entity) -> bool {
world.$has(entity)
}
fn add_component(&self, world: &mut World, entity: Entity) {
$add_body(world, entity);
}
fn remove_component(&self, world: &mut World, entity: Entity) {
world.$remove(entity);
}
fn ui(
&mut self,
world: &mut World,
entity: Entity,
ui: &mut egui::Ui,
context: &mut InspectorContext,
) {
$ui_fn(world, entity, ui, context);
}
}
};
}
pub(crate) use impl_simple_inspector;
pub struct ComponentInspectorUi {
inspectors: Vec<Box<dyn ComponentInspector>>,
}
impl Default for ComponentInspectorUi {
fn default() -> Self {
let inspectors: Vec<Box<dyn ComponentInspector>> = vec![
Box::new(NameInspector),
Box::new(TransformInspector::default()),
Box::new(VisibilityInspector),
Box::new(CameraInspector),
Box::new(LightInspector),
Box::new(LinesInspector),
Box::new(MeshInspector),
Box::new(InstancedMeshInspector),
Box::new(MaterialInspector::default()),
Box::new(BoundingVolumeInspector),
Box::new(WaterInspector),
Box::new(DecalInspector),
Box::new(TextInspector),
Box::new(RenderLayerInspector),
Box::new(ParticleEmitterInspector),
Box::new(AudioSourceInspector),
Box::new(AudioListenerInspector),
Box::new(GrassRegionInspector),
Box::new(GrassInteractorInspector),
Box::new(NavMeshAgentInspector),
Box::new(AnimationInspector),
#[cfg(feature = "physics")]
Box::new(CharacterControllerInspector),
#[cfg(feature = "physics")]
Box::new(PhysicsInspector),
#[cfg(feature = "scripting")]
Box::new(ScriptInspector::default()),
];
Self { inspectors }
}
}
impl ComponentInspectorUi {
pub fn add_inspector(&mut self, inspector: Box<dyn ComponentInspector>) {
self.inspectors.push(inspector);
}
pub fn ui(
&mut self,
context: &mut InspectorContext,
world: &mut World,
ui: &mut egui::Ui,
) -> bool {
let mut project_modified = false;
let selection_count = context.selection.len();
if selection_count > 1 {
ui.vertical_centered(|ui| {
ui.add_space(8.0);
ui.label(format!("{} entities selected", selection_count));
ui.add_space(8.0);
});
return false;
}
if let Some(entity) = context.selection.primary() {
ui.group(|ui| {
ui.horizontal(|ui| {
ui.label("Add Component:");
egui::ComboBox::new("add_component", "").show_ui(ui, |ui| {
for inspector in self.inspectors.iter() {
if !inspector.has_component(world, entity)
&& ui.button(inspector.name()).clicked()
{
inspector.add_component(world, entity);
project_modified = true;
}
}
});
});
});
ui.separator();
for inspector in self.inspectors.iter_mut() {
if inspector.has_component(world, entity) {
ui.group(|ui| {
inspector.ui(world, entity, ui, context);
if ui.button("Remove Component").clicked() {
inspector.remove_component(world, entity);
project_modified = true;
}
});
ui.separator();
}
}
#[cfg(feature = "navmesh")]
{
navmesh::navmesh_section_ui(context.selection, world, ui);
navmesh::colliders_section_ui(context.selection, world, ui);
}
} else {
ui.vertical_centered(|ui| {
ui.add_space(8.0);
ui.label(
egui::RichText::new("No entity selected").color(egui::Color32::from_gray(128)),
);
ui.add_space(8.0);
});
}
project_modified
}
}