Skip to main content

gpui_authoring/
inspector.rs

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