1use std::{
2 any::Any,
3 borrow::Cow,
4 collections::{
5 VecDeque,
6 hash_map::Entry,
7 },
8 fmt::Debug,
9 rc::Rc,
10};
11
12use bitflags::bitflags;
13use freya_engine::prelude::{
14 FontCollection,
15 FontMgr,
16};
17use futures_channel::mpsc::UnboundedSender;
18use itertools::Itertools;
19use rustc_hash::{
20 FxHashMap,
21 FxHashSet,
22};
23use torin::{
24 prelude::{
25 Area,
26 LayoutMeasurer,
27 LayoutNode,
28 PostMeasure,
29 Size2D,
30 },
31 torin::{
32 DirtyReason,
33 Torin,
34 },
35};
36
37use crate::{
38 accessibility::groups::AccessibilityGroups,
39 data::{
40 AccessibilityState,
41 EffectState,
42 LayerState,
43 TextStyleState,
44 },
45 element::{
46 ElementExt,
47 LayoutContext,
48 PostMeasureContext,
49 },
50 elements::rect::RectElement,
51 events::{
52 data::{
53 EventType,
54 SizedEventData,
55 },
56 emittable::EmmitableEvent,
57 name::EventName,
58 },
59 extended_hashmap::ExtendedHashMap,
60 integration::{
61 AccessibilityDirtyNodes,
62 AccessibilityGenerator,
63 EventsChunk,
64 },
65 layers::Layers,
66 node_id::NodeId,
67 runner::{
68 MutationAdd,
69 MutationModified,
70 MutationMove,
71 MutationRemove,
72 Mutations,
73 },
74 text_cache::TextCache,
75 tree_layout_adapter::TreeAdapterFreya,
76};
77
78#[derive(Default)]
79pub struct Tree {
80 pub parents: FxHashMap<NodeId, NodeId>,
81 pub children: FxHashMap<NodeId, Vec<NodeId>>,
82 pub heights: FxHashMap<NodeId, u16>,
83
84 pub elements: FxHashMap<NodeId, Rc<dyn ElementExt>>,
85
86 pub listeners: FxHashMap<EventName, Vec<NodeId>>,
88
89 pub layer_state: FxHashMap<NodeId, LayerState>,
91 pub accessibility_state: FxHashMap<NodeId, AccessibilityState>,
92 pub effect_state: FxHashMap<NodeId, EffectState>,
93 pub text_style_state: FxHashMap<NodeId, TextStyleState>,
94
95 pub layout: Torin<NodeId>,
97 pub layers: Layers,
98 pub text_cache: TextCache,
99
100 pub accessibility_groups: AccessibilityGroups,
102 pub accessibility_diff: AccessibilityDirtyNodes,
103 pub accessibility_generator: AccessibilityGenerator,
104}
105
106impl Debug for Tree {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 f.debug_struct("Tree")
109 .field("children", &self.children.capacity())
110 .field("parents", &self.parents.capacity())
111 .field("elements", &self.elements.capacity())
112 .field("heights", &self.heights.capacity())
113 .field("listeners", &self.listeners.capacity())
114 .field("layer_state", &self.layer_state.capacity())
115 .field("layout_size", &self.layout.size())
116 .field("layers", &self.layers.capacity())
117 .field("effect_state", &self.effect_state.capacity())
118 .field("accessibility_state", &self.accessibility_state.capacity())
119 .field("text_style_state", &self.text_style_state.capacity())
120 .field("text_cache", &self.text_cache)
121 .finish()
122 }
123}
124
125impl Tree {
126 pub fn size(&self) -> usize {
127 self.elements.len()
128 }
129
130 pub fn traverse_depth(&self, mut then: impl FnMut(NodeId)) {
131 let mut buffer = vec![NodeId::ROOT];
132 while let Some(node_id) = buffer.pop() {
133 if let Some(children) = self.children.get(&node_id) {
134 buffer.extend(children.iter().rev());
135 }
136 then(node_id);
137 }
138 }
139
140 pub fn traverse_depth_cancel(&self, mut then: impl FnMut(NodeId) -> bool) {
141 let mut buffer = vec![NodeId::ROOT];
142 while let Some(node_id) = buffer.pop() {
143 if let Some(children) = self.children.get(&node_id) {
144 buffer.extend(children.iter().rev());
145 }
146 if then(node_id) {
147 break;
148 }
149 }
150 }
151
152 #[cfg_attr(feature = "hotpath", hotpath::measure)]
153 pub fn apply_mutations(&mut self, mutations: Mutations) -> MutationsApplyResult {
154 let mut needs_render = !mutations.removed.is_empty();
155 let mut needs_accessibility = !mutations.removed.is_empty();
156 let mut dirty = Vec::<(NodeId, DiffModifies)>::default();
157
158 #[cfg(debug_assertions)]
159 tracing::info!("{mutations:?}");
160
161 if let Entry::Vacant(e) = self.elements.entry(NodeId::ROOT) {
162 e.insert(Rc::new(RectElement::default()));
163 self.heights.insert(NodeId::ROOT, 0);
164 dirty.push((NodeId::ROOT, DiffModifies::all()));
165 }
166
167 hotpath::measure_block!("mutations run", {
168 for remove in mutations.removed.into_iter().sorted() {
169 let node_id = remove.node_id();
170 let mut buff = vec![remove];
171 let Some(parent_id) = self.parents.get(&node_id).copied() else {
172 continue;
173 };
174 self.layout.invalidate(parent_id);
175 needs_render = true;
176
177 while let Some(remove) = buff.pop() {
178 let node_id = remove.node_id();
179 self.layout.raw_remove(node_id);
180
181 let parent_id = self.parents.remove(&node_id).unwrap();
182
183 let old_element = self.elements.remove(&node_id).unwrap();
185
186 if let Some(children) = self.children.get_mut(&parent_id) {
187 match remove {
188 MutationRemove::Element { index, .. } => {
189 children.remove(index as usize);
190 }
191 MutationRemove::Scope { .. } => {
192 children.retain(|id| *id != node_id);
193 }
194 }
195 }
196
197 if let Some(children) = self.children.remove(&node_id) {
199 buff.extend(children.into_iter().enumerate().map(|(i, e)| {
200 MutationRemove::Element {
201 id: e,
202 index: i as u32,
203 }
204 }));
205 }
206
207 if let Some(events) = old_element.events_handlers() {
209 for event in events.keys() {
210 self.listeners
211 .entry(*event)
212 .or_default()
213 .retain(|id| *id != node_id);
214 }
215 }
216
217 let layer_state = self.layer_state.remove(&node_id).unwrap();
219 layer_state.remove(node_id, &mut self.layers);
220
221 let accessibility_state = self.accessibility_state.remove(&node_id).unwrap();
223 accessibility_state.remove(
224 node_id,
225 parent_id,
226 &mut self.accessibility_diff,
227 &mut self.accessibility_groups,
228 );
229
230 self.heights.remove(&node_id);
232 self.effect_state.remove(&node_id);
233 self.text_style_state.remove(&node_id);
234 self.text_cache.remove(&node_id);
235 }
236 }
237
238 for MutationAdd {
239 node_id,
240 parent_id,
241 index,
242 element,
243 } in mutations
244 .added
245 .into_iter()
246 .sorted_by_key(|m| (m.parent_id, m.index))
247 {
248 let parent_height = *self.heights.entry(parent_id).or_default();
249
250 self.parents.insert(node_id, parent_id);
251 self.heights.insert(node_id, parent_height + 1);
252
253 let parent = self.children.entry(parent_id).or_default();
254
255 if parent.len() < index as usize + 1 {
257 parent.resize(index as usize + 1, NodeId::PLACEHOLDER);
258
259 parent[index as usize] = node_id;
260 } else if parent.get(index as usize) == Some(&NodeId::PLACEHOLDER) {
261 parent[index as usize] = node_id;
262 } else {
263 parent.insert(index as usize, node_id);
264 }
265
266 if let Some(events) = element.events_handlers() {
268 for event in events.keys() {
269 self.listeners.entry(*event).or_default().push(node_id);
270 }
271 }
272
273 self.elements.insert(node_id, element);
274 dirty.push((node_id, DiffModifies::all()));
275 }
276
277 for (parent_node_id, movements) in mutations.moved {
278 let parent = self.children.get_mut(&parent_node_id).unwrap();
279 for MutationMove { index: to, node_id } in
280 movements.into_iter().sorted_by_key(|m| m.index)
281 {
282 let from = parent.iter().position(|id| *id == node_id).unwrap();
283
284 if from < to as usize {
285 parent.insert(to as usize, node_id);
286 parent.remove(from);
287 } else {
288 parent.remove(from);
289 parent.insert(to as usize, node_id);
290 }
291 }
292 let mut diff = DiffModifies::empty();
293 diff.insert(DiffModifies::REORDER_LAYOUT);
294 diff.insert(DiffModifies::ACCESSIBILITY);
295 diff.insert(DiffModifies::STYLE);
296 dirty.push((parent_node_id, diff));
297 }
298
299 for MutationModified {
300 node_id,
301 element,
302 flags,
303 } in mutations.modified
304 {
305 dirty.push((node_id, flags));
306
307 let old_element = self.elements.remove(&node_id).unwrap();
308
309 if flags.contains(DiffModifies::EVENT_HANDLERS) {
310 if let Some(events) = old_element.events_handlers() {
312 for event in events.keys() {
313 self.listeners
314 .entry(*event)
315 .or_default()
316 .retain(|id| *id != node_id);
317 }
318 }
319
320 if let Some(events) = element.events_handlers() {
322 for event in events.keys() {
323 self.listeners.entry(*event).or_default().push(node_id);
324 }
325 }
326 }
327
328 self.elements.insert(node_id, element);
329 }
330 });
331
332 let mut layer_cascades: Vec<NodeId> = Vec::new();
334 let mut effects_cascades: Vec<NodeId> = Vec::new();
335 let mut text_style_cascades: Vec<NodeId> = Vec::new();
336
337 assert_eq!(dirty.len(), FxHashSet::from_iter(&dirty).len());
338
339 hotpath::measure_block!("dirty run", {
340 for (node_id, flags) in dirty {
341 let element = self.elements.get(&node_id).unwrap();
342 let height_b = self.heights.get(&node_id).unwrap();
343
344 if flags.contains(DiffModifies::REORDER_LAYOUT) {
345 self.layout
346 .invalidate_with_reason(node_id, DirtyReason::Reorder);
347 }
348
349 if flags.contains(DiffModifies::INNER_LAYOUT) {
350 self.layout
351 .invalidate_with_reason(node_id, DirtyReason::InnerLayout);
352 }
353
354 if flags.contains(DiffModifies::LAYOUT) {
355 self.layout.invalidate(node_id);
356 }
357
358 if !needs_render
359 && (flags.intersects(
360 DiffModifies::STYLE
361 | DiffModifies::LAYER
362 | DiffModifies::EFFECT
363 | DiffModifies::TEXT_STYLE
364 | DiffModifies::LAYOUT
365 | DiffModifies::INNER_LAYOUT
366 | DiffModifies::REORDER_LAYOUT,
367 ))
368 {
369 needs_render = true;
370 }
371
372 if !needs_accessibility && (flags.intersects(DiffModifies::ACCESSIBILITY)) {
373 needs_accessibility = true;
374 }
375
376 if flags.contains(DiffModifies::ACCESSIBILITY) {
377 match self.accessibility_state.get_mut(&node_id) {
378 Some(accessibility_state) => accessibility_state.update(
379 node_id,
380 element,
381 &mut self.accessibility_diff,
382 &mut self.accessibility_groups,
383 ),
384 None => {
385 self.accessibility_state.insert(
386 node_id,
387 AccessibilityState::create(
388 node_id,
389 element,
390 &mut self.accessibility_diff,
391 &self.accessibility_generator,
392 &mut self.accessibility_groups,
393 ),
394 );
395 }
396 }
397 }
398
399 let handle_cascade = |cascades: &mut Vec<NodeId>| {
400 if cascades.iter_mut().any(|root| {
402 let height_a = self.heights.get(root).unwrap();
403
404 match height_a.cmp(height_b) {
405 std::cmp::Ordering::Less => {
406 self.balance_heights(&node_id, root) == Some(*root)
407 }
408 std::cmp::Ordering::Greater => {
409 let balanced_root = self.balance_heights(root, &node_id);
410 match balanced_root {
411 Some(r) if r == node_id => {
412 *root = node_id;
415 true
416 }
417 _ => false,
418 }
419 }
420 std::cmp::Ordering::Equal => false,
421 }
422 }) {
423 return;
424 }
425 cascades.push(node_id);
426 };
427
428 if flags.intersects(DiffModifies::LAYER) {
429 handle_cascade(&mut layer_cascades);
430 }
431 if flags.intersects(DiffModifies::EFFECT | DiffModifies::LAYER) {
432 let element = self.elements.get(&node_id).unwrap();
433 let run_cascade = element.effect().is_some()
435 || self
436 .parents
437 .get(&node_id)
438 .map(|parent| self.effect_state.contains_key(parent))
439 .unwrap_or_default();
440 if run_cascade {
441 handle_cascade(&mut effects_cascades);
442 }
443 }
444 if flags.intersects(DiffModifies::TEXT_STYLE) {
445 handle_cascade(&mut text_style_cascades);
446 }
447 }
448 });
449
450 hotpath::measure_block!("layer cascade", {
451 for layer_root in layer_cascades {
453 let mut buffer = VecDeque::new();
454 buffer.push_front(&layer_root);
455
456 while let Some(node_id) = buffer.pop_front() {
457 let element = self.elements.get(node_id).unwrap();
458 if let Some(parent_node_id) = self.parents.get(node_id) {
459 let entries = self
460 .layer_state
461 .get_disjoint_entries([node_id, parent_node_id], |_id| {
462 LayerState::default()
463 });
464 if let Some([layer_state, parent_layer_state]) = entries {
465 layer_state.update(
466 parent_layer_state,
467 *node_id,
468 element,
469 &mut self.layers,
470 );
471 }
472 } else {
473 assert_eq!(*node_id, NodeId::ROOT);
474 self.layer_state.insert(
475 NodeId::ROOT,
476 LayerState::create_for_root(*node_id, &mut self.layers),
477 );
478 }
479 if let Some(children) = self.children.get(node_id) {
480 buffer.extend(children);
481 }
482 }
483 }
484 });
485
486 hotpath::measure_block!("effect cascade", {
487 for effect_root in effects_cascades {
489 let mut buffer = VecDeque::new();
490 buffer.push_front(&effect_root);
491
492 while let Some(node_id) = buffer.pop_front() {
493 let element = self.elements.get(node_id).unwrap();
494 if let Some(parent_node_id) = self.parents.get(node_id) {
495 let entries = self.effect_state.get_disjoint_two_entries(
496 parent_node_id,
497 node_id,
498 |_id| EffectState::default(),
499 |left, _id| left.clone(),
500 );
501 if let [Some(parent_effect_state), Some(effect_state)] = entries {
502 let effect_data = element.effect();
503 let layer = element.layer();
504 effect_state.update(
505 *parent_node_id,
506 parent_effect_state,
507 *node_id,
508 effect_data,
509 layer,
510 );
511 }
512 } else {
513 assert_eq!(*node_id, NodeId::ROOT);
514 }
515 if let Some(children) = self.children.get(node_id) {
516 buffer.extend(children);
517 }
518 }
519 }
520 });
521
522 hotpath::measure_block!("text style cascade", {
523 for text_style_root in text_style_cascades {
525 let mut buffer = VecDeque::new();
526 buffer.push_front(&text_style_root);
527
528 while let Some(node_id) = buffer.pop_front() {
529 let element = self.elements.get(node_id).unwrap();
530 if let Some(parent_node_id) = self.parents.get(node_id) {
531 let entries = self
532 .text_style_state
533 .get_disjoint_entries([node_id, parent_node_id], |_id| {
534 TextStyleState::default()
535 });
536 if let Some([text_style_state, parent_text_style_state]) = entries {
537 text_style_state.update(
538 *node_id,
539 parent_text_style_state,
540 element,
541 &mut self.layout,
542 );
543 }
544 } else {
545 assert_eq!(*node_id, NodeId::ROOT);
546 self.text_style_state
547 .insert(NodeId::ROOT, TextStyleState::default());
548 }
549 if let Some(children) = self.children.get(node_id) {
550 buffer.extend(children);
551 }
552 }
553 }
554
555 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
556 self.verify_tree_integrity();
557 });
558
559 MutationsApplyResult {
560 needs_render,
561 needs_accessibility,
562 }
563 }
564
565 fn balance_heights(&self, base: &NodeId, target: &NodeId) -> Option<NodeId> {
567 let target_height = self.heights.get(target)?;
568 let mut current = base;
569 loop {
570 if self.heights.get(current)? == target_height {
571 break;
572 }
573
574 let parent_current = self.parents.get(current);
575 if let Some(parent_current) = parent_current {
576 current = parent_current;
577 }
578 }
579 Some(*current)
580 }
581
582 pub fn measure_layout(
583 &mut self,
584 size: Size2D,
585 font_collection: &mut FontCollection,
586 font_manager: &FontMgr,
587 events_sender: &UnboundedSender<EventsChunk>,
588 scale_factor: f64,
589 fallback_fonts: &[Cow<'static, str>],
590 ) {
591 let mut tree_adapter = TreeAdapterFreya {
592 elements: &self.elements,
593 parents: &self.parents,
594 children: &self.children,
595 heights: &self.heights,
596 scale_factor,
597 };
598
599 let mut events = Vec::new();
600
601 let layout_adapter = LayoutMeasurerAdapter {
602 elements: &self.elements,
603 text_style_state: &self.text_style_state,
604 font_collection,
605 font_manager,
606 events: &mut events,
607 scale_factor,
608 fallback_fonts,
609 text_cache: &mut self.text_cache,
610 };
611
612 self.layout.find_best_root(&mut tree_adapter);
613 self.layout.measure(
614 NodeId::ROOT,
615 Area::from_size(size),
616 &mut Some(layout_adapter),
617 &mut tree_adapter,
618 );
619 events_sender
620 .unbounded_send(EventsChunk::Batch(events))
621 .unwrap();
622 }
623
624 pub fn print_ascii(&self, node_id: NodeId, prefix: String, last: bool) {
625 let height = self.heights.get(&node_id).unwrap();
626 let layer = self.layer_state.get(&node_id).unwrap();
627
628 println!(
630 "{}{}{:?} [{}] ({})",
631 prefix,
632 if last { "└── " } else { "├── " },
633 node_id,
634 height,
635 layer.layer
636 );
637
638 if let Some(children) = self.children.get(&node_id) {
640 let len = children.len();
641 for (i, child) in children.iter().enumerate() {
642 let is_last = i == len - 1;
643 let new_prefix = format!("{}{}", prefix, if last { " " } else { "│ " });
645 self.print_ascii(*child, new_prefix, is_last);
646 }
647 }
648 }
649
650 #[cfg(all(debug_assertions, feature = "debug-integrity"))]
651 #[cfg_attr(feature = "hotpath", hotpath::measure)]
652 pub fn verify_tree_integrity(&self) {
653 let mut visited = FxHashSet::default();
654 let size = self.elements.len();
655 let mut buffer = vec![NodeId::ROOT];
656 while let Some(node_id) = buffer.pop() {
657 if visited.contains(&node_id) {
658 continue;
659 }
660 visited.insert(node_id);
661 if let Some(parent) = self.parents.get(&node_id) {
662 buffer.push(*parent);
663 }
664 if let Some(children) = self.children.get(&node_id) {
665 buffer.extend(children);
666 }
667 }
668 assert_eq!(size, visited.len())
669 }
670}
671
672bitflags! {
673 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
674 pub struct DiffModifies: u32 {
675 const LAYOUT = 1 << 0;
676 const STYLE = 1 << 1;
677 const ACCESSIBILITY = 1 << 2;
678 const EVENT_HANDLERS = 1 << 3;
679 const LAYER = 1 << 4;
680 const TEXT_STYLE = 1 << 5;
681 const EFFECT = 1 << 6;
682 const INNER_LAYOUT = 1 << 7;
683 const REORDER_LAYOUT = 1 << 8;
684 }
685}
686
687pub struct MutationsApplyResult {
688 pub needs_render: bool,
689 pub needs_accessibility: bool,
690}
691
692pub struct LayoutMeasurerAdapter<'a> {
693 pub font_collection: &'a mut FontCollection,
694 pub font_manager: &'a FontMgr,
695 elements: &'a FxHashMap<NodeId, Rc<dyn ElementExt>>,
696 text_style_state: &'a FxHashMap<NodeId, TextStyleState>,
697 events: &'a mut Vec<EmmitableEvent>,
698 scale_factor: f64,
699 fallback_fonts: &'a [Cow<'static, str>],
700 text_cache: &'a mut TextCache,
701}
702
703impl LayoutMeasurer<NodeId> for LayoutMeasurerAdapter<'_> {
704 fn measure(
705 &mut self,
706 node_id: NodeId,
707 torin_node: &torin::node::Node,
708 area_size: &Size2D,
709 ) -> Option<(Size2D, Rc<dyn Any>)> {
710 self.elements.get(&node_id)?.measure(LayoutContext {
711 node_id,
712 torin_node,
713 area_size,
714 font_collection: self.font_collection,
715 font_manager: self.font_manager,
716 text_style_state: self.text_style_state.get(&node_id).unwrap(),
717 scale_factor: self.scale_factor,
718 fallback_fonts: self.fallback_fonts,
719 text_cache: self.text_cache,
720 })
721 }
722
723 fn should_hook_measurement(&mut self, node_id: NodeId) -> bool {
724 if let Some(element) = self.elements.get(&node_id) {
725 element.should_hook_measurement()
726 } else {
727 false
728 }
729 }
730
731 fn should_measure_inner_children(&mut self, node_id: NodeId) -> bool {
732 if let Some(element) = self.elements.get(&node_id) {
733 element.should_measure_inner_children()
734 } else {
735 false
736 }
737 }
738
739 fn should_post_measure(&mut self, node_id: NodeId) -> bool {
740 self.elements
741 .get(&node_id)
742 .is_some_and(|element| element.needs_post_measure())
743 }
744
745 fn post_measure(
746 &mut self,
747 node_id: NodeId,
748 node_layout: &LayoutNode,
749 children: &[NodeId],
750 layout: &Torin<NodeId>,
751 ) -> PostMeasure<NodeId> {
752 self.elements
753 .get(&node_id)
754 .unwrap()
755 .post_measure(PostMeasureContext {
756 node_layout,
757 children,
758 layout,
759 font_collection: self.font_collection,
760 text_style_state: self.text_style_state.get(&node_id).unwrap(),
761 fallback_fonts: self.fallback_fonts,
762 scale_factor: self.scale_factor,
763 })
764 }
765
766 fn notify_layout_references(
767 &mut self,
768 node_id: NodeId,
769 area: Area,
770 visible_area: Area,
771 inner_sizes: Size2D,
772 ) {
773 let mut data = SizedEventData::new(area, visible_area, inner_sizes);
774 data.div(self.scale_factor as f32);
775 self.events.push(EmmitableEvent {
776 node_id,
777 name: EventName::Sized,
778 data: EventType::Sized(data),
779 bubbles: false,
780 source_event: EventName::Sized,
781 });
782 }
783}