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