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