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