1use std::{
2 collections::HashMap,
3 mem,
4};
5
6pub use euclid::Rect;
7use itertools::Itertools;
8use rustc_hash::FxHashMap;
9
10use crate::{
11 custom_measurer::LayoutMeasurer,
12 geometry::Area,
13 measure::{
14 MeasureContext,
15 Phase,
16 },
17 prelude::{
18 AreaConverter,
19 AreaModel,
20 AvailableAreaModel,
21 Gaps,
22 Length,
23 },
24 tree_adapter::{
25 LayoutNode,
26 NodeKey,
27 TreeAdapter,
28 },
29};
30
31pub struct LayoutMetadata {
32 pub root_area: Area,
33}
34
35#[derive(PartialEq, Debug, Clone)]
37pub enum RootNodeCandidate<Key: NodeKey> {
38 Valid(Key),
40
41 None,
43}
44
45impl<Key: NodeKey> RootNodeCandidate<Key> {
46 #[must_use]
47 pub fn take(&mut self) -> Self {
48 mem::replace(self, Self::None)
49 }
50
51 pub fn propose_new_candidate(
53 &mut self,
54 proposed_candidate: &Key,
55 tree_adapter: &mut impl TreeAdapter<Key>,
56 dirty: &mut FxHashMap<Key, DirtyReason>,
57 ) {
58 if let RootNodeCandidate::Valid(current_candidate) = self {
59 if current_candidate != proposed_candidate {
60 let mut continue_waking = true;
61 let closest_parent = tree_adapter.closest_common_parent(
62 proposed_candidate,
63 current_candidate,
64 |id| {
65 if !continue_waking {
66 return;
67 }
68 let reason = dirty.get(&id);
69 match reason {
70 Some(DirtyReason::InnerLayout) => {
71 dirty.insert(id, DirtyReason::None);
74 }
75 Some(DirtyReason::None | DirtyReason::Reorder)
76 if id != *proposed_candidate =>
77 {
78 continue_waking = false;
81 }
82 _ => {}
83 }
84 },
85 );
86
87 if let Some(closest_parent) = closest_parent {
88 *self = RootNodeCandidate::Valid(closest_parent);
89 }
90 }
91 } else {
92 *self = RootNodeCandidate::Valid(*proposed_candidate);
93 }
94 }
95}
96
97#[derive(Clone, Copy, Debug, PartialEq)]
98pub enum DirtyReason {
99 None,
100 Reorder,
102 InnerLayout,
104}
105
106pub struct Torin<Key: NodeKey> {
107 pub results: FxHashMap<Key, LayoutNode>,
109
110 pub dirty: FxHashMap<Key, DirtyReason>,
112
113 pub root_node_candidate: RootNodeCandidate<Key>,
115}
116
117impl<Key: NodeKey> Default for Torin<Key> {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123impl<Key: NodeKey> Torin<Key> {
124 pub fn new() -> Self {
126 Self {
127 results: HashMap::default(),
128 dirty: FxHashMap::default(),
129 root_node_candidate: RootNodeCandidate::None,
130 }
131 }
132
133 pub fn size(&self) -> usize {
134 self.results.len()
135 }
136
137 pub fn clear_dirty(&mut self) {
139 self.dirty.clear();
140 }
141
142 pub fn reset(&mut self) {
144 self.root_node_candidate = RootNodeCandidate::None;
145 self.results.clear();
146 self.dirty.clear();
147 }
148
149 pub fn get_dirty_nodes(&self) -> &FxHashMap<Key, DirtyReason> {
151 &self.dirty
152 }
153
154 pub fn raw_remove(&mut self, node_id: Key) {
156 self.results.remove(&node_id);
157 self.dirty.remove(&node_id);
158 if let RootNodeCandidate::Valid(id) = self.root_node_candidate
159 && id == node_id
160 {
161 self.root_node_candidate = RootNodeCandidate::None;
162 }
163 }
164
165 pub fn remove(
169 &mut self,
170 node_id: Key,
171 tree_adapter: &mut impl TreeAdapter<Key>,
172 invalidate_parent: bool,
173 ) {
174 self.raw_remove(node_id);
176
177 if invalidate_parent && let Some(parent) = tree_adapter.parent_of(&node_id) {
179 self.invalidate(parent);
180 }
181
182 for child_id in tree_adapter.children_of(&node_id) {
184 self.remove(child_id, tree_adapter, false);
185 }
186 }
187
188 pub fn safe_invalidate(&mut self, node_id: Key) {
190 self.dirty.insert(node_id, DirtyReason::None);
191 }
192
193 pub fn invalidate(&mut self, node_id: Key) {
195 self.dirty.insert(node_id, DirtyReason::None);
196 }
197
198 pub fn invalidate_with_reason(&mut self, node_id: Key, reason: DirtyReason) {
200 self.dirty.entry(node_id).or_insert(reason);
201 }
202
203 pub fn check_dirty_dependants(
205 &mut self,
206 node_id: Key,
207 reason: DirtyReason,
208 tree_adapter: &mut impl TreeAdapter<Key>,
209 ignore: bool,
210 ) {
211 if self.dirty.contains_key(&node_id) && ignore {
212 return;
213 }
214
215 self.invalidate_with_reason(node_id, reason);
217
218 self.root_node_candidate
219 .propose_new_candidate(&node_id, tree_adapter, &mut self.dirty);
220
221 let parent_id = tree_adapter.parent_of(&node_id);
223
224 if let Some(parent_id) = parent_id {
225 if reason == DirtyReason::InnerLayout {
226 self.root_node_candidate.propose_new_candidate(
227 &parent_id,
228 tree_adapter,
229 &mut self.dirty,
230 );
231 return;
232 }
233
234 let parent = tree_adapter.get_node(&parent_id);
235
236 if let Some(parent) = parent {
237 if parent.does_depend_on_inner() {
238 self.check_dirty_dependants(parent_id, DirtyReason::None, tree_adapter, true);
240 } else {
241 let parent_children = tree_adapter.children_of(&parent_id);
242 let multiple_children = parent_children.len() > 1;
243
244 let mut found_node = match reason {
245 DirtyReason::None | DirtyReason::InnerLayout => false,
246 DirtyReason::Reorder => true,
248 };
249 for child_id in parent_children {
250 if found_node {
251 self.safe_invalidate(child_id);
252 }
253 if child_id == node_id {
254 found_node = true;
255 }
256 }
257
258 if multiple_children || parent.do_inner_depend_on_parent() {
260 self.root_node_candidate.propose_new_candidate(
261 &parent_id,
262 tree_adapter,
263 &mut self.dirty,
264 );
265 }
266 }
267 }
268 }
269 }
270
271 pub fn get_root_candidate(&self) -> RootNodeCandidate<Key> {
273 self.root_node_candidate.clone()
274 }
275
276 pub fn find_best_root(&mut self, tree_adapter: &mut impl TreeAdapter<Key>) {
278 if self.results.is_empty() {
279 return;
280 }
281 for (id, reason) in self
282 .dirty
283 .clone()
284 .into_iter()
285 .sorted_by_key(|e| tree_adapter.height(&e.0))
286 {
287 self.check_dirty_dependants(id, reason, tree_adapter, false);
288 }
289 }
290
291 pub fn measure(
295 &mut self,
296 suggested_root_id: Key,
297 root_area: Area,
298 measurer: &mut Option<impl LayoutMeasurer<Key>>,
299 tree_adapter: &mut impl TreeAdapter<Key>,
300 ) {
301 if self.dirty.is_empty() && !self.results.is_empty() {
305 return;
306 }
307
308 let root_id = if let RootNodeCandidate::Valid(id) = self.root_node_candidate.take() {
310 id
311 } else {
312 suggested_root_id
313 };
314 let root_parent_id = tree_adapter.parent_of(&root_id);
315 let layout_node = root_parent_id
316 .and_then(|root_parent_id| self.get(&root_parent_id).cloned())
317 .unwrap_or(LayoutNode {
318 area: root_area,
319 inner_area: root_area.as_inner(),
320 margin: Gaps::default(),
321 offset_x: Length::default(),
322 offset_y: Length::default(),
323 data: None,
324 });
325 let root = tree_adapter.get_node(&root_id).unwrap();
326
327 #[cfg(debug_assertions)]
328 {
329 let root_height = tree_adapter.height(&root_id).unwrap();
330 tracing::info!(
331 "Processing {} dirty nodes and {} cached nodes from a height of {}",
332 self.dirty.len(),
333 self.results.len(),
334 root_height
335 );
336 }
337
338 let layout_metadata = LayoutMetadata { root_area };
339
340 let mut available_area = layout_node.inner_area.as_available();
341 if let Some(root_parent_id) = root_parent_id {
342 let root_parent = tree_adapter.get_node(&root_parent_id).unwrap();
343 available_area.move_with_offsets(&root_parent.offset_x, &root_parent.offset_y);
344 }
345
346 let mut measure_context = MeasureContext {
347 layout: self,
348 layout_metadata,
349 tree_adapter,
350 measurer,
351 };
352
353 let (root_revalidated, mut root_layout_node) = measure_context.measure_node(
354 root_id,
355 &root,
356 layout_node.inner_area.as_parent(),
357 available_area,
358 true,
359 false,
360 Phase::Final,
361 );
362
363 if root_revalidated {
365 root_layout_node.area.adjust_size(&root);
367
368 self.cache_node(root_id, root_layout_node);
369 }
370
371 self.dirty.clear();
372 self.root_node_candidate = RootNodeCandidate::None;
373 }
374
375 pub fn get(&self, node_id: &Key) -> Option<&LayoutNode> {
377 self.results.get(node_id)
378 }
379
380 pub fn get_mut(&mut self, node_id: &Key) -> Option<&mut LayoutNode> {
382 self.results.get_mut(node_id)
383 }
384
385 pub fn cache_node(&mut self, node_id: Key, layout_node: LayoutNode) {
387 self.results.insert(node_id, layout_node);
388 }
389}