Skip to main content

freya_core/
tree.rs

1use std::{
2    any::Any,
3    borrow::Cow,
4    collections::VecDeque,
5    fmt::Debug,
6    rc::Rc,
7};
8
9use bitflags::bitflags;
10use cursor_icon::CursorIcon;
11use freya_engine::prelude::{
12    FontCollection,
13    FontMgr,
14};
15use futures_channel::mpsc::UnboundedSender;
16use itertools::Itertools;
17use ragnarok::NodesState;
18use rustc_hash::{
19    FxHashMap,
20    FxHashSet,
21};
22use torin::{
23    node::Node,
24    prelude::{
25        Area,
26        LayoutMeasurer,
27        LayoutNode,
28        PostMeasure,
29        Scaled,
30        Size2D,
31    },
32    torin::{
33        DirtyReason,
34        Torin,
35    },
36};
37
38use crate::{
39    accessibility::groups::AccessibilityGroups,
40    data::{
41        AccessibilityState,
42        EffectState,
43        LayerState,
44        TextStyleState,
45    },
46    element::{
47        ElementExt,
48        LayoutContext,
49        PostMeasureContext,
50    },
51    elements::rect::RectElement,
52    events::{
53        data::{
54            EventType,
55            SizedEventData,
56            StyledEventData,
57            VisibleEventData,
58        },
59        emittable::EmmitableEvent,
60        name::EventName,
61    },
62    extended_hashmap::ExtendedHashMap,
63    integration::{
64        AccessibilityDirtyNodes,
65        AccessibilityFocusStrategy,
66        AccessibilityGenerator,
67        EventsChunk,
68    },
69    layers::Layers,
70    node_id::NodeId,
71    runner::{
72        MutationAdd,
73        MutationModified,
74        MutationRemove,
75        Mutations,
76    },
77    text_cache::TextCache,
78    tree_layout_adapter::TreeAdapterFreya,
79};
80
81#[derive(Default)]
82pub struct Tree {
83    pub parents: FxHashMap<NodeId, NodeId>,
84    pub children: FxHashMap<NodeId, Vec<NodeId>>,
85    pub heights: FxHashMap<NodeId, u16>,
86
87    pub elements: FxHashMap<NodeId, Rc<dyn ElementExt>>,
88
89    /// Scaled layout nodes of `elements`.
90    layout_nodes: FxHashMap<NodeId, Node>,
91
92    // Event listeners
93    pub listeners: FxHashMap<EventName, Vec<NodeId>>,
94
95    // Events queued until the next layout measure
96    pub events: Vec<EmmitableEvent>,
97
98    // Derived states
99    pub layer_state: FxHashMap<NodeId, LayerState>,
100    pub accessibility_state: FxHashMap<NodeId, AccessibilityState>,
101    pub effect_state: FxHashMap<NodeId, EffectState>,
102    pub text_style_state: FxHashMap<NodeId, TextStyleState>,
103
104    // Other
105    pub layout: Torin<NodeId>,
106    pub layers: Layers,
107    pub text_cache: TextCache,
108
109    // Accessibility
110    pub accessibility_groups: AccessibilityGroups,
111    pub accessibility_diff: AccessibilityDirtyNodes,
112    pub accessibility_generator: AccessibilityGenerator,
113}
114
115impl Tree {
116    /// The scaled layout node of an element.
117    fn scaled_layout_node(element: &Rc<dyn ElementExt>, scale_factor: f32) -> Node {
118        let mut layout_node = element.layout().layout.clone();
119        layout_node.depends_on_inner = element.needs_post_measure();
120        layout_node.scale(scale_factor);
121        layout_node
122    }
123
124    /// Register an element and its scaled layout node.
125    fn insert_element(&mut self, node_id: NodeId, element: Rc<dyn ElementExt>, scale_factor: f32) {
126        self.layout_nodes
127            .insert(node_id, Self::scaled_layout_node(&element, scale_factor));
128        self.elements.insert(node_id, element);
129    }
130
131    /// Unregister an element and its scaled layout node.
132    fn remove_element(&mut self, node_id: NodeId) -> Option<Rc<dyn ElementExt>> {
133        self.layout_nodes.remove(&node_id);
134        self.elements.remove(&node_id)
135    }
136
137    /// Rescale every layout node.
138    pub fn set_scale_factor(&mut self, scale_factor: f32) {
139        self.layout_nodes.clear();
140        for (node_id, element) in &self.elements {
141            self.layout_nodes
142                .insert(*node_id, Self::scaled_layout_node(element, scale_factor));
143        }
144    }
145}
146
147impl Debug for Tree {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        f.debug_struct("Tree")
150            .field("children", &self.children.capacity())
151            .field("parents", &self.parents.capacity())
152            .field("elements", &self.elements.capacity())
153            .field("layout_nodes", &self.layout_nodes.capacity())
154            .field("heights", &self.heights.capacity())
155            .field("listeners", &self.listeners.capacity())
156            .field("layer_state", &self.layer_state.capacity())
157            .field("layout_size", &self.layout.size())
158            .field("layers", &self.layers.capacity())
159            .field("effect_state", &self.effect_state.capacity())
160            .field("accessibility_state", &self.accessibility_state.capacity())
161            .field("text_style_state", &self.text_style_state.capacity())
162            .field("text_cache", &self.text_cache)
163            .finish()
164    }
165}
166
167impl Tree {
168    pub fn size(&self) -> usize {
169        self.elements.len()
170    }
171
172    pub fn traverse_depth(&self, mut then: impl FnMut(NodeId)) {
173        let mut buffer = vec![NodeId::ROOT];
174        while let Some(node_id) = buffer.pop() {
175            if let Some(children) = self.children.get(&node_id) {
176                buffer.extend(children.iter().rev());
177            }
178            then(node_id);
179        }
180    }
181
182    /// Resolve the [CursorIcon] for the currently hovered nodes,
183    /// checking from the topmost node (by layer) downwards until one defines a cursor.
184    pub fn cursor_icon(&self, nodes_state: &ragnarok::NodesState<NodeId>) -> CursorIcon {
185        nodes_state
186            .hovered_nodes()
187            .sorted_by_key(|node_id| {
188                std::cmp::Reverse(
189                    self.layer_state
190                        .get(node_id)
191                        .map(|layer_state| layer_state.layer)
192                        .unwrap_or_default(),
193                )
194            })
195            .find_map(|node_id| {
196                self.elements
197                    .get(node_id)
198                    .and_then(|element| element.effect())
199                    .and_then(|effect| effect.cursor)
200            })
201            .unwrap_or_default()
202    }
203
204    /// Resolve cursor visibility from the topmost hovered element that defines it.
205    pub fn cursor_visible(&self, nodes_state: &ragnarok::NodesState<NodeId>) -> bool {
206        nodes_state
207            .hovered_nodes()
208            .sorted_by_key(|node_id| {
209                std::cmp::Reverse(
210                    self.layer_state
211                        .get(node_id)
212                        .map(|layer_state| layer_state.layer)
213                        .unwrap_or_default(),
214                )
215            })
216            .find_map(|node_id| {
217                self.elements
218                    .get(node_id)
219                    .and_then(|element| element.effect())
220                    .and_then(|effect| effect.cursor_visible)
221            })
222            .unwrap_or(true)
223    }
224
225    pub fn traverse_depth_cancel(&self, mut then: impl FnMut(NodeId) -> bool) {
226        let mut buffer = vec![NodeId::ROOT];
227        while let Some(node_id) = buffer.pop() {
228            if let Some(children) = self.children.get(&node_id) {
229                buffer.extend(children.iter().rev());
230            }
231            if then(node_id) {
232                break;
233            }
234        }
235    }
236
237    #[cfg_attr(feature = "hotpath", hotpath::measure)]
238    pub fn apply_mutations(
239        &mut self,
240        mutations: Mutations,
241        scale_factor: f32,
242    ) -> MutationsApplyResult {
243        // No need to check anything
244        if mutations.is_empty() && self.elements.contains_key(&NodeId::ROOT) {
245            return MutationsApplyResult {
246                needs_render: false,
247                needs_accessibility: false,
248                auto_focus: self.accessibility_diff.requested_auto_focus.take(),
249            };
250        }
251
252        let mut needs_render = !mutations.removed.is_empty();
253        let mut needs_accessibility = !mutations.removed.is_empty();
254        let mut dirty = Vec::<(NodeId, DiffModifies)>::default();
255
256        #[cfg(debug_assertions)]
257        tracing::info!("{mutations:?}");
258
259        if !self.elements.contains_key(&NodeId::ROOT) {
260            self.insert_element(NodeId::ROOT, Rc::new(RectElement::default()), scale_factor);
261            self.heights.insert(NodeId::ROOT, 0);
262            dirty.push((NodeId::ROOT, DiffModifies::all()));
263        }
264
265        hotpath::measure_block!("mutations run", {
266            for remove in mutations.removed.into_iter().sorted() {
267                let node_id = remove.node_id();
268                let mut buff = vec![remove];
269                let Some(parent_id) = self.parents.get(&node_id).copied() else {
270                    continue;
271                };
272                self.layout.invalidate(parent_id);
273                needs_render = true;
274
275                while let Some(remove) = buff.pop() {
276                    let node_id = remove.node_id();
277                    self.layout.raw_remove(node_id);
278
279                    let parent_id = self.parents.remove(&node_id).unwrap();
280
281                    // Remove element
282                    let old_element = self.remove_element(node_id).unwrap();
283
284                    if let Some(children) = self.children.get_mut(&parent_id) {
285                        match remove {
286                            MutationRemove::Element { index, .. } => {
287                                children.remove(index as usize);
288                            }
289                            MutationRemove::Scope { .. } => {
290                                children.retain(|id| *id != node_id);
291                            }
292                        }
293                    }
294
295                    // Remove its children too
296                    if let Some(children) = self.children.remove(&node_id) {
297                        buff.extend(children.into_iter().enumerate().map(|(i, e)| {
298                            MutationRemove::Element {
299                                id: e,
300                                index: i as u32,
301                            }
302                        }));
303                    }
304
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                    // Remove from the layers
316                    let layer_state = self.layer_state.remove(&node_id).unwrap();
317                    layer_state.remove(node_id, &mut self.layers);
318
319                    // Remove from the accessibility
320                    let accessibility_state = self.accessibility_state.remove(&node_id).unwrap();
321                    accessibility_state.remove(
322                        node_id,
323                        parent_id,
324                        &mut self.accessibility_diff,
325                        &mut self.accessibility_groups,
326                    );
327
328                    // Remove from other states
329                    self.heights.remove(&node_id);
330                    self.effect_state.remove(&node_id);
331                    self.text_style_state.remove(&node_id);
332                    self.text_cache.remove(&node_id);
333                }
334            }
335
336            for MutationAdd {
337                node_id,
338                parent_id,
339                index,
340                element,
341            } in mutations
342                .added
343                .into_iter()
344                .sorted_by_key(|m| (m.parent_id, m.index))
345            {
346                let parent_height = *self.heights.entry(parent_id).or_default();
347
348                self.parents.insert(node_id, parent_id);
349                self.heights.insert(node_id, parent_height + 1);
350
351                let parent = self.children.entry(parent_id).or_default();
352
353                // TODO: Improve this
354                if parent.len() < index as usize + 1 {
355                    parent.resize(index as usize + 1, NodeId::PLACEHOLDER);
356
357                    parent[index as usize] = node_id;
358                } else if parent.get(index as usize) == Some(&NodeId::PLACEHOLDER) {
359                    parent[index as usize] = node_id;
360                } else {
361                    parent.insert(index as usize, node_id);
362                }
363
364                // Add events
365                if let Some(events) = element.events_handlers() {
366                    for event in events.keys() {
367                        self.listeners.entry(*event).or_default().push(node_id);
368                    }
369                }
370
371                self.insert_element(node_id, element, scale_factor);
372                dirty.push((node_id, DiffModifies::all()));
373            }
374
375            for (parent_node_id, children) in mutations.moved {
376                let parent = self.children.entry(parent_node_id).or_default();
377                if *parent == children {
378                    continue;
379                }
380                *parent = children;
381                dirty.push((
382                    parent_node_id,
383                    DiffModifies::REORDER_LAYOUT
384                        | DiffModifies::ACCESSIBILITY
385                        | DiffModifies::STYLE,
386                ));
387            }
388
389            for MutationModified {
390                node_id,
391                element,
392                flags,
393            } in mutations.modified
394            {
395                dirty.push((node_id, flags));
396
397                let old_element = self.remove_element(node_id).unwrap();
398
399                if flags.contains(DiffModifies::EVENT_HANDLERS) {
400                    // Remove old events
401                    if let Some(events) = old_element.events_handlers() {
402                        for event in events.keys() {
403                            self.listeners
404                                .entry(*event)
405                                .or_default()
406                                .retain(|id| *id != node_id);
407                        }
408                    }
409
410                    // Add new events
411                    if let Some(events) = element.events_handlers() {
412                        for event in events.keys() {
413                            self.listeners.entry(*event).or_default().push(node_id);
414                        }
415                    }
416                }
417
418                self.insert_element(node_id, element, scale_factor);
419            }
420        });
421
422        // Run states cascades
423        let mut layer_cascades: Vec<NodeId> = Vec::new();
424        let mut effects_cascades: Vec<NodeId> = Vec::new();
425        let mut text_style_cascades: Vec<NodeId> = Vec::new();
426        let mut styled_nodes: FxHashSet<NodeId> = FxHashSet::default();
427
428        #[cfg(debug_assertions)]
429        assert_eq!(dirty.len(), FxHashSet::from_iter(&dirty).len());
430
431        hotpath::measure_block!("dirty run", {
432            for (node_id, flags) in dirty {
433                let element = self.elements.get(&node_id).unwrap();
434                let height_b = self.heights.get(&node_id).unwrap();
435
436                if flags.contains(DiffModifies::REORDER_LAYOUT) {
437                    self.layout
438                        .invalidate_with_reason(node_id, DirtyReason::Reorder);
439                }
440
441                if flags.contains(DiffModifies::INNER_LAYOUT) {
442                    self.layout
443                        .invalidate_with_reason(node_id, DirtyReason::InnerLayout);
444                }
445
446                if flags.contains(DiffModifies::LAYOUT) {
447                    self.layout.invalidate(node_id);
448                }
449
450                if !needs_render
451                    && (flags.intersects(
452                        DiffModifies::STYLE
453                            | DiffModifies::LAYER
454                            | DiffModifies::EFFECT
455                            | DiffModifies::TEXT_STYLE
456                            | DiffModifies::LAYOUT
457                            | DiffModifies::INNER_LAYOUT
458                            | DiffModifies::REORDER_LAYOUT,
459                    ))
460                {
461                    needs_render = true;
462                }
463
464                if !needs_accessibility && (flags.intersects(DiffModifies::ACCESSIBILITY)) {
465                    needs_accessibility = true;
466                }
467
468                if flags.intersects(DiffModifies::STYLE | DiffModifies::TEXT_STYLE)
469                    && self
470                        .listeners
471                        .get(&EventName::Styled)
472                        .is_some_and(|listeners| listeners.contains(&node_id))
473                {
474                    styled_nodes.insert(node_id);
475                }
476
477                if flags.contains(DiffModifies::ACCESSIBILITY) {
478                    match self.accessibility_state.get_mut(&node_id) {
479                        Some(accessibility_state) => accessibility_state.update(
480                            node_id,
481                            element,
482                            &mut self.accessibility_diff,
483                            &mut self.accessibility_groups,
484                        ),
485                        None => {
486                            self.accessibility_state.insert(
487                                node_id,
488                                AccessibilityState::create(
489                                    node_id,
490                                    element,
491                                    &mut self.accessibility_diff,
492                                    &self.accessibility_generator,
493                                    &mut self.accessibility_groups,
494                                ),
495                            );
496                        }
497                    }
498                }
499
500                let handle_cascade = |cascades: &mut Vec<NodeId>| {
501                    // Skip scanning if we already know this node is the a root
502                    if cascades.iter_mut().any(|root| {
503                        let height_a = self.heights.get(root).unwrap();
504
505                        match height_a.cmp(height_b) {
506                            std::cmp::Ordering::Less => {
507                                self.balance_heights(&node_id, root) == Some(*root)
508                            }
509                            std::cmp::Ordering::Greater => {
510                                let balanced_root = self.balance_heights(root, &node_id);
511                                match balanced_root {
512                                    Some(r) if r == node_id => {
513                                        // If this node is ascendant than the
514                                        // current root we set it as the new root
515                                        *root = node_id;
516                                        true
517                                    }
518                                    _ => false,
519                                }
520                            }
521                            std::cmp::Ordering::Equal => false,
522                        }
523                    }) {
524                        return;
525                    }
526                    cascades.push(node_id);
527                };
528
529                if flags.intersects(DiffModifies::LAYER) {
530                    handle_cascade(&mut layer_cascades);
531                }
532                if flags.intersects(DiffModifies::EFFECT | DiffModifies::LAYER) {
533                    let element = self.elements.get(&node_id).unwrap();
534                    // Has data or the parent has state
535                    let run_cascade = element.effect().is_some()
536                        || self
537                            .parents
538                            .get(&node_id)
539                            .map(|parent| self.effect_state.contains_key(parent))
540                            .unwrap_or_default();
541                    if run_cascade {
542                        handle_cascade(&mut effects_cascades);
543                    }
544                }
545                if flags.intersects(DiffModifies::TEXT_STYLE) {
546                    handle_cascade(&mut text_style_cascades);
547                }
548            }
549        });
550
551        hotpath::measure_block!("layer cascade", {
552            // Run the layer state
553            for layer_root in layer_cascades {
554                let mut buffer = VecDeque::new();
555                buffer.push_front(&layer_root);
556
557                while let Some(node_id) = buffer.pop_front() {
558                    let element = self.elements.get(node_id).unwrap();
559                    if let Some(parent_node_id) = self.parents.get(node_id) {
560                        let entries = self
561                            .layer_state
562                            .get_disjoint_entries([node_id, parent_node_id], |_id| {
563                                LayerState::default()
564                            });
565                        if let Some([layer_state, parent_layer_state]) = entries {
566                            layer_state.update(
567                                parent_layer_state,
568                                *node_id,
569                                element,
570                                &mut self.layers,
571                            );
572                        }
573                    } else {
574                        assert_eq!(*node_id, NodeId::ROOT);
575                        self.layer_state.insert(
576                            NodeId::ROOT,
577                            LayerState::create_for_root(*node_id, &mut self.layers),
578                        );
579                    }
580                    if let Some(children) = self.children.get(node_id) {
581                        buffer.extend(children);
582                    }
583                }
584            }
585        });
586
587        hotpath::measure_block!("effect cascade", {
588            // Run the effect state
589            for effect_root in effects_cascades {
590                let mut buffer = VecDeque::new();
591                buffer.push_front(&effect_root);
592
593                while let Some(node_id) = buffer.pop_front() {
594                    let element = self.elements.get(node_id).unwrap();
595                    if let Some(parent_node_id) = self.parents.get(node_id) {
596                        let entries = self.effect_state.get_disjoint_two_entries(
597                            parent_node_id,
598                            node_id,
599                            |_id| EffectState::default(),
600                            |left, _id| left.clone(),
601                        );
602                        if let [Some(parent_effect_state), Some(effect_state)] = entries {
603                            let effect_data = element.effect();
604                            let layer = element.layer();
605                            effect_state.update(
606                                *parent_node_id,
607                                parent_effect_state,
608                                *node_id,
609                                effect_data,
610                                layer,
611                            );
612                        }
613                    } else {
614                        assert_eq!(*node_id, NodeId::ROOT);
615                    }
616                    if let Some(children) = self.children.get(node_id) {
617                        buffer.extend(children);
618                    }
619                }
620            }
621        });
622
623        hotpath::measure_block!("text style cascade", {
624            // Run the text style state
625            for text_style_root in text_style_cascades {
626                let mut buffer = VecDeque::new();
627                buffer.push_front(&text_style_root);
628
629                while let Some(node_id) = buffer.pop_front() {
630                    let element = self.elements.get(node_id).unwrap();
631                    if let Some(parent_node_id) = self.parents.get(node_id) {
632                        let entries = self
633                            .text_style_state
634                            .get_disjoint_entries([node_id, parent_node_id], |_id| {
635                                TextStyleState::default()
636                            });
637                        if let Some([text_style_state, parent_text_style_state]) = entries {
638                            let changed = text_style_state.update(
639                                *node_id,
640                                parent_text_style_state,
641                                element,
642                                &mut self.layout,
643                            );
644                            if changed
645                                && self
646                                    .listeners
647                                    .get(&EventName::Styled)
648                                    .is_some_and(|listeners| listeners.contains(node_id))
649                            {
650                                styled_nodes.insert(*node_id);
651                            }
652                        }
653                    } else {
654                        assert_eq!(*node_id, NodeId::ROOT);
655                        self.text_style_state
656                            .insert(NodeId::ROOT, TextStyleState::default());
657                    }
658                    if let Some(children) = self.children.get(node_id) {
659                        buffer.extend(children);
660                    }
661                }
662            }
663
664            #[cfg(all(debug_assertions, feature = "debug-integrity"))]
665            self.verify_tree_integrity();
666        });
667
668        for node_id in styled_nodes {
669            let element = self.elements.get(&node_id).unwrap();
670            let text_style_state = self.text_style_state.get(&node_id).unwrap();
671            self.events.push(EmmitableEvent {
672                name: EventName::Styled,
673                source_event: EventName::Styled,
674                node_id,
675                data: EventType::Styled(StyledEventData {
676                    style: element.style().into_owned(),
677                    text_style: text_style_state.clone(),
678                }),
679                bubbles: false,
680            });
681        }
682
683        MutationsApplyResult {
684            needs_render,
685            needs_accessibility,
686            auto_focus: self.accessibility_diff.requested_auto_focus.take(),
687        }
688    }
689
690    /// Walk to the ancestor of `base` with the same height of `target`
691    fn balance_heights(&self, base: &NodeId, target: &NodeId) -> Option<NodeId> {
692        let target_height = self.heights.get(target)?;
693        let mut current = base;
694        loop {
695            if self.heights.get(current)? == target_height {
696                break;
697            }
698
699            let parent_current = self.parents.get(current);
700            if let Some(parent_current) = parent_current {
701                current = parent_current;
702            }
703        }
704        Some(*current)
705    }
706
707    #[allow(clippy::too_many_arguments)]
708    pub fn measure_layout(
709        &mut self,
710        size: Size2D,
711        font_collection: &mut FontCollection,
712        font_manager: &FontMgr,
713        events_sender: &UnboundedSender<EventsChunk>,
714        nodes_state: &mut NodesState<NodeId>,
715        scale_factor: f64,
716        fallback_fonts: &[Cow<'static, str>],
717    ) {
718        let tree_adapter = TreeAdapterFreya {
719            layout_nodes: &self.layout_nodes,
720            parents: &self.parents,
721            children: &self.children,
722            heights: &self.heights,
723        };
724
725        let layout_adapter = LayoutMeasurerAdapter {
726            elements: &self.elements,
727            text_style_state: &self.text_style_state,
728            font_collection,
729            font_manager,
730            events: &mut self.events,
731            scale_factor,
732            fallback_fonts,
733            text_cache: &mut self.text_cache,
734        };
735
736        self.layout.find_best_root(&tree_adapter);
737        self.layout.measure(
738            NodeId::ROOT,
739            Area::from_size(size),
740            &mut Some(layout_adapter),
741            &tree_adapter,
742        );
743        self.measure_visibility_events(nodes_state, scale_factor);
744        events_sender
745            .unbounded_send(EventsChunk::Batch(std::mem::take(&mut self.events)))
746            .unwrap();
747    }
748
749    /// Measure the Visible and Hidden listeners, emitting events for those whose visibility just changed.
750    fn measure_visibility_events(
751        &mut self,
752        nodes_state: &mut NodesState<NodeId>,
753        scale_factor: f64,
754    ) {
755        let visible_listeners = self.listeners.get(&EventName::Visible);
756        let hidden_listeners = self.listeners.get(&EventName::Hidden);
757        let listeners = visible_listeners
758            .into_iter()
759            .flatten()
760            .map(|node_id| (node_id, EventName::Visible))
761            .chain(
762                hidden_listeners
763                    .into_iter()
764                    .flatten()
765                    .map(|node_id| (node_id, EventName::Hidden)),
766            );
767
768        let mut visible_nodes = FxHashSet::default();
769        for (node_id, event_name) in listeners {
770            let Some(layout_node) = self.layout.get(node_id) else {
771                continue;
772            };
773            let is_visible = !layout_node.hidden
774                && self.effect_state.get(node_id).is_none_or(|effect_state| {
775                    effect_state.is_visible(&self.layout, &layout_node.area)
776                });
777            if is_visible {
778                visible_nodes.insert(*node_id);
779            }
780
781            let was_visible = nodes_state.is_visible(*node_id);
782            let just_changed = match event_name {
783                EventName::Visible => is_visible && !was_visible,
784                _ => !is_visible && was_visible,
785            };
786            if !just_changed {
787                continue;
788            }
789
790            let mut data = VisibleEventData::new(layout_node.area);
791            data.div(scale_factor as f32);
792            self.events.push(EmmitableEvent {
793                node_id: *node_id,
794                name: event_name,
795                data: EventType::Visible(data),
796                bubbles: false,
797                source_event: event_name,
798            });
799        }
800        nodes_state.set_visible_nodes(visible_nodes);
801    }
802
803    pub fn print_ascii(&self, node_id: NodeId, prefix: String, last: bool) {
804        let height = self.heights.get(&node_id).unwrap();
805        let layer = self.layer_state.get(&node_id).unwrap();
806
807        // Print current node
808        println!(
809            "{}{}{:?} [{}] ({})",
810            prefix,
811            if last { "└── " } else { "├── " },
812            node_id,
813            height,
814            layer.layer
815        );
816
817        // Get children
818        if let Some(children) = self.children.get(&node_id) {
819            let len = children.len();
820            for (i, child) in children.iter().enumerate() {
821                let is_last = i == len - 1;
822                // Extend prefix
823                let new_prefix = format!("{}{}", prefix, if last { "    " } else { "│   " });
824                self.print_ascii(*child, new_prefix, is_last);
825            }
826        }
827    }
828
829    #[cfg(all(debug_assertions, feature = "debug-integrity"))]
830    #[cfg_attr(feature = "hotpath", hotpath::measure)]
831    pub fn verify_tree_integrity(&self) {
832        let mut visited = FxHashSet::default();
833        let size = self.elements.len();
834        let mut buffer = vec![NodeId::ROOT];
835        while let Some(node_id) = buffer.pop() {
836            if visited.contains(&node_id) {
837                continue;
838            }
839            visited.insert(node_id);
840            if let Some(parent) = self.parents.get(&node_id) {
841                buffer.push(*parent);
842            }
843            if let Some(children) = self.children.get(&node_id) {
844                buffer.extend(children);
845            }
846        }
847        assert_eq!(size, visited.len());
848        assert_eq!(size, self.layout_nodes.len());
849    }
850}
851
852bitflags! {
853    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
854    pub struct DiffModifies: u32 {
855        const LAYOUT = 1 << 0;
856        const STYLE = 1 << 1;
857        const ACCESSIBILITY = 1 << 2;
858        const EVENT_HANDLERS = 1 << 3;
859        const LAYER = 1 << 4;
860        const TEXT_STYLE = 1 << 5;
861        const EFFECT = 1 << 6;
862        const INNER_LAYOUT = 1 << 7;
863        const REORDER_LAYOUT = 1 << 8;
864    }
865}
866
867pub struct MutationsApplyResult {
868    pub needs_render: bool,
869    pub needs_accessibility: bool,
870    pub auto_focus: Option<AccessibilityFocusStrategy>,
871}
872
873pub struct LayoutMeasurerAdapter<'a> {
874    pub font_collection: &'a mut FontCollection,
875    pub font_manager: &'a FontMgr,
876    elements: &'a FxHashMap<NodeId, Rc<dyn ElementExt>>,
877    text_style_state: &'a FxHashMap<NodeId, TextStyleState>,
878    events: &'a mut Vec<EmmitableEvent>,
879    scale_factor: f64,
880    fallback_fonts: &'a [Cow<'static, str>],
881    text_cache: &'a mut TextCache,
882}
883
884impl LayoutMeasurer<NodeId> for LayoutMeasurerAdapter<'_> {
885    fn measure(
886        &mut self,
887        node_id: NodeId,
888        torin_node: &torin::node::Node,
889        area_size: &Size2D,
890    ) -> Option<(Size2D, Rc<dyn Any>)> {
891        self.elements.get(&node_id)?.measure(LayoutContext {
892            node_id,
893            torin_node,
894            area_size,
895            font_collection: self.font_collection,
896            font_manager: self.font_manager,
897            text_style_state: self.text_style_state.get(&node_id).unwrap(),
898            scale_factor: self.scale_factor,
899            fallback_fonts: self.fallback_fonts,
900            text_cache: self.text_cache,
901        })
902    }
903
904    fn should_hook_measurement(&mut self, node_id: NodeId) -> bool {
905        if let Some(element) = self.elements.get(&node_id) {
906            element.should_hook_measurement()
907        } else {
908            false
909        }
910    }
911
912    fn should_measure_inner_children(&mut self, node_id: NodeId) -> bool {
913        if let Some(element) = self.elements.get(&node_id) {
914            element.should_measure_inner_children()
915        } else {
916            false
917        }
918    }
919
920    fn should_post_measure(&mut self, node_id: NodeId) -> bool {
921        self.elements
922            .get(&node_id)
923            .is_some_and(|element| element.needs_post_measure())
924    }
925
926    fn post_measure(
927        &mut self,
928        node_id: NodeId,
929        node_layout: &LayoutNode,
930        children: &[NodeId],
931        layout: &Torin<NodeId>,
932    ) -> PostMeasure<NodeId> {
933        self.elements
934            .get(&node_id)
935            .unwrap()
936            .post_measure(PostMeasureContext {
937                node_layout,
938                children,
939                layout,
940                font_collection: self.font_collection,
941                text_style_state: self.text_style_state.get(&node_id).unwrap(),
942                fallback_fonts: self.fallback_fonts,
943                scale_factor: self.scale_factor,
944            })
945    }
946
947    fn notify_layout_references(
948        &mut self,
949        node_id: NodeId,
950        area: Area,
951        visible_area: Area,
952        inner_sizes: Size2D,
953    ) {
954        let mut data = SizedEventData::new(area, visible_area, inner_sizes);
955        data.div(self.scale_factor as f32);
956        self.events.push(EmmitableEvent {
957            node_id,
958            name: EventName::Sized,
959            data: EventType::Sized(data),
960            bubbles: false,
961            source_event: EventName::Sized,
962        });
963    }
964}