1#[cfg(feature = "inspector")]
7mod inspector_only {
8 use std::sync::Arc;
9
10 use crate::widget::{
11 builder::{InputKind, PropertyId},
12 node::{BoxedUiNode, UiNode, UiNodeOp, match_node},
13 };
14
15 pub(crate) fn insert_widget_builder_info(child: BoxedUiNode, info: super::InspectorInfo) -> impl UiNode {
16 let insp_info = Arc::new(info);
17 match_node(child, move |_, op| {
18 if let UiNodeOp::Info { info } = op {
19 info.set_meta(*super::INSPECTOR_INFO_ID, insp_info.clone());
20 }
21 })
22 }
23
24 pub(crate) fn actualize_var_info(child: BoxedUiNode, property: PropertyId) -> impl UiNode {
25 match_node(child, move |_, op| {
26 if let UiNodeOp::Info { info } = op {
27 info.with_meta(|mut m| {
28 let info = m.get_mut(*super::INSPECTOR_INFO_ID).unwrap();
29 let prop = info.properties().find(|p| p.0.id() == property).unwrap().0;
30 for (i, input) in prop.property().inputs.iter().enumerate() {
31 if matches!(input.kind, InputKind::Var) {
32 let var = prop.var(i);
33 if var.is_contextual() {
34 let var = var.actual_var_any();
35 info.actual_vars.insert(property, i, var);
36 }
37 }
38 }
39 });
40 }
41 })
42 }
43}
44#[cfg(feature = "inspector")]
45pub(crate) use inspector_only::*;
46
47use parking_lot::RwLock;
48use zng_state_map::StateId;
49use zng_txt::Txt;
50use zng_unique_id::static_id;
51use zng_var::{BoxedAnyVar, BoxedVar, VarValue};
52
53use std::{any::TypeId, collections::HashMap, sync::Arc};
54
55use super::{
56 WIDGET, WidgetUpdateMode,
57 builder::{InputKind, NestGroup, PropertyArgs, PropertyId, WidgetBuilder, WidgetType},
58 info::WidgetInfo,
59};
60
61static_id! {
62 pub(super) static ref INSPECTOR_INFO_ID: StateId<Arc<InspectorInfo>>;
63}
64
65#[derive(Debug)]
69pub enum InstanceItem {
70 Property {
72 args: Box<dyn PropertyArgs>,
76 captured: bool,
80 },
81 Intrinsic {
83 group: NestGroup,
85 name: &'static str,
87 },
88}
89
90#[derive(Default)]
92pub struct InspectorActualVars(RwLock<HashMap<(PropertyId, usize), BoxedAnyVar>>);
93impl InspectorActualVars {
94 pub fn get(&self, property: PropertyId, member: usize) -> Option<BoxedAnyVar> {
96 self.0.read().get(&(property, member)).cloned()
97 }
98
99 pub fn downcast<T: VarValue>(&self, property: PropertyId, member: usize) -> Option<BoxedVar<T>> {
101 let b = self.get(property, member)?.double_boxed_any().downcast::<BoxedVar<T>>().ok()?;
102 Some(*b)
103 }
104
105 pub fn get_debug(&self, property: PropertyId, member: usize) -> Option<BoxedVar<Txt>> {
107 let b = self.get(property, member)?;
108 Some(b.map_debug())
109 }
110
111 #[cfg(feature = "inspector")]
112 fn insert(&self, property: PropertyId, member: usize, var: BoxedAnyVar) {
113 self.0.write().insert((property, member), var);
114 }
115}
116
117pub struct InspectorInfo {
121 pub builder: WidgetBuilder,
123
124 pub items: Box<[InstanceItem]>,
126
127 pub actual_vars: InspectorActualVars,
129}
130
131impl std::fmt::Debug for InspectorInfo {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 f.debug_struct("InspectorInfo")
134 .field("builder", &self.builder)
135 .field("items", &self.items)
136 .field("actual_vars", &self.actual_vars.0.read().keys())
137 .finish()
138 }
139}
140impl InspectorInfo {
141 pub fn properties(&self) -> impl Iterator<Item = (&dyn PropertyArgs, bool)> {
143 self.items.iter().filter_map(|it| match it {
144 InstanceItem::Property { args, captured } => Some((&**args, *captured)),
145 InstanceItem::Intrinsic { .. } => None,
146 })
147 }
148}
149
150pub trait WidgetInfoInspectorExt {
152 fn inspector_info(&self) -> Option<Arc<InspectorInfo>>;
157
158 fn can_inspect(&self) -> bool;
162
163 fn inspect_child<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
165
166 fn inspect_descendant<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
189
190 fn inspect_ancestor<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
192
193 fn inspect_property<P: InspectPropertyPattern>(&self, pattern: P) -> Option<&dyn PropertyArgs>;
206
207 fn parent_property(&self) -> Option<(PropertyId, usize)>;
211}
212impl WidgetInfoInspectorExt for WidgetInfo {
213 fn inspector_info(&self) -> Option<Arc<InspectorInfo>> {
214 self.meta().get_clone(*INSPECTOR_INFO_ID)
215 }
216
217 fn can_inspect(&self) -> bool {
218 self.meta().contains(*INSPECTOR_INFO_ID)
219 }
220
221 fn inspect_child<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
222 self.children().find(|c| match c.meta().get(*INSPECTOR_INFO_ID) {
223 Some(wgt) => pattern.matches(wgt),
224 None => false,
225 })
226 }
227
228 fn inspect_descendant<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
229 self.descendants().find(|c| match c.meta().get(*INSPECTOR_INFO_ID) {
230 Some(info) => pattern.matches(info),
231 None => false,
232 })
233 }
234
235 fn inspect_ancestor<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
236 self.ancestors().find(|c| match c.meta().get(*INSPECTOR_INFO_ID) {
237 Some(info) => pattern.matches(info),
238 None => false,
239 })
240 }
241
242 fn inspect_property<P: InspectPropertyPattern>(&self, pattern: P) -> Option<&dyn PropertyArgs> {
243 self.meta()
244 .get(*INSPECTOR_INFO_ID)?
245 .properties()
246 .find_map(|(args, cap)| if pattern.matches(args, cap) { Some(args) } else { None })
247 }
248
249 fn parent_property(&self) -> Option<(PropertyId, usize)> {
250 self.parent()?.meta().get(*INSPECTOR_INFO_ID)?.properties().find_map(|(args, _)| {
251 let id = self.id();
252 let info = args.property();
253 for (i, input) in info.inputs.iter().enumerate() {
254 match input.kind {
255 InputKind::UiNode => {
256 let node = args.ui_node(i);
257 if let Some(true) = node.try_context(WidgetUpdateMode::Ignore, || WIDGET.id() == id) {
258 return Some((args.id(), i));
259 }
260 }
261 InputKind::UiNodeList => {
262 let list = args.ui_node_list(i);
263 let mut found = false;
264 list.for_each_ctx(WidgetUpdateMode::Ignore, |_| {
265 if !found {
266 found = WIDGET.id() == id;
267 }
268 });
269 if found {
270 return Some((args.id(), i));
271 }
272 }
273 _ => continue,
274 }
275 }
276 None
277 })
278 }
279}
280
281pub trait InspectWidgetPattern {
283 fn matches(&self, info: &InspectorInfo) -> bool;
285}
286impl InspectWidgetPattern for &str {
288 fn matches(&self, info: &InspectorInfo) -> bool {
289 info.builder.widget_type().path.ends_with(self)
290 }
291}
292impl InspectWidgetPattern for TypeId {
293 fn matches(&self, info: &InspectorInfo) -> bool {
294 info.builder.widget_type().type_id == *self
295 }
296}
297impl InspectWidgetPattern for WidgetType {
298 fn matches(&self, info: &InspectorInfo) -> bool {
299 info.builder.widget_type().type_id == self.type_id
300 }
301}
302
303pub trait InspectPropertyPattern {
305 fn matches(&self, args: &dyn PropertyArgs, captured: bool) -> bool;
307}
308impl InspectPropertyPattern for &str {
312 fn matches(&self, args: &dyn PropertyArgs, _: bool) -> bool {
313 args.property().name == *self
314 }
315}
316impl InspectPropertyPattern for PropertyId {
317 fn matches(&self, args: &dyn PropertyArgs, _: bool) -> bool {
318 args.id() == *self
319 }
320}