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: self.global_id.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}