argui_accessibility/
tree.rs1use std::collections::{HashMap, HashSet};
2
3use argui_core::Rect;
4
5use crate::Semantics;
6
7#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct SemanticNodeId(u64);
9
10impl SemanticNodeId {
11 #[must_use]
12 pub const fn new(value: u64) -> Self {
13 Self(value)
14 }
15
16 #[must_use]
17 pub const fn get(self) -> u64 {
18 self.0
19 }
20}
21
22#[derive(Clone, Debug, PartialEq)]
23pub struct SemanticNode {
24 pub id: SemanticNodeId,
25 pub bounds: Rect,
26 pub semantics: Semantics,
27 pub children: Vec<SemanticNodeId>,
28}
29
30#[derive(Clone, Debug, PartialEq)]
31pub struct SemanticTree {
32 pub root: SemanticNodeId,
33 pub focus: SemanticNodeId,
34 pub nodes: Vec<SemanticNode>,
35}
36
37impl SemanticTree {
38 #[must_use]
43 pub fn parent_relative_bounds(&self) -> HashMap<SemanticNodeId, Rect> {
44 let mut bounds = self
45 .nodes
46 .iter()
47 .map(|node| (node.id, node.bounds))
48 .collect::<HashMap<_, _>>();
49 for parent in &self.nodes {
50 for child in &parent.children {
51 if let Some(bounds) = bounds.get_mut(child) {
52 bounds.origin.x -= parent.bounds.origin.x;
53 bounds.origin.y -= parent.bounds.origin.y;
54 }
55 }
56 }
57 bounds
58 }
59
60 #[must_use]
61 pub fn node(&self, id: SemanticNodeId) -> Option<&SemanticNode> {
62 self.nodes.iter().find(|node| node.id == id)
63 }
64
65 #[must_use]
66 pub fn diff(&self, next: &Self) -> SemanticPatch {
67 let old = self
68 .nodes
69 .iter()
70 .map(|node| (node.id, node))
71 .collect::<HashMap<_, _>>();
72 let next_ids = next
73 .nodes
74 .iter()
75 .map(|node| node.id)
76 .collect::<HashSet<_>>();
77 SemanticPatch {
78 root: (self.root != next.root).then_some(next.root),
79 focus: (self.focus != next.focus).then_some(next.focus),
80 upserts: next
81 .nodes
82 .iter()
83 .filter(|node| old.get(&node.id).copied() != Some(*node))
84 .cloned()
85 .collect(),
86 removed: self
87 .nodes
88 .iter()
89 .filter(|node| !next_ids.contains(&node.id))
90 .map(|node| node.id)
91 .collect(),
92 }
93 }
94}
95
96#[derive(Clone, Debug, Default, PartialEq)]
97pub struct SemanticPatch {
98 pub root: Option<SemanticNodeId>,
99 pub focus: Option<SemanticNodeId>,
100 pub upserts: Vec<SemanticNode>,
101 pub removed: Vec<SemanticNodeId>,
102}
103
104impl SemanticPatch {
105 #[must_use]
106 pub fn is_empty(&self) -> bool {
107 self.root.is_none()
108 && self.focus.is_none()
109 && self.upserts.is_empty()
110 && self.removed.is_empty()
111 }
112}