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 scroll: Option<Vec2>,
111 pub events: Vec<(&'static str, Value)>,
115}
116
117impl NodeInfo {
118 pub fn to_value(&self, h: crate::value::Handles) -> Value {
123 Value::map([
124 ("key", (h.key)(self.key)),
125 ("parent", h.opt_key(self.parent)),
126 ("depth", Value::Int(self.depth as i64)),
127 ("kind", Value::str(self.kind.name())),
128 ("label", Value::opt_str(&self.label)),
129 ("rect", self.rect.to_value()),
130 ("dir", Value::str(self.dir.name())),
131 ("width", Value::Str(self.width.describe())),
132 ("height", Value::Str(self.height.describe())),
133 ("bg", Value::Int(self.bg.to_hex() as i64)),
134 ("float", Value::Bool(self.float)),
135 ("role", Value::opt(self.role, |r| Value::str(r.name()))),
136 ("text", Value::opt_str(&self.text)),
137 (
138 "flags",
139 Value::list(self.flags.iter().map(|f| Value::str(*f))),
140 ),
141 ("layer", Value::Int(self.layer as i64)),
142 ("origin", Value::Int(self.origin.0 as i64)),
143 ("children", Value::Int(self.children as i64)),
144 (
145 "padding",
146 Value::map([
147 ("t", Value::float(self.padding.t)),
148 ("r", Value::float(self.padding.r)),
149 ("b", Value::float(self.padding.b)),
150 ("l", Value::float(self.padding.l)),
151 ]),
152 ),
153 ("gap", Value::float(self.gap)),
154 ("main_align", Value::str(self.main_align.name())),
155 ("cross_align", Value::str(self.cross_align.name())),
156 ("wrap", Value::Bool(self.wrap)),
157 ("table", Value::Bool(self.table)),
158 ("min_width", Value::opt_float(self.min_w)),
159 ("min_height", Value::opt_float(self.min_h)),
160 ("max_width", Value::opt_float(self.max_w)),
161 ("max_height", Value::opt_float(self.max_h)),
162 ("radius", Value::floats(&self.radius)),
163 ("border_width", Value::float(self.border_w)),
164 (
165 "border_color",
166 Value::Int(self.border_color.to_hex() as i64),
167 ),
168 ("opacity", Value::float(self.opacity)),
169 ("scroll", Value::opt(self.scroll, Vec2::to_value)),
170 (
171 "events",
172 Value::Map(
173 self.events
174 .iter()
175 .map(|(name, v)| (name.to_string(), v.clone()))
176 .collect(),
177 ),
178 ),
179 ])
180 }
181}
182
183const TEXT_CUT: usize = 60;
184
185impl Core {
186 pub fn set_inspect(&mut self, on: bool) {
192 self.inspect = on;
193 if !on && !self.dt_inspect {
194 self.inspected.clear();
195 }
196 }
197
198 pub fn inspect(&self) -> bool {
200 self.inspect
201 }
202
203 pub fn nodes(&self) -> Vec<NodeInfo> {
210 let mut out = self.snapshot().to_vec();
211 let shift = self.dt_shift();
212 if shift != Vec2::ZERO {
213 for n in &mut out {
214 n.rect.x -= shift.x;
215 n.rect.y -= shift.y;
216 }
217 }
218 out
219 }
220
221 pub(crate) fn snapshot(&self) -> &[NodeInfo] {
224 &self.inspected
225 }
226
227 pub(crate) fn snapshot_nodes(&mut self) {
229 if !self.inspect && !self.dt_inspect {
230 self.inspected.clear();
232 return;
233 }
234 let tree = &self.tree;
235 let n = tree.len();
236 let mut out = Vec::with_capacity(n);
237 let mut depth = vec![0u16; n];
238 let layer_of: rustc_hash::FxHashMap<Key, u16> = self
241 .float_stack
242 .iter()
243 .enumerate()
244 .map(|(pos, &(k, _))| (k, pos as u16 + 1))
245 .collect();
246 let mut children = vec![0u32; n];
247 for i in 1..n {
248 children[tree.parent[i] as usize] += 1;
249 }
250 for i in 0..n {
251 let parent = if i == 0 {
252 None
253 } else {
254 let p = tree.parent[i] as usize;
255 depth[i] = depth[p] + 1;
256 Some(tree.keys[p])
257 };
258 let spec = &tree.specs[i];
259 let (kind, text) = match tree.content[i] {
260 NodeContent::Container => (NodeKind::Box, None),
261 NodeContent::Text(id) => {
262 let s = self.text.content(id);
263 let cut = s.char_indices().nth(TEXT_CUT).map_or(s.len(), |(i, _)| i);
264 let mut t = s[..cut].to_string();
265 if cut < s.len() {
266 t.push('…');
267 }
268 (NodeKind::Text, Some(t))
269 }
270 NodeContent::Edit(_) => (NodeKind::Edit, None),
271 NodeContent::Image(..) => (NodeKind::Image, None),
272 NodeContent::Line(_) => (NodeKind::Line, None),
273 NodeContent::Cells(_) => (NodeKind::Cells, None),
274 NodeContent::Fragment(_) => (NodeKind::Fragment, None),
275 NodeContent::Polygon(_) => (NodeKind::Polygon, None),
276 NodeContent::Path(_) => (NodeKind::Path, None),
277 };
278 let rect = if i == 0 {
279 Rect::new(0.0, 0.0, self.viewport.w, self.viewport.h)
280 } else {
281 Rect::from_pos_size(tree.pos[i], tree.size[i])
282 };
283 let ev = spec.events();
284 let it = spec.interact();
285 let mut flags = Vec::new();
286 if ev.on_click.is_some() {
287 flags.push("click");
288 }
289 if ev.on_drag.is_some() {
290 flags.push("drag");
291 }
292 if ev.on_key.is_some() {
293 flags.push("key");
294 }
295 if ev.on_hover.is_some() {
296 flags.push("hover");
297 }
298 if ev.on_drop.is_some() {
299 flags.push("drop");
300 }
301 if ev.on_context_menu.is_some() {
302 flags.push("context-menu");
303 }
304 if ev.modal.is_some() {
305 flags.push("modal");
306 }
307 if spec.hoverable {
308 flags.push("hoverable");
309 }
310 if it.selectable {
311 flags.push("selectable");
312 }
313 if spec.focusable {
314 flags.push("focusable");
315 }
316 if spec.disabled {
317 flags.push("disabled");
318 }
319 if spec.layout.scroll_x || spec.layout.scroll_y {
320 flags.push("scroll");
321 } else if spec.layout.clip {
322 flags.push("clip");
323 }
324 if spec.transition.is_some() {
325 flags.push("transition");
326 }
327 let mut events = Vec::new();
328 for (name, v) in [
329 ("click", &ev.on_click),
330 ("drag", &ev.on_drag),
331 ("key", &ev.on_key),
332 ("hover", &ev.on_hover),
333 ("drop", &ev.on_drop),
334 ("context-menu", &ev.on_context_menu),
335 ("force-click", &ev.on_force_click),
336 ("button", &ev.on_button),
337 ("layout", &ev.on_layout),
338 ("modal", &ev.modal),
339 ] {
340 if let Some(v) = v {
341 events.push((name, v.clone()));
342 }
343 }
344 let layer =
345 if self.tree.any_float && self.float_root.len() > i && self.float_root[i] != NIL {
346 layer_of
347 .get(&tree.keys[self.float_root[i] as usize])
348 .copied()
349 .unwrap_or(0)
350 } else {
351 0
352 };
353 let floor = |m: crate::spec::Min| (!m.is_fit()).then(|| m.resolved());
354 let ceiling = |v: f32| v.is_finite().then_some(v);
355 let l = &spec.layout;
356 out.push(NodeInfo {
357 layer,
358 origin: tree.origins[i],
359 children: children[i],
360 padding: l.padding,
361 gap: l.gap,
362 main_align: l.main_align,
363 cross_align: l.cross_align,
364 wrap: l.wrap,
365 table: l.is_table(),
366 min_w: floor(l.min_w),
367 min_h: floor(l.min_h),
368 max_w: ceiling(l.max_w_px()),
369 max_h: ceiling(l.max_h_px()),
370 radius: spec.style.radius,
371 border_w: spec.style.border_w,
372 border_color: spec.style.border_color,
373 opacity: spec.style.opacity,
374 scroll: (l.scroll_x || l.scroll_y).then(|| self.scroll.drawn(tree.keys[i])),
375 events,
376 key: tree.keys[i],
377 parent,
378 depth: depth[i],
379 kind,
380 label: self.key_labels.label_of(tree.keys[i]).map(str::to_string),
381 rect,
382 dir: spec.layout.dir,
383 width: spec.layout.width,
384 height: spec.layout.height,
385 bg: spec.style.bg,
386 float: spec.layout.float.is_some(),
387 role: crate::access::derived_role(tree, i),
388 text,
389 flags,
390 });
391 }
392 self.inspected = out;
393 }
394}