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 layout_nodes: FxHashMap<NodeId, Node>,
92
93 pub listeners: FxHashMap<EventName, Vec<NodeId>>,
95
96 pub events: Vec<EmmitableEvent>,
98
99 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 pub layout: Torin<NodeId>,
107 pub layers: Layers,
108 pub text_cache: TextCache,
109
110 pub accessibility_groups: AccessibilityGroups,
112 pub accessibility_diff: AccessibilityDirtyNodes,
113 pub accessibility_generator: AccessibilityGenerator,
114}
115
116impl Tree {
117 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 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 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 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 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 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 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 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 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 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 let layer_state = self.layer_state.remove(&node_id).unwrap();
318 layer_state.remove(node_id, &mut self.layers);
319
320 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 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 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 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 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 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 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 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 *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 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 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 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 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 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 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 println!(
812 "{}{}{:?} [{}] ({})",
813 prefix,
814 if last { "└── " } else { "├── " },
815 node_id,
816 height,
817 layer.layer
818 );
819
820 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 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}