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