1use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::String, vec::Vec};
12use core::{
13 fmt,
14 hash::{Hash, Hasher},
15};
16
17use azul_css::{
18 css::Css,
19 props::{
20 basic::{StyleFontFamily, StyleFontFamilyVec, StyleFontSize},
21 property::{
22 BoxDecorationBreakValue, BreakInsideValue, CaretAnimationDurationValue,
23 CaretColorValue, ColumnCountValue, ColumnFillValue, ColumnRuleColorValue,
24 ColumnRuleStyleValue, ColumnRuleWidthValue, ColumnSpanValue, ColumnWidthValue,
25 ContentValue, CounterIncrementValue, CounterResetValue, CssProperty, CssPropertyType,
26 RelayoutScope,
27 FlowFromValue, FlowIntoValue, LayoutAlignContentValue, LayoutAlignItemsValue,
28 LayoutAlignSelfValue, LayoutBorderBottomWidthValue, LayoutBorderLeftWidthValue,
29 LayoutBorderRightWidthValue, LayoutBorderTopWidthValue, LayoutBoxSizingValue,
30 LayoutClearValue, LayoutColumnGapValue, LayoutDisplayValue, LayoutFlexBasisValue,
31 LayoutFlexDirectionValue, LayoutFlexGrowValue, LayoutFlexShrinkValue,
32 LayoutFlexWrapValue, LayoutFloatValue, LayoutGapValue, LayoutGridAutoColumnsValue,
33 LayoutGridAutoFlowValue, LayoutGridAutoRowsValue, LayoutGridColumnValue,
34 LayoutGridRowValue, LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
35 LayoutHeightValue, LayoutInsetBottomValue, LayoutJustifyContentValue,
36 LayoutJustifyItemsValue, LayoutJustifySelfValue, LayoutLeftValue,
37 LayoutMarginBottomValue, LayoutMarginLeftValue, LayoutMarginRightValue,
38 LayoutMarginTopValue, LayoutMaxHeightValue, LayoutMaxWidthValue, LayoutMinHeightValue,
39 LayoutMinWidthValue, LayoutOverflowValue, LayoutPaddingBottomValue,
40 LayoutPaddingLeftValue, LayoutPaddingRightValue, LayoutPaddingTopValue,
41 LayoutPositionValue, LayoutRightValue, LayoutRowGapValue, LayoutScrollbarWidthValue,
42 LayoutTextJustifyValue, LayoutTopValue, LayoutWidthValue, LayoutWritingModeValue,
43 LayoutZIndexValue, OrphansValue, PageBreakValue,
44 SelectionBackgroundColorValue, SelectionColorValue, ShapeImageThresholdValue,
45 ShapeMarginValue, ShapeOutsideValue, StringSetValue, StyleBackfaceVisibilityValue,
46 StyleBackgroundContentVecValue, StyleBackgroundPositionVecValue,
47 StyleBackgroundRepeatVecValue, StyleBackgroundSizeVecValue,
48 StyleBorderBottomColorValue, StyleBorderBottomLeftRadiusValue,
49 StyleBorderBottomRightRadiusValue, StyleBorderBottomStyleValue,
50 StyleBorderLeftColorValue, StyleBorderLeftStyleValue, StyleBorderRightColorValue,
51 StyleBorderRightStyleValue, StyleBorderTopColorValue, StyleBorderTopLeftRadiusValue,
52 StyleBorderTopRightRadiusValue, StyleBorderTopStyleValue, StyleBoxShadowValue,
53 StyleCursorValue, StyleDirectionValue, StyleFilterVecValue, StyleFontFamilyVecValue,
54 StyleFontSizeValue, StyleFontValue, StyleHyphensValue, StyleLetterSpacingValue,
55 StyleLineHeightValue, StyleMixBlendModeValue, StyleOpacityValue,
56 StylePerspectiveOriginValue, StyleScrollbarColorValue, StyleTabSizeValue,
57 StyleTextAlignValue, StyleTextColorValue, StyleTransformOriginValue,
58 StyleTransformVecValue, StyleVisibilityValue, StyleWhiteSpaceValue,
59 StyleWordSpacingValue, WidowsValue,
60 },
61 style::StyleTextColor,
62 },
63 AzString,
64};
65
66use crate::{
67 callbacks::Update,
68 dom::{Dom, DomId, NodeData, NodeDataVec, OptionTabIndex, TabIndex, TagId},
69 events::{RelayoutNodes, RestyleNodes},
70 id::{
71 Node, NodeDataContainer, NodeDataContainerRef, NodeDataContainerRefMut, NodeHierarchy,
72 NodeId,
73 },
74 menu::Menu,
75 prop_cache::{CssPropertyCache, CssPropertyCachePtr},
76 refany::RefAny,
77 resources::{Au, ImageCache, ImageRef, ImmediateFontId, RendererResources},
78 style::{
79 construct_html_cascade_tree, matches_html_element, rule_ends_with, CascadeInfo,
80 CascadeInfoVec,
81 },
82 FastBTreeSet, OrderedMap,
83};
84
85#[repr(C)]
86#[derive(Debug, Clone, PartialEq, Hash, PartialOrd, Eq, Ord)]
87pub struct ChangedCssProperty {
88 pub previous_state: StyledNodeState,
89 pub previous_prop: CssProperty,
90 pub current_state: StyledNodeState,
91 pub current_prop: CssProperty,
92}
93
94impl_option!(
95 ChangedCssProperty,
96 OptionChangedCssProperty,
97 copy = false,
98 [Debug, Clone, PartialEq, Hash, PartialOrd, Eq, Ord]
99);
100
101impl_vec!(ChangedCssProperty, ChangedCssPropertyVec, ChangedCssPropertyVecDestructor, ChangedCssPropertyVecDestructorType, ChangedCssPropertyVecSlice, OptionChangedCssProperty);
102impl_vec_debug!(ChangedCssProperty, ChangedCssPropertyVec);
103impl_vec_partialord!(ChangedCssProperty, ChangedCssPropertyVec);
104impl_vec_clone!(
105 ChangedCssProperty,
106 ChangedCssPropertyVec,
107 ChangedCssPropertyVecDestructor
108);
109impl_vec_partialeq!(ChangedCssProperty, ChangedCssPropertyVec);
110
111#[derive(Debug, Clone, PartialEq)]
113pub struct FocusChange {
114 pub lost_focus: Option<NodeId>,
116 pub gained_focus: Option<NodeId>,
118}
119
120#[derive(Debug, Clone, PartialEq)]
122pub struct HoverChange {
123 pub left_nodes: Vec<NodeId>,
125 pub entered_nodes: Vec<NodeId>,
127}
128
129#[derive(Debug, Clone, PartialEq)]
131pub struct ActiveChange {
132 pub deactivated: Vec<NodeId>,
134 pub activated: Vec<NodeId>,
136}
137
138#[derive(Debug, Clone, Default)]
140pub struct RestyleResult {
141 pub changed_nodes: RestyleNodes,
143 pub needs_layout: bool,
145 pub needs_display_list: bool,
147 pub gpu_only_changes: bool,
150 pub max_relayout_scope: RelayoutScope,
161}
162
163impl RestyleResult {
164 pub fn has_changes(&self) -> bool {
166 !self.changed_nodes.is_empty()
167 }
168
169 pub fn merge(&mut self, other: RestyleResult) {
171 for (node_id, changes) in other.changed_nodes {
172 self.changed_nodes.entry(node_id).or_default().extend(changes);
173 }
174 self.needs_layout = self.needs_layout || other.needs_layout;
175 self.needs_display_list = self.needs_display_list || other.needs_display_list;
176 self.gpu_only_changes = self.gpu_only_changes && other.gpu_only_changes;
177 if other.max_relayout_scope > self.max_relayout_scope {
179 self.max_relayout_scope = other.max_relayout_scope;
180 }
181 }
182}
183
184#[repr(C)]
189#[derive(Clone, Copy, PartialEq, Hash, PartialOrd, Eq, Ord, Default)]
190pub struct StyledNodeState {
191 pub hover: bool,
193 pub active: bool,
195 pub focused: bool,
197 pub disabled: bool,
199 pub checked: bool,
201 pub focus_within: bool,
203 pub visited: bool,
205 pub backdrop: bool,
207 pub dragging: bool,
209 pub drag_over: bool,
211}
212
213impl core::fmt::Debug for StyledNodeState {
214 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
215 let mut v = Vec::new();
216 if self.hover {
217 v.push("hover");
218 }
219 if self.active {
220 v.push("active");
221 }
222 if self.focused {
223 v.push("focused");
224 }
225 if self.disabled {
226 v.push("disabled");
227 }
228 if self.checked {
229 v.push("checked");
230 }
231 if self.focus_within {
232 v.push("focus_within");
233 }
234 if self.visited {
235 v.push("visited");
236 }
237 if self.backdrop {
238 v.push("backdrop");
239 }
240 if self.dragging {
241 v.push("dragging");
242 }
243 if self.drag_over {
244 v.push("drag_over");
245 }
246 if v.is_empty() {
247 v.push("normal");
248 }
249 write!(f, "{:?}", v)
250 }
251}
252
253impl StyledNodeState {
254 pub const fn new() -> Self {
256 StyledNodeState {
257 hover: false,
258 active: false,
259 focused: false,
260 disabled: false,
261 checked: false,
262 focus_within: false,
263 visited: false,
264 backdrop: false,
265 dragging: false,
266 drag_over: false,
267 }
268 }
269
270 pub fn has_state(&self, state_type: u8) -> bool {
272 match state_type {
273 0 => true, 1 => self.hover,
275 2 => self.active,
276 3 => self.focused,
277 4 => self.disabled,
278 5 => self.checked,
279 6 => self.focus_within,
280 7 => self.visited,
281 8 => self.backdrop,
282 9 => self.dragging,
283 10 => self.drag_over,
284 _ => false,
285 }
286 }
287
288 pub fn is_normal(&self) -> bool {
290 !self.hover
291 && !self.active
292 && !self.focused
293 && !self.disabled
294 && !self.checked
295 && !self.focus_within
296 && !self.visited
297 && !self.backdrop
298 && !self.dragging
299 && !self.drag_over
300 }
301
302 pub fn from_pseudo_state_flags(flags: &azul_css::dynamic_selector::PseudoStateFlags) -> Self {
304 StyledNodeState {
305 hover: flags.hover,
306 active: flags.active,
307 focused: flags.focused,
308 disabled: flags.disabled,
309 checked: flags.checked,
310 focus_within: flags.focus_within,
311 visited: flags.visited,
312 backdrop: flags.backdrop,
313 dragging: flags.dragging,
314 drag_over: flags.drag_over,
315 }
316 }
317}
318
319#[repr(C)]
321#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
322pub struct StyledNode {
323 pub styled_node_state: StyledNodeState,
325}
326
327impl_option!(
328 StyledNode,
329 OptionStyledNode,
330 copy = false,
331 [Debug, Clone, PartialEq, PartialOrd]
332);
333
334impl_vec!(StyledNode, StyledNodeVec, StyledNodeVecDestructor, StyledNodeVecDestructorType, StyledNodeVecSlice, OptionStyledNode);
335impl_vec_mut!(StyledNode, StyledNodeVec);
336impl_vec_debug!(StyledNode, StyledNodeVec);
337impl_vec_partialord!(StyledNode, StyledNodeVec);
338impl_vec_clone!(StyledNode, StyledNodeVec, StyledNodeVecDestructor);
339impl_vec_partialeq!(StyledNode, StyledNodeVec);
340
341impl StyledNodeVec {
342 pub fn as_container<'a>(&'a self) -> NodeDataContainerRef<'a, StyledNode> {
344 NodeDataContainerRef {
345 internal: self.as_ref(),
346 }
347 }
348 pub fn as_container_mut<'a>(&'a mut self) -> NodeDataContainerRefMut<'a, StyledNode> {
350 NodeDataContainerRefMut {
351 internal: self.as_mut(),
352 }
353 }
354}
355
356#[test]
357fn test_css_styling_with_nested_divs() {
358 let s = "
359 html, body, p {
360 margin: 0;
361 padding: 0;
362 }
363 #div1 {
364 border: solid black;
365 height: 2in;
366 position: absolute;
367 top: 1in;
368 width: 3in;
369 }
370 div div {
371 background: blue;
372 height: 1in;
373 position: fixed;
374 width: 1in;
375 }
376 ";
377
378 let css = azul_css::parser2::new_from_str(s);
379 let _styled_dom = Dom::create_body()
380 .with_children(
381 vec![Dom::create_div()
382 .with_ids_and_classes(
383 vec![crate::dom::IdOrClass::Id("div1".to_string().into())].into(),
384 )
385 .with_children(vec![Dom::create_div()].into())]
386 .into(),
387 )
388 .with_component_css(css.0);
389}
390
391#[test]
399fn test_recompute_preserves_hot_flag_has_background() {
400 use azul_css::compact_cache::HOT_FLAG_HAS_BACKGROUND;
401
402 let css_str = "
403 body { margin: 0; padding: 0; }
404 .painted { background: red; width: 100px; height: 100px; }
405 ";
406 let css = azul_css::parser2::new_from_str(css_str).0;
407
408 let mut dom = Dom::create_body().with_children(
409 vec![Dom::create_div().with_class("painted".to_string().into())].into(),
410 );
411 let mut styled = StyledDom::create(&mut dom, css);
412
413 let any_bg_frame1 = {
415 let cache = styled
416 .css_property_cache
417 .ptr
418 .compact_cache
419 .as_ref()
420 .expect("compact_cache populated by create_from_compact_dom");
421 (0..styled.node_hierarchy.as_ref().len())
422 .any(|i| cache.tier2_cold[i].hot_flags & HOT_FLAG_HAS_BACKGROUND != 0)
423 };
424 assert!(
425 any_bg_frame1,
426 "frame 1: expected HOT_FLAG_HAS_BACKGROUND on the .painted node",
427 );
428
429 styled.recompute_inheritance_and_compact_cache();
433
434 let any_bg_frame2 = {
435 let cache = styled
436 .css_property_cache
437 .ptr
438 .compact_cache
439 .as_ref()
440 .expect("compact_cache rebuilt by recompute_inheritance_and_compact_cache");
441 (0..styled.node_hierarchy.as_ref().len())
442 .any(|i| cache.tier2_cold[i].hot_flags & HOT_FLAG_HAS_BACKGROUND != 0)
443 };
444 assert!(
445 any_bg_frame2,
446 "frame ≥2 after recompute_inheritance_and_compact_cache: \
447 HOT_FLAG_HAS_BACKGROUND disappeared. The recompute path must \
448 use build_compact_cache_with_inheritance (not plain \
449 build_compact_cache) so apply_css_property_to_compact runs and \
450 populates hot_flags for the renderer's negative fast-paths.",
451 );
452}
453
454#[derive(Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
456pub struct StyleFontFamilyHash(pub u64);
457
458impl ::core::fmt::Debug for StyleFontFamilyHash {
459 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
460 write!(f, "StyleFontFamilyHash({})", self.0)
461 }
462}
463
464impl StyleFontFamilyHash {
465 pub fn new(family: &StyleFontFamily) -> Self {
467 use std::hash::Hasher;
468 let mut hasher = std::hash::DefaultHasher::new();
469 family.hash(&mut hasher);
470 Self(hasher.finish())
471 }
472}
473
474#[derive(Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
476pub struct StyleFontFamiliesHash(pub u64);
477
478impl ::core::fmt::Debug for StyleFontFamiliesHash {
479 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
480 write!(f, "StyleFontFamiliesHash({})", self.0)
481 }
482}
483
484impl StyleFontFamiliesHash {
485 pub fn new(families: &[StyleFontFamily]) -> Self {
487 use std::hash::Hasher;
488 let mut hasher = std::hash::DefaultHasher::new();
489 for f in families.iter() {
490 f.hash(&mut hasher);
491 }
492 Self(hasher.finish())
493 }
494}
495
496#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
538#[repr(C)]
539pub struct NodeHierarchyItemId {
540 inner: usize,
543}
544
545impl fmt::Debug for NodeHierarchyItemId {
546 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
547 match self.into_crate_internal() {
548 Some(n) => write!(f, "Some(NodeId({}))", n),
549 None => write!(f, "None"),
550 }
551 }
552}
553
554impl fmt::Display for NodeHierarchyItemId {
555 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
556 write!(f, "{:?}", self)
557 }
558}
559
560impl NodeHierarchyItemId {
561 pub const NONE: NodeHierarchyItemId = NodeHierarchyItemId { inner: 0 };
563
564 #[inline]
571 pub const fn from_raw(value: usize) -> Self {
572 Self { inner: value }
573 }
574
575 #[inline]
581 pub const fn into_raw(&self) -> usize {
582 self.inner
583 }
584}
585
586impl_option!(
587 NodeHierarchyItemId,
588 OptionNodeHierarchyItemId,
589 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
590);
591
592impl_vec!(NodeHierarchyItemId, NodeHierarchyItemIdVec, NodeHierarchyItemIdVecDestructor, NodeHierarchyItemIdVecDestructorType, NodeHierarchyItemIdVecSlice, OptionNodeHierarchyItemId);
593impl_vec_mut!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
594impl_vec_debug!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
595impl_vec_ord!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
596impl_vec_eq!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
597impl_vec_hash!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
598impl_vec_partialord!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
599impl_vec_clone!(NodeHierarchyItemId, NodeHierarchyItemIdVec, NodeHierarchyItemIdVecDestructor);
600impl_vec_partialeq!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
601
602impl NodeHierarchyItemId {
603 #[inline]
605 pub const fn into_crate_internal(&self) -> Option<NodeId> {
606 NodeId::from_usize(self.inner)
607 }
608
609 #[inline]
611 pub const fn from_crate_internal(t: Option<NodeId>) -> Self {
612 Self {
613 inner: NodeId::into_raw(&t),
614 }
615 }
616}
617
618impl From<Option<NodeId>> for NodeHierarchyItemId {
619 #[inline]
620 fn from(opt: Option<NodeId>) -> Self {
621 NodeHierarchyItemId::from_crate_internal(opt)
622 }
623}
624
625impl From<NodeHierarchyItemId> for Option<NodeId> {
626 #[inline]
627 fn from(id: NodeHierarchyItemId) -> Self {
628 id.into_crate_internal()
629 }
630}
631
632#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
633#[repr(C)]
634pub struct NodeHierarchyItem {
635 pub parent: usize,
636 pub previous_sibling: usize,
637 pub next_sibling: usize,
638 pub last_child: usize,
639}
640
641impl_option!(
642 NodeHierarchyItem,
643 OptionNodeHierarchyItem,
644 [Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash]
645);
646
647impl NodeHierarchyItem {
648 pub const fn zeroed() -> Self {
650 Self {
651 parent: 0,
652 previous_sibling: 0,
653 next_sibling: 0,
654 last_child: 0,
655 }
656 }
657}
658
659impl From<Node> for NodeHierarchyItem {
660 fn from(node: Node) -> NodeHierarchyItem {
661 NodeHierarchyItem {
662 parent: NodeId::into_raw(&node.parent),
663 previous_sibling: NodeId::into_raw(&node.previous_sibling),
664 next_sibling: NodeId::into_raw(&node.next_sibling),
665 last_child: NodeId::into_raw(&node.last_child),
666 }
667 }
668}
669
670impl NodeHierarchyItem {
671 pub fn parent_id(&self) -> Option<NodeId> {
673 NodeId::from_usize(self.parent)
674 }
675 pub fn previous_sibling_id(&self) -> Option<NodeId> {
677 NodeId::from_usize(self.previous_sibling)
678 }
679 pub fn next_sibling_id(&self) -> Option<NodeId> {
681 NodeId::from_usize(self.next_sibling)
682 }
683 pub fn first_child_id(&self, current_node_id: NodeId) -> Option<NodeId> {
685 self.last_child_id().map(|_| current_node_id + 1)
686 }
687 pub fn last_child_id(&self) -> Option<NodeId> {
689 NodeId::from_usize(self.last_child)
690 }
691}
692
693impl_vec!(NodeHierarchyItem, NodeHierarchyItemVec, NodeHierarchyItemVecDestructor, NodeHierarchyItemVecDestructorType, NodeHierarchyItemVecSlice, OptionNodeHierarchyItem);
694impl_vec_mut!(NodeHierarchyItem, NodeHierarchyItemVec);
695impl_vec_debug!(AzNode, NodeHierarchyItemVec);
696impl_vec_partialord!(AzNode, NodeHierarchyItemVec);
697impl_vec_clone!(
698 NodeHierarchyItem,
699 NodeHierarchyItemVec,
700 NodeHierarchyItemVecDestructor
701);
702impl_vec_partialeq!(AzNode, NodeHierarchyItemVec);
703
704impl NodeHierarchyItemVec {
705 pub fn as_container<'a>(&'a self) -> NodeDataContainerRef<'a, NodeHierarchyItem> {
707 NodeDataContainerRef {
708 internal: self.as_ref(),
709 }
710 }
711 pub fn as_container_mut<'a>(&'a mut self) -> NodeDataContainerRefMut<'a, NodeHierarchyItem> {
713 NodeDataContainerRefMut {
714 internal: self.as_mut(),
715 }
716 }
717}
718
719impl<'a> NodeDataContainerRef<'a, NodeHierarchyItem> {
720 #[inline]
722 pub fn subtree_len(&self, parent_id: NodeId) -> usize {
723 let self_item_index = parent_id.index();
724 let next_item_index = match self[parent_id].next_sibling_id() {
725 None => self.len(),
726 Some(s) => s.index(),
727 };
728 next_item_index - self_item_index - 1
729 }
730}
731
732#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
733#[repr(C)]
734pub struct ParentWithNodeDepth {
735 pub depth: usize,
736 pub node_id: NodeHierarchyItemId,
737}
738
739impl_option!(
740 ParentWithNodeDepth,
741 OptionParentWithNodeDepth,
742 [Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash]
743);
744
745impl core::fmt::Debug for ParentWithNodeDepth {
746 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
747 write!(
748 f,
749 "{{ depth: {}, node: {:?} }}",
750 self.depth,
751 self.node_id.into_crate_internal()
752 )
753 }
754}
755
756impl_vec!(ParentWithNodeDepth, ParentWithNodeDepthVec, ParentWithNodeDepthVecDestructor, ParentWithNodeDepthVecDestructorType, ParentWithNodeDepthVecSlice, OptionParentWithNodeDepth);
757impl_vec_mut!(ParentWithNodeDepth, ParentWithNodeDepthVec);
758impl_vec_debug!(ParentWithNodeDepth, ParentWithNodeDepthVec);
759impl_vec_partialord!(ParentWithNodeDepth, ParentWithNodeDepthVec);
760impl_vec_clone!(
761 ParentWithNodeDepth,
762 ParentWithNodeDepthVec,
763 ParentWithNodeDepthVecDestructor
764);
765impl_vec_partialeq!(ParentWithNodeDepth, ParentWithNodeDepthVec);
766
767#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
768#[repr(C)]
769pub struct TagIdToNodeIdMapping {
770 pub tag_id: TagId,
772 pub node_id: NodeHierarchyItemId,
774 pub tab_index: OptionTabIndex,
776}
777
778impl_option!(
779 TagIdToNodeIdMapping,
780 OptionTagIdToNodeIdMapping,
781 copy = false,
782 [Debug, Clone, PartialEq, Eq, Ord, PartialOrd]
783);
784
785impl_vec!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec, TagIdToNodeIdMappingVecDestructor, TagIdToNodeIdMappingVecDestructorType, TagIdToNodeIdMappingVecSlice, OptionTagIdToNodeIdMapping);
786impl_vec_mut!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec);
787impl_vec_debug!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec);
788impl_vec_partialord!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec);
789impl_vec_clone!(
790 TagIdToNodeIdMapping,
791 TagIdToNodeIdMappingVec,
792 TagIdToNodeIdMappingVecDestructor
793);
794impl_vec_partialeq!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec);
795
796#[derive(Debug, Clone, PartialEq, PartialOrd)]
797#[repr(C)]
798pub struct ContentGroup {
799 pub root: NodeHierarchyItemId,
802 pub children: ContentGroupVec,
804}
805
806impl_option!(
807 ContentGroup,
808 OptionContentGroup,
809 copy = false,
810 [Debug, Clone, PartialEq, PartialOrd]
811);
812
813impl_vec!(ContentGroup, ContentGroupVec, ContentGroupVecDestructor, ContentGroupVecDestructorType, ContentGroupVecSlice, OptionContentGroup);
814impl_vec_mut!(ContentGroup, ContentGroupVec);
815impl_vec_debug!(ContentGroup, ContentGroupVec);
816impl_vec_partialord!(ContentGroup, ContentGroupVec);
817impl_vec_clone!(ContentGroup, ContentGroupVec, ContentGroupVecDestructor);
818impl_vec_partialeq!(ContentGroup, ContentGroupVec);
819
820#[derive(Debug, PartialEq, Clone)]
821#[repr(C)]
822pub struct StyledDom {
823 pub root: NodeHierarchyItemId,
824 pub node_hierarchy: NodeHierarchyItemVec,
825 pub node_data: NodeDataVec,
826 pub styled_nodes: StyledNodeVec,
827 pub cascade_info: CascadeInfoVec,
828 pub nodes_with_window_callbacks: NodeHierarchyItemIdVec,
829 pub nodes_with_datasets: NodeHierarchyItemIdVec,
830 pub tag_ids_to_node_ids: TagIdToNodeIdMappingVec,
831 pub non_leaf_nodes: ParentWithNodeDepthVec,
832 pub css_property_cache: CssPropertyCachePtr,
833 pub dom_id: DomId,
835}
836impl_option!(
837 StyledDom,
838 OptionStyledDom,
839 copy = false,
840 [Debug, Clone, PartialEq]
841);
842
843impl Default for StyledDom {
844 fn default() -> Self {
845 let root_node: NodeHierarchyItem = Node::ROOT.into();
846 let root_node_id: NodeHierarchyItemId =
847 NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO));
848 Self {
849 root: root_node_id,
850 node_hierarchy: vec![root_node].into(),
851 node_data: vec![NodeData::create_body()].into(),
852 styled_nodes: vec![StyledNode::default()].into(),
853 cascade_info: vec![CascadeInfo {
854 index_in_parent: 0,
855 is_last_child: true,
856 }]
857 .into(),
858 tag_ids_to_node_ids: Vec::new().into(),
859 non_leaf_nodes: vec![ParentWithNodeDepth {
860 depth: 0,
861 node_id: root_node_id,
862 }]
863 .into(),
864 nodes_with_window_callbacks: Vec::new().into(),
865 nodes_with_datasets: Vec::new().into(),
866 css_property_cache: CssPropertyCachePtr::new(CssPropertyCache::empty(1)),
867 dom_id: DomId::ROOT_ID,
868 }
869 }
870}
871
872#[derive(Debug, Clone, Default)]
874pub struct StyledDomMemoryReport {
875 pub node_count: usize,
876 pub node_hierarchy_bytes: usize,
877 pub node_data_bytes: usize,
878 pub styled_nodes_bytes: usize,
879 pub cascade_info_bytes: usize,
880 pub tag_ids_bytes: usize,
881 pub non_leaf_nodes_bytes: usize,
882 pub callback_vecs_bytes: usize,
883 pub css_property_cache: crate::prop_cache::CssPropertyCacheBreakdown,
884}
885
886impl StyledDomMemoryReport {
887 pub fn total_bytes(&self) -> usize {
888 self.node_hierarchy_bytes
889 + self.node_data_bytes
890 + self.styled_nodes_bytes
891 + self.cascade_info_bytes
892 + self.tag_ids_bytes
893 + self.non_leaf_nodes_bytes
894 + self.callback_vecs_bytes
895 + self.css_property_cache.total_bytes()
896 }
897}
898
899impl StyledDom {
900 pub fn memory_report(&self) -> StyledDomMemoryReport {
902 let n = self.node_data.len();
903 StyledDomMemoryReport {
904 node_count: n,
905 node_hierarchy_bytes: self.node_hierarchy.as_ref().len()
906 * core::mem::size_of::<NodeHierarchyItem>(),
907 node_data_bytes: {
908 let base = n * core::mem::size_of::<crate::dom::NodeData>();
909 let mut inner = 0usize;
912 for nd in self.node_data.as_ref().iter() {
913 inner += nd.get_callbacks().len() * 64; inner += nd.style.rules.as_ref().len() * 64;
917 }
918 base + inner
919 },
920 styled_nodes_bytes: n * core::mem::size_of::<StyledNode>(),
921 cascade_info_bytes: n * core::mem::size_of::<CascadeInfo>(),
922 tag_ids_bytes: self.tag_ids_to_node_ids.as_ref().len()
923 * core::mem::size_of::<TagIdToNodeIdMapping>(),
924 non_leaf_nodes_bytes: self.non_leaf_nodes.as_ref().len()
925 * core::mem::size_of::<ParentWithNodeDepth>(),
926 callback_vecs_bytes:
927 self.nodes_with_window_callbacks.as_ref().len() * 8
928 + self.nodes_with_datasets.as_ref().len() * 8,
929 css_property_cache: self.css_property_cache.ptr.memory_breakdown(),
930 }
931 }
932
933 pub fn create(dom: &mut Dom, css: Css) -> Self {
940 use core::mem;
941
942 let mut swap_dom = Dom::create_body();
943 mem::swap(dom, &mut swap_dom);
944
945 let compact_dom: CompactDom = swap_dom.into();
946 let node_hierarchy: NodeHierarchyItemVec = compact_dom
947 .node_hierarchy
948 .as_ref()
949 .internal
950 .iter()
951 .map(|i| (*i).into())
952 .collect::<Vec<NodeHierarchyItem>>()
953 .into();
954
955 Self::create_from_compact_dom(compact_dom, css, node_hierarchy)
956 }
957
958 pub fn create_from_fast_dom(fast_dom: crate::dom::FastDom) -> Self {
964 use azul_css::css::Css;
965
966 let mut combined_rules: Vec<azul_css::css::CssRuleBlock> = Vec::new();
969 for css_with_id in fast_dom.css.into_library_owned_vec() {
970 combined_rules.extend(css_with_id.css.rules.into_library_owned_vec());
971 }
972 let combined_css = if combined_rules.is_empty() {
973 Css::empty()
974 } else {
975 Css::new(combined_rules)
976 };
977
978 let node_hierarchy_items = fast_dom.node_hierarchy;
981 let nodes: Vec<crate::id::Node> = node_hierarchy_items.as_ref()
982 .iter()
983 .map(|item| crate::id::Node {
984 parent: NodeId::from_usize(item.parent),
985 previous_sibling: NodeId::from_usize(item.previous_sibling),
986 next_sibling: NodeId::from_usize(item.next_sibling),
987 last_child: NodeId::from_usize(item.last_child),
988 })
989 .collect();
990 let node_hierarchy_internal = crate::id::NodeHierarchy { internal: nodes };
991
992 let node_data_vec = fast_dom.node_data.into_library_owned_vec();
994 let compact_dom = CompactDom {
995 node_hierarchy: node_hierarchy_internal,
996 node_data: crate::id::NodeDataContainer { internal: node_data_vec },
997 root: NodeId::ZERO,
998 };
999
1000 Self::create_from_compact_dom(compact_dom, combined_css, node_hierarchy_items)
1004 }
1005
1006 fn create_from_compact_dom(
1010 compact_dom: CompactDom,
1011 mut css: Css,
1012 node_hierarchy: NodeHierarchyItemVec,
1013 ) -> Self {
1014 use crate::dom::EventFilter;
1015
1016 static CASCADE_BREAKDOWN: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1017 let cascade_dbg = *CASCADE_BREAKDOWN.get_or_init(crate::profile::memory_enabled);
1018
1019 let node_count = compact_dom.len();
1020
1021 let non_leaf_nodes = compact_dom
1022 .node_hierarchy
1023 .as_ref()
1024 .get_parents_sorted_by_depth();
1025
1026 let mut styled_nodes = vec![
1027 StyledNode {
1028 styled_node_state: StyledNodeState::new()
1029 };
1030 node_count
1031 ];
1032
1033 let mut css_property_cache = CssPropertyCache::empty(compact_dom.node_data.len());
1034
1035 let html_tree = construct_html_cascade_tree(
1036 &compact_dom.node_hierarchy.as_ref(),
1037 &non_leaf_nodes[..],
1038 &compact_dom.node_data.as_ref(),
1039 );
1040
1041 let non_leaf_nodes = non_leaf_nodes
1042 .iter()
1043 .map(|(depth, node_id)| ParentWithNodeDepth {
1044 depth: *depth,
1045 node_id: NodeHierarchyItemId::from_crate_internal(Some(*node_id)),
1046 })
1047 .collect::<Vec<_>>();
1048
1049 let non_leaf_nodes: ParentWithNodeDepthVec = non_leaf_nodes.into();
1050
1051 let _restyle_tag_ids = css_property_cache.restyle(
1052 &mut css,
1053 &compact_dom.node_data.as_ref(),
1054 &node_hierarchy,
1055 &non_leaf_nodes,
1056 &html_tree.as_ref(),
1057 );
1058
1059 drop(css);
1063
1064 css_property_cache.apply_ua_css(compact_dom.node_data.as_ref().internal);
1072 css_property_cache.compute_inherited_values(
1073 node_hierarchy.as_container().internal,
1074 compact_dom.node_data.as_ref().internal,
1075 );
1076
1077 let prev_font_hashes: Vec<u64> = css_property_cache.compact_cache
1078 .as_ref()
1079 .map(|c| c.prev_font_hashes.clone())
1080 .unwrap_or_default();
1081 let compact = css_property_cache.build_compact_cache_with_inheritance(
1082 compact_dom.node_data.as_ref().internal,
1083 node_hierarchy.as_container().internal,
1084 &prev_font_hashes,
1085 );
1086 css_property_cache.compact_cache = Some(compact);
1087 let pre_prune = if cascade_dbg {
1088 Some(css_property_cache.memory_breakdown())
1089 } else { None };
1090 css_property_cache.prune_compact_normal_props();
1091 if let Some(pre) = pre_prune {
1092 let post = css_property_cache.memory_breakdown();
1093 eprintln!("[PRUNE] css_props {} → {} KiB cascaded {} → {} KiB (saved {} KiB)",
1094 pre.css_props_bytes / 1024, post.css_props_bytes / 1024,
1095 pre.cascaded_props_bytes / 1024, post.cascaded_props_bytes / 1024,
1096 (pre.total_bytes().saturating_sub(post.total_bytes())) / 1024);
1097 }
1098
1099 let tag_ids = css_property_cache.generate_tag_ids(
1100 &compact_dom.node_data.as_ref(),
1101 &node_hierarchy,
1102 );
1103
1104 if cascade_dbg {
1105 let bd = css_property_cache.memory_breakdown();
1106 eprintln!("[CASCADE] {} nodes cascaded_props={} KiB css_props={} KiB compact={} KiB computed={} KiB total={} KiB",
1107 node_count,
1108 bd.cascaded_props_bytes / 1024, bd.css_props_bytes / 1024,
1109 bd.compact_cache_bytes / 1024, bd.computed_values_bytes / 1024,
1110 bd.total_bytes() / 1024);
1111 }
1112
1113 let has_any_callbacks = compact_dom.node_data.as_ref().internal.iter()
1116 .any(|c| !c.get_callbacks().is_empty() || c.get_dataset().is_some());
1117
1118 let (nodes_with_window_callbacks, nodes_with_datasets) = if has_any_callbacks {
1119 let mut win_cbs = Vec::new();
1120 let mut datasets = Vec::new();
1121 for (node_id, c) in compact_dom.node_data.as_ref().internal.iter().enumerate() {
1122 let cbs = c.get_callbacks();
1123 let has_dataset = c.get_dataset().is_some();
1124 if !cbs.is_empty() || has_dataset {
1125 datasets.push(NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(node_id))));
1126 }
1127 for cb in cbs.iter() {
1128 if let EventFilter::Window(_) = cb.event {
1129 win_cbs.push(NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(node_id))));
1130 break;
1131 }
1132 }
1133 }
1134 (win_cbs, datasets)
1135 } else {
1136 (Vec::new(), Vec::new())
1137 };
1138 let mut styled_dom = StyledDom {
1139 root: NodeHierarchyItemId::from_crate_internal(Some(compact_dom.root)),
1140 node_hierarchy,
1141 node_data: compact_dom.node_data.internal.into(),
1142 cascade_info: html_tree.internal.into(),
1143 styled_nodes: styled_nodes.into(),
1144 tag_ids_to_node_ids: tag_ids.into(),
1145 nodes_with_window_callbacks: nodes_with_window_callbacks.into(),
1146 nodes_with_datasets: nodes_with_datasets.into(),
1147 non_leaf_nodes,
1148 css_property_cache: CssPropertyCachePtr::new(css_property_cache),
1149 dom_id: DomId::ROOT_ID,
1150 };
1151 #[cfg(feature = "table_layout")]
1152 if let Err(_e) = crate::dom_table::generate_anonymous_table_elements(&mut styled_dom) {
1153 }
1154
1155 styled_dom
1156 }
1157
1158 pub fn create_from_dom(mut dom: Dom) -> Self {
1169 use azul_css::css::Css;
1170
1171 let mut all_css = Vec::new();
1173 collect_css_from_dom(&dom, &mut all_css);
1174
1175 let mut combined_css = if all_css.is_empty() {
1177 Css::empty()
1178 } else {
1179 let mut combined_rules: Vec<azul_css::css::CssRuleBlock> = Vec::new();
1180 for css in all_css {
1181 combined_rules.extend(css.rules.into_library_owned_vec());
1182 }
1183 Css::new(combined_rules)
1184 };
1185
1186 strip_css_from_dom(&mut dom);
1189
1190 Self::create(&mut dom, combined_css)
1192 }
1193
1194 pub fn append_child(&mut self, other: Self) {
1197 let self_root_id = self.root.into_crate_internal().unwrap_or(NodeId::ZERO);
1198 let current_root_children_count = self_root_id
1199 .az_children(&self.node_hierarchy.as_container())
1200 .count();
1201 self.append_child_with_index(other, current_root_children_count);
1202 self.finalize_non_leaf_nodes();
1203 }
1204
1205 pub fn append_child_with_index(&mut self, mut other: Self, child_index: usize) {
1208 let self_len = self.node_hierarchy.as_ref().len();
1210 let other_len = other.node_hierarchy.as_ref().len();
1211 let self_root_id = self.root.into_crate_internal().unwrap_or(NodeId::ZERO);
1212 let other_root_id = other.root.into_crate_internal().unwrap_or(NodeId::ZERO);
1213
1214 other.cascade_info.as_mut()[other_root_id.index()].index_in_parent = child_index as u32;
1216 other.cascade_info.as_mut()[other_root_id.index()].is_last_child = true;
1217
1218 self.cascade_info.append(&mut other.cascade_info);
1219
1220 for other in other.node_hierarchy.as_mut().iter_mut() {
1222 if other.parent != 0 {
1223 other.parent += self_len;
1224 }
1225 if other.previous_sibling != 0 {
1226 other.previous_sibling += self_len;
1227 }
1228 if other.next_sibling != 0 {
1229 other.next_sibling += self_len;
1230 }
1231 if other.last_child != 0 {
1232 other.last_child += self_len;
1233 }
1234 }
1235
1236 other.node_hierarchy.as_container_mut()[other_root_id].parent =
1237 NodeId::into_raw(&Some(self_root_id));
1238 let current_last_child = self.node_hierarchy.as_container()[self_root_id].last_child_id();
1239 other.node_hierarchy.as_container_mut()[other_root_id].previous_sibling =
1240 NodeId::into_raw(¤t_last_child);
1241 if let Some(current_last) = current_last_child {
1242 if self.node_hierarchy.as_container_mut()[current_last]
1243 .next_sibling_id()
1244 .is_some()
1245 {
1246 self.node_hierarchy.as_container_mut()[current_last].next_sibling +=
1247 other_root_id.index() + other_len;
1248 } else {
1249 self.node_hierarchy.as_container_mut()[current_last].next_sibling =
1250 NodeId::into_raw(&Some(NodeId::new(self_len + other_root_id.index())));
1251 }
1252 }
1253 self.node_hierarchy.as_container_mut()[self_root_id].last_child =
1254 NodeId::into_raw(&Some(NodeId::new(self_len + other_root_id.index())));
1255
1256 self.node_hierarchy.append(&mut other.node_hierarchy);
1257 self.node_data.append(&mut other.node_data);
1258 self.styled_nodes.append(&mut other.styled_nodes);
1259 self.get_css_property_cache_mut()
1260 .append(other.get_css_property_cache_mut());
1261
1262 for tag_id_node_id in other.tag_ids_to_node_ids.iter_mut() {
1265 tag_id_node_id.node_id.inner += self_len;
1266 }
1267
1268 self.tag_ids_to_node_ids
1269 .append(&mut other.tag_ids_to_node_ids);
1270
1271 for nid in other.nodes_with_window_callbacks.iter_mut() {
1272 nid.inner += self_len;
1273 }
1274 self.nodes_with_window_callbacks
1275 .append(&mut other.nodes_with_window_callbacks);
1276
1277 for nid in other.nodes_with_datasets.iter_mut() {
1278 nid.inner += self_len;
1279 }
1280 self.nodes_with_datasets
1281 .append(&mut other.nodes_with_datasets);
1282
1283 if other_len != 1 {
1286 for other_non_leaf_node in other.non_leaf_nodes.iter_mut() {
1287 other_non_leaf_node.node_id.inner += self_len;
1288 other_non_leaf_node.depth += 1;
1289 }
1290 self.non_leaf_nodes.append(&mut other.non_leaf_nodes);
1291 }
1293 }
1294
1295 pub fn finalize_non_leaf_nodes(&mut self) {
1298 self.non_leaf_nodes.sort_by(|a, b| a.depth.cmp(&b.depth));
1299 }
1300
1301 pub fn with_child(mut self, other: Self) -> Self {
1303 self.append_child(other);
1304 self
1305 }
1306
1307 pub fn set_context_menu(&mut self, context_menu: Menu) {
1309 if let Some(root_id) = self.root.into_crate_internal() {
1310 self.node_data.as_container_mut()[root_id].set_context_menu(context_menu);
1311 }
1312 }
1313
1314 pub fn with_context_menu(mut self, context_menu: Menu) -> Self {
1316 self.set_context_menu(context_menu);
1317 self
1318 }
1319
1320 pub fn set_menu_bar(&mut self, menu_bar: Menu) {
1322 if let Some(root_id) = self.root.into_crate_internal() {
1323 self.node_data.as_container_mut()[root_id].set_menu_bar(menu_bar);
1324 }
1325 }
1326
1327 pub fn with_menu_bar(mut self, menu_bar: Menu) -> Self {
1329 self.set_menu_bar(menu_bar);
1330 self
1331 }
1332
1333 pub fn recompute_inheritance_and_compact_cache(&mut self) {
1348 let prev_font_hashes: Vec<u64> = self.css_property_cache
1356 .downcast_mut()
1357 .compact_cache
1358 .as_ref()
1359 .map(|c| c.prev_font_hashes.clone())
1360 .unwrap_or_default();
1361 let compact = self.css_property_cache
1362 .downcast_mut()
1363 .build_compact_cache_with_inheritance(
1364 self.node_data.as_container().internal,
1365 self.node_hierarchy.as_container().internal,
1366 &prev_font_hashes,
1367 );
1368 self.css_property_cache.downcast_mut().compact_cache = Some(compact);
1369 }
1370
1371 pub fn restyle(&mut self, mut css: Css) {
1373 let new_tag_ids = self.css_property_cache.downcast_mut().restyle(
1374 &mut css,
1375 &self.node_data.as_container(),
1376 &self.node_hierarchy,
1377 &self.non_leaf_nodes,
1378 &self.cascade_info.as_container(),
1379 );
1380
1381 self.css_property_cache
1383 .downcast_mut()
1384 .apply_ua_css(self.node_data.as_container().internal);
1385
1386 self.css_property_cache
1388 .downcast_mut()
1389 .compute_inherited_values(
1390 self.node_hierarchy.as_container().internal,
1391 self.node_data.as_container().internal,
1392 );
1393
1394 self.tag_ids_to_node_ids = new_tag_ids.into();
1395 }
1396
1397 #[inline]
1399 pub fn node_count(&self) -> usize {
1400 self.node_data.len()
1401 }
1402
1403 #[inline]
1405 pub fn get_css_property_cache<'a>(&'a self) -> &'a CssPropertyCache {
1406 &*self.css_property_cache.ptr
1407 }
1408
1409 #[inline]
1411 pub fn get_css_property_cache_mut<'a>(&'a mut self) -> &'a mut CssPropertyCache {
1412 &mut *self.css_property_cache.ptr
1413 }
1414
1415 #[inline]
1417 pub fn get_styled_node_state(&self, node_id: &NodeId) -> StyledNodeState {
1418 self.styled_nodes.as_container()[*node_id]
1419 .styled_node_state
1420 .clone()
1421 }
1422
1423 #[must_use]
1425 pub fn restyle_nodes_hover(
1426 &mut self,
1427 nodes: &[NodeId],
1428 new_hover_state: bool,
1429 ) -> RestyleNodes {
1430 self.restyle_nodes_state(
1431 nodes,
1432 new_hover_state,
1433 |state, val| state.hover = val,
1434 azul_css::dynamic_selector::PseudoStateType::Hover,
1435 )
1436 }
1437
1438 #[must_use]
1440 pub fn restyle_nodes_active(
1441 &mut self,
1442 nodes: &[NodeId],
1443 new_active_state: bool,
1444 ) -> RestyleNodes {
1445 self.restyle_nodes_state(
1446 nodes,
1447 new_active_state,
1448 |state, val| state.active = val,
1449 azul_css::dynamic_selector::PseudoStateType::Active,
1450 )
1451 }
1452
1453 #[must_use]
1455 pub fn restyle_nodes_focus(
1456 &mut self,
1457 nodes: &[NodeId],
1458 new_focus_state: bool,
1459 ) -> RestyleNodes {
1460 self.restyle_nodes_state(
1461 nodes,
1462 new_focus_state,
1463 |state, val| state.focused = val,
1464 azul_css::dynamic_selector::PseudoStateType::Focus,
1465 )
1466 }
1467
1468 fn restyle_nodes_state(
1470 &mut self,
1471 nodes: &[NodeId],
1472 new_state_value: bool,
1473 set_state: impl Fn(&mut StyledNodeState, bool),
1474 pseudo_state_type: azul_css::dynamic_selector::PseudoStateType,
1475 ) -> RestyleNodes {
1476 let old_node_states = nodes
1478 .iter()
1479 .map(|nid| {
1480 self.styled_nodes.as_container()[*nid]
1481 .styled_node_state
1482 .clone()
1483 })
1484 .collect::<Vec<_>>();
1485
1486 for nid in nodes.iter() {
1487 set_state(
1488 &mut self.styled_nodes.as_container_mut()[*nid].styled_node_state,
1489 new_state_value,
1490 );
1491 }
1492
1493 let css_property_cache = self.get_css_property_cache();
1494 let styled_nodes = self.styled_nodes.as_container();
1495 let node_data = self.node_data.as_container();
1496
1497 let v = nodes
1499 .iter()
1500 .zip(old_node_states.iter())
1501 .filter_map(|(node_id, old_node_state)| {
1502 let mut keys_normal: Vec<_> = CssPropertyCache::prop_types_for_state(
1503 css_property_cache.css_props.get_slice(node_id.index()),
1504 pseudo_state_type,
1505 ).collect();
1506 let mut keys_inherited: Vec<_> = CssPropertyCache::prop_types_for_state(
1507 css_property_cache.cascaded_props.get_slice(node_id.index()),
1508 pseudo_state_type,
1509 ).collect();
1510 let keys_inline: Vec<CssPropertyType> = {
1511 use azul_css::dynamic_selector::DynamicSelector;
1512 node_data[*node_id]
1513 .style
1514 .iter_inline_properties()
1515 .filter_map(|(prop, conds)| {
1516 let matches = conds.as_slice().iter().any(|c| {
1517 matches!(c, DynamicSelector::PseudoState(pst) if *pst == pseudo_state_type)
1518 });
1519 if matches {
1520 Some(prop.get_type())
1521 } else {
1522 None
1523 }
1524 })
1525 .collect()
1526 };
1527 let mut keys_inline_ref: Vec<_> = keys_inline.iter().collect();
1528
1529 keys_normal.append(&mut keys_inherited);
1530 keys_normal.append(&mut keys_inline_ref);
1531
1532 let node_properties_that_could_have_changed = keys_normal;
1533
1534 if node_properties_that_could_have_changed.is_empty() {
1535 return None;
1536 }
1537
1538 let new_node_state = &styled_nodes[*node_id].styled_node_state;
1539 let node_data = &node_data[*node_id];
1540
1541 let changes = node_properties_that_could_have_changed
1542 .into_iter()
1543 .filter_map(|prop| {
1544 let old = css_property_cache.get_property_slow(
1546 node_data,
1547 node_id,
1548 old_node_state,
1549 prop,
1550 );
1551 let new = css_property_cache.get_property_slow(
1552 node_data,
1553 node_id,
1554 new_node_state,
1555 prop,
1556 );
1557 if old == new {
1558 None
1559 } else {
1560 Some(ChangedCssProperty {
1561 previous_state: old_node_state.clone(),
1562 previous_prop: match old {
1563 None => CssProperty::auto(*prop),
1564 Some(s) => s.clone(),
1565 },
1566 current_state: new_node_state.clone(),
1567 current_prop: match new {
1568 None => CssProperty::auto(*prop),
1569 Some(s) => s.clone(),
1570 },
1571 })
1572 }
1573 })
1574 .collect::<Vec<_>>();
1575
1576 if changes.is_empty() {
1577 None
1578 } else {
1579 Some((*node_id, changes))
1580 }
1581 })
1582 .collect::<Vec<_>>();
1583
1584 v.into_iter().collect()
1585 }
1586
1587 #[must_use]
1601 pub fn restyle_on_state_change(
1602 &mut self,
1603 focus_changes: Option<FocusChange>,
1604 hover_changes: Option<HoverChange>,
1605 active_changes: Option<ActiveChange>,
1606 ) -> RestyleResult {
1607
1608 let mut result = RestyleResult::default();
1609 result.gpu_only_changes = true; let mut process_changes = |changes: RestyleNodes| {
1613 for (node_id, props) in changes {
1614 for change in &props {
1615 let prop_type = change.current_prop.get_type();
1616
1617 let scope = prop_type.relayout_scope(true);
1624
1625 if scope > result.max_relayout_scope {
1627 result.max_relayout_scope = scope;
1628 }
1629
1630 if scope != RelayoutScope::None {
1632 result.needs_layout = true;
1633 result.gpu_only_changes = false;
1634 }
1635
1636 if !prop_type.is_gpu_only_property() {
1638 result.gpu_only_changes = false;
1639 }
1640
1641 result.needs_display_list = true;
1643 }
1644
1645 result.changed_nodes.entry(node_id).or_default().extend(props);
1646 }
1647 };
1648
1649 if let Some(focus) = focus_changes {
1651 if let Some(old) = focus.lost_focus {
1652 let changes = self.restyle_nodes_focus(&[old], false);
1653 process_changes(changes);
1654 }
1655 if let Some(new) = focus.gained_focus {
1656 let changes = self.restyle_nodes_focus(&[new], true);
1657 process_changes(changes);
1658 }
1659 }
1660
1661 if let Some(hover) = hover_changes {
1663 if !hover.left_nodes.is_empty() {
1664 let changes = self.restyle_nodes_hover(&hover.left_nodes, false);
1665 process_changes(changes);
1666 }
1667 if !hover.entered_nodes.is_empty() {
1668 let changes = self.restyle_nodes_hover(&hover.entered_nodes, true);
1669 process_changes(changes);
1670 }
1671 }
1672
1673 if let Some(active) = active_changes {
1675 if !active.deactivated.is_empty() {
1676 let changes = self.restyle_nodes_active(&active.deactivated, false);
1677 process_changes(changes);
1678 }
1679 if !active.activated.is_empty() {
1680 let changes = self.restyle_nodes_active(&active.activated, true);
1681 process_changes(changes);
1682 }
1683 }
1684
1685 if result.changed_nodes.is_empty() {
1687 result.needs_display_list = false;
1688 result.gpu_only_changes = false;
1689 }
1690
1691 if result.needs_layout {
1693 result.needs_display_list = true;
1694 result.gpu_only_changes = false;
1695 }
1696
1697 result
1698 }
1699
1700 #[must_use]
1711 pub fn restyle_user_property(
1712 &mut self,
1713 node_id: &NodeId,
1714 new_properties: &[CssProperty],
1715 ) -> RestyleNodes {
1716 let mut map = BTreeMap::default();
1717
1718 if new_properties.is_empty() {
1719 return map;
1720 }
1721
1722 let node_count = self.node_data.as_ref().len();
1723 if node_id.index() >= node_count {
1724 return map;
1725 }
1726
1727 let node_data = self.node_data.as_container();
1728 let node_data = &node_data[*node_id];
1729
1730 let node_states = &self.styled_nodes.as_container();
1731 let old_node_state = &node_states[*node_id].styled_node_state;
1732
1733 let changes: Vec<ChangedCssProperty> = {
1734 let css_property_cache = self.get_css_property_cache();
1735
1736 new_properties
1737 .iter()
1738 .filter_map(|new_prop| {
1739 let old_prop = css_property_cache.get_property_slow(
1740 node_data,
1741 node_id,
1742 old_node_state,
1743 &new_prop.get_type(),
1744 );
1745
1746 let old_prop = match old_prop {
1747 None => CssProperty::auto(new_prop.get_type()),
1748 Some(s) => s.clone(),
1749 };
1750
1751 if old_prop == *new_prop {
1752 None
1753 } else {
1754 Some(ChangedCssProperty {
1755 previous_state: old_node_state.clone(),
1756 previous_prop: old_prop,
1757 current_state: old_node_state.clone(),
1759 current_prop: new_prop.clone(),
1760 })
1761 }
1762 })
1763 .collect()
1764 };
1765
1766 let css_property_cache_mut = self.get_css_property_cache_mut();
1767
1768 if css_property_cache_mut.user_overridden_properties.len() < node_count {
1773 css_property_cache_mut
1774 .user_overridden_properties
1775 .resize(node_count, Vec::new());
1776 }
1777
1778 for new_prop in new_properties.iter() {
1779 let prop_type = new_prop.get_type();
1780 let vec = &mut css_property_cache_mut
1781 .user_overridden_properties[node_id.index()];
1782 if new_prop.is_initial() {
1783 if let Ok(idx) = vec.binary_search_by_key(&prop_type, |(k, _)| *k) {
1785 vec.remove(idx);
1786 }
1787 } else {
1788 match vec.binary_search_by_key(&prop_type, |(k, _)| *k) {
1789 Ok(idx) => vec[idx].1 = new_prop.clone(),
1790 Err(idx) => vec.insert(idx, (prop_type, new_prop.clone())),
1791 }
1792 }
1793 }
1794
1795 if !changes.is_empty() {
1796 map.insert(*node_id, changes);
1797 }
1798
1799 map
1800 }
1801
1802 pub fn get_html_string(&self, custom_head: &str, custom_body: &str, test_mode: bool) -> String {
1812 let css_property_cache = self.get_css_property_cache();
1813
1814 let mut output = String::new();
1815
1816 let mut should_print_close_tag_after_node: BTreeMap<NodeId, Vec<(NodeId, usize)>> = BTreeMap::new();
1818
1819 let should_print_close_tag_debug = self
1820 .non_leaf_nodes
1821 .iter()
1822 .filter_map(|p| {
1823 let parent_node_id = p.node_id.into_crate_internal()?;
1824 let mut total_last_child = None;
1825 recursive_get_last_child(
1826 parent_node_id,
1827 &self.node_hierarchy.as_ref(),
1828 &mut total_last_child,
1829 );
1830 let total_last_child = total_last_child?;
1831 Some((parent_node_id, (total_last_child, p.depth)))
1832 })
1833 .collect::<BTreeMap<_, _>>();
1834
1835 for (parent_id, (last_child, parent_depth)) in should_print_close_tag_debug {
1836 should_print_close_tag_after_node
1837 .entry(last_child)
1838 .or_default()
1839 .push((parent_id, parent_depth));
1840 }
1841
1842 let mut all_node_depths = self
1843 .non_leaf_nodes
1844 .iter()
1845 .filter_map(|p| {
1846 let parent_node_id = p.node_id.into_crate_internal()?;
1847 Some((parent_node_id, p.depth))
1848 })
1849 .collect::<BTreeMap<_, _>>();
1850
1851 for (parent_node_id, parent_depth) in self
1852 .non_leaf_nodes
1853 .iter()
1854 .filter_map(|p| Some((p.node_id.into_crate_internal()?, p.depth)))
1855 {
1856 for child_id in parent_node_id.az_children(&self.node_hierarchy.as_container()) {
1857 all_node_depths.insert(child_id, parent_depth + 1);
1858 }
1859 }
1860
1861 for node_id in self.node_hierarchy.as_container().linear_iter() {
1862 let depth = all_node_depths[&node_id];
1863
1864 let node_data = &self.node_data.as_container()[node_id];
1865 let node_state = &self.styled_nodes.as_container()[node_id].styled_node_state;
1866 let tabs = String::from(" ").repeat(depth);
1867
1868 output.push_str("\r\n");
1869 output.push_str(&tabs);
1870 output.push_str(&node_data.debug_print_start(css_property_cache, &node_id, node_state));
1871
1872 if let Some(content) = node_data.get_node_type().format().as_ref() {
1873 output.push_str(content);
1874 }
1875
1876 let node_has_children = self.node_hierarchy.as_container()[node_id]
1877 .first_child_id(node_id)
1878 .is_some();
1879 if !node_has_children {
1880 let node_data = &self.node_data.as_container()[node_id];
1881 output.push_str(&node_data.debug_print_end());
1882 }
1883
1884 if let Some(close_tag_vec) = should_print_close_tag_after_node.get(&node_id) {
1885 let mut close_tag_vec = close_tag_vec.clone();
1886 close_tag_vec.sort_by(|a, b| b.1.cmp(&a.1)); for (close_tag_parent_id, close_tag_depth) in close_tag_vec {
1888 let node_data = &self.node_data.as_container()[close_tag_parent_id];
1889 let tabs = String::from(" ").repeat(close_tag_depth);
1890 output.push_str("\r\n");
1891 output.push_str(&tabs);
1892 output.push_str(&node_data.debug_print_end());
1893 }
1894 }
1895 }
1896
1897 if !test_mode {
1898 format!(
1899 "
1900 <html>
1901 <head>
1902 <style>* {{ margin:0px; padding:0px; }}</style>
1903 {custom_head}
1904 </head>
1905 {output}
1906 {custom_body}
1907 </html>
1908 "
1909 )
1910 } else {
1911 output
1912 }
1913 }
1914
1915 pub fn get_rects_in_rendering_order(&self) -> ContentGroup {
1917 Self::determine_rendering_order(
1918 &self.non_leaf_nodes.as_ref(),
1919 &self.node_hierarchy.as_container(),
1920 &self.styled_nodes.as_container(),
1921 &self.node_data.as_container(),
1922 &self.get_css_property_cache(),
1923 )
1924 }
1925
1926 fn determine_rendering_order<'a>(
1929 non_leaf_nodes: &[ParentWithNodeDepth],
1930 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
1931 styled_nodes: &NodeDataContainerRef<StyledNode>,
1932 node_data_container: &NodeDataContainerRef<NodeData>,
1933 css_property_cache: &CssPropertyCache,
1934 ) -> ContentGroup {
1935 let children_sorted = non_leaf_nodes
1936 .iter()
1937 .filter_map(|parent| {
1938 Some((
1939 parent.node_id,
1940 sort_children_by_position(
1941 parent.node_id.into_crate_internal()?,
1942 node_hierarchy,
1943 styled_nodes,
1944 node_data_container,
1945 css_property_cache,
1946 ),
1947 ))
1948 })
1949 .collect::<Vec<_>>();
1950
1951 let children_sorted: BTreeMap<NodeHierarchyItemId, Vec<NodeHierarchyItemId>> =
1952 children_sorted.into_iter().collect();
1953
1954 let mut root_content_group = ContentGroup {
1955 root: NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO)),
1956 children: Vec::new().into(),
1957 };
1958
1959 fill_content_group_children(&mut root_content_group, &children_sorted);
1960
1961 root_content_group
1962 }
1963
1964 pub fn swap_with_default(&mut self) -> Self {
1966 let mut new = Self::default();
1967 core::mem::swap(self, &mut new);
1968 new
1969 }
1970
1971}
1972
1973#[derive(Debug, PartialEq, PartialOrd, Eq)]
1975pub struct CompactDom {
1976 pub node_hierarchy: NodeHierarchy,
1978 pub node_data: NodeDataContainer<NodeData>,
1980 pub root: NodeId,
1982}
1983
1984impl CompactDom {
1985 #[inline(always)]
1987 pub fn len(&self) -> usize {
1988 self.node_hierarchy.as_ref().len()
1989 }
1990}
1991
1992impl From<Dom> for CompactDom {
1993 fn from(dom: Dom) -> Self {
1994 convert_dom_into_compact_dom(dom)
1995 }
1996}
1997
1998pub fn convert_dom_into_compact_dom(mut dom: Dom) -> CompactDom {
2000 fn convert_dom_into_compact_dom_internal(
2002 dom: &mut Dom,
2003 node_hierarchy: &mut [Node],
2004 node_data: &mut Vec<NodeData>,
2005 parent_node_id: NodeId,
2006 node: Node,
2007 cur_node_id: &mut usize,
2008 ) {
2009 node_hierarchy[parent_node_id.index()] = node.clone();
2020
2021 let copy = dom.root.copy_special();
2022
2023 node_data[parent_node_id.index()] = copy;
2024
2025 *cur_node_id += 1;
2026
2027 let mut previous_sibling_id = None;
2028 let children_len = dom.children.len();
2029 for (child_index, child_dom) in dom.children.as_mut().iter_mut().enumerate() {
2030 let child_node_id = NodeId::new(*cur_node_id);
2031 let is_last_child = (child_index + 1) == children_len;
2032 let child_dom_is_empty = child_dom.children.is_empty();
2033 let child_node = Node {
2034 parent: Some(parent_node_id),
2035 previous_sibling: previous_sibling_id,
2036 next_sibling: if is_last_child {
2037 None
2038 } else {
2039 Some(child_node_id + child_dom.estimated_total_children + 1)
2040 },
2041 last_child: if child_dom_is_empty {
2042 None
2043 } else {
2044 Some(child_node_id + child_dom.estimated_total_children)
2045 },
2046 };
2047 previous_sibling_id = Some(child_node_id);
2048 convert_dom_into_compact_dom_internal(
2050 child_dom,
2051 node_hierarchy,
2052 node_data,
2053 child_node_id,
2054 child_node,
2055 cur_node_id,
2056 );
2057 }
2058 }
2059
2060 let sum_nodes = dom.fixup_children_estimated();
2062
2063 let mut node_hierarchy = vec![Node::ROOT; sum_nodes + 1];
2064 let mut node_data = vec![NodeData::create_div(); sum_nodes + 1];
2065 let mut cur_node_id = 0;
2066
2067 let root_node_id = NodeId::ZERO;
2068 let root_node = Node {
2069 parent: None,
2070 previous_sibling: None,
2071 next_sibling: None,
2072 last_child: if dom.children.is_empty() {
2073 None
2074 } else {
2075 Some(root_node_id + dom.estimated_total_children)
2076 },
2077 };
2078
2079 convert_dom_into_compact_dom_internal(
2080 &mut dom,
2081 &mut node_hierarchy,
2082 &mut node_data,
2083 root_node_id,
2084 root_node,
2085 &mut cur_node_id,
2086 );
2087
2088 CompactDom {
2089 node_hierarchy: NodeHierarchy {
2090 internal: node_hierarchy,
2091 },
2092 node_data: NodeDataContainer {
2093 internal: node_data,
2094 },
2095 root: root_node_id,
2096 }
2097}
2098
2099fn collect_css_from_dom(dom: &Dom, out: &mut Vec<azul_css::css::Css>) {
2103 for child in dom.children.iter() {
2105 collect_css_from_dom(child, out);
2106 }
2107 for css in dom.css.iter() {
2109 out.push(css.clone());
2110 }
2111}
2112
2113fn strip_css_from_dom(dom: &mut Dom) {
2116 dom.css = Vec::new().into();
2117 for child in dom.children.as_mut().iter_mut() {
2118 strip_css_from_dom(child);
2119 }
2120}
2121
2122fn fill_content_group_children(
2123 group: &mut ContentGroup,
2124 children_sorted: &BTreeMap<NodeHierarchyItemId, Vec<NodeHierarchyItemId>>,
2125) {
2126 if let Some(c) = children_sorted.get(&group.root) {
2127 group.children = c
2129 .iter()
2130 .map(|child| ContentGroup {
2131 root: *child,
2132 children: Vec::new().into(),
2133 })
2134 .collect::<Vec<ContentGroup>>()
2135 .into();
2136
2137 for c in group.children.as_mut() {
2138 fill_content_group_children(c, children_sorted);
2139 }
2140 }
2141}
2142
2143fn sort_children_by_position<'a>(
2144 parent: NodeId,
2145 node_hierarchy: &NodeDataContainerRef<'a, NodeHierarchyItem>,
2146 rectangles: &NodeDataContainerRef<StyledNode>,
2147 node_data_container: &NodeDataContainerRef<NodeData>,
2148 css_property_cache: &CssPropertyCache,
2149) -> Vec<NodeHierarchyItemId> {
2150 use azul_css::props::layout::LayoutPosition::*;
2151
2152 let children_positions = parent
2153 .az_children(node_hierarchy)
2154 .map(|nid| {
2155 let position = css_property_cache
2156 .get_position(
2157 &node_data_container[nid],
2158 &nid,
2159 &rectangles[nid].styled_node_state,
2160 )
2161 .and_then(|p| p.clone().get_property_or_default())
2162 .unwrap_or_default();
2163 let id = NodeHierarchyItemId::from_crate_internal(Some(nid));
2164 (id, position)
2165 })
2166 .collect::<Vec<_>>();
2167
2168 let mut not_absolute_children = children_positions
2169 .iter()
2170 .filter_map(|(node_id, position)| {
2171 if *position != Absolute {
2172 Some(*node_id)
2173 } else {
2174 None
2175 }
2176 })
2177 .collect::<Vec<_>>();
2178
2179 let mut absolute_children = children_positions
2180 .iter()
2181 .filter_map(|(node_id, position)| {
2182 if *position == Absolute {
2183 Some(*node_id)
2184 } else {
2185 None
2186 }
2187 })
2188 .collect::<Vec<_>>();
2189
2190 not_absolute_children.append(&mut absolute_children);
2192 not_absolute_children
2193}
2194
2195fn recursive_get_last_child(
2198 node_id: NodeId,
2199 node_hierarchy: &[NodeHierarchyItem],
2200 target: &mut Option<NodeId>,
2201) {
2202 match node_hierarchy[node_id.index()].last_child_id() {
2203 None => return,
2204 Some(s) => {
2205 *target = Some(s);
2206 recursive_get_last_child(s, node_hierarchy, target);
2207 }
2208 }
2209}
2210
2211pub fn is_before_in_document_order(
2225 hierarchy: &NodeHierarchyItemVec,
2226 node_a: NodeId,
2227 node_b: NodeId,
2228) -> bool {
2229 if node_a == node_b {
2230 return false;
2231 }
2232
2233 let hierarchy = hierarchy.as_container();
2234
2235 let path_a = get_path_to_root(&hierarchy, node_a);
2237 let path_b = get_path_to_root(&hierarchy, node_b);
2238
2239 let min_len = path_a.len().min(path_b.len());
2241
2242 for i in 0..min_len {
2243 if path_a[i] != path_b[i] {
2244 let child_towards_a = path_a[i];
2246 let child_towards_b = path_b[i];
2247
2248 return child_towards_a.index() < child_towards_b.index();
2251 }
2252 }
2253
2254 path_a.len() < path_b.len()
2256}
2257
2258fn get_path_to_root(
2260 hierarchy: &NodeDataContainerRef<'_, NodeHierarchyItem>,
2261 node: NodeId,
2262) -> Vec<NodeId> {
2263 let mut path = Vec::new();
2264 let mut current = Some(node);
2265
2266 while let Some(node_id) = current {
2267 path.push(node_id);
2268 current = hierarchy.get(node_id).and_then(|h| h.parent_id());
2269 }
2270
2271 path.reverse();
2273 path
2274}
2275
2276pub fn collect_nodes_in_document_order(
2289 hierarchy: &NodeHierarchyItemVec,
2290 start_node: NodeId,
2291 end_node: NodeId,
2292) -> Vec<NodeId> {
2293 if start_node == end_node {
2294 return vec![start_node];
2295 }
2296
2297 let hierarchy_container = hierarchy.as_container();
2298 let hierarchy_slice = hierarchy.as_ref();
2299
2300 let mut result = Vec::new();
2301 let mut in_range = false;
2302
2303 let mut stack: Vec<NodeId> = vec![NodeId::ZERO]; while let Some(current) = stack.pop() {
2308 if current == start_node {
2310 in_range = true;
2311 }
2312
2313 if in_range {
2315 result.push(current);
2316 }
2317
2318 if current == end_node {
2320 break;
2321 }
2322
2323 if let Some(item) = hierarchy_container.get(current) {
2326 if let Some(first_child) = item.first_child_id(current) {
2328 let mut children = Vec::new();
2330 let mut child = Some(first_child);
2331 while let Some(child_id) = child {
2332 children.push(child_id);
2333 child = hierarchy_container.get(child_id).and_then(|h| h.next_sibling_id());
2334 }
2335 for child_id in children.into_iter().rev() {
2337 stack.push(child_id);
2338 }
2339 }
2340 }
2341 }
2342
2343 result
2344}
2345
2346pub fn is_layout_equivalent(old: &StyledDom, new: &StyledDom) -> bool {
2360 use crate::dom::NodeType;
2361 use crate::resources::DecodedImage;
2362
2363 let old_nodes = old.node_data.as_ref();
2365 let new_nodes = new.node_data.as_ref();
2366 if old_nodes.len() != new_nodes.len() {
2367 return false;
2368 }
2369
2370 let old_hier = old.node_hierarchy.as_ref();
2372 let new_hier = new.node_hierarchy.as_ref();
2373 if old_hier.len() != new_hier.len() {
2374 return false;
2375 }
2376 if old_hier != new_hier {
2377 return false;
2378 }
2379
2380 for (old_node, new_node) in old_nodes.iter().zip(new_nodes.iter()) {
2382
2383 if core::mem::discriminant(&old_node.node_type)
2385 != core::mem::discriminant(&new_node.node_type)
2386 {
2387 return false;
2388 }
2389
2390 match (&old_node.node_type, &new_node.node_type) {
2392 (NodeType::Image(old_img), NodeType::Image(new_img)) => {
2393 match (old_img.get_data(), new_img.get_data()) {
2394 (DecodedImage::Callback(old_cb), DecodedImage::Callback(new_cb)) => {
2395 if old_cb.callback.cb != new_cb.callback.cb {
2397 return false;
2398 }
2399 if old_cb.refany.get_type_id() != new_cb.refany.get_type_id() {
2401 return false;
2402 }
2403 }
2404 _ => {
2405 if old_img != new_img {
2407 return false;
2408 }
2409 }
2410 }
2411 }
2412 _ => {
2413 if old_node.node_type != new_node.node_type {
2414 return false;
2415 }
2416 }
2417 }
2418
2419 {
2421 use crate::dom::AttributeType;
2422 let old_ids_classes: Vec<_> = old_node.attributes().as_ref().iter()
2423 .filter(|a| matches!(a, AttributeType::Id(_) | AttributeType::Class(_)))
2424 .collect();
2425 let new_ids_classes: Vec<_> = new_node.attributes().as_ref().iter()
2426 .filter(|a| matches!(a, AttributeType::Id(_) | AttributeType::Class(_)))
2427 .collect();
2428 if old_ids_classes != new_ids_classes {
2429 return false;
2430 }
2431 }
2432
2433 if old_node.style != new_node.style {
2435 return false;
2436 }
2437
2438 let old_cbs = old_node.callbacks.as_ref();
2441 let new_cbs = new_node.callbacks.as_ref();
2442 if old_cbs.len() != new_cbs.len() {
2443 return false;
2444 }
2445 for (old_cb, new_cb) in old_cbs.iter().zip(new_cbs.iter()) {
2446 if old_cb.event != new_cb.event {
2447 return false;
2448 }
2449 }
2450
2451 if old_node.attributes().as_ref() != new_node.attributes().as_ref() {
2453 return false;
2454 }
2455 }
2456
2457 let old_styled = old.styled_nodes.as_ref();
2459 let new_styled = new.styled_nodes.as_ref();
2460 if old_styled.len() != new_styled.len() {
2461 return false;
2462 }
2463 if old_styled != new_styled {
2464 return false;
2465 }
2466
2467 true
2468}