Skip to content

Commit

Permalink
add ReflectedUI wrapper type for implementing Inspectable based on
Browse files Browse the repository at this point in the history
Reflect
  • Loading branch information
jakobhellermann committed Jan 22, 2021
1 parent e6ae23c commit caf1c31
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 0 deletions.
25 changes: 25 additions & 0 deletions examples/reflect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use bevy::prelude::*;
use bevy_inspector_egui::{reflect::ReflectedUI, Inspectable, InspectorPlugin};

#[derive(Inspectable, Default, Debug)]
struct Data {
// it works for custom reflect types
custom: ReflectedUI<MyComponent>,
// also for builtin implementations
color: ReflectedUI<Color>,
// and for most of bevy's types
timer: ReflectedUI<Timer>,
}

#[derive(Reflect, Default, Debug)]
struct MyComponent {
a: f32,
b: Vec2,
}

fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_plugin(InspectorPlugin::<Data>::new())
.run();
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
mod impls;
mod plugin;

/// `Inspectable` implementation for foreign types implementing `Reflect`
pub mod reflect;

pub use bevy_egui::egui;

/// Derives the [`Inspectable`](Inspectable) trait.
Expand Down
143 changes: 143 additions & 0 deletions src/reflect/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use std::ops::{Deref, DerefMut};

use bevy::prelude::*;
use bevy::reflect::{List, Map};
use bevy_egui::egui;
use egui::Grid;

use crate::Inspectable;

/// Wrapper type for displaying inspector UI based on the types [`Reflect`](bevy::reflect::Reflect) implementation.
///
/// Say you wanted to display a type defined in another crate in the inspector, and that type implements `Reflect`.
/// ```rust
/// # use bevy::prelude::*;
/// #[derive(Reflect, Default)]
/// struct SomeComponent {
/// a: f32,
/// b: Vec2,
/// }
/// ```
///
/// Using the `ReflectedUI` wrapper type, you can include it in your inspector
/// and edit the fields like you would expect:
///
/// ```rust
/// # use bevy::prelude::*;
/// # use bevy_inspector_egui::{Inspectable, reflect::ReflectedUI};
/// # #[derive(Reflect, Default)] struct SomeComponent;
/// #[derive(Inspectable, Default)]
/// struct Data {
/// component: ReflectedUI<SomeComponent>,
/// // it also works for bevy's types
/// timer: ReflectedUI<Timer>,
/// }
/// ```
#[derive(Debug, Default)]
pub struct ReflectedUI<T>(T);
impl<T> ReflectedUI<T> {
pub fn new(val: T) -> Self {
ReflectedUI(val)
}
}

impl<T> Deref for ReflectedUI<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for ReflectedUI<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl<T: Reflect> Inspectable for ReflectedUI<T> {
type Attributes = ();

fn ui(&mut self, ui: &mut egui::Ui, _: Self::Attributes) {
ui_for_reflect(&mut self.0, ui);
}
}

macro_rules! try_downcast_ui {
($value:ident $ui:ident => $ty:ty) => {
if let Some(v) = $value.downcast_mut::<$ty>() {
<$ty as Inspectable>::ui(v, $ui, <$ty as Inspectable>::Attributes::default());
return;
}
};

( $value:ident $ui:ident => $( $ty:ty ),+ $(,)? ) => {
$(try_downcast_ui!($value $ui => $ty);)*
};
}

fn ui_for_reflect(value: &mut dyn Reflect, ui: &mut egui::Ui) {
try_downcast_ui!(value ui => Color);

match value.reflect_mut() {
bevy::reflect::ReflectMut::Struct(s) => ui_for_reflect_struct(s, ui),
bevy::reflect::ReflectMut::TupleStruct(value) => ui_for_tuple_struct(value, ui),
bevy::reflect::ReflectMut::List(value) => ui_for_list(value, ui),
bevy::reflect::ReflectMut::Map(value) => ui_for_map(value, ui),
bevy::reflect::ReflectMut::Value(value) => ui_for_reflect_value(value, ui),
}
}

fn ui_for_reflect_struct(value: &mut dyn Struct, ui: &mut egui::Ui) {
ui.vertical_centered(|ui| {
let grid = Grid::new(value.type_id());
grid.show(ui, |ui| {
for i in 0..value.field_len() {
match value.name_at(i) {
Some(name) => ui.label(name),
None => ui.label("<missing>"),
};
if let Some(field) = value.field_at_mut(i) {
ui_for_reflect(field, ui);
} else {
ui.label("<missing>");
}
ui.end_row();
}
});
});
}

fn ui_for_tuple_struct(value: &mut dyn TupleStruct, ui: &mut egui::Ui) {
let grid = Grid::new(value.type_id());
grid.show(ui, |ui| {
for i in 0..value.field_len() {
ui.label(i.to_string());
if let Some(field) = value.field_mut(i) {
ui_for_reflect(field, ui);
} else {
ui.label("<missing>");
}
ui.end_row();
}
});
}

fn ui_for_list(_value: &mut dyn List, ui: &mut egui::Ui) {
ui.label("List not yet implemented");
}

fn ui_for_map(_value: &mut dyn Map, ui: &mut egui::Ui) {
ui.label("Map not yet implemented");
}

fn ui_for_reflect_value(value: &mut dyn Reflect, ui: &mut egui::Ui) {
try_downcast_ui!(
value ui =>
f32, f64, u8, u16, u32, u64, i8, i16, i32, i64,
String, bool,
Vec2, Vec3, Vec4, Mat3, Mat4,
Transform, Quat,
);

ui.label(format!("Not implemented: {}", value.type_name()));
}

0 comments on commit caf1c31

Please sign in to comment.