Skip to main content

apace_dom/
tree.rs

1//! Versioned storage for the nodes of the DOM tree.
2
3use std::ops::{Index, IndexMut};
4use std::sync::atomic::{AtomicU64, Ordering};
5
6use apace_traits::node_id::NodeId;
7use slotmap::{Key as _, KeyData, SlotMap};
8
9use crate::Node;
10
11slotmap::new_key_type! {
12    /// The internal [`slotmap`] key for node storage. Only used at the
13    /// storage boundary: all public APIs use [`NodeId`].
14    struct NodeKey;
15}
16
17#[inline(always)]
18fn to_key(id: NodeId) -> NodeKey {
19    NodeKey::from(KeyData::from_ffi(id.as_u64()))
20}
21
22#[inline(always)]
23fn to_id(key: NodeKey) -> NodeId {
24    NodeId::from_u64(key.data().as_ffi())
25}
26
27/// The versioned map in which the nodes of the DOM tree are stored, backed by
28/// a [`slotmap::SlotMap`].
29///
30/// Nodes are addressed by [`NodeId`], which carries the slot's version in
31/// addition to its index: when a node is dropped and its slot reused, ids
32/// referring to the dropped node no longer resolve ([`NodeTree::get`] returns
33/// `None`, and indexing panics) instead of aliasing the new occupant.
34pub struct NodeTree {
35    map: SlotMap<NodeKey, Node>,
36    /// Bumped whenever box geometry may have moved (layout ran or a scroll
37    /// offset changed). Values derived from `final_layout()` (e.g. hoisted
38    /// paint child offsets) are memoised against it. Starts at 1 so that a
39    /// stamp of 0 is never current.
40    geometry_generation: AtomicU64,
41    /// Scroll offset of the viewport (the root element scrolls the viewport,
42    /// so its offset lives here rather than on the node).
43    viewport_scroll: crate::Point<f64>,
44}
45
46impl NodeTree {
47    pub(crate) fn new() -> Self {
48        Self {
49            map: SlotMap::with_key(),
50            geometry_generation: AtomicU64::new(1),
51            viewport_scroll: crate::Point::ZERO,
52        }
53    }
54
55    pub fn viewport_scroll(&self) -> crate::Point<f64> {
56        self.viewport_scroll
57    }
58
59    /// Memoised paint geometry may depend on the viewport scroll, so this
60    /// bumps the geometry generation like any other scroll write.
61    pub(crate) fn set_viewport_scroll(&mut self, scroll: crate::Point<f64>) {
62        self.viewport_scroll = scroll;
63        self.bump_geometry_generation();
64    }
65
66    /// See the `geometry_generation` field.
67    pub fn geometry_generation(&self) -> u64 {
68        self.geometry_generation.load(Ordering::Relaxed)
69    }
70
71    pub(crate) fn bump_geometry_generation(&self) {
72        self.geometry_generation.fetch_add(1, Ordering::Relaxed);
73    }
74
75    /// The number of live nodes in the map.
76    pub fn len(&self) -> usize {
77        self.map.len()
78    }
79
80    pub fn is_empty(&self) -> bool {
81        self.map.is_empty()
82    }
83
84    /// Whether `id` resolves to a live node.
85    pub fn contains_key(&self, id: NodeId) -> bool {
86        self.map.contains_key(to_key(id))
87    }
88
89    /// Get a reference to the node with the given id, if it is still live.
90    pub fn get(&self, id: NodeId) -> Option<&Node> {
91        self.map.get(to_key(id))
92    }
93
94    /// Get a mutable reference to the node with the given id, if it is still live.
95    pub fn get_mut(&mut self, id: NodeId) -> Option<&mut Node> {
96        self.map.get_mut(to_key(id))
97    }
98
99    /// Insert a node constructed with knowledge of its own id.
100    pub(crate) fn insert_with_key(&mut self, f: impl FnOnce(NodeId) -> Node) -> NodeId {
101        to_id(self.map.insert_with_key(|key| f(to_id(key))))
102    }
103
104    /// Remove the node with the given id, returning it if it was still live.
105    pub(crate) fn remove(&mut self, id: NodeId) -> Option<Node> {
106        self.map.remove(to_key(id))
107    }
108
109    /// Iterate over all live `(NodeId, &Node)` pairs.
110    pub fn iter(&self) -> impl Iterator<Item = (NodeId, &Node)> {
111        self.map.iter().map(|(key, node)| (to_id(key), node))
112    }
113
114    /// Iterate over all live `(NodeId, &mut Node)` pairs.
115    pub fn iter_mut(&mut self) -> impl Iterator<Item = (NodeId, &mut Node)> {
116        self.map.iter_mut().map(|(key, node)| (to_id(key), node))
117    }
118}
119
120impl Index<NodeId> for NodeTree {
121    type Output = Node;
122
123    #[track_caller]
124    #[inline]
125    fn index(&self, id: NodeId) -> &Node {
126        &self.map[to_key(id)]
127    }
128}
129
130impl IndexMut<NodeId> for NodeTree {
131    #[track_caller]
132    #[inline]
133    fn index_mut(&mut self, id: NodeId) -> &mut Node {
134        &mut self.map[to_key(id)]
135    }
136}