torin/
dom_adapter.rs

1use std::{
2    any::Any,
3    rc::Rc,
4};
5
6pub use euclid::Rect;
7
8use crate::{
9    geometry::Area,
10    node::Node,
11    prelude::{
12        AreaModel,
13        Gaps,
14        Length,
15    },
16};
17
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19/// Cached layout results of a Node
20#[derive(Debug, Default, Clone)]
21pub struct LayoutNode {
22    /// Area that ocuppies this node
23    pub area: Area,
24
25    /// Area inside this Node
26    pub inner_area: Area,
27
28    /// Outer margin
29    pub margin: Gaps,
30
31    /// Offsets
32    pub offset_x: Length,
33    pub offset_y: Length,
34
35    /// Associated data
36    #[cfg_attr(feature = "serde", serde(skip_deserializing, skip_serializing))]
37    pub data: Option<Rc<dyn Any>>,
38}
39
40impl PartialEq for LayoutNode {
41    fn eq(&self, other: &Self) -> bool {
42        self.area == other.area
43            && self.inner_area == other.inner_area
44            && self.margin == other.margin
45            && self.offset_x == other.offset_x
46            && self.offset_y == other.offset_y
47    }
48}
49
50impl LayoutNode {
51    // The area without any margin
52    pub fn visible_area(&self) -> Area {
53        self.area.without_gaps(&self.margin)
54    }
55}
56
57pub trait NodeKey: Clone + PartialEq + Eq + std::hash::Hash + Copy + std::fmt::Debug {}
58
59impl NodeKey for usize {}
60
61pub trait TreeAdapter<Key: NodeKey> {
62    fn root_id(&self) -> Key;
63
64    /// Get the Node size
65    fn get_node(&self, node_id: &Key) -> Option<Node>;
66
67    /// Get the height in the Tree of the given Node
68    fn height(&self, node_id: &Key) -> Option<u16>;
69
70    /// Get the parent of a Node
71    fn parent_of(&self, node_id: &Key) -> Option<Key>;
72
73    /// Get the children of a Node
74    fn children_of(&mut self, node_id: &Key) -> Vec<Key>;
75
76    /// Get the closest common parent Node of two Nodes
77    fn closest_common_parent(
78        &self,
79        node_a: &Key,
80        node_b: &Key,
81        walker: impl FnMut(Key),
82    ) -> Option<Key> {
83        let height_a = self.height(node_a)?;
84        let height_b = self.height(node_b)?;
85
86        let (node_a, node_b) = match height_a.cmp(&height_b) {
87            std::cmp::Ordering::Less => (
88                *node_a,
89                // Does not make sense to call the walker for when the node a is higher than the node b
90                balance_heights(self, *node_b, *node_a, None::<fn(_)>).unwrap_or(*node_b),
91            ),
92            std::cmp::Ordering::Equal => (*node_a, *node_b),
93            std::cmp::Ordering::Greater => (
94                balance_heights(self, *node_a, *node_b, Some(walker)).unwrap_or(*node_a),
95                *node_b,
96            ),
97        };
98
99        let mut currents = (node_a, node_b);
100
101        loop {
102            // Common parent of node_a and node_b
103            if currents.0 == currents.1 {
104                return Some(currents.0);
105            }
106
107            let parent_a = self.parent_of(&currents.0);
108            if let Some(parent_a) = parent_a {
109                currents.0 = parent_a;
110            } else if self.root_id() != currents.0 {
111                // Skip unconected nodes
112                break;
113            }
114
115            let parent_b = self.parent_of(&currents.1);
116            if let Some(parent_b) = parent_b {
117                currents.1 = parent_b;
118            } else if self.root_id() != currents.1 {
119                // Skip unconected nodes
120                break;
121            }
122        }
123
124        None
125    }
126}
127
128/// Walk to the ancestor of `base` with the same height of `target`
129fn balance_heights<Key: NodeKey>(
130    dom_adapter: &(impl TreeAdapter<Key> + ?Sized),
131    base: Key,
132    target: Key,
133    mut walker: Option<impl FnMut(Key)>,
134) -> Option<Key> {
135    let target_height = dom_adapter.height(&target)?;
136    let mut current = base;
137    loop {
138        if let Some(walker) = &mut walker {
139            (walker)(current);
140        }
141        if dom_adapter.height(&current)? == target_height {
142            break;
143        }
144
145        let parent_current = dom_adapter.parent_of(&current);
146        if let Some(parent_current) = parent_current {
147            current = parent_current;
148        }
149    }
150    Some(current)
151}