Skip to main content

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