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