use crate::WuiEnv;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_inspector_open(env: *const WuiEnv) {
let env = unsafe { crate::borrow_ffi(env) };
let Some(inspector) = env.get::<waterui::inspector::InspectorRuntime>() else {
tracing::warn!(
target: "waterui::inspector",
"Asked to open the inspector in an environment that has no endpoint"
);
return;
};
inspector.open();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_inspector_inspect_node(env: *const WuiEnv, node: u64) {
let env = unsafe { crate::borrow_ffi(env) };
let Some(inspector) = env.get::<waterui::inspector::InspectorRuntime>() else {
tracing::warn!(
target: "waterui::inspector",
node,
"Asked to inspect a node in an environment that has no endpoint"
);
return;
};
inspector.inspect_node(waterui::inspector::protocol::NodeId(node));
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_inspector_is_available(env: *const WuiEnv) -> bool {
let env = unsafe { crate::borrow_ffi(env) };
env.get::<waterui::inspector::InspectorRuntime>().is_some()
}
#[repr(C)]
pub struct WuiInspectorNode {
pub id: u64,
pub role: crate::WuiStr,
pub label: crate::WuiStr,
pub value: crate::WuiStr,
pub has_bounds: bool,
pub bounds: [f32; 4],
pub enabled: bool,
pub hidden: bool,
pub selected: bool,
pub has_checked: bool,
pub checked: bool,
pub children: *const u64,
pub children_len: usize,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_inspector_wants_tree(env: *const WuiEnv) -> bool {
let env = unsafe { crate::borrow_ffi(env) };
env.get::<waterui::inspector::TreeRecorder>()
.is_some_and(waterui::inspector::TreeRecorder::is_active)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn waterui_inspector_publish_tree(
env: *const WuiEnv,
root: u64,
has_focus: bool,
focus: u64,
nodes: *const WuiInspectorNode,
len: usize,
) {
use waterui::inspector::protocol::{Bounds, NodeId, NodeState, TreeNode};
let env = unsafe { crate::borrow_ffi(env) };
let Some(recorder) = env.get::<waterui::inspector::TreeRecorder>() else {
return;
};
if !recorder.is_active() {
return;
}
if nodes.is_null() {
return;
}
let raw = unsafe { core::slice::from_raw_parts(nodes, len) };
let projected = raw
.iter()
.map(|node| {
let text = |value: &crate::WuiStr| unsafe { value.as_str() }.to_string();
let optional = |value: &crate::WuiStr| {
let text = text(value);
(!text.is_empty()).then_some(text)
};
let children = if node.children.is_null() || node.children_len == 0 {
Vec::new()
} else {
unsafe { core::slice::from_raw_parts(node.children, node.children_len) }
.iter()
.map(|id| NodeId(*id))
.collect()
};
TreeNode {
id: NodeId(node.id),
role: text(&node.role),
label: optional(&node.label),
value: optional(&node.value),
bounds: node.has_bounds.then(|| Bounds {
x: node.bounds[0],
y: node.bounds[1],
width: node.bounds[2],
height: node.bounds[3],
}),
state: NodeState {
enabled: node.enabled,
selected: node.selected,
checked: node.has_checked.then_some(node.checked),
expanded: None,
hidden: node.hidden,
},
children,
}
})
.collect();
recorder.record_snapshot(NodeId(root), has_focus.then_some(NodeId(focus)), projected);
}