/// A unique identifier for an element that can be inspected. #[derive(Debug, Eq, PartialEq, Hash, Clone)] pub struct InspectorElementId { /// Stable part of the ID. #[cfg(any(feature = "inspector", debug_assertions))] pub path: std::rc::Rc, /// Disambiguates elements that have the same path. #[cfg(any(feature = "inspector", debug_assertions))] pub instance_id: usize, } impl Into 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; use std::any::{Any, TypeId}; /// `GlobalElementId` qualified by source location of element construction. #[derive(Debug, Eq, PartialEq, Hash)] pub struct InspectorElementPath { /// The path to the nearest ancestor element that has an `ElementId`. #[cfg(any(feature = "inspector", debug_assertions))] pub global_id: crate::GlobalElementId, /// Source location where this element was constructed. #[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: crate::GlobalElementId(self.global_id.0.clone()), source_location: self.source_location, } } } impl Into for &InspectorElementPath { fn into(self) -> InspectorElementPath { self.clone() } } /// Function set on `App` to render the inspector UI. pub type InspectorRenderer = Box) -> AnyElement>; /// Manages inspector state - which element is currently selected and whether the inspector is /// in picking mode. pub struct Inspector { active_element: Option, pub(crate) pick_depth: Option, } struct InspectedElement { id: InspectorElementId, states: FxHashMap>, } impl InspectedElement { fn new(id: InspectorElementId) -> Self { InspectedElement { id, states: FxHashMap::default(), } } } impl Inspector { pub(crate) fn new() -> Self { Self { active_element: None, pick_depth: Some(0.0), } } 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 } /// ID of the currently hovered or selected element. pub fn active_element_id(&self) -> Option<&InspectorElementId> { self.active_element.as_ref().map(|e| &e.id) } pub(crate) fn with_active_element_state( &mut self, window: &mut Window, f: impl FnOnce(&mut Option, &mut Window) -> R, ) -> R { let Some(active_element) = &mut self.active_element else { return f(&mut None, window); }; let type_id = TypeId::of::(); 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 } /// Starts element picking mode, allowing the user to select elements by clicking. pub fn start_picking(&mut self) { self.pick_depth = Some(0.0); } /// Returns whether the inspector is currently in picking mode. pub fn is_picking(&self) -> bool { self.pick_depth.is_some() } /// Renders elements for all registered inspector states of the active inspector element. pub fn render_inspector_states( &mut self, window: &mut Window, cx: &mut Context, ) -> Vec { let mut elements = Vec::new(); if let Some(active_element) = self.active_element.take() { for (type_id, state) in &active_element.states { if let Some(render_inspector) = cx .inspector_element_registry .renderers_by_type_id .remove(type_id) { let mut element = (render_inspector)( active_element.id.clone(), state.as_ref(), window, cx, ); elements.push(element); cx.inspector_element_registry .renderers_by_type_id .insert(*type_id, render_inspector); } } self.active_element = Some(active_element); } elements } } impl Render for Inspector { fn render(&mut self, window: &mut Window, cx: &mut Context) -> 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 { renderers_by_type_id: FxHashMap< TypeId, Box AnyElement>, >, } impl InspectorElementRegistry { pub fn register( &mut self, f: impl 'static + Fn(InspectorElementId, &T, &mut Window, &mut App) -> R, ) { self.renderers_by_type_id.insert( TypeId::of::(), Box::new(move |id, value, window, cx| { let value = value.downcast_ref().unwrap(); f(id, value, window, cx).into_any_element() }), ); } } } /// Provides definitions used by `#[derive_inspector_reflection]`. #[cfg(any(feature = "inspector", debug_assertions))] pub mod inspector_reflection { use std::any::Any; /// Reification of a function that has the signature `fn some_fn(T) -> T`. Provides the name, /// documentation, and ability to invoke the function. #[derive(Clone, Copy)] pub struct FunctionReflection { /// The name of the function pub name: &'static str, /// The method pub function: fn(Box) -> Box, /// Documentation for the function pub documentation: Option<&'static str>, /// `PhantomData` for the type of the argument and result pub _type: std::marker::PhantomData, } impl FunctionReflection { /// Invoke this method on a value and return the result. pub fn invoke(&self, value: T) -> T { let boxed = Box::new(value) as Box; let result = (self.function)(boxed); *result .downcast::() .expect("Type mismatch in reflection invoke") } } }