use std::ops::{Deref, DerefMut};
use bevy::prelude::*;
use bevy::reflect::{List, Map, Tuple};
use bevy_egui::egui;
use egui::Grid;
use crate::{Context, Inspectable, InspectableRegistry};
#[derive(Debug, Default)]
pub struct ReflectedUI<T>(T);
impl<T> ReflectedUI<T> {
#[allow(missing_docs)]
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, context: &Context) -> bool {
ui_for_reflect(&mut self.0, ui, context)
}
}
macro_rules! try_downcast_ui {
($value:ident $ui:ident $context:ident => $ty:ty) => {
if let Some(v) = $value.downcast_mut::<$ty>() {
return <$ty as Inspectable>::ui(v, $ui, <$ty as Inspectable>::Attributes::default(), $context);
}
};
( $value:ident $ui:ident $context:ident => $( $ty:ty ),+ $(,)? ) => {
$(try_downcast_ui!($value $ui $context => $ty);)*
};
}
pub fn ui_for_reflect(value: &mut dyn Reflect, ui: &mut egui::Ui, context: &Context) -> bool {
if let Some(world) = unsafe { context.world() } {
if let Some(inspect_registry) = world.get_resource::<InspectableRegistry>() {
if let Ok(changed) = inspect_registry.try_execute(value, ui, context) {
return changed;
}
}
}
match value.reflect_mut() {
bevy::reflect::ReflectMut::Struct(s) => ui_for_reflect_struct(s, ui, context),
bevy::reflect::ReflectMut::TupleStruct(value) => ui_for_tuple_struct(value, ui, context),
bevy::reflect::ReflectMut::Tuple(value) => ui_for_tuple(value, ui, context),
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, context),
}
}
fn ui_for_reflect_struct(value: &mut dyn Struct, ui: &mut egui::Ui, context: &Context) -> bool {
let mut changed = false;
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) {
changed |= ui_for_reflect(field, ui, &context.with_id(i as u64));
} else {
ui.label("<missing>");
}
ui.end_row();
}
});
});
changed
}
fn ui_for_tuple_struct(value: &mut dyn TupleStruct, ui: &mut egui::Ui, context: &Context) -> bool {
let mut changed = false;
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) {
changed |= ui_for_reflect(field, ui, &context.with_id(i as u64));
} else {
ui.label("<missing>");
}
ui.end_row();
}
});
changed
}
fn ui_for_tuple(value: &mut dyn Tuple, ui: &mut egui::Ui, context: &Context) -> bool {
let mut changed = false;
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) {
changed |= ui_for_reflect(field, ui, &context.with_id(i as u64));
} else {
ui.label("<missing>");
}
ui.end_row();
}
});
changed
}
fn ui_for_list(_value: &mut dyn List, ui: &mut egui::Ui) -> bool {
ui.label("List not yet implemented");
false
}
fn ui_for_map(_value: &mut dyn Map, ui: &mut egui::Ui) -> bool {
ui.label("Map not yet implemented");
false
}
fn ui_for_reflect_value(value: &mut dyn Reflect, ui: &mut egui::Ui, context: &Context) -> bool {
try_downcast_ui!(
value ui context =>
f32, f64, u8, u16, u32, u64, i8, i16, i32, i64,
String, bool,
Vec2, Vec3, Vec4, Mat3, Mat4,
);
try_downcast_ui!(value ui context => Option<String>);
ui.label(format!("Not implemented: {}", value.type_name()));
false
}