1/// A unique identifier for an element that can be inspected.
  2#[derive(Debug, Eq, PartialEq, Hash, Clone)]
  3pub struct InspectorElementId {
  4    /// Stable part of the ID.
  5    #[cfg(any(feature = "inspector", debug_assertions))]
  6    pub path: std::rc::Rc<InspectorElementPath>,
  7    /// Disambiguates elements that have the same path.
  8    #[cfg(any(feature = "inspector", debug_assertions))]
  9    pub instance_id: usize,
 10}
 11
 12impl Into<InspectorElementId> for &InspectorElementId {
 13    fn into(self) -> InspectorElementId {
 14        self.clone()
 15    }
 16}
 17
 18#[cfg(any(feature = "inspector", debug_assertions))]
 19pub use conditional::*;
 20
 21#[cfg(any(feature = "inspector", debug_assertions))]
 22mod conditional {
 23    use super::*;
 24    use crate::{AnyElement, App, Context, Empty, IntoElement, Render, Window};
 25    use collections::FxHashMap;
 26    use std::any::{Any, TypeId};
 27
 28    /// `GlobalElementId` qualified by source location of element construction.
 29    #[derive(Debug, Eq, PartialEq, Hash)]
 30    pub struct InspectorElementPath {
 31        /// The path to the nearest ancestor element that has an `ElementId`.
 32        #[cfg(any(feature = "inspector", debug_assertions))]
 33        pub global_id: crate::GlobalElementId,
 34        /// Source location where this element was constructed.
 35        #[cfg(any(feature = "inspector", debug_assertions))]
 36        pub source_location: &'static std::panic::Location<'static>,
 37    }
 38
 39    impl Clone for InspectorElementPath {
 40        fn clone(&self) -> Self {
 41            Self {
 42                global_id: crate::GlobalElementId(self.global_id.0.clone()),
 43                source_location: self.source_location,
 44            }
 45        }
 46    }
 47
 48    impl Into<InspectorElementPath> for &InspectorElementPath {
 49        fn into(self) -> InspectorElementPath {
 50            self.clone()
 51        }
 52    }
 53
 54    /// Function set on `App` to render the inspector UI.
 55    pub type InspectorRenderer =
 56        Box<dyn Fn(&mut Inspector, &mut Window, &mut Context<Inspector>) -> AnyElement>;
 57
 58    /// Manages inspector state - which element is currently selected and whether the inspector is
 59    /// in picking mode.
 60    pub struct Inspector {
 61        active_element: Option<InspectedElement>,
 62        pub(crate) pick_depth: Option<f32>,
 63    }
 64
 65    struct InspectedElement {
 66        id: InspectorElementId,
 67        states: FxHashMap<TypeId, Box<dyn Any>>,
 68    }
 69
 70    impl InspectedElement {
 71        fn new(id: InspectorElementId) -> Self {
 72            InspectedElement {
 73                id,
 74                states: FxHashMap::default(),
 75            }
 76        }
 77    }
 78
 79    impl Inspector {
 80        pub(crate) fn new() -> Self {
 81            Self {
 82                active_element: None,
 83                pick_depth: Some(0.0),
 84            }
 85        }
 86
 87        pub(crate) fn select(&mut self, id: InspectorElementId, window: &mut Window) {
 88            self.set_active_element_id(id, window);
 89            self.pick_depth = None;
 90        }
 91
 92        pub(crate) fn hover(&mut self, id: InspectorElementId, window: &mut Window) {
 93            if self.is_picking() {
 94                let changed = self.set_active_element_id(id, window);
 95                if changed {
 96                    self.pick_depth = Some(0.0);
 97                }
 98            }
 99        }
100
101        pub(crate) fn set_active_element_id(
102            &mut self,
103            id: InspectorElementId,
104            window: &mut Window,
105        ) -> bool {
106            let changed = Some(&id) != self.active_element_id();
107            if changed {
108                self.active_element = Some(InspectedElement::new(id));
109                window.refresh();
110            }
111            changed
112        }
113
114        /// ID of the currently hovered or selected element.
115        pub fn active_element_id(&self) -> Option<&InspectorElementId> {
116            self.active_element.as_ref().map(|e| &e.id)
117        }
118
119        pub(crate) fn with_active_element_state<T: 'static, R>(
120            &mut self,
121            window: &mut Window,
122            f: impl FnOnce(&mut Option<T>, &mut Window) -> R,
123        ) -> R {
124            let Some(active_element) = &mut self.active_element else {
125                return f(&mut None, window);
126            };
127
128            let type_id = TypeId::of::<T>();
129            let mut inspector_state = active_element
130                .states
131                .remove(&type_id)
132                .map(|state| *state.downcast().unwrap());
133
134            let result = f(&mut inspector_state, window);
135
136            if let Some(inspector_state) = inspector_state {
137                active_element
138                    .states
139                    .insert(type_id, Box::new(inspector_state));
140            }
141
142            result
143        }
144
145        /// Starts element picking mode, allowing the user to select elements by clicking.
146        pub fn start_picking(&mut self) {
147            self.pick_depth = Some(0.0);
148        }
149
150        /// Returns whether the inspector is currently in picking mode.
151        pub fn is_picking(&self) -> bool {
152            self.pick_depth.is_some()
153        }
154
155        /// Renders elements for all registered inspector states of the active inspector element.
156        pub fn render_inspector_states(
157            &mut self,
158            window: &mut Window,
159            cx: &mut Context<Self>,
160        ) -> Vec<AnyElement> {
161            let mut elements = Vec::new();
162            if let Some(active_element) = self.active_element.take() {
163                for (type_id, state) in &active_element.states {
164                    if let Some(render_inspector) = cx
165                        .inspector_element_registry
166                        .renderers_by_type_id
167                        .remove(type_id)
168                    {
169                        let mut element = (render_inspector)(
170                            active_element.id.clone(),
171                            state.as_ref(),
172                            window,
173                            cx,
174                        );
175                        elements.push(element);
176                        cx.inspector_element_registry
177                            .renderers_by_type_id
178                            .insert(*type_id, render_inspector);
179                    }
180                }
181
182                self.active_element = Some(active_element);
183            }
184
185            elements
186        }
187    }
188
189    impl Render for Inspector {
190        fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
191            if let Some(inspector_renderer) = cx.inspector_renderer.take() {
192                let result = inspector_renderer(self, window, cx);
193                cx.inspector_renderer = Some(inspector_renderer);
194                result
195            } else {
196                Empty.into_any_element()
197            }
198        }
199    }
200
201    #[derive(Default)]
202    pub(crate) struct InspectorElementRegistry {
203        renderers_by_type_id: FxHashMap<
204            TypeId,
205            Box<dyn Fn(InspectorElementId, &dyn Any, &mut Window, &mut App) -> AnyElement>,
206        >,
207    }
208
209    impl InspectorElementRegistry {
210        pub fn register<T: 'static, R: IntoElement>(
211            &mut self,
212            f: impl 'static + Fn(InspectorElementId, &T, &mut Window, &mut App) -> R,
213        ) {
214            self.renderers_by_type_id.insert(
215                TypeId::of::<T>(),
216                Box::new(move |id, value, window, cx| {
217                    let value = value.downcast_ref().unwrap();
218                    f(id, value, window, cx).into_any_element()
219                }),
220            );
221        }
222    }
223}
224
225/// Provides definitions used by `#[derive_inspector_reflection]`.
226#[cfg(any(feature = "inspector", debug_assertions))]
227pub mod inspector_reflection {
228    use std::any::Any;
229
230    /// Reification of a function that has the signature `fn some_fn(T) -> T`. Provides the name,
231    /// documentation, and ability to invoke the function.
232    #[derive(Clone, Copy)]
233    pub struct FunctionReflection<T> {
234        /// The name of the function
235        pub name: &'static str,
236        /// The method
237        pub function: fn(Box<dyn Any>) -> Box<dyn Any>,
238        /// Documentation for the function
239        pub documentation: Option<&'static str>,
240        /// `PhantomData` for the type of the argument and result
241        pub _type: std::marker::PhantomData<T>,
242    }
243
244    impl<T: 'static> FunctionReflection<T> {
245        /// Invoke this method on a value and return the result.
246        pub fn invoke(&self, value: T) -> T {
247            let boxed = Box::new(value) as Box<dyn Any>;
248            let result = (self.function)(boxed);
249            *result
250                .downcast::<T>()
251                .expect("Type mismatch in reflection invoke")
252        }
253    }
254}