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