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