1use super::*;
13use crate::access::Role;
14use crate::geom::{Edges, Rect};
15use crate::spec::{Align, Dir, Sizing};
16use crate::tree::{NIL, NodeContent, OriginId};
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum NodeKind {
21 Box,
22 Text,
23 Edit,
24 Image,
25 Line,
26 Cells,
27 Fragment,
28 Polygon,
29 Path,
30}
31
32impl NodeKind {
33 pub fn name(self) -> &'static str {
34 match self {
35 NodeKind::Box => "box",
36 NodeKind::Text => "text",
37 NodeKind::Edit => "edit",
38 NodeKind::Image => "image",
39 NodeKind::Line => "line",
40 NodeKind::Cells => "cells",
41 NodeKind::Fragment => "fragment",
42 NodeKind::Polygon => "polygon",
43 NodeKind::Path => "path",
44 }
45 }
46}
47
48#[derive(Clone, Debug)]
50pub struct NodeInfo {
51 pub key: Key,
52 pub parent: Option<Key>,
53 pub depth: u16,
55 pub kind: NodeKind,
56 pub label: Option<String>,
58 pub rect: Rect,
61 pub dir: Dir,
62 pub width: Sizing,
63 pub height: Sizing,
64 pub bg: Color,
65 pub float: bool,
66 pub role: Option<Role>,
70 pub text: Option<String>,
72 pub flags: Vec<&'static str>,
77 pub layer: u16,
81 pub origin: OriginId,
83 pub children: u32,
85 pub padding: Edges,
87 pub gap: f32,
88 pub main_align: Align,
89 pub cross_align: Align,
90 pub wrap: bool,
91 pub table: bool,
94 pub min_w: Option<f32>,
101 pub min_h: Option<f32>,
102 pub max_w: Option<f32>,
103 pub max_h: Option<f32>,
104 pub radius: [f32; 4],
106 pub border_w: f32,
107 pub border_color: Color,
108 pub opacity: f32,
109 pub backdrop_blur: f32,
112 pub scroll: Option<Vec2>,
114 pub events: Vec<(&'static str, Value)>,
118}
119
120impl NodeInfo {
121 pub fn to_value(&self, h: crate::value::Handles) -> Value {
126 Value::map([
127 ("key", (h.key)(self.key)),
128 ("parent", h.opt_key(self.parent)),
129 ("depth", Value::Int(self.depth as i64)),
130 ("kind", Value::str(self.kind.name())),
131 ("label", Value::opt_str(&self.label)),
132 ("rect", self.rect.to_value()),
133 ("dir", Value::str(self.dir.name())),
134 ("width", Value::Str(self.width.describe())),
135 ("height", Value::Str(self.height.describe())),
136 ("bg", Value::Int(self.bg.to_hex() as i64)),
137 ("float", Value::Bool(self.float)),
138 ("role", Value::opt(self.role, |r| Value::str(r.name()))),
139 ("text", Value::opt_str(&self.text)),
140 (
141 "flags",
142 Value::list(self.flags.iter().map(|f| Value::str(*f))),
143 ),
144 ("layer", Value::Int(self.layer as i64)),
145 ("origin", Value::Int(self.origin.0 as i64)),
146 ("children", Value::Int(self.children as i64)),
147 (
148 "padding",
149 Value::map([
150 ("t", Value::float(self.padding.t)),
151 ("r", Value::float(self.padding.r)),
152 ("b", Value::float(self.padding.b)),
153 ("l", Value::float(self.padding.l)),
154 ]),
155 ),
156 ("gap", Value::float(self.gap)),
157 ("main_align", Value::str(self.main_align.name())),
158 ("cross_align", Value::str(self.cross_align.name())),
159 ("wrap", Value::Bool(self.wrap)),
160 ("table", Value::Bool(self.table)),
161 ("min_width", Value::opt_float(self.min_w)),
162 ("min_height", Value::opt_float(self.min_h)),
163 ("max_width", Value::opt_float(self.max_w)),
164 ("max_height", Value::opt_float(self.max_h)),
165 ("radius", Value::floats(&self.radius)),
166 ("border_width", Value::float(self.border_w)),
167 (
168 "border_color",
169 Value::Int(self.border_color.to_hex() as i64),
170 ),
171 ("opacity", Value::float(self.opacity)),
172 ("backdrop_blur", Value::float(self.backdrop_blur)),
173 ("scroll", Value::opt(self.scroll, Vec2::to_value)),
174 (
175 "events",
176 Value::Map(
177 self.events
178 .iter()
179 .map(|(name, v)| (name.to_string(), v.clone()))
180 .collect(),
181 ),
182 ),
183 ])
184 }
185}
186
187const TEXT_CUT: usize = 60;
188
189impl Core {
190 pub fn set_inspect(&mut self, on: bool) {
196 self.inspect = on;
197 if !on && !self.dt_inspect {
198 self.inspected.clear();
199 }
200 }
201
202 pub fn inspect(&self) -> bool {
204 self.inspect
205 }
206
207 pub fn nodes(&self) -> Vec<NodeInfo> {
214 let mut out = self.snapshot().to_vec();
215 let shift = self.dt_shift();
216 if shift != Vec2::ZERO {
217 for n in &mut out {
218 n.rect.x -= shift.x;
219 n.rect.y -= shift.y;
220 }
221 }
222 out
223 }
224
225 pub(crate) fn snapshot(&self) -> &[NodeInfo] {
228 &self.inspected
229 }
230
231 pub(crate) fn snapshot_nodes(&mut self) {
233 if !self.inspect && !self.dt_inspect {
234 self.inspected.clear();
236 return;
237 }
238 let tree = &self.tree;
239 let n = tree.len();
240 let mut out = Vec::with_capacity(n);
241 let mut depth = vec![0u16; n];
242 let layer_of: rustc_hash::FxHashMap<Key, u16> = self
245 .float_stack
246 .iter()
247 .enumerate()
248 .map(|(pos, &(k, _))| (k, pos as u16 + 1))
249 .collect();
250 let mut children = vec![0u32; n];
251 for i in 1..n {
252 children[tree.parent[i] as usize] += 1;
253 }
254 for i in 0..n {
255 let parent = if i == 0 {
256 None
257 } else {
258 let p = tree.parent[i] as usize;
259 depth[i] = depth[p] + 1;
260 Some(tree.keys[p])
261 };
262 let spec = &tree.specs[i];
263 let (kind, text) = match tree.content[i] {
264 NodeContent::Container => (NodeKind::Box, None),
265 NodeContent::Text(id) => {
266 let s = self.text.content(id);
267 let cut = s.char_indices().nth(TEXT_CUT).map_or(s.len(), |(i, _)| i);
268 let mut t = s[..cut].to_string();
269 if cut < s.len() {
270 t.push('…');
271 }
272 (NodeKind::Text, Some(t))
273 }
274 NodeContent::Edit(_) => (NodeKind::Edit, None),
275 NodeContent::Image(..) => (NodeKind::Image, None),
276 NodeContent::Line(_) => (NodeKind::Line, None),
277 NodeContent::Cells(_) => (NodeKind::Cells, None),
278 NodeContent::Fragment(_) => (NodeKind::Fragment, None),
279 NodeContent::Polygon(_) => (NodeKind::Polygon, None),
280 NodeContent::Path(_) => (NodeKind::Path, None),
281 };
282 let rect = if i == 0 {
283 Rect::new(0.0, 0.0, self.viewport.w, self.viewport.h)
284 } else {
285 Rect::from_pos_size(tree.pos[i], tree.size[i])
286 };
287 let ev = spec.events();
288 let it = spec.interact();
289 let mut flags = Vec::new();
290 if ev.on_click.is_some() {
291 flags.push("click");
292 }
293 if ev.on_drag.is_some() {
294 flags.push("drag");
295 }
296 if ev.on_key.is_some() {
297 flags.push("key");
298 }
299 if ev.on_hover.is_some() {
300 flags.push("hover");
301 }
302 if ev.on_drop.is_some() {
303 flags.push("drop");
304 }
305 if ev.on_context_menu.is_some() {
306 flags.push("context-menu");
307 }
308 if ev.modal.is_some() {
309 flags.push("modal");
310 }
311 if spec.hoverable {
312 flags.push("hoverable");
313 }
314 if it.selectable {
315 flags.push("selectable");
316 }
317 if spec.focusable {
318 flags.push("focusable");
319 }
320 if spec.disabled {
321 flags.push("disabled");
322 }
323 if spec.layout.scroll_x || spec.layout.scroll_y {
324 flags.push("scroll");
325 } else if spec.layout.clip {
326 flags.push("clip");
327 }
328 if spec.transition.is_some() {
329 flags.push("transition");
330 }
331 let mut events = Vec::new();
332 for (name, v) in [
333 ("click", &ev.on_click),
334 ("drag", &ev.on_drag),
335 ("key", &ev.on_key),
336 ("hover", &ev.on_hover),
337 ("drop", &ev.on_drop),
338 ("context-menu", &ev.on_context_menu),
339 ("force-click", &ev.on_force_click),
340 ("button", &ev.on_button),
341 ("layout", &ev.on_layout),
342 ("modal", &ev.modal),
343 ] {
344 if let Some(v) = v {
345 events.push((name, v.clone()));
346 }
347 }
348 let layer =
349 if self.tree.any_float && self.float_root.len() > i && self.float_root[i] != NIL {
350 layer_of
351 .get(&tree.keys[self.float_root[i] as usize])
352 .copied()
353 .unwrap_or(0)
354 } else {
355 0
356 };
357 let floor = |m: crate::spec::Min| (!m.is_fit()).then(|| m.resolved());
358 let ceiling = |v: f32| v.is_finite().then_some(v);
359 let l = &spec.layout;
360 out.push(NodeInfo {
361 layer,
362 origin: tree.origins[i],
363 children: children[i],
364 padding: l.padding,
365 gap: l.gap,
366 main_align: l.main_align,
367 cross_align: l.cross_align,
368 wrap: l.wrap,
369 table: l.is_table(),
370 min_w: floor(l.min_w),
371 min_h: floor(l.min_h),
372 max_w: ceiling(l.max_w_px()),
373 max_h: ceiling(l.max_h_px()),
374 radius: spec.style.radius,
375 border_w: spec.style.border_w,
376 border_color: spec.style.border_color,
377 opacity: spec.style.opacity,
378 backdrop_blur: spec.interact().backdrop_blur,
379 scroll: (l.scroll_x || l.scroll_y).then(|| self.scroll.drawn(tree.keys[i])),
380 events,
381 key: tree.keys[i],
382 parent,
383 depth: depth[i],
384 kind,
385 label: self.key_labels.label_of(tree.keys[i]).map(str::to_string),
386 rect,
387 dir: spec.layout.dir,
388 width: spec.layout.width,
389 height: spec.layout.height,
390 bg: spec.style.bg,
391 float: spec.layout.float.is_some(),
392 role: crate::access::derived_role(tree, i),
393 text,
394 flags,
395 });
396 }
397 self.inspected = out;
398 }
399}