#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct InspectorElementId {
#[cfg(any(feature = "inspector", debug_assertions))]
pub path: std::rc::Rc<InspectorElementPath>,
#[cfg(any(feature = "inspector", debug_assertions))]
pub instance_id: usize,
}
impl Into<InspectorElementId> for &InspectorElementId {
fn into(self) -> InspectorElementId {
self.clone()
}
}
#[cfg(any(feature = "inspector", debug_assertions))]
pub use conditional::*;
#[cfg(any(feature = "inspector", debug_assertions))]
mod conditional {
use super::*;
use crate::{AnyElement, App, Context, Empty, IntoElement, Render, Window};
use collections::{FxHashMap, TypeIdHashMap, hash_map::Entry};
use std::any::{Any, TypeId};
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct InspectorElementPath {
#[cfg(any(feature = "inspector", debug_assertions))]
pub global_id: crate::GlobalElementId,
#[cfg(any(feature = "inspector", debug_assertions))]
pub source_location: &'static std::panic::Location<'static>,
}
impl Clone for InspectorElementPath {
fn clone(&self) -> Self {
Self {
global_id: self.global_id.clone(),
source_location: self.source_location,
}
}
}
impl Into<InspectorElementPath> for &InspectorElementPath {
fn into(self) -> InspectorElementPath {
self.clone()
}
}
pub type InspectorRenderer =
Box<dyn Fn(&mut Inspector, &mut Window, &mut Context<Inspector>) -> AnyElement>;
pub struct Inspector {
active_element: Option<InspectedElement>,
pub(crate) pick_depth: Option<f32>,
renderers: TypeIdHashMap<InspectorElementRenderer>,
}
struct InspectedElement {
id: InspectorElementId,
states: TypeIdHashMap<Box<dyn Any>>,
}
impl InspectedElement {
fn new(id: InspectorElementId) -> Self {
InspectedElement {
id,
states: Default::default(),
}
}
}
impl Inspector {
pub(crate) fn new() -> Self {
Self {
active_element: None,
pick_depth: Some(0.0),
renderers: TypeIdHashMap::default(),
}
}
pub(crate) fn select(&mut self, id: InspectorElementId, window: &mut Window) {
self.set_active_element_id(id, window);
self.pick_depth = None;
}
pub(crate) fn hover(&mut self, id: InspectorElementId, window: &mut Window) {
if self.is_picking() {
let changed = self.set_active_element_id(id, window);
if changed {
self.pick_depth = Some(0.0);
}
}
}
pub(crate) fn set_active_element_id(
&mut self,
id: InspectorElementId,
window: &mut Window,
) -> bool {
let changed = Some(&id) != self.active_element_id();
if changed {
self.active_element = Some(InspectedElement::new(id));
window.refresh();
}
changed
}
pub fn active_element_id(&self) -> Option<&InspectorElementId> {
self.active_element.as_ref().map(|e| &e.id)
}
pub(crate) fn with_active_element_state<T: 'static, R>(
&mut self,
window: &mut Window,
f: impl FnOnce(&mut Option<T>, &mut Window) -> R,
) -> R {
let Some(active_element) = &mut self.active_element else {
return f(&mut None, window);
};
let type_id = TypeId::of::<T>();
let mut inspector_state = active_element
.states
.remove(&type_id)
.map(|state| *state.downcast().unwrap());
let result = f(&mut inspector_state, window);
if let Some(inspector_state) = inspector_state {
active_element
.states
.insert(type_id, Box::new(inspector_state));
}
result
}
pub fn start_picking(&mut self) {
self.pick_depth = Some(0.0);
}
pub fn is_picking(&self) -> bool {
self.pick_depth.is_some()
}
pub fn render_inspector_states(
&mut self,
window: &mut Window,
cx: &mut Context<Self>,
) -> Vec<AnyElement> {
let mut elements = Vec::new();
if let Some(active_element) = self.active_element.take() {
for (type_id, state) in &active_element.states {
let renderer = match self.renderers.entry(*type_id) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
let Some(factory) = cx
.inspector_element_registry
.factories_by_type_id
.remove(type_id)
else {
continue;
};
let renderer = factory(window, cx);
cx.inspector_element_registry
.factories_by_type_id
.insert(*type_id, factory);
entry.insert(renderer)
}
};
elements.push(renderer(
active_element.id.clone(),
state.as_ref(),
window,
cx,
));
}
self.active_element = Some(active_element);
}
elements
}
}
impl Render for Inspector {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
if let Some(inspector_renderer) = cx.inspector_renderer.take() {
let result = inspector_renderer(self, window, cx);
cx.inspector_renderer = Some(inspector_renderer);
result
} else {
Empty.into_any_element()
}
}
}
#[derive(Default)]
pub(crate) struct InspectorElementRegistry {
factories_by_type_id:
FxHashMap<TypeId, Box<dyn Fn(&mut Window, &mut App) -> InspectorElementRenderer>>,
}
impl InspectorElementRegistry {
pub fn register<T: 'static, R: IntoElement, F>(
&mut self,
factory: impl 'static + Fn(&mut Window, &mut App) -> F,
) where
F: 'static + FnMut(InspectorElementId, &T, &mut Window, &mut App) -> R,
{
self.factories_by_type_id.insert(
TypeId::of::<T>(),
Box::new(move |window, cx| {
let mut renderer = factory(window, cx);
Box::new(move |id, value, window, cx| {
let value = value
.downcast_ref()
.expect("registered inspector state type");
renderer(id, value, window, cx).into_any_element()
})
}),
);
}
}
type InspectorElementRenderer =
Box<dyn FnMut(InspectorElementId, &dyn Any, &mut Window, &mut App) -> AnyElement>;
}
#[cfg(any(feature = "inspector", debug_assertions))]
pub mod inspector_reflection {
use std::any::Any;
#[derive(Clone, Copy)]
pub struct FunctionReflection<T> {
pub name: &'static str,
pub function: fn(Box<dyn Any>) -> Box<dyn Any>,
pub documentation: Option<&'static str>,
pub _type: std::marker::PhantomData<T>,
}
impl<T: 'static> FunctionReflection<T> {
pub fn invoke(&self, value: T) -> T {
let boxed = Box::new(value) as Box<dyn Any>;
let result = (self.function)(boxed);
*result
.downcast::<T>()
.expect("Type mismatch in reflection invoke")
}
}
}