torin/
dom_adapter.rs

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