zng_wgt_inspector/live/
data_model.rs

1use std::{fmt, ops, sync::Arc};
2
3use parking_lot::Mutex;
4use zng_app::widget::{
5    builder::WidgetType,
6    info::WidgetInfoTree,
7    inspector::{InspectorInfo, WidgetInfoInspectorExt},
8};
9use zng_var::{WeakVar, types::WeakArcVar};
10use zng_view_api::window::FrameId;
11use zng_wgt::prelude::*;
12
13#[derive(Default)]
14pub struct InspectedTreeData {
15    widgets: IdMap<WidgetId, InspectedWidget>,
16    latest_frame: Option<ArcVar<FrameId>>,
17}
18
19/// Represents an actively inspected widget tree.
20#[derive(Clone)]
21pub struct InspectedTree {
22    tree: ArcVar<WidgetInfoTree>,
23    data: Arc<Mutex<InspectedTreeData>>,
24}
25impl fmt::Debug for InspectedTree {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        f.debug_struct("InspectedTree")
28            .field("tree", &self.tree.get())
29            .finish_non_exhaustive()
30    }
31}
32impl PartialEq for InspectedTree {
33    fn eq(&self, other: &Self) -> bool {
34        self.tree.var_ptr() == other.tree.var_ptr()
35    }
36}
37impl InspectedTree {
38    /// Initial inspection.
39    pub fn new(tree: WidgetInfoTree) -> Self {
40        Self {
41            data: Arc::new(Mutex::new(InspectedTreeData::default())),
42            tree: var(tree),
43        }
44    }
45
46    /// Update inspection.
47    ///
48    /// # Panics
49    ///
50    /// Panics if info is not for the same window ID.
51    pub fn update(&self, tree: WidgetInfoTree) {
52        assert_eq!(self.tree.with(|t| t.window_id()), tree.window_id());
53
54        // update and retain
55        self.tree.set(tree.clone());
56
57        let mut data = self.data.lock();
58        let mut removed = false;
59        for (k, v) in data.widgets.iter() {
60            if let Some(w) = tree.get(*k) {
61                v.update(w);
62            } else {
63                v.removed.set(true);
64                removed = true;
65            }
66        }
67        // update can drop children inspectors so we can't update inside the retain closure.
68        data.widgets
69            .retain(|k, v| v.info.strong_count() > 1 && (!removed || tree.get(*k).is_some()));
70
71        if let Some(f) = &data.latest_frame {
72            if f.strong_count() == 1 {
73                data.latest_frame = None;
74            } else {
75                f.set(tree.stats().last_frame);
76            }
77        }
78    }
79
80    /// Update all render watcher variables.
81    pub fn update_render(&self) {
82        let mut data = self.data.lock();
83        if let Some(f) = &data.latest_frame {
84            if f.strong_count() == 1 {
85                data.latest_frame = None;
86            } else {
87                f.set(self.tree.with(|t| t.stats().last_frame));
88            }
89        }
90    }
91
92    /// Create a weak reference to this tree.
93    pub fn downgrade(&self) -> WeakInspectedTree {
94        WeakInspectedTree {
95            tree: self.tree.downgrade(),
96            data: Arc::downgrade(&self.data),
97        }
98    }
99
100    /// Gets a widget inspector if the widget is in the latest info.
101    pub fn inspect(&self, widget_id: WidgetId) -> Option<InspectedWidget> {
102        match self.data.lock().widgets.entry(widget_id) {
103            IdEntry::Occupied(e) => Some(e.get().clone()),
104            IdEntry::Vacant(e) => self.tree.with(|t| {
105                t.get(widget_id)
106                    .map(|w| e.insert(InspectedWidget::new(w, self.downgrade())).clone())
107            }),
108        }
109    }
110
111    /// Gets a widget inspector for the root widget.
112    pub fn inspect_root(&self) -> InspectedWidget {
113        self.inspect(self.tree.with(|t| t.root().id())).unwrap()
114    }
115
116    /// Latest frame updated using [`update_render`].
117    ///
118    /// [`update_render`]: Self::update_render
119    pub fn last_frame(&self) -> impl Var<FrameId> {
120        let mut data = self.data.lock();
121        data.latest_frame
122            .get_or_insert_with(|| var(self.tree.with(|t| t.stats().last_frame)))
123            .clone()
124    }
125}
126
127/// Represents a weak reference to a [`InspectedTree`].
128#[derive(Clone)]
129pub struct WeakInspectedTree {
130    tree: WeakArcVar<WidgetInfoTree>,
131    data: std::sync::Weak<Mutex<InspectedTreeData>>,
132}
133impl WeakInspectedTree {
134    /// Try to get a strong reference to the inspected tree.
135    pub fn upgrade(&self) -> Option<InspectedTree> {
136        Some(InspectedTree {
137            tree: self.tree.upgrade()?,
138            data: self.data.upgrade()?,
139        })
140    }
141}
142
143struct InspectedWidgetCache {
144    tree: WeakInspectedTree,
145    children: Option<BoxedVar<Vec<InspectedWidget>>>,
146    parent_property_name: Option<BoxedVar<Txt>>,
147}
148
149/// Represents an actively inspected widget.
150///
151/// See [`InspectedTree::inspect`].
152#[derive(Clone)]
153pub struct InspectedWidget {
154    info: ArcVar<WidgetInfo>,
155    removed: ArcVar<bool>,
156    cache: Arc<Mutex<InspectedWidgetCache>>,
157}
158impl fmt::Debug for InspectedWidget {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        f.debug_struct("InspectedWidget")
161            .field("info", &self.info.get())
162            .field("removed", &self.removed.get())
163            .finish_non_exhaustive()
164    }
165}
166impl PartialEq for InspectedWidget {
167    fn eq(&self, other: &Self) -> bool {
168        self.info.var_ptr() == other.info.var_ptr()
169    }
170}
171impl Eq for InspectedWidget {}
172impl InspectedWidget {
173    /// Initial inspection.
174    fn new(info: WidgetInfo, tree: WeakInspectedTree) -> Self {
175        Self {
176            info: var(info),
177            removed: var(false),
178            cache: Arc::new(Mutex::new(InspectedWidgetCache {
179                tree,
180                children: None,
181                parent_property_name: None,
182            })),
183        }
184    }
185
186    /// Update inspection.
187    ///
188    /// # Panics
189    ///
190    /// Panics if info is not for the same widget ID.
191    fn update(&self, info: WidgetInfo) {
192        assert_eq!(self.info.with(|i| i.id()), info.id());
193        self.info.set(info);
194
195        let mut cache = self.cache.lock();
196        if let Some(c) = &cache.children {
197            if c.strong_count() == 1 {
198                cache.children = None;
199            }
200        }
201        if let Some(c) = &cache.parent_property_name {
202            if c.strong_count() == 1 {
203                cache.parent_property_name = None;
204            }
205        }
206    }
207
208    // /// If this widget inspector is permanently disconnected and will not update.
209    // ///
210    // /// This is set to `true` when an inspected widget is not found after an update, when `true`
211    // /// this inspector will not update even if the same widget ID is re-inserted in another update.
212    // pub fn removed(&self) -> impl Var<bool> {
213    //     self.removed.read_only()
214    // }
215
216    /// Latest info.
217    pub fn info(&self) -> impl Var<WidgetInfo> {
218        self.info.read_only()
219    }
220
221    /// Widget id.
222    pub fn id(&self) -> WidgetId {
223        self.info.with(|i| i.id())
224    }
225
226    // /// Count of ancestor widgets.
227    // pub fn depth(&self) -> impl Var<usize> {
228    //     self.info.map(|w| w.depth()).actual_var()
229    // }
230
231    /// Count of descendant widgets.
232    pub fn descendants_len(&self) -> impl Var<usize> {
233        self.info.map(|w| w.descendants_len()).actual_var()
234    }
235
236    /// Widget type, if the widget was built with inspection info.
237    pub fn wgt_type(&self) -> impl Var<Option<WidgetType>> {
238        self.info.map(|w| Some(w.inspector_info()?.builder.widget_type())).actual_var()
239    }
240
241    /// Widget macro name, or `"<widget>!"` if widget was not built with inspection info.
242    pub fn wgt_macro_name(&self) -> impl Var<Txt> {
243        self.info
244            .map(|w| match w.inspector_info().map(|i| i.builder.widget_type()) {
245                Some(t) => formatx!("{}!", t.name()),
246                None => Txt::from_static("<widget>!"),
247            })
248            .actual_var()
249    }
250
251    /// Gets the parent's property that has this widget as an input.
252    ///
253    /// Is an empty string if the widget is not inserted by any property.
254    pub fn parent_property_name(&self) -> impl Var<Txt> {
255        let mut cache = self.cache.lock();
256        cache
257            .parent_property_name
258            .get_or_insert_with(|| {
259                self.info
260                    .map(|w| {
261                        Txt::from_static(
262                            w.parent_property()
263                                .map(|(p, _)| w.parent().unwrap().inspect_property(p).unwrap().property().name)
264                                .unwrap_or(""),
265                        )
266                    })
267                    .actual_var()
268                    .boxed()
269            })
270            .clone()
271    }
272
273    /// Inspect the widget children.
274    pub fn children(&self) -> impl Var<Vec<InspectedWidget>> {
275        let mut cache = self.cache.lock();
276        let cache = &mut *cache;
277        cache
278            .children
279            .get_or_insert_with(|| {
280                let tree = cache.tree.clone();
281                self.info
282                    .map(move |w| {
283                        if let Some(tree) = tree.upgrade() {
284                            assert_eq!(&tree.tree.get(), w.tree());
285
286                            w.children().map(|w| tree.inspect(w.id()).unwrap()).collect()
287                        } else {
288                            vec![]
289                        }
290                    })
291                    .actual_var()
292                    .boxed()
293            })
294            .clone()
295    }
296
297    /// Inspect the builder, properties and intrinsic nodes that make up the widget.
298    ///
299    /// Is `None` when the widget is built without inspector info collection.
300    pub fn inspector_info(&self) -> impl Var<Option<InspectedInfo>> {
301        self.info.map(move |w| w.inspector_info().map(InspectedInfo)).actual_var().boxed()
302    }
303
304    /// Create a variable that probes info after every frame is rendered.
305    pub fn render_watcher<T: VarValue>(&self, mut probe: impl FnMut(&WidgetInfo) -> T + Send + 'static) -> impl Var<T> {
306        merge_var!(
307            self.info.clone(),
308            self.cache.lock().tree.upgrade().unwrap().last_frame(),
309            move |w, _| probe(w)
310        )
311    }
312}
313
314/// [`InspectorInfo`] that can be placed in a variable.
315#[derive(Clone)]
316pub struct InspectedInfo(pub Arc<InspectorInfo>);
317impl fmt::Debug for InspectedInfo {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        fmt::Debug::fmt(&self.0, f)
320    }
321}
322impl PartialEq for InspectedInfo {
323    fn eq(&self, other: &Self) -> bool {
324        Arc::ptr_eq(&self.0, &other.0)
325    }
326}
327impl ops::Deref for InspectedInfo {
328    type Target = InspectorInfo;
329
330    fn deref(&self) -> &Self::Target {
331        &self.0
332    }
333}