1#[cfg(feature = "inspector")]
7mod inspector_only {
8 use std::sync::Arc;
9
10 use crate::widget::{
11 builder::{InputKind, PropertyId},
12 node::{UiNode, UiNodeOp, match_node},
13 };
14
15 pub(crate) fn insert_widget_builder_info(child: UiNode, info: super::InspectorInfo) -> 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: UiNode, property: PropertyId) -> 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.capabilities().is_contextual() {
34 let var = var.current_context();
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::{AnyVar, Var, VarValue};
52
53use std::{any::TypeId, collections::HashMap, sync::Arc};
54
55use super::{
56 builder::{InputKind, NestGroup, PropertyArgs, PropertyId, WidgetBuilder, WidgetType},
57 info::WidgetInfo,
58};
59
60static_id! {
61 pub(super) static ref INSPECTOR_INFO_ID: StateId<Arc<InspectorInfo>>;
62}
63
64#[derive(Debug)]
68pub enum InstanceItem {
69 Property {
71 args: Box<dyn PropertyArgs>,
75 captured: bool,
79 },
80 Intrinsic {
82 group: NestGroup,
84 name: &'static str,
86 },
87}
88
89#[derive(Default)]
91pub struct InspectorActualVars(RwLock<HashMap<(PropertyId, usize), AnyVar>>);
92impl InspectorActualVars {
93 pub fn get(&self, property: PropertyId, member: usize) -> Option<AnyVar> {
95 self.0.read().get(&(property, member)).cloned()
96 }
97
98 pub fn downcast<T: VarValue>(&self, property: PropertyId, member: usize) -> Option<Var<T>> {
100 self.get(property, member)?.downcast::<T>().ok()
101 }
102
103 pub fn get_debug(&self, property: PropertyId, member: usize) -> Option<Var<Txt>> {
105 let b = self.get(property, member)?;
106 Some(b.map_debug(false))
107 }
108
109 #[cfg(feature = "inspector")]
110 fn insert(&self, property: PropertyId, member: usize, var: AnyVar) {
111 self.0.write().insert((property, member), var);
112 }
113}
114
115#[non_exhaustive]
119pub struct InspectorInfo {
120 pub builder: WidgetBuilder,
122
123 pub items: Box<[InstanceItem]>,
125
126 pub actual_vars: InspectorActualVars,
128}
129
130impl std::fmt::Debug for InspectorInfo {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 f.debug_struct("InspectorInfo")
133 .field("builder", &self.builder)
134 .field("items", &self.items)
135 .field("actual_vars", &self.actual_vars.0.read().keys())
136 .finish_non_exhaustive()
137 }
138}
139impl InspectorInfo {
140 pub fn properties(&self) -> impl Iterator<Item = (&dyn PropertyArgs, bool)> {
142 self.items.iter().filter_map(|it| match it {
143 InstanceItem::Property { args, captured } => Some((&**args, *captured)),
144 InstanceItem::Intrinsic { .. } => None,
145 })
146 }
147}
148
149pub trait WidgetInfoInspectorExt {
151 fn inspector_info(&self) -> Option<Arc<InspectorInfo>>;
156
157 fn can_inspect(&self) -> bool;
161
162 fn inspect_child<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
164
165 fn inspect_descendant<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
188
189 fn inspect_ancestor<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
191
192 fn inspect_property<P: InspectPropertyPattern>(&self, pattern: P) -> Option<&dyn PropertyArgs>;
205
206 fn parent_property(&self) -> Option<(PropertyId, usize)>;
210}
211impl WidgetInfoInspectorExt for WidgetInfo {
212 fn inspector_info(&self) -> Option<Arc<InspectorInfo>> {
213 self.meta().get_clone(*INSPECTOR_INFO_ID)
214 }
215
216 fn can_inspect(&self) -> bool {
217 self.meta().contains(*INSPECTOR_INFO_ID)
218 }
219
220 fn inspect_child<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
221 self.children().find(|c| match c.meta().get(*INSPECTOR_INFO_ID) {
222 Some(wgt) => pattern.matches(wgt),
223 None => false,
224 })
225 }
226
227 fn inspect_descendant<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
228 self.descendants().find(|c| match c.meta().get(*INSPECTOR_INFO_ID) {
229 Some(info) => pattern.matches(info),
230 None => false,
231 })
232 }
233
234 fn inspect_ancestor<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
235 self.ancestors().find(|c| match c.meta().get(*INSPECTOR_INFO_ID) {
236 Some(info) => pattern.matches(info),
237 None => false,
238 })
239 }
240
241 fn inspect_property<P: InspectPropertyPattern>(&self, pattern: P) -> Option<&dyn PropertyArgs> {
242 self.meta()
243 .get(*INSPECTOR_INFO_ID)?
244 .properties()
245 .find_map(|(args, cap)| if pattern.matches(args, cap) { Some(args) } else { None })
246 }
247
248 fn parent_property(&self) -> Option<(PropertyId, usize)> {
249 self.parent()?.meta().get(*INSPECTOR_INFO_ID)?.properties().find_map(|(args, _)| {
250 let id = self.id();
251 let info = args.property();
252 for (i, input) in info.inputs.iter().enumerate() {
253 match input.kind {
254 InputKind::UiNode => {
255 let node = args.ui_node(i);
256 let mut found = false;
257 node.try_node(|n| {
258 if n.is_list() {
259 n.for_each_child(|_, n| {
261 if !found && let Some(mut wgt) = n.as_widget() {
262 found = wgt.id() == id;
263 }
264 });
265 } else if let Some(mut wgt) = n.as_widget() {
266 found = wgt.id() == id;
268 }
269 });
270 if found {
271 return Some((args.id(), i));
272 }
273 }
274 _ => continue,
275 }
276 }
277 None
278 })
279 }
280}
281
282pub trait InspectWidgetPattern {
284 fn matches(&self, info: &InspectorInfo) -> bool;
286}
287impl InspectWidgetPattern for &str {
289 fn matches(&self, info: &InspectorInfo) -> bool {
290 info.builder.widget_type().path.ends_with(self)
291 }
292}
293impl InspectWidgetPattern for TypeId {
294 fn matches(&self, info: &InspectorInfo) -> bool {
295 info.builder.widget_type().type_id == *self
296 }
297}
298impl InspectWidgetPattern for WidgetType {
299 fn matches(&self, info: &InspectorInfo) -> bool {
300 info.builder.widget_type().type_id == self.type_id
301 }
302}
303
304pub trait InspectPropertyPattern {
306 fn matches(&self, args: &dyn PropertyArgs, captured: bool) -> bool;
308}
309impl InspectPropertyPattern for &str {
313 fn matches(&self, args: &dyn PropertyArgs, _: bool) -> bool {
314 args.property().name == *self
315 }
316}
317impl InspectPropertyPattern for PropertyId {
318 fn matches(&self, args: &dyn PropertyArgs, _: bool) -> bool {
319 args.id() == *self
320 }
321}