1mod layout;
4mod node;
5mod reconcile;
6
7pub use layout::measure_graph;
8pub use node::GraphRenderNode;
9pub(crate) use node::graph_local_content_point;
10pub use reconcile::reconcile_graph;
11
12use std::sync::Arc;
13
14use crate::callback::Callback;
15use crate::core::element::{Element, ElementKind};
16use crate::style::{BorderStyle, Length, Padding, Style, StyleSlot};
17
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
20pub enum GraphDirection {
21 #[default]
23 TopDown,
24 LeftRight,
26}
27
28#[non_exhaustive]
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
31pub enum GraphLayout {
32 #[default]
34 Tree,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq, Hash)]
39pub struct GraphNodePath(Arc<[usize]>);
40
41impl GraphNodePath {
42 pub fn root() -> Self {
44 Self(Arc::new([]))
45 }
46
47 pub fn from_segments(segments: impl IntoIterator<Item = usize>) -> Self {
49 Self(segments.into_iter().collect::<Vec<_>>().into())
50 }
51
52 pub fn segments(&self) -> &[usize] {
54 &self.0
55 }
56}
57
58impl From<Vec<usize>> for GraphNodePath {
59 fn from(value: Vec<usize>) -> Self {
60 Self(value.into())
61 }
62}
63
64impl AsRef<[usize]> for GraphNodePath {
65 fn as_ref(&self) -> &[usize] {
66 self.segments()
67 }
68}
69
70#[derive(Clone, Debug, PartialEq, Eq, Hash)]
72pub struct GraphNodeEvent {
73 pub path: GraphNodePath,
75 pub label: Arc<str>,
77}
78
79#[derive(Clone, Debug)]
81pub struct GraphNode {
82 pub(crate) label: Arc<str>,
83 pub(crate) children: Arc<[GraphNode]>,
84 pub(crate) style: Style,
85 pub(crate) hover_style: Style,
86 pub(crate) focus_style: StyleSlot,
87 pub(crate) border: Option<bool>,
88}
89
90impl GraphNode {
91 pub fn new(label: impl Into<Arc<str>>) -> Self {
93 Self {
94 label: label.into(),
95 children: Arc::new([]),
96 style: Style::default(),
97 hover_style: Style::default(),
98 focus_style: StyleSlot::Replace(Style::default()),
99 border: None,
100 }
101 }
102
103 pub fn children(mut self, children: impl IntoIterator<Item = GraphNode>) -> Self {
106 self.children = children.into_iter().collect::<Vec<_>>().into();
107 self
108 }
109
110 pub fn child(mut self, child: GraphNode) -> Self {
112 let mut children = self.children.to_vec();
113 children.push(child);
114 self.children = children.into();
115 self
116 }
117
118 pub fn style(mut self, style: Style) -> Self {
120 self.style = style;
121 self
122 }
123
124 pub fn hover_style(mut self, style: Style) -> Self {
126 self.hover_style = style;
127 self
128 }
129
130 pub fn focus_style(mut self, style: Style) -> Self {
132 self.focus_style = StyleSlot::Replace(style);
133 self
134 }
135
136 pub fn extend_focus_style(mut self, style: Style) -> Self {
138 self.focus_style = StyleSlot::Extend(style);
139 self
140 }
141
142 pub fn inherit_focus_style(mut self) -> Self {
144 self.focus_style = StyleSlot::Inherit;
145 self
146 }
147
148 pub fn focus_style_slot(mut self, slot: StyleSlot) -> Self {
150 self.focus_style = slot;
151 self
152 }
153
154 pub fn border(mut self, border: bool) -> Self {
156 self.border = Some(border);
157 self
158 }
159}
160
161#[derive(Clone)]
163pub struct Graph {
164 pub(crate) root: Option<GraphNode>,
165 pub(crate) direction: GraphDirection,
166 pub(crate) layout: GraphLayout,
167 pub(crate) gap_x: u16,
168 pub(crate) gap_y: u16,
169 pub(crate) max_node_width: u16,
170 pub(crate) node_padding: Padding,
171 pub(crate) node_border: bool,
172 pub(crate) node_border_style: BorderStyle,
173 pub(crate) style: Style,
174 pub(crate) node_style: Style,
175 pub(crate) node_hover_style: Style,
176 pub(crate) focusable: bool,
177 pub(crate) focused_path: Option<GraphNodePath>,
178 pub(crate) node_focus_style: StyleSlot,
179 pub(crate) edge_style: Style,
180 pub(crate) edge_border_style: BorderStyle,
181 pub(crate) on_node_click: Option<Callback<GraphNodeEvent>>,
182 pub(crate) on_node_hover: Option<Callback<GraphNodeEvent>>,
183 pub(crate) on_node_focus: Option<Callback<GraphNodeEvent>>,
184 pub(crate) on_node_activate: Option<Callback<GraphNodeEvent>>,
185 pub(crate) padding: Padding,
186 pub(crate) border: bool,
187 pub(crate) border_style: BorderStyle,
188 pub(crate) width: Length,
191 pub(crate) height: Length,
194}
195
196impl Default for Graph {
197 fn default() -> Self {
198 Self {
199 root: None,
200 direction: GraphDirection::TopDown,
201 layout: GraphLayout::Tree,
202 gap_x: 2,
203 gap_y: 1,
204 max_node_width: 24,
205 node_padding: (0, 1).into(),
206 node_border: true,
207 node_border_style: BorderStyle::Plain,
208 style: Style::default(),
209 node_style: Style::default(),
210 node_hover_style: Style::default(),
211 focusable: false,
212 focused_path: None,
213 node_focus_style: StyleSlot::Inherit,
214 edge_style: Style::default(),
215 edge_border_style: BorderStyle::Plain,
216 on_node_click: None,
217 on_node_hover: None,
218 on_node_focus: None,
219 on_node_activate: None,
220 padding: Padding::default(),
221 border: false,
222 border_style: BorderStyle::Plain,
223 width: Length::Auto,
224 height: Length::Auto,
225 }
226 }
227}
228
229impl Graph {
230 pub fn new() -> Self {
232 Self::default()
233 }
234
235 pub fn root(mut self, root: GraphNode) -> Self {
237 self.root = Some(root);
238 self
239 }
240
241 pub fn empty(mut self) -> Self {
243 self.root = None;
244 self
245 }
246
247 pub fn direction(mut self, direction: GraphDirection) -> Self {
249 self.direction = direction;
250 self
251 }
252
253 pub fn layout(mut self, layout: GraphLayout) -> Self {
255 self.layout = layout;
256 self
257 }
258
259 pub fn gap_x(mut self, gap_x: u16) -> Self {
261 self.gap_x = gap_x;
262 self
263 }
264
265 pub fn gap_y(mut self, gap_y: u16) -> Self {
267 self.gap_y = gap_y;
268 self
269 }
270
271 pub fn max_node_width(mut self, width: u16) -> Self {
273 self.max_node_width = width.max(1);
274 self
275 }
276
277 pub fn node_padding(mut self, padding: impl Into<Padding>) -> Self {
279 self.node_padding = padding.into();
280 self
281 }
282
283 pub fn node_border(mut self, node_border: bool) -> Self {
285 self.node_border = node_border;
286 self
287 }
288
289 pub fn node_border_style(mut self, border_style: BorderStyle) -> Self {
291 self.node_border_style = border_style;
292 self
293 }
294
295 pub fn style(mut self, style: Style) -> Self {
297 self.style = style;
298 self
299 }
300
301 pub fn node_style(mut self, style: Style) -> Self {
303 self.node_style = style;
304 self
305 }
306
307 pub fn node_hover_style(mut self, style: Style) -> Self {
309 self.node_hover_style = style;
310 self
311 }
312
313 pub fn focusable(mut self, focusable: bool) -> Self {
315 self.focusable = focusable;
316 self
317 }
318
319 pub fn focused_path(mut self, path: GraphNodePath) -> Self {
321 self.focused_path = Some(path);
322 self
323 }
324
325 pub fn node_focus_style(mut self, style: Style) -> Self {
327 self.node_focus_style = StyleSlot::Replace(style);
328 self
329 }
330
331 pub fn extend_node_focus_style(mut self, style: Style) -> Self {
333 self.node_focus_style = StyleSlot::Extend(style);
334 self
335 }
336
337 pub fn inherit_node_focus_style(mut self) -> Self {
339 self.node_focus_style = StyleSlot::Inherit;
340 self
341 }
342
343 pub fn node_focus_style_slot(mut self, slot: StyleSlot) -> Self {
345 self.node_focus_style = slot;
346 self
347 }
348
349 pub fn on_node_click(mut self, cb: Callback<GraphNodeEvent>) -> Self {
351 self.on_node_click = Some(cb);
352 self
353 }
354
355 pub fn on_node_hover(mut self, cb: Callback<GraphNodeEvent>) -> Self {
357 self.on_node_hover = Some(cb);
358 self
359 }
360
361 pub fn on_node_focus(mut self, cb: Callback<GraphNodeEvent>) -> Self {
363 self.on_node_focus = Some(cb);
364 self
365 }
366
367 pub fn on_node_activate(mut self, cb: Callback<GraphNodeEvent>) -> Self {
369 self.on_node_activate = Some(cb);
370 self
371 }
372
373 pub fn edge_style(mut self, style: Style) -> Self {
375 self.edge_style = style;
376 self
377 }
378
379 pub fn edge_border_style(mut self, border_style: BorderStyle) -> Self {
381 self.edge_border_style = border_style;
382 self
383 }
384
385 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
387 self.padding = padding.into();
388 self
389 }
390
391 pub fn border(mut self, border: bool) -> Self {
393 self.border = border;
394 self
395 }
396
397 pub fn border_style(mut self, border_style: BorderStyle) -> Self {
399 self.border_style = border_style;
400 self
401 }
402
403 pub fn width(mut self, width: Length) -> Self {
405 self.width = width;
406 self
407 }
408
409 pub fn height(mut self, height: Length) -> Self {
411 self.height = height;
412 self
413 }
414
415 pub fn center_offset_for(
429 &self,
430 path: &GraphNodePath,
431 viewport_w: u16,
432 viewport_h: u16,
433 ) -> Option<(i32, i32)> {
434 let output = layout::build_graph_output(self);
435 let node = output.nodes.iter().find(|node| &node.path == path)?;
436 let border = i32::from(self.border);
437 let node_center_x = border
438 + i32::from(self.padding.left)
439 + i32::from(node.rect.x)
440 + i32::from(node.rect.w) / 2;
441 let node_center_y = border
442 + i32::from(self.padding.top)
443 + i32::from(node.rect.y)
444 + i32::from(node.rect.h) / 2;
445 Some((
446 node_center_x - i32::from(viewport_w) / 2,
447 node_center_y - i32::from(viewport_h) / 2,
448 ))
449 }
450
451 pub fn focus_offset_for(
458 &self,
459 path: &GraphNodePath,
460 viewport_w: u16,
461 viewport_h: u16,
462 ) -> Option<(i32, i32)> {
463 let (x, y) = self.center_offset_for(path, viewport_w, viewport_h)?;
464 let (content_w, content_h) = layout::measure_graph(self);
465 Some((
466 clamp_focus_axis(x, content_w, viewport_w),
467 clamp_focus_axis(y, content_h, viewport_h),
468 ))
469 }
470}
471
472fn clamp_focus_axis(desired: i32, content: u16, viewport: u16) -> i32 {
476 let diff = i32::from(content) - i32::from(viewport);
477 if diff >= 0 {
478 desired.clamp(0, diff)
479 } else {
480 desired.clamp(diff, 0)
481 }
482}
483
484impl From<Graph> for Element {
485 fn from(value: Graph) -> Self {
486 Element::new(ElementKind::Graph(Box::new(value)))
487 }
488}