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