freya_core/
tree.rs

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