1use std::{
2 collections::HashMap,
3 mem,
4};
5
6use itertools::Itertools;
7use rustc_hash::FxHashMap;
8
9use crate::{
10 custom_measurer::LayoutMeasurer,
11 geometry::Area,
12 measure::{
13 MeasureContext,
14 Phase,
15 },
16 prelude::{
17 AreaConverter,
18 AreaModel,
19 Gaps,
20 Length,
21 Size2D,
22 },
23 tree_adapter::{
24 LayoutNode,
25 NodeKey,
26 TreeAdapter,
27 },
28};
29
30pub struct LayoutMetadata {
31 pub root_area: Area,
32}
33
34#[derive(PartialEq, Debug, Clone)]
36pub enum RootNodeCandidate<Key: NodeKey> {
37 Valid(Key),
39
40 None,
42}
43
44impl<Key: NodeKey> RootNodeCandidate<Key> {
45 #[must_use]
46 pub fn take(&mut self) -> Self {
47 mem::replace(self, Self::None)
48 }
49
50 pub fn propose_new_candidate(
52 &mut self,
53 proposed_candidate: &Key,
54 tree_adapter: &mut impl TreeAdapter<Key>,
55 dirty: &mut FxHashMap<Key, DirtyReason>,
56 ) {
57 if let RootNodeCandidate::Valid(current_candidate) = self {
58 if current_candidate != proposed_candidate {
59 let mut continue_walking = true;
60 let closest_parent = tree_adapter.closest_common_parent(
61 proposed_candidate,
62 current_candidate,
63 |id| {
64 if !continue_walking {
65 return;
66 }
67 let reason = dirty.get(&id);
68 match reason {
69 Some(DirtyReason::InnerLayout) => {
70 dirty.insert(id, DirtyReason::None);
73 }
74 Some(DirtyReason::None | DirtyReason::Reorder)
75 if id != *proposed_candidate =>
76 {
77 continue_walking = false;
80 }
81 _ => {}
82 }
83 },
84 );
85
86 if let Some(closest_parent) = closest_parent {
87 *self = RootNodeCandidate::Valid(closest_parent);
88 }
89 }
90 } else {
91 *self = RootNodeCandidate::Valid(*proposed_candidate);
92 }
93 }
94}
95
96#[derive(Clone, Copy, Debug, PartialEq)]
97pub enum DirtyReason {
98 None,
99 Reorder,
101 InnerLayout,
103}
104
105pub struct Torin<Key: NodeKey> {
106 pub results: FxHashMap<Key, LayoutNode>,
108
109 pub dirty: FxHashMap<Key, DirtyReason>,
111
112 pub root_node_candidate: RootNodeCandidate<Key>,
114}
115
116impl<Key: NodeKey> Default for Torin<Key> {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122impl<Key: NodeKey> Torin<Key> {
123 pub fn new() -> Self {
125 Self {
126 results: HashMap::default(),
127 dirty: FxHashMap::default(),
128 root_node_candidate: RootNodeCandidate::None,
129 }
130 }
131
132 pub fn size(&self) -> usize {
133 self.results.len()
134 }
135
136 pub fn clear_dirty(&mut self) {
138 self.dirty.clear();
139 }
140
141 pub fn reset(&mut self) {
143 self.root_node_candidate = RootNodeCandidate::None;
144 self.results.clear();
145 self.dirty.clear();
146 }
147
148 pub fn get_dirty_nodes(&self) -> &FxHashMap<Key, DirtyReason> {
150 &self.dirty
151 }
152
153 pub fn raw_remove(&mut self, node_id: Key) {
155 self.results.remove(&node_id);
156 self.dirty.remove(&node_id);
157 if let RootNodeCandidate::Valid(id) = self.root_node_candidate
158 && id == node_id
159 {
160 self.root_node_candidate = RootNodeCandidate::None;
161 }
162 }
163
164 pub fn remove(
168 &mut self,
169 node_id: Key,
170 tree_adapter: &mut impl TreeAdapter<Key>,
171 invalidate_parent: bool,
172 ) {
173 self.raw_remove(node_id);
175
176 if invalidate_parent && let Some(parent) = tree_adapter.parent_of(&node_id) {
178 self.invalidate(parent);
179 }
180
181 for child_id in tree_adapter.children_of(&node_id) {
183 self.remove(child_id, tree_adapter, false);
184 }
185 }
186
187 pub fn safe_invalidate(&mut self, node_id: Key) {
189 self.dirty.insert(node_id, DirtyReason::None);
190 }
191
192 pub fn invalidate(&mut self, node_id: Key) {
194 self.dirty.insert(node_id, DirtyReason::None);
195 }
196
197 pub fn invalidate_with_reason(&mut self, node_id: Key, reason: DirtyReason) {
199 self.dirty.entry(node_id).or_insert(reason);
200 }
201
202 pub fn check_dirty_dependants(
204 &mut self,
205 node_id: Key,
206 reason: DirtyReason,
207 tree_adapter: &mut impl TreeAdapter<Key>,
208 ignore: bool,
209 ) {
210 if self.dirty.contains_key(&node_id) && ignore {
211 return;
212 }
213
214 self.invalidate_with_reason(node_id, reason);
216
217 self.root_node_candidate
218 .propose_new_candidate(&node_id, tree_adapter, &mut self.dirty);
219
220 let parent_id = tree_adapter.parent_of(&node_id);
222
223 if let Some(parent_id) = parent_id {
224 if reason == DirtyReason::InnerLayout {
225 self.root_node_candidate.propose_new_candidate(
226 &parent_id,
227 tree_adapter,
228 &mut self.dirty,
229 );
230 return;
231 }
232
233 let parent = tree_adapter.get_node(&parent_id);
234
235 if let Some(parent) = parent {
236 if parent.does_depend_on_inner() {
237 self.check_dirty_dependants(parent_id, DirtyReason::None, tree_adapter, true);
239 } else {
240 let parent_children = tree_adapter.children_of(&parent_id);
241 let multiple_children = parent_children.len() > 1;
242
243 let mut found_node = match reason {
244 DirtyReason::None | DirtyReason::InnerLayout => false,
245 DirtyReason::Reorder => true,
247 };
248 for child_id in parent_children {
249 if found_node {
250 self.safe_invalidate(child_id);
251 }
252 if child_id == node_id {
253 found_node = true;
254 }
255 }
256
257 if multiple_children || parent.do_inner_depend_on_parent() {
259 self.root_node_candidate.propose_new_candidate(
260 &parent_id,
261 tree_adapter,
262 &mut self.dirty,
263 );
264 }
265 }
266 }
267 }
268 }
269
270 pub fn get_root_candidate(&self) -> RootNodeCandidate<Key> {
272 self.root_node_candidate.clone()
273 }
274
275 pub fn find_best_root(&mut self, tree_adapter: &mut impl TreeAdapter<Key>) {
277 if self.results.is_empty() {
278 return;
279 }
280 for (id, reason) in self
281 .dirty
282 .clone()
283 .into_iter()
284 .sorted_by_key(|e| tree_adapter.height(&e.0))
285 {
286 self.check_dirty_dependants(id, reason, tree_adapter, false);
287 }
288 }
289
290 pub fn measure(
294 &mut self,
295 suggested_root_id: Key,
296 root_area: Area,
297 measurer: &mut Option<impl LayoutMeasurer<Key>>,
298 tree_adapter: &mut impl TreeAdapter<Key>,
299 ) {
300 if self.dirty.is_empty() && !self.results.is_empty() {
304 return;
305 }
306
307 let root_id = if let RootNodeCandidate::Valid(id) = self.root_node_candidate.take() {
309 id
310 } else {
311 suggested_root_id
312 };
313 let root_parent_id = tree_adapter.parent_of(&root_id);
314 let layout_node = root_parent_id
315 .and_then(|root_parent_id| self.get(&root_parent_id).cloned())
316 .unwrap_or(LayoutNode {
317 area: root_area,
318 inner_area: root_area.as_inner(),
319 inner_sizes: Size2D::default(),
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 inner_area = layout_node.inner_area.as_inner();
341
342 let available_area = inner_area.as_available();
343 let mut measure_context = MeasureContext {
344 layout: self,
345 layout_metadata,
346 tree_adapter,
347 measurer,
348 };
349
350 let (root_revalidated, mut root_layout_node) = measure_context.measure_node(
351 root_id,
352 &root,
353 inner_area.as_parent(),
354 available_area,
355 true,
356 false,
357 Phase::Final,
358 );
359
360 if root_revalidated {
362 root_layout_node.area.adjust_size(&root);
364
365 self.cache_node(root_id, root_layout_node);
366 }
367
368 self.dirty.clear();
369 self.root_node_candidate = RootNodeCandidate::None;
370 }
371
372 pub fn get(&self, node_id: &Key) -> Option<&LayoutNode> {
374 self.results.get(node_id)
375 }
376
377 pub fn get_mut(&mut self, node_id: &Key) -> Option<&mut LayoutNode> {
379 self.results.get_mut(node_id)
380 }
381
382 pub fn cache_node(&mut self, node_id: Key, layout_node: LayoutNode) {
384 self.results.insert(node_id, layout_node);
385 }
386}