freya_core/dom/
dom_adapter.rs

1use freya_native_core::{
2    prelude::NodeType,
3    real_dom::NodeImmutable,
4    tree::TreeRef,
5    NodeId,
6};
7use rustc_hash::FxHashMap;
8use torin::prelude::*;
9
10use crate::{
11    dom::DioxusDOM,
12    states::LayoutState,
13};
14
15/// RealDOM adapter for Torin.
16pub struct DioxusDOMAdapter<'a> {
17    pub rdom: &'a DioxusDOM,
18    pub scale_factor: f32,
19    cache: FxHashMap<NodeId, bool>,
20}
21
22impl<'a> DioxusDOMAdapter<'a> {
23    pub fn new(rdom: &'a DioxusDOM, scale_factor: f32) -> Self {
24        Self {
25            rdom,
26            scale_factor,
27            cache: FxHashMap::default(),
28        }
29    }
30}
31
32impl DOMAdapter<NodeId> for DioxusDOMAdapter<'_> {
33    fn get_node(&self, node_id: &NodeId) -> Option<Node> {
34        let node = self.rdom.get(*node_id)?;
35        let contains_text = node
36            .node_type()
37            .tag()
38            .map(|t| t.contains_text())
39            .unwrap_or_default();
40
41        let mut layout = node.get::<LayoutState>()?.clone();
42
43        // The root node expands by default
44        if *node_id == self.rdom.root_id() {
45            layout.width = Size::Percentage(Length::new(100.0));
46            layout.height = Size::Percentage(Length::new(100.0));
47        }
48
49        let mut node = Node {
50            width: layout.width,
51            height: layout.height,
52            minimum_width: layout.minimum_width,
53            minimum_height: layout.minimum_height,
54            maximum_width: layout.maximum_width,
55            maximum_height: layout.maximum_height,
56            visible_width: layout.visible_width,
57            visible_height: layout.visible_height,
58            direction: layout.direction,
59            padding: layout.padding,
60            margin: layout.margin,
61            main_alignment: layout.main_alignment,
62            cross_alignment: layout.cross_alignment,
63            offset_x: layout.offset_x,
64            offset_y: layout.offset_y,
65            has_layout_references: layout.node_ref.is_some(),
66            position: layout.position,
67            content: layout.content,
68            contains_text,
69            spacing: layout.spacing,
70        };
71
72        node.scale_if_needed(self.scale_factor);
73
74        Some(node)
75    }
76
77    fn height(&self, node_id: &NodeId) -> Option<u16> {
78        self.rdom.tree_ref().height(*node_id)
79    }
80
81    fn parent_of(&self, node_id: &NodeId) -> Option<NodeId> {
82        self.rdom.tree_ref().parent_id(*node_id)
83    }
84
85    fn children_of(&mut self, node_id: &NodeId) -> Vec<NodeId> {
86        let mut children = self.rdom.tree_ref().children_ids(*node_id);
87        children.retain(|id| is_node_valid(self.rdom, &mut self.cache, id));
88        children
89    }
90
91    fn is_node_valid(&mut self, node_id: &NodeId) -> bool {
92        is_node_valid(self.rdom, &mut self.cache, node_id)
93    }
94
95    fn root_id(&self) -> NodeId {
96        self.rdom.root_id()
97    }
98}
99
100/// Check is the given Node is valid or not, this means not being a placeholder or an unconnected Node.
101fn is_node_valid(rdom: &DioxusDOM, cache: &mut FxHashMap<NodeId, bool>, node_id: &NodeId) -> bool {
102    // Check if Node was valid from cache
103    if let Some(is_valid) = cache.get(node_id) {
104        return *is_valid;
105    }
106
107    let node = rdom.get(*node_id);
108
109    let is_valid = 'validation: {
110        if let Some(node) = node {
111            let is_placeholder = matches!(*node.node_type(), NodeType::Placeholder);
112
113            // Placeholders can't be measured
114            if is_placeholder {
115                break 'validation false;
116            }
117
118            // Make sure this Node isn't part of an unconnected Node
119            // This walkes up to the ancestor that has a height of 0 and checks if it has the same ID as the root Node
120            // If it has the same ID, it means that is not an unconnected ID, otherwise, it is and should be skipped.
121            let tree = rdom.tree_ref();
122            let mut current = *node_id;
123            loop {
124                let height = tree.height(current);
125                if let Some(height) = height {
126                    if height == 0 {
127                        break;
128                    }
129                }
130
131                let parent_current = tree.parent_id(current);
132                if let Some(parent_current) = parent_current {
133                    current = parent_current;
134                }
135            }
136
137            current == rdom.root_id()
138        } else {
139            false
140        }
141    };
142
143    // Save the validation result in the cache
144    cache.insert(*node_id, is_valid);
145
146    is_valid
147}