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