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 FlowFromValue, FlowIntoValue, LayoutAlignContentValue, LayoutAlignItemsValue,
27 LayoutAlignSelfValue, LayoutBorderBottomWidthValue, LayoutBorderLeftWidthValue,
28 LayoutBorderRightWidthValue, LayoutBorderTopWidthValue, LayoutBoxSizingValue,
29 LayoutClearValue, LayoutColumnGapValue, LayoutDisplayValue, LayoutFlexBasisValue,
30 LayoutFlexDirectionValue, LayoutFlexGrowValue, LayoutFlexShrinkValue,
31 LayoutFlexWrapValue, LayoutFloatValue, LayoutGapValue, LayoutGridAutoColumnsValue,
32 LayoutGridAutoFlowValue, LayoutGridAutoRowsValue, LayoutGridColumnValue,
33 LayoutGridRowValue, LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
34 LayoutHeightValue, LayoutInsetBottomValue, LayoutJustifyContentValue,
35 LayoutJustifyItemsValue, LayoutJustifySelfValue, LayoutLeftValue,
36 LayoutMarginBottomValue, LayoutMarginLeftValue, LayoutMarginRightValue,
37 LayoutMarginTopValue, LayoutMaxHeightValue, LayoutMaxWidthValue, LayoutMinHeightValue,
38 LayoutMinWidthValue, LayoutOverflowValue, LayoutPaddingBottomValue,
39 LayoutPaddingLeftValue, LayoutPaddingRightValue, LayoutPaddingTopValue,
40 LayoutPositionValue, LayoutRightValue, LayoutRowGapValue, LayoutScrollbarWidthValue,
41 LayoutTextJustifyValue, LayoutTopValue, LayoutWidthValue, LayoutWritingModeValue,
42 LayoutZIndexValue, OrphansValue, PageBreakValue, RelayoutScope,
43 SelectionBackgroundColorValue, SelectionColorValue, ShapeImageThresholdValue,
44 ShapeMarginValue, ShapeOutsideValue, StringSetValue, StyleBackfaceVisibilityValue,
45 StyleBackgroundContentVecValue, StyleBackgroundPositionVecValue,
46 StyleBackgroundRepeatVecValue, StyleBackgroundSizeVecValue,
47 StyleBorderBottomColorValue, StyleBorderBottomLeftRadiusValue,
48 StyleBorderBottomRightRadiusValue, StyleBorderBottomStyleValue,
49 StyleBorderLeftColorValue, StyleBorderLeftStyleValue, StyleBorderRightColorValue,
50 StyleBorderRightStyleValue, StyleBorderTopColorValue, StyleBorderTopLeftRadiusValue,
51 StyleBorderTopRightRadiusValue, StyleBorderTopStyleValue, StyleBoxShadowValue,
52 StyleCursorValue, StyleDirectionValue, StyleFilterVecValue, StyleFontFamilyVecValue,
53 StyleFontSizeValue, StyleFontValue, StyleHyphensValue, StyleLetterSpacingValue,
54 StyleLineHeightValue, StyleMixBlendModeValue, StyleOpacityValue,
55 StylePerspectiveOriginValue, StyleScrollbarColorValue, StyleTabSizeValue,
56 StyleTextAlignValue, StyleTextColorValue, StyleTransformOriginValue,
57 StyleTransformVecValue, StyleVisibilityValue, StyleWhiteSpaceValue,
58 StyleWordSpacingValue, WidowsValue,
59 },
60 style::StyleTextColor,
61 },
62 AzString,
63};
64
65use crate::{
66 callbacks::Update,
67 dom::{Dom, DomId, NodeData, NodeDataVec, OptionTabIndex, TabIndex, TagId},
68 events::{RelayoutNodes, RestyleNodes},
69 id::{
70 Node, NodeDataContainer, NodeDataContainerRef, NodeDataContainerRefMut, NodeHierarchy,
71 NodeId,
72 },
73 menu::Menu,
74 prop_cache::{CssPropertyCache, CssPropertyCachePtr},
75 refany::RefAny,
76 resources::{Au, ImageCache, ImageRef, ImmediateFontId, RendererResources},
77 style::{
78 construct_html_cascade_tree, matches_html_element, rule_ends_with, CascadeInfo,
79 CascadeInfoVec,
80 },
81 FastBTreeSet, OrderedMap,
82};
83
84#[repr(C)]
85#[derive(Debug, Clone, PartialEq, Hash, PartialOrd, Eq, Ord)]
86pub struct ChangedCssProperty {
87 pub previous_state: StyledNodeState,
88 pub previous_prop: CssProperty,
89 pub current_state: StyledNodeState,
90 pub current_prop: CssProperty,
91}
92
93impl_option!(
94 ChangedCssProperty,
95 OptionChangedCssProperty,
96 copy = false,
97 [Debug, Clone, PartialEq, Hash, PartialOrd, Eq, Ord]
98);
99
100impl_vec!(
101 ChangedCssProperty,
102 ChangedCssPropertyVec,
103 ChangedCssPropertyVecDestructor,
104 ChangedCssPropertyVecDestructorType,
105 ChangedCssPropertyVecSlice,
106 OptionChangedCssProperty
107);
108impl_vec_debug!(ChangedCssProperty, ChangedCssPropertyVec);
109impl_vec_partialord!(ChangedCssProperty, ChangedCssPropertyVec);
110impl_vec_clone!(
111 ChangedCssProperty,
112 ChangedCssPropertyVec,
113 ChangedCssPropertyVecDestructor
114);
115impl_vec_partialeq!(ChangedCssProperty, ChangedCssPropertyVec);
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub struct FocusChange {
120 pub lost_focus: Option<NodeId>,
122 pub gained_focus: Option<NodeId>,
124}
125
126#[derive(Debug, Clone, PartialEq, Eq)]
128pub struct HoverChange {
129 pub left_nodes: Vec<NodeId>,
131 pub entered_nodes: Vec<NodeId>,
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct ActiveChange {
138 pub deactivated: Vec<NodeId>,
140 pub activated: Vec<NodeId>,
142}
143
144#[derive(Debug, Clone, Default)]
146pub struct RestyleResult {
147 pub changed_nodes: RestyleNodes,
149 pub needs_layout: bool,
151 pub needs_display_list: bool,
153 pub gpu_only_changes: bool,
156 pub max_relayout_scope: RelayoutScope,
167}
168
169impl RestyleResult {
170 #[must_use]
172 pub fn has_changes(&self) -> bool {
173 !self.changed_nodes.is_empty()
174 }
175
176 pub fn merge(&mut self, other: Self) {
178 for (node_id, changes) in other.changed_nodes {
179 self.changed_nodes
180 .entry(node_id)
181 .or_default()
182 .extend(changes);
183 }
184 self.needs_layout = self.needs_layout || other.needs_layout;
185 self.needs_display_list = self.needs_display_list || other.needs_display_list;
186 self.gpu_only_changes = self.gpu_only_changes && other.gpu_only_changes;
187 if other.max_relayout_scope > self.max_relayout_scope {
189 self.max_relayout_scope = other.max_relayout_scope;
190 }
191 }
192}
193
194#[repr(C)]
199#[derive(Clone, Copy, PartialEq, Hash, PartialOrd, Eq, Ord, Default)]
200pub struct StyledNodeState {
201 pub hover: bool,
203 pub active: bool,
205 pub focused: bool,
207 pub disabled: bool,
209 pub checked: bool,
211 pub focus_within: bool,
213 pub visited: bool,
215 pub backdrop: bool,
217 pub dragging: bool,
219 pub drag_over: bool,
221 pub placeholder: bool,
229 pub seat_focused: bool,
232}
233
234impl fmt::Debug for StyledNodeState {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 let mut v = Vec::new();
237 if self.hover {
238 v.push("hover");
239 }
240 if self.active {
241 v.push("active");
242 }
243 if self.focused {
244 v.push("focused");
245 }
246 if self.disabled {
247 v.push("disabled");
248 }
249 if self.checked {
250 v.push("checked");
251 }
252 if self.seat_focused {
253 v.push("seat_focused");
254 }
255 if self.focus_within {
256 v.push("focus_within");
257 }
258 if self.visited {
259 v.push("visited");
260 }
261 if self.backdrop {
262 v.push("backdrop");
263 }
264 if self.dragging {
265 v.push("dragging");
266 }
267 if self.drag_over {
268 v.push("drag_over");
269 }
270 if v.is_empty() {
271 v.push("normal");
272 }
273 write!(f, "{v:?}")
274 }
275}
276
277impl StyledNodeState {
278 #[must_use]
280 pub const fn new() -> Self {
281 Self {
282 hover: false,
283 active: false,
284 focused: false,
285 disabled: false,
286 checked: false,
287 focus_within: false,
288 visited: false,
289 backdrop: false,
290 dragging: false,
291 drag_over: false,
292 placeholder: false,
293 seat_focused: false,
294 }
295 }
296
297 #[must_use]
299 pub const fn has_state(&self, state_type: u8) -> bool {
300 match state_type {
301 0 => true, 1 => self.hover,
303 2 => self.active,
304 3 => self.focused,
305 4 => self.disabled,
306 5 => self.checked,
307 6 => self.focus_within,
308 7 => self.visited,
309 8 => self.backdrop,
310 9 => self.dragging,
311 10 => self.drag_over,
312 11 => self.seat_focused,
313 _ => false,
314 }
315 }
316
317 #[must_use]
319 pub const fn is_normal(&self) -> bool {
320 !self.hover
321 && !self.active
322 && !self.focused
323 && !self.disabled
324 && !self.checked
325 && !self.focus_within
326 && !self.visited
327 && !self.backdrop
328 && !self.dragging
329 && !self.drag_over
330 && !self.placeholder
335 && !self.seat_focused
336 }
337
338 #[must_use]
340 pub const fn from_pseudo_state_flags(
341 flags: &azul_css::dynamic_selector::PseudoStateFlags,
342 ) -> Self {
343 Self {
344 hover: flags.hover,
345 active: flags.active,
346 focused: flags.focused,
347 disabled: flags.disabled,
348 checked: flags.checked,
349 focus_within: flags.focus_within,
350 visited: flags.visited,
351 backdrop: flags.backdrop,
352 dragging: flags.dragging,
353 drag_over: flags.drag_over,
354 placeholder: flags.placeholder,
355 seat_focused: flags.seat_focused,
356 }
357 }
358}
359
360#[allow(missing_copy_implementations)]
365#[repr(C)]
366#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd)]
367pub struct StyledNode {
368 pub styled_node_state: StyledNodeState,
370}
371
372impl_option!(
373 StyledNode,
374 OptionStyledNode,
375 copy = false,
376 [Debug, Clone, PartialEq, Eq, PartialOrd]
377);
378
379impl_vec!(
380 StyledNode,
381 StyledNodeVec,
382 StyledNodeVecDestructor,
383 StyledNodeVecDestructorType,
384 StyledNodeVecSlice,
385 OptionStyledNode
386);
387impl_vec_mut!(StyledNode, StyledNodeVec);
388impl_vec_debug!(StyledNode, StyledNodeVec);
389impl_vec_partialord!(StyledNode, StyledNodeVec);
390impl_vec_clone!(StyledNode, StyledNodeVec, StyledNodeVecDestructor);
391impl_vec_partialeq!(StyledNode, StyledNodeVec);
392
393impl StyledNodeVec {
394 #[must_use]
396 pub fn as_container(&self) -> NodeDataContainerRef<'_, StyledNode> {
397 NodeDataContainerRef {
398 internal: self.as_ref(),
399 }
400 }
401 pub fn as_container_mut(&mut self) -> NodeDataContainerRefMut<'_, StyledNode> {
403 NodeDataContainerRefMut {
404 internal: self.as_mut(),
405 }
406 }
407}
408
409#[test]
410#[allow(clippy::used_underscore_binding)] fn test_css_styling_with_nested_divs() {
412 let s = "
413 html, body, p {
414 margin: 0;
415 padding: 0;
416 }
417 #div1 {
418 border: solid black;
419 height: 2in;
420 position: absolute;
421 top: 1in;
422 width: 3in;
423 }
424 div div {
425 background: blue;
426 height: 1in;
427 position: fixed;
428 width: 1in;
429 }
430 ";
431
432 let css = azul_css::parser2::new_from_str(s);
433 let mut _styled_dom = Dom::create_body().with_children(
434 vec![Dom::create_div()
435 .with_ids_and_classes(vec![crate::dom::IdOrClass::Id("div1".to_string().into())].into())
436 .with_children(vec![Dom::create_div()].into())]
437 .into(),
438 );
439 _styled_dom.add_component_css(css.0);
440}
441
442#[test]
450fn test_recompute_preserves_hot_flag_has_background() {
451 use azul_css::compact_cache::HOT_FLAG_HAS_BACKGROUND;
452
453 let css_str = "
454 body { margin: 0; padding: 0; }
455 .painted { background: red; width: 100px; height: 100px; }
456 ";
457 let css = azul_css::parser2::new_from_str(css_str).0;
458
459 let mut dom = Dom::create_body()
460 .with_children(vec![Dom::create_div().with_class("painted".to_string().into())].into());
461 let mut styled = StyledDom::create(&mut dom, css);
462
463 let any_bg_frame1 = {
465 let cache = styled
466 .css_property_cache
467 .ptr
468 .compact_cache
469 .as_ref()
470 .expect("compact_cache populated by create_from_compact_dom");
471 (0..styled.node_hierarchy.as_ref().len())
472 .any(|i| cache.tier2_cold[i].hot_flags & HOT_FLAG_HAS_BACKGROUND != 0)
473 };
474 assert!(
475 any_bg_frame1,
476 "frame 1: expected HOT_FLAG_HAS_BACKGROUND on the .painted node",
477 );
478
479 styled.recompute_inheritance_and_compact_cache();
483
484 let any_bg_frame2 = {
485 let cache = styled
486 .css_property_cache
487 .ptr
488 .compact_cache
489 .as_ref()
490 .expect("compact_cache rebuilt by recompute_inheritance_and_compact_cache");
491 (0..styled.node_hierarchy.as_ref().len())
492 .any(|i| cache.tier2_cold[i].hot_flags & HOT_FLAG_HAS_BACKGROUND != 0)
493 };
494 assert!(
495 any_bg_frame2,
496 "frame ≥2 after recompute_inheritance_and_compact_cache: \
497 HOT_FLAG_HAS_BACKGROUND disappeared. The recompute path must \
498 use build_compact_cache_with_inheritance (not plain \
499 build_compact_cache) so apply_css_property_to_compact runs and \
500 populates hot_flags for the renderer's negative fast-paths.",
501 );
502}
503
504#[derive(Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
506pub struct StyleFontFamilyHash(pub u64);
507
508impl ::core::fmt::Debug for StyleFontFamilyHash {
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 write!(f, "StyleFontFamilyHash({})", self.0)
511 }
512}
513
514impl StyleFontFamilyHash {
515 #[must_use]
517 pub fn new(family: &StyleFontFamily) -> Self {
518 use core::hash::Hasher;
519 let mut hasher = crate::hash::DefaultHasher::new();
520 family.hash(&mut hasher);
521 Self(hasher.finish())
522 }
523}
524
525#[derive(Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
527pub struct StyleFontFamiliesHash(pub u64);
528
529impl ::core::fmt::Debug for StyleFontFamiliesHash {
530 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531 write!(f, "StyleFontFamiliesHash({})", self.0)
532 }
533}
534
535impl StyleFontFamiliesHash {
536 #[must_use]
538 pub fn new(families: &[StyleFontFamily]) -> Self {
539 use core::hash::Hasher;
540 let mut hasher = crate::hash::DefaultHasher::new();
541 families.len().hash(&mut hasher);
545 for f in families {
546 f.hash(&mut hasher);
547 }
548 Self(hasher.finish())
549 }
550}
551
552#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
594#[repr(C)]
595pub struct NodeHierarchyItemId {
596 inner: usize,
599}
600
601impl fmt::Debug for NodeHierarchyItemId {
602 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
603 match self.into_crate_internal() {
604 Some(n) => write!(f, "Some(NodeId({n}))"),
605 None => write!(f, "None"),
606 }
607 }
608}
609
610impl fmt::Display for NodeHierarchyItemId {
611 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612 write!(f, "{self:?}")
613 }
614}
615
616impl NodeHierarchyItemId {
617 pub const NONE: Self = Self { inner: 0 };
619
620 #[inline]
627 #[must_use]
628 pub const fn from_raw(value: usize) -> Self {
629 Self { inner: value }
630 }
631
632 #[inline]
638 #[must_use]
639 pub const fn into_raw(&self) -> usize {
640 self.inner
641 }
642}
643
644impl_option!(
645 NodeHierarchyItemId,
646 OptionNodeHierarchyItemId,
647 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
648);
649
650impl_vec!(
651 NodeHierarchyItemId,
652 NodeHierarchyItemIdVec,
653 NodeHierarchyItemIdVecDestructor,
654 NodeHierarchyItemIdVecDestructorType,
655 NodeHierarchyItemIdVecSlice,
656 OptionNodeHierarchyItemId
657);
658impl_vec_mut!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
659impl_vec_debug!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
660impl_vec_ord!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
661impl_vec_eq!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
662impl_vec_hash!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
663impl_vec_partialord!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
664impl_vec_clone!(
665 NodeHierarchyItemId,
666 NodeHierarchyItemIdVec,
667 NodeHierarchyItemIdVecDestructor
668);
669impl_vec_partialeq!(NodeHierarchyItemId, NodeHierarchyItemIdVec);
670
671impl NodeHierarchyItemId {
672 #[inline]
674 #[must_use]
675 pub const fn into_crate_internal(&self) -> Option<NodeId> {
676 NodeId::from_usize(self.inner)
677 }
678
679 #[inline]
681 #[must_use]
682 pub const fn from_crate_internal(t: Option<NodeId>) -> Self {
683 Self {
684 inner: NodeId::into_raw(&t),
685 }
686 }
687}
688
689impl From<Option<NodeId>> for NodeHierarchyItemId {
690 #[inline]
691 fn from(opt: Option<NodeId>) -> Self {
692 Self::from_crate_internal(opt)
693 }
694}
695
696impl From<NodeHierarchyItemId> for Option<NodeId> {
697 #[inline]
698 fn from(id: NodeHierarchyItemId) -> Self {
699 id.into_crate_internal()
700 }
701}
702
703#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
704#[repr(C)]
705pub struct NodeHierarchyItem {
706 pub parent: usize,
707 pub previous_sibling: usize,
708 pub next_sibling: usize,
709 pub last_child: usize,
710}
711
712impl_option!(
713 NodeHierarchyItem,
714 OptionNodeHierarchyItem,
715 [Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash]
716);
717
718#[derive(Debug)]
720pub struct HierarchyAncestors<'a> {
721 hierarchy: &'a [NodeHierarchyItem],
722 cursor: Option<NodeId>,
723 budget: usize,
724}
725
726impl Iterator for HierarchyAncestors<'_> {
727 type Item = NodeId;
728
729 fn next(&mut self) -> Option<Self::Item> {
730 if self.budget == 0 {
731 return None;
732 }
733 let nid = self.cursor?;
734 self.budget -= 1;
735 self.cursor = self
736 .hierarchy
737 .get(nid.index())
738 .and_then(NodeHierarchyItem::parent_id);
739 Some(nid)
740 }
741}
742
743#[inline]
755pub fn hierarchy_ancestors(
756 hierarchy: &[NodeHierarchyItem],
757 node: NodeId,
758 inclusivity: crate::spaces::Inclusivity,
759) -> HierarchyAncestors<'_> {
760 let cursor = if inclusivity.includes_self() {
761 Some(node)
762 } else {
763 hierarchy
764 .get(node.index())
765 .and_then(NodeHierarchyItem::parent_id)
766 };
767 HierarchyAncestors {
768 hierarchy,
769 cursor,
770 budget: hierarchy.len(),
771 }
772}
773
774impl NodeHierarchyItem {
775 #[must_use]
777 pub const fn zeroed() -> Self {
778 Self {
779 parent: 0,
780 previous_sibling: 0,
781 next_sibling: 0,
782 last_child: 0,
783 }
784 }
785}
786
787impl From<Node> for NodeHierarchyItem {
788 fn from(node: Node) -> Self {
789 Self {
790 parent: NodeId::into_raw(&node.parent),
791 previous_sibling: NodeId::into_raw(&node.previous_sibling),
792 next_sibling: NodeId::into_raw(&node.next_sibling),
793 last_child: NodeId::into_raw(&node.last_child),
794 }
795 }
796}
797
798impl NodeHierarchyItem {
799 #[must_use]
801 pub const fn parent_id(&self) -> Option<NodeId> {
802 NodeId::from_usize(self.parent)
803 }
804 #[must_use]
806 pub const fn previous_sibling_id(&self) -> Option<NodeId> {
807 NodeId::from_usize(self.previous_sibling)
808 }
809 #[must_use]
811 pub const fn next_sibling_id(&self) -> Option<NodeId> {
812 NodeId::from_usize(self.next_sibling)
813 }
814 #[must_use]
816 pub fn first_child_id(&self, current_node_id: NodeId) -> Option<NodeId> {
817 self.last_child_id().map(|_| current_node_id + 1)
818 }
819 #[must_use]
821 pub const fn last_child_id(&self) -> Option<NodeId> {
822 NodeId::from_usize(self.last_child)
823 }
824}
825
826impl_vec!(
827 NodeHierarchyItem,
828 NodeHierarchyItemVec,
829 NodeHierarchyItemVecDestructor,
830 NodeHierarchyItemVecDestructorType,
831 NodeHierarchyItemVecSlice,
832 OptionNodeHierarchyItem
833);
834impl_vec_mut!(NodeHierarchyItem, NodeHierarchyItemVec);
835impl_vec_debug!(AzNode, NodeHierarchyItemVec);
836impl_vec_partialord!(AzNode, NodeHierarchyItemVec);
837impl_vec_clone!(
838 NodeHierarchyItem,
839 NodeHierarchyItemVec,
840 NodeHierarchyItemVecDestructor
841);
842impl_vec_partialeq!(AzNode, NodeHierarchyItemVec);
843
844impl NodeHierarchyItemVec {
845 #[must_use]
847 pub fn as_container(&self) -> NodeDataContainerRef<'_, NodeHierarchyItem> {
848 NodeDataContainerRef {
849 internal: self.as_ref(),
850 }
851 }
852 pub fn as_container_mut(&mut self) -> NodeDataContainerRefMut<'_, NodeHierarchyItem> {
854 NodeDataContainerRefMut {
855 internal: self.as_mut(),
856 }
857 }
858}
859
860impl NodeDataContainerRef<'_, NodeHierarchyItem> {
861 #[inline]
863 #[must_use]
864 pub fn subtree_len(&self, parent_id: NodeId) -> usize {
865 let self_item_index = parent_id.index();
866 let next_item_index = self[parent_id]
867 .next_sibling_id()
868 .map_or_else(|| self.len(), |s| s.index());
869 next_item_index
872 .saturating_sub(self_item_index)
873 .saturating_sub(1)
874 }
875}
876
877#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
878#[repr(C)]
879pub struct ParentWithNodeDepth {
880 pub depth: usize,
881 pub node_id: NodeHierarchyItemId,
882}
883
884impl_option!(
885 ParentWithNodeDepth,
886 OptionParentWithNodeDepth,
887 [Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash]
888);
889
890impl fmt::Debug for ParentWithNodeDepth {
891 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
892 write!(
893 f,
894 "{{ depth: {}, node: {:?} }}",
895 self.depth,
896 self.node_id.into_crate_internal()
897 )
898 }
899}
900
901impl_vec!(
902 ParentWithNodeDepth,
903 ParentWithNodeDepthVec,
904 ParentWithNodeDepthVecDestructor,
905 ParentWithNodeDepthVecDestructorType,
906 ParentWithNodeDepthVecSlice,
907 OptionParentWithNodeDepth
908);
909impl_vec_mut!(ParentWithNodeDepth, ParentWithNodeDepthVec);
910impl_vec_debug!(ParentWithNodeDepth, ParentWithNodeDepthVec);
911impl_vec_partialord!(ParentWithNodeDepth, ParentWithNodeDepthVec);
912impl_vec_clone!(
913 ParentWithNodeDepth,
914 ParentWithNodeDepthVec,
915 ParentWithNodeDepthVecDestructor
916);
917impl_vec_partialeq!(ParentWithNodeDepth, ParentWithNodeDepthVec);
918
919#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
920#[repr(C)]
921pub struct TagIdToNodeIdMapping {
922 pub tag_id: TagId,
924 pub node_id: NodeHierarchyItemId,
926 pub tab_index: OptionTabIndex,
928}
929
930impl_option!(
931 TagIdToNodeIdMapping,
932 OptionTagIdToNodeIdMapping,
933 copy = false,
934 [Debug, Clone, PartialEq, Eq, Ord, PartialOrd]
935);
936
937impl_vec!(
938 TagIdToNodeIdMapping,
939 TagIdToNodeIdMappingVec,
940 TagIdToNodeIdMappingVecDestructor,
941 TagIdToNodeIdMappingVecDestructorType,
942 TagIdToNodeIdMappingVecSlice,
943 OptionTagIdToNodeIdMapping
944);
945impl_vec_mut!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec);
946impl_vec_debug!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec);
947impl_vec_partialord!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec);
948impl_vec_clone!(
949 TagIdToNodeIdMapping,
950 TagIdToNodeIdMappingVec,
951 TagIdToNodeIdMappingVecDestructor
952);
953impl_vec_partialeq!(TagIdToNodeIdMapping, TagIdToNodeIdMappingVec);
954
955#[derive(Debug, Clone, PartialEq, PartialOrd)]
956#[repr(C)]
957pub struct ContentGroup {
958 pub root: NodeHierarchyItemId,
961 pub children: ContentGroupVec,
963}
964
965impl_option!(
966 ContentGroup,
967 OptionContentGroup,
968 copy = false,
969 [Debug, Clone, PartialEq, PartialOrd]
970);
971
972impl_vec!(
973 ContentGroup,
974 ContentGroupVec,
975 ContentGroupVecDestructor,
976 ContentGroupVecDestructorType,
977 ContentGroupVecSlice,
978 OptionContentGroup
979);
980impl_vec_mut!(ContentGroup, ContentGroupVec);
981impl_vec_debug!(ContentGroup, ContentGroupVec);
982impl_vec_partialord!(ContentGroup, ContentGroupVec);
983impl_vec_clone!(ContentGroup, ContentGroupVec, ContentGroupVecDestructor);
984impl_vec_partialeq!(ContentGroup, ContentGroupVec);
985
986#[derive(Debug, PartialEq, Clone)]
987#[repr(C)]
988pub struct StyledDom {
989 pub root: NodeHierarchyItemId,
990 pub node_hierarchy: NodeHierarchyItemVec,
991 pub node_data: NodeDataVec,
992 pub styled_nodes: StyledNodeVec,
993 pub cascade_info: CascadeInfoVec,
994 pub nodes_with_window_callbacks: NodeHierarchyItemIdVec,
995 pub nodes_with_datasets: NodeHierarchyItemIdVec,
996 pub tag_ids_to_node_ids: TagIdToNodeIdMappingVec,
997 pub non_leaf_nodes: ParentWithNodeDepthVec,
998 pub css_property_cache: CssPropertyCachePtr,
999 pub dom_id: DomId,
1001}
1002impl_option!(
1003 StyledDom,
1004 OptionStyledDom,
1005 copy = false,
1006 [Debug, Clone, PartialEq]
1007);
1008
1009impl Default for StyledDom {
1010 fn default() -> Self {
1011 let root_node: NodeHierarchyItem = Node::ROOT.into();
1012 let root_node_id: NodeHierarchyItemId =
1013 NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO));
1014 Self {
1015 root: root_node_id,
1016 node_hierarchy: vec![root_node].into(),
1017 node_data: vec![NodeData::create_body()].into(),
1018 styled_nodes: vec![StyledNode::default()].into(),
1019 cascade_info: vec![CascadeInfo {
1020 index_in_parent: 0,
1021 is_last_child: true,
1022 }]
1023 .into(),
1024 tag_ids_to_node_ids: Vec::new().into(),
1025 non_leaf_nodes: vec![ParentWithNodeDepth {
1026 depth: 0,
1027 node_id: root_node_id,
1028 }]
1029 .into(),
1030 nodes_with_window_callbacks: Vec::new().into(),
1031 nodes_with_datasets: Vec::new().into(),
1032 css_property_cache: CssPropertyCachePtr::new(CssPropertyCache::empty(1)),
1033 dom_id: DomId::ROOT_ID,
1034 }
1035 }
1036}
1037
1038#[derive(Debug, Clone, Copy, Default)]
1040pub struct StyledDomMemoryReport {
1041 pub node_count: usize,
1042 pub node_hierarchy_bytes: usize,
1043 pub node_data_bytes: usize,
1044 pub styled_nodes_bytes: usize,
1045 pub cascade_info_bytes: usize,
1046 pub tag_ids_bytes: usize,
1047 pub non_leaf_nodes_bytes: usize,
1048 pub callback_vecs_bytes: usize,
1049 pub css_property_cache: crate::prop_cache::CssPropertyCacheBreakdown,
1050}
1051
1052impl StyledDomMemoryReport {
1053 #[must_use]
1054 pub const fn total_bytes(&self) -> usize {
1055 self.node_hierarchy_bytes
1056 + self.node_data_bytes
1057 + self.styled_nodes_bytes
1058 + self.cascade_info_bytes
1059 + self.tag_ids_bytes
1060 + self.non_leaf_nodes_bytes
1061 + self.callback_vecs_bytes
1062 + self.css_property_cache.total_bytes()
1063 }
1064}
1065
1066#[cfg(feature = "std")]
1081pub(crate) fn cascade_trace(msg: impl FnOnce() -> String) {
1082 use std::sync::atomic::{AtomicUsize, Ordering};
1083 use std::sync::OnceLock;
1084 static ON: OnceLock<bool> = OnceLock::new();
1085 static N: AtomicUsize = AtomicUsize::new(0);
1086 if *ON.get_or_init(|| std::env::var("AZ_CASCADE_TRACE").is_ok()) {
1087 eprintln!("[cascade #{}] {}", N.fetch_add(1, Ordering::Relaxed) + 1, msg());
1088 }
1089}
1090
1091#[cfg(not(feature = "std"))]
1092pub(crate) fn cascade_trace(_: impl FnOnce() -> alloc::string::String) {}
1093
1094impl StyledDom {
1095 #[must_use]
1097 pub fn memory_report(&self) -> StyledDomMemoryReport {
1098 let n = self.node_data.len();
1099 StyledDomMemoryReport {
1100 node_count: n,
1101 node_hierarchy_bytes: size_of_val(self.node_hierarchy.as_ref()),
1102 node_data_bytes: {
1103 let base = n * size_of::<NodeData>();
1104 let mut inner = 0usize;
1107 for nd in self.node_data.as_ref() {
1108 inner += nd.get_callbacks().len() * 64; inner += nd.style.rules.as_ref().len() * 64;
1112 }
1113 base + inner
1114 },
1115 styled_nodes_bytes: n * size_of::<StyledNode>(),
1116 cascade_info_bytes: n * size_of::<CascadeInfo>(),
1117 tag_ids_bytes: size_of_val(self.tag_ids_to_node_ids.as_ref()),
1118 non_leaf_nodes_bytes: size_of_val(self.non_leaf_nodes.as_ref()),
1119 callback_vecs_bytes: self.nodes_with_window_callbacks.as_ref().len() * 8
1120 + self.nodes_with_datasets.as_ref().len() * 8,
1121 css_property_cache: self.css_property_cache.ptr.memory_breakdown(),
1122 }
1123 }
1124
1125 pub fn create(dom: &mut Dom, css: Css) -> Self {
1132 use core::mem;
1133
1134 let mut swap_dom = Dom::create_body();
1135 mem::swap(dom, &mut swap_dom);
1136
1137 swap_dom.fixup_children_estimated();
1148 let mut next_scope_id = 0usize;
1149 scope_inline_css(&mut swap_dom, &mut next_scope_id);
1150 let mut node_css: Vec<Css> = Vec::new();
1151 collect_css_from_dom(&swap_dom, &mut node_css);
1152 let css = if node_css.is_empty() {
1153 css
1154 } else {
1155 let mut combined_rules = css.rules.into_library_owned_vec();
1156 let mut combined_keyframes = css.keyframes.into_library_owned_vec();
1157 for c in node_css {
1158 combined_rules.extend(c.rules.into_library_owned_vec());
1159 combined_keyframes.extend(c.keyframes.into_library_owned_vec());
1160 }
1161 let mut merged = Css::new(combined_rules);
1162 merged.keyframes = combined_keyframes.into();
1163 merged
1164 };
1165 strip_css_from_dom(&mut swap_dom);
1166
1167 let compact_dom: CompactDom = swap_dom.into();
1168 let node_hierarchy: NodeHierarchyItemVec = compact_dom
1169 .node_hierarchy
1170 .as_ref()
1171 .internal
1172 .iter()
1173 .map(|i| (*i).into())
1174 .collect::<Vec<NodeHierarchyItem>>()
1175 .into();
1176
1177 Self::create_from_compact_dom(compact_dom, css, node_hierarchy)
1178 }
1179
1180 #[must_use]
1186 pub fn create_from_fast_dom(fast_dom: crate::dom::FastDom) -> Self {
1187 use azul_css::css::Css;
1188
1189 let mut combined_rules: Vec<azul_css::css::CssRuleBlock> = Vec::new();
1195 let mut combined_keyframes: Vec<azul_css::css::Keyframes> = Vec::new();
1196 let css_entries = fast_dom.css.into_library_owned_vec();
1197 {
1198 let hierarchy = fast_dom.node_hierarchy.as_container();
1199 for mut css_with_id in css_entries {
1200 combined_keyframes.extend(
1203 core::mem::take(&mut css_with_id.css.keyframes).into_library_owned_vec(),
1204 );
1205 let owner = css_with_id.node_id;
1206 let end = if owner < hierarchy.len() {
1207 owner + hierarchy.subtree_len(NodeId::new(owner))
1208 } else {
1209 owner
1210 };
1211 for mut rule in css_with_id.css.rules.into_library_owned_vec() {
1212 let node_only = rule.priority >= azul_css::css::rule_priority::INLINE;
1217 rule.path.push_front_scope_for(owner, end, node_only);
1218 combined_rules.push(rule);
1219 }
1220 }
1221 }
1222 let combined_css = if combined_rules.is_empty() && combined_keyframes.is_empty() {
1223 Css::empty()
1224 } else {
1225 let mut css = Css::new(combined_rules);
1226 css.keyframes = combined_keyframes.into();
1227 css
1228 };
1229
1230 let node_hierarchy_items = fast_dom.node_hierarchy;
1233 let nodes: Vec<Node> = node_hierarchy_items
1234 .as_ref()
1235 .iter()
1236 .map(|item| Node {
1237 parent: NodeId::from_usize(item.parent),
1238 previous_sibling: NodeId::from_usize(item.previous_sibling),
1239 next_sibling: NodeId::from_usize(item.next_sibling),
1240 last_child: NodeId::from_usize(item.last_child),
1241 })
1242 .collect();
1243 let node_hierarchy_internal = NodeHierarchy { internal: nodes };
1244
1245 let node_data_vec = fast_dom.node_data.into_library_owned_vec();
1247 let compact_dom = CompactDom {
1248 node_hierarchy: node_hierarchy_internal,
1249 node_data: NodeDataContainer {
1250 internal: node_data_vec,
1251 },
1252 root: NodeId::ZERO,
1253 };
1254
1255 Self::create_from_compact_dom(compact_dom, combined_css, node_hierarchy_items)
1259 }
1260
1261 #[allow(clippy::similar_names)] #[allow(clippy::too_many_lines)] fn create_from_compact_dom(
1267 compact_dom: CompactDom,
1268 mut css: Css,
1269 node_hierarchy: NodeHierarchyItemVec,
1270 ) -> Self {
1271 use crate::dom::EventFilter;
1272
1273 static CASCADE_BREAKDOWN: crate::sync::OnceLock<bool> = crate::sync::OnceLock::new();
1274 let cascade_dbg = *CASCADE_BREAKDOWN.get_or_init(crate::profile::memory_enabled);
1275
1276 let node_count = compact_dom.len();
1277
1278 let non_leaf_nodes = compact_dom
1279 .node_hierarchy
1280 .as_ref()
1281 .get_parents_sorted_by_depth();
1282
1283 let mut styled_nodes = vec![
1284 StyledNode {
1285 styled_node_state: StyledNodeState::new()
1286 };
1287 node_count
1288 ];
1289
1290 let mut css_property_cache = CssPropertyCache::empty(compact_dom.node_data.len());
1291
1292 let html_tree = construct_html_cascade_tree(
1293 &compact_dom.node_hierarchy.as_ref(),
1294 &non_leaf_nodes[..],
1295 &compact_dom.node_data.as_ref(),
1296 );
1297
1298 let non_leaf_nodes = non_leaf_nodes
1299 .iter()
1300 .map(|(depth, node_id)| ParentWithNodeDepth {
1301 depth: *depth,
1302 node_id: NodeHierarchyItemId::from_crate_internal(Some(*node_id)),
1303 })
1304 .collect::<Vec<_>>();
1305
1306 let non_leaf_nodes: ParentWithNodeDepthVec = non_leaf_nodes.into();
1307
1308 let _restyle_tag_ids = css_property_cache.restyle(
1309 &mut css,
1310 &compact_dom.node_data.as_ref(),
1311 &node_hierarchy,
1312 &non_leaf_nodes,
1313 &html_tree.as_ref(),
1314 );
1315
1316 css_property_cache.retained_author_css = css;
1321
1322 css_property_cache.apply_ua_css(compact_dom.node_data.as_ref().internal);
1330 css_property_cache.compute_inherited_values(
1331 node_hierarchy.as_container().internal,
1332 compact_dom.node_data.as_ref().internal,
1333 );
1334
1335 let prev_font_hashes: Vec<u64> = css_property_cache
1336 .compact_cache
1337 .as_ref()
1338 .map(|c| c.prev_font_hashes.clone())
1339 .unwrap_or_default();
1340 let compact = css_property_cache.build_compact_cache_with_inheritance(
1341 compact_dom.node_data.as_ref().internal,
1342 node_hierarchy.as_container().internal,
1343 &prev_font_hashes,
1344 );
1345 css_property_cache.compact_cache = Some(compact);
1346 let pre_prune = if cascade_dbg {
1347 Some(css_property_cache.memory_breakdown())
1348 } else {
1349 None
1350 };
1351 css_property_cache.prune_compact_normal_props();
1352 if let Some(pre) = pre_prune {
1353 let post = css_property_cache.memory_breakdown();
1354 #[cfg(feature = "std")]
1355 eprintln!(
1356 "[PRUNE] css_props {} → {} KiB cascaded {} → {} KiB (saved {} KiB)",
1357 pre.css_props_bytes / 1024,
1358 post.css_props_bytes / 1024,
1359 pre.cascaded_props_bytes / 1024,
1360 post.cascaded_props_bytes / 1024,
1361 (pre.total_bytes().saturating_sub(post.total_bytes())) / 1024
1362 );
1363 #[cfg(not(feature = "std"))]
1364 let _ = post;
1365 }
1366
1367 let tag_ids =
1368 css_property_cache.generate_tag_ids(&compact_dom.node_data.as_ref(), &node_hierarchy);
1369
1370 if cascade_dbg {
1371 let bd = css_property_cache.memory_breakdown();
1372 #[cfg(feature = "std")]
1373 eprintln!("[CASCADE] {} nodes cascaded_props={} KiB css_props={} KiB compact={} KiB computed={} KiB total={} KiB",
1374 node_count,
1375 bd.cascaded_props_bytes / 1024, bd.css_props_bytes / 1024,
1376 bd.compact_cache_bytes / 1024, bd.computed_values_bytes / 1024,
1377 bd.total_bytes() / 1024);
1378 #[cfg(not(feature = "std"))]
1379 let _ = bd;
1380 }
1381
1382 let has_any_callbacks = compact_dom
1385 .node_data
1386 .as_ref()
1387 .internal
1388 .iter()
1389 .any(|c| !c.get_callbacks().is_empty() || c.get_dataset().is_some());
1390
1391 let (nodes_with_window_callbacks, nodes_with_datasets) = if has_any_callbacks {
1392 let mut win_cbs = Vec::new();
1393 let mut datasets = Vec::new();
1394 for (node_id, c) in compact_dom.node_data.as_ref().internal.iter().enumerate() {
1395 let cbs = c.get_callbacks();
1396 let has_dataset = c.get_dataset().is_some();
1397 if !cbs.is_empty() || has_dataset {
1398 datasets.push(NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(
1399 node_id,
1400 ))));
1401 }
1402 for cb in cbs {
1403 if let EventFilter::Window(_) = cb.event {
1404 win_cbs.push(NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(
1405 node_id,
1406 ))));
1407 break;
1408 }
1409 }
1410 }
1411 (win_cbs, datasets)
1412 } else {
1413 (Vec::new(), Vec::new())
1414 };
1415 let mut styled_dom = Self {
1416 root: NodeHierarchyItemId::from_crate_internal(Some(compact_dom.root)),
1417 node_hierarchy,
1418 node_data: compact_dom.node_data.internal.into(),
1419 cascade_info: html_tree.internal.into(),
1420 styled_nodes: styled_nodes.into(),
1421 tag_ids_to_node_ids: tag_ids.into(),
1422 nodes_with_window_callbacks: nodes_with_window_callbacks.into(),
1423 nodes_with_datasets: nodes_with_datasets.into(),
1424 non_leaf_nodes,
1425 css_property_cache: CssPropertyCachePtr::new(css_property_cache),
1426 dom_id: DomId::ROOT_ID,
1427 };
1428 #[cfg(feature = "table_layout")]
1429 if let Err(_e) = crate::dom_table::generate_anonymous_table_elements(&mut styled_dom) {}
1430
1431 styled_dom
1432 }
1433
1434 #[must_use]
1445 pub fn create_from_dom(mut dom: Dom) -> Self {
1446 use azul_css::css::Css;
1447
1448 dom.fixup_children_estimated();
1453 let mut next_scope_id = 0usize;
1454 scope_inline_css(&mut dom, &mut next_scope_id);
1455
1456 let mut all_css = Vec::new();
1458 collect_css_from_dom(&dom, &mut all_css);
1459
1460 let mut combined_css = if all_css.is_empty() {
1462 Css::empty()
1463 } else {
1464 let mut combined_rules: Vec<azul_css::css::CssRuleBlock> = Vec::new();
1465 let mut combined_keyframes: Vec<azul_css::css::Keyframes> = Vec::new();
1466 for css in all_css {
1467 combined_rules.extend(css.rules.into_library_owned_vec());
1468 combined_keyframes.extend(css.keyframes.into_library_owned_vec());
1469 }
1470 let mut css = Css::new(combined_rules);
1471 css.keyframes = combined_keyframes.into();
1472 css
1473 };
1474
1475 strip_css_from_dom(&mut dom);
1478
1479 Self::create(&mut dom, combined_css)
1481 }
1482
1483 pub fn append_child(&mut self, other: Self) {
1486 let self_root_id = self.root.into_crate_internal().unwrap_or(NodeId::ZERO);
1487 let current_root_children_count = self_root_id
1488 .az_children(&self.node_hierarchy.as_container())
1489 .count();
1490 self.append_child_with_index(other, current_root_children_count);
1491 self.finalize_non_leaf_nodes();
1492 }
1493
1494 pub fn append_child_with_index(&mut self, mut other: Self, child_index: usize) {
1497 let self_len = self.node_hierarchy.as_ref().len();
1499 let other_len = other.node_hierarchy.as_ref().len();
1500 let self_root_id = self.root.into_crate_internal().unwrap_or(NodeId::ZERO);
1501 let other_root_id = other.root.into_crate_internal().unwrap_or(NodeId::ZERO);
1502
1503 other.cascade_info.as_mut()[other_root_id.index()].index_in_parent =
1505 u32::try_from(child_index).unwrap_or(u32::MAX);
1506 other.cascade_info.as_mut()[other_root_id.index()].is_last_child = true;
1507
1508 self.cascade_info.append(&mut other.cascade_info);
1509
1510 for other in other.node_hierarchy.as_mut().iter_mut() {
1512 if other.parent != 0 {
1513 other.parent += self_len;
1514 }
1515 if other.previous_sibling != 0 {
1516 other.previous_sibling += self_len;
1517 }
1518 if other.next_sibling != 0 {
1519 other.next_sibling += self_len;
1520 }
1521 if other.last_child != 0 {
1522 other.last_child += self_len;
1523 }
1524 }
1525
1526 other.node_hierarchy.as_container_mut()[other_root_id].parent =
1527 NodeId::into_raw(&Some(self_root_id));
1528 let current_last_child = self.node_hierarchy.as_container()[self_root_id].last_child_id();
1529 other.node_hierarchy.as_container_mut()[other_root_id].previous_sibling =
1530 NodeId::into_raw(¤t_last_child);
1531 if let Some(current_last) = current_last_child {
1532 if self.node_hierarchy.as_container_mut()[current_last]
1533 .next_sibling_id()
1534 .is_some()
1535 {
1536 self.node_hierarchy.as_container_mut()[current_last].next_sibling +=
1537 other_root_id.index() + other_len;
1538 } else {
1539 self.node_hierarchy.as_container_mut()[current_last].next_sibling =
1540 NodeId::into_raw(&Some(NodeId::new(self_len + other_root_id.index())));
1541 }
1542 }
1543 self.node_hierarchy.as_container_mut()[self_root_id].last_child =
1544 NodeId::into_raw(&Some(NodeId::new(self_len + other_root_id.index())));
1545
1546 self.node_hierarchy.append(&mut other.node_hierarchy);
1547 self.node_data.append(&mut other.node_data);
1548 self.styled_nodes.append(&mut other.styled_nodes);
1549 self.get_css_property_cache_mut()
1550 .append(other.get_css_property_cache_mut());
1551
1552 for tag_id_node_id in &mut other.tag_ids_to_node_ids {
1555 tag_id_node_id.node_id.inner += self_len;
1556 }
1557
1558 self.tag_ids_to_node_ids
1559 .append(&mut other.tag_ids_to_node_ids);
1560
1561 for nid in &mut other.nodes_with_window_callbacks {
1562 nid.inner += self_len;
1563 }
1564 self.nodes_with_window_callbacks
1565 .append(&mut other.nodes_with_window_callbacks);
1566
1567 for nid in &mut other.nodes_with_datasets {
1568 nid.inner += self_len;
1569 }
1570 self.nodes_with_datasets
1571 .append(&mut other.nodes_with_datasets);
1572
1573 if other_len != 1 {
1576 for other_non_leaf_node in &mut other.non_leaf_nodes {
1577 other_non_leaf_node.node_id.inner += self_len;
1578 other_non_leaf_node.depth += 1;
1579 }
1580 self.non_leaf_nodes.append(&mut other.non_leaf_nodes);
1581 }
1583 }
1584
1585 pub fn finalize_non_leaf_nodes(&mut self) {
1588 self.non_leaf_nodes.sort_by(|a, b| a.depth.cmp(&b.depth));
1589 }
1590
1591 #[must_use]
1593 pub fn with_child(mut self, other: Self) -> Self {
1594 self.append_child(other);
1595 self
1596 }
1597
1598 pub fn set_context_menu(&mut self, context_menu: Menu) {
1600 if let Some(root_id) = self.root.into_crate_internal() {
1601 self.node_data.as_container_mut()[root_id].set_context_menu(context_menu);
1602 }
1603 }
1604
1605 #[must_use]
1607 pub fn with_context_menu(mut self, context_menu: Menu) -> Self {
1608 self.set_context_menu(context_menu);
1609 self
1610 }
1611
1612 pub fn set_menu_bar(&mut self, menu_bar: Menu) {
1614 if let Some(root_id) = self.root.into_crate_internal() {
1615 self.node_data.as_container_mut()[root_id].set_menu_bar(menu_bar);
1616 }
1617 }
1618
1619 #[must_use]
1621 pub fn with_menu_bar(mut self, menu_bar: Menu) -> Self {
1622 self.set_menu_bar(menu_bar);
1623 self
1624 }
1625
1626 pub fn recompute_inheritance_and_compact_cache(&mut self) {
1641 cascade_trace(|| "compact cache REBUILT from css_props".to_string());
1642 let prev_font_hashes: Vec<u64> = self
1650 .css_property_cache
1651 .downcast_mut()
1652 .compact_cache
1653 .as_ref()
1654 .map(|c| c.prev_font_hashes.clone())
1655 .unwrap_or_default();
1656 let compact = self
1657 .css_property_cache
1658 .downcast_mut()
1659 .build_compact_cache_with_inheritance(
1660 self.node_data.as_container().internal,
1661 self.node_hierarchy.as_container().internal,
1662 &prev_font_hashes,
1663 );
1664 self.css_property_cache.downcast_mut().compact_cache = Some(compact);
1665 }
1666
1667 #[allow(clippy::similar_names)] pub fn extend_author_scopes_for_appended(&mut self, new_node: NodeId, parent: NodeId) {
1678 use azul_css::css::CssPathSelector;
1679 let p = parent.index();
1680 let n = new_node.index();
1681 let cache = self.css_property_cache.downcast_mut();
1682 for rule in cache.retained_author_css.rules.as_mut() {
1683 let mut sels = rule.path.selectors.as_ref().to_vec();
1684 let mut changed = false;
1685 for sel in &mut sels {
1686 if let CssPathSelector::Root(range) = sel {
1687 if range.contains(p) && range.end < n {
1688 range.end = n;
1689 changed = true;
1690 }
1691 }
1692 }
1693 if changed {
1694 rule.path.selectors = sels.into();
1695 }
1696 }
1697 }
1698
1699 pub fn set_user_property_override_fast(
1710 &mut self,
1711 node_id: &NodeId,
1712 new_properties: &[CssProperty],
1713 ) {
1714 let node_count = self.node_data.as_ref().len();
1715 if node_id.index() >= node_count {
1716 return;
1717 }
1718 let cache = self.get_css_property_cache_mut();
1719 if cache.user_overridden_properties.len() < node_count {
1720 cache
1721 .user_overridden_properties
1722 .resize(node_count, Vec::new());
1723 }
1724 for new_prop in new_properties {
1725 let prop_type = new_prop.get_type();
1726 let vec = &mut cache.user_overridden_properties[node_id.index()];
1727 if new_prop.is_initial() {
1728 if let Ok(idx) = vec.binary_search_by_key(&prop_type, |(k, _)| *k) {
1729 vec.remove(idx);
1730 }
1731 } else {
1732 match vec.binary_search_by_key(&prop_type, |(k, _)| *k) {
1733 Ok(idx) => vec[idx].1 = new_prop.clone(),
1734 Err(idx) => vec.insert(idx, (prop_type, new_prop.clone())),
1735 }
1736 }
1737 }
1738 }
1739
1740 pub fn restyle_retained(&mut self) {
1741 let css = self
1742 .css_property_cache
1743 .downcast_mut()
1744 .retained_author_css
1745 .clone();
1746 if css.is_empty() {
1747 return;
1748 }
1749 self.restyle(css);
1750 }
1751
1752 pub fn restyle(&mut self, mut css: Css) {
1753 let _stale_tag_ids = self.css_property_cache.downcast_mut().restyle(
1758 &mut css,
1759 &self.node_data.as_container(),
1760 &self.node_hierarchy,
1761 &self.non_leaf_nodes,
1762 &self.cascade_info.as_container(),
1763 );
1764
1765 self.css_property_cache.downcast_mut().retained_author_css = css;
1767
1768 self.css_property_cache
1770 .downcast_mut()
1771 .apply_ua_css(self.node_data.as_container().internal);
1772
1773 self.css_property_cache
1775 .downcast_mut()
1776 .compute_inherited_values(
1777 self.node_hierarchy.as_container().internal,
1778 self.node_data.as_container().internal,
1779 );
1780
1781 let prev_font_hashes: Vec<u64> = self
1787 .css_property_cache
1788 .downcast_mut()
1789 .compact_cache
1790 .as_ref()
1791 .map(|c| c.prev_font_hashes.clone())
1792 .unwrap_or_default();
1793 self.css_property_cache.downcast_mut().compact_cache = None;
1794 let compact = self
1795 .css_property_cache
1796 .downcast_mut()
1797 .build_compact_cache_with_inheritance(
1798 self.node_data.as_container().internal,
1799 self.node_hierarchy.as_container().internal,
1800 &prev_font_hashes,
1801 );
1802 self.css_property_cache.downcast_mut().compact_cache = Some(compact);
1803 self.css_property_cache
1804 .downcast_mut()
1805 .invalidate_resolved_font_sizes();
1806
1807 let new_tag_ids = self
1810 .css_property_cache
1811 .downcast_mut()
1812 .generate_tag_ids(&self.node_data.as_container(), &self.node_hierarchy);
1813 self.tag_ids_to_node_ids = new_tag_ids.into();
1814 }
1815
1816 #[inline]
1818 #[must_use]
1819 pub const fn node_count(&self) -> usize {
1820 self.node_data.len()
1821 }
1822
1823 #[inline]
1825 #[must_use]
1826 pub fn get_css_property_cache(&self) -> &CssPropertyCache {
1827 &self.css_property_cache.ptr
1828 }
1829
1830 #[inline]
1832 pub fn get_css_property_cache_mut(&mut self) -> &mut CssPropertyCache {
1833 &mut self.css_property_cache.ptr
1834 }
1835
1836 #[inline]
1838 #[must_use]
1839 pub fn get_styled_node_state(&self, node_id: &NodeId) -> StyledNodeState {
1840 self.styled_nodes.as_container()[*node_id].styled_node_state
1841 }
1842
1843 #[must_use]
1845 pub fn restyle_nodes_hover(&mut self, nodes: &[NodeId], new_hover_state: bool) -> RestyleNodes {
1846 self.restyle_nodes_state(
1847 nodes,
1848 new_hover_state,
1849 |state, val| state.hover = val,
1850 azul_css::dynamic_selector::PseudoStateType::Hover,
1851 )
1852 }
1853
1854 #[must_use]
1856 pub fn restyle_nodes_active(
1857 &mut self,
1858 nodes: &[NodeId],
1859 new_active_state: bool,
1860 ) -> RestyleNodes {
1861 self.restyle_nodes_state(
1862 nodes,
1863 new_active_state,
1864 |state, val| state.active = val,
1865 azul_css::dynamic_selector::PseudoStateType::Active,
1866 )
1867 }
1868
1869 #[must_use]
1871 pub fn restyle_nodes_focus(&mut self, nodes: &[NodeId], new_focus_state: bool) -> RestyleNodes {
1872 self.restyle_nodes_state(
1873 nodes,
1874 new_focus_state,
1875 |state, val| state.focused = val,
1876 azul_css::dynamic_selector::PseudoStateType::Focus,
1877 )
1878 }
1879
1880 pub fn restyle_nodes_seat_focus(&mut self, nodes: &[NodeId], on: bool) -> RestyleNodes {
1882 self.restyle_nodes_state(
1883 nodes,
1884 on,
1885 |state, val| state.seat_focused = val,
1886 azul_css::dynamic_selector::PseudoStateType::SeatFocus,
1887 )
1888 }
1889
1890 pub fn restyle_on_seat_focus_change(
1893 &mut self,
1894 lost: Option<NodeId>,
1895 gained: Option<NodeId>,
1896 ) -> RestyleResult {
1897 let mut result = RestyleResult {
1898 gpu_only_changes: true,
1899 ..RestyleResult::default()
1900 };
1901 let mut process = |changes: RestyleNodes, result: &mut RestyleResult| {
1902 for (node_id, props) in changes {
1903 for change in &props {
1904 let prop_type = change.current_prop.get_type();
1905 let scope = prop_type.relayout_scope(true);
1906 if scope > result.max_relayout_scope {
1907 result.max_relayout_scope = scope;
1908 }
1909 if scope != RelayoutScope::None {
1910 result.needs_layout = true;
1911 result.gpu_only_changes = false;
1912 }
1913 if !prop_type.is_gpu_only_property() {
1914 result.gpu_only_changes = false;
1915 }
1916 result.needs_display_list = true;
1917 }
1918 result.changed_nodes.entry(node_id).or_default().extend(props);
1919 }
1920 };
1921 if let Some(old) = lost {
1922 let changes = self.restyle_nodes_seat_focus(&[old], false);
1923 process(changes, &mut result);
1924 }
1925 if let Some(new) = gained {
1926 let changes = self.restyle_nodes_seat_focus(&[new], true);
1927 process(changes, &mut result);
1928 }
1929 result
1930 }
1931
1932 fn restyle_nodes_state(
1934 &mut self,
1935 nodes: &[NodeId],
1936 new_state_value: bool,
1937 set_state: impl Fn(&mut StyledNodeState, bool),
1938 pseudo_state_type: azul_css::dynamic_selector::PseudoStateType,
1939 ) -> RestyleNodes {
1940 let node_count = self.node_count();
1945 let nodes: Vec<NodeId> = nodes
1946 .iter()
1947 .copied()
1948 .filter(|nid| nid.index() < node_count)
1949 .collect();
1950
1951 let old_node_states = nodes
1953 .iter()
1954 .map(|nid| self.styled_nodes.as_container()[*nid].styled_node_state)
1955 .collect::<Vec<_>>();
1956
1957 for nid in &nodes {
1958 set_state(
1959 &mut self.styled_nodes.as_container_mut()[*nid].styled_node_state,
1960 new_state_value,
1961 );
1962 }
1963
1964 let css_property_cache = self.get_css_property_cache();
1965 let styled_nodes = self.styled_nodes.as_container();
1966 let node_data = self.node_data.as_container();
1967
1968 let v = nodes
1970 .iter()
1971 .zip(old_node_states.iter())
1972 .filter_map(|(node_id, old_node_state)| {
1973 let mut keys_normal: Vec<_> = CssPropertyCache::prop_types_for_state(
1974 css_property_cache.css_props.get_slice(node_id.index()),
1975 pseudo_state_type,
1976 ).collect();
1977 let mut keys_inherited: Vec<_> = CssPropertyCache::prop_types_for_state(
1978 css_property_cache.cascaded_props.get_slice(node_id.index()),
1979 pseudo_state_type,
1980 ).collect();
1981 let keys_inline: Vec<CssPropertyType> = {
1982 use azul_css::dynamic_selector::DynamicSelector;
1983 node_data[*node_id]
1984 .style
1985 .iter_inline_properties()
1986 .filter_map(|(prop, conds)| {
1987 let matches = conds.as_slice().iter().any(|c| {
1988 matches!(c, DynamicSelector::PseudoState(pst) if *pst == pseudo_state_type)
1989 });
1990 if matches {
1991 Some(prop.get_type())
1992 } else {
1993 None
1994 }
1995 })
1996 .collect()
1997 };
1998 let mut keys_inline_ref: Vec<_> = keys_inline.iter().collect();
1999
2000 keys_normal.append(&mut keys_inherited);
2001 keys_normal.append(&mut keys_inline_ref);
2002
2003 let node_properties_that_could_have_changed = keys_normal;
2004
2005 if node_properties_that_could_have_changed.is_empty() {
2006 return None;
2007 }
2008
2009 let new_node_state = &styled_nodes[*node_id].styled_node_state;
2010 let node_data = &node_data[*node_id];
2011
2012 let changes = node_properties_that_could_have_changed
2013 .into_iter()
2014 .filter_map(|prop| {
2015 let old = css_property_cache.get_property_slow(
2017 node_data,
2018 node_id,
2019 old_node_state,
2020 prop,
2021 );
2022 let new = css_property_cache.get_property_slow(
2023 node_data,
2024 node_id,
2025 new_node_state,
2026 prop,
2027 );
2028 if old == new {
2029 None
2030 } else {
2031 Some(ChangedCssProperty {
2032 previous_state: *old_node_state,
2033 previous_prop: old.map_or_else(|| CssProperty::auto(*prop), Clone::clone),
2034 current_state: *new_node_state,
2035 current_prop: new.map_or_else(|| CssProperty::auto(*prop), Clone::clone),
2036 })
2037 }
2038 })
2039 .collect::<Vec<_>>();
2040
2041 if changes.is_empty() {
2042 None
2043 } else {
2044 Some((*node_id, changes))
2045 }
2046 })
2047 .collect::<Vec<_>>();
2048
2049 v.into_iter().collect()
2050 }
2051
2052 #[must_use]
2066 pub fn restyle_on_state_change(
2067 &mut self,
2068 focus_changes: Option<FocusChange>,
2069 hover_changes: Option<HoverChange>,
2070 active_changes: Option<ActiveChange>,
2071 ) -> RestyleResult {
2072 let mut result = RestyleResult {
2074 gpu_only_changes: true,
2075 ..RestyleResult::default()
2076 };
2077
2078 let mut process_changes = |changes: RestyleNodes| {
2080 for (node_id, props) in changes {
2081 for change in &props {
2082 let prop_type = change.current_prop.get_type();
2083
2084 let scope = prop_type.relayout_scope(true);
2091
2092 if scope > result.max_relayout_scope {
2094 result.max_relayout_scope = scope;
2095 }
2096
2097 if scope != RelayoutScope::None {
2099 result.needs_layout = true;
2100 result.gpu_only_changes = false;
2101 }
2102
2103 if !prop_type.is_gpu_only_property() {
2105 result.gpu_only_changes = false;
2106 }
2107
2108 result.needs_display_list = true;
2110 }
2111
2112 result
2113 .changed_nodes
2114 .entry(node_id)
2115 .or_default()
2116 .extend(props);
2117 }
2118 };
2119
2120 if let Some(focus) = focus_changes {
2122 if let Some(old) = focus.lost_focus {
2123 let changes = self.restyle_nodes_focus(&[old], false);
2124 process_changes(changes);
2125 }
2126 if let Some(new) = focus.gained_focus {
2127 let changes = self.restyle_nodes_focus(&[new], true);
2128 process_changes(changes);
2129 }
2130 }
2131
2132 if let Some(hover) = hover_changes {
2134 if !hover.left_nodes.is_empty() {
2135 let changes = self.restyle_nodes_hover(&hover.left_nodes, false);
2136 process_changes(changes);
2137 }
2138 if !hover.entered_nodes.is_empty() {
2139 let changes = self.restyle_nodes_hover(&hover.entered_nodes, true);
2140 process_changes(changes);
2141 }
2142 }
2143
2144 if let Some(active) = active_changes {
2146 if !active.deactivated.is_empty() {
2147 let changes = self.restyle_nodes_active(&active.deactivated, false);
2148 process_changes(changes);
2149 }
2150 if !active.activated.is_empty() {
2151 let changes = self.restyle_nodes_active(&active.activated, true);
2152 process_changes(changes);
2153 }
2154 }
2155
2156 if result.changed_nodes.is_empty() {
2158 result.needs_display_list = false;
2159 result.gpu_only_changes = false;
2160 }
2161
2162 if result.needs_layout {
2164 result.needs_display_list = true;
2165 result.gpu_only_changes = false;
2166 }
2167
2168 result
2169 }
2170
2171 #[must_use]
2182 pub fn restyle_user_property(
2183 &mut self,
2184 node_id: &NodeId,
2185 new_properties: &[CssProperty],
2186 ) -> RestyleNodes {
2187 let mut map = BTreeMap::default();
2188
2189 if new_properties.is_empty() {
2190 return map;
2191 }
2192
2193 let node_count = self.node_data.as_ref().len();
2194 if node_id.index() >= node_count {
2195 return map;
2196 }
2197
2198 let node_data = self.node_data.as_container();
2199 let node_data = &node_data[*node_id];
2200
2201 let node_states = &self.styled_nodes.as_container();
2202 let old_node_state = &node_states[*node_id].styled_node_state;
2203
2204 let changes: Vec<ChangedCssProperty> = {
2205 let css_property_cache = self.get_css_property_cache();
2206
2207 new_properties
2208 .iter()
2209 .filter_map(|new_prop| {
2210 let old_prop = css_property_cache.get_property_slow(
2211 node_data,
2212 node_id,
2213 old_node_state,
2214 &new_prop.get_type(),
2215 );
2216
2217 let old_prop = old_prop
2218 .map_or_else(|| CssProperty::auto(new_prop.get_type()), Clone::clone);
2219
2220 if old_prop == *new_prop {
2221 None
2222 } else {
2223 Some(ChangedCssProperty {
2224 previous_state: *old_node_state,
2225 previous_prop: old_prop,
2226 current_state: *old_node_state,
2228 current_prop: new_prop.clone(),
2229 })
2230 }
2231 })
2232 .collect()
2233 };
2234
2235 let css_property_cache_mut = self.get_css_property_cache_mut();
2236
2237 if css_property_cache_mut.user_overridden_properties.len() < node_count {
2242 css_property_cache_mut
2243 .user_overridden_properties
2244 .resize(node_count, Vec::new());
2245 }
2246
2247 for new_prop in new_properties {
2248 let prop_type = new_prop.get_type();
2249 let vec = &mut css_property_cache_mut.user_overridden_properties[node_id.index()];
2250 if new_prop.is_initial() {
2251 if let Ok(idx) = vec.binary_search_by_key(&prop_type, |(k, _)| *k) {
2253 vec.remove(idx);
2254 }
2255 } else {
2256 match vec.binary_search_by_key(&prop_type, |(k, _)| *k) {
2257 Ok(idx) => vec[idx].1 = new_prop.clone(),
2258 Err(idx) => vec.insert(idx, (prop_type, new_prop.clone())),
2259 }
2260 }
2261 }
2262
2263 if new_properties
2292 .iter()
2293 .any(|p| p.get_type().can_trigger_relayout() || p.get_type().is_inheritable())
2294 {
2295 self.recompute_inheritance_and_compact_cache();
2296 self.get_css_property_cache_mut()
2297 .invalidate_resolved_font_sizes();
2298 }
2299
2300 if !changes.is_empty() {
2301 map.insert(*node_id, changes);
2302 }
2303
2304 map
2305 }
2306
2307 pub fn set_dynamic_selector_context(
2326 &mut self,
2327 context: azul_css::dynamic_selector::DynamicSelectorContext,
2328 ) {
2329 {
2330 let cache = self.get_css_property_cache_mut();
2331 let same = cache.dynamic_context.as_deref() == Some(&context);
2332 cascade_trace(|| format!("dynamic context offered, unchanged={same}"));
2333 if same {
2334 return;
2335 }
2336 cache.dynamic_context = Some(Box::new(context));
2337 }
2338 let author_conditional = self
2346 .get_css_property_cache()
2347 .retained_author_css
2348 .rules
2349 .as_ref()
2350 .iter()
2351 .any(azul_css::css::CssRuleBlock::depends_on_dynamic_context);
2352 if author_conditional {
2353 self.restyle_retained();
2354 }
2355 let needs_rebuild = self
2356 .get_css_property_cache()
2357 .compact_cache
2358 .as_ref()
2359 .is_none_or(|cc| cc.has_dynamic_conditions);
2360 if needs_rebuild {
2361 self.recompute_inheritance_and_compact_cache();
2362 self.get_css_property_cache_mut()
2363 .invalidate_resolved_font_sizes();
2364 let new_tag_ids = self
2365 .css_property_cache
2366 .downcast_mut()
2367 .generate_tag_ids(&self.node_data.as_container(), &self.node_hierarchy);
2368 self.tag_ids_to_node_ids = new_tag_ids.into();
2369 }
2370 }
2371
2372 #[must_use]
2387 pub fn viewport_breakpoints(&self) -> Option<(Vec<f32>, Vec<f32>)> {
2388 let cache = self.get_css_property_cache();
2389 let cc = cache.compact_cache.as_ref()?;
2390 let (mut w, mut h) = cache.retained_author_css.viewport_breakpoints();
2391 w.extend(cc.inline_viewport_w.iter().copied().map(f32::from_bits));
2392 h.extend(cc.inline_viewport_h.iter().copied().map(f32::from_bits));
2393 w.sort_by_key(|v| v.to_bits());
2394 w.dedup_by_key(|v| v.to_bits());
2395 h.sort_by_key(|v| v.to_bits());
2396 h.dedup_by_key(|v| v.to_bits());
2397 Some((w, h))
2398 }
2399
2400 pub fn migrate_user_overrides_from(
2416 &mut self,
2417 old_cache: &CssPropertyCache,
2418 node_moves: &[crate::diff::NodeMove],
2419 ) {
2420 let node_count = self.node_data.as_ref().len();
2421 let mut migrated_any = false;
2422 for m in node_moves {
2423 let Some(old_vec) = old_cache
2424 .user_overridden_properties
2425 .get(m.old_node_id.index())
2426 .filter(|v| !v.is_empty())
2427 else {
2428 continue;
2429 };
2430 let new_idx = m.new_node_id.index();
2431 if new_idx >= node_count {
2432 continue;
2433 }
2434 let old_vec = old_vec.clone();
2435 let cache = self.get_css_property_cache_mut();
2436 if cache.user_overridden_properties.len() < node_count {
2437 cache
2438 .user_overridden_properties
2439 .resize(node_count, Vec::new());
2440 }
2441 cache.user_overridden_properties[new_idx] = old_vec;
2442 migrated_any = true;
2443 }
2444 if migrated_any {
2445 self.recompute_inheritance_and_compact_cache();
2446 self.get_css_property_cache_mut()
2447 .invalidate_resolved_font_sizes();
2448 }
2449 }
2450
2451 #[must_use]
2470 pub fn reconstruct_dom_subtree(&self, root: Option<NodeId>) -> Dom {
2471 use crate::dom::NodeData;
2472
2473 let hierarchy = self.node_hierarchy.as_container();
2474 let node_data = self.node_data.as_container();
2475 let root_id = root.unwrap_or(NodeId::ZERO);
2476
2477 let make_dom = |id: NodeId| -> Dom {
2478 Dom {
2479 root: node_data
2480 .get(id)
2481 .cloned()
2482 .unwrap_or_else(NodeData::create_div),
2483 children: Vec::new().into(),
2484 css: Vec::new().into(),
2485 estimated_total_children: 0,
2486 }
2487 };
2488
2489 let mut result_stack: Vec<Dom> = vec![make_dom(root_id)];
2494 let mut visit_stack: Vec<(NodeId, Option<NodeId>)> = vec![(
2495 root_id,
2496 hierarchy
2497 .get(root_id)
2498 .and_then(|n| n.first_child_id(root_id)),
2499 )];
2500
2501 while let Some((node, next_child)) = visit_stack.pop() {
2502 if let Some(child) = next_child {
2503 let sibling = hierarchy
2506 .get(child)
2507 .and_then(NodeHierarchyItem::next_sibling_id);
2508 visit_stack.push((node, sibling));
2509 result_stack.push(make_dom(child));
2510 visit_stack.push((
2511 child,
2512 hierarchy.get(child).and_then(|c| c.first_child_id(child)),
2513 ));
2514 } else {
2515 let Some(finished) = result_stack.pop() else {
2516 break;
2517 };
2518 if let Some(parent) = result_stack.last_mut() {
2519 parent.add_child(finished);
2520 } else {
2521 let mut finished = finished;
2522 let author_css = self.get_css_property_cache().retained_author_css.clone();
2523 if !author_css.is_empty() {
2524 finished.css = vec![author_css].into();
2525 }
2526 return finished;
2527 }
2528 }
2529 }
2530
2531 Dom::create_div()
2533 }
2534
2535 #[must_use]
2545 pub fn get_html_string(&self, custom_head: &str, custom_body: &str, test_mode: bool) -> String {
2546 let css_property_cache = self.get_css_property_cache();
2547
2548 let mut output = String::new();
2549
2550 let mut should_print_close_tag_after_node: BTreeMap<NodeId, Vec<(NodeId, usize)>> =
2552 BTreeMap::new();
2553
2554 let should_print_close_tag_debug = self
2555 .non_leaf_nodes
2556 .iter()
2557 .filter_map(|p| {
2558 let parent_node_id = p.node_id.into_crate_internal()?;
2559 let mut total_last_child = None;
2560 recursive_get_last_child(
2561 parent_node_id,
2562 self.node_hierarchy.as_ref(),
2563 &mut total_last_child,
2564 );
2565 let total_last_child = total_last_child?;
2566 Some((parent_node_id, (total_last_child, p.depth)))
2567 })
2568 .collect::<BTreeMap<_, _>>();
2569
2570 for (parent_id, (last_child, parent_depth)) in should_print_close_tag_debug {
2571 should_print_close_tag_after_node
2572 .entry(last_child)
2573 .or_default()
2574 .push((parent_id, parent_depth));
2575 }
2576
2577 let mut all_node_depths = self
2578 .non_leaf_nodes
2579 .iter()
2580 .filter_map(|p| {
2581 let parent_node_id = p.node_id.into_crate_internal()?;
2582 Some((parent_node_id, p.depth))
2583 })
2584 .collect::<BTreeMap<_, _>>();
2585
2586 for (parent_node_id, parent_depth) in self
2587 .non_leaf_nodes
2588 .iter()
2589 .filter_map(|p| Some((p.node_id.into_crate_internal()?, p.depth)))
2590 {
2591 for child_id in parent_node_id.az_children(&self.node_hierarchy.as_container()) {
2592 all_node_depths.insert(child_id, parent_depth + 1);
2593 }
2594 }
2595
2596 for node_id in self.node_hierarchy.as_container().linear_iter() {
2597 let depth = all_node_depths.get(&node_id).copied().unwrap_or(0);
2601
2602 let node_data = &self.node_data.as_container()[node_id];
2603 let node_state = &self.styled_nodes.as_container()[node_id].styled_node_state;
2604 let tabs = String::from(" ").repeat(depth);
2605
2606 output.push_str("\r\n");
2607 output.push_str(&tabs);
2608 output.push_str(&node_data.debug_print_start(css_property_cache, &node_id, node_state));
2609
2610 if let Some(content) = node_data.get_node_type().format().as_ref() {
2611 output.push_str(content);
2612 }
2613
2614 let node_has_children = self.node_hierarchy.as_container()[node_id]
2615 .first_child_id(node_id)
2616 .is_some();
2617 if !node_has_children {
2618 let node_data = &self.node_data.as_container()[node_id];
2619 output.push_str(&node_data.debug_print_end());
2620 }
2621
2622 if let Some(close_tag_vec) = should_print_close_tag_after_node.get(&node_id) {
2623 let mut close_tag_vec = close_tag_vec.clone();
2624 close_tag_vec.sort_by(|a, b| b.1.cmp(&a.1)); for (close_tag_parent_id, close_tag_depth) in close_tag_vec {
2626 let node_data = &self.node_data.as_container()[close_tag_parent_id];
2627 let tabs = String::from(" ").repeat(close_tag_depth);
2628 output.push_str("\r\n");
2629 output.push_str(&tabs);
2630 output.push_str(&node_data.debug_print_end());
2631 }
2632 }
2633 }
2634
2635 if test_mode {
2636 output
2637 } else {
2638 format!(
2639 "
2640 <html>
2641 <head>
2642 <style>* {{ margin:0px; padding:0px; }}</style>
2643 {custom_head}
2644 </head>
2645 {output}
2646 {custom_body}
2647 </html>
2648 "
2649 )
2650 }
2651 }
2652
2653 #[must_use]
2655 pub fn get_rects_in_rendering_order(&self) -> ContentGroup {
2656 Self::determine_rendering_order(
2657 self.non_leaf_nodes.as_ref(),
2658 &self.node_hierarchy.as_container(),
2659 &self.styled_nodes.as_container(),
2660 &self.node_data.as_container(),
2661 self.get_css_property_cache(),
2662 )
2663 }
2664
2665 fn determine_rendering_order(
2668 non_leaf_nodes: &[ParentWithNodeDepth],
2669 node_hierarchy: &NodeDataContainerRef<'_, NodeHierarchyItem>,
2670 styled_nodes: &NodeDataContainerRef<'_, StyledNode>,
2671 node_data_container: &NodeDataContainerRef<'_, NodeData>,
2672 css_property_cache: &CssPropertyCache,
2673 ) -> ContentGroup {
2674 let children_sorted = non_leaf_nodes
2675 .iter()
2676 .filter_map(|parent| {
2677 Some((
2678 parent.node_id,
2679 sort_children_by_position(
2680 parent.node_id.into_crate_internal()?,
2681 node_hierarchy,
2682 styled_nodes,
2683 node_data_container,
2684 css_property_cache,
2685 ),
2686 ))
2687 })
2688 .collect::<Vec<_>>();
2689
2690 let children_sorted: BTreeMap<NodeHierarchyItemId, Vec<NodeHierarchyItemId>> =
2691 children_sorted.into_iter().collect();
2692
2693 let mut root_content_group = ContentGroup {
2694 root: NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO)),
2695 children: Vec::new().into(),
2696 };
2697
2698 fill_content_group_children(&mut root_content_group, &children_sorted);
2699
2700 root_content_group
2701 }
2702
2703 #[must_use]
2705 pub fn swap_with_default(&mut self) -> Self {
2706 let mut new = Self::default();
2707 core::mem::swap(self, &mut new);
2708 new
2709 }
2710}
2711
2712#[derive(Debug, PartialEq, PartialOrd, Eq)]
2714pub struct CompactDom {
2715 pub node_hierarchy: NodeHierarchy,
2717 pub node_data: NodeDataContainer<NodeData>,
2719 pub root: NodeId,
2721}
2722
2723impl CompactDom {
2724 #[inline]
2726 #[must_use]
2727 pub fn len(&self) -> usize {
2728 self.node_hierarchy.as_ref().len()
2729 }
2730
2731 #[inline]
2733 #[must_use]
2734 pub fn is_empty(&self) -> bool {
2735 self.node_hierarchy.as_ref().is_empty()
2736 }
2737}
2738
2739impl From<Dom> for CompactDom {
2740 fn from(dom: Dom) -> Self {
2741 convert_dom_into_compact_dom(dom)
2742 }
2743}
2744
2745#[must_use]
2747pub fn convert_dom_into_compact_dom(mut dom: Dom) -> CompactDom {
2748 fn convert_dom_into_compact_dom_internal(
2750 dom: &mut Dom,
2751 node_hierarchy: &mut [Node],
2752 node_data: &mut Vec<NodeData>,
2753 parent_node_id: NodeId,
2754 node: Node,
2755 cur_node_id: &mut usize,
2756 ) {
2757 node_hierarchy[parent_node_id.index()] = node;
2768
2769 let copy = dom.root.copy_special_moving_complex();
2781
2782 node_data[parent_node_id.index()] = copy;
2783
2784 *cur_node_id += 1;
2785
2786 let mut previous_sibling_id = None;
2787 let children_len = dom.children.len();
2788 for (child_index, child_dom) in dom.children.as_mut().iter_mut().enumerate() {
2789 let child_node_id = NodeId::new(*cur_node_id);
2790 let is_last_child = (child_index + 1) == children_len;
2791 let child_dom_is_empty = child_dom.children.is_empty();
2792 let child_node = Node {
2793 parent: Some(parent_node_id),
2794 previous_sibling: previous_sibling_id,
2795 next_sibling: if is_last_child {
2796 None
2797 } else {
2798 Some(child_node_id + child_dom.estimated_total_children + 1)
2799 },
2800 last_child: if child_dom_is_empty {
2801 None
2802 } else {
2803 Some(child_node_id + child_dom.estimated_total_children)
2804 },
2805 };
2806 previous_sibling_id = Some(child_node_id);
2807 convert_dom_into_compact_dom_internal(
2809 child_dom,
2810 node_hierarchy,
2811 node_data,
2812 child_node_id,
2813 child_node,
2814 cur_node_id,
2815 );
2816 }
2817
2818 node_hierarchy[parent_node_id.index()].last_child = previous_sibling_id;
2827 }
2828
2829 let sum_nodes = dom.fixup_children_estimated();
2831
2832 let mut node_hierarchy = vec![Node::ROOT; sum_nodes + 1];
2833 let mut node_data = vec![NodeData::create_div(); sum_nodes + 1];
2834 let mut cur_node_id = 0;
2835
2836 let root_node_id = NodeId::ZERO;
2837 let root_node = Node {
2838 parent: None,
2839 previous_sibling: None,
2840 next_sibling: None,
2841 last_child: if dom.children.is_empty() {
2842 None
2843 } else {
2844 Some(root_node_id + dom.estimated_total_children)
2845 },
2846 };
2847
2848 convert_dom_into_compact_dom_internal(
2849 &mut dom,
2850 &mut node_hierarchy,
2851 &mut node_data,
2852 root_node_id,
2853 root_node,
2854 &mut cur_node_id,
2855 );
2856
2857 CompactDom {
2858 node_hierarchy: NodeHierarchy {
2859 internal: node_hierarchy,
2860 },
2861 node_data: NodeDataContainer {
2862 internal: node_data,
2863 },
2864 root: root_node_id,
2865 }
2866}
2867
2868fn scope_inline_css(dom: &mut Dom, next_id: &mut usize) {
2876 let start = *next_id;
2877 let end = start + dom.estimated_total_children;
2878 for css in dom.css.as_mut().iter_mut() {
2879 for rule in css.rules.as_mut().iter_mut() {
2880 let node_only = rule.priority >= azul_css::css::rule_priority::INLINE;
2887 rule.path.push_front_scope_for(start, end, node_only);
2888 }
2889 }
2890 *next_id += 1;
2891 for child in dom.children.as_mut().iter_mut() {
2892 scope_inline_css(child, next_id);
2893 }
2894}
2895
2896fn collect_css_from_dom(dom: &Dom, out: &mut Vec<Css>) {
2900 for child in &dom.children {
2902 collect_css_from_dom(child, out);
2903 }
2904 for css in &dom.css {
2906 out.push(css.clone());
2907 }
2908}
2909
2910fn strip_css_from_dom(dom: &mut Dom) {
2913 dom.css = Vec::new().into();
2914 for child in dom.children.as_mut().iter_mut() {
2915 strip_css_from_dom(child);
2916 }
2917}
2918
2919fn fill_content_group_children(
2920 group: &mut ContentGroup,
2921 children_sorted: &BTreeMap<NodeHierarchyItemId, Vec<NodeHierarchyItemId>>,
2922) {
2923 if let Some(c) = children_sorted.get(&group.root) {
2924 group.children = c
2926 .iter()
2927 .map(|child| ContentGroup {
2928 root: *child,
2929 children: Vec::new().into(),
2930 })
2931 .collect::<Vec<ContentGroup>>()
2932 .into();
2933
2934 for c in group.children.as_mut() {
2935 fill_content_group_children(c, children_sorted);
2936 }
2937 }
2938}
2939
2940fn sort_children_by_position(
2941 parent: NodeId,
2942 node_hierarchy: &NodeDataContainerRef<'_, NodeHierarchyItem>,
2943 rectangles: &NodeDataContainerRef<'_, StyledNode>,
2944 node_data_container: &NodeDataContainerRef<'_, NodeData>,
2945 css_property_cache: &CssPropertyCache,
2946) -> Vec<NodeHierarchyItemId> {
2947 use azul_css::props::layout::LayoutPosition::Absolute;
2948
2949 let children_positions = parent
2950 .az_children(node_hierarchy)
2951 .map(|nid| {
2952 let position = css_property_cache
2953 .get_position(
2954 &node_data_container[nid],
2955 &nid,
2956 &rectangles[nid].styled_node_state,
2957 )
2958 .and_then(|p| (*p).get_property_or_default())
2959 .unwrap_or_default();
2960 let id = NodeHierarchyItemId::from_crate_internal(Some(nid));
2961 (id, position)
2962 })
2963 .collect::<Vec<_>>();
2964
2965 let mut not_absolute_children = children_positions
2966 .iter()
2967 .filter_map(|(node_id, position)| {
2968 if *position == Absolute {
2969 None
2970 } else {
2971 Some(*node_id)
2972 }
2973 })
2974 .collect::<Vec<_>>();
2975
2976 let mut absolute_children = children_positions
2977 .iter()
2978 .filter_map(|(node_id, position)| {
2979 if *position == Absolute {
2980 Some(*node_id)
2981 } else {
2982 None
2983 }
2984 })
2985 .collect::<Vec<_>>();
2986
2987 not_absolute_children.append(&mut absolute_children);
2989 not_absolute_children
2990}
2991
2992fn recursive_get_last_child(
2995 node_id: NodeId,
2996 node_hierarchy: &[NodeHierarchyItem],
2997 target: &mut Option<NodeId>,
2998) {
2999 match node_hierarchy[node_id.index()].last_child_id() {
3000 None => (),
3001 Some(s) => {
3002 *target = Some(s);
3003 recursive_get_last_child(s, node_hierarchy, target);
3004 }
3005 }
3006}
3007
3008#[must_use]
3022pub fn is_before_in_document_order(
3023 hierarchy: &NodeHierarchyItemVec,
3024 node_a: NodeId,
3025 node_b: NodeId,
3026) -> bool {
3027 if node_a == node_b {
3028 return false;
3029 }
3030
3031 let hierarchy = hierarchy.as_container();
3032
3033 let path_a = get_path_to_root(&hierarchy, node_a);
3035 let path_b = get_path_to_root(&hierarchy, node_b);
3036
3037 let min_len = path_a.len().min(path_b.len());
3039
3040 for i in 0..min_len {
3041 if path_a[i] != path_b[i] {
3042 let child_towards_a = path_a[i];
3044 let child_towards_b = path_b[i];
3045
3046 return child_towards_a.index() < child_towards_b.index();
3049 }
3050 }
3051
3052 path_a.len() < path_b.len()
3054}
3055
3056fn get_path_to_root(
3058 hierarchy: &NodeDataContainerRef<'_, NodeHierarchyItem>,
3059 node: NodeId,
3060) -> Vec<NodeId> {
3061 let mut path = Vec::new();
3062 let mut current = Some(node);
3063
3064 while let Some(node_id) = current {
3065 path.push(node_id);
3066 current = hierarchy
3067 .get(node_id)
3068 .and_then(NodeHierarchyItem::parent_id);
3069 }
3070
3071 path.reverse();
3073 path
3074}
3075
3076#[must_use]
3089pub fn collect_nodes_in_document_order(
3090 hierarchy: &NodeHierarchyItemVec,
3091 start_node: NodeId,
3092 end_node: NodeId,
3093) -> Vec<NodeId> {
3094 if start_node == end_node {
3095 return vec![start_node];
3096 }
3097
3098 let hierarchy_container = hierarchy.as_container();
3099 let hierarchy_slice = hierarchy.as_ref();
3100
3101 let mut result = Vec::new();
3102 let mut in_range = false;
3103
3104 let mut stack: Vec<NodeId> = vec![NodeId::ZERO]; while let Some(current) = stack.pop() {
3109 if current == start_node {
3111 in_range = true;
3112 }
3113
3114 if in_range {
3116 result.push(current);
3117 }
3118
3119 if current == end_node {
3121 break;
3122 }
3123
3124 if let Some(item) = hierarchy_container.get(current) {
3127 if let Some(first_child) = item.first_child_id(current) {
3129 let mut children = Vec::new();
3131 let mut child = Some(first_child);
3132 while let Some(child_id) = child {
3133 children.push(child_id);
3134 child = hierarchy_container
3135 .get(child_id)
3136 .and_then(NodeHierarchyItem::next_sibling_id);
3137 }
3138 for child_id in children.into_iter().rev() {
3140 stack.push(child_id);
3141 }
3142 }
3143 }
3144 }
3145
3146 result
3147}
3148
3149#[must_use]
3163pub fn is_layout_equivalent(old: &StyledDom, new: &StyledDom) -> bool {
3164 use crate::dom::NodeType;
3165 use crate::resources::DecodedImage;
3166
3167 let old_nodes = old.node_data.as_ref();
3169 let new_nodes = new.node_data.as_ref();
3170 if old_nodes.len() != new_nodes.len() {
3171 return false;
3172 }
3173
3174 let old_hier = old.node_hierarchy.as_ref();
3176 let new_hier = new.node_hierarchy.as_ref();
3177 if old_hier.len() != new_hier.len() {
3178 return false;
3179 }
3180 if old_hier != new_hier {
3181 return false;
3182 }
3183
3184 for (old_node, new_node) in old_nodes.iter().zip(new_nodes.iter()) {
3186 if core::mem::discriminant(&old_node.node_type)
3188 != core::mem::discriminant(&new_node.node_type)
3189 {
3190 return false;
3191 }
3192
3193 match (&old_node.node_type, &new_node.node_type) {
3195 (NodeType::Image(old_img), NodeType::Image(new_img)) => {
3196 match (old_img.get_data(), new_img.get_data()) {
3197 (DecodedImage::Callback(old_cb), DecodedImage::Callback(new_cb)) => {
3198 if old_cb.callback.cb != new_cb.callback.cb {
3200 return false;
3201 }
3202 if old_cb.refany.get_type_id() != new_cb.refany.get_type_id() {
3204 return false;
3205 }
3206 }
3207 _ => {
3208 if old_img != new_img {
3210 return false;
3211 }
3212 }
3213 }
3214 }
3215 _ => {
3216 if old_node.node_type != new_node.node_type {
3217 return false;
3218 }
3219 }
3220 }
3221
3222 {
3224 use crate::dom::AttributeType;
3225 let old_ids_classes: Vec<_> = old_node
3226 .attributes()
3227 .as_ref()
3228 .iter()
3229 .filter(|a| matches!(a, AttributeType::Id(_) | AttributeType::Class(_)))
3230 .collect();
3231 let new_ids_classes: Vec<_> = new_node
3232 .attributes()
3233 .as_ref()
3234 .iter()
3235 .filter(|a| matches!(a, AttributeType::Id(_) | AttributeType::Class(_)))
3236 .collect();
3237 if old_ids_classes != new_ids_classes {
3238 return false;
3239 }
3240 }
3241
3242 if old_node.style != new_node.style {
3244 return false;
3245 }
3246
3247 let old_cbs = old_node.callbacks.as_ref();
3250 let new_cbs = new_node.callbacks.as_ref();
3251 if old_cbs.len() != new_cbs.len() {
3252 return false;
3253 }
3254 for (old_cb, new_cb) in old_cbs.iter().zip(new_cbs.iter()) {
3255 if old_cb.event != new_cb.event {
3256 return false;
3257 }
3258 }
3259
3260 if old_node.attributes().as_ref() != new_node.attributes().as_ref() {
3262 return false;
3263 }
3264 }
3265
3266 let old_styled = old.styled_nodes.as_ref();
3268 let new_styled = new.styled_nodes.as_ref();
3269 if old_styled.len() != new_styled.len() {
3270 return false;
3271 }
3272 if old_styled != new_styled {
3273 return false;
3274 }
3275
3276 true
3277}
3278
3279#[cfg(test)]
3280#[path = "styled_dom_test.rs"]
3281mod styled_dom_test;