#[cfg(feature = "inspector")]
mod inspector_only {
use std::sync::Arc;
use crate::widget::{
builder::{InputKind, PropertyId},
node::{match_node, BoxedUiNode, UiNode, UiNodeOp},
};
pub(crate) fn insert_widget_builder_info(child: BoxedUiNode, info: super::InspectorInfo) -> impl UiNode {
let insp_info = Arc::new(info);
match_node(child, move |_, op| {
if let UiNodeOp::Info { info } = op {
info.set_meta(&super::INSPECTOR_INFO_ID, insp_info.clone());
}
})
}
pub(crate) fn actualize_var_info(child: BoxedUiNode, property: PropertyId) -> impl UiNode {
match_node(child, move |_, op| {
if let UiNodeOp::Info { info } = op {
info.with_meta(|mut m| {
let info = m.get_mut(&super::INSPECTOR_INFO_ID).unwrap();
let prop = info.properties().find(|p| p.0.id() == property).unwrap().0;
for (i, input) in prop.property().inputs.iter().enumerate() {
if matches!(input.kind, InputKind::Var) {
let var = prop.var(i);
if var.is_contextual() {
let var = var.actual_var_any();
info.actual_vars.insert(property, i, var);
}
}
}
});
}
})
}
}
#[cfg(feature = "inspector")]
pub(crate) use inspector_only::*;
use parking_lot::RwLock;
use zng_state_map::StaticStateId;
use zng_txt::Txt;
use zng_var::{BoxedAnyVar, BoxedVar, VarValue};
use std::{any::TypeId, collections::HashMap, sync::Arc};
use super::{
builder::{InputKind, NestGroup, PropertyArgs, PropertyId, WidgetBuilder, WidgetType},
info::WidgetInfo,
WidgetUpdateMode, WIDGET,
};
pub(super) static INSPECTOR_INFO_ID: StaticStateId<Arc<InspectorInfo>> = StaticStateId::new_unique();
#[derive(Debug)]
pub enum InstanceItem {
Property {
args: Box<dyn PropertyArgs>,
captured: bool,
},
Intrinsic {
group: NestGroup,
name: &'static str,
},
}
#[derive(Default)]
pub struct InspectorActualVars(RwLock<HashMap<(PropertyId, usize), BoxedAnyVar>>);
impl InspectorActualVars {
pub fn get(&self, property: PropertyId, member: usize) -> Option<BoxedAnyVar> {
self.0.read().get(&(property, member)).cloned()
}
pub fn downcast<T: VarValue>(&self, property: PropertyId, member: usize) -> Option<BoxedVar<T>> {
let b = self.get(property, member)?.double_boxed_any().downcast::<BoxedVar<T>>().ok()?;
Some(*b)
}
pub fn get_debug(&self, property: PropertyId, member: usize) -> Option<BoxedVar<Txt>> {
let b = self.get(property, member)?;
Some(b.map_debug())
}
#[cfg(feature = "inspector")]
fn insert(&self, property: PropertyId, member: usize, var: BoxedAnyVar) {
self.0.write().insert((property, member), var);
}
}
pub struct InspectorInfo {
pub builder: WidgetBuilder,
pub items: Box<[InstanceItem]>,
pub actual_vars: InspectorActualVars,
}
impl std::fmt::Debug for InspectorInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InspectorInfo")
.field("builder", &self.builder)
.field("items", &self.items)
.field("actual_vars", &self.actual_vars.0.read().keys())
.finish()
}
}
impl InspectorInfo {
pub fn properties(&self) -> impl Iterator<Item = (&dyn PropertyArgs, bool)> {
self.items.iter().filter_map(|it| match it {
InstanceItem::Property { args, captured } => Some((&**args, *captured)),
InstanceItem::Intrinsic { .. } => None,
})
}
}
pub trait WidgetInfoInspectorExt {
fn inspector_info(&self) -> Option<Arc<InspectorInfo>>;
fn can_inspect(&self) -> bool;
fn inspect_child<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
fn inspect_descendant<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
fn inspect_ancestor<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo>;
fn inspect_property<P: InspectPropertyPattern>(&self, pattern: P) -> Option<&dyn PropertyArgs>;
fn parent_property(&self) -> Option<(PropertyId, usize)>;
}
impl WidgetInfoInspectorExt for WidgetInfo {
fn inspector_info(&self) -> Option<Arc<InspectorInfo>> {
self.meta().get_clone(&INSPECTOR_INFO_ID)
}
fn can_inspect(&self) -> bool {
self.meta().contains(&INSPECTOR_INFO_ID)
}
fn inspect_child<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
self.children().find(|c| match c.meta().get(&INSPECTOR_INFO_ID) {
Some(wgt) => pattern.matches(wgt),
None => false,
})
}
fn inspect_descendant<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
self.descendants().find(|c| match c.meta().get(&INSPECTOR_INFO_ID) {
Some(info) => pattern.matches(info),
None => false,
})
}
fn inspect_ancestor<P: InspectWidgetPattern>(&self, pattern: P) -> Option<WidgetInfo> {
self.ancestors().find(|c| match c.meta().get(&INSPECTOR_INFO_ID) {
Some(info) => pattern.matches(info),
None => false,
})
}
fn inspect_property<P: InspectPropertyPattern>(&self, pattern: P) -> Option<&dyn PropertyArgs> {
self.meta()
.get(&INSPECTOR_INFO_ID)?
.properties()
.find_map(|(args, cap)| if pattern.matches(args, cap) { Some(args) } else { None })
}
fn parent_property(&self) -> Option<(PropertyId, usize)> {
self.parent()?.meta().get(&INSPECTOR_INFO_ID)?.properties().find_map(|(args, _)| {
let id = self.id();
let info = args.property();
for (i, input) in info.inputs.iter().enumerate() {
match input.kind {
InputKind::UiNode => {
let node = args.ui_node(i);
if let Some(true) = node.try_context(WidgetUpdateMode::Ignore, || WIDGET.id() == id) {
return Some((args.id(), i));
}
}
InputKind::UiNodeList => {
let list = args.ui_node_list(i);
let mut found = false;
list.for_each_ctx(WidgetUpdateMode::Ignore, |_| {
if !found {
found = WIDGET.id() == id;
}
});
if found {
return Some((args.id(), i));
}
}
_ => continue,
}
}
None
})
}
}
pub trait InspectWidgetPattern {
fn matches(&self, info: &InspectorInfo) -> bool;
}
impl<'s> InspectWidgetPattern for &'s str {
fn matches(&self, info: &InspectorInfo) -> bool {
info.builder.widget_type().path.ends_with(self)
}
}
impl InspectWidgetPattern for TypeId {
fn matches(&self, info: &InspectorInfo) -> bool {
info.builder.widget_type().type_id == *self
}
}
impl InspectWidgetPattern for WidgetType {
fn matches(&self, info: &InspectorInfo) -> bool {
info.builder.widget_type().type_id == self.type_id
}
}
pub trait InspectPropertyPattern {
fn matches(&self, args: &dyn PropertyArgs, captured: bool) -> bool;
}
impl<'s> InspectPropertyPattern for &'s str {
fn matches(&self, args: &dyn PropertyArgs, _: bool) -> bool {
args.property().name == *self
}
}
impl InspectPropertyPattern for PropertyId {
fn matches(&self, args: &dyn PropertyArgs, _: bool) -> bool {
args.id() == *self
}
}